From 8d36fd3761bc5529ecfc62a95c43016696f85fed Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 22 Sep 2026 11:48:40 +0200 Subject: [PATCH] more events --- satrs-example/src/acs/mgm.rs | 42 +++++- satrs-example/src/acs/mgm_assembly.rs | 22 ++- satrs-example/src/eps/pcdu.rs | 27 ++++ satrs-example/src/event_manager.rs | 27 +++- satrs-example/src/main.rs | 14 +- satrs-example/src/tmtc/tc_source.rs | 153 +++++++++++++++++++- satrs-example/types/src/acs/mgm.rs | 15 ++ satrs-example/types/src/acs/mgm_assembly.rs | 12 ++ satrs-example/types/src/lib.rs | 1 + satrs-example/types/src/pcdu.rs | 12 ++ satrs-example/types/src/tmtc.rs | 17 +++ 11 files changed, 329 insertions(+), 13 deletions(-) create mode 100644 satrs-example/types/src/tmtc.rs diff --git a/satrs-example/src/acs/mgm.rs b/satrs-example/src/acs/mgm.rs index bf3d76c..ae714f7 100644 --- a/satrs-example/src/acs/mgm.rs +++ b/satrs-example/src/acs/mgm.rs @@ -170,6 +170,7 @@ pub struct MgmHandlerLis3Mdl { mode_leaf_helper: ModeLeafHelper, spi_fault_counter: FaultCounterStd, health_table: HealthTableMapSync, + event_tx: mpsc::SyncSender<(ComponentId, mgm::Event)>, } impl MgmHandlerLis3Mdl { @@ -183,6 +184,7 @@ impl MgmHandlerLis3Mdl { mode_leaf_helper: ModeLeafHelper, mode_timeout: Duration, health_table: HealthTableMapSync, + event_tx: mpsc::SyncSender<(ComponentId, mgm::Event)>, ) -> Self { Self { id, @@ -201,6 +203,7 @@ impl MgmHandlerLis3Mdl { mode_leaf_helper, spi_fault_counter: FaultCounterStd::new(SPI_FAULT_THRESHOLD, SPI_FAULT_DECREMENT_AFTER), health_table, + event_tx, } } @@ -442,9 +445,12 @@ impl MgmHandlerLis3Mdl { ); self.health_table .set_health(component_id, HealthState::Faulty); - // TODO: Event? Health-table changes are currently invisible to the ground - // except through this log line. Likely applies to other health/mode - // transitions across the example app too, not just this one. + if let Err(e) = self.event_tx.send(( + self.id.component_id(), + mgm::Event::SpiFaultThresholdExceeded, + )) { + log::warn!("{}: failed to send fault event: {}", self.id.str(), e); + } // Do not restart an already pending Off transition: poll_sensor still calls // this every cycle the fault persists, and current stays Normal until the // transition completes, so re-triggering here would keep resetting the @@ -496,7 +502,16 @@ impl MgmHandlerLis3Mdl { fn announce_mode(&self) { log::info!("{} announcing mode: {:?}", self.id.str(), self.mode()); - // TODO: Event? + if let Err(e) = self + .event_tx + .send((self.id.component_id(), mgm::Event::ModeChanged(self.mode()))) + { + log::warn!( + "{}: failed to send mode changed event: {}", + self.id.str(), + e + ); + } } fn report_mode_to_parent(&self) { @@ -567,6 +582,7 @@ mod tests { pub tm_rx: mpsc::Receiver, pub switch_rx: mpsc::Receiver, pub health_table: HealthTableMapSync, + pub event_rx: mpsc::Receiver<(ComponentId, mgm::Event)>, pub handler: MgmHandlerLis3Mdl, } @@ -587,6 +603,7 @@ mod tests { let switch_map = SwitchSet::new(switch_map); let shared_switch_set = SharedSwitchSet::new(Mutex::new(switch_map)); let health_table = HealthTableMapSync::default(); + let (event_tx, event_rx) = mpsc::sync_channel(5); let handler = MgmHandlerLis3Mdl::new( MgmId::_0, TmtcQueues { tc_rx, tm_tx }, @@ -596,6 +613,7 @@ mod tests { mode_leaf_helper, Duration::from_millis(100), health_table.clone(), + event_tx, ); Self { assembly_mode_request_tx, @@ -603,6 +621,7 @@ mod tests { shared_switch_set, switch_rx, health_table, + event_rx, handler, tm_rx, tc_tx, @@ -685,6 +704,13 @@ mod tests { postcard::from_bytes::(&tm_packet.payload) .expect("failed to deserialize mode reply"); matches!(response, types::acs::mgm::response::Response::Ok); + + let (sender_id, event) = testbench + .event_rx + .try_recv() + .expect("expected mode changed event"); + assert_eq!(sender_id, ComponentId::AcsMgm0); + assert!(matches!(event, mgm::Event::ModeChanged(DeviceMode::Normal))); // The device should have been polled once. assert_eq!(testbench.test_spi_interface().call_count, 1); let mgm_set = *testbench.handler.shared_mgm_set.lock().unwrap(); @@ -871,6 +897,11 @@ mod tests { fn test_spi_fault_above_threshold_marks_component_faulty() { let mut testbench = MgmTestbench::new(); testbench.switch_to_normal(); + // Drain the mode changed event emitted by switch_to_normal(). + testbench + .event_rx + .try_recv() + .expect("expected mode changed event"); testbench.test_spi_interface().next_mgm_data = MgmLis3RawValues { x: -1, y: -1, @@ -885,6 +916,9 @@ mod tests { Some(HealthState::Faulty) ); assert!(!testbench.handler.shared_mgm_set.lock().unwrap().valid); + let (sender_id, event) = testbench.event_rx.try_recv().expect("expected fault event"); + assert_eq!(sender_id, ComponentId::AcsMgm0); + assert!(matches!(event, mgm::Event::SpiFaultThresholdExceeded)); } #[test] diff --git a/satrs-example/src/acs/mgm_assembly.rs b/satrs-example/src/acs/mgm_assembly.rs index 6a0af15..98b336d 100644 --- a/satrs-example/src/acs/mgm_assembly.rs +++ b/satrs-example/src/acs/mgm_assembly.rs @@ -44,6 +44,7 @@ pub struct Assembly { mgm_modes: [MgmInfo; 2], parent_queues: ParentQueueHelper, pub(crate) children_queues: ChildrenQueueHelper, + event_tx: mpsc::SyncSender, } impl Assembly { @@ -54,6 +55,7 @@ impl Assembly { children_queues: ChildrenQueueHelper, tmtc_queues: TmtcQueues, mode_timeout: Duration, + event_tx: mpsc::SyncSender, ) -> Self { Self { mode_helper: ModeHelper::new(Mode::NoModeKeeping, mode_timeout), @@ -62,6 +64,7 @@ impl Assembly { mgm_modes: [MgmInfo::default(); 2], parent_queues, children_queues, + event_tx, } } @@ -295,12 +298,19 @@ impl Assembly { } fn announce_mode(&self) { - // TODO: Event? log::info!( "{:?} announcing mode: {:?}", Self::ID, self.mode_helper.current ); + if let Err(e) = self + .event_tx + .send(types::acs::mgm_assembly::Event::ModeChanged( + self.mode_helper.current, + )) + { + log::warn!("{:?}: failed to send mode changed event: {}", Self::ID, e); + } } #[inline] @@ -336,6 +346,7 @@ mod tests { mgm_report_tx: [mpsc::SyncSender; 2], tc_tx: mpsc::SyncSender, tm_rx: mpsc::Receiver, + event_rx: mpsc::Receiver, assembly: Assembly, } @@ -351,6 +362,7 @@ mod tests { let (tc_tx, tc_rx) = mpsc::sync_channel(5); let (tm_tx, tm_rx) = mpsc::sync_channel(5); + let (event_tx, event_rx) = mpsc::sync_channel(5); Self { subsystem_req_tx, @@ -359,6 +371,7 @@ mod tests { mgm_report_tx: [mgm_0_mode_report_tx, mgm_1_mode_report_tx], tc_tx, tm_rx, + event_rx, assembly: Assembly::new( ParentQueueHelper { request_rx: subsystem_req_rx, @@ -370,6 +383,7 @@ mod tests { }, TmtcQueues { tc_rx, tm_tx }, Duration::from_millis(20), + event_tx, ), } } @@ -450,6 +464,12 @@ mod tests { 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); + + let event = tb.event_rx.try_recv().expect("expected mode changed event"); + assert!(matches!( + event, + mgm_assembly::Event::ModeChanged(Mode::Device(DeviceMode::Normal)) + )); } #[test] diff --git a/satrs-example/src/eps/pcdu.rs b/satrs-example/src/eps/pcdu.rs index 41190cc..d9e8842 100644 --- a/satrs-example/src/eps/pcdu.rs +++ b/satrs-example/src/eps/pcdu.rs @@ -271,6 +271,7 @@ pub struct PcduHandler { shared_switch_map: Arc>, mode: DeviceMode, stamp_helper: TimestampHelper, + event_tx: mpsc::SyncSender, } impl PcduHandler { @@ -281,6 +282,7 @@ impl PcduHandler { com_interface: ComInterface, shared_switch_map: Arc>, init_mode: DeviceMode, + event_tx: mpsc::SyncSender, ) -> Self { Self { dev_str: "PCDU", @@ -292,6 +294,7 @@ impl PcduHandler { stamp_helper: TimestampHelper::default(), // Start in normal mode by default. Assume that the PCDU itself is on by default. mode: init_mode, + event_tx, } } @@ -437,6 +440,9 @@ impl PcduHandler { let pcdu_req_ser = serde_json::to_string(&pcdu_req).unwrap(); if let Err(_e) = self.com_interface.send(pcdu_req_ser.as_bytes()) { log::warn!("polling PCDU switch info failed"); + if let Err(e) = self.event_tx.send(pcdu::Event::SerialCommError) { + log::warn!("failed to send comm error event: {}", e); + } } } @@ -551,12 +557,17 @@ mod tests { pub inner: SerialInterfaceDummy, pub send_queue: RefCell>>, pub reply_queue: RefCell>, + /// Makes the next `send` call fail, to exercise comm-error handling. + pub fail_next_send: RefCell, } impl SerialInterface for SerialInterfaceTest { type Error = (); fn send(&self, data: &[u8]) -> Result<(), Self::Error> { + if self.fail_next_send.replace(false) { + return Err(()); + } let mut send_queue_mut = self.send_queue.borrow_mut(); send_queue_mut.push_back(data.to_vec()); self.inner.send(data) @@ -588,6 +599,7 @@ mod tests { pub tc_tx: mpsc::SyncSender, pub tm_rx: mpsc::Receiver, pub switch_request_tx: mpsc::Sender, + pub event_rx: mpsc::Receiver, pub handler: PcduHandler, } @@ -598,6 +610,7 @@ mod tests { let (tc_tx, tc_rx) = mpsc::sync_channel(5); let (tm_tx, tm_rx) = mpsc::sync_channel(5); let (switch_request_tx, switch_reqest_rx) = mpsc::channel(); + let (event_tx, event_rx) = mpsc::sync_channel(5); let shared_switch_map = Arc::new(Mutex::new(SwitchSet::new_with_init_switches_unknown())); let handler = PcduHandler::new( @@ -607,6 +620,7 @@ mod tests { SerialInterfaceTest::default(), shared_switch_map, DeviceMode::Off, + event_tx, ); Self { mode_request_tx, @@ -614,6 +628,7 @@ mod tests { tc_tx, tm_rx, switch_request_tx, + event_rx, handler, } } @@ -664,6 +679,18 @@ mod tests { } } + #[test] + fn test_periodic_command_send_failure_sends_event() { + let testbench = PcduTestbench::new(); + *testbench.handler.com_interface.fail_next_send.borrow_mut() = true; + testbench.handler.handle_periodic_commands(); + let event = testbench + .event_rx + .try_recv() + .expect("expected comm error event"); + assert!(matches!(event, pcdu::Event::SerialCommError)); + } + #[test] fn test_basic_handler() { let mut testbench = PcduTestbench::new(); diff --git a/satrs-example/src/event_manager.rs b/satrs-example/src/event_manager.rs index 9cd6b6f..ee965af 100644 --- a/satrs-example/src/event_manager.rs +++ b/satrs-example/src/event_manager.rs @@ -1,4 +1,9 @@ -use types::{ComponentId, Event, Message, ccsds::CcsdsTmPacketOwned, control}; +use types::{ + ComponentId, Event, Message, + acs::{mgm, mgm_assembly}, + ccsds::CcsdsTmPacketOwned, + control, pcdu, tmtc, +}; use crate::ccsds::pack_ccsds_tm_packet_for_now; @@ -6,14 +11,32 @@ use crate::ccsds::pack_ccsds_tm_packet_for_now; // event groups as well. pub struct EventManager { pub ctrl_rx: std::sync::mpsc::Receiver, + /// Shared by all MGM instances, which is why the sender ID is part of the message. + pub mgm_rx: std::sync::mpsc::Receiver<(ComponentId, mgm::Event)>, + pub mgm_assembly_rx: std::sync::mpsc::Receiver, + pub pcdu_rx: std::sync::mpsc::Receiver, + /// Shared by all TC sources, which is why the sender ID is part of the message. + pub tc_source_rx: std::sync::mpsc::Receiver<(ComponentId, tmtc::Event)>, pub tm_tx: std::sync::mpsc::SyncSender, } impl EventManager { pub fn periodic_operation(&mut self) { - if let Ok(event) = self.ctrl_rx.try_recv() { + while let Ok(event) = self.ctrl_rx.try_recv() { self.event_to_tm(ComponentId::Controller, &Event::ControllerEvent(event)); } + while let Ok((sender_id, event)) = self.mgm_rx.try_recv() { + self.event_to_tm(sender_id, &event); + } + while let Ok(event) = self.mgm_assembly_rx.try_recv() { + self.event_to_tm(ComponentId::AcsMgmAssembly, &event); + } + while let Ok(event) = self.pcdu_rx.try_recv() { + self.event_to_tm(ComponentId::EpsPcdu, &event); + } + while let Ok((sender_id, event)) = self.tc_source_rx.try_recv() { + self.event_to_tm(sender_id, &event); + } } pub fn event_to_tm( diff --git a/satrs-example/src/main.rs b/satrs-example/src/main.rs index 2d8386c..9834208 100644 --- a/satrs-example/src/main.rs +++ b/satrs-example/src/main.rs @@ -101,15 +101,23 @@ fn main() { let (pcdu_handler_mode_tx, _pcdu_handler_mode_rx) = mpsc::sync_channel(5); let (event_ctrl_tx, event_ctrl_rx) = mpsc::sync_channel(10); + let (mgm_event_tx, mgm_event_rx) = mpsc::sync_channel(10); + let (mgm_assembly_event_tx, mgm_assembly_event_rx) = mpsc::sync_channel(10); + let (pcdu_event_tx, pcdu_event_rx) = mpsc::sync_channel(10); + let (tc_source_event_tx, tc_source_event_rx) = mpsc::sync_channel(10); let mut event_manager = EventManager { ctrl_rx: event_ctrl_rx, + mgm_rx: mgm_event_rx, + mgm_assembly_rx: mgm_assembly_event_rx, + pcdu_rx: pcdu_event_rx, + tc_source_rx: tc_source_event_rx, tm_tx: tm_sink_tx.clone(), }; let mut controller = Controller::new(controller_tc_rx, tm_sink_tx.clone(), event_ctrl_tx); let ccsds_distributor = CcsdsDistributor::default(); - let mut tc_source = TcSourceTask::new(tc_source_rx, ccsds_distributor); + let mut tc_source = TcSourceTask::new(tc_source_rx, ccsds_distributor, tc_source_event_tx); tc_source.add_target(ComponentId::EpsPcdu, pcdu_handler_tc_tx); tc_source.add_target(ComponentId::Controller, controller_tc_tx); tc_source.add_target(ComponentId::AcsMgm0, mgm_0_handler_tc_tx); @@ -199,6 +207,7 @@ fn main() { }, Duration::from_millis(1000), health_table.clone(), + mgm_event_tx.clone(), ); let mut mgm_1_handler = mgm::MgmHandlerLis3Mdl::new( mgm::MgmId::_1, @@ -215,6 +224,7 @@ fn main() { }, Duration::from_millis(1000), health_table.clone(), + mgm_event_tx, ); let mut mgm_assembly = mgm_assembly::Assembly::new( mgm_assembly::ParentQueueHelper { @@ -230,6 +240,7 @@ fn main() { tm_tx: tm_sink_tx.clone(), }, Duration::from_millis(2000), + mgm_assembly_event_tx, ); let mut acs_controller = ctrl::Controller::new(ctrl::ModeLeafHelper { @@ -275,6 +286,7 @@ fn main() { pcdu_serial_interface, shared_switch_set, DeviceMode::Normal, + pcdu_event_tx, ); // The PCDU is a critical component which should be in normal mode immediately. diff --git a/satrs-example/src/tmtc/tc_source.rs b/satrs-example/src/tmtc/tc_source.rs index bfbde64..0be7329 100644 --- a/satrs-example/src/tmtc/tc_source.rs +++ b/satrs-example/src/tmtc/tc_source.rs @@ -1,5 +1,5 @@ use satrs::{ - HandlingStatus, + ComponentId as RawComponentId, HandlingStatus, spacepackets::{CcsdsPacketReader, ChecksumType}, tmtc::PacketAsVec, }; @@ -7,7 +7,7 @@ use std::{ collections::HashMap, sync::mpsc::{self, TryRecvError}, }; -use types::{ComponentId, TcHeader, ccsds::CcsdsTcPacketOwned}; +use types::{ComponentId, TcHeader, ccsds::CcsdsTcPacketOwned, tmtc}; pub type CcsdsDistributor = HashMap>; @@ -15,16 +15,19 @@ pub type CcsdsDistributor = HashMap, ccsds_distributor: CcsdsDistributor, + event_tx: mpsc::SyncSender<(ComponentId, tmtc::Event)>, } impl TcSourceTask { pub fn new( tc_receiver: mpsc::Receiver, ccsds_distributor: CcsdsDistributor, + event_tx: mpsc::SyncSender<(ComponentId, tmtc::Event)>, ) -> Self { Self { tc_receiver, ccsds_distributor, + event_tx, } } @@ -55,7 +58,7 @@ impl TcSourceTask { "received invalid CCSDS TC packet: {:?}", ccsds_tc_reader_result.err() ); - // TODO: Send a dedicated TM packet. + self.send_event(packet.sender_id, tmtc::Event::InvalidTcPacket); return HandlingStatus::HandledOne; } let ccsds_tc_reader = ccsds_tc_reader_result.unwrap(); @@ -66,7 +69,7 @@ impl TcSourceTask { "received CCSDS TC packet with invalid TC header: {:?}", tc_header_result.err() ); - // TODO: Send a dedicated TM packet. + self.send_event(packet.sender_id, tmtc::Event::InvalidTcHeader); return HandlingStatus::HandledOne; } let (tc_header, payload) = tc_header_result.unwrap(); @@ -81,7 +84,10 @@ impl TcSourceTask { .ok(); } else { log::warn!("no TC handler for target ID {:?}", tc_header.target_id); - // TODO: Send a dedicated TM packet. + self.send_event( + packet.sender_id, + tmtc::Event::UnknownTargetId(tc_header.target_id), + ); } HandlingStatus::HandledOne } @@ -94,4 +100,141 @@ impl TcSourceTask { }, } } + + /// `sender_id` is the raw ID tagged on the received packet, which is not necessarily a + /// known [ComponentId] (e.g. a spoofed or garbled packet). Falls back to [ComponentId::Ground] + /// as the event sender in that case. + fn send_event(&self, sender_id: RawComponentId, event: tmtc::Event) { + let sender_id = ComponentId::try_from(sender_id).unwrap_or_else(|_| { + log::warn!("TC source event for unknown raw sender ID {}", sender_id); + ComponentId::Ground + }); + if let Err(e) = self.event_tx.send((sender_id, event)) { + log::warn!("failed to send TC source event: {}", e); + } + } +} + +#[cfg(test)] +mod tests { + use std::sync::mpsc::TryRecvError; + + use arbitrary_int::u11; + use satrs::spacepackets::{ + CcsdsPacketCreatorOwned, ChecksumType, PacketType, SpacePacketHeader, + }; + use types::{Apid, MessageType, ccsds::CcsdsTcPacketOwned}; + + use super::*; + + struct Testbench { + tc_tx: mpsc::SyncSender, + target_rx: mpsc::Receiver, + event_rx: mpsc::Receiver<(ComponentId, tmtc::Event)>, + tc_source: TcSourceTask, + } + + impl Testbench { + fn new() -> Self { + let (tc_tx, tc_source_rx) = mpsc::sync_channel(5); + let (target_tx, target_rx) = mpsc::sync_channel(5); + let (event_tx, event_rx) = mpsc::sync_channel(5); + let mut tc_source = + TcSourceTask::new(tc_source_rx, CcsdsDistributor::default(), event_tx); + tc_source.add_target(ComponentId::EpsPcdu, target_tx); + Self { + tc_tx, + target_rx, + event_rx, + tc_source, + } + } + } + + fn valid_tc_raw(target_id: ComponentId) -> Vec { + CcsdsTcPacketOwned::new_with_request( + SpacePacketHeader::new_from_apid(u11::new(Apid::Acs as u16)), + TcHeader::new(target_id, MessageType::Ping), + (), + ) + .to_vec() + } + + /// A structurally valid CCSDS packet (correct length and CRC), but with arbitrary user data + /// instead of a postcard-encoded [TcHeader]. + fn raw_ccsds_tc_with_user_data(user_data: &[u8]) -> Vec { + CcsdsPacketCreatorOwned::new( + SpacePacketHeader::new_from_apid(u11::new(Apid::Acs as u16)), + PacketType::Tc, + user_data, + Some(ChecksumType::WithCrc16), + ) + .unwrap() + .to_vec() + } + + #[test] + fn test_valid_tc_is_routed_without_event() { + let mut tb = Testbench::new(); + tb.tc_tx + .send(PacketAsVec::new( + ComponentId::UdpServer as u32, + valid_tc_raw(ComponentId::EpsPcdu), + )) + .unwrap(); + tb.tc_source.periodic_operation(); + tb.target_rx.try_recv().expect("TC was not routed"); + assert!(matches!(tb.event_rx.try_recv(), Err(TryRecvError::Empty))); + } + + #[test] + fn test_invalid_ccsds_packet_sends_event() { + let mut tb = Testbench::new(); + tb.tc_tx + .send(PacketAsVec::new( + ComponentId::UdpServer as u32, + vec![1, 2, 3], + )) + .unwrap(); + tb.tc_source.periodic_operation(); + let (sender_id, event) = tb.event_rx.try_recv().expect("expected event"); + assert_eq!(sender_id, ComponentId::UdpServer); + assert!(matches!(event, tmtc::Event::InvalidTcPacket)); + } + + #[test] + fn test_invalid_tc_header_sends_event() { + let mut tb = Testbench::new(); + tb.tc_tx + .send(PacketAsVec::new( + ComponentId::UdpServer as u32, + // A single byte is enough to decode the `ComponentId` discriminant, but not + // enough for the trailing `MessageType`, so `TcHeader` deserialization fails + // while the CCSDS packet itself stays valid. + raw_ccsds_tc_with_user_data(&[0]), + )) + .unwrap(); + tb.tc_source.periodic_operation(); + let (sender_id, event) = tb.event_rx.try_recv().expect("expected event"); + assert_eq!(sender_id, ComponentId::UdpServer); + assert!(matches!(event, tmtc::Event::InvalidTcHeader)); + } + + #[test] + fn test_unknown_target_id_sends_event() { + let mut tb = Testbench::new(); + tb.tc_tx + .send(PacketAsVec::new( + ComponentId::UdpServer as u32, + valid_tc_raw(ComponentId::Ground), + )) + .unwrap(); + tb.tc_source.periodic_operation(); + let (sender_id, event) = tb.event_rx.try_recv().expect("expected event"); + assert_eq!(sender_id, ComponentId::UdpServer); + assert!(matches!( + event, + tmtc::Event::UnknownTargetId(ComponentId::Ground) + )); + } } diff --git a/satrs-example/types/src/acs/mgm.rs b/satrs-example/types/src/acs/mgm.rs index 1f6c147..011bc51 100644 --- a/satrs-example/types/src/acs/mgm.rs +++ b/satrs-example/types/src/acs/mgm.rs @@ -58,6 +58,21 @@ pub struct SensorData { pub z: f32, } +#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +pub enum Event { + /// The SPI fault counter exceeded its threshold, the component was marked faulty and + /// commanded off. + SpiFaultThresholdExceeded, + /// A commanded or autonomous mode transition completed. + ModeChanged(crate::DeviceMode), +} + +impl crate::Message for Event { + fn message_type(&self) -> crate::MessageType { + crate::MessageType::Event + } +} + pub mod response { use crate::{DeviceMode, Message, acs::mgm::SensorData}; diff --git a/satrs-example/types/src/acs/mgm_assembly.rs b/satrs-example/types/src/acs/mgm_assembly.rs index eb1f1fa..1587407 100644 --- a/satrs-example/types/src/acs/mgm_assembly.rs +++ b/satrs-example/types/src/acs/mgm_assembly.rs @@ -136,3 +136,15 @@ pub mod response { } } } + +#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +pub enum Event { + /// A commanded or autonomous mode transition completed. + ModeChanged(Mode), +} + +impl crate::Message for Event { + fn message_type(&self) -> crate::MessageType { + crate::MessageType::Event + } +} diff --git a/satrs-example/types/src/lib.rs b/satrs-example/types/src/lib.rs index 44c370f..a0298a2 100644 --- a/satrs-example/types/src/lib.rs +++ b/satrs-example/types/src/lib.rs @@ -10,6 +10,7 @@ pub mod acs; pub mod ccsds; pub mod control; pub mod pcdu; +pub mod tmtc; #[derive( Debug, diff --git a/satrs-example/types/src/pcdu.rs b/satrs-example/types/src/pcdu.rs index cc1f592..6c72a2d 100644 --- a/satrs-example/types/src/pcdu.rs +++ b/satrs-example/types/src/pcdu.rs @@ -56,6 +56,18 @@ pub enum SwitchStateBinary { On = 1, } +#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +pub enum Event { + /// Sending a request to the PCDU over the serial/simulator link failed. + SerialCommError, +} + +impl crate::Message for Event { + fn message_type(&self) -> crate::MessageType { + crate::MessageType::Event + } +} + pub type SwitchMapBinary = HashMap; pub struct SwitchMapBinaryWrapper(pub SwitchMapBinary); diff --git a/satrs-example/types/src/tmtc.rs b/satrs-example/types/src/tmtc.rs new file mode 100644 index 0000000..4ebca86 --- /dev/null +++ b/satrs-example/types/src/tmtc.rs @@ -0,0 +1,17 @@ +use crate::{ComponentId, Message}; + +#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +pub enum Event { + /// A received CCSDS packet failed CRC or basic format validation. + InvalidTcPacket, + /// The CCSDS packet was valid, but its embedded TC header could not be decoded. + InvalidTcHeader, + /// The TC header decoded fine, but no handler is registered for its target ID. + UnknownTargetId(ComponentId), +} + +impl Message for Event { + fn message_type(&self) -> crate::MessageType { + crate::MessageType::Event + } +}