From 175b61decaa1786d2929ed5602a3721f3eb476c9 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 20 Aug 2024 10:57:53 +0200 Subject: [PATCH] Added generic sequence counter module --- CHANGELOG.md | 1 + Cargo.toml | 1 + src/lib.rs | 1 + src/seq_count.rs | 250 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 253 insertions(+) create mode 100644 src/seq_count.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d988ee..7813628 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). file data field as a mutable slice. This allows to read data from the virtual file system API to the file data buffer without an intermediate buffer. - Generic `EofPdu::new` constructor. +- Added generic sequence counter module. ## Added and Changed diff --git a/Cargo.toml b/Cargo.toml index cc11ca2..fe85ee1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-sup [dependencies] crc = "3" delegate = ">=0.8, <0.13" +paste = "1" [dependencies.zerocopy] version = "0.7" diff --git a/src/lib.rs b/src/lib.rs index 2b17e9b..0271b69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,7 @@ use serde::{Deserialize, Serialize}; pub mod cfdp; pub mod ecss; +pub mod seq_count; pub mod time; pub mod util; diff --git a/src/seq_count.rs b/src/seq_count.rs new file mode 100644 index 0000000..7194c0d --- /dev/null +++ b/src/seq_count.rs @@ -0,0 +1,250 @@ +use crate::MAX_SEQ_COUNT; +use core::cell::Cell; +use paste::paste; +#[cfg(feature = "std")] +pub use stdmod::*; + +/// Core trait for objects which can provide a sequence count. +/// +/// The core functions are not mutable on purpose to allow easier usage with +/// static structs when using the interior mutability pattern. This can be achieved by using +/// [Cell], [core::cell::RefCell] or atomic types. +pub trait SequenceCountProvider { + type Raw: Into; + const MAX_BIT_WIDTH: usize; + + fn get(&self) -> Self::Raw; + + fn increment(&self); + + fn get_and_increment(&self) -> Self::Raw { + let val = self.get(); + self.increment(); + val + } +} + +#[derive(Clone)] +pub struct SeqCountProviderSimple { + seq_count: Cell, + max_val: T, +} + +macro_rules! impl_for_primitives { + ($($ty: ident,)+) => { + $( + paste! { + impl SeqCountProviderSimple<$ty> { + pub fn [](max_val: $ty) -> Self { + Self { + seq_count: Cell::new(0), + max_val, + } + } + pub fn []() -> Self { + Self { + seq_count: Cell::new(0), + max_val: $ty::MAX + } + } + } + + impl Default for SeqCountProviderSimple<$ty> { + fn default() -> Self { + Self::[]() + } + } + + impl SequenceCountProvider for SeqCountProviderSimple<$ty> { + type Raw = $ty; + const MAX_BIT_WIDTH: usize = core::mem::size_of::() * 8; + + fn get(&self) -> Self::Raw { + self.seq_count.get() + } + + fn increment(&self) { + self.get_and_increment(); + } + + fn get_and_increment(&self) -> Self::Raw { + let curr_count = self.seq_count.get(); + + if curr_count == self.max_val { + self.seq_count.set(0); + } else { + self.seq_count.set(curr_count + 1); + } + curr_count + } + } + } + )+ + } +} + +impl_for_primitives!(u8, u16, u32, u64,); + +/// This is a sequence count provider which wraps around at [MAX_SEQ_COUNT]. +#[derive(Clone)] +pub struct CcsdsSimpleSeqCountProvider { + provider: SeqCountProviderSimple, +} + +impl Default for CcsdsSimpleSeqCountProvider { + fn default() -> Self { + Self { + provider: SeqCountProviderSimple::new_custom_max_val_u16(MAX_SEQ_COUNT), + } + } +} + +impl SequenceCountProvider for CcsdsSimpleSeqCountProvider { + type Raw = u16; + const MAX_BIT_WIDTH: usize = core::mem::size_of::() * 8; + delegate::delegate! { + to self.provider { + fn get(&self) -> u16; + fn increment(&self); + fn get_and_increment(&self) -> u16; + } + } +} + +#[cfg(feature = "std")] +pub mod stdmod { + use super::*; + use std::sync::{Arc, Mutex}; + + macro_rules! sync_clonable_seq_counter_impl { + ($($ty: ident,)+) => { + $(paste! { + /// These sequence counters can be shared between threads and can also be + /// configured to wrap around at specified maximum values. Please note that + /// that the API provided by this class will not panic und [Mutex] lock errors, + /// but it will yield 0 for the getter functions. + #[derive(Clone, Default)] + pub struct [] { + seq_count: Arc>, + max_val: $ty + } + + impl [] { + pub fn new() -> Self { + Self::new_with_max_val($ty::MAX) + } + + pub fn new_with_max_val(max_val: $ty) -> Self { + Self { + seq_count: Arc::default(), + max_val + } + } + } + impl SequenceCountProvider for [] { + type Raw = $ty; + const MAX_BIT_WIDTH: usize = core::mem::size_of::() * 8; + + fn get(&self) -> $ty { + match self.seq_count.lock() { + Ok(counter) => *counter, + Err(_) => 0 + } + } + + fn increment(&self) { + self.get_and_increment(); + } + + fn get_and_increment(&self) -> $ty { + match self.seq_count.lock() { + Ok(mut counter) => { + let val = *counter; + if val == self.max_val { + *counter = 0; + } else { + *counter += 1; + } + val + } + Err(_) => 0, + } + } + } + })+ + } + } + sync_clonable_seq_counter_impl!(u8, u16, u32, u64,); +} + +#[cfg(test)] +mod tests { + use crate::seq_count::{ + CcsdsSimpleSeqCountProvider, SeqCountProviderSimple, SeqCountProviderSyncU8, + SequenceCountProvider, + }; + use crate::MAX_SEQ_COUNT; + + #[test] + fn test_u8_counter() { + let u8_counter = SeqCountProviderSimple::::default(); + assert_eq!(u8_counter.get(), 0); + assert_eq!(u8_counter.get_and_increment(), 0); + assert_eq!(u8_counter.get_and_increment(), 1); + assert_eq!(u8_counter.get(), 2); + } + + #[test] + fn test_u8_counter_overflow() { + let u8_counter = SeqCountProviderSimple::new_u8(); + for _ in 0..256 { + u8_counter.increment(); + } + assert_eq!(u8_counter.get(), 0); + } + + #[test] + fn test_ccsds_counter() { + let ccsds_counter = CcsdsSimpleSeqCountProvider::default(); + assert_eq!(ccsds_counter.get(), 0); + assert_eq!(ccsds_counter.get_and_increment(), 0); + assert_eq!(ccsds_counter.get_and_increment(), 1); + assert_eq!(ccsds_counter.get(), 2); + } + + #[test] + fn test_ccsds_counter_overflow() { + let ccsds_counter = CcsdsSimpleSeqCountProvider::default(); + for _ in 0..MAX_SEQ_COUNT + 1 { + ccsds_counter.increment(); + } + assert_eq!(ccsds_counter.get(), 0); + } + + #[test] + fn test_atomic_ref_counters() { + let sync_u8_counter = SeqCountProviderSyncU8::new(); + assert_eq!(sync_u8_counter.get(), 0); + assert_eq!(sync_u8_counter.get_and_increment(), 0); + assert_eq!(sync_u8_counter.get_and_increment(), 1); + assert_eq!(sync_u8_counter.get(), 2); + } + + #[test] + fn test_atomic_ref_counters_overflow() { + let sync_u8_counter = SeqCountProviderSyncU8::new(); + for _ in 0..u8::MAX as u16 + 1 { + sync_u8_counter.increment(); + } + assert_eq!(sync_u8_counter.get(), 0); + } + + #[test] + fn test_atomic_ref_counters_overflow_custom_max_val() { + let sync_u8_counter = SeqCountProviderSyncU8::new_with_max_val(128); + for _ in 0..129 { + sync_u8_counter.increment(); + } + assert_eq!(sync_u8_counter.get(), 0); + } +}