From 5ec5124ea31a8a9fc164a8a04130b742c5d6bf60 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 24 Apr 2024 14:30:45 +0200 Subject: [PATCH 01/15] Updated events modules and docs --- satrs-book/src/events.md | 17 +-- satrs-example/pytmtc/common.py | 1 + satrs-example/pytmtc/main.py | 4 +- satrs-example/src/events.rs | 192 +++++++++++++++--------------- satrs-example/src/main.rs | 49 ++++---- satrs-example/src/pus/test.rs | 6 +- satrs-example/src/tmtc/tm_sink.rs | 15 ++- satrs/CHANGELOG.md | 10 ++ satrs/src/event_man.rs | 73 ++++++------ satrs/src/pus/event_man.rs | 98 ++++++++------- satrs/tests/pus_events.rs | 24 ++-- 11 files changed, 257 insertions(+), 232 deletions(-) diff --git a/satrs-book/src/events.md b/satrs-book/src/events.md index 01dbb59..e21aac9 100644 --- a/satrs-book/src/events.md +++ b/satrs-book/src/events.md @@ -1,14 +1,17 @@ # Events -Events can be an extremely important mechanism used for remote systems to monitor unexpected -or expected anomalies and events occuring on these systems. They are oftentimes tied to +Events are an important mechanism used for remote systems to monitor unexpected +or expected anomalies and events occuring on these systems. + +One common use case for events on remote systems is to offer a light-weight publish-subscribe +mechanism and IPC mechanism for software and hardware events which are also packaged as telemetry +(TM) or can trigger a system response. They can also be tied to Fault Detection, Isolation and Recovery (FDIR) operations, which need to happen autonomously. -Events can also be used as a convenient Inter-Process Communication (IPC) mechansism, which is -also observable for the Ground segment. The PUS Service 5 standardizes how the ground interface -for events might look like, but does not specify how other software components might react -to those events. There is the PUS Service 19, which might be used for that purpose, but the -event components recommended by this framework do not really need this service. +The PUS Service 5 standardizes how the ground interface for events might look like, but does not +specify how other software components might react to those events. There is the PUS Service 19, +which might be used for that purpose, but the event components recommended by this framework do not +rely on the present of this service. The following images shows how the flow of events could look like in a system where components can generate events, and where other system components might be interested in those events: diff --git a/satrs-example/pytmtc/common.py b/satrs-example/pytmtc/common.py index 6f56604..a37967e 100644 --- a/satrs-example/pytmtc/common.py +++ b/satrs-example/pytmtc/common.py @@ -10,6 +10,7 @@ class Apid(enum.IntEnum): GENERIC_PUS = 2 ACS = 3 CFDP = 4 + TMTC = 5 class EventSeverity(enum.IntEnum): diff --git a/satrs-example/pytmtc/main.py b/satrs-example/pytmtc/main.py index 23f10b0..a90a011 100755 --- a/satrs-example/pytmtc/main.py +++ b/satrs-example/pytmtc/main.py @@ -144,7 +144,9 @@ class PusHandler(GenericApidHandlerBase): ) src_data = tm_packet.source_data event_u32 = EventU32.unpack(src_data) - _LOGGER.info(f"Received event packet. Event: {event_u32}") + _LOGGER.info( + f"Received event packet. Source APID: {Apid(tm_packet.apid)!r}, Event: {event_u32}" + ) if event_u32.group_id == 0 and event_u32.unique_id == 0: _LOGGER.info("Received test event") elif service == 17: diff --git a/satrs-example/src/events.rs b/satrs-example/src/events.rs index 5d1bdaf..bed3f6f 100644 --- a/satrs-example/src/events.rs +++ b/satrs-example/src/events.rs @@ -8,13 +8,10 @@ use satrs::pus::verification::VerificationReporter; use satrs::pus::EcssTmSender; use satrs::request::UniqueApidTargetId; use satrs::{ - event_man::{ - EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded, - MpscEventReceiver, - }, + event_man::{EventManagerWithBoundedMpsc, EventSendProvider, EventU32SenderMpscBounded}, pus::{ event_man::{ - DefaultPusEventU32Dispatcher, EventReporter, EventRequest, EventRequestWithToken, + DefaultPusEventU32TmCreator, EventReporter, EventRequest, EventRequestWithToken, }, verification::{TcStateStarted, VerificationReportingProvider, VerificationToken}, }, @@ -40,13 +37,12 @@ impl EventTmHookProvider for EventApidSetter { /// packets. It also handles the verification completion of PUS event service requests. pub struct PusEventHandler { event_request_rx: mpsc::Receiver, - pus_event_dispatcher: DefaultPusEventU32Dispatcher<()>, + pus_event_tm_creator: DefaultPusEventU32TmCreator, pus_event_man_rx: mpsc::Receiver, tm_sender: TmSender, time_provider: CdsTime, timestamp: [u8; 7], verif_handler: VerificationReporter, - event_apid_setter: EventApidSetter, } impl PusEventHandler { @@ -61,9 +57,16 @@ impl PusEventHandler { // All events sent to the manager are routed to the PUS event manager, which generates PUS event // telemetry for each event. - let event_reporter = EventReporter::new(PUS_EVENT_MANAGEMENT.raw(), 0, 0, 128).unwrap(); + let event_reporter = EventReporter::new_with_hook( + PUS_EVENT_MANAGEMENT.raw(), + 0, + 0, + 128, + EventApidSetter::default(), + ) + .unwrap(); let pus_event_dispatcher = - DefaultPusEventU32Dispatcher::new_with_default_backend(event_reporter); + DefaultPusEventU32TmCreator::new_with_default_backend(event_reporter); let pus_event_man_send_provider = EventU32SenderMpscBounded::new( PUS_EVENT_MANAGEMENT.raw(), pus_event_man_tx, @@ -75,13 +78,12 @@ impl PusEventHandler { Self { event_request_rx, - pus_event_dispatcher, + pus_event_tm_creator: pus_event_dispatcher, pus_event_man_rx, time_provider: CdsTime::new_with_u16_days(0, 0), timestamp: [0; 7], verif_handler, tm_sender, - event_apid_setter: EventApidSetter::default(), } } @@ -95,75 +97,105 @@ impl PusEventHandler { .completion_success(&self.tm_sender, started_token, timestamp) .expect("Sending completion success failed"); }; - // handle event requests - if let Ok(event_req) = self.event_request_rx.try_recv() { - match event_req.request { - EventRequest::Enable(event) => { - self.pus_event_dispatcher - .enable_tm_for_event(&event) - .expect("Enabling TM failed"); - update_time(&mut self.time_provider, &mut self.timestamp); - report_completion(event_req, &self.timestamp); - } - EventRequest::Disable(event) => { - self.pus_event_dispatcher - .disable_tm_for_event(&event) - .expect("Disabling TM failed"); - update_time(&mut self.time_provider, &mut self.timestamp); - report_completion(event_req, &self.timestamp); - } + loop { + // handle event requests + match self.event_request_rx.try_recv() { + Ok(event_req) => match event_req.request { + EventRequest::Enable(event) => { + self.pus_event_tm_creator + .enable_tm_for_event(&event) + .expect("Enabling TM failed"); + update_time(&mut self.time_provider, &mut self.timestamp); + report_completion(event_req, &self.timestamp); + } + EventRequest::Disable(event) => { + self.pus_event_tm_creator + .disable_tm_for_event(&event) + .expect("Disabling TM failed"); + update_time(&mut self.time_provider, &mut self.timestamp); + report_completion(event_req, &self.timestamp); + } + }, + Err(e) => match e { + mpsc::TryRecvError::Empty => break, + mpsc::TryRecvError::Disconnected => { + log::warn!("all event request senders have disconnected"); + break; + } + }, } } } pub fn generate_pus_event_tm(&mut self) { - // Perform the generation of PUS event packets - if let Ok(event_msg) = self.pus_event_man_rx.try_recv() { - update_time(&mut self.time_provider, &mut self.timestamp); - let param_vec = event_msg.params().map_or(Vec::new(), |param| { - param.to_vec().expect("failed to convert params to vec") - }); - self.event_apid_setter.next_apid = UniqueApidTargetId::from(event_msg.sender_id()).apid; - self.pus_event_dispatcher - .generate_pus_event_tm_generic( - &self.tm_sender, - &self.timestamp, - event_msg.event(), - Some(¶m_vec), - ) - .expect("Sending TM as event failed"); + loop { + // Perform the generation of PUS event packets + match self.pus_event_man_rx.try_recv() { + Ok(event_msg) => { + update_time(&mut self.time_provider, &mut self.timestamp); + let param_vec = event_msg.params().map_or(Vec::new(), |param| { + param.to_vec().expect("failed to convert params to vec") + }); + // We use the TM modification hook to set the sender APID for each event. + self.pus_event_tm_creator.reporter.tm_hook.next_apid = + UniqueApidTargetId::from(event_msg.sender_id()).apid; + self.pus_event_tm_creator + .generate_pus_event_tm_generic( + &self.tm_sender, + &self.timestamp, + event_msg.event(), + Some(¶m_vec), + ) + .expect("Sending TM as event failed"); + } + Err(e) => match e { + mpsc::TryRecvError::Empty => break, + mpsc::TryRecvError::Disconnected => { + log::warn!("All event senders have disconnected"); + break; + } + }, + } } } } -/// This is a thin wrapper around the event manager which also caches the sender component -/// used to send events to the event manager. -pub struct EventManagerWrapper { +pub struct EventHandler { + pub pus_event_handler: PusEventHandler, event_manager: EventManagerWithBoundedMpsc, - event_sender: mpsc::Sender, } -impl EventManagerWrapper { - pub fn new() -> Self { - // The sender handle is the primary sender handle for all components which want to create events. - // The event manager will receive the RX handle to receive all the events. - let (event_sender, event_man_rx) = mpsc::channel(); - let event_recv = MpscEventReceiver::new(event_man_rx); +impl EventHandler { + pub fn new( + tm_sender: TmSender, + event_rx: mpsc::Receiver, + event_request_rx: mpsc::Receiver, + ) -> Self { + let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx); + let pus_event_handler = PusEventHandler::new( + tm_sender, + create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid), + &mut event_manager, + event_request_rx, + ); + Self { - event_manager: EventManagerWithBoundedMpsc::new(event_recv), - event_sender, + pus_event_handler, + event_manager, } } - // Returns a cached event sender to send events to the event manager for routing. - pub fn clone_event_sender(&self) -> mpsc::Sender { - self.event_sender.clone() - } - + #[allow(dead_code)] pub fn event_manager(&mut self) -> &mut EventManagerWithBoundedMpsc { &mut self.event_manager } + pub fn periodic_operation(&mut self) { + self.pus_event_handler.handle_event_requests(); + self.try_event_routing(); + self.pus_event_handler.generate_pus_event_tm(); + } + pub fn try_event_routing(&mut self) { let error_handler = |event_msg: &EventMessageU32, error: EventRoutingError| { self.routing_error_handler(event_msg, error) @@ -177,41 +209,5 @@ impl EventManagerWrapper { } } -pub struct EventHandler { - pub event_man_wrapper: EventManagerWrapper, - pub pus_event_handler: PusEventHandler, -} - -impl EventHandler { - pub fn new( - tm_sender: TmSender, - event_request_rx: mpsc::Receiver, - ) -> Self { - let mut event_man_wrapper = EventManagerWrapper::new(); - let pus_event_handler = PusEventHandler::new( - tm_sender, - create_verification_reporter(PUS_EVENT_MANAGEMENT.id(), PUS_EVENT_MANAGEMENT.apid), - event_man_wrapper.event_manager(), - event_request_rx, - ); - Self { - event_man_wrapper, - pus_event_handler, - } - } - - pub fn clone_event_sender(&self) -> mpsc::Sender { - self.event_man_wrapper.clone_event_sender() - } - - #[allow(dead_code)] - pub fn event_manager(&mut self) -> &mut EventManagerWithBoundedMpsc { - self.event_man_wrapper.event_manager() - } - - pub fn periodic_operation(&mut self) { - self.pus_event_handler.handle_event_requests(); - self.event_man_wrapper.try_event_routing(); - self.pus_event_handler.generate_pus_event_tm(); - } -} +#[cfg(test)] +mod tests {} diff --git a/satrs-example/src/main.rs b/satrs-example/src/main.rs index cf8e050..d6cf6ff 100644 --- a/satrs-example/src/main.rs +++ b/satrs-example/src/main.rs @@ -11,7 +11,7 @@ use crate::events::EventHandler; use crate::interface::udp::DynamicUdpTmHandler; use crate::pus::stack::PusStack; use crate::tmtc::tc_source::{TcSourceTaskDynamic, TcSourceTaskStatic}; -use crate::tmtc::tm_sink::{TmFunnelDynamic, TmFunnelStatic}; +use crate::tmtc::tm_sink::{TmSinkDynamic, TmSinkStatic}; use log::info; use pus::test::create_test_service_dynamic; use satrs::hal::std::tcp_server::ServerConfig; @@ -54,11 +54,11 @@ fn static_tmtc_pool_main() { let shared_tm_pool_wrapper = SharedPacketPool::new(&shared_tm_pool); let shared_tc_pool_wrapper = SharedPacketPool::new(&shared_tc_pool); let (tc_source_tx, tc_source_rx) = mpsc::sync_channel(50); - let (tm_funnel_tx, tm_funnel_rx) = mpsc::sync_channel(50); + let (tm_sink_tx, tm_sink_rx) = mpsc::sync_channel(50); let (tm_server_tx, tm_server_rx) = mpsc::sync_channel(50); - let tm_funnel_tx_sender = - PacketSenderWithSharedPool::new(tm_funnel_tx.clone(), shared_tm_pool_wrapper.clone()); + let tm_sink_tx_sender = + PacketSenderWithSharedPool::new(tm_sink_tx.clone(), shared_tm_pool_wrapper.clone()); let (mgm_handler_composite_tx, mgm_handler_composite_rx) = mpsc::channel::>(); @@ -80,11 +80,12 @@ fn static_tmtc_pool_main() { // Create event handling components // These sender handles are used to send event requests, for example to enable or disable // certain events. + let (event_tx, event_rx) = mpsc::sync_channel(100); let (event_request_tx, event_request_rx) = mpsc::channel::(); // The event task is the core handler to perform the event routing and TM handling as specified // in the sat-rs documentation. - let mut event_handler = EventHandler::new(tm_funnel_tx.clone(), event_request_rx); + let mut event_handler = EventHandler::new(tm_sink_tx.clone(), event_rx, event_request_rx); let (pus_test_tx, pus_test_rx) = mpsc::channel(); let (pus_event_tx, pus_event_rx) = mpsc::channel(); @@ -106,39 +107,39 @@ fn static_tmtc_pool_main() { mode_tc_sender: pus_mode_tx, }; let pus_test_service = create_test_service_static( - tm_funnel_tx_sender.clone(), + tm_sink_tx_sender.clone(), shared_tc_pool.clone(), - event_handler.clone_event_sender(), + event_tx.clone(), pus_test_rx, ); let pus_scheduler_service = create_scheduler_service_static( - tm_funnel_tx_sender.clone(), + tm_sink_tx_sender.clone(), tc_source.clone(), pus_sched_rx, create_sched_tc_pool(), ); let pus_event_service = create_event_service_static( - tm_funnel_tx_sender.clone(), + tm_sink_tx_sender.clone(), shared_tc_pool.clone(), pus_event_rx, event_request_tx, ); let pus_action_service = create_action_service_static( - tm_funnel_tx_sender.clone(), + tm_sink_tx_sender.clone(), shared_tc_pool.clone(), pus_action_rx, request_map.clone(), pus_action_reply_rx, ); let pus_hk_service = create_hk_service_static( - tm_funnel_tx_sender.clone(), + tm_sink_tx_sender.clone(), shared_tc_pool.clone(), pus_hk_rx, request_map.clone(), pus_hk_reply_rx, ); let pus_mode_service = create_mode_service_static( - tm_funnel_tx_sender.clone(), + tm_sink_tx_sender.clone(), shared_tc_pool.clone(), pus_mode_rx, request_map, @@ -156,7 +157,7 @@ fn static_tmtc_pool_main() { let mut tmtc_task = TcSourceTaskStatic::new( shared_tc_pool_wrapper.clone(), tc_source_rx, - PusTcDistributor::new(tm_funnel_tx_sender, pus_router), + PusTcDistributor::new(tm_sink_tx_sender, pus_router), ); let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT); @@ -186,10 +187,10 @@ fn static_tmtc_pool_main() { ) .expect("tcp server creation failed"); - let mut tm_funnel = TmFunnelStatic::new( + let mut tm_sink = TmSinkStatic::new( shared_tm_pool_wrapper, sync_tm_tcp_source, - tm_funnel_rx, + tm_sink_rx, tm_server_tx, ); @@ -209,7 +210,7 @@ fn static_tmtc_pool_main() { mode_leaf_interface, mgm_handler_composite_rx, pus_hk_reply_tx, - tm_funnel_tx, + tm_sink_tx, dummy_spi_interface, shared_mgm_set, ); @@ -240,9 +241,9 @@ fn static_tmtc_pool_main() { info!("Starting TM funnel task"); let jh_tm_funnel = thread::Builder::new() - .name("TM Funnel".to_string()) + .name("tm sink".to_string()) .spawn(move || loop { - tm_funnel.operation(); + tm_sink.operation(); }) .unwrap(); @@ -314,10 +315,11 @@ fn dyn_tmtc_pool_main() { // Create event handling components // These sender handles are used to send event requests, for example to enable or disable // certain events. + let (event_tx, event_rx) = mpsc::sync_channel(100); let (event_request_tx, event_request_rx) = mpsc::channel::(); // The event task is the core handler to perform the event routing and TM handling as specified // in the sat-rs documentation. - let mut event_handler = EventHandler::new(tm_funnel_tx.clone(), event_request_rx); + let mut event_handler = EventHandler::new(tm_funnel_tx.clone(), event_rx, event_request_rx); let (pus_test_tx, pus_test_rx) = mpsc::channel(); let (pus_event_tx, pus_event_rx) = mpsc::channel(); @@ -339,11 +341,8 @@ fn dyn_tmtc_pool_main() { mode_tc_sender: pus_mode_tx, }; - let pus_test_service = create_test_service_dynamic( - tm_funnel_tx.clone(), - event_handler.clone_event_sender(), - pus_test_rx, - ); + let pus_test_service = + create_test_service_dynamic(tm_funnel_tx.clone(), event_tx.clone(), pus_test_rx); let pus_scheduler_service = create_scheduler_service_dynamic( tm_funnel_tx.clone(), tc_source_tx.clone(), @@ -411,7 +410,7 @@ fn dyn_tmtc_pool_main() { ) .expect("tcp server creation failed"); - let mut tm_funnel = TmFunnelDynamic::new(sync_tm_tcp_source, tm_funnel_rx, tm_server_tx); + let mut tm_funnel = TmSinkDynamic::new(sync_tm_tcp_source, tm_funnel_rx, tm_server_tx); let (mgm_handler_mode_reply_to_parent_tx, _mgm_handler_mode_reply_to_parent_rx) = mpsc::channel(); diff --git a/satrs-example/src/pus/test.rs b/satrs-example/src/pus/test.rs index 583b72c..585e93b 100644 --- a/satrs-example/src/pus/test.rs +++ b/satrs-example/src/pus/test.rs @@ -23,7 +23,7 @@ use super::HandlingStatus; pub fn create_test_service_static( tm_sender: PacketSenderWithSharedPool, tc_pool: SharedStaticMemoryPool, - event_sender: mpsc::Sender, + event_sender: mpsc::SyncSender, pus_test_rx: mpsc::Receiver, ) -> TestCustomServiceWrapper { let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new( @@ -41,7 +41,7 @@ pub fn create_test_service_static( pub fn create_test_service_dynamic( tm_funnel_tx: mpsc::Sender, - event_sender: mpsc::Sender, + event_sender: mpsc::SyncSender, pus_test_rx: mpsc::Receiver, ) -> TestCustomServiceWrapper { let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new( @@ -61,7 +61,7 @@ pub struct TestCustomServiceWrapper, - pub test_srv_event_sender: mpsc::Sender, + pub test_srv_event_sender: mpsc::SyncSender, } impl diff --git a/satrs-example/src/tmtc/tm_sink.rs b/satrs-example/src/tmtc/tm_sink.rs index 955a997..0771a79 100644 --- a/satrs-example/src/tmtc/tm_sink.rs +++ b/satrs-example/src/tmtc/tm_sink.rs @@ -70,18 +70,23 @@ impl TmFunnelCommon { } fn packet_printout(tm: &PusTmZeroCopyWriter) { - info!("Sending PUS TM[{},{}]", tm.service(), tm.subservice()); + info!( + "Sending PUS TM[{},{}] with APID {}", + tm.service(), + tm.subservice(), + tm.apid() + ); } } -pub struct TmFunnelStatic { +pub struct TmSinkStatic { common: TmFunnelCommon, shared_tm_store: SharedPacketPool, tm_funnel_rx: mpsc::Receiver, tm_server_tx: mpsc::SyncSender, } -impl TmFunnelStatic { +impl TmSinkStatic { pub fn new( shared_tm_store: SharedPacketPool, sync_tm_tcp_source: SyncTcpTmSource, @@ -121,13 +126,13 @@ impl TmFunnelStatic { } } -pub struct TmFunnelDynamic { +pub struct TmSinkDynamic { common: TmFunnelCommon, tm_funnel_rx: mpsc::Receiver, tm_server_tx: mpsc::Sender, } -impl TmFunnelDynamic { +impl TmSinkDynamic { pub fn new( sync_tm_tcp_source: SyncTcpTmSource, tm_funnel_rx: mpsc::Receiver, diff --git a/satrs/CHANGELOG.md b/satrs/CHANGELOG.md index eeab142..4bdba43 100644 --- a/satrs/CHANGELOG.md +++ b/satrs/CHANGELOG.md @@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/). # [unreleased] +# [v0.2.0-rc.5] 2024-04-23 + +## Changed + +- Removed `MpscEventReceiver`, the `EventReceiveProvider` trait is implemented directly + on `mpsc::Receiver>` +- Renamed `PusEventDispatcher` to `PusEventTmCreatorWithMap`. +- Renamed `DefaultPusEventU32Dispatcher` to `DefaultPusEventU32EventCreator`. +- Renamed `PusEventMgmtBackendProvider` renamed to `PusEventReportingMap`. + # [v0.2.0-rc.4] 2024-04-23 ## Changed diff --git a/satrs/src/event_man.rs b/satrs/src/event_man.rs index 38752eb..8e53f43 100644 --- a/satrs/src/event_man.rs +++ b/satrs/src/event_man.rs @@ -1,14 +1,12 @@ //! Event management and forwarding //! -//! This module provides components to perform event routing. The most important component for this -//! task is the [EventManager]. It receives all events and then routes them to event subscribers -//! where appropriate. One common use case for satellite systems is to offer a light-weight -//! publish-subscribe mechanism and IPC mechanism for software and hardware events which are also -//! packaged as telemetry (TM) or can trigger a system response. -//! //! It is recommended to read the //! [sat-rs book chapter](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/book/events.html) -//! about events first: +//! about events first. +//! +//! This module provides components to perform event routing. The most important component for this +//! task is the [EventManager]. It receives all events and then routes them to event subscribers +//! where appropriate. //! //! The event manager has a listener table abstracted by the [ListenerMapProvider], which maps //! listener groups identified by [ListenerKey]s to a [listener ID][ComponentId]. @@ -21,8 +19,8 @@ //! //! 1. Provide a concrete [EventReceiveProvider] implementation. This abstraction allow to use different //! message queue backends. A straightforward implementation where dynamic memory allocation is -//! not a big concern could use [std::sync::mpsc::channel] to do this and is provided in -//! form of the [MpscEventReceiver]. +//! not a big concern would be to use the [std::sync::mpsc::Receiver] handle. The trait is +//! already implemented for this type. //! 2. To set up event creators, create channel pairs using some message queue implementation. //! Each event creator gets a (cloned) sender component which allows it to send events to the //! manager. @@ -44,6 +42,12 @@ //! You can check [integration test](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs/tests/pus_events.rs) //! for a concrete example using multi-threading where events are routed to //! different threads. +//! +//! The [satrs-example](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example) +//! also contains a full event manager instance and exposes a test event via the PUS test service. +//! The [PUS event](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example/src/pus/event.rs) +//! module and the generic [events module](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example/src/events.rs) +//! show how the event management modules can be integrated into a more complex software. use crate::events::{EventU16, EventU32, GenericEvent, LargestEventRaw, LargestGroupIdRaw}; use crate::params::Params; use crate::queue::GenericSendError; @@ -157,9 +161,10 @@ pub trait SenderMapProvider< /// * `SenderMap`: [SenderMapProvider] which maps channel IDs to send providers. /// * `ListenerMap`: [ListenerMapProvider] which maps listener keys to channel IDs. /// * `EventSender`: [EventSendProvider] contained within the sender map which sends the events. -/// * `Ev`: The event type. This type must implement the [GenericEvent]. Currently only [EventU32] +/// * `Event`: The event type. This type must implement the [GenericEvent]. Currently only [EventU32] /// and [EventU16] are supported. -/// * `Data`: Auxiliary data which is sent with the event to provide optional context information +/// * `ParamProvider`: Auxiliary data which is sent with the event to provide optional context +/// information pub struct EventManager< EventReceiver: EventReceiveProvider, SenderMap: SenderMapProvider, @@ -331,11 +336,11 @@ pub mod alloc_mod { /// Helper type which constrains the sender map and listener map generics to the [DefaultSenderMap] /// and the [DefaultListenerMap]. It uses regular mpsc channels as the message queue backend. - pub type EventManagerWithMpsc = EventManager< - MpscEventReceiver, - DefaultSenderMap, EV, AUX>, + pub type EventManagerWithMpsc = EventManager< + EventU32ReceiverMpsc, + DefaultSenderMap, Event, ParamProvider>, DefaultListenerMap, - EventSenderMpsc, + EventSenderMpsc, >; /// Helper type which constrains the sender map and listener map generics to the [DefaultSenderMap] @@ -343,7 +348,7 @@ pub mod alloc_mod { /// [bounded mpsc senders](https://doc.rust-lang.org/std/sync/mpsc/struct.SyncSender.html) as the /// message queue backend. pub type EventManagerWithBoundedMpsc = EventManager< - MpscEventReceiver, + EventU32ReceiverMpsc, DefaultSenderMap, Event, ParamProvider>, DefaultListenerMap, EventSenderMpscBounded, @@ -479,20 +484,16 @@ pub mod std_mod { use super::*; use std::sync::mpsc; - pub struct MpscEventReceiver { - receiver: mpsc::Receiver>, - } - - impl MpscEventReceiver { - pub fn new(receiver: mpsc::Receiver>) -> Self { - Self { receiver } - } - } - impl EventReceiveProvider for MpscEventReceiver { + impl + EventReceiveProvider + for mpsc::Receiver> + { type Error = GenericReceiveError; - fn try_recv_event(&self) -> Result>, Self::Error> { - match self.receiver.try_recv() { + fn try_recv_event( + &self, + ) -> Result>, Self::Error> { + match self.try_recv() { Ok(msg) => Ok(Some(msg)), Err(e) => match e { mpsc::TryRecvError::Empty => Ok(None), @@ -504,8 +505,10 @@ pub mod std_mod { } } - pub type MpscEventU32Receiver = MpscEventReceiver; - pub type MpscEventU16Receiver = MpscEventReceiver; + pub type EventU32ReceiverMpsc = + mpsc::Receiver>; + pub type EventU16ReceiverMpsc = + mpsc::Receiver>; /// Generic event sender which uses a regular [mpsc::Sender] as the messaging backend to /// send events. @@ -624,9 +627,8 @@ mod tests { } fn generic_event_man() -> (mpsc::Sender, EventManagerWithMpsc) { - let (event_sender, manager_queue) = mpsc::channel(); - let event_man_receiver = MpscEventReceiver::new(manager_queue); - (event_sender, EventManager::new(event_man_receiver)) + let (event_sender, event_receiver) = mpsc::channel(); + (event_sender, EventManager::new(event_receiver)) } #[test] @@ -793,9 +795,8 @@ mod tests { let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| { panic!("routing error occurred for event {:?}: {:?}", event_msg, e); }; - let (event_sender, manager_queue) = mpsc::channel(); - let event_man_receiver = MpscEventReceiver::new(manager_queue); - let mut event_man = EventManagerWithMpsc::new(event_man_receiver); + let (event_sender, event_receiver) = mpsc::channel(); + let mut event_man = EventManagerWithMpsc::new(event_receiver); let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap(); let event_1 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); let (event_0_tx_0, all_events_rx) = mpsc::channel(); diff --git a/satrs/src/pus/event_man.rs b/satrs/src/pus/event_man.rs index b8ddb6b..4a123f9 100644 --- a/satrs/src/pus/event_man.rs +++ b/satrs/src/pus/event_man.rs @@ -28,7 +28,7 @@ pub use heapless_mod::*; /// structure to track disabled events. A more primitive and embedded friendly /// solution could track this information in a static or pre-allocated list which contains /// the disabled events. -pub trait PusEventMgmtBackendProvider { +pub trait PusEventReportingMapProvider { type Error; fn event_enabled(&self, event: &Event) -> bool; @@ -56,7 +56,7 @@ pub mod heapless_mod { { } - impl PusEventMgmtBackendProvider + impl PusEventReportingMapProvider for HeaplessPusMgmtBackendProvider { type Error = (); @@ -105,20 +105,23 @@ impl From for EventManError { pub mod alloc_mod { use core::marker::PhantomData; - use crate::events::EventU16; + use crate::{ + events::EventU16, + pus::event::{DummyEventHook, EventTmHookProvider}, + }; use super::*; /// Default backend provider which uses a hash set as the event reporting status container - /// like mentioned in the example of the [PusEventMgmtBackendProvider] documentation. + /// like mentioned in the example of the [PusEventReportingMapProvider] documentation. /// /// This provider is a good option for host systems or larger embedded systems where /// the expected occasional memory allocation performed by the [HashSet] is not an issue. - pub struct DefaultPusEventMgmtBackend { + pub struct DefaultPusEventReportingMap { disabled: HashSet, } - impl Default for DefaultPusEventMgmtBackend { + impl Default for DefaultPusEventReportingMap { fn default() -> Self { Self { disabled: HashSet::default(), @@ -126,51 +129,54 @@ pub mod alloc_mod { } } - impl PusEventMgmtBackendProvider - for DefaultPusEventMgmtBackend + impl + PusEventReportingMapProvider for DefaultPusEventReportingMap { type Error = (); - fn event_enabled(&self, event: &EV) -> bool { + fn event_enabled(&self, event: &Event) -> bool { !self.disabled.contains(event) } - fn enable_event_reporting(&mut self, event: &EV) -> Result { + fn enable_event_reporting(&mut self, event: &Event) -> Result { Ok(self.disabled.remove(event)) } - fn disable_event_reporting(&mut self, event: &EV) -> Result { + fn disable_event_reporting(&mut self, event: &Event) -> Result { Ok(self.disabled.insert(*event)) } } - pub struct PusEventDispatcher< - B: PusEventMgmtBackendProvider, - EV: GenericEvent, - E, + pub struct PusEventTmCreatorWithMap< + ReportingMap: PusEventReportingMapProvider, + Event: GenericEvent, + EventTmHook: EventTmHookProvider = DummyEventHook, > { - reporter: EventReporter, - backend: B, - phantom: PhantomData<(E, EV)>, + pub reporter: EventReporter, + reporting_map: ReportingMap, + phantom: PhantomData, } - impl, Event: GenericEvent, E> - PusEventDispatcher + impl< + ReportingMap: PusEventReportingMapProvider, + Event: GenericEvent, + EventTmHook: EventTmHookProvider, + > PusEventTmCreatorWithMap { - pub fn new(reporter: EventReporter, backend: B) -> Self { + pub fn new(reporter: EventReporter, backend: ReportingMap) -> Self { Self { reporter, - backend, + reporting_map: backend, phantom: PhantomData, } } - pub fn enable_tm_for_event(&mut self, event: &Event) -> Result { - self.backend.enable_event_reporting(event) + pub fn enable_tm_for_event(&mut self, event: &Event) -> Result { + self.reporting_map.enable_event_reporting(event) } - pub fn disable_tm_for_event(&mut self, event: &Event) -> Result { - self.backend.disable_event_reporting(event) + pub fn disable_tm_for_event(&mut self, event: &Event) -> Result { + self.reporting_map.disable_event_reporting(event) } pub fn generate_pus_event_tm_generic( @@ -180,7 +186,7 @@ pub mod alloc_mod { event: Event, params: Option<&[u8]>, ) -> Result { - if !self.backend.event_enabled(&event) { + if !self.reporting_map.event_enabled(&event) { return Ok(false); } match event.severity() { @@ -208,31 +214,33 @@ pub mod alloc_mod { } } - impl - PusEventDispatcher, EV, ()> + impl + PusEventTmCreatorWithMap, Event, EventTmHook> { - pub fn new_with_default_backend(reporter: EventReporter) -> Self { + pub fn new_with_default_backend(reporter: EventReporter) -> Self { Self { reporter, - backend: DefaultPusEventMgmtBackend::default(), + reporting_map: DefaultPusEventReportingMap::default(), phantom: PhantomData, } } } - impl, E> PusEventDispatcher { + impl> + PusEventTmCreatorWithMap + { pub fn enable_tm_for_event_with_sev( &mut self, event: &EventU32TypedSev, - ) -> Result { - self.backend.enable_event_reporting(event.as_ref()) + ) -> Result { + self.reporting_map.enable_event_reporting(event.as_ref()) } pub fn disable_tm_for_event_with_sev( &mut self, event: &EventU32TypedSev, - ) -> Result { - self.backend.disable_event_reporting(event.as_ref()) + ) -> Result { + self.reporting_map.disable_event_reporting(event.as_ref()) } pub fn generate_pus_event_tm( @@ -246,10 +254,10 @@ pub mod alloc_mod { } } - pub type DefaultPusEventU16Dispatcher = - PusEventDispatcher, EventU16, E>; - pub type DefaultPusEventU32Dispatcher = - PusEventDispatcher, EventU32, E>; + pub type DefaultPusEventU16TmCreator = + PusEventTmCreatorWithMap, EventU16, EventTmHook>; + pub type DefaultPusEventU32TmCreator = + PusEventTmCreatorWithMap, EventU32, EventTmHook>; } #[cfg(test)] mod tests { @@ -265,16 +273,16 @@ mod tests { const TEST_APID: u16 = 0x02; const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05); - fn create_basic_man_1() -> DefaultPusEventU32Dispatcher<()> { + fn create_basic_man_1() -> DefaultPusEventU32TmCreator { let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128) .expect("Creating event repoter failed"); - PusEventDispatcher::new_with_default_backend(reporter) + PusEventTmCreatorWithMap::new_with_default_backend(reporter) } - fn create_basic_man_2() -> DefaultPusEventU32Dispatcher<()> { + fn create_basic_man_2() -> DefaultPusEventU32TmCreator { let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128) .expect("Creating event repoter failed"); - let backend = DefaultPusEventMgmtBackend::default(); - PusEventDispatcher::new(reporter, backend) + let backend = DefaultPusEventReportingMap::default(); + PusEventTmCreatorWithMap::new(reporter, backend) } #[test] diff --git a/satrs/tests/pus_events.rs b/satrs/tests/pus_events.rs index a5c3061..62bb8b0 100644 --- a/satrs/tests/pus_events.rs +++ b/satrs/tests/pus_events.rs @@ -1,11 +1,11 @@ use satrs::event_man::{ EventManagerWithMpsc, EventMessage, EventMessageU32, EventRoutingError, EventSendProvider, - EventU32SenderMpsc, MpscEventU32Receiver, + EventU32SenderMpsc, }; use satrs::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo}; use satrs::params::U32Pair; use satrs::params::{Params, ParamsHeapless, WritableToBeBytes}; -use satrs::pus::event_man::{DefaultPusEventMgmtBackend, EventReporter, PusEventDispatcher}; +use satrs::pus::event_man::{DefaultPusEventReportingMap, EventReporter, PusEventTmCreatorWithMap}; use satrs::pus::test_util::TEST_COMPONENT_ID_0; use satrs::request::UniqueApidTargetId; use satrs::tmtc::PacketAsVec; @@ -29,18 +29,18 @@ pub enum CustomTmSenderError { #[test] fn test_threaded_usage() { - let (event_sender, event_man_receiver) = mpsc::channel(); - let event_receiver = MpscEventU32Receiver::new(event_man_receiver); - let mut event_man = EventManagerWithMpsc::new(event_receiver); + let (event_tx, event_rx) = mpsc::sync_channel(100); + let mut event_man = EventManagerWithMpsc::new(event_rx); let (pus_event_man_tx, pus_event_man_rx) = mpsc::channel(); let pus_event_man_send_provider = EventU32SenderMpsc::new(1, pus_event_man_tx); event_man.subscribe_all(pus_event_man_send_provider.target_id()); event_man.add_sender(pus_event_man_send_provider); - let (event_tx, event_rx) = mpsc::channel::(); + let (event_packet_tx, event_packet_rx) = mpsc::channel::(); let reporter = EventReporter::new(TEST_ID.raw(), 0x02, 0, 128).expect("Creating event reporter failed"); - let pus_event_man = PusEventDispatcher::new(reporter, DefaultPusEventMgmtBackend::default()); + let pus_event_man = + PusEventTmCreatorWithMap::new(reporter, DefaultPusEventReportingMap::default()); let error_handler = |event_msg: &EventMessageU32, error: EventRoutingError| { panic!("received routing error for event {event_msg:?}: {error:?}"); }; @@ -54,7 +54,7 @@ fn test_threaded_usage() { Ok(event_msg) => { let gen_event = |aux_data| { pus_event_man.generate_pus_event_tm_generic( - &event_tx, + &event_packet_tx, &EMPTY_STAMP, event_msg.event(), aux_data, @@ -100,14 +100,14 @@ fn test_threaded_usage() { // Event sender and TM checker thread let jh1 = thread::spawn(move || { - event_sender + event_tx .send(EventMessage::new( TEST_COMPONENT_ID_0.id(), INFO_EVENT.into(), )) .expect("Sending info event failed"); loop { - match event_rx.try_recv() { + match event_packet_rx.try_recv() { // Event TM received successfully Ok(event_tm) => { let tm = PusTmReader::new(event_tm.packet.as_slice(), 7) @@ -129,7 +129,7 @@ fn test_threaded_usage() { } } } - event_sender + event_tx .send(EventMessage::new_with_params( TEST_COMPONENT_ID_0.id(), LOW_SEV_EVENT, @@ -137,7 +137,7 @@ fn test_threaded_usage() { )) .expect("Sending low severity event failed"); loop { - match event_rx.try_recv() { + match event_packet_rx.try_recv() { // Event TM received successfully Ok(event_tm) => { let tm = PusTmReader::new(event_tm.packet.as_slice(), 7) From b7ce039406077fae59a19ee0787683210919e345 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 24 Apr 2024 18:36:00 +0200 Subject: [PATCH 02/15] add optional defmt support for events --- satrs-example/src/config.rs | 3 +- satrs-example/src/events.rs | 45 ++++- satrs/CHANGELOG.md | 1 + satrs/Cargo.toml | 6 +- satrs/src/event_man.rs | 47 ++--- satrs/src/events.rs | 293 ++++++++++++++++-------------- satrs/src/pus/event.rs | 32 ++-- satrs/src/pus/event_man.rs | 13 +- satrs/src/pus/event_srv.rs | 2 +- satrs/tests/pus_autogen_events.rs | 8 +- satrs/tests/pus_events.rs | 5 +- 11 files changed, 259 insertions(+), 196 deletions(-) diff --git a/satrs-example/src/config.rs b/satrs-example/src/config.rs index 5168927..a8c495a 100644 --- a/satrs-example/src/config.rs +++ b/satrs-example/src/config.rs @@ -38,8 +38,7 @@ pub enum GroupId { pub const OBSW_SERVER_ADDR: Ipv4Addr = Ipv4Addr::UNSPECIFIED; pub const SERVER_PORT: u16 = 7301; -pub const TEST_EVENT: EventU32TypedSev = - EventU32TypedSev::::const_new(0, 0); +pub const TEST_EVENT: EventU32TypedSev = EventU32TypedSev::::new(0, 0); lazy_static! { pub static ref PACKET_ID_VALIDATOR: HashSet = { diff --git a/satrs-example/src/events.rs b/satrs-example/src/events.rs index bed3f6f..abfcb6b 100644 --- a/satrs-example/src/events.rs +++ b/satrs-example/src/events.rs @@ -210,4 +210,47 @@ impl EventHandler { } #[cfg(test)] -mod tests {} +mod tests { + use satrs::{ + events::EventU32, + pus::verification::VerificationReporterCfg, + spacepackets::{ecss::tm::PusTmReader, CcsdsPacket}, + tmtc::PacketAsVec, + }; + + use super::*; + + const TEST_CREATOR_ID: UniqueApidTargetId = UniqueApidTargetId::new(1, 2); + + #[test] + fn test_basic() { + let (event_tx, event_rx) = mpsc::sync_channel(10); + let (_event_req_tx, event_req_rx) = mpsc::sync_channel(10); + let (tm_sender, tm_receiver) = mpsc::channel(); + let verif_reporter_cfg = VerificationReporterCfg::new(0x05, 2, 2, 128).unwrap(); + let verif_reporter = + VerificationReporter::new(PUS_EVENT_MANAGEMENT.id(), &verif_reporter_cfg); + let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx); + let mut pus_event_handler = PusEventHandler::>::new( + tm_sender, + verif_reporter, + &mut event_manager, + event_req_rx, + ); + event_tx + .send(EventMessageU32::new( + TEST_CREATOR_ID.id(), + EventU32::new(satrs::events::Severity::Info, 1, 1), + )) + .expect("failed to send event"); + pus_event_handler.handle_event_requests(); + event_manager.try_event_handling(|_, _| {}); + pus_event_handler.generate_pus_event_tm(); + let tm_packet = tm_receiver.try_recv().expect("failed to receive TM packet"); + assert_eq!(tm_packet.sender_id, PUS_EVENT_MANAGEMENT.id()); + let tm_reader = PusTmReader::new(&tm_packet.packet, 7) + .expect("failed to create TM reader") + .0; + assert_eq!(tm_reader.apid(), TEST_CREATOR_ID.apid); + } +} diff --git a/satrs/CHANGELOG.md b/satrs/CHANGELOG.md index 4bdba43..33ba773 100644 --- a/satrs/CHANGELOG.md +++ b/satrs/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Renamed `PusEventDispatcher` to `PusEventTmCreatorWithMap`. - Renamed `DefaultPusEventU32Dispatcher` to `DefaultPusEventU32EventCreator`. - Renamed `PusEventMgmtBackendProvider` renamed to `PusEventReportingMap`. +- Reanmed Event `const_new` methods to `new` and the former `new` methods to `new_checked`. # [v0.2.0-rc.4] 2024-04-23 diff --git a/satrs/Cargo.toml b/satrs/Cargo.toml index dcf8d42..17627c7 100644 --- a/satrs/Cargo.toml +++ b/satrs/Cargo.toml @@ -84,6 +84,10 @@ version = "0.8" features = ["os-poll", "net"] optional = true +[dependencies.defmt] +version = "0.3" +optional = true + [dev-dependencies] serde = "1" zerocopy = "0.7" @@ -120,7 +124,7 @@ alloc = [ serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"] crossbeam = ["crossbeam-channel"] heapless = ["dep:heapless"] -defmt = ["spacepackets/defmt"] +defmt = ["dep:defmt", "spacepackets/defmt"] test_util = [] doc-images = [] diff --git a/satrs/src/event_man.rs b/satrs/src/event_man.rs index 8e53f43..bf33a68 100644 --- a/satrs/src/event_man.rs +++ b/satrs/src/event_man.rs @@ -295,7 +295,7 @@ impl< for id in ids { if let Some(sender) = self.sender_map.get_send_event_provider(id) { if let Err(e) = sender.send(EventMessage::new_generic( - *id, + event_msg.sender_id, event_msg.event, event_msg.params.as_ref(), )) { @@ -597,7 +597,7 @@ mod tests { use std::format; use std::sync::mpsc::{self}; - const TEST_EVENT: EventU32 = EventU32::const_new(Severity::INFO, 0, 5); + const TEST_EVENT: EventU32 = EventU32::new(Severity::Info, 0, 5); fn check_next_event( expected: EventU32, @@ -614,6 +614,7 @@ mod tests { res: EventRoutingResult, expected: EventU32, expected_num_sent: u32, + expected_sender_id: ComponentId, ) { assert!(matches!(res, EventRoutingResult::Handled { .. })); if let EventRoutingResult::Handled { @@ -622,6 +623,7 @@ mod tests { } = res { assert_eq!(event_msg.event, expected); + assert_eq!(event_msg.sender_id, expected_sender_id); assert_eq!(num_recipients, expected_num_sent); } } @@ -634,8 +636,8 @@ mod tests { #[test] fn test_basic() { let (event_sender, mut event_man) = generic_event_man(); - let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap(); - let event_grp_1_0 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); + let event_grp_0 = EventU32::new(Severity::Info, 0, 0); + let event_grp_1_0 = EventU32::new(Severity::High, 1, 0); let (single_event_sender, single_event_receiver) = mpsc::channel(); let single_event_listener = EventSenderMpsc::new(0, single_event_sender); event_man.subscribe_single(&event_grp_0, single_event_listener.target_id()); @@ -653,8 +655,7 @@ mod tests { .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_grp_0)) .expect("Sending single error failed"); let res = event_man.try_event_handling(&error_handler); - // assert!(res.is_ok()); - check_handled_event(res, event_grp_0, 1); + check_handled_event(res, event_grp_0, 1, TEST_COMPONENT_ID_0.id()); check_next_event(event_grp_0, &single_event_receiver); // Test event which is sent to all group listeners @@ -662,7 +663,7 @@ mod tests { .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0)) .expect("Sending group error failed"); let res = event_man.try_event_handling(&error_handler); - check_handled_event(res, event_grp_1_0, 1); + check_handled_event(res, event_grp_1_0, 1, TEST_COMPONENT_ID_1.id()); check_next_event(event_grp_1_0, &group_event_receiver_0); } @@ -672,7 +673,7 @@ mod tests { panic!("routing error occurred for event {:?}: {:?}", event_msg, e); }; let (event_sender, mut event_man) = generic_event_man(); - let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap(); + let event_grp_0 = EventU32::new(Severity::Info, 0, 0); let (single_event_sender, single_event_receiver) = mpsc::channel(); let single_event_listener = EventSenderMpsc::new(0, single_event_sender); event_man.subscribe_single(&event_grp_0, single_event_listener.target_id()); @@ -685,7 +686,7 @@ mod tests { )) .expect("Sending group error failed"); let res = event_man.try_event_handling(&error_handler); - check_handled_event(res, event_grp_0, 1); + check_handled_event(res, event_grp_0, 1, TEST_COMPONENT_ID_0.id()); let aux = check_next_event(event_grp_0, &single_event_receiver); assert!(aux.is_some()); let aux = aux.unwrap(); @@ -707,8 +708,8 @@ mod tests { let res = event_man.try_event_handling(error_handler); assert!(matches!(res, EventRoutingResult::Empty)); - let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap(); - let event_grp_1_0 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); + let event_grp_0 = EventU32::new(Severity::Info, 0, 0); + let event_grp_1_0 = EventU32::new(Severity::High, 1, 0); let (event_grp_0_sender, event_grp_0_receiver) = mpsc::channel(); let event_grp_0_and_1_listener = EventU32SenderMpsc::new(0, event_grp_0_sender); event_man.subscribe_group( @@ -728,9 +729,9 @@ mod tests { .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0)) .expect("Sendign Event Group 1 failed"); let res = event_man.try_event_handling(error_handler); - check_handled_event(res, event_grp_0, 1); + check_handled_event(res, event_grp_0, 1, TEST_COMPONENT_ID_0.id()); let res = event_man.try_event_handling(error_handler); - check_handled_event(res, event_grp_1_0, 1); + check_handled_event(res, event_grp_1_0, 1, TEST_COMPONENT_ID_1.id()); check_next_event(event_grp_0, &event_grp_0_receiver); check_next_event(event_grp_1_0, &event_grp_0_receiver); @@ -744,8 +745,8 @@ mod tests { panic!("routing error occurred for event {:?}: {:?}", event_msg, e); }; let (event_sender, mut event_man) = generic_event_man(); - let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap(); - let event_1 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); + let event_0 = EventU32::new(Severity::Info, 0, 5); + let event_1 = EventU32::new(Severity::High, 1, 0); let (event_0_tx_0, event_0_rx_0) = mpsc::channel(); let (event_0_tx_1, event_0_rx_1) = mpsc::channel(); let event_listener_0 = EventU32SenderMpsc::new(0, event_0_tx_0); @@ -760,7 +761,7 @@ mod tests { .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0)) .expect("Triggering Event 0 failed"); let res = event_man.try_event_handling(error_handler); - check_handled_event(res, event_0, 2); + check_handled_event(res, event_0, 2, TEST_COMPONENT_ID_0.id()); check_next_event(event_0, &event_0_rx_0); check_next_event(event_0, &event_0_rx_1); event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id); @@ -773,9 +774,9 @@ mod tests { // 3 Events messages will be sent now let res = event_man.try_event_handling(error_handler); - check_handled_event(res, event_0, 2); + check_handled_event(res, event_0, 2, TEST_COMPONENT_ID_0.id()); let res = event_man.try_event_handling(error_handler); - check_handled_event(res, event_1, 1); + check_handled_event(res, event_1, 1, TEST_COMPONENT_ID_1.id()); // Both the single event and the group event should arrive now check_next_event(event_0, &event_0_rx_0); check_next_event(event_1, &event_0_rx_0); @@ -787,7 +788,7 @@ mod tests { .send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_1)) .expect("Triggering Event 1 failed"); let res = event_man.try_event_handling(error_handler); - check_handled_event(res, event_1, 1); + check_handled_event(res, event_1, 1, TEST_COMPONENT_ID_0.id()); } #[test] @@ -797,8 +798,8 @@ mod tests { }; let (event_sender, event_receiver) = mpsc::channel(); let mut event_man = EventManagerWithMpsc::new(event_receiver); - let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap(); - let event_1 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); + let event_0 = EventU32::new(Severity::Info, 0, 5); + let event_1 = EventU32::new(Severity::High, 1, 0); let (event_0_tx_0, all_events_rx) = mpsc::channel(); let all_events_listener = EventU32SenderMpsc::new(0, event_0_tx_0); event_man.subscribe_all(all_events_listener.target_id()); @@ -810,9 +811,9 @@ mod tests { .send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_1)) .expect("Triggering event 1 failed"); let res = event_man.try_event_handling(error_handler); - check_handled_event(res, event_0, 1); + check_handled_event(res, event_0, 1, TEST_COMPONENT_ID_0.id()); let res = event_man.try_event_handling(error_handler); - check_handled_event(res, event_1, 1); + check_handled_event(res, event_1, 1, TEST_COMPONENT_ID_1.id()); check_next_event(event_0, &all_events_rx); check_next_event(event_1, &all_events_rx); } diff --git a/satrs/src/events.rs b/satrs/src/events.rs index 032322a..cb87a05 100644 --- a/satrs/src/events.rs +++ b/satrs/src/events.rs @@ -20,10 +20,10 @@ //! ``` //! use satrs::events::{EventU16, EventU32, EventU32TypedSev, Severity, SeverityHigh, SeverityInfo}; //! -//! const MSG_RECVD: EventU32TypedSev = EventU32TypedSev::const_new(1, 0); -//! const MSG_FAILED: EventU32 = EventU32::const_new(Severity::LOW, 1, 1); +//! const MSG_RECVD: EventU32TypedSev = EventU32TypedSev::new(1, 0); +//! const MSG_FAILED: EventU32 = EventU32::new(Severity::LOW, 1, 1); //! -//! const TEMPERATURE_HIGH: EventU32TypedSev = EventU32TypedSev::const_new(2, 0); +//! const TEMPERATURE_HIGH: EventU32TypedSev = EventU32TypedSev::new(2, 0); //! //! let small_event = EventU16::new(Severity::INFO, 3, 0); //! ``` @@ -40,12 +40,17 @@ pub type LargestEventRaw = u32; /// Using a type definition allows to change this to u32 in the future more easily pub type LargestGroupIdRaw = u16; +pub const MAX_GROUP_ID_U32_EVENT: u16 = 2_u16.pow(14) - 1; +pub const MAX_GROUP_ID_U16_EVENT: u16 = 2_u16.pow(6) - 1; + #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Severity { - INFO = 0, - LOW = 1, - MEDIUM = 2, - HIGH = 3, + Info = 0, + Low = 1, + Medium = 2, + High = 3, } pub trait HasSeverity: Debug + PartialEq + Eq + Copy + Clone { @@ -56,28 +61,28 @@ pub trait HasSeverity: Debug + PartialEq + Eq + Copy + Clone { #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct SeverityInfo {} impl HasSeverity for SeverityInfo { - const SEVERITY: Severity = Severity::INFO; + const SEVERITY: Severity = Severity::Info; } /// Type level support struct #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct SeverityLow {} impl HasSeverity for SeverityLow { - const SEVERITY: Severity = Severity::LOW; + const SEVERITY: Severity = Severity::Low; } /// Type level support struct #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct SeverityMedium {} impl HasSeverity for SeverityMedium { - const SEVERITY: Severity = Severity::MEDIUM; + const SEVERITY: Severity = Severity::Medium; } /// Type level support struct #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct SeverityHigh {} impl HasSeverity for SeverityHigh { - const SEVERITY: Severity = Severity::HIGH; + const SEVERITY: Severity = Severity::High; } pub trait GenericEvent: EcssEnumeration + Copy + Clone { @@ -99,27 +104,29 @@ impl TryFrom for Severity { fn try_from(value: u8) -> Result { match value { - x if x == Severity::INFO as u8 => Ok(Severity::INFO), - x if x == Severity::LOW as u8 => Ok(Severity::LOW), - x if x == Severity::MEDIUM as u8 => Ok(Severity::MEDIUM), - x if x == Severity::HIGH as u8 => Ok(Severity::HIGH), + x if x == Severity::Info as u8 => Ok(Severity::Info), + x if x == Severity::Low as u8 => Ok(Severity::Low), + x if x == Severity::Medium as u8 => Ok(Severity::Medium), + x if x == Severity::High as u8 => Ok(Severity::High), _ => Err(()), } } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -struct EventBase { +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +struct EventBase { severity: Severity, - group_id: GID, - unique_id: UID, - phantom: PhantomData, + group_id: GroupId, + unique_id: UniqueId, + phantom: PhantomData, } -impl EventBase { +impl EventBase { fn write_to_bytes( &self, - raw: RAW, + raw: Raw, buf: &mut [u8], width: usize, ) -> Result { @@ -267,6 +274,7 @@ macro_rules! const_from_fn { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct EventU32 { base: EventBase, } @@ -309,12 +317,12 @@ impl EventU32 { /// next 14 bits after the severity. Therefore, the size is limited by dec 16383 hex 0x3FFF. /// * `unique_id`: Each event has a unique 16 bit ID occupying the last 16 bits of the /// raw event ID - pub fn new( + pub fn new_checked( severity: Severity, group_id: ::GroupId, unique_id: ::UniqueId, ) -> Option { - if group_id > (2u16.pow(14) - 1) { + if group_id > MAX_GROUP_ID_U32_EVENT { return None; } Some(Self { @@ -326,12 +334,14 @@ impl EventU32 { }, }) } - pub const fn const_new( + + /// This constructor will panic if the passed group is is larger than [MAX_GROUP_ID_U32_EVENT]. + pub const fn new( severity: Severity, group_id: ::GroupId, unique_id: ::UniqueId, ) -> Self { - if group_id > (2u16.pow(14) - 1) { + if group_id > MAX_GROUP_ID_U32_EVENT { panic!("Group ID too large"); } Self { @@ -344,50 +354,16 @@ impl EventU32 { } } + pub fn from_be_bytes(bytes: [u8; 4]) -> Self { + Self::from(u32::from_be_bytes(bytes)) + } + const_from_fn!(const_from_info, EventU32TypedSev, SeverityInfo); const_from_fn!(const_from_low, EventU32TypedSev, SeverityLow); const_from_fn!(const_from_medium, EventU32TypedSev, SeverityMedium); const_from_fn!(const_from_high, EventU32TypedSev, SeverityHigh); } -impl EventU32TypedSev { - /// This is similar to [EventU32::new] but the severity is a type generic, which allows to - /// have distinct types for events with different severities - pub fn new( - group_id: ::GroupId, - unique_id: ::UniqueId, - ) -> Option { - let event = EventU32::new(SEVERITY::SEVERITY, group_id, unique_id)?; - Some(Self { - event, - phantom: PhantomData, - }) - } - - /// Const version of [Self::new], but panics on invalid group ID input values. - pub const fn const_new( - group_id: ::GroupId, - unique_id: ::UniqueId, - ) -> Self { - let event = EventU32::const_new(SEVERITY::SEVERITY, group_id, unique_id); - Self { - event, - phantom: PhantomData, - } - } - - fn try_from_generic(expected: Severity, raw: u32) -> Result { - let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap(); - if severity != expected { - return Err(severity); - } - Ok(Self::const_new( - ((raw >> 16) & 0x3FFF) as u16, - (raw & 0xFFFF) as u16, - )) - } -} - impl From for EventU32 { fn from(raw: u32) -> Self { // Severity conversion from u8 should never fail @@ -395,15 +371,10 @@ impl From for EventU32 { let group_id = ((raw >> 16) & 0x3FFF) as u16; let unique_id = (raw & 0xFFFF) as u16; // Sanitized input, should never fail - Self::const_new(severity, group_id, unique_id) + Self::new(severity, group_id, unique_id) } } -try_from_impls!(SeverityInfo, Severity::INFO, u32, EventU32TypedSev); -try_from_impls!(SeverityLow, Severity::LOW, u32, EventU32TypedSev); -try_from_impls!(SeverityMedium, Severity::MEDIUM, u32, EventU32TypedSev); -try_from_impls!(SeverityHigh, Severity::HIGH, u32, EventU32TypedSev); - impl UnsignedEnum for EventU32 { fn size(&self) -> usize { core::mem::size_of::() @@ -424,6 +395,49 @@ impl EcssEnumeration for EventU32 { } } +impl EventU32TypedSev { + /// This is similar to [EventU32::new] but the severity is a type generic, which allows to + /// have distinct types for events with different severities + pub fn new_checked( + group_id: ::GroupId, + unique_id: ::UniqueId, + ) -> Option { + let event = EventU32::new_checked(SEVERITY::SEVERITY, group_id, unique_id)?; + Some(Self { + event, + phantom: PhantomData, + }) + } + + /// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U32_EVENT]. + pub const fn new( + group_id: ::GroupId, + unique_id: ::UniqueId, + ) -> Self { + let event = EventU32::new(SEVERITY::SEVERITY, group_id, unique_id); + Self { + event, + phantom: PhantomData, + } + } + + fn try_from_generic(expected: Severity, raw: u32) -> Result { + let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap(); + if severity != expected { + return Err(severity); + } + Ok(Self::new( + ((raw >> 16) & 0x3FFF) as u16, + (raw & 0xFFFF) as u16, + )) + } +} + +try_from_impls!(SeverityInfo, Severity::Info, u32, EventU32TypedSev); +try_from_impls!(SeverityLow, Severity::Low, u32, EventU32TypedSev); +try_from_impls!(SeverityMedium, Severity::Medium, u32, EventU32TypedSev); +try_from_impls!(SeverityHigh, Severity::High, u32, EventU32TypedSev); + //noinspection RsTraitImplementation impl UnsignedEnum for EventU32TypedSev { delegate!(to self.event { @@ -441,6 +455,8 @@ impl EcssEnumeration for EventU32TypedSev { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct EventU16 { base: EventBase, } @@ -475,7 +491,7 @@ impl EventU16 { /// next 6 bits after the severity. Therefore, the size is limited by dec 63 hex 0x3F. /// * `unique_id`: Each event has a unique 8 bit ID occupying the last 8 bits of the /// raw event ID - pub fn new( + pub fn new_checked( severity: Severity, group_id: ::GroupId, unique_id: ::UniqueId, @@ -493,8 +509,8 @@ impl EventU16 { }) } - /// Const version of [Self::new], but panics on invalid group ID input values. - pub const fn const_new( + /// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U16_EVENT]. + pub const fn new( severity: Severity, group_id: ::GroupId, unique_id: ::UniqueId, @@ -511,52 +527,26 @@ impl EventU16 { }, } } + pub fn from_be_bytes(bytes: [u8; 2]) -> Self { + Self::from(u16::from_be_bytes(bytes)) + } + const_from_fn!(const_from_info, EventU16TypedSev, SeverityInfo); const_from_fn!(const_from_low, EventU16TypedSev, SeverityLow); const_from_fn!(const_from_medium, EventU16TypedSev, SeverityMedium); const_from_fn!(const_from_high, EventU16TypedSev, SeverityHigh); } -impl EventU16TypedSev { - /// This is similar to [EventU16::new] but the severity is a type generic, which allows to - /// have distinct types for events with different severities - pub fn new( - group_id: ::GroupId, - unique_id: ::UniqueId, - ) -> Option { - let event = EventU16::new(SEVERITY::SEVERITY, group_id, unique_id)?; - Some(Self { - event, - phantom: PhantomData, - }) - } - - /// Const version of [Self::new], but panics on invalid group ID input values. - pub const fn const_new( - group_id: ::GroupId, - unique_id: ::UniqueId, - ) -> Self { - let event = EventU16::const_new(SEVERITY::SEVERITY, group_id, unique_id); - Self { - event, - phantom: PhantomData, - } - } - - fn try_from_generic(expected: Severity, raw: u16) -> Result { +impl From for EventU16 { + fn from(raw: ::Raw) -> Self { let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap(); - if severity != expected { - return Err(severity); - } - Ok(Self::const_new( - ((raw >> 8) & 0x3F) as u8, - (raw & 0xFF) as u8, - )) + let group_id = ((raw >> 8) & 0x3F) as u8; + let unique_id = (raw & 0xFF) as u8; + // Sanitized input, new call should never fail + Self::new(severity, group_id, unique_id) } } -impl_event_provider!(EventU16, EventU16TypedSev, u16, u8, u8); - impl UnsignedEnum for EventU16 { fn size(&self) -> usize { core::mem::size_of::() @@ -577,6 +567,43 @@ impl EcssEnumeration for EventU16 { } } +impl EventU16TypedSev { + /// This is similar to [EventU16::new] but the severity is a type generic, which allows to + /// have distinct types for events with different severities + pub fn new_checked( + group_id: ::GroupId, + unique_id: ::UniqueId, + ) -> Option { + let event = EventU16::new_checked(SEVERITY::SEVERITY, group_id, unique_id)?; + Some(Self { + event, + phantom: PhantomData, + }) + } + + /// This constructor will panic if the `group_id` is larger than [MAX_GROUP_ID_U16_EVENT]. + pub const fn new( + group_id: ::GroupId, + unique_id: ::UniqueId, + ) -> Self { + let event = EventU16::new(SEVERITY::SEVERITY, group_id, unique_id); + Self { + event, + phantom: PhantomData, + } + } + + fn try_from_generic(expected: Severity, raw: u16) -> Result { + let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap(); + if severity != expected { + return Err(severity); + } + Ok(Self::new(((raw >> 8) & 0x3F) as u8, (raw & 0xFF) as u8)) + } +} + +impl_event_provider!(EventU16, EventU16TypedSev, u16, u8, u8); + //noinspection RsTraitImplementation impl UnsignedEnum for EventU16TypedSev { delegate!(to self.event { @@ -593,20 +620,10 @@ impl EcssEnumeration for EventU16TypedSev { }); } -impl From for EventU16 { - fn from(raw: ::Raw) -> Self { - let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap(); - let group_id = ((raw >> 8) & 0x3F) as u8; - let unique_id = (raw & 0xFF) as u8; - // Sanitized input, new call should never fail - Self::const_new(severity, group_id, unique_id) - } -} - -try_from_impls!(SeverityInfo, Severity::INFO, u16, EventU16TypedSev); -try_from_impls!(SeverityLow, Severity::LOW, u16, EventU16TypedSev); -try_from_impls!(SeverityMedium, Severity::MEDIUM, u16, EventU16TypedSev); -try_from_impls!(SeverityHigh, Severity::HIGH, u16, EventU16TypedSev); +try_from_impls!(SeverityInfo, Severity::Info, u16, EventU16TypedSev); +try_from_impls!(SeverityLow, Severity::Low, u16, EventU16TypedSev); +try_from_impls!(SeverityMedium, Severity::Medium, u16, EventU16TypedSev); +try_from_impls!(SeverityHigh, Severity::High, u16, EventU16TypedSev); impl PartialEq for EventU32TypedSev { #[inline] @@ -647,12 +664,10 @@ mod tests { assert_eq!(size_of::(), val); } - const INFO_EVENT: EventU32TypedSev = EventU32TypedSev::const_new(0, 0); - const INFO_EVENT_SMALL: EventU16TypedSev = EventU16TypedSev::const_new(0, 0); - const HIGH_SEV_EVENT: EventU32TypedSev = - EventU32TypedSev::const_new(0x3FFF, 0xFFFF); - const HIGH_SEV_EVENT_SMALL: EventU16TypedSev = - EventU16TypedSev::const_new(0x3F, 0xff); + const INFO_EVENT: EventU32TypedSev = EventU32TypedSev::new(0, 0); + const INFO_EVENT_SMALL: EventU16TypedSev = EventU16TypedSev::new(0, 0); + const HIGH_SEV_EVENT: EventU32TypedSev = EventU32TypedSev::new(0x3FFF, 0xFFFF); + const HIGH_SEV_EVENT_SMALL: EventU16TypedSev = EventU16TypedSev::new(0x3F, 0xff); /// This working is a test in itself. const INFO_REDUCED: EventU32 = EventU32::const_from_info(INFO_EVENT); @@ -683,7 +698,7 @@ mod tests { #[test] fn test_normal_event_getters() { - assert_eq!(INFO_EVENT.severity(), Severity::INFO); + assert_eq!(INFO_EVENT.severity(), Severity::Info); assert_eq!(INFO_EVENT.unique_id(), 0); assert_eq!(INFO_EVENT.group_id(), 0); let raw_event = INFO_EVENT.raw(); @@ -692,7 +707,7 @@ mod tests { #[test] fn test_small_event_getters() { - assert_eq!(INFO_EVENT_SMALL.severity(), Severity::INFO); + assert_eq!(INFO_EVENT_SMALL.severity(), Severity::Info); assert_eq!(INFO_EVENT_SMALL.unique_id(), 0); assert_eq!(INFO_EVENT_SMALL.group_id(), 0); let raw_event = INFO_EVENT_SMALL.raw(); @@ -701,7 +716,7 @@ mod tests { #[test] fn all_ones_event_regular() { - assert_eq!(HIGH_SEV_EVENT.severity(), Severity::HIGH); + assert_eq!(HIGH_SEV_EVENT.severity(), Severity::High); assert_eq!(HIGH_SEV_EVENT.group_id(), 0x3FFF); assert_eq!(HIGH_SEV_EVENT.unique_id(), 0xFFFF); let raw_event = HIGH_SEV_EVENT.raw(); @@ -710,7 +725,7 @@ mod tests { #[test] fn all_ones_event_small() { - assert_eq!(HIGH_SEV_EVENT_SMALL.severity(), Severity::HIGH); + assert_eq!(HIGH_SEV_EVENT_SMALL.severity(), Severity::High); assert_eq!(HIGH_SEV_EVENT_SMALL.group_id(), 0x3F); assert_eq!(HIGH_SEV_EVENT_SMALL.unique_id(), 0xFF); let raw_event = HIGH_SEV_EVENT_SMALL.raw(); @@ -719,18 +734,19 @@ mod tests { #[test] fn invalid_group_id_normal() { - assert!(EventU32TypedSev::::new(2_u16.pow(14), 0).is_none()); + assert!(EventU32TypedSev::::new_checked(2_u16.pow(14), 0).is_none()); } #[test] fn invalid_group_id_small() { - assert!(EventU16TypedSev::::new(2_u8.pow(6), 0).is_none()); + assert!(EventU16TypedSev::::new_checked(2_u8.pow(6), 0).is_none()); } #[test] fn regular_new() { assert_eq!( - EventU32TypedSev::::new(0, 0).expect("Creating regular event failed"), + EventU32TypedSev::::new_checked(0, 0) + .expect("Creating regular event failed"), INFO_EVENT ); } @@ -738,7 +754,8 @@ mod tests { #[test] fn small_new() { assert_eq!( - EventU16TypedSev::::new(0, 0).expect("Creating regular event failed"), + EventU16TypedSev::::new_checked(0, 0) + .expect("Creating regular event failed"), INFO_EVENT_SMALL ); } @@ -815,13 +832,13 @@ mod tests { fn severity_from_invalid_raw_val() { let invalid = 0xFF; assert!(Severity::try_from(invalid).is_err()); - let invalid = Severity::HIGH as u8 + 1; + let invalid = Severity::High as u8 + 1; assert!(Severity::try_from(invalid).is_err()); } #[test] fn reduction() { - let event = EventU32TypedSev::::const_new(1, 1); + let event = EventU32TypedSev::::new(1, 1); let raw = event.raw(); let reduced: EventU32 = event.into(); assert_eq!(reduced.group_id(), 1); diff --git a/satrs/src/pus/event.rs b/satrs/src/pus/event.rs index c4363bd..578167f 100644 --- a/satrs/src/pus/event.rs +++ b/satrs/src/pus/event.rs @@ -331,10 +331,10 @@ mod tests { fn severity_to_subservice(severity: Severity) -> Subservice { match severity { - Severity::INFO => Subservice::TmInfoReport, - Severity::LOW => Subservice::TmLowSeverityReport, - Severity::MEDIUM => Subservice::TmMediumSeverityReport, - Severity::HIGH => Subservice::TmHighSeverityReport, + Severity::Info => Subservice::TmInfoReport, + Severity::Low => Subservice::TmLowSeverityReport, + Severity::Medium => Subservice::TmMediumSeverityReport, + Severity::High => Subservice::TmHighSeverityReport, } } @@ -347,22 +347,22 @@ mod tests { aux_data: Option<&[u8]>, ) { match severity { - Severity::INFO => { + Severity::Info => { reporter .event_info(sender, time_stamp, event, aux_data) .expect("Error reporting info event"); } - Severity::LOW => { + Severity::Low => { reporter .event_low_severity(sender, time_stamp, event, aux_data) .expect("Error reporting low event"); } - Severity::MEDIUM => { + Severity::Medium => { reporter .event_medium_severity(sender, time_stamp, event, aux_data) .expect("Error reporting medium event"); } - Severity::HIGH => { + Severity::High => { reporter .event_high_severity(sender, time_stamp, event, aux_data) .expect("Error reporting high event"); @@ -389,7 +389,7 @@ mod tests { if let Some(err_data) = error_data { error_copy.extend_from_slice(err_data); } - let event = EventU32::new(severity, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0) + let event = EventU32::new_checked(severity, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0) .expect("Error creating example event"); report_basic_event( &mut reporter, @@ -417,35 +417,35 @@ mod tests { #[test] fn basic_info_event_generation() { - basic_event_test(4, Severity::INFO, None); + basic_event_test(4, Severity::Info, None); } #[test] fn basic_low_severity_event() { - basic_event_test(4, Severity::LOW, None); + basic_event_test(4, Severity::Low, None); } #[test] fn basic_medium_severity_event() { - basic_event_test(4, Severity::MEDIUM, None); + basic_event_test(4, Severity::Medium, None); } #[test] fn basic_high_severity_event() { - basic_event_test(4, Severity::HIGH, None); + basic_event_test(4, Severity::High, None); } #[test] fn event_with_info_string() { let info_string = "Test Information"; - basic_event_test(32, Severity::INFO, Some(info_string.as_bytes())); + basic_event_test(32, Severity::Info, Some(info_string.as_bytes())); } #[test] fn low_severity_with_raw_err_data() { let raw_err_param: i32 = -1; let raw_err = raw_err_param.to_be_bytes(); - basic_event_test(8, Severity::LOW, Some(&raw_err)) + basic_event_test(8, Severity::Low, Some(&raw_err)) } fn check_buf_too_small( @@ -454,7 +454,7 @@ mod tests { expected_found_len: usize, ) { let time_stamp_empty: [u8; 7] = [0; 7]; - let event = EventU32::new(Severity::INFO, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0) + let event = EventU32::new_checked(Severity::Info, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0) .expect("Error creating example event"); let err = reporter.event_info(sender, &time_stamp_empty, event, None); assert!(err.is_err()); diff --git a/satrs/src/pus/event_man.rs b/satrs/src/pus/event_man.rs index 4a123f9..c8aec2d 100644 --- a/satrs/src/pus/event_man.rs +++ b/satrs/src/pus/event_man.rs @@ -190,22 +190,22 @@ pub mod alloc_mod { return Ok(false); } match event.severity() { - Severity::INFO => self + Severity::Info => self .reporter .event_info(sender, time_stamp, event, params) .map(|_| true) .map_err(|e| e.into()), - Severity::LOW => self + Severity::Low => self .reporter .event_low_severity(sender, time_stamp, event, params) .map(|_| true) .map_err(|e| e.into()), - Severity::MEDIUM => self + Severity::Medium => self .reporter .event_medium_severity(sender, time_stamp, event, params) .map(|_| true) .map_err(|e| e.into()), - Severity::HIGH => self + Severity::High => self .reporter .event_high_severity(sender, time_stamp, event, params) .map(|_| true) @@ -266,9 +266,8 @@ mod tests { use crate::{events::SeverityInfo, tmtc::PacketAsVec}; use std::sync::mpsc::{self, TryRecvError}; - const INFO_EVENT: EventU32TypedSev = - EventU32TypedSev::::const_new(1, 0); - const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5); + const INFO_EVENT: EventU32TypedSev = EventU32TypedSev::::new(1, 0); + const LOW_SEV_EVENT: EventU32 = EventU32::new(Severity::Low, 1, 5); const EMPTY_STAMP: [u8; 7] = [0; 7]; const TEST_APID: u16 = 0x02; const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05); diff --git a/satrs/src/pus/event_srv.rs b/satrs/src/pus/event_srv.rs index c782b3a..8ea54ec 100644 --- a/satrs/src/pus/event_srv.rs +++ b/satrs/src/pus/event_srv.rs @@ -181,7 +181,7 @@ mod tests { use super::PusEventServiceHandler; - const TEST_EVENT_0: EventU32 = EventU32::const_new(crate::events::Severity::INFO, 5, 25); + const TEST_EVENT_0: EventU32 = EventU32::new(crate::events::Severity::Info, 5, 25); struct Pus5HandlerWithStoreTester { common: PusServiceHandlerWithSharedStoreCommon, diff --git a/satrs/tests/pus_autogen_events.rs b/satrs/tests/pus_autogen_events.rs index 4aced1d..22800de 100644 --- a/satrs/tests/pus_autogen_events.rs +++ b/satrs/tests/pus_autogen_events.rs @@ -21,7 +21,7 @@ struct EventIntrospection { } //#[event(descr="This is some info event")] -const INFO_EVENT_0: EventU32TypedSev = EventU32TypedSev::const_new(0, 0); +const INFO_EVENT_0: EventU32TypedSev = EventU32TypedSev::new(0, 0); const INFO_EVENT_0_ERASED: EventU32 = EventU32::const_from_info(INFO_EVENT_0); // This is ideally auto-generated @@ -36,7 +36,7 @@ const INFO_EVENT_0_INTROSPECTION: EventIntrospection = EventIntrospection { }; //#[event(descr="This is some low severity event")] -const SOME_LOW_SEV_EVENT: EventU32TypedSev = EventU32TypedSev::const_new(0, 12); +const SOME_LOW_SEV_EVENT: EventU32TypedSev = EventU32TypedSev::new(0, 12); //const EVENT_LIST: [&'static Event; 2] = [&INFO_EVENT_0, &SOME_LOW_SEV_EVENT]; @@ -47,7 +47,7 @@ const TEST_GROUP_NAME_NAME: &str = "TEST_GROUP_NAME"; //#[event(desc="Some medium severity event")] const MEDIUM_SEV_EVENT_IN_OTHER_GROUP: EventU32TypedSev = - EventU32TypedSev::const_new(TEST_GROUP_NAME, 0); + EventU32TypedSev::new(TEST_GROUP_NAME, 0); const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_REDUCED: EventU32 = EventU32::const_from_medium(MEDIUM_SEV_EVENT_IN_OTHER_GROUP); @@ -62,7 +62,7 @@ const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_INTROSPECTION: EventIntrospection = EventI info: "Some medium severity event", }; -const CONST_SLICE: &'static [u8] = &[0, 1, 2, 3]; +const CONST_SLICE: &[u8] = &[0, 1, 2, 3]; const INTROSPECTION_FOR_TEST_GROUP_0: [&EventIntrospection; 2] = [&INFO_EVENT_0_INTROSPECTION, &INFO_EVENT_0_INTROSPECTION]; diff --git a/satrs/tests/pus_events.rs b/satrs/tests/pus_events.rs index 62bb8b0..d9d572e 100644 --- a/satrs/tests/pus_events.rs +++ b/satrs/tests/pus_events.rs @@ -14,9 +14,8 @@ use spacepackets::ecss::{PusError, PusPacket}; use std::sync::mpsc::{self, SendError, TryRecvError}; use std::thread; -const INFO_EVENT: EventU32TypedSev = - EventU32TypedSev::::const_new(1, 0); -const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5); +const INFO_EVENT: EventU32TypedSev = EventU32TypedSev::::new(1, 0); +const LOW_SEV_EVENT: EventU32 = EventU32::new(Severity::Low, 1, 5); const EMPTY_STAMP: [u8; 7] = [0; 7]; const TEST_APID: u16 = 0x02; const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05); From c477739f6d62c68968e61760d9e9d59a6c0a6b78 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 24 Apr 2024 18:50:08 +0200 Subject: [PATCH 03/15] more improvements for API, tests for example event module --- satrs-example/src/events.rs | 75 ++++++++++++++++++++++++++---------- satrs-shared/Cargo.toml | 5 +++ satrs-shared/src/res_code.rs | 23 +++++++++++ satrs/src/events.rs | 4 ++ 4 files changed, 87 insertions(+), 20 deletions(-) diff --git a/satrs-example/src/events.rs b/satrs-example/src/events.rs index abfcb6b..cb0caf8 100644 --- a/satrs-example/src/events.rs +++ b/satrs-example/src/events.rs @@ -214,43 +214,78 @@ mod tests { use satrs::{ events::EventU32, pus::verification::VerificationReporterCfg, - spacepackets::{ecss::tm::PusTmReader, CcsdsPacket}, + spacepackets::{ + ecss::{tm::PusTmReader, PusPacket}, + CcsdsPacket, + }, tmtc::PacketAsVec, }; use super::*; const TEST_CREATOR_ID: UniqueApidTargetId = UniqueApidTargetId::new(1, 2); + const TEST_EVENT: EventU32 = EventU32::new(satrs::events::Severity::Info, 1, 1); + + pub struct EventManagementTestbench { + pub event_tx: mpsc::SyncSender, + pub event_manager: EventManagerWithBoundedMpsc, + pub tm_receiver: mpsc::Receiver, + pub pus_event_handler: PusEventHandler>, + } + + impl EventManagementTestbench { + pub fn new() -> Self { + let (event_tx, event_rx) = mpsc::sync_channel(10); + let (_event_req_tx, event_req_rx) = mpsc::sync_channel(10); + let (tm_sender, tm_receiver) = mpsc::channel(); + let verif_reporter_cfg = VerificationReporterCfg::new(0x05, 2, 2, 128).unwrap(); + let verif_reporter = + VerificationReporter::new(PUS_EVENT_MANAGEMENT.id(), &verif_reporter_cfg); + let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx); + let pus_event_handler = PusEventHandler::>::new( + tm_sender, + verif_reporter, + &mut event_manager, + event_req_rx, + ); + Self { + event_tx, + tm_receiver, + event_manager, + pus_event_handler, + } + } + } #[test] - fn test_basic() { - let (event_tx, event_rx) = mpsc::sync_channel(10); - let (_event_req_tx, event_req_rx) = mpsc::sync_channel(10); - let (tm_sender, tm_receiver) = mpsc::channel(); - let verif_reporter_cfg = VerificationReporterCfg::new(0x05, 2, 2, 128).unwrap(); - let verif_reporter = - VerificationReporter::new(PUS_EVENT_MANAGEMENT.id(), &verif_reporter_cfg); - let mut event_manager = EventManagerWithBoundedMpsc::new(event_rx); - let mut pus_event_handler = PusEventHandler::>::new( - tm_sender, - verif_reporter, - &mut event_manager, - event_req_rx, - ); - event_tx + fn test_basic_event_generation() { + let mut testbench = EventManagementTestbench::new(); + testbench + .event_tx .send(EventMessageU32::new( TEST_CREATOR_ID.id(), EventU32::new(satrs::events::Severity::Info, 1, 1), )) .expect("failed to send event"); - pus_event_handler.handle_event_requests(); - event_manager.try_event_handling(|_, _| {}); - pus_event_handler.generate_pus_event_tm(); - let tm_packet = tm_receiver.try_recv().expect("failed to receive TM packet"); + testbench.pus_event_handler.handle_event_requests(); + testbench.event_manager.try_event_handling(|_, _| {}); + testbench.pus_event_handler.generate_pus_event_tm(); + let tm_packet = testbench + .tm_receiver + .try_recv() + .expect("failed to receive TM packet"); assert_eq!(tm_packet.sender_id, PUS_EVENT_MANAGEMENT.id()); let tm_reader = PusTmReader::new(&tm_packet.packet, 7) .expect("failed to create TM reader") .0; assert_eq!(tm_reader.apid(), TEST_CREATOR_ID.apid); + assert_eq!(tm_reader.user_data().len(), 4); + let event_read_back = EventU32::from_be_bytes(tm_reader.user_data().try_into().unwrap()); + assert_eq!(event_read_back, TEST_EVENT); + } + + #[test] + fn test_basic_event_disabled() { + // TODO: Add test. } } diff --git a/satrs-shared/Cargo.toml b/satrs-shared/Cargo.toml index 2ed8f26..cfac25d 100644 --- a/satrs-shared/Cargo.toml +++ b/satrs-shared/Cargo.toml @@ -17,12 +17,17 @@ version = "1" default-features = false optional = true +[dependencies.defmt] +version = "0.3" +optional = true + [dependencies.spacepackets] version = ">0.9, <=0.11" default-features = false [features] serde = ["dep:serde", "spacepackets/serde"] +spacepackets = ["dep:defmt", "spacepackets/defmt"] [package.metadata.docs.rs] rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"] diff --git a/satrs-shared/src/res_code.rs b/satrs-shared/src/res_code.rs index e7816f3..099e84d 100644 --- a/satrs-shared/src/res_code.rs +++ b/satrs-shared/src/res_code.rs @@ -7,6 +7,7 @@ use spacepackets::ByteConversionError; /// Simple [u16] based result code type which also allows to group related resultcodes. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResultU16 { group_id: u8, unique_id: u8, @@ -19,15 +20,28 @@ impl ResultU16 { unique_id, } } + pub fn raw(&self) -> u16 { ((self.group_id as u16) << 8) | self.unique_id as u16 } + pub fn group_id(&self) -> u8 { self.group_id } + pub fn unique_id(&self) -> u8 { self.unique_id } + + pub fn from_be_bytes(bytes: [u8; 2]) -> Self { + Self::from(u16::from_be_bytes(bytes)) + } +} + +impl From for ResultU16 { + fn from(value: u16) -> Self { + Self::new(((value >> 8) & 0xff) as u8, (value & 0xff) as u8) + } } impl From for EcssEnumU16 { @@ -84,5 +98,14 @@ mod tests { assert_eq!(written, 2); assert_eq!(buf[0], 1); assert_eq!(buf[1], 1); + let read_back = ResultU16::from_be_bytes(buf); + assert_eq!(read_back, result_code); + } + + #[test] + fn test_from_u16() { + let result_code = ResultU16::new(1, 1); + let result_code_2 = ResultU16::from(result_code.raw()); + assert_eq!(result_code, result_code_2); } } diff --git a/satrs/src/events.rs b/satrs/src/events.rs index cb87a05..5544eb2 100644 --- a/satrs/src/events.rs +++ b/satrs/src/events.rs @@ -794,6 +794,8 @@ mod tests { assert!(HIGH_SEV_EVENT.write_to_be_bytes(&mut buf).is_ok()); let val_from_raw = u32::from_be_bytes(buf); assert_eq!(val_from_raw, 0xFFFFFFFF); + let event_read_back = EventU32::from_be_bytes(buf); + assert_eq!(event_read_back, HIGH_SEV_EVENT); } #[test] @@ -802,6 +804,8 @@ mod tests { assert!(HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf).is_ok()); let val_from_raw = u16::from_be_bytes(buf); assert_eq!(val_from_raw, 0xFFFF); + let event_read_back = EventU16::from_be_bytes(buf); + assert_eq!(event_read_back, HIGH_SEV_EVENT_SMALL); } #[test] From b0159a3ba71485d70fd77c0332152565bdb08533 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 24 Apr 2024 19:18:45 +0200 Subject: [PATCH 04/15] prep next release candidate --- satrs-shared/CHANGELOG.md | 9 +++++++++ satrs-shared/Cargo.toml | 4 ++-- satrs-shared/src/lib.rs | 1 + satrs/CHANGELOG.md | 8 ++++++-- satrs/Cargo.toml | 4 ++-- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/satrs-shared/CHANGELOG.md b/satrs-shared/CHANGELOG.md index 0c62de9..8e718d8 100644 --- a/satrs-shared/CHANGELOG.md +++ b/satrs-shared/CHANGELOG.md @@ -8,6 +8,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/). # [unreleased] +# [v0.1.4] 2024-04-24 + +## Added + +- `ResultU16::from_be_bytes` +- `From` impl for `ResultU16`. +- Optional `defmt` support: `defmt::Format` impl on `ResultU16` if the `defmt` feature is + activated. + # [v0.1.3] 2024-04-16 Allow `spacepackets` range starting with v0.10 and v0.11. diff --git a/satrs-shared/Cargo.toml b/satrs-shared/Cargo.toml index cfac25d..175f909 100644 --- a/satrs-shared/Cargo.toml +++ b/satrs-shared/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "satrs-shared" description = "Components shared by multiple sat-rs crates" -version = "0.1.3" +version = "0.1.4" edition = "2021" authors = ["Robin Mueller "] homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/" @@ -30,4 +30,4 @@ serde = ["dep:serde", "spacepackets/serde"] spacepackets = ["dep:defmt", "spacepackets/defmt"] [package.metadata.docs.rs] -rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"] +rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"] diff --git a/satrs-shared/src/lib.rs b/satrs-shared/src/lib.rs index 6ecdbac..428f01f 100644 --- a/satrs-shared/src/lib.rs +++ b/satrs-shared/src/lib.rs @@ -1,3 +1,4 @@ //! This crates contains modules shared among other sat-rs framework crates. #![no_std] +#![cfg_attr(docs_rs, feature(doc_auto_cfg))] pub mod res_code; diff --git a/satrs/CHANGELOG.md b/satrs/CHANGELOG.md index 33ba773..6124919 100644 --- a/satrs/CHANGELOG.md +++ b/satrs/CHANGELOG.md @@ -8,7 +8,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). # [unreleased] -# [v0.2.0-rc.5] 2024-04-23 +# [v0.2.0-rc.5] 2024-04-24 + +## Added + +- Optional `defmt::Format` support for the event types, if the `defmt` feature is activated. ## Changed @@ -17,7 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Renamed `PusEventDispatcher` to `PusEventTmCreatorWithMap`. - Renamed `DefaultPusEventU32Dispatcher` to `DefaultPusEventU32EventCreator`. - Renamed `PusEventMgmtBackendProvider` renamed to `PusEventReportingMap`. -- Reanmed Event `const_new` methods to `new` and the former `new` methods to `new_checked`. +- Reanmed Event `const_new` methods to `new` and the former `new` methods to `new_checked` # [v0.2.0-rc.4] 2024-04-23 diff --git a/satrs/Cargo.toml b/satrs/Cargo.toml index 17627c7..94f8e79 100644 --- a/satrs/Cargo.toml +++ b/satrs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "satrs" -version = "0.2.0-rc.4" +version = "0.2.0-rc.5" edition = "2021" rust-version = "1.71.1" authors = ["Robin Mueller "] @@ -91,7 +91,7 @@ optional = true [dev-dependencies] serde = "1" zerocopy = "0.7" -once_cell = "1.13" +once_cell = "1" serde_json = "1" rand = "0.8" tempfile = "3" From 958ab9bab67d736506f3522647a3a90de461baf6 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 25 Apr 2024 11:11:31 +0200 Subject: [PATCH 05/15] renamed thread name --- satrs-example/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satrs-example/src/main.rs b/satrs-example/src/main.rs index d6cf6ff..02138c5 100644 --- a/satrs-example/src/main.rs +++ b/satrs-example/src/main.rs @@ -458,7 +458,7 @@ fn dyn_tmtc_pool_main() { info!("Starting TM funnel task"); let jh_tm_funnel = thread::Builder::new() - .name("sat-rs tm-funnel".to_string()) + .name("sat-rs tm-sink".to_string()) .spawn(move || loop { tm_funnel.operation(); }) From 9c60427f894ac8345d435955da0be66434710c0f Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 30 Apr 2024 13:29:55 +0200 Subject: [PATCH 06/15] Reworked generic parameter handling for PUS service 1 and 5 --- satrs-example/src/events.rs | 22 +- satrs-example/src/pus/action.rs | 54 +-- satrs-example/src/pus/mod.rs | 21 +- satrs/Cargo.toml | 1 + satrs/src/params.rs | 80 ++-- satrs/src/pus/event.rs | 2 +- satrs/src/pus/event_man.rs | 126 +++++++ satrs/src/pus/mod.rs | 28 +- satrs/src/pus/verification.rs | 629 +++++++++++++++++--------------- 9 files changed, 563 insertions(+), 400 deletions(-) diff --git a/satrs-example/src/events.rs b/satrs-example/src/events.rs index cb0caf8..8f4bd08 100644 --- a/satrs-example/src/events.rs +++ b/satrs-example/src/events.rs @@ -2,7 +2,6 @@ use std::sync::mpsc::{self}; use crate::pus::create_verification_reporter; use satrs::event_man::{EventMessageU32, EventRoutingError}; -use satrs::params::WritableToBeBytes; use satrs::pus::event::EventTmHookProvider; use satrs::pus::verification::VerificationReporter; use satrs::pus::EcssTmSender; @@ -42,6 +41,7 @@ pub struct PusEventHandler { tm_sender: TmSender, time_provider: CdsTime, timestamp: [u8; 7], + small_data_buf: [u8; 64], verif_handler: VerificationReporter, } @@ -82,6 +82,7 @@ impl PusEventHandler { pus_event_man_rx, time_provider: CdsTime::new_with_u16_days(0, 0), timestamp: [0; 7], + small_data_buf: [0; 64], verif_handler, tm_sender, } @@ -132,21 +133,26 @@ impl PusEventHandler { // Perform the generation of PUS event packets match self.pus_event_man_rx.try_recv() { Ok(event_msg) => { - update_time(&mut self.time_provider, &mut self.timestamp); - let param_vec = event_msg.params().map_or(Vec::new(), |param| { - param.to_vec().expect("failed to convert params to vec") - }); // We use the TM modification hook to set the sender APID for each event. self.pus_event_tm_creator.reporter.tm_hook.next_apid = UniqueApidTargetId::from(event_msg.sender_id()).apid; - self.pus_event_tm_creator - .generate_pus_event_tm_generic( + update_time(&mut self.time_provider, &mut self.timestamp); + let generation_result = self + .pus_event_tm_creator + .generate_pus_event_tm_generic_with_generic_params( &self.tm_sender, &self.timestamp, event_msg.event(), - Some(¶m_vec), + &mut self.small_data_buf, + event_msg.params(), ) .expect("Sending TM as event failed"); + if !generation_result.params_were_propagated { + log::warn!( + "Event TM parameters were not propagated: {:?}", + event_msg.params() + ); + } } Err(e) => match e { mpsc::TryRecvError::Empty => break, diff --git a/satrs-example/src/pus/action.rs b/satrs-example/src/pus/action.rs index dcdb345..7f4e76f 100644 --- a/satrs-example/src/pus/action.rs +++ b/satrs-example/src/pus/action.rs @@ -1,12 +1,12 @@ use log::{error, warn}; use satrs::action::{ActionRequest, ActionRequestVariant}; -use satrs::params::WritableToBeBytes; use satrs::pool::SharedStaticMemoryPool; use satrs::pus::action::{ ActionReplyPus, ActionReplyVariant, ActivePusActionRequestStd, DefaultActiveActionRequestMap, }; use satrs::pus::verification::{ - FailParams, FailParamsWithStep, TcStateAccepted, TcStateStarted, VerificationReporter, + handle_completion_failure_with_generic_params, handle_step_failure_with_generic_params, + FailParamHelper, FailParams, TcStateAccepted, TcStateStarted, VerificationReporter, VerificationReportingProvider, VerificationToken, }; use satrs::pus::{ @@ -61,7 +61,7 @@ impl PusReplyHandler for ActionReplyH active_request: &ActivePusActionRequestStd, tm_sender: &(impl EcssTmSender + ?Sized), verification_handler: &impl VerificationReportingProvider, - time_stamp: &[u8], + timestamp: &[u8], ) -> Result { let verif_token: VerificationToken = active_request .token() @@ -69,15 +69,23 @@ impl PusReplyHandler for ActionReplyH .expect("invalid token state"); let remove_entry = match &reply.message.variant { ActionReplyVariant::CompletionFailed { error_code, params } => { - let mut fail_data_len = 0; - if let Some(params) = params { - fail_data_len = params.write_to_be_bytes(&mut self.fail_data_buf)?; - } - verification_handler.completion_failure( + let error_propagated = handle_completion_failure_with_generic_params( tm_sender, verif_token, - FailParams::new(time_stamp, error_code, &self.fail_data_buf[..fail_data_len]), + verification_handler, + FailParamHelper { + error_code, + params: params.as_ref(), + timestamp, + small_data_buf: &mut self.fail_data_buf, + }, )?; + if !error_propagated { + log::warn!( + "error params for completion failure were not propated: {:?}", + params.as_ref() + ); + } true } ActionReplyVariant::StepFailed { @@ -85,31 +93,35 @@ impl PusReplyHandler for ActionReplyH step, params, } => { - let mut fail_data_len = 0; - if let Some(params) = params { - fail_data_len = params.write_to_be_bytes(&mut self.fail_data_buf)?; - } - verification_handler.step_failure( + let error_propagated = handle_step_failure_with_generic_params( tm_sender, verif_token, - FailParamsWithStep::new( - time_stamp, - &EcssEnumU16::new(*step), + verification_handler, + FailParamHelper { error_code, - &self.fail_data_buf[..fail_data_len], - ), + params: params.as_ref(), + timestamp, + small_data_buf: &mut self.fail_data_buf, + }, + &EcssEnumU16::new(*step), )?; + if !error_propagated { + log::warn!( + "error params for completion failure were not propated: {:?}", + params.as_ref() + ); + } true } ActionReplyVariant::Completed => { - verification_handler.completion_success(tm_sender, verif_token, time_stamp)?; + verification_handler.completion_success(tm_sender, verif_token, timestamp)?; true } ActionReplyVariant::StepSuccess { step } => { verification_handler.step_success( tm_sender, &verif_token, - time_stamp, + timestamp, EcssEnumU16::new(*step), )?; false diff --git a/satrs-example/src/pus/mod.rs b/satrs-example/src/pus/mod.rs index 6e5ec37..75b5be3 100644 --- a/satrs-example/src/pus/mod.rs +++ b/satrs-example/src/pus/mod.rs @@ -439,20 +439,17 @@ where return Ok(()); } let active_request = active_req_opt.unwrap(); - let request_finished = self - .reply_handler - .handle_reply( - reply, - active_request, - &self.service_helper.common.tm_sender, - &self.service_helper.common.verif_reporter, - time_stamp, - ) - .unwrap_or(false); - if request_finished { + let result = self.reply_handler.handle_reply( + reply, + active_request, + &self.service_helper.common.tm_sender, + &self.service_helper.common.verif_reporter, + time_stamp, + ); + if result.is_err() || (result.is_ok() && *result.as_ref().unwrap()) { self.active_request_map.remove(reply.request_id()); } - Ok(()) + result.map(|_| ()) } pub fn check_for_request_timeouts(&mut self) { diff --git a/satrs/Cargo.toml b/satrs/Cargo.toml index 94f8e79..92b753d 100644 --- a/satrs/Cargo.toml +++ b/satrs/Cargo.toml @@ -15,6 +15,7 @@ categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-sup [dependencies] delegate = ">0.7, <=0.10" paste = "1" +derive-new = "0.6" smallvec = "1" crc = "3" diff --git a/satrs/src/params.rs b/satrs/src/params.rs index da770eb..63135c5 100644 --- a/satrs/src/params.rs +++ b/satrs/src/params.rs @@ -643,52 +643,18 @@ impl From<&str> for Params { } } -/// Please note while [WritableToBeBytes] is implemented for [Params], the default implementation -/// will not be able to process the [Params::Store] parameter variant. -impl WritableToBeBytes for Params { +impl WritableToBeBytes for ParamsHeapless { fn written_len(&self) -> usize { match self { - Params::Heapless(p) => match p { - ParamsHeapless::Raw(raw) => raw.written_len(), - ParamsHeapless::EcssEnum(enumeration) => enumeration.written_len(), - }, - Params::Store(_) => 0, - #[cfg(feature = "alloc")] - Params::Vec(vec) => vec.len(), - #[cfg(feature = "alloc")] - Params::String(string) => string.len(), + ParamsHeapless::Raw(raw) => raw.written_len(), + ParamsHeapless::EcssEnum(ecss_enum) => ecss_enum.written_len(), } } fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { match self { - Params::Heapless(p) => match p { - ParamsHeapless::Raw(raw) => raw.write_to_be_bytes(buf), - ParamsHeapless::EcssEnum(enumeration) => enumeration.write_to_be_bytes(buf), - }, - Params::Store(_) => Ok(0), - #[cfg(feature = "alloc")] - Params::Vec(vec) => { - if buf.len() < vec.len() { - return Err(ByteConversionError::ToSliceTooSmall { - found: buf.len(), - expected: vec.len(), - }); - } - buf[0..vec.len()].copy_from_slice(vec); - Ok(vec.len()) - } - #[cfg(feature = "alloc")] - Params::String(string) => { - if buf.len() < string.len() { - return Err(ByteConversionError::ToSliceTooSmall { - found: buf.len(), - expected: string.len(), - }); - } - buf[0..string.len()].copy_from_slice(string.as_bytes()); - Ok(string.len()) - } + ParamsHeapless::Raw(raw) => raw.write_to_be_bytes(buf), + ParamsHeapless::EcssEnum(ecss_enum) => ecss_enum.write_to_be_bytes(buf), } } } @@ -837,10 +803,9 @@ mod tests { #[test] fn test_params_written_len_raw() { let param_raw = ParamsRaw::from((500_u32, 1000_u32)); - let param: Params = Params::Heapless(param_raw.into()); - assert_eq!(param.written_len(), 8); + assert_eq!(param_raw.written_len(), 8); let mut buf: [u8; 8] = [0; 8]; - param + param_raw .write_to_be_bytes(&mut buf) .expect("writing to buffer failed"); assert_eq!(u32::from_be_bytes(buf[0..4].try_into().unwrap()), 500); @@ -848,21 +813,28 @@ mod tests { } #[test] - fn test_params_written_string() { - let string = "Test String".to_string(); - let param = Params::String(string.clone()); - assert_eq!(param.written_len(), string.len()); - let vec = param.to_vec().unwrap(); - let string_conv_back = String::from_utf8(vec).expect("conversion to string failed"); - assert_eq!(string_conv_back, string); + fn test_heapless_param_writable_trait_raw() { + let param_heapless = ParamsHeapless::Raw(ParamsRaw::from((500_u32, 1000_u32))); + assert_eq!(param_heapless.written_len(), 8); + let mut buf: [u8; 8] = [0; 8]; + let size = param_heapless + .write_to_be_bytes(&mut buf) + .expect("writing failed"); + assert_eq!(size, 8); + assert_eq!(u32::from_be_bytes(buf[0..4].try_into().unwrap()), 500); + assert_eq!(u32::from_be_bytes(buf[4..8].try_into().unwrap()), 1000); } #[test] - fn test_params_written_vec() { - let vec: Vec = alloc::vec![1, 2, 3, 4, 5]; - let param = Params::Vec(vec.clone()); - assert_eq!(param.written_len(), vec.len()); - assert_eq!(param.to_vec().expect("writing vec params failed"), vec); + fn test_heapless_param_writable_trait_ecss_enum() { + let param_heapless = ParamsHeapless::EcssEnum(ParamsEcssEnum::U16(5.into())); + assert_eq!(param_heapless.written_len(), 2); + let mut buf: [u8; 2] = [0; 2]; + let size = param_heapless + .write_to_be_bytes(&mut buf) + .expect("writing failed"); + assert_eq!(size, 2); + assert_eq!(u16::from_be_bytes(buf[0..2].try_into().unwrap()), 5); } #[test] diff --git a/satrs/src/pus/event.rs b/satrs/src/pus/event.rs index 578167f..a6301e2 100644 --- a/satrs/src/pus/event.rs +++ b/satrs/src/pus/event.rs @@ -407,7 +407,7 @@ mod tests { severity_to_subservice(severity) as u8 ); assert_eq!(tm_info.common.dest_id, 0); - assert_eq!(tm_info.common.time_stamp, time_stamp_empty); + assert_eq!(tm_info.common.timestamp, time_stamp_empty); assert_eq!(tm_info.common.msg_counter, 0); assert_eq!(tm_info.common.apid, EXAMPLE_APID); assert_eq!(tm_info.event, event); diff --git a/satrs/src/pus/event_man.rs b/satrs/src/pus/event_man.rs index c8aec2d..ed8238c 100644 --- a/satrs/src/pus/event_man.rs +++ b/satrs/src/pus/event_man.rs @@ -107,6 +107,7 @@ pub mod alloc_mod { use crate::{ events::EventU16, + params::{Params, WritableToBeBytes}, pus::event::{DummyEventHook, EventTmHookProvider}, }; @@ -147,6 +148,12 @@ pub mod alloc_mod { } } + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub struct EventGenerationResult { + pub event_was_enabled: bool, + pub params_were_propagated: bool, + } + pub struct PusEventTmCreatorWithMap< ReportingMap: PusEventReportingMapProvider, Event: GenericEvent, @@ -212,6 +219,53 @@ pub mod alloc_mod { .map_err(|e| e.into()), } } + + pub fn generate_pus_event_tm_generic_with_generic_params( + &self, + sender: &(impl EcssTmSender + ?Sized), + time_stamp: &[u8], + event: Event, + small_data_buf: &mut [u8], + params: Option<&Params>, + ) -> Result { + let mut result = EventGenerationResult { + event_was_enabled: false, + params_were_propagated: true, + }; + if params.is_none() { + result.event_was_enabled = + self.generate_pus_event_tm_generic(sender, time_stamp, event, None)?; + return Ok(result); + } + let params = params.unwrap(); + result.event_was_enabled = match params { + Params::Heapless(heapless_param) => { + heapless_param + .write_to_be_bytes(&mut small_data_buf[..heapless_param.written_len()]) + .map_err(EcssTmtcError::ByteConversion)?; + self.generate_pus_event_tm_generic( + sender, + time_stamp, + event, + Some(small_data_buf), + )? + } + Params::Vec(vec) => { + self.generate_pus_event_tm_generic(sender, time_stamp, event, Some(vec))? + } + Params::String(string) => self.generate_pus_event_tm_generic( + sender, + time_stamp, + event, + Some(string.as_bytes()), + )?, + _ => { + result.params_were_propagated = false; + self.generate_pus_event_tm_generic(sender, time_stamp, event, None)? + } + }; + Ok(result) + } } impl @@ -261,6 +315,12 @@ pub mod alloc_mod { } #[cfg(test)] mod tests { + use alloc::string::{String, ToString}; + use alloc::vec; + use spacepackets::ecss::event::Subservice; + use spacepackets::ecss::tm::PusTmReader; + use spacepackets::ecss::PusPacket; + use super::*; use crate::request::UniqueApidTargetId; use crate::{events::SeverityInfo, tmtc::PacketAsVec}; @@ -336,4 +396,70 @@ mod tests { assert!(event_sent); event_rx.try_recv().expect("No info event received"); } + + #[test] + fn test_event_with_generic_string_param() { + let event_man = create_basic_man_1(); + let mut small_data_buf = [0; 128]; + let param_data = "hello world"; + let (event_tx, event_rx) = mpsc::channel::(); + let res = event_man.generate_pus_event_tm_generic_with_generic_params( + &event_tx, + &EMPTY_STAMP, + INFO_EVENT.into(), + &mut small_data_buf, + Some(¶m_data.to_string().into()), + ); + assert!(res.is_ok()); + let res = res.unwrap(); + assert!(res.event_was_enabled); + assert!(res.params_were_propagated); + let event_tm = event_rx.try_recv().expect("no event received"); + let (tm, _) = PusTmReader::new(&event_tm.packet, 7).expect("reading TM failed"); + assert_eq!(tm.service(), 5); + assert_eq!(tm.subservice(), Subservice::TmInfoReport as u8); + assert_eq!(tm.user_data().len(), 4 + param_data.len()); + let u32_event = u32::from_be_bytes(tm.user_data()[0..4].try_into().unwrap()); + assert_eq!(u32_event, INFO_EVENT.raw()); + let string_data = String::from_utf8_lossy(&tm.user_data()[4..]); + assert_eq!(string_data, param_data); + } + + #[test] + fn test_event_with_generic_vec_param() { + let event_man = create_basic_man_1(); + let mut small_data_buf = [0; 128]; + let param_data = vec![1, 2, 3, 4]; + let (event_tx, event_rx) = mpsc::channel::(); + let res = event_man.generate_pus_event_tm_generic_with_generic_params( + &event_tx, + &EMPTY_STAMP, + INFO_EVENT.into(), + &mut small_data_buf, + Some(¶m_data.clone().into()), + ); + assert!(res.is_ok()); + let res = res.unwrap(); + assert!(res.event_was_enabled); + assert!(res.params_were_propagated); + let event_tm = event_rx.try_recv().expect("no event received"); + let (tm, _) = PusTmReader::new(&event_tm.packet, 7).expect("reading TM failed"); + assert_eq!(tm.service(), 5); + assert_eq!(tm.subservice(), Subservice::TmInfoReport as u8); + assert_eq!(tm.user_data().len(), 4 + param_data.len()); + let u32_event = u32::from_be_bytes(tm.user_data()[0..4].try_into().unwrap()); + assert_eq!(u32_event, INFO_EVENT.raw()); + let vec_data = tm.user_data()[4..].to_vec(); + assert_eq!(vec_data, param_data); + } + + #[test] + fn test_event_with_generic_store_param_not_propagated() { + // TODO: Test this. + } + + #[test] + fn test_event_with_generic_heapless_param() { + // TODO: Test this. + } } diff --git a/satrs/src/pus/mod.rs b/satrs/src/pus/mod.rs index 78caa4d..4a76757 100644 --- a/satrs/src/pus/mod.rs +++ b/satrs/src/pus/mod.rs @@ -1284,36 +1284,46 @@ pub mod tests { pub seq_count: u16, pub msg_counter: u16, pub dest_id: u16, - pub time_stamp: [u8; 7], + pub timestamp: Vec, } impl CommonTmInfo { - pub fn new_zero_seq_count( + pub fn new( subservice: u8, apid: u16, + seq_count: u16, + msg_counter: u16, dest_id: u16, - time_stamp: [u8; 7], + timestamp: &[u8], ) -> Self { Self { subservice, apid, - seq_count: 0, - msg_counter: 0, + seq_count, + msg_counter, dest_id, - time_stamp, + timestamp: timestamp.to_vec(), } } + pub fn new_zero_seq_count( + subservice: u8, + apid: u16, + dest_id: u16, + timestamp: &[u8], + ) -> Self { + Self::new(subservice, apid, 0, 0, dest_id, timestamp) + } pub fn new_from_tm(tm: &PusTmCreator) -> Self { - let mut time_stamp = [0; 7]; - time_stamp.clone_from_slice(&tm.timestamp()[0..7]); + let mut timestamp = [0; 7]; + timestamp.clone_from_slice(&tm.timestamp()[0..7]); Self { subservice: PusPacket::subservice(tm), apid: tm.apid(), seq_count: tm.seq_count(), msg_counter: tm.msg_counter(), dest_id: tm.dest_id(), - time_stamp, + timestamp: timestamp.to_vec(), } } } diff --git a/satrs/src/pus/verification.rs b/satrs/src/pus/verification.rs index 2f81e41..81358a4 100644 --- a/satrs/src/pus/verification.rs +++ b/satrs/src/pus/verification.rs @@ -79,6 +79,7 @@ //! The [integration test](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-core/tests/verification_test.rs) //! for the verification module contains examples how this module could be used in a more complex //! context involving multiple threads +use crate::params::{Params, WritableToBeBytes}; use crate::pus::{source_buffer_large_enough, EcssTmSender, EcssTmtcError}; use core::fmt::{Debug, Display, Formatter}; use core::hash::{Hash, Hasher}; @@ -353,7 +354,7 @@ pub struct FailParams<'stamp, 'fargs> { impl<'stamp, 'fargs> FailParams<'stamp, 'fargs> { pub fn new( time_stamp: &'stamp [u8], - failure_code: &'fargs impl EcssEnumeration, + failure_code: &'fargs dyn EcssEnumeration, failure_data: &'fargs [u8], ) -> Self { Self { @@ -381,7 +382,7 @@ impl<'stamp, 'fargs> FailParamsWithStep<'stamp, 'fargs> { pub fn new( time_stamp: &'stamp [u8], step: &'fargs impl EcssEnumeration, - failure_code: &'fargs impl EcssEnumeration, + failure_code: &'fargs dyn EcssEnumeration, failure_data: &'fargs [u8], ) -> Self { Self { @@ -1171,26 +1172,143 @@ pub mod alloc_mod { } } -/* -#[cfg(feature = "std")] -pub mod std_mod { - use std::sync::mpsc; - - use crate::pool::StoreAddr; - use crate::pus::verification::VerificationReporterWithSender; - - use super::alloc_mod::VerificationReporterWithSharedPoolSender; - - pub type VerificationReporterWithSharedPoolMpscSender = - VerificationReporterWithSharedPoolSender>; - pub type VerificationReporterWithSharedPoolMpscBoundedSender = - VerificationReporterWithSharedPoolSender>; - pub type VerificationReporterWithVecMpscSender = - VerificationReporterWithSender>>; - pub type VerificationReporterWithVecMpscBoundedSender = - VerificationReporterWithSender>>; +pub struct FailParamHelper<'stamp, 'fargs, 'buf, 'params> { + pub timestamp: &'stamp [u8], + pub error_code: &'fargs dyn EcssEnumeration, + pub small_data_buf: &'buf mut [u8], + pub params: Option<&'params Params>, +} + +/// This helper function simplifies generating completion failures where the error data has +/// the generic [Params] type. +/// +/// A small data buffer needs to be supplied for the [Params::Heapless] type because all data +/// suplied as error data must be held in a slice. Passing a static buffer avoids dynamic memory +/// allocation for this case. +/// +/// Please note that this specific function can not propagate the [Params::Store] variant. +/// This function also might not be able to propagate other error variants which are added in +/// the future. The returned boolean on success denotes whether the error parameters were +/// propagated properly. +pub fn handle_completion_failure_with_generic_params( + tm_sender: &(impl EcssTmSender + ?Sized), + verif_token: VerificationToken, + verif_reporter: &impl VerificationReportingProvider, + helper: FailParamHelper, +) -> Result { + let mut error_params_propagated = true; + if helper.params.is_none() { + verif_reporter.completion_failure( + tm_sender, + verif_token, + FailParams::new(helper.timestamp, helper.error_code, &[]), + )?; + return Ok(true); + } + let error_params = helper.params.unwrap(); + match error_params { + Params::Heapless(heapless_param) => { + heapless_param + .write_to_be_bytes(&mut helper.small_data_buf[..heapless_param.written_len()])?; + verif_reporter.completion_failure( + tm_sender, + verif_token, + FailParams::new( + helper.timestamp, + helper.error_code, + &helper.small_data_buf[..heapless_param.written_len()], + ), + )?; + } + #[cfg(feature = "alloc")] + Params::Vec(vec) => { + verif_reporter.completion_failure( + tm_sender, + verif_token, + FailParams::new(helper.timestamp, helper.error_code, vec), + )?; + } + #[cfg(feature = "alloc")] + Params::String(str) => { + verif_reporter.completion_failure( + tm_sender, + verif_token, + FailParams::new(helper.timestamp, helper.error_code, str.as_bytes()), + )?; + } + _ => { + verif_reporter.completion_failure( + tm_sender, + verif_token, + FailParams::new(helper.timestamp, helper.error_code, &[]), + )?; + error_params_propagated = false; + } + } + Ok(error_params_propagated) +} + +/// This function is similar to [handle_completion_failure_with_error_as_params] but handles the +/// step failure case. +pub fn handle_step_failure_with_generic_params( + tm_sender: &(impl EcssTmSender + ?Sized), + verif_token: VerificationToken, + verif_reporter: &impl VerificationReportingProvider, + helper: FailParamHelper, + step: &impl EcssEnumeration, +) -> Result { + if helper.params.is_none() { + verif_reporter.step_failure( + tm_sender, + verif_token, + FailParamsWithStep::new(helper.timestamp, step, helper.error_code, &[]), + )?; + return Ok(true); + } + let error_params = helper.params.unwrap(); + let mut error_params_propagated = true; + match error_params { + Params::Heapless(heapless_param) => { + heapless_param + .write_to_be_bytes(&mut helper.small_data_buf[..heapless_param.written_len()])?; + verif_reporter.step_failure( + tm_sender, + verif_token, + FailParamsWithStep::new( + helper.timestamp, + step, + helper.error_code, + &helper.small_data_buf[..heapless_param.written_len()], + ), + )?; + } + #[cfg(feature = "alloc")] + Params::Vec(vec) => { + verif_reporter.step_failure( + tm_sender, + verif_token, + FailParamsWithStep::new(helper.timestamp, step, helper.error_code, vec), + )?; + } + #[cfg(feature = "alloc")] + Params::String(str) => { + verif_reporter.step_failure( + tm_sender, + verif_token, + FailParamsWithStep::new(helper.timestamp, step, helper.error_code, str.as_bytes()), + )?; + } + _ => { + verif_reporter.step_failure( + tm_sender, + verif_token, + FailParamsWithStep::new(helper.timestamp, step, helper.error_code, &[]), + )?; + error_params_propagated = false; + } + } + Ok(error_params_propagated) } - */ #[cfg(any(feature = "test_util", test))] pub mod test_util { @@ -1566,73 +1684,19 @@ pub mod test_util { .pop_front() .expect("report queue is empty") } - /* - pub fn verification_info(&self, req_id: &RequestId) -> Option { - let verif_map = self.verification_map.lock().unwrap(); - let value = verif_map.borrow().get(req_id).cloned(); - value - } - - - pub fn check_started(&self, req_id: &RequestId) -> bool { - let verif_map = self.verification_map.lock().unwrap(); - if let Some(entry) = verif_map.borrow().get(req_id) { - return entry.started.unwrap_or(false); - } - false - } - - fn generic_completion_checks( - entry: &VerificationStatus, - step: Option, - completion_success: bool, - ) { - assert!(entry.accepted.unwrap()); - assert!(entry.started.unwrap()); - if let Some(step) = step { - assert!(entry.step_status.unwrap()); - assert_eq!(entry.step, step); - } else { - assert!(entry.step_status.is_none()); - } - assert_eq!(entry.completed.unwrap(), completion_success); - } - - - pub fn assert_completion_failure( - &self, - req_id: &RequestId, - step: Option, - error_code: u64, - ) { - let verif_map = self.verification_map.lock().unwrap(); - if let Some(entry) = verif_map.borrow().get(req_id) { - Self::generic_completion_checks(entry, step, false); - assert_eq!(entry.fail_enum.unwrap(), error_code); - return; - } - panic!("request not in verification map"); - } - - pub fn completion_status(&self, req_id: &RequestId) -> Option { - let verif_map = self.verification_map.lock().unwrap(); - if let Some(entry) = verif_map.borrow().get(req_id) { - return entry.completed; - } - panic!("request not in verification map"); - } - */ } } #[cfg(test)] pub mod tests { + use crate::params::Params; use crate::pool::{SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig}; use crate::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0}; use crate::pus::tests::CommonTmInfo; use crate::pus::verification::{ - EcssTmSender, EcssTmtcError, FailParams, FailParamsWithStep, RequestId, TcStateNone, - VerificationReporter, VerificationReporterCfg, VerificationToken, + handle_step_failure_with_generic_params, EcssTmSender, EcssTmtcError, FailParams, + FailParamsWithStep, RequestId, TcStateNone, VerificationReporter, VerificationReporterCfg, + VerificationToken, }; use crate::pus::{ChannelWithId, PusTmVariant}; use crate::request::MessageMetadata; @@ -1640,6 +1704,7 @@ pub mod tests { use crate::tmtc::{PacketSenderWithSharedPool, SharedPacketPool}; use crate::ComponentId; use alloc::format; + use alloc::string::ToString; use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader}; use spacepackets::ecss::{ EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration, PusError, PusPacket, @@ -1654,8 +1719,9 @@ pub mod tests { use std::vec::Vec; use super::{ - DummyVerificationHook, SeqCountProviderSimple, TcStateAccepted, TcStateStarted, - VerificationHookProvider, VerificationReportingProvider, WasAtLeastAccepted, + handle_completion_failure_with_generic_params, DummyVerificationHook, FailParamHelper, + SeqCountProviderSimple, TcStateAccepted, TcStateStarted, VerificationHookProvider, + VerificationReportingProvider, WasAtLeastAccepted, }; fn is_send(_: &T) {} @@ -1663,6 +1729,7 @@ pub mod tests { fn is_sync(_: &T) {} const EMPTY_STAMP: [u8; 7] = [0; 7]; + const DUMMY_STAMP: &[u8] = &[0, 1, 0, 1, 0, 1, 0]; #[derive(Debug, Eq, PartialEq, Clone)] struct TmInfo { @@ -1740,8 +1807,8 @@ pub mod tests { tc: Vec, } - fn base_reporter(id: ComponentId) -> VerificationReporter { - let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap(); + fn base_reporter(id: ComponentId, max_fail_data_len: usize) -> VerificationReporter { + let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, max_fail_data_len).unwrap(); VerificationReporter::new(id, &cfg) } @@ -1844,66 +1911,57 @@ pub mod tests { .completion_failure(&self.sender, token, params) } - fn completion_success_check(&mut self, incrementing_couters: bool) { - assert_eq!(self.sender.service_queue.borrow().len(), 3); - let mut current_seq_count = 0; + fn check_acceptance_success(&self, timestamp: &[u8; 7]) { let cmp_info = TmInfo { requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - seq_count: current_seq_count, - msg_counter: current_seq_count, - dest_id: self.reporter.dest_id(), - time_stamp: EMPTY_STAMP, - }, + common: CommonTmInfo::new(1, TEST_APID, 0, 0, self.reporter.dest_id(), timestamp), additional_data: None, }; - let mut info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); + let mut service_queue = self.sender.service_queue.borrow_mut(); + assert!(service_queue.len() >= 1); + let info = service_queue.pop_front().unwrap(); assert_eq!(info, cmp_info); + } - if incrementing_couters { - current_seq_count += 1; - } - + fn check_start_success(&mut self, seq_count: u16, msg_counter: u16, timestamp: &[u8]) { + let mut srv_queue = self.sender.service_queue.borrow_mut(); let cmp_info = TmInfo { requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo { - subservice: 3, - apid: TEST_APID, - msg_counter: current_seq_count, - seq_count: current_seq_count, - dest_id: self.reporter.dest_id(), - time_stamp: [0, 1, 0, 1, 0, 1, 0], - }, + common: CommonTmInfo::new( + 3, + TEST_APID, + seq_count, + msg_counter, + self.reporter.dest_id(), + timestamp, + ), additional_data: None, }; - info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); + let info = srv_queue.pop_front().unwrap(); assert_eq!(info, cmp_info); + } - if incrementing_couters { - current_seq_count += 1; - } + fn check_completion_success(&mut self, seq_count: u16, msg_counter: u16) { let cmp_info = TmInfo { requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo { - subservice: 7, - apid: TEST_APID, - msg_counter: current_seq_count, - seq_count: current_seq_count, - dest_id: self.reporter.dest_id(), - time_stamp: EMPTY_STAMP, - }, + common: CommonTmInfo::new( + 7, + TEST_APID, + seq_count, + msg_counter, + self.reporter.dest_id(), + &EMPTY_STAMP, + ), additional_data: None, }; - info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); + let info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); assert_eq!(info, cmp_info); } } impl VerificationReporterTestbench { - fn new(id: ComponentId, tc: PusTcCreator) -> Self { - let reporter = base_reporter(id); + fn new(id: ComponentId, tc: PusTcCreator, max_fail_data_len: usize) -> Self { + let reporter = base_reporter(id, max_fail_data_len); Self { id, sender: TestSender::default(), @@ -1913,36 +1971,10 @@ pub mod tests { } } - fn acceptance_check(&self, time_stamp: &[u8; 7]) { + fn check_acceptance_failure(&mut self, timestamp: &[u8; 7]) { let cmp_info = TmInfo { requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - seq_count: 0, - msg_counter: 0, - dest_id: self.reporter.dest_id(), - time_stamp: *time_stamp, - }, - additional_data: None, - }; - let mut service_queue = self.sender.service_queue.borrow_mut(); - assert_eq!(service_queue.len(), 1); - let info = service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - } - - fn acceptance_fail_check(&mut self, stamp_buf: [u8; 7]) { - let cmp_info = TmInfo { - requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo { - subservice: 2, - seq_count: 0, - apid: TEST_APID, - msg_counter: 0, - dest_id: self.reporter.dest_id(), - time_stamp: stamp_buf, - }, + common: CommonTmInfo::new(2, TEST_APID, 0, 0, self.reporter.dest_id(), timestamp), additional_data: Some([0, 2].to_vec()), }; let service_queue = self.sender.service_queue.get_mut(); @@ -1951,12 +1983,12 @@ pub mod tests { assert_eq!(info, cmp_info); } - fn start_fail_check(&mut self, fail_data_raw: [u8; 4]) { + fn check_start_failure(&mut self, fail_data_raw: [u8; 4]) { let mut srv_queue = self.sender.service_queue.borrow_mut(); assert_eq!(srv_queue.len(), 2); let mut cmp_info = TmInfo { requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo::new_zero_seq_count(1, TEST_APID, 0, EMPTY_STAMP), + common: CommonTmInfo::new_zero_seq_count(1, TEST_APID, 0, &EMPTY_STAMP), additional_data: None, }; let mut info = srv_queue.pop_front().unwrap(); @@ -1964,148 +1996,67 @@ pub mod tests { cmp_info = TmInfo { requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo::new_zero_seq_count(4, TEST_APID, 0, EMPTY_STAMP), + common: CommonTmInfo::new_zero_seq_count(4, TEST_APID, 0, &EMPTY_STAMP), additional_data: Some([&[22], fail_data_raw.as_slice()].concat().to_vec()), }; info = srv_queue.pop_front().unwrap(); assert_eq!(info, cmp_info); } - fn step_success_check(&mut self, time_stamp: &[u8; 7]) { - let mut cmp_info = TmInfo { + fn check_step_success(&mut self, step: u8, timestamp: &[u8; 7]) { + let cmp_info = TmInfo { requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo::new_zero_seq_count(1, TEST_APID, 0, *time_stamp), - additional_data: None, + common: CommonTmInfo::new_zero_seq_count(5, TEST_APID, 0, timestamp), + additional_data: Some([step].to_vec()), }; let mut srv_queue = self.sender.service_queue.borrow_mut(); - let mut info = srv_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - cmp_info = TmInfo { - requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo::new_zero_seq_count(3, TEST_APID, 0, *time_stamp), - additional_data: None, - }; - info = srv_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - cmp_info = TmInfo { - requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo::new_zero_seq_count(5, TEST_APID, 0, *time_stamp), - additional_data: Some([0].to_vec()), - }; - info = srv_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - cmp_info = TmInfo { - requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo::new_zero_seq_count(5, TEST_APID, 0, *time_stamp), - additional_data: Some([1].to_vec()), - }; - info = srv_queue.pop_front().unwrap(); + let info = srv_queue.pop_front().unwrap(); assert_eq!(info, cmp_info); } - fn check_step_failure(&mut self, fail_data_raw: [u8; 4]) { - assert_eq!(self.sender.service_queue.borrow().len(), 4); - let mut cmp_info = TmInfo { - requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo::new_zero_seq_count( - 1, - TEST_APID, - self.reporter.dest_id(), - EMPTY_STAMP, - ), - additional_data: None, - }; - let mut info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo::new_zero_seq_count( - 3, - TEST_APID, - self.reporter.dest_id(), - [0, 1, 0, 1, 0, 1, 0], - ), - additional_data: None, - }; - info = self.sender.service_queue.borrow_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo::new_zero_seq_count( - 5, - TEST_APID, - self.reporter.dest_id(), - EMPTY_STAMP, - ), - additional_data: Some([0].to_vec()), - }; - info = self.sender.service_queue.get_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { + fn check_step_failure( + &mut self, + step: &impl EcssEnumeration, + error_code: &impl EcssEnumeration, + fail_data: &[u8], + ) { + let mut additional_data = Vec::new(); + additional_data.extend(step.to_vec()); + additional_data.extend(error_code.to_vec()); + additional_data.extend(fail_data); + let cmp_info = TmInfo { requestor: MessageMetadata::new(self.request_id.into(), self.id), common: CommonTmInfo::new_zero_seq_count( 6, TEST_APID, self.reporter.dest_id(), - EMPTY_STAMP, - ), - additional_data: Some( - [ - [1].as_slice(), - &[0, 0, 0x10, 0x20], - fail_data_raw.as_slice(), - ] - .concat() - .to_vec(), + &EMPTY_STAMP, ), + additional_data: Some(additional_data), }; - info = self.sender.service_queue.get_mut().pop_front().unwrap(); + let info = self.sender.service_queue.get_mut().pop_front().unwrap(); assert_eq!(info, cmp_info); } - fn completion_fail_check(&mut self) { - assert_eq!(self.sender.service_queue.borrow().len(), 3); - - let mut cmp_info = TmInfo { - requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo::new_zero_seq_count( - 1, - TEST_APID, - self.reporter.dest_id(), - EMPTY_STAMP, - ), - additional_data: None, - }; - let mut info = self.sender.service_queue.get_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - requestor: MessageMetadata::new(self.request_id.into(), self.id), - common: CommonTmInfo::new_zero_seq_count( - 3, - TEST_APID, - self.reporter.dest_id(), - [0, 1, 0, 1, 0, 1, 0], - ), - additional_data: None, - }; - info = self.sender.service_queue.get_mut().pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { + fn check_completion_failure( + &mut self, + error_code: &impl EcssEnumeration, + fail_data: &[u8], + ) { + let mut additional_data = Vec::new(); + additional_data.extend(error_code.to_vec()); + additional_data.extend(fail_data); + let cmp_info = TmInfo { requestor: MessageMetadata::new(self.request_id.into(), self.id), common: CommonTmInfo::new_zero_seq_count( 8, TEST_APID, self.reporter.dest_id(), - EMPTY_STAMP, + &EMPTY_STAMP, ), - additional_data: Some([0, 0, 0x10, 0x20].to_vec()), + additional_data: Some(additional_data), }; - info = self.sender.service_queue.get_mut().pop_front().unwrap(); + let info = self.sender.service_queue.get_mut().pop_front().unwrap(); assert_eq!(info, cmp_info); } } @@ -2128,7 +2079,7 @@ pub mod tests { #[test] fn test_state() { - let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16); assert_eq!(testbench.reporter.apid(), TEST_APID); testbench.reporter.set_apid(TEST_APID + 1); assert_eq!(testbench.reporter.apid(), TEST_APID + 1); @@ -2136,43 +2087,43 @@ pub mod tests { #[test] fn test_basic_acceptance_success() { - let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16); let token = testbench.init(); testbench .acceptance_success(token, &EMPTY_STAMP) .expect("sending acceptance success failed"); - testbench.acceptance_check(&EMPTY_STAMP); + testbench.check_acceptance_success(&EMPTY_STAMP); } #[test] fn test_basic_acceptance_failure() { - let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16); let init_token = testbench.init(); - let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; + let timestamp = [1, 2, 3, 4, 5, 6, 7]; let fail_code = EcssEnumU16::new(2); - let fail_params = FailParams::new_no_fail_data(stamp_buf.as_slice(), &fail_code); + let fail_params = FailParams::new_no_fail_data(timestamp.as_slice(), &fail_code); testbench .acceptance_failure(init_token, fail_params) .expect("sending acceptance failure failed"); - testbench.acceptance_fail_check(stamp_buf); + testbench.check_acceptance_failure(×tamp); } #[test] fn test_basic_acceptance_failure_with_helper() { - let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16); let init_token = testbench.init(); - let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; + let timestamp = [1, 2, 3, 4, 5, 6, 7]; let fail_code = EcssEnumU16::new(2); - let fail_params = FailParams::new_no_fail_data(stamp_buf.as_slice(), &fail_code); + let fail_params = FailParams::new_no_fail_data(timestamp.as_slice(), &fail_code); testbench .acceptance_failure(init_token, fail_params) .expect("sending acceptance failure failed"); - testbench.acceptance_fail_check(stamp_buf); + testbench.check_acceptance_failure(×tamp); } #[test] fn test_acceptance_fail_data_too_large() { - let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 8); let init_token = testbench.init(); let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; let fail_code = EcssEnumU16::new(2); @@ -2204,7 +2155,7 @@ pub mod tests { #[test] fn test_basic_acceptance_failure_with_fail_data() { - let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16); let fail_code = EcssEnumU8::new(10); let fail_data = EcssEnumU32::new(12); let mut fail_data_raw = [0; 4]; @@ -2216,7 +2167,7 @@ pub mod tests { .expect("sending acceptance failure failed"); let cmp_info = TmInfo { requestor: MessageMetadata::new(testbench.request_id.into(), testbench.id), - common: CommonTmInfo::new_zero_seq_count(2, TEST_APID, 0, EMPTY_STAMP), + common: CommonTmInfo::new_zero_seq_count(2, TEST_APID, 0, &EMPTY_STAMP), additional_data: Some([10, 0, 0, 0, 12].to_vec()), }; let mut service_queue = testbench.sender.service_queue.borrow_mut(); @@ -2227,7 +2178,7 @@ pub mod tests { #[test] fn test_start_failure() { - let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16); let init_token = testbench.init(); let fail_code = EcssEnumU8::new(22); let fail_data: i32 = -12; @@ -2241,12 +2192,12 @@ pub mod tests { testbench .start_failure(accepted_token, fail_params) .expect("Start failure failure"); - testbench.start_fail_check(fail_data_raw); + testbench.check_start_failure(fail_data_raw); } #[test] fn test_start_failure_with_helper() { - let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16); let token = testbench.init(); let fail_code = EcssEnumU8::new(22); let fail_data: i32 = -12; @@ -2260,12 +2211,12 @@ pub mod tests { testbench .start_failure(accepted_token, fail_params) .expect("start failure failed"); - testbench.start_fail_check(fail_data_raw); + testbench.check_start_failure(fail_data_raw); } #[test] fn test_steps_success() { - let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16); let token = testbench.init(); let accepted_token = testbench .acceptance_success(token, &EMPTY_STAMP) @@ -2280,12 +2231,15 @@ pub mod tests { .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(1)) .expect("step 1 failed"); assert_eq!(testbench.sender.service_queue.borrow().len(), 4); - testbench.step_success_check(&EMPTY_STAMP); + testbench.check_acceptance_success(&EMPTY_STAMP); + testbench.check_start_success(0, 0, &EMPTY_STAMP); + testbench.check_step_success(0, &EMPTY_STAMP); + testbench.check_step_success(1, &EMPTY_STAMP); } #[test] fn test_step_failure() { - let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16); let token = testbench.init(); let fail_code = EcssEnumU32::new(0x1020); let fail_data: f32 = -22.3232; @@ -2303,7 +2257,7 @@ pub mod tests { .acceptance_success(token, &EMPTY_STAMP) .expect("Sending acceptance success failed"); let started_token = testbench - .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) + .start_success(accepted_token, DUMMY_STAMP) .expect("Sending start success failed"); testbench .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0)) @@ -2311,12 +2265,15 @@ pub mod tests { testbench .step_failure(started_token, fail_params) .expect("Step failure failed"); - testbench.check_step_failure(fail_data_raw); + testbench.check_acceptance_success(&EMPTY_STAMP); + testbench.check_start_success(0, 0, DUMMY_STAMP); + testbench.check_step_success(0, &EMPTY_STAMP); + testbench.check_step_failure(&fail_step, &fail_code, &fail_data_raw); } #[test] fn test_completion_failure() { - let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping()); + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 16); let token = testbench.init(); let fail_code = EcssEnumU32::new(0x1020); let fail_params = FailParams::new_no_fail_data(&EMPTY_STAMP, &fail_code); @@ -2325,29 +2282,34 @@ pub mod tests { .acceptance_success(token, &EMPTY_STAMP) .expect("Sending acceptance success failed"); let started_token = testbench - .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) + .start_success(accepted_token, DUMMY_STAMP) .expect("Sending start success failed"); testbench .completion_failure(started_token, fail_params) .expect("Completion failure"); - testbench.completion_fail_check(); + testbench.check_acceptance_success(&EMPTY_STAMP); + testbench.check_start_success(0, 0, DUMMY_STAMP); + + testbench.check_completion_failure(&fail_code, &[]); } #[test] fn test_complete_success_sequence() { let mut testbench = - VerificationReporterTestbench::new(TEST_COMPONENT_ID_0.id(), create_generic_ping()); + VerificationReporterTestbench::new(TEST_COMPONENT_ID_0.id(), create_generic_ping(), 16); let token = testbench.init(); let accepted_token = testbench .acceptance_success(token, &EMPTY_STAMP) .expect("Sending acceptance success failed"); let started_token = testbench - .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) + .start_success(accepted_token, DUMMY_STAMP) .expect("Sending start success failed"); testbench .completion_success(started_token, &EMPTY_STAMP) .expect("Sending completion success failed"); - testbench.completion_success_check(false); + testbench.check_acceptance_success(&EMPTY_STAMP); + testbench.check_start_success(0, 0, DUMMY_STAMP); + testbench.check_completion_success(0, 0); } #[test] @@ -2367,6 +2329,83 @@ pub mod tests { testbench .completion_success(started_token, &EMPTY_STAMP) .expect("Sending completion success failed"); - testbench.completion_success_check(true); + testbench.check_acceptance_success(&EMPTY_STAMP); + testbench.check_start_success(1, 1, DUMMY_STAMP); + testbench.check_completion_success(2, 2); + } + + #[test] + fn test_completion_failure_helper_string_param() { + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 32); + let token = testbench.init(); + let accepted_token = testbench + .acceptance_success(token, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let mut small_data_buf: [u8; 16] = [0; 16]; + let fail_code = EcssEnumU8::new(1); + let fail_data = "error 404 oh no".to_string(); + let fail_params = Params::String(fail_data.clone()); + let result = handle_completion_failure_with_generic_params( + &testbench.sender, + accepted_token, + &testbench.reporter, + FailParamHelper { + timestamp: &EMPTY_STAMP, + error_code: &fail_code, + small_data_buf: &mut small_data_buf, + params: Some(&fail_params), + }, + ); + assert!(result.unwrap()); + testbench.check_acceptance_success(&EMPTY_STAMP); + testbench.check_completion_failure(&fail_code, fail_data.as_bytes()); + } + + #[test] + fn test_step_failure_helper_string_param() { + let mut testbench = VerificationReporterTestbench::new(0, create_generic_ping(), 32); + let token = testbench.init(); + let accepted_token = testbench + .acceptance_success(token, &EMPTY_STAMP) + .expect("Sending acceptance success failed"); + let started_token = testbench + .start_success(accepted_token, &EMPTY_STAMP) + .expect("Sending start success failed"); + let mut small_data_buf: [u8; 16] = [0; 16]; + let step = EcssEnumU8::new(2); + let fail_code = EcssEnumU8::new(1); + let fail_data = "AAAAAAAAAAAHHHHHH".to_string(); + let fail_params = Params::String(fail_data.clone()); + let result = handle_step_failure_with_generic_params( + &testbench.sender, + started_token, + &testbench.reporter, + FailParamHelper { + timestamp: &EMPTY_STAMP, + error_code: &fail_code, + small_data_buf: &mut small_data_buf, + params: Some(&fail_params), + }, + &step, + ); + assert!(result.unwrap()); + testbench.check_acceptance_success(&EMPTY_STAMP); + testbench.check_start_success(0, 0, &EMPTY_STAMP); + testbench.check_step_failure(&step, &fail_code, fail_data.as_bytes()); + } + + #[test] + fn test_completion_failure_helper_vec_param() { + // TODO: Test this. + } + + #[test] + fn test_completion_failure_helper_raw_param() { + // TODO: Test this. + } + + #[test] + fn test_completion_failure_helper_store_param_ignored() { + // TODO: Test this. } } From 736eb74e66fed421a7a92ca4e8e8f1fea7698735 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 1 May 2024 21:13:08 +0200 Subject: [PATCH 07/15] simplified PUS stack --- satrs-example/src/interface/udp.rs | 3 +- satrs-example/src/pus/action.rs | 64 ++++++---------- satrs-example/src/pus/event.rs | 25 ++++--- satrs-example/src/pus/hk.rs | 61 ++++++--------- satrs-example/src/pus/mod.rs | 74 +++++++++++++----- satrs-example/src/pus/mode.rs | 58 +++++--------- satrs-example/src/pus/scheduler.rs | 33 ++++---- satrs-example/src/pus/stack.rs | 46 ++++++++---- satrs-example/src/pus/test.rs | 37 ++++----- satrs-example/src/tmtc/tc_source.rs | 3 +- satrs/src/pus/action.rs | 4 +- satrs/src/pus/event_srv.rs | 76 +++++++++---------- satrs/src/pus/mod.rs | 35 +++++---- satrs/src/pus/scheduler_srv.rs | 112 ++++++++++++++-------------- satrs/src/pus/test.rs | 102 ++++++++++++------------- 15 files changed, 365 insertions(+), 368 deletions(-) diff --git a/satrs-example/src/interface/udp.rs b/satrs-example/src/interface/udp.rs index d7816e2..e7720bb 100644 --- a/satrs-example/src/interface/udp.rs +++ b/satrs-example/src/interface/udp.rs @@ -3,14 +3,13 @@ use std::net::{SocketAddr, UdpSocket}; use std::sync::mpsc; use log::{info, warn}; +use satrs::pus::HandlingStatus; use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderRaw}; use satrs::{ hal::std::udp_server::{ReceiveResult, UdpTcServer}, pool::{PoolProviderWithGuards, SharedStaticMemoryPool}, }; -use crate::pus::HandlingStatus; - pub trait UdpTmHandler { fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, recv_addr: &SocketAddr); } diff --git a/satrs-example/src/pus/action.rs b/satrs-example/src/pus/action.rs index 7f4e76f..445ad86 100644 --- a/satrs-example/src/pus/action.rs +++ b/satrs-example/src/pus/action.rs @@ -1,4 +1,4 @@ -use log::{error, warn}; +use log::warn; use satrs::action::{ActionRequest, ActionRequestVariant}; use satrs::pool::SharedStaticMemoryPool; use satrs::pus::action::{ @@ -12,7 +12,7 @@ use satrs::pus::verification::{ use satrs::pus::{ ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, EcssTmtcError, GenericConversionError, MpscTcReceiver, - MpscTmAsVecSender, PusPacketHandlerResult, PusReplyHandler, PusServiceHelper, + MpscTmAsVecSender, PusPacketHandlingError, PusReplyHandler, PusServiceHelper, PusTcToRequestConverter, }; use satrs::request::{GenericMessage, UniqueApidTargetId}; @@ -278,43 +278,23 @@ pub struct ActionServiceWrapper TargetedPusService for ActionServiceWrapper { - /// Returns [true] if the packet handling is finished. - fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { - match self.service.poll_and_handle_next_tc(time_stamp) { - Ok(result) => match result { - PusPacketHandlerResult::RequestHandled => {} - PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { - warn!("PUS 8 partial packet handling success: {e:?}") - } - PusPacketHandlerResult::CustomSubservice(invalid, _) => { - warn!("PUS 8 invalid subservice {invalid}"); - } - PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { - warn!("PUS 8 subservice {subservice} not implemented"); - } - PusPacketHandlerResult::Empty => return HandlingStatus::Empty, - }, - Err(error) => { - error!("PUS packet handling error: {error:?}"); - // To avoid permanent loops on error cases. - return HandlingStatus::Empty; - } + const SERVICE_ID: u8 = 8; + const SERVICE_STR: &'static str = "action"; + + delegate::delegate! { + to self.service { + fn poll_and_handle_next_tc( + &mut self, + time_stamp: &[u8], + ) -> Result; + + fn poll_and_handle_next_reply( + &mut self, + time_stamp: &[u8], + ) -> Result; + + fn check_for_request_timeouts(&mut self); } - HandlingStatus::HandledOne - } - - fn poll_and_handle_next_reply(&mut self, time_stamp: &[u8]) -> HandlingStatus { - // This only fails if all senders disconnected. Treat it like an empty queue. - self.service - .poll_and_check_next_reply(time_stamp) - .unwrap_or_else(|e| { - warn!("PUS 8: Handling reply failed with error {e:?}"); - HandlingStatus::Empty - }) - } - - fn check_for_request_timeouts(&mut self) { - self.service.check_for_request_timeouts(); } } @@ -429,7 +409,7 @@ mod tests { } let result = result.unwrap(); match result { - PusPacketHandlerResult::RequestHandled => (), + HandlingStatus::HandledOne => (), _ => panic!("unexpected result {result:?}"), } } @@ -441,19 +421,19 @@ mod tests { } let result = result.unwrap(); match result { - PusPacketHandlerResult::Empty => (), + HandlingStatus::Empty => (), _ => panic!("unexpected result {result:?}"), } } pub fn verify_next_reply_is_handled_properly(&mut self, time_stamp: &[u8]) { - let result = self.service.poll_and_check_next_reply(time_stamp); + let result = self.service.poll_and_handle_next_reply(time_stamp); assert!(result.is_ok()); assert_eq!(result.unwrap(), HandlingStatus::HandledOne); } pub fn verify_all_replies_handled(&mut self, time_stamp: &[u8]) { - let result = self.service.poll_and_check_next_reply(time_stamp); + let result = self.service.poll_and_handle_next_reply(time_stamp); assert!(result.is_ok()); assert_eq!(result.unwrap(), HandlingStatus::Empty); } diff --git a/satrs-example/src/pus/event.rs b/satrs-example/src/pus/event.rs index 4726ba0..2f96ba9 100644 --- a/satrs-example/src/pus/event.rs +++ b/satrs-example/src/pus/event.rs @@ -7,8 +7,9 @@ use satrs::pus::event_man::EventRequestWithToken; use satrs::pus::event_srv::PusEventServiceHandler; use satrs::pus::verification::VerificationReporter; use satrs::pus::{ - EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, - EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, PusPacketHandlerResult, PusServiceHelper, + DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter, + EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, MpscTcReceiver, + MpscTmAsVecSender, PartialPusHandlingError, PusServiceHelper, }; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use satrs_example::config::components::PUS_EVENT_MANAGEMENT; @@ -65,22 +66,24 @@ impl EventServiceWrapper { pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { - match self.handler.poll_and_handle_next_tc(time_stamp) { + let error_handler = |partial_error: &PartialPusHandlingError| { + log::warn!("PUS 5 partial error: {:?}", partial_error); + }; + match self + .handler + .poll_and_handle_next_tc(error_handler, time_stamp) + { Ok(result) => match result { - PusPacketHandlerResult::RequestHandled => {} - PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { - warn!("PUS 5 partial packet handling success: {e:?}") - } - PusPacketHandlerResult::CustomSubservice(invalid, _) => { + DirectPusPacketHandlerResult::Handled(handling_status) => return handling_status, + DirectPusPacketHandlerResult::CustomSubservice(invalid, _) => { warn!("PUS 5 invalid subservice {invalid}"); } - PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { + DirectPusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { warn!("PUS 5 subservice {subservice} not implemented"); } - PusPacketHandlerResult::Empty => return HandlingStatus::Empty, }, Err(error) => { - error!("PUS packet handling error: {error:?}") + error!("PUS 5 packet handling error: {error:?}") } } HandlingStatus::HandledOne diff --git a/satrs-example/src/pus/hk.rs b/satrs-example/src/pus/hk.rs index bbecf19..33e01ac 100644 --- a/satrs-example/src/pus/hk.rs +++ b/satrs-example/src/pus/hk.rs @@ -1,5 +1,4 @@ use derive_new::new; -use log::{error, warn}; use satrs::hk::{CollectionIntervalFactor, HkRequest, HkRequestVariant, UniqueId}; use satrs::pool::SharedStaticMemoryPool; use satrs::pus::verification::{ @@ -10,7 +9,7 @@ use satrs::pus::{ ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, EcssTmtcError, GenericConversionError, MpscTcReceiver, MpscTmAsVecSender, - PusPacketHandlerResult, PusReplyHandler, PusServiceHelper, PusTcToRequestConverter, + PusPacketHandlingError, PusReplyHandler, PusServiceHelper, PusTcToRequestConverter, }; use satrs::request::{GenericMessage, UniqueApidTargetId}; use satrs::spacepackets::ecss::tc::PusTcReader; @@ -24,7 +23,7 @@ use std::time::Duration; use crate::pus::{create_verification_reporter, generic_pus_request_timeout_handler}; use crate::requests::GenericRequestRouter; -use super::{HandlingStatus, PusTargetedRequestService}; +use super::{HandlingStatus, PusTargetedRequestService, TargetedPusService}; #[derive(Clone, PartialEq, Debug, new)] pub struct HkReply { @@ -297,45 +296,27 @@ pub struct HkServiceWrapper, } -impl - HkServiceWrapper +impl TargetedPusService + for HkServiceWrapper { - pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { - match self.service.poll_and_handle_next_tc(time_stamp) { - Ok(result) => match result { - PusPacketHandlerResult::RequestHandled => {} - PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { - warn!("PUS 3 partial packet handling success: {e:?}") - } - PusPacketHandlerResult::CustomSubservice(invalid, _) => { - warn!("PUS 3 invalid subservice {invalid}"); - } - PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { - warn!("PUS 3 subservice {subservice} not implemented"); - } - PusPacketHandlerResult::Empty => return HandlingStatus::Empty, - }, - Err(error) => { - error!("PUS packet handling error: {error:?}"); - // To avoid permanent loops on error cases. - return HandlingStatus::Empty; - } + const SERVICE_ID: u8 = 3; + + const SERVICE_STR: &'static str = "housekeeping"; + + delegate::delegate! { + to self.service { + fn poll_and_handle_next_tc( + &mut self, + time_stamp: &[u8], + ) -> Result; + + fn poll_and_handle_next_reply( + &mut self, + time_stamp: &[u8], + ) -> Result; + + fn check_for_request_timeouts(&mut self); } - HandlingStatus::HandledOne - } - - pub fn poll_and_handle_next_reply(&mut self, time_stamp: &[u8]) -> HandlingStatus { - // This only fails if all senders disconnected. Treat it like an empty queue. - self.service - .poll_and_check_next_reply(time_stamp) - .unwrap_or_else(|e| { - warn!("PUS 3: Handling reply failed with error {e:?}"); - HandlingStatus::Empty - }) - } - - pub fn check_for_request_timeouts(&mut self) { - self.service.check_for_request_timeouts(); } } diff --git a/satrs-example/src/pus/mod.rs b/satrs-example/src/pus/mod.rs index 75b5be3..25fbdf6 100644 --- a/satrs-example/src/pus/mod.rs +++ b/satrs-example/src/pus/mod.rs @@ -8,8 +8,8 @@ use satrs::pus::verification::{ use satrs::pus::{ ActiveRequestMapProvider, ActiveRequestProvider, EcssTcAndToken, EcssTcInMemConverter, EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericConversionError, GenericRoutingError, - PusPacketHandlerResult, PusPacketHandlingError, PusReplyHandler, PusRequestRouter, - PusServiceHelper, PusTcToRequestConverter, TcInMemory, + HandlingStatus, PusPacketHandlingError, PusReplyHandler, PusRequestRouter, PusServiceHelper, + PusTcToRequestConverter, TcInMemory, }; use satrs::queue::{GenericReceiveError, GenericSendError}; use satrs::request::{Apid, GenericMessage, MessageMetadata}; @@ -31,12 +31,6 @@ pub mod scheduler; pub mod stack; pub mod test; -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum HandlingStatus { - Empty, - HandledOne, -} - pub fn create_verification_reporter(owner_id: ComponentId, apid: Apid) -> VerificationReporter { let verif_cfg = VerificationReporterCfg::new(apid, 1, 2, 8).unwrap(); // Every software component which needs to generate verification telemetry, gets a cloned @@ -79,7 +73,7 @@ impl PusTcDistributor { pub fn handle_tc_packet_vec( &mut self, packet_as_vec: PacketAsVec, - ) -> Result { + ) -> Result { self.handle_tc_generic(packet_as_vec.sender_id, None, &packet_as_vec.packet) } @@ -87,7 +81,7 @@ impl PusTcDistributor { &mut self, packet_in_pool: PacketInPool, pus_tc_copy: &[u8], - ) -> Result { + ) -> Result { self.handle_tc_generic( packet_in_pool.sender_id, Some(packet_in_pool.store_addr), @@ -100,7 +94,7 @@ impl PusTcDistributor { sender_id: ComponentId, addr_opt: Option, raw_tc: &[u8], - ) -> Result { + ) -> Result { let pus_tc_result = PusTcReader::new(raw_tc); if pus_tc_result.is_err() { log::warn!( @@ -109,7 +103,8 @@ impl PusTcDistributor { pus_tc_result.unwrap_err() ); log::warn!("raw data: {:x?}", raw_tc); - return Ok(PusPacketHandlerResult::RequestHandled); + // TODO: Shouldn't this be an error? + return Ok(HandlingStatus::HandledOne); } let pus_tc = pus_tc_result.unwrap().0; let init_token = self.verif_reporter.add_tc(&pus_tc); @@ -189,14 +184,53 @@ impl PusTcDistributor { } } } - Ok(PusPacketHandlerResult::RequestHandled) + Ok(HandlingStatus::HandledOne) } } pub trait TargetedPusService { - /// Returns [true] if the packet handling is finished. - fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus; - fn poll_and_handle_next_reply(&mut self, time_stamp: &[u8]) -> HandlingStatus; + const SERVICE_ID: u8; + const SERVICE_STR: &'static str; + + fn poll_and_handle_next_tc_default_handler(&mut self, time_stamp: &[u8]) -> HandlingStatus { + let result = self.poll_and_handle_next_tc(time_stamp); + if let Err(e) = result { + log::error!( + "PUS service {}({})packet handling error: {:?}", + Self::SERVICE_ID, + Self::SERVICE_STR, + e + ); + // To avoid permanent loops on error cases. + return HandlingStatus::Empty; + } + result.unwrap() + } + + fn poll_and_handle_next_reply_default_handler(&mut self, time_stamp: &[u8]) -> HandlingStatus { + // This only fails if all senders disconnected. Treat it like an empty queue. + self.poll_and_handle_next_reply(time_stamp) + .unwrap_or_else(|e| { + warn!( + "PUS servce {}({}): Handling reply failed with error {:?}", + Self::SERVICE_ID, + Self::SERVICE_STR, + e + ); + HandlingStatus::Empty + }) + } + + fn poll_and_handle_next_tc( + &mut self, + time_stamp: &[u8], + ) -> Result; + + fn poll_and_handle_next_reply( + &mut self, + time_stamp: &[u8], + ) -> Result; + fn check_for_request_timeouts(&mut self); } @@ -297,10 +331,10 @@ where pub fn poll_and_handle_next_tc( &mut self, time_stamp: &[u8], - ) -> Result { + ) -> Result { let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; if possible_packet.is_none() { - return Ok(PusPacketHandlerResult::Empty); + return Ok(HandlingStatus::Empty); } let ecss_tc_and_token = possible_packet.unwrap(); self.service_helper @@ -356,7 +390,7 @@ where return Err(e.into()); } } - Ok(PusPacketHandlerResult::RequestHandled) + Ok(HandlingStatus::HandledOne) } fn handle_conversion_to_request_error( @@ -409,7 +443,7 @@ where } } - pub fn poll_and_check_next_reply( + pub fn poll_and_handle_next_reply( &mut self, time_stamp: &[u8], ) -> Result { diff --git a/satrs-example/src/pus/mode.rs b/satrs-example/src/pus/mode.rs index 5f3c0ff..e990a80 100644 --- a/satrs-example/src/pus/mode.rs +++ b/satrs-example/src/pus/mode.rs @@ -1,5 +1,4 @@ use derive_new::new; -use log::{error, warn}; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use std::sync::mpsc; use std::time::Duration; @@ -9,7 +8,7 @@ use satrs::pool::SharedStaticMemoryPool; use satrs::pus::verification::VerificationReporter; use satrs::pus::{ DefaultActiveRequestMap, EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, - EcssTcInVecConverter, MpscTcReceiver, MpscTmAsVecSender, PusPacketHandlerResult, + EcssTcInVecConverter, MpscTcReceiver, MpscTmAsVecSender, PusPacketHandlingError, PusServiceHelper, }; use satrs::request::GenericMessage; @@ -36,7 +35,7 @@ use satrs::{ ComponentId, }; use satrs_example::config::components::PUS_MODE_SERVICE; -use satrs_example::config::{mode_err, tmtc_err}; +use satrs_example::config::{mode_err, tmtc_err, CustomPusServiceId}; use super::{ create_verification_reporter, generic_pus_request_timeout_handler, HandlingStatus, @@ -272,44 +271,27 @@ pub struct ModeServiceWrapper TargetedPusService for ModeServiceWrapper { - /// Returns [true] if the packet handling is finished. - fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { - match self.service.poll_and_handle_next_tc(time_stamp) { - Ok(result) => match result { - PusPacketHandlerResult::RequestHandled => {} - PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { - warn!("PUS mode service: partial packet handling success: {e:?}") - } - PusPacketHandlerResult::CustomSubservice(invalid, _) => { - warn!("PUS mode service: invalid subservice {invalid}"); - } - PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { - warn!("PUS mode service: {subservice} not implemented"); - } - PusPacketHandlerResult::Empty => return HandlingStatus::Empty, - }, - Err(error) => { - error!("PUS mode service: packet handling error: {error:?}"); - // To avoid permanent loops on error cases. - return HandlingStatus::Empty; - } + const SERVICE_ID: u8 = CustomPusServiceId::Mode as u8; + + const SERVICE_STR: &'static str = "mode"; + + delegate::delegate! { + to self.service { + fn poll_and_handle_next_tc( + &mut self, + time_stamp: &[u8], + ) -> Result; + + fn poll_and_handle_next_reply( + &mut self, + time_stamp: &[u8], + ) -> Result; + + fn check_for_request_timeouts(&mut self); } - HandlingStatus::HandledOne - } - - fn poll_and_handle_next_reply(&mut self, time_stamp: &[u8]) -> HandlingStatus { - self.service - .poll_and_check_next_reply(time_stamp) - .unwrap_or_else(|e| { - warn!("PUS action service: Handling reply failed with error {e:?}"); - HandlingStatus::HandledOne - }) - } - - fn check_for_request_timeouts(&mut self) { - self.service.check_for_request_timeouts(); } } + #[cfg(test)] mod tests { use satrs::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0, TEST_UNIQUE_ID_0}; diff --git a/satrs-example/src/pus/scheduler.rs b/satrs-example/src/pus/scheduler.rs index 5346e19..9fa534f 100644 --- a/satrs-example/src/pus/scheduler.rs +++ b/satrs-example/src/pus/scheduler.rs @@ -8,8 +8,9 @@ use satrs::pus::scheduler::{PusScheduler, TcInfo}; use satrs::pus::scheduler_srv::PusSchedServiceHandler; use satrs::pus::verification::VerificationReporter; use satrs::pus::{ - EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, - EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, PusPacketHandlerResult, PusServiceHelper, + DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter, + EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, MpscTcReceiver, + MpscTmAsVecSender, PartialPusHandlingError, PusServiceHelper, }; use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool}; use satrs::ComponentId; @@ -105,25 +106,25 @@ impl } pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { - match self - .pus_11_handler - .poll_and_handle_next_tc(time_stamp, &mut self.sched_tc_pool) - { + let error_handler = |patial_error: &PartialPusHandlingError| { + log::warn!("PUS 11 partial error: {:?}", patial_error); + }; + match self.pus_11_handler.poll_and_handle_next_tc( + error_handler, + time_stamp, + &mut self.sched_tc_pool, + ) { Ok(result) => match result { - PusPacketHandlerResult::RequestHandled => {} - PusPacketHandlerResult::RequestHandledPartialSuccess(e) => { - warn!("PUS11 partial packet handling success: {e:?}") + DirectPusPacketHandlerResult::Handled(handling_status) => return handling_status, + DirectPusPacketHandlerResult::CustomSubservice(invalid, _) => { + warn!("PUS 11 invalid subservice {invalid}"); } - PusPacketHandlerResult::CustomSubservice(invalid, _) => { - warn!("PUS11 invalid subservice {invalid}"); + DirectPusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { + warn!("PUS 11 Subservice {subservice} not implemented"); } - PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { - warn!("PUS11: Subservice {subservice} not implemented"); - } - PusPacketHandlerResult::Empty => return HandlingStatus::Empty, }, Err(error) => { - error!("PUS packet handling error: {error:?}") + error!("PUS 11 packet handling error: {error:?}") } } HandlingStatus::HandledOne diff --git a/satrs-example/src/pus/stack.rs b/satrs-example/src/pus/stack.rs index fac9bce..d964935 100644 --- a/satrs-example/src/pus/stack.rs +++ b/satrs-example/src/pus/stack.rs @@ -2,8 +2,12 @@ use crate::pus::mode::ModeServiceWrapper; use derive_new::new; use satrs::{ pus::{EcssTcInMemConverter, EcssTmSender}, - spacepackets::time::{cds, TimeWriter}, + spacepackets::{ + ecss::PusServiceId, + time::{cds, TimeWriter}, + }, }; +use satrs_example::config::CustomPusServiceId; use super::{ action::ActionServiceWrapper, event::EventServiceWrapper, hk::HkServiceWrapper, @@ -32,6 +36,7 @@ impl .expect("time stamp generation error") .to_vec() .unwrap(); + // Hot loop which will run continuously until all request and reply handling is done. loop { let mut nothing_to_do = true; let mut is_srv_finished = @@ -46,33 +51,46 @@ impl } }; is_srv_finished( - 17, - self.test_srv.poll_and_handle_next_packet(&time_stamp), + PusServiceId::Test as u8, + self.test_srv.poll_and_handle_next_tc(&time_stamp), None, ); is_srv_finished( - 11, + PusServiceId::Scheduling as u8, self.schedule_srv.poll_and_handle_next_tc(&time_stamp), None, ); - is_srv_finished(5, self.event_srv.poll_and_handle_next_tc(&time_stamp), None); is_srv_finished( - 8, - self.action_srv_wrapper.poll_and_handle_next_tc(&time_stamp), + PusServiceId::Event as u8, + self.event_srv.poll_and_handle_next_tc(&time_stamp), + None, + ); + is_srv_finished( + PusServiceId::Action as u8, + self.action_srv_wrapper + .poll_and_handle_next_tc_default_handler(&time_stamp), Some( self.action_srv_wrapper - .poll_and_handle_next_reply(&time_stamp), + .poll_and_handle_next_reply_default_handler(&time_stamp), ), ); is_srv_finished( - 3, - self.hk_srv_wrapper.poll_and_handle_next_tc(&time_stamp), - Some(self.hk_srv_wrapper.poll_and_handle_next_reply(&time_stamp)), + PusServiceId::Housekeeping as u8, + self.hk_srv_wrapper + .poll_and_handle_next_tc_default_handler(&time_stamp), + Some( + self.hk_srv_wrapper + .poll_and_handle_next_reply_default_handler(&time_stamp), + ), ); is_srv_finished( - 200, - self.mode_srv.poll_and_handle_next_tc(&time_stamp), - Some(self.mode_srv.poll_and_handle_next_reply(&time_stamp)), + CustomPusServiceId::Mode as u8, + self.mode_srv + .poll_and_handle_next_tc_default_handler(&time_stamp), + Some( + self.mode_srv + .poll_and_handle_next_reply_default_handler(&time_stamp), + ), ); if nothing_to_do { // Timeout checking is only done once. diff --git a/satrs-example/src/pus/test.rs b/satrs-example/src/pus/test.rs index 585e93b..933c679 100644 --- a/satrs-example/src/pus/test.rs +++ b/satrs-example/src/pus/test.rs @@ -4,11 +4,11 @@ use satrs::event_man::{EventMessage, EventMessageU32}; use satrs::pool::SharedStaticMemoryPool; use satrs::pus::test::PusService17TestHandler; use satrs::pus::verification::{FailParams, VerificationReporter, VerificationReportingProvider}; -use satrs::pus::EcssTcInSharedStoreConverter; use satrs::pus::{ - EcssTcAndToken, EcssTcInMemConverter, EcssTcInVecConverter, EcssTmSender, MpscTcReceiver, - MpscTmAsVecSender, PusPacketHandlerResult, PusServiceHelper, + DirectPusPacketHandlerResult, EcssTcAndToken, EcssTcInMemConverter, EcssTcInVecConverter, + EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, PusServiceHelper, }; +use satrs::pus::{EcssTcInSharedStoreConverter, PartialPusHandlingError}; use satrs::spacepackets::ecss::tc::PusTcReader; use satrs::spacepackets::ecss::PusPacket; use satrs::spacepackets::time::cds::CdsTime; @@ -67,27 +67,29 @@ pub struct TestCustomServiceWrapper TestCustomServiceWrapper { - pub fn poll_and_handle_next_packet(&mut self, time_stamp: &[u8]) -> HandlingStatus { - let res = self.handler.poll_and_handle_next_tc(time_stamp); + pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { + let error_handler = |patial_error: &PartialPusHandlingError| { + log::warn!("PUS 17 partial error: {:?}", patial_error); + }; + let res = self + .handler + .poll_and_handle_next_tc(error_handler, time_stamp); if res.is_err() { - warn!("PUS17 handler failed with error {:?}", res.unwrap_err()); + warn!("PUS 17 handler error: {:?}", res.unwrap_err()); return HandlingStatus::HandledOne; } match res.unwrap() { - PusPacketHandlerResult::RequestHandled => { - info!("Received PUS ping command TC[17,1]"); - info!("Sent ping reply PUS TM[17,2]"); + DirectPusPacketHandlerResult::Handled(handling_status) => { + if handling_status == HandlingStatus::HandledOne { + info!("Received PUS ping command TC[17,1]"); + info!("Sent ping reply PUS TM[17,2]"); + } + return handling_status; } - PusPacketHandlerResult::RequestHandledPartialSuccess(partial_err) => { - warn!( - "Handled PUS ping command with partial success: {:?}", - partial_err - ); - } - PusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { + DirectPusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { warn!("PUS17: Subservice {subservice} not implemented") } - PusPacketHandlerResult::CustomSubservice(subservice, token) => { + DirectPusPacketHandlerResult::CustomSubservice(subservice, token) => { let (tc, _) = PusTcReader::new( self.handler .service_helper @@ -135,7 +137,6 @@ impl .expect("Sending start failure verification failed"); } } - PusPacketHandlerResult::Empty => return HandlingStatus::Empty, } HandlingStatus::HandledOne } diff --git a/satrs-example/src/tmtc/tc_source.rs b/satrs-example/src/tmtc/tc_source.rs index bd99fb2..94b642c 100644 --- a/satrs-example/src/tmtc/tc_source.rs +++ b/satrs-example/src/tmtc/tc_source.rs @@ -1,12 +1,13 @@ use satrs::{ pool::PoolProvider, + pus::HandlingStatus, tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool, SharedPacketPool}, }; use std::sync::mpsc::{self, TryRecvError}; use satrs::pus::MpscTmAsVecSender; -use crate::pus::{HandlingStatus, PusTcDistributor}; +use crate::pus::PusTcDistributor; // TC source components where static pools are the backing memory of the received telecommands. pub struct TcSourceTaskStatic { diff --git a/satrs/src/pus/action.rs b/satrs/src/pus/action.rs index 75d5962..6bcd270 100644 --- a/satrs/src/pus/action.rs +++ b/satrs/src/pus/action.rs @@ -54,11 +54,11 @@ pub type GenericActionReplyPus = GenericMessage; impl GenericActionReplyPus { pub fn new_action_reply( - requestor_info: MessageMetadata, + replier_info: MessageMetadata, action_id: ActionId, reply: ActionReplyVariant, ) -> Self { - Self::new(requestor_info, ActionReplyPus::new(action_id, reply)) + Self::new(replier_info, ActionReplyPus::new(action_id, reply)) } } diff --git a/satrs/src/pus/event_srv.rs b/satrs/src/pus/event_srv.rs index 8ea54ec..cb1bcb5 100644 --- a/satrs/src/pus/event_srv.rs +++ b/satrs/src/pus/event_srv.rs @@ -1,7 +1,7 @@ use crate::events::EventU32; use crate::pus::event_man::{EventRequest, EventRequestWithToken}; use crate::pus::verification::TcStateToken; -use crate::pus::{PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError}; +use crate::pus::{DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError}; use crate::queue::GenericSendError; use spacepackets::ecss::event::Subservice; use spacepackets::ecss::PusPacket; @@ -10,7 +10,7 @@ use std::sync::mpsc::Sender; use super::verification::VerificationReportingProvider; use super::{ EcssTcInMemConverter, EcssTcReceiver, EcssTmSender, GenericConversionError, - GenericRoutingError, PusServiceHelper, + GenericRoutingError, HandlingStatus, PusServiceHelper, }; pub struct PusEventServiceHandler< @@ -46,13 +46,14 @@ impl< } } - pub fn poll_and_handle_next_tc( + pub fn poll_and_handle_next_tc( &mut self, + mut error_callback: ErrorCb, time_stamp: &[u8], - ) -> Result { + ) -> Result { let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; if possible_packet.is_none() { - return Ok(PusPacketHandlerResult::Empty); + return Ok(HandlingStatus::Empty.into()); } let ecss_tc_and_token = possible_packet.unwrap(); self.service_helper @@ -62,13 +63,13 @@ impl< let subservice = tc.subservice(); let srv = Subservice::try_from(subservice); if srv.is_err() { - return Ok(PusPacketHandlerResult::CustomSubservice( + return Ok(DirectPusPacketHandlerResult::CustomSubservice( tc.subservice(), ecss_tc_and_token.token, )); } - let handle_enable_disable_request = - |enable: bool| -> Result { + let mut handle_enable_disable_request = + |enable: bool| -> Result { if tc.user_data().len() < 4 { return Err(GenericConversionError::NotEnoughAppData { expected: 4, @@ -79,21 +80,20 @@ impl< let user_data = tc.user_data(); let event_u32 = EventU32::from(u32::from_be_bytes(user_data[0..4].try_into().unwrap())); - let start_token = self - .service_helper - .common - .verif_reporter - .start_success( - &self.service_helper.common.tm_sender, - ecss_tc_and_token.token, - time_stamp, - ) - .map_err(|_| PartialPusHandlingError::Verification); - let partial_error = start_token.clone().err(); let mut token: TcStateToken = ecss_tc_and_token.token.into(); - if let Ok(start_token) = start_token { - token = start_token.into(); + match self.service_helper.common.verif_reporter.start_success( + &self.service_helper.common.tm_sender, + ecss_tc_and_token.token, + time_stamp, + ) { + Ok(start_token) => { + token = start_token.into(); + } + Err(e) => { + error_callback(&PartialPusHandlingError::Verification(e)); + } } + let event_req_with_token = if enable { EventRequestWithToken { request: EventRequest::Enable(event_u32), @@ -112,12 +112,7 @@ impl< GenericSendError::RxDisconnected, )) })?; - if let Some(partial_error) = partial_error { - return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess( - partial_error, - )); - } - Ok(PusPacketHandlerResult::RequestHandled) + Ok(HandlingStatus::HandledOne.into()) }; match srv.unwrap() { @@ -136,14 +131,14 @@ impl< handle_enable_disable_request(false)?; } Subservice::TcReportDisabledList | Subservice::TmDisabledEventsReport => { - return Ok(PusPacketHandlerResult::SubserviceNotImplemented( + return Ok(DirectPusPacketHandlerResult::SubserviceNotImplemented( subservice, ecss_tc_and_token.token, )); } } - Ok(PusPacketHandlerResult::RequestHandled) + Ok(HandlingStatus::HandledOne.into()) } } @@ -167,7 +162,7 @@ mod tests { use crate::pus::verification::{ RequestId, VerificationReporter, VerificationReportingProvider, }; - use crate::pus::{GenericConversionError, MpscTcReceiver}; + use crate::pus::{GenericConversionError, HandlingStatus, MpscTcReceiver}; use crate::tmtc::PacketSenderWithSharedPool; use crate::{ events::EventU32, @@ -175,7 +170,7 @@ mod tests { event_man::EventRequestWithToken, tests::PusServiceHandlerWithSharedStoreCommon, verification::{TcStateAccepted, VerificationToken}, - EcssTcInSharedStoreConverter, PusPacketHandlerResult, PusPacketHandlingError, + DirectPusPacketHandlerResult, EcssTcInSharedStoreConverter, PusPacketHandlingError, }, }; @@ -229,9 +224,11 @@ mod tests { } impl SimplePusPacketHandler for Pus5HandlerWithStoreTester { - fn handle_one_tc(&mut self) -> Result { + fn handle_one_tc( + &mut self, + ) -> Result { let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); - self.handler.poll_and_handle_next_tc(&time_stamp) + self.handler.poll_and_handle_next_tc(|_| {}, &time_stamp) } } @@ -293,10 +290,13 @@ mod tests { let result = test_harness.handle_one_tc(); assert!(result.is_ok()); let result = result.unwrap(); - if let PusPacketHandlerResult::Empty = result { - } else { - panic!("unexpected result type {result:?}") - } + assert!( + matches!( + result, + DirectPusPacketHandlerResult::Handled(HandlingStatus::Empty) + ), + "unexpected result type {result:?}" + ) } #[test] @@ -311,7 +311,7 @@ mod tests { let result = test_harness.handle_one_tc(); assert!(result.is_ok()); let result = result.unwrap(); - if let PusPacketHandlerResult::CustomSubservice(subservice, _) = result { + if let DirectPusPacketHandlerResult::CustomSubservice(subservice, _) = result { assert_eq!(subservice, 200); } else { panic!("unexpected result type {result:?}") diff --git a/satrs/src/pus/mod.rs b/satrs/src/pus/mod.rs index 4a76757..349df62 100644 --- a/satrs/src/pus/mod.rs +++ b/satrs/src/pus/mod.rs @@ -45,6 +45,15 @@ pub use std_mod::*; use self::verification::VerificationReportingProvider; +/// Generic handling status for an object which is able to continuosly handle a queue to handle +/// request or replies until the queue is empty. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum HandlingStatus { + HandledOne, + Empty, +} + #[derive(Debug, PartialEq, Eq, Clone)] pub enum PusTmVariant<'time, 'src_data> { InStore(PoolAddr), @@ -649,14 +658,11 @@ pub mod alloc_mod { #[cfg(feature = "std")] pub mod std_mod { + use super::*; use crate::pool::{ PoolAddr, PoolError, PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool, }; use crate::pus::verification::{TcStateAccepted, VerificationToken}; - use crate::pus::{ - EcssTcAndToken, EcssTcReceiver, EcssTmSender, EcssTmtcError, GenericReceiveError, - GenericSendError, PusTmVariant, TryRecvTmtcError, - }; use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use crate::ComponentId; use alloc::vec::Vec; @@ -920,26 +926,24 @@ pub mod std_mod { #[error("generic timestamp generation error")] Time(#[from] StdTimestampError), #[error("error sending telemetry: {0}")] - TmSend(#[from] EcssTmtcError), + TmSend(EcssTmtcError), #[error("error sending verification message")] - Verification, + Verification(EcssTmtcError), #[error("invalid verification token")] NoVerificationToken, } /// Generic result type for handlers which can process PUS packets. #[derive(Debug, Clone)] - pub enum PusPacketHandlerResult { - RequestHandled, - RequestHandledPartialSuccess(PartialPusHandlingError), + pub enum DirectPusPacketHandlerResult { + Handled(HandlingStatus), SubserviceNotImplemented(u8, VerificationToken), CustomSubservice(u8, VerificationToken), - Empty, } - impl From for PusPacketHandlerResult { - fn from(value: PartialPusHandlingError) -> Self { - Self::RequestHandledPartialSuccess(value) + impl From for DirectPusPacketHandlerResult { + fn from(value: HandlingStatus) -> Self { + Self::Handled(value) } } @@ -1222,7 +1226,7 @@ pub mod test_util { use super::{ verification::{self, TcStateAccepted, VerificationToken}, - PusPacketHandlerResult, PusPacketHandlingError, + DirectPusPacketHandlerResult, PusPacketHandlingError, }; pub const TEST_APID: u16 = 0x101; @@ -1246,7 +1250,8 @@ pub mod test_util { } pub trait SimplePusPacketHandler { - fn handle_one_tc(&mut self) -> Result; + fn handle_one_tc(&mut self) + -> Result; } } diff --git a/satrs/src/pus/scheduler_srv.rs b/satrs/src/pus/scheduler_srv.rs index 4d538b8..a3930d9 100644 --- a/satrs/src/pus/scheduler_srv.rs +++ b/satrs/src/pus/scheduler_srv.rs @@ -1,11 +1,12 @@ use super::scheduler::PusSchedulerProvider; use super::verification::{VerificationReporter, VerificationReportingProvider}; use super::{ - EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiver, - EcssTmSender, MpscTcReceiver, PusServiceHelper, + DirectPusPacketHandlerResult, EcssTcInMemConverter, EcssTcInSharedStoreConverter, + EcssTcInVecConverter, EcssTcReceiver, EcssTmSender, HandlingStatus, MpscTcReceiver, + PartialPusHandlingError, PusServiceHelper, }; use crate::pool::PoolProvider; -use crate::pus::{PusPacketHandlerResult, PusPacketHandlingError}; +use crate::pus::PusPacketHandlingError; use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use alloc::string::ToString; use spacepackets::ecss::{scheduling, PusPacket}; @@ -64,14 +65,15 @@ impl< &self.scheduler } - pub fn poll_and_handle_next_tc( + pub fn poll_and_handle_next_tc( &mut self, + mut error_callback: ErrorCb, time_stamp: &[u8], sched_tc_pool: &mut (impl PoolProvider + ?Sized), - ) -> Result { + ) -> Result { let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; if possible_packet.is_none() { - return Ok(PusPacketHandlerResult::Empty); + return Ok(HandlingStatus::Empty.into()); } let ecss_tc_and_token = possible_packet.unwrap(); self.service_helper @@ -81,34 +83,34 @@ impl< let subservice = PusPacket::subservice(&tc); let standard_subservice = scheduling::Subservice::try_from(subservice); if standard_subservice.is_err() { - return Ok(PusPacketHandlerResult::CustomSubservice( + return Ok(DirectPusPacketHandlerResult::CustomSubservice( subservice, ecss_tc_and_token.token, )); } - let partial_error = None; match standard_subservice.unwrap() { scheduling::Subservice::TcEnableScheduling => { - let start_token = self - .service_helper - .verif_reporter() - .start_success( - &self.service_helper.common.tm_sender, - ecss_tc_and_token.token, - time_stamp, - ) - .expect("Error sending start success"); - + let opt_started_token = match self.service_helper.verif_reporter().start_success( + &self.service_helper.common.tm_sender, + ecss_tc_and_token.token, + time_stamp, + ) { + Ok(started_token) => Some(started_token), + Err(e) => { + error_callback(&PartialPusHandlingError::Verification(e)); + None + } + }; self.scheduler.enable(); - if self.scheduler.is_enabled() { - self.service_helper - .verif_reporter() - .completion_success( - &self.service_helper.common.tm_sender, - start_token, - time_stamp, - ) - .expect("Error sending completion success"); + + if self.scheduler.is_enabled() && opt_started_token.is_some() { + if let Err(e) = self.service_helper.verif_reporter().completion_success( + &self.service_helper.common.tm_sender, + opt_started_token.unwrap(), + time_stamp, + ) { + error_callback(&PartialPusHandlingError::Verification(e)); + } } else { return Err(PusPacketHandlingError::Other( "failed to enabled scheduler".to_string(), @@ -116,26 +118,27 @@ impl< } } scheduling::Subservice::TcDisableScheduling => { - let start_token = self - .service_helper - .verif_reporter() - .start_success( - &self.service_helper.common.tm_sender, - ecss_tc_and_token.token, - time_stamp, - ) - .expect("Error sending start success"); + let opt_started_token = match self.service_helper.verif_reporter().start_success( + &self.service_helper.common.tm_sender, + ecss_tc_and_token.token, + time_stamp, + ) { + Ok(started_token) => Some(started_token), + Err(e) => { + error_callback(&PartialPusHandlingError::Verification(e)); + None + } + }; self.scheduler.disable(); - if !self.scheduler.is_enabled() { - self.service_helper - .verif_reporter() - .completion_success( - &self.service_helper.common.tm_sender, - start_token, - time_stamp, - ) - .expect("Error sending completion success"); + if !self.scheduler.is_enabled() && opt_started_token.is_some() { + if let Err(e) = self.service_helper.verif_reporter().completion_success( + &self.service_helper.common.tm_sender, + opt_started_token.unwrap(), + time_stamp, + ) { + error_callback(&PartialPusHandlingError::Verification(e)); + } } else { return Err(PusPacketHandlingError::Other( "failed to disable scheduler".to_string(), @@ -194,18 +197,13 @@ impl< } _ => { // Treat unhandled standard subservices as custom subservices for now. - return Ok(PusPacketHandlerResult::CustomSubservice( + return Ok(DirectPusPacketHandlerResult::CustomSubservice( subservice, ecss_tc_and_token.token, )); } } - if let Some(partial_error) = partial_error { - return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess( - partial_error, - )); - } - Ok(PusPacketHandlerResult::RequestHandled) + Ok(HandlingStatus::HandledOne.into()) } } /// Helper type definition for a PUS 11 handler with a dynamic TMTC memory backend and regular @@ -257,7 +255,7 @@ mod tests { verification::{RequestId, TcStateAccepted, VerificationToken}, EcssTcInSharedStoreConverter, }; - use crate::pus::{MpscTcReceiver, PusPacketHandlerResult, PusPacketHandlingError}; + use crate::pus::{DirectPusPacketHandlerResult, MpscTcReceiver, PusPacketHandlingError}; use crate::tmtc::PacketSenderWithSharedPool; use alloc::collections::VecDeque; use delegate::delegate; @@ -298,10 +296,12 @@ mod tests { } } - pub fn handle_one_tc(&mut self) -> Result { + pub fn handle_one_tc( + &mut self, + ) -> Result { let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); self.handler - .poll_and_handle_next_tc(&time_stamp, &mut self.sched_tc_pool) + .poll_and_handle_next_tc(|_| {}, &time_stamp, &mut self.sched_tc_pool) } } @@ -387,7 +387,7 @@ mod tests { let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); test_harness .handler - .poll_and_handle_next_tc(&time_stamp, &mut test_harness.sched_tc_pool) + .poll_and_handle_next_tc(|_| {}, &time_stamp, &mut test_harness.sched_tc_pool) .unwrap(); test_harness.check_next_verification_tm(1, request_id); test_harness.check_next_verification_tm(3, request_id); diff --git a/satrs/src/pus/test.rs b/satrs/src/pus/test.rs index a1ca93e..9bd0937 100644 --- a/satrs/src/pus/test.rs +++ b/satrs/src/pus/test.rs @@ -1,5 +1,5 @@ use crate::pus::{ - PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError, PusTmVariant, + DirectPusPacketHandlerResult, PartialPusHandlingError, PusPacketHandlingError, PusTmVariant, }; use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader}; @@ -10,7 +10,7 @@ use std::sync::mpsc; use super::verification::{VerificationReporter, VerificationReportingProvider}; use super::{ EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiver, - EcssTmSender, GenericConversionError, MpscTcReceiver, PusServiceHelper, + EcssTmSender, GenericConversionError, HandlingStatus, MpscTcReceiver, PusServiceHelper, }; /// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets. @@ -43,13 +43,14 @@ impl< Self { service_helper } } - pub fn poll_and_handle_next_tc( + pub fn poll_and_handle_next_tc( &mut self, + mut error_callback: ErrorCb, time_stamp: &[u8], - ) -> Result { + ) -> Result { let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?; if possible_packet.is_none() { - return Ok(PusPacketHandlerResult::Empty); + return Ok(HandlingStatus::Empty.into()); } let ecss_tc_and_token = possible_packet.unwrap(); self.service_helper @@ -60,21 +61,16 @@ impl< return Err(GenericConversionError::WrongService(tc.service()).into()); } if tc.subservice() == 1 { - let mut partial_error = None; - let result = self - .service_helper - .verif_reporter() - .start_success( - &self.service_helper.common.tm_sender, - ecss_tc_and_token.token, - time_stamp, - ) - .map_err(|_| PartialPusHandlingError::Verification); - let start_token = if let Ok(result) = result { - Some(result) - } else { - partial_error = Some(result.unwrap_err()); - None + let opt_started_token = match self.service_helper.verif_reporter().start_success( + &self.service_helper.common.tm_sender, + ecss_tc_and_token.token, + time_stamp, + ) { + Ok(token) => Some(token), + Err(e) => { + error_callback(&PartialPusHandlingError::Verification(e)); + None + } }; // Sequence count will be handled centrally in TM funnel. // It is assumed that the verification reporter was built with a valid APID, so we use @@ -83,42 +79,30 @@ impl< SpHeader::new_for_unseg_tm(self.service_helper.verif_reporter().apid(), 0, 0); let tc_header = PusTmSecondaryHeader::new_simple(17, 2, time_stamp); let ping_reply = PusTmCreator::new(reply_header, tc_header, &[], true); - let result = self + if let Err(e) = self .service_helper .common .tm_sender .send_tm(self.service_helper.id(), PusTmVariant::Direct(ping_reply)) - .map_err(PartialPusHandlingError::TmSend); - if let Err(err) = result { - partial_error = Some(err); + { + error_callback(&PartialPusHandlingError::TmSend(e)); } - - if let Some(start_token) = start_token { - if self - .service_helper - .verif_reporter() - .completion_success( - &self.service_helper.common.tm_sender, - start_token, - time_stamp, - ) - .is_err() - { - partial_error = Some(PartialPusHandlingError::Verification) + if let Some(start_token) = opt_started_token { + if let Err(e) = self.service_helper.verif_reporter().completion_success( + &self.service_helper.common.tm_sender, + start_token, + time_stamp, + ) { + error_callback(&PartialPusHandlingError::Verification(e)); } } - if let Some(partial_error) = partial_error { - return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess( - partial_error, - )); - }; } else { - return Ok(PusPacketHandlerResult::CustomSubservice( + return Ok(DirectPusPacketHandlerResult::CustomSubservice( tc.subservice(), ecss_tc_and_token.token, )); } - Ok(PusPacketHandlerResult::RequestHandled) + Ok(HandlingStatus::HandledOne.into()) } } @@ -158,8 +142,9 @@ mod tests { }; use crate::pus::verification::{TcStateAccepted, VerificationToken}; use crate::pus::{ - EcssTcInSharedStoreConverter, EcssTcInVecConverter, GenericConversionError, MpscTcReceiver, - MpscTmAsVecSender, PusPacketHandlerResult, PusPacketHandlingError, + DirectPusPacketHandlerResult, EcssTcInSharedStoreConverter, EcssTcInVecConverter, + GenericConversionError, HandlingStatus, MpscTcReceiver, MpscTmAsVecSender, + PartialPusHandlingError, PusPacketHandlingError, }; use crate::tmtc::PacketSenderWithSharedPool; use crate::ComponentId; @@ -221,9 +206,12 @@ mod tests { } } impl SimplePusPacketHandler for Pus17HandlerWithStoreTester { - fn handle_one_tc(&mut self) -> Result { + fn handle_one_tc( + &mut self, + ) -> Result { let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); - self.handler.poll_and_handle_next_tc(&time_stamp) + self.handler + .poll_and_handle_next_tc(|_partial_error: &PartialPusHandlingError| {}, &time_stamp) } } @@ -276,9 +264,12 @@ mod tests { } } impl SimplePusPacketHandler for Pus17HandlerWithVecTester { - fn handle_one_tc(&mut self) -> Result { + fn handle_one_tc( + &mut self, + ) -> Result { let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap(); - self.handler.poll_and_handle_next_tc(&time_stamp) + self.handler + .poll_and_handle_next_tc(|_partial_error: &PartialPusHandlingError| {}, &time_stamp) } } @@ -328,10 +319,11 @@ mod tests { let mut test_harness = Pus17HandlerWithStoreTester::new(0); let result = test_harness.handle_one_tc(); assert!(result.is_ok()); - let result = result.unwrap(); - if let PusPacketHandlerResult::Empty = result { - } else { - panic!("unexpected result type {result:?}") + match result.unwrap() { + DirectPusPacketHandlerResult::Handled(handled) => { + assert_eq!(handled, HandlingStatus::Empty); + }, + _ => panic!("unexpected result"), } } @@ -367,7 +359,7 @@ mod tests { let result = test_harness.handle_one_tc(); assert!(result.is_ok()); let result = result.unwrap(); - if let PusPacketHandlerResult::CustomSubservice(subservice, _) = result { + if let DirectPusPacketHandlerResult::CustomSubservice(subservice, _) = result { assert_eq!(subservice, 200); } else { panic!("unexpected result type {result:?}") From 45eb2f13432e1d3f37df2865d5208186d601f710 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 1 May 2024 21:16:26 +0200 Subject: [PATCH 08/15] cargo fmt --- satrs/src/pus/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satrs/src/pus/test.rs b/satrs/src/pus/test.rs index 9bd0937..5094be9 100644 --- a/satrs/src/pus/test.rs +++ b/satrs/src/pus/test.rs @@ -322,7 +322,7 @@ mod tests { match result.unwrap() { DirectPusPacketHandlerResult::Handled(handled) => { assert_eq!(handled, HandlingStatus::Empty); - }, + } _ => panic!("unexpected result"), } } From 7e78e70a172ccca64146d8b41ac7abf5d88d98d5 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 2 May 2024 11:14:05 +0200 Subject: [PATCH 09/15] try unifying some direct PUS handler code --- satrs-example/src/pus/event.rs | 61 ++++++++++++++------ satrs-example/src/pus/mod.rs | 5 ++ satrs-example/src/pus/scheduler.rs | 63 +++++++++++++++------ satrs-example/src/pus/test.rs | 90 +++++++++++++++++++----------- 4 files changed, 153 insertions(+), 66 deletions(-) diff --git a/satrs-example/src/pus/event.rs b/satrs-example/src/pus/event.rs index 2f96ba9..80951a4 100644 --- a/satrs-example/src/pus/event.rs +++ b/satrs-example/src/pus/event.rs @@ -1,7 +1,6 @@ use std::sync::mpsc; use crate::pus::create_verification_reporter; -use log::{error, warn}; use satrs::pool::SharedStaticMemoryPool; use satrs::pus::event_man::EventRequestWithToken; use satrs::pus::event_srv::PusEventServiceHandler; @@ -11,10 +10,11 @@ use satrs::pus::{ EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, PartialPusHandlingError, PusServiceHelper, }; +use satrs::spacepackets::ecss::PusServiceId; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use satrs_example::config::components::PUS_EVENT_MANAGEMENT; -use super::HandlingStatus; +use super::{DirectPusService, HandlingStatus}; pub fn create_event_service_static( tm_sender: PacketSenderWithSharedPool, @@ -62,28 +62,55 @@ pub struct EventServiceWrapper, } +impl DirectPusService + for EventServiceWrapper +{ + const SERVICE_ID: u8 = PusServiceId::Event as u8; + + const SERVICE_STR: &'static str = "events"; +} + impl EventServiceWrapper { pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { let error_handler = |partial_error: &PartialPusHandlingError| { - log::warn!("PUS 5 partial error: {:?}", partial_error); + log::warn!( + "PUS {}({}) partial error: {:?}", + Self::SERVICE_ID, + Self::SERVICE_STR, + partial_error + ); }; - match self + let result = self .handler - .poll_and_handle_next_tc(error_handler, time_stamp) - { - Ok(result) => match result { - DirectPusPacketHandlerResult::Handled(handling_status) => return handling_status, - DirectPusPacketHandlerResult::CustomSubservice(invalid, _) => { - warn!("PUS 5 invalid subservice {invalid}"); - } - DirectPusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { - warn!("PUS 5 subservice {subservice} not implemented"); - } - }, - Err(error) => { - error!("PUS 5 packet handling error: {error:?}") + .poll_and_handle_next_tc(error_handler, time_stamp); + if let Err(e) = result { + log::warn!( + "PUS {}({}) error: {:?}", + Self::SERVICE_ID, + Self::SERVICE_STR, + e + ); + return HandlingStatus::HandledOne; + } + match result.unwrap() { + DirectPusPacketHandlerResult::Handled(handling_status) => return handling_status, + DirectPusPacketHandlerResult::CustomSubservice(subservice, _) => { + log::warn!( + "PUS {}({}) subservice {} not implemented", + Self::SERVICE_ID, + Self::SERVICE_STR, + subservice + ); + } + DirectPusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { + log::warn!( + "PUS {}({}) subservice {} not implemented", + Self::SERVICE_ID, + Self::SERVICE_STR, + subservice + ); } } HandlingStatus::HandledOne diff --git a/satrs-example/src/pus/mod.rs b/satrs-example/src/pus/mod.rs index 25fbdf6..37367ff 100644 --- a/satrs-example/src/pus/mod.rs +++ b/satrs-example/src/pus/mod.rs @@ -524,6 +524,11 @@ pub fn generic_pus_request_timeout_handler( Ok(()) } +pub trait DirectPusService { + const SERVICE_ID: u8; + const SERVICE_STR: &'static str; +} + #[cfg(test)] pub(crate) mod tests { use std::time::Duration; diff --git a/satrs-example/src/pus/scheduler.rs b/satrs-example/src/pus/scheduler.rs index 9fa534f..0c2242d 100644 --- a/satrs-example/src/pus/scheduler.rs +++ b/satrs-example/src/pus/scheduler.rs @@ -2,7 +2,7 @@ use std::sync::mpsc; use std::time::Duration; use crate::pus::create_verification_reporter; -use log::{error, info, warn}; +use log::info; use satrs::pool::{PoolProvider, StaticMemoryPool}; use satrs::pus::scheduler::{PusScheduler, TcInfo}; use satrs::pus::scheduler_srv::PusSchedServiceHandler; @@ -12,11 +12,12 @@ use satrs::pus::{ EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, PartialPusHandlingError, PusServiceHelper, }; +use satrs::spacepackets::ecss::PusServiceId; use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool}; use satrs::ComponentId; use satrs_example::config::components::PUS_SCHED_SERVICE; -use super::HandlingStatus; +use super::{DirectPusService, HandlingStatus}; pub trait TcReleaser { fn release(&mut self, sender_id: ComponentId, enabled: bool, info: &TcInfo, tc: &[u8]) -> bool; @@ -77,6 +78,13 @@ pub struct SchedulingServiceWrapper, } +impl DirectPusService + for SchedulingServiceWrapper +{ + const SERVICE_ID: u8 = PusServiceId::Verification as u8; + + const SERVICE_STR: &'static str = "verification"; +} impl SchedulingServiceWrapper @@ -106,25 +114,46 @@ impl } pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { - let error_handler = |patial_error: &PartialPusHandlingError| { - log::warn!("PUS 11 partial error: {:?}", patial_error); + let error_handler = |partial_error: &PartialPusHandlingError| { + log::warn!( + "PUS {}({}) partial error: {:?}", + Self::SERVICE_ID, + Self::SERVICE_STR, + partial_error + ); }; - match self.pus_11_handler.poll_and_handle_next_tc( + + let result = self.pus_11_handler.poll_and_handle_next_tc( error_handler, time_stamp, &mut self.sched_tc_pool, - ) { - Ok(result) => match result { - DirectPusPacketHandlerResult::Handled(handling_status) => return handling_status, - DirectPusPacketHandlerResult::CustomSubservice(invalid, _) => { - warn!("PUS 11 invalid subservice {invalid}"); - } - DirectPusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { - warn!("PUS 11 Subservice {subservice} not implemented"); - } - }, - Err(error) => { - error!("PUS 11 packet handling error: {error:?}") + ); + if let Err(e) = result { + log::warn!( + "PUS {}({}) error: {:?}", + Self::SERVICE_ID, + Self::SERVICE_STR, + e + ); + return HandlingStatus::HandledOne; + } + match result.unwrap() { + DirectPusPacketHandlerResult::Handled(handling_status) => return handling_status, + DirectPusPacketHandlerResult::CustomSubservice(subservice, _) => { + log::warn!( + "PUS {}({}) subservice {} not implemented", + Self::SERVICE_ID, + Self::SERVICE_STR, + subservice + ); + } + DirectPusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { + log::warn!( + "PUS {}({}) subservice {} not implemented", + Self::SERVICE_ID, + Self::SERVICE_STR, + subservice + ); } } HandlingStatus::HandledOne diff --git a/satrs-example/src/pus/test.rs b/satrs-example/src/pus/test.rs index 933c679..372d1c7 100644 --- a/satrs-example/src/pus/test.rs +++ b/satrs-example/src/pus/test.rs @@ -1,5 +1,5 @@ use crate::pus::create_verification_reporter; -use log::{info, warn}; +use log::info; use satrs::event_man::{EventMessage, EventMessageU32}; use satrs::pool::SharedStaticMemoryPool; use satrs::pus::test::PusService17TestHandler; @@ -10,15 +10,13 @@ use satrs::pus::{ }; use satrs::pus::{EcssTcInSharedStoreConverter, PartialPusHandlingError}; use satrs::spacepackets::ecss::tc::PusTcReader; -use satrs::spacepackets::ecss::PusPacket; -use satrs::spacepackets::time::cds::CdsTime; -use satrs::spacepackets::time::TimeWriter; +use satrs::spacepackets::ecss::{PusPacket, PusServiceId}; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use satrs_example::config::components::PUS_TEST_SERVICE; use satrs_example::config::{tmtc_err, TEST_EVENT}; use std::sync::mpsc; -use super::HandlingStatus; +use super::{DirectPusService, HandlingStatus}; pub fn create_test_service_static( tm_sender: PacketSenderWithSharedPool, @@ -64,18 +62,36 @@ pub struct TestCustomServiceWrapper, } +impl DirectPusService + for TestCustomServiceWrapper +{ + const SERVICE_ID: u8 = PusServiceId::Test as u8; + + const SERVICE_STR: &'static str = "test"; +} + impl TestCustomServiceWrapper { - pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { - let error_handler = |patial_error: &PartialPusHandlingError| { - log::warn!("PUS 17 partial error: {:?}", patial_error); + pub fn poll_and_handle_next_tc(&mut self, timestamp: &[u8]) -> HandlingStatus { + let error_handler = |partial_error: &PartialPusHandlingError| { + log::warn!( + "PUS {}({}) partial error: {:?}", + Self::SERVICE_ID, + Self::SERVICE_STR, + partial_error + ); }; let res = self .handler - .poll_and_handle_next_tc(error_handler, time_stamp); - if res.is_err() { - warn!("PUS 17 handler error: {:?}", res.unwrap_err()); + .poll_and_handle_next_tc(error_handler, timestamp); + if let Err(e) = res { + log::warn!( + "PUS {}({}) error: {:?}", + Self::SERVICE_ID, + Self::SERVICE_STR, + e + ); return HandlingStatus::HandledOne; } match res.unwrap() { @@ -87,7 +103,12 @@ impl return handling_status; } DirectPusPacketHandlerResult::SubserviceNotImplemented(subservice, _) => { - warn!("PUS17: Subservice {subservice} not implemented") + log::warn!( + "PUS {}({}) subservice {} not implemented", + Self::SERVICE_ID, + Self::SERVICE_STR, + subservice + ); } DirectPusPacketHandlerResult::CustomSubservice(subservice, token) => { let (tc, _) = PusTcReader::new( @@ -97,29 +118,34 @@ impl .tc_slice_raw(), ) .unwrap(); - let time_stamper = CdsTime::now_with_u16_days().unwrap(); - let mut stamp_buf: [u8; 7] = [0; 7]; - time_stamper.write_to_bytes(&mut stamp_buf).unwrap(); if subservice == 128 { - info!("Generating test event"); + info!("generating test event"); self.test_srv_event_sender .send(EventMessage::new(PUS_TEST_SERVICE.id(), TEST_EVENT.into())) .expect("Sending test event failed"); - let start_token = self - .handler - .service_helper - .verif_reporter() - .start_success(self.handler.service_helper.tm_sender(), token, &stamp_buf) - .expect("Error sending start success"); - self.handler - .service_helper - .verif_reporter() - .completion_success( - self.handler.service_helper.tm_sender(), - start_token, - &stamp_buf, - ) - .expect("Error sending completion success"); + match self.handler.service_helper.verif_reporter().start_success( + self.handler.service_helper.tm_sender(), + token, + timestamp, + ) { + Ok(started_token) => { + if let Err(e) = self + .handler + .service_helper + .verif_reporter() + .completion_success( + self.handler.service_helper.tm_sender(), + started_token, + timestamp, + ) + { + error_handler(&PartialPusHandlingError::Verification(e)); + } + } + Err(e) => { + error_handler(&PartialPusHandlingError::Verification(e)); + } + } } else { let fail_data = [tc.subservice()]; self.handler @@ -129,7 +155,7 @@ impl self.handler.service_helper.tm_sender(), token, FailParams::new( - &stamp_buf, + timestamp, &tmtc_err::INVALID_PUS_SUBSERVICE, &fail_data, ), From a00c843698a5831a1d7258f601d7713d7b9a9e5e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 2 May 2024 11:58:46 +0200 Subject: [PATCH 10/15] optimize PUS stack code --- satrs-example/src/pus/event.rs | 6 +- satrs-example/src/pus/mod.rs | 14 ++-- satrs-example/src/pus/scheduler.rs | 58 ++++++++-------- satrs-example/src/pus/stack.rs | 102 ++++++++++++----------------- satrs-example/src/pus/test.rs | 14 ++-- 5 files changed, 87 insertions(+), 107 deletions(-) diff --git a/satrs-example/src/pus/event.rs b/satrs-example/src/pus/event.rs index 80951a4..42b812c 100644 --- a/satrs-example/src/pus/event.rs +++ b/satrs-example/src/pus/event.rs @@ -68,12 +68,8 @@ impl DirectPusSe const SERVICE_ID: u8 = PusServiceId::Event as u8; const SERVICE_STR: &'static str = "events"; -} -impl - EventServiceWrapper -{ - pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { + fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { let error_handler = |partial_error: &PartialPusHandlingError| { log::warn!( "PUS {}({}) partial error: {:?}", diff --git a/satrs-example/src/pus/mod.rs b/satrs-example/src/pus/mod.rs index 37367ff..f305308 100644 --- a/satrs-example/src/pus/mod.rs +++ b/satrs-example/src/pus/mod.rs @@ -234,6 +234,15 @@ pub trait TargetedPusService { fn check_for_request_timeouts(&mut self); } +/// Generic trait for services which handle packets directly. Kept minimal right now because +/// of the difficulty to allow flexible user code for these services.. +pub trait DirectPusService { + const SERVICE_ID: u8; + const SERVICE_STR: &'static str; + + fn poll_and_handle_next_tc(&mut self, timestamp: &[u8]) -> HandlingStatus; +} + /// This is a generic handler class for all PUS services where a PUS telecommand is converted /// to a targeted request. /// @@ -524,11 +533,6 @@ pub fn generic_pus_request_timeout_handler( Ok(()) } -pub trait DirectPusService { - const SERVICE_ID: u8; - const SERVICE_STR: &'static str; -} - #[cfg(test)] pub(crate) mod tests { use std::time::Duration; diff --git a/satrs-example/src/pus/scheduler.rs b/satrs-example/src/pus/scheduler.rs index 0c2242d..d6488a2 100644 --- a/satrs-example/src/pus/scheduler.rs +++ b/satrs-example/src/pus/scheduler.rs @@ -84,36 +84,8 @@ impl DirectPusSe const SERVICE_ID: u8 = PusServiceId::Verification as u8; const SERVICE_STR: &'static str = "verification"; -} -impl - SchedulingServiceWrapper -{ - pub fn release_tcs(&mut self) { - let id = self.pus_11_handler.service_helper.id(); - let releaser = |enabled: bool, info: &TcInfo, tc: &[u8]| -> bool { - self.tc_releaser.release(id, enabled, info, tc) - }; - - self.pus_11_handler - .scheduler_mut() - .update_time_from_now() - .unwrap(); - let released_tcs = self - .pus_11_handler - .scheduler_mut() - .release_telecommands_with_buffer( - releaser, - &mut self.sched_tc_pool, - &mut self.releaser_buf, - ) - .expect("releasing TCs failed"); - if released_tcs > 0 { - info!("{released_tcs} TC(s) released from scheduler"); - } - } - - pub fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { + fn poll_and_handle_next_tc(&mut self, time_stamp: &[u8]) -> HandlingStatus { let error_handler = |partial_error: &PartialPusHandlingError| { log::warn!( "PUS {}({}) partial error: {:?}", @@ -160,6 +132,34 @@ impl } } +impl + SchedulingServiceWrapper +{ + pub fn release_tcs(&mut self) { + let id = self.pus_11_handler.service_helper.id(); + let releaser = |enabled: bool, info: &TcInfo, tc: &[u8]| -> bool { + self.tc_releaser.release(id, enabled, info, tc) + }; + + self.pus_11_handler + .scheduler_mut() + .update_time_from_now() + .unwrap(); + let released_tcs = self + .pus_11_handler + .scheduler_mut() + .release_telecommands_with_buffer( + releaser, + &mut self.sched_tc_pool, + &mut self.releaser_buf, + ) + .expect("releasing TCs failed"); + if released_tcs > 0 { + info!("{released_tcs} TC(s) released from scheduler"); + } + } +} + pub fn create_scheduler_service_static( tm_sender: PacketSenderWithSharedPool, tc_releaser: PacketSenderWithSharedPool, diff --git a/satrs-example/src/pus/stack.rs b/satrs-example/src/pus/stack.rs index d964935..073406f 100644 --- a/satrs-example/src/pus/stack.rs +++ b/satrs-example/src/pus/stack.rs @@ -2,19 +2,17 @@ use crate::pus::mode::ModeServiceWrapper; use derive_new::new; use satrs::{ pus::{EcssTcInMemConverter, EcssTmSender}, - spacepackets::{ - ecss::PusServiceId, - time::{cds, TimeWriter}, - }, + spacepackets::time::{cds, TimeWriter}, }; -use satrs_example::config::CustomPusServiceId; use super::{ action::ActionServiceWrapper, event::EventServiceWrapper, hk::HkServiceWrapper, - scheduler::SchedulingServiceWrapper, test::TestCustomServiceWrapper, HandlingStatus, - TargetedPusService, + scheduler::SchedulingServiceWrapper, test::TestCustomServiceWrapper, DirectPusService, + HandlingStatus, TargetedPusService, }; +// TODO: For better extensibility, we could create 2 vectors: One for direct PUS services and one +// for targeted services.. #[derive(new)] pub struct PusStack { test_srv: TestCustomServiceWrapper, @@ -28,70 +26,56 @@ pub struct PusStack PusStack { + pub fn direct_service_checker( + service: &mut S, + timestamp: &[u8], + nothing_to_do: &mut bool, + ) { + let handling_status = service.poll_and_handle_next_tc(timestamp); + if handling_status == HandlingStatus::HandledOne { + *nothing_to_do = false; + } + } + + pub fn targeted_service_checker( + service: &mut S, + timestamp: &[u8], + nothing_to_do: &mut bool, + ) { + let request_handling = service.poll_and_handle_next_tc_default_handler(timestamp); + let reply_handling = service.poll_and_handle_next_reply_default_handler(timestamp); + if request_handling == HandlingStatus::HandledOne + || reply_handling == HandlingStatus::HandledOne + { + *nothing_to_do = false; + } + } + pub fn periodic_operation(&mut self) { // Release all telecommands which reached their release time before calling the service // handlers. self.schedule_srv.release_tcs(); - let time_stamp = cds::CdsTime::now_with_u16_days() + let timestamp = cds::CdsTime::now_with_u16_days() .expect("time stamp generation error") .to_vec() .unwrap(); // Hot loop which will run continuously until all request and reply handling is done. loop { let mut nothing_to_do = true; - let mut is_srv_finished = - |_srv_id: u8, - tc_handling_status: HandlingStatus, - reply_handling_status: Option| { - if tc_handling_status == HandlingStatus::HandledOne - || (reply_handling_status.is_some() - && reply_handling_status.unwrap() == HandlingStatus::HandledOne) - { - nothing_to_do = false; - } - }; - is_srv_finished( - PusServiceId::Test as u8, - self.test_srv.poll_and_handle_next_tc(&time_stamp), - None, + Self::direct_service_checker(&mut self.test_srv, ×tamp, &mut nothing_to_do); + Self::direct_service_checker(&mut self.schedule_srv, ×tamp, &mut nothing_to_do); + Self::direct_service_checker(&mut self.event_srv, ×tamp, &mut nothing_to_do); + Self::targeted_service_checker( + &mut self.action_srv_wrapper, + ×tamp, + &mut nothing_to_do, ); - is_srv_finished( - PusServiceId::Scheduling as u8, - self.schedule_srv.poll_and_handle_next_tc(&time_stamp), - None, - ); - is_srv_finished( - PusServiceId::Event as u8, - self.event_srv.poll_and_handle_next_tc(&time_stamp), - None, - ); - is_srv_finished( - PusServiceId::Action as u8, - self.action_srv_wrapper - .poll_and_handle_next_tc_default_handler(&time_stamp), - Some( - self.action_srv_wrapper - .poll_and_handle_next_reply_default_handler(&time_stamp), - ), - ); - is_srv_finished( - PusServiceId::Housekeeping as u8, - self.hk_srv_wrapper - .poll_and_handle_next_tc_default_handler(&time_stamp), - Some( - self.hk_srv_wrapper - .poll_and_handle_next_reply_default_handler(&time_stamp), - ), - ); - is_srv_finished( - CustomPusServiceId::Mode as u8, - self.mode_srv - .poll_and_handle_next_tc_default_handler(&time_stamp), - Some( - self.mode_srv - .poll_and_handle_next_reply_default_handler(&time_stamp), - ), + Self::targeted_service_checker( + &mut self.hk_srv_wrapper, + ×tamp, + &mut nothing_to_do, ); + Self::targeted_service_checker(&mut self.mode_srv, ×tamp, &mut nothing_to_do); if nothing_to_do { // Timeout checking is only done once. self.action_srv_wrapper.check_for_request_timeouts(); diff --git a/satrs-example/src/pus/test.rs b/satrs-example/src/pus/test.rs index 372d1c7..06cfe29 100644 --- a/satrs-example/src/pus/test.rs +++ b/satrs-example/src/pus/test.rs @@ -33,7 +33,7 @@ pub fn create_test_service_static( )); TestCustomServiceWrapper { handler: pus17_handler, - test_srv_event_sender: event_sender, + event_tx: event_sender, } } @@ -51,7 +51,7 @@ pub fn create_test_service_dynamic( )); TestCustomServiceWrapper { handler: pus17_handler, - test_srv_event_sender: event_sender, + event_tx: event_sender, } } @@ -59,7 +59,7 @@ pub struct TestCustomServiceWrapper, - pub test_srv_event_sender: mpsc::SyncSender, + pub event_tx: mpsc::SyncSender, } impl DirectPusService @@ -68,12 +68,8 @@ impl DirectPusSe const SERVICE_ID: u8 = PusServiceId::Test as u8; const SERVICE_STR: &'static str = "test"; -} -impl - TestCustomServiceWrapper -{ - pub fn poll_and_handle_next_tc(&mut self, timestamp: &[u8]) -> HandlingStatus { + fn poll_and_handle_next_tc(&mut self, timestamp: &[u8]) -> HandlingStatus { let error_handler = |partial_error: &PartialPusHandlingError| { log::warn!( "PUS {}({}) partial error: {:?}", @@ -120,7 +116,7 @@ impl .unwrap(); if subservice == 128 { info!("generating test event"); - self.test_srv_event_sender + self.event_tx .send(EventMessage::new(PUS_TEST_SERVICE.id(), TEST_EVENT.into())) .expect("Sending test event failed"); match self.handler.service_helper.verif_reporter().start_success( From 23327a77864db6a58aee8dc999454fe71f297f09 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 2 May 2024 12:01:24 +0200 Subject: [PATCH 11/15] bounded the PUS stack hot loop --- satrs-example/src/pus/stack.rs | 57 +++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/satrs-example/src/pus/stack.rs b/satrs-example/src/pus/stack.rs index 073406f..3594bd6 100644 --- a/satrs-example/src/pus/stack.rs +++ b/satrs-example/src/pus/stack.rs @@ -26,31 +26,6 @@ pub struct PusStack PusStack { - pub fn direct_service_checker( - service: &mut S, - timestamp: &[u8], - nothing_to_do: &mut bool, - ) { - let handling_status = service.poll_and_handle_next_tc(timestamp); - if handling_status == HandlingStatus::HandledOne { - *nothing_to_do = false; - } - } - - pub fn targeted_service_checker( - service: &mut S, - timestamp: &[u8], - nothing_to_do: &mut bool, - ) { - let request_handling = service.poll_and_handle_next_tc_default_handler(timestamp); - let reply_handling = service.poll_and_handle_next_reply_default_handler(timestamp); - if request_handling == HandlingStatus::HandledOne - || reply_handling == HandlingStatus::HandledOne - { - *nothing_to_do = false; - } - } - pub fn periodic_operation(&mut self) { // Release all telecommands which reached their release time before calling the service // handlers. @@ -59,6 +34,7 @@ impl .expect("time stamp generation error") .to_vec() .unwrap(); + let mut loop_count = 0_u32; // Hot loop which will run continuously until all request and reply handling is done. loop { let mut nothing_to_do = true; @@ -83,6 +59,37 @@ impl self.mode_srv.check_for_request_timeouts(); break; } + // Safety mechanism to avoid infinite loops. + loop_count += 1; + if loop_count >= 500 { + log::warn!("reached PUS stack loop count 500, breaking"); + break; + } + } + } + + pub fn direct_service_checker( + service: &mut S, + timestamp: &[u8], + nothing_to_do: &mut bool, + ) { + let handling_status = service.poll_and_handle_next_tc(timestamp); + if handling_status == HandlingStatus::HandledOne { + *nothing_to_do = false; + } + } + + pub fn targeted_service_checker( + service: &mut S, + timestamp: &[u8], + nothing_to_do: &mut bool, + ) { + let request_handling = service.poll_and_handle_next_tc_default_handler(timestamp); + let reply_handling = service.poll_and_handle_next_reply_default_handler(timestamp); + if request_handling == HandlingStatus::HandledOne + || reply_handling == HandlingStatus::HandledOne + { + *nothing_to_do = false; } } } From e37061dcf0db1eedb2d0c8300b5e6c51a7084295 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 2 May 2024 12:28:09 +0200 Subject: [PATCH 12/15] smaller improvements --- satrs-example/src/pus/action.rs | 4 ++-- satrs-example/src/pus/hk.rs | 5 ++--- satrs-example/src/pus/mode.rs | 1 - satrs-example/src/pus/scheduler.rs | 1 + 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/satrs-example/src/pus/action.rs b/satrs-example/src/pus/action.rs index 445ad86..03d362c 100644 --- a/satrs-example/src/pus/action.rs +++ b/satrs-example/src/pus/action.rs @@ -17,7 +17,7 @@ use satrs::pus::{ }; use satrs::request::{GenericMessage, UniqueApidTargetId}; use satrs::spacepackets::ecss::tc::PusTcReader; -use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket}; +use satrs::spacepackets::ecss::{EcssEnumU16, PusPacket, PusServiceId}; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use satrs_example::config::components::PUS_ACTION_SERVICE; use satrs_example::config::tmtc_err; @@ -278,7 +278,7 @@ pub struct ActionServiceWrapper TargetedPusService for ActionServiceWrapper { - const SERVICE_ID: u8 = 8; + const SERVICE_ID: u8 = PusServiceId::Action as u8; const SERVICE_STR: &'static str = "action"; delegate::delegate! { diff --git a/satrs-example/src/pus/hk.rs b/satrs-example/src/pus/hk.rs index 33e01ac..0092241 100644 --- a/satrs-example/src/pus/hk.rs +++ b/satrs-example/src/pus/hk.rs @@ -13,7 +13,7 @@ use satrs::pus::{ }; use satrs::request::{GenericMessage, UniqueApidTargetId}; use satrs::spacepackets::ecss::tc::PusTcReader; -use satrs::spacepackets::ecss::{hk, PusPacket}; +use satrs::spacepackets::ecss::{hk, PusPacket, PusServiceId}; use satrs::tmtc::{PacketAsVec, PacketSenderWithSharedPool}; use satrs_example::config::components::PUS_HK_SERVICE; use satrs_example::config::{hk_err, tmtc_err}; @@ -299,8 +299,7 @@ pub struct HkServiceWrapper TargetedPusService for HkServiceWrapper { - const SERVICE_ID: u8 = 3; - + const SERVICE_ID: u8 = PusServiceId::Housekeeping as u8; const SERVICE_STR: &'static str = "housekeeping"; delegate::delegate! { diff --git a/satrs-example/src/pus/mode.rs b/satrs-example/src/pus/mode.rs index e990a80..56ae9d4 100644 --- a/satrs-example/src/pus/mode.rs +++ b/satrs-example/src/pus/mode.rs @@ -272,7 +272,6 @@ impl TargetedPus for ModeServiceWrapper { const SERVICE_ID: u8 = CustomPusServiceId::Mode as u8; - const SERVICE_STR: &'static str = "mode"; delegate::delegate! { diff --git a/satrs-example/src/pus/scheduler.rs b/satrs-example/src/pus/scheduler.rs index d6488a2..38cee95 100644 --- a/satrs-example/src/pus/scheduler.rs +++ b/satrs-example/src/pus/scheduler.rs @@ -78,6 +78,7 @@ pub struct SchedulingServiceWrapper, } + impl DirectPusService for SchedulingServiceWrapper { From 408803fe86d4400b133703f964b1d682ce5cb38d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 2 May 2024 13:41:27 +0200 Subject: [PATCH 13/15] small robustness fix --- satrs-example/src/pus/event.rs | 3 ++- satrs-example/src/pus/scheduler.rs | 3 ++- satrs-example/src/pus/test.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/satrs-example/src/pus/event.rs b/satrs-example/src/pus/event.rs index 42b812c..caecdd9 100644 --- a/satrs-example/src/pus/event.rs +++ b/satrs-example/src/pus/event.rs @@ -88,7 +88,8 @@ impl DirectPusSe Self::SERVICE_STR, e ); - return HandlingStatus::HandledOne; + // To avoid permanent loops on continuous errors. + return HandlingStatus::Empty; } match result.unwrap() { DirectPusPacketHandlerResult::Handled(handling_status) => return handling_status, diff --git a/satrs-example/src/pus/scheduler.rs b/satrs-example/src/pus/scheduler.rs index 38cee95..eaa03c4 100644 --- a/satrs-example/src/pus/scheduler.rs +++ b/satrs-example/src/pus/scheduler.rs @@ -108,7 +108,8 @@ impl DirectPusSe Self::SERVICE_STR, e ); - return HandlingStatus::HandledOne; + // To avoid permanent loops on continuous errors. + return HandlingStatus::Empty; } match result.unwrap() { DirectPusPacketHandlerResult::Handled(handling_status) => return handling_status, diff --git a/satrs-example/src/pus/test.rs b/satrs-example/src/pus/test.rs index 06cfe29..473dc3e 100644 --- a/satrs-example/src/pus/test.rs +++ b/satrs-example/src/pus/test.rs @@ -88,7 +88,8 @@ impl DirectPusSe Self::SERVICE_STR, e ); - return HandlingStatus::HandledOne; + // To avoid permanent loops on continuous errors. + return HandlingStatus::Empty; } match res.unwrap() { DirectPusPacketHandlerResult::Handled(handling_status) => { From 42d1257e83be896322ee1edb6bd6161632fa86be Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 2 May 2024 14:39:30 +0200 Subject: [PATCH 14/15] prepare next release v0.2.0 --- .github/workflows/ci.yml | 64 ++++++++++++++++++++++++++++++++++++++++ automation/Jenkinsfile | 1 + satrs/CHANGELOG.md | 10 +++++++ satrs/Cargo.toml | 2 +- satrs/src/events.rs | 4 +-- 5 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e21a645 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,64 @@ +name: ci +on: [push, pull_request] + +jobs: + check: + name: Check build + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo check --release + + test: + name: Run Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: Install nextest + uses: taiki-e/install-action@nextest + - run: cargo nextest run --all-features + - run: cargo test --doc + + cross-check: + name: Check Cross-Compilation + runs-on: ubuntu-latest + strategy: + matrix: + target: + - armv7-unknown-linux-gnueabihf + - thumbv7em-none-eabihf + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf" + - run: cargo check -p satrs --release --target=${{matrix.target}} --no-default-features + + fmt: + name: Check formatting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo fmt --all -- --check + + docs: + name: Check Documentation Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]' + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo clippy -- -D warnings diff --git a/automation/Jenkinsfile b/automation/Jenkinsfile index 7aafe26..c3c8c7c 100644 --- a/automation/Jenkinsfile +++ b/automation/Jenkinsfile @@ -33,6 +33,7 @@ pipeline { stage('Test') { steps { sh 'cargo nextest r --all-features' + sh 'cargo test --doc' } } stage('Check with all features') { diff --git a/satrs/CHANGELOG.md b/satrs/CHANGELOG.md index 6124919..a9038c9 100644 --- a/satrs/CHANGELOG.md +++ b/satrs/CHANGELOG.md @@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/). # [unreleased] +# [v0.2.0] 2024-05-02 + +## Changed + +- Various improvements for the PUS stack components. + +## Added + +- Added `HandlingStatus` enumeration. + # [v0.2.0-rc.5] 2024-04-24 ## Added diff --git a/satrs/Cargo.toml b/satrs/Cargo.toml index 92b753d..0338da2 100644 --- a/satrs/Cargo.toml +++ b/satrs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "satrs" -version = "0.2.0-rc.5" +version = "0.2.0" edition = "2021" rust-version = "1.71.1" authors = ["Robin Mueller "] diff --git a/satrs/src/events.rs b/satrs/src/events.rs index 5544eb2..352bf5e 100644 --- a/satrs/src/events.rs +++ b/satrs/src/events.rs @@ -21,11 +21,11 @@ //! use satrs::events::{EventU16, EventU32, EventU32TypedSev, Severity, SeverityHigh, SeverityInfo}; //! //! const MSG_RECVD: EventU32TypedSev = EventU32TypedSev::new(1, 0); -//! const MSG_FAILED: EventU32 = EventU32::new(Severity::LOW, 1, 1); +//! const MSG_FAILED: EventU32 = EventU32::new(Severity::Low, 1, 1); //! //! const TEMPERATURE_HIGH: EventU32TypedSev = EventU32TypedSev::new(2, 0); //! -//! let small_event = EventU16::new(Severity::INFO, 3, 0); +//! let small_event = EventU16::new(Severity::Info, 3, 0); //! ``` use core::fmt::Debug; use core::hash::Hash; From 46dbb4309b2b4021030c93a0adfe7066a1a7cc8a Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 2 May 2024 14:44:22 +0200 Subject: [PATCH 15/15] new clippy check --- satrs/src/request.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/satrs/src/request.rs b/satrs/src/request.rs index 3999278..f188798 100644 --- a/satrs/src/request.rs +++ b/satrs/src/request.rs @@ -10,7 +10,7 @@ pub use std_mod::*; use spacepackets::{ ecss::{tc::IsPusTelecommand, PusPacket}, - ByteConversionError, CcsdsPacket, + ByteConversionError, }; use crate::{queue::GenericTargetedMessagingError, ComponentId}; @@ -47,7 +47,7 @@ impl UniqueApidTargetId { /// This function attempts to build the ID from a PUS telecommand by extracting the APID /// and the first four bytes of the application data field as the target field. pub fn from_pus_tc( - tc: &(impl CcsdsPacket + PusPacket + IsPusTelecommand), + tc: &(impl PusPacket + IsPusTelecommand), ) -> Result { if tc.user_data().len() < 4 { return Err(ByteConversionError::FromSliceTooSmall {