Merge pull request 'more events' (#281) from more-events into main

Reviewed-on: #281
This commit was merged in pull request #281.
This commit is contained in:
2026-09-22 11:54:28 +02:00
11 changed files with 329 additions and 13 deletions
+38 -4
View File
@@ -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<CcsdsTmPacketOwned>,
pub switch_rx: mpsc::Receiver<SwitchRequest>,
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::<types::acs::mgm::response::Response>(&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]
+21 -1
View File
@@ -44,6 +44,7 @@ pub struct Assembly {
mgm_modes: [MgmInfo; 2],
parent_queues: ParentQueueHelper,
pub(crate) children_queues: ChildrenQueueHelper,
event_tx: mpsc::SyncSender<types::acs::mgm_assembly::Event>,
}
impl Assembly {
@@ -54,6 +55,7 @@ impl Assembly {
children_queues: ChildrenQueueHelper,
tmtc_queues: TmtcQueues,
mode_timeout: Duration,
event_tx: mpsc::SyncSender<types::acs::mgm_assembly::Event>,
) -> 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<types::acs::mgm::response::ModeResponse>; 2],
tc_tx: mpsc::SyncSender<CcsdsTcPacketOwned>,
tm_rx: mpsc::Receiver<CcsdsTmPacketOwned>,
event_rx: mpsc::Receiver<mgm_assembly::Event>,
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]
+27
View File
@@ -271,6 +271,7 @@ pub struct PcduHandler<ComInterface: SerialInterface> {
shared_switch_map: Arc<Mutex<SwitchSet>>,
mode: DeviceMode,
stamp_helper: TimestampHelper,
event_tx: mpsc::SyncSender<pcdu::Event>,
}
impl<ComInterface: SerialInterface> PcduHandler<ComInterface> {
@@ -281,6 +282,7 @@ impl<ComInterface: SerialInterface> PcduHandler<ComInterface> {
com_interface: ComInterface,
shared_switch_map: Arc<Mutex<SwitchSet>>,
init_mode: DeviceMode,
event_tx: mpsc::SyncSender<pcdu::Event>,
) -> Self {
Self {
dev_str: "PCDU",
@@ -292,6 +294,7 @@ impl<ComInterface: SerialInterface> PcduHandler<ComInterface> {
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<ComInterface: SerialInterface> PcduHandler<ComInterface> {
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<VecDeque<Vec<u8>>>,
pub reply_queue: RefCell<VecDeque<String>>,
/// Makes the next `send` call fail, to exercise comm-error handling.
pub fail_next_send: RefCell<bool>,
}
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<CcsdsTcPacketOwned>,
pub tm_rx: mpsc::Receiver<CcsdsTmPacketOwned>,
pub switch_request_tx: mpsc::Sender<SwitchRequest>,
pub event_rx: mpsc::Receiver<pcdu::Event>,
pub handler: PcduHandler<SerialInterfaceTest>,
}
@@ -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();
+25 -2
View File
@@ -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<control::Event>,
/// 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<mgm_assembly::Event>,
pub pcdu_rx: std::sync::mpsc::Receiver<pcdu::Event>,
/// 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<CcsdsTmPacketOwned>,
}
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(
+13 -1
View File
@@ -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.
+148 -5
View File
@@ -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<ComponentId, std::sync::mpsc::SyncSender<CcsdsTcPacketOwned>>;
@@ -15,16 +15,19 @@ pub type CcsdsDistributor = HashMap<ComponentId, std::sync::mpsc::SyncSender<Ccs
pub struct TcSourceTask {
pub tc_receiver: mpsc::Receiver<PacketAsVec>,
ccsds_distributor: CcsdsDistributor,
event_tx: mpsc::SyncSender<(ComponentId, tmtc::Event)>,
}
impl TcSourceTask {
pub fn new(
tc_receiver: mpsc::Receiver<PacketAsVec>,
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<PacketAsVec>,
target_rx: mpsc::Receiver<CcsdsTcPacketOwned>,
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<u8> {
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<u8> {
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)
));
}
}
+15
View File
@@ -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};
@@ -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
}
}
+1
View File
@@ -10,6 +10,7 @@ pub mod acs;
pub mod ccsds;
pub mod control;
pub mod pcdu;
pub mod tmtc;
#[derive(
Debug,
+12
View File
@@ -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<SwitchId, SwitchStateBinary>;
pub struct SwitchMapBinaryWrapper(pub SwitchMapBinary);
+17
View File
@@ -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
}
}