extern crate alloc;
use alloc::vec::Vec;
use pros_core::{bail_errno, bail_on, error::PortError, map_errno};
use pros_sys::{PROS_ERR, VISION_OBJECT_ERR_SIG};
use snafu::Snafu;
use super::{SmartDevice, SmartDeviceType, SmartPort};
use crate::color::Rgb;
#[derive(Debug, Eq, PartialEq)]
pub struct VisionSensor {
port: SmartPort,
}
impl VisionSensor {
pub fn new(port: SmartPort, zero: VisionZeroPoint) -> Result<Self, VisionError> {
unsafe {
bail_on!(
PROS_ERR,
pros_sys::vision_set_zero_point(port.index(), zero as _)
);
}
Ok(Self { port })
}
pub fn nth_largest_object(&self, n: u32) -> Result<VisionObject, VisionError> {
unsafe { pros_sys::vision_get_by_size(self.port.index(), n).try_into() }
}
pub fn objects(&self) -> Result<Vec<VisionObject>, VisionError> {
let obj_count = self.num_objects()?;
let mut objects_buf = Vec::with_capacity(obj_count);
unsafe {
pros_sys::vision_read_by_size(
self.port.index(),
0,
obj_count as _,
objects_buf.as_mut_ptr(),
);
}
bail_errno!();
Ok(objects_buf
.into_iter()
.filter_map(|object| object.try_into().ok())
.collect())
}
pub fn num_objects(&self) -> Result<usize, PortError> {
unsafe {
Ok(bail_on!(
PROS_ERR,
pros_sys::vision_get_object_count(self.port.index())
)
.try_into()
.unwrap())
}
}
pub fn exposure(&self) -> f32 {
unsafe { (pros_sys::vision_get_exposure(self.port.index()) as f32) * 1.5 / 150.0 }
}
pub fn current_white_balance(&self) -> Rgb {
unsafe { (pros_sys::vision_get_white_balance(self.port.index()) as u32).into() }
}
pub fn set_exposure(&mut self, exposure: f32) {
unsafe {
pros_sys::vision_set_exposure(self.port.index(), (exposure * 150.0 / 1.5) as u8);
}
}
pub fn set_white_balance(&mut self, white_balance: WhiteBalance) {
unsafe {
match white_balance {
WhiteBalance::Auto => pros_sys::vision_set_auto_white_balance(self.port.index(), 1),
WhiteBalance::Rgb(rgb) => {
pros_sys::vision_set_auto_white_balance(self.port.index(), 0);
pros_sys::vision_set_white_balance(
self.port.index(),
<Rgb as Into<u32>>::into(rgb) as i32,
)
}
};
}
}
pub fn set_zero_point(&mut self, zero: VisionZeroPoint) {
unsafe {
pros_sys::vision_set_zero_point(self.port.index(), zero as _);
}
}
pub fn set_led(&mut self, mode: LedMode) {
unsafe {
match mode {
LedMode::Off => pros_sys::vision_clear_led(self.port.index()),
LedMode::On(rgb) => pros_sys::vision_set_led(
self.port.index(),
<Rgb as Into<u32>>::into(rgb) as i32,
),
};
}
}
}
impl SmartDevice for VisionSensor {
fn port_index(&self) -> u8 {
self.port.index()
}
fn device_type(&self) -> SmartDeviceType {
SmartDeviceType::Vision
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub struct VisionObject {
pub top: i16,
pub left: i16,
pub middle_x: i16,
pub middle_y: i16,
pub width: i16,
pub height: i16,
}
impl TryFrom<pros_sys::vision_object_s_t> for VisionObject {
type Error = VisionError;
fn try_from(value: pros_sys::vision_object_s_t) -> Result<VisionObject, VisionError> {
if value.signature == VISION_OBJECT_ERR_SIG {
bail_errno!();
unreachable!("Errno should be non-zero")
}
Ok(Self {
top: value.top_coord,
left: value.left_coord,
middle_x: value.x_middle_coord,
middle_y: value.y_middle_coord,
width: value.width,
height: value.height,
})
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VisionZeroPoint {
TopLeft,
Center,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WhiteBalance {
Rgb(Rgb),
Auto,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LedMode {
On(Rgb),
Off,
}
#[derive(Debug, Snafu)]
pub enum VisionError {
ReadingFailed,
IndexTooHigh,
PortTaken,
#[snafu(display("{source}"), context(false))]
Port {
source: PortError,
},
}
map_errno! {
VisionError {
EHOSTDOWN => Self::ReadingFailed,
EDOM => Self::IndexTooHigh,
EACCES => Self::PortTaken,
}
inherit PortError;
}