From fe363d29627851f87f8a5be99ffef4848e97baa6 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 23 Oct 2022 22:32:30 +0200 Subject: [PATCH 01/40] some experimental eventcode --- fsrc-core/src/events.rs | 3 +- fsrc-core/tests/pus_events.rs | 62 +++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/fsrc-core/src/events.rs b/fsrc-core/src/events.rs index fc9c66f..2934d1f 100644 --- a/fsrc-core/src/events.rs +++ b/fsrc-core/src/events.rs @@ -200,8 +200,7 @@ impl Event { }) } - /// Const version of [new], but panics on invalid input which is invalid group ID - /// values + /// Const version of [new], but panics on invalid group ID input values. pub const fn const_new( severity: Severity, group_id: ::GroupId, diff --git a/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index 8b13789..2f554c1 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -1 +1,63 @@ +#![allow(dead_code, unused_imports)] +use fsrc_core::events::{Event, EventProvider, LargestEventRaw, LargestGroupIdRaw, Severity}; +struct GroupIdIntrospection { + name: &'static str, + id: LargestGroupIdRaw, +} + +struct EventIntrospection { + name: &'static str, + group_id: GroupIdIntrospection, + event: &'static Event, + info: &'static str, +} + +//#[event(descr="This is some info event")] +const INFO_EVENT_0: Event = Event::const_new(Severity::INFO, 0, 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, + info: "This is some info event", +}; + +//#[event(descr="This is some low severity event")] +const SOME_LOW_SEV_EVENT: Event = Event::const_new(Severity::LOW, 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: &'static str = "TEST_GROUP_NAME"; + +//#[event(desc="Some medium severity event")] +const MEDIUM_SEV_EVENT_IN_OTHER_GROUP: Event = + Event::const_new(Severity::MEDIUM, TEST_GROUP_NAME, 0); + +// 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, + info: "Some medium severity event", +}; + +#[test] +fn main() { + let test = stringify!(INFO_EVENT); + println!("{:?}", test); + for event in EVENT_LIST { + println!("{:?}", event); + } + //let test_struct = +} From 6fe373836457e5f96bf01e6a91325c267cfc6ef2 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Sun, 23 Oct 2022 23:15:11 +0200 Subject: [PATCH 02/40] that seems to work --- fsrc-core/src/events.rs | 2 +- fsrc-core/src/pus/event_man.rs | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/fsrc-core/src/events.rs b/fsrc-core/src/events.rs index 2934d1f..a74cb01 100644 --- a/fsrc-core/src/events.rs +++ b/fsrc-core/src/events.rs @@ -18,7 +18,7 @@ pub enum Severity { HIGH = 3, } -pub trait EventProvider: PartialEq + Eq + Copy + Clone + Hash { +pub trait EventProvider: EcssEnumeration + PartialEq + Eq + Copy + Clone + Hash { type Raw; type GroupId; type UniqueId; diff --git a/fsrc-core/src/pus/event_man.rs b/fsrc-core/src/pus/event_man.rs index 2e33618..8195b04 100644 --- a/fsrc-core/src/pus/event_man.rs +++ b/fsrc-core/src/pus/event_man.rs @@ -1,6 +1,9 @@ use crate::events::EventProvider; +use alloc::boxed::Box; use hashbrown::HashSet; +use crate::pus::event::EventReporter; +use crate::pus::{EcssTmError, EcssTmSender}; #[cfg(feature = "heapless")] pub use heapless_mod::*; @@ -84,4 +87,24 @@ pub mod heapless_mod { } } -pub struct PusEventManager {} +pub struct PusEventManager { + reporter: EventReporter, + backend: Box>, +} + +impl PusEventManager { + pub fn handle_event( + &mut self, + sender: &mut (impl EcssTmSender + ?Sized), + time_stamp: &[u8], + event: Provider, + aux_data: Option<&[u8]>, + ) -> Result> { + if !self.backend.event_enabled(&event) { + return Ok(false); + } + self.reporter + .event_info(sender, time_stamp, event, aux_data) + .map(|_| true) + } +} From 478673327b98193c787e3bfbe0a6742f801714a5 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 24 Oct 2022 01:51:33 +0200 Subject: [PATCH 03/40] basic PUS event manager --- fsrc-core/src/event_man.rs | 80 +++++---- fsrc-core/src/events.rs | 309 ++++++++++++++++++++++++++------- fsrc-core/src/pus/event.rs | 14 +- fsrc-core/src/pus/event_man.rs | 83 +++++++-- fsrc-core/tests/pus_events.rs | 50 +++--- 5 files changed, 385 insertions(+), 151 deletions(-) diff --git a/fsrc-core/src/event_man.rs b/fsrc-core/src/event_man.rs index 4fe4092..5d1c6ba 100644 --- a/fsrc-core/src/event_man.rs +++ b/fsrc-core/src/event_man.rs @@ -1,5 +1,5 @@ //! [Event][crate::events::Event] management and forwarding -use crate::events::{Event, EventProvider, EventSmall}; +use crate::events::{EventU16TypedSev, EventU32, GenericEvent, HasSeverity}; use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; @@ -11,34 +11,34 @@ enum ListenerType { Group(u16), } -pub trait EventListener { +pub trait EventListener { type Error; fn id(&self) -> u32; fn send_to(&mut self, event: Provider) -> Result<(), Self::Error>; } -struct Listener { +struct Listener { ltype: ListenerType, dest: Box>, } -pub trait ReceivesAllEvent { +pub trait ReceivesAllEvent { fn receive(&mut self) -> Option; } -pub struct EventManager { +pub struct EventManager { listeners: HashMap>>, event_receiver: Box>, } -pub enum HandlerResult { +pub enum HandlerResult { Empty, Handled(u32, Provider), } -impl EventManager { - pub fn new(event_receiver: Box>) -> Self { +impl EventManager { + pub fn new(event_receiver: Box>) -> Self { EventManager { listeners: HashMap::new(), event_receiver, @@ -46,43 +46,43 @@ impl EventManager { } } -impl EventManager { +impl EventManager { pub fn subscribe_single( &mut self, - event: Event, - dest: impl EventListener + 'static, + event: EventU32, + dest: impl EventListener + 'static, ) { self.update_listeners(ListenerType::Single(event.raw_as_largest_type()), dest); } pub fn subscribe_group( &mut self, - group_id: ::GroupId, - dest: impl EventListener + 'static, + group_id: ::GroupId, + dest: impl EventListener + 'static, ) { self.update_listeners(ListenerType::Group(group_id), dest); } } -impl EventManager { +impl EventManager> { pub fn subscribe_single( &mut self, - event: EventSmall, - dest: impl EventListener + 'static, + event: EventU16TypedSev, + dest: impl EventListener, Error = E> + 'static, ) { self.update_listeners(ListenerType::Single(event.raw_as_largest_type()), dest); } pub fn subscribe_group( &mut self, - group_id: ::GroupId, - dest: impl EventListener + 'static, + group_id: as GenericEvent>::GroupId, + dest: impl EventListener, Error = E> + 'static, ) { self.update_listeners(ListenerType::Group(group_id.into()), dest); } } -impl EventManager { +impl EventManager { fn update_listeners( &mut self, key: ListenerType, @@ -145,17 +145,17 @@ impl EventManager { mod tests { use super::{EventListener, HandlerResult, ReceivesAllEvent}; use crate::event_man::EventManager; - use crate::events::{Event, EventProvider, Severity}; + use crate::events::{EventU32, GenericEvent, Severity}; use alloc::boxed::Box; use std::sync::mpsc::{channel, Receiver, SendError, Sender}; use std::thread; use std::time::Duration; struct EventReceiver { - mpsc_receiver: Receiver, + mpsc_receiver: Receiver, } - impl ReceivesAllEvent for EventReceiver { - fn receive(&mut self) -> Option { + impl ReceivesAllEvent for EventReceiver { + fn receive(&mut self) -> Option { self.mpsc_receiver.try_recv().ok() } } @@ -163,21 +163,21 @@ mod tests { #[derive(Clone)] struct MpscEventSenderQueue { id: u32, - mpsc_sender: Sender, + mpsc_sender: Sender, } - impl EventListener for MpscEventSenderQueue { - type Error = SendError; + impl EventListener for MpscEventSenderQueue { + type Error = SendError; fn id(&self) -> u32 { self.id } - fn send_to(&mut self, event: Event) -> Result<(), Self::Error> { + fn send_to(&mut self, event: EventU32) -> Result<(), Self::Error> { self.mpsc_sender.send(event) } } - fn check_next_event(expected: Event, receiver: &Receiver) { + fn check_next_event(expected: EventU32, receiver: &Receiver) { for _ in 0..5 { if let Ok(event) = receiver.try_recv() { assert_eq!(event, expected); @@ -187,7 +187,11 @@ mod tests { } } - fn check_handled_event(res: HandlerResult, expected: Event, expected_num_sent: u32) { + fn check_handled_event( + res: HandlerResult, + expected: EventU32, + expected_num_sent: u32, + ) { assert!(matches!(res, HandlerResult::Handled { .. })); if let HandlerResult::Handled(num_recipients, event) = res { assert_eq!(event, expected); @@ -201,10 +205,10 @@ mod tests { let event_man_receiver = EventReceiver { mpsc_receiver: manager_queue, }; - let mut event_man: EventManager, Event> = + let mut event_man: EventManager, EventU32> = EventManager::new(Box::new(event_man_receiver)); - let event_grp_0 = Event::new(Severity::INFO, 0, 0).unwrap(); - let event_grp_1_0 = Event::new(Severity::HIGH, 1, 0).unwrap(); + 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 { id: 0, @@ -244,15 +248,15 @@ mod tests { let event_man_receiver = EventReceiver { mpsc_receiver: manager_queue, }; - let mut event_man: EventManager, Event> = + let mut event_man: EventManager, EventU32> = EventManager::new(Box::new(event_man_receiver)); let res = event_man.try_event_handling(); assert!(res.is_ok()); let hres = res.unwrap(); assert!(matches!(hres, HandlerResult::Empty)); - let event_grp_0 = Event::new(Severity::INFO, 0, 0).unwrap(); - let event_grp_1_0 = Event::new(Severity::HIGH, 1, 0).unwrap(); + 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, @@ -286,10 +290,10 @@ mod tests { let event_man_receiver = EventReceiver { mpsc_receiver: manager_queue, }; - let mut event_man: EventManager, Event> = + let mut event_man: EventManager, EventU32> = EventManager::new(Box::new(event_man_receiver)); - let event_0 = Event::new(Severity::INFO, 0, 5).unwrap(); - let event_1 = Event::new(Severity::HIGH, 1, 0).unwrap(); + 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 { diff --git a/fsrc-core/src/events.rs b/fsrc-core/src/events.rs index a74cb01..4a27cf0 100644 --- a/fsrc-core/src/events.rs +++ b/fsrc-core/src/events.rs @@ -1,6 +1,7 @@ //! Event support module use core::hash::Hash; +use delegate::delegate; use spacepackets::ecss::{EcssEnumeration, ToBeBytes}; use spacepackets::{ByteConversionError, SizeMissmatch}; use std::marker::PhantomData; @@ -18,7 +19,34 @@ pub enum Severity { HIGH = 3, } -pub trait EventProvider: EcssEnumeration + PartialEq + Eq + Copy + Clone + Hash { +pub trait HasSeverity { + const SEVERITY: Severity; +} +#[derive(Debug, PartialEq, Eq)] +pub struct SeverityInfo {} +impl HasSeverity for SeverityInfo { + const SEVERITY: Severity = Severity::INFO; +} + +#[derive(Debug, PartialEq, Eq)] +pub struct SeverityLow {} +impl HasSeverity for SeverityLow { + const SEVERITY: Severity = Severity::LOW; +} + +#[derive(Debug, PartialEq, Eq)] +pub struct SeverityMedium {} +impl HasSeverity for SeverityMedium { + const SEVERITY: Severity = Severity::MEDIUM; +} + +#[derive(Debug, PartialEq, Eq)] +pub struct SeverityHigh {} +impl HasSeverity for SeverityHigh { + const SEVERITY: Severity = Severity::HIGH; +} + +pub trait GenericEvent: EcssEnumeration { type Raw; type GroupId; type UniqueId; @@ -149,43 +177,78 @@ macro_rules! event_provider_impl { } }; } + +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) + } + } + }; +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct Event { +pub struct EventU32 { base: EventBase, } -impl EventProvider for Event { - type Raw = u32; - type GroupId = u16; - type UniqueId = u16; +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct EventU32TypedSev { + event: EventU32, + phantom: PhantomData, +} - event_provider_impl!(); - - fn raw_as_largest_type(&self) -> LargestEventRaw { - self.raw() - } - - fn group_id_as_largest_type(&self) -> LargestGroupIdRaw { - self.group_id() +impl From> for EventU32 { + fn from(e: EventU32TypedSev) -> Self { + Self { base: e.event.base } } } -impl Event { - /// 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 +impl_event_provider!(EventU32, EventU32TypedSev, u32, u16, u16); + +impl EventU32 { pub fn new( severity: Severity, - group_id: ::GroupId, - unique_id: ::UniqueId, + group_id: ::GroupId, + unique_id: ::UniqueId, ) -> Option { if group_id > (2u16.pow(14) - 1) { return None; @@ -199,12 +262,10 @@ impl Event { }, }) } - - /// Const version of [new], but panics on invalid group ID input values. pub const fn const_new( severity: Severity, - group_id: ::GroupId, - unique_id: ::UniqueId, + group_id: ::GroupId, + unique_id: ::UniqueId, ) -> Self { if group_id > (2u16.pow(14) - 1) { panic!("Group ID too large"); @@ -220,7 +281,54 @@ impl Event { } } -impl From for Event { +impl EventU32TypedSev { + /// 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( + 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 [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(); @@ -231,7 +339,12 @@ impl From for Event { } } -impl EcssEnumeration for Event { +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 } @@ -241,12 +354,26 @@ impl EcssEnumeration for Event { } } +//noinspection RsTraitImplementation +impl EcssEnumeration for EventU32TypedSev { + delegate!(to self.event { + fn pfc(&self) -> u8; + fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>; + }); +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct EventSmall { +pub struct EventU16 { base: EventBase, } -impl EventSmall { +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct EventU16TypedSev { + event: EventU16, + phantom: PhantomData, +} + +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 /// @@ -260,8 +387,8 @@ impl EventSmall { /// raw event ID pub fn new( severity: Severity, - group_id: ::GroupId, - unique_id: ::UniqueId, + group_id: ::GroupId, + unique_id: ::UniqueId, ) -> Option { if group_id > (2u8.pow(6) - 1) { return None; @@ -278,8 +405,8 @@ impl EventSmall { pub const fn const_new( severity: Severity, - group_id: ::GroupId, - unique_id: ::UniqueId, + group_id: ::GroupId, + unique_id: ::UniqueId, ) -> Self { if group_id > (2u8.pow(6) - 1) { panic!("Group ID too large"); @@ -294,24 +421,55 @@ impl EventSmall { } } } - -impl EventProvider for EventSmall { - type Raw = u16; - type GroupId = u8; - type UniqueId = u8; - - event_provider_impl!(); - - fn raw_as_largest_type(&self) -> LargestEventRaw { - self.raw().into() +impl EventU16TypedSev { + /// 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( + group_id: ::GroupId, + unique_id: ::UniqueId, + ) -> Option { + let event = EventU16::new(SEVERITY::SEVERITY, group_id, unique_id)?; + Some(Self { + event, + phantom: PhantomData, + }) } - fn group_id_as_largest_type(&self) -> LargestGroupIdRaw { - self.group_id().into() + 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 EcssEnumeration for EventSmall { +impl_event_provider!(EventU16, EventU16TypedSev, u16, u8, u8); + +impl EcssEnumeration for EventU16 { fn pfc(&self) -> u8 { 16 } @@ -321,8 +479,16 @@ impl EcssEnumeration for EventSmall { } } -impl From for EventSmall { - fn from(raw: ::Raw) -> Self { +//noinspection RsTraitImplementation +impl EcssEnumeration for EventU16TypedSev { + delegate!(to self.event { + fn pfc(&self) -> u8; + fn write_to_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; @@ -331,10 +497,15 @@ impl From for EventSmall { } } +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); + #[cfg(test)] mod tests { - use super::Event; - use crate::events::{EventProvider, EventSmall, Severity}; + use super::EventU32TypedSev; + use super::*; use spacepackets::ecss::EcssEnumeration; use spacepackets::ByteConversionError; use std::mem::size_of; @@ -343,20 +514,24 @@ mod tests { assert_eq!(size_of::(), val); } - const INFO_EVENT: Event = Event::const_new(Severity::INFO, 0, 0); - const INFO_EVENT_SMALL: EventSmall = EventSmall::const_new(Severity::INFO, 0, 0); - const HIGH_SEV_EVENT: Event = Event::const_new(Severity::HIGH, 0x3FFF, 0xFFFF); - const HIGH_SEV_EVENT_SMALL: EventSmall = EventSmall::const_new(Severity::HIGH, 0x3F, 0xff); + 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); #[test] fn test_normal_from_raw_conversion() { - let conv_from_raw = Event::from(INFO_EVENT.raw()); + 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 = EventSmall::from(INFO_EVENT_SMALL.raw()); + let conv_from_raw = EventU16TypedSev::::try_from(INFO_EVENT_SMALL.raw()) + .expect("Creating typed EventU16 failed"); assert_eq!(conv_from_raw, INFO_EVENT_SMALL); } @@ -408,18 +583,18 @@ mod tests { #[test] fn invalid_group_id_normal() { - assert!(Event::new(Severity::MEDIUM, 2_u16.pow(14), 0).is_none()); + assert!(EventU32TypedSev::::new(2_u16.pow(14), 0).is_none()); } #[test] fn invalid_group_id_small() { - assert!(EventSmall::new(Severity::MEDIUM, 2_u8.pow(6), 0).is_none()); + assert!(EventU16TypedSev::::new(2_u8.pow(6), 0).is_none()); } #[test] fn regular_new() { assert_eq!( - Event::new(Severity::INFO, 0, 0).expect("Creating regular event failed"), + EventU32TypedSev::::new(0, 0).expect("Creating regular event failed"), INFO_EVENT ); } @@ -427,7 +602,7 @@ mod tests { #[test] fn small_new() { assert_eq!( - EventSmall::new(Severity::INFO, 0, 0).expect("Creating regular event failed"), + EventU16TypedSev::::new(0, 0).expect("Creating regular event failed"), INFO_EVENT_SMALL ); } diff --git a/fsrc-core/src/pus/event.rs b/fsrc-core/src/pus/event.rs index bea74a4..bfc3fa0 100644 --- a/fsrc-core/src/pus/event.rs +++ b/fsrc-core/src/pus/event.rs @@ -255,7 +255,7 @@ mod allocvec { #[cfg(test)] mod tests { use super::*; - use crate::events::{Event, Severity}; + use crate::events::{EventU32, Severity}; use crate::pus::tests::CommonTmInfo; use spacepackets::ByteConversionError; use std::collections::VecDeque; @@ -270,7 +270,7 @@ mod tests { #[derive(Debug, Eq, PartialEq)] struct TmInfo { pub common: CommonTmInfo, - pub event: Event, + pub event: EventU32, pub aux_data: Vec, } @@ -284,9 +284,7 @@ mod tests { assert!(tm.source_data().is_some()); let src_data = tm.source_data().unwrap(); assert!(src_data.len() >= 4); - let event = Event::try_from(u32::from_be_bytes(src_data[0..4].try_into().unwrap())); - assert!(event.is_ok()); - let event = event.unwrap(); + 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..]); @@ -313,7 +311,7 @@ mod tests { reporter: &mut EventReporter, sender: &mut TestSender, time_stamp: &[u8], - event: Event, + event: EventU32, severity: Severity, aux_data: Option<&[u8]>, ) { @@ -355,7 +353,7 @@ mod tests { if let Some(err_data) = error_data { error_copy.extend_from_slice(err_data); } - let event = Event::new(severity, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0) + let event = EventU32::new(severity, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0) .expect("Error creating example event"); report_basic_event( &mut reporter, @@ -418,7 +416,7 @@ mod tests { expected_found_len: usize, ) { let time_stamp_empty: [u8; 7] = [0; 7]; - let event = Event::new(Severity::INFO, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0) + 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()); diff --git a/fsrc-core/src/pus/event_man.rs b/fsrc-core/src/pus/event_man.rs index 8195b04..86581f6 100644 --- a/fsrc-core/src/pus/event_man.rs +++ b/fsrc-core/src/pus/event_man.rs @@ -1,5 +1,6 @@ -use crate::events::EventProvider; +use crate::events::{EventU16TypedSev, EventU32TypedSev, GenericEvent, HasSeverity, Severity}; use alloc::boxed::Box; +use core::hash::Hash; use hashbrown::HashSet; use crate::pus::event::EventReporter; @@ -17,7 +18,7 @@ pub use heapless_mod::*; /// structure to track disabled events. A more primitive and embedded friendly /// solution could track this information in a static or pre-allocated list which contains /// the disabled events. -pub trait PusEventMgmtBackendProvider { +pub trait PusEventMgmtBackendProvider { type Error; fn event_enabled(&self, event: &Provider) -> bool; @@ -31,12 +32,12 @@ pub trait PusEventMgmtBackendProvider { /// 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. #[derive(Default)] -pub struct DefaultPusMgmtBackendProvider { +pub struct DefaultPusMgmtBackendProvider { disabled: HashSet, } -impl PusEventMgmtBackendProvider - for DefaultPusMgmtBackendProvider +impl + PusEventMgmtBackendProvider for DefaultPusMgmtBackendProvider { type Error = (); fn event_enabled(&self, event: &Provider) -> bool { @@ -55,18 +56,18 @@ impl PusEventMgmtBackendProvider #[cfg(feature = "heapless")] pub mod heapless_mod { use super::*; - use crate::events::{EventProvider, LargestEventRaw}; + use crate::events::{GenericEvent, LargestEventRaw}; use std::marker::PhantomData; // 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 HeaplessPusMgmtBckendProvider { + pub struct HeaplessPusMgmtBckendProvider { disabled: heapless::FnvIndexSet, phantom: PhantomData, } - impl PusEventMgmtBackendProvider + impl PusEventMgmtBackendProvider for HeaplessPusMgmtBckendProvider { type Error = (); @@ -87,24 +88,76 @@ pub mod heapless_mod { } } -pub struct PusEventManager { +pub struct PusEventManager { reporter: EventReporter, backend: Box>, } -impl PusEventManager { - pub fn handle_event( +impl PusEventManager { + 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, + severity: Severity, sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], - event: Provider, + event: Event, aux_data: Option<&[u8]>, ) -> Result> { if !self.backend.event_enabled(&event) { return Ok(false); } - self.reporter - .event_info(sender, time_stamp, event, aux_data) - .map(|_| true) + match severity { + Severity::INFO => self + .reporter + .event_info(sender, time_stamp, event, aux_data) + .map(|_| true), + Severity::LOW => self + .reporter + .event_low_severity(sender, time_stamp, event, aux_data) + .map(|_| true), + Severity::MEDIUM => self + .reporter + .event_medium_severity(sender, time_stamp, event, aux_data) + .map(|_| true), + Severity::HIGH => self + .reporter + .event_high_severity(sender, time_stamp, event, aux_data) + .map(|_| true), + } + } +} + +impl + PusEventManager> +{ + pub fn generate_pus_event_tm( + &mut self, + sender: &mut (impl EcssTmSender + ?Sized), + time_stamp: &[u8], + event: EventU32TypedSev, + aux_data: Option<&[u8]>, + ) -> Result> { + self.generate_pus_event_tm_generic(SEVERITY::SEVERITY, sender, time_stamp, event, aux_data) + } +} + +impl + PusEventManager> +{ + pub fn generate_pus_event_tm( + &mut self, + sender: &mut (impl EcssTmSender + ?Sized), + time_stamp: &[u8], + event: EventU16TypedSev, + aux_data: Option<&[u8]>, + ) -> Result> { + self.generate_pus_event_tm_generic(SEVERITY::SEVERITY, sender, time_stamp, event, aux_data) } } diff --git a/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index 2f554c1..db82b52 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -1,23 +1,26 @@ #![allow(dead_code, unused_imports)] -use fsrc_core::events::{Event, EventProvider, LargestEventRaw, LargestGroupIdRaw, Severity}; +use fsrc_core::events::{ + EventU32TypedSev, GenericEvent, HasSeverity, LargestEventRaw, LargestGroupIdRaw, Severity, + SeverityInfo, SeverityLow, SeverityMedium, +}; struct GroupIdIntrospection { name: &'static str, id: LargestGroupIdRaw, } -struct EventIntrospection { +struct EventIntrospection { name: &'static str, group_id: GroupIdIntrospection, - event: &'static Event, + event: &'static EventU32TypedSev, info: &'static str, } //#[event(descr="This is some info event")] -const INFO_EVENT_0: Event = Event::const_new(Severity::INFO, 0, 0); +const INFO_EVENT_0: EventU32TypedSev = EventU32TypedSev::const_new(0, 0); // This is ideally auto-generated -const INFO_EVENT_0_INTROSPECTION: EventIntrospection = EventIntrospection { +const INFO_EVENT_0_INTROSPECTION: EventIntrospection = EventIntrospection { name: "INFO_EVENT_0", group_id: GroupIdIntrospection { id: 0, @@ -28,9 +31,9 @@ const INFO_EVENT_0_INTROSPECTION: EventIntrospection = EventIntrospection { }; //#[event(descr="This is some low severity event")] -const SOME_LOW_SEV_EVENT: Event = Event::const_new(Severity::LOW, 0, 12); +const SOME_LOW_SEV_EVENT: EventU32TypedSev = EventU32TypedSev::const_new(0, 12); -const EVENT_LIST: [&'static Event; 2] = [&INFO_EVENT_0, &SOME_LOW_SEV_EVENT]; +//const EVENT_LIST: [&'static Event; 2] = [&INFO_EVENT_0, &SOME_LOW_SEV_EVENT]; //#[event_group] const TEST_GROUP_NAME: u16 = 1; @@ -38,26 +41,27 @@ const TEST_GROUP_NAME: u16 = 1; const TEST_GROUP_NAME_NAME: &'static str = "TEST_GROUP_NAME"; //#[event(desc="Some medium severity event")] -const MEDIUM_SEV_EVENT_IN_OTHER_GROUP: Event = - Event::const_new(Severity::MEDIUM, TEST_GROUP_NAME, 0); +const MEDIUM_SEV_EVENT_IN_OTHER_GROUP: EventU32TypedSev = + EventU32TypedSev::const_new(TEST_GROUP_NAME, 0); // 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, - info: "Some medium severity event", -}; +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, + info: "Some medium severity event", + }; #[test] fn main() { - let test = stringify!(INFO_EVENT); - println!("{:?}", test); - for event in EVENT_LIST { - println!("{:?}", event); - } + //let test = stringify!(INFO_EVENT); + //println!("{:?}", test); + //for event in EVENT_LIST { + // println!("{:?}", event); + //} //let test_struct = } From ac8718f1afcf3dd8ba353f696db7ecb86ca4bf3a Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 24 Oct 2022 14:03:21 +0200 Subject: [PATCH 04/40] cool stuff --- fsrc-core/src/events.rs | 11 ++++++++ fsrc-core/tests/pus_events.rs | 47 +++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/fsrc-core/src/events.rs b/fsrc-core/src/events.rs index 4a27cf0..2523a2e 100644 --- a/fsrc-core/src/events.rs +++ b/fsrc-core/src/events.rs @@ -279,6 +279,17 @@ impl EventU32 { }, } } + + pub const fn const_from_info(event: EventU32TypedSev) -> Self { + Self { + base: event.event.base + } + } + pub const fn const_from_medium(event: EventU32TypedSev) -> Self { + Self { + base: event.event.base + } + } } impl EventU32TypedSev { diff --git a/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index db82b52..f0edbe2 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -1,32 +1,34 @@ #![allow(dead_code, unused_imports)] -use fsrc_core::events::{ - EventU32TypedSev, GenericEvent, HasSeverity, LargestEventRaw, LargestGroupIdRaw, Severity, - SeverityInfo, SeverityLow, SeverityMedium, -}; +use std::convert::AsRef; +use fsrc_core::events::{EventU32, EventU32TypedSev, GenericEvent, HasSeverity, LargestEventRaw, LargestGroupIdRaw, Severity, SeverityInfo, SeverityLow, SeverityMedium}; + +#[derive(Debug)] struct GroupIdIntrospection { name: &'static str, id: LargestGroupIdRaw, } -struct EventIntrospection { +#[derive(Debug)] +struct EventIntrospection { name: &'static str, group_id: GroupIdIntrospection, - event: &'static EventU32TypedSev, + 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 { +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, + event: &INFO_EVENT_0_ERASED, info: "This is some info event", }; @@ -43,19 +45,38 @@ const TEST_GROUP_NAME_NAME: &'static 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 = +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, + 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: [&'static 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: [&'static EventIntrospection; 1] = [ + &MEDIUM_SEV_EVENT_IN_OTHER_GROUP_INTROSPECTION +]; +const BLAH: &'static [&EventIntrospection] = &INTROSPECTION_FOR_TEST_GROUP_NAME; + +const ALL_EVENTS: [&'static [&EventIntrospection]; 2] = [ + INTROSPECTION_FOR_TABLE, + BLAH +]; + #[test] fn main() { //let test = stringify!(INFO_EVENT); @@ -63,5 +84,11 @@ fn main() { //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 = } From d93dd92fda2c714c5c65d262411899bf620fc85d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 25 Oct 2022 23:32:12 +0200 Subject: [PATCH 05/40] completion and fixes --- fsrc-core/src/events.rs | 46 +++++++++++++++++++++++++++-------- fsrc-core/tests/pus_events.rs | 16 ++++++------ 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/fsrc-core/src/events.rs b/fsrc-core/src/events.rs index 2523a2e..12d71a4 100644 --- a/fsrc-core/src/events.rs +++ b/fsrc-core/src/events.rs @@ -225,6 +225,16 @@ macro_rules! try_from_impls { }; } +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, @@ -280,16 +290,10 @@ impl EventU32 { } } - pub const fn const_from_info(event: EventU32TypedSev) -> Self { - Self { - base: event.event.base - } - } - pub const fn const_from_medium(event: EventU32TypedSev) -> Self { - Self { - base: event.event.base - } - } + 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 { @@ -431,6 +435,10 @@ impl EventU16 { }, } } + 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 { /// Generate a small event. The raw representation of a small event has 16 bits. @@ -532,6 +540,9 @@ mod tests { 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()) @@ -693,4 +704,19 @@ mod tests { 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/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index f0edbe2..cb7e1e1 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -40,7 +40,7 @@ const SOME_LOW_SEV_EVENT: EventU32TypedSev = EventU32TypedSev::cons //#[event_group] const TEST_GROUP_NAME: u16 = 1; // Auto-generated? -const TEST_GROUP_NAME_NAME: &'static str = "TEST_GROUP_NAME"; +const TEST_GROUP_NAME_NAME: &str = "TEST_GROUP_NAME"; //#[event(desc="Some medium severity event")] const MEDIUM_SEV_EVENT_IN_OTHER_GROUP: EventU32TypedSev = @@ -60,21 +60,21 @@ const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_INTROSPECTION: EventIntrospection = }; const CONST_SLICE: &'static [u8] = &[0, 1, 2, 3]; -const INTROSPECTION_FOR_TEST_GROUP_0: [&'static EventIntrospection; 2] = [ +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_TABLE: &'static [&EventIntrospection] = &INTROSPECTION_FOR_TEST_GROUP_0; -const INTROSPECTION_FOR_TEST_GROUP_NAME: [&'static EventIntrospection; 1] = [ +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 BLAH: &'static [&EventIntrospection] = &INTROSPECTION_FOR_TEST_GROUP_NAME; -const ALL_EVENTS: [&'static [&EventIntrospection]; 2] = [ - INTROSPECTION_FOR_TABLE, - BLAH +const ALL_EVENTS: [&[&EventIntrospection]; 2] = [ + &INTROSPECTION_FOR_TEST_GROUP_0, + &INTROSPECTION_FOR_TEST_GROUP_NAME ]; #[test] From 18a3be84393ceffad31055cb70455dfc22f732d3 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 25 Oct 2022 23:38:45 +0200 Subject: [PATCH 06/40] cargo doc --- fsrc-core/src/events.rs | 45 ++++++++++++++++------------------- fsrc-core/tests/pus_events.rs | 40 +++++++++++++++---------------- 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/fsrc-core/src/events.rs b/fsrc-core/src/events.rs index 12d71a4..b2dbec8 100644 --- a/fsrc-core/src/events.rs +++ b/fsrc-core/src/events.rs @@ -22,6 +22,7 @@ pub enum Severity { pub trait HasSeverity { const SEVERITY: Severity; } + #[derive(Debug, PartialEq, Eq)] pub struct SeverityInfo {} impl HasSeverity for SeverityInfo { @@ -229,10 +230,10 @@ 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 + base: event.event.base, } } - } + }; } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] @@ -255,6 +256,17 @@ impl From> for EventU32 { 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, @@ -297,17 +309,8 @@ impl EventU32 { } impl EventU32TypedSev { - /// 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 + /// 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, @@ -418,6 +421,7 @@ impl EventU16 { }) } + /// Const version of [new], but panics on invalid group ID input values. pub const fn const_new( severity: Severity, group_id: ::GroupId, @@ -440,18 +444,10 @@ impl EventU16 { const_from_fn!(const_from_medium, EventU16TypedSev, SeverityMedium); const_from_fn!(const_from_high, EventU16TypedSev, SeverityHigh); } + impl EventU16TypedSev { - /// 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 + /// 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, @@ -463,6 +459,7 @@ impl EventU16TypedSev { }) } + /// Const version of [new], but panics on invalid group ID input values. pub const fn const_new( group_id: ::GroupId, unique_id: ::UniqueId, diff --git a/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index cb7e1e1..b304939 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -1,7 +1,10 @@ #![allow(dead_code, unused_imports)] +use fsrc_core::events::{ + EventU32, EventU32TypedSev, GenericEvent, HasSeverity, LargestEventRaw, LargestGroupIdRaw, + Severity, SeverityInfo, SeverityLow, SeverityMedium, +}; use std::convert::AsRef; -use fsrc_core::events::{EventU32, EventU32TypedSev, GenericEvent, HasSeverity, LargestEventRaw, LargestGroupIdRaw, Severity, SeverityInfo, SeverityLow, SeverityMedium}; #[derive(Debug)] struct GroupIdIntrospection { @@ -45,36 +48,33 @@ 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); +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 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_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 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 + &INTROSPECTION_FOR_TEST_GROUP_NAME, ]; #[test] From 4b5b11486e0e38af3b51582b2a43a972aa639697 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 26 Oct 2022 00:23:10 +0200 Subject: [PATCH 07/40] doc cfg support --- fsrc-core/Cargo.toml | 4 +++ fsrc-core/src/event_man.rs | 2 +- fsrc-core/src/events.rs | 41 +++++++++++++++++++++++++++---- fsrc-core/src/lib.rs | 3 +++ fsrc-core/src/pus/event_man.rs | 9 ++++--- fsrc-core/src/pus/verification.rs | 4 +-- spacepackets | 2 +- 7 files changed, 53 insertions(+), 12 deletions(-) diff --git a/fsrc-core/Cargo.toml b/fsrc-core/Cargo.toml index 69df909..35072fe 100644 --- a/fsrc-core/Cargo.toml +++ b/fsrc-core/Cargo.toml @@ -42,3 +42,7 @@ default = ["std"] std = ["downcast-rs/std", "alloc", "bus", "postcard/use-std", "crossbeam-channel/std"] alloc = [] heapless = [] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "doc_cfg"] diff --git a/fsrc-core/src/event_man.rs b/fsrc-core/src/event_man.rs index 5d1c6ba..33884b6 100644 --- a/fsrc-core/src/event_man.rs +++ b/fsrc-core/src/event_man.rs @@ -1,4 +1,4 @@ -//! [Event][crate::events::Event] management and forwarding +//! Event management and forwarding use crate::events::{EventU16TypedSev, EventU32, GenericEvent, HasSeverity}; use alloc::boxed::Box; use alloc::vec; diff --git a/fsrc-core/src/events.rs b/fsrc-core/src/events.rs index b2dbec8..02f4dfd 100644 --- a/fsrc-core/src/events.rs +++ b/fsrc-core/src/events.rs @@ -1,5 +1,32 @@ //! 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 fsrc_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::hash::Hash; use delegate::delegate; use spacepackets::ecss::{EcssEnumeration, ToBeBytes}; @@ -23,24 +50,28 @@ pub trait HasSeverity { const SEVERITY: Severity; } +/// Type level support struct #[derive(Debug, PartialEq, Eq)] pub struct SeverityInfo {} impl HasSeverity for SeverityInfo { const SEVERITY: Severity = Severity::INFO; } +/// Type level support struct #[derive(Debug, PartialEq, Eq)] pub struct SeverityLow {} impl HasSeverity for SeverityLow { const SEVERITY: Severity = Severity::LOW; } +/// Type level support struct #[derive(Debug, PartialEq, Eq)] pub struct SeverityMedium {} impl HasSeverity for SeverityMedium { const SEVERITY: Severity = Severity::MEDIUM; } +/// Type level support struct #[derive(Debug, PartialEq, Eq)] pub struct SeverityHigh {} impl HasSeverity for SeverityHigh { @@ -76,7 +107,7 @@ impl TryFrom for Severity { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct EventBase { +struct EventBase { severity: Severity, group_id: GID, unique_id: UID, @@ -322,7 +353,7 @@ impl EventU32TypedSev { }) } - /// Const version of [new], but panics on invalid group ID input values. + /// Const version of [Self::new], but panics on invalid group ID input values. pub const fn const_new( group_id: ::GroupId, unique_id: ::UniqueId, @@ -421,7 +452,7 @@ impl EventU16 { }) } - /// Const version of [new], but panics on invalid group ID input values. + /// Const version of [Self::new], but panics on invalid group ID input values. pub const fn const_new( severity: Severity, group_id: ::GroupId, @@ -459,7 +490,7 @@ impl EventU16TypedSev { }) } - /// Const version of [new], but panics on invalid group ID input values. + /// Const version of [Self::new], but panics on invalid group ID input values. pub const fn const_new( group_id: ::GroupId, unique_id: ::UniqueId, diff --git a/fsrc-core/src/lib.rs b/fsrc-core/src/lib.rs index 02298a0..e8349da 100644 --- a/fsrc-core/src/lib.rs +++ b/fsrc-core/src/lib.rs @@ -15,13 +15,16 @@ 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; #[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] pub mod pool; pub mod pus; pub mod tmtc; diff --git a/fsrc-core/src/pus/event_man.rs b/fsrc-core/src/pus/event_man.rs index 86581f6..5b48143 100644 --- a/fsrc-core/src/pus/event_man.rs +++ b/fsrc-core/src/pus/event_man.rs @@ -6,12 +6,14 @@ use hashbrown::HashSet; use crate::pus::event::EventReporter; use crate::pus::{EcssTmError, EcssTmSender}; #[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. +/// 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) @@ -59,6 +61,7 @@ pub mod heapless_mod { 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)] diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 479dc11..41b74e1 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -987,7 +987,7 @@ mod stdmod { } /// Verification sender with a [mpsc::Sender] backend. - /// It implements the [VerificationSender] trait to be used as PUS Verification TM sender. + /// It implements the [EcssTmSender] trait to be used as PUS Verification TM sender. impl MpscVerifSender { pub fn new(tm_store: SharedPool, tx: mpsc::Sender) -> Self { Self { @@ -1014,7 +1014,7 @@ mod stdmod { } /// Verification sender with a [crossbeam_channel::Sender] backend. - /// It implements the [VerificationSender] trait to be used as PUS Verification TM sender + /// It implements the [EcssTmSender] trait to be used as PUS Verification TM sender pub struct CrossbeamVerifSender { base: StdSenderBase>, } diff --git a/spacepackets b/spacepackets index a2673c9..65e85f2 160000 --- a/spacepackets +++ b/spacepackets @@ -1 +1 @@ -Subproject commit a2673c98707ecbbabb9535bef607025c92b54724 +Subproject commit 65e85f20e03507f19a0d5086ee19360ff7596c27 From 294ae2cf52d0fdc33c97c5be8636a5ed1cb6201c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 26 Oct 2022 00:29:11 +0200 Subject: [PATCH 08/40] some minor adaptions --- fsrc-core/src/hal/mod.rs | 1 + fsrc-core/src/pus/mod.rs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fsrc-core/src/hal/mod.rs b/fsrc-core/src/hal/mod.rs index ddaf8f3..c422a72 100644 --- a/fsrc-core/src/hal/mod.rs +++ b/fsrc-core/src/hal/mod.rs @@ -1,3 +1,4 @@ //! # Hardware Abstraction Layer module #[cfg(feature = "std")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] pub mod host; diff --git a/fsrc-core/src/pus/mod.rs b/fsrc-core/src/pus/mod.rs index a09b398..82e49f9 100644 --- a/fsrc-core/src/pus/mod.rs +++ b/fsrc-core/src/pus/mod.rs @@ -32,8 +32,9 @@ impl From for EcssTmError { } } -/// Generic trait for a user supplied sender object. This sender object is responsible for sending -/// telemetry to a TM sink. The [Downcast] trait +/// Generic trait for a user supplied sender object. +/// +/// This sender object is responsible for sending telemetry to a TM sink. The [Downcast] trait /// is implemented to allow passing the sender as a boxed trait object and still retrieve the /// concrete type at a later point. pub trait EcssTmSender: Downcast + Send { From c57a4efd88cf7cd3b0681b8b557cbe32a02b2271 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 27 Oct 2022 23:56:47 +0200 Subject: [PATCH 09/40] needs some research --- fsrc-core/src/event_man.rs | 92 +++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/fsrc-core/src/event_man.rs b/fsrc-core/src/event_man.rs index 33884b6..fec1b6c 100644 --- a/fsrc-core/src/event_man.rs +++ b/fsrc-core/src/event_man.rs @@ -15,7 +15,10 @@ pub trait EventListener { type Error; fn id(&self) -> u32; - fn send_to(&mut self, event: Provider) -> Result<(), Self::Error>; + fn send_to_no_data(&mut self, event: Provider) -> Result<(), Self::Error> { + self.send_to(event, None) + } + fn send_to(&mut self, event: Provider, aux_data: Option<&[u8]>) -> Result<(), Self::Error>; } struct Listener { @@ -24,7 +27,7 @@ struct Listener { } pub trait ReceivesAllEvent { - fn receive(&mut self) -> Option; + fn receive(&mut self) -> Option<(Provider, Option<&[u8]>)>; } pub struct EventManager { @@ -114,23 +117,28 @@ impl EventManager { pub fn try_event_handling(&mut self) -> Result, E> { let mut err_status = None; let mut num_recipients = 0; - let mut send_handler = |event: Provider, llist: &mut Vec>| { - for listener in llist.iter_mut() { - if let Err(e) = listener.dest.send_to(event) { - err_status = Some(Err(e)); - } else { - num_recipients += 1; + let mut send_handler = + |event: Provider, aux_data: Option<&[u8]>, llist: &mut Vec>| { + for listener in llist.iter_mut() { + if let Err(e) = listener.dest.send_to(event, aux_data) { + err_status = Some(Err(e)); + } else { + num_recipients += 1; + } } - } - }; - if let Some(event) = self.event_receiver.receive() { + }; + if let Some((event, aux_data)) = self.event_receiver.receive() { let single_key = ListenerType::Single(event.raw_as_largest_type()); if self.listeners.contains_key(&single_key) { - send_handler(event, self.listeners.get_mut(&single_key).unwrap()); + send_handler( + event, + aux_data, + self.listeners.get_mut(&single_key).unwrap(), + ); } let group_key = ListenerType::Group(event.group_id_as_largest_type()); if self.listeners.contains_key(&group_key) { - send_handler(event, self.listeners.get_mut(&group_key).unwrap()); + send_handler(event, aux_data, self.listeners.get_mut(&group_key).unwrap()); } if let Some(err) = err_status { return err; @@ -151,36 +159,46 @@ mod tests { use std::thread; use std::time::Duration; - struct EventReceiver { - mpsc_receiver: Receiver, + type EventAndParams<'a> = (EventU32, Option<&'a [u8]>); + + struct EventReceiver<'a> { + mpsc_receiver: Receiver>, } - impl ReceivesAllEvent for EventReceiver { - fn receive(&mut self) -> Option { - self.mpsc_receiver.try_recv().ok() + + impl ReceivesAllEvent for EventReceiver<'_> { + fn receive(&mut self) -> Option<(EventU32, Option<&[u8]>)> { + if let Some((event, _params)) = self.mpsc_receiver.try_recv().ok() { + return Some((event, None)); + } + None } } #[derive(Clone)] - struct MpscEventSenderQueue { + struct MpscEventSenderQueue<'a> { id: u32, - mpsc_sender: Sender, + mpsc_sender: Sender>, } - impl EventListener for MpscEventSenderQueue { - type Error = SendError; + impl<'a> EventListener for MpscEventSenderQueue<'a> { + type Error = SendError>; fn id(&self) -> u32 { self.id } - fn send_to(&mut self, event: EventU32) -> Result<(), Self::Error> { - self.mpsc_sender.send(event) + fn send_to( + &mut self, + event: EventU32, + _aux_data: Option<&[u8]>, + ) -> Result<(), Self::Error> { + self.mpsc_sender.send((event, None)) } } - fn check_next_event(expected: EventU32, receiver: &Receiver) { + fn check_next_event(expected: EventU32, receiver: &Receiver) { for _ in 0..5 { if let Ok(event) = receiver.try_recv() { - assert_eq!(event, expected); + assert_eq!(event.0, expected); break; } thread::sleep(Duration::from_millis(1)); @@ -205,7 +223,7 @@ mod tests { let event_man_receiver = EventReceiver { mpsc_receiver: manager_queue, }; - let mut event_man: EventManager, EventU32> = + let mut event_man: EventManager, EventU32> = EventManager::new(Box::new(event_man_receiver)); let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap(); let event_grp_1_0 = EventU32::new(Severity::HIGH, 1, 0).unwrap(); @@ -224,7 +242,7 @@ mod tests { // Test event with one listener event_sender - .send(event_grp_0) + .send((event_grp_0, None)) .expect("Sending single error failed"); let res = event_man.try_event_handling(); assert!(res.is_ok()); @@ -233,7 +251,7 @@ mod tests { // Test event which is sent to all group listeners event_sender - .send(event_grp_1_0) + .send((event_grp_1_0, None)) .expect("Sending group error failed"); let res = event_man.try_event_handling(); assert!(res.is_ok()); @@ -248,7 +266,7 @@ mod tests { let event_man_receiver = EventReceiver { mpsc_receiver: manager_queue, }; - let mut event_man: EventManager, EventU32> = + let mut event_man: EventManager, EventU32> = EventManager::new(Box::new(event_man_receiver)); let res = event_man.try_event_handling(); assert!(res.is_ok()); @@ -266,10 +284,10 @@ mod tests { event_man.subscribe_group(event_grp_1_0.group_id(), event_grp_0_and_1_listener); event_sender - .send(event_grp_0) + .send((event_grp_0, None)) .expect("Sending Event Group 0 failed"); event_sender - .send(event_grp_1_0) + .send((event_grp_1_0, None)) .expect("Sendign Event Group 1 failed"); let res = event_man.try_event_handling(); assert!(res.is_ok()); @@ -290,7 +308,7 @@ mod tests { let event_man_receiver = EventReceiver { mpsc_receiver: manager_queue, }; - let mut event_man: EventManager, EventU32> = + let mut event_man: EventManager, EventU32> = 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(); @@ -307,7 +325,7 @@ mod tests { event_man.subscribe_single(event_0, event_listener_0.clone()); event_man.subscribe_single(event_0, event_listener_1); event_sender - .send(event_0) + .send((event_0, None)) .expect("Triggering Event 0 failed"); let res = event_man.try_event_handling(); assert!(res.is_ok()); @@ -316,10 +334,10 @@ mod tests { check_next_event(event_0, &event_0_rx_1); event_man.subscribe_group(event_1.group_id(), event_listener_0.clone()); event_sender - .send(event_0) + .send((event_0, None)) .expect("Triggering Event 0 failed"); event_sender - .send(event_1) + .send((event_1, None)) .expect("Triggering Event 1 failed"); // 3 Events messages will be sent now @@ -336,7 +354,7 @@ mod tests { // Double insertion should be detected, result should remain the same event_man.subscribe_group(event_1.group_id(), event_listener_0); event_sender - .send(event_1) + .send((event_1, None)) .expect("Triggering Event 1 failed"); let res = event_man.try_event_handling(); assert!(res.is_ok()); From 10849229e6237b5746f9c1ad2eb437100ac6a762 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 28 Oct 2022 02:02:28 +0200 Subject: [PATCH 10/40] more docs for event manager --- fsrc-core/src/event_man.rs | 170 +++++++++++++++++++++++-------------- 1 file changed, 107 insertions(+), 63 deletions(-) diff --git a/fsrc-core/src/event_man.rs b/fsrc-core/src/event_man.rs index fec1b6c..cce5e12 100644 --- a/fsrc-core/src/event_man.rs +++ b/fsrc-core/src/event_man.rs @@ -1,5 +1,25 @@ //! Event management and forwarding -use crate::events::{EventU16TypedSev, EventU32, GenericEvent, HasSeverity}; +//! +//! This module provides components to perform event routing. The most important component for this +//! task is the [EventManager]. It has a map of event listeners and uses a dynamic [EventReceiver] +//! instance to receive all events and then route them to event subscribers where appropriate. +//! +//! One common use case for satellite systems is to offer a light-weight publish-subscribe mechanism +//! and IPC mechanism for software and hardware events which are also packaged as telemetry. +//! This can be done with the [EventManager] like this: +//! +//! 1. Provide a concrete [SendEventProvider] implementation and a concrete [EventReceiver] +//! implementation. These 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. It is recommended that these implementations +//! derive [Clone]. +//! 2. Each event creator gets a sender component which allows it to send events to the manager. +//! 3. The event manager receives all receiver ends so all events are routed to the +//! manager. +//! 4. Each event receiver and/or subscriber gets a receiver component. The sender component is +//! used with the [SendEventProvider] trait and the subscription API provided by the +//! [EventManager] to subscribe for individual events or whole group of events. +use crate::events::{GenericEvent, LargestEventRaw, LargestGroupIdRaw}; use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; @@ -7,32 +27,42 @@ use hashbrown::HashMap; #[derive(PartialEq, Eq, Hash, Copy, Clone)] enum ListenerType { - Single(u32), - Group(u16), + Single(LargestEventRaw), + Group(LargestGroupIdRaw), + All, } -pub trait EventListener { +pub trait SendEventProvider { type Error; fn id(&self) -> u32; - fn send_to_no_data(&mut self, event: Provider) -> Result<(), Self::Error> { - self.send_to(event, None) + fn send_no_data(&mut self, event: Provider) -> Result<(), Self::Error> { + self.send(event, None) } - fn send_to(&mut self, event: Provider, aux_data: Option<&[u8]>) -> Result<(), Self::Error>; + fn send(&mut self, event: Provider, aux_data: Option<&[u8]>) -> Result<(), Self::Error>; } -struct Listener { +struct Listener { ltype: ListenerType, - dest: Box>, + send_provider: Box>, } -pub trait ReceivesAllEvent { - fn receive(&mut self) -> Option<(Provider, Option<&[u8]>)>; +/// 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, aux_data: &mut [u8]) -> Option; } -pub struct EventManager { - listeners: HashMap>>, - event_receiver: Box>, +/// Generic event manager implementation. +pub struct EventManager { + aux_data_buf: Vec, + listeners: HashMap>>, + event_receiver: Box>, } pub enum HandlerResult { @@ -40,48 +70,50 @@ pub enum HandlerResult { Handled(u32, Provider), } -impl EventManager { - pub fn new(event_receiver: Box>) -> Self { +impl EventManager { + pub fn new(event_receiver: Box>, buf_len_aux_data: usize) -> Self { EventManager { + aux_data_buf: vec![0; buf_len_aux_data], listeners: HashMap::new(), event_receiver, } } -} - -impl EventManager { pub fn subscribe_single( &mut self, - event: EventU32, - dest: impl EventListener + 'static, + event: Event, + dest: impl SendEventProvider + 'static, ) { self.update_listeners(ListenerType::Single(event.raw_as_largest_type()), dest); } pub fn subscribe_group( &mut self, - group_id: ::GroupId, - dest: impl EventListener + 'static, + group_id: LargestGroupIdRaw, + dest: impl SendEventProvider + 'static, ) { self.update_listeners(ListenerType::Group(group_id), dest); } -} -impl EventManager> { - pub fn subscribe_single( - &mut self, - event: EventU16TypedSev, - dest: impl EventListener, Error = E> + 'static, - ) { - self.update_listeners(ListenerType::Single(event.raw_as_largest_type()), dest); + pub fn subscribe_all(&mut self, dest: impl SendEventProvider + 'static) { + self.update_listeners(ListenerType::All, dest); } - pub fn subscribe_group( + /// Helper function which removes single subscriptions for which a group subscription already + /// exists. + pub fn remove_single_subscriptions_for_group( &mut self, - group_id: as GenericEvent>::GroupId, - dest: impl EventListener, Error = E> + 'static, + group_id: LargestGroupIdRaw, + dest: impl SendEventProvider + 'static ) { - self.update_listeners(ListenerType::Group(group_id.into()), dest); + if self.listeners.contains_key(&ListenerType::Group(group_id)) { + for (ltype, listeners) in &mut self.listeners { + if let ListenerType::Single(_) = ltype { + listeners.retain(|f| { + f.send_provider.id() != dest.id() + }); + } + } + } } } @@ -89,27 +121,27 @@ impl EventManager { fn update_listeners( &mut self, key: ListenerType, - dest: impl EventListener + 'static, + dest: impl SendEventProvider + 'static, ) { if !self.listeners.contains_key(&key) { self.listeners.insert( key, vec![Listener { ltype: key, - dest: Box::new(dest), + send_provider: Box::new(dest), }], ); } else { let vec = self.listeners.get_mut(&key).unwrap(); // To prevent double insertions for entry in vec.iter() { - if entry.ltype == key && entry.dest.id() == dest.id() { + if entry.ltype == key && entry.send_provider.id() == dest.id() { return; } } vec.push(Listener { ltype: key, - dest: Box::new(dest), + send_provider: Box::new(dest), }); } } @@ -120,25 +152,35 @@ impl EventManager { let mut send_handler = |event: Provider, aux_data: Option<&[u8]>, llist: &mut Vec>| { for listener in llist.iter_mut() { - if let Err(e) = listener.dest.send_to(event, aux_data) { + if let Err(e) = listener.send_provider.send(event, aux_data) { err_status = Some(Err(e)); } else { num_recipients += 1; } } }; - if let Some((event, aux_data)) = self.event_receiver.receive() { + if let Some(event) = self + .event_receiver + .receive(self.aux_data_buf.as_mut_slice()) + { let single_key = ListenerType::Single(event.raw_as_largest_type()); if self.listeners.contains_key(&single_key) { send_handler( event, - aux_data, + Some(self.aux_data_buf.as_slice()), self.listeners.get_mut(&single_key).unwrap(), ); } let group_key = ListenerType::Group(event.group_id_as_largest_type()); if self.listeners.contains_key(&group_key) { - send_handler(event, aux_data, self.listeners.get_mut(&group_key).unwrap()); + send_handler( + event, + Some(self.aux_data_buf.as_slice()), + self.listeners.get_mut(&group_key).unwrap(), + ); + } + if let Some(all_receivers) = self.listeners.get_mut(&ListenerType::All) { + send_handler(event, Some(self.aux_data_buf.as_slice()), all_receivers); } if let Some(err) = err_status { return err; @@ -151,24 +193,30 @@ impl EventManager { #[cfg(test)] mod tests { - use super::{EventListener, HandlerResult, ReceivesAllEvent}; + use super::{EventReceiver, HandlerResult, SendEventProvider}; use crate::event_man::EventManager; use crate::events::{EventU32, GenericEvent, Severity}; use alloc::boxed::Box; use std::sync::mpsc::{channel, Receiver, SendError, Sender}; - use std::thread; use std::time::Duration; + use std::{thread, vec}; + use vec::Vec; type EventAndParams<'a> = (EventU32, Option<&'a [u8]>); - struct EventReceiver<'a> { - mpsc_receiver: Receiver>, + struct MpscEventReceiver { + mpsc_receiver: Receiver<(EventU32, Option>)>, } - impl ReceivesAllEvent for EventReceiver<'_> { - fn receive(&mut self) -> Option<(EventU32, Option<&[u8]>)> { - if let Some((event, _params)) = self.mpsc_receiver.try_recv().ok() { - return Some((event, None)); + impl EventReceiver for MpscEventReceiver { + fn receive(&mut self, aux_data: &mut [u8]) -> Option { + if let Some((event, params)) = self.mpsc_receiver.try_recv().ok() { + if let Some(params) = params { + if params.len() < aux_data.len() { + aux_data[0..params.len()].copy_from_slice(params.as_slice()) + } + } + return Some(event); } None } @@ -180,17 +228,13 @@ mod tests { mpsc_sender: Sender>, } - impl<'a> EventListener for MpscEventSenderQueue<'a> { + impl<'a> SendEventProvider for MpscEventSenderQueue<'a> { type Error = SendError>; fn id(&self) -> u32 { self.id } - fn send_to( - &mut self, - event: EventU32, - _aux_data: Option<&[u8]>, - ) -> Result<(), Self::Error> { + fn send(&mut self, event: EventU32, _aux_data: Option<&[u8]>) -> Result<(), Self::Error> { self.mpsc_sender.send((event, None)) } } @@ -220,11 +264,11 @@ mod tests { #[test] fn test_basic() { let (event_sender, manager_queue) = channel(); - let event_man_receiver = EventReceiver { + let event_man_receiver = MpscEventReceiver { mpsc_receiver: manager_queue, }; let mut event_man: EventManager, EventU32> = - EventManager::new(Box::new(event_man_receiver)); + EventManager::new(Box::new(event_man_receiver), 128); 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(); @@ -263,11 +307,11 @@ mod tests { #[test] fn test_multi_group() { let (event_sender, manager_queue) = channel(); - let event_man_receiver = EventReceiver { + let event_man_receiver = MpscEventReceiver { mpsc_receiver: manager_queue, }; let mut event_man: EventManager, EventU32> = - EventManager::new(Box::new(event_man_receiver)); + EventManager::new(Box::new(event_man_receiver), 128); let res = event_man.try_event_handling(); assert!(res.is_ok()); let hres = res.unwrap(); @@ -305,11 +349,11 @@ mod tests { #[test] fn test_listening_to_same_event_and_multi_type() { let (event_sender, manager_queue) = channel(); - let event_man_receiver = EventReceiver { + let event_man_receiver = MpscEventReceiver { mpsc_receiver: manager_queue, }; let mut event_man: EventManager, EventU32> = - EventManager::new(Box::new(event_man_receiver)); + EventManager::new(Box::new(event_man_receiver), 128); 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(); From 819c121fd7002fc4a0547dd2c83013be367291ef Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 28 Oct 2022 02:02:50 +0200 Subject: [PATCH 11/40] cargo fmt --- fsrc-core/src/event_man.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fsrc-core/src/event_man.rs b/fsrc-core/src/event_man.rs index cce5e12..d050a70 100644 --- a/fsrc-core/src/event_man.rs +++ b/fsrc-core/src/event_man.rs @@ -103,14 +103,12 @@ impl EventManager { pub fn remove_single_subscriptions_for_group( &mut self, group_id: LargestGroupIdRaw, - dest: impl SendEventProvider + 'static + dest: impl SendEventProvider + 'static, ) { if self.listeners.contains_key(&ListenerType::Group(group_id)) { for (ltype, listeners) in &mut self.listeners { if let ListenerType::Single(_) = ltype { - listeners.retain(|f| { - f.send_provider.id() != dest.id() - }); + listeners.retain(|f| f.send_provider.id() != dest.id()); } } } From b7da30d741e5047316d1e314cbc2184b856f958b Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 29 Oct 2022 01:10:51 +0200 Subject: [PATCH 12/40] added unittest for all subscription --- fsrc-core/src/event_man.rs | 39 +++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/fsrc-core/src/event_man.rs b/fsrc-core/src/event_man.rs index d050a70..6135613 100644 --- a/fsrc-core/src/event_man.rs +++ b/fsrc-core/src/event_man.rs @@ -18,7 +18,12 @@ //! manager. //! 4. Each event receiver and/or subscriber gets a receiver component. The sender component is //! used with the [SendEventProvider] trait and the subscription API provided by the -//! [EventManager] to subscribe for individual events or whole group of events. +//! [EventManager] to subscribe for individual events, whole group of events or all events +//! +//! 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. use crate::events::{GenericEvent, LargestEventRaw, LargestGroupIdRaw}; use alloc::boxed::Box; use alloc::vec; @@ -402,4 +407,36 @@ mod tests { 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 { + mpsc_receiver: manager_queue, + }; + let mut event_man: EventManager, EventU32> = + EventManager::new(Box::new(event_man_receiver), 128); + 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); + 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); + } } From 0b94256b9d8384c2ab9767522e7d25a2ac00b49c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 29 Oct 2022 01:16:00 +0200 Subject: [PATCH 13/40] some clarifications --- fsrc-core/src/event_man.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/fsrc-core/src/event_man.rs b/fsrc-core/src/event_man.rs index 6135613..9627c79 100644 --- a/fsrc-core/src/event_man.rs +++ b/fsrc-core/src/event_man.rs @@ -1,24 +1,27 @@ //! Event management and forwarding //! //! This module provides components to perform event routing. The most important component for this -//! task is the [EventManager]. It has a map of event listeners and uses a dynamic [EventReceiver] +//! task is the [EventManager]. It uses a map of event listeners and uses a dynamic [EventReceiver] //! instance to receive all events and then route them to event subscribers where appropriate. //! //! One common use case for satellite systems is to offer a light-weight publish-subscribe mechanism -//! and IPC mechanism for software and hardware events which are also packaged as telemetry. -//! This can be done with the [EventManager] like this: +//! and IPC mechanism for software and hardware events which are also packaged as telemetry or can +//! trigger a system response. This can be done with the [EventManager] like this: //! //! 1. Provide a concrete [SendEventProvider] implementation and a concrete [EventReceiver] //! implementation. These 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. It is recommended that these implementations //! derive [Clone]. -//! 2. Each event creator gets a sender component which allows it to send events to the manager. -//! 3. The event manager receives all receiver ends so all events are routed to the +//! 2. Each event creator gets a (cloned) sender component which allows it to send events to the //! manager. -//! 4. Each event receiver and/or subscriber gets a receiver component. The sender component is -//! used with the [SendEventProvider] trait and the subscription API provided by the -//! [EventManager] to subscribe for individual events, whole group of events or all events +//! 3. The event manager receives the receiver component so all events are routed to the +//! manager. +//! 4. Additional channels are created for each event receiver and/or subscriber. +//! The sender component is used with the [SendEventProvider] trait and the subscription API +//! provided by the [EventManager] to subscribe for individual events, whole group of events or +//! all events. The receiver/subscribers can then receive all subscribed events via the receiver +//! end. //! //! 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. From 190b6d035a8d19cbe648e145412054ae7f329f99 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 29 Oct 2022 11:23:19 +0200 Subject: [PATCH 14/40] some components are generic --- fsrc-core/src/event_man.rs | 71 +++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/fsrc-core/src/event_man.rs b/fsrc-core/src/event_man.rs index 9627c79..27d3b04 100644 --- a/fsrc-core/src/event_man.rs +++ b/fsrc-core/src/event_man.rs @@ -33,6 +33,9 @@ use alloc::vec; use alloc::vec::Vec; use hashbrown::HashMap; +#[cfg(feature = "std")] +pub use stdmod::MpscEventReceiver; + #[derive(PartialEq, Eq, Hash, Copy, Clone)] enum ListenerType { Single(LargestEventRaw), @@ -197,25 +200,26 @@ impl EventManager { } } -#[cfg(test)] -mod tests { - use super::{EventReceiver, HandlerResult, SendEventProvider}; - use crate::event_man::EventManager; - use crate::events::{EventU32, GenericEvent, Severity}; - use alloc::boxed::Box; - use std::sync::mpsc::{channel, Receiver, SendError, Sender}; - use std::time::Duration; - use std::{thread, vec}; - use vec::Vec; +#[cfg(feature = "std")] +pub mod stdmod { + use crate::event_man::EventReceiver; + use crate::events::{EventU16, EventU32, GenericEvent}; + use std::sync::mpsc::Receiver; + use std::vec::Vec; - type EventAndParams<'a> = (EventU32, Option<&'a [u8]>); - - struct MpscEventReceiver { - mpsc_receiver: Receiver<(EventU32, Option>)>, + pub struct MpscEventReceiver { + mpsc_receiver: Receiver<(Event, Option>)>, } - impl EventReceiver for MpscEventReceiver { - fn receive(&mut self, aux_data: &mut [u8]) -> Option { + impl MpscEventReceiver { + pub fn new(receiver: Receiver<(Event, Option>)>) -> Self { + Self { + mpsc_receiver: receiver, + } + } + } + impl EventReceiver for MpscEventReceiver { + fn receive(&mut self, aux_data: &mut [u8]) -> Option { if let Some((event, params)) = self.mpsc_receiver.try_recv().ok() { if let Some(params) = params { if params.len() < aux_data.len() { @@ -228,6 +232,20 @@ mod tests { } } + pub type MpscEventU32Receiver = MpscEventReceiver; + pub type MpscEventU16Receiver = MpscEventReceiver; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::event_man::EventManager; + use crate::events::{EventU32, GenericEvent, Severity}; + use alloc::boxed::Box; + use std::sync::mpsc::{channel, Receiver, SendError, Sender}; + + type EventAndParams<'a> = (EventU32, Option<&'a [u8]>); + #[derive(Clone)] struct MpscEventSenderQueue<'a> { id: u32, @@ -240,8 +258,8 @@ mod tests { fn id(&self) -> u32 { self.id } - fn send(&mut self, event: EventU32, _aux_data: Option<&[u8]>) -> Result<(), Self::Error> { - self.mpsc_sender.send((event, None)) + fn send(&mut self, event: EventU32, aux_data: Option<&'a [u8]>) -> Result<(), Self::Error> { + self.mpsc_sender.send((event, aux_data)) } } @@ -251,7 +269,6 @@ mod tests { assert_eq!(event.0, expected); break; } - thread::sleep(Duration::from_millis(1)); } } @@ -270,9 +287,7 @@ mod tests { #[test] fn test_basic() { let (event_sender, manager_queue) = channel(); - let event_man_receiver = MpscEventReceiver { - mpsc_receiver: manager_queue, - }; + let event_man_receiver = MpscEventReceiver::new(manager_queue); let mut event_man: EventManager, EventU32> = EventManager::new(Box::new(event_man_receiver), 128); let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap(); @@ -313,9 +328,7 @@ mod tests { #[test] fn test_multi_group() { let (event_sender, manager_queue) = channel(); - let event_man_receiver = MpscEventReceiver { - mpsc_receiver: manager_queue, - }; + let event_man_receiver = MpscEventReceiver::new(manager_queue); let mut event_man: EventManager, EventU32> = EventManager::new(Box::new(event_man_receiver), 128); let res = event_man.try_event_handling(); @@ -355,9 +368,7 @@ mod tests { #[test] fn test_listening_to_same_event_and_multi_type() { let (event_sender, manager_queue) = channel(); - let event_man_receiver = MpscEventReceiver { - mpsc_receiver: manager_queue, - }; + let event_man_receiver = MpscEventReceiver::new(manager_queue); let mut event_man: EventManager, EventU32> = EventManager::new(Box::new(event_man_receiver), 128); let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap(); @@ -414,9 +425,7 @@ mod tests { #[test] fn test_all_events_listener() { let (event_sender, manager_queue) = channel(); - let event_man_receiver = MpscEventReceiver { - mpsc_receiver: manager_queue, - }; + let event_man_receiver = MpscEventReceiver::new(manager_queue); let mut event_man: EventManager, EventU32> = EventManager::new(Box::new(event_man_receiver), 128); let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap(); From af288a1b2ba3f33f8ed05c78dd8ff02ddec18b66 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 29 Oct 2022 13:05:29 +0200 Subject: [PATCH 15/40] first tests with auxiliary data --- fsrc-core/src/event_man.rs | 200 +++++++++++++++++++++++-------------- fsrc-core/src/lib.rs | 1 + fsrc-core/src/util.rs | 97 ++++++++++++++++++ 3 files changed, 225 insertions(+), 73 deletions(-) create mode 100644 fsrc-core/src/util.rs diff --git a/fsrc-core/src/event_man.rs b/fsrc-core/src/event_man.rs index 27d3b04..381ccd7 100644 --- a/fsrc-core/src/event_man.rs +++ b/fsrc-core/src/event_man.rs @@ -27,7 +27,8 @@ //! 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. -use crate::events::{GenericEvent, LargestEventRaw, LargestGroupIdRaw}; +use crate::events::{EventU16, EventU32, GenericEvent, LargestEventRaw, LargestGroupIdRaw}; +use crate::util::{AuxData, AuxDataHeapless}; use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; @@ -43,48 +44,69 @@ enum ListenerType { All, } -pub trait SendEventProvider { +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 trait SendEventProvider { type Error; fn id(&self) -> u32; fn send_no_data(&mut self, event: Provider) -> Result<(), Self::Error> { self.send(event, None) } - fn send(&mut self, event: Provider, aux_data: Option<&[u8]>) -> Result<(), Self::Error>; + fn send( + &mut self, + event: Provider, + aux_data: Option, + ) -> Result<(), Self::Error>; } -struct Listener { +struct Listener { ltype: ListenerType, - send_provider: Box>, + send_provider: Box>, } /// Generic abstraction for an event receiver. -pub trait EventReceiver { +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, aux_data: &mut [u8]) -> Option; + fn receive(&mut self) -> Option<(Event, Option)>; } /// Generic event manager implementation. -pub struct EventManager { - aux_data_buf: Vec, - listeners: HashMap>>, - event_receiver: Box>, +/// +/// # Generics +/// +/// * `SendProviderError`: [SendEventProvider] error type +/// * `Event`: Concrete event provider, currently either [EventU32] or [EventU16] +/// * `AuxDataProvider`: Concrete auxiliary data provder, currently either [AuxData] or +/// [AuxDataHeapless] +pub struct EventManager< + SendProviderError, + Event: GenericEvent = EventU32, + AuxDataProvider = AuxData, +> { + listeners: HashMap>>, + event_receiver: Box>, } -pub enum HandlerResult { +pub enum HandlerResult { Empty, - Handled(u32, Provider), + Handled(u32, Provider, Option), } impl EventManager { - pub fn new(event_receiver: Box>, buf_len_aux_data: usize) -> Self { + pub fn new(event_receiver: Box>) -> Self { EventManager { - aux_data_buf: vec![0; buf_len_aux_data], listeners: HashMap::new(), event_receiver, } @@ -105,6 +127,10 @@ impl EventManager { self.update_listeners(ListenerType::Group(group_id), dest); } + /// 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, dest: impl SendEventProvider + 'static) { self.update_listeners(ListenerType::All, dest); } @@ -126,11 +152,13 @@ impl EventManager { } } -impl EventManager { +impl + EventManager +{ fn update_listeners( &mut self, key: ListenerType, - dest: impl SendEventProvider + 'static, + dest: impl SendEventProvider + 'static, ) { if !self.listeners.contains_key(&key) { self.listeners.insert( @@ -155,28 +183,27 @@ impl EventManager { } } - pub fn try_event_handling(&mut self) -> Result, E> { + pub fn try_event_handling(&mut self) -> Result, E> { let mut err_status = None; let mut num_recipients = 0; let mut send_handler = - |event: Provider, aux_data: Option<&[u8]>, llist: &mut Vec>| { + |event: Event, + aux_data: Option, + llist: &mut Vec>| { for listener in llist.iter_mut() { - if let Err(e) = listener.send_provider.send(event, aux_data) { + if let Err(e) = listener.send_provider.send(event, aux_data.clone()) { err_status = Some(Err(e)); } else { num_recipients += 1; } } }; - if let Some(event) = self - .event_receiver - .receive(self.aux_data_buf.as_mut_slice()) - { + if let Some((event, aux_data)) = self.event_receiver.receive() { let single_key = ListenerType::Single(event.raw_as_largest_type()); if self.listeners.contains_key(&single_key) { send_handler( event, - Some(self.aux_data_buf.as_slice()), + aux_data.clone(), self.listeners.get_mut(&single_key).unwrap(), ); } @@ -184,17 +211,17 @@ impl EventManager { if self.listeners.contains_key(&group_key) { send_handler( event, - Some(self.aux_data_buf.as_slice()), + aux_data.clone(), self.listeners.get_mut(&group_key).unwrap(), ); } if let Some(all_receivers) = self.listeners.get_mut(&ListenerType::All) { - send_handler(event, Some(self.aux_data_buf.as_slice()), all_receivers); + send_handler(event, aux_data.clone(), all_receivers); } if let Some(err) = err_status { return err; } - return Ok(HandlerResult::Handled(num_recipients, event)); + return Ok(HandlerResult::Handled(num_recipients, event, aux_data)); } Ok(HandlerResult::Empty) } @@ -202,31 +229,26 @@ impl EventManager { #[cfg(feature = "std")] pub mod stdmod { - use crate::event_man::EventReceiver; + use crate::event_man::{EventReceiver, EventWithAuxData}; use crate::events::{EventU16, EventU32, GenericEvent}; + use crate::util::AuxData; use std::sync::mpsc::Receiver; - use std::vec::Vec; pub struct MpscEventReceiver { - mpsc_receiver: Receiver<(Event, Option>)>, + mpsc_receiver: Receiver<(Event, Option)>, } impl MpscEventReceiver { - pub fn new(receiver: Receiver<(Event, Option>)>) -> Self { + pub fn new(receiver: Receiver<(Event, Option)>) -> Self { Self { mpsc_receiver: receiver, } } } impl EventReceiver for MpscEventReceiver { - fn receive(&mut self, aux_data: &mut [u8]) -> Option { - if let Some((event, params)) = self.mpsc_receiver.try_recv().ok() { - if let Some(params) = params { - if params.len() < aux_data.len() { - aux_data[0..params.len()].copy_from_slice(params.as_slice()) - } - } - return Some(event); + fn receive(&mut self) -> Option> { + if let Ok(event_and_data) = self.mpsc_receiver.try_recv() { + return Some(event_and_data); } None } @@ -241,62 +263,76 @@ mod tests { use super::*; use crate::event_man::EventManager; use crate::events::{EventU32, GenericEvent, Severity}; + use crate::util::AuxDataRaw; use alloc::boxed::Box; + use std::format; use std::sync::mpsc::{channel, Receiver, SendError, Sender}; - type EventAndParams<'a> = (EventU32, Option<&'a [u8]>); - #[derive(Clone)] - struct MpscEventSenderQueue<'a> { + struct MpscEventSenderQueue { id: u32, - mpsc_sender: Sender>, + mpsc_sender: Sender, } - impl<'a> SendEventProvider for MpscEventSenderQueue<'a> { - type Error = SendError>; + 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<&'a [u8]>) -> Result<(), Self::Error> { + 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) { - for _ in 0..5 { - if let Ok(event) = receiver.try_recv() { - assert_eq!(event.0, expected); - break; - } + 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: HandlerResult, + res: HandlerResult, expected: EventU32, expected_num_sent: u32, ) { assert!(matches!(res, HandlerResult::Handled { .. })); - if let HandlerResult::Handled(num_recipients, event) = res { + if let HandlerResult::Handled(num_recipients, event, _aux_data) = res { assert_eq!(event, expected); assert_eq!(num_recipients, expected_num_sent); } } - #[test] - fn test_basic() { + fn generic_event_man() -> ( + Sender, + EventManager>, + ) { let (event_sender, manager_queue) = channel(); let event_man_receiver = MpscEventReceiver::new(manager_queue); - let mut event_man: EventManager, EventU32> = - EventManager::new(Box::new(event_man_receiver), 128); + ( + 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 { - id: 0, - mpsc_sender: single_event_sender, - }; + let single_event_listener = MpscEventSenderQueue::new(0, single_event_sender); event_man.subscribe_single(event_grp_0, single_event_listener); let (group_event_sender_0, group_event_receiver_0) = channel(); let group_event_listener = MpscEventSenderQueue { @@ -324,13 +360,34 @@ mod tests { 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); + event_sender + .send((event_grp_0, Some(AuxData::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 AuxData::Heapless(AuxDataHeapless::Raw(AuxDataRaw::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, manager_queue) = channel(); - let event_man_receiver = MpscEventReceiver::new(manager_queue); - let mut event_man: EventManager, EventU32> = - EventManager::new(Box::new(event_man_receiver), 128); + let (event_sender, mut event_man) = generic_event_man(); let res = event_man.try_event_handling(); assert!(res.is_ok()); let hres = res.unwrap(); @@ -367,10 +424,7 @@ mod tests { /// to both group and single events from one listener #[test] fn test_listening_to_same_event_and_multi_type() { - let (event_sender, manager_queue) = channel(); - let event_man_receiver = MpscEventReceiver::new(manager_queue); - let mut event_man: EventManager, EventU32> = - EventManager::new(Box::new(event_man_receiver), 128); + 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(); @@ -426,8 +480,8 @@ mod tests { fn test_all_events_listener() { let (event_sender, manager_queue) = channel(); let event_man_receiver = MpscEventReceiver::new(manager_queue); - let mut event_man: EventManager, EventU32> = - EventManager::new(Box::new(event_man_receiver), 128); + 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(); diff --git a/fsrc-core/src/lib.rs b/fsrc-core/src/lib.rs index e8349da..c5178d4 100644 --- a/fsrc-core/src/lib.rs +++ b/fsrc-core/src/lib.rs @@ -28,5 +28,6 @@ pub mod objects; pub mod pool; pub mod pus; pub mod tmtc; +pub mod util; extern crate downcast_rs; diff --git a/fsrc-core/src/util.rs b/fsrc-core/src/util.rs new file mode 100644 index 0000000..ef8033c --- /dev/null +++ b/fsrc-core/src/util.rs @@ -0,0 +1,97 @@ +use crate::pool::StoreAddr; +#[cfg(feature = "alloc")] +use alloc::string::String; +#[cfg(feature = "alloc")] +use alloc::string::ToString; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +#[derive(Debug, Copy, Clone)] +pub enum AuxDataRaw { + U8(u8), + U8Pair((u8, u8)), + U8Triplet((u8, u8, u8)), + I8(i8), + I8Pair((i8, i8)), + I8Triplet((i8, i8, i8)), + U16(u16), + U16Pair((u16, u16)), + U16Triplet((u16, u16, u16)), + I16(i16), + I16Pair((i16, i16)), + I16Triplet((i16, i16, i16)), + U32(u32), + U32Pair((u32, u32)), + U32Triplet((u32, u32, u32)), + I32(i32), + I32Tuple((i32, i32)), + I32Triplet((i32, i32, i32)), + F32(f32), + F32Pair((f32, f32)), + F32Triplet((f32, f32, f32)), + U64(u64), + F64(f64), +} + +#[derive(Debug, Copy, Clone)] +pub enum AuxDataHeapless { + Raw(AuxDataRaw), + Store(StoreAddr), +} + +impl From for AuxDataHeapless { + fn from(x: StoreAddr) -> Self { + Self::Store(x) + } +} + +impl From<(u32, u32)> for AuxDataRaw { + fn from(val: (u32, u32)) -> Self { + Self::U32Pair(val) + } +} + +impl From<(u32, u32)> for AuxDataHeapless { + fn from(val: (u32, u32)) -> Self { + AuxDataHeapless::Raw(val.into()) + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +#[derive(Debug, Clone)] +pub enum AuxData { + Heapless(AuxDataHeapless), + Vec(Vec), + String(String), +} + +impl From for AuxData { + fn from(x: AuxDataHeapless) -> Self { + Self::Heapless(x) + } +} + +impl From> for AuxData { + fn from(val: Vec) -> Self { + Self::Vec(val) + } +} + +impl From<&[u8]> for AuxData { + fn from(val: &[u8]) -> Self { + Self::Vec(val.to_vec()) + } +} + +impl From for AuxData { + fn from(val: String) -> Self { + Self::String(val) + } +} + +impl From<&str> for AuxData { + fn from(val: &str) -> Self { + Self::String(val.to_string()) + } +} From 70fb9c8cd69a395e09884de0538cb8789856862c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 29 Oct 2022 13:23:07 +0200 Subject: [PATCH 16/40] all value to value conversions as declarative macros --- fsrc-core/src/util.rs | 52 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/fsrc-core/src/util.rs b/fsrc-core/src/util.rs index ef8033c..33d506c 100644 --- a/fsrc-core/src/util.rs +++ b/fsrc-core/src/util.rs @@ -24,7 +24,7 @@ pub enum AuxDataRaw { U32Pair((u32, u32)), U32Triplet((u32, u32, u32)), I32(i32), - I32Tuple((i32, i32)), + I32Pair((i32, i32)), I32Triplet((i32, i32, i32)), F32(f32), F32Pair((f32, f32)), @@ -45,17 +45,49 @@ impl From for AuxDataHeapless { } } -impl From<(u32, u32)> for AuxDataRaw { - fn from(val: (u32, u32)) -> Self { - Self::U32Pair(val) - } +macro_rules! from_conversions_for_raw { + ($(($raw_ty: ty, $TargetPath: path),)+) => { + $( + impl From<$raw_ty> for AuxDataRaw { + fn from(val: $raw_ty) -> Self { + $TargetPath(val) + } + } + + impl From<$raw_ty> for AuxDataHeapless { + fn from(val: $raw_ty) -> Self { + AuxDataHeapless::Raw(val.into()) + } + } + )+ + }; } -impl From<(u32, u32)> for AuxDataHeapless { - fn from(val: (u32, u32)) -> Self { - AuxDataHeapless::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")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] From 15ad96a8435e65480b9710ee4b1ad894da0a5a4a Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 30 Oct 2022 21:27:36 +0100 Subject: [PATCH 17/40] cargo fmt, start basic docs --- Cargo.lock | 7 ++ fsrc-core/Cargo.toml | 1 + fsrc-core/src/util.rs | 240 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 224 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9314533..b83d042 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -300,6 +300,7 @@ dependencies = [ "heapless", "num-traits", "once_cell", + "paste", "postcard", "serde", "spacepackets", @@ -511,6 +512,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "paste" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" + [[package]] name = "postcard" version = "1.0.2" diff --git a/fsrc-core/Cargo.toml b/fsrc-core/Cargo.toml index 35072fe..0db0e44 100644 --- a/fsrc-core/Cargo.toml +++ b/fsrc-core/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" delegate = "0.8" hashbrown = "0.12" heapless = "0.7" +paste = "1.0" [dependencies.num-traits] version = "0.2" diff --git a/fsrc-core/src/util.rs b/fsrc-core/src/util.rs index 33d506c..bb9439e 100644 --- a/fsrc-core/src/util.rs +++ b/fsrc-core/src/util.rs @@ -1,3 +1,6 @@ +//! Utility types and enums +//! +//! This module contains helper types. use crate::pool::StoreAddr; #[cfg(feature = "alloc")] use alloc::string::String; @@ -5,32 +8,177 @@ use alloc::string::String; use alloc::string::ToString; #[cfg(feature = "alloc")] use alloc::vec::Vec; +use core::mem::size_of; +use paste::paste; +use spacepackets::ecss::ToBeBytes; + +macro_rules! primitive_newtypes { + ($($ty: ty,)+) => { + $( + paste! { + #[derive(Debug, Copy, Clone)] + pub struct [<$ty:upper>](pub $ty); + #[derive(Debug, Copy, Clone)] + pub struct [<$ty:upper Pair>](pub $ty, pub $ty); + #[derive(Debug, Copy, Clone)] + pub struct [<$ty:upper Triplet>](pub $ty, pub $ty, pub $ty); + + 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!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64,); + +macro_rules! scalar_to_be_bytes_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() + } + } + } + )+ + } +} + +macro_rules! pair_to_be_bytes_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 + } + } + } + )+ + } +} + +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 + } + } + } + )+ + } +} + +scalar_to_be_bytes_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_to_be_bytes_impl!(u16, u32, u64, i16, i32, i64, f32, f64,); +triplet_to_be_bytes_impl!(u16, u32, u64, i16, i32, i64, f32, f64,); #[derive(Debug, Copy, Clone)] pub enum AuxDataRaw { - U8(u8), - U8Pair((u8, u8)), - U8Triplet((u8, u8, u8)), - I8(i8), - I8Pair((i8, i8)), - I8Triplet((i8, i8, i8)), - U16(u16), - U16Pair((u16, u16)), - U16Triplet((u16, u16, u16)), - I16(i16), - I16Pair((i16, i16)), - I16Triplet((i16, i16, i16)), - U32(u32), - U32Pair((u32, u32)), - U32Triplet((u32, u32, u32)), - I32(i32), - I32Pair((i32, i32)), - I32Triplet((i32, i32, i32)), - F32(f32), - F32Pair((f32, f32)), - F32Triplet((f32, f32, f32)), - U64(u64), - F64(f64), + 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), + F64(F64), } #[derive(Debug, Copy, Clone)] @@ -50,7 +198,7 @@ macro_rules! from_conversions_for_raw { $( impl From<$raw_ty> for AuxDataRaw { fn from(val: $raw_ty) -> Self { - $TargetPath(val) + $TargetPath(val.into()) } } @@ -127,3 +275,47 @@ impl From<&str> for AuxData { 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); + } +} From 6d90da15c8c0209e7a378f089e25353628b88061 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 31 Oct 2022 00:23:24 +0100 Subject: [PATCH 18/40] continued util module --- fsrc-core/src/event_man.rs | 41 +++++----- fsrc-core/src/events.rs | 11 +-- fsrc-core/src/util.rs | 156 +++++++++++++++++++++++++++++++------ spacepackets | 2 +- 4 files changed, 158 insertions(+), 52 deletions(-) diff --git a/fsrc-core/src/event_man.rs b/fsrc-core/src/event_man.rs index 381ccd7..98fe9a8 100644 --- a/fsrc-core/src/event_man.rs +++ b/fsrc-core/src/event_man.rs @@ -28,7 +28,7 @@ //! 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. use crate::events::{EventU16, EventU32, GenericEvent, LargestEventRaw, LargestGroupIdRaw}; -use crate::util::{AuxData, AuxDataHeapless}; +use crate::util::{Params, ParamsHeapless}; use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; @@ -44,15 +44,15 @@ enum ListenerType { All, } -pub type EventWithHeaplessAuxData = (Event, Option); +pub type EventWithHeaplessAuxData = (Event, Option); pub type EventU32WithHeaplessAuxData = EventWithHeaplessAuxData; pub type EventU16WithHeaplessAuxData = EventWithHeaplessAuxData; -pub type EventWithAuxData = (Event, Option); +pub type EventWithAuxData = (Event, Option); pub type EventU32WithAuxData = EventWithAuxData; pub type EventU16WithAuxData = EventWithAuxData; -pub trait SendEventProvider { +pub trait SendEventProvider { type Error; fn id(&self) -> u32; @@ -66,13 +66,13 @@ pub trait SendEventProvider { ) -> Result<(), Self::Error>; } -struct Listener { +struct Listener { ltype: ListenerType, send_provider: Box>, } /// Generic abstraction for an event receiver. -pub trait EventReceiver { +pub trait EventReceiver { /// This function has to be provided by any event receiver. A receive call may or may not return /// an event. /// @@ -88,13 +88,10 @@ pub trait EventReceiver { /// /// * `SendProviderError`: [SendEventProvider] error type /// * `Event`: Concrete event provider, currently either [EventU32] or [EventU16] -/// * `AuxDataProvider`: Concrete auxiliary data provder, currently either [AuxData] or -/// [AuxDataHeapless] -pub struct EventManager< - SendProviderError, - Event: GenericEvent = EventU32, - AuxDataProvider = AuxData, -> { +/// * `AuxDataProvider`: Concrete auxiliary data provder, currently either [Params] or +/// [ParamsHeapless] +pub struct EventManager +{ listeners: HashMap>>, event_receiver: Box>, } @@ -231,15 +228,15 @@ impl pub mod stdmod { use crate::event_man::{EventReceiver, EventWithAuxData}; use crate::events::{EventU16, EventU32, GenericEvent}; - use crate::util::AuxData; + use crate::util::Params; use std::sync::mpsc::Receiver; pub struct MpscEventReceiver { - mpsc_receiver: Receiver<(Event, Option)>, + mpsc_receiver: Receiver<(Event, Option)>, } impl MpscEventReceiver { - pub fn new(receiver: Receiver<(Event, Option)>) -> Self { + pub fn new(receiver: Receiver<(Event, Option)>) -> Self { Self { mpsc_receiver: receiver, } @@ -263,7 +260,7 @@ mod tests { use super::*; use crate::event_man::EventManager; use crate::events::{EventU32, GenericEvent, Severity}; - use crate::util::AuxDataRaw; + use crate::util::ParamsRaw; use alloc::boxed::Box; use std::format; use std::sync::mpsc::{channel, Receiver, SendError, Sender}; @@ -286,7 +283,7 @@ mod tests { fn id(&self) -> u32 { self.id } - fn send(&mut self, event: EventU32, aux_data: Option) -> Result<(), Self::Error> { + fn send(&mut self, event: EventU32, aux_data: Option) -> Result<(), Self::Error> { self.mpsc_sender.send((event, aux_data)) } } @@ -294,7 +291,7 @@ mod tests { fn check_next_event( expected: EventU32, receiver: &Receiver, - ) -> Option { + ) -> Option { if let Ok(event) = receiver.try_recv() { assert_eq!(event.0, expected); return event.1; @@ -303,7 +300,7 @@ mod tests { } fn check_handled_event( - res: HandlerResult, + res: HandlerResult, expected: EventU32, expected_num_sent: u32, ) { @@ -368,7 +365,7 @@ mod tests { let single_event_listener = MpscEventSenderQueue::new(0, single_event_sender); event_man.subscribe_single(event_grp_0, single_event_listener); event_sender - .send((event_grp_0, Some(AuxData::Heapless((2_u32, 3_u32).into())))) + .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()); @@ -376,7 +373,7 @@ mod tests { let aux = check_next_event(event_grp_0, &single_event_receiver); assert!(aux.is_some()); let aux = aux.unwrap(); - if let AuxData::Heapless(AuxDataHeapless::Raw(AuxDataRaw::U32Pair(pair))) = aux { + if let Params::Heapless(ParamsHeapless::Raw(ParamsRaw::U32Pair(pair))) = aux { assert_eq!(pair.0, 2); assert_eq!(pair.1, 3); } else { diff --git a/fsrc-core/src/events.rs b/fsrc-core/src/events.rs index 02f4dfd..8920a12 100644 --- a/fsrc-core/src/events.rs +++ b/fsrc-core/src/events.rs @@ -27,6 +27,7 @@ //! //! let small_event = EventU16::new(Severity::INFO, 3, 0); //! ``` +use core::fmt::Debug; use core::hash::Hash; use delegate::delegate; use spacepackets::ecss::{EcssEnumeration, ToBeBytes}; @@ -46,33 +47,33 @@ pub enum Severity { HIGH = 3, } -pub trait HasSeverity { +pub trait HasSeverity: Debug + PartialEq + Eq + Copy + Clone { const SEVERITY: Severity; } /// Type level support struct -#[derive(Debug, PartialEq, Eq)] +#[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)] +#[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)] +#[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)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct SeverityHigh {} impl HasSeverity for SeverityHigh { const SEVERITY: Severity = Severity::HIGH; diff --git a/fsrc-core/src/util.rs b/fsrc-core/src/util.rs index bb9439e..5041306 100644 --- a/fsrc-core/src/util.rs +++ b/fsrc-core/src/util.rs @@ -1,6 +1,27 @@ //! Utility types and enums //! -//! This module contains helper types. +//! This module contains various helper types. This includes wrapper for primitive rust types +//! using the newtype pattern. This was also done for pairs and triplets of these primtive types. +//! +//! The [ToBeBytes] was implemented for those types as well, which allows to easily convert them +//! into a network friendly raw byte format. +//! +//! The module also contains generic parameter enumerations. +//! +//! # Example for primitive type wrapper +//! +//! ``` +//! use fsrc_core::util::{ParamsRaw, ToBeBytes, U32Pair}; +//! +//! let u32_pair = U32Pair(0x1010, 25); +//! assert_eq!(u32_pair.0, 0x1010); +//! assert_eq!(u32_pair.1, 25); +//! let raw_buf = u32_pair.to_be_bytes(); +//! assert_eq!(raw_buf, [0, 0, 0x10, 0x10, 0, 0, 0, 25]); +//! +//! let params_raw: ParamsRaw = u32_pair.into(); +//! assert_eq!(params_raw, (0x1010_u32, 25_u32).into()); +//! ``` use crate::pool::StoreAddr; #[cfg(feature = "alloc")] use alloc::string::String; @@ -8,19 +29,21 @@ use alloc::string::String; use alloc::string::ToString; #[cfg(feature = "alloc")] use alloc::vec::Vec; +use core::fmt::Debug; use core::mem::size_of; use paste::paste; -use spacepackets::ecss::ToBeBytes; +pub use spacepackets::ecss::ToBeBytes; +use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU64, EcssEnumU8}; -macro_rules! primitive_newtypes { +macro_rules! primitive_newtypes_with_eq { ($($ty: ty,)+) => { $( paste! { - #[derive(Debug, Copy, Clone)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct [<$ty:upper>](pub $ty); - #[derive(Debug, Copy, Clone)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct [<$ty:upper Pair>](pub $ty, pub $ty); - #[derive(Debug, Copy, Clone)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct [<$ty:upper Triplet>](pub $ty, pub $ty, pub $ty); impl From<$ty> for [<$ty:upper>] { @@ -43,7 +66,39 @@ macro_rules! primitive_newtypes { } } -primitive_newtypes!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64,); +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); + + 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_to_be_bytes_impl { ($($ty: ty,)+) => { @@ -154,8 +209,9 @@ impl ToBeBytes for I8Triplet { pair_to_be_bytes_impl!(u16, u32, u64, i16, i32, i64, f32, f64,); triplet_to_be_bytes_impl!(u16, u32, u64, i16, i32, i64, f32, f64,); -#[derive(Debug, Copy, Clone)] -pub enum AuxDataRaw { +/// Generic enumeration for additonal parameters only consisting of primitive data types. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum ParamsRaw { U8(U8), U8Pair(U8Pair), U8Triplet(U8Triplet), @@ -178,16 +234,45 @@ pub enum AuxDataRaw { F32Pair(F32Pair), F32Triplet(F32Triplet), U64(U64), + I64(I64), F64(F64), } -#[derive(Debug, Copy, Clone)] -pub enum AuxDataHeapless { - Raw(AuxDataRaw), +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), +} + +/// Generic enumeration for parameters which do not rely on heap allocations. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum ParamsHeapless { + Raw(ParamsRaw), + EcssEnum(EcssEnumParams), Store(StoreAddr), } -impl From for AuxDataHeapless { +impl From for ParamsHeapless { fn from(x: StoreAddr) -> Self { Self::Store(x) } @@ -196,15 +281,15 @@ impl From for AuxDataHeapless { macro_rules! from_conversions_for_raw { ($(($raw_ty: ty, $TargetPath: path),)+) => { $( - impl From<$raw_ty> for AuxDataRaw { + impl From<$raw_ty> for ParamsRaw { fn from(val: $raw_ty) -> Self { $TargetPath(val.into()) } } - impl From<$raw_ty> for AuxDataHeapless { + impl From<$raw_ty> for ParamsHeapless { fn from(val: $raw_ty) -> Self { - AuxDataHeapless::Raw(val.into()) + ParamsHeapless::Raw(val.into()) } } )+ @@ -237,40 +322,42 @@ from_conversions_for_raw!( (f64, Self::F64), ); +/// Generic enumeration for additional parameters, including parameters which rely on heap +/// allocations. #[cfg(feature = "alloc")] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[derive(Debug, Clone)] -pub enum AuxData { - Heapless(AuxDataHeapless), +pub enum Params { + Heapless(ParamsHeapless), Vec(Vec), String(String), } -impl From for AuxData { - fn from(x: AuxDataHeapless) -> Self { +impl From for Params { + fn from(x: ParamsHeapless) -> Self { Self::Heapless(x) } } -impl From> for AuxData { +impl From> for Params { fn from(val: Vec) -> Self { Self::Vec(val) } } -impl From<&[u8]> for AuxData { +impl From<&[u8]> for Params { fn from(val: &[u8]) -> Self { Self::Vec(val.to_vec()) } } -impl From for AuxData { +impl From for Params { fn from(val: String) -> Self { Self::String(val) } } -impl From<&str> for AuxData { +impl From<&str> for Params { fn from(val: &str) -> Self { Self::String(val.to_string()) } @@ -318,4 +405,25 @@ mod tests { 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/spacepackets b/spacepackets index 65e85f2..38b789c 160000 --- a/spacepackets +++ b/spacepackets @@ -1 +1 @@ -Subproject commit 65e85f20e03507f19a0d5086ee19360ff7596c27 +Subproject commit 38b789ca6d061239e6cecc76f0843d6ba8b9bd01 From 7c34802ae0714aa4c25ec2f89ed89ba688c5985e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 31 Oct 2022 00:34:11 +0100 Subject: [PATCH 19/40] run cargo update --- Cargo.lock | 16 ++--- fsrc-core/src/lib.rs | 3 +- fsrc-core/src/util.rs | 7 +- fsrc-core/tests/pus_autogen_events.rs | 94 +++++++++++++++++++++++++++ fsrc-core/tests/pus_events.rs | 92 +------------------------- 5 files changed, 109 insertions(+), 103 deletions(-) create mode 100644 fsrc-core/tests/pus_autogen_events.rs diff --git a/Cargo.lock b/Cargo.lock index b83d042..0ae6d25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,9 +104,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" [[package]] name = "cfg-if" @@ -372,9 +372,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.51" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -411,9 +411,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.135" +version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "link-cplusplus" @@ -495,9 +495,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "parking_lot_core" diff --git a/fsrc-core/src/lib.rs b/fsrc-core/src/lib.rs index c5178d4..789dc36 100644 --- a/fsrc-core/src/lib.rs +++ b/fsrc-core/src/lib.rs @@ -12,6 +12,7 @@ extern crate alloc; #[cfg(any(feature = "std", test))] extern crate std; +extern crate downcast_rs; pub mod error; #[cfg(feature = "alloc")] @@ -29,5 +30,3 @@ pub mod pool; pub mod pus; pub mod tmtc; pub mod util; - -extern crate downcast_rs; diff --git a/fsrc-core/src/util.rs b/fsrc-core/src/util.rs index 5041306..27afd18 100644 --- a/fsrc-core/src/util.rs +++ b/fsrc-core/src/util.rs @@ -1,8 +1,7 @@ -//! Utility types and enums +//! Utility types and enums. //! //! This module contains various helper types. This includes wrapper for primitive rust types -//! using the newtype pattern. This was also done for pairs and triplets of these primtive types. -//! +//! using the newtype pattern. This was also done for pairs and triplets of these primitive types. //! The [ToBeBytes] was implemented for those types as well, which allows to easily convert them //! into a network friendly raw byte format. //! @@ -345,6 +344,7 @@ impl From> for Params { } } +/// Converts a byte slice into the [Params::Vec] variant impl From<&[u8]> for Params { fn from(val: &[u8]) -> Self { Self::Vec(val.to_vec()) @@ -357,6 +357,7 @@ impl From for Params { } } +/// Converts a string slice into the [Params::String] variant impl From<&str> for Params { fn from(val: &str) -> Self { Self::String(val.to_string()) diff --git a/fsrc-core/tests/pus_autogen_events.rs b/fsrc-core/tests/pus_autogen_events.rs new file mode 100644 index 0000000..03b4006 --- /dev/null +++ b/fsrc-core/tests/pus_autogen_events.rs @@ -0,0 +1,94 @@ +#![allow(dead_code, unused_imports)] + +use fsrc_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/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index b304939..99c13c5 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -1,94 +1,6 @@ -#![allow(dead_code, unused_imports)] - -use fsrc_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 = + + //let event_man; } From 01766464ebe568ff2cece4aaefcade6c5cf57faf Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 31 Oct 2022 01:25:02 +0100 Subject: [PATCH 20/40] created basic integration test --- fsrc-core/src/lib.rs | 2 +- fsrc-core/src/pus/event.rs | 22 +++---- fsrc-core/src/pus/event_man.rs | 96 ++++++++++++++++++++----------- fsrc-core/src/pus/mod.rs | 14 ++++- fsrc-core/src/pus/verification.rs | 56 ++++++++++-------- fsrc-core/tests/pus_events.rs | 35 ++++++++++- 6 files changed, 150 insertions(+), 75 deletions(-) diff --git a/fsrc-core/src/lib.rs b/fsrc-core/src/lib.rs index 789dc36..5ac3541 100644 --- a/fsrc-core/src/lib.rs +++ b/fsrc-core/src/lib.rs @@ -10,9 +10,9 @@ #![no_std] #[cfg(feature = "alloc")] extern crate alloc; +extern crate downcast_rs; #[cfg(any(feature = "std", test))] extern crate std; -extern crate downcast_rs; pub mod error; #[cfg(feature = "alloc")] diff --git a/fsrc-core/src/pus/event.rs b/fsrc-core/src/pus/event.rs index bfc3fa0..f7b7e36 100644 --- a/fsrc-core/src/pus/event.rs +++ b/fsrc-core/src/pus/event.rs @@ -46,7 +46,7 @@ impl EventReporterBase { pub fn event_info( &mut self, buf: &mut [u8], - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, aux_data: Option<&[u8]>, @@ -64,7 +64,7 @@ impl EventReporterBase { pub fn event_low_severity( &mut self, buf: &mut [u8], - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, aux_data: Option<&[u8]>, @@ -82,7 +82,7 @@ impl EventReporterBase { pub fn event_medium_severity( &mut self, buf: &mut [u8], - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, aux_data: Option<&[u8]>, @@ -100,7 +100,7 @@ impl EventReporterBase { pub fn event_high_severity( &mut self, buf: &mut [u8], - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, aux_data: Option<&[u8]>, @@ -119,7 +119,7 @@ impl EventReporterBase { &mut self, buf: &mut [u8], subservice: Subservices, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, aux_data: Option<&[u8]>, @@ -188,7 +188,7 @@ mod allocvec { } pub fn event_info( &mut self, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, aux_data: Option<&[u8]>, @@ -204,7 +204,7 @@ mod allocvec { pub fn event_low_severity( &mut self, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, aux_data: Option<&[u8]>, @@ -220,7 +220,7 @@ mod allocvec { pub fn event_medium_severity( &mut self, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, aux_data: Option<&[u8]>, @@ -236,7 +236,7 @@ mod allocvec { pub fn event_high_severity( &mut self, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], event_id: impl EcssEnumeration, aux_data: Option<&[u8]>, @@ -279,7 +279,9 @@ mod tests { pub service_queue: VecDeque, } - impl EcssTmSender<()> for TestSender { + impl EcssTmSender for TestSender { + type Error = (); + fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<()>> { assert!(tm.source_data().is_some()); let src_data = tm.source_data().unwrap(); diff --git a/fsrc-core/src/pus/event_man.rs b/fsrc-core/src/pus/event_man.rs index 5b48143..33d860f 100644 --- a/fsrc-core/src/pus/event_man.rs +++ b/fsrc-core/src/pus/event_man.rs @@ -1,9 +1,10 @@ -use crate::events::{EventU16TypedSev, EventU32TypedSev, GenericEvent, HasSeverity, Severity}; +use crate::events::{EventU32, EventU32TypedSev, GenericEvent, HasSeverity, Severity}; use alloc::boxed::Box; use core::hash::Hash; use hashbrown::HashSet; -use crate::pus::event::EventReporter; +#[cfg(feature = "alloc")] +pub use crate::pus::event::EventReporter; use crate::pus::{EcssTmError, EcssTmSender}; #[cfg(feature = "heapless")] #[cfg_attr(doc_cfg, doc(cfg(feature = "heapless")))] @@ -33,9 +34,16 @@ pub trait PusEventMgmtBackendProvider { /// /// 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. -#[derive(Default)] -pub struct DefaultPusMgmtBackendProvider { - disabled: HashSet, +pub struct DefaultPusMgmtBackendProvider { + disabled: HashSet, +} + +impl Default for DefaultPusMgmtBackendProvider { + fn default() -> Self { + Self { + disabled: HashSet::default(), + } + } } impl @@ -91,12 +99,33 @@ pub mod heapless_mod { } } -pub struct PusEventManager { +#[derive(Debug)] +pub enum EventManError { + EcssTmError(EcssTmError), + SeverityMissmatch(Severity, Severity), +} + +impl From> for EventManError { + fn from(v: EcssTmError) -> Self { + Self::EcssTmError(v) + } +} + +pub struct PusEventTmManager { reporter: EventReporter, backend: Box>, } -impl PusEventManager { +impl PusEventTmManager { + pub fn new( + reporter: EventReporter, + backend: Box>, + ) -> Self { + Self { reporter, backend } + } +} + +impl PusEventTmManager { pub fn enable_tm_for_event(&mut self, event: &Event) -> Result { self.backend.enable_event_reporting(event) } @@ -108,59 +137,56 @@ impl PusEventManager { pub fn generate_pus_event_tm_generic( &mut self, severity: Severity, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], event: Event, aux_data: Option<&[u8]>, - ) -> Result> { + ) -> Result> { if !self.backend.event_enabled(&event) { return Ok(false); } + if event.severity() != severity { + return Err(EventManError::SeverityMissmatch(severity, event.severity())); + } match severity { Severity::INFO => self .reporter .event_info(sender, time_stamp, event, aux_data) - .map(|_| true), + .map(|_| true) + .map_err(|e| e.into()), Severity::LOW => self .reporter .event_low_severity(sender, time_stamp, event, aux_data) - .map(|_| true), + .map(|_| true) + .map_err(|e| e.into()), Severity::MEDIUM => self .reporter .event_medium_severity(sender, time_stamp, event, aux_data) - .map(|_| true), + .map(|_| true) + .map_err(|e| e.into()), Severity::HIGH => self .reporter .event_high_severity(sender, time_stamp, event, aux_data) - .map(|_| true), + .map(|_| true) + .map_err(|e| e.into()), } } } -impl - PusEventManager> -{ - pub fn generate_pus_event_tm( +impl PusEventTmManager { + pub fn generate_pus_event_tm( &mut self, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], - event: EventU32TypedSev, + event: EventU32TypedSev, aux_data: Option<&[u8]>, - ) -> Result> { - self.generate_pus_event_tm_generic(SEVERITY::SEVERITY, sender, time_stamp, event, aux_data) - } -} - -impl - PusEventManager> -{ - pub fn generate_pus_event_tm( - &mut self, - sender: &mut (impl EcssTmSender + ?Sized), - time_stamp: &[u8], - event: EventU16TypedSev, - aux_data: Option<&[u8]>, - ) -> Result> { - self.generate_pus_event_tm_generic(SEVERITY::SEVERITY, sender, time_stamp, event, aux_data) + ) -> Result> { + self.generate_pus_event_tm_generic( + Severity::SEVERITY, + sender, + time_stamp, + event.into(), + aux_data, + ) } } diff --git a/fsrc-core/src/pus/mod.rs b/fsrc-core/src/pus/mod.rs index 82e49f9..0703a8e 100644 --- a/fsrc-core/src/pus/mod.rs +++ b/fsrc-core/src/pus/mod.rs @@ -26,6 +26,12 @@ pub enum EcssTmError { 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) @@ -37,11 +43,13 @@ impl From for EcssTmError { /// This sender object is responsible for sending telemetry to a TM sink. The [Downcast] trait /// is implemented to allow passing the sender as a boxed trait object and still retrieve the /// concrete type at a later point. -pub trait EcssTmSender: Downcast + Send { - fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError>; +pub trait EcssTmSender: Downcast + Send { + type Error; + + fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError>; } -impl_downcast!(EcssTmSender); +impl_downcast!(EcssTmSender assoc Error); pub(crate) fn source_buffer_large_enough(cap: usize, len: usize) -> Result<(), EcssTmError> { if len > cap { diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 41b74e1..996d558 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -312,7 +312,7 @@ impl VerificationReporterBasic { &mut self, buf: &mut [u8], token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], ) -> Result, VerificationErrorWithToken> { let tm = self @@ -339,7 +339,7 @@ impl VerificationReporterBasic { &mut self, buf: &mut [u8], token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), params: FailParams, ) -> Result<(), VerificationErrorWithToken> { let tm = self @@ -365,7 +365,7 @@ impl VerificationReporterBasic { &mut self, buf: &mut [u8], token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], ) -> Result, VerificationErrorWithToken> { let tm = self @@ -395,7 +395,7 @@ impl VerificationReporterBasic { &mut self, buf: &mut [u8], token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), params: FailParams, ) -> Result<(), VerificationErrorWithToken> { let tm = self @@ -421,7 +421,7 @@ impl VerificationReporterBasic { &mut self, buf: &mut [u8], token: &VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], step: impl EcssEnumeration, ) -> Result<(), EcssTmError> { @@ -445,7 +445,7 @@ impl VerificationReporterBasic { &mut self, buf: &mut [u8], token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), params: FailParamsWithStep, ) -> Result<(), VerificationErrorWithToken> { let tm = self @@ -472,7 +472,7 @@ impl VerificationReporterBasic { &mut self, buf: &mut [u8], token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], ) -> Result<(), VerificationErrorWithToken> { let tm = self @@ -499,7 +499,7 @@ impl VerificationReporterBasic { &mut self, buf: &mut [u8], token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), params: FailParams, ) -> Result<(), VerificationErrorWithToken> { let tm = self @@ -685,7 +685,7 @@ mod allocmod { pub fn acceptance_success( &mut self, token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], ) -> Result, VerificationErrorWithToken> { @@ -701,7 +701,7 @@ mod allocmod { pub fn acceptance_failure( &mut self, token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), params: FailParams, ) -> Result<(), VerificationErrorWithToken> { self.reporter.acceptance_failure( @@ -718,7 +718,7 @@ mod allocmod { pub fn start_success( &mut self, token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], ) -> Result, VerificationErrorWithToken> { @@ -737,7 +737,7 @@ mod allocmod { pub fn start_failure( &mut self, token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), params: FailParams, ) -> Result<(), VerificationErrorWithToken> { self.reporter @@ -750,7 +750,7 @@ mod allocmod { pub fn step_success( &mut self, token: &VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], step: impl EcssEnumeration, ) -> Result<(), EcssTmError> { @@ -770,7 +770,7 @@ mod allocmod { pub fn step_failure( &mut self, token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), params: FailParamsWithStep, ) -> Result<(), VerificationErrorWithToken> { self.reporter @@ -784,7 +784,7 @@ mod allocmod { pub fn completion_success( &mut self, token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], ) -> Result<(), VerificationErrorWithToken> { self.reporter.completion_success( @@ -802,7 +802,7 @@ mod allocmod { pub fn completion_failure( &mut self, token: VerificationToken, - sender: &mut (impl EcssTmSender + ?Sized), + sender: &mut (impl EcssTmSender + ?Sized), params: FailParams, ) -> Result<(), VerificationErrorWithToken> { self.reporter.completion_failure( @@ -818,18 +818,18 @@ mod allocmod { /// API as [VerificationReporter] but without the explicit sender arguments. pub struct VerificationReporterWithSender { pub reporter: VerificationReporter, - pub sender: Box>, + pub sender: Box>, } impl VerificationReporterWithSender { - pub fn new(cfg: VerificationReporterCfg, sender: Box>) -> Self { + 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>, + sender: Box>, ) -> Self { Self { reporter, sender } } @@ -997,7 +997,9 @@ mod stdmod { } //noinspection RsTraitImplementation - impl EcssTmSender for MpscVerifSender { + impl EcssTmSender for MpscVerifSender { + type Error = StdVerifSenderError; + delegate!( to self.base { fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError>; @@ -1028,7 +1030,9 @@ mod stdmod { } //noinspection RsTraitImplementation - impl EcssTmSender for CrossbeamVerifSender { + impl EcssTmSender for CrossbeamVerifSender { + type Error = StdVerifSenderError; + delegate!( to self.base { fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError>; @@ -1039,8 +1043,9 @@ mod stdmod { unsafe impl Sync for CrossbeamVerifSender {} unsafe impl Send for CrossbeamVerifSender {} - impl EcssTmSender for StdSenderBase { - fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError> { + impl EcssTmSender for StdSenderBase { + type Error = StdVerifSenderError; + fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError> { let operation = |mut mg: RwLockWriteGuard| { let (addr, buf) = mg.free_element(tm.len_packed())?; tm.write_to_bytes(buf).map_err(EcssTmError::PusError)?; @@ -1096,8 +1101,9 @@ mod tests { pub service_queue: VecDeque, } - impl EcssTmSender<()> for TestSender { - fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<()>> { + impl EcssTmSender for TestSender { + type Error = (); + fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError> { assert_eq!(PusPacket::service(&tm), 1); assert!(tm.source_data().is_some()); let mut time_stamp = [0; 7]; diff --git a/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index 99c13c5..85b8a29 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -1,6 +1,39 @@ +use fsrc_core::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo}; +use fsrc_core::pus::event_man::{DefaultPusMgmtBackendProvider, EventReporter, PusEventTmManager}; +use fsrc_core::pus::{EcssTmError, EcssTmSender}; +use spacepackets::tm::PusTm; +use std::sync::mpsc::{channel, SendError}; + +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]; + +struct EventTmSender { + sender: std::sync::mpsc::Sender>, +} + +impl EcssTmSender for EventTmSender { + type Error = SendError>; + fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError> { + let mut vec = Vec::new(); + tm.append_to_vec(&mut vec)?; + self.sender.send(vec).map_err(EcssTmError::SendError)?; + Ok(()) + } +} #[test] fn main() { - + let reporter = EventReporter::new(0x02, 128).expect("Creating event repoter failed"); + let backend = DefaultPusMgmtBackendProvider::::default(); + let mut event_man = PusEventTmManager::new(reporter, Box::new(backend)); + let (event_tx, event_rx) = channel(); + let mut sender = EventTmSender { sender: event_tx }; + event_man + .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None) + .expect("Sending info event failed"); + let packet = event_rx.recv().expect("Receiving event TM failed"); + println!("{:?}", packet); //let event_man; } From 0ac9f888b2af8d72ca30bb3e5727ef9d0758a717 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 31 Oct 2022 01:25:55 +0100 Subject: [PATCH 21/40] check retval --- fsrc-core/tests/pus_events.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index 85b8a29..e4faf35 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -30,9 +30,11 @@ fn main() { let mut event_man = PusEventTmManager::new(reporter, Box::new(backend)); let (event_tx, event_rx) = channel(); let mut sender = EventTmSender { sender: event_tx }; - event_man + let event_sent = event_man .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None) .expect("Sending info event failed"); + + assert!(event_sent); let packet = event_rx.recv().expect("Receiving event TM failed"); println!("{:?}", packet); //let event_man; From cc1f3ed091fe7c5042e7e91ef4d4c569b1612281 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 1 Nov 2022 18:03:01 +0100 Subject: [PATCH 22/40] basic unittests for PUS event man --- fsrc-core/src/events.rs | 24 +++++++ fsrc-core/src/pus/event_man.rs | 109 ++++++++++++++++++++++++++++++ fsrc-core/src/pus/verification.rs | 3 +- fsrc-core/tests/pus_events.rs | 21 ++++-- 4 files changed, 150 insertions(+), 7 deletions(-) diff --git a/fsrc-core/src/events.rs b/fsrc-core/src/events.rs index 8920a12..d0ea2f2 100644 --- a/fsrc-core/src/events.rs +++ b/fsrc-core/src/events.rs @@ -285,6 +285,18 @@ impl From> for EventU32 { } } +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 { @@ -423,6 +435,18 @@ pub struct EventU16TypedSev { 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 diff --git a/fsrc-core/src/pus/event_man.rs b/fsrc-core/src/pus/event_man.rs index 33d860f..11075d9 100644 --- a/fsrc-core/src/pus/event_man.rs +++ b/fsrc-core/src/pus/event_man.rs @@ -174,6 +174,20 @@ impl PusEventTmManager { } impl PusEventTmManager { + 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 EcssTmSender + ?Sized), @@ -190,3 +204,98 @@ impl PusEventTmManager { ) } } + +#[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]; + + struct EventTmSender { + sender: std::sync::mpsc::Sender>, + } + + impl EcssTmSender for EventTmSender { + type Error = SendError>; + fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError> { + let mut vec = Vec::new(); + tm.append_to_vec(&mut vec)?; + self.sender.send(vec).map_err(EcssTmError::SendError)?; + Ok(()) + } + } + + fn create_basic_man() -> PusEventTmManager<(), EventU32> { + let reporter = EventReporter::new(0x02, 128).expect("Creating event repoter failed"); + let backend = DefaultPusMgmtBackendProvider::::default(); + PusEventTmManager::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( + Severity::LOW, + &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/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 996d558..0908b01 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -1131,7 +1131,8 @@ mod tests { #[derive(Default)] struct FallibleSender {} - impl EcssTmSender for FallibleSender { + impl EcssTmSender for FallibleSender { + type Error = DummyError; fn send_tm(&mut self, _: PusTm) -> Result<(), EcssTmError> { Err(EcssTmError::SendError(DummyError {})) } diff --git a/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index e4faf35..0534e96 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -2,7 +2,7 @@ use fsrc_core::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo}; use fsrc_core::pus::event_man::{DefaultPusMgmtBackendProvider, EventReporter, PusEventTmManager}; use fsrc_core::pus::{EcssTmError, EcssTmSender}; use spacepackets::tm::PusTm; -use std::sync::mpsc::{channel, SendError}; +use std::sync::mpsc::{channel, SendError, TryRecvError}; const INFO_EVENT: EventU32TypedSev = EventU32TypedSev::::const_new(1, 0); @@ -24,18 +24,27 @@ impl EcssTmSender for EventTmSender { } #[test] -fn main() { +fn test_basic() { let reporter = EventReporter::new(0x02, 128).expect("Creating event repoter failed"); let backend = DefaultPusMgmtBackendProvider::::default(); let mut event_man = PusEventTmManager::new(reporter, Box::new(backend)); let (event_tx, event_rx) = channel(); let mut sender = EventTmSender { sender: event_tx }; - let event_sent = event_man + let mut event_sent = event_man .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None) .expect("Sending info event failed"); assert!(event_sent); - let packet = event_rx.recv().expect("Receiving event TM failed"); - println!("{:?}", packet); - //let event_man; + // Will not check packet here, correctness of packet was tested somewhere else + event_rx.recv().expect("Receiving event TM failed"); + let res = event_man.disable_tm_for_event_with_sev(&INFO_EVENT); + assert!(res.is_ok()); + assert!(res.unwrap()); + event_sent = event_man + .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None) + .expect("Sending info event failed"); + assert!(!event_sent); + let res = event_rx.try_recv(); + assert!(res.is_err()); + assert!(matches!(res.unwrap_err(), TryRecvError::Empty)); } From 74f65243e4f587734eab14ff605fecdbb968a4fd Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 1 Nov 2022 19:24:09 +0100 Subject: [PATCH 23/40] basic test framework done --- fsrc-core/src/event_man.rs | 55 ++++++++++++++--- fsrc-core/src/pus/event_man.rs | 38 ++++++------ fsrc-core/tests/pus_events.rs | 107 ++++++++++++++++++++++++++------- 3 files changed, 150 insertions(+), 50 deletions(-) diff --git a/fsrc-core/src/event_man.rs b/fsrc-core/src/event_man.rs index 98fe9a8..fb43184 100644 --- a/fsrc-core/src/event_man.rs +++ b/fsrc-core/src/event_man.rs @@ -35,7 +35,7 @@ use alloc::vec::Vec; use hashbrown::HashMap; #[cfg(feature = "std")] -pub use stdmod::MpscEventReceiver; +pub use stdmod::*; #[derive(PartialEq, Eq, Hash, Copy, Clone)] enum ListenerType { @@ -96,6 +96,14 @@ pub struct EventManager>, } +/// 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 +{ +} + pub enum HandlerResult { Empty, Handled(u32, Provider, Option), @@ -128,8 +136,11 @@ impl EventManager { /// /// For example, this can be useful for a handler component which sends every event as /// a telemetry packet. - pub fn subscribe_all(&mut self, dest: impl SendEventProvider + 'static) { - self.update_listeners(ListenerType::All, dest); + pub fn subscribe_all( + &mut self, + send_provider: impl SendEventProvider + 'static, + ) { + self.update_listeners(ListenerType::All, send_provider); } /// Helper function which removes single subscriptions for which a group subscription already @@ -226,23 +237,24 @@ impl #[cfg(feature = "std")] pub mod stdmod { + use super::*; use crate::event_man::{EventReceiver, EventWithAuxData}; use crate::events::{EventU16, EventU32, GenericEvent}; use crate::util::Params; - use std::sync::mpsc::Receiver; + use std::sync::mpsc::{Receiver, SendError, Sender}; - pub struct MpscEventReceiver { + pub struct MpscEventReceiver { mpsc_receiver: Receiver<(Event, Option)>, } - impl MpscEventReceiver { + impl MpscEventReceiver { pub fn new(receiver: Receiver<(Event, Option)>) -> Self { Self { mpsc_receiver: receiver, } } } - impl EventReceiver for MpscEventReceiver { + 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); @@ -253,6 +265,35 @@ pub mod stdmod { pub type MpscEventU32Receiver = MpscEventReceiver; pub type MpscEventU16Receiver = MpscEventReceiver; + + #[derive(Clone)] + pub struct MpscEventSendProvider { + id: u32, + sender: Sender<(Event, Option)>, + } + + /// Safety: Send is safe to implement because both the ID and the MPSC sender are Send + //unsafe impl Send for MpscEventSendProvider {} + + 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)] diff --git a/fsrc-core/src/pus/event_man.rs b/fsrc-core/src/pus/event_man.rs index 11075d9..feef852 100644 --- a/fsrc-core/src/pus/event_man.rs +++ b/fsrc-core/src/pus/event_man.rs @@ -38,6 +38,9 @@ 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 { @@ -73,13 +76,19 @@ pub mod heapless_mod { // 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 HeaplessPusMgmtBckendProvider { + pub struct HeaplessPusMgmtBackendProvider { disabled: heapless::FnvIndexSet, phantom: PhantomData, } + /// Safety: All contained field are [Send] as well + unsafe impl Send + for HeaplessPusMgmtBackendProvider + { + } + impl PusEventMgmtBackendProvider - for HeaplessPusMgmtBckendProvider + for HeaplessPusMgmtBackendProvider { type Error = (); @@ -116,6 +125,9 @@ pub struct PusEventTmManager { backend: Box>, } +/// Safety: All contained fields are send as well. +unsafe impl Send for PusEventTmManager {} + impl PusEventTmManager { pub fn new( reporter: EventReporter, @@ -136,7 +148,6 @@ impl PusEventTmManager { pub fn generate_pus_event_tm_generic( &mut self, - severity: Severity, sender: &mut (impl EcssTmSender + ?Sized), time_stamp: &[u8], event: Event, @@ -145,10 +156,7 @@ impl PusEventTmManager { if !self.backend.event_enabled(&event) { return Ok(false); } - if event.severity() != severity { - return Err(EventManError::SeverityMissmatch(severity, event.severity())); - } - match severity { + match event.severity() { Severity::INFO => self .reporter .event_info(sender, time_stamp, event, aux_data) @@ -195,13 +203,7 @@ impl PusEventTmManager { event: EventU32TypedSev, aux_data: Option<&[u8]>, ) -> Result> { - self.generate_pus_event_tm_generic( - Severity::SEVERITY, - sender, - time_stamp, - event.into(), - aux_data, - ) + self.generate_pus_event_tm_generic(sender, time_stamp, event.into(), aux_data) } } @@ -261,13 +263,7 @@ mod tests { assert!(res.is_ok()); assert!(res.unwrap()); let mut event_sent = event_man - .generate_pus_event_tm_generic( - Severity::LOW, - &mut sender, - &EMPTY_STAMP, - LOW_SEV_EVENT, - None, - ) + .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(); diff --git a/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index 0534e96..058b0f6 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -1,8 +1,11 @@ +use fsrc_core::event_man::{EventManager, MpscEventReceiver, MpscEventU32SendProvider}; use fsrc_core::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo}; use fsrc_core::pus::event_man::{DefaultPusMgmtBackendProvider, EventReporter, PusEventTmManager}; use fsrc_core::pus::{EcssTmError, EcssTmSender}; +use fsrc_core::util::Params; use spacepackets::tm::PusTm; use std::sync::mpsc::{channel, SendError, TryRecvError}; +use std::thread; const INFO_EVENT: EventU32TypedSev = EventU32TypedSev::::const_new(1, 0); @@ -24,27 +27,87 @@ impl EcssTmSender for EventTmSender { } #[test] -fn test_basic() { - let reporter = EventReporter::new(0x02, 128).expect("Creating event repoter failed"); - let backend = DefaultPusMgmtBackendProvider::::default(); - let mut event_man = PusEventTmManager::new(reporter, Box::new(backend)); - let (event_tx, event_rx) = channel(); - let mut sender = EventTmSender { sender: event_tx }; - let mut event_sent = event_man - .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None) - .expect("Sending info event failed"); +fn test_threaded_usage() { + let (event_sender, event_man_receiver) = channel(); + let event_receiver = MpscEventReceiver::new(event_man_receiver); + let mut event_man = EventManager::new(Box::new(event_receiver)); - assert!(event_sent); - // Will not check packet here, correctness of packet was tested somewhere else - event_rx.recv().expect("Receiving event TM failed"); - let res = event_man.disable_tm_for_event_with_sev(&INFO_EVENT); - assert!(res.is_ok()); - assert!(res.unwrap()); - event_sent = event_man - .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None) - .expect("Sending info event failed"); - assert!(!event_sent); - let res = event_rx.try_recv(); - assert!(res.is_err()); - assert!(matches!(res.unwrap_err(), TryRecvError::Empty)); + 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); + 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 = PusEventTmManager::new(reporter, Box::new(backend)); + // PUS + Generic event manager thread + let jh0 = thread::spawn(move || { + let mut sender = EventTmSender { sender: event_tx }; + loop { + let res = event_man.try_event_handling(); + assert!(res.is_ok()); + match pus_event_man_rx.try_recv() { + Ok((event, aux_data)) => { + // TODO: Convert auxiliary data into raw byte format + if let Some(aux_data) = aux_data { + match aux_data { + Params::Heapless(_) => {} + Params::Vec(_) => {} + Params::String(_) => {} + } + } + let res = pus_event_man.generate_pus_event_tm_generic( + &mut sender, + &EMPTY_STAMP, + event, + None, + ); + assert!(res.is_ok()); + assert!(res.unwrap()); + 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(_) => 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(_) => 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"); } From ffb6fe83ada5d48c969d4983fd926fca4564e226 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 1 Nov 2022 20:07:02 +0100 Subject: [PATCH 24/40] test works --- fsrc-core/tests/pus_events.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index 058b0f6..251f585 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -42,6 +42,7 @@ fn test_threaded_usage() { // PUS + Generic event manager thread let jh0 = thread::spawn(move || { let mut sender = EventTmSender { sender: event_tx }; + let mut event_cnt = 0; loop { let res = event_man.try_event_handling(); assert!(res.is_ok()); @@ -61,9 +62,12 @@ fn test_threaded_usage() { event, None, ); + event_cnt += 1; assert!(res.is_ok()); assert!(res.unwrap()); - break; + if event_cnt == 2 { + break; + } } Err(e) => { if let TryRecvError::Disconnected = e { @@ -82,7 +86,10 @@ fn test_threaded_usage() { loop { match event_rx.try_recv() { // Event TM received successfully - Ok(_) => break, + Ok(event) => { + println!("{:x?}", event); + break; + } Err(e) => { if let TryRecvError::Disconnected = e { panic!("Event sender disconnected!") @@ -99,7 +106,10 @@ fn test_threaded_usage() { loop { match event_rx.try_recv() { // Event TM received successfully - Ok(_) => break, + Ok(event) => { + println!("{:x?}", event); + break; + } Err(e) => { if let TryRecvError::Disconnected = e { panic!("Event sender disconnected!") From e5a912c6369a8456954ea7f77b35c69d179f2812 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 1 Nov 2022 22:01:27 +0100 Subject: [PATCH 25/40] this works --- fsrc-core/src/util.rs | 97 +++++++++++++++++++++++++++++++++++ fsrc-core/tests/pus_events.rs | 34 +++++++++++- 2 files changed, 129 insertions(+), 2 deletions(-) diff --git a/fsrc-core/src/util.rs b/fsrc-core/src/util.rs index 27afd18..6622eb9 100644 --- a/fsrc-core/src/util.rs +++ b/fsrc-core/src/util.rs @@ -33,6 +33,36 @@ use core::mem::size_of; use paste::paste; pub use spacepackets::ecss::ToBeBytes; use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU64, EcssEnumU8}; +use spacepackets::ByteConversionError; +use spacepackets::SizeMissmatch; + +pub trait WritableAsBeBytes { + fn raw_len(&self) -> usize; + fn write_be(&self, buf: &mut [u8]) -> Result; +} + +macro_rules! param_to_be_bytes_impl { + ($Newtype: ident) => { + impl WritableAsBeBytes for $Newtype { + #[inline] + fn raw_len(&self) -> usize { + size_of::<::ByteArray>() + } + + fn write_be(&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,)+) => { @@ -45,6 +75,10 @@ macro_rules! primitive_newtypes_with_eq { #[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) @@ -76,6 +110,10 @@ macro_rules! primitive_newtypes { #[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) @@ -237,6 +275,65 @@ pub enum ParamsRaw { F64(F64), } +impl WritableAsBeBytes 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_be(&self, buf: &mut [u8]) -> Result { + match self { + ParamsRaw::U8(v) => v.write_be(buf), + ParamsRaw::U8Pair(v) => v.write_be(buf), + ParamsRaw::U8Triplet(v) => v.write_be(buf), + ParamsRaw::I8(v) => v.write_be(buf), + ParamsRaw::I8Pair(v) => v.write_be(buf), + ParamsRaw::I8Triplet(v) => v.write_be(buf), + ParamsRaw::U16(v) => v.write_be(buf), + ParamsRaw::U16Pair(v) => v.write_be(buf), + ParamsRaw::U16Triplet(v) => v.write_be(buf), + ParamsRaw::I16(v) => v.write_be(buf), + ParamsRaw::I16Pair(v) => v.write_be(buf), + ParamsRaw::I16Triplet(v) => v.write_be(buf), + ParamsRaw::U32(v) => v.write_be(buf), + ParamsRaw::U32Pair(v) => v.write_be(buf), + ParamsRaw::U32Triplet(v) => v.write_be(buf), + ParamsRaw::I32(v) => v.write_be(buf), + ParamsRaw::I32Pair(v) => v.write_be(buf), + ParamsRaw::I32Triplet(v) => v.write_be(buf), + ParamsRaw::F32(v) => v.write_be(buf), + ParamsRaw::F32Pair(v) => v.write_be(buf), + ParamsRaw::F32Triplet(v) => v.write_be(buf), + ParamsRaw::U64(v) => v.write_be(buf), + ParamsRaw::I64(v) => v.write_be(buf), + ParamsRaw::F64(v) => v.write_be(buf), + } + } +} macro_rules! params_raw_from_newtype { ($($newtype: ident,)+) => { $( diff --git a/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index 251f585..5d4f1c8 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -2,7 +2,7 @@ use fsrc_core::event_man::{EventManager, MpscEventReceiver, MpscEventU32SendProv use fsrc_core::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo}; use fsrc_core::pus::event_man::{DefaultPusMgmtBackendProvider, EventReporter, PusEventTmManager}; use fsrc_core::pus::{EcssTmError, EcssTmSender}; -use fsrc_core::util::Params; +use fsrc_core::util::{Params, ParamsHeapless, ParamsRaw}; use spacepackets::tm::PusTm; use std::sync::mpsc::{channel, SendError, TryRecvError}; use std::thread; @@ -43,6 +43,7 @@ fn test_threaded_usage() { let jh0 = thread::spawn(move || { let mut sender = EventTmSender { sender: event_tx }; let mut event_cnt = 0; + let _params_array: [u8; 256] = [0; 256]; loop { let res = event_man.try_event_handling(); assert!(res.is_ok()); @@ -51,7 +52,36 @@ fn test_threaded_usage() { // TODO: Convert auxiliary data into raw byte format if let Some(aux_data) = aux_data { match aux_data { - Params::Heapless(_) => {} + Params::Heapless(heapless) => match heapless { + ParamsHeapless::Raw(raw) => match raw { + ParamsRaw::U8(_) => {} + ParamsRaw::U8Pair(_) => {} + ParamsRaw::U8Triplet(_) => {} + ParamsRaw::I8(_) => {} + ParamsRaw::I8Pair(_) => {} + ParamsRaw::I8Triplet(_) => {} + ParamsRaw::U16(_) => {} + ParamsRaw::U16Pair(_) => {} + ParamsRaw::U16Triplet(_) => {} + ParamsRaw::I16(_) => {} + ParamsRaw::I16Pair(_) => {} + ParamsRaw::I16Triplet(_) => {} + ParamsRaw::U32(_) => {} + ParamsRaw::U32Pair(_) => {} + ParamsRaw::U32Triplet(_) => {} + ParamsRaw::I32(_) => {} + ParamsRaw::I32Pair(_) => {} + ParamsRaw::I32Triplet(_) => {} + ParamsRaw::F32(_) => {} + ParamsRaw::F32Pair(_) => {} + ParamsRaw::F32Triplet(_) => {} + ParamsRaw::U64(_) => {} + ParamsRaw::I64(_) => {} + ParamsRaw::F64(_) => {} + }, + ParamsHeapless::EcssEnum(_) => {} + ParamsHeapless::Store(_) => {} + }, Params::Vec(_) => {} Params::String(_) => {} } From 0fd9d9c9b6dba9219301223827b7aafc41b9c3c1 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Nov 2022 00:36:49 +0100 Subject: [PATCH 26/40] all working --- fsrc-core/src/events.rs | 45 +++++++-- fsrc-core/src/pus/event.rs | 2 +- fsrc-core/src/pus/verification.rs | 8 +- fsrc-core/src/util.rs | 159 +++++++++++++++++++++++------- fsrc-core/tests/pus_events.rs | 102 ++++++++++--------- spacepackets | 2 +- 6 files changed, 225 insertions(+), 93 deletions(-) diff --git a/fsrc-core/src/events.rs b/fsrc-core/src/events.rs index d0ea2f2..eded68e 100644 --- a/fsrc-core/src/events.rs +++ b/fsrc-core/src/events.rs @@ -411,7 +411,7 @@ impl EcssEnumeration for EventU32 { 32 } - fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> { + fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> { self.base.write_to_bytes(self.raw(), buf, self.byte_width()) } } @@ -420,7 +420,7 @@ impl EcssEnumeration for EventU32 { impl EcssEnumeration for EventU32TypedSev { delegate!(to self.event { fn pfc(&self) -> u8; - fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>; + fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>; }); } @@ -542,11 +542,12 @@ impl EventU16TypedSev { impl_event_provider!(EventU16, EventU16TypedSev, u16, u8, u8); impl EcssEnumeration for EventU16 { + #[inline] fn pfc(&self) -> u8 { 16 } - fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> { + fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> { self.base.write_to_bytes(self.raw(), buf, self.byte_width()) } } @@ -555,7 +556,7 @@ impl EcssEnumeration for EventU16 { impl EcssEnumeration for EventU16TypedSev { delegate!(to self.event { fn pfc(&self) -> u8; - fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>; + fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>; }); } @@ -574,6 +575,34 @@ 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; @@ -713,7 +742,7 @@ mod tests { #[test] fn write_to_buf() { let mut buf: [u8; 4] = [0; 4]; - assert!(HIGH_SEV_EVENT.write_to_bytes(&mut buf).is_ok()); + 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); } @@ -721,7 +750,7 @@ mod tests { #[test] fn write_to_buf_small() { let mut buf: [u8; 2] = [0; 2]; - assert!(HIGH_SEV_EVENT_SMALL.write_to_bytes(&mut buf).is_ok()); + 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); } @@ -729,7 +758,7 @@ mod tests { #[test] fn write_to_buf_insufficient_buf() { let mut buf: [u8; 3] = [0; 3]; - let err = HIGH_SEV_EVENT.write_to_bytes(&mut buf); + 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 { @@ -741,7 +770,7 @@ mod tests { #[test] fn write_to_buf_small_insufficient_buf() { let mut buf: [u8; 1] = [0; 1]; - let err = HIGH_SEV_EVENT_SMALL.write_to_bytes(&mut buf); + 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 { diff --git a/fsrc-core/src/pus/event.rs b/fsrc-core/src/pus/event.rs index f7b7e36..a4867e6 100644 --- a/fsrc-core/src/pus/event.rs +++ b/fsrc-core/src/pus/event.rs @@ -152,7 +152,7 @@ impl EventReporterBase { time_stamp, ); let mut current_idx = 0; - event_id.write_to_bytes(&mut buf[0..event_id.byte_width()])?; + 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); diff --git a/fsrc-core/src/pus/verification.rs b/fsrc-core/src/pus/verification.rs index 0908b01..da56f69 100644 --- a/fsrc-core/src/pus/verification.rs +++ b/fsrc-core/src/pus/verification.rs @@ -536,7 +536,7 @@ impl VerificationReporterBasic { idx += RequestId::SIZE_AS_BYTES; if let Some(step) = step { // Size check was done beforehand - step.write_to_bytes(&mut buf[idx..idx + step.byte_width() as usize]) + step.write_to_be_bytes(&mut buf[idx..idx + step.byte_width() as usize]) .unwrap(); } let mut sp_header = SpHeader::tm(self.apid(), 0, 0).unwrap(); @@ -571,13 +571,13 @@ impl VerificationReporterBasic { idx += RequestId::SIZE_AS_BYTES; if let Some(step) = step { // Size check done beforehand - step.write_to_bytes(&mut buf[idx..idx + step.byte_width() as usize]) + step.write_to_be_bytes(&mut buf[idx..idx + step.byte_width() as usize]) .unwrap(); idx += step.byte_width() as usize; } params .failure_code - .write_to_bytes(&mut buf[idx..idx + params.failure_code.byte_width() as usize])?; + .write_to_be_bytes(&mut buf[idx..idx + params.failure_code.byte_width() as usize])?; idx += params.failure_code.byte_width() as usize; if let Some(failure_data) = params.failure_data { buf[idx..idx + failure_data.len()].copy_from_slice(failure_data); @@ -1344,7 +1344,7 @@ mod tests { let fail_code = EcssEnumU8::new(10); let fail_data = EcssEnumU32::new(12); let mut fail_data_raw = [0; 4]; - fail_data.write_to_bytes(&mut fail_data_raw).unwrap(); + 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"); diff --git a/fsrc-core/src/util.rs b/fsrc-core/src/util.rs index 6622eb9..3967fee 100644 --- a/fsrc-core/src/util.rs +++ b/fsrc-core/src/util.rs @@ -32,13 +32,16 @@ use core::fmt::Debug; use core::mem::size_of; use paste::paste; pub use spacepackets::ecss::ToBeBytes; -use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU64, EcssEnumU8}; +use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU64, EcssEnumU8, EcssEnumeration}; use spacepackets::ByteConversionError; use spacepackets::SizeMissmatch; +/// Generic trait which is used for objects which can be converted into a raw network (big) endian +/// byte format. pub trait WritableAsBeBytes { fn raw_len(&self) -> usize; - fn write_be(&self, buf: &mut [u8]) -> Result; + /// 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 { @@ -49,7 +52,7 @@ macro_rules! param_to_be_bytes_impl { size_of::<::ByteArray>() } - fn write_be(&self, buf: &mut [u8]) -> Result { + 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 { @@ -137,7 +140,7 @@ macro_rules! primitive_newtypes { primitive_newtypes_with_eq!(u8, u16, u32, u64, i8, i16, i32, i64,); primitive_newtypes!(f32, f64,); -macro_rules! scalar_to_be_bytes_impl { +macro_rules! scalar_byte_conversions_impl { ($($ty: ty,)+) => { $( paste! { @@ -147,12 +150,26 @@ macro_rules! scalar_to_be_bytes_impl { 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_to_be_bytes_impl { +macro_rules! pair_byte_conversions_impl { ($($ty: ty,)+) => { $( paste! { @@ -167,6 +184,23 @@ macro_rules! pair_to_be_bytes_impl { 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()) + )) + } + } } )+ } @@ -182,20 +216,37 @@ macro_rules! triplet_to_be_bytes_impl { 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>() + size_of::<$ty>()..2 * size_of::<$ty>() ].copy_from_slice(&self.1.to_be_bytes()); array[ - 2 * size_of::<$ty>()..3* size_of::<$ty>() + 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_to_be_bytes_impl!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64,); +scalar_byte_conversions_impl!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64,); impl ToBeBytes for U8Pair { type ByteArray = [u8; 2]; @@ -243,7 +294,7 @@ impl ToBeBytes for I8Triplet { } } -pair_to_be_bytes_impl!(u16, u32, u64, i16, i32, i64, f32, f64,); +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. @@ -305,35 +356,36 @@ impl WritableAsBeBytes for ParamsRaw { } } - fn write_be(&self, buf: &mut [u8]) -> Result { + fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { match self { - ParamsRaw::U8(v) => v.write_be(buf), - ParamsRaw::U8Pair(v) => v.write_be(buf), - ParamsRaw::U8Triplet(v) => v.write_be(buf), - ParamsRaw::I8(v) => v.write_be(buf), - ParamsRaw::I8Pair(v) => v.write_be(buf), - ParamsRaw::I8Triplet(v) => v.write_be(buf), - ParamsRaw::U16(v) => v.write_be(buf), - ParamsRaw::U16Pair(v) => v.write_be(buf), - ParamsRaw::U16Triplet(v) => v.write_be(buf), - ParamsRaw::I16(v) => v.write_be(buf), - ParamsRaw::I16Pair(v) => v.write_be(buf), - ParamsRaw::I16Triplet(v) => v.write_be(buf), - ParamsRaw::U32(v) => v.write_be(buf), - ParamsRaw::U32Pair(v) => v.write_be(buf), - ParamsRaw::U32Triplet(v) => v.write_be(buf), - ParamsRaw::I32(v) => v.write_be(buf), - ParamsRaw::I32Pair(v) => v.write_be(buf), - ParamsRaw::I32Triplet(v) => v.write_be(buf), - ParamsRaw::F32(v) => v.write_be(buf), - ParamsRaw::F32Pair(v) => v.write_be(buf), - ParamsRaw::F32Triplet(v) => v.write_be(buf), - ParamsRaw::U64(v) => v.write_be(buf), - ParamsRaw::I64(v) => v.write_be(buf), - ParamsRaw::F64(v) => v.write_be(buf), + 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,)+) => { $( @@ -360,6 +412,45 @@ pub enum EcssEnumParams { U64(EcssEnumU64), } +macro_rules! writable_as_be_bytes_ecss_enum_impl { + ($EnumIdent: ident) => { + impl WritableAsBeBytes 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 WritableAsBeBytes 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) => WritableAsBeBytes::write_to_be_bytes(e, buf), + EcssEnumParams::U16(e) => WritableAsBeBytes::write_to_be_bytes(e, buf), + EcssEnumParams::U32(e) => WritableAsBeBytes::write_to_be_bytes(e, buf), + EcssEnumParams::U64(e) => WritableAsBeBytes::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 { diff --git a/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index 5d4f1c8..9d402e7 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -2,7 +2,9 @@ use fsrc_core::event_man::{EventManager, MpscEventReceiver, MpscEventU32SendProv use fsrc_core::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo}; use fsrc_core::pus::event_man::{DefaultPusMgmtBackendProvider, EventReporter, PusEventTmManager}; use fsrc_core::pus::{EcssTmError, EcssTmSender}; -use fsrc_core::util::{Params, ParamsHeapless, ParamsRaw}; +use fsrc_core::util::U32Pair; +use fsrc_core::util::{Params, ParamsHeapless, WritableAsBeBytes}; +use spacepackets::ecss::PusPacket; use spacepackets::tm::PusTm; use std::sync::mpsc::{channel, SendError, TryRecvError}; use std::thread; @@ -43,55 +45,41 @@ fn test_threaded_usage() { let jh0 = thread::spawn(move || { let mut sender = EventTmSender { sender: event_tx }; let mut event_cnt = 0; - let _params_array: [u8; 256] = [0; 256]; + 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)) => { - // TODO: Convert auxiliary data into raw byte format - if let Some(aux_data) = 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) => match raw { - ParamsRaw::U8(_) => {} - ParamsRaw::U8Pair(_) => {} - ParamsRaw::U8Triplet(_) => {} - ParamsRaw::I8(_) => {} - ParamsRaw::I8Pair(_) => {} - ParamsRaw::I8Triplet(_) => {} - ParamsRaw::U16(_) => {} - ParamsRaw::U16Pair(_) => {} - ParamsRaw::U16Triplet(_) => {} - ParamsRaw::I16(_) => {} - ParamsRaw::I16Pair(_) => {} - ParamsRaw::I16Triplet(_) => {} - ParamsRaw::U32(_) => {} - ParamsRaw::U32Pair(_) => {} - ParamsRaw::U32Triplet(_) => {} - ParamsRaw::I32(_) => {} - ParamsRaw::I32Pair(_) => {} - ParamsRaw::I32Triplet(_) => {} - ParamsRaw::F32(_) => {} - ParamsRaw::F32Pair(_) => {} - ParamsRaw::F32Triplet(_) => {} - ParamsRaw::U64(_) => {} - ParamsRaw::I64(_) => {} - ParamsRaw::F64(_) => {} - }, - ParamsHeapless::EcssEnum(_) => {} - ParamsHeapless::Store(_) => {} + 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()])) + } + ParamsHeapless::Store(_) => gen_event(None), }, - Params::Vec(_) => {} - Params::String(_) => {} + Params::Vec(vec) => gen_event(Some(vec.as_slice())), + Params::String(str) => gen_event(Some(str.as_bytes())), } - } - let res = pus_event_man.generate_pus_event_tm_generic( - &mut sender, - &EMPTY_STAMP, - event, - None, - ); + } else { + gen_event(None) + }; event_cnt += 1; assert!(res.is_ok()); assert!(res.unwrap()); @@ -116,8 +104,18 @@ fn test_threaded_usage() { loop { match event_rx.try_recv() { // Event TM received successfully - Ok(event) => { - println!("{:x?}", event); + 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) => { @@ -136,8 +134,22 @@ fn test_threaded_usage() { loop { match event_rx.try_recv() { // Event TM received successfully - Ok(event) => { - println!("{:x?}", event); + 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) => { diff --git a/spacepackets b/spacepackets index 38b789c..4c1101f 160000 --- a/spacepackets +++ b/spacepackets @@ -1 +1 @@ -Subproject commit 38b789ca6d061239e6cecc76f0843d6ba8b9bd01 +Subproject commit 4c1101f65f20419606932c4ee40fb2768e8cc736 From b0e58e9f8d26ed4d38a622e4a891d94297fb4bfb Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Nov 2022 00:39:09 +0100 Subject: [PATCH 27/40] bump spacepackets --- spacepackets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spacepackets b/spacepackets index 4c1101f..f8199ca 160000 --- a/spacepackets +++ b/spacepackets @@ -1 +1 @@ -Subproject commit 4c1101f65f20419606932c4ee40fb2768e8cc736 +Subproject commit f8199ca87a8fc97a51d09bf8bfa57627cd54f74d From cc3740c676165d95f775775331fba51580719fee Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Nov 2022 11:07:56 +0100 Subject: [PATCH 28/40] add empty changelog --- fsrc-core/CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 fsrc-core/CHANGELOG.md diff --git a/fsrc-core/CHANGELOG.md b/fsrc-core/CHANGELOG.md new file mode 100644 index 0000000..68e54a2 --- /dev/null +++ b/fsrc-core/CHANGELOG.md @@ -0,0 +1,9 @@ +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] From 6768af8f17322da17401af007aec307aa6aaff89 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Nov 2022 11:10:56 +0100 Subject: [PATCH 29/40] bump spacepackets --- spacepackets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spacepackets b/spacepackets index f8199ca..1d6cf3a 160000 --- a/spacepackets +++ b/spacepackets @@ -1 +1 @@ -Subproject commit f8199ca87a8fc97a51d09bf8bfa57627cd54f74d +Subproject commit 1d6cf3a75d4082b3d962700038c21dc7dacb156f From 575fb0e64b0000415501a70d0d5e541ab3af52e8 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 2 Nov 2022 11:33:59 +0100 Subject: [PATCH 30/40] rename module --- fsrc-core/src/event_man.rs | 6 +-- fsrc-core/src/lib.rs | 2 +- fsrc-core/src/{util.rs => params.rs} | 59 ++++++++++++++++++++-------- fsrc-core/tests/pus_events.rs | 4 +- 4 files changed, 48 insertions(+), 23 deletions(-) rename fsrc-core/src/{util.rs => params.rs} (90%) diff --git a/fsrc-core/src/event_man.rs b/fsrc-core/src/event_man.rs index fb43184..c5a6c9f 100644 --- a/fsrc-core/src/event_man.rs +++ b/fsrc-core/src/event_man.rs @@ -28,7 +28,7 @@ //! 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. use crate::events::{EventU16, EventU32, GenericEvent, LargestEventRaw, LargestGroupIdRaw}; -use crate::util::{Params, ParamsHeapless}; +use crate::params::{Params, ParamsHeapless}; use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; @@ -240,7 +240,7 @@ pub mod stdmod { use super::*; use crate::event_man::{EventReceiver, EventWithAuxData}; use crate::events::{EventU16, EventU32, GenericEvent}; - use crate::util::Params; + use crate::params::Params; use std::sync::mpsc::{Receiver, SendError, Sender}; pub struct MpscEventReceiver { @@ -301,7 +301,7 @@ mod tests { use super::*; use crate::event_man::EventManager; use crate::events::{EventU32, GenericEvent, Severity}; - use crate::util::ParamsRaw; + use crate::params::ParamsRaw; use alloc::boxed::Box; use std::format; use std::sync::mpsc::{channel, Receiver, SendError, Sender}; diff --git a/fsrc-core/src/lib.rs b/fsrc-core/src/lib.rs index 5ac3541..2ad940b 100644 --- a/fsrc-core/src/lib.rs +++ b/fsrc-core/src/lib.rs @@ -29,4 +29,4 @@ pub mod objects; pub mod pool; pub mod pus; pub mod tmtc; -pub mod util; +pub mod params; diff --git a/fsrc-core/src/util.rs b/fsrc-core/src/params.rs similarity index 90% rename from fsrc-core/src/util.rs rename to fsrc-core/src/params.rs index 3967fee..3117b65 100644 --- a/fsrc-core/src/util.rs +++ b/fsrc-core/src/params.rs @@ -1,26 +1,48 @@ -//! Utility types and enums. +//! Parameter types and enums. //! -//! This module contains various helper types. This includes wrapper for primitive rust types -//! using the newtype pattern. This was also done for pairs and triplets of these primitive types. -//! The [ToBeBytes] was implemented for those types as well, which allows to easily convert them -//! into a network friendly raw byte format. +//! This module contains various helper types. //! -//! The module also contains generic parameter enumerations. +//! # Primtive Parameter Wrappers and Enumeration //! -//! # Example for primitive type wrapper +//! 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 fsrc_core::util::{ParamsRaw, ToBeBytes, U32Pair}; +//! use fsrc_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. use crate::pool::StoreAddr; #[cfg(feature = "alloc")] use alloc::string::String; @@ -38,7 +60,7 @@ use spacepackets::SizeMissmatch; /// Generic trait which is used for objects which can be converted into a raw network (big) endian /// byte format. -pub trait WritableAsBeBytes { +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; @@ -46,7 +68,7 @@ pub trait WritableAsBeBytes { macro_rules! param_to_be_bytes_impl { ($Newtype: ident) => { - impl WritableAsBeBytes for $Newtype { + impl WritableToBeBytes for $Newtype { #[inline] fn raw_len(&self) -> usize { size_of::<::ByteArray>() @@ -298,6 +320,9 @@ 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), @@ -326,7 +351,7 @@ pub enum ParamsRaw { F64(F64), } -impl WritableAsBeBytes for ParamsRaw { +impl WritableToBeBytes for ParamsRaw { fn raw_len(&self) -> usize { match self { ParamsRaw::U8(v) => v.raw_len(), @@ -414,7 +439,7 @@ pub enum EcssEnumParams { macro_rules! writable_as_be_bytes_ecss_enum_impl { ($EnumIdent: ident) => { - impl WritableAsBeBytes for $EnumIdent { + impl WritableToBeBytes for $EnumIdent { fn raw_len(&self) -> usize { self.byte_width() } @@ -431,7 +456,7 @@ 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 WritableAsBeBytes for EcssEnumParams { +impl WritableToBeBytes for EcssEnumParams { fn raw_len(&self) -> usize { match self { EcssEnumParams::U8(e) => e.byte_width(), @@ -443,10 +468,10 @@ impl WritableAsBeBytes for EcssEnumParams { fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { match self { - EcssEnumParams::U8(e) => WritableAsBeBytes::write_to_be_bytes(e, buf), - EcssEnumParams::U16(e) => WritableAsBeBytes::write_to_be_bytes(e, buf), - EcssEnumParams::U32(e) => WritableAsBeBytes::write_to_be_bytes(e, buf), - EcssEnumParams::U64(e) => WritableAsBeBytes::write_to_be_bytes(e, buf), + 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), } } } diff --git a/fsrc-core/tests/pus_events.rs b/fsrc-core/tests/pus_events.rs index 9d402e7..62c5dae 100644 --- a/fsrc-core/tests/pus_events.rs +++ b/fsrc-core/tests/pus_events.rs @@ -2,8 +2,8 @@ use fsrc_core::event_man::{EventManager, MpscEventReceiver, MpscEventU32SendProv use fsrc_core::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo}; use fsrc_core::pus::event_man::{DefaultPusMgmtBackendProvider, EventReporter, PusEventTmManager}; use fsrc_core::pus::{EcssTmError, EcssTmSender}; -use fsrc_core::util::U32Pair; -use fsrc_core::util::{Params, ParamsHeapless, WritableAsBeBytes}; +use fsrc_core::params::U32Pair; +use fsrc_core::params::{Params, ParamsHeapless, WritableToBeBytes}; use spacepackets::ecss::PusPacket; use spacepackets::tm::PusTm; use std::sync::mpsc::{channel, SendError, TryRecvError}; From 140e6737064246d9a6d165933a1b8480c509db4c Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 3 Nov 2022 12:55:03 +0100 Subject: [PATCH 31/40] add graphml and png --- fsrc-core/img/event_man_arch.graphml | 277 +++++++++++++++++++++++++++ fsrc-core/img/event_man_arch.png | Bin 0 -> 71833 bytes 2 files changed, 277 insertions(+) create mode 100644 fsrc-core/img/event_man_arch.graphml create mode 100644 fsrc-core/img/event_man_arch.png diff --git a/fsrc-core/img/event_man_arch.graphml b/fsrc-core/img/event_man_arch.graphml new file mode 100644 index 0000000..2ff6c97 --- /dev/null +++ b/fsrc-core/img/event_man_arch.graphml @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + 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/fsrc-core/img/event_man_arch.png b/fsrc-core/img/event_man_arch.png new file mode 100644 index 0000000000000000000000000000000000000000..24f7063efca9bf68b13bbe4d32883d6a04076919 GIT binary patch literal 71833 zcmeFZbyU?`7%d7aC?KGqAmK&?L`mrmK|oNtq($itkxior2nZWV>F$(9X_W5n+H`Na zxoe}Iqvv?;dtpv=N`2E&e-}=7!%{kZdl93X_#w5l>K|#S5fAmle1?55< z3d(uXOXt8>E-H~LqM*2g%E zTtJ9)2)uhE%0+o8^A_E$Sh}(cS1!CHfB4`UuRmI8S&m{`EZr@L`fUTHP?85N?{fJ$JbrC z|9AN3+X;kJ4F564&lh|h$z@%)*m{KbraqyZ<>yz=?e|xn=x*ydV+_Juqj+|;etx5- zg{7UBq6Y3h^mDW*Bm?=+)}cQ>aEoGx?glU9$Nw7M@nNCEF8=r*^YivM)ql;#`~UM} zC@BAbn-V<4;aX+o$-xr-ohO_&i$R4r7f|^H&*CE%4;eYp4;@dBcU!)}1WpXH&OdO) zy>j-=)VF+C1i_HC4`L9ai3qogxT+lQ@JNgA2Dr%7!^o&*uM-lg(Uwh~s<%aQCAd$X zzwoJLp*uwY6{k9<>M zN=r+R?qgzMW!a3ayD$ix9_15w;9mL4d_;oF`R4Me)Z?&C5!6b6tFe24MQDos8l{C-5tm zxA2o?^7_*epQA#od2v|Rn_o-L>+F4@T!EY3idT@N4IlMi#x4yc47@No6*O5oYaeX3}UzStnd73$uH>c2f54V z59aG{1{9AwM<_9tPUBgfn++8oL_{^>Ww4?%*q6s^<_g(O zhqjt%bB@|ik8l}}He*hCT~3a`-rFnR9s6>6a%e|Bi1OmJZZ`0ar2SG>G5^6l#ZZ|T z3{qw>-w`MJIETP}Z+QGf)ZNR=t2^Qjp}a<$z$yVjvT1oZY5 z7lR{-YP=OqSfav9wJ3I&l*^~Pgmb{x3#w|w<loK zZ5Fzcl6W?%ZTdCD=uZ#&D>oEtiyaSFibW%GDo?@*1ALh<3j@Mp9M;PF?;<{PG1)9C zXr6o{)LO^OA(&a8tewtGwHVCP6(#==9WCXq6Vu{O@Mb@D@2$S5sE3^}vy}q07Mo+e zcLMByMgl@Y2l=eGFWG=^A|9lEF2 z9C&k{K6wB;n?OD8ujJ{MPSUj#lus21&$_h zXL)edip?b5^YvP%GOEekasp?{6O)pNsCIaxmD$Zk8it|HuKD`y$-P5`#u1XlDT*0N z3Tze=f)(5^>MpCAcE-DRx*kD-d$QC*PuT9Ni{Ybf-4z?~rpsqv6c`xe= zH8&iu;IkgBGGs_R&dqz(&_Db21&pswoggU3UiI2k_LtRoJZ8%FpqWlFvKimwvP_nb zxQo+`fsTG{B{Kp1*t4HyHw8}jaTzgAZa79Qe_5+&Rnc+O%L4&zOseZ>g^%zg(y{Kk9@5@^s4M!Ze)F`O&h7ND^0b>#r4Ie_ zjb~ZitjsrrJ#nZ6YKK2!%p=Uox0Prj?`yv)^{2adJ>U~7Sk~;|7G@3mg(Sz`?sx+# zsV-dSJ3()bV)tO@Jueg0DDP)R?IWnq7VvmEza3gC`@9{#FF@Fi>rb{%0ppb`ifb#g z+e}TDisBY+3|7_@r+OSh7dJy8=GU=t({{vid4MUvQ+sHw;?QhXd--+1ttBhU=j{4# zoNKO0(12o08KgicgW^EbYJX5Sjl7G|tO!@#f#GEY7$bfb|v%y>s{eWKd9KYO&q z67GRb0#fR?w@}`(!|TfG)yjPI-bxz|T-+b?7>^ldK-yy=R=c#}rdn>#W<6@9A|n$U zd^FYqjZY?MV%pBHFft!zm|)s37P_$N+`wEd@lTd6>S=aG9N<5BIABPRS;SM zr(YS^*m_HHV6`st`e7k~os6M8Ne=VBU{bCOC*1TL%@`y38}+8}!H{vADhSjlGD~>O$_cN}2Zwy%A9Tw#YQ4EaKom_X(<_6TvA^-9=d{YC$Q~ z`0|R#{Q=(eMUf}rtX`^JMA#(k@9oN(8TMYR+vG(<)+Vam%Dx`)+Y4Nyqq&;}j-zA|YCB`{iY6qmTIrpO!dwR8^>9x9#T-M2U_^~t%pGmzw zwuZ&V+;LDh?P4S+8BKDg6%ukv;9mNIcQZBaaSjTKIZdupAUPTPtJL@us6 zFbH3ZbnxZC`sSvlDGp(yY{G8D#W4Twy@iw*=;T`EX`2g?0lyG)YN(bomo?wbuTXK6 z`gTSHXSJhDWLa6+*RKy3`M9{kPWUV)Tua%(n6_2y?8|Cugq+6{5S;im&@r#?Yw=V; zp(j&?W!B1|Zte-0^X}?snDOVXjV|jGtVW4on$Akf^9hklZ<`tqJN0#k&fUfOuR0z- z0GDNtnZQ12sf{rE>}A&?hM3DqOia8H#^>_nwsmQqX1Tqtj?UJ~aA@WDh$XCEz0z)} zCr!TI@8T8FHv=Yj?QCnZSB|GXW5 zHy@WicvT*4_}JAR9UXz~m!CJbmK3UKG*pnLORa;lT6yX`RuyWF(F-4aHEinm=AAIQ zAxIF(k~S6lvn2d$I%O`A`R3#NR)j}3W9X9MtW4v*3*uF{O(O$)uSckpcN;)Oy5Z=g zq{%l1@L^w$mcXX4q@g(y(3ko?@Q1W7T`X}qJ;~Pivavg4nGG5ekd(uVXx&exVCc>2 zP>{P$JiN}ZCeAhV|JwGl3kcGbl9Ii8GMAA&-DVKUp8*&-J(?2`5fSMVeZ@2!t>tnY zocRrW;E8kNP}^5rfum=oY5~@(BV1O`byxuU(uvl^^Mcn~fVn)F2GC}9PPA19ae#WV zTX}j6Q1h6}=`je`F*!6>>i^mfZuM9^70E@Qn{>-;{_t)v65{|v{%+dcRFMD~ISoP7 zWtIETvfT5qi-N3g{-1onda}k{UwzL7{SJyC&41;Tvvyzezk=Y58i1ny*Nh@*gbB3& zs_{tfz?JZS(+6Mix0&&K(d}UG6=0GUcoGVE*)n+>r$4L6-XLGZmA?oT4 zA^JgFi6fRK&pFuRDVj@RQ7%OVM+4pV+3fj*B-XSf4zy|`F;P*-hVHmL^OYZIzU_j7pU)MD za(rV_>k*hn5t#pbz!Ti=ouWO;c(h5Im@5D8N%f&t+&s*Uk`AhC&zB(48BO9CU{=!N zXO+~7woj=5sPfylg?%MGdqY-{%t7qi3SS)9t!ORvJhuIkFT`+$&;Hm_TM53yrh~Ra@3ioR^f7ru_#B_6# zV+@F0gvbCDBS@$z%& zig_#LrP}6{)Jh8bz7FqP&(`7Z0J|!4whn&|FdZ@8m-1nHEE=tM2lGs%>iaSuEGPxj z%%@$?*@#iNuA5K3@|a^eQg1Zh4Zv4qR9$`V@0j-M&bg3qyS4ZaJBL|Eqx#|4<)Rno z4$Z=CobNm%bqHUHetHl+bzcPVE1mIV6qJO>si9fynWBvs`RC4S($VH1qu)0}voqmq zvT%O6f4`4?2KELmX^j|1zU=6hn$|{oh78C>$s0?ao;R)=PG0Ncsa2_y%#0Au$L^7PS!lM`_QF+PZv#rHpeHpd>?L4!3Px6 zGPjeK1DXyNx1#$(W?*B<#TYygcdWq|QCr8mU&d6g>wvtg06AX2l6ITkL}+nXbli0G z@qn-7(^i^;4X0OyUxmd*g0<)?gs`t;OX^!%X4(mdj0N?kr`s}3zA`2e`#nd_G=4r9 zkEO=sQ=K=zFiRyexP1I!xt`9rc|$B`ol^1Jpvm?XzBGw<{kym@LYkRQT-*;dT{U+S z?M`9z%k>NlJ)!Nb`lby14ufo+Lv(v!&@;+DIWoc`V#)bA63G|a+g=!^9qe|c>1Vb! zk^NTwa-(dV*j3x{%uJ@*jQQX-*30Er?N@oSX(I+C+l$|)k679_zVkcq6`zZtr%x%+ zvvet6?8Gryj9Olzew+c7E2OKv$#W-2X~ppkpPgq#G1{)`Cb>X;)r;|KPQ_V_Hq?CU z(75Mn0=yS89$`iJ(=qKr%0p*}`ZgwzGbB%6aXZ$VwUWX>_|bC(*3Uj;&2#XqnHk(4 z&Bpgg9ccTEek9V4cUKpYImMKhFy@-7;;xvMdaB?Rs-L{%F78EF__?B^fPv?r;LBnJ z^|o5@iGK@mPe%sL*Zg9LjWQy!>6wDQ?LJ&%OY*K!iJ58tkfLB$3+NMb)8`W^7g@Zcx}H8i=dkd=IE&(U>@lpOOs{+} zrDAqL+JLUo)6nVO<20o^Bnbsx61*HKdoKNpX=-t_vO5RoZw#sRN-Xa!cLnS_ZRugH z+T_^tq358&^l^s|Cdr?QsP4Hx%yz{|miWfJyEb@<>bjy-;hwiEO_$^QSW|L$MUj4v zu86Cq47knRJZvK0o5#P_b5WxvomvH}buz3&#{L>%{bGN|8jLw!)_bgFFL0->ep@SU z%P>KJYVC9Lc6xWVS}<>5`Wv(ZmaU;<>9Cse?D78X%IZ0$J^&M z3b*(YP4#5ajGm*0CGK96UNF82Z8t2Tz@LUX;(!q!56&O^AJX@as17vU)z2o@7=Sy*m-2 zS$v0ydm0lxoTTugeOs5r<5Ts@Hv8;wQW?ap-?ctp%xtX+1dnp!IRkF_eA%@{msbg`H8>CABQ?{OxoN z{Oy!RyNaXTf$jtAOYcLkkyuWw9mGuU8>ihy@>pE)WRMz#L~%MwiXep`HxNzi3SW!! z$P+D6=f9E`>CzpV6r31HwyUQ6UgFN>Wj}rQ|2Z)7zYGQap9lXW2`i%gqm{`~vaQ_g>Jt>4S#x;gK}C8j;Hi(B|g*W`h)blp1*2oOIB3{LCWmdctc z>vld{pc1M+u5BDCwO+`GI4M-R^p^k0(p(1jOh4@Md1`v^d+A3;SB2jj$T59atdW^s zko5RqJ43y611Y^;*LHKU>8|XY^A<@5CCb(6XtcA}ctFZ$Um%wxu_c1kii|Y| z1XT%=o=v!#2nM1&yx>4^R=zGUnGUhe#>*y%B3Zb_b=LwqiRvoULDxH|?=Jl?r@Wg0 zWNE0T{k>i2*Mse)Ec%-BU3quW&R^6yAAaH1?H`!x)*CkCz8mPU`Juv$LJ{bn9`>-K z)LK8k-UkK~fUd_D%HH_XhicjYJ3Tcu)dGRtU^5CT)cB82HGH6G9^fGalG?#a@pz_W z6uO~G(3{ui^H3$vUAc;6@jw_}Vw`J_$pNDya!{@bpn9B{^&cPO;^NYMALV77Q{Fc; zWau9va_=A0>AKzpSWD)*Y`Wnh(|oguyN{aVuU#hf$2P!oeFwC1B!wY(W3bH5n&|P| zgVU4atYJ>fKTOjVQ80!21EoPhK};{|I)pNmvfkG;1=ErVIFEb|v%W>|BX{3IkUdr$ z<@TE&0W&!@-7fHKKu(BV2A}#j%OY4a%Xw4GeL~{&K-86p48OThO#)fKRs1@ z60ABPdNs?SmX?;EWaq;uoiU!{pw@6z?G&mUe zT)90JC=0m73+{WA;*PMYtG}(er^PL>c$e`0IJ!weV2ry$`Nuh|dF@(%{??;E3=+!w z_B<-!wEh1$m6tA_gW$T}x*9|2wRV;B+;0nZ1?K{CEVutOqvWKQpu8+-XlTF{^C`q$ zMqT}=28(eEge%Y?S@heEj(5ki=v}@4^P%le*2c;gx^qM9JAmM4)K_5Gt(2wODP(WY z9VTo{5&MTwc{=f=r%LnU)oP&4L*oI2f0CnFe$KuOm~?!Gc=smUzJ2>v!0mMKQZz~PiQv0dUxF0yDtXqKXq}~sF69F_uU)5DaNO10pdYpu&t~-p3upEMG*C?r} zIe=Nigf{lpf2^1PAY4~Zuj>;P6;)DlGIM|V2jCY;+5n;0v6!xy@jgVY$RsWxGn4L= z1aJCa3tul2=(^Ww6k^?h#0%km`L5sURPVk~&s|NkWFUDJ2wOL16^}{y0*?iQ@1&-+im9arjLE=SX51 z7VAkcE^80JbYPI;u(Md~xo2bnCeKLl#5bgIv~vSg zwKszu-S99c4MCVP@RE7t0#$gn23Y)JikO{yE-L&Xr~K;?AA-}>ORYfB$h>~1tGioX zPHtm!v%9NnsK~S#>%7_gIDC7d+cN|U3(HqkTD8bTB})}psJKyR;<6>PT;Ke2BbGQx zzB6R!h19$TuYa|8^p%>U=)U^H*d_GaBQt1_B28dch@+K z`0S>2MH_q`t@r#n=vpj=>l;QXsi+ZP6yF^ z4)4+3-Q9s2p6He%pW?TVL3{R~R6cs!npLFv#n|+7L(t62o3iER5cwch}-${Y9+7eyd0iy z_=Pjg)Mfk|!LrSjt0SJ6Flw5=%U2ZDM34UiZS6_m5EAu01lT9bM~IZDWMpK-3!mEW z2Geb7>bax+%`ywH*+EjE@X=+Z=q_~Jx7xHV>Bna4XY}pQ*5Cn<8Nj9Y0K*`rzUj9| zvl*4vt0~p^-Q;;slY`XLhvfSK2&VEgz(cu011wxJr4lRsEkwEP|%M5XByt9W(IKe8sjb+k%-aG>X$WDTGyZ;tw0nMlCw?r%Jr19bzte% z?GoJU5NU-5IHD~^U>rb7V3!$Hy{h;min|b4!TRK4FMz~W@36&Pi$9na=XAIu8_}1g zwi)PNPk~I?LyjG>5BB%>3qI>K;;G)`w75l2#%1+3^q3Tr+ zr^mv?+_c)eN8t$c3Wjd5dR17N;R;J|QRy5k>X1!CzLZgCC#b4HK$emrv<+9 zuZ>)NA#I}7=YZ%^DDxD&C^HKSJ{}(b>5;Xud(bsP!pij;EZ`mkK%bVXq;82w(|&>K zfHyxsznT5whm<4Og=;aCT*C=DL}0G5kAWjC*@aQPq-p}8?hls~7iX&6N&@L$p(b>= zz)-%@*-5yHUbP@rwPM*txV_ko-LNas5?<%X^-)~8EsRCqpja_o0bw-sIx7I>5NvvrC= z_9*a=j@Fcssmd@2lbe*$YPwkCP4WG82NGh4AmYg$69`U_j{xAEo?@0n`ukor%spy! z=w{xxXx(lVZ_>;l6IV&)bfgs5yOr;?wE$#V3wO&|9yrkPGklqz-* zgA;4d&wt-$qHXZ3n@yO6AsFpoZW*Dxk|f+O4TOYhCTl&juFGxoshM|r?o7DhZU`hv z$M8of&9z3<8*`Wp2xcHVlOois-#&<)^Og=EUG6-+Ed4dbgB4na*YL2j2w;(lMmO+U zvgfUOdR&hpWE_fBAIvrb?UB@=<=$3}8>4;4PXa zEmYfprpC z-u6Mlw&R*1S}Wg$=&pb*pOC&g?lL~01Avw^|2}0BGZq2egrRS>evDI*$sn_JBgm~^ z?6-|79KkODgVh6mN?YKEfBNaUdyE%n^yQhE8AR`!H*WxN(`yY!C`ts8uo{5c?%9R? z9No~fPLOI&(dTg zRdAqF`qNLh>2G+!L7%&?uLumH!0o7`qqDvN{4u<3BEH0IAD7k!BRI_2U(JV^0h{=? zPTgJ}B0t`!H~m}v68z<$Zs>maT$*x@CIC#WQ~)2-nu6J{Tvu$8U-;OH`{^B;4ulzN z2ZC{zU)(Ca-j#*-Y9ukUYduTJO40D6uB~qdS$8Kv4T(L*1#Y!ch~bN%!|{&OXV_%? zAi~zh&3jZET3eIQWnYT%-ItdF6SJ)S>%{al*p;Q(=HZHl)K#b1_g+2LtiRRfyhI9A`G+rfsc>RzGKbCOmkW;Xg#QIoWxegG~norZ0Gho{8xb2sQcSCm^(2fmJVAI>yX7g4ZdL(q{2ZHsR)0 z;VJepE4L8BM=k*;q`Pz^$qk*{uukkdo*^F z?u760aBLpT6mVFR#}G0<83nNF>EOldG_V$7y=2~>S@DzzLN6pQtT%yZ>9Dl|!+nbJ z@}5exB{LF5r7%ic$IaRbpSp=SJ2&`~*LeU_C40Cf0H(gGZoo_TG#r)dUgaHB6!tU3 zLW;ABnBCRpiMi_=qhD&AzuIM7{Ju97SF$@aWh{-6kU_`N^7C=?6GmUrd-mr~zVIOP zELVm?+{^2bHQ*&qda(!Go%=M>+jN!so8~t#F){CAoAzf%$qFq4V{rKP+IUQkhtUmy zz(uI)G~Cfsq+^mdcveel)f=n_M@G~u4o(i&@}Jr-dru4N$+Z2QQDFpeY2+;H(CEJb z9o9*4GU+f0gy)qBuqoDkVRB{WOcF;Cd~}8_aXe$j;j*MXF7fe)9IAJ0#g-%D8DlzQ zMyrgJbvf8R)urigp+`g7pD#`zz_Lr%TTE1;R@eh8+w5oe_K~_u?TJd_#m)pz+4$0c z>om*2&absH`h_El;QDnU>?0ENjYtKn-BDW-D$=3zH@&_^jsR<0&j6HuV6ivDaJ#Ty zlciYpMx*C@`IGPIZKbpQGOpNBk3SKBb2S%{9FWCf_?2!GD6eO2WqraU^Yro8VW+qu zAx%S?lMHlcDpf|nAv+&MZ2eM#z&DMrkBQ4XE<<1f(-r`pi;Axd6_S8ma#9-PQVTkJ zb-=WNqbwv;s*MTip!uv*zKBEmC}Xx#`jF$YmLtO5&YA@nQZtpZ!uOR$4Gj$yo|k-x zj3gb}Nr3S)6W@hR3nz0Sko!rF)4qVO_Ju?3jYwk5`YKcz$Qj!;-2e8YUM<)TsC;82 z2QSzLa&ZnSo0%n=%@0|8U@wO2Rfab&%_BU<<8xL|zt{*fk-OEyZP^)A3mWbcalgD9 z4A5c7mt;^13(ZD{VA>rL(W|+p!|Vyulb&SB0l-b@pVRh{$t^fa4aCkKZs2?B?TE8R zW)ZZ^QyGUlD+!9+C<&+n&hXXlr-B>g*3%zc|Jh}lY2iN)$A-;WSit&=eNS4RSZCf` zG?OWjaalo?r7ms3S85ogx|18OI1mIeX_YBr2$WoF+toWSw?%kuJnkk)& z9?sRS1y*5|K{eDy%T5SL^=yE8*%m%G^d)#2Nn zK1fpGI59an`{}1Qkz>8eT9RT0WmtZ(0Z$#Sy`C1HZJg^sps3(i9%p+LzJ9*3&lW2@i#u4Rf`A-poZtPYGySKsNIAoHb^E?;dTo z3v6`=19N?AVCwJcNm?*4_lm^-z$_8#U@ap*S;>rA= zMj=pryi}>ct;E{x1KnWuIfp7yEHuQ3D;t$w%H??e_5!FZQeW4Di;L>>dXbQn@ zKK8_u$zjLD*xd)X(>0ywL?2f;ZZs#VZ$-R^&Qk;|})a*4fd z(m4K}LXp`hF&UYF{g#2T`{Khje>B)0Wm+U(I#U{+um07=DnKmVp`>&?*`MpfY~-|E zRhN>qlU{rhJ<&j^ax33n5SszsY5l2>Kk*d&SLw zQn%o#C(*VFOKg$a140uwj3-lfG|!t1I6WO1sH zJLZhzE&m%HI5#CiUx8M#ySv-CH(g|oQ@iGZ^?4d;NlEPO#tcCeaznfe;;biVP55kW zLaHMbcfPWcKyW7oK?m{<9A>M2da{_2)0e4||8k)Vv~3a1wO;|o1SgwRoXthYgYE3> z`nm>gNqEvhLBl??z@(b&L01L{f~Ec(4yA;bMt2imQ@|fm*zB!q8@oG_f~JYVy225# z;ROY(+#4Wm5Mi?ez)ih+s=IsZ_S;19E0_0ph%cd^DJ7GFk7f`k{zL!`tUwYBj2dEd zbJsuBLta2~9>D6-;wCO;r>5QoBpyH?vo+=w`+DF_6U1QAoR(97TWe`wF1MJh0VZU4_a$^vVvi`ZQT1~bqMdg9!ClI42sLXJ4qy}Ne@^7UbbJ#SAB{mb!0 zT*Mr}$w6jjW`KfNpH;akYudSI%O}uCoca-n{NPA_GQWeXOy0>Jtf5S|nmd7t!L26^ zBlMLQuEre!@t-!+62{_-$NU*|MhYJzBTeisVlY`vKRTw3_ykxTr8GJB4R+0wJvgks zG*vbMy|*u0!vU;yS-LXgv0X8?qAQLA_?+ z?hnb4(-S}c5NFV!BTJ1#8m4d8b7c??76k(Ir@LVSF8pj1=hM)L@3jv9iWFH2xX9Ec z2>8}qCj5vRY2ieDjOJk65n5l#4-Djp&%L5FKweysX@D#{~ zU{fS9mj6xQgeYfiWIR*B04Heg_uEg|k>wHPxQK0Ti?O!$jdvcMUX{cGdMespe)YFXcQhMqu&-57c+%V|m*k zjJWGuE6(6Y@E4b-3}^={cr|+@vC#x9VEhMn3y%Q6EDY!_)=@>}(|qs%wR9eUNg}GR zfLcpa0qMU`kRr)$n7Y)GhH0b%jrc35Y_bGufW)v$>7wvFubZAlBvcJt6iD>Ls9y zr*T{<>~C?uHFY7UF?VCihp|ihGHAUjRrDJJs0z5j!NyjbVoG{k2=9PFOJx%dgm>OW zIO}0;nJ!q1m~>{B!1c8ZYpOl_92m%O*zpJgV<}Bd$bKWT^;hG-j$i$2yVQb-86n;_ zsNT>kdF-7xct|k_Q|SxwdziBhEi2v<*k05thIxIl%zhWe(AQ57vo$8o}Rfo~p;eu1!d&ngLaZ`jvACaXf~9 z&hr|5xu&l$pYqb6yJ$;8;T@{chq*WW3_9 zjxG}(pM2vmn5O@R8@<^KJ3=+rl}cH!mH27IAdmrnhSMepr?1QY`q@`3j2>aJ@=TYu zKTGy-v@1)EAjWi_C~?vNc~%P`gS%OPy$x~6O_7RpOpwo3FU_-TLbkWCs8YyF=Uzo+yOZ4dO*bmQ zc}4;XJ0M_V#Jb&dAXgh43T@HpyoaB}*ORVj4B!sHLoVPvAmf*1=5`$fD7|fMZ6|XA zr;TDe;l|mYMp~0fos}MHbl5qqS6>7AIUm6|BoIa^@C9&5eB!EpDh8@AT`E}eGDelqqI z-FV?7UjJg%t5rz`1|9v`&!M)`J#*cvC!bz=j=;|xxt=pb_S*d!gNzP;uuV6^ zMMisM9-X%Z5b6#!H3@Eh9PT724=K|DZQ(7Zpk2&R%+O=qIeNyWQ_$7bwPkKyHwA|S zW>a(18Vz|=8enIH>KnOoWc%c^9Xb(46TeJ4(uHip3o?z|u) z-AM9MP8!5Wulk2vaRB6@Q{lF2%=6i#M zN_#+MJ|A>vLyYrjES()FsngY|zB5Ei9w-LFMiafGL`cXB8od~&ov{5lcl7h; zuuS$B^&D=EjYDo}m~yV5OQQal(|htIw%%izr`Th#YP)fU55vSKHrE`g3>HXzn z%{<^D6EJ>Lq2FTqd-3!mA?|J)kY>r65rrOPupC@2b7q{8l+3Iq3t~>sADie>U3O8+ zTW#GAY00UF^DKA9KbicLvRxCMRYvAU<(;RJgsBt?tz5E;^GsGD@gzpNUA*=v8(+bk zL3X>M?|B4br$XOTD4cjhnpFzho!dgpk2FoQoLe^p@QhuRA0FQ_e`1%IGgrpr-hZJ7 zJxD^+ZYCI9ToF-t?vY?Bh=d?>R^z^wD_mSe&GLeJZIO*-$13&h9?`@_P~L-NVrHFp zpG<+S;7cP_b?8t3u#>)TVPT<8>T-)81My!S(CZg9E1!d?ZHhxkCKqxVWE7=(#vjZL zew-3J4wKG=$4HpBO-t)D@oIu#NSDnhb$+f~h-W;O+u~dV>yE%)ie+?wxak)hQDi8k zwU~9wZ3o=$&$!lv|6{yqynMv+!Uc5BuNsu4@M&bZ*>qk^fU8+9R;&XWCJWhCcO`+F zX4o-nF~y(HGzHJ>>II3;&CIAgeoZ>iq4ShH4LDAa3UmGl7wMH@5~FO-aB%O;aK_cH zbEBRn)$QY*LI)J#v%6~s6({eKza5Qt#$Q7Atc(`^mJ$*@TJ{w{$n*A8cCi_b+5SS? z=wVkhzW;X2xg{%!iU3w`9yfBj%dBPzFJ3WEqQWiieawH9QL(&zgAVbwIg=Q7ePr%) zZ{Vw?LFS&H`+jDj^CNM*E+Su8%C78g6fsSE73vk7Sn>L5`o3E?&R}}%^M&)DwKL>C z@b!6RLE?uKBM%2g`{$*bdmQ&@t!#k}JmT`t16~I_^`(!?)+0}@gsLiTD^Hu=%n;Z} zj998et%YZGx)h~LZ)Fk3SCYXF;%jP8Q`B06jFw9)Olt45mz0myl{j=SF+H7{@3v1# z>mBrdeQ-}*BStZ^sv=UyRFC4vUXeUXmNf>=SzRnRO41 z1Q@>8%2|a^e3wIJhqXFUS&e0eD&){c7Z5ZXHo(LGK-F_ckJm5hZUnZa3a8XuL049n z+^G9B*IIe@OMI>RXI|iBCK;*~(lzj|@OAkF#C&z6GzMkjE-2DPnq#tU)HT1l-fB_7 z!%-(gHB*r*RnQO-N0Q|WTjG#I-)CX}Y49f5`C;&`f3b1#)6*&7@XpwL-=BQu@TMep zc^vIqQX(S|ZR*mUg3o8Xg0>4q;qkH5w7s{-CE~(b!LRu`Y+zI zMvE$~cZ4gcmIUC$O1TU@ID1_4x@rx4JT?5uI|0VO4SP3{aydiyygdjEc=h zGn1;g_L2yV05f`KUi;@yy|a`AcBqfz1*RW_uhr3F|T+XYU6PD{2{ zwkahlDhlu*xRqla&`KQ`#EOjNTX+~0JUqiaq?{O)TSAigli!^W7Nm-S7>GHlpN2uE z>_(*jp_{hzn8he4`bKZI#;~e3@(u=Im%734SW+m&f3UOiI7J@3FQ^+x38%nu+-WzA zBrzo=B{_KrI)3XCy7Pj2xZXutrB<`QEoQi!e(7LRH-B0ReQ)&Z0Mprer{htWMsjv`Rtl8GukWr-(*ho`9g73l(||h4 zOfJfjM+I&z;+Uh>iQf z4T=Zgna!MeHDzUGB_t$($)eRAfEH|1PCzJf*MR#;9Kk(NqP>8h9&=dZ+fYS{wL?g; zHg%}fdM;3!UtIs;7!q8I?gOz_wB9fi5BN-0K||}?i%%`paZevewXp9}t%yIqH%}y2XI?QQu3lXHn^Z` z0a#bl9D)0;;ImOLO~MRta;3gCd4Gys7(n<2A9-b$Ox!toR+E8su<&)+CG{*w6S1tW zpcEZF{fZyYYOL-l#2jF^0{pxn(MDiO0oMtMxi>a8g61p{ZNpo11qiNomB2HU3B1#y zqUek+M~Hg3Iw;g`rK>ojWp*q%8`nQ+ya6uPYHDv!=>-$tu?QfN;XZhg0$%9)vjhY{ zg=y1(_dnt2k+_U&4z6pdN=a`hzVT%$WI-Lnk(wYHI)!1S0xq}cK4*v2N z5vy1nhg)v$1D1KCx1gW^Nc|wdZeSZ&0|JYmx79`zP*Qx(MN#c6DnxZne8>&ev{#?og$iRk0|Ktw+D8!j3Ij7nm z|Fq`nbm(YngKI-bOG}>M1A0FGJX~H4kg^43fXr##o{&(D28r-Ki9K;NxS}XRBP2yP zuLIoM1ab_~PFE$c^K^=RzfoiMb8TPR<*bSA2*|ygC0f!{OD`*I%gEV$ChwTIK*(eM=2+j%^z9bw{5)$$5 zKrA&jmLw5y=3Q9WE2#1dVs(+ z&6PF{tWWF`mmA)VS=3@x!U4>S#6j}8Qi=;oc$MIRU$TPrQVtb-fv0Dn0IDDH%Z zw01$wKv6TWLpongcojskYT!=b61}=v0*K@KgLKu;FVhO+-vSlWXdUR!f4%&fV-lIE zDTJA?;(Uz>{b@pf6yG>(_63;_THb>nLM&%`vL(48{ZR4+$Z?>Q`mJ;7eox6=|m13=}`3(a(i0u$16|HlQjIQ32iZez4=-q-OyyIL4v$ z>N$OEJ)A5&gFcC(vkUDY{1yHv&FQ4`^`J^MHN}}{w@?a z8B+oCfEa1(PLT#mM&ynbD5e?NpPM#EF`_-i{-jlgEv!oBsTFvt78a1S= z=NKCj#G5$4$ddIT3=NG>qtBh06#lY$rg-H+%i`nX$%Q>`48K|(8BE>qc7chF*v&V(tF9h38?Q2n~{7DMc`UU25>9`l;=RZ{O=9*aIyKEd1Jm{-^-sJwIlZUBN92c zjmREX)bqgcvgd>P8ovIRvFE$02FRxR5A zaB>2WgA-Ul5BAEE8lYVW=2-0=VUPNd|kI03KV~ptx#|6NwfEL&n_ZIl z<;!|s^q|YPIU*`HAj)X5qrAEQ@rOOlpxPLJ0r}<1F{NibjoMh^e(`@wYXA6FSHba^ zK!D(n6yk>n zgLru5pF(9n_){(MbAv#|6+y1@mq2Kx@a`^iZQ~GAt6TA_mgyO zy41S6QMLR2dhEV$-h42e4m5^-^hq7#i7|h%0!x(m{{Q%7gU62_KjSeqT)L-@bYKVe zxg8S!=WT@4t~F&4?M_P6AK@{n(pA#omSek`5)dA4Jg^_trTmZk^A+e5ea`>^Vho)L zm53{tR*J0;<7lIyK7w3hY;4TFD^WH$JqjyM@ISuMI0WfW4Xd-g%OKOx?&W$9lycDT zg{gxhDG;Ae_hZNL{l~8xfqVx{-rfo=lwA+etPuBo{5U5!7nlk8hHr@yNKn~s|N5TL zwT!z3(9Yl5a=IJe!}T7L(Xy3x`AKqANd1@mIMkm)->-LI{ebHV>ijD-@&Ek$&%_#G zejx!C*`xn|SRb+|VYnK>WzMyQ8J&e^Cc)9dtnAgZZ2cH^AJ`-Is(jABWG4Njg_+TP z7B1y#cRaX?CNE~;6hdXb=aGl)cBTx7_hgn3s*$4*OziZ){i6|ZTcLc7g{%s67WO_9 z+HuyelK48?Ke8Pm{KBgk+YTucat(vKU^{Exii}#P+q1(Aya5l)GmBpW01UyFSMD^9 zE%bXV2THmCSB49mw-3b_^xDN43+065PDqT=t?i>Rxp1Kxa^a< zW#8Z@cLml4pbIIX<(RwRK@!Xk6s9rxIlB|qZF=Q4>2WW zC?3`OIM(DQeXWC7IELLe22xlD19XnPaEqJA#vYNT#+@MZNy80fdK7?rb~j5!Hd$2! zWJ{4TkumnJZQsVHGdD}pUY3+>pi7dy)KAq-6J`!ft=IZcnC{+a3b6-1ZrgLAsrG!C zw5u{|d2$9zVT>v7p7HuVl?NwD2o_4u1t;3+iXe%E#V>Z+SjrQ;_3@0oJPO-$Ae;;3 zrc;|bl?IDU%yE1UyARBxH_NP3AAn~i5bsrufXXy3NX99={O(-3_az4B6`(4 z))si%5G6oT+J^D-^ZWQwlUo-+`5oZrOZsw^En;UpwkC>$ze0AEQ@7|!xr5CeHvrf9 zAe~0MAH(z==kg(m^0Bsd16f8$Aui*UkIoy|s3NHrcYCEQ-p(T^(SxQm??JG~t?C~h z8>^(X1J=nj6clfUF*VN+nudyIU^6*D5}i9lDPe}=Ez#1bRtzLPnzTIXx6Q9s%3X0Q zBO$=5-8C}hvFL;UzPB)_H@MFYMef#2gR1g=hIdXri+S%4tq_}NYxl`>l>;jB8n7W? z(EHRto!1)0&vovcDsd^~e1Pb!23OBL%5*O!f{IN+Q4tvXE1ZQP8Ao>s2@v zQ=bZI0Om@Rl}x}Fie6bzaMjX0ASWk(N-x4@=2_oyh}+PEwI>()C7V&tpSQr`(P5lk zvZSaFRz1L-JZc5ac3~%}E1^e8E9~g~fE?*8M)jSA@SU>_6 z!RZIZPFScDonYgSHaS>JYy!6*zyVz7vXoGe!9J3!wT7$(s@JI4ST1!q-IwzXdz~ak zFn|Qccl}Vw81{bois2c?9QV-BcUBAUrk0qnPB};TP9P>UR1Ktm_GHB9$#~b0BOXTp z?g7$Yywk@+@PeaPaDq^{F;%)1C=|IF^*a>@jw|MGDC0Qj9Lj&krVY;WVf;J!vXlLoO>;--TZ+zP8Ey04Hnd%r}K4y@1nR5l1hG! z>b3);}O) zVq%imqKt_F$yVbX6mGPe9(*f@&eIV|E8210s70S^dg5IA6$)CO8_kDG?#}^fG8QPR z5fL8&6I1?dBXJj~yjJ?!t z+lJODE=2j$D1UZse-buf#zN6>*9l6NzBA-`Y?s^ui~u0p#Nh{ay#Th`HL?ac%;FL) z!1D2=zEI{CB+Cx$K;X~hQPG}OsKmjGNc-A7c*BrPKJ zZ#%4^JP!k$uj0dr&xPzud zfuHBA9qciB=(-gc0AoJ?453|_w|h_@=vH34$Exkm#pSQs3+-{5EmXi+Y8kfN3fs8#8dze4*an{TXkz)uzv$_7@DJIVo?X&(mo5IH`z7L zN26lL?peR37T^1{?~!;F91Y;OHrYV45v33N0Gk&o3>xiWY$CVeeyhqSPoKW5wDx>M zxFxW28_W6>fjzW~EtgDiP!CuZkqbk_O}TnS*Fhi2rh;Y$H8 zP-K)4HG+wS6JT=_>WV**cu@Z^E`VH^eo+W{03tJLzecWM@9dsQrVJ`A!8 z(xc}WL23qs#a*DA0*iNofDF58?7|fpLVD8m=Q8r}+XsJrwvSAy+uHm<6xB9;i4FpY z5v2go3YanwAEcY@h@DKZ{qZjxW403>wmr@P-%b)vf&%BFA9B8t`!-F3>#qM-Fz0oy z^E({QsrO<68a_ZLRo#MUJbs?+)-Z;VH4)bze@ihji52BR@Y|EW=ZXUn0knVG0ilED zZe(aEGZADa=RvNSqp5u;GIL{j>I=)mei9$p8z5~1js_T6J3v|w?PEdLoicTH;lZ-w zfb^uQ6$5#~b2Qbo7#o+?PMCn`6xelCQmjzoxiv_(xN{d*oiigr#s07FqhkBzn{1IF z9^QC_TPzx5at83^P{7mPUj!{5v?io77VVBON&Asa#4skv7(f#tkX`)suh&z3SsCT6 z`P&LJqQwCdk+@8Hgol-d#Y-Ket(Vi3gfX}rUKbO+o$v}L3CEK(#-LrhO(j{m%7U^0b#X~va!_ns+%3s>BJ?zsyJ@TH7? z`%(~ZmzS0RYHN7QJdJR&Y^0r>Ex}G84?OU1YKwwn6_4G|hX#uh)3 zaI-o>cldDrJ~t&?47S|K4A85C&I^G0Ef8=4*_7q-vpi_=a4>RlaUDDMphN=%;M=t7 z?~w3JsniTrw@!tVz(yS;f@TI_mxHfT<;zvEPD%&<3qdvF?|<#pzrEIBwD|OJfmzq% zDLlN|x;nXq^RkMHFPDJLK25Cm68Q?yi6lLIR!u%n-7j7q%Yv-nsdlDun-CL&Ha~WLV3cuJZX6psql8 z^A`X0zWzJ-;c~`R#sB;LEL{y+G3V|0>|vSoumeO@sYXs>K)x$$kS(KBcVL&|5Hsmr?ZZQ<6E zQNhh8x(5Vg{{1yisjI$R6z*>*9BK;OQCk=+qq<)Fa?6RDw)w@2B4Xa|ouOS!3xl0F zr`ORF$Q}F7+u<(z2u3~A$ljjW$ZsXfVRdN~bM~wq>du!8>#oM&bVDh0SALo<-hV#` z^u3mRbQSxG0zr6w_MERgr=H>RRQRDcc|2?F@=#Lq-96dd#Ky_uC0)TS5c!0@1u)p$ zo7u}#^0T^j!hMzRTc0zV$vS>ra&uT+AIhraROYolQCX=`$Z6i2QxveKlySv4#^u@N zh^n;(uFR#r!@(Aw2aXQoNX@Cdo9&&AvXbj`IN~vTtKdGzw{Jte#9WJRZDQfJv?ECT z|CjSucot*V@e=cDqS;rT&uWr6n1v?uBr~R~=)8LJ0ezEhzaaw(H6uG*NI=itSig7? zTgV~LPNQbb)bG%-LdB=vAO`G8skSW5DB9{g~M z9QazsZ0y)Xa$#$xTS!-W3%HVCEJx9!Z|3%N%hVXE1+sCx5C1~Rl zHS0V02xVGTjL;=`%7+s*n~u!()=ZQ;7BiT4yu96IZmdw?JVi(UDT5ZKNoAoc-j&#L zt=XyX8+m`MB0839NEl`vx(#g*0WbC%qoRm@*3=)FTrDHc@RecAiDKuerjOa4`E&it zmyS2adUtha__ipO-rwTud+YnQ^aa~o-Lu#HEQ*vpe5N%=_&L$5o6f=`wFS2fj9Xu` zXI^vuwp5dT+C^s}vvhqaf?IevTz{Ozb8}^+aIBVwxcq*dQGczZ<%Y|4e$iT+E9aVB z#R2R@8YHVlxOBfA?osGm=wl6w-)3mb@ThnE!cpL8o9(hw{QCZq5xUg=oW7_-2$xbs zRe0mBw^c1OGgX>06FuX+`r)~j=eA*XwI9;z9XD>!Golv+9C&1c5^7LKOZfwHiz_~who!WL1_TWEIH>&XM3)vF%Dl>rNfVcQDWn~aCN zRoi;L2z*b+QHLl?LdBdSg5Shy1A6m% zap{J7g|KD!!ia&8k6z8%`UJ1rbj!zk{6q|wNzj9(WV%1s7E-=; zLYe*B2@%Jf&n8e5ALT;+YCT2abj3Ht!7Cl*5H3D_s_D`zT{nBU@9ll_X%+PG(?jzE zPWCIGM_V!+g_kX29UgP}AAUnem$9=(yL%vJEjl^tA!-^AiiQJ-G1;$c8Fd`XPPmYq zexVwFL+IS9c5%)bMlS!MhT~@4r!m$C&+^@9Ued=qBo>pQS*@bFK{LKK$DYpHN832| z;bi*oeDBi4C&AWeEnVi$av@iYPsO;{?ueLmT!zcq;h{IB3riETw+72+x;ENAS@hQM zS&Z8DNAUU^S8!~X&kI&)r%~?;R9G{+3RU-9a9dGXw1Y`n;Fp!DOC)~q&-)kUjXR9_ z)HLVF>r+q9{!~_RlG$i0(ZckMPCnb9M>J{VQRN%RGLm_g^`Hg!fi8V?9hu*l*pb*g^y9keD+yP9gJ6T1(q zy_sciCejetO#-5-1qNKY`2YAsw^SSi`-_9=1XM;jW2J5wEe<7gxbT#PG__4V@6Gk1 z6V_bsrudRmG?yfwZXmJKC7D{r^N=h%XA}d4K~zvsJ5VYN;sH5u5x_>#{liyg#G4yx zqvjZ`^Y>#F5XYR12-gpL!-yB@SO&1ketO{|kc!p}97JrLF!c-*=TZ0mVN*Y(JR#D2 zgzD|)>X_AGUg?V!O&Rsgjg4AGw+)~$mG^VRg29H}m$0wlLLmE;_#OTPecGo-+V0Bx*PC?)`L%4k$4)eXGQ-q6Kv}cxk%1Er$mEQJ|nCv88(O1wME%va*uX16R<&feT-J`T@j?s-M*u zO8f+BuTmSqKpoQs6$km@k_YSS>usXn{Dxj)Rbj(GslZ(r9s99SL++279z{k-smCxb zN#PzBC*!Cdtk?!z%#|-aTzq^59)%r55`~utGe<7isLbv3eDF9Re2YWw$+}>X%1}YpCa7lE#BK|WW-aC0vRogGw z9RWt=0PHmlKp$HsIRG9%0E33fZv9N!_d*lE6+s&QEMO{C15h6OV6!zSSU|7bbPxuZ zxGVCE+=rF+8=7T6It9*froM2-toC1+W}@DSjh-T$MQn3&q&^GuhsYMM$P? zhD=BhPl^8{NB_PX_6jTT$Q1tjFTn0akCZ4C)ZExNWjoz*~u&4IJ7D4ue7-J?W$}IqU*9cxz2cX$||MNDe zs$9KLW_Vl~&59}s`wU2SO`WSM&Z^Cwl{VSe`!XaB5CWFBq1m|EU z!`5X~al;~TyzTuzkM;llY7r=_sHngc+ZFH>E78Lv`s?lx_dW}hV8JmMBsp=P_w)GV z1-7}1mgm?UVpyN5?uY05q`IwPA-gihDU<<*fT0SPFYK}t1Nqak%!R=H% z_TUPW@PRwtvEMk22kT%4xz~%E)lM=W0BmDyZ)j^9D!!YntL;&-2Ax8HS78!ncnolc z2`Ns(=v94&Y4ezHn1ojfwn`r%InT|>ksN_hb>Lza9vPYH2_8$PJ6ju@6uongB&DV6 zK-UHh80e~q+Rm7v1^mfcH<1kDH{CPv-9&7v7IL zb4>U^cGi^3@SRZ$Clu;7D>SaW%=5J2cpQM65}B0;v=x<1T>l%>36abPCj(IM$9qB-;<>PWP*d{dDCsl+uzHR|3tjA% zZ~88tK~;Gyp05>s{_R4wPa)hWQNSrS>HGjBV*%Wsxq+m0w+LSJloT}rJvb~bUs$P?k-K} zUiKM;&4H4Yyl}Ny6*1;bV)wDns3ai8K`9W-ALFa^TF7yt9oj7By6wn5SmC(N$zU}W zHI&4kg$FxnF9cojI$Hqw(*TGc@0lTbQ_vj1eB^|;+DyKd3q2W8}C!cv^rB zc%>cYh5AKw;Ll!XntdzqQq2|H|K8|aXV@6mNO>N2ZQzaVh8^wD7H7FV2ZYwWSnDPj z85wy`1q>R%e~}lQUX!(}kqM_ykM;s6j)k)iG#w8{^Km~I#$1XCI4fm+M^+FHr)2eL z<>0BLgd1`TCXqQj+A%OJHk9NE{X(nIg5Q%TRL#=FDDpGL~H@Z-CSuc3xftY?sHYd{|EZ64WhvMc(^f-VZEGa-B%F& z0Cc4QBP%DMW(YZgOA{ub7pc|z{xfE!qBWyFArC%c8hWNlufQDy{P0wW7#j*mj-Cg{ zT#y%Ahw#e9Hk1{F;10r`pm;4YyNawQ;kJd7?^H!|e}-Mu{7C7i=R4@1d%#Uby2ZWm z)H6b4Td#qO@(vL86%`Z`DLiIdKyh969@h7anlR2cPeFquztQ-*H3vBF^==`>71OLil2zir4GP=+2>b4dD)E}BW1PcLL z7>E)pAm!GQU(%JzQm_M;UeE;H)a{b^$3{WouBV|KKhT#=?wTp@?tXzb#FRZ{8_~Q3 zyp(9C1UoKT$8RvXSSR01Eh7=wRIo$33P|vT_WVQcHXbKNRRgNFERyacY^iM*V*2?V9Q*XIs(QlF1y>dN@?VIX+Y4X7qG7F2saRp zFEfA}3Z!KDySB-cbI_v!ml8x1Mex$R`Yej^8tzxJsG&EuS{48Dmr%z&hmWAhP)F~1 zU$z|cT8ATj&T0(uMTzhEqMJe_gt^_EJ_^f z|3v=h2j=9H?2w?IM;g;-FR|&L`jPrEavJJaqdd6I*H(E)P@z!9SoQILNgn;YwF1FC zzzXF?fdG~x=U4ucH!?M#QMakj8#8zAgBts10$W9pLxYOOYtp3EO|Nea^Ct>zsuyPoL zo>`GM7>Xd>R-j9ftAVdoXd!OvF#!Rsqn8BZvMP53>2%eHF;KFAH4%$4;w%a>!6qGd zZ>(NoR4evWx<{WIARnljFfi=#J{`*nMwftKn8ZV#sEv$HhG_{CEpB($L7f5lQWo8N z;8b-CfnSyXfkO3l7y!@^^*{#uuO9~bmbbOFCdCzjaIH4*%0UNLTfne-G)43s&9`o%mW?^<72PJKwuQck2<*TFK_7_X#Y4Pmuc=o8rbxq zDiXS&Xwed>bg&3}y6^8l4eJe~kWWRf(Yz?PqBsxM?UN0c3f&-@jwGeV@$||oDz<`@ z3zC;H2u+Z#$Zv{BX90sQ9$q}?B%kOS0FXW#N4?k3UH7hB!*Rn-5-@&4{(bv<2*Ap7 zL~h&#P8V1sPh1&R_v#)Gq3pNDKx7$D-5AnD6?2RU5=*h1oXv}#+o)l#khb32Qs2*$K`Xouu$5TxIl>Lu zw7`M$k%3H?lYYb|8n82;a&DqPFT&RO=LoINvi& zfN-(#Amef6X7tXYmzboK6wF~z&V0`eJU-CL$n%$i&Bhvd_vi3@76ga!_8xXqipsy5)xl@qE3 z!}E}|3yCkn%7xcHUeBXCOivNT4S<(?XS+t^-}6F>_*npS@U=m3%u)EM zhUi@cwbDGhprFHeLm<2w4AfO86{AyH24`n)VOl@J_11ARkDsXYOJ?bcP~}>mIRynp zDhsH(SbUkC{aelP0gdD{dC$mZKa-k#lX7t)T)$-h5aRo!07^PI_Da-G1!Y~YU%w7+ z%eOaIOr0gbw#c-jAYh}R*2USO4jpuDE21rfX&D*sGwRPRxb++JCT*92yf$wWh?u5W zAOTGzU%F+e#?x71V?Otw9X{Vv_*k)Ow%HOlR={r?T@p9seQkYxhm^wZIvK$^$VjxR zuU|X)6)m0^A6*W@E>Z?j-Q6*Ez+OK=%%#5eVNCYq<##G`+zn7n0Gta3M=~&U4!ne6 z?}%K6PAR-z3H{IAW0AI1kLyb(pP7H&j0_J?#(TdMs)6nWEA(u(BhR?6KcW`T;}Aa` z<)nZ*@!K&EtN}JIXy^01e8tx>01g1HZ^x4c9l$P*tGYSM%*1pZMwGxMW`swIZSuAD zv_lyvi9h3jKccK>U-&3B7g`r8WA(+(8!9U8K;O-802Pl(>DlA=6&-=Uw`w;@_j(lb zxyjTiF9$L!Zf@?;Y=RH(;dHEELAhZLkx!FvBLXE8vJ*{m?)%xCx`R;U+!jvj7A>~9 z?&RtoF#vsV48)FpEFx~$qNsK3GG2A~_+yHRdFzBRbmI0ti17*4g5o-6wCpbarL)9q3W z;#<>7uAo;TZ_6r~+izdCkK~9+T8!vP6uFDA@f)1Is8wa}9efalcdO*lbwzBYk319U zWA(EWP&wWxp$S9OD|u|&4t8%JQ!)GV1M}D!nr|CQHy5wEr>Cd0#5x6;(SVgZ+iPFA zx7Sv-4GIgO<#2AH3XYWDcHmk^(Va2ahSN9%OIDzFHSD!8KH;By%o$$amR;*3d@~&2 zbGR*&v0QH=SX+oBGg7lUV7#XHhMVPB!ma1l-4Man7KXTJigm%*hIVcSA`4ks4Z*a2 zP%*3yTcGeDP{WhIngYucc+zkH{nTF=LmbO%giWB>4D4CM7=6a^ZuMehWnkU|$y-P> zG>C5>UONYKE!3Bk6|%cP=YCRs;C@|j{oT+lc!w#^a}E6VuK8hMBBi>>clkK(=`(4z z5zalg-^n(x>Lf?oz-a`KDc_<$OrZg};oI90Lqkm3XG9nXH-w~uy1IG>HZ>40VM+nm zi>CpH4DQNMVKkTvz?=}Jml(JN{Ka<8Bi6j?xCr=wl|TY`$<`ZEF;G;d0zv4*hY!d# z+6T8K^NABcE2@BCbTtPkOiPZSmF-#h^tk@qH0loIGf?j5-RiZ5okFQ3!aeV`dH*ey z2J~Qzp(cciJ-!lN5LB*bIlj;?jbq-4!6q=9x^*alj}cJX^Q7Y)0q(ah7yY*GH{V5k zsBm^%I<|F=@(OUiKmicq6fSxTPV6WPH9?L@tm{rO<~cjKJb*2$uQ%vt!Du*Tjdp)= zaWSO=wY+%}#Ij9|M6f4H2Q&+K(=XtD%|WV_@6YW<9TpM6I_PuJ@_iA5yF&VUqfj+i z@=A1TRN`MZ=7D1GI(FQpxu2Ka)B~p@DghaU{F5h)LNeggBsHR8`~vVcUxSC>9s!n> z<-!CE6#?0J_y(=y)1v}8_REuge*24G#D&6pP=vT z5WMyrRuo)@dfLrzo*soi&B)p!KpXZwI=A9%@FX(oP+0!){*Mbd-f?s!aU)|1YSSwG z_aA~}U-86$|HuaT07KpDt`UVYKG-;wuJa>&{Po-ZIbZ(UV*JlPBL&z0EX$9x@xQwr z`VKrFs#@a+BYq=WaPm_`uo=;UmhqR6+yj5AB&zIj`C~;_4u1P4!#AVMw}Q1cD{*?` zaDgdx^AK9|+eGEZ#Q52%{3Z2V{Yjl^`KK>)KKSR9mcH&6$~jj4@m;p?P-wvtZw^mM z0!7;;{xbIPaOv2c=l+k-rD7r1u}{JQE1gQ*NWZHUW7jIdG*uu@q8xw$+Uj4fI_nYzpfOOvJ#`SLlRdh&24zi`=j-g1FiC{pOu9w1twA6 zC?}bDy&x9WET4d1vK*t~$KLJ8)yRz%oIzMPaJSv5yEy%_@Rz%d4o0VL)TdK4$zOl- z4P*JWv|Q4D(>UGKS-s-um)^i_%s%?}#ZUD3+PY5}(;0E0&J#!HlK;5$UJJ$Yg*O!B zN8!|KWC!{$28BK8ov;W~u^>v*d#Lv6%IM4Pms+#!eD>^L0^?7~CG_(I6F+4BwM2{G zm+1Y~o%?GaDDUg3Qd6|lOD@@MFN_b`j5&IyQN)aLw~O3&ZTz=C9%4Z}FpzZS&Dt}w z;GM!p^;?*I0(GqHfu?tR<^6LFNSBhQbNHyjjje8)4^1|HA;z(*n%V_;x(st#^=mEN zjk#rgj-!oq%XS$qq={D4*&4A%Z7UP5h3_zfDTz_q7?x4Li3@>0SM0Q%F#BEE2D!v! zLNcP<`S+K~cIUJy>F+d-!P489YLv-+^YOjmTBJDWqi+OTPw`Z4cyx3$*jtYP3BlSE z57G6%!vP&dSb_v%c>%=$D1z)D0ju2EOw*my+<|#IoH_-5qMcyOGcH(}19qcOm?NVZ ztvxNERC=h5cxY;pIN@)}oNDV`l-Mg1Z`0qO>X@{Svha;!i!GlJ>AFvq7g4r|BI7vE1(HRV3A%{G0rF|#y2+QQ!kG`rZz z<`6*&7E$(0=bfyrr})abg1#K}be=uZ%)A+Go1ae7?30HRcE3$_S!*h17*p2Dd>PGj z`J_1OJHhr$ye+Q7it6oSdh5@^KYgnwueNxGVY&81=v4WSbO?aDSf`~%fOX>mhtAso zFMQ@pn%4jpaU%Ex8RubvK0elkg`O-R3YAU}*k2O z7%&rCb6cK=Lvkn{F0c>2DY{TLzW8|5j-wxaJ}_%-R&)J`|EG*CUj;O6=+z|h(h_>d z85h3ojhdE+tiNx2&W_>}jVIcjusv$hJB(KBPULv6nlFo(GrN{OD3#kLzO}{tBSkAK z6JFx$K|9WYbWs~%qISD7&{ni^2LuN*UFxp`H~CcBajbD79B6)--nw-QKp{jt1a;O} zy?@6LpcDJL0R5;uyBID*(ueR(ZcrDkY_^D1FbTUV+DbD%tS68vns2(#5j=ibI zzC64%VYjsD>lHr=7TSv#0-5%9{Su#e<;s;*h9IZLCks-NymMsQ~n_+R-kT(0@ZPz{1y zlPR!hd99uUW8c^gpSkA7*(Ab4Lb<!%<+`=xLG;&MuoRElpPKA{s~m^Z>Qak2 z>Gsnl0ou$TN!6Qqr#{`|{oSgWr)h7~2z_Nhn=^NHhvfT|@ABUt%XbQ%(Rhs`KYR8yz$2?y<+3OVf37dN)wT8`EcurXGS*6vLnMXa+BBuTL z!XH?u6+_Z~yLmRwV*KvP_SK$f|AI@9sx{R;7U)bT6fk5!ooCXZ-xBmuX0V$p$EHau zd8OCancfO4Tfp2{KhGra>P`cbKhhGLCF1~hFEB!S1)7t*w9lVE!}L5WNdJ8mE?-U( z0hr7HLJ<@LH}I5>K^37!3_c90I^jy;KhYBHj2JL^QPs4z&e4sR;JG9r#~{b^6~Mic zGMjd_YmA`9?2NmB^C~F<7*Qi{ykJa$uXeEF%j9Haq69JmQ+pEx*81DFOd_k73_qQB zVRwSMW3vW401BWW+awo(IS$AS(b|q7DT)mx$L)&jqWcQ2X!drka&5kw`LCKipP^kJS=so`tC7Vd zO2usR>&LB>tD0+7IyW*6-0b(#SiC3~s3&DbN$KAui!q$)(}?G>x9F6-q`qX~xOIV( zGy;m3xtQR|+}0!>OpQx7?C*=?D{!d^ja!9dBI*|cakh>lQ9&-Y{Nogc^+_hY8*q|Z z^&B^29dQ9pogSW*6Ed6v+RR`k2375%zw1^4=r<;s!-t@rAfQfl0!0wal9X381;rMG z6T=d(6(}~OCm+kjILxFT#zRcuv~?U(ju8c?`+Ar>{iJMYI0|kJFat?(t=*i(2kKPS z`kl4H%9dEMT^OD9d8ks%S-Rz%-OM;>P5?)jDLsx~3g&g2v>+ms#=z8*liMGTbQQrE zkW+|?`vZ;W)|uVdV@g&X$IK-~U{J$2jJ#5~3_x0-XhO=1M<#Co5=S#A?ab8CT6L;R z)k9P1f=-~9{txi$9(AYDE^hG8-IhdBSL!k8@d#k$%Xx;OsGjn>4rt z-Ns;@z{$+VcBf5}K}pEp*QUcD>&Y|UeIq3=tqp#QOkF-SgSoA>W2Q5%EdCpP!%w#V zcu^tkV^H?qJ4Lv0nByjlP=XQ?fYlB;56_QzrW72NFoZKt;iXKPh|SDZ@Xvw!?3lU~ z+Yo|YX$e`M4M`2=f6vG5x?hD!RI~zE2-c>ul6OH1Y@@Corv5oU*aIW-!iau_;gbam z%;=F~a{_zfydZcHDufp7>H}-_1d$YG@(wQp%lIwiCsp-RlmY5@oG-q_$x&5Op9VJM zYqeY(_RnN^%{s|!xh}6Qu$dWdtz)b`r~GFD%eq(fMfu!9e8%sfkb$kJ1?TduZ&6ZZ zVL2bm@oZw#Tns4)IgZcy}ZHtj}z3pjdxF%c5OOK=LieMkBOn-Y|#fa-E~! z?%itN3BHyfetxS?$#2xlbb~g&y7XsypWW^kVQDdbE^j*Oxt)IQtDVvLdQxs;*1fFZ zl=GTlTD`oyd_n}wxR7F(RZu|i1zIVZ*y#_Y&ipy^HJ6nxm^|0jFqK$l6vRNFL zSMsDoM``4DtVqlU-L@^rHJsAefT9& z+nwlBH;{C8IyYR!K{(i$7Z27f@nmhzYbTJqlcfQt7ZBa{Rp$84XetqVRu%i{sGC1f z#e$h`(Zi`=E9gl?HQpZNJqeIyD%VP%ti}U=S^;aLi6%U*v+dkq8+CBFAQ*Cyx+xsc ziXEk(V6to~p7Rfc`$ak89v24YBPG-TVE&ny7tqerE_`EPICoi2PP_DehWd?xF%GR9 zNt)3YE-LZsD$$KQgo0$7<2lOlPc?XkHAkA47Sv#rT36TJN|Dc7b& zx01_wc5c&sTL>COHC;|k9QOU{c{R1x^_+GY4Vo(}kcS{6lQT0(`qVa+8Z5esb#+f# z9SNJViQleL?E49LxQfGsUW#oXJu zdBWD4pZW1sw1brB4=B`fE;tZ!l(P?Q0&n+6_b8S;jwwluUk5}LMgcvKjFhbinXaIp zEC|k<0vD~%^4?~8*5)I_9->8^OEg#WT#x^O;i`Bg|A-n*%IBv47B%K* zwkraMoIWmce#591%YFXj`thl~aDvE+vX2n0wEYu>UB4leKu)`}#VM%`{(KjnP*)B) zeaoZ|%e0z(x-N++Q}?%aB9wA?M-Ds}Fcn4WjxT}gC*5-7&V6}D)nIS(eT&kRiVW`V ze60jUXg$5-EC^wt%1RE=PBViJ25d^3?6a2VTpGtXbn5go#=6EXKNk$GL57?bU-asq zEW6*XM%DoV*trqBq9R+zisy`=H2Kn%mdthRa{@$j?;8*oE=qgWfE*Jp-=;FrD7J>C zrsE5j9W`HH!^)^YAm`KDx{d%jKcTmRKUzZ8gJ8~M#`g|GfSrlzVUT)1wijKSlsnF> z{rRFIcI^Z`X6E?+Y;~31r4D{`XUWBf)hZNY_-KnQT?2TYv?TOmxa@`R)cf5>A02s$@F;)`0p=;AHe3#f6DZ>HIuS3B_54I8^iu$5M(bO5 zT;}I~UVpd0@tDNAt@NtWi^pe{-WYaSxcVK1A+&5TQ)u78J0@T>cTf4|t$~uzQ9y}p zGyvJ@#N%kg1~j?N7T~JnFfP_Bv=H%sR-L&1qli(_NSX$LG?n=@`K^jFf&^KhQ~i1 z0yV=O6WI!|L;wSW5OxhqsFA=qBe4NY7og^bp|n1;Va*bE(L%D`vC?+nT~`54t7a3B z!BXpQw?_x{EAGG?3m!(wVt|Cf$l(1DTr?foaW}F<`*(_xTz7XUH(*8sw3f}Ga|5jd zNSC`}d_o&qTTj}qfJ|SRKZVrD0c=5z(9v;Jl!b$(N17W{{gy)&&ueD7Gvpo5Ig~+v zMx_+Mc9!4`sAW|fA-yY~A5uG$pW>CBot;&_c7zHDqW)WhV3L(DLW^8HQ>j0S5DTe| zXcCQo!D5?weeIOJJa%q|04E8kMFpRk-l{&k%IQL_)P@)~m)b5wL>#ivY1#o3`r~E; z$Qs_I@?buB^09`=mG-QxAV$(|80jImoj=*I3JqAFg^Xg0)`^VZOzcY^AZTQ)74%y& zeQl5%B)Qu!i+SVF_ozqrF3Dl??UV2HT>S#>(nB%T{RTSnvR>j!mX@4!>PykurBJ);ibgiaAP zw=|3qwh_N(-I>1fUi4RU?KXWw@-aNfFs>_Jl0^_|^iZ^!fka4947C>WDr=FSXmwbRg;pwwb z`o0Tx(YY>?KQM{EJmJGXkwB0yAfYwIZ?`*=6W7q~L3_8|(sQ=#Y#`D?_zO&GpfCEI zFMPKkM>)ANglRkNQwDmeM}i@{(%&e7exh<t2Y=w)8LcQIURqn5)6_q;=_Tz;lh0NWqBzC(_5T@N9GPTdvEr8}}oR zPV2AE*XeMFnUmA8>pW7+tZEA7nC)8ine|c{ZxC;?DAYexb)?f^$n|tMz8>6+Yb~;O z@ov3j1v>>$D4(On2i?BUMyD zIcoUy?N+%zYV-;x?Hm;c3n%{;+P1U&l;OI4b(59aFKGWj5_2N2YjwXIUGlRLCRTEJ z^u#6P$#mPz@^_WGi9^LQW~RFrg{Hz&Z0tXOY_YH(2@HM993!~+g{BM#U=bz;gX{P? zZ-YO+ZLy|E9AL<)BpHoTa*f$mUoxniZP|MCZs^id|EbPez$QH{OUtj*>r-bqZVz9? z4_hLYaZV6vbBT1$l{v8Ji`0lw!IRcP+#a)0$ZOJ>(`#VTcX1#1%0f-o@-(Rl>xAH^ zEYZf8>E>O_$gw-SoAybKMs?3zJ*pc12}I?JJ7Og?JBG9y5D9cJRMW+>{mVjP)Af(iI62lzaRG>istop^;#sf37yJHJ| zL;$=xc3%mh(1haxUkHALMmRDYXD11jvTZtiZS3%}1S=kEkW^%yxBJA1K5jPUH`W~T zVe%FTcE2NmhfcL$J{VaVTIufo+13%0DX9`w9Dl`-#o@5(M(*y-Cs~FQ&DnQ%v-=(_ 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 QWrA$+boFyt=akR{06B0bivR!s literal 0 HcmV?d00001 From 9421465ffe3494b8f525848325ab3f02aea9ef18 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Thu, 3 Nov 2022 13:22:41 +0100 Subject: [PATCH 32/40] images in docs.rs now work --- .idea/runConfigurations/Docs.xml | 3 +- Cargo.lock | 19 +++++++++++++ fsrc-core/Cargo.toml | 2 ++ .../{img => images}/event_man_arch.graphml | 26 +++--------------- fsrc-core/images/event_man_arch.png | Bin 0 -> 71832 bytes fsrc-core/img/event_man_arch.png | Bin 71833 -> 0 bytes fsrc-core/src/event_man.rs | 18 ++++++++++-- fsrc-core/src/lib.rs | 2 +- fsrc-core/tests/pus_events.rs | 4 +-- 9 files changed, 46 insertions(+), 28 deletions(-) rename fsrc-core/{img => images}/event_man_arch.graphml (98%) create mode 100644 fsrc-core/images/event_man_arch.png delete mode 100644 fsrc-core/img/event_man_arch.png diff --git a/.idea/runConfigurations/Docs.xml b/.idea/runConfigurations/Docs.xml index c4efeaa..43ef045 100644 --- a/.idea/runConfigurations/Docs.xml +++ b/.idea/runConfigurations/Docs.xml @@ -1,12 +1,13 @@ -