add granular event management control
This commit is contained in:
@@ -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<u16>,
|
||||
}
|
||||
|
||||
/// Components which emit events.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, clap::ValueEnum)]
|
||||
enum EventSenderSelect {
|
||||
Controller,
|
||||
Mgm0,
|
||||
Mgm1,
|
||||
MgmAssy,
|
||||
Pcdu,
|
||||
UdpServer,
|
||||
TcpServer,
|
||||
Ground,
|
||||
}
|
||||
|
||||
impl From<EventSenderSelect> 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::<types::event_manager::response::Response>(remainder);
|
||||
log::info!(
|
||||
"Received response from event manager: {:?}",
|
||||
response.unwrap()
|
||||
);
|
||||
}
|
||||
types::ComponentId::AcsController => todo!(),
|
||||
types::ComponentId::AcsMgt => todo!(),
|
||||
}
|
||||
|
||||
@@ -46,7 +46,11 @@ impl Controller {
|
||||
control::request::Request::Ping => self
|
||||
.send_telemetry(Some(tc_id), control::response::Response::Ok),
|
||||
control::request::Request::TestEvent => {
|
||||
self.event_ctrl_tx.send(control::Event::TestEvent).unwrap()
|
||||
self.event_ctrl_tx.send(control::Event::TestEvent).unwrap();
|
||||
self.send_telemetry(
|
||||
Some(tc_id),
|
||||
control::response::Response::Ok,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<CcsdsTcPacketOwned>,
|
||||
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)>,
|
||||
@@ -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<CcsdsTmPacketOwned>,
|
||||
/// Senders with all their event TM generation disabled.
|
||||
disabled_components: HashSet<ComponentId>,
|
||||
/// 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<CcsdsTcPacketOwned>,
|
||||
ctrl_rx: std::sync::mpsc::Receiver<control::Event>,
|
||||
mgm_rx: std::sync::mpsc::Receiver<(ComponentId, mgm::Event)>,
|
||||
mgm_assembly_rx: std::sync::mpsc::Receiver<mgm_assembly::Event>,
|
||||
pcdu_rx: std::sync::mpsc::Receiver<pcdu::Event>,
|
||||
tc_source_rx: std::sync::mpsc::Receiver<(ComponentId, tmtc::Event)>,
|
||||
tm_tx: std::sync::mpsc::SyncSender<CcsdsTmPacketOwned>,
|
||||
) -> 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,237 @@ 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::<Request>(&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 + core::fmt::Debug),
|
||||
) {
|
||||
match pack_ccsds_tm_packet_for_now(sender_id, None, event) {
|
||||
let tm_enabled = self.tm_generation_enabled(sender_id, event.event_id());
|
||||
log::info!(
|
||||
"event {:?} (ID {}) from {:?}{}",
|
||||
event,
|
||||
event.event_id(),
|
||||
sender_id,
|
||||
if tm_enabled { "" } else { ", TM disabled" }
|
||||
);
|
||||
if !tm_enabled {
|
||||
return;
|
||||
}
|
||||
self.send_tm(sender_id, None, event);
|
||||
}
|
||||
|
||||
fn send_tm(
|
||||
&self,
|
||||
sender_id: ComponentId,
|
||||
tc_id: Option<CcsdsPacketIdAndPsc>,
|
||||
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<CcsdsTcPacketOwned>,
|
||||
mgm_event_tx: mpsc::SyncSender<(ComponentId, mgm::Event)>,
|
||||
tm_rx: mpsc::Receiver<CcsdsTmPacketOwned>,
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<SwitchId, SwitchStateBinary>;
|
||||
|
||||
pub struct SwitchMapBinaryWrapper(pub SwitchMapBinary);
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user