From d34635741409a43135f3c33bc0acf347f48f4e32 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 22 Sep 2026 13:16:31 +0200 Subject: [PATCH] add granular event management control --- satrs-example/client/src/main.rs | 95 ++++++- satrs-example/src/event_manager.rs | 283 +++++++++++++++++++- satrs-example/src/main.rs | 19 +- satrs-example/types/src/acs/mgm.rs | 10 +- satrs-example/types/src/acs/mgm_assembly.rs | 10 +- satrs-example/types/src/control.rs | 22 +- satrs-example/types/src/event_manager.rs | 34 +++ satrs-example/types/src/lib.rs | 17 ++ satrs-example/types/src/pcdu.rs | 10 +- satrs-example/types/src/tmtc.rs | 12 +- 10 files changed, 487 insertions(+), 25 deletions(-) create mode 100644 satrs-example/types/src/event_manager.rs diff --git a/satrs-example/client/src/main.rs b/satrs-example/client/src/main.rs index 4979574..f9c7bec 100644 --- a/satrs-example/client/src/main.rs +++ b/satrs-example/client/src/main.rs @@ -34,6 +34,58 @@ enum Commands { Mgm1(MgmArgs), MgmAssy(MgmAssemblyArgs), AcsSubsystem(SubsystemArgs), + EventManager(EventManagerArgs), +} + +#[derive(clap::Parser)] +struct EventManagerArgs { + #[command(subcommand)] + action: EventFilterAction, +} + +#[derive(clap::Subcommand)] +enum EventFilterAction { + /// Enable event TM generation. + Enable(EventFilterArgs), + /// Disable event TM generation. + Disable(EventFilterArgs), +} + +#[derive(clap::Args)] +struct EventFilterArgs { + #[arg(value_enum)] + component: EventSenderSelect, + /// Raw event ID. Without it, the filter applies to all events of the component. + #[arg(short, long)] + event_id: Option, +} + +/// Components which emit events. +#[derive(Debug, PartialEq, Eq, Clone, Copy, clap::ValueEnum)] +enum EventSenderSelect { + Controller, + Mgm0, + Mgm1, + MgmAssy, + Pcdu, + UdpServer, + TcpServer, + Ground, +} + +impl From for types::ComponentId { + fn from(sender: EventSenderSelect) -> Self { + match sender { + EventSenderSelect::Controller => types::ComponentId::Controller, + EventSenderSelect::Mgm0 => types::ComponentId::AcsMgm0, + EventSenderSelect::Mgm1 => types::ComponentId::AcsMgm1, + EventSenderSelect::MgmAssy => types::ComponentId::AcsMgmAssembly, + EventSenderSelect::Pcdu => types::ComponentId::EpsPcdu, + EventSenderSelect::UdpServer => types::ComponentId::UdpServer, + EventSenderSelect::TcpServer => types::ComponentId::TcpServer, + EventSenderSelect::Ground => types::ComponentId::Ground, + } + } } #[derive(Debug, PartialEq, Eq, Clone, Copy, clap::ValueEnum)] @@ -216,6 +268,39 @@ fn handle_mgm_command( Ok(()) } +fn handle_event_manager_command(client: &UdpSocket, addr: SocketAddr, args: EventManagerArgs) { + use types::event_manager::request::Request; + + let request = match args.action { + EventFilterAction::Enable(filter) => match filter.event_id { + Some(event_id) => Request::EnableEvent { + sender_id: filter.component.into(), + event_id, + }, + None => Request::EnableComponent(filter.component.into()), + }, + EventFilterAction::Disable(filter) => match filter.event_id { + Some(event_id) => Request::DisableEvent { + sender_id: filter.component.into(), + event_id, + }, + None => Request::DisableComponent(filter.component.into()), + }, + }; + let request_packet = types::ccsds::CcsdsTcPacketOwned::new_with_request( + SpacePacketHeader::new_from_apid(u11::new(Apid::Tmtc as u16)), + TcHeader::new(types::ComponentId::EventManager, MessageType::Event), + request, + ); + let sent_tc_id = CcsdsPacketIdAndPsc::new_from_ccsds_packet(&request_packet.sp_header); + log::info!( + "sending event manager request {:?} with TC ID {:#010x}", + request, + sent_tc_id.raw() + ); + client.send_to(&request_packet.to_vec(), addr).unwrap(); +} + fn setup_logger(level: log::LevelFilter) -> Result<(), fern::InitError> { fern::Dispatch::new() .format(|out, message, record| { @@ -366,6 +451,7 @@ fn main() -> anyhow::Result<()> { client.send_to(&request_packet, addr).unwrap(); } } + Commands::EventManager(args) => handle_event_manager_command(&client, addr, args), } } @@ -508,7 +594,14 @@ fn handle_raw_tm_packet(data: &[u8]) -> anyhow::Result<()> { types::ComponentId::UdpServer => todo!(), types::ComponentId::TcpServer => todo!(), types::ComponentId::Ground => todo!(), - types::ComponentId::EventManager => {} + types::ComponentId::EventManager => { + let response = + postcard::from_bytes::(remainder); + log::info!( + "Received response from event manager: {:?}", + response.unwrap() + ); + } types::ComponentId::AcsController => todo!(), types::ComponentId::AcsMgt => todo!(), } diff --git a/satrs-example/src/event_manager.rs b/satrs-example/src/event_manager.rs index ee965af..5397569 100644 --- a/satrs-example/src/event_manager.rs +++ b/satrs-example/src/event_manager.rs @@ -1,15 +1,19 @@ +use std::collections::HashSet; + +use satrs::spacepackets::CcsdsPacketIdAndPsc; use types::{ - ComponentId, Event, Message, + ComponentId, Event, EventId, Message, acs::{mgm, mgm_assembly}, - ccsds::CcsdsTmPacketOwned, - control, pcdu, tmtc, + ccsds::{CcsdsTcPacketOwned, CcsdsTmPacketOwned}, + control, + event_manager::{request::Request, response::Response}, + pcdu, tmtc, }; use crate::ccsds::pack_ccsds_tm_packet_for_now; -// TODO: We should add the capability to enable/disable the TM generation of individual events and -// event groups as well. pub struct EventManager { + pub tc_rx: std::sync::mpsc::Receiver, 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)>, @@ -18,10 +22,62 @@ pub struct EventManager { /// 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, + /// Senders with all their event TM generation disabled. + disabled_components: HashSet, + /// Individual (sender, event ID) pairs with their TM generation disabled. + disabled_events: HashSet<(ComponentId, u16)>, } impl EventManager { + pub fn new( + tc_rx: std::sync::mpsc::Receiver, + ctrl_rx: std::sync::mpsc::Receiver, + mgm_rx: std::sync::mpsc::Receiver<(ComponentId, mgm::Event)>, + mgm_assembly_rx: std::sync::mpsc::Receiver, + pcdu_rx: std::sync::mpsc::Receiver, + tc_source_rx: std::sync::mpsc::Receiver<(ComponentId, tmtc::Event)>, + tm_tx: std::sync::mpsc::SyncSender, + ) -> Self { + Self { + tc_rx, + ctrl_rx, + mgm_rx, + mgm_assembly_rx, + pcdu_rx, + tc_source_rx, + tm_tx, + disabled_components: HashSet::new(), + disabled_events: HashSet::new(), + } + } + + /// Silences all event TM generation for `sender_id`, regardless of the specific event. + fn disable_component(&mut self, sender_id: ComponentId) { + self.disabled_components.insert(sender_id); + } + + fn enable_component(&mut self, sender_id: ComponentId) { + self.disabled_components.remove(&sender_id); + } + + /// Silences TM generation for one specific event of `sender_id`, leaving its other events + /// unaffected. + fn disable_event(&mut self, sender_id: ComponentId, event_id: u16) { + self.disabled_events.insert((sender_id, event_id)); + } + + fn enable_event(&mut self, sender_id: ComponentId, event_id: u16) { + self.disabled_events.remove(&(sender_id, event_id)); + } + + fn tm_generation_enabled(&self, sender_id: ComponentId, event_id: u16) -> bool { + !self.disabled_components.contains(&sender_id) + && !self.disabled_events.contains(&(sender_id, event_id)) + } + pub fn periodic_operation(&mut self) { + // Telecommands first, so filter changes already apply to the events of this cycle. + self.handle_telecommands(); while let Ok(event) = self.ctrl_rx.try_recv() { self.event_to_tm(ComponentId::Controller, &Event::ControllerEvent(event)); } @@ -39,20 +95,229 @@ impl EventManager { } } + fn handle_telecommands(&mut self) { + while let Ok(packet) = self.tc_rx.try_recv() { + let tc_id = CcsdsPacketIdAndPsc::new_from_ccsds_packet(&packet.sp_header); + let request = match postcard::from_bytes::(&packet.payload) { + Ok(request) => request, + Err(e) => { + log::warn!("failed to deserialize event manager request: {}", e); + continue; + } + }; + log::info!( + "received request {:?} with TC ID {:#010x}", + request, + tc_id.raw() + ); + match request { + Request::EnableComponent(sender_id) => self.enable_component(sender_id), + Request::DisableComponent(sender_id) => self.disable_component(sender_id), + Request::EnableEvent { + sender_id, + event_id, + } => self.enable_event(sender_id, event_id), + Request::DisableEvent { + sender_id, + event_id, + } => self.disable_event(sender_id, event_id), + } + self.send_tm(ComponentId::EventManager, Some(tc_id), &Response::Ok); + } + } + pub fn event_to_tm( &mut self, sender_id: ComponentId, - event: &(impl serde::Serialize + Message), + event: &(impl serde::Serialize + Message + EventId), ) { - match pack_ccsds_tm_packet_for_now(sender_id, None, event) { + if !self.tm_generation_enabled(sender_id, event.event_id()) { + return; + } + self.send_tm(sender_id, None, event); + } + + fn send_tm( + &self, + sender_id: ComponentId, + tc_id: Option, + payload: &(impl serde::Serialize + Message), + ) { + match pack_ccsds_tm_packet_for_now(sender_id, tc_id, payload) { Ok(packet) => { if let Err(e) = self.tm_tx.send(packet) { - log::warn!("error sending event TM packet: {:?}", e); + log::warn!("error sending TM packet: {:?}", e); } } Err(e) => { - log::warn!("error packing event TM packet: {:?}", e); + log::warn!("error packing TM packet: {:?}", e); } } } } + +#[cfg(test)] +mod tests { + use std::sync::mpsc::{self, TryRecvError}; + + use arbitrary_int::u11; + use satrs::spacepackets::SpacePacketHeader; + use types::{Apid, MessageType, TcHeader, acs::mgm}; + + use super::*; + + struct Testbench { + tc_tx: mpsc::SyncSender, + mgm_event_tx: mpsc::SyncSender<(ComponentId, mgm::Event)>, + tm_rx: mpsc::Receiver, + event_manager: EventManager, + } + + impl Testbench { + fn new() -> Self { + let (tc_tx, tc_rx) = mpsc::sync_channel(5); + let (_ctrl_tx, ctrl_rx) = mpsc::sync_channel(5); + let (mgm_event_tx, mgm_rx) = mpsc::sync_channel(5); + let (_mgm_assembly_tx, mgm_assembly_rx) = mpsc::sync_channel(5); + let (_pcdu_tx, pcdu_rx) = mpsc::sync_channel(5); + let (_tc_source_tx, tc_source_rx) = mpsc::sync_channel(5); + let (tm_tx, tm_rx) = mpsc::sync_channel(5); + Self { + tc_tx, + mgm_event_tx, + tm_rx, + event_manager: EventManager::new( + tc_rx, + ctrl_rx, + mgm_rx, + mgm_assembly_rx, + pcdu_rx, + tc_source_rx, + tm_tx, + ), + } + } + + fn send_request(&self, request: Request) { + self.tc_tx + .send(CcsdsTcPacketOwned::new_with_request( + SpacePacketHeader::new_from_apid(u11::new(Apid::Tmtc as u16)), + TcHeader::new(ComponentId::EventManager, MessageType::Event), + request, + )) + .unwrap(); + } + } + + #[test] + fn test_events_enabled_by_default() { + let mut tb = Testbench::new(); + tb.mgm_event_tx + .send((ComponentId::AcsMgm0, mgm::Event::SpiFaultThresholdExceeded)) + .unwrap(); + tb.event_manager.periodic_operation(); + tb.tm_rx.try_recv().expect("expected event TM"); + } + + #[test] + fn test_disabled_component_silences_all_its_events() { + let mut tb = Testbench::new(); + tb.event_manager.disable_component(ComponentId::AcsMgm0); + tb.mgm_event_tx + .send((ComponentId::AcsMgm0, mgm::Event::SpiFaultThresholdExceeded)) + .unwrap(); + tb.mgm_event_tx + .send(( + ComponentId::AcsMgm0, + mgm::Event::ModeChanged(types::DeviceMode::Normal), + )) + .unwrap(); + tb.event_manager.periodic_operation(); + assert!(matches!(tb.tm_rx.try_recv(), Err(TryRecvError::Empty))); + } + + #[test] + fn test_disabled_component_does_not_affect_other_components() { + let mut tb = Testbench::new(); + tb.event_manager.disable_component(ComponentId::AcsMgm1); + tb.mgm_event_tx + .send((ComponentId::AcsMgm0, mgm::Event::SpiFaultThresholdExceeded)) + .unwrap(); + tb.event_manager.periodic_operation(); + tb.tm_rx.try_recv().expect("expected event TM"); + } + + #[test] + fn test_disabled_event_only_silences_that_specific_event() { + let mut tb = Testbench::new(); + tb.event_manager.disable_event( + ComponentId::AcsMgm0, + mgm::Event::SpiFaultThresholdExceeded.event_id(), + ); + tb.mgm_event_tx + .send((ComponentId::AcsMgm0, mgm::Event::SpiFaultThresholdExceeded)) + .unwrap(); + tb.mgm_event_tx + .send(( + ComponentId::AcsMgm0, + mgm::Event::ModeChanged(types::DeviceMode::Normal), + )) + .unwrap(); + tb.event_manager.periodic_operation(); + let tm = tb.tm_rx.try_recv().expect("expected event TM"); + let event: mgm::Event = postcard::from_bytes(&tm.payload).unwrap(); + assert!(matches!(event, mgm::Event::ModeChanged(_))); + assert!(matches!(tb.tm_rx.try_recv(), Err(TryRecvError::Empty))); + } + + #[test] + fn test_re_enabling_a_component_restores_its_events() { + let mut tb = Testbench::new(); + tb.event_manager.disable_component(ComponentId::AcsMgm0); + tb.event_manager.enable_component(ComponentId::AcsMgm0); + tb.mgm_event_tx + .send((ComponentId::AcsMgm0, mgm::Event::SpiFaultThresholdExceeded)) + .unwrap(); + tb.event_manager.periodic_operation(); + tb.tm_rx.try_recv().expect("expected event TM"); + } + + #[test] + fn test_disable_component_request() { + let mut tb = Testbench::new(); + tb.send_request(Request::DisableComponent(ComponentId::AcsMgm0)); + tb.mgm_event_tx + .send((ComponentId::AcsMgm0, mgm::Event::SpiFaultThresholdExceeded)) + .unwrap(); + tb.event_manager.periodic_operation(); + + let tm = tb.tm_rx.try_recv().expect("expected request response TM"); + assert_eq!(tm.tm_header.sender_id, ComponentId::EventManager); + assert!(tm.tm_header.tc_id.is_some()); + let response: Response = postcard::from_bytes(&tm.payload).unwrap(); + assert_eq!(response, Response::Ok); + assert!(matches!(tb.tm_rx.try_recv(), Err(TryRecvError::Empty))); + } + + #[test] + fn test_disable_event_request() { + let mut tb = Testbench::new(); + tb.send_request(Request::DisableEvent { + sender_id: ComponentId::AcsMgm0, + event_id: mgm::Event::SpiFaultThresholdExceeded.event_id(), + }); + tb.event_manager.periodic_operation(); + tb.tm_rx.try_recv().expect("expected request response TM"); + + tb.send_request(Request::EnableEvent { + sender_id: ComponentId::AcsMgm0, + event_id: mgm::Event::SpiFaultThresholdExceeded.event_id(), + }); + tb.mgm_event_tx + .send((ComponentId::AcsMgm0, mgm::Event::SpiFaultThresholdExceeded)) + .unwrap(); + tb.event_manager.periodic_operation(); + tb.tm_rx.try_recv().expect("expected request response TM"); + tb.tm_rx.try_recv().expect("expected event TM"); + } +} diff --git a/satrs-example/src/main.rs b/satrs-example/src/main.rs index 9834208..2adef6f 100644 --- a/satrs-example/src/main.rs +++ b/satrs-example/src/main.rs @@ -81,6 +81,7 @@ fn main() { let (acs_subsystem_tc_tx, acs_subsystem_tc_rx) = mpsc::sync_channel(10); let (pcdu_handler_tc_tx, pcdu_handler_tc_rx) = mpsc::sync_channel(30); let (controller_tc_tx, controller_tc_rx) = mpsc::sync_channel(10); + let (event_manager_tc_tx, event_manager_tc_rx) = mpsc::sync_channel(10); let (mgt_request_tx, mgt_request_rx) = mpsc::sync_channel(5); let (mgt_report_tx, mgt_report_rx) = mpsc::sync_channel(5); @@ -105,14 +106,15 @@ fn main() { 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 event_manager = EventManager::new( + event_manager_tc_rx, + event_ctrl_rx, + mgm_event_rx, + mgm_assembly_event_rx, + pcdu_event_rx, + tc_source_event_rx, + tm_sink_tx.clone(), + ); let mut controller = Controller::new(controller_tc_rx, tm_sink_tx.clone(), event_ctrl_tx); @@ -124,6 +126,7 @@ fn main() { tc_source.add_target(ComponentId::AcsMgm1, mgm_1_handler_tc_tx); tc_source.add_target(ComponentId::AcsMgmAssembly, mgm_assembly_tc_tx); tc_source.add_target(ComponentId::AcsSubsystem, acs_subsystem_tc_tx); + tc_source.add_target(ComponentId::EventManager, event_manager_tc_tx); let tc_sender = TmTcSender::Normal(tc_source_tx.clone()); let udp_tm_handler = UdpTmHandlerWithChannel { diff --git a/satrs-example/types/src/acs/mgm.rs b/satrs-example/types/src/acs/mgm.rs index 011bc51..2f266eb 100644 --- a/satrs-example/types/src/acs/mgm.rs +++ b/satrs-example/types/src/acs/mgm.rs @@ -58,7 +58,9 @@ pub struct SensorData { pub z: f32, } -#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +#[derive(strum::EnumDiscriminants, serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +#[strum_discriminants(derive(num_enum::IntoPrimitive))] +#[repr(u16)] pub enum Event { /// The SPI fault counter exceeded its threshold, the component was marked faulty and /// commanded off. @@ -73,6 +75,12 @@ impl crate::Message for Event { } } +impl crate::EventId for Event { + fn event_id(&self) -> u16 { + EventDiscriminants::from(self).into() + } +} + 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 1587407..0b890ba 100644 --- a/satrs-example/types/src/acs/mgm_assembly.rs +++ b/satrs-example/types/src/acs/mgm_assembly.rs @@ -137,7 +137,9 @@ pub mod response { } } -#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +#[derive(strum::EnumDiscriminants, serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +#[strum_discriminants(derive(num_enum::IntoPrimitive))] +#[repr(u16)] pub enum Event { /// A commanded or autonomous mode transition completed. ModeChanged(Mode), @@ -148,3 +150,9 @@ impl crate::Message for Event { crate::MessageType::Event } } + +impl crate::EventId for Event { + fn event_id(&self) -> u16 { + EventDiscriminants::from(self).into() + } +} diff --git a/satrs-example/types/src/control.rs b/satrs-example/types/src/control.rs index a56743f..dc447bc 100644 --- a/satrs-example/types/src/control.rs +++ b/satrs-example/types/src/control.rs @@ -1,6 +1,8 @@ -use crate::Message; +use crate::{EventId, Message}; -#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +#[derive(strum::EnumDiscriminants, serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +#[strum_discriminants(derive(num_enum::IntoPrimitive))] +#[repr(u16)] pub enum Event { TestEvent, } @@ -11,6 +13,12 @@ impl Message for Event { } } +impl EventId for Event { + fn event_id(&self) -> u16 { + EventDiscriminants::from(self).into() + } +} + pub mod request { #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] pub enum Request { @@ -37,3 +45,13 @@ pub mod response { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_event_id() { + assert_eq!(Event::TestEvent.event_id(), 0); + } +} diff --git a/satrs-example/types/src/event_manager.rs b/satrs-example/types/src/event_manager.rs new file mode 100644 index 0000000..a63a982 --- /dev/null +++ b/satrs-example/types/src/event_manager.rs @@ -0,0 +1,34 @@ +pub mod request { + use crate::ComponentId; + + /// Controls which events are converted to TM. Component and event filters are independent: + /// re-enabling a component does not re-enable its individually disabled events. + #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug, PartialEq, Eq)] + pub enum Request { + EnableComponent(ComponentId), + DisableComponent(ComponentId), + EnableEvent { + sender_id: ComponentId, + event_id: u16, + }, + DisableEvent { + sender_id: ComponentId, + event_id: u16, + }, + } +} + +pub mod response { + use crate::Message; + + #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug, PartialEq, Eq)] + pub enum Response { + Ok, + } + + impl Message for Response { + fn message_type(&self) -> crate::MessageType { + crate::MessageType::Verification + } + } +} diff --git a/satrs-example/types/src/lib.rs b/satrs-example/types/src/lib.rs index a0298a2..aedb2d8 100644 --- a/satrs-example/types/src/lib.rs +++ b/satrs-example/types/src/lib.rs @@ -9,6 +9,7 @@ use spacepackets::{ pub mod acs; pub mod ccsds; pub mod control; +pub mod event_manager; pub mod pcdu; pub mod tmtc; @@ -66,6 +67,14 @@ impl Message for Event { } } +impl EventId for Event { + fn event_id(&self) -> u16 { + match self { + Event::ControllerEvent(event) => event.event_id(), + } + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[non_exhaustive] pub struct TmHeader { @@ -158,6 +167,14 @@ pub trait Message { fn message_type(&self) -> MessageType; } +/// Stable, per-variant numeric identifier for an event, meant to be referenced from ground +/// (e.g. to enable/disable TM generation for one specific event) and to stay the same across +/// releases. Unlike [core::mem::discriminant], this can be constructed from a raw number +/// received in a telecommand. +pub trait EventId { + fn event_id(&self) -> u16; +} + /// Generic device mode which covers the requirements of most devices. /// /// The states are related both to the physical and the logical state of the device. Some diff --git a/satrs-example/types/src/pcdu.rs b/satrs-example/types/src/pcdu.rs index 6c72a2d..623774d 100644 --- a/satrs-example/types/src/pcdu.rs +++ b/satrs-example/types/src/pcdu.rs @@ -56,7 +56,9 @@ pub enum SwitchStateBinary { On = 1, } -#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +#[derive(strum::EnumDiscriminants, serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +#[strum_discriminants(derive(num_enum::IntoPrimitive))] +#[repr(u16)] pub enum Event { /// Sending a request to the PCDU over the serial/simulator link failed. SerialCommError, @@ -68,6 +70,12 @@ impl crate::Message for Event { } } +impl crate::EventId for Event { + fn event_id(&self) -> u16 { + EventDiscriminants::from(self).into() + } +} + 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 index 4ebca86..dc8a997 100644 --- a/satrs-example/types/src/tmtc.rs +++ b/satrs-example/types/src/tmtc.rs @@ -1,6 +1,8 @@ -use crate::{ComponentId, Message}; +use crate::{ComponentId, EventId, Message}; -#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +#[derive(strum::EnumDiscriminants, serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] +#[strum_discriminants(derive(num_enum::IntoPrimitive))] +#[repr(u16)] pub enum Event { /// A received CCSDS packet failed CRC or basic format validation. InvalidTcPacket, @@ -15,3 +17,9 @@ impl Message for Event { crate::MessageType::Event } } + +impl EventId for Event { + fn event_id(&self) -> u16 { + EventDiscriminants::from(self).into() + } +}