|
|
|
|
@ -2,9 +2,9 @@
|
|
|
|
|
/// command messages.
|
|
|
|
|
|
|
|
|
|
use log::*; //{trace, debug, info, warn, error}
|
|
|
|
|
use anyhow::Result;
|
|
|
|
|
use anyhow::{anyhow, Result};
|
|
|
|
|
use async_channel::{Receiver, Sender};
|
|
|
|
|
use crate::commands::{Commands, Button};
|
|
|
|
|
use crate::commands::{Commands, Button, Toggle};
|
|
|
|
|
use crate::dispatch::{Dispatch, DispatchRecvQ, DispatchSendQ};
|
|
|
|
|
|
|
|
|
|
// The main internal state of the controller, representing the current control method of the motors.
|
|
|
|
|
@ -18,8 +18,7 @@ enum ControllerStates {
|
|
|
|
|
AutoDown,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
|
|
|
pub enum MotorCommands {
|
|
|
|
|
StartUp,
|
|
|
|
|
StartDown,
|
|
|
|
|
@ -76,7 +75,7 @@ impl Controller {
|
|
|
|
|
Commands::BluetoothStop{data: Button::Released},
|
|
|
|
|
Commands::PicRecvLimitUp{data: Button::Released},
|
|
|
|
|
Commands::PicRecvLimitDown{data: Button::Released},
|
|
|
|
|
Commands::PicRecvAutoMode{data: Button::Released},
|
|
|
|
|
Commands::PicRecvAutoMode{data: Toggle::Inactive},
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
Commands::ButtonTimerExpired,
|
|
|
|
|
];
|
|
|
|
|
@ -155,34 +154,35 @@ impl Controller {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Determines the state the controller should be in based on the command received.
|
|
|
|
|
async fn handle_cmd(&mut self, cmd: &Commands) -> ControllerStates {
|
|
|
|
|
async fn handle_cmd(&mut self, cmd: &Commands) -> Result<ControllerStates> {
|
|
|
|
|
let mut rc = self.state.clone(); // Don't transition by default.
|
|
|
|
|
match cmd {
|
|
|
|
|
Commands::PicRecvUp { data } | Commands::BluetoothUp { data }=> {
|
|
|
|
|
match self.state {
|
|
|
|
|
ControllerStates::Stopped => {return self.change_state_if_pressed(data, self.remote_up_or_auto_up())}
|
|
|
|
|
ControllerStates::Stopped => {rc = self.change_state_if_pressed(data, self.remote_up_or_auto_up())}
|
|
|
|
|
ControllerStates::Stopping => {}
|
|
|
|
|
ControllerStates::GoingUp => {
|
|
|
|
|
self.send.send(Commands::ButtonTimerRestart).await.expect("Failed to necessary timer");
|
|
|
|
|
return self.change_state_if_released(data, ControllerStates::Stopping)
|
|
|
|
|
rc = self.change_state_if_released(data, ControllerStates::Stopping)
|
|
|
|
|
}
|
|
|
|
|
ControllerStates::AutoUp => {} // Don't stop auto on button release
|
|
|
|
|
ControllerStates::GoingDown |
|
|
|
|
|
ControllerStates::AutoDown => {
|
|
|
|
|
return self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
rc= self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Commands::PicRecvDown { data } | Commands::BluetoothDown { data } => {
|
|
|
|
|
match self.state {
|
|
|
|
|
ControllerStates::Stopped => {return self.change_state_if_pressed(data, self.remote_down_or_auto_down())}
|
|
|
|
|
ControllerStates::Stopped => {rc = self.change_state_if_pressed(data, self.remote_down_or_auto_down())}
|
|
|
|
|
ControllerStates::Stopping => {}
|
|
|
|
|
ControllerStates::GoingUp |
|
|
|
|
|
ControllerStates::AutoUp => {
|
|
|
|
|
return self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
rc = self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
}
|
|
|
|
|
ControllerStates::GoingDown => {
|
|
|
|
|
self.send.send(Commands::ButtonTimerRestart).await.expect("Failed to necessary timer");
|
|
|
|
|
return self.change_state_if_released(data, ControllerStates::Stopping)
|
|
|
|
|
rc = self.change_state_if_released(data, ControllerStates::Stopping)
|
|
|
|
|
}
|
|
|
|
|
ControllerStates::AutoDown => {}
|
|
|
|
|
}
|
|
|
|
|
@ -195,7 +195,7 @@ impl Controller {
|
|
|
|
|
ControllerStates::AutoUp |
|
|
|
|
|
ControllerStates::GoingDown |
|
|
|
|
|
ControllerStates::AutoDown => {
|
|
|
|
|
return self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
rc = self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -207,13 +207,13 @@ impl Controller {
|
|
|
|
|
ControllerStates::GoingUp |
|
|
|
|
|
ControllerStates::AutoUp=> {
|
|
|
|
|
released_warning(data, "Limit switches may be installed incorrectly!");
|
|
|
|
|
return self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
rc = self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
}
|
|
|
|
|
ControllerStates::GoingDown |
|
|
|
|
|
ControllerStates::AutoDown=> {
|
|
|
|
|
pressed_warning(data, "Limit switches may be installed incorrectly!");
|
|
|
|
|
// Stop out of an abundance of caution. We should not get a limit press, even if it's the wrong one.
|
|
|
|
|
return self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
rc = self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -226,20 +226,30 @@ impl Controller {
|
|
|
|
|
ControllerStates::AutoUp => {
|
|
|
|
|
pressed_warning(data, "Limit switches may be installed incorrectly!");
|
|
|
|
|
// Stop out of an abundance of caution. We should not get a limit press, even if it's the wrong one.
|
|
|
|
|
return self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
rc = self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
}
|
|
|
|
|
ControllerStates::GoingDown |
|
|
|
|
|
ControllerStates::AutoDown => {
|
|
|
|
|
released_warning(data, "Limit switches may be installed incorrectly!");
|
|
|
|
|
return self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
rc = self.change_state_if_pressed(data, ControllerStates::Stopping)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Commands::PicRecvAutoMode { data } => {self.set_auto(data);}
|
|
|
|
|
Commands::PicRecvAutoMode { data } => {
|
|
|
|
|
self.set_auto(data);
|
|
|
|
|
match self.state {
|
|
|
|
|
ControllerStates::Stopped => {}
|
|
|
|
|
ControllerStates::Stopping => {}
|
|
|
|
|
ControllerStates::GoingUp => {}
|
|
|
|
|
ControllerStates::AutoUp => {rc = ControllerStates::GoingUp}
|
|
|
|
|
ControllerStates::GoingDown => {}
|
|
|
|
|
ControllerStates::AutoDown => {rc = ControllerStates::GoingDown}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Commands::StopTimerExpired => {
|
|
|
|
|
match self.state {
|
|
|
|
|
ControllerStates::Stopped => {}
|
|
|
|
|
ControllerStates::Stopping => {return ControllerStates::Stopped}
|
|
|
|
|
ControllerStates::Stopping => {rc = ControllerStates::Stopped}
|
|
|
|
|
ControllerStates::GoingUp |
|
|
|
|
|
ControllerStates::AutoUp |
|
|
|
|
|
ControllerStates::GoingDown |
|
|
|
|
|
@ -252,9 +262,9 @@ impl Controller {
|
|
|
|
|
match self.state {
|
|
|
|
|
ControllerStates::Stopped => {}
|
|
|
|
|
ControllerStates::Stopping => {}
|
|
|
|
|
ControllerStates::GoingUp => {return ControllerStates::Stopping}
|
|
|
|
|
ControllerStates::GoingUp => {rc = ControllerStates::Stopping}
|
|
|
|
|
ControllerStates::AutoUp => {}
|
|
|
|
|
ControllerStates::GoingDown => {return ControllerStates::Stopping}
|
|
|
|
|
ControllerStates::GoingDown => {rc = ControllerStates::Stopping}
|
|
|
|
|
ControllerStates::AutoDown => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -277,8 +287,12 @@ impl Controller {
|
|
|
|
|
Commands::BluetoothName { data } => {
|
|
|
|
|
warn!("Unexpected command received by motor controller {:?}", data) // TODO: internal "us" error.
|
|
|
|
|
}
|
|
|
|
|
Commands::TestingExit => {
|
|
|
|
|
return Err(anyhow!("Exiting due to testing"))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
self.state.clone() // Don't transition by default
|
|
|
|
|
//self.state.clone() // Don't transition by default
|
|
|
|
|
Ok(rc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn transition_state(&mut self, old_s: &ControllerStates, new_s: &ControllerStates) -> Result<()> {
|
|
|
|
|
@ -298,10 +312,17 @@ impl Controller {
|
|
|
|
|
loop {
|
|
|
|
|
let cmd = self.recv.recv().await.expect("Motor controller command queue unexpectedly failed");
|
|
|
|
|
trace!("Got command {:?}",cmd);
|
|
|
|
|
let new_s = self.handle_cmd(&cmd).await;
|
|
|
|
|
trace!("State current {:?} new {:?}", self.state, new_s);
|
|
|
|
|
self.transition_state(&self.state.clone(), &new_s).await.expect("Unexpected state change failure in motor controller");
|
|
|
|
|
match self.handle_cmd(&cmd).await {
|
|
|
|
|
Ok(new_s) => {
|
|
|
|
|
trace!("State current {:?} new {:?}", self.state, new_s);
|
|
|
|
|
self.transition_state(&self.state.clone(), &new_s)
|
|
|
|
|
.await
|
|
|
|
|
.expect("Unexpected state change failure in motor controller");
|
|
|
|
|
}
|
|
|
|
|
Err(_) => {break;}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(anyhow!("Unexpectedly exited loop"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn remote_up_or_auto_up(&self) -> ControllerStates {
|
|
|
|
|
@ -356,12 +377,12 @@ impl Controller {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_auto(&mut self, data: &Button) {
|
|
|
|
|
fn set_auto(&mut self, data: &Toggle) {
|
|
|
|
|
match data {
|
|
|
|
|
Button::Released => {
|
|
|
|
|
Toggle::Inactive => {
|
|
|
|
|
self.auto_mode = AutoMode::Disallowed;
|
|
|
|
|
}
|
|
|
|
|
Button::Pressed => {
|
|
|
|
|
Toggle::Active => {
|
|
|
|
|
if self.limit_state == LimitState::BothHit {
|
|
|
|
|
warn!("Limit switches not detected. Aborting auto mode.");
|
|
|
|
|
} else {
|
|
|
|
|
@ -371,6 +392,7 @@ impl Controller {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Adjusts the current limit state, based on its present state and an incoming button press or release.
|
|
|
|
|
fn adjust_limit(&mut self, limit: LimitState, pressed: &Button) {
|
|
|
|
|
match pressed {
|
|
|
|
|
Button::Released => {
|
|
|
|
|
@ -430,16 +452,315 @@ impl Controller {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Give a user warning if the given button state is pressed at the time
|
|
|
|
|
fn pressed_warning(data: &Button, warn: &str) {
|
|
|
|
|
match data {
|
|
|
|
|
Button::Pressed => {warn!("{}", warn);} // TODO: user warning, not intenral
|
|
|
|
|
Button::Pressed => {warn!("{}", warn);} // TODO: user warning, not internal
|
|
|
|
|
Button::Released => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Give a user warning if the given button state is released at the time
|
|
|
|
|
fn released_warning(data: &Button, warn: &str) {
|
|
|
|
|
match data {
|
|
|
|
|
Button::Released => {warn!("{}", warn);} // TODO: user warning, not intenral
|
|
|
|
|
Button::Released => {warn!("{}", warn);} // TODO: user warning, not internal
|
|
|
|
|
Button::Pressed => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//// Test ////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
use async_channel::{unbounded, TryRecvError};
|
|
|
|
|
use async_io::block_on;
|
|
|
|
|
|
|
|
|
|
// Creates a new controller for use in tests. Has async-channen send, recv, and motor
|
|
|
|
|
// endpoints to use. Starts in AutoMode::Disallowed and LimitState::BothHit
|
|
|
|
|
fn create_controller() -> (Controller, DispatchSendQ, DispatchRecvQ, MotorRecvQ) {
|
|
|
|
|
let (ch1s, ch1r) = unbounded();
|
|
|
|
|
let (ch2s, ch2r) = unbounded();
|
|
|
|
|
let (chms, chmr) = unbounded();
|
|
|
|
|
let con = Controller::new(ch1r, ch2s, chms);
|
|
|
|
|
(con, ch1s, ch2r, chmr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send a series of messages to the controller, then exit.
|
|
|
|
|
fn run_cmd_queue(con: &mut Controller, cs: DispatchSendQ, q: &mut Vec<Commands>) -> Result<()>{
|
|
|
|
|
block_on (
|
|
|
|
|
async {
|
|
|
|
|
q.push(Commands::TestingExit);
|
|
|
|
|
for c in q {
|
|
|
|
|
cs.send(c.clone()).await?
|
|
|
|
|
}
|
|
|
|
|
let _ = con.run().await; // If this doesn't error out of the loop, we'll hang here
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* States we want to test:
|
|
|
|
|
Controller states:
|
|
|
|
|
Stopped,
|
|
|
|
|
Stopping,
|
|
|
|
|
GoingUp,
|
|
|
|
|
AutoUp,
|
|
|
|
|
GoingDown,
|
|
|
|
|
AutoDown,
|
|
|
|
|
Auto states:
|
|
|
|
|
Disallowed
|
|
|
|
|
Allowed
|
|
|
|
|
Limit states:
|
|
|
|
|
NoLimitsHit,
|
|
|
|
|
UpperHit,
|
|
|
|
|
LowerHit,
|
|
|
|
|
BothHit,
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_stops_on_initialization() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::Stopping);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_stop_state_after_timeout() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::Stopped);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_refuses_auto_when_bothhit() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, _mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::PicRecvAutoMode{data: Toggle::Active},
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.auto_mode, AutoMode::Disallowed);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_succeeds_auto_when_not_bothhit() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, _mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::PicRecvLimitDown { data: Button::Released },
|
|
|
|
|
Commands::PicRecvAutoMode{data: Toggle::Active},
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.auto_mode, AutoMode::Allowed);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_enters_timed_up() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
Commands::PicRecvUp { data: Button::Pressed }
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::GoingUp);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::StartUp);
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_stops_after_no_input_no_auto() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
Commands::BluetoothDown { data: Button::Pressed },
|
|
|
|
|
Commands::ButtonTimerExpired,
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::Stopping);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::StartDown);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_stops_on_release_timed_up() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
Commands::PicRecvUp { data: Button::Pressed },
|
|
|
|
|
Commands::PicRecvUp { data: Button::Released },
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::Stopping);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::StartUp);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_stops_on_limit_timed_up() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
Commands::PicRecvUp { data: Button::Pressed },
|
|
|
|
|
Commands::PicRecvLimitUp { data: Button::Pressed },
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::Stopping);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::StartUp);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_enters_auto_up() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
Commands::PicRecvLimitUp{data: Button::Released},
|
|
|
|
|
Commands::PicRecvAutoMode{data: Toggle::Active},
|
|
|
|
|
Commands::PicRecvUp { data: Button::Pressed },
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::AutoUp);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::StartUp);
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_does_not_stop_after_no_input_in_auto() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
Commands::PicRecvLimitUp{data: Button::Released},
|
|
|
|
|
Commands::PicRecvAutoMode{data: Toggle::Active},
|
|
|
|
|
Commands::BluetoothDown { data: Button::Pressed },
|
|
|
|
|
Commands::ButtonTimerExpired,
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::AutoDown);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::StartDown);
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_does_not_stop_auto_release() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
Commands::PicRecvLimitUp{data: Button::Released},
|
|
|
|
|
Commands::PicRecvAutoMode{data: Toggle::Active},
|
|
|
|
|
Commands::PicRecvUp { data: Button::Pressed },
|
|
|
|
|
Commands::PicRecvUp { data: Button::Released },
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::AutoUp);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::StartUp);
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_switches_to_timed_when_auto_toggled_off() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
Commands::PicRecvLimitUp{data: Button::Released},
|
|
|
|
|
Commands::PicRecvAutoMode{data: Toggle::Active},
|
|
|
|
|
Commands::PicRecvUp { data: Button::Pressed },
|
|
|
|
|
Commands::PicRecvAutoMode{data: Toggle::Inactive},
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::GoingUp);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::StartUp);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::StartUp); // Second up commanded should be okay
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_continues_with_additional_presses() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
Commands::PicRecvLimitUp{data: Button::Released},
|
|
|
|
|
Commands::PicRecvAutoMode{data: Toggle::Active},
|
|
|
|
|
Commands::PicRecvUp { data: Button::Pressed },
|
|
|
|
|
Commands::PicRecvUp { data: Button::Released },
|
|
|
|
|
Commands::BluetoothUp { data: Button::Pressed },
|
|
|
|
|
Commands::BluetoothUp { data: Button::Released },
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::AutoUp);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::StartUp);
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_stops_on_opposite_button() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
Commands::PicRecvLimitUp{data: Button::Released},
|
|
|
|
|
Commands::PicRecvAutoMode{data: Toggle::Active},
|
|
|
|
|
Commands::PicRecvUp { data: Button::Pressed },
|
|
|
|
|
Commands::PicRecvUp { data: Button::Released },
|
|
|
|
|
Commands::BluetoothDown { data: Button::Pressed },
|
|
|
|
|
Commands::BluetoothDown { data: Button::Released },
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::Stopping);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::StartUp);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn motor_stops_if_limits_installed_backwards() -> Result<()>{
|
|
|
|
|
let (mut con, cs, _cr, mr) = create_controller();
|
|
|
|
|
let mut q = vec![
|
|
|
|
|
Commands::StopTimerExpired,
|
|
|
|
|
Commands::PicRecvLimitUp{data: Button::Released},
|
|
|
|
|
Commands::PicRecvLimitDown{data: Button::Released},
|
|
|
|
|
Commands::PicRecvAutoMode{data: Toggle::Active},
|
|
|
|
|
Commands::PicRecvUp { data: Button::Pressed },
|
|
|
|
|
Commands::PicRecvUp { data: Button::Released },
|
|
|
|
|
Commands::PicRecvLimitDown { data: Button::Pressed },
|
|
|
|
|
];
|
|
|
|
|
run_cmd_queue(&mut con, cs, &mut q)?;
|
|
|
|
|
assert_eq!(con.state, ControllerStates::Stopping);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::StartUp);
|
|
|
|
|
assert_eq!(mr.try_recv().unwrap(), MotorCommands::Stop);
|
|
|
|
|
assert_eq!(mr.try_recv(), Err(TryRecvError::Empty)); // Make sure queue is empty
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|