use crate::common_utils::common::macros::{fx_err_and_bail, with_line};
use crate::temperature::types;
use anyhow::Error;
use fidl_fuchsia_hardware_temperature::{DeviceMarker, DeviceProxy};
use fidl_fuchsia_power_metrics::{Metric, RecorderMarker, RecorderProxy, Temperature};
use fuchsia_component::client::connect_to_protocol;
use serde_json::Value;
use std::path::Path;
const CLIENT_ID: &'static str = "sl4f_temperature";
#[derive(Debug)]
pub struct TemperatureFacade {
device_proxy: Option<DeviceProxy>,
logger_proxy: Option<RecorderProxy>,
}
impl TemperatureFacade {
pub fn new() -> TemperatureFacade {
TemperatureFacade { device_proxy: None, logger_proxy: None }
}
fn get_device_proxy(&self, device_path: String) -> Result<DeviceProxy, Error> {
let tag = "TemperatureFacade::get_device_proxy";
if let Some(proxy) = &self.device_proxy {
Ok(proxy.clone())
} else {
let (proxy, server) = fidl::endpoints::create_proxy::<DeviceMarker>();
if Path::new(&device_path).exists() {
fdio::service_connect(device_path.as_ref(), server.into_channel())?;
Ok(proxy)
} else {
fx_err_and_bail!(
&with_line!(tag),
format_err!("Failed to find device: {}", device_path)
);
}
}
}
fn get_logger_proxy(&self) -> Result<RecorderProxy, Error> {
let tag = "TemperatureFacade::get_logger_proxy";
if let Some(proxy) = &self.logger_proxy {
Ok(proxy.clone())
} else {
match connect_to_protocol::<RecorderMarker>() {
Ok(proxy) => Ok(proxy),
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("Failed to create proxy: {:?}", e)
),
}
}
}
pub async fn get_temperature_celsius(&self, args: Value) -> Result<f32, Error> {
let req: types::TemperatureRequest = serde_json::from_value(args)?;
let (status, temperature) =
self.get_device_proxy(req.device_path)?.get_temperature_celsius().await?;
zx::Status::ok(status)?;
Ok(temperature)
}
pub async fn start_logging(
&self,
args: Value,
) -> Result<types::TemperatureLoggerResult, Error> {
let req: types::StartLoggingRequest = serde_json::from_value(args)?;
let proxy = self.get_logger_proxy()?;
proxy
.start_logging(
CLIENT_ID,
&[Metric::Temperature(Temperature {
sampling_interval_ms: req.interval_ms,
statistics_args: None,
})],
req.duration_ms,
false,
false,
)
.await?
.map_err(|e| format_err!("Received TemperatureLoggerError: {:?}", e))?;
Ok(types::TemperatureLoggerResult::Success)
}
pub async fn start_logging_forever(
&self,
args: Value,
) -> Result<types::TemperatureLoggerResult, Error> {
let req: types::StartLoggingForeverRequest = serde_json::from_value(args)?;
let proxy = self.get_logger_proxy()?;
proxy
.start_logging_forever(
CLIENT_ID,
&[Metric::Temperature(Temperature {
sampling_interval_ms: req.interval_ms,
statistics_args: None,
})],
false,
false,
)
.await?
.map_err(|e| format_err!("Received TemperatureLoggerError: {:?}", e))?;
Ok(types::TemperatureLoggerResult::Success)
}
pub async fn stop_logging(&self) -> Result<types::TemperatureLoggerResult, Error> {
self.get_logger_proxy()?.stop_logging(CLIENT_ID).await?;
Ok(types::TemperatureLoggerResult::Success)
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use fidl::endpoints::create_proxy_and_stream;
use fidl_fuchsia_hardware_temperature::DeviceRequest;
use fidl_fuchsia_power_metrics::RecorderRequest;
use fuchsia_async as fasync;
use futures::prelude::*;
use serde_json::json;
#[fasync::run_singlethreaded(test)]
async fn test_get_temperature_celsius() {
let (proxy, mut stream) = create_proxy_and_stream::<DeviceMarker>();
let facade = TemperatureFacade { device_proxy: Some(proxy), logger_proxy: None };
let facade_fut = async move {
assert_eq!(
facade
.get_temperature_celsius(json!({
"device_path": "/dev/class/temperature/000"
}))
.await
.unwrap(),
12.34
);
};
let stream_fut = async move {
match stream.try_next().await {
Ok(Some(DeviceRequest::GetTemperatureCelsius { responder })) => {
responder.send(0, 12.34).unwrap();
}
err => panic!("Err in request handler: {:?}", err),
}
};
future::join(facade_fut, stream_fut).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_start_logging() {
let query_interval_ms = 500;
let query_duration_ms = 10_000;
let (proxy, mut stream) = create_proxy_and_stream::<RecorderMarker>();
let _stream_task = fasync::Task::local(async move {
match stream.try_next().await {
Ok(Some(RecorderRequest::StartLogging {
client_id,
metrics,
duration_ms,
output_samples_to_syslog,
output_stats_to_syslog,
responder,
})) => {
assert_eq!(String::from("sl4f_temperature"), client_id);
assert_eq!(metrics.len(), 1);
assert_eq!(
metrics[0],
Metric::Temperature(Temperature {
sampling_interval_ms: query_interval_ms,
statistics_args: None,
}),
);
assert_eq!(output_samples_to_syslog, false);
assert_eq!(output_stats_to_syslog, false);
assert_eq!(duration_ms, query_duration_ms);
responder.send(Ok(())).unwrap();
}
other => panic!("Unexpected stream item: {:?}", other),
}
});
let facade = TemperatureFacade { device_proxy: None, logger_proxy: Some(proxy) };
assert_matches!(
facade
.start_logging(json!({
"interval_ms": query_interval_ms,
"duration_ms": query_duration_ms
}))
.await,
Ok(types::TemperatureLoggerResult::Success)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_start_logging_forever() {
let query_interval_ms = 500;
let (proxy, mut stream) = create_proxy_and_stream::<RecorderMarker>();
let _stream_task = fasync::Task::local(async move {
match stream.try_next().await {
Ok(Some(RecorderRequest::StartLoggingForever {
client_id,
metrics,
output_samples_to_syslog,
output_stats_to_syslog,
responder,
})) => {
assert_eq!(String::from("sl4f_temperature"), client_id);
assert_eq!(metrics.len(), 1);
assert_eq!(
metrics[0],
Metric::Temperature(Temperature {
sampling_interval_ms: query_interval_ms,
statistics_args: None,
}),
);
assert_eq!(output_samples_to_syslog, false);
assert_eq!(output_stats_to_syslog, false);
responder.send(Ok(())).unwrap();
}
other => panic!("Unexpected stream item: {:?}", other),
}
});
let facade = TemperatureFacade { device_proxy: None, logger_proxy: Some(proxy) };
assert_matches!(
facade.start_logging_forever(json!({ "interval_ms": query_interval_ms })).await,
Ok(types::TemperatureLoggerResult::Success)
);
}
#[fasync::run_singlethreaded(test)]
async fn test_stop_logging() {
let (proxy, mut stream) = create_proxy_and_stream::<RecorderMarker>();
let _stream_task = fasync::Task::local(async move {
match stream.try_next().await {
Ok(Some(RecorderRequest::StopLogging { client_id, responder })) => {
assert_eq!(String::from("sl4f_temperature"), client_id);
responder.send(true).unwrap()
}
other => panic!("Unexpected stream item: {:?}", other),
}
});
let facade = TemperatureFacade { device_proxy: None, logger_proxy: Some(proxy) };
assert_matches!(facade.stop_logging().await, Ok(types::TemperatureLoggerResult::Success));
}
}