From b10e1b7806b0cfbc542a298d4069b08363e7b2e1 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 16 Mar 2026 14:25:26 +0100 Subject: [PATCH] more tests --- satrs-example/models/src/mgm_assembly.rs | 4 +- satrs-example/src/acs/mgm.rs | 12 +- satrs-example/src/acs/mgm_assembly.rs | 158 +++++++++++++++++++++-- satrs-example/src/lib.rs | 10 +- 4 files changed, 161 insertions(+), 23 deletions(-) diff --git a/satrs-example/models/src/mgm_assembly.rs b/satrs-example/models/src/mgm_assembly.rs index 9287e2d..6985afb 100644 --- a/satrs-example/models/src/mgm_assembly.rs +++ b/satrs-example/models/src/mgm_assembly.rs @@ -63,12 +63,12 @@ pub mod request { pub mod response { use crate::Message; - #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] + #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug, PartialEq, Eq)] pub enum ModeCommandFailure { Timeout, } - #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] + #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug, PartialEq, Eq)] pub enum Response { Ok, ModeFailure(ModeCommandFailure), diff --git a/satrs-example/src/acs/mgm.rs b/satrs-example/src/acs/mgm.rs index d80c892..ee9d198 100644 --- a/satrs-example/src/acs/mgm.rs +++ b/satrs-example/src/acs/mgm.rs @@ -56,7 +56,7 @@ pub enum TransitionState { Done, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ModeRequest { SetMode(DeviceMode), ReadMode, @@ -255,7 +255,7 @@ impl MgmHandlerLis3Mdl { self.handle_hk_request(Some(tc_id), &hk_request) } mgm::request::Request::Mode(device_mode) => { - self.mode_helpers.tc_id = Some(tc_id); + self.mode_helpers.tc_commander = Some(tc_id); self.start_transition(device_mode, false); } } @@ -410,10 +410,10 @@ impl MgmHandlerLis3Mdl { // Should be called to complete a mode transition which failed. fn handle_mode_transition_failure(&mut self) { - self.mode_helpers.finish(false); - if let Some(requestor) = self.mode_helpers.tc_id { + let tc_commander = self.mode_helpers.finish(false); + if tc_commander.is_some() { self.send_telemetry( - Some(requestor), + tc_commander, mgm::response::Response::ModeFailure(ModeFailure::Timeout), ); } @@ -426,7 +426,7 @@ impl MgmHandlerLis3Mdl { // Should be called to complete a mode transition successfully. fn handle_mode_reached(&mut self) { self.announce_mode(); - if let Some(requestor) = self.mode_helpers.tc_id { + if let Some(requestor) = self.mode_helpers.tc_commander { self.send_mode_tm(requestor); } // Inform our parent about mode changes. diff --git a/satrs-example/src/acs/mgm_assembly.rs b/satrs-example/src/acs/mgm_assembly.rs index d8de7d5..cdd0658 100644 --- a/satrs-example/src/acs/mgm_assembly.rs +++ b/satrs-example/src/acs/mgm_assembly.rs @@ -13,13 +13,13 @@ use satrs_example::{ModeHelper, TmtcQueues}; use crate::ccsds::pack_ccsds_tm_packet_for_now; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ModeRequest { SetMode(AssemblyMode), ReadMode, } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ModeReport { /// Mode of the assembly. Mode(AssemblyMode), @@ -67,7 +67,7 @@ pub struct Assembly { } impl Assembly { - const ID: ComponentId = ComponentId::AcsMgmAssembly; + pub const ID: ComponentId = ComponentId::AcsMgmAssembly; pub fn new( parent_queues: ParentQueueHelper, @@ -246,15 +246,15 @@ impl Assembly { } pub fn handle_mode_reached(&mut self) { + let tc_commander = self.mode_helper.finish(true); self.announce_mode(); - if self.mode_helper.tc_id.is_some() { - self.send_telemetry(self.mode_helper.tc_id, response::Response::Ok); + if tc_commander.is_some() { + self.send_telemetry(tc_commander, response::Response::Ok); } self.parent_queues .report_tx .send(ModeReport::Mode(self.mode_helper.current)) .unwrap(); - self.mode_helper.finish(true); } pub fn handle_mode_transition_failure(&mut self) { @@ -263,9 +263,9 @@ impl Assembly { } else { ModeReport::SetModeTimeout(self.mgm_modes.map(|info| info.mode)) }; - if self.mode_helper.tc_id.is_some() { + if self.mode_helper.tc_commander.is_some() { self.send_telemetry( - self.mode_helper.tc_id, + self.mode_helper.tc_commander, response::Response::ModeFailure(ModeCommandFailure::Timeout), ); } @@ -286,7 +286,7 @@ impl Assembly { tc_id: Option, ) { self.mode_keeping_transition = mode_keeping; - self.mode_helper.tc_id = tc_id; + self.mode_helper.tc_commander = tc_id; self.mgm_modes .iter_mut() .for_each(|m| m.reply_received = false); @@ -301,13 +301,29 @@ impl Assembly { self.mode_helper.current ); } + + #[inline] + pub fn mode(&self) -> AssemblyMode { + self.mode_helper.current + } + + #[inline] + pub fn mode_transition_active(&self) -> bool { + self.mode_helper.transition_active() + } } #[cfg(test)] mod tests { use std::sync::mpsc::TryRecvError; - use models::ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned}; + use arbitrary_int::u11; + use models::{ + Apid, Message, MessageType, TcHeader, + ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned}, + mgm_assembly, + }; + use satrs::spacepackets::SpacePacketHeader; use super::*; @@ -377,10 +393,132 @@ mod tests { } } + pub fn create_request_tc( + request: models::mgm_assembly::request::Request, + ) -> models::ccsds::CcsdsTcPacketOwned { + models::ccsds::CcsdsTcPacketOwned::new_with_request( + SpacePacketHeader::new_from_apid(u11::new(Apid::Acs as u16)), + TcHeader::new(Assembly::ID, request.message_type()), + request, + ) + } + #[test] fn basic_test() { let mut tb = Testbench::new(); tb.assert_all_queues_empty(); tb.assembly.periodic_operation(); + tb.assert_all_queues_empty(); + assert_eq!(tb.assembly.mode(), AssemblyMode::NoModeKeeping); + } + + #[test] + fn test_tc_commanded_transition() { + let mut tb = Testbench::new(); + tb.tc_tx + .send(create_request_tc(mgm_assembly::request::Request::Mode( + AssemblyMode::Device(DeviceMode::Normal), + ))) + .unwrap(); + tb.assembly.periodic_operation(); + assert!(tb.assembly.mode_transition_active()); + + for rx in tb.mgm_request_rx.iter() { + let request = rx.try_recv().unwrap(); + assert_eq!( + request, + crate::mgm::ModeRequest::SetMode(DeviceMode::Normal) + ); + } + + // Confirm the mode is set. + for tx in tb.mgm_report_tx.iter() { + tx.send(crate::mgm::ModeReport::Mode(DeviceMode::Normal)) + .unwrap(); + } + + tb.assembly.periodic_operation(); + assert!(!tb.assembly.mode_transition_active()); + assert_eq!(tb.assembly.mode(), AssemblyMode::Device(DeviceMode::Normal)); + + let response = tb.tm_rx.try_recv().unwrap(); + assert_eq!(response.tm_header.sender_id, Assembly::ID); + assert_eq!(response.tm_header.message_type, MessageType::Verification); + let response: response::Response = postcard::from_bytes(&response.payload).unwrap(); + assert_eq!(response, response::Response::Ok); + } + + #[test] + fn test_parent_commanded_transition() { + let mut tb = Testbench::new(); + tb.subsystem_req_tx + .send(ModeRequest::SetMode(AssemblyMode::Device( + DeviceMode::Normal, + ))) + .unwrap(); + tb.assembly.periodic_operation(); + assert!(tb.assembly.mode_transition_active()); + + for rx in tb.mgm_request_rx.iter() { + let request = rx.try_recv().unwrap(); + assert_eq!( + request, + crate::mgm::ModeRequest::SetMode(DeviceMode::Normal) + ); + } + + // Confirm the mode is set. + for tx in tb.mgm_report_tx.iter() { + tx.send(crate::mgm::ModeReport::Mode(DeviceMode::Normal)) + .unwrap(); + } + + tb.assembly.periodic_operation(); + assert!(!tb.assembly.mode_transition_active()); + assert_eq!(tb.assembly.mode(), AssemblyMode::Device(DeviceMode::Normal)); + + let report = tb.subsystem_report_rx.try_recv().unwrap(); + assert_eq!( + report, + ModeReport::Mode(AssemblyMode::Device(DeviceMode::Normal)) + ); + } + + #[test] + fn test_one_mgm_is_sufficient() { + let mut tb = Testbench::new(); + tb.subsystem_req_tx + .send(ModeRequest::SetMode(AssemblyMode::Device( + DeviceMode::Normal, + ))) + .unwrap(); + tb.assembly.periodic_operation(); + assert!(tb.assembly.mode_transition_active()); + + for rx in tb.mgm_request_rx.iter() { + let request = rx.try_recv().unwrap(); + assert_eq!( + request, + crate::mgm::ModeRequest::SetMode(DeviceMode::Normal) + ); + } + + // One device is sufficient. + tb.mgm_report_tx[0] + .send(crate::mgm::ModeReport::Mode(DeviceMode::Normal)) + .unwrap(); + tb.mgm_report_tx[1] + .send(crate::mgm::ModeReport::Mode(DeviceMode::Off)) + .unwrap(); + + tb.assembly.periodic_operation(); + assert!(!tb.assembly.mode_transition_active()); + assert_eq!(tb.assembly.mode(), AssemblyMode::Device(DeviceMode::Normal)); + + let report = tb.subsystem_report_rx.try_recv().unwrap(); + assert_eq!( + report, + ModeReport::Mode(AssemblyMode::Device(DeviceMode::Normal)) + ); } } diff --git a/satrs-example/src/lib.rs b/satrs-example/src/lib.rs index ca1f1a1..4d33845 100644 --- a/satrs-example/src/lib.rs +++ b/satrs-example/src/lib.rs @@ -104,7 +104,7 @@ pub struct TmtcQueues { pub struct ModeHelper { pub current: Mode, pub target: Option, - pub tc_id: Option, + pub tc_commander: Option, pub transition_start: Option, pub timeout: Duration, pub transition_state: TransitionState, @@ -115,7 +115,7 @@ impl ModeHelper ModeHelper Option { if self.target.is_none() { - return; + return None; } if success { self.current = self.target.take().unwrap(); } else { self.target = None; } - self.tc_id = None; self.transition_state = Default::default(); self.transition_start = None; + self.tc_commander.take() } }