1use core::time::Duration;
4
5use pros_core::{bail_on, error::PortError, map_errno};
6use pros_sys::{OPT_GESTURE_ERR, PROS_ERR, PROS_ERR_F};
7use snafu::Snafu;
8
9use super::{SmartDevice, SmartDeviceType, SmartPort};
10
11#[derive(Debug, Eq, PartialEq)]
13pub struct OpticalSensor {
14 port: SmartPort,
15 gesture_detection_enabled: bool,
16}
17
18impl OpticalSensor {
19 pub const MIN_INTEGRATION_TIME: Duration = Duration::from_millis(3);
21
22 pub const MAX_INTEGRATION_TIME: Duration = Duration::from_millis(712);
24
25 pub const MAX_LED_PWM: u8 = 100;
27
28 pub fn new(port: SmartPort, gesture_detection_enabled: bool) -> Result<Self, OpticalError> {
32 let mut sensor = Self {
33 port,
34 gesture_detection_enabled,
35 };
36
37 if gesture_detection_enabled {
38 sensor.enable_gesture_detection()?;
39 } else {
40 sensor.disable_gesture_detection()?;
41 }
42
43 Ok(sensor)
44 }
45
46 pub fn led_pwm(&self) -> Result<i32, OpticalError> {
48 unsafe {
49 Ok(bail_on!(
50 PROS_ERR,
51 pros_sys::optical_get_led_pwm(self.port.index())
52 ))
53 }
54 }
55
56 pub fn set_led_pwm(&mut self, value: u8) -> Result<(), OpticalError> {
58 if value > Self::MAX_LED_PWM {
59 return Err(OpticalError::InvalidLedPwm);
60 }
61 unsafe {
62 bail_on!(
63 PROS_ERR,
64 pros_sys::optical_set_led_pwm(self.port.index(), value)
65 );
66 }
67 Ok(())
68 }
69
70 pub fn integration_time(&self) -> Result<Duration, OpticalError> {
73 unsafe {
74 Ok(Duration::from_millis(bail_on!(
75 PROS_ERR_F,
76 pros_sys::optical_get_integration_time(self.port.index())
77 ) as u64))
78 }
79 }
80
81 pub fn set_integration_time(&mut self, time: Duration) -> Result<(), OpticalError> {
90 if time < Self::MIN_INTEGRATION_TIME || time > Self::MAX_INTEGRATION_TIME {
91 return Err(OpticalError::InvalidIntegrationTime);
92 }
93
94 unsafe {
95 bail_on!(
96 PROS_ERR,
97 pros_sys::optical_set_integration_time(self.port.index(), time.as_millis() as f64)
98 );
99 }
100
101 Ok(())
102 }
103
104 pub fn hue(&self) -> Result<f64, OpticalError> {
108 unsafe {
109 Ok(bail_on!(
110 PROS_ERR_F,
111 pros_sys::optical_get_hue(self.port.index())
112 ))
113 }
114 }
115
116 pub fn saturation(&self) -> Result<f64, OpticalError> {
120 unsafe {
121 Ok(bail_on!(
122 PROS_ERR_F,
123 pros_sys::optical_get_saturation(self.port.index())
124 ))
125 }
126 }
127
128 pub fn brightness(&self) -> Result<f64, OpticalError> {
132 unsafe {
133 Ok(bail_on!(
134 PROS_ERR_F,
135 pros_sys::optical_get_brightness(self.port.index())
136 ))
137 }
138 }
139
140 pub fn proximity(&self) -> Result<i32, OpticalError> {
144 unsafe {
145 Ok(bail_on!(
146 PROS_ERR,
147 pros_sys::optical_get_proximity(self.port.index())
148 ))
149 }
150 }
151
152 pub fn rgbc(&self) -> Result<Rgbc, OpticalError> {
154 unsafe { pros_sys::optical_get_rgb(self.port.index()).try_into() }
155 }
156
157 pub fn rgbc_raw(&self) -> Result<RgbcRaw, OpticalError> {
159 unsafe { pros_sys::optical_get_raw(self.port.index()).try_into() }
160 }
161
162 pub fn enable_gesture_detection(&mut self) -> Result<(), OpticalError> {
167 bail_on!(PROS_ERR, unsafe {
168 pros_sys::optical_enable_gesture(self.port.index())
169 });
170
171 self.gesture_detection_enabled = true;
172 Ok(())
173 }
174
175 pub fn disable_gesture_detection(&mut self) -> Result<(), OpticalError> {
177 bail_on!(PROS_ERR, unsafe {
178 pros_sys::optical_disable_gesture(self.port.index())
179 });
180
181 self.gesture_detection_enabled = false;
182 Ok(())
183 }
184
185 pub const fn gesture_detection_enabled(&self) -> bool {
187 self.gesture_detection_enabled
188 }
189
190 pub fn last_gesture_direction(&self) -> Result<GestureDirection, OpticalError> {
195 if !self.gesture_detection_enabled {
196 return Err(OpticalError::GestureDetectionDisabled);
197 }
198
199 unsafe { pros_sys::optical_get_gesture(self.port.index()).try_into() }
200 }
201
202 pub fn last_gesture_raw(&self) -> Result<GestureRaw, OpticalError> {
207 if !self.gesture_detection_enabled {
208 return Err(OpticalError::GestureDetectionDisabled);
209 }
210
211 unsafe { pros_sys::optical_get_gesture_raw(self.port.index()).try_into() }
212 }
213}
214
215impl SmartDevice for OpticalSensor {
216 fn port_index(&self) -> u8 {
217 self.port.index()
218 }
219
220 fn device_type(&self) -> SmartDeviceType {
221 SmartDeviceType::Optical
222 }
223}
224
225#[derive(Default, Debug, Clone, Copy, PartialEq)]
226pub enum GestureDirection {
228 Up,
230 Down,
232 Left,
234 Right,
236 Error,
238 #[default]
239 NoGesture,
241}
242
243impl TryFrom<pros_sys::optical_direction_e_t> for GestureDirection {
244 type Error = OpticalError;
245
246 fn try_from(value: pros_sys::optical_direction_e_t) -> Result<GestureDirection, OpticalError> {
247 bail_on!(pros_sys::E_OPTICAL_DIRECTION_ERROR, value);
248
249 Ok(match value {
250 pros_sys::E_OPTICAL_DIRECTION_UP => Self::Up,
251 pros_sys::E_OPTICAL_DIRECTION_DOWN => Self::Down,
252 pros_sys::E_OPTICAL_DIRECTION_LEFT => Self::Left,
253 pros_sys::E_OPTICAL_DIRECTION_RIGHT => Self::Right,
254 pros_sys::E_OPTICAL_DIRECTION_NO_GESTURE => Self::NoGesture,
255 _ => unreachable!("Encountered unknown gesture direction code."),
256 })
257 }
258}
259
260#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
261pub struct GestureRaw {
263 pub up: u8,
265 pub down: u8,
267 pub left: u8,
269 pub right: u8,
271 pub gesture_type: u8,
273 pub count: u16,
275 pub time: u32,
277}
278
279impl TryFrom<pros_sys::optical_gesture_s_t> for GestureRaw {
280 type Error = OpticalError;
281
282 fn try_from(value: pros_sys::optical_gesture_s_t) -> Result<GestureRaw, OpticalError> {
283 Ok(Self {
284 up: bail_on!(OPT_GESTURE_ERR as u8, value.udata),
285 down: value.ddata,
286 left: value.ldata,
287 right: value.rdata,
288 gesture_type: value.r#type,
289 count: value.count,
290 time: value.time,
291 })
292 }
293}
294
295#[derive(Default, Debug, Clone, Copy, PartialEq)]
296pub struct Rgbc {
298 pub red: f64,
300 pub green: f64,
302 pub blue: f64,
304 pub brightness: f64,
306}
307
308impl TryFrom<pros_sys::optical_rgb_s_t> for Rgbc {
309 type Error = OpticalError;
310
311 fn try_from(value: pros_sys::optical_rgb_s_t) -> Result<Rgbc, OpticalError> {
312 Ok(Self {
313 red: bail_on!(PROS_ERR_F, value.red), green: value.green,
315 blue: value.blue,
316 brightness: value.brightness,
317 })
318 }
319}
320
321#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
322pub struct RgbcRaw {
324 pub red: u32,
326 pub green: u32,
328 pub blue: u32,
330 pub clear: u32,
332}
333
334impl TryFrom<pros_sys::optical_raw_s_t> for RgbcRaw {
335 type Error = OpticalError;
336
337 fn try_from(value: pros_sys::optical_raw_s_t) -> Result<RgbcRaw, OpticalError> {
338 Ok(Self {
339 clear: bail_on!(PROS_ERR_F as u32, value.clear),
340 red: value.red,
341 green: value.green,
342 blue: value.blue,
343 })
344 }
345}
346
347#[derive(Debug, Snafu)]
348pub enum OpticalError {
350 InvalidLedPwm,
352
353 InvalidIntegrationTime,
357
358 GestureDetectionDisabled,
360
361 #[snafu(display("{source}"), context(false))]
362 Port {
364 source: PortError,
366 },
367}
368
369map_errno! {
370 OpticalError {} inherit PortError;
371}