use super::{Sensor, SensorPort};
use crate::{sensor_mode, Attribute, Device, Driver, Ev3Error, Ev3Result};
use std::cell::RefCell;
use std::collections::HashSet;
use std::fmt;
use std::rc::Rc;
#[derive(Debug, Clone, Device, Sensor)]
pub struct InfraredSensor {
driver: Driver,
}
impl InfraredSensor {
fn new(driver: Driver) -> Self {
Self { driver }
}
findable!(
"lego-sensor",
["lego-ev3-ir"],
SensorPort,
"InfraredSensor",
"in"
);
sensor_mode!(
"IR-PROX",
MODE_IR_PROX,
"Proximity",
set_mode_ir_prox,
is_mode_ir_prox
);
sensor_mode!(
"IR-SEEK",
MODE_IR_SEEK,
"IR Seeker",
set_mode_ir_seek,
is_mode_ir_seek
);
sensor_mode!(
"IR-REMOTE",
MODE_IR_REMOTE,
"IR Remote Control",
set_mode_ir_remote,
is_mode_ir_remote
);
sensor_mode!(
"IR-REM-A",
MODE_IR_REM_A,
"IR Remote Control",
set_mode_ir_rem_a,
is_mode_ir_rem_a
);
sensor_mode!(
"IR-S-ALT",
MODE_IR_S_ALT,
"Alternate IR Seeker ???",
set_mode_ir_s_alt,
is_mode_ir_s_alt
);
sensor_mode!(
"IR-CAL",
MODE_IR_CAL,
"Calibration ???",
set_mode_ir_cal,
is_mode_ir_cal
);
pub fn get_distance(&self) -> Ev3Result<i32> {
self.get_value0()
}
}
struct RemoteControlHelper {
last_buttons: i32,
pressed_buttons: HashSet<String>,
}
impl RemoteControlHelper {
fn new() -> RemoteControlHelper {
RemoteControlHelper {
last_buttons: 0,
pressed_buttons: HashSet::new(),
}
}
fn contains(&self, button: &str) -> bool {
self.pressed_buttons.contains(button)
}
}
#[derive(Clone)]
pub struct RemoteControl {
sensor: InfraredSensor,
channel: u8,
helper: Rc<RefCell<RemoteControlHelper>>,
}
impl fmt::Debug for RemoteControl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RemoteControl")
.field("sensor", &self.sensor)
.field("channel", &self.channel)
.finish()
}
}
impl RemoteControl {
pub fn new(sensor: InfraredSensor, channel: u8) -> Ev3Result<RemoteControl> {
sensor.set_mode_ir_remote()?;
Ok(RemoteControl {
sensor,
channel: u8::max(1, u8::min(4, channel)) - 1,
helper: Rc::new(RefCell::new(RemoteControlHelper::new())),
})
}
pub fn is_red_up(&self) -> bool {
self.helper.borrow().contains("red_up")
}
pub fn is_red_down(&self) -> bool {
self.helper.borrow().contains("red_down")
}
pub fn is_blue_up(&self) -> bool {
self.helper.borrow().contains("blue_up")
}
pub fn is_blue_down(&self) -> bool {
self.helper.borrow().contains("blue_down")
}
pub fn is_beacon(&self) -> bool {
self.helper.borrow().contains("beacon")
}
pub fn process(&self) -> Ev3Result<()> {
let buttons = self.sensor.get_value(self.channel)?;
let mut helper = self.helper.borrow_mut();
if helper.last_buttons != buttons {
helper.last_buttons = buttons;
helper.pressed_buttons.clear();
match buttons {
1 => {
helper.pressed_buttons.insert("red_up".to_owned());
}
2 => {
helper.pressed_buttons.insert("red_down".to_owned());
}
3 => {
helper.pressed_buttons.insert("blue_up".to_owned());
}
4 => {
helper.pressed_buttons.insert("blue_down".to_owned());
}
5 => {
helper.pressed_buttons.insert("red_up".to_owned());
helper.pressed_buttons.insert("blue_up".to_owned());
}
6 => {
helper.pressed_buttons.insert("red_up".to_owned());
helper.pressed_buttons.insert("blue_down".to_owned());
}
7 => {
helper.pressed_buttons.insert("red_down".to_owned());
helper.pressed_buttons.insert("blue_up".to_owned());
}
8 => {
helper.pressed_buttons.insert("red_down".to_owned());
helper.pressed_buttons.insert("blue_down".to_owned());
}
9 => {
helper.pressed_buttons.insert("beacon".to_owned());
}
10 => {
helper.pressed_buttons.insert("red_up".to_owned());
helper.pressed_buttons.insert("red_down".to_owned());
}
11 => {
helper.pressed_buttons.insert("blue_up".to_owned());
helper.pressed_buttons.insert("blue_down".to_owned());
}
_ => {}
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct BeaconSeeker {
sensor: InfraredSensor,
channel: u8,
}
impl BeaconSeeker {
pub fn new(sensor: InfraredSensor, channel: u8) -> Ev3Result<BeaconSeeker> {
sensor.set_mode_ir_seek()?;
Ok(BeaconSeeker {
sensor,
channel: u8::max(1, u8::min(4, channel)) - 1,
})
}
pub fn get_heading(&self) -> Ev3Result<i32> {
self.sensor.get_value(self.channel * 2)
}
pub fn get_distance(&self) -> Ev3Result<i32> {
self.sensor.get_value(self.channel * 2 + 1)
}
pub fn get_heading_and_distance(&self) -> Ev3Result<(i32, i32)> {
Ok((
self.sensor.get_value(self.channel * 2)?,
self.sensor.get_value(self.channel * 2 + 1)?,
))
}
}