| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| 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_thermal_test::{TemperatureLoggerMarker, TemperatureLoggerProxy}; |
| use fuchsia_component::client::connect_to_service; |
| use fuchsia_syslog::macros::fx_log_err; |
| use fuchsia_zircon as zx; |
| use serde_json::Value; |
| use std::path::Path; |
| |
| /// Perform Temperature operations. |
| /// |
| /// Note this object is shared among all threads created by server. |
| /// |
| #[derive(Debug)] |
| pub struct TemperatureFacade { |
| /// Temperature device proxy that may be optionally provided for testing. The proxy is not |
| /// cached during normal operation. |
| device_proxy: Option<DeviceProxy>, |
| |
| /// Optional logger proxy for testing, similar to `device_proxy`. |
| logger_proxy: Option<TemperatureLoggerProxy>, |
| } |
| |
| impl TemperatureFacade { |
| pub fn new() -> TemperatureFacade { |
| TemperatureFacade { device_proxy: None, logger_proxy: None } |
| } |
| |
| /// Connect to the temperature device specified by `device_path`. |
| /// |
| /// # Arguments |
| /// * `device_path` - String representing the temperature device path (e.g., |
| /// /dev/class/temperature/000) |
| 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) = match fidl::endpoints::create_proxy::<DeviceMarker>() { |
| Ok(r) => r, |
| Err(e) => fx_err_and_bail!( |
| &with_line!(tag), |
| format_err!("Failed to create proxy {:?}", e) |
| ), |
| }; |
| |
| 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) |
| ); |
| } |
| } |
| } |
| |
| /// Connect to the discoverable TemperatureLogger service and return the proxy. |
| fn get_logger_proxy(&self) -> Result<TemperatureLoggerProxy, Error> { |
| let tag = "TemperatureFacade::get_logger_proxy"; |
| |
| if let Some(proxy) = &self.logger_proxy { |
| Ok(proxy.clone()) |
| } else { |
| match connect_to_service::<TemperatureLoggerMarker>() { |
| Ok(proxy) => Ok(proxy), |
| Err(e) => fx_err_and_bail!( |
| &with_line!(tag), |
| format_err!("Failed to create proxy: {:?}", e) |
| ), |
| } |
| } |
| } |
| |
| /// Reads the temperature from a specified temperature device. |
| /// |
| /// # Arguments |
| /// * `args`: JSON value containing the TemperatureRequest information, where TemperatureRequest |
| /// contains the device path to read from. |
| 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) |
| } |
| |
| /// Initiates fixed-duration logging with the TemperatureLogger service. |
| /// |
| /// # Arguments |
| /// * `args`: JSON value containing the StartLoggingRequest information. Key "interval_ms" |
| /// specifies the logging interval, and "duration_ms" specifies the duration of logging. |
| 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(req.interval_ms, req.duration_ms) |
| .await? |
| .map_err(|e| format_err!("Received TemperatureLoggerError: {:?}", e))?; |
| Ok(types::TemperatureLoggerResult::Success) |
| } |
| |
| /// Initiates durationless logging with the TemperatureLogger service. |
| /// |
| /// # Arguments |
| /// * `args`: JSON value containing the StartLoggingRequest information. Key "interval_ms" |
| /// specifies the logging interval. |
| 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(req.interval_ms) |
| .await? |
| .map_err(|e| format_err!("Received TemperatureLoggerError: {:?}", e))?; |
| Ok(types::TemperatureLoggerResult::Success) |
| } |
| |
| /// Terminates logging by the TemperatureLogger service. |
| pub fn stop_logging(&self) -> Result<types::TemperatureLoggerResult, Error> { |
| self.get_logger_proxy()?.stop_logging()?; |
| Ok(types::TemperatureLoggerResult::Success) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use fidl::endpoints::create_proxy_and_stream; |
| use fidl_fuchsia_hardware_temperature::DeviceRequest; |
| use fidl_fuchsia_thermal_test::TemperatureLoggerRequest; |
| use fuchsia_async as fasync; |
| use futures::prelude::*; |
| use matches::assert_matches; |
| use serde_json::json; |
| |
| /// Tests that the `get_temperature_celsius` method correctly queries the temperature device and |
| /// returns the expected temperature value. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_temperature_celsius() { |
| let (proxy, mut stream) = create_proxy_and_stream::<DeviceMarker>().unwrap(); |
| 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; |
| } |
| |
| /// Tests that the `start_logging` method correctly queries the logger. |
| #[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::<TemperatureLoggerMarker>().unwrap(); |
| |
| let _stream_task = fasync::Task::local(async move { |
| match stream.try_next().await { |
| Ok(Some(TemperatureLoggerRequest::StartLogging { |
| interval_ms, |
| duration_ms, |
| responder, |
| })) => { |
| assert_eq!(query_interval_ms, interval_ms); |
| assert_eq!(query_duration_ms, duration_ms); |
| responder.send(&mut 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) |
| ); |
| } |
| |
| /// Tests that the `start_logging_forever` method correctly queries the logger. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_start_logging_forever() { |
| let query_interval_ms = 500; |
| |
| let (proxy, mut stream) = create_proxy_and_stream::<TemperatureLoggerMarker>().unwrap(); |
| |
| let _stream_task = fasync::Task::local(async move { |
| match stream.try_next().await { |
| Ok(Some(TemperatureLoggerRequest::StartLoggingForever { |
| interval_ms, |
| responder, |
| })) => { |
| assert_eq!(query_interval_ms, interval_ms); |
| responder.send(&mut 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) |
| ); |
| } |
| |
| /// Tests that the `stop_logging` method correctly queries the logger. |
| #[fasync::run_singlethreaded(test)] |
| async fn test_stop_logging() { |
| let (proxy, mut stream) = create_proxy_and_stream::<TemperatureLoggerMarker>().unwrap(); |
| |
| let _stream_task = fasync::Task::local(async move { |
| match stream.try_next().await { |
| Ok(Some(TemperatureLoggerRequest::StopLogging { .. })) => {} |
| other => panic!("Unexpected stream item: {:?}", other), |
| } |
| }); |
| |
| let facade = TemperatureFacade { device_proxy: None, logger_proxy: Some(proxy) }; |
| |
| assert_matches!(facade.stop_logging(), Ok(types::TemperatureLoggerResult::Success)); |
| } |
| } |