diff --git a/satrs-core/CHANGELOG.md b/satrs-core/CHANGELOG.md deleted file mode 100644 index 68e54a2..0000000 --- a/satrs-core/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -Change Log -======= - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/) -and this project adheres to [Semantic Versioning](http://semver.org/). - -# [unreleased] diff --git a/satrs-core/Cargo.toml b/satrs-core/Cargo.toml deleted file mode 100644 index a7c5a73..0000000 --- a/satrs-core/Cargo.toml +++ /dev/null @@ -1,73 +0,0 @@ -[package] -name = "satrs-core" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -delegate = "0.8" -paste = "1.0" -embed-doc-image = "0.1" - -[dependencies.dyn-clone] -version = "1.0" -optional = true - -[dependencies.hashbrown] -version = "0.13" -optional = true - -[dependencies.heapless] -version = "0.7" -optional = true - -[dependencies.num-traits] -version = "0.2" -default-features = false - -[dependencies.downcast-rs] -version = "1.2" -default-features = false -optional = true - -[dependencies.bus] -version = "2.2" -optional = true - -[dependencies.crossbeam-channel] -version= "0.5" -default-features = false -optional = true - -[dependencies.serde] -version = "1.0" -default-features = false -optional = true - -[dependencies.spacepackets] -# git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git" -version = "0.4.0" -default-features = false - -[dev-dependencies] -serde = "1.0" -zerocopy = "0.6" -once_cell = "1.13" -serde_json = "1" - -[dev-dependencies.postcard] -version = "1.0" - -[features] -default = ["std"] -std = ["downcast-rs/std", "alloc", "bus", "postcard/use-std", "crossbeam-channel/std", "serde/std", "spacepackets/std"] -alloc = ["serde/alloc", "spacepackets/alloc", "hashbrown", "dyn-clone", "downcast-rs"] -serde = ["dep:serde", "spacepackets/serde"] -crossbeam = ["crossbeam-channel"] -heapless = ["dep:heapless"] -doc-images = [] - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "doc_cfg"] diff --git a/satrs-core/images/event_man_arch.graphml b/satrs-core/images/event_man_arch.graphml deleted file mode 100644 index 1336793..0000000 --- a/satrs-core/images/event_man_arch.graphml +++ /dev/null @@ -1,259 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Example Event Flow - - - - - - - - - - - Event Manager - - - - - - - - - - - Event -Creator 0 - - - - - - - - - - - Event -Creator 2 - - - - - - - - - - - Event -Creator 1 - - - - - - - - - - - Event -Creator 3 - - - - - - - - - - - PUS Service 5 -Event Reporting - - - - - - - - - - - - PUS Service 19 -Event Action - - - - - - - - - - - Telemetry -Sink - - - - - - - - - - - Subscriptions - -1. Event Creator 0 subscribes - for event 0 -2. Event Creator 1 subscribes - for event group 2 -3. PUS Service 5 handler - subscribes for all events -4. PUS Service 19 handler - subscribes for all events - - - - - - - - - - - event 1 -(group 1) - - - - - - - - - - - - - event 0 -(group 0) - - - - - - - - - - - event 2 -(group 3) - - - - - - - - - - - - - event 3 (group 2) -event 4 (group 2) - - - - - - - - - - - <<all events>> - - - - - - - - - - - <<all events>> - - - - - - - - - - - - - event 1 -event 2 - - - - - - - - - - - group 2 - - - - - - - - - - - enabled Events -as PUS 5 TM - - - - - - - - - diff --git a/satrs-core/images/event_man_arch.png b/satrs-core/images/event_man_arch.png deleted file mode 100644 index 61c8d72..0000000 Binary files a/satrs-core/images/event_man_arch.png and /dev/null differ diff --git a/satrs-core/src/error.rs b/satrs-core/src/error.rs deleted file mode 100644 index 2144839..0000000 --- a/satrs-core/src/error.rs +++ /dev/null @@ -1,49 +0,0 @@ -pub enum FsrcGroupIds { - Tmtc = 0, -} - -pub struct FsrcErrorRaw { - pub group_id: u8, - pub unique_id: u8, - pub group_name: &'static str, - pub info: &'static str, -} - -pub trait FsrcErrorHandler { - fn error(&mut self, e: FsrcErrorRaw); - fn error_with_one_param(&mut self, e: FsrcErrorRaw, _p1: u32) { - self.error(e); - } - fn error_with_two_params(&mut self, e: FsrcErrorRaw, _p1: u32, _p2: u32) { - self.error(e); - } -} - -impl FsrcErrorRaw { - pub const fn new( - group_id: u8, - unique_id: u8, - group_name: &'static str, - info: &'static str, - ) -> Self { - FsrcErrorRaw { - group_id, - unique_id, - group_name, - info, - } - } -} - -#[derive(Clone, Copy, Default)] -pub struct SimpleStdErrorHandler {} - -#[cfg(feature = "use_std")] -impl FsrcErrorHandler for SimpleStdErrorHandler { - fn error(&mut self, e: FsrcErrorRaw) { - println!( - "Received error from group {} with ID ({},{}): {}", - e.group_name, e.group_id, e.unique_id, e.info - ); - } -} diff --git a/satrs-core/src/event_man.rs b/satrs-core/src/event_man.rs deleted file mode 100644 index 5d2db19..0000000 --- a/satrs-core/src/event_man.rs +++ /dev/null @@ -1,723 +0,0 @@ -//! 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. -#![cfg_attr(feature = "doc-images", -cfg_attr(all(), -doc = ::embed_doc_image::embed_image!("event_man_arch", "images/event_man_arch.png" -)))] -#![cfg_attr( - not(feature = "doc-images"), - doc = "**Doc images not enabled**. Compile with feature `doc-images` and Rust version >= 1.54 \ - to enable." -)] -//! 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. -//! -//! The following graph shows how the event flow for such a setup could look like: -//! -//! ![Event flow][event_man_arch] -//! -//! The event manager has a listener table abstracted by the [ListenerTable], which maps -//! listener groups identified by [ListenerKey]s to a [sender ID][SenderId]. -//! It also contains a sender table abstracted by the [SenderTable] which maps these sender IDs -//! to a concrete [SendEventProvider]s. A simple approach would be to use one send event provider -//! for each OBSW thread and then subscribe for all interesting events for a particular thread -//! using the send event provider ID. -//! -//! This can be done with the [EventManager] like this: -//! -//! 1. Provide a concrete [EventReceiver] 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]. -//! 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. -//! 3. The event manager receives the receiver component as part of a [EventReceiver] -//! implementation so all events are routed to the manager. -//! 4. Create the [send event providers][SendEventProvider]s which allow routing events to -//! subscribers. You can now use their [sender IDs][SendEventProvider::id] to subscribe for -//! event groups, for example by using the [EventManager::subscribe_single] method. -//! 5. Add the send provider as well using the [EventManager::add_sender] call so the event -//! manager can route listener groups to a the send provider. -//! -//! Some components like a PUS Event Service or PUS Event Action Service might require all -//! events to package them as telemetry or start actions where applicable. -//! Other components might only be interested in certain events. For example, a thermal system -//! handler might only be interested in temperature events generated by a thermal sensor component. -//! -//! # Examples -//! -//! You can check [integration test](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-core/tests/pus_events.rs) -//! for a concrete example using multi-threading where events are routed to -//! different threads. -use crate::events::{EventU16, EventU32, GenericEvent, LargestEventRaw, LargestGroupIdRaw}; -use crate::params::{Params, ParamsHeapless}; -#[cfg(feature = "alloc")] -use alloc::boxed::Box; -#[cfg(feature = "alloc")] -use alloc::vec; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; -use core::slice::Iter; -#[cfg(feature = "alloc")] -use hashbrown::HashMap; - -#[cfg(feature = "std")] -pub use stdmod::*; - -#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] -pub enum ListenerKey { - Single(LargestEventRaw), - Group(LargestGroupIdRaw), - All, -} - -pub type EventWithHeaplessAuxData = (Event, Option); -pub type EventU32WithHeaplessAuxData = EventWithHeaplessAuxData; -pub type EventU16WithHeaplessAuxData = EventWithHeaplessAuxData; - -pub type EventWithAuxData = (Event, Option); -pub type EventU32WithAuxData = EventWithAuxData; -pub type EventU16WithAuxData = EventWithAuxData; - -pub type SenderId = u32; - -pub trait SendEventProvider { - type Error; - - fn id(&self) -> SenderId; - fn send_no_data(&mut self, event: Provider) -> Result<(), Self::Error> { - self.send(event, None) - } - fn send( - &mut self, - event: Provider, - aux_data: Option, - ) -> Result<(), Self::Error>; -} - -/// Generic abstraction for an event receiver. -pub trait EventReceiver { - /// This function has to be provided by any event receiver. A receive call may or may not return - /// an event. - /// - /// To allow returning arbitrary additional auxiliary data, a mutable slice is passed to the - /// [Self::receive] call as well. Receivers can write data to this slice, but care must be taken - /// to avoid panics due to size missmatches or out of bound writes. - fn receive(&mut self) -> Option<(Event, Option)>; -} - -pub trait ListenerTable { - fn get_listeners(&self) -> Vec; - fn contains_listener(&self, key: &ListenerKey) -> bool; - fn get_listener_ids(&self, key: &ListenerKey) -> Option>; - fn add_listener(&mut self, key: ListenerKey, sender_id: SenderId) -> bool; - fn remove_duplicates(&mut self, key: &ListenerKey); -} - -pub trait SenderTable { - fn contains_send_event_provider(&self, id: &SenderId) -> bool; - fn get_send_event_provider( - &mut self, - id: &SenderId, - ) -> Option<&mut Box>>; - fn add_send_event_provider( - &mut self, - send_provider: Box< - dyn SendEventProvider, - >, - ) -> bool; -} - -/// Generic event manager implementation. -/// -/// # Generics -/// -/// * `SendProviderError`: [SendEventProvider] error type -/// * `Event`: Concrete event provider, currently either [EventU32] or [EventU16] -/// * `AuxDataProvider`: Concrete auxiliary data provider, currently either [Params] or -/// [ParamsHeapless] -pub struct EventManager -{ - listener_table: Box, - sender_table: Box>, - event_receiver: Box>, -} - -/// Safety: It is safe to implement [Send] because all fields in the [EventManager] are [Send] -/// as well -#[cfg(feature = "std")] -unsafe impl Send - for EventManager -{ -} - -#[cfg(feature = "std")] -pub type EventManagerWithMpscQueue = EventManager< - std::sync::mpsc::SendError<(Event, Option)>, - Event, - AuxDataProvider, ->; - -#[derive(Debug)] -pub enum EventRoutingResult { - /// No event was received - Empty, - /// An event was received and routed. - /// The first tuple entry will contain the number of recipients. - Handled(u32, Event, Option), -} - -#[derive(Debug)] -pub enum EventRoutingError { - SendError(E), - NoSendersForKey(ListenerKey), - NoSenderForId(SenderId), -} - -#[derive(Debug)] -pub struct EventRoutingErrorsWithResult { - pub result: EventRoutingResult, - pub errors: [Option>; 3], -} - -impl EventManager { - pub fn remove_duplicates(&mut self, key: &ListenerKey) { - self.listener_table.remove_duplicates(key) - } - - /// Subscribe for a unique event. - pub fn subscribe_single(&mut self, event: &Event, sender_id: SenderId) { - self.update_listeners(ListenerKey::Single(event.raw_as_largest_type()), sender_id); - } - - /// Subscribe for an event group. - pub fn subscribe_group(&mut self, group_id: LargestGroupIdRaw, sender_id: SenderId) { - self.update_listeners(ListenerKey::Group(group_id), sender_id); - } - - /// Subscribe for all events received by the manager. - /// - /// For example, this can be useful for a handler component which sends every event as - /// a telemetry packet. - pub fn subscribe_all(&mut self, sender_id: SenderId) { - self.update_listeners(ListenerKey::All, sender_id); - } -} - -impl - EventManager -{ - /// Create an event manager where the sender table will be the [DefaultSenderTableProvider] - /// and the listener table will be the [DefaultListenerTableProvider]. - pub fn new(event_receiver: Box>) -> Self { - let listener_table: Box = Box::default(); - let sender_table: Box> = - Box::default(); - Self::new_custom_tables(listener_table, sender_table, event_receiver) - } -} - -impl - EventManager -{ - pub fn new_custom_tables( - listener_table: Box, - sender_table: Box>, - event_receiver: Box>, - ) -> Self { - EventManager { - listener_table, - sender_table, - event_receiver, - } - } - - pub fn add_sender( - &mut self, - send_provider: impl SendEventProvider + 'static, - ) { - if !self - .sender_table - .contains_send_event_provider(&send_provider.id()) - { - self.sender_table - .add_send_event_provider(Box::new(send_provider)); - } - } - - fn update_listeners(&mut self, key: ListenerKey, sender_id: SenderId) { - self.listener_table.add_listener(key, sender_id); - } - - /// This function will use the cached event receiver and try to receive one event. - /// If an event was received, it will try to route that event to all subscribed event listeners. - /// If this works without any issues, the [EventRoutingResult] will contain context information - /// about the routed event. - /// - /// This function will track up to 3 errors returned as part of the - /// [EventRoutingErrorsWithResult] error struct. - pub fn try_event_handling( - &mut self, - ) -> Result< - EventRoutingResult, - EventRoutingErrorsWithResult, - > { - let mut err_idx = 0; - let mut err_slice = [None, None, None]; - let mut num_recipients = 0; - let mut add_error = |error: EventRoutingError| { - if err_idx < 3 { - err_slice[err_idx] = Some(error); - err_idx += 1; - } - }; - let mut send_handler = - |key: &ListenerKey, event: Event, aux_data: &Option| { - if self.listener_table.contains_listener(key) { - if let Some(ids) = self.listener_table.get_listener_ids(key) { - for id in ids { - if let Some(sender) = self.sender_table.get_send_event_provider(id) { - if let Err(e) = sender.send(event, aux_data.clone()) { - add_error(EventRoutingError::SendError(e)); - } else { - num_recipients += 1; - } - } else { - add_error(EventRoutingError::NoSenderForId(*id)); - } - } - } else { - add_error(EventRoutingError::NoSendersForKey(*key)); - } - } - }; - if let Some((event, aux_data)) = self.event_receiver.receive() { - let single_key = ListenerKey::Single(event.raw_as_largest_type()); - send_handler(&single_key, event, &aux_data); - let group_key = ListenerKey::Group(event.group_id_as_largest_type()); - send_handler(&group_key, event, &aux_data); - send_handler(&ListenerKey::All, event, &aux_data); - if err_idx > 0 { - return Err(EventRoutingErrorsWithResult { - result: EventRoutingResult::Handled(num_recipients, event, aux_data), - errors: err_slice, - }); - } - return Ok(EventRoutingResult::Handled(num_recipients, event, aux_data)); - } - Ok(EventRoutingResult::Empty) - } -} - -#[derive(Default)] -pub struct DefaultListenerTableProvider { - listeners: HashMap>, -} - -pub struct DefaultSenderTableProvider< - SendProviderError, - Event: GenericEvent = EventU32, - AuxDataProvider = Params, -> { - senders: HashMap< - SenderId, - Box>, - >, -} - -impl Default - for DefaultSenderTableProvider -{ - fn default() -> Self { - Self { - senders: HashMap::new(), - } - } -} - -impl ListenerTable for DefaultListenerTableProvider { - fn get_listeners(&self) -> Vec { - let mut key_list = Vec::new(); - for key in self.listeners.keys() { - key_list.push(*key); - } - key_list - } - - fn contains_listener(&self, key: &ListenerKey) -> bool { - self.listeners.contains_key(key) - } - - fn get_listener_ids(&self, key: &ListenerKey) -> Option> { - self.listeners.get(key).map(|vec| vec.iter()) - } - - fn add_listener(&mut self, key: ListenerKey, sender_id: SenderId) -> bool { - if let Some(existing_list) = self.listeners.get_mut(&key) { - existing_list.push(sender_id); - } else { - let new_list = vec![sender_id]; - self.listeners.insert(key, new_list); - } - true - } - - fn remove_duplicates(&mut self, key: &ListenerKey) { - if let Some(list) = self.listeners.get_mut(key) { - list.sort_unstable(); - list.dedup(); - } - } -} - -impl - SenderTable - for DefaultSenderTableProvider -{ - fn contains_send_event_provider(&self, id: &SenderId) -> bool { - self.senders.contains_key(id) - } - - fn get_send_event_provider( - &mut self, - id: &SenderId, - ) -> Option<&mut Box>> - { - self.senders.get_mut(id).filter(|sender| sender.id() == *id) - } - - fn add_send_event_provider( - &mut self, - send_provider: Box< - dyn SendEventProvider, - >, - ) -> bool { - let id = send_provider.id(); - if self.senders.contains_key(&id) { - return false; - } - self.senders.insert(id, send_provider).is_none() - } -} - -#[cfg(feature = "std")] -pub mod stdmod { - use super::*; - use crate::event_man::{EventReceiver, EventWithAuxData}; - use crate::events::{EventU16, EventU32, GenericEvent}; - use crate::params::Params; - use std::sync::mpsc::{Receiver, SendError, Sender}; - - pub struct MpscEventReceiver { - mpsc_receiver: Receiver<(Event, Option)>, - } - - impl MpscEventReceiver { - pub fn new(receiver: Receiver<(Event, Option)>) -> Self { - Self { - mpsc_receiver: receiver, - } - } - } - impl EventReceiver for MpscEventReceiver { - fn receive(&mut self) -> Option> { - if let Ok(event_and_data) = self.mpsc_receiver.try_recv() { - return Some(event_and_data); - } - None - } - } - - pub type MpscEventU32Receiver = MpscEventReceiver; - pub type MpscEventU16Receiver = MpscEventReceiver; - - #[derive(Clone)] - pub struct MpscEventSendProvider { - id: u32, - sender: Sender<(Event, Option)>, - } - - impl MpscEventSendProvider { - pub fn new(id: u32, sender: Sender<(Event, Option)>) -> Self { - Self { id, sender } - } - } - - impl SendEventProvider for MpscEventSendProvider { - type Error = SendError<(Event, Option)>; - - fn id(&self) -> u32 { - self.id - } - fn send(&mut self, event: Event, aux_data: Option) -> Result<(), Self::Error> { - self.sender.send((event, aux_data)) - } - } - - pub type MpscEventU32SendProvider = MpscEventSendProvider; - pub type MpscEventU16SendProvider = MpscEventSendProvider; -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::event_man::EventManager; - use crate::events::{EventU32, GenericEvent, Severity}; - use crate::params::ParamsRaw; - use alloc::boxed::Box; - use std::format; - use std::sync::mpsc::{channel, Receiver, SendError, Sender}; - - #[derive(Clone)] - struct MpscEventSenderQueue { - id: u32, - mpsc_sender: Sender, - } - - impl MpscEventSenderQueue { - fn new(id: u32, mpsc_sender: Sender) -> Self { - Self { id, mpsc_sender } - } - } - - impl SendEventProvider for MpscEventSenderQueue { - type Error = SendError; - - fn id(&self) -> u32 { - self.id - } - fn send(&mut self, event: EventU32, aux_data: Option) -> Result<(), Self::Error> { - self.mpsc_sender.send((event, aux_data)) - } - } - - fn check_next_event( - expected: EventU32, - receiver: &Receiver, - ) -> Option { - if let Ok(event) = receiver.try_recv() { - assert_eq!(event.0, expected); - return event.1; - } - None - } - - fn check_handled_event( - res: EventRoutingResult, - expected: EventU32, - expected_num_sent: u32, - ) { - assert!(matches!(res, EventRoutingResult::Handled { .. })); - if let EventRoutingResult::Handled(num_recipients, event, _aux_data) = res { - assert_eq!(event, expected); - assert_eq!(num_recipients, expected_num_sent); - } - } - - fn generic_event_man() -> ( - Sender, - EventManager>, - ) { - let (event_sender, manager_queue) = channel(); - let event_man_receiver = MpscEventReceiver::new(manager_queue); - ( - event_sender, - EventManager::new(Box::new(event_man_receiver)), - ) - } - - #[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 (single_event_sender, single_event_receiver) = channel(); - let single_event_listener = MpscEventSenderQueue::new(0, single_event_sender); - event_man.subscribe_single(&event_grp_0, single_event_listener.id()); - event_man.add_sender(single_event_listener); - let (group_event_sender_0, group_event_receiver_0) = channel(); - let group_event_listener = MpscEventSenderQueue { - id: 1, - mpsc_sender: group_event_sender_0, - }; - event_man.subscribe_group(event_grp_1_0.group_id(), group_event_listener.id()); - event_man.add_sender(group_event_listener); - - // Test event with one listener - event_sender - .send((event_grp_0, None)) - .expect("Sending single error failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_grp_0, 1); - check_next_event(event_grp_0, &single_event_receiver); - - // Test event which is sent to all group listeners - event_sender - .send((event_grp_1_0, None)) - .expect("Sending group error failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_grp_1_0, 1); - check_next_event(event_grp_1_0, &group_event_receiver_0); - } - - #[test] - fn test_with_basic_aux_data() { - let (event_sender, mut event_man) = generic_event_man(); - let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap(); - let (single_event_sender, single_event_receiver) = channel(); - let single_event_listener = MpscEventSenderQueue::new(0, single_event_sender); - event_man.subscribe_single(&event_grp_0, single_event_listener.id()); - event_man.add_sender(single_event_listener); - event_sender - .send((event_grp_0, Some(Params::Heapless((2_u32, 3_u32).into())))) - .expect("Sending group error failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_grp_0, 1); - let aux = check_next_event(event_grp_0, &single_event_receiver); - assert!(aux.is_some()); - let aux = aux.unwrap(); - if let Params::Heapless(ParamsHeapless::Raw(ParamsRaw::U32Pair(pair))) = aux { - assert_eq!(pair.0, 2); - assert_eq!(pair.1, 3); - } else { - panic!("{}", format!("Unexpected auxiliary value type {:?}", aux)); - } - } - - /// Test listening for multiple groups - #[test] - fn test_multi_group() { - let (event_sender, mut event_man) = generic_event_man(); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - let hres = res.unwrap(); - assert!(matches!(hres, 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_sender, event_grp_0_receiver) = channel(); - let event_grp_0_and_1_listener = MpscEventSenderQueue { - id: 0, - mpsc_sender: event_grp_0_sender, - }; - event_man.subscribe_group(event_grp_0.group_id(), event_grp_0_and_1_listener.id()); - event_man.subscribe_group(event_grp_1_0.group_id(), event_grp_0_and_1_listener.id()); - event_man.add_sender(event_grp_0_and_1_listener); - - event_sender - .send((event_grp_0, None)) - .expect("Sending Event Group 0 failed"); - event_sender - .send((event_grp_1_0, None)) - .expect("Sendign Event Group 1 failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_grp_0, 1); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_grp_1_0, 1); - - check_next_event(event_grp_0, &event_grp_0_receiver); - check_next_event(event_grp_1_0, &event_grp_0_receiver); - } - - /// Test listening to the same event from multiple listeners. Also test listening - /// to both group and single events from one listener - #[test] - fn test_listening_to_same_event_and_multi_type() { - 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_tx_0, event_0_rx_0) = channel(); - let (event_0_tx_1, event_0_rx_1) = channel(); - let event_listener_0 = MpscEventSenderQueue { - id: 0, - mpsc_sender: event_0_tx_0, - }; - let event_listener_1 = MpscEventSenderQueue { - id: 1, - mpsc_sender: event_0_tx_1, - }; - let event_listener_0_sender_id = event_listener_0.id(); - event_man.subscribe_single(&event_0, event_listener_0_sender_id); - event_man.add_sender(event_listener_0); - let event_listener_1_sender_id = event_listener_1.id(); - event_man.subscribe_single(&event_0, event_listener_1_sender_id); - event_man.add_sender(event_listener_1); - event_sender - .send((event_0, None)) - .expect("Triggering Event 0 failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_0, 2); - 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); - event_sender - .send((event_0, None)) - .expect("Triggering Event 0 failed"); - event_sender - .send((event_1, None)) - .expect("Triggering Event 1 failed"); - - // 3 Events messages will be sent now - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_0, 2); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_1, 1); - // 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); - - // Do double insertion and then remove duplicates - event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id); - event_man.remove_duplicates(&ListenerKey::Group(event_1.group_id())); - event_sender - .send((event_1, None)) - .expect("Triggering Event 1 failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_1, 1); - } - - #[test] - fn test_all_events_listener() { - let (event_sender, manager_queue) = channel(); - let event_man_receiver = MpscEventReceiver::new(manager_queue); - let mut event_man: EventManager> = - EventManager::new(Box::new(event_man_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) = channel(); - let all_events_listener = MpscEventSenderQueue { - id: 0, - mpsc_sender: event_0_tx_0, - }; - event_man.subscribe_all(all_events_listener.id()); - event_man.add_sender(all_events_listener); - event_sender - .send((event_0, None)) - .expect("Triggering event 0 failed"); - event_sender - .send((event_1, None)) - .expect("Triggering event 1 failed"); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_0, 1); - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - check_handled_event(res.unwrap(), event_1, 1); - check_next_event(event_0, &all_events_rx); - check_next_event(event_1, &all_events_rx); - } -} diff --git a/satrs-core/src/events.rs b/satrs-core/src/events.rs deleted file mode 100644 index d5763a9..0000000 --- a/satrs-core/src/events.rs +++ /dev/null @@ -1,801 +0,0 @@ -//! Event support module -//! -//! This module includes the basic event structs [EventU32] and [EventU16] and versions with the -//! ECSS severity levels as a type parameter. These structs are simple abstractions on top of the -//! [u32] and [u16] types where the raw value is the unique identifier for a particular event. -//! The abstraction also allows to group related events using a group ID, and the severity -//! of an event is encoded inside the raw value itself with four possible [Severity] levels: -//! -//! - INFO -//! - LOW -//! - MEDIUM -//! - HIGH -//! -//! All event structs implement the [EcssEnumeration] trait and can be created as constants. -//! This allows to easily create a static list of constant events which can then be used to generate -//! event telemetry using the PUS event manager modules. -//! -//! # Examples -//! -//! ``` -//! use satrs_core::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 TEMPERATURE_HIGH: EventU32TypedSev = EventU32TypedSev::const_new(2, 0); -//! -//! let small_event = EventU16::new(Severity::INFO, 3, 0); -//! ``` -use core::fmt::Debug; -use core::hash::Hash; -use core::marker::PhantomData; -use delegate::delegate; -use spacepackets::ecss::{EcssEnumeration, ToBeBytes}; -use spacepackets::{ByteConversionError, SizeMissmatch}; - -/// Using a type definition allows to change this to u64 in the future more easily -pub type LargestEventRaw = u32; -/// Using a type definition allows to change this to u32 in the future more easily -pub type LargestGroupIdRaw = u16; - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] -pub enum Severity { - INFO = 0, - LOW = 1, - MEDIUM = 2, - HIGH = 3, -} - -pub trait HasSeverity: Debug + PartialEq + Eq + Copy + Clone { - const SEVERITY: Severity; -} - -/// Type level support struct -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub struct SeverityInfo {} -impl HasSeverity for SeverityInfo { - 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; -} - -/// Type level support struct -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub struct SeverityMedium {} -impl HasSeverity for SeverityMedium { - 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; -} - -pub trait GenericEvent: EcssEnumeration { - type Raw; - type GroupId; - type UniqueId; - - fn raw(&self) -> Self::Raw; - fn severity(&self) -> Severity; - fn group_id(&self) -> Self::GroupId; - fn unique_id(&self) -> Self::UniqueId; - - fn raw_as_largest_type(&self) -> LargestEventRaw; - fn group_id_as_largest_type(&self) -> LargestGroupIdRaw; -} - -impl TryFrom for Severity { - type Error = (); - - 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), - _ => Err(()), - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -struct EventBase { - severity: Severity, - group_id: GID, - unique_id: UID, - phantom: PhantomData, -} - -impl EventBase { - fn write_to_bytes( - &self, - raw: RAW, - buf: &mut [u8], - width: usize, - ) -> Result<(), ByteConversionError> { - if buf.len() < width { - return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch { - found: buf.len(), - expected: width, - })); - } - buf.copy_from_slice(raw.to_be_bytes().as_ref()); - Ok(()) - } -} - -impl EventBase { - #[inline] - fn raw(&self) -> u32 { - ((self.severity as u32) << 30) | ((self.group_id as u32) << 16) | self.unique_id as u32 - } -} - -impl EventBase { - #[inline] - fn raw(&self) -> u16 { - ((self.severity as u16) << 14) | ((self.group_id as u16) << 8) | self.unique_id as u16 - } -} - -impl EventBase { - #[inline] - pub fn severity(&self) -> Severity { - self.severity - } -} - -impl EventBase { - #[inline] - pub fn unique_id(&self) -> u16 { - self.unique_id - } -} - -impl EventBase { - #[inline] - pub fn unique_id(&self) -> u8 { - self.unique_id - } -} - -impl EventBase { - #[inline] - pub fn group_id(&self) -> u16 { - self.group_id - } -} - -impl EventBase { - #[inline] - pub fn group_id(&self) -> u8 { - self.group_id - } -} - -macro_rules! event_provider_impl { - () => { - #[inline] - fn raw(&self) -> Self::Raw { - self.base.raw() - } - - /// Retrieve the severity of an event. Returns None if that severity bit field of the raw event - /// ID is invalid - #[inline] - fn severity(&self) -> Severity { - self.base.severity() - } - - #[inline] - fn group_id(&self) -> Self::GroupId { - self.base.group_id() - } - - #[inline] - fn unique_id(&self) -> Self::UniqueId { - self.base.unique_id() - } - }; -} - -macro_rules! impl_event_provider { - ($BaseIdent: ident, $TypedIdent: ident, $raw: ty, $gid: ty, $uid: ty) => { - impl GenericEvent for $BaseIdent { - type Raw = $raw; - type GroupId = $gid; - type UniqueId = $uid; - - event_provider_impl!(); - - fn raw_as_largest_type(&self) -> LargestEventRaw { - self.raw().into() - } - - fn group_id_as_largest_type(&self) -> LargestGroupIdRaw { - self.group_id().into() - } - } - - impl GenericEvent for $TypedIdent { - type Raw = $raw; - type GroupId = $gid; - type UniqueId = $uid; - - delegate!(to self.event { - fn raw(&self) -> Self::Raw; - fn severity(&self) -> Severity; - fn group_id(&self) -> Self::GroupId; - fn unique_id(&self) -> Self::UniqueId; - fn raw_as_largest_type(&self) -> LargestEventRaw; - fn group_id_as_largest_type(&self) -> LargestGroupIdRaw; - }); - } - } -} - -macro_rules! try_from_impls { - ($SevIdent: ident, $severity: path, $raw: ty, $TypedSevIdent: ident) => { - impl TryFrom<$raw> for $TypedSevIdent<$SevIdent> { - type Error = Severity; - - fn try_from(raw: $raw) -> Result { - Self::try_from_generic($severity, raw) - } - } - }; -} - -macro_rules! const_from_fn { - ($from_fn_name: ident, $TypedIdent: ident, $SevIdent: ident) => { - pub const fn $from_fn_name(event: $TypedIdent<$SevIdent>) -> Self { - Self { - base: event.event.base, - } - } - }; -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct EventU32 { - base: EventBase, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct EventU32TypedSev { - event: EventU32, - phantom: PhantomData, -} - -impl From> for EventU32 { - fn from(e: EventU32TypedSev) -> Self { - Self { base: e.event.base } - } -} - -impl AsRef for EventU32TypedSev { - fn as_ref(&self) -> &EventU32 { - &self.event - } -} - -impl AsMut for EventU32TypedSev { - fn as_mut(&mut self) -> &mut EventU32 { - &mut self.event - } -} - -impl_event_provider!(EventU32, EventU32TypedSev, u32, u16, u16); - -impl EventU32 { - /// Generate an event. The raw representation of an event has 32 bits. - /// If the passed group ID is invalid (too large), None wil be returned - /// - /// # Parameter - /// - /// * `severity`: Each event has a [severity][Severity]. The raw value of the severity will - /// be stored inside the uppermost 2 bits of the raw event ID - /// * `group_id`: Related events can be grouped using a group ID. The group ID will occupy the - /// 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( - severity: Severity, - group_id: ::GroupId, - unique_id: ::UniqueId, - ) -> Option { - if group_id > (2u16.pow(14) - 1) { - return None; - } - Some(Self { - base: EventBase { - severity, - group_id, - unique_id, - phantom: PhantomData, - }, - }) - } - pub const fn const_new( - severity: Severity, - group_id: ::GroupId, - unique_id: ::UniqueId, - ) -> Self { - if group_id > (2u16.pow(14) - 1) { - panic!("Group ID too large"); - } - Self { - base: EventBase { - severity, - group_id, - unique_id, - phantom: PhantomData, - }, - } - } - - 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 - let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap(); - 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) - } -} - -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 EcssEnumeration for EventU32 { - fn pfc(&self) -> u8 { - 32 - } - - fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> { - self.base.write_to_bytes(self.raw(), buf, self.byte_width()) - } -} - -//noinspection RsTraitImplementation -impl EcssEnumeration for EventU32TypedSev { - delegate!(to self.event { - fn pfc(&self) -> u8; - fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>; - }); -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct EventU16 { - base: EventBase, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct EventU16TypedSev { - event: EventU16, - phantom: PhantomData, -} - -impl AsRef for EventU16TypedSev { - fn as_ref(&self) -> &EventU16 { - &self.event - } -} - -impl AsMut for EventU16TypedSev { - fn as_mut(&mut self) -> &mut EventU16 { - &mut self.event - } -} - -impl EventU16 { - /// Generate a small event. The raw representation of a small event has 16 bits. - /// If the passed group ID is invalid (too large), [None] wil be returned - /// - /// # Parameter - /// - /// * `severity`: Each event has a [severity][Severity]. The raw value of the severity will - /// be stored inside the uppermost 2 bits of the raw event ID - /// * `group_id`: Related events can be grouped using a group ID. The group ID will occupy the - /// 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( - severity: Severity, - group_id: ::GroupId, - unique_id: ::UniqueId, - ) -> Option { - if group_id > (2u8.pow(6) - 1) { - return None; - } - Some(Self { - base: EventBase { - severity, - group_id, - unique_id, - phantom: Default::default(), - }, - }) - } - - /// Const version of [Self::new], but panics on invalid group ID input values. - pub const fn const_new( - severity: Severity, - group_id: ::GroupId, - unique_id: ::UniqueId, - ) -> Self { - if group_id > (2u8.pow(6) - 1) { - panic!("Group ID too large"); - } - Self { - base: EventBase { - severity, - group_id, - unique_id, - phantom: PhantomData, - }, - } - } - 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 { - 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, - )) - } -} - -impl_event_provider!(EventU16, EventU16TypedSev, u16, u8, u8); - -impl EcssEnumeration for EventU16 { - #[inline] - fn pfc(&self) -> u8 { - 16 - } - - fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> { - self.base.write_to_bytes(self.raw(), buf, self.byte_width()) - } -} - -//noinspection RsTraitImplementation -impl EcssEnumeration for EventU16TypedSev { - delegate!(to self.event { - fn pfc(&self) -> u8; - fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>; - }); -} - -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); - -impl PartialEq for EventU32TypedSev { - #[inline] - fn eq(&self, other: &EventU32) -> bool { - self.raw() == other.raw() - } -} - -impl PartialEq> for EventU32 { - #[inline] - fn eq(&self, other: &EventU32TypedSev) -> bool { - self.raw() == other.raw() - } -} - -impl PartialEq for EventU16TypedSev { - #[inline] - fn eq(&self, other: &EventU16) -> bool { - self.raw() == other.raw() - } -} - -impl PartialEq> for EventU16 { - #[inline] - fn eq(&self, other: &EventU16TypedSev) -> bool { - self.raw() == other.raw() - } -} - -#[cfg(test)] -mod tests { - use super::EventU32TypedSev; - use super::*; - use spacepackets::ecss::EcssEnumeration; - use spacepackets::ByteConversionError; - use std::mem::size_of; - - fn assert_size(_: T, val: usize) { - 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); - - /// This working is a test in itself. - const INFO_REDUCED: EventU32 = EventU32::const_from_info(INFO_EVENT); - - #[test] - fn test_normal_from_raw_conversion() { - let conv_from_raw = EventU32TypedSev::::try_from(INFO_EVENT.raw()) - .expect("Creating typed EventU32 failed"); - assert_eq!(conv_from_raw, INFO_EVENT); - } - - #[test] - fn test_small_from_raw_conversion() { - let conv_from_raw = EventU16TypedSev::::try_from(INFO_EVENT_SMALL.raw()) - .expect("Creating typed EventU16 failed"); - assert_eq!(conv_from_raw, INFO_EVENT_SMALL); - } - - #[test] - fn verify_normal_size() { - assert_size(INFO_EVENT.raw(), 4) - } - - #[test] - fn verify_small_size() { - assert_size(INFO_EVENT_SMALL.raw(), 2) - } - - #[test] - fn test_normal_event_getters() { - 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(); - assert_eq!(raw_event, 0x00000000); - } - - #[test] - fn test_small_event_getters() { - 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(); - assert_eq!(raw_event, 0x00000000); - } - - #[test] - fn all_ones_event_regular() { - 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(); - assert_eq!(raw_event, 0xFFFFFFFF); - } - - #[test] - fn all_ones_event_small() { - 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(); - assert_eq!(raw_event, 0xFFFF); - } - - #[test] - fn invalid_group_id_normal() { - assert!(EventU32TypedSev::::new(2_u16.pow(14), 0).is_none()); - } - - #[test] - fn invalid_group_id_small() { - assert!(EventU16TypedSev::::new(2_u8.pow(6), 0).is_none()); - } - - #[test] - fn regular_new() { - assert_eq!( - EventU32TypedSev::::new(0, 0).expect("Creating regular event failed"), - INFO_EVENT - ); - } - - #[test] - fn small_new() { - assert_eq!( - EventU16TypedSev::::new(0, 0).expect("Creating regular event failed"), - INFO_EVENT_SMALL - ); - } - - #[test] - fn as_largest_type() { - let event_raw = HIGH_SEV_EVENT.raw_as_largest_type(); - assert_size(event_raw, 4); - assert_eq!(event_raw, 0xFFFFFFFF); - } - - #[test] - fn as_largest_type_for_small_event() { - let event_raw = HIGH_SEV_EVENT_SMALL.raw_as_largest_type(); - assert_size(event_raw, 4); - assert_eq!(event_raw, 0xFFFF); - } - - #[test] - fn as_largest_group_id() { - let group_id = HIGH_SEV_EVENT.group_id_as_largest_type(); - assert_size(group_id, 2); - assert_eq!(group_id, 0x3FFF); - } - - #[test] - fn as_largest_group_id_small_event() { - let group_id = HIGH_SEV_EVENT_SMALL.group_id_as_largest_type(); - assert_size(group_id, 2); - assert_eq!(group_id, 0x3F); - } - - #[test] - fn write_to_buf() { - let mut buf: [u8; 4] = [0; 4]; - 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); - } - - #[test] - fn write_to_buf_small() { - let mut buf: [u8; 2] = [0; 2]; - 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); - } - - #[test] - fn write_to_buf_insufficient_buf() { - let mut buf: [u8; 3] = [0; 3]; - let err = HIGH_SEV_EVENT.write_to_be_bytes(&mut buf); - assert!(err.is_err()); - let err = err.unwrap_err(); - if let ByteConversionError::ToSliceTooSmall(missmatch) = err { - assert_eq!(missmatch.expected, 4); - assert_eq!(missmatch.found, 3); - } - } - - #[test] - fn write_to_buf_small_insufficient_buf() { - let mut buf: [u8; 1] = [0; 1]; - let err = HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf); - assert!(err.is_err()); - let err = err.unwrap_err(); - if let ByteConversionError::ToSliceTooSmall(missmatch) = err { - assert_eq!(missmatch.expected, 2); - assert_eq!(missmatch.found, 1); - } - } - - #[test] - fn severity_from_invalid_raw_val() { - let invalid = 0xFF; - assert!(Severity::try_from(invalid).is_err()); - 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 raw = event.raw(); - let reduced: EventU32 = event.into(); - assert_eq!(reduced.group_id(), 1); - assert_eq!(reduced.unique_id(), 1); - assert_eq!(raw, reduced.raw()); - } - - #[test] - fn const_reducation() { - assert_eq!(INFO_REDUCED.raw(), INFO_EVENT.raw()); - } -} diff --git a/satrs-core/src/executable.rs b/satrs-core/src/executable.rs deleted file mode 100644 index 440f8ee..0000000 --- a/satrs-core/src/executable.rs +++ /dev/null @@ -1,503 +0,0 @@ -//! Task scheduling module -use bus::BusReader; -use std::boxed::Box; -use std::error::Error; -use std::sync::mpsc::TryRecvError; -use std::thread; -use std::thread::JoinHandle; -use std::time::Duration; -use std::vec; -use std::vec::Vec; - -#[derive(Debug, PartialEq, Eq)] -pub enum OpResult { - Ok, - TerminationRequested, -} - -pub enum ExecutionType { - Infinite, - Cycles(u32), - OneShot, -} - -pub trait Executable: Send { - type Error; - - fn exec_type(&self) -> ExecutionType; - fn task_name(&self) -> &'static str; - fn periodic_op(&mut self, op_code: i32) -> Result; -} - -/// This function allows executing one task which implements the [Executable][Executable] trait -/// -/// # Arguments -/// -/// * `executable`: Executable task -/// * `task_freq`: Optional frequency of task. Required for periodic and fixed cycle tasks -/// * `op_code`: Operation code which is passed to the executable task [operation call][Executable::periodic_op] -/// * `termination`: Optional termination handler which can cancel threads with a broadcast -pub fn exec_sched_single< - T: Executable + Send + 'static + ?Sized, - E: Error + Send + 'static, ->( - mut executable: Box, - task_freq: Option, - op_code: i32, - mut termination: Option>, -) -> JoinHandle> { - let mut cycle_count = 0; - thread::spawn(move || loop { - if let Some(ref mut terminator) = termination { - match terminator.try_recv() { - Ok(_) | Err(TryRecvError::Disconnected) => { - return Ok(OpResult::Ok); - } - Err(TryRecvError::Empty) => (), - } - } - match executable.exec_type() { - ExecutionType::OneShot => { - executable.periodic_op(op_code)?; - return Ok(OpResult::Ok); - } - ExecutionType::Infinite => { - executable.periodic_op(op_code)?; - } - ExecutionType::Cycles(cycles) => { - executable.periodic_op(op_code)?; - cycle_count += 1; - if cycle_count == cycles { - return Ok(OpResult::Ok); - } - } - } - let freq = task_freq.unwrap_or_else(|| panic!("No task frequency specified")); - thread::sleep(freq); - }) -} - -/// This function allows executing multiple tasks as long as the tasks implement the -/// [Executable][Executable] trait -/// -/// # Arguments -/// -/// * `executable_vec`: Vector of executable objects -/// * `task_freq`: Optional frequency of task. Required for periodic and fixed cycle tasks -/// * `op_code`: Operation code which is passed to the executable task [operation call][Executable::periodic_op] -/// * `termination`: Optional termination handler which can cancel threads with a broadcast -pub fn exec_sched_multi< - T: Executable + Send + 'static + ?Sized, - E: Error + Send + 'static, ->( - mut executable_vec: Vec>, - task_freq: Option, - op_code: i32, - mut termination: Option>, -) -> JoinHandle> { - let mut cycle_counts = vec![0; executable_vec.len()]; - let mut removal_flags = vec![false; executable_vec.len()]; - thread::spawn(move || loop { - if let Some(ref mut terminator) = termination { - match terminator.try_recv() { - Ok(_) | Err(TryRecvError::Disconnected) => { - removal_flags.iter_mut().for_each(|x| *x = true); - } - Err(TryRecvError::Empty) => (), - } - } - for (idx, executable) in executable_vec.iter_mut().enumerate() { - match executable.exec_type() { - ExecutionType::OneShot => { - executable.periodic_op(op_code)?; - removal_flags[idx] = true; - } - ExecutionType::Infinite => { - executable.periodic_op(op_code)?; - } - ExecutionType::Cycles(cycles) => { - executable.periodic_op(op_code)?; - cycle_counts[idx] += 1; - if cycle_counts[idx] == cycles { - removal_flags[idx] = true; - } - } - } - } - let mut removal_iter = removal_flags.iter(); - executable_vec.retain(|_| !*removal_iter.next().unwrap()); - removal_iter = removal_flags.iter(); - cycle_counts.retain(|_| !*removal_iter.next().unwrap()); - removal_flags.retain(|&i| !i); - if executable_vec.is_empty() { - return Ok(OpResult::Ok); - } - let freq = task_freq.unwrap_or_else(|| panic!("No task frequency specified")); - thread::sleep(freq); - }) -} - -#[cfg(test)] -mod tests { - use super::{exec_sched_multi, exec_sched_single, Executable, ExecutionType, OpResult}; - use bus::Bus; - use std::boxed::Box; - use std::error::Error; - use std::string::{String, ToString}; - use std::sync::{Arc, Mutex}; - use std::time::Duration; - use std::vec::Vec; - use std::{fmt, thread, vec}; - - struct TestInfo { - exec_num: u32, - op_code: i32, - } - struct OneShotTask { - exec_num: Arc>, - } - struct FixedCyclesTask { - cycles: u32, - exec_num: Arc>, - } - struct PeriodicTask { - exec_num: Arc>, - } - - #[derive(Clone, Debug)] - struct ExampleError { - kind: ErrorKind, - } - - /// The kind of an error that can occur. - #[derive(Clone, Debug)] - pub enum ErrorKind { - Generic(String, i32), - } - - impl ExampleError { - fn new(msg: &str, code: i32) -> ExampleError { - ExampleError { - kind: ErrorKind::Generic(msg.to_string(), code), - } - } - - /// Return the kind of this error. - pub fn kind(&self) -> &ErrorKind { - &self.kind - } - } - - impl fmt::Display for ExampleError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.kind() { - ErrorKind::Generic(str, code) => { - write!(f, "{str} with code {code}") - } - } - } - } - - impl Error for ExampleError {} - - const ONE_SHOT_TASK_NAME: &str = "One Shot Task"; - - impl Executable for OneShotTask { - type Error = ExampleError; - - fn exec_type(&self) -> ExecutionType { - ExecutionType::OneShot - } - - fn task_name(&self) -> &'static str { - ONE_SHOT_TASK_NAME - } - - fn periodic_op(&mut self, op_code: i32) -> Result { - let mut data = self.exec_num.lock().expect("Locking Mutex failed"); - data.exec_num += 1; - data.op_code = op_code; - std::mem::drop(data); - if op_code >= 0 { - Ok(OpResult::Ok) - } else { - Err(ExampleError::new("One Shot Task Failure", op_code)) - } - } - } - - const CYCLE_TASK_NAME: &str = "Fixed Cycles Task"; - - impl Executable for FixedCyclesTask { - type Error = ExampleError; - - fn exec_type(&self) -> ExecutionType { - ExecutionType::Cycles(self.cycles) - } - - fn task_name(&self) -> &'static str { - CYCLE_TASK_NAME - } - - fn periodic_op(&mut self, op_code: i32) -> Result { - let mut data = self.exec_num.lock().expect("Locking Mutex failed"); - data.exec_num += 1; - data.op_code = op_code; - std::mem::drop(data); - if op_code >= 0 { - Ok(OpResult::Ok) - } else { - Err(ExampleError::new("Fixed Cycle Task Failure", op_code)) - } - } - } - - const PERIODIC_TASK_NAME: &str = "Periodic Task"; - - impl Executable for PeriodicTask { - type Error = ExampleError; - - fn exec_type(&self) -> ExecutionType { - ExecutionType::Infinite - } - - fn task_name(&self) -> &'static str { - PERIODIC_TASK_NAME - } - - fn periodic_op(&mut self, op_code: i32) -> Result { - let mut data = self.exec_num.lock().expect("Locking Mutex failed"); - data.exec_num += 1; - data.op_code = op_code; - std::mem::drop(data); - if op_code >= 0 { - Ok(OpResult::Ok) - } else { - Err(ExampleError::new("Example Task Failure", op_code)) - } - } - } - - #[test] - fn test_simple_one_shot() { - let expected_op_code = 42; - let shared = Arc::new(Mutex::new(TestInfo { - exec_num: 0, - op_code: 0, - })); - let exec_task = OneShotTask { - exec_num: shared.clone(), - }; - let task = Box::new(exec_task); - let jhandle = exec_sched_single( - task, - Some(Duration::from_millis(100)), - expected_op_code, - None, - ); - let thread_res = jhandle.join().expect("One Shot Task failed"); - assert!(thread_res.is_ok()); - assert_eq!(thread_res.unwrap(), OpResult::Ok); - let data = shared.lock().expect("Locking Mutex failed"); - assert_eq!(data.exec_num, 1); - assert_eq!(data.op_code, expected_op_code); - } - - #[test] - fn test_failed_one_shot() { - let op_code_inducing_failure = -1; - let shared = Arc::new(Mutex::new(TestInfo { - exec_num: 0, - op_code: 0, - })); - let exec_task = OneShotTask { - exec_num: shared.clone(), - }; - let task = Box::new(exec_task); - let jhandle = exec_sched_single( - task, - Some(Duration::from_millis(100)), - op_code_inducing_failure, - None, - ); - let thread_res = jhandle.join().expect("One Shot Task failed"); - assert!(thread_res.is_err()); - let error = thread_res.unwrap_err(); - let err = error.kind(); - assert!(matches!(err, &ErrorKind::Generic { .. })); - match err { - ErrorKind::Generic(str, op_code) => { - assert_eq!(str, &String::from("One Shot Task Failure")); - assert_eq!(op_code, &op_code_inducing_failure); - } - } - let error_display = error.to_string(); - assert_eq!(error_display, "One Shot Task Failure with code -1"); - let data = shared.lock().expect("Locking Mutex failed"); - assert_eq!(data.exec_num, 1); - assert_eq!(data.op_code, op_code_inducing_failure); - } - - #[test] - fn test_simple_multi_one_shot() { - let expected_op_code = 43; - let shared = Arc::new(Mutex::new(TestInfo { - exec_num: 0, - op_code: 0, - })); - let exec_task_0 = OneShotTask { - exec_num: shared.clone(), - }; - let exec_task_1 = OneShotTask { - exec_num: shared.clone(), - }; - let task_vec = vec![Box::new(exec_task_0), Box::new(exec_task_1)]; - for task in task_vec.iter() { - assert_eq!(task.task_name(), ONE_SHOT_TASK_NAME); - } - let jhandle = exec_sched_multi( - task_vec, - Some(Duration::from_millis(100)), - expected_op_code, - None, - ); - let thread_res = jhandle.join().expect("One Shot Task failed"); - assert!(thread_res.is_ok()); - assert_eq!(thread_res.unwrap(), OpResult::Ok); - let data = shared.lock().expect("Locking Mutex failed"); - assert_eq!(data.exec_num, 2); - assert_eq!(data.op_code, expected_op_code); - } - - #[test] - fn test_cycles_single() { - let expected_op_code = 44; - let shared = Arc::new(Mutex::new(TestInfo { - exec_num: 0, - op_code: 0, - })); - let cycled_task = Box::new(FixedCyclesTask { - exec_num: shared.clone(), - cycles: 1, - }); - assert_eq!(cycled_task.task_name(), CYCLE_TASK_NAME); - let jh = exec_sched_single( - cycled_task, - Some(Duration::from_millis(100)), - expected_op_code, - None, - ); - let thread_res = jh.join().expect("Cycles Task failed"); - assert!(thread_res.is_ok()); - let data = shared.lock().expect("Locking Mutex failed"); - assert_eq!(thread_res.unwrap(), OpResult::Ok); - assert_eq!(data.exec_num, 1); - assert_eq!(data.op_code, expected_op_code); - } - - #[test] - fn test_single_and_cycles() { - let expected_op_code = 50; - let shared = Arc::new(Mutex::new(TestInfo { - exec_num: 0, - op_code: 0, - })); - let one_shot_task = Box::new(OneShotTask { - exec_num: shared.clone(), - }); - let cycled_task_0 = Box::new(FixedCyclesTask { - exec_num: shared.clone(), - cycles: 1, - }); - let cycled_task_1 = Box::new(FixedCyclesTask { - exec_num: shared.clone(), - cycles: 1, - }); - assert_eq!(cycled_task_0.task_name(), CYCLE_TASK_NAME); - assert_eq!(one_shot_task.task_name(), ONE_SHOT_TASK_NAME); - let task_vec: Vec>> = - vec![one_shot_task, cycled_task_0, cycled_task_1]; - let jh = exec_sched_multi( - task_vec, - Some(Duration::from_millis(100)), - expected_op_code, - None, - ); - let thread_res = jh.join().expect("Cycles Task failed"); - assert!(thread_res.is_ok()); - let data = shared.lock().expect("Locking Mutex failed"); - assert_eq!(thread_res.unwrap(), OpResult::Ok); - assert_eq!(data.exec_num, 3); - assert_eq!(data.op_code, expected_op_code); - } - - #[test] - #[ignore] - fn test_periodic_single() { - let mut terminator = Bus::new(5); - let expected_op_code = 45; - let shared = Arc::new(Mutex::new(TestInfo { - exec_num: 0, - op_code: 0, - })); - let periodic_task = Box::new(PeriodicTask { - exec_num: shared.clone(), - }); - assert_eq!(periodic_task.task_name(), PERIODIC_TASK_NAME); - let jh = exec_sched_single( - periodic_task, - Some(Duration::from_millis(20)), - expected_op_code, - Some(terminator.add_rx()), - ); - thread::sleep(Duration::from_millis(40)); - terminator.broadcast(()); - let thread_res = jh.join().expect("Periodic Task failed"); - assert!(thread_res.is_ok()); - let data = shared.lock().expect("Locking Mutex failed"); - assert_eq!(thread_res.unwrap(), OpResult::Ok); - let range = 2..4; - assert!(range.contains(&data.exec_num)); - assert_eq!(data.op_code, expected_op_code); - } - - #[test] - #[ignore] - fn test_periodic_multi() { - let mut terminator = Bus::new(5); - let expected_op_code = 46; - let shared = Arc::new(Mutex::new(TestInfo { - exec_num: 0, - op_code: 0, - })); - let cycled_task = Box::new(FixedCyclesTask { - exec_num: shared.clone(), - cycles: 1, - }); - let periodic_task_0 = Box::new(PeriodicTask { - exec_num: shared.clone(), - }); - let periodic_task_1 = Box::new(PeriodicTask { - exec_num: shared.clone(), - }); - assert_eq!(periodic_task_0.task_name(), PERIODIC_TASK_NAME); - assert_eq!(periodic_task_1.task_name(), PERIODIC_TASK_NAME); - let task_vec: Vec>> = - vec![cycled_task, periodic_task_0, periodic_task_1]; - let jh = exec_sched_multi( - task_vec, - Some(Duration::from_millis(20)), - expected_op_code, - Some(terminator.add_rx()), - ); - thread::sleep(Duration::from_millis(60)); - terminator.broadcast(()); - let thread_res = jh.join().expect("Periodic Task failed"); - assert!(thread_res.is_ok()); - let data = shared.lock().expect("Locking Mutex failed"); - assert_eq!(thread_res.unwrap(), OpResult::Ok); - let range = 7..11; - assert!(range.contains(&data.exec_num)); - assert_eq!(data.op_code, expected_op_code); - } -} diff --git a/satrs-core/src/hal/host/mod.rs b/satrs-core/src/hal/host/mod.rs deleted file mode 100644 index 8057db1..0000000 --- a/satrs-core/src/hal/host/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -//! Helper modules intended to be used on hosts with a full [std] runtime -pub mod udp_server; diff --git a/satrs-core/src/hal/host/udp_server.rs b/satrs-core/src/hal/host/udp_server.rs deleted file mode 100644 index ca0a26e..0000000 --- a/satrs-core/src/hal/host/udp_server.rs +++ /dev/null @@ -1,212 +0,0 @@ -//! UDP server helper components -use crate::tmtc::{ReceivesTc, ReceivesTcCore}; -use std::boxed::Box; -use std::io::{Error, ErrorKind}; -use std::net::{SocketAddr, ToSocketAddrs, UdpSocket}; -use std::vec; -use std::vec::Vec; - -/// This TC server helper can be used to receive raw PUS telecommands thorough a UDP interface. -/// -/// It caches all received telecomands into a vector. The maximum expected telecommand size should -/// be declared upfront. This avoids dynamic allocation during run-time. The user can specify a TC -/// receiver in form of a special trait object which implements [ReceivesTc]. Please note that the -/// receiver should copy out the received data if it the data is required past the -/// [ReceivesTcCore::pass_tc] call. -/// -/// # Examples -/// -/// ``` -/// use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; -/// use satrs_core::hal::host::udp_server::UdpTcServer; -/// use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore}; -/// use spacepackets::SpHeader; -/// use spacepackets::tc::PusTc; -/// -/// #[derive (Default)] -/// struct PingReceiver {} -/// impl ReceivesTcCore for PingReceiver { -/// type Error = (); -/// fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> { -/// assert_eq!(tc_raw.len(), 13); -/// Ok(()) -/// } -/// } -/// -/// let mut buf = [0; 32]; -/// let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7777); -/// let ping_receiver = PingReceiver::default(); -/// let mut udp_tc_server = UdpTcServer::new(dest_addr, 2048, Box::new(ping_receiver)) -/// .expect("Creating UDP TMTC server failed"); -/// let mut sph = SpHeader::tc_unseg(0x02, 0, 0).unwrap(); -/// let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true); -/// let len = pus_tc -/// .write_to_bytes(&mut buf) -/// .expect("Error writing PUS TC packet"); -/// assert_eq!(len, 13); -/// let client = UdpSocket::bind("127.0.0.1:7778").expect("Connecting to UDP server failed"); -/// client -/// .send_to(&buf[0..len], dest_addr) -/// .expect("Error sending PUS TC via UDP"); -/// ``` -/// -/// The [fsrc-example crate](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-example) -/// server code also includes -/// [example code](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-example/src/bin/obsw/tmtc.rs) -/// on how to use this TC server. It uses the server to receive PUS telecommands on a specific port -/// and then forwards them to a generic CCSDS packet receiver. -pub struct UdpTcServer { - pub socket: UdpSocket, - recv_buf: Vec, - sender_addr: Option, - tc_receiver: Box>, -} - -#[derive(Debug)] -pub enum ReceiveResult { - NothingReceived, - IoError(Error), - ReceiverError(E), -} - -impl From for ReceiveResult { - fn from(e: Error) -> Self { - ReceiveResult::IoError(e) - } -} - -impl PartialEq for ReceiveResult { - fn eq(&self, other: &Self) -> bool { - use ReceiveResult::*; - match (self, other) { - (IoError(ref e), IoError(ref other_e)) => e.kind() == other_e.kind(), - (NothingReceived, NothingReceived) => true, - (ReceiverError(e), ReceiverError(other_e)) => e == other_e, - _ => false, - } - } -} - -impl Eq for ReceiveResult {} - -impl ReceivesTcCore for UdpTcServer { - type Error = E; - - fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> { - self.tc_receiver.pass_tc(tc_raw) - } -} - -impl UdpTcServer { - pub fn new( - addr: A, - max_recv_size: usize, - tc_receiver: Box>, - ) -> Result { - let server = Self { - socket: UdpSocket::bind(addr)?, - recv_buf: vec![0; max_recv_size], - sender_addr: None, - tc_receiver, - }; - server.socket.set_nonblocking(true)?; - Ok(server) - } - - pub fn try_recv_tc(&mut self) -> Result<(usize, SocketAddr), ReceiveResult> { - let res = match self.socket.recv_from(&mut self.recv_buf) { - Ok(res) => res, - Err(e) => { - return if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::TimedOut { - Err(ReceiveResult::NothingReceived) - } else { - Err(e.into()) - } - } - }; - let (num_bytes, from) = res; - self.sender_addr = Some(from); - self.tc_receiver - .pass_tc(&self.recv_buf[0..num_bytes]) - .map_err(|e| ReceiveResult::ReceiverError(e))?; - Ok(res) - } - - pub fn last_sender(&self) -> Option { - self.sender_addr - } -} - -#[cfg(test)] -mod tests { - use crate::hal::host::udp_server::{ReceiveResult, UdpTcServer}; - use crate::tmtc::ReceivesTcCore; - use spacepackets::tc::PusTc; - use spacepackets::SpHeader; - use std::boxed::Box; - use std::collections::VecDeque; - use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; - use std::vec::Vec; - - fn is_send(_: &T) {} - - #[derive(Default)] - struct PingReceiver { - pub sent_cmds: VecDeque>, - } - - impl ReceivesTcCore for PingReceiver { - type Error = (); - - fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> { - let mut sent_data = Vec::new(); - sent_data.extend_from_slice(tc_raw); - self.sent_cmds.push_back(sent_data); - Ok(()) - } - } - - #[test] - fn basic_test() { - let mut buf = [0; 32]; - let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7777); - let ping_receiver = PingReceiver::default(); - is_send(&ping_receiver); - let mut udp_tc_server = UdpTcServer::new(dest_addr, 2048, Box::new(ping_receiver)) - .expect("Creating UDP TMTC server failed"); - is_send(&udp_tc_server); - let mut sph = SpHeader::tc_unseg(0x02, 0, 0).unwrap(); - let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true); - let len = pus_tc - .write_to_bytes(&mut buf) - .expect("Error writing PUS TC packet"); - let client = UdpSocket::bind("127.0.0.1:7778").expect("Connecting to UDP server failed"); - client - .send_to(&buf[0..len], dest_addr) - .expect("Error sending PUS TC via UDP"); - let local_addr = client.local_addr().unwrap(); - udp_tc_server - .try_recv_tc() - .expect("Error receiving sent telecommand"); - assert_eq!( - udp_tc_server.last_sender().expect("No sender set"), - local_addr - ); - let ping_receiver: &mut PingReceiver = udp_tc_server.tc_receiver.downcast_mut().unwrap(); - assert_eq!(ping_receiver.sent_cmds.len(), 1); - let sent_cmd = ping_receiver.sent_cmds.pop_front().unwrap(); - assert_eq!(sent_cmd, buf[0..len]); - } - - #[test] - fn test_nothing_received() { - let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7779); - let ping_receiver = PingReceiver::default(); - let mut udp_tc_server = UdpTcServer::new(dest_addr, 2048, Box::new(ping_receiver)) - .expect("Creating UDP TMTC server failed"); - let res = udp_tc_server.try_recv_tc(); - assert!(res.is_err()); - let err = res.unwrap_err(); - assert_eq!(err, ReceiveResult::NothingReceived); - } -} diff --git a/satrs-core/src/hal/mod.rs b/satrs-core/src/hal/mod.rs deleted file mode 100644 index c422a72..0000000 --- a/satrs-core/src/hal/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! # Hardware Abstraction Layer module -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -pub mod host; diff --git a/satrs-core/src/lib.rs b/satrs-core/src/lib.rs deleted file mode 100644 index 51ee886..0000000 --- a/satrs-core/src/lib.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! # Core components of the Flight Software Rust Crate (FSRC) collection -//! -//! This is a collection of Rust crates which can be used to build On-Board Software for remote -//! systems like satellites of rovers. It has special support for space tailored protocols -//! like the ones provided by CCSDS and ECSS. -//! -//! The crates can generally be used in a `no_std` environment, but some crates expect that the -//! [alloc](https://doc.rust-lang.org/alloc) crate is available to allow boxed trait objects. -//! These are used to supply user code to the crates. -#![no_std] -#![cfg_attr(doc_cfg, feature(doc_cfg))] -#[cfg(feature = "alloc")] -extern crate alloc; -#[cfg(feature = "alloc")] -extern crate downcast_rs; -#[cfg(any(feature = "std", test))] -extern crate std; - -pub mod error; -#[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] -pub mod event_man; -pub mod events; -#[cfg(feature = "std")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] -pub mod executable; -pub mod hal; -pub mod objects; -pub mod params; -#[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] -pub mod pool; -pub mod pus; -pub mod res_code; -pub mod seq_count; -pub mod tmtc; - -pub use spacepackets; diff --git a/satrs-core/src/objects.rs b/satrs-core/src/objects.rs deleted file mode 100644 index bc17696..0000000 --- a/satrs-core/src/objects.rs +++ /dev/null @@ -1,306 +0,0 @@ -//! # Module providing addressable object support and a manager for them -//! -//! Each addressable object can be identified using an [object ID][ObjectId]. -//! The [system object][ManagedSystemObject] trait also allows storing these objects into the -//! [object manager][ObjectManager]. They can then be retrieved and casted back to a known type -//! using the object ID. -//! -//! # Examples -//! -//! ```rust -//! use std::any::Any; -//! use std::error::Error; -//! use satrs_core::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject}; -//! -//! struct ExampleSysObj { -//! id: ObjectId, -//! dummy: u32, -//! was_initialized: bool, -//! } -//! -//! impl ExampleSysObj { -//! fn new(id: ObjectId, dummy: u32) -> ExampleSysObj { -//! ExampleSysObj { -//! id, -//! dummy, -//! was_initialized: false, -//! } -//! } -//! } -//! -//! impl SystemObject for ExampleSysObj { -//! type Error = (); -//! fn get_object_id(&self) -> &ObjectId { -//! &self.id -//! } -//! -//! fn initialize(&mut self) -> Result<(), Self::Error> { -//! self.was_initialized = true; -//! Ok(()) -//! } -//! } -//! -//! impl ManagedSystemObject for ExampleSysObj {} -//! -//! let mut obj_manager = ObjectManager::default(); -//! let obj_id = ObjectId { id: 0, name: "Example 0"}; -//! let example_obj = ExampleSysObj::new(obj_id, 42); -//! obj_manager.insert(Box::new(example_obj)); -//! let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get_ref(&obj_id); -//! let example_obj = obj_back_casted.unwrap(); -//! assert_eq!(example_obj.id, obj_id); -//! assert_eq!(example_obj.dummy, 42); -//! ``` -#[cfg(feature = "alloc")] -use alloc::boxed::Box; -#[cfg(feature = "alloc")] -pub use alloc_mod::*; -#[cfg(feature = "alloc")] -use downcast_rs::Downcast; -#[cfg(feature = "alloc")] -use hashbrown::HashMap; -#[cfg(feature = "std")] -use std::error::Error; - -#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] -pub struct ObjectId { - pub id: u32, - pub name: &'static str, -} - -#[cfg(feature = "alloc")] -pub mod alloc_mod { - use super::*; - - /// Each object which is stored inside the [object manager][ObjectManager] needs to implemented - /// this trait - pub trait SystemObject: Downcast { - type Error; - fn get_object_id(&self) -> &ObjectId; - fn initialize(&mut self) -> Result<(), Self::Error>; - } - downcast_rs::impl_downcast!(SystemObject assoc Error); - - pub trait ManagedSystemObject: SystemObject + Send {} - downcast_rs::impl_downcast!(ManagedSystemObject assoc Error); - - /// Helper module to manage multiple [ManagedSystemObjects][ManagedSystemObject] by mapping them - /// using an [object ID][ObjectId] - #[cfg(feature = "alloc")] - pub struct ObjectManager { - obj_map: HashMap>>, - } - - #[cfg(feature = "alloc")] - impl Default for ObjectManager { - fn default() -> Self { - Self::new() - } - } - - #[cfg(feature = "alloc")] - impl ObjectManager { - pub fn new() -> Self { - ObjectManager { - obj_map: HashMap::new(), - } - } - pub fn insert(&mut self, sys_obj: Box>) -> bool { - let obj_id = sys_obj.get_object_id(); - if self.obj_map.contains_key(obj_id) { - return false; - } - self.obj_map.insert(*obj_id, sys_obj).is_none() - } - - /// Initializes all System Objects in the hash map and returns the number of successful - /// initializations - pub fn initialize(&mut self) -> Result> { - let mut init_success = 0; - for val in self.obj_map.values_mut() { - if val.initialize().is_ok() { - init_success += 1 - } - } - Ok(init_success) - } - - /// Retrieve a reference to an object stored inside the manager. The type to retrieve needs to - /// be explicitly passed as a generic parameter or specified on the left hand side of the - /// expression. - pub fn get_ref>(&self, key: &ObjectId) -> Option<&T> { - self.obj_map.get(key).and_then(|o| o.downcast_ref::()) - } - - /// Retrieve a mutable reference to an object stored inside the manager. The type to retrieve - /// needs to be explicitly passed as a generic parameter or specified on the left hand side - /// of the expression. - pub fn get_mut>( - &mut self, - key: &ObjectId, - ) -> Option<&mut T> { - self.obj_map - .get_mut(key) - .and_then(|o| o.downcast_mut::()) - } - } -} - -#[cfg(test)] -mod tests { - use crate::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject}; - use std::boxed::Box; - use std::string::String; - use std::sync::{Arc, Mutex}; - use std::thread; - - struct ExampleSysObj { - id: ObjectId, - dummy: u32, - was_initialized: bool, - } - - impl ExampleSysObj { - fn new(id: ObjectId, dummy: u32) -> ExampleSysObj { - ExampleSysObj { - id, - dummy, - was_initialized: false, - } - } - } - - impl SystemObject for ExampleSysObj { - type Error = (); - fn get_object_id(&self) -> &ObjectId { - &self.id - } - - fn initialize(&mut self) -> Result<(), Self::Error> { - self.was_initialized = true; - Ok(()) - } - } - - impl ManagedSystemObject for ExampleSysObj {} - - struct OtherExampleObject { - id: ObjectId, - string: String, - was_initialized: bool, - } - - impl SystemObject for OtherExampleObject { - type Error = (); - fn get_object_id(&self) -> &ObjectId { - &self.id - } - - fn initialize(&mut self) -> Result<(), Self::Error> { - self.was_initialized = true; - Ok(()) - } - } - - impl ManagedSystemObject for OtherExampleObject {} - - #[test] - fn test_obj_manager_simple() { - let mut obj_manager = ObjectManager::default(); - let expl_obj_id = ObjectId { - id: 0, - name: "Example 0", - }; - let example_obj = ExampleSysObj::new(expl_obj_id, 42); - assert!(obj_manager.insert(Box::new(example_obj))); - let res = obj_manager.initialize(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), 1); - let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get_ref(&expl_obj_id); - assert!(obj_back_casted.is_some()); - let expl_obj_back_casted = obj_back_casted.unwrap(); - assert_eq!(expl_obj_back_casted.dummy, 42); - assert!(expl_obj_back_casted.was_initialized); - - let second_obj_id = ObjectId { - id: 12, - name: "Example 1", - }; - let second_example_obj = OtherExampleObject { - id: second_obj_id, - string: String::from("Hello Test"), - was_initialized: false, - }; - - assert!(obj_manager.insert(Box::new(second_example_obj))); - let res = obj_manager.initialize(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), 2); - let obj_back_casted: Option<&OtherExampleObject> = obj_manager.get_ref(&second_obj_id); - assert!(obj_back_casted.is_some()); - let expl_obj_back_casted = obj_back_casted.unwrap(); - assert_eq!(expl_obj_back_casted.string, String::from("Hello Test")); - assert!(expl_obj_back_casted.was_initialized); - - let existing_obj_id = ObjectId { - id: 12, - name: "Example 1", - }; - let invalid_obj = OtherExampleObject { - id: existing_obj_id, - string: String::from("Hello Test"), - was_initialized: false, - }; - - assert!(!obj_manager.insert(Box::new(invalid_obj))); - } - - #[test] - fn object_man_threaded() { - let obj_manager = Arc::new(Mutex::new(ObjectManager::new())); - let expl_obj_id = ObjectId { - id: 0, - name: "Example 0", - }; - let example_obj = ExampleSysObj::new(expl_obj_id, 42); - let second_obj_id = ObjectId { - id: 12, - name: "Example 1", - }; - let second_example_obj = OtherExampleObject { - id: second_obj_id, - string: String::from("Hello Test"), - was_initialized: false, - }; - - let mut obj_man_handle = obj_manager.lock().expect("Mutex lock failed"); - assert!(obj_man_handle.insert(Box::new(example_obj))); - assert!(obj_man_handle.insert(Box::new(second_example_obj))); - let res = obj_man_handle.initialize(); - std::mem::drop(obj_man_handle); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), 2); - let obj_man_0 = obj_manager.clone(); - let jh0 = thread::spawn(move || { - let locked_man = obj_man_0.lock().expect("Mutex lock failed"); - let obj_back_casted: Option<&ExampleSysObj> = locked_man.get_ref(&expl_obj_id); - assert!(obj_back_casted.is_some()); - let expl_obj_back_casted = obj_back_casted.unwrap(); - assert_eq!(expl_obj_back_casted.dummy, 42); - assert!(expl_obj_back_casted.was_initialized); - std::mem::drop(locked_man) - }); - - let jh1 = thread::spawn(move || { - let locked_man = obj_manager.lock().expect("Mutex lock failed"); - let obj_back_casted: Option<&OtherExampleObject> = locked_man.get_ref(&second_obj_id); - assert!(obj_back_casted.is_some()); - let expl_obj_back_casted = obj_back_casted.unwrap(); - assert_eq!(expl_obj_back_casted.string, String::from("Hello Test")); - assert!(expl_obj_back_casted.was_initialized); - std::mem::drop(locked_man) - }); - jh0.join().expect("Joining thread 0 failed"); - jh1.join().expect("Joining thread 1 failed"); - } -} diff --git a/satrs-core/src/params.rs b/satrs-core/src/params.rs deleted file mode 100644 index bd616bd..0000000 --- a/satrs-core/src/params.rs +++ /dev/null @@ -1,648 +0,0 @@ -//! Parameter types and enums. -//! -//! This module contains various helper types. -//! -//! # Primtive Parameter Wrappers and Enumeration -//! -//! This module includes wrapper for primitive rust types using the newtype pattern. -//! This was also done for pairs and triplets of these primitive types. -//! The [WritableToBeBytes] was implemented for all those types as well, which allows to easily -//! convert them into a network friendly raw byte format. The [ParamsRaw] enumeration groups -//! all newtypes and implements the [WritableToBeBytes] trait itself. -//! -//! ## Example for primitive type wrapper -//! -//! ``` -//! use satrs_core::params::{ParamsRaw, ToBeBytes, U32Pair, WritableToBeBytes}; -//! -//! let u32_pair = U32Pair(0x1010, 25); -//! assert_eq!(u32_pair.0, 0x1010); -//! assert_eq!(u32_pair.1, 25); -//! // Convert to raw stream -//! let raw_buf = u32_pair.to_be_bytes(); -//! assert_eq!(raw_buf, [0, 0, 0x10, 0x10, 0, 0, 0, 25]); -//! -//! // Convert to enum variant -//! let params_raw: ParamsRaw = u32_pair.into(); -//! assert_eq!(params_raw, (0x1010_u32, 25_u32).into()); -//! -//! // Convert to stream using the enum variant -//! let mut other_raw_buf: [u8; 8] = [0; 8]; -//! params_raw.write_to_be_bytes(&mut other_raw_buf).expect("Writing parameter to buffer failed"); -//! assert_eq!(other_raw_buf, [0, 0, 0x10, 0x10, 0, 0, 0, 25]); -//! -//! // Create a pair from a raw stream -//! let u32_pair_from_stream: U32Pair = raw_buf.as_slice().try_into().unwrap(); -//! assert_eq!(u32_pair_from_stream.0, 0x1010); -//! assert_eq!(u32_pair_from_stream.1, 25); -//! ``` -//! -//! # Generic Parameter Enumeration -//! -//! The module also contains generic parameter enumerations. -//! This includes the [ParamsHeapless] enumeration for contained values which do not require heap -//! allocation, and the [Params] which enumerates [ParamsHeapless] and some additional types which -//! require [alloc] support but allow for more flexbility. -#[cfg(feature = "alloc")] -use crate::pool::StoreAddr; -#[cfg(feature = "alloc")] -use alloc::string::{String, ToString}; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; -use core::fmt::Debug; -use core::mem::size_of; -use paste::paste; -use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU64, EcssEnumU8, EcssEnumeration}; -use spacepackets::ByteConversionError; -use spacepackets::SizeMissmatch; - -#[cfg(feature = "alloc")] -pub use alloc_mod::*; -pub use spacepackets::ecss::ToBeBytes; - -/// Generic trait which is used for objects which can be converted into a raw network (big) endian -/// byte format. -pub trait WritableToBeBytes { - fn raw_len(&self) -> usize; - /// Writes the object to a raw buffer in network endianness (big) - fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result; -} - -macro_rules! param_to_be_bytes_impl { - ($Newtype: ident) => { - impl WritableToBeBytes for $Newtype { - #[inline] - fn raw_len(&self) -> usize { - size_of::<::ByteArray>() - } - - fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { - let raw_len = self.raw_len(); - if buf.len() < raw_len { - return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch { - found: buf.len(), - expected: raw_len, - })); - } - buf[0..raw_len].copy_from_slice(&self.to_be_bytes()); - Ok(raw_len) - } - } - }; -} - -macro_rules! primitive_newtypes_with_eq { - ($($ty: ty,)+) => { - $( - paste! { - #[derive(Debug, Copy, Clone, PartialEq, Eq)] - pub struct [<$ty:upper>](pub $ty); - #[derive(Debug, Copy, Clone, PartialEq, Eq)] - pub struct [<$ty:upper Pair>](pub $ty, pub $ty); - #[derive(Debug, Copy, Clone, PartialEq, Eq)] - pub struct [<$ty:upper Triplet>](pub $ty, pub $ty, pub $ty); - - param_to_be_bytes_impl!([<$ty:upper>]); - param_to_be_bytes_impl!([<$ty:upper Pair>]); - param_to_be_bytes_impl!([<$ty:upper Triplet>]); - - impl From<$ty> for [<$ty:upper>] { - fn from(v: $ty) -> Self { - Self(v) - } - } - impl From<($ty, $ty)> for [<$ty:upper Pair>] { - fn from(v: ($ty, $ty)) -> Self { - Self(v.0, v.1) - } - } - impl From<($ty, $ty, $ty)> for [<$ty:upper Triplet>] { - fn from(v: ($ty, $ty, $ty)) -> Self { - Self(v.0, v.1, v.2) - } - } - } - )+ - } -} - -macro_rules! primitive_newtypes { - ($($ty: ty,)+) => { - $( - paste! { - #[derive(Debug, Copy, Clone, PartialEq)] - pub struct [<$ty:upper>](pub $ty); - #[derive(Debug, Copy, Clone, PartialEq)] - pub struct [<$ty:upper Pair>](pub $ty, pub $ty); - #[derive(Debug, Copy, Clone, PartialEq)] - pub struct [<$ty:upper Triplet>](pub $ty, pub $ty, pub $ty); - - param_to_be_bytes_impl!([<$ty:upper>]); - param_to_be_bytes_impl!([<$ty:upper Pair>]); - param_to_be_bytes_impl!([<$ty:upper Triplet>]); - - impl From<$ty> for [<$ty:upper>] { - fn from(v: $ty) -> Self { - Self(v) - } - } - impl From<($ty, $ty)> for [<$ty:upper Pair>] { - fn from(v: ($ty, $ty)) -> Self { - Self(v.0, v.1) - } - } - impl From<($ty, $ty, $ty)> for [<$ty:upper Triplet>] { - fn from(v: ($ty, $ty, $ty)) -> Self { - Self(v.0, v.1, v.2) - } - } - } - )+ - } -} - -primitive_newtypes_with_eq!(u8, u16, u32, u64, i8, i16, i32, i64,); -primitive_newtypes!(f32, f64,); - -macro_rules! scalar_byte_conversions_impl { - ($($ty: ty,)+) => { - $( - paste! { - impl ToBeBytes for [<$ty:upper>] { - type ByteArray = [u8; size_of::<$ty>()]; - fn to_be_bytes(&self) -> Self::ByteArray { - self.0.to_be_bytes() - } - } - - impl TryFrom<&[u8]> for [<$ty:upper>] { - type Error = ByteConversionError; - - fn try_from(v: &[u8]) -> Result { - if v.len() < size_of::<$ty>() { - return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { - expected: size_of::<$ty>(), - found: v.len() - })); - } - Ok([<$ty:upper>]($ty::from_be_bytes(v[0..size_of::<$ty>()].try_into().unwrap()))) - } - } - } - )+ - } -} - -macro_rules! pair_byte_conversions_impl { - ($($ty: ty,)+) => { - $( - paste! { - impl ToBeBytes for [<$ty:upper Pair>] { - type ByteArray = [u8; size_of::<$ty>() * 2]; - fn to_be_bytes(&self) -> Self::ByteArray { - let mut array = [0; size_of::<$ty>() * 2]; - array[0..size_of::<$ty>()].copy_from_slice(&self.0.to_be_bytes()); - array[ - size_of::<$ty>()..2 * size_of::<$ty>() - ].copy_from_slice(&self.1.to_be_bytes()); - array - } - } - - impl TryFrom<&[u8]> for [<$ty:upper Pair>] { - type Error = ByteConversionError; - - fn try_from(v: &[u8]) -> Result { - if v.len() < 2 * size_of::<$ty>() { - return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { - expected: 2 * size_of::<$ty>(), - found: v.len() - })); - } - Ok([<$ty:upper Pair>]( - $ty::from_be_bytes(v[0..size_of::<$ty>()].try_into().unwrap()), - $ty::from_be_bytes(v[size_of::<$ty>()..2 * size_of::<$ty>()].try_into().unwrap()) - )) - } - } - } - )+ - } -} - -macro_rules! triplet_to_be_bytes_impl { - ($($ty: ty,)+) => { - $( - paste! { - impl ToBeBytes for [<$ty:upper Triplet>] { - type ByteArray = [u8; size_of::<$ty>() * 3]; - fn to_be_bytes(&self) -> Self::ByteArray { - let mut array = [0; size_of::<$ty>() * 3]; - array[0..size_of::<$ty>()].copy_from_slice(&self.0.to_be_bytes()); - array[ - size_of::<$ty>()..2 * size_of::<$ty>() - ].copy_from_slice(&self.1.to_be_bytes()); - array[ - 2 * size_of::<$ty>()..3 * size_of::<$ty>() - ].copy_from_slice(&self.2.to_be_bytes()); - array - } - } - impl TryFrom<&[u8]> for [<$ty:upper Triplet>] { - type Error = ByteConversionError; - - fn try_from(v: &[u8]) -> Result { - if v.len() < 3 * size_of::<$ty>() { - return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { - expected: 3 * size_of::<$ty>(), - found: v.len() - })); - } - Ok([<$ty:upper Triplet>]( - $ty::from_be_bytes(v[0..size_of::<$ty>()].try_into().unwrap()), - $ty::from_be_bytes(v[size_of::<$ty>()..2 * size_of::<$ty>()].try_into().unwrap()), - $ty::from_be_bytes(v[2 * size_of::<$ty>()..3 * size_of::<$ty>()].try_into().unwrap()) - )) - } - } - } - )+ - } -} - -scalar_byte_conversions_impl!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64,); - -impl ToBeBytes for U8Pair { - type ByteArray = [u8; 2]; - - fn to_be_bytes(&self) -> Self::ByteArray { - let mut array = [0; 2]; - array[0] = self.0; - array[1] = self.1; - array - } -} - -impl ToBeBytes for I8Pair { - type ByteArray = [u8; 2]; - - fn to_be_bytes(&self) -> Self::ByteArray { - let mut array = [0; 2]; - array[0] = self.0 as u8; - array[1] = self.1 as u8; - array - } -} - -impl ToBeBytes for U8Triplet { - type ByteArray = [u8; 3]; - - fn to_be_bytes(&self) -> Self::ByteArray { - let mut array = [0; 3]; - array[0] = self.0; - array[1] = self.1; - array[2] = self.2; - array - } -} - -impl ToBeBytes for I8Triplet { - type ByteArray = [u8; 3]; - - fn to_be_bytes(&self) -> Self::ByteArray { - let mut array = [0; 3]; - array[0] = self.0 as u8; - array[1] = self.1 as u8; - array[2] = self.2 as u8; - array - } -} - -pair_byte_conversions_impl!(u16, u32, u64, i16, i32, i64, f32, f64,); -triplet_to_be_bytes_impl!(u16, u32, u64, i16, i32, i64, f32, f64,); - -/// Generic enumeration for additonal parameters only consisting of primitive data types. -/// -/// All contained variants and the enum itself implement the [WritableToBeBytes] trait, which -/// allows to easily convert them into a network-friendly format. -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum ParamsRaw { - U8(U8), - U8Pair(U8Pair), - U8Triplet(U8Triplet), - I8(I8), - I8Pair(I8Pair), - I8Triplet(I8Triplet), - U16(U16), - U16Pair(U16Pair), - U16Triplet(U16Triplet), - I16(I16), - I16Pair(I16Pair), - I16Triplet(I16Triplet), - U32(U32), - U32Pair(U32Pair), - U32Triplet(U32Triplet), - I32(I32), - I32Pair(I32Pair), - I32Triplet(I32Triplet), - F32(F32), - F32Pair(F32Pair), - F32Triplet(F32Triplet), - U64(U64), - I64(I64), - F64(F64), -} - -impl WritableToBeBytes for ParamsRaw { - fn raw_len(&self) -> usize { - match self { - ParamsRaw::U8(v) => v.raw_len(), - ParamsRaw::U8Pair(v) => v.raw_len(), - ParamsRaw::U8Triplet(v) => v.raw_len(), - ParamsRaw::I8(v) => v.raw_len(), - ParamsRaw::I8Pair(v) => v.raw_len(), - ParamsRaw::I8Triplet(v) => v.raw_len(), - ParamsRaw::U16(v) => v.raw_len(), - ParamsRaw::U16Pair(v) => v.raw_len(), - ParamsRaw::U16Triplet(v) => v.raw_len(), - ParamsRaw::I16(v) => v.raw_len(), - ParamsRaw::I16Pair(v) => v.raw_len(), - ParamsRaw::I16Triplet(v) => v.raw_len(), - ParamsRaw::U32(v) => v.raw_len(), - ParamsRaw::U32Pair(v) => v.raw_len(), - ParamsRaw::U32Triplet(v) => v.raw_len(), - ParamsRaw::I32(v) => v.raw_len(), - ParamsRaw::I32Pair(v) => v.raw_len(), - ParamsRaw::I32Triplet(v) => v.raw_len(), - ParamsRaw::F32(v) => v.raw_len(), - ParamsRaw::F32Pair(v) => v.raw_len(), - ParamsRaw::F32Triplet(v) => v.raw_len(), - ParamsRaw::U64(v) => v.raw_len(), - ParamsRaw::I64(v) => v.raw_len(), - ParamsRaw::F64(v) => v.raw_len(), - } - } - - fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { - match self { - ParamsRaw::U8(v) => v.write_to_be_bytes(buf), - ParamsRaw::U8Pair(v) => v.write_to_be_bytes(buf), - ParamsRaw::U8Triplet(v) => v.write_to_be_bytes(buf), - ParamsRaw::I8(v) => v.write_to_be_bytes(buf), - ParamsRaw::I8Pair(v) => v.write_to_be_bytes(buf), - ParamsRaw::I8Triplet(v) => v.write_to_be_bytes(buf), - ParamsRaw::U16(v) => v.write_to_be_bytes(buf), - ParamsRaw::U16Pair(v) => v.write_to_be_bytes(buf), - ParamsRaw::U16Triplet(v) => v.write_to_be_bytes(buf), - ParamsRaw::I16(v) => v.write_to_be_bytes(buf), - ParamsRaw::I16Pair(v) => v.write_to_be_bytes(buf), - ParamsRaw::I16Triplet(v) => v.write_to_be_bytes(buf), - ParamsRaw::U32(v) => v.write_to_be_bytes(buf), - ParamsRaw::U32Pair(v) => v.write_to_be_bytes(buf), - ParamsRaw::U32Triplet(v) => v.write_to_be_bytes(buf), - ParamsRaw::I32(v) => v.write_to_be_bytes(buf), - ParamsRaw::I32Pair(v) => v.write_to_be_bytes(buf), - ParamsRaw::I32Triplet(v) => v.write_to_be_bytes(buf), - ParamsRaw::F32(v) => v.write_to_be_bytes(buf), - ParamsRaw::F32Pair(v) => v.write_to_be_bytes(buf), - ParamsRaw::F32Triplet(v) => v.write_to_be_bytes(buf), - ParamsRaw::U64(v) => v.write_to_be_bytes(buf), - ParamsRaw::I64(v) => v.write_to_be_bytes(buf), - ParamsRaw::F64(v) => v.write_to_be_bytes(buf), - } - } -} - -macro_rules! params_raw_from_newtype { - ($($newtype: ident,)+) => { - $( - impl From<$newtype> for ParamsRaw { - fn from(v: $newtype) -> Self { - Self::$newtype(v) - } - } - )+ - } -} - -params_raw_from_newtype!( - U8, U8Pair, U8Triplet, U16, U16Pair, U16Triplet, U32, U32Pair, U32Triplet, I8, I8Pair, - I8Triplet, I16, I16Pair, I16Triplet, I32, I32Pair, I32Triplet, F32, F32Pair, F32Triplet, U64, - I64, F64, -); - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum EcssEnumParams { - U8(EcssEnumU8), - U16(EcssEnumU16), - U32(EcssEnumU32), - U64(EcssEnumU64), -} - -macro_rules! writable_as_be_bytes_ecss_enum_impl { - ($EnumIdent: ident) => { - impl WritableToBeBytes for $EnumIdent { - fn raw_len(&self) -> usize { - self.byte_width() - } - - fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { - EcssEnumeration::write_to_be_bytes(self, buf).map(|_| self.byte_width()) - } - } - }; -} - -writable_as_be_bytes_ecss_enum_impl!(EcssEnumU8); -writable_as_be_bytes_ecss_enum_impl!(EcssEnumU16); -writable_as_be_bytes_ecss_enum_impl!(EcssEnumU32); -writable_as_be_bytes_ecss_enum_impl!(EcssEnumU64); - -impl WritableToBeBytes for EcssEnumParams { - fn raw_len(&self) -> usize { - match self { - EcssEnumParams::U8(e) => e.byte_width(), - EcssEnumParams::U16(e) => e.byte_width(), - EcssEnumParams::U32(e) => e.byte_width(), - EcssEnumParams::U64(e) => e.byte_width(), - } - } - - fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { - match self { - EcssEnumParams::U8(e) => WritableToBeBytes::write_to_be_bytes(e, buf), - EcssEnumParams::U16(e) => WritableToBeBytes::write_to_be_bytes(e, buf), - EcssEnumParams::U32(e) => WritableToBeBytes::write_to_be_bytes(e, buf), - EcssEnumParams::U64(e) => WritableToBeBytes::write_to_be_bytes(e, buf), - } - } -} - -/// Generic enumeration for parameters which do not rely on heap allocations. -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum ParamsHeapless { - Raw(ParamsRaw), - EcssEnum(EcssEnumParams), -} - -macro_rules! from_conversions_for_raw { - ($(($raw_ty: ty, $TargetPath: path),)+) => { - $( - impl From<$raw_ty> for ParamsRaw { - fn from(val: $raw_ty) -> Self { - $TargetPath(val.into()) - } - } - - impl From<$raw_ty> for ParamsHeapless { - fn from(val: $raw_ty) -> Self { - ParamsHeapless::Raw(val.into()) - } - } - )+ - }; -} - -from_conversions_for_raw!( - (u8, Self::U8), - ((u8, u8), Self::U8Pair), - ((u8, u8, u8), Self::U8Triplet), - (i8, Self::I8), - ((i8, i8), Self::I8Pair), - ((i8, i8, i8), Self::I8Triplet), - (u16, Self::U16), - ((u16, u16), Self::U16Pair), - ((u16, u16, u16), Self::U16Triplet), - (i16, Self::I16), - ((i16, i16), Self::I16Pair), - ((i16, i16, i16), Self::I16Triplet), - (u32, Self::U32), - ((u32, u32), Self::U32Pair), - ((u32, u32, u32), Self::U32Triplet), - (i32, Self::I32), - ((i32, i32), Self::I32Pair), - ((i32, i32, i32), Self::I32Triplet), - (f32, Self::F32), - ((f32, f32), Self::F32Pair), - ((f32, f32, f32), Self::F32Triplet), - (u64, Self::U64), - (f64, Self::F64), -); - -#[cfg(feature = "alloc")] -mod alloc_mod { - use super::*; - /// Generic enumeration for additional parameters, including parameters which rely on heap - /// allocations. - #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] - #[derive(Debug, Clone)] - pub enum Params { - Heapless(ParamsHeapless), - Store(StoreAddr), - Vec(Vec), - String(String), - } - - impl From for Params { - fn from(x: StoreAddr) -> Self { - Self::Store(x) - } - } - - impl From for Params { - fn from(x: ParamsHeapless) -> Self { - Self::Heapless(x) - } - } - - impl From> for Params { - fn from(val: Vec) -> Self { - Self::Vec(val) - } - } - - /// Converts a byte slice into the [Params::Vec] variant - impl From<&[u8]> for Params { - fn from(val: &[u8]) -> Self { - Self::Vec(val.to_vec()) - } - } - - impl From for Params { - fn from(val: String) -> Self { - Self::String(val) - } - } - - /// Converts a string slice into the [Params::String] variant - impl From<&str> for Params { - fn from(val: &str) -> Self { - Self::String(val.to_string()) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_basic_u32_pair() { - let u32_pair = U32Pair(4, 8); - assert_eq!(u32_pair.0, 4); - assert_eq!(u32_pair.1, 8); - let raw = u32_pair.to_be_bytes(); - let mut u32_conv_back = u32::from_be_bytes(raw[0..4].try_into().unwrap()); - assert_eq!(u32_conv_back, 4); - u32_conv_back = u32::from_be_bytes(raw[4..8].try_into().unwrap()); - assert_eq!(u32_conv_back, 8); - } - - #[test] - fn basic_signed_test_pair() { - let i8_pair = I8Pair(-3, -16); - assert_eq!(i8_pair.0, -3); - assert_eq!(i8_pair.1, -16); - let raw = i8_pair.to_be_bytes(); - let mut i8_conv_back = i8::from_be_bytes(raw[0..1].try_into().unwrap()); - assert_eq!(i8_conv_back, -3); - i8_conv_back = i8::from_be_bytes(raw[1..2].try_into().unwrap()); - assert_eq!(i8_conv_back, -16); - } - - #[test] - fn basic_signed_test_triplet() { - let i8_triplet = I8Triplet(-3, -16, -126); - assert_eq!(i8_triplet.0, -3); - assert_eq!(i8_triplet.1, -16); - assert_eq!(i8_triplet.2, -126); - let raw = i8_triplet.to_be_bytes(); - let mut i8_conv_back = i8::from_be_bytes(raw[0..1].try_into().unwrap()); - assert_eq!(i8_conv_back, -3); - i8_conv_back = i8::from_be_bytes(raw[1..2].try_into().unwrap()); - assert_eq!(i8_conv_back, -16); - i8_conv_back = i8::from_be_bytes(raw[2..3].try_into().unwrap()); - assert_eq!(i8_conv_back, -126); - } - - #[test] - fn conversion_test_string() { - let param: Params = "Test String".into(); - if let Params::String(str) = param { - assert_eq!(str, String::from("Test String")); - } else { - panic!("Params type is not String") - } - } - - #[test] - fn conversion_from_slice() { - let test_slice: [u8; 5] = [0; 5]; - let vec_param: Params = test_slice.as_slice().into(); - if let Params::Vec(vec) = vec_param { - assert_eq!(vec, test_slice.to_vec()); - } else { - panic!("Params type is not a vector") - } - } -} diff --git a/satrs-core/src/pool.rs b/satrs-core/src/pool.rs deleted file mode 100644 index a37e406..0000000 --- a/satrs-core/src/pool.rs +++ /dev/null @@ -1,771 +0,0 @@ -//! # Pool implementation providing pre-allocated sub-pools with fixed size memory blocks -//! -//! This is a simple memory pool implementation which pre-allocates all sub-pools using a given pool -//! configuration. After the pre-allocation, no dynamic memory allocation will be performed -//! during run-time. This makes the implementation suitable for real-time applications and -//! embedded environments. The pool implementation will also track the size of the data stored -//! inside it. -//! -//! Transactions with the [pool][LocalPool] are done using a special [address][StoreAddr] type. -//! Adding any data to the pool will yield a store address. Modification and read operations are -//! done using a reference to a store address. Deletion will consume the store address. -//! -//! # Example -//! -//! ``` -//! use satrs_core::pool::{LocalPool, PoolCfg, PoolProvider}; -//! -//! // 4 buckets of 4 bytes, 2 of 8 bytes and 1 of 16 bytes -//! let pool_cfg = PoolCfg::new(vec![(4, 4), (2, 8), (1, 16)]); -//! let mut local_pool = LocalPool::new(pool_cfg); -//! let mut addr; -//! { -//! // Add new data to the pool -//! let mut example_data = [0; 4]; -//! example_data[0] = 42; -//! let res = local_pool.add(&example_data); -//! assert!(res.is_ok()); -//! addr = res.unwrap(); -//! } -//! -//! { -//! // Read the store data back -//! let res = local_pool.read(&addr); -//! assert!(res.is_ok()); -//! let buf_read_back = res.unwrap(); -//! assert_eq!(buf_read_back.len(), 4); -//! assert_eq!(buf_read_back[0], 42); -//! // Modify the stored data -//! let res = local_pool.modify(&addr); -//! assert!(res.is_ok()); -//! let buf_read_back = res.unwrap(); -//! buf_read_back[0] = 12; -//! } -//! -//! { -//! // Read the modified data back -//! let res = local_pool.read(&addr); -//! assert!(res.is_ok()); -//! let buf_read_back = res.unwrap(); -//! assert_eq!(buf_read_back.len(), 4); -//! assert_eq!(buf_read_back[0], 12); -//! } -//! -//! // Delete the stored data -//! local_pool.delete(addr); -//! -//! // Get a free element in the pool with an appropriate size -//! { -//! let res = local_pool.free_element(12); -//! assert!(res.is_ok()); -//! let (tmp, mut_buf) = res.unwrap(); -//! addr = tmp; -//! mut_buf[0] = 7; -//! } -//! -//! // Read back the data -//! { -//! // Read the store data back -//! let res = local_pool.read(&addr); -//! assert!(res.is_ok()); -//! let buf_read_back = res.unwrap(); -//! assert_eq!(buf_read_back.len(), 12); -//! assert_eq!(buf_read_back[0], 7); -//! } -//! ``` -use alloc::format; -use alloc::string::String; -use alloc::vec; -use alloc::vec::Vec; -use core::fmt::{Display, Formatter}; -use delegate::delegate; -#[cfg(feature = "std")] -use std::boxed::Box; -#[cfg(feature = "std")] -use std::error::Error; -#[cfg(feature = "std")] -use std::sync::{Arc, RwLock}; - -type NumBlocks = u16; - -#[cfg(feature = "std")] -pub type ShareablePoolProvider = Box; -#[cfg(feature = "std")] -pub type SharedPool = Arc>; - -/// Configuration structure of the [local pool][LocalPool] -/// -/// # Parameters -/// -/// * `cfg`: Vector of tuples which represent a subpool. The first entry in the tuple specifies the -/// number of memory blocks in the subpool, the second entry the size of the blocks -#[derive(Clone)] -pub struct PoolCfg { - cfg: Vec<(NumBlocks, usize)>, -} - -impl PoolCfg { - pub fn new(cfg: Vec<(NumBlocks, usize)>) -> Self { - PoolCfg { cfg } - } - - pub fn sanitize(&mut self) -> usize { - self.cfg - .retain(|&(bucket_num, size)| bucket_num > 0 && size < LocalPool::MAX_SIZE); - self.cfg - .sort_unstable_by(|(_, sz0), (_, sz1)| sz0.partial_cmp(sz1).unwrap()); - self.cfg.len() - } -} - -type PoolSize = usize; - -/// Pool implementation providing sub-pools with fixed size memory blocks. More details in -/// the [module documentation][super::pool] -pub struct LocalPool { - pool_cfg: PoolCfg, - pool: Vec>, - sizes_lists: Vec>, -} - -/// Simple address type used for transactions with the local pool. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct StoreAddr { - pool_idx: u16, - packet_idx: NumBlocks, -} - -impl StoreAddr { - pub const INVALID_ADDR: u32 = 0xFFFFFFFF; - - pub fn raw(&self) -> u32 { - ((self.pool_idx as u32) << 16) | self.packet_idx as u32 - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum StoreIdError { - InvalidSubpool(u16), - InvalidPacketIdx(u16), -} - -impl Display for StoreIdError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - StoreIdError::InvalidSubpool(pool) => { - write!(f, "invalid subpool, index: {}", pool) - } - StoreIdError::InvalidPacketIdx(packet_idx) => { - write!(f, "invalid packet index: {}", packet_idx) - } - } - } -} - -#[cfg(feature = "std")] -impl Error for StoreIdError {} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum StoreError { - /// Requested data block is too large - DataTooLarge(usize), - /// The store is full. Contains the index of the full subpool - StoreFull(u16), - /// Store ID is invalid. This also includes partial errors where only the subpool is invalid - InvalidStoreId(StoreIdError, Option), - /// Valid subpool and packet index, but no data is stored at the given address - DataDoesNotExist(StoreAddr), - /// Internal or configuration errors - InternalError(String), -} - -impl Display for StoreError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - StoreError::DataTooLarge(size) => { - write!(f, "data to store with size {} is too large", size) - } - StoreError::StoreFull(u16) => { - write!(f, "store is too full. index for full subpool: {}", u16) - } - StoreError::InvalidStoreId(id_e, addr) => { - write!(f, "invalid store ID: {}, address: {:?}", id_e, addr) - } - StoreError::DataDoesNotExist(addr) => { - write!(f, "no data exists at address {:?}", addr) - } - StoreError::InternalError(e) => { - write!(f, "internal error: {}", e) - } - } - } -} - -#[cfg(feature = "std")] -impl Error for StoreError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - if let StoreError::InvalidStoreId(e, _) = self { - return Some(e); - } - None - } -} - -pub trait PoolProvider { - /// Add new data to the pool. The provider should attempt to reserve a memory block with the - /// appropriate size and then copy the given data to the block. Yields a [StoreAddr] which can - /// be used to access the data stored in the pool - fn add(&mut self, data: &[u8]) -> Result; - - /// The provider should attempt to reserve a free memory block with the appropriate size and - /// then return a mutable reference to it. Yields a [StoreAddr] which can be used to access - /// the data stored in the pool - fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError>; - - /// Modify data added previously using a given [StoreAddr] by yielding a mutable reference - /// to it - fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError>; - - /// This function behaves like [Self::modify], but consumes the provided address and returns a - /// RAII conformant guard object. - /// - /// Unless the guard [PoolRwGuard::release] method is called, the data for the - /// given address will be deleted automatically when the guard is dropped. - /// This can prevent memory leaks. Users can read (and modify) the data and release the guard - /// if the data in the store is valid for further processing. If the data is faulty, no - /// manual deletion is necessary when returning from a processing function prematurely. - fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard; - - /// Read data by yielding a read-only reference given a [StoreAddr] - fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError>; - - /// This function behaves like [Self::read], but consumes the provided address and returns a - /// RAII conformant guard object. - /// - /// Unless the guard [PoolRwGuard::release] method is called, the data for the - /// given address will be deleted automatically when the guard is dropped. - /// This can prevent memory leaks. Users can read the data and release the guard - /// if the data in the store is valid for further processing. If the data is faulty, no - /// manual deletion is necessary when returning from a processing function prematurely. - fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard; - - /// Delete data inside the pool given a [StoreAddr] - fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError>; - fn has_element_at(&self, addr: &StoreAddr) -> Result; -} - -impl LocalPool { - const STORE_FREE: PoolSize = PoolSize::MAX; - const MAX_SIZE: PoolSize = Self::STORE_FREE - 1; - /// Create a new local pool from the [given configuration][PoolCfg]. This function will sanitize - /// the given configuration as well. - pub fn new(mut cfg: PoolCfg) -> LocalPool { - let subpools_num = cfg.sanitize(); - let mut local_pool = LocalPool { - pool_cfg: cfg, - pool: Vec::with_capacity(subpools_num), - sizes_lists: Vec::with_capacity(subpools_num), - }; - for &(num_elems, elem_size) in local_pool.pool_cfg.cfg.iter() { - let next_pool_len = elem_size * num_elems as usize; - local_pool.pool.push(vec![0; next_pool_len]); - let next_sizes_list_len = num_elems as usize; - local_pool - .sizes_lists - .push(vec![Self::STORE_FREE; next_sizes_list_len]); - } - local_pool - } - - fn addr_check(&self, addr: &StoreAddr) -> Result { - self.validate_addr(addr)?; - let pool_idx = addr.pool_idx as usize; - let size_list = self.sizes_lists.get(pool_idx).unwrap(); - let curr_size = size_list[addr.packet_idx as usize]; - if curr_size == Self::STORE_FREE { - return Err(StoreError::DataDoesNotExist(*addr)); - } - Ok(curr_size) - } - - fn validate_addr(&self, addr: &StoreAddr) -> Result<(), StoreError> { - let pool_idx = addr.pool_idx as usize; - if pool_idx >= self.pool_cfg.cfg.len() { - return Err(StoreError::InvalidStoreId( - StoreIdError::InvalidSubpool(addr.pool_idx), - Some(*addr), - )); - } - if addr.packet_idx >= self.pool_cfg.cfg[addr.pool_idx as usize].0 { - return Err(StoreError::InvalidStoreId( - StoreIdError::InvalidPacketIdx(addr.packet_idx), - Some(*addr), - )); - } - Ok(()) - } - - fn reserve(&mut self, data_len: usize) -> Result { - let subpool_idx = self.find_subpool(data_len, 0)?; - let (slot, size_slot_ref) = self.find_empty(subpool_idx)?; - *size_slot_ref = data_len; - Ok(StoreAddr { - pool_idx: subpool_idx, - packet_idx: slot, - }) - } - - fn find_subpool(&self, req_size: usize, start_at_subpool: u16) -> Result { - for (i, &(_, elem_size)) in self.pool_cfg.cfg.iter().enumerate() { - if i < start_at_subpool as usize { - continue; - } - if elem_size >= req_size { - return Ok(i as u16); - } - } - Err(StoreError::DataTooLarge(req_size)) - } - - fn write(&mut self, addr: &StoreAddr, data: &[u8]) -> Result<(), StoreError> { - let packet_pos = self.raw_pos(addr).ok_or_else(|| { - StoreError::InternalError(format!( - "write: Error in raw_pos func with address {:?}", - addr - )) - })?; - let subpool = self.pool.get_mut(addr.pool_idx as usize).ok_or_else(|| { - StoreError::InternalError(format!( - "write: Error retrieving pool slice with address {:?}", - addr - )) - })?; - let pool_slice = &mut subpool[packet_pos..packet_pos + data.len()]; - pool_slice.copy_from_slice(data); - Ok(()) - } - - fn find_empty(&mut self, subpool: u16) -> Result<(u16, &mut usize), StoreError> { - if let Some(size_list) = self.sizes_lists.get_mut(subpool as usize) { - for (i, elem_size) in size_list.iter_mut().enumerate() { - if *elem_size == Self::STORE_FREE { - return Ok((i as u16, elem_size)); - } - } - } else { - return Err(StoreError::InvalidStoreId( - StoreIdError::InvalidSubpool(subpool), - None, - )); - } - Err(StoreError::StoreFull(subpool)) - } - - fn raw_pos(&self, addr: &StoreAddr) -> Option { - let (_, size) = self.pool_cfg.cfg.get(addr.pool_idx as usize)?; - Some(addr.packet_idx as usize * size) - } -} - -impl PoolProvider for LocalPool { - fn add(&mut self, data: &[u8]) -> Result { - let data_len = data.len(); - if data_len > Self::MAX_SIZE { - return Err(StoreError::DataTooLarge(data_len)); - } - let addr = self.reserve(data_len)?; - self.write(&addr, data)?; - Ok(addr) - } - - fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError> { - if len > Self::MAX_SIZE { - return Err(StoreError::DataTooLarge(len)); - } - let addr = self.reserve(len)?; - let raw_pos = self.raw_pos(&addr).unwrap(); - let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + len]; - Ok((addr, block)) - } - - fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError> { - let curr_size = self.addr_check(addr)?; - let raw_pos = self.raw_pos(addr).unwrap(); - let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..curr_size]; - Ok(block) - } - - fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard { - PoolRwGuard::new(self, addr) - } - - fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError> { - let curr_size = self.addr_check(addr)?; - let raw_pos = self.raw_pos(addr).unwrap(); - let block = &self.pool.get(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + curr_size]; - Ok(block) - } - - fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard { - PoolGuard::new(self, addr) - } - - fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError> { - self.addr_check(&addr)?; - let block_size = self.pool_cfg.cfg.get(addr.pool_idx as usize).unwrap().1; - let raw_pos = self.raw_pos(&addr).unwrap(); - let block = - &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + block_size]; - let size_list = self.sizes_lists.get_mut(addr.pool_idx as usize).unwrap(); - size_list[addr.packet_idx as usize] = Self::STORE_FREE; - block.fill(0); - Ok(()) - } - - fn has_element_at(&self, addr: &StoreAddr) -> Result { - self.validate_addr(addr)?; - let pool_idx = addr.pool_idx as usize; - let size_list = self.sizes_lists.get(pool_idx).unwrap(); - let curr_size = size_list[addr.packet_idx as usize]; - if curr_size == Self::STORE_FREE { - return Ok(false); - } - Ok(true) - } -} - -pub struct PoolGuard<'a> { - pool: &'a mut LocalPool, - pub addr: StoreAddr, - no_deletion: bool, - deletion_failed_error: Option, -} - -/// This helper object -impl<'a> PoolGuard<'a> { - pub fn new(pool: &'a mut LocalPool, addr: StoreAddr) -> Self { - Self { - pool, - addr, - no_deletion: false, - deletion_failed_error: None, - } - } - - pub fn read(&self) -> Result<&[u8], StoreError> { - self.pool.read(&self.addr) - } - - /// Releasing the pool guard will disable the automatic deletion of the data when the guard - /// is dropped. - pub fn release(&mut self) { - self.no_deletion = true; - } -} - -impl Drop for PoolGuard<'_> { - fn drop(&mut self) { - if !self.no_deletion { - if let Err(e) = self.pool.delete(self.addr) { - self.deletion_failed_error = Some(e); - } - } - } -} - -pub struct PoolRwGuard<'a> { - guard: PoolGuard<'a>, -} - -impl<'a> PoolRwGuard<'a> { - pub fn new(pool: &'a mut LocalPool, addr: StoreAddr) -> Self { - Self { - guard: PoolGuard::new(pool, addr), - } - } - - pub fn modify(&mut self) -> Result<&mut [u8], StoreError> { - self.guard.pool.modify(&self.guard.addr) - } - - delegate!( - to self.guard { - pub fn read(&self) -> Result<&[u8], StoreError>; - /// Releasing the pool guard will disable the automatic deletion of the data when the guard - /// is dropped. - pub fn release(&mut self); - } - ); -} - -#[cfg(test)] -mod tests { - use crate::pool::{ - LocalPool, PoolCfg, PoolGuard, PoolProvider, PoolRwGuard, StoreAddr, StoreError, - StoreIdError, - }; - use std::vec; - - fn basic_small_pool() -> LocalPool { - // 4 buckets of 4 bytes, 2 of 8 bytes and 1 of 16 bytes - let pool_cfg = PoolCfg::new(vec![(4, 4), (2, 8), (1, 16)]); - LocalPool::new(pool_cfg) - } - - #[test] - fn test_cfg() { - // Values where number of buckets is 0 or size is too large should be removed - let mut pool_cfg = PoolCfg::new(vec![(0, 0), (1, 0), (2, LocalPool::MAX_SIZE)]); - pool_cfg.sanitize(); - assert_eq!(pool_cfg.cfg, vec![(1, 0)]); - // Entries should be ordered according to bucket size - pool_cfg = PoolCfg::new(vec![(16, 6), (32, 3), (8, 12)]); - pool_cfg.sanitize(); - assert_eq!(pool_cfg.cfg, vec![(32, 3), (16, 6), (8, 12)]); - // Unstable sort is used, so order of entries with same block length should not matter - pool_cfg = PoolCfg::new(vec![(12, 12), (14, 16), (10, 12)]); - pool_cfg.sanitize(); - assert!( - pool_cfg.cfg == vec![(12, 12), (10, 12), (14, 16)] - || pool_cfg.cfg == vec![(10, 12), (12, 12), (14, 16)] - ); - } - - #[test] - fn test_add_and_read() { - let mut local_pool = basic_small_pool(); - let mut test_buf: [u8; 16] = [0; 16]; - for (i, val) in test_buf.iter_mut().enumerate() { - *val = i as u8; - } - let addr = local_pool.add(&test_buf).expect("Adding data failed"); - // Read back data and verify correctness - let res = local_pool.read(&addr); - assert!(res.is_ok()); - let buf_read_back = res.unwrap(); - assert_eq!(buf_read_back.len(), 16); - for (i, &val) in buf_read_back.iter().enumerate() { - assert_eq!(val, i as u8); - } - } - - #[test] - fn test_add_smaller_than_full_slot() { - let mut local_pool = basic_small_pool(); - let test_buf: [u8; 12] = [0; 12]; - let addr = local_pool.add(&test_buf).expect("Adding data failed"); - let res = local_pool.read(&addr).expect("Read back failed"); - assert_eq!(res.len(), 12); - } - - #[test] - fn test_delete() { - let mut local_pool = basic_small_pool(); - let test_buf: [u8; 16] = [0; 16]; - let addr = local_pool.add(&test_buf).expect("Adding data failed"); - // Delete the data - let res = local_pool.delete(addr); - assert!(res.is_ok()); - // Verify that the slot is free by trying to get a reference to it - let res = local_pool.free_element(12); - assert!(res.is_ok()); - let (addr, buf_ref) = res.unwrap(); - assert_eq!( - addr, - StoreAddr { - pool_idx: 2, - packet_idx: 0 - } - ); - assert_eq!(buf_ref.len(), 12); - } - - #[test] - fn test_modify() { - let mut local_pool = basic_small_pool(); - let mut test_buf: [u8; 16] = [0; 16]; - for (i, val) in test_buf.iter_mut().enumerate() { - *val = i as u8; - } - let addr = local_pool.add(&test_buf).expect("Adding data failed"); - - { - // Verify that the slot is free by trying to get a reference to it - let res = local_pool.modify(&addr).expect("Modifying data failed"); - res[0] = 0; - res[1] = 0x42; - } - - let res = local_pool.read(&addr).expect("Reading back data failed"); - assert_eq!(res[0], 0); - assert_eq!(res[1], 0x42); - assert_eq!(res[2], 2); - assert_eq!(res[3], 3); - } - - #[test] - fn test_consecutive_reservation() { - let mut local_pool = basic_small_pool(); - // Reserve two smaller blocks consecutively and verify that the third reservation fails - let res = local_pool.free_element(8); - assert!(res.is_ok()); - let (addr0, _) = res.unwrap(); - let res = local_pool.free_element(8); - assert!(res.is_ok()); - let (addr1, _) = res.unwrap(); - let res = local_pool.free_element(8); - assert!(res.is_err()); - let err = res.unwrap_err(); - assert_eq!(err, StoreError::StoreFull(1)); - - // Verify that the two deletions are successful - assert!(local_pool.delete(addr0).is_ok()); - assert!(local_pool.delete(addr1).is_ok()); - } - - #[test] - fn test_read_does_not_exist() { - let local_pool = basic_small_pool(); - // Try to access data which does not exist - let res = local_pool.read(&StoreAddr { - packet_idx: 0, - pool_idx: 0, - }); - assert!(res.is_err()); - assert!(matches!( - res.unwrap_err(), - StoreError::DataDoesNotExist { .. } - )); - } - - #[test] - fn test_store_full() { - let mut local_pool = basic_small_pool(); - let test_buf: [u8; 16] = [0; 16]; - assert!(local_pool.add(&test_buf).is_ok()); - // The subpool is now full and the call should fail accordingly - let res = local_pool.add(&test_buf); - assert!(res.is_err()); - let err = res.unwrap_err(); - assert!(matches!(err, StoreError::StoreFull { .. })); - if let StoreError::StoreFull(subpool) = err { - assert_eq!(subpool, 2); - } - } - - #[test] - fn test_invalid_pool_idx() { - let local_pool = basic_small_pool(); - let addr = StoreAddr { - pool_idx: 3, - packet_idx: 0, - }; - let res = local_pool.read(&addr); - assert!(res.is_err()); - let err = res.unwrap_err(); - assert!(matches!( - err, - StoreError::InvalidStoreId(StoreIdError::InvalidSubpool(3), Some(_)) - )); - } - - #[test] - fn test_invalid_packet_idx() { - let local_pool = basic_small_pool(); - let addr = StoreAddr { - pool_idx: 2, - packet_idx: 1, - }; - assert_eq!(addr.raw(), 0x00020001); - let res = local_pool.read(&addr); - assert!(res.is_err()); - let err = res.unwrap_err(); - assert!(matches!( - err, - StoreError::InvalidStoreId(StoreIdError::InvalidPacketIdx(1), Some(_)) - )); - } - - #[test] - fn test_add_too_large() { - let mut local_pool = basic_small_pool(); - let data_too_large = [0; 20]; - let res = local_pool.add(&data_too_large); - assert!(res.is_err()); - let err = res.unwrap_err(); - assert_eq!(err, StoreError::DataTooLarge(20)); - } - - #[test] - fn test_data_too_large_1() { - let mut local_pool = basic_small_pool(); - let res = local_pool.free_element(LocalPool::MAX_SIZE + 1); - assert!(res.is_err()); - assert_eq!( - res.unwrap_err(), - StoreError::DataTooLarge(LocalPool::MAX_SIZE + 1) - ); - } - - #[test] - fn test_free_element_too_large() { - let mut local_pool = basic_small_pool(); - // Try to request a slot which is too large - let res = local_pool.free_element(20); - assert!(res.is_err()); - assert_eq!(res.unwrap_err(), StoreError::DataTooLarge(20)); - } - - #[test] - fn test_pool_guard_deletion_man_creation() { - let mut local_pool = basic_small_pool(); - let test_buf: [u8; 16] = [0; 16]; - let addr = local_pool.add(&test_buf).expect("Adding data failed"); - let read_guard = PoolGuard::new(&mut local_pool, addr); - drop(read_guard); - assert!(!local_pool.has_element_at(&addr).expect("Invalid address")); - } - - #[test] - fn test_pool_guard_deletion() { - let mut local_pool = basic_small_pool(); - let test_buf: [u8; 16] = [0; 16]; - let addr = local_pool.add(&test_buf).expect("Adding data failed"); - let read_guard = local_pool.read_with_guard(addr); - drop(read_guard); - assert!(!local_pool.has_element_at(&addr).expect("Invalid address")); - } - - #[test] - fn test_pool_guard_with_release() { - let mut local_pool = basic_small_pool(); - let test_buf: [u8; 16] = [0; 16]; - let addr = local_pool.add(&test_buf).expect("Adding data failed"); - let mut read_guard = PoolGuard::new(&mut local_pool, addr); - read_guard.release(); - drop(read_guard); - assert!(local_pool.has_element_at(&addr).expect("Invalid address")); - } - - #[test] - fn test_pool_modify_guard_man_creation() { - let mut local_pool = basic_small_pool(); - let test_buf: [u8; 16] = [0; 16]; - let addr = local_pool.add(&test_buf).expect("Adding data failed"); - let mut rw_guard = PoolRwGuard::new(&mut local_pool, addr); - let _ = rw_guard.modify().expect("modify failed"); - drop(rw_guard); - assert!(!local_pool.has_element_at(&addr).expect("Invalid address")); - } - - #[test] - fn test_pool_modify_guard() { - let mut local_pool = basic_small_pool(); - let test_buf: [u8; 16] = [0; 16]; - let addr = local_pool.add(&test_buf).expect("Adding data failed"); - let mut rw_guard = local_pool.modify_with_guard(addr); - let _ = rw_guard.modify().expect("modify failed"); - drop(rw_guard); - assert!(!local_pool.has_element_at(&addr).expect("Invalid address")); - } -} diff --git a/satrs-core/src/pus/event.rs b/satrs-core/src/pus/event.rs deleted file mode 100644 index dae184a..0000000 --- a/satrs-core/src/pus/event.rs +++ /dev/null @@ -1,479 +0,0 @@ -use crate::pus::{source_buffer_large_enough, EcssTmError, EcssTmErrorWithSend}; -use spacepackets::ecss::EcssEnumeration; -use spacepackets::tm::PusTm; -use spacepackets::tm::PusTmSecondaryHeader; -use spacepackets::{SpHeader, MAX_APID}; - -use crate::pus::EcssTmSenderCore; -#[cfg(feature = "alloc")] -pub use allocvec::EventReporter; - -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub enum Subservices { - TmInfoReport = 1, - TmLowSeverityReport = 2, - TmMediumSeverityReport = 3, - TmHighSeverityReport = 4, - TcEnableEventGeneration = 5, - TcDisableEventGeneration = 6, - TcReportDisabledList = 7, - TmDisabledEventsReport = 8, -} - -impl From for u8 { - fn from(enumeration: Subservices) -> Self { - enumeration as u8 - } -} - -impl TryFrom for Subservices { - type Error = (); - - fn try_from(value: u8) -> Result { - match value { - x if x == Subservices::TmInfoReport as u8 => Ok(Subservices::TmInfoReport), - x if x == Subservices::TmLowSeverityReport as u8 => { - Ok(Subservices::TmLowSeverityReport) - } - x if x == Subservices::TmMediumSeverityReport as u8 => { - Ok(Subservices::TmMediumSeverityReport) - } - x if x == Subservices::TmHighSeverityReport as u8 => { - Ok(Subservices::TmHighSeverityReport) - } - x if x == Subservices::TcEnableEventGeneration as u8 => { - Ok(Subservices::TcEnableEventGeneration) - } - x if x == Subservices::TcDisableEventGeneration as u8 => { - Ok(Subservices::TcDisableEventGeneration) - } - x if x == Subservices::TcReportDisabledList as u8 => { - Ok(Subservices::TcReportDisabledList) - } - x if x == Subservices::TmDisabledEventsReport as u8 => { - Ok(Subservices::TmDisabledEventsReport) - } - _ => Err(()), - } - } -} -pub struct EventReporterBase { - msg_count: u16, - apid: u16, - pub dest_id: u16, -} - -impl EventReporterBase { - pub fn new(apid: u16) -> Option { - if apid > MAX_APID { - return None; - } - Some(Self { - msg_count: 0, - dest_id: 0, - apid, - }) - } - - pub fn event_info( - &mut self, - buf: &mut [u8], - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmErrorWithSend> { - self.generate_and_send_generic_tm( - buf, - Subservices::TmInfoReport, - sender, - time_stamp, - event_id, - aux_data, - ) - } - - pub fn event_low_severity( - &mut self, - buf: &mut [u8], - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmErrorWithSend> { - self.generate_and_send_generic_tm( - buf, - Subservices::TmLowSeverityReport, - sender, - time_stamp, - event_id, - aux_data, - ) - } - - pub fn event_medium_severity( - &mut self, - buf: &mut [u8], - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmErrorWithSend> { - self.generate_and_send_generic_tm( - buf, - Subservices::TmMediumSeverityReport, - sender, - time_stamp, - event_id, - aux_data, - ) - } - - pub fn event_high_severity( - &mut self, - buf: &mut [u8], - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmErrorWithSend> { - self.generate_and_send_generic_tm( - buf, - Subservices::TmHighSeverityReport, - sender, - time_stamp, - event_id, - aux_data, - ) - } - - fn generate_and_send_generic_tm( - &mut self, - buf: &mut [u8], - subservice: Subservices, - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmErrorWithSend> { - let tm = self.generate_generic_event_tm(buf, subservice, time_stamp, event_id, aux_data)?; - sender.send_tm(tm)?; - self.msg_count += 1; - Ok(()) - } - - fn generate_generic_event_tm<'a>( - &'a self, - buf: &'a mut [u8], - subservice: Subservices, - time_stamp: &'a [u8], - event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result { - let mut src_data_len = event_id.byte_width(); - if let Some(aux_data) = aux_data { - src_data_len += aux_data.len(); - } - source_buffer_large_enough(buf.len(), src_data_len)?; - let mut sp_header = SpHeader::tm_unseg(self.apid, 0, 0).unwrap(); - let sec_header = PusTmSecondaryHeader::new( - 5, - subservice.into(), - self.msg_count, - self.dest_id, - time_stamp, - ); - let mut current_idx = 0; - event_id.write_to_be_bytes(&mut buf[0..event_id.byte_width()])?; - current_idx += event_id.byte_width(); - if let Some(aux_data) = aux_data { - buf[current_idx..current_idx + aux_data.len()].copy_from_slice(aux_data); - current_idx += aux_data.len(); - } - Ok(PusTm::new( - &mut sp_header, - sec_header, - Some(&buf[0..current_idx]), - true, - )) - } -} - -#[cfg(feature = "alloc")] -mod allocvec { - use super::*; - use alloc::vec; - use alloc::vec::Vec; - - pub struct EventReporter { - source_data_buf: Vec, - pub reporter: EventReporterBase, - } - - impl EventReporter { - pub fn new(apid: u16, max_event_id_and_aux_data_size: usize) -> Option { - let reporter = EventReporterBase::new(apid)?; - Some(Self { - source_data_buf: vec![0; max_event_id_and_aux_data_size], - reporter, - }) - } - pub fn event_info( - &mut self, - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmErrorWithSend> { - self.reporter.event_info( - self.source_data_buf.as_mut_slice(), - sender, - time_stamp, - event_id, - aux_data, - ) - } - - pub fn event_low_severity( - &mut self, - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmErrorWithSend> { - self.reporter.event_low_severity( - self.source_data_buf.as_mut_slice(), - sender, - time_stamp, - event_id, - aux_data, - ) - } - - pub fn event_medium_severity( - &mut self, - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmErrorWithSend> { - self.reporter.event_medium_severity( - self.source_data_buf.as_mut_slice(), - sender, - time_stamp, - event_id, - aux_data, - ) - } - - pub fn event_high_severity( - &mut self, - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - event_id: impl EcssEnumeration, - aux_data: Option<&[u8]>, - ) -> Result<(), EcssTmErrorWithSend> { - self.reporter.event_high_severity( - self.source_data_buf.as_mut_slice(), - sender, - time_stamp, - event_id, - aux_data, - ) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::events::{EventU32, Severity}; - use crate::pus::tests::CommonTmInfo; - use spacepackets::ByteConversionError; - use std::collections::VecDeque; - use std::vec::Vec; - - const EXAMPLE_APID: u16 = 0xee; - const EXAMPLE_GROUP_ID: u16 = 2; - const EXAMPLE_EVENT_ID_0: u16 = 1; - #[allow(dead_code)] - const EXAMPLE_EVENT_ID_1: u16 = 2; - - #[derive(Debug, Eq, PartialEq, Clone)] - struct TmInfo { - pub common: CommonTmInfo, - pub event: EventU32, - pub aux_data: Vec, - } - - #[derive(Default, Clone)] - struct TestSender { - pub service_queue: VecDeque, - } - - impl EcssTmSenderCore for TestSender { - type Error = (); - - fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmErrorWithSend<()>> { - assert!(tm.source_data().is_some()); - let src_data = tm.source_data().unwrap(); - assert!(src_data.len() >= 4); - let event = EventU32::from(u32::from_be_bytes(src_data[0..4].try_into().unwrap())); - let mut aux_data = Vec::new(); - if src_data.len() > 4 { - aux_data.extend_from_slice(&src_data[4..]); - } - self.service_queue.push_back(TmInfo { - common: CommonTmInfo::new_from_tm(&tm), - event, - aux_data, - }); - Ok(()) - } - } - - fn severity_to_subservice(severity: Severity) -> Subservices { - match severity { - Severity::INFO => Subservices::TmInfoReport, - Severity::LOW => Subservices::TmLowSeverityReport, - Severity::MEDIUM => Subservices::TmMediumSeverityReport, - Severity::HIGH => Subservices::TmHighSeverityReport, - } - } - - fn report_basic_event( - reporter: &mut EventReporter, - sender: &mut TestSender, - time_stamp: &[u8], - event: EventU32, - severity: Severity, - aux_data: Option<&[u8]>, - ) { - match severity { - Severity::INFO => { - reporter - .event_info(sender, time_stamp, event, aux_data) - .expect("Error reporting info event"); - } - Severity::LOW => { - reporter - .event_low_severity(sender, time_stamp, event, aux_data) - .expect("Error reporting low event"); - } - Severity::MEDIUM => { - reporter - .event_medium_severity(sender, time_stamp, event, aux_data) - .expect("Error reporting medium event"); - } - Severity::HIGH => { - reporter - .event_high_severity(sender, time_stamp, event, aux_data) - .expect("Error reporting high event"); - } - } - } - - fn basic_event_test( - max_event_aux_data_buf: usize, - severity: Severity, - error_data: Option<&[u8]>, - ) { - let mut sender = TestSender::default(); - let reporter = EventReporter::new(EXAMPLE_APID, max_event_aux_data_buf); - assert!(reporter.is_some()); - let mut reporter = reporter.unwrap(); - let time_stamp_empty: [u8; 7] = [0; 7]; - let mut error_copy = Vec::new(); - 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) - .expect("Error creating example event"); - report_basic_event( - &mut reporter, - &mut sender, - &time_stamp_empty, - event, - severity, - error_data, - ); - assert_eq!(sender.service_queue.len(), 1); - let tm_info = sender.service_queue.pop_front().unwrap(); - assert_eq!( - tm_info.common.subservice, - 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.msg_counter, 0); - assert_eq!(tm_info.common.apid, EXAMPLE_APID); - assert_eq!(tm_info.event, event); - assert_eq!(tm_info.aux_data, error_copy); - } - - #[test] - fn basic_info_event_generation() { - basic_event_test(4, Severity::INFO, None); - } - - #[test] - fn basic_low_severity_event() { - basic_event_test(4, Severity::LOW, None); - } - - #[test] - fn basic_medium_severity_event() { - basic_event_test(4, Severity::MEDIUM, None); - } - - #[test] - fn basic_high_severity_event() { - 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())); - } - - #[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)) - } - - fn check_buf_too_small( - reporter: &mut EventReporter, - sender: &mut TestSender, - 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) - .expect("Error creating example event"); - let err = reporter.event_info(sender, &time_stamp_empty, event, None); - assert!(err.is_err()); - let err = err.unwrap_err(); - if let EcssTmErrorWithSend::EcssTmError(EcssTmError::ByteConversionError( - ByteConversionError::ToSliceTooSmall(missmatch), - )) = err - { - assert_eq!(missmatch.expected, 4); - assert_eq!(missmatch.found, expected_found_len); - } else { - panic!("Unexpected error {:?}", err); - } - } - - #[test] - fn insufficient_buffer() { - let mut sender = TestSender::default(); - for i in 0..3 { - let reporter = EventReporter::new(EXAMPLE_APID, i); - assert!(reporter.is_some()); - let mut reporter = reporter.unwrap(); - check_buf_too_small(&mut reporter, &mut sender, i); - } - } -} diff --git a/satrs-core/src/pus/event_man.rs b/satrs-core/src/pus/event_man.rs deleted file mode 100644 index 9096792..0000000 --- a/satrs-core/src/pus/event_man.rs +++ /dev/null @@ -1,328 +0,0 @@ -use crate::events::{EventU32, GenericEvent, Severity}; -#[cfg(feature = "alloc")] -use crate::events::{EventU32TypedSev, HasSeverity}; -#[cfg(feature = "alloc")] -use alloc::boxed::Box; -#[cfg(feature = "alloc")] -use core::hash::Hash; -#[cfg(feature = "alloc")] -use hashbrown::HashSet; - -#[cfg(feature = "alloc")] -pub use crate::pus::event::EventReporter; -use crate::pus::verification::{TcStateStarted, VerificationToken}; -use crate::pus::EcssTmErrorWithSend; -#[cfg(feature = "alloc")] -use crate::pus::EcssTmSenderCore; -#[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] -pub use alloc_mod::*; -#[cfg(feature = "heapless")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "heapless")))] -pub use heapless_mod::*; - -/// This trait allows the PUS event manager implementation to stay generic over various types -/// of backend containers. -/// -/// These backend containers keep track on whether a particular event is enabled or disabled for -/// reporting and also expose a simple API to enable or disable the event reporting. -/// -/// For example, a straight forward implementation for host systems could use a -/// [hash set](https://docs.rs/hashbrown/latest/hashbrown/struct.HashSet.html) -/// 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 { - type Error; - - fn event_enabled(&self, event: &Provider) -> bool; - fn enable_event_reporting(&mut self, event: &Provider) -> Result; - fn disable_event_reporting(&mut self, event: &Provider) -> Result; -} - -#[cfg(feature = "heapless")] -pub mod heapless_mod { - use super::*; - use crate::events::{GenericEvent, LargestEventRaw}; - use std::marker::PhantomData; - - #[cfg_attr(doc_cfg, doc(cfg(feature = "heapless")))] - // TODO: After a new version of heapless is released which uses hash32 version 0.3, try using - // regular Event type again. - #[derive(Default)] - pub struct HeaplessPusMgmtBackendProvider { - disabled: heapless::FnvIndexSet, - phantom: PhantomData, - } - - /// Safety: All contained field are [Send] as well - unsafe impl Send - for HeaplessPusMgmtBackendProvider - { - } - - impl PusEventMgmtBackendProvider - for HeaplessPusMgmtBackendProvider - { - type Error = (); - - fn event_enabled(&self, event: &Provider) -> bool { - self.disabled.contains(&event.raw_as_largest_type()) - } - - fn enable_event_reporting(&mut self, event: &Provider) -> Result { - self.disabled - .insert(event.raw_as_largest_type()) - .map_err(|_| ()) - } - - fn disable_event_reporting(&mut self, event: &Provider) -> Result { - Ok(self.disabled.remove(&event.raw_as_largest_type())) - } - } -} - -#[derive(Debug)] -pub enum EventRequest { - Enable(Event), - Disable(Event), -} - -#[derive(Debug)] -pub struct EventRequestWithToken { - pub request: EventRequest, - pub token: VerificationToken, -} - -#[derive(Debug)] -pub enum EventManError { - EcssTmError(EcssTmErrorWithSend), - SeverityMissmatch(Severity, Severity), -} - -impl From> for EventManError { - fn from(v: EcssTmErrorWithSend) -> Self { - Self::EcssTmError(v) - } -} - -#[cfg(feature = "alloc")] -pub mod alloc_mod { - 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. - /// - /// 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 DefaultPusMgmtBackendProvider { - disabled: HashSet, - } - - /// Safety: All contained field are [Send] as well - unsafe impl Send for DefaultPusMgmtBackendProvider {} - - impl Default for DefaultPusMgmtBackendProvider { - fn default() -> Self { - Self { - disabled: HashSet::default(), - } - } - } - - impl - PusEventMgmtBackendProvider for DefaultPusMgmtBackendProvider - { - type Error = (); - fn event_enabled(&self, event: &Provider) -> bool { - !self.disabled.contains(event) - } - - fn enable_event_reporting(&mut self, event: &Provider) -> Result { - Ok(self.disabled.remove(event)) - } - - fn disable_event_reporting(&mut self, event: &Provider) -> Result { - Ok(self.disabled.insert(*event)) - } - } - - pub struct PusEventDispatcher { - reporter: EventReporter, - backend: Box>, - } - - /// Safety: All contained fields are send as well. - unsafe impl Send for PusEventDispatcher {} - - impl PusEventDispatcher { - pub fn new( - reporter: EventReporter, - backend: Box>, - ) -> Self { - Self { reporter, backend } - } - } - - impl PusEventDispatcher { - pub fn enable_tm_for_event(&mut self, event: &Event) -> Result { - self.backend.enable_event_reporting(event) - } - - pub fn disable_tm_for_event(&mut self, event: &Event) -> Result { - self.backend.disable_event_reporting(event) - } - - pub fn generate_pus_event_tm_generic( - &mut self, - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - event: Event, - aux_data: Option<&[u8]>, - ) -> Result> { - if !self.backend.event_enabled(&event) { - return Ok(false); - } - match event.severity() { - Severity::INFO => self - .reporter - .event_info(sender, time_stamp, event, aux_data) - .map(|_| true) - .map_err(|e| e.into()), - Severity::LOW => self - .reporter - .event_low_severity(sender, time_stamp, event, aux_data) - .map(|_| true) - .map_err(|e| e.into()), - Severity::MEDIUM => self - .reporter - .event_medium_severity(sender, time_stamp, event, aux_data) - .map(|_| true) - .map_err(|e| e.into()), - Severity::HIGH => self - .reporter - .event_high_severity(sender, time_stamp, event, aux_data) - .map(|_| true) - .map_err(|e| e.into()), - } - } - } - - impl PusEventDispatcher { - pub fn enable_tm_for_event_with_sev( - &mut self, - event: &EventU32TypedSev, - ) -> Result { - self.backend.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()) - } - - pub fn generate_pus_event_tm( - &mut self, - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - event: EventU32TypedSev, - aux_data: Option<&[u8]>, - ) -> Result> { - self.generate_pus_event_tm_generic(sender, time_stamp, event.into(), aux_data) - } - } -} -#[cfg(test)] -mod tests { - use super::*; - use crate::events::SeverityInfo; - use spacepackets::tm::PusTm; - use std::sync::mpsc::{channel, SendError, TryRecvError}; - use std::vec::Vec; - - const INFO_EVENT: EventU32TypedSev = - EventU32TypedSev::::const_new(1, 0); - const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5); - const EMPTY_STAMP: [u8; 7] = [0; 7]; - - #[derive(Clone)] - struct EventTmSender { - sender: std::sync::mpsc::Sender>, - } - - impl EcssTmSenderCore for EventTmSender { - type Error = SendError>; - fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmErrorWithSend> { - let mut vec = Vec::new(); - tm.append_to_vec(&mut vec) - .map_err(|e| EcssTmErrorWithSend::EcssTmError(e.into()))?; - self.sender - .send(vec) - .map_err(EcssTmErrorWithSend::SendError)?; - Ok(()) - } - } - - fn create_basic_man() -> PusEventDispatcher<(), EventU32> { - let reporter = EventReporter::new(0x02, 128).expect("Creating event repoter failed"); - let backend = DefaultPusMgmtBackendProvider::::default(); - PusEventDispatcher::new(reporter, Box::new(backend)) - } - - #[test] - fn test_basic() { - let mut event_man = create_basic_man(); - let (event_tx, event_rx) = channel(); - let mut sender = EventTmSender { sender: event_tx }; - let event_sent = event_man - .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None) - .expect("Sending info event failed"); - - assert!(event_sent); - // Will not check packet here, correctness of packet was tested somewhere else - event_rx.try_recv().expect("Receiving event TM failed"); - } - - #[test] - fn test_disable_event() { - let mut event_man = create_basic_man(); - let (event_tx, event_rx) = channel(); - let mut sender = EventTmSender { sender: event_tx }; - let res = event_man.disable_tm_for_event(&LOW_SEV_EVENT); - assert!(res.is_ok()); - assert!(res.unwrap()); - let mut event_sent = event_man - .generate_pus_event_tm_generic(&mut sender, &EMPTY_STAMP, LOW_SEV_EVENT, None) - .expect("Sending low severity event failed"); - assert!(!event_sent); - let res = event_rx.try_recv(); - assert!(res.is_err()); - assert!(matches!(res.unwrap_err(), TryRecvError::Empty)); - // Check that only the low severity event was disabled - event_sent = event_man - .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None) - .expect("Sending info event failed"); - assert!(event_sent); - event_rx.try_recv().expect("No info event received"); - } - - #[test] - fn test_reenable_event() { - let mut event_man = create_basic_man(); - let (event_tx, event_rx) = channel(); - let mut sender = EventTmSender { sender: event_tx }; - let mut res = event_man.disable_tm_for_event_with_sev(&INFO_EVENT); - assert!(res.is_ok()); - assert!(res.unwrap()); - res = event_man.enable_tm_for_event_with_sev(&INFO_EVENT); - assert!(res.is_ok()); - assert!(res.unwrap()); - let event_sent = event_man - .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None) - .expect("Sending info event failed"); - assert!(event_sent); - event_rx.try_recv().expect("No info event received"); - } -} diff --git a/satrs-core/src/pus/hk.rs b/satrs-core/src/pus/hk.rs deleted file mode 100644 index 3771670..0000000 --- a/satrs-core/src/pus/hk.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Subservice { - TcEnableGeneration = 5, - TcDisableGeneration = 6, - TmHkPacket = 25, - TcGenerateOneShotHk = 27, - TcModifyCollectionInterval = 31, -} diff --git a/satrs-core/src/pus/mod.rs b/satrs-core/src/pus/mod.rs deleted file mode 100644 index 3083b34..0000000 --- a/satrs-core/src/pus/mod.rs +++ /dev/null @@ -1,127 +0,0 @@ -//! All PUS support modules -//! -//! Currenty includes: -//! -//! 1. PUS Verification Service 1 module inside [verification]. Requires [alloc] support. -#[cfg(feature = "alloc")] -use downcast_rs::{impl_downcast, Downcast}; -#[cfg(feature = "alloc")] -use dyn_clone::DynClone; -use spacepackets::ecss::PusError; -use spacepackets::time::TimestampError; -use spacepackets::tm::PusTm; -use spacepackets::{ByteConversionError, SizeMissmatch}; - -pub mod event; -pub mod event_man; -pub mod hk; -pub mod verification; - -#[derive(Debug, Clone)] -pub enum EcssTmErrorWithSend { - /// Errors related to sending the verification telemetry to a TM recipient - SendError(E), - EcssTmError(EcssTmError), -} - -impl From for EcssTmErrorWithSend { - fn from(value: EcssTmError) -> Self { - Self::EcssTmError(value) - } -} - -/// Generic error type which is also able to wrap a user send error with the user supplied type E. -#[derive(Debug, Clone)] -pub enum EcssTmError { - /// Errors related to the time stamp format of the telemetry - TimestampError(TimestampError), - /// Errors related to byte conversion, for example insufficient buffer size for given data - ByteConversionError(ByteConversionError), - /// Errors related to PUS packet format - PusError(PusError), -} - -impl From for EcssTmError { - fn from(e: PusError) -> Self { - EcssTmError::PusError(e) - } -} - -impl From for EcssTmError { - fn from(e: ByteConversionError) -> Self { - EcssTmError::ByteConversionError(e) - } -} - -/// Generic trait for a user supplied sender object. -/// -/// This sender object is responsible for sending telemetry to a TM sink. -pub trait EcssTmSenderCore: Send { - type Error; - - fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmErrorWithSend>; -} - -#[cfg(feature = "alloc")] -pub mod alloc_mod { - use super::*; - - /// Extension trait for [EcssTmSenderCore]. - /// - /// It provides additional functionality, for example by implementing the [Downcast] trait - /// and the [DynClone] trait. - /// - /// [Downcast] is implemented to allow passing the sender as a boxed trait object and still - /// retrieve the concrete type at a later point. - /// - /// [DynClone] allows cloning the trait object as long as the boxed object implements - /// [Clone]. - pub trait EcssTmSender: EcssTmSenderCore + Downcast + DynClone {} - - /// Blanket implementation for all types which implement [EcssTmSenderCore] and are clonable. - impl EcssTmSender for T where T: EcssTmSenderCore + Clone + 'static {} - - dyn_clone::clone_trait_object!( EcssTmSender); - impl_downcast!(EcssTmSender assoc Error); -} - -pub(crate) fn source_buffer_large_enough(cap: usize, len: usize) -> Result<(), EcssTmError> { - if len > cap { - return Err(EcssTmError::ByteConversionError( - ByteConversionError::ToSliceTooSmall(SizeMissmatch { - found: cap, - expected: len, - }), - )); - } - Ok(()) -} - -#[cfg(test)] -pub(crate) mod tests { - use spacepackets::tm::{GenericPusTmSecondaryHeader, PusTm}; - use spacepackets::CcsdsPacket; - - #[derive(Debug, Eq, PartialEq, Clone)] - pub(crate) struct CommonTmInfo { - pub subservice: u8, - pub apid: u16, - pub msg_counter: u16, - pub dest_id: u16, - pub time_stamp: [u8; 7], - } - - impl CommonTmInfo { - pub fn new_from_tm(tm: &PusTm) -> Self { - let mut time_stamp = [0; 7]; - time_stamp.clone_from_slice(&tm.time_stamp()[0..7]); - Self { - subservice: tm.subservice(), - apid: tm.apid(), - msg_counter: tm.msg_counter(), - dest_id: tm.dest_id(), - time_stamp, - } - } - } -} diff --git a/satrs-core/src/pus/verification.rs b/satrs-core/src/pus/verification.rs deleted file mode 100644 index c694294..0000000 --- a/satrs-core/src/pus/verification.rs +++ /dev/null @@ -1,2207 +0,0 @@ -//! # PUS Verification Service 1 Module -//! -//! This module allows packaging and sending PUS Service 1 packets. It is conforming to section -//! 8 of the PUS standard ECSS-E-ST-70-41C. -//! -//! The core object to report TC verification progress is the [VerificationReporter]. It exposes -//! an API which uses type-state programming to avoid calling the verification steps in -//! an invalid order. -//! -//! # Examples -//! -//! Basic single-threaded example where a full success sequence for a given ping telecommand is -//! executed. Note that the verification part could also be done in a separate thread. -//! -//! ``` -//! use std::sync::{Arc, mpsc, RwLock}; -//! use std::time::Duration; -//! use satrs_core::pool::{LocalPool, PoolCfg, PoolProvider, SharedPool}; -//! use satrs_core::pus::verification::{MpscVerifSender, VerificationReporterCfg, VerificationReporterWithSender}; -//! use satrs_core::seq_count::SeqCountProviderSimple; -//! use spacepackets::ecss::PusPacket; -//! use spacepackets::SpHeader; -//! use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; -//! use spacepackets::tm::PusTm; -//! -//! const EMPTY_STAMP: [u8; 7] = [0; 7]; -//! const TEST_APID: u16 = 0x02; -//! -//! let pool_cfg = PoolCfg::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]); -//! let shared_tm_pool: SharedPool = Arc::new(RwLock::new(Box::new(LocalPool::new(pool_cfg.clone())))); -//! let (verif_tx, verif_rx) = mpsc::channel(); -//! let sender = MpscVerifSender::new(shared_tm_pool.clone(), verif_tx); -//! let cfg = VerificationReporterCfg::new(TEST_APID, Box::new(SeqCountProviderSimple::default()), 1, 2, 8).unwrap(); -//! let mut reporter = VerificationReporterWithSender::new(&cfg , Box::new(sender)); -//! -//! let mut sph = SpHeader::tc_unseg(TEST_APID, 0, 0).unwrap(); -//! let tc_header = PusTcSecondaryHeader::new_simple(17, 1); -//! let pus_tc_0 = PusTc::new(&mut sph, tc_header, None, true); -//! let init_token = reporter.add_tc(&pus_tc_0); -//! -//! // Complete success sequence for a telecommand -//! let accepted_token = reporter.acceptance_success(init_token, &EMPTY_STAMP).unwrap(); -//! let started_token = reporter.start_success(accepted_token, &EMPTY_STAMP).unwrap(); -//! reporter.completion_success(started_token, &EMPTY_STAMP).unwrap(); -//! -//! // Verify it arrives correctly on receiver end -//! let mut tm_buf: [u8; 1024] = [0; 1024]; -//! let mut packet_idx = 0; -//! while packet_idx < 3 { -//! let addr = verif_rx.recv_timeout(Duration::from_millis(10)).unwrap(); -//! let tm_len; -//! { -//! let mut rg = shared_tm_pool.write().expect("Error locking shared pool"); -//! let store_guard = rg.read_with_guard(addr); -//! let slice = store_guard.read().expect("Error reading TM slice"); -//! tm_len = slice.len(); -//! tm_buf[0..tm_len].copy_from_slice(slice); -//! } -//! let (pus_tm, _) = PusTm::from_bytes(&tm_buf[0..tm_len], 7) -//! .expect("Error reading verification TM"); -//! if packet_idx == 0 { -//! assert_eq!(pus_tm.subservice(), 1); -//! } else if packet_idx == 1 { -//! assert_eq!(pus_tm.subservice(), 3); -//! } else if packet_idx == 2 { -//! assert_eq!(pus_tm.subservice(), 7); -//! } -//! packet_idx += 1; -//! } -//! ``` -//! -//! 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::pus::{source_buffer_large_enough, EcssTmError, EcssTmErrorWithSend, EcssTmSenderCore}; -use core::fmt::{Display, Formatter}; -use core::hash::{Hash, Hasher}; -use core::marker::PhantomData; -use core::mem::size_of; -#[cfg(feature = "alloc")] -use delegate::delegate; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use spacepackets::ecss::EcssEnumeration; -use spacepackets::tc::PusTc; -use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; -use spacepackets::{CcsdsPacket, PacketId, PacketSequenceCtrl}; -use spacepackets::{SpHeader, MAX_APID}; - -pub use crate::seq_count::SeqCountProviderSimple; - -#[cfg(feature = "alloc")] -pub use alloc_mod::{ - VerificationReporter, VerificationReporterCfg, VerificationReporterWithSender, -}; - -use crate::seq_count::SequenceCountProviderCore; -#[cfg(all(feature = "crossbeam", feature = "std"))] -pub use stdmod::CrossbeamVerifSender; -#[cfg(feature = "std")] -pub use stdmod::{ - MpscVerifSender, SharedStdVerifReporterWithSender, StdVerifReporterWithSender, - StdVerifSenderError, -}; - -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub enum Subservice { - TmAcceptanceSuccess = 1, - TmAcceptanceFailure = 2, - TmStartSuccess = 3, - TmStartFailure = 4, - TmStepSuccess = 5, - TmStepFailure = 6, - TmCompletionSuccess = 7, - TmCompletionFailure = 8, -} - -impl From for u8 { - fn from(enumeration: Subservice) -> Self { - enumeration as u8 - } -} - -/// This is a request identifier as specified in 5.4.11.2 c. of the PUS standard -/// This field equivalent to the first two bytes of the CCSDS space packet header. -#[derive(Debug, Eq, Copy, Clone)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct RequestId { - version_number: u8, - packet_id: PacketId, - psc: PacketSequenceCtrl, -} - -impl Display for RequestId { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - write!(f, "{:#08x}", self.raw()) - } -} - -impl Hash for RequestId { - fn hash(&self, state: &mut H) { - self.raw().hash(state); - } -} - -// Implement manually to satisfy derive_hash_xor_eq lint -impl PartialEq for RequestId { - fn eq(&self, other: &Self) -> bool { - self.version_number == other.version_number - && self.packet_id == other.packet_id - && self.psc == other.psc - } -} - -impl RequestId { - pub const SIZE_AS_BYTES: usize = size_of::(); - - pub fn raw(&self) -> u32 { - ((self.version_number as u32) << 29) - | ((self.packet_id.raw() as u32) << 16) - | self.psc.raw() as u32 - } - - pub fn to_bytes(&self, buf: &mut [u8]) { - let raw = self.raw(); - buf.copy_from_slice(raw.to_be_bytes().as_slice()); - } - - pub fn from_bytes(buf: &[u8]) -> Option { - if buf.len() < 4 { - return None; - } - let raw = u32::from_be_bytes(buf[0..Self::SIZE_AS_BYTES].try_into().unwrap()); - Some(Self { - version_number: ((raw >> 29) & 0b111) as u8, - packet_id: PacketId::from(((raw >> 16) & 0xffff) as u16), - psc: PacketSequenceCtrl::from((raw & 0xffff) as u16), - }) - } -} -impl RequestId { - /// This allows extracting the request ID from a given PUS telecommand. - pub fn new(tc: &PusTc) -> Self { - RequestId { - version_number: tc.ccsds_version(), - packet_id: tc.packet_id(), - psc: tc.psc(), - } - } -} - -/// If a verification operation fails, the passed token will be returned as well. This allows -/// re-trying the operation at a later point. -#[derive(Debug, Clone)] -pub struct VerificationOrSendErrorWithToken( - pub EcssTmErrorWithSend, - pub VerificationToken, -); - -#[derive(Debug, Clone)] -pub struct VerificationErrorWithToken(pub EcssTmError, pub VerificationToken); - -impl From> for VerificationOrSendErrorWithToken { - fn from(value: VerificationErrorWithToken) -> Self { - VerificationOrSendErrorWithToken(value.0.into(), value.1) - } -} -/// Support token to allow type-state programming. This prevents calling the verification -/// steps in an invalid order. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub struct VerificationToken { - state: PhantomData, - req_id: RequestId, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct TcStateNone; -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct TcStateAccepted; -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct TcStateStarted; - -#[derive(Debug, Eq, PartialEq)] -pub enum TcStateToken { - None(VerificationToken), - Accepted(VerificationToken), - Started(VerificationToken), -} - -impl From> for TcStateToken { - fn from(t: VerificationToken) -> Self { - TcStateToken::None(t) - } -} - -impl From> for TcStateToken { - fn from(t: VerificationToken) -> Self { - TcStateToken::Accepted(t) - } -} - -impl From> for TcStateToken { - fn from(t: VerificationToken) -> Self { - TcStateToken::Started(t) - } -} - -impl VerificationToken { - fn new(req_id: RequestId) -> VerificationToken { - VerificationToken { - state: PhantomData, - req_id, - } - } - - pub fn req_id(&self) -> RequestId { - self.req_id - } -} - -/// Composite helper struct to pass failure parameters to the [VerificationReporter] -pub struct FailParams<'stamp, 'fargs> { - time_stamp: &'stamp [u8], - failure_code: &'fargs dyn EcssEnumeration, - failure_data: Option<&'fargs [u8]>, -} - -impl<'stamp, 'fargs> FailParams<'stamp, 'fargs> { - pub fn new( - time_stamp: &'stamp [u8], - failure_code: &'fargs impl EcssEnumeration, - failure_data: Option<&'fargs [u8]>, - ) -> Self { - Self { - time_stamp, - failure_code, - failure_data, - } - } -} - -/// Composite helper struct to pass step failure parameters to the [VerificationReporter] -pub struct FailParamsWithStep<'stamp, 'fargs> { - bp: FailParams<'stamp, 'fargs>, - step: &'fargs dyn EcssEnumeration, -} - -impl<'stamp, 'fargs> FailParamsWithStep<'stamp, 'fargs> { - pub fn new( - time_stamp: &'stamp [u8], - step: &'fargs impl EcssEnumeration, - failure_code: &'fargs impl EcssEnumeration, - failure_data: Option<&'fargs [u8]>, - ) -> Self { - Self { - bp: FailParams::new(time_stamp, failure_code, failure_data), - step, - } - } -} - -#[derive(Clone)] -pub struct VerificationReporterCore { - pub dest_id: u16, - apid: u16, -} - -pub(crate) fn increment_seq_counter( - seq_counter: Option<&(impl SequenceCountProviderCore + ?Sized)>, -) { - if let Some(seq_counter) = seq_counter { - seq_counter.increment(); - } -} - -pub enum VerifSuccess {} -pub enum VerifFailure {} - -/// Abstraction for a sendable PUS TM. The user is expected to send the TM packet to a TM sink. -/// -/// This struct generally mutably borrows the source data buffer. -pub struct VerificationSendable<'src_data, State, SuccessOrFailure> { - token: Option>, - pus_tm: Option>, - phantom: PhantomData, -} - -impl<'src_data, State, SuccessOrFailure> VerificationSendable<'src_data, State, SuccessOrFailure> { - pub(crate) fn new(pus_tm: PusTm<'src_data>, token: VerificationToken) -> Self { - Self { - token: Some(token), - pus_tm: Some(pus_tm), - phantom: PhantomData, - } - } - pub(crate) fn new_no_token(pus_tm: PusTm<'src_data>) -> Self { - Self { - token: None, - pus_tm: Some(pus_tm), - phantom: PhantomData, - } - } - - pub fn len_packed(&self) -> usize { - self.pus_tm.as_ref().unwrap().len_packed() - } - - pub fn pus_tm(&self) -> &PusTm<'src_data> { - self.pus_tm.as_ref().unwrap() - } - - pub fn pus_tm_mut(&mut self) -> &mut PusTm<'src_data> { - self.pus_tm.as_mut().unwrap() - } -} - -impl<'src_data, State> VerificationSendable<'src_data, State, VerifFailure> { - pub fn send_success_verif_failure( - self, - seq_counter: Option<&(impl SequenceCountProviderCore + ?Sized)>, - ) { - increment_seq_counter(seq_counter) - } -} - -impl<'src_data, State> VerificationSendable<'src_data, State, VerifFailure> { - pub fn send_failure(self) -> (PusTm<'src_data>, VerificationToken) { - (self.pus_tm.unwrap(), self.token.unwrap()) - } -} - -impl<'src_data> VerificationSendable<'src_data, TcStateNone, VerifSuccess> { - pub fn send_success_acceptance_success( - self, - seq_counter: Option<&(impl SequenceCountProviderCore + ?Sized)>, - ) -> VerificationToken { - increment_seq_counter(seq_counter); - VerificationToken { - state: PhantomData, - req_id: self.token.unwrap().req_id(), - } - } -} - -impl<'src_data> VerificationSendable<'src_data, TcStateAccepted, VerifSuccess> { - pub fn send_success_start_success( - self, - seq_counter: Option<&(impl SequenceCountProviderCore + ?Sized)>, - ) -> VerificationToken { - increment_seq_counter(seq_counter); - VerificationToken { - state: PhantomData, - req_id: self.token.unwrap().req_id(), - } - } -} - -impl<'src_data> VerificationSendable<'src_data, TcStateStarted, VerifSuccess> { - pub fn send_success_step_or_completion_success( - self, - seq_counter: Option<&(impl SequenceCountProviderCore + ?Sized)>, - ) { - increment_seq_counter(seq_counter); - } -} - -/// Primary verification handler. It provides an API to send PUS 1 verification telemetry packets -/// and verify the various steps of telecommand handling as specified in the PUS standard. -/// -/// This is the core component which can be used without [`alloc`] support. Please note that -/// the buffer passed to the API exposes by this struct will be used to serialize the source data. -/// This buffer may not be re-used to serialize the whole telemetry because that would overwrite -/// the source data itself. -impl VerificationReporterCore { - pub fn new(apid: u16) -> Option { - if apid > MAX_APID { - return None; - } - Some(Self { apid, dest_id: 0 }) - } - - pub fn set_apid(&mut self, apid: u16) -> bool { - if apid > MAX_APID { - return false; - } - self.apid = apid; - true - } - - pub fn apid(&self) -> u16 { - self.apid - } - - pub fn dest_id(&self) -> u16 { - self.dest_id - } - - pub fn set_dest_id(&mut self, dest_id: u16) { - self.dest_id = dest_id; - } - - /// Initialize verification handling by passing a TC reference. This returns a token required - /// to call the acceptance functions - pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken { - self.add_tc_with_req_id(RequestId::new(pus_tc)) - } - - /// Same as [Self::add_tc] but pass a request ID instead of the direct telecommand. - /// This can be useful if the executing thread does not have full access to the telecommand. - pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken { - VerificationToken::::new(req_id) - } - - fn sendable_success_no_step<'src_data, State: Copy>( - &mut self, - src_data_buf: &'src_data mut [u8], - subservice: u8, - token: VerificationToken, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - time_stamp: &'src_data [u8], - ) -> Result< - VerificationSendable<'src_data, State, VerifSuccess>, - VerificationErrorWithToken, - > { - Ok(VerificationSendable::new( - self.create_pus_verif_success_tm( - src_data_buf, - subservice, - seq_counter.get(), - &token.req_id, - time_stamp, - None::<&dyn EcssEnumeration>, - ) - .map_err(|e| VerificationErrorWithToken(e, token))?, - token, - )) - } - - fn sendable_failure_no_step<'src_data, State: Copy>( - &mut self, - src_data_buf: &'src_data mut [u8], - subservice: u8, - token: VerificationToken, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - step: Option<&(impl EcssEnumeration + ?Sized)>, - params: &FailParams<'src_data, '_>, - ) -> Result< - VerificationSendable<'src_data, State, VerifFailure>, - VerificationErrorWithToken, - > { - Ok(VerificationSendable::new( - self.create_pus_verif_fail_tm( - src_data_buf, - subservice, - seq_counter.get(), - &token.req_id, - step, - params, - ) - .map_err(|e| VerificationErrorWithToken(e, token))?, - token, - )) - } - - /// Package a PUS TM\[1, 1\] packet, see 8.1.2.1 of the PUS standard. - pub fn acceptance_success<'src_data>( - &mut self, - src_data_buf: &'src_data mut [u8], - token: VerificationToken, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - time_stamp: &'src_data [u8], - ) -> Result< - VerificationSendable<'src_data, TcStateNone, VerifSuccess>, - VerificationErrorWithToken, - > { - self.sendable_success_no_step( - src_data_buf, - Subservice::TmAcceptanceSuccess.into(), - token, - seq_counter, - time_stamp, - ) - } - - pub fn send_acceptance_success<'src_data, E>( - &self, - mut sendable: VerificationSendable<'src_data, TcStateNone, VerifSuccess>, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - sender: &mut (impl EcssTmSenderCore + ?Sized), - ) -> Result, VerificationOrSendErrorWithToken> - { - sender - .send_tm(sendable.pus_tm.take().unwrap()) - .map_err(|e| VerificationOrSendErrorWithToken(e, sendable.token.unwrap()))?; - Ok(sendable.send_success_acceptance_success(Some(seq_counter))) - } - - pub fn send_acceptance_failure<'src_data, E>( - &self, - mut sendable: VerificationSendable<'src_data, TcStateNone, VerifFailure>, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - sender: &mut (impl EcssTmSenderCore + ?Sized), - ) -> Result<(), VerificationOrSendErrorWithToken> { - sender - .send_tm(sendable.pus_tm.take().unwrap()) - .map_err(|e| VerificationOrSendErrorWithToken(e, sendable.token.unwrap()))?; - sendable.send_success_verif_failure(Some(seq_counter)); - Ok(()) - } - - /// Package a PUS TM\[1, 2\] packet, see 8.1.2.2 of the PUS standard. - pub fn acceptance_failure<'src_data>( - &mut self, - src_data_buf: &'src_data mut [u8], - token: VerificationToken, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - params: FailParams<'src_data, '_>, - ) -> Result< - VerificationSendable<'src_data, TcStateNone, VerifFailure>, - VerificationErrorWithToken, - > { - self.sendable_failure_no_step( - src_data_buf, - Subservice::TmAcceptanceFailure.into(), - token, - seq_counter, - None::<&dyn EcssEnumeration>, - ¶ms, - ) - } - - /// Package and send a PUS TM\[1, 3\] packet, see 8.1.2.3 of the PUS standard. - /// - /// Requires a token previously acquired by calling [Self::acceptance_success]. - pub fn start_success<'src_data>( - &mut self, - src_data_buf: &'src_data mut [u8], - token: VerificationToken, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - time_stamp: &'src_data [u8], - ) -> Result< - VerificationSendable<'src_data, TcStateAccepted, VerifSuccess>, - VerificationErrorWithToken, - > { - self.sendable_success_no_step( - src_data_buf, - Subservice::TmStartSuccess.into(), - token, - seq_counter, - time_stamp, - ) - } - - pub fn send_start_success<'src_data, E>( - &self, - mut sendable: VerificationSendable<'src_data, TcStateAccepted, VerifSuccess>, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - sender: &mut (impl EcssTmSenderCore + ?Sized), - ) -> Result< - VerificationToken, - VerificationOrSendErrorWithToken, - > { - sender - .send_tm(sendable.pus_tm.take().unwrap()) - .map_err(|e| VerificationOrSendErrorWithToken(e, sendable.token.unwrap()))?; - Ok(sendable.send_success_start_success(Some(seq_counter))) - } - - /// Package and send a PUS TM\[1, 4\] packet, see 8.1.2.4 of the PUS standard. - /// - /// Requires a token previously acquired by calling [Self::acceptance_success]. It consumes - /// the token because verification handling is done. - pub fn start_failure<'src_data>( - &mut self, - src_data_buf: &'src_data mut [u8], - token: VerificationToken, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - params: FailParams<'src_data, '_>, - ) -> Result< - VerificationSendable<'src_data, TcStateAccepted, VerifFailure>, - VerificationErrorWithToken, - > { - self.sendable_failure_no_step( - src_data_buf, - Subservice::TmStartFailure.into(), - token, - seq_counter, - None::<&dyn EcssEnumeration>, - ¶ms, - ) - } - - pub fn send_start_failure<'src_data, E>( - &self, - mut sendable: VerificationSendable<'src_data, TcStateAccepted, VerifFailure>, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - sender: &mut (impl EcssTmSenderCore + ?Sized), - ) -> Result<(), VerificationOrSendErrorWithToken> { - sender - .send_tm(sendable.pus_tm.take().unwrap()) - .map_err(|e| VerificationOrSendErrorWithToken(e, sendable.token.unwrap()))?; - sendable.send_success_verif_failure(Some(seq_counter)); - Ok(()) - } - - /// Package and send a PUS TM\[1, 5\] packet, see 8.1.2.5 of the PUS standard. - /// - /// Requires a token previously acquired by calling [Self::start_success]. - pub fn step_success<'src_data>( - &mut self, - src_data_buf: &'src_data mut [u8], - token: &VerificationToken, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - time_stamp: &'src_data [u8], - step: impl EcssEnumeration, - ) -> Result, EcssTmError> { - Ok(VerificationSendable::new_no_token( - self.create_pus_verif_success_tm( - src_data_buf, - Subservice::TmStepSuccess.into(), - seq_counter.get(), - &token.req_id, - time_stamp, - Some(&step), - )?, - )) - } - - /// Package and send a PUS TM\[1, 6\] packet, see 8.1.2.6 of the PUS standard. - /// - /// Requires a token previously acquired by calling [Self::start_success]. It consumes the - /// token because verification handling is done. - pub fn step_failure<'src_data>( - &mut self, - src_data_buf: &'src_data mut [u8], - token: VerificationToken, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - params: FailParamsWithStep<'src_data, '_>, - ) -> Result< - VerificationSendable<'src_data, TcStateStarted, VerifFailure>, - VerificationErrorWithToken, - > { - Ok(VerificationSendable::new( - self.create_pus_verif_fail_tm( - src_data_buf, - Subservice::TmStepFailure.into(), - seq_counter.get(), - &token.req_id, - Some(params.step), - ¶ms.bp, - ) - .map_err(|e| VerificationErrorWithToken(e, token))?, - token, - )) - } - - /// Package and send a PUS TM\[1, 7\] packet, see 8.1.2.7 of the PUS standard. - /// - /// Requires a token previously acquired by calling [Self::start_success]. It consumes the - /// token because verification handling is done. - pub fn completion_success<'src_data>( - &mut self, - src_data_buf: &'src_data mut [u8], - token: VerificationToken, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - time_stamp: &'src_data [u8], - ) -> Result< - VerificationSendable<'src_data, TcStateStarted, VerifSuccess>, - VerificationErrorWithToken, - > { - self.sendable_success_no_step( - src_data_buf, - Subservice::TmCompletionSuccess.into(), - token, - seq_counter, - time_stamp, - ) - } - - /// Package and send a PUS TM\[1, 8\] packet, see 8.1.2.8 of the PUS standard. - /// - /// Requires a token previously acquired by calling [Self::start_success]. It consumes the - /// token because verification handling is done. - pub fn completion_failure<'src_data>( - &mut self, - src_data_buf: &'src_data mut [u8], - token: VerificationToken, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - params: FailParams<'src_data, '_>, - ) -> Result< - VerificationSendable<'src_data, TcStateStarted, VerifFailure>, - VerificationErrorWithToken, - > { - self.sendable_failure_no_step( - src_data_buf, - Subservice::TmCompletionFailure.into(), - token, - seq_counter, - None::<&dyn EcssEnumeration>, - ¶ms, - ) - } - - pub fn send_step_or_completion_success<'src_data, E>( - &self, - mut sendable: VerificationSendable<'src_data, TcStateStarted, VerifSuccess>, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - sender: &mut (impl EcssTmSenderCore + ?Sized), - ) -> Result<(), VerificationOrSendErrorWithToken> { - sender - .send_tm(sendable.pus_tm.take().unwrap()) - .map_err(|e| VerificationOrSendErrorWithToken(e, sendable.token.unwrap()))?; - sendable.send_success_step_or_completion_success(Some(seq_counter)); - Ok(()) - } - - pub fn send_step_or_completion_failure<'src_data, E>( - &self, - mut sendable: VerificationSendable<'src_data, TcStateStarted, VerifFailure>, - seq_counter: &(impl SequenceCountProviderCore + ?Sized), - sender: &mut (impl EcssTmSenderCore + ?Sized), - ) -> Result<(), VerificationOrSendErrorWithToken> { - sender - .send_tm(sendable.pus_tm.take().unwrap()) - .map_err(|e| VerificationOrSendErrorWithToken(e, sendable.token.unwrap()))?; - sendable.send_success_verif_failure(Some(seq_counter)); - Ok(()) - } - - fn create_pus_verif_success_tm<'src_data>( - &mut self, - src_data_buf: &'src_data mut [u8], - subservice: u8, - msg_counter: u16, - req_id: &RequestId, - time_stamp: &'src_data [u8], - step: Option<&(impl EcssEnumeration + ?Sized)>, - ) -> Result, EcssTmError> { - let mut source_data_len = size_of::(); - if let Some(step) = step { - source_data_len += step.byte_width(); - } - source_buffer_large_enough(src_data_buf.len(), source_data_len)?; - let mut idx = 0; - req_id.to_bytes(&mut src_data_buf[0..RequestId::SIZE_AS_BYTES]); - idx += RequestId::SIZE_AS_BYTES; - if let Some(step) = step { - // Size check was done beforehand - step.write_to_be_bytes(&mut src_data_buf[idx..idx + step.byte_width()]) - .unwrap(); - } - let mut sp_header = SpHeader::tm_unseg(self.apid(), 0, 0).unwrap(); - Ok(self.create_pus_verif_tm_base( - src_data_buf, - subservice, - msg_counter, - &mut sp_header, - time_stamp, - source_data_len, - )) - } - - fn create_pus_verif_fail_tm<'src_data>( - &mut self, - src_data_buf: &'src_data mut [u8], - subservice: u8, - msg_counter: u16, - req_id: &RequestId, - step: Option<&(impl EcssEnumeration + ?Sized)>, - params: &FailParams<'src_data, '_>, - ) -> Result, EcssTmError> { - let mut idx = 0; - let mut source_data_len = RequestId::SIZE_AS_BYTES + params.failure_code.byte_width(); - if let Some(step) = step { - source_data_len += step.byte_width(); - } - if let Some(failure_data) = params.failure_data { - source_data_len += failure_data.len(); - } - source_buffer_large_enough(src_data_buf.len(), source_data_len)?; - req_id.to_bytes(&mut src_data_buf[0..RequestId::SIZE_AS_BYTES]); - idx += RequestId::SIZE_AS_BYTES; - if let Some(step) = step { - // Size check done beforehand - step.write_to_be_bytes(&mut src_data_buf[idx..idx + step.byte_width()]) - .unwrap(); - idx += step.byte_width(); - } - params - .failure_code - .write_to_be_bytes(&mut src_data_buf[idx..idx + params.failure_code.byte_width()])?; - idx += params.failure_code.byte_width(); - if let Some(failure_data) = params.failure_data { - src_data_buf[idx..idx + failure_data.len()].copy_from_slice(failure_data); - } - let mut sp_header = SpHeader::tm_unseg(self.apid(), 0, 0).unwrap(); - Ok(self.create_pus_verif_tm_base( - src_data_buf, - subservice, - msg_counter, - &mut sp_header, - params.time_stamp, - source_data_len, - )) - } - - fn create_pus_verif_tm_base<'src_data>( - &mut self, - src_data_buf: &'src_data mut [u8], - subservice: u8, - msg_counter: u16, - sp_header: &mut SpHeader, - time_stamp: &'src_data [u8], - source_data_len: usize, - ) -> PusTm<'src_data> { - let tm_sec_header = - PusTmSecondaryHeader::new(1, subservice, msg_counter, self.dest_id, time_stamp); - PusTm::new( - sp_header, - tm_sec_header, - Some(&src_data_buf[0..source_data_len]), - true, - ) - } -} - -#[cfg(feature = "alloc")] -mod alloc_mod { - use super::*; - use crate::pus::alloc_mod::EcssTmSender; - use crate::seq_count::SequenceCountProvider; - use alloc::boxed::Box; - use alloc::vec; - use alloc::vec::Vec; - - #[derive(Clone)] - pub struct VerificationReporterCfg { - apid: u16, - seq_counter: Box + Send>, - pub step_field_width: usize, - pub fail_code_field_width: usize, - pub max_fail_data_len: usize, - } - - impl VerificationReporterCfg { - pub fn new( - apid: u16, - seq_counter: Box + Send>, - step_field_width: usize, - fail_code_field_width: usize, - max_fail_data_len: usize, - ) -> Option { - if apid > MAX_APID { - return None; - } - Some(Self { - apid, - seq_counter, - step_field_width, - fail_code_field_width, - max_fail_data_len, - }) - } - } - - /// Primary verification handler. It provides an API to send PUS 1 verification telemetry packets - /// and verify the various steps of telecommand handling as specified in the PUS standard. - #[derive(Clone)] - pub struct VerificationReporter { - source_data_buf: Vec, - seq_counter: Box + Send + 'static>, - pub reporter: VerificationReporterCore, - } - - impl VerificationReporter { - pub fn new(cfg: &VerificationReporterCfg) -> Self { - let reporter = VerificationReporterCore::new(cfg.apid).unwrap(); - Self { - source_data_buf: vec![ - 0; - RequestId::SIZE_AS_BYTES - + cfg.step_field_width - + cfg.fail_code_field_width - + cfg.max_fail_data_len - ], - seq_counter: cfg.seq_counter.clone(), - reporter, - } - } - - delegate!( - to self.reporter { - pub fn set_apid(&mut self, apid: u16) -> bool; - pub fn apid(&self) -> u16; - pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken; - pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken; - pub fn dest_id(&self) -> u16; - pub fn set_dest_id(&mut self, dest_id: u16); - } - ); - - pub fn allowed_source_data_len(&self) -> usize { - self.source_data_buf.capacity() - } - - /// Package and send a PUS TM\[1, 1\] packet, see 8.1.2.1 of the PUS standard - pub fn acceptance_success( - &mut self, - token: VerificationToken, - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - ) -> Result< - VerificationToken, - VerificationOrSendErrorWithToken, - > { - let sendable = self.reporter.acceptance_success( - self.source_data_buf.as_mut_slice(), - token, - self.seq_counter.as_ref(), - time_stamp, - )?; - self.reporter - .send_acceptance_success(sendable, self.seq_counter.as_ref(), sender) - } - - /// Package and send a PUS TM\[1, 2\] packet, see 8.1.2.2 of the PUS standard - pub fn acceptance_failure( - &mut self, - token: VerificationToken, - sender: &mut (impl EcssTmSenderCore + ?Sized), - params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken> { - let sendable = self.reporter.acceptance_failure( - self.source_data_buf.as_mut_slice(), - token, - self.seq_counter.as_ref(), - params, - )?; - self.reporter - .send_acceptance_failure(sendable, self.seq_counter.as_ref(), sender) - } - - /// Package and send a PUS TM\[1, 3\] packet, see 8.1.2.3 of the PUS standard. - /// - /// Requires a token previously acquired by calling [Self::acceptance_success]. - pub fn start_success( - &mut self, - token: VerificationToken, - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - ) -> Result< - VerificationToken, - VerificationOrSendErrorWithToken, - > { - let sendable = self.reporter.start_success( - self.source_data_buf.as_mut_slice(), - token, - self.seq_counter.as_mut(), - time_stamp, - )?; - self.reporter - .send_start_success(sendable, self.seq_counter.as_ref(), sender) - } - - /// Package and send a PUS TM\[1, 4\] packet, see 8.1.2.4 of the PUS standard. - /// - /// Requires a token previously acquired by calling [Self::acceptance_success]. It consumes - /// the token because verification handling is done. - pub fn start_failure( - &mut self, - token: VerificationToken, - sender: &mut (impl EcssTmSenderCore + ?Sized), - params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken> { - let sendable = self.reporter.start_failure( - self.source_data_buf.as_mut_slice(), - token, - self.seq_counter.as_mut(), - params, - )?; - self.reporter - .send_start_failure(sendable, self.seq_counter.as_ref(), sender) - } - - /// Package and send a PUS TM\[1, 5\] packet, see 8.1.2.5 of the PUS standard. - /// - /// Requires a token previously acquired by calling [Self::start_success]. - pub fn step_success( - &mut self, - token: &VerificationToken, - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - step: impl EcssEnumeration, - ) -> Result<(), EcssTmErrorWithSend> { - let sendable = self.reporter.step_success( - self.source_data_buf.as_mut_slice(), - token, - self.seq_counter.as_mut(), - time_stamp, - step, - )?; - self.reporter - .send_step_or_completion_success(sendable, self.seq_counter.as_ref(), sender) - .map_err(|e| e.0) - } - - /// Package and send a PUS TM\[1, 6\] packet, see 8.1.2.6 of the PUS standard. - /// - /// Requires a token previously acquired by calling [Self::start_success]. It consumes the - /// token because verification handling is done. - pub fn step_failure( - &mut self, - token: VerificationToken, - sender: &mut (impl EcssTmSenderCore + ?Sized), - params: FailParamsWithStep, - ) -> Result<(), VerificationOrSendErrorWithToken> { - let sendable = self.reporter.step_failure( - self.source_data_buf.as_mut_slice(), - token, - self.seq_counter.as_mut(), - params, - )?; - self.reporter.send_step_or_completion_failure( - sendable, - self.seq_counter.as_ref(), - sender, - ) - } - - /// Package and send a PUS TM\[1, 7\] packet, see 8.1.2.7 of the PUS standard. - /// - /// Requires a token previously acquired by calling [Self::start_success]. It consumes the - /// token because verification handling is done. - pub fn completion_success( - &mut self, - token: VerificationToken, - sender: &mut (impl EcssTmSenderCore + ?Sized), - time_stamp: &[u8], - ) -> Result<(), VerificationOrSendErrorWithToken> { - let sendable = self.reporter.completion_success( - self.source_data_buf.as_mut_slice(), - token, - self.seq_counter.as_mut(), - time_stamp, - )?; - self.reporter.send_step_or_completion_success( - sendable, - self.seq_counter.as_ref(), - sender, - ) - } - - /// Package and send a PUS TM\[1, 8\] packet, see 8.1.2.8 of the PUS standard. - /// - /// Requires a token previously acquired by calling [Self::start_success]. It consumes the - /// token because verification handling is done. - pub fn completion_failure( - &mut self, - token: VerificationToken, - sender: &mut (impl EcssTmSenderCore + ?Sized), - params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken> { - let sendable = self.reporter.completion_failure( - self.source_data_buf.as_mut_slice(), - token, - self.seq_counter.as_mut(), - params, - )?; - self.reporter.send_step_or_completion_failure( - sendable, - self.seq_counter.as_ref(), - sender, - ) - } - } - - /// Helper object which caches the sender passed as a trait object. Provides the same - /// API as [VerificationReporter] but without the explicit sender arguments. - #[derive(Clone)] - pub struct VerificationReporterWithSender { - pub reporter: VerificationReporter, - pub sender: Box>, - } - - impl VerificationReporterWithSender { - pub fn new( - cfg: &VerificationReporterCfg, - sender: Box>, - ) -> Self { - let reporter = VerificationReporter::new(cfg); - Self::new_from_reporter(reporter, sender) - } - - pub fn new_from_reporter( - reporter: VerificationReporter, - sender: Box>, - ) -> Self { - Self { reporter, sender } - } - - delegate! { - to self.reporter { - pub fn set_apid(&mut self, apid: u16) -> bool; - pub fn apid(&self) -> u16; - pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken; - pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken; - pub fn dest_id(&self) -> u16; - pub fn set_dest_id(&mut self, dest_id: u16); - } - } - - pub fn acceptance_success( - &mut self, - token: VerificationToken, - time_stamp: &[u8], - ) -> Result< - VerificationToken, - VerificationOrSendErrorWithToken, - > { - self.reporter - .acceptance_success(token, self.sender.as_mut(), time_stamp) - } - - pub fn acceptance_failure( - &mut self, - token: VerificationToken, - params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken> { - self.reporter - .acceptance_failure(token, self.sender.as_mut(), params) - } - - pub fn start_success( - &mut self, - token: VerificationToken, - time_stamp: &[u8], - ) -> Result< - VerificationToken, - VerificationOrSendErrorWithToken, - > { - self.reporter - .start_success(token, self.sender.as_mut(), time_stamp) - } - - pub fn start_failure( - &mut self, - token: VerificationToken, - params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken> { - self.reporter - .start_failure(token, self.sender.as_mut(), params) - } - - pub fn step_success( - &mut self, - token: &VerificationToken, - time_stamp: &[u8], - step: impl EcssEnumeration, - ) -> Result<(), EcssTmErrorWithSend> { - self.reporter - .step_success(token, self.sender.as_mut(), time_stamp, step) - } - - pub fn step_failure( - &mut self, - token: VerificationToken, - params: FailParamsWithStep, - ) -> Result<(), VerificationOrSendErrorWithToken> { - self.reporter - .step_failure(token, self.sender.as_mut(), params) - } - - pub fn completion_success( - &mut self, - token: VerificationToken, - time_stamp: &[u8], - ) -> Result<(), VerificationOrSendErrorWithToken> { - self.reporter - .completion_success(token, self.sender.as_mut(), time_stamp) - } - - pub fn completion_failure( - &mut self, - token: VerificationToken, - params: FailParams, - ) -> Result<(), VerificationOrSendErrorWithToken> { - self.reporter - .completion_failure(token, self.sender.as_mut(), params) - } - } -} - -#[cfg(feature = "std")] -mod stdmod { - use super::alloc_mod::VerificationReporterWithSender; - use super::*; - use crate::pool::{ShareablePoolProvider, SharedPool, StoreAddr, StoreError}; - use delegate::delegate; - use spacepackets::tm::PusTm; - use std::sync::{mpsc, Arc, Mutex, RwLockWriteGuard}; - - pub type StdVerifReporterWithSender = VerificationReporterWithSender; - pub type SharedStdVerifReporterWithSender = Arc>; - - #[derive(Debug, Eq, PartialEq, Clone)] - pub enum StdVerifSenderError { - PoisonError, - StoreError(StoreError), - RxDisconnected(StoreAddr), - } - - impl From for StdVerifSenderError { - fn from(e: StoreError) -> Self { - StdVerifSenderError::StoreError(e) - } - } - - impl From for EcssTmErrorWithSend { - fn from(e: StoreError) -> Self { - EcssTmErrorWithSend::SendError(e.into()) - } - } - - trait SendBackend: Send { - fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr>; - } - - #[derive(Clone)] - struct StdSenderBase { - pub ignore_poison_error: bool, - tm_store: SharedPool, - tx: S, - } - - impl StdSenderBase { - pub fn new(tm_store: SharedPool, tx: S) -> Self { - Self { - ignore_poison_error: false, - tm_store, - tx, - } - } - } - - unsafe impl Sync for StdSenderBase {} - unsafe impl Send for StdSenderBase {} - - impl SendBackend for mpsc::Sender { - fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr> { - self.send(addr).map_err(|_| addr) - } - } - - #[derive(Clone)] - pub struct MpscVerifSender { - base: StdSenderBase>, - } - - /// Verification sender with a [mpsc::Sender] backend. - /// It implements the [EcssTmSenderCore] trait to be used as PUS Verification TM sender. - impl MpscVerifSender { - pub fn new(tm_store: SharedPool, tx: mpsc::Sender) -> Self { - Self { - base: StdSenderBase::new(tm_store, tx), - } - } - } - - //noinspection RsTraitImplementation - impl EcssTmSenderCore for MpscVerifSender { - type Error = StdVerifSenderError; - - delegate!( - to self.base { - fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmErrorWithSend>; - } - ); - } - - impl SendBackend for crossbeam_channel::Sender { - fn send(&self, addr: StoreAddr) -> Result<(), StoreAddr> { - self.send(addr).map_err(|_| addr) - } - } - - /// Verification sender with a [crossbeam_channel::Sender] backend. - /// It implements the [EcssTmSenderCore] trait to be used as PUS Verification TM sender - #[cfg(feature = "crossbeam")] - #[derive(Clone)] - pub struct CrossbeamVerifSender { - base: StdSenderBase>, - } - - #[cfg(feature = "crossbeam")] - impl CrossbeamVerifSender { - pub fn new(tm_store: SharedPool, tx: crossbeam_channel::Sender) -> Self { - Self { - base: StdSenderBase::new(tm_store, tx), - } - } - } - - //noinspection RsTraitImplementation - #[cfg(feature = "crossbeam")] - impl EcssTmSenderCore for CrossbeamVerifSender { - type Error = StdVerifSenderError; - - delegate!( - to self.base { - fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmErrorWithSend>; - } - ); - } - - impl EcssTmSenderCore for StdSenderBase { - type Error = StdVerifSenderError; - fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmErrorWithSend> { - let operation = |mut mg: RwLockWriteGuard| { - let (addr, buf) = mg.free_element(tm.len_packed())?; - tm.write_to_bytes(buf).map_err(EcssTmError::PusError)?; - drop(mg); - self.tx.send(addr).map_err(|_| { - EcssTmErrorWithSend::SendError(StdVerifSenderError::RxDisconnected(addr)) - })?; - Ok(()) - }; - match self.tm_store.write() { - Ok(lock) => operation(lock), - Err(poison_error) => { - if self.ignore_poison_error { - operation(poison_error.into_inner()) - } else { - Err(EcssTmErrorWithSend::SendError( - StdVerifSenderError::PoisonError, - )) - } - } - } - } - } -} - -#[cfg(test)] -mod tests { - use crate::pool::{LocalPool, PoolCfg, SharedPool}; - use crate::pus::tests::CommonTmInfo; - use crate::pus::verification::{ - EcssTmError, EcssTmSenderCore, FailParams, FailParamsWithStep, MpscVerifSender, RequestId, - TcStateNone, VerificationReporter, VerificationReporterCfg, VerificationReporterWithSender, - VerificationToken, - }; - use crate::pus::EcssTmErrorWithSend; - use crate::seq_count::SeqCountProviderSimple; - use alloc::boxed::Box; - use alloc::format; - use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration, PusPacket}; - use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; - use spacepackets::tm::PusTm; - use spacepackets::{ByteConversionError, SpHeader}; - use std::collections::VecDeque; - use std::sync::{mpsc, Arc, RwLock}; - use std::vec; - use std::vec::Vec; - - fn is_send(_: &T) {} - #[allow(dead_code)] - fn is_sync(_: &T) {} - - const TEST_APID: u16 = 0x02; - const EMPTY_STAMP: [u8; 7] = [0; 7]; - - #[derive(Debug, Eq, PartialEq, Clone)] - struct TmInfo { - pub common: CommonTmInfo, - pub req_id: RequestId, - pub additional_data: Option>, - } - - #[derive(Default, Clone)] - struct TestSender { - pub service_queue: VecDeque, - } - - impl EcssTmSenderCore for TestSender { - type Error = (); - fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmErrorWithSend> { - assert_eq!(PusPacket::service(&tm), 1); - assert!(tm.source_data().is_some()); - let mut time_stamp = [0; 7]; - time_stamp.clone_from_slice(&tm.time_stamp()[0..7]); - let src_data = tm.source_data().unwrap(); - assert!(src_data.len() >= 4); - let req_id = RequestId::from_bytes(&src_data[0..RequestId::SIZE_AS_BYTES]).unwrap(); - let mut vec = None; - if src_data.len() > 4 { - let mut new_vec = Vec::new(); - new_vec.extend_from_slice(&src_data[RequestId::SIZE_AS_BYTES..]); - vec = Some(new_vec); - } - self.service_queue.push_back(TmInfo { - common: CommonTmInfo::new_from_tm(&tm), - req_id, - additional_data: vec, - }); - Ok(()) - } - } - - #[derive(Debug, Copy, Clone, Eq, PartialEq)] - struct DummyError {} - #[derive(Default, Clone)] - struct FallibleSender {} - - impl EcssTmSenderCore for FallibleSender { - type Error = DummyError; - fn send_tm(&mut self, _: PusTm) -> Result<(), EcssTmErrorWithSend> { - Err(EcssTmErrorWithSend::SendError(DummyError {})) - } - } - - struct TestBase<'a> { - vr: VerificationReporter, - #[allow(dead_code)] - tc: PusTc<'a>, - } - - impl<'a> TestBase<'a> { - fn rep(&mut self) -> &mut VerificationReporter { - &mut self.vr - } - } - struct TestBaseWithHelper<'a, E> { - helper: VerificationReporterWithSender, - #[allow(dead_code)] - tc: PusTc<'a>, - } - - impl<'a, E> TestBaseWithHelper<'a, E> { - fn rep(&mut self) -> &mut VerificationReporter { - &mut self.helper.reporter - } - } - - fn base_reporter() -> VerificationReporter { - let cfg = VerificationReporterCfg::new( - TEST_APID, - Box::new(SeqCountProviderSimple::default()), - 1, - 2, - 8, - ) - .unwrap(); - VerificationReporter::new(&cfg) - } - - fn base_tc_init(app_data: Option<&[u8]>) -> (PusTc, RequestId) { - let mut sph = SpHeader::tc_unseg(TEST_APID, 0x34, 0).unwrap(); - let tc_header = PusTcSecondaryHeader::new_simple(17, 1); - let pus_tc = PusTc::new(&mut sph, tc_header, app_data, true); - let req_id = RequestId::new(&pus_tc); - (pus_tc, req_id) - } - - fn base_init(api_sel: bool) -> (TestBase<'static>, VerificationToken) { - let mut reporter = base_reporter(); - let (tc, req_id) = base_tc_init(None); - let init_tok; - if api_sel { - init_tok = reporter.add_tc_with_req_id(req_id); - } else { - init_tok = reporter.add_tc(&tc); - } - (TestBase { vr: reporter, tc }, init_tok) - } - - fn base_with_helper_init() -> ( - TestBaseWithHelper<'static, ()>, - VerificationToken, - ) { - let mut reporter = base_reporter(); - let (tc, _) = base_tc_init(None); - let init_tok = reporter.add_tc(&tc); - let sender = TestSender::default(); - let helper = VerificationReporterWithSender::new_from_reporter(reporter, Box::new(sender)); - (TestBaseWithHelper { helper, tc }, init_tok) - } - - fn acceptance_check(sender: &mut TestSender, req_id: &RequestId) { - let cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id: req_id.clone(), - }; - assert_eq!(sender.service_queue.len(), 1); - let info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - } - - #[test] - fn test_mpsc_verif_send_sync() { - let pool = LocalPool::new(PoolCfg::new(vec![(8, 8)])); - let shared_pool: SharedPool = Arc::new(RwLock::new(Box::new(pool))); - let (tx, _) = mpsc::channel(); - let mpsc_verif_sender = MpscVerifSender::new(shared_pool, tx); - is_send(&mpsc_verif_sender); - } - - #[test] - fn test_state() { - let (mut b, _) = base_init(false); - assert_eq!(b.vr.apid(), TEST_APID); - b.vr.set_apid(TEST_APID + 1); - assert_eq!(b.vr.apid(), TEST_APID + 1); - } - - #[test] - fn test_basic_acceptance_success() { - let (mut b, tok) = base_init(false); - let mut sender = TestSender::default(); - b.vr.acceptance_success(tok, &mut sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - acceptance_check(&mut sender, &tok.req_id); - } - - #[test] - fn test_basic_acceptance_success_with_helper() { - let (mut b, tok) = base_with_helper_init(); - b.helper - .acceptance_success(tok, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); - acceptance_check(sender, &tok.req_id); - } - - #[test] - fn test_acceptance_send_fails() { - let (mut b, tok) = base_init(false); - let mut faulty_sender = FallibleSender::default(); - let res = - b.vr.acceptance_success(tok, &mut faulty_sender, &EMPTY_STAMP); - assert!(res.is_err()); - let err = res.unwrap_err(); - assert_eq!(err.1, tok); - match err.0 { - EcssTmErrorWithSend::SendError(e) => { - assert_eq!(e, DummyError {}) - } - _ => panic!("{}", format!("Unexpected error {:?}", err.0)), - } - } - - fn acceptance_fail_check(sender: &mut TestSender, req_id: RequestId, stamp_buf: [u8; 7]) { - let cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 2, - apid: TEST_APID, - msg_counter: 0, - dest_id: 5, - time_stamp: stamp_buf, - }, - additional_data: Some([0, 2].to_vec()), - req_id, - }; - assert_eq!(sender.service_queue.len(), 1); - let info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - } - - #[test] - fn test_basic_acceptance_failure() { - let (mut b, tok) = base_init(true); - b.rep().reporter.dest_id = 5; - let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; - let mut sender = TestSender::default(); - let fail_code = EcssEnumU16::new(2); - let fail_params = FailParams::new(stamp_buf.as_slice(), &fail_code, None); - b.vr.acceptance_failure(tok, &mut sender, fail_params) - .expect("Sending acceptance success failed"); - acceptance_fail_check(&mut sender, tok.req_id, stamp_buf); - } - - #[test] - fn test_basic_acceptance_failure_with_helper() { - let (mut b, tok) = base_with_helper_init(); - b.rep().reporter.dest_id = 5; - let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; - let fail_code = EcssEnumU16::new(2); - let fail_params = FailParams::new(stamp_buf.as_slice(), &fail_code, None); - b.helper - .acceptance_failure(tok, fail_params) - .expect("Sending acceptance success failed"); - let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); - acceptance_fail_check(sender, tok.req_id, stamp_buf); - } - - #[test] - fn test_acceptance_fail_data_too_large() { - let (mut b, tok) = base_with_helper_init(); - b.rep().reporter.dest_id = 5; - let stamp_buf = [1, 2, 3, 4, 5, 6, 7]; - let fail_code = EcssEnumU16::new(2); - let fail_data: [u8; 16] = [0; 16]; - // 4 req ID + 1 byte step + 2 byte error code + 8 byte fail data - assert_eq!(b.rep().allowed_source_data_len(), 15); - let fail_params = - FailParams::new(stamp_buf.as_slice(), &fail_code, Some(fail_data.as_slice())); - let res = b.helper.acceptance_failure(tok, fail_params); - assert!(res.is_err()); - let err_with_token = res.unwrap_err(); - assert_eq!(err_with_token.1, tok); - match err_with_token.0 { - EcssTmErrorWithSend::EcssTmError(EcssTmError::ByteConversionError(e)) => match e { - ByteConversionError::ToSliceTooSmall(missmatch) => { - assert_eq!( - missmatch.expected, - fail_data.len() + RequestId::SIZE_AS_BYTES + fail_code.byte_width() - ); - assert_eq!(missmatch.found, b.rep().allowed_source_data_len()); - } - _ => { - panic!("{}", format!("Unexpected error {:?}", e)) - } - }, - _ => { - panic!("{}", format!("Unexpected error {:?}", err_with_token.0)) - } - } - } - - #[test] - fn test_basic_acceptance_failure_with_fail_data() { - let (mut b, tok) = base_init(false); - let mut sender = TestSender::default(); - let fail_code = EcssEnumU8::new(10); - let fail_data = EcssEnumU32::new(12); - let mut fail_data_raw = [0; 4]; - fail_data.write_to_be_bytes(&mut fail_data_raw).unwrap(); - let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, Some(fail_data_raw.as_slice())); - b.vr.acceptance_failure(tok, &mut sender, fail_params) - .expect("Sending acceptance success failed"); - let cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 2, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some([10, 0, 0, 0, 12].to_vec()), - req_id: tok.req_id, - }; - assert_eq!(sender.service_queue.len(), 1); - let info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - } - - fn start_fail_check(sender: &mut TestSender, req_id: RequestId, fail_data_raw: [u8; 4]) { - assert_eq!(sender.service_queue.len(), 2); - let mut cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id, - }; - let mut info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 4, - apid: TEST_APID, - msg_counter: 1, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some([&[22], fail_data_raw.as_slice()].concat().to_vec()), - req_id, - }; - info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - } - - #[test] - fn test_start_failure() { - let (mut b, tok) = base_init(false); - let mut sender = TestSender::default(); - let fail_code = EcssEnumU8::new(22); - let fail_data: i32 = -12; - let mut fail_data_raw = [0; 4]; - fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); - let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, Some(fail_data_raw.as_slice())); - - let accepted_token = - b.vr.acceptance_success(tok, &mut sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let empty = - b.vr.start_failure(accepted_token, &mut sender, fail_params) - .expect("Start failure failure"); - assert_eq!(empty, ()); - start_fail_check(&mut sender, tok.req_id, fail_data_raw); - } - - #[test] - fn test_start_failure_with_helper() { - let (mut b, tok) = base_with_helper_init(); - let fail_code = EcssEnumU8::new(22); - let fail_data: i32 = -12; - let mut fail_data_raw = [0; 4]; - fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); - let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, Some(fail_data_raw.as_slice())); - - let accepted_token = b - .helper - .acceptance_success(tok, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let empty = b - .helper - .start_failure(accepted_token, fail_params) - .expect("Start failure failure"); - assert_eq!(empty, ()); - let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); - start_fail_check(sender, tok.req_id, fail_data_raw); - } - - fn step_success_check(sender: &mut TestSender, req_id: RequestId) { - let mut cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id, - }; - let mut info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 3, - apid: TEST_APID, - msg_counter: 1, - dest_id: 0, - time_stamp: [0, 1, 0, 1, 0, 1, 0], - }, - additional_data: None, - req_id, - }; - info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 5, - apid: TEST_APID, - msg_counter: 2, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some([0].to_vec()), - req_id, - }; - info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 5, - apid: TEST_APID, - msg_counter: 3, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some([1].to_vec()), - req_id, - }; - info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - } - - #[test] - fn test_steps_success() { - let (mut b, tok) = base_init(false); - let mut sender = TestSender::default(); - let accepted_token = b - .rep() - .acceptance_success(tok, &mut sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = b - .rep() - .start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - let mut empty = b - .rep() - .step_success( - &started_token, - &mut sender, - &EMPTY_STAMP, - EcssEnumU8::new(0), - ) - .expect("Sending step 0 success failed"); - assert_eq!(empty, ()); - empty = - b.vr.step_success( - &started_token, - &mut sender, - &EMPTY_STAMP, - EcssEnumU8::new(1), - ) - .expect("Sending step 1 success failed"); - assert_eq!(empty, ()); - assert_eq!(sender.service_queue.len(), 4); - step_success_check(&mut sender, tok.req_id); - } - - #[test] - fn test_steps_success_with_helper() { - let (mut b, tok) = base_with_helper_init(); - let accepted_token = b - .helper - .acceptance_success(tok, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = b - .helper - .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - let mut empty = b - .helper - .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0)) - .expect("Sending step 0 success failed"); - assert_eq!(empty, ()); - empty = b - .helper - .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(1)) - .expect("Sending step 1 success failed"); - assert_eq!(empty, ()); - let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); - assert_eq!(sender.service_queue.len(), 4); - step_success_check(sender, tok.req_id); - } - - fn check_step_failure(sender: &mut TestSender, req_id: RequestId, fail_data_raw: [u8; 4]) { - assert_eq!(sender.service_queue.len(), 4); - let mut cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id, - }; - let mut info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 3, - apid: TEST_APID, - msg_counter: 1, - dest_id: 0, - time_stamp: [0, 1, 0, 1, 0, 1, 0], - }, - additional_data: None, - req_id, - }; - info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 5, - apid: TEST_APID, - msg_counter: 2, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some([0].to_vec()), - req_id, - }; - info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 6, - apid: TEST_APID, - msg_counter: 3, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some( - [ - [1].as_slice(), - &[0, 0, 0x10, 0x20], - fail_data_raw.as_slice(), - ] - .concat() - .to_vec(), - ), - req_id, - }; - info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - } - - #[test] - fn test_step_failure() { - let (mut b, tok) = base_init(false); - let mut sender = TestSender::default(); - let req_id = tok.req_id; - let fail_code = EcssEnumU32::new(0x1020); - let fail_data: f32 = -22.3232; - let mut fail_data_raw = [0; 4]; - fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); - let fail_step = EcssEnumU8::new(1); - let fail_params = FailParamsWithStep::new( - &EMPTY_STAMP, - &fail_step, - &fail_code, - Some(fail_data_raw.as_slice()), - ); - - let accepted_token = - b.vr.acceptance_success(tok, &mut sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = - b.vr.start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - let mut empty = - b.vr.step_success( - &started_token, - &mut sender, - &EMPTY_STAMP, - EcssEnumU8::new(0), - ) - .expect("Sending completion success failed"); - assert_eq!(empty, ()); - empty = - b.vr.step_failure(started_token, &mut sender, fail_params) - .expect("Step failure failed"); - assert_eq!(empty, ()); - check_step_failure(&mut sender, req_id, fail_data_raw); - } - - #[test] - fn test_steps_failure_with_helper() { - let (mut b, tok) = base_with_helper_init(); - let req_id = tok.req_id; - let fail_code = EcssEnumU32::new(0x1020); - let fail_data: f32 = -22.3232; - let mut fail_data_raw = [0; 4]; - fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice()); - let fail_step = EcssEnumU8::new(1); - let fail_params = FailParamsWithStep::new( - &EMPTY_STAMP, - &fail_step, - &fail_code, - Some(fail_data_raw.as_slice()), - ); - - let accepted_token = b - .helper - .acceptance_success(tok, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = b - .helper - .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - let mut empty = b - .helper - .step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0)) - .expect("Sending completion success failed"); - assert_eq!(empty, ()); - empty = b - .helper - .step_failure(started_token, fail_params) - .expect("Step failure failed"); - assert_eq!(empty, ()); - let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); - check_step_failure(sender, req_id, fail_data_raw); - } - - fn completion_fail_check(sender: &mut TestSender, req_id: RequestId) { - assert_eq!(sender.service_queue.len(), 3); - - let mut cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id, - }; - let mut info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 3, - apid: TEST_APID, - msg_counter: 1, - dest_id: 0, - time_stamp: [0, 1, 0, 1, 0, 1, 0], - }, - additional_data: None, - req_id, - }; - info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - - cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 8, - apid: TEST_APID, - msg_counter: 2, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: Some([0, 0, 0x10, 0x20].to_vec()), - req_id, - }; - info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - } - - #[test] - fn test_completion_failure() { - let (mut b, tok) = base_init(false); - let mut sender = TestSender::default(); - let req_id = tok.req_id; - let fail_code = EcssEnumU32::new(0x1020); - let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, None); - - let accepted_token = - b.vr.acceptance_success(tok, &mut sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = - b.vr.start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - let empty = - b.vr.completion_failure(started_token, &mut sender, fail_params) - .expect("Completion failure"); - assert_eq!(empty, ()); - completion_fail_check(&mut sender, req_id); - } - - #[test] - fn test_completion_failure_with_helper() { - let (mut b, tok) = base_with_helper_init(); - let req_id = tok.req_id; - let fail_code = EcssEnumU32::new(0x1020); - let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, None); - - let accepted_token = b - .helper - .acceptance_success(tok, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = b - .helper - .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - let empty = b - .helper - .completion_failure(started_token, fail_params) - .expect("Completion failure"); - assert_eq!(empty, ()); - let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); - completion_fail_check(sender, req_id); - } - - fn completion_success_check(sender: &mut TestSender, req_id: RequestId) { - assert_eq!(sender.service_queue.len(), 3); - let cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 1, - apid: TEST_APID, - msg_counter: 0, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id, - }; - let mut info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - - let cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 3, - apid: TEST_APID, - msg_counter: 1, - dest_id: 0, - time_stamp: [0, 1, 0, 1, 0, 1, 0], - }, - additional_data: None, - req_id, - }; - info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - let cmp_info = TmInfo { - common: CommonTmInfo { - subservice: 7, - apid: TEST_APID, - msg_counter: 2, - dest_id: 0, - time_stamp: EMPTY_STAMP, - }, - additional_data: None, - req_id, - }; - info = sender.service_queue.pop_front().unwrap(); - assert_eq!(info, cmp_info); - } - - #[test] - fn test_complete_success_sequence() { - let (mut b, tok) = base_init(false); - let mut sender = TestSender::default(); - let accepted_token = - b.vr.acceptance_success(tok, &mut sender, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = - b.vr.start_success(accepted_token, &mut sender, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - let empty = - b.vr.completion_success(started_token, &mut sender, &EMPTY_STAMP) - .expect("Sending completion success failed"); - assert_eq!(empty, ()); - completion_success_check(&mut sender, tok.req_id); - } - - #[test] - fn test_complete_success_sequence_with_helper() { - let (mut b, tok) = base_with_helper_init(); - let accepted_token = b - .helper - .acceptance_success(tok, &EMPTY_STAMP) - .expect("Sending acceptance success failed"); - let started_token = b - .helper - .start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0]) - .expect("Sending start success failed"); - let empty = b - .helper - .completion_success(started_token, &EMPTY_STAMP) - .expect("Sending completion success failed"); - assert_eq!(empty, ()); - let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap(); - completion_success_check(sender, tok.req_id); - } -} diff --git a/satrs-core/src/res_code.rs b/satrs-core/src/res_code.rs deleted file mode 100644 index 9918ecf..0000000 --- a/satrs-core/src/res_code.rs +++ /dev/null @@ -1,53 +0,0 @@ -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use spacepackets::ecss::{EcssEnumU16, EcssEnumeration}; -use spacepackets::{ByteConversionError, SizeMissmatch}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct ResultU16 { - group_id: u8, - unique_id: u8, -} - -impl ResultU16 { - pub const fn const_new(group_id: u8, unique_id: u8) -> Self { - Self { - group_id, - 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 - } -} - -impl From for EcssEnumU16 { - fn from(v: ResultU16) -> Self { - EcssEnumU16::new(v.raw()) - } -} - -impl EcssEnumeration for ResultU16 { - fn pfc(&self) -> u8 { - 16 - } - - fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> { - if buf.len() < 2 { - return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch { - found: buf.len(), - expected: 2, - })); - } - buf[0] = self.group_id; - buf[1] = self.unique_id; - Ok(()) - } -} diff --git a/satrs-core/src/seq_count.rs b/satrs-core/src/seq_count.rs deleted file mode 100644 index 6871491..0000000 --- a/satrs-core/src/seq_count.rs +++ /dev/null @@ -1,121 +0,0 @@ -use core::cell::Cell; -use core::sync::atomic::{AtomicU16, Ordering}; -#[cfg(feature = "alloc")] -use dyn_clone::DynClone; -#[cfg(feature = "std")] -pub use stdmod::*; - -/// Core trait for objects which can provide a sequence count. -/// -/// The core functions are not mutable on purpose to allow easier usage with -/// static structs when using the interior mutability pattern. This can be achieved by using -/// [Cell], [RefCell] or atomic types. -pub trait SequenceCountProviderCore { - fn get(&self) -> Raw; - - fn increment(&self); - - // TODO: Maybe remove this? - fn increment_mut(&mut self) { - self.increment(); - } - - fn get_and_increment(&self) -> Raw { - let val = self.get(); - self.increment(); - val - } - - // TODO: Maybe remove this? - fn get_and_increment_mut(&mut self) -> Raw { - self.get_and_increment() - } -} - -/// Extension trait which allows cloning a sequence count provider after it was turned into -/// a trait object. -#[cfg(feature = "alloc")] -pub trait SequenceCountProvider: SequenceCountProviderCore + DynClone {} -#[cfg(feature = "alloc")] -dyn_clone::clone_trait_object!(SequenceCountProvider); -#[cfg(feature = "alloc")] -impl SequenceCountProvider for T where T: SequenceCountProviderCore + Clone {} - -#[derive(Default, Clone)] -pub struct SeqCountProviderSimple { - seq_count: Cell, -} - -impl SequenceCountProviderCore for SeqCountProviderSimple { - fn get(&self) -> u16 { - self.seq_count.get() - } - - fn increment(&self) { - self.get_and_increment(); - } - - fn get_and_increment(&self) -> u16 { - let curr_count = self.seq_count.get(); - - if curr_count == u16::MAX { - self.seq_count.set(0); - } else { - self.seq_count.set(curr_count + 1); - } - curr_count - } -} - -pub struct SeqCountProviderAtomicRef { - atomic: AtomicU16, - ordering: Ordering, -} - -impl SeqCountProviderAtomicRef { - pub const fn new(ordering: Ordering) -> Self { - Self { - atomic: AtomicU16::new(0), - ordering, - } - } -} - -impl SequenceCountProviderCore for SeqCountProviderAtomicRef { - fn get(&self) -> u16 { - self.atomic.load(self.ordering) - } - - fn increment(&self) { - self.atomic.fetch_add(1, self.ordering); - } - - fn get_and_increment(&self) -> u16 { - self.atomic.fetch_add(1, self.ordering) - } -} - -#[cfg(feature = "std")] -pub mod stdmod { - use super::*; - use std::sync::Arc; - - #[derive(Clone, Default)] - pub struct SeqCountProviderSyncClonable { - seq_count: Arc, - } - - impl SequenceCountProviderCore for SeqCountProviderSyncClonable { - fn get(&self) -> u16 { - self.seq_count.load(Ordering::SeqCst) - } - - fn increment(&self) { - self.seq_count.fetch_add(1, Ordering::SeqCst); - } - - fn get_and_increment(&self) -> u16 { - self.seq_count.fetch_add(1, Ordering::SeqCst) - } - } -} diff --git a/satrs-core/src/tmtc/ccsds_distrib.rs b/satrs-core/src/tmtc/ccsds_distrib.rs deleted file mode 100644 index 55fd2ec..0000000 --- a/satrs-core/src/tmtc/ccsds_distrib.rs +++ /dev/null @@ -1,333 +0,0 @@ -//! CCSDS packet routing components. -//! -//! The routing components consist of two core components: -//! 1. [CcsdsDistributor] component which dispatches received packets to a user-provided handler -//! 2. [CcsdsPacketHandler] trait which should be implemented by the user-provided packet handler. -//! -//! The [CcsdsDistributor] implements the [ReceivesCcsdsTc] and [ReceivesTcCore] trait which allows to -//! pass raw or CCSDS packets to it. Upon receiving a packet, it performs the following steps: -//! -//! 1. It tries to identify the target Application Process Identifier (APID) based on the -//! respective CCSDS space packet header field. If that process fails, a [ByteConversionError] is -//! returned to the user -//! 2. If a valid APID is found and matches one of the APIDs provided by -//! [CcsdsPacketHandler::valid_apids], it will pass the packet to the user provided -//! [CcsdsPacketHandler::handle_known_apid] function. If no valid APID is found, the packet -//! will be passed to the [CcsdsPacketHandler::handle_unknown_apid] function. -//! -//! # Example -//! -//! ```rust -//! use satrs_core::tmtc::ccsds_distrib::{CcsdsPacketHandler, CcsdsDistributor}; -//! use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore}; -//! use spacepackets::{CcsdsPacket, SpHeader}; -//! use spacepackets::tc::PusTc; -//! -//! #[derive (Default)] -//! struct ConcreteApidHandler { -//! known_call_count: u32, -//! unknown_call_count: u32 -//! } -//! -//! impl ConcreteApidHandler { -//! fn mutable_foo(&mut self) {} -//! } -//! -//! impl CcsdsPacketHandler for ConcreteApidHandler { -//! type Error = (); -//! fn valid_apids(&self) -> &'static [u16] { &[0x002] } -//! fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> { -//! assert_eq!(sp_header.apid(), 0x002); -//! assert_eq!(tc_raw.len(), 13); -//! self.known_call_count += 1; -//! Ok(()) -//! } -//! fn handle_unknown_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> { -//! assert_eq!(sp_header.apid(), 0x003); -//! assert_eq!(tc_raw.len(), 13); -//! self.unknown_call_count += 1; -//! Ok(()) -//! } -//! } -//! -//! let apid_handler = ConcreteApidHandler::default(); -//! let mut ccsds_distributor = CcsdsDistributor::new(Box::new(apid_handler)); -//! -//! // Create and pass PUS telecommand with a valid APID -//! let mut space_packet_header = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap(); -//! let mut pus_tc = PusTc::new_simple(&mut space_packet_header, 17, 1, None, true); -//! let mut test_buf: [u8; 32] = [0; 32]; -//! let mut size = pus_tc -//! .write_to_bytes(test_buf.as_mut_slice()) -//! .expect("Error writing TC to buffer"); -//! let tc_slice = &test_buf[0..size]; -//! ccsds_distributor.pass_tc(&tc_slice).expect("Passing TC slice failed"); -//! -//! // Now pass a packet with an unknown APID to the distributor -//! pus_tc.set_apid(0x003); -//! size = pus_tc -//! .write_to_bytes(test_buf.as_mut_slice()) -//! .expect("Error writing TC to buffer"); -//! let tc_slice = &test_buf[0..size]; -//! ccsds_distributor.pass_tc(&tc_slice).expect("Passing TC slice failed"); -//! -//! // User helper function to retrieve concrete class -//! let concrete_handler_ref: &ConcreteApidHandler = ccsds_distributor -//! .apid_handler_ref() -//! .expect("Casting back to concrete type failed"); -//! assert_eq!(concrete_handler_ref.known_call_count, 1); -//! assert_eq!(concrete_handler_ref.unknown_call_count, 1); -//! -//! // It's also possible to retrieve a mutable reference -//! let mutable_ref: &mut ConcreteApidHandler = ccsds_distributor -//! .apid_handler_mut() -//! .expect("Casting back to concrete type failed"); -//! mutable_ref.mutable_foo(); -//! ``` -use crate::tmtc::{ReceivesCcsdsTc, ReceivesTcCore}; -use alloc::boxed::Box; -use downcast_rs::Downcast; -use spacepackets::{ByteConversionError, CcsdsPacket, SizeMissmatch, SpHeader}; - -/// Generic trait for a handler or dispatcher object handling CCSDS packets. -/// -/// Users should implement this trait on their custom CCSDS packet handler and then pass a boxed -/// instance of this handler to the [CcsdsDistributor]. The distributor will use the trait -/// interface to dispatch received packets to the user based on the Application Process Identifier -/// (APID) field of the CCSDS packet. -/// -/// This trait automatically implements the [downcast_rs::Downcast] to allow a more convenient API -/// to cast trait objects back to their concrete type after the handler was passed to the -/// distributor. -pub trait CcsdsPacketHandler: Downcast + Send { - type Error; - - fn valid_apids(&self) -> &'static [u16]; - fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) - -> Result<(), Self::Error>; - fn handle_unknown_apid( - &mut self, - sp_header: &SpHeader, - tc_raw: &[u8], - ) -> Result<(), Self::Error>; -} - -downcast_rs::impl_downcast!(CcsdsPacketHandler assoc Error); - -/// The CCSDS distributor dispatches received CCSDS packets to a user provided packet handler. -pub struct CcsdsDistributor { - /// User provided APID handler stored as a generic trait object. - /// It can be cast back to the original concrete type using the [Self::apid_handler_ref] or - /// the [Self::apid_handler_mut] method. - pub apid_handler: Box>, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum CcsdsError { - CustomError(E), - PacketError(ByteConversionError), -} - -impl ReceivesCcsdsTc for CcsdsDistributor { - type Error = CcsdsError; - - fn pass_ccsds(&mut self, header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> { - self.dispatch_ccsds(header, tc_raw) - } -} - -impl ReceivesTcCore for CcsdsDistributor { - type Error = CcsdsError; - - fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> { - if tc_raw.len() < 7 { - return Err(CcsdsError::PacketError( - ByteConversionError::FromSliceTooSmall(SizeMissmatch { - found: tc_raw.len(), - expected: 7, - }), - )); - } - let (sp_header, _) = - SpHeader::from_be_bytes(tc_raw).map_err(|e| CcsdsError::PacketError(e))?; - self.dispatch_ccsds(&sp_header, tc_raw) - } -} - -impl CcsdsDistributor { - pub fn new(apid_handler: Box>) -> Self { - CcsdsDistributor { apid_handler } - } - - /// This function can be used to retrieve a reference to the concrete instance of the APID - /// handler after it was passed to the distributor. See the - /// [module documentation][crate::tmtc::ccsds_distrib] for an fsrc-example. - pub fn apid_handler_ref>(&self) -> Option<&T> { - self.apid_handler.downcast_ref::() - } - - /// This function can be used to retrieve a mutable reference to the concrete instance of the - /// APID handler after it was passed to the distributor. - pub fn apid_handler_mut>(&mut self) -> Option<&mut T> { - self.apid_handler.downcast_mut::() - } - - fn dispatch_ccsds(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), CcsdsError> { - let apid = sp_header.apid(); - let valid_apids = self.apid_handler.valid_apids(); - for &valid_apid in valid_apids { - if valid_apid == apid { - return self - .apid_handler - .handle_known_apid(sp_header, tc_raw) - .map_err(|e| CcsdsError::CustomError(e)); - } - } - self.apid_handler - .handle_unknown_apid(sp_header, tc_raw) - .map_err(|e| CcsdsError::CustomError(e)) - } -} - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use crate::tmtc::ccsds_distrib::{CcsdsDistributor, CcsdsPacketHandler}; - use spacepackets::tc::PusTc; - use spacepackets::CcsdsPacket; - use std::collections::VecDeque; - use std::sync::{Arc, Mutex}; - use std::vec::Vec; - - fn is_send(_: &T) {} - - pub fn generate_ping_tc(buf: &mut [u8]) -> &[u8] { - let mut sph = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap(); - let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true); - let size = pus_tc - .write_to_bytes(buf) - .expect("Error writing TC to buffer"); - assert_eq!(size, 13); - &buf[0..size] - } - - pub struct BasicApidHandlerSharedQueue { - pub known_packet_queue: Arc)>>>, - pub unknown_packet_queue: Arc)>>>, - } - - #[derive(Default)] - pub struct BasicApidHandlerOwnedQueue { - pub known_packet_queue: VecDeque<(u16, Vec)>, - pub unknown_packet_queue: VecDeque<(u16, Vec)>, - } - - impl CcsdsPacketHandler for BasicApidHandlerSharedQueue { - type Error = (); - fn valid_apids(&self) -> &'static [u16] { - &[0x000, 0x002] - } - - fn handle_known_apid( - &mut self, - sp_header: &SpHeader, - tc_raw: &[u8], - ) -> Result<(), Self::Error> { - let mut vec = Vec::new(); - vec.extend_from_slice(tc_raw); - Ok(self - .known_packet_queue - .lock() - .unwrap() - .push_back((sp_header.apid(), vec))) - } - - fn handle_unknown_apid( - &mut self, - sp_header: &SpHeader, - tc_raw: &[u8], - ) -> Result<(), Self::Error> { - let mut vec = Vec::new(); - vec.extend_from_slice(tc_raw); - Ok(self - .unknown_packet_queue - .lock() - .unwrap() - .push_back((sp_header.apid(), vec))) - } - } - - impl CcsdsPacketHandler for BasicApidHandlerOwnedQueue { - type Error = (); - - fn valid_apids(&self) -> &'static [u16] { - &[0x000, 0x002] - } - - fn handle_known_apid( - &mut self, - sp_header: &SpHeader, - tc_raw: &[u8], - ) -> Result<(), Self::Error> { - let mut vec = Vec::new(); - vec.extend_from_slice(tc_raw); - Ok(self.known_packet_queue.push_back((sp_header.apid(), vec))) - } - - fn handle_unknown_apid( - &mut self, - sp_header: &SpHeader, - tc_raw: &[u8], - ) -> Result<(), Self::Error> { - let mut vec = Vec::new(); - vec.extend_from_slice(tc_raw); - Ok(self.unknown_packet_queue.push_back((sp_header.apid(), vec))) - } - } - - #[test] - fn test_distribs_known_apid() { - let known_packet_queue = Arc::new(Mutex::default()); - let unknown_packet_queue = Arc::new(Mutex::default()); - let apid_handler = BasicApidHandlerSharedQueue { - known_packet_queue: known_packet_queue.clone(), - unknown_packet_queue: unknown_packet_queue.clone(), - }; - let mut ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler)); - is_send(&ccsds_distrib); - let mut test_buf: [u8; 32] = [0; 32]; - let tc_slice = generate_ping_tc(test_buf.as_mut_slice()); - - ccsds_distrib.pass_tc(tc_slice).expect("Passing TC failed"); - let recvd = known_packet_queue.lock().unwrap().pop_front(); - assert!(unknown_packet_queue.lock().unwrap().is_empty()); - assert!(recvd.is_some()); - let (apid, packet) = recvd.unwrap(); - assert_eq!(apid, 0x002); - assert_eq!(packet, tc_slice); - } - - #[test] - fn test_distribs_unknown_apid() { - let known_packet_queue = Arc::new(Mutex::default()); - let unknown_packet_queue = Arc::new(Mutex::default()); - let apid_handler = BasicApidHandlerSharedQueue { - known_packet_queue: known_packet_queue.clone(), - unknown_packet_queue: unknown_packet_queue.clone(), - }; - let mut ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler)); - let mut sph = SpHeader::tc_unseg(0x004, 0x34, 0).unwrap(); - let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true); - let mut test_buf: [u8; 32] = [0; 32]; - pus_tc - .write_to_bytes(test_buf.as_mut_slice()) - .expect("Error writing TC to buffer"); - ccsds_distrib.pass_tc(&test_buf).expect("Passing TC failed"); - let recvd = unknown_packet_queue.lock().unwrap().pop_front(); - assert!(known_packet_queue.lock().unwrap().is_empty()); - assert!(recvd.is_some()); - let (apid, packet) = recvd.unwrap(); - assert_eq!(apid, 0x004); - assert_eq!(packet.as_slice(), test_buf); - } -} diff --git a/satrs-core/src/tmtc/mod.rs b/satrs-core/src/tmtc/mod.rs deleted file mode 100644 index 1ae4ac8..0000000 --- a/satrs-core/src/tmtc/mod.rs +++ /dev/null @@ -1,100 +0,0 @@ -//! Telemetry and Telecommanding (TMTC) module. Contains packet routing components with special -//! support for CCSDS and ECSS packets. -//! -//! The distributor modules provided by this module use trait objects provided by the user to -//! directly dispatch received packets to packet listeners based on packet fields like the CCSDS -//! Application Process ID (APID) or the ECSS PUS service type. This allows for fast packet -//! routing without the overhead and complication of using message queues. However, it also requires -#[cfg(feature = "alloc")] -use downcast_rs::{impl_downcast, Downcast}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use spacepackets::tc::PusTc; -use spacepackets::{ByteConversionError, SizeMissmatch, SpHeader}; - -#[cfg(feature = "alloc")] -pub mod ccsds_distrib; -#[cfg(feature = "alloc")] -pub mod pus_distrib; -pub mod tm_helper; - -#[cfg(feature = "alloc")] -pub use ccsds_distrib::{CcsdsDistributor, CcsdsError, CcsdsPacketHandler}; -#[cfg(feature = "alloc")] -pub use pus_distrib::{PusDistributor, PusServiceProvider}; - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct AddressableId { - pub target_id: u32, - pub unique_id: u32, -} - -impl AddressableId { - pub fn from_raw_be(buf: &[u8]) -> Result { - if buf.len() < 8 { - return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { - found: buf.len(), - expected: 8, - })); - } - Ok(Self { - target_id: u32::from_be_bytes(buf[0..4].try_into().unwrap()), - unique_id: u32::from_be_bytes(buf[4..8].try_into().unwrap()), - }) - } - - pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { - if buf.len() < 8 { - return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch { - found: buf.len(), - expected: 8, - })); - } - buf[0..4].copy_from_slice(&self.target_id.to_be_bytes()); - buf[4..8].copy_from_slice(&self.unique_id.to_be_bytes()); - Ok(8) - } -} - -/// Generic trait for object which can receive any telecommands in form of a raw bytestream, with -/// no assumptions about the received protocol. -/// -/// This trait is implemented by both the [crate::tmtc::pus_distrib::PusDistributor] and the -/// [crate::tmtc::ccsds_distrib::CcsdsDistributor] which allows to pass the respective packets in -/// raw byte format into them. -pub trait ReceivesTcCore: Send { - type Error; - fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error>; -} - -/// Extension trait of [ReceivesTcCore] which allows downcasting by implementing [Downcast] -#[cfg(feature = "alloc")] -pub trait ReceivesTc: ReceivesTcCore + Downcast {} - -/// Blanket implementation to automatically implement [ReceivesTc] when the [alloc] feature -/// is enabled. -#[cfg(feature = "alloc")] -impl ReceivesTc for T where T: ReceivesTcCore + 'static {} - -#[cfg(feature = "alloc")] -impl_downcast!(ReceivesTc assoc Error); - -/// Generic trait for object which can receive CCSDS space packets, for example ECSS PUS packets -/// for CCSDS File Delivery Protocol (CFDP) packets. -/// -/// This trait is implemented by both the [crate::tmtc::pus_distrib::PusDistributor] and the -/// [crate::tmtc::ccsds_distrib::CcsdsDistributor] which allows -/// to pass the respective packets in raw byte format or in CCSDS format into them. -pub trait ReceivesCcsdsTc { - type Error; - fn pass_ccsds(&mut self, header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error>; -} - -/// Generic trait for objects which can receive ECSS PUS telecommands. This trait is -/// implemented by the [crate::tmtc::pus_distrib::PusDistributor] objects to allow passing PUS TC -/// packets into it. -pub trait ReceivesEcssPusTc { - type Error; - fn pass_pus_tc(&mut self, header: &SpHeader, pus_tc: &PusTc) -> Result<(), Self::Error>; -} diff --git a/satrs-core/src/tmtc/pus_distrib.rs b/satrs-core/src/tmtc/pus_distrib.rs deleted file mode 100644 index 6e457ac..0000000 --- a/satrs-core/src/tmtc/pus_distrib.rs +++ /dev/null @@ -1,333 +0,0 @@ -//! ECSS PUS packet routing components. -//! -//! The routing components consist of two core components: -//! 1. [PusDistributor] component which dispatches received packets to a user-provided handler. -//! 2. [PusServiceProvider] trait which should be implemented by the user-provided PUS packet -//! handler. -//! -//! The [PusDistributor] implements the [ReceivesEcssPusTc], [ReceivesCcsdsTc] and the -//! [ReceivesTcCore] trait which allows to pass raw packets, CCSDS packets and PUS TC packets into -//! it. Upon receiving a packet, it performs the following steps: -//! -//! 1. It tries to extract the [SpHeader] and [PusTc] objects from the raw bytestream. If this -//! process fails, a [PusDistribError::PusError] is returned to the user. -//! 2. If it was possible to extract both components, the packet will be passed to the -//! [PusServiceProvider::handle_pus_tc_packet] method provided by the user. -//! -//! # Example -//! -//! ```rust -//! use satrs_core::tmtc::pus_distrib::{PusDistributor, PusServiceProvider}; -//! use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore}; -//! use spacepackets::SpHeader; -//! use spacepackets::tc::PusTc; -//! struct ConcretePusHandler { -//! handler_call_count: u32 -//! } -//! -//! // This is a very simple possible service provider. It increments an internal call count field, -//! // which is used to verify the handler was called -//! impl PusServiceProvider for ConcretePusHandler { -//! type Error = (); -//! fn handle_pus_tc_packet(&mut self, service: u8, header: &SpHeader, pus_tc: &PusTc) -> Result<(), Self::Error> { -//! assert_eq!(service, 17); -//! assert_eq!(pus_tc.len_packed(), 13); -//! self.handler_call_count += 1; -//! Ok(()) -//! } -//! } -//! -//! let service_handler = ConcretePusHandler { -//! handler_call_count: 0 -//! }; -//! let mut pus_distributor = PusDistributor::new(Box::new(service_handler)); -//! -//! // Create and pass PUS ping telecommand with a valid APID -//! let mut space_packet_header = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap(); -//! let mut pus_tc = PusTc::new_simple(&mut space_packet_header, 17, 1, None, true); -//! let mut test_buf: [u8; 32] = [0; 32]; -//! let mut size = pus_tc -//! .write_to_bytes(test_buf.as_mut_slice()) -//! .expect("Error writing TC to buffer"); -//! let tc_slice = &test_buf[0..size]; -//! -//! pus_distributor.pass_tc(tc_slice).expect("Passing PUS telecommand failed"); -//! -//! // User helper function to retrieve concrete class. We check the call count here to verify -//! // that the PUS ping telecommand was routed successfully. -//! let concrete_handler_ref: &ConcretePusHandler = pus_distributor -//! .service_provider_ref() -//! .expect("Casting back to concrete type failed"); -//! assert_eq!(concrete_handler_ref.handler_call_count, 1); -//! ``` -use crate::tmtc::{ReceivesCcsdsTc, ReceivesEcssPusTc, ReceivesTcCore}; -use alloc::boxed::Box; -use downcast_rs::Downcast; -use spacepackets::ecss::{PusError, PusPacket}; -use spacepackets::tc::PusTc; -use spacepackets::SpHeader; - -pub trait PusServiceProvider: Downcast + Send { - type Error; - fn handle_pus_tc_packet( - &mut self, - service: u8, - header: &SpHeader, - pus_tc: &PusTc, - ) -> Result<(), Self::Error>; -} -downcast_rs::impl_downcast!(PusServiceProvider assoc Error); - -pub struct PusDistributor { - pub service_provider: Box>, -} - -impl PusDistributor { - pub fn new(service_provider: Box>) -> Self { - PusDistributor { service_provider } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum PusDistribError { - CustomError(E), - PusError(PusError), -} - -impl ReceivesTcCore for PusDistributor { - type Error = PusDistribError; - fn pass_tc(&mut self, tm_raw: &[u8]) -> Result<(), Self::Error> { - // Convert to ccsds and call pass_ccsds - let (sp_header, _) = SpHeader::from_be_bytes(tm_raw) - .map_err(|e| PusDistribError::PusError(PusError::ByteConversionError(e)))?; - self.pass_ccsds(&sp_header, tm_raw) - } -} - -impl ReceivesCcsdsTc for PusDistributor { - type Error = PusDistribError; - fn pass_ccsds(&mut self, header: &SpHeader, tm_raw: &[u8]) -> Result<(), Self::Error> { - let (tc, _) = PusTc::from_bytes(tm_raw).map_err(|e| PusDistribError::PusError(e))?; - self.pass_pus_tc(header, &tc) - } -} - -impl ReceivesEcssPusTc for PusDistributor { - type Error = PusDistribError; - fn pass_pus_tc(&mut self, header: &SpHeader, pus_tc: &PusTc) -> Result<(), Self::Error> { - self.service_provider - .handle_pus_tc_packet(pus_tc.service(), header, pus_tc) - .map_err(|e| PusDistribError::CustomError(e)) - } -} - -impl PusDistributor { - pub fn service_provider_ref>(&self) -> Option<&T> { - self.service_provider.downcast_ref::() - } - - pub fn service_provider_mut>(&mut self) -> Option<&mut T> { - self.service_provider.downcast_mut::() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::tmtc::ccsds_distrib::tests::{ - generate_ping_tc, BasicApidHandlerOwnedQueue, BasicApidHandlerSharedQueue, - }; - use crate::tmtc::ccsds_distrib::{CcsdsDistributor, CcsdsPacketHandler}; - use alloc::vec::Vec; - use spacepackets::ecss::PusError; - use spacepackets::tc::PusTc; - use spacepackets::CcsdsPacket; - #[cfg(feature = "std")] - use std::collections::VecDeque; - #[cfg(feature = "std")] - use std::sync::{Arc, Mutex}; - - fn is_send(_: &T) {} - - struct PusHandlerSharedQueue { - pub pus_queue: Arc)>>>, - } - - #[derive(Default)] - struct PusHandlerOwnedQueue { - pub pus_queue: VecDeque<(u8, u16, Vec)>, - } - - impl PusServiceProvider for PusHandlerSharedQueue { - type Error = PusError; - fn handle_pus_tc_packet( - &mut self, - service: u8, - sp_header: &SpHeader, - pus_tc: &PusTc, - ) -> Result<(), Self::Error> { - let mut vec: Vec = Vec::new(); - pus_tc.append_to_vec(&mut vec)?; - Ok(self - .pus_queue - .lock() - .expect("Mutex lock failed") - .push_back((service, sp_header.apid(), vec))) - } - } - - impl PusServiceProvider for PusHandlerOwnedQueue { - type Error = PusError; - fn handle_pus_tc_packet( - &mut self, - service: u8, - sp_header: &SpHeader, - pus_tc: &PusTc, - ) -> Result<(), Self::Error> { - let mut vec: Vec = Vec::new(); - pus_tc.append_to_vec(&mut vec)?; - Ok(self.pus_queue.push_back((service, sp_header.apid(), vec))) - } - } - - struct ApidHandlerShared { - pub pus_distrib: PusDistributor, - pub handler_base: BasicApidHandlerSharedQueue, - } - - struct ApidHandlerOwned { - pub pus_distrib: PusDistributor, - handler_base: BasicApidHandlerOwnedQueue, - } - - macro_rules! apid_handler_impl { - () => { - type Error = PusError; - - fn valid_apids(&self) -> &'static [u16] { - &[0x000, 0x002] - } - - fn handle_known_apid( - &mut self, - sp_header: &SpHeader, - tc_raw: &[u8], - ) -> Result<(), Self::Error> { - self.handler_base - .handle_known_apid(&sp_header, tc_raw) - .ok() - .expect("Unexpected error"); - match self.pus_distrib.pass_ccsds(&sp_header, tc_raw) { - Ok(_) => Ok(()), - Err(e) => match e { - PusDistribError::CustomError(_) => Ok(()), - PusDistribError::PusError(e) => Err(e), - }, - } - } - - fn handle_unknown_apid( - &mut self, - sp_header: &SpHeader, - tc_raw: &[u8], - ) -> Result<(), Self::Error> { - self.handler_base - .handle_unknown_apid(&sp_header, tc_raw) - .ok() - .expect("Unexpected error"); - Ok(()) - } - }; - } - - impl CcsdsPacketHandler for ApidHandlerOwned { - apid_handler_impl!(); - } - - impl CcsdsPacketHandler for ApidHandlerShared { - apid_handler_impl!(); - } - - #[test] - #[cfg(feature = "std")] - fn test_pus_distribution() { - let known_packet_queue = Arc::new(Mutex::default()); - let unknown_packet_queue = Arc::new(Mutex::default()); - let pus_queue = Arc::new(Mutex::default()); - let pus_handler = PusHandlerSharedQueue { - pus_queue: pus_queue.clone(), - }; - let handler_base = BasicApidHandlerSharedQueue { - known_packet_queue: known_packet_queue.clone(), - unknown_packet_queue: unknown_packet_queue.clone(), - }; - - let pus_distrib = PusDistributor { - service_provider: Box::new(pus_handler), - }; - is_send(&pus_distrib); - let apid_handler = ApidHandlerShared { - pus_distrib, - handler_base, - }; - let mut ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler)); - let mut test_buf: [u8; 32] = [0; 32]; - let tc_slice = generate_ping_tc(test_buf.as_mut_slice()); - - // Pass packet to distributor - ccsds_distrib - .pass_tc(tc_slice) - .expect("Passing TC slice failed"); - let recvd_ccsds = known_packet_queue.lock().unwrap().pop_front(); - assert!(unknown_packet_queue.lock().unwrap().is_empty()); - assert!(recvd_ccsds.is_some()); - let (apid, packet) = recvd_ccsds.unwrap(); - assert_eq!(apid, 0x002); - assert_eq!(packet.as_slice(), tc_slice); - let recvd_pus = pus_queue.lock().unwrap().pop_front(); - assert!(recvd_pus.is_some()); - let (service, apid, tc_raw) = recvd_pus.unwrap(); - assert_eq!(service, 17); - assert_eq!(apid, 0x002); - assert_eq!(tc_raw, tc_slice); - } - - #[test] - fn test_as_any_cast() { - let pus_handler = PusHandlerOwnedQueue::default(); - let handler_base = BasicApidHandlerOwnedQueue::default(); - let pus_distrib = PusDistributor { - service_provider: Box::new(pus_handler), - }; - - let apid_handler = ApidHandlerOwned { - pus_distrib, - handler_base, - }; - let mut ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler)); - - let mut test_buf: [u8; 32] = [0; 32]; - let tc_slice = generate_ping_tc(test_buf.as_mut_slice()); - - ccsds_distrib - .pass_tc(tc_slice) - .expect("Passing TC slice failed"); - - let apid_handler_casted_back: &mut ApidHandlerOwned = ccsds_distrib - .apid_handler_mut() - .expect("Cast to concrete type ApidHandler failed"); - assert!(!apid_handler_casted_back - .handler_base - .known_packet_queue - .is_empty()); - let handler_casted_back: &mut PusHandlerOwnedQueue = apid_handler_casted_back - .pus_distrib - .service_provider_mut() - .expect("Cast to concrete type PusHandlerOwnedQueue failed"); - assert!(!handler_casted_back.pus_queue.is_empty()); - let (service, apid, packet_raw) = handler_casted_back.pus_queue.pop_front().unwrap(); - assert_eq!(service, 17); - assert_eq!(apid, 0x002); - assert_eq!(packet_raw.as_slice(), tc_slice); - } -} diff --git a/satrs-core/src/tmtc/tm_helper.rs b/satrs-core/src/tmtc/tm_helper.rs deleted file mode 100644 index 88deea7..0000000 --- a/satrs-core/src/tmtc/tm_helper.rs +++ /dev/null @@ -1,52 +0,0 @@ -use spacepackets::time::cds::TimeProvider; -use spacepackets::time::TimeWriter; -use spacepackets::tm::{PusTm, PusTmSecondaryHeader}; -use spacepackets::SpHeader; - -pub struct PusTmWithCdsShortHelper { - apid: u16, - cds_short_buf: [u8; 7], -} - -impl PusTmWithCdsShortHelper { - pub fn new(apid: u16) -> Self { - Self { - apid, - cds_short_buf: [0; 7], - } - } - - #[cfg(feature = "std")] - pub fn create_pus_tm_timestamp_now<'a>( - &'a mut self, - service: u8, - subservice: u8, - source_data: Option<&'a [u8]>, - ) -> PusTm { - let time_stamp = TimeProvider::from_now_with_u16_days().unwrap(); - time_stamp.write_to_bytes(&mut self.cds_short_buf).unwrap(); - self.create_pus_tm_common(service, subservice, source_data) - } - - pub fn create_pus_tm_with_stamp<'a>( - &'a mut self, - service: u8, - subservice: u8, - source_data: Option<&'a [u8]>, - stamper: &TimeProvider, - ) -> PusTm { - stamper.write_to_bytes(&mut self.cds_short_buf).unwrap(); - self.create_pus_tm_common(service, subservice, source_data) - } - - fn create_pus_tm_common<'a>( - &'a self, - service: u8, - subservice: u8, - source_data: Option<&'a [u8]>, - ) -> PusTm { - let mut reply_header = SpHeader::tm_unseg(self.apid, 0, 0).unwrap(); - let tc_header = PusTmSecondaryHeader::new_simple(service, subservice, &self.cds_short_buf); - PusTm::new(&mut reply_header, tc_header, source_data, true) - } -} diff --git a/satrs-core/tests/hk_helpers.rs b/satrs-core/tests/hk_helpers.rs deleted file mode 100644 index 07264ef..0000000 --- a/satrs-core/tests/hk_helpers.rs +++ /dev/null @@ -1,164 +0,0 @@ -#![allow(dead_code)] -use core::mem::size_of; -use serde::{Deserialize, Serialize}; -use spacepackets::ecss::{Ptc, RealPfc, UnsignedPfc}; -use spacepackets::time::cds::TimeProvider; -use spacepackets::time::{CcsdsTimeProvider, TimeWriter}; - -enum NumOfParamsInfo { - /// The parameter entry is a scalar field - Scalar = 0b00, - /// The parameter entry is a vector, and its length field is one byte wide (max. 255 entries) - VecLenFieldOneByte = 0b01, - /// The parameter entry is a vecotr, and its length field is two bytes wide (max. 65565 entries) - VecLenFieldTwoBytes = 0b10, - /// The parameter entry is a matrix, and its length field contains a one byte row number - /// and a one byte column number. - MatrixRowsAndColumns = 0b11, -} - -const HAS_VALIDITY_MASK: u8 = 1 << 7; - -struct ParamWithValidity { - valid: bool, - val: T, -} - -struct TestMgmHk { - temp: f32, - mgm_vals: [u16; 3], -} - -struct TestMgmHkWithIndividualValidity { - temp: ParamWithValidity, - mgm_vals: ParamWithValidity<[u16; 3]>, -} - -#[derive(Serialize, Deserialize)] -struct TestMgmHkWithGroupValidity { - last_valid_stamp: TimeProvider, - valid: bool, - temp: f32, - mgm_vals: [u16; 3], -} - -impl TestMgmHk { - pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { - let mut curr_idx = 0; - buf[curr_idx..curr_idx + size_of::()].copy_from_slice(&self.temp.to_be_bytes()); - curr_idx += size_of::(); - for val in self.mgm_vals { - buf[curr_idx..curr_idx + size_of::()].copy_from_slice(&val.to_be_bytes()); - curr_idx += size_of::(); - } - Ok(curr_idx) - } -} - -/// This could in principle be auto-generated. -impl TestMgmHkWithIndividualValidity { - pub fn write_to_be_bytes_self_describing(&self, buf: &mut [u8]) -> Result { - let mut curr_idx = 0; - buf[curr_idx] = 0; - buf[curr_idx] |= HAS_VALIDITY_MASK | (self.temp.valid as u8) << 6; - curr_idx += 1; - buf[curr_idx] = Ptc::Real as u8; - curr_idx += 1; - buf[curr_idx] = RealPfc::Float as u8; - curr_idx += 1; - buf[curr_idx..curr_idx + size_of::()].copy_from_slice(&self.temp.val.to_be_bytes()); - curr_idx += size_of::(); - buf[curr_idx] = 0; - buf[curr_idx] |= HAS_VALIDITY_MASK - | (self.mgm_vals.valid as u8) << 6 - | (NumOfParamsInfo::VecLenFieldOneByte as u8) << 4; - curr_idx += 1; - buf[curr_idx] = Ptc::UnsignedInt as u8; - curr_idx += 1; - buf[curr_idx] = UnsignedPfc::TwoBytes as u8; - curr_idx += 1; - buf[curr_idx] = 3; - curr_idx += 1; - for val in self.mgm_vals.val { - buf[curr_idx..curr_idx + size_of::()].copy_from_slice(&val.to_be_bytes()); - curr_idx += size_of::(); - } - Ok(curr_idx) - } -} - -impl TestMgmHkWithGroupValidity { - pub fn write_to_be_bytes_self_describing(&self, buf: &mut [u8]) -> Result { - let mut curr_idx = 0; - buf[curr_idx] = self.valid as u8; - curr_idx += 1; - self.last_valid_stamp - .write_to_bytes(&mut buf[curr_idx..curr_idx + self.last_valid_stamp.len_as_bytes()]) - .unwrap(); - curr_idx += self.last_valid_stamp.len_as_bytes(); - buf[curr_idx] = 0; - curr_idx += 1; - buf[curr_idx] = Ptc::Real as u8; - curr_idx += 1; - buf[curr_idx] = RealPfc::Float as u8; - curr_idx += 1; - buf[curr_idx..curr_idx + size_of::()].copy_from_slice(&self.temp.to_be_bytes()); - curr_idx += size_of::(); - buf[curr_idx] = 0; - buf[curr_idx] |= (NumOfParamsInfo::VecLenFieldOneByte as u8) << 4; - curr_idx += 1; - buf[curr_idx] = Ptc::UnsignedInt as u8; - curr_idx += 1; - buf[curr_idx] = UnsignedPfc::TwoBytes as u8; - curr_idx += 1; - buf[curr_idx] = 3; - for val in self.mgm_vals { - buf[curr_idx..curr_idx + size_of::()].copy_from_slice(&val.to_be_bytes()); - curr_idx += size_of::(); - } - Ok(curr_idx) - } -} - -#[test] -pub fn main() { - let mut raw_buf: [u8; 32] = [0; 32]; - let mgm_hk = TestMgmHk { - temp: 20.0, - mgm_vals: [0x1f1f, 0x2f2f, 0x3f3f], - }; - // 4 byte float + 3 * 2 bytes MGM values - let written = mgm_hk.write_to_be_bytes(&mut raw_buf).unwrap(); - assert_eq!(written, 10); - - let mgm_hk_individual_validity = TestMgmHkWithIndividualValidity { - temp: ParamWithValidity { - valid: true, - val: 20.0, - }, - mgm_vals: ParamWithValidity { - valid: true, - val: [0x1f1f, 0x2f2f, 0x3f3f], - }, - }; - let written = mgm_hk_individual_validity - .write_to_be_bytes_self_describing(&mut raw_buf) - .unwrap(); - // 3 byte float description, 4 byte float, 4 byte MGM val description, 3 * 2 bytes MGM values - assert_eq!(written, 17); - - // The easiest and probably best approach, trading off big advantages for TM downlink capacity: - // Use a JSON format - let mgm_hk_group_validity = TestMgmHkWithGroupValidity { - last_valid_stamp: TimeProvider::from_now_with_u16_days().unwrap(), - valid: false, - temp: 20.0, - mgm_vals: [0x1f1f, 0x2f2f, 0x3f3f], - }; - let mgm_as_json_str = serde_json::to_string(&mgm_hk_group_validity).unwrap(); - println!( - "JSON string with length {}: {}", - mgm_as_json_str.len(), - mgm_as_json_str - ); -} diff --git a/satrs-core/tests/pools.rs b/satrs-core/tests/pools.rs deleted file mode 100644 index c92570e..0000000 --- a/satrs-core/tests/pools.rs +++ /dev/null @@ -1,35 +0,0 @@ -use satrs_core::pool::{LocalPool, PoolCfg, PoolGuard, PoolProvider, StoreAddr}; -use std::ops::DerefMut; -use std::sync::mpsc; -use std::sync::mpsc::{Receiver, Sender}; -use std::sync::{Arc, RwLock}; -use std::thread; - -const DUMMY_DATA: [u8; 4] = [0, 1, 2, 3]; - -#[test] -fn threaded_usage() { - let pool_cfg = PoolCfg::new(vec![(16, 6), (32, 3), (8, 12)]); - let shared_pool = Arc::new(RwLock::new(LocalPool::new(pool_cfg))); - let shared_clone = shared_pool.clone(); - let (tx, rx): (Sender, Receiver) = mpsc::channel(); - let jh0 = thread::spawn(move || { - let mut dummy = shared_pool.write().unwrap(); - let addr = dummy.add(&DUMMY_DATA).expect("Writing data failed"); - tx.send(addr).expect("Sending store address failed"); - }); - - let jh1 = thread::spawn(move || { - let mut pool_access = shared_clone.write().unwrap(); - let addr; - { - addr = rx.recv().expect("Receiving store address failed"); - let pg = PoolGuard::new(pool_access.deref_mut(), addr); - let read_res = pg.read().expect("Reading failed"); - assert_eq!(read_res, DUMMY_DATA); - } - assert!(!pool_access.has_element_at(&addr).expect("Invalid address")); - }); - jh0.join().unwrap(); - jh1.join().unwrap(); -} diff --git a/satrs-core/tests/pus_autogen_events.rs b/satrs-core/tests/pus_autogen_events.rs deleted file mode 100644 index eaec7e6..0000000 --- a/satrs-core/tests/pus_autogen_events.rs +++ /dev/null @@ -1,94 +0,0 @@ -#![allow(dead_code, unused_imports)] - -use satrs_core::events::{ - EventU32, EventU32TypedSev, GenericEvent, HasSeverity, LargestEventRaw, LargestGroupIdRaw, - Severity, SeverityInfo, SeverityLow, SeverityMedium, -}; -use std::convert::AsRef; - -#[derive(Debug)] -struct GroupIdIntrospection { - name: &'static str, - id: LargestGroupIdRaw, -} - -#[derive(Debug)] -struct EventIntrospection { - name: &'static str, - group_id: GroupIdIntrospection, - event: &'static EventU32, - info: &'static str, -} - -//#[event(descr="This is some info event")] -const INFO_EVENT_0: EventU32TypedSev = EventU32TypedSev::const_new(0, 0); -const INFO_EVENT_0_ERASED: EventU32 = EventU32::const_from_info(INFO_EVENT_0); - -// This is ideally auto-generated -const INFO_EVENT_0_INTROSPECTION: EventIntrospection = EventIntrospection { - name: "INFO_EVENT_0", - group_id: GroupIdIntrospection { - id: 0, - name: "Group ID 0 without name", - }, - event: &INFO_EVENT_0_ERASED, - info: "This is some info event", -}; - -//#[event(descr="This is some low severity event")] -const SOME_LOW_SEV_EVENT: EventU32TypedSev = EventU32TypedSev::const_new(0, 12); - -//const EVENT_LIST: [&'static Event; 2] = [&INFO_EVENT_0, &SOME_LOW_SEV_EVENT]; - -//#[event_group] -const TEST_GROUP_NAME: u16 = 1; -// Auto-generated? -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); -const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_REDUCED: EventU32 = - EventU32::const_from_medium(MEDIUM_SEV_EVENT_IN_OTHER_GROUP); - -// Also auto-generated -const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_INTROSPECTION: EventIntrospection = EventIntrospection { - name: "MEDIUM_SEV_EVENT_IN_OTHER_GROUP", - group_id: GroupIdIntrospection { - name: TEST_GROUP_NAME_NAME, - id: TEST_GROUP_NAME, - }, - event: &MEDIUM_SEV_EVENT_IN_OTHER_GROUP_REDUCED, - info: "Some medium severity event", -}; - -const CONST_SLICE: &'static [u8] = &[0, 1, 2, 3]; -const INTROSPECTION_FOR_TEST_GROUP_0: [&EventIntrospection; 2] = - [&INFO_EVENT_0_INTROSPECTION, &INFO_EVENT_0_INTROSPECTION]; - -//const INTROSPECTION_FOR_TABLE: &'static [&EventIntrospection] = &INTROSPECTION_FOR_TEST_GROUP_0; - -const INTROSPECTION_FOR_TEST_GROUP_NAME: [&EventIntrospection; 1] = - [&MEDIUM_SEV_EVENT_IN_OTHER_GROUP_INTROSPECTION]; -//const BLAH: &'static [&EventIntrospection] = &INTROSPECTION_FOR_TEST_GROUP_NAME; - -const ALL_EVENTS: [&[&EventIntrospection]; 2] = [ - &INTROSPECTION_FOR_TEST_GROUP_0, - &INTROSPECTION_FOR_TEST_GROUP_NAME, -]; - -#[test] -fn main() { - //let test = stringify!(INFO_EVENT); - //println!("{:?}", test); - //for event in EVENT_LIST { - // println!("{:?}", event); - //} - //for events in ALL_EVENTS.into_iter().flatten() { - // dbg!("{:?}", events); - //} - //for introspection_info in INTROSPECTION_FOR_TEST_GROUP { - // dbg!("{:?}", introspection_info); - //} - //let test_struct = -} diff --git a/satrs-core/tests/pus_events.rs b/satrs-core/tests/pus_events.rs deleted file mode 100644 index 8c47162..0000000 --- a/satrs-core/tests/pus_events.rs +++ /dev/null @@ -1,174 +0,0 @@ -use satrs_core::event_man::{ - EventManagerWithMpscQueue, MpscEventU32Receiver, MpscEventU32SendProvider, SendEventProvider, -}; -use satrs_core::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo}; -use satrs_core::params::U32Pair; -use satrs_core::params::{Params, ParamsHeapless, WritableToBeBytes}; -use satrs_core::pus::event_man::{ - DefaultPusMgmtBackendProvider, EventReporter, PusEventDispatcher, -}; -use satrs_core::pus::{EcssTmErrorWithSend, EcssTmSenderCore}; -use spacepackets::ecss::PusPacket; -use spacepackets::tm::PusTm; -use std::sync::mpsc::{channel, 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 EMPTY_STAMP: [u8; 7] = [0; 7]; - -#[derive(Clone)] -struct EventTmSender { - sender: std::sync::mpsc::Sender>, -} - -impl EcssTmSenderCore for EventTmSender { - type Error = SendError>; - fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmErrorWithSend> { - let mut vec = Vec::new(); - tm.append_to_vec(&mut vec) - .map_err(|e| EcssTmErrorWithSend::EcssTmError(e.into()))?; - self.sender - .send(vec) - .map_err(EcssTmErrorWithSend::SendError)?; - Ok(()) - } -} - -#[test] -fn test_threaded_usage() { - let (event_sender, event_man_receiver) = channel(); - let event_receiver = MpscEventU32Receiver::new(event_man_receiver); - let mut event_man = EventManagerWithMpscQueue::new(Box::new(event_receiver)); - - let (pus_event_man_tx, pus_event_man_rx) = channel(); - let pus_event_man_send_provider = MpscEventU32SendProvider::new(1, pus_event_man_tx); - event_man.subscribe_all(pus_event_man_send_provider.id()); - event_man.add_sender(pus_event_man_send_provider); - let (event_tx, event_rx) = channel(); - let reporter = EventReporter::new(0x02, 128).expect("Creating event reporter failed"); - let backend = DefaultPusMgmtBackendProvider::::default(); - let mut pus_event_man = PusEventDispatcher::new(reporter, Box::new(backend)); - // PUS + Generic event manager thread - let jh0 = thread::spawn(move || { - let mut sender = EventTmSender { sender: event_tx }; - let mut event_cnt = 0; - let mut params_array: [u8; 128] = [0; 128]; - loop { - let res = event_man.try_event_handling(); - assert!(res.is_ok()); - match pus_event_man_rx.try_recv() { - Ok((event, aux_data)) => { - let mut gen_event = |aux_data| { - pus_event_man.generate_pus_event_tm_generic( - &mut sender, - &EMPTY_STAMP, - event, - aux_data, - ) - }; - let res = if let Some(aux_data) = aux_data { - match aux_data { - Params::Heapless(heapless) => match heapless { - ParamsHeapless::Raw(raw) => { - raw.write_to_be_bytes(&mut params_array) - .expect("Writing raw parameter failed"); - gen_event(Some(¶ms_array[0..raw.raw_len()])) - } - ParamsHeapless::EcssEnum(e) => { - e.write_to_be_bytes(&mut params_array) - .expect("Writing ECSS enum failed"); - gen_event(Some(¶ms_array[0..e.raw_len()])) - } - }, - Params::Vec(vec) => gen_event(Some(vec.as_slice())), - Params::String(str) => gen_event(Some(str.as_bytes())), - Params::Store(_) => gen_event(None), - } - } else { - gen_event(None) - }; - event_cnt += 1; - assert!(res.is_ok()); - assert!(res.unwrap()); - if event_cnt == 2 { - break; - } - } - Err(e) => { - if let TryRecvError::Disconnected = e { - panic!("Event receiver disconnected!") - } - } - } - } - }); - - // Event sender and TM checker thread - let jh1 = thread::spawn(move || { - event_sender - .send((INFO_EVENT.into(), None)) - .expect("Sending info event failed"); - loop { - match event_rx.try_recv() { - // Event TM received successfully - Ok(event_tm) => { - let tm = - PusTm::from_bytes(event_tm.as_slice(), 7).expect("Deserializing TM failed"); - assert_eq!(tm.0.service(), 5); - assert_eq!(tm.0.subservice(), 1); - let src_data = tm.0.source_data(); - assert!(src_data.is_some()); - let src_data = src_data.unwrap(); - assert_eq!(src_data.len(), 4); - let event = - EventU32::from(u32::from_be_bytes(src_data[0..4].try_into().unwrap())); - assert_eq!(event, INFO_EVENT); - break; - } - Err(e) => { - if let TryRecvError::Disconnected = e { - panic!("Event sender disconnected!") - } - } - } - } - event_sender - .send(( - LOW_SEV_EVENT.into(), - Some(Params::Heapless((2_u32, 3_u32).into())), - )) - .expect("Sending low severity event failed"); - loop { - match event_rx.try_recv() { - // Event TM received successfully - Ok(event_tm) => { - let tm = - PusTm::from_bytes(event_tm.as_slice(), 7).expect("Deserializing TM failed"); - assert_eq!(tm.0.service(), 5); - assert_eq!(tm.0.subservice(), 2); - let src_data = tm.0.source_data(); - assert!(src_data.is_some()); - let src_data = src_data.unwrap(); - assert_eq!(src_data.len(), 12); - let event = - EventU32::from(u32::from_be_bytes(src_data[0..4].try_into().unwrap())); - assert_eq!(event, LOW_SEV_EVENT); - let u32_pair: U32Pair = - src_data[4..].try_into().expect("Creating U32Pair failed"); - assert_eq!(u32_pair.0, 2); - assert_eq!(u32_pair.1, 3); - break; - } - Err(e) => { - if let TryRecvError::Disconnected = e { - panic!("Event sender disconnected!") - } - } - } - } - }); - jh0.join().expect("Joining manager thread failed"); - jh1.join().expect("Joining creator thread failed"); -} diff --git a/satrs-core/tests/pus_verification.rs b/satrs-core/tests/pus_verification.rs deleted file mode 100644 index 6023dfb..0000000 --- a/satrs-core/tests/pus_verification.rs +++ /dev/null @@ -1,199 +0,0 @@ -// TODO: Refactor this to also test the STD impl using mpsc -#[cfg(feature = "crossbeam")] -pub mod crossbeam_test { - use hashbrown::HashMap; - use satrs_core::pool::{LocalPool, PoolCfg, PoolProvider, SharedPool}; - use satrs_core::pus::verification::{ - CrossbeamVerifSender, FailParams, RequestId, VerificationReporterCfg, - VerificationReporterWithSender, - }; - use satrs_core::seq_count::SeqCountProviderSyncClonable; - use spacepackets::ecss::{EcssEnumU16, EcssEnumU8, PusPacket}; - use spacepackets::tc::{PusTc, PusTcSecondaryHeader}; - use spacepackets::tm::PusTm; - use spacepackets::SpHeader; - use std::sync::{Arc, RwLock}; - use std::thread; - use std::time::Duration; - - const TEST_APID: u16 = 0x03; - const FIXED_STAMP: [u8; 7] = [0; 7]; - const PACKETS_SENT: u8 = 8; - - /// This test also shows how the verification report could be used in a multi-threaded context, - /// wrapping it into an [Arc] and [Mutex] and then passing it to two threads. - /// - /// - The first thread generates a acceptance, a start, two steps and one completion report - /// - The second generates an acceptance and start success report and a completion failure - /// - The third thread is the verification receiver. In the test case, it verifies the other two - /// threads have sent the correct expected verification reports - #[test] - fn test_shared_reporter() { - // We use a synced sequence count provider here because both verification reporters have the - // the same APID. If they had distinct APIDs, the more correct approach would be to have - // each reporter have an own sequence count provider. - let cfg = VerificationReporterCfg::new( - TEST_APID, - Box::new(SeqCountProviderSyncClonable::default()), - 1, - 2, - 8, - ) - .unwrap(); - // Shared pool object to store the verification PUS telemetry - let pool_cfg = PoolCfg::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]); - let shared_tm_pool: SharedPool = - Arc::new(RwLock::new(Box::new(LocalPool::new(pool_cfg.clone())))); - let shared_tc_pool_0 = Arc::new(RwLock::new(LocalPool::new(pool_cfg))); - let shared_tc_pool_1 = shared_tc_pool_0.clone(); - let (tx, rx) = crossbeam_channel::bounded(5); - let sender = CrossbeamVerifSender::new(shared_tm_pool.clone(), tx.clone()); - let mut reporter_with_sender_0 = - VerificationReporterWithSender::new(&cfg, Box::new(sender)); - let mut reporter_with_sender_1 = reporter_with_sender_0.clone(); - // For test purposes, we retrieve the request ID from the TCs and pass them to the receiver - // tread. - let req_id_0; - let req_id_1; - - let (tx_tc_0, rx_tc_0) = crossbeam_channel::bounded(3); - let (tx_tc_1, rx_tc_1) = crossbeam_channel::bounded(3); - { - let mut tc_guard = shared_tc_pool_0.write().unwrap(); - let mut sph = SpHeader::tc_unseg(TEST_APID, 0, 0).unwrap(); - let tc_header = PusTcSecondaryHeader::new_simple(17, 1); - let pus_tc_0 = PusTc::new(&mut sph, tc_header, None, true); - req_id_0 = RequestId::new(&pus_tc_0); - let (addr, mut buf) = tc_guard.free_element(pus_tc_0.len_packed()).unwrap(); - pus_tc_0.write_to_bytes(&mut buf).unwrap(); - tx_tc_0.send(addr).unwrap(); - let mut sph = SpHeader::tc_unseg(TEST_APID, 1, 0).unwrap(); - let tc_header = PusTcSecondaryHeader::new_simple(5, 1); - let pus_tc_1 = PusTc::new(&mut sph, tc_header, None, true); - req_id_1 = RequestId::new(&pus_tc_1); - let (addr, mut buf) = tc_guard.free_element(pus_tc_0.len_packed()).unwrap(); - pus_tc_1.write_to_bytes(&mut buf).unwrap(); - tx_tc_1.send(addr).unwrap(); - } - let verif_sender_0 = thread::spawn(move || { - let mut tc_buf: [u8; 1024] = [0; 1024]; - let tc_addr = rx_tc_0 - .recv_timeout(Duration::from_millis(20)) - .expect("Receive timeout"); - let tc_len; - { - let mut tc_guard = shared_tc_pool_0.write().unwrap(); - let pg = tc_guard.read_with_guard(tc_addr); - let buf = pg.read().unwrap(); - tc_len = buf.len(); - tc_buf[0..tc_len].copy_from_slice(buf); - } - let (_tc, _) = PusTc::from_bytes(&tc_buf[0..tc_len]).unwrap(); - let accepted_token; - - let token = reporter_with_sender_0.add_tc_with_req_id(req_id_0); - accepted_token = reporter_with_sender_0 - .acceptance_success(token, &FIXED_STAMP) - .expect("Acceptance success failed"); - - // Do some start handling here - let started_token; - started_token = reporter_with_sender_0 - .start_success(accepted_token, &FIXED_STAMP) - .expect("Start success failed"); - // Do some step handling here - reporter_with_sender_0 - .step_success(&started_token, &FIXED_STAMP, EcssEnumU8::new(0)) - .expect("Start success failed"); - - // Finish up - reporter_with_sender_0 - .step_success(&started_token, &FIXED_STAMP, EcssEnumU8::new(1)) - .expect("Start success failed"); - reporter_with_sender_0 - .completion_success(started_token, &FIXED_STAMP) - .expect("Completion success failed"); - }); - - let verif_sender_1 = thread::spawn(move || { - let mut tc_buf: [u8; 1024] = [0; 1024]; - let tc_addr = rx_tc_1 - .recv_timeout(Duration::from_millis(20)) - .expect("Receive timeout"); - let tc_len; - { - let mut tc_guard = shared_tc_pool_1.write().unwrap(); - let pg = tc_guard.read_with_guard(tc_addr); - let buf = pg.read().unwrap(); - tc_len = buf.len(); - tc_buf[0..tc_len].copy_from_slice(buf); - } - let (tc, _) = PusTc::from_bytes(&tc_buf[0..tc_len]).unwrap(); - let token = reporter_with_sender_1.add_tc(&tc); - let accepted_token = reporter_with_sender_1 - .acceptance_success(token, &FIXED_STAMP) - .expect("Acceptance success failed"); - let started_token = reporter_with_sender_1 - .start_success(accepted_token, &FIXED_STAMP) - .expect("Start success failed"); - let fail_code = EcssEnumU16::new(2); - let params = FailParams::new(&FIXED_STAMP, &fail_code, None); - reporter_with_sender_1 - .completion_failure(started_token, params) - .expect("Completion success failed"); - }); - - let verif_receiver = thread::spawn(move || { - let mut packet_counter = 0; - let mut tm_buf: [u8; 1024] = [0; 1024]; - let mut verif_map = HashMap::new(); - while packet_counter < PACKETS_SENT { - let verif_addr = rx - .recv_timeout(Duration::from_millis(50)) - .expect("Packet reception timeout"); - let tm_len; - { - let mut rg = shared_tm_pool.write().expect("Error locking shared pool"); - let store_guard = rg.read_with_guard(verif_addr); - let slice = store_guard.read().expect("Error reading TM slice"); - tm_len = slice.len(); - tm_buf[0..tm_len].copy_from_slice(slice); - } - let (pus_tm, _) = PusTm::from_bytes(&tm_buf[0..tm_len], 7) - .expect("Error reading verification TM"); - let req_id = RequestId::from_bytes( - &pus_tm.source_data().expect("Invalid TM source data") - [0..RequestId::SIZE_AS_BYTES], - ) - .unwrap(); - if !verif_map.contains_key(&req_id) { - let mut content = Vec::new(); - content.push(pus_tm.subservice()); - verif_map.insert(req_id, content); - } else { - let content = verif_map.get_mut(&req_id).unwrap(); - content.push(pus_tm.subservice()) - } - packet_counter += 1; - } - for (req_id, content) in verif_map { - if req_id == req_id_1 { - assert_eq!(content[0], 1); - assert_eq!(content[1], 3); - assert_eq!(content[2], 8); - } else if req_id == req_id_0 { - assert_eq!(content[0], 1); - assert_eq!(content[1], 3); - assert_eq!(content[2], 5); - assert_eq!(content[3], 5); - assert_eq!(content[4], 7); - } else { - panic!("Unexpected request ID {:?}", req_id); - } - } - }); - verif_sender_0.join().expect("Joining thread 0 failed"); - verif_sender_1.join().expect("Joining thread 1 failed"); - verif_receiver.join().expect("Joining thread 2 failed"); - } -}