use anyhow::{bail, Context};
use ev3dev_lang_rust::motors::TachoMotor;
use ev3dev_lang_rust::sensors::SensorPort;
use crate::error::Result;
use crate::input::Input;
use crate::movement::spec::RobotSpec;
use crate::types::{Distance, Heading, Percent, Speed};
use std::cell::RefMut;
use std::time::Duration;
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum TurnType {
Right,
Left,
Center,
}
impl TurnType {
pub fn wheels(&self) -> (bool, bool) {
match self {
TurnType::Right => (false, true),
TurnType::Left => (true, false),
TurnType::Center => (true, true),
}
}
}
#[derive(Eq, PartialEq, Copy, Clone, Hash, Debug)]
pub enum MotorId {
DriveRight,
DriveLeft,
Attachment1,
Attachment2,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Command {
On(Speed),
Stop(StopAction),
Distance(Distance, Speed),
To(Distance, Speed),
Time(Duration, Speed),
Queue(Box<Command>),
Execute,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum StopAction {
Coast,
Break,
Hold,
}
impl StopAction {
pub fn to_str(&self) -> &'static str {
match self {
StopAction::Coast => TachoMotor::STOP_ACTION_COAST,
StopAction::Break => TachoMotor::STOP_ACTION_BRAKE,
StopAction::Hold => TachoMotor::STOP_ACTION_HOLD,
}
}
}
#[derive(Eq, PartialEq, Copy, Clone, Hash, Debug)]
pub enum SensorId {
DriveLeft,
AttachmentRight,
AttachmentLeft,
}
pub trait Robot: AngleProvider {
fn drive(&self, distance: impl Into<Distance>, speed: impl Into<Speed>) -> Result<()>;
fn turn(&self, angle: Heading, speed: impl Into<Speed>) -> Result<()>;
fn turn_named(&self, angle: Heading, speed: impl Into<Speed>, turn: TurnType) -> Result<()>;
fn set_heading(&self, angle: Heading) -> Result<()>;
fn motor(&self, motor: MotorId) -> Option<RefMut<dyn Motor>>;
fn color_sensor(&self, port: SensorPort) -> Option<RefMut<dyn ColorSensor>>;
fn process_buttons(&self) -> Result<Input>;
fn handle_interrupt(&self) -> Result<()> {
let input = self.process_buttons().context("Process buttons")?;
if input.is_left() {
bail!("Interupt requested")
}
Ok(())
}
fn battery(&self) -> Result<Percent>;
fn await_input(&self, timeout: Option<Duration>) -> Result<Input>;
fn stop(&self) -> Result<()>;
fn reset(&self) -> Result<()>;
fn spec(&self) -> &RobotSpec;
}
pub trait ColorSensor {
fn reflected_light(&self) -> Result<Percent>;
fn cal_white(&mut self) -> Result<()>;
fn cal_black(&mut self) -> Result<()>;
fn reset(&mut self) -> Result<()>;
}
pub trait Motor {
fn raw(&mut self, command: Command) -> Result<()>;
fn motor_reset(&mut self, stop_action: Option<StopAction>) -> Result<()>;
fn wait(&self, timeout: Option<Duration>) -> Result<()>;
fn speed(&self) -> Result<Speed>;
fn motor_angle(&self) -> Result<Distance>;
}
pub trait MotorExt {
fn dist(&mut self, dist: impl Into<Distance>, speed: impl Into<Speed>) -> Result<()>;
fn to_pos(&mut self, position: impl Into<Distance>, speed: impl Into<Speed>) -> Result<()>;
fn time(&mut self, duration: Duration, speed: impl Into<Speed>) -> Result<()>;
}
impl MotorExt for dyn Motor + '_ {
fn dist(&mut self, dist: impl Into<Distance>, speed: impl Into<Speed>) -> Result<()> {
self.raw(Command::Distance(dist.into(), speed.into()))?;
self.wait(None)
}
fn to_pos(&mut self, position: impl Into<Distance>, speed: impl Into<Speed>) -> Result<()> {
self.raw(Command::To(position.into(), speed.into()))?;
self.wait(None)
}
fn time(&mut self, duration: Duration, speed: impl Into<Speed>) -> Result<()> {
self.raw(Command::Time(duration, speed.into()))?;
self.wait(None)
}
}
impl<M: Motor> MotorExt for M {
fn dist(&mut self, dist: impl Into<Distance>, speed: impl Into<Speed>) -> Result<()> {
self.raw(Command::Distance(dist.into(), speed.into()))?;
self.wait(None)
}
fn to_pos(&mut self, position: impl Into<Distance>, speed: impl Into<Speed>) -> Result<()> {
self.raw(Command::To(position.into(), speed.into()))?;
self.wait(None)
}
fn time(&mut self, duration: Duration, speed: impl Into<Speed>) -> Result<()> {
self.raw(Command::Time(duration, speed.into()))?;
self.wait(None)
}
}
pub trait AngleProvider {
fn angle(&self) -> Result<Heading>;
}