//! Event support module //! //! This module includes the basic event structs [EventU32] and [EventU16] and versions with the //! ECSS severity levels as a type parameter. These structs are simple abstractions on top of the //! [u32] and [u16] types where the raw value is the unique identifier for a particular event. //! The abstraction also allows to group related events using a group ID, and the severity //! of an event is encoded inside the raw value itself with four possible [Severity] levels: //! //! - INFO //! - LOW //! - MEDIUM //! - HIGH //! //! All event structs implement the [EcssEnumeration] trait and can be created as constants. //! This allows to easily create a static list of constant events which can then be used to generate //! event telemetry using the PUS event manager modules. //! //! # Examples //! //! ``` //! use satrs::events::{EventU16, EventU32, EventU32TypedSev, Severity, SeverityHigh, SeverityInfo}; //! //! const MSG_RECVD: EventU32TypedSev = EventU32TypedSev::const_new(1, 0); //! const MSG_FAILED: EventU32 = EventU32::const_new(Severity::LOW, 1, 1); //! //! const TEMPERATURE_HIGH: EventU32TypedSev = EventU32TypedSev::const_new(2, 0); //! //! let small_event = EventU16::new(Severity::INFO, 3, 0); //! ``` use core::fmt::Debug; use core::hash::Hash; use core::marker::PhantomData; use delegate::delegate; use spacepackets::ecss::EcssEnumeration; use spacepackets::util::{ToBeBytes, UnsignedEnum}; use spacepackets::ByteConversionError; /// Using a type definition allows to change this to u64 in the future more easily pub type LargestEventRaw = u32; /// Using a type definition allows to change this to u32 in the future more easily pub type LargestGroupIdRaw = u16; #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum Severity { INFO = 0, LOW = 1, MEDIUM = 2, HIGH = 3, } pub trait HasSeverity: Debug + PartialEq + Eq + Copy + Clone { const SEVERITY: Severity; } /// Type level support struct #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct SeverityInfo {} impl HasSeverity for SeverityInfo { const SEVERITY: Severity = Severity::INFO; } /// Type level support struct #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct SeverityLow {} impl HasSeverity for SeverityLow { const SEVERITY: Severity = Severity::LOW; } /// Type level support struct #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct SeverityMedium {} impl HasSeverity for SeverityMedium { const SEVERITY: Severity = Severity::MEDIUM; } /// Type level support struct #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct SeverityHigh {} impl HasSeverity for SeverityHigh { const SEVERITY: Severity = Severity::HIGH; } pub trait GenericEvent: EcssEnumeration + Copy + Clone { type Raw; type GroupId; type UniqueId; fn raw(&self) -> Self::Raw; fn severity(&self) -> Severity; fn group_id(&self) -> Self::GroupId; fn unique_id(&self) -> Self::UniqueId; fn raw_as_largest_type(&self) -> LargestEventRaw; fn group_id_as_largest_type(&self) -> LargestGroupIdRaw; } impl TryFrom for Severity { type Error = (); fn try_from(value: u8) -> Result { match value { x if x == Severity::INFO as u8 => Ok(Severity::INFO), x if x == Severity::LOW as u8 => Ok(Severity::LOW), x if x == Severity::MEDIUM as u8 => Ok(Severity::MEDIUM), x if x == Severity::HIGH as u8 => Ok(Severity::HIGH), _ => Err(()), } } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] struct EventBase { severity: Severity, group_id: GID, unique_id: UID, phantom: PhantomData, } impl EventBase { fn write_to_bytes( &self, raw: RAW, buf: &mut [u8], width: usize, ) -> Result { if buf.len() < width { return Err(ByteConversionError::ToSliceTooSmall { found: buf.len(), expected: width, }); } buf.copy_from_slice(raw.to_be_bytes().as_ref()); Ok(raw.written_len()) } } impl EventBase { #[inline] fn raw(&self) -> u32 { ((self.severity as u32) << 30) | ((self.group_id as u32) << 16) | self.unique_id as u32 } } impl EventBase { #[inline] fn raw(&self) -> u16 { ((self.severity as u16) << 14) | ((self.group_id as u16) << 8) | self.unique_id as u16 } } impl EventBase { #[inline] pub fn severity(&self) -> Severity { self.severity } } impl EventBase { #[inline] pub fn unique_id(&self) -> u16 { self.unique_id } } impl EventBase { #[inline] pub fn unique_id(&self) -> u8 { self.unique_id } } impl EventBase { #[inline] pub fn group_id(&self) -> u16 { self.group_id } } impl EventBase { #[inline] pub fn group_id(&self) -> u8 { self.group_id } } macro_rules! event_provider_impl { () => { #[inline] fn raw(&self) -> Self::Raw { self.base.raw() } /// Retrieve the severity of an event. Returns None if that severity bit field of the raw event /// ID is invalid #[inline] fn severity(&self) -> Severity { self.base.severity() } #[inline] fn group_id(&self) -> Self::GroupId { self.base.group_id() } #[inline] fn unique_id(&self) -> Self::UniqueId { self.base.unique_id() } }; } macro_rules! impl_event_provider { ($BaseIdent: ident, $TypedIdent: ident, $raw: ty, $gid: ty, $uid: ty) => { impl GenericEvent for $BaseIdent { type Raw = $raw; type GroupId = $gid; type UniqueId = $uid; event_provider_impl!(); fn raw_as_largest_type(&self) -> LargestEventRaw { self.raw().into() } fn group_id_as_largest_type(&self) -> LargestGroupIdRaw { self.group_id().into() } } impl GenericEvent for $TypedIdent { type Raw = $raw; type GroupId = $gid; type UniqueId = $uid; delegate!(to self.event { fn raw(&self) -> Self::Raw; fn severity(&self) -> Severity; fn group_id(&self) -> Self::GroupId; fn unique_id(&self) -> Self::UniqueId; fn raw_as_largest_type(&self) -> LargestEventRaw; fn group_id_as_largest_type(&self) -> LargestGroupIdRaw; }); } } } macro_rules! try_from_impls { ($SevIdent: ident, $severity: path, $raw: ty, $TypedSevIdent: ident) => { impl TryFrom<$raw> for $TypedSevIdent<$SevIdent> { type Error = Severity; fn try_from(raw: $raw) -> Result { Self::try_from_generic($severity, raw) } } }; } macro_rules! const_from_fn { ($from_fn_name: ident, $TypedIdent: ident, $SevIdent: ident) => { pub const fn $from_fn_name(event: $TypedIdent<$SevIdent>) -> Self { Self { base: event.event.base, } } }; } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct EventU32 { base: EventBase, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct EventU32TypedSev { event: EventU32, phantom: PhantomData, } impl From> for EventU32 { fn from(e: EventU32TypedSev) -> Self { Self { base: e.event.base } } } impl AsRef for EventU32TypedSev { fn as_ref(&self) -> &EventU32 { &self.event } } impl AsMut for EventU32TypedSev { fn as_mut(&mut self) -> &mut EventU32 { &mut self.event } } impl_event_provider!(EventU32, EventU32TypedSev, u32, u16, u16); impl EventU32 { /// Generate an event. The raw representation of an event has 32 bits. /// If the passed group ID is invalid (too large), None wil be returned /// /// # Parameter /// /// * `severity`: Each event has a [severity][Severity]. The raw value of the severity will /// be stored inside the uppermost 2 bits of the raw event ID /// * `group_id`: Related events can be grouped using a group ID. The group ID will occupy the /// next 14 bits after the severity. Therefore, the size is limited by dec 16383 hex 0x3FFF. /// * `unique_id`: Each event has a unique 16 bit ID occupying the last 16 bits of the /// raw event ID pub fn new( severity: Severity, group_id: ::GroupId, unique_id: ::UniqueId, ) -> Option { if group_id > (2u16.pow(14) - 1) { return None; } Some(Self { base: EventBase { severity, group_id, unique_id, phantom: PhantomData, }, }) } pub const fn const_new( severity: Severity, group_id: ::GroupId, unique_id: ::UniqueId, ) -> Self { if group_id > (2u16.pow(14) - 1) { panic!("Group ID too large"); } Self { base: EventBase { severity, group_id, unique_id, phantom: PhantomData, }, } } const_from_fn!(const_from_info, EventU32TypedSev, SeverityInfo); const_from_fn!(const_from_low, EventU32TypedSev, SeverityLow); const_from_fn!(const_from_medium, EventU32TypedSev, SeverityMedium); const_from_fn!(const_from_high, EventU32TypedSev, SeverityHigh); } impl EventU32TypedSev { /// This is similar to [EventU32::new] but the severity is a type generic, which allows to /// have distinct types for events with different severities pub fn new( group_id: ::GroupId, unique_id: ::UniqueId, ) -> Option { let event = EventU32::new(SEVERITY::SEVERITY, group_id, unique_id)?; Some(Self { event, phantom: PhantomData, }) } /// Const version of [Self::new], but panics on invalid group ID input values. pub const fn const_new( group_id: ::GroupId, unique_id: ::UniqueId, ) -> Self { let event = EventU32::const_new(SEVERITY::SEVERITY, group_id, unique_id); Self { event, phantom: PhantomData, } } fn try_from_generic(expected: Severity, raw: u32) -> Result { let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap(); if severity != expected { return Err(severity); } Ok(Self::const_new( ((raw >> 16) & 0x3FFF) as u16, (raw & 0xFFFF) as u16, )) } } impl From for EventU32 { fn from(raw: u32) -> Self { // Severity conversion from u8 should never fail let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap(); let group_id = ((raw >> 16) & 0x3FFF) as u16; let unique_id = (raw & 0xFFFF) as u16; // Sanitized input, should never fail Self::const_new(severity, group_id, unique_id) } } try_from_impls!(SeverityInfo, Severity::INFO, u32, EventU32TypedSev); try_from_impls!(SeverityLow, Severity::LOW, u32, EventU32TypedSev); try_from_impls!(SeverityMedium, Severity::MEDIUM, u32, EventU32TypedSev); try_from_impls!(SeverityHigh, Severity::HIGH, u32, EventU32TypedSev); impl UnsignedEnum for EventU32 { fn size(&self) -> usize { core::mem::size_of::() } fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { self.base.write_to_bytes(self.raw(), buf, self.size()) } fn value(&self) -> u64 { self.raw().into() } } impl EcssEnumeration for EventU32 { fn pfc(&self) -> u8 { u32::BITS as u8 } } //noinspection RsTraitImplementation impl UnsignedEnum for EventU32TypedSev { delegate!(to self.event { fn size(&self) -> usize; fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result; fn value(&self) -> u64; }); } //noinspection RsTraitImplementation impl EcssEnumeration for EventU32TypedSev { delegate!(to self.event { fn pfc(&self) -> u8; }); } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct EventU16 { base: EventBase, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct EventU16TypedSev { event: EventU16, phantom: PhantomData, } impl AsRef for EventU16TypedSev { fn as_ref(&self) -> &EventU16 { &self.event } } impl AsMut for EventU16TypedSev { fn as_mut(&mut self) -> &mut EventU16 { &mut self.event } } impl EventU16 { /// Generate a small event. The raw representation of a small event has 16 bits. /// If the passed group ID is invalid (too large), [None] wil be returned /// /// # Parameter /// /// * `severity`: Each event has a [severity][Severity]. The raw value of the severity will /// be stored inside the uppermost 2 bits of the raw event ID /// * `group_id`: Related events can be grouped using a group ID. The group ID will occupy the /// next 6 bits after the severity. Therefore, the size is limited by dec 63 hex 0x3F. /// * `unique_id`: Each event has a unique 8 bit ID occupying the last 8 bits of the /// raw event ID pub fn new( severity: Severity, group_id: ::GroupId, unique_id: ::UniqueId, ) -> Option { if group_id > (2u8.pow(6) - 1) { return None; } Some(Self { base: EventBase { severity, group_id, unique_id, phantom: Default::default(), }, }) } /// Const version of [Self::new], but panics on invalid group ID input values. pub const fn const_new( severity: Severity, group_id: ::GroupId, unique_id: ::UniqueId, ) -> Self { if group_id > (2u8.pow(6) - 1) { panic!("Group ID too large"); } Self { base: EventBase { severity, group_id, unique_id, phantom: PhantomData, }, } } const_from_fn!(const_from_info, EventU16TypedSev, SeverityInfo); const_from_fn!(const_from_low, EventU16TypedSev, SeverityLow); const_from_fn!(const_from_medium, EventU16TypedSev, SeverityMedium); const_from_fn!(const_from_high, EventU16TypedSev, SeverityHigh); } impl EventU16TypedSev { /// This is similar to [EventU16::new] but the severity is a type generic, which allows to /// have distinct types for events with different severities pub fn new( group_id: ::GroupId, unique_id: ::UniqueId, ) -> Option { let event = EventU16::new(SEVERITY::SEVERITY, group_id, unique_id)?; Some(Self { event, phantom: PhantomData, }) } /// Const version of [Self::new], but panics on invalid group ID input values. pub const fn const_new( group_id: ::GroupId, unique_id: ::UniqueId, ) -> Self { let event = EventU16::const_new(SEVERITY::SEVERITY, group_id, unique_id); Self { event, phantom: PhantomData, } } fn try_from_generic(expected: Severity, raw: u16) -> Result { let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap(); if severity != expected { return Err(severity); } Ok(Self::const_new( ((raw >> 8) & 0x3F) as u8, (raw & 0xFF) as u8, )) } } impl_event_provider!(EventU16, EventU16TypedSev, u16, u8, u8); impl UnsignedEnum for EventU16 { fn size(&self) -> usize { core::mem::size_of::() } fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { self.base.write_to_bytes(self.raw(), buf, self.size()) } fn value(&self) -> u64 { self.raw().into() } } impl EcssEnumeration for EventU16 { #[inline] fn pfc(&self) -> u8 { u16::BITS as u8 } } //noinspection RsTraitImplementation impl UnsignedEnum for EventU16TypedSev { delegate!(to self.event { fn size(&self) -> usize; fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result; fn value(&self) -> u64; }); } //noinspection RsTraitImplementation impl EcssEnumeration for EventU16TypedSev { delegate!(to self.event { fn pfc(&self) -> u8; }); } impl From for EventU16 { fn from(raw: ::Raw) -> Self { let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap(); let group_id = ((raw >> 8) & 0x3F) as u8; let unique_id = (raw & 0xFF) as u8; // Sanitized input, new call should never fail Self::const_new(severity, group_id, unique_id) } } try_from_impls!(SeverityInfo, Severity::INFO, u16, EventU16TypedSev); try_from_impls!(SeverityLow, Severity::LOW, u16, EventU16TypedSev); try_from_impls!(SeverityMedium, Severity::MEDIUM, u16, EventU16TypedSev); try_from_impls!(SeverityHigh, Severity::HIGH, u16, EventU16TypedSev); impl PartialEq for EventU32TypedSev { #[inline] fn eq(&self, other: &EventU32) -> bool { self.raw() == other.raw() } } impl PartialEq> for EventU32 { #[inline] fn eq(&self, other: &EventU32TypedSev) -> bool { self.raw() == other.raw() } } impl PartialEq for EventU16TypedSev { #[inline] fn eq(&self, other: &EventU16) -> bool { self.raw() == other.raw() } } impl PartialEq> for EventU16 { #[inline] fn eq(&self, other: &EventU16TypedSev) -> bool { self.raw() == other.raw() } } #[cfg(test)] mod tests { use super::EventU32TypedSev; use super::*; use spacepackets::ByteConversionError; use std::mem::size_of; fn assert_size(_: T, val: usize) { assert_eq!(size_of::(), val); } const INFO_EVENT: EventU32TypedSev = EventU32TypedSev::const_new(0, 0); const INFO_EVENT_SMALL: EventU16TypedSev = EventU16TypedSev::const_new(0, 0); const HIGH_SEV_EVENT: EventU32TypedSev = EventU32TypedSev::const_new(0x3FFF, 0xFFFF); const HIGH_SEV_EVENT_SMALL: EventU16TypedSev = EventU16TypedSev::const_new(0x3F, 0xff); /// This working is a test in itself. const INFO_REDUCED: EventU32 = EventU32::const_from_info(INFO_EVENT); #[test] fn test_normal_from_raw_conversion() { let conv_from_raw = EventU32TypedSev::::try_from(INFO_EVENT.raw()) .expect("Creating typed EventU32 failed"); assert_eq!(conv_from_raw, INFO_EVENT); } #[test] fn test_small_from_raw_conversion() { let conv_from_raw = EventU16TypedSev::::try_from(INFO_EVENT_SMALL.raw()) .expect("Creating typed EventU16 failed"); assert_eq!(conv_from_raw, INFO_EVENT_SMALL); } #[test] fn verify_normal_size() { assert_size(INFO_EVENT.raw(), 4) } #[test] fn verify_small_size() { assert_size(INFO_EVENT_SMALL.raw(), 2) } #[test] fn test_normal_event_getters() { assert_eq!(INFO_EVENT.severity(), Severity::INFO); assert_eq!(INFO_EVENT.unique_id(), 0); assert_eq!(INFO_EVENT.group_id(), 0); let raw_event = INFO_EVENT.raw(); assert_eq!(raw_event, 0x00000000); } #[test] fn test_small_event_getters() { assert_eq!(INFO_EVENT_SMALL.severity(), Severity::INFO); assert_eq!(INFO_EVENT_SMALL.unique_id(), 0); assert_eq!(INFO_EVENT_SMALL.group_id(), 0); let raw_event = INFO_EVENT_SMALL.raw(); assert_eq!(raw_event, 0x00000000); } #[test] fn all_ones_event_regular() { assert_eq!(HIGH_SEV_EVENT.severity(), Severity::HIGH); assert_eq!(HIGH_SEV_EVENT.group_id(), 0x3FFF); assert_eq!(HIGH_SEV_EVENT.unique_id(), 0xFFFF); let raw_event = HIGH_SEV_EVENT.raw(); assert_eq!(raw_event, 0xFFFFFFFF); } #[test] fn all_ones_event_small() { assert_eq!(HIGH_SEV_EVENT_SMALL.severity(), Severity::HIGH); assert_eq!(HIGH_SEV_EVENT_SMALL.group_id(), 0x3F); assert_eq!(HIGH_SEV_EVENT_SMALL.unique_id(), 0xFF); let raw_event = HIGH_SEV_EVENT_SMALL.raw(); assert_eq!(raw_event, 0xFFFF); } #[test] fn invalid_group_id_normal() { assert!(EventU32TypedSev::::new(2_u16.pow(14), 0).is_none()); } #[test] fn invalid_group_id_small() { assert!(EventU16TypedSev::::new(2_u8.pow(6), 0).is_none()); } #[test] fn regular_new() { assert_eq!( EventU32TypedSev::::new(0, 0).expect("Creating regular event failed"), INFO_EVENT ); } #[test] fn small_new() { assert_eq!( EventU16TypedSev::::new(0, 0).expect("Creating regular event failed"), INFO_EVENT_SMALL ); } #[test] fn as_largest_type() { let event_raw = HIGH_SEV_EVENT.raw_as_largest_type(); assert_size(event_raw, 4); assert_eq!(event_raw, 0xFFFFFFFF); } #[test] fn as_largest_type_for_small_event() { let event_raw = HIGH_SEV_EVENT_SMALL.raw_as_largest_type(); assert_size(event_raw, 4); assert_eq!(event_raw, 0xFFFF); } #[test] fn as_largest_group_id() { let group_id = HIGH_SEV_EVENT.group_id_as_largest_type(); assert_size(group_id, 2); assert_eq!(group_id, 0x3FFF); } #[test] fn as_largest_group_id_small_event() { let group_id = HIGH_SEV_EVENT_SMALL.group_id_as_largest_type(); assert_size(group_id, 2); assert_eq!(group_id, 0x3F); } #[test] fn write_to_buf() { let mut buf: [u8; 4] = [0; 4]; assert!(HIGH_SEV_EVENT.write_to_be_bytes(&mut buf).is_ok()); let val_from_raw = u32::from_be_bytes(buf); assert_eq!(val_from_raw, 0xFFFFFFFF); } #[test] fn write_to_buf_small() { let mut buf: [u8; 2] = [0; 2]; assert!(HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf).is_ok()); let val_from_raw = u16::from_be_bytes(buf); assert_eq!(val_from_raw, 0xFFFF); } #[test] fn write_to_buf_insufficient_buf() { let mut buf: [u8; 3] = [0; 3]; let err = HIGH_SEV_EVENT.write_to_be_bytes(&mut buf); assert!(err.is_err()); let err = err.unwrap_err(); if let ByteConversionError::ToSliceTooSmall { found, expected } = err { assert_eq!(expected, 4); assert_eq!(found, 3); } } #[test] fn write_to_buf_small_insufficient_buf() { let mut buf: [u8; 1] = [0; 1]; let err = HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf); assert!(err.is_err()); let err = err.unwrap_err(); if let ByteConversionError::ToSliceTooSmall { found, expected } = err { assert_eq!(expected, 2); assert_eq!(found, 1); } } #[test] fn severity_from_invalid_raw_val() { let invalid = 0xFF; assert!(Severity::try_from(invalid).is_err()); let invalid = Severity::HIGH as u8 + 1; assert!(Severity::try_from(invalid).is_err()); } #[test] fn reduction() { let event = EventU32TypedSev::::const_new(1, 1); let raw = event.raw(); let reduced: EventU32 = event.into(); assert_eq!(reduced.group_id(), 1); assert_eq!(reduced.unique_id(), 1); assert_eq!(raw, reduced.raw()); } #[test] fn const_reducation() { assert_eq!(INFO_REDUCED.raw(), INFO_EVENT.raw()); } }