diff --git a/gem-remotes-esp32/src/ble_server.rs b/gem-remotes-esp32/src/ble_server.rs index 6f2c05f..490d33d 100644 --- a/gem-remotes-esp32/src/ble_server.rs +++ b/gem-remotes-esp32/src/ble_server.rs @@ -147,6 +147,8 @@ impl BleServer { .on_write(closure!(clone sender, |args: &mut OnWriteArgs| { on_bluetooth_cmd(&sender, args, Commands::BluetoothDown {data: Button::Released}) })); + let button_down_name = button_down.lock().create_descriptor(BleUuid::Uuid16(0x2901), DescriptorProperties::READ); + button_down_name.lock().set_value(b"Command Down"); // --- Button Stop Bluetooth GATT -------------------------------------------------------- let button_stop = lift_service.lock().create_characteristic( UUID_BUTTON_STOP, @@ -156,6 +158,8 @@ impl BleServer { .on_write(closure!(clone sender, |args: &mut OnWriteArgs| { on_bluetooth_cmd(&sender, args, Commands::BluetoothStop {data: Button::Released}) })); + let button_stop_name = button_stop.lock().create_descriptor(BleUuid::Uuid16(0x2901), DescriptorProperties::READ); + button_stop_name.lock().set_value(b"Command Stop"); // --- Button Aux Bluetooth GATT -------------------------------------------------------- let button_aux = lift_service.lock().create_characteristic( UUID_BUTTON_AUX, @@ -165,6 +169,8 @@ impl BleServer { .on_write(closure!(clone sender, |args: &mut OnWriteArgs| { on_bluetooth_cmd(&sender, args, Commands::BluetoothAux {data: Button::Released}) })); + let button_aux_name = button_aux.lock().create_descriptor(BleUuid::Uuid16(0x2901), DescriptorProperties::READ); + button_aux_name.lock().set_value(b"Command Aux"); // --- Button Learn Bluetooth GATT -------------------------------------------------------- let button_learn = lift_service.lock().create_characteristic( UUID_BUTTON_LEARN, @@ -174,6 +180,8 @@ impl BleServer { .on_write(closure!(clone sender, |args: &mut OnWriteArgs| { on_bluetooth_cmd(&sender, args, Commands::BluetoothLearn {data: Button::Released}) })); + let button_learn_name = button_learn.lock().create_descriptor(BleUuid::Uuid16(0x2901), DescriptorProperties::READ); + button_learn_name.lock().set_value(b"Command Learn"); // --- Button Auto Bluetooth GATT -------------------------------------------------------- let button_auto = lift_service.lock().create_characteristic( UUID_BUTTON_AUTO, @@ -183,6 +191,8 @@ impl BleServer { .on_write(closure!(clone sender, |args: &mut OnWriteArgs| { on_bluetooth_cmd(&sender, args, Commands::BluetoothAuto {data: Button::Released}) })); + let button_auto_name = button_auto.lock().create_descriptor(BleUuid::Uuid16(0x2901), DescriptorProperties::READ); + button_auto_name.lock().set_value(b"Command Learn"); // --- Device Name Bluetooth GATT -------------------------------------------------------- let device_name = lift_service.lock().create_characteristic( UUID_BLUETOOTH_NAME, @@ -197,21 +207,29 @@ impl BleServer { UUID_STATUS_LIMITS, NimbleProperties::READ | NimbleProperties::INDICATE, ); + let status_limits_name = status_limits.lock().create_descriptor(BleUuid::Uuid16(0x2901), DescriptorProperties::READ); + status_limits_name.lock().set_value(b"Status of limits"); // --- Status Motor Bluetooth GATT -------------------------------------------------------- let status_motor = lift_service.lock().create_characteristic( UUID_STATUS_MOTOR, NimbleProperties::READ | NimbleProperties::INDICATE, ); + let status_motor_name = status_limits.lock().create_descriptor(BleUuid::Uuid16(0x2901), DescriptorProperties::READ); + status_motor_name.lock().set_value(b"Status of motors"); // --- Status Status Bluetooth GATT -------------------------------------------------------- let status_status = lift_service.lock().create_characteristic( UUID_STATUS_STATUS, NimbleProperties::READ | NimbleProperties::INDICATE, ); + let status_status_name = status_limits.lock().create_descriptor(BleUuid::Uuid16(0x2901), DescriptorProperties::READ); + status_status_name.lock().set_value(b"Status flags"); // --- Status Reason Bluetooth GATT -------------------------------------------------------- let status_reason = lift_service.lock().create_characteristic( UUID_STATUS_REASON, NimbleProperties::READ | NimbleProperties::INDICATE, ); + let status_reason_name = status_limits.lock().create_descriptor(BleUuid::Uuid16(0x2901), DescriptorProperties::READ); + status_reason_name.lock().set_value(b"Status reason"); // Default to not pairable self.advertise_unpairable()?; diff --git a/gem-remotes-esp32/src/message_timer.rs b/gem-remotes-esp32/src/message_timer.rs index 40a140a..6bddb64 100644 --- a/gem-remotes-esp32/src/message_timer.rs +++ b/gem-remotes-esp32/src/message_timer.rs @@ -100,6 +100,7 @@ impl MessageTimer { Err(_) => { trace!("Timeout reached"); self.send_q.send(self.done.clone()).await.expect("Failed to send timeout"); + self.state = State::Stopped; } } } diff --git a/gem-remotes-esp32/src/test_console.rs b/gem-remotes-esp32/src/test_console.rs index 0756809..d74c6f6 100644 --- a/gem-remotes-esp32/src/test_console.rs +++ b/gem-remotes-esp32/src/test_console.rs @@ -23,14 +23,14 @@ use ::{ //time::Duration, // could also use core::time::Duration? }, core::time::Duration, + std::sync::Arc, }; use async_channel::Sender; use log::*; //{trace, debug, info, warn, error} use gem_remotes_lib::{ - Button, - Commands + Button, Commands, LimitStatus, EMPTY_LIMITS }; #[derive(Command)] @@ -61,10 +61,37 @@ pub enum Menu{//<'a> { /// 0 for not pressed, 1 for pressed data: u8, }, - /// Send a bluetooth characteristic: Limits - BluetoothTopLimit { data: u8 }, - /// Send a bluetooth characteristic: Limits - BluetoothBottomLimit { data: u8 }, + /// Send a bluetooth characteristic: Aux + BluetoothAux { + /// 0 for not pressed, 1 for pressed + data: u8, + }, + /// Send command from Fake PIC to toggle Aux + FPicAux, + /// Send command from Fake PIC to toggle Auto + FPicAuto, + /// Send command from Fake PIC to trigger learn mode + FPicLearn, + /// Send command from Fake PIC that is button press Up + FPicUp, + /// Send command from Fake PIC that is button press Down + FPicDown, + /// Send command from Fake PIC that is button press Stop (Can also be used as Up or Down release) + FPicStop, + /// Send command from Fake PIC to toggle Panic + FPicPanic, + /// Send command from Fake PIC to engage Lockout + FPicLockout, + /// Clear limit for first limit + FPicLimitClear1, + /// Activate top limit for first limit + FPicLimitTop1, + /// Activate bottom limit for first limit + FPicLimitBottom1, + /// Set hardware fault (Reason will be given as "test fault") + FPicFault, + /// Have the fake PIC output its internal state + FPicOutput, /// Send a bluetooth characteristic: Wifi SSID //BluetoothWifiSsid { ssid: &'a str }, @@ -132,25 +159,81 @@ pub fn process_menu( } } Menu::BluetoothLearn { data } => { - cli.writer() - .write_str("TODO: simulate bluetooth characteristic change")?; - let _ = data; + let but = input_to_button(data); + match but { + Some(d) => { + println!("Sending PicRecvUp command"); + let _ = dispatch.send_blocking(Commands::BluetoothLearn{data: d}); + } + None => {println!("Incorrect value; enter 0 or 1")} + } } Menu::BluetoothAuto { data } => { - cli.writer() - .write_str("TODO: simulate bluetooth characteristic change")?; - let _ = data; + let but = input_to_button(data); + match but { + Some(d) => { + println!("Sending PicRecvUp command"); + let _ = dispatch.send_blocking(Commands::BluetoothAuto{data: d}); + } + None => {println!("Incorrect value; enter 0 or 1")} + } } - Menu::BluetoothTopLimit { data } => { - cli.writer() - .write_str("TODO: simulate bluetooth characteristic change")?; - let _ = data; + Menu::BluetoothAux { data } => { + let but = input_to_button(data); + match but { + Some(d) => { + println!("Sending PicRecvUp command"); + let _ = dispatch.send_blocking(Commands::BluetoothAux{data: d}); + } + None => {println!("Incorrect value; enter 0 or 1")} + } } - Menu::BluetoothBottomLimit { data } => { - cli.writer() - .write_str("TODO: simulate bluetooth characteristic change")?; - let _ = data; + Menu::FPicAux => { + let _ = dispatch.send_blocking(Commands::FPicToggleAux); } + Menu::FPicAuto => { + let _ = dispatch.send_blocking(Commands::FPicToggleAuto); + } + Menu::FPicLearn => { + let _ = dispatch.send_blocking(Commands::FPicPressLearn); + } + Menu::FPicUp => { + let _ = dispatch.send_blocking(Commands::FPicPressUp); + } + Menu::FPicDown => { + let _ = dispatch.send_blocking(Commands::FPicPressDown); + } + Menu::FPicStop => { + let _ = dispatch.send_blocking(Commands::FPicPressStop); + } + Menu::FPicPanic => { + let _ = dispatch.send_blocking(Commands::FPicTogglePanic); + } + Menu::FPicLockout => { + let _ = dispatch.send_blocking(Commands::FPicLockout); + } + Menu::FPicFault => { + let _ = dispatch.send_blocking(Commands::FPicFault { data: Arc::new("Test Fault".to_string()) }); + } + Menu::FPicOutput => { + let _ = dispatch.send_blocking(Commands::FPicOutput); + } + Menu::FPicLimitClear1 => { + let mut lim = EMPTY_LIMITS; + lim.only_starboard_bow = LimitStatus::NotActive; + let _ = dispatch.send_blocking(Commands::FPicLimit { data: lim }); + } + Menu::FPicLimitTop1 => { + let mut lim = EMPTY_LIMITS; + lim.only_starboard_bow = LimitStatus::TopActive; + let _ = dispatch.send_blocking(Commands::FPicLimit { data: lim }); + } + Menu::FPicLimitBottom1 => { + let mut lim = EMPTY_LIMITS; + lim.only_starboard_bow = LimitStatus::BottomActive; + let _ = dispatch.send_blocking(Commands::FPicLimit { data: lim }); + } + /*Menu::BluetoothWifiSsid { ssid } => { cli.writer() .write_str("TODO: simulate bluetooth characteristic change")?; diff --git a/gem-remotes-lib/src/commands.rs b/gem-remotes-lib/src/commands.rs index 5c89b66..f9ed27a 100644 --- a/gem-remotes-lib/src/commands.rs +++ b/gem-remotes-lib/src/commands.rs @@ -26,6 +26,7 @@ pub enum Commands { FPicLockout, FPicFault {data: Arc}, // String is cause. Send empty string to clear fault. FPicLimit {data: Limits}, + FPicOutput, // TODO: real hardware buttons - consider re-sending occasionally when pressed, so that transitions like holding up -> stopping -> holding down -> stopped -> (should go down but gets no new notice) work. diff --git a/gem-remotes-lib/src/fake_pic.rs b/gem-remotes-lib/src/fake_pic.rs index 8508a33..433e4a7 100644 --- a/gem-remotes-lib/src/fake_pic.rs +++ b/gem-remotes-lib/src/fake_pic.rs @@ -49,6 +49,7 @@ impl FakePic { Commands::PairTimerExpired, Commands::StopTimerExpired, Commands::ButtonTimerExpired, + Commands::FPicOutput, ]; FakePic { motor_state: Motors{ @@ -99,11 +100,11 @@ impl FakePic { Ok(()) } pub async fn press_up(&mut self) -> Result<()> { - if self.status_state.union(Statuses::PANIC).is_empty() || self.status_state.union(Statuses::LOCKOUT).is_empty(){ + if !self.status_state.intersection(Statuses::PANIC| Statuses::LOCKOUT).is_empty() { warn!("Ignoring commands while in panic or lockout mode!") } - if self.motor_state.are_cooldown() { - info!("Ignoring commands while motors cool down") + else if self.motor_state.are_cooldown() { + warn!("Ignoring commands while motors cool down") } else if self.motor_state.going_down() { warn!("Reversing from down to up; entering cooldown!"); @@ -120,11 +121,11 @@ impl FakePic { Ok(()) } pub async fn press_down(&mut self) -> Result<()> { - if self.status_state.union(Statuses::PANIC).is_empty() || self.status_state.union(Statuses::LOCKOUT).is_empty() { + if !self.status_state.intersection(Statuses::PANIC| Statuses::LOCKOUT).is_empty() { warn!("Ignoring commands while in panic or lockout mode!") } else if self.motor_state.are_cooldown() { - info!("Ignoring commands while motors cool down") + warn!("Ignoring commands while motors cool down") } else if self.motor_state.going_up() { warn!("Reversing from up to down; entering cooldown!"); @@ -143,18 +144,25 @@ impl FakePic { pub async fn press_stop(&mut self) -> Result<()> { self.motor_state.stop(); self.send_q.send(Commands::BluetoothStatusMotor { data: self.motor_state }).await?; + //self.send_q.send(Commands::ButtonTimerClear).await?; // Stop waiting on button timeouts Ok(()) } pub async fn toggle_panic(&mut self) -> Result<()> { - self.status_state ^= Statuses::PANIC; + self.status_state = self.status_state.symmetric_difference(Statuses::PANIC); + if self.status_state.intersection(Statuses::PANIC).is_empty(){ + warn!("Clearing Panic status") + } else { + warn!("Entering panic status") + } self.motor_state.stop(); self.send_q.send(Commands::BluetoothStatusStatus { data: self.status_state }).await?; - self.send_q.send(Commands::BluetoothStatusMotor { data: self.motor_state }).await?; + //self.send_q.send(Commands::BluetoothStatusMotor { data: self.motor_state }).await?; Ok(()) } pub async fn lockout(&mut self) -> Result<()> { self.status_state &= Statuses::LOCKOUT; // No toggle; lockout can only be cleared by reboot per spec self.motor_state.stop(); + warn!("Locking out all movement controls until device is reset!"); self.send_q.send(Commands::BluetoothStatusStatus { data: self.status_state }).await?; self.send_q.send(Commands::BluetoothStatusMotor { data: self.motor_state }).await?; Ok(()) @@ -167,6 +175,7 @@ impl FakePic { } self.fault_cause = cause; self.send_q.send(Commands::BluetoothStatusStatus { data: self.status_state }).await?; + self.send_q.send(Commands::BluetoothStatusReason { data: Arc::new(self.fault_cause.clone())}).await?; Ok(()) } pub async fn change_limit(&mut self, limits: Limits) -> Result<()> { @@ -182,55 +191,91 @@ impl FakePic { // === Handle events from event manager ===================================================== pub async fn run(&mut self) -> Result<()> { - let cmd = self.recv_q.recv().await.expect("PIC simulator failed waiting for messages"); - match cmd { - // Commands from testing and user - Commands::FPicToggleAux => {self.toggle_aux().await} - Commands::FPicToggleAuto => {self.toggle_auto().await} - Commands::FPicPressLearn => {self.press_learn().await} - Commands::FPicPressUp => {self.press_up().await} - Commands::FPicPressDown => {self.press_down().await} - Commands::FPicPressStop => {self.press_stop().await} - Commands::FPicTogglePanic => {self.toggle_panic().await} - Commands::FPicLockout => {self.lockout().await} - Commands::FPicFault{data} => {self.fault(data.to_string()).await} - Commands::FPicLimit { data } => {self.change_limit(data).await} - // Commands from bluetooth - Commands::BluetoothAuto { data } => { - if data.is_pressed() { - self.toggle_auto().await?; - } Ok(()) + loop { + let cmd = self.recv_q.recv().await.expect("PIC simulator failed waiting for messages"); + match cmd { + // Commands from testing and user + Commands::FPicToggleAux => {self.toggle_aux().await?} + Commands::FPicToggleAuto => {self.toggle_auto().await?} + Commands::FPicPressLearn => {self.press_learn().await?} + Commands::FPicPressUp => {self.press_up().await?} + Commands::FPicPressDown => {self.press_down().await?} + Commands::FPicPressStop => {self.press_stop().await?} + Commands::FPicTogglePanic => {self.toggle_panic().await?} + Commands::FPicLockout => {self.lockout().await?} + Commands::FPicFault{data} => {self.fault(data.to_string()).await?} + Commands::FPicLimit { data } => {self.change_limit(data).await?} + // Commands from bluetooth + Commands::BluetoothAuto { data } => { + if data.is_pressed() { + self.toggle_auto().await?; + } + } + Commands::BluetoothAux { data } => { + if data.is_pressed() { + self.toggle_aux().await?; + } + } + Commands::BluetoothLearn { data } => { + if data.is_pressed() { + self.press_learn().await?; + } + } + Commands::BluetoothUp { data } => { + if data.is_pressed() { + self.press_up().await?; + } else { + self.press_stop().await?; // Releasing up is equivalent to stop + } + } + Commands::BluetoothDown { data } => { + if data.is_pressed() { + self.press_down().await?; + } else { + self.press_stop().await?; // Releasing down is equivalent to stop + } + } + Commands::BluetoothStop { .. } => {self.press_stop().await?} // Stopping on release of stop button is a noop but safe. + // Commands from timers + Commands::PairTimerExpired => {self.exit_learn().await?} + Commands::StopTimerExpired => {self.exit_cooldown().await?} + Commands::ButtonTimerExpired => {self.press_stop().await?} + Commands::FPicOutput => { + warn!("== Fake PIC internal state ==---------------"); + if self.status_state.intersection(Statuses::LOCKOUT).is_empty() { + info!("Lockout flag: Off"); + } else { + error!("Lockout flag: On"); + } + if self.status_state.intersection(Statuses::PANIC).is_empty() { + info!("Panic flag: Off"); + } else { + warn!("Panic flag: On"); + } + if self.status_state.intersection(Statuses::FAULT).is_empty() { + info!("Fault flag: Off"); + } else { + warn!("Fault flag: On"); + } + if self.status_state.intersection(Statuses::LEARN).is_empty() { + info!("Learn flag: Off"); + } else { + info!("Learn flag: On"); + } + if self.status_state.intersection(Statuses::AUTO).is_empty() { + info!("Auto flag: Off"); + } else { + info!("Auto flag: On"); + } + if self.status_state.intersection(Statuses::AUX).is_empty() { + info!("Aux flag: Off"); + } else { + warn!("Aux flag: On"); + } + warn!("==------------------------------------------"); + } + _ => {warn!("Unknown command received by Fake PIC simulator!");} } - Commands::BluetoothAux { data } => { - if data.is_pressed() { - self.toggle_aux().await?; - } Ok(()) - } - Commands::BluetoothLearn { data } => { - if data.is_pressed() { - self.press_learn().await?; - } Ok(()) - } - Commands::BluetoothUp { data } => { - if data.is_pressed() { - self.press_up().await?; - } else { - self.press_stop().await?; // Releasing up is equivalent to stop - } Ok(()) - } - Commands::BluetoothDown { data } => { - if data.is_pressed() { - self.press_down().await?; - } else { - self.press_stop().await?; // Releasing down is equivalent to stop - } Ok(()) - } - Commands::BluetoothStop { .. } => {self.press_stop().await} // Stopping on release of stop button is a noop but safe. - // Commands from timers - Commands::PairTimerExpired => {self.exit_learn().await} - Commands::StopTimerExpired => {self.exit_cooldown().await} - Commands::ButtonTimerExpired => {self.press_stop().await} - _ => {warn!("Unknown command received by Fake PIC simulator!"); Ok(())} } }