1use crate::common_utils::common::macros::{fx_err_and_bail, with_line};
6use crate::temperature::types;
7use anyhow::Error;
8use fidl_fuchsia_hardware_temperature::{DeviceMarker, DeviceProxy};
9use fidl_fuchsia_power_metrics::{Metric, RecorderMarker, RecorderProxy, Temperature};
10use fuchsia_component::client::connect_to_protocol;
11
12use serde_json::Value;
13use std::path::Path;
14
15const CLIENT_ID: &'static str = "sl4f_temperature";
16
17#[derive(Debug)]
22pub struct TemperatureFacade {
23 device_proxy: Option<DeviceProxy>,
26
27 logger_proxy: Option<RecorderProxy>,
29}
30
31impl TemperatureFacade {
32 pub fn new() -> TemperatureFacade {
33 TemperatureFacade { device_proxy: None, logger_proxy: None }
34 }
35
36 fn get_device_proxy(&self, device_path: String) -> Result<DeviceProxy, Error> {
42 let tag = "TemperatureFacade::get_device_proxy";
43
44 if let Some(proxy) = &self.device_proxy {
45 Ok(proxy.clone())
46 } else {
47 let (proxy, server) = fidl::endpoints::create_proxy::<DeviceMarker>();
48
49 if Path::new(&device_path).exists() {
50 fdio::service_connect(device_path.as_ref(), server.into_channel())?;
51 Ok(proxy)
52 } else {
53 fx_err_and_bail!(
54 &with_line!(tag),
55 format_err!("Failed to find device: {}", device_path)
56 );
57 }
58 }
59 }
60
61 fn get_logger_proxy(&self) -> Result<RecorderProxy, Error> {
63 let tag = "TemperatureFacade::get_logger_proxy";
64
65 if let Some(proxy) = &self.logger_proxy {
66 Ok(proxy.clone())
67 } else {
68 match connect_to_protocol::<RecorderMarker>() {
69 Ok(proxy) => Ok(proxy),
70 Err(e) => fx_err_and_bail!(
71 &with_line!(tag),
72 format_err!("Failed to create proxy: {:?}", e)
73 ),
74 }
75 }
76 }
77
78 pub async fn get_temperature_celsius(&self, args: Value) -> Result<f32, Error> {
84 let req: types::TemperatureRequest = serde_json::from_value(args)?;
85 let (status, temperature) =
86 self.get_device_proxy(req.device_path)?.get_temperature_celsius().await?;
87 zx::Status::ok(status)?;
88 Ok(temperature)
89 }
90
91 pub async fn start_logging(
97 &self,
98 args: Value,
99 ) -> Result<types::TemperatureLoggerResult, Error> {
100 let req: types::StartLoggingRequest = serde_json::from_value(args)?;
101 let proxy = self.get_logger_proxy()?;
102
103 proxy
104 .start_logging(
105 CLIENT_ID,
106 &[Metric::Temperature(Temperature {
107 sampling_interval_ms: req.interval_ms,
108 statistics_args: None,
109 })],
110 req.duration_ms,
111 false,
112 false,
113 )
114 .await?
115 .map_err(|e| format_err!("Received TemperatureLoggerError: {:?}", e))?;
116 Ok(types::TemperatureLoggerResult::Success)
117 }
118
119 pub async fn start_logging_forever(
125 &self,
126 args: Value,
127 ) -> Result<types::TemperatureLoggerResult, Error> {
128 let req: types::StartLoggingForeverRequest = serde_json::from_value(args)?;
129 let proxy = self.get_logger_proxy()?;
130
131 proxy
132 .start_logging_forever(
133 CLIENT_ID,
134 &[Metric::Temperature(Temperature {
135 sampling_interval_ms: req.interval_ms,
136 statistics_args: None,
137 })],
138 false,
139 false,
140 )
141 .await?
142 .map_err(|e| format_err!("Received TemperatureLoggerError: {:?}", e))?;
143 Ok(types::TemperatureLoggerResult::Success)
144 }
145
146 pub async fn stop_logging(&self) -> Result<types::TemperatureLoggerResult, Error> {
148 self.get_logger_proxy()?.stop_logging(CLIENT_ID).await?;
149 Ok(types::TemperatureLoggerResult::Success)
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156 use assert_matches::assert_matches;
157 use fidl::endpoints::create_proxy_and_stream;
158 use fidl_fuchsia_hardware_temperature::DeviceRequest;
159 use fidl_fuchsia_power_metrics::RecorderRequest;
160 use fuchsia_async as fasync;
161 use futures::prelude::*;
162 use serde_json::json;
163
164 #[fasync::run_singlethreaded(test)]
167 async fn test_get_temperature_celsius() {
168 let (proxy, mut stream) = create_proxy_and_stream::<DeviceMarker>();
169 let facade = TemperatureFacade { device_proxy: Some(proxy), logger_proxy: None };
170 let facade_fut = async move {
171 assert_eq!(
172 facade
173 .get_temperature_celsius(json!({
174 "device_path": "/dev/class/temperature/000"
175 }))
176 .await
177 .unwrap(),
178 12.34
179 );
180 };
181 let stream_fut = async move {
182 match stream.try_next().await {
183 Ok(Some(DeviceRequest::GetTemperatureCelsius { responder })) => {
184 responder.send(0, 12.34).unwrap();
185 }
186 err => panic!("Err in request handler: {:?}", err),
187 }
188 };
189 future::join(facade_fut, stream_fut).await;
190 }
191
192 #[fasync::run_singlethreaded(test)]
194 async fn test_start_logging() {
195 let query_interval_ms = 500;
196 let query_duration_ms = 10_000;
197
198 let (proxy, mut stream) = create_proxy_and_stream::<RecorderMarker>();
199
200 let _stream_task = fasync::Task::local(async move {
201 match stream.try_next().await {
202 Ok(Some(RecorderRequest::StartLogging {
203 client_id,
204 metrics,
205 duration_ms,
206 output_samples_to_syslog,
207 output_stats_to_syslog,
208 responder,
209 })) => {
210 assert_eq!(String::from("sl4f_temperature"), client_id);
211 assert_eq!(metrics.len(), 1);
212 assert_eq!(
213 metrics[0],
214 Metric::Temperature(Temperature {
215 sampling_interval_ms: query_interval_ms,
216 statistics_args: None,
217 }),
218 );
219 assert_eq!(output_samples_to_syslog, false);
220 assert_eq!(output_stats_to_syslog, false);
221 assert_eq!(duration_ms, query_duration_ms);
222
223 responder.send(Ok(())).unwrap();
224 }
225 other => panic!("Unexpected stream item: {:?}", other),
226 }
227 });
228
229 let facade = TemperatureFacade { device_proxy: None, logger_proxy: Some(proxy) };
230
231 assert_matches!(
232 facade
233 .start_logging(json!({
234 "interval_ms": query_interval_ms,
235 "duration_ms": query_duration_ms
236 }))
237 .await,
238 Ok(types::TemperatureLoggerResult::Success)
239 );
240 }
241
242 #[fasync::run_singlethreaded(test)]
244 async fn test_start_logging_forever() {
245 let query_interval_ms = 500;
246
247 let (proxy, mut stream) = create_proxy_and_stream::<RecorderMarker>();
248
249 let _stream_task = fasync::Task::local(async move {
250 match stream.try_next().await {
251 Ok(Some(RecorderRequest::StartLoggingForever {
252 client_id,
253 metrics,
254 output_samples_to_syslog,
255 output_stats_to_syslog,
256 responder,
257 })) => {
258 assert_eq!(String::from("sl4f_temperature"), client_id);
259 assert_eq!(metrics.len(), 1);
260 assert_eq!(
261 metrics[0],
262 Metric::Temperature(Temperature {
263 sampling_interval_ms: query_interval_ms,
264 statistics_args: None,
265 }),
266 );
267 assert_eq!(output_samples_to_syslog, false);
268 assert_eq!(output_stats_to_syslog, false);
269
270 responder.send(Ok(())).unwrap();
271 }
272 other => panic!("Unexpected stream item: {:?}", other),
273 }
274 });
275
276 let facade = TemperatureFacade { device_proxy: None, logger_proxy: Some(proxy) };
277
278 assert_matches!(
279 facade.start_logging_forever(json!({ "interval_ms": query_interval_ms })).await,
280 Ok(types::TemperatureLoggerResult::Success)
281 );
282 }
283
284 #[fasync::run_singlethreaded(test)]
286 async fn test_stop_logging() {
287 let (proxy, mut stream) = create_proxy_and_stream::<RecorderMarker>();
288
289 let _stream_task = fasync::Task::local(async move {
290 match stream.try_next().await {
291 Ok(Some(RecorderRequest::StopLogging { client_id, responder })) => {
292 assert_eq!(String::from("sl4f_temperature"), client_id);
293 responder.send(true).unwrap()
294 }
295 other => panic!("Unexpected stream item: {:?}", other),
296 }
297 });
298
299 let facade = TemperatureFacade { device_proxy: None, logger_proxy: Some(proxy) };
300
301 assert_matches!(facade.stop_logging().await, Ok(types::TemperatureLoggerResult::Success));
302 }
303}