Skip to main content

lib/
sensor.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{Context as _, Error, format_err};
6use async_trait::async_trait;
7use fidl_fuchsia_input_report::{
8    DeviceDescriptor, InputReportsReaderMarker, InputReportsReaderProxy, SensorInputDescriptor,
9    SensorType, ServiceMarker,
10};
11use fuchsia_component::client::Service;
12
13#[derive(Debug)]
14pub struct AmbientLightInputRpt {
15    pub illuminance: f32,
16    pub red: f32,
17    pub green: f32,
18    pub blue: f32,
19}
20
21struct AmbientLightComponent {
22    pub report_index: usize,
23    pub exponent: i32,
24    pub report_id: u8, // report ID associated with descriptor
25}
26
27struct AmbientLightInputReportReaderProxy {
28    pub proxy: InputReportsReaderProxy,
29
30    pub illuminance: Option<AmbientLightComponent>,
31    pub red: Option<AmbientLightComponent>,
32    pub green: Option<AmbientLightComponent>,
33    pub blue: Option<AmbientLightComponent>,
34}
35
36use futures::StreamExt;
37
38async fn open_sensor_input_report_reader() -> Result<AmbientLightInputReportReaderProxy, Error> {
39    let mut watcher = Service::open(ServiceMarker)?
40        .watch()
41        .await
42        .context("Failed to watch for sensor service instances")?;
43
44    while let Some(instance_result) = watcher.next().await {
45        let instance = instance_result?;
46        let device = instance.connect_to_input_device()?;
47
48        fn get_sensor_input(
49            descriptor: &DeviceDescriptor,
50        ) -> Result<&Vec<SensorInputDescriptor>, Error> {
51            let sensor = descriptor.sensor.as_ref().context("device has no sensor")?;
52            let input_desc = sensor.input.as_ref().context("sensor has no input descriptor")?;
53            Ok(input_desc)
54        }
55
56        if let Ok(descriptor) = device.get_descriptor().await {
57            match get_sensor_input(&descriptor) {
58                Ok(input_desc) => {
59                    let mut illuminance = None;
60                    let mut red = None;
61                    let mut green = None;
62                    let mut blue = None;
63
64                    for input in input_desc {
65                        let axes =
66                            input.values.as_ref().context("SensorInputDescriptor has no values")?;
67                        for (i, val) in axes.iter().enumerate() {
68                            let component = AmbientLightComponent {
69                                report_index: i,
70                                exponent: val.axis.unit.exponent,
71                                report_id: input.report_id.unwrap_or(0),
72                            };
73                            match val.type_ {
74                                SensorType::LightIlluminance => illuminance = Some(component),
75                                SensorType::LightRed => red = Some(component),
76                                SensorType::LightGreen => green = Some(component),
77                                SensorType::LightBlue => blue = Some(component),
78                                _ => {}
79                            }
80                        }
81                    }
82
83                    if illuminance.is_some() {
84                        let (proxy, server_end) =
85                            fidl::endpoints::create_proxy::<InputReportsReaderMarker>();
86                        if let Ok(()) = device.get_input_reports_reader(server_end) {
87                            return Ok(AmbientLightInputReportReaderProxy {
88                                proxy: proxy,
89                                illuminance,
90                                red,
91                                blue,
92                                green,
93                            });
94                        }
95                    }
96                }
97                Err(e) => {
98                    log::info!("Skip device {:?}: {}", instance.instance_name(), e);
99                }
100            };
101        }
102    }
103    Err(format_err!("no sensor found"))
104}
105
106/// Reads the sensor's input report and decodes it.
107async fn read_sensor_input_report(
108    device: &AmbientLightInputReportReaderProxy,
109) -> Result<Option<AmbientLightInputRpt>, Error> {
110    let r = device.proxy.read_input_reports().await;
111
112    match r {
113        Ok(Ok(reports)) => {
114            for report in reports {
115                if report.report_id.unwrap_or(0) != device.illuminance.as_ref().unwrap().report_id {
116                    continue;
117                }
118
119                let sensor =
120                    report.sensor.context("sensor required in InputReport for sensor device")?;
121                let values = sensor.values.context("values required in SensorInputReport")?;
122                let f = |component: &Option<AmbientLightComponent>| match component {
123                    Some(val) => match val.exponent {
124                        0 => values[val.report_index] as f32,
125                        _ => values[val.report_index] as f32 * f32::powf(10.0, val.exponent as f32),
126                    },
127                    None => 0.0,
128                };
129
130                let illuminance = f(&device.illuminance);
131                let red = f(&device.red);
132                let green = f(&device.green);
133                let blue = f(&device.blue);
134
135                return Ok(Some(AmbientLightInputRpt { illuminance, red, blue, green }));
136            }
137            Ok(None)
138        }
139        Ok(Err(e)) => Err(format_err!("ReadInputReports error: {}", e)),
140        Err(e) => Err(format_err!("FIDL call failed: {}", e)),
141    }
142}
143
144/// TODO(lingxueluo) Default and temporary report when sensor is not valid(https://fxbug.dev/42119013).
145fn default_report() -> Result<Option<AmbientLightInputRpt>, Error> {
146    Ok(Some(AmbientLightInputRpt { illuminance: 200.0, red: 200.0, green: 200.0, blue: 200.0 }))
147}
148
149pub struct Sensor {
150    proxy: Option<AmbientLightInputReportReaderProxy>,
151}
152
153impl Sensor {
154    pub async fn new() -> Sensor {
155        let proxy = open_sensor_input_report_reader().await;
156        match proxy {
157            Ok(proxy) => return Sensor { proxy: Some(proxy) },
158            Err(_e) => {
159                println!("No valid sensor found.");
160                return Sensor { proxy: None };
161            }
162        }
163    }
164
165    async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error> {
166        if self.proxy.is_none() {
167            default_report()
168        } else {
169            read_sensor_input_report(self.proxy.as_ref().unwrap()).await
170        }
171    }
172}
173
174#[async_trait]
175pub trait SensorControl: Send {
176    async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error>;
177}
178
179#[async_trait]
180impl SensorControl for Sensor {
181    async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error> {
182        self.read().await
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189    use fuchsia_async as fasync;
190
191    #[fasync::run_singlethreaded(test)]
192    async fn test_open_sensor_error() {
193        let sensor = Sensor { proxy: None };
194        if let Some(ambient_light_input_rpt) = sensor.read().await.unwrap() {
195            assert_eq!(ambient_light_input_rpt.illuminance, 200.0);
196            assert_eq!(ambient_light_input_rpt.red, 200.0);
197            assert_eq!(ambient_light_input_rpt.green, 200.0);
198            assert_eq!(ambient_light_input_rpt.blue, 200.0);
199        }
200    }
201}