From f24b27ccaf165dae74432d397df9356f084c1c49 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 11 Jan 2023 09:49:34 +0100 Subject: [PATCH] remove satrs-core to replace with submodule --- satrs-core/CHANGELOG.md | 9 - satrs-core/Cargo.toml | 73 - satrs-core/images/event_man_arch.graphml | 259 --- satrs-core/images/event_man_arch.png | Bin 71832 -> 0 bytes satrs-core/src/error.rs | 49 - satrs-core/src/event_man.rs | 723 ------- satrs-core/src/events.rs | 801 -------- satrs-core/src/executable.rs | 503 ----- satrs-core/src/hal/host/mod.rs | 2 - satrs-core/src/hal/host/udp_server.rs | 212 --- satrs-core/src/hal/mod.rs | 4 - satrs-core/src/lib.rs | 38 - satrs-core/src/objects.rs | 306 --- satrs-core/src/params.rs | 648 ------- satrs-core/src/pool.rs | 771 -------- satrs-core/src/pus/event.rs | 479 ----- satrs-core/src/pus/event_man.rs | 328 ---- satrs-core/src/pus/hk.rs | 8 - satrs-core/src/pus/mod.rs | 127 -- satrs-core/src/pus/verification.rs | 2207 ---------------------- satrs-core/src/res_code.rs | 53 - satrs-core/src/seq_count.rs | 121 -- satrs-core/src/tmtc/ccsds_distrib.rs | 333 ---- satrs-core/src/tmtc/mod.rs | 100 - satrs-core/src/tmtc/pus_distrib.rs | 333 ---- satrs-core/src/tmtc/tm_helper.rs | 52 - satrs-core/tests/hk_helpers.rs | 164 -- satrs-core/tests/pools.rs | 35 - satrs-core/tests/pus_autogen_events.rs | 94 - satrs-core/tests/pus_events.rs | 174 -- satrs-core/tests/pus_verification.rs | 199 -- 31 files changed, 9205 deletions(-) delete mode 100644 satrs-core/CHANGELOG.md delete mode 100644 satrs-core/Cargo.toml delete mode 100644 satrs-core/images/event_man_arch.graphml delete mode 100644 satrs-core/images/event_man_arch.png delete mode 100644 satrs-core/src/error.rs delete mode 100644 satrs-core/src/event_man.rs delete mode 100644 satrs-core/src/events.rs delete mode 100644 satrs-core/src/executable.rs delete mode 100644 satrs-core/src/hal/host/mod.rs delete mode 100644 satrs-core/src/hal/host/udp_server.rs delete mode 100644 satrs-core/src/hal/mod.rs delete mode 100644 satrs-core/src/lib.rs delete mode 100644 satrs-core/src/objects.rs delete mode 100644 satrs-core/src/params.rs delete mode 100644 satrs-core/src/pool.rs delete mode 100644 satrs-core/src/pus/event.rs delete mode 100644 satrs-core/src/pus/event_man.rs delete mode 100644 satrs-core/src/pus/hk.rs delete mode 100644 satrs-core/src/pus/mod.rs delete mode 100644 satrs-core/src/pus/verification.rs delete mode 100644 satrs-core/src/res_code.rs delete mode 100644 satrs-core/src/seq_count.rs delete mode 100644 satrs-core/src/tmtc/ccsds_distrib.rs delete mode 100644 satrs-core/src/tmtc/mod.rs delete mode 100644 satrs-core/src/tmtc/pus_distrib.rs delete mode 100644 satrs-core/src/tmtc/tm_helper.rs delete mode 100644 satrs-core/tests/hk_helpers.rs delete mode 100644 satrs-core/tests/pools.rs delete mode 100644 satrs-core/tests/pus_autogen_events.rs delete mode 100644 satrs-core/tests/pus_events.rs delete mode 100644 satrs-core/tests/pus_verification.rs 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 61c8d7255dfe663247b6d21db6f368570fd5105c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71832 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

57b?fjoQm3O83H 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 Y_NLHOi$+i4r^p 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"); - } -}