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