diff --git a/satrs-core/src/lib.rs b/satrs-core/src/lib.rs index 940300e..43c8cfb 100644 --- a/satrs-core/src/lib.rs +++ b/satrs-core/src/lib.rs @@ -33,8 +33,6 @@ pub mod hk; pub mod mode; pub mod objects; pub mod params; -#[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] pub mod pool; pub mod power; pub mod pus; diff --git a/satrs-core/src/mode.rs b/satrs-core/src/mode.rs index e29cbf0..7746572 100644 --- a/satrs-core/src/mode.rs +++ b/satrs-core/src/mode.rs @@ -1,5 +1,6 @@ use crate::tmtc::TargetId; use core::mem::size_of; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use spacepackets::{ByteConversionError, SizeMissmatch}; diff --git a/satrs-core/src/pool.rs b/satrs-core/src/pool.rs index d7253d5..62aa4f4 100644 --- a/satrs-core/src/pool.rs +++ b/satrs-core/src/pool.rs @@ -73,63 +73,17 @@ //! assert_eq!(buf_read_back[0], 7); //! } //! ``` -use alloc::format; -use alloc::string::String; -use alloc::vec; -use alloc::vec::Vec; +#[cfg(feature = "alloc")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +pub use alloc_mod::*; use core::fmt::{Display, Formatter}; -use delegate::delegate; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] -use std::boxed::Box; -#[cfg(feature = "std")] use std::error::Error; -#[cfg(feature = "std")] -use std::sync::{Arc, RwLock}; type NumBlocks = u16; -#[cfg(feature = "std")] -pub type ShareablePoolProvider = Box; -#[cfg(feature = "std")] -pub type SharedPool = Arc>; - -/// Configuration structure of the [local pool][LocalPool] -/// -/// # Parameters -/// -/// * `cfg`: Vector of tuples which represent a subpool. The first entry in the tuple specifies the -/// number of memory blocks in the subpool, the second entry the size of the blocks -#[derive(Clone)] -pub struct PoolCfg { - cfg: Vec<(NumBlocks, usize)>, -} - -impl PoolCfg { - pub fn new(cfg: Vec<(NumBlocks, usize)>) -> Self { - PoolCfg { cfg } - } - - pub fn sanitize(&mut self) -> usize { - self.cfg - .retain(|&(bucket_num, size)| bucket_num > 0 && size < LocalPool::MAX_SIZE); - self.cfg - .sort_unstable_by(|(_, sz0), (_, sz1)| sz0.partial_cmp(sz1).unwrap()); - self.cfg.len() - } -} - -type PoolSize = usize; - -/// Pool implementation providing sub-pools with fixed size memory blocks. More details in -/// the [module documentation][super::pool] -pub struct LocalPool { - pool_cfg: PoolCfg, - pool: Vec>, - sizes_lists: Vec>, -} - /// Simple address type used for transactions with the local pool. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -138,6 +92,14 @@ pub struct StoreAddr { pub(crate) packet_idx: NumBlocks, } +impl StoreAddr { + pub const INVALID_ADDR: u32 = 0xFFFFFFFF; + + pub fn raw(&self) -> u32 { + ((self.pool_idx as u32) << 16) | self.packet_idx as u32 + } +} + impl Display for StoreAddr { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!( @@ -148,14 +110,6 @@ impl Display for StoreAddr { } } -impl StoreAddr { - pub const INVALID_ADDR: u32 = 0xFFFFFFFF; - - pub fn raw(&self) -> u32 { - ((self.pool_idx as u32) << 16) | self.packet_idx as u32 - } -} - #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum StoreIdError { @@ -191,7 +145,7 @@ pub enum StoreError { /// Valid subpool and packet index, but no data is stored at the given address DataDoesNotExist(StoreAddr), /// Internal or configuration errors - InternalError(String), + InternalError(u32), } impl Display for StoreError { @@ -226,298 +180,351 @@ impl Error for StoreError { } } -pub trait PoolProvider { - /// Add new data to the pool. The provider should attempt to reserve a memory block with the - /// appropriate size and then copy the given data to the block. Yields a [StoreAddr] which can - /// be used to access the data stored in the pool - fn add(&mut self, data: &[u8]) -> Result; +type PoolSize = usize; +const STORE_FREE: PoolSize = PoolSize::MAX; +const POOL_MAX_SIZE: PoolSize = STORE_FREE - 1; - /// The provider should attempt to reserve a free memory block with the appropriate size and - /// then return a mutable reference to it. Yields a [StoreAddr] which can be used to access - /// the data stored in the pool - fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError>; +pub mod alloc_mod { + use crate::pool::{ + NumBlocks, PoolSize, StoreAddr, StoreError, StoreIdError, POOL_MAX_SIZE, STORE_FREE, + }; + use alloc::boxed::Box; + use alloc::vec; + use alloc::vec::Vec; + use delegate::delegate; + #[cfg(feature = "std")] + use std::sync::{Arc, RwLock}; - /// Modify data added previously using a given [StoreAddr] by yielding a mutable reference - /// to it - fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError>; + #[cfg(feature = "std")] + pub type ShareablePoolProvider = Box; + #[cfg(feature = "std")] + pub type SharedPool = Arc>; - /// This function behaves like [Self::modify], but consumes the provided address and returns a - /// RAII conformant guard object. + /// Configuration structure of the [local pool][LocalPool] /// - /// Unless the guard [PoolRwGuard::release] method is called, the data for the - /// given address will be deleted automatically when the guard is dropped. - /// This can prevent memory leaks. Users can read (and modify) the data and release the guard - /// if the data in the store is valid for further processing. If the data is faulty, no - /// manual deletion is necessary when returning from a processing function prematurely. - fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard; - - /// Read data by yielding a read-only reference given a [StoreAddr] - fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError>; - - /// This function behaves like [Self::read], but consumes the provided address and returns a - /// RAII conformant guard object. + /// # Parameters /// - /// Unless the guard [PoolRwGuard::release] method is called, the data for the - /// given address will be deleted automatically when the guard is dropped. - /// This can prevent memory leaks. Users can read the data and release the guard - /// if the data in the store is valid for further processing. If the data is faulty, no - /// manual deletion is necessary when returning from a processing function prematurely. - fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard; - - /// Delete data inside the pool given a [StoreAddr] - fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError>; - fn has_element_at(&self, addr: &StoreAddr) -> Result; - - /// Retrieve the length of the data at the given store address. - fn len_of_data(&self, addr: &StoreAddr) -> Result { - if !self.has_element_at(addr)? { - return Err(StoreError::DataDoesNotExist(*addr)); - } - Ok(self.read(addr)?.len()) - } -} - -impl LocalPool { - const STORE_FREE: PoolSize = PoolSize::MAX; - const MAX_SIZE: PoolSize = Self::STORE_FREE - 1; - /// Create a new local pool from the [given configuration][PoolCfg]. This function will sanitize - /// the given configuration as well. - pub fn new(mut cfg: PoolCfg) -> LocalPool { - let subpools_num = cfg.sanitize(); - let mut local_pool = LocalPool { - pool_cfg: cfg, - pool: Vec::with_capacity(subpools_num), - sizes_lists: Vec::with_capacity(subpools_num), - }; - for &(num_elems, elem_size) in local_pool.pool_cfg.cfg.iter() { - let next_pool_len = elem_size * num_elems as usize; - local_pool.pool.push(vec![0; next_pool_len]); - let next_sizes_list_len = num_elems as usize; - local_pool - .sizes_lists - .push(vec![Self::STORE_FREE; next_sizes_list_len]); - } - local_pool + /// * `cfg`: Vector of tuples which represent a subpool. The first entry in the tuple specifies the + /// number of memory blocks in the subpool, the second entry the size of the blocks + #[derive(Clone)] + pub struct PoolCfg { + cfg: Vec<(NumBlocks, usize)>, } - fn addr_check(&self, addr: &StoreAddr) -> Result { - self.validate_addr(addr)?; - let pool_idx = addr.pool_idx as usize; - let size_list = self.sizes_lists.get(pool_idx).unwrap(); - let curr_size = size_list[addr.packet_idx as usize]; - if curr_size == Self::STORE_FREE { - return Err(StoreError::DataDoesNotExist(*addr)); + impl PoolCfg { + pub fn new(cfg: Vec<(NumBlocks, usize)>) -> Self { + PoolCfg { cfg } + } + + pub fn cfg(&self) -> &Vec<(NumBlocks, usize)> { + &self.cfg + } + + pub fn sanitize(&mut self) -> usize { + self.cfg + .retain(|&(bucket_num, size)| bucket_num > 0 && size < POOL_MAX_SIZE); + self.cfg + .sort_unstable_by(|(_, sz0), (_, sz1)| sz0.partial_cmp(sz1).unwrap()); + self.cfg.len() } - Ok(curr_size) } - fn validate_addr(&self, addr: &StoreAddr) -> Result<(), StoreError> { - let pool_idx = addr.pool_idx as usize; - if pool_idx >= self.pool_cfg.cfg.len() { - return Err(StoreError::InvalidStoreId( - StoreIdError::InvalidSubpool(addr.pool_idx), - Some(*addr), - )); - } - if addr.packet_idx >= self.pool_cfg.cfg[addr.pool_idx as usize].0 { - return Err(StoreError::InvalidStoreId( - StoreIdError::InvalidPacketIdx(addr.packet_idx), - Some(*addr), - )); - } - Ok(()) + pub struct PoolGuard<'a> { + pool: &'a mut LocalPool, + pub addr: StoreAddr, + no_deletion: bool, + deletion_failed_error: Option, } - fn reserve(&mut self, data_len: usize) -> Result { - let subpool_idx = self.find_subpool(data_len, 0)?; - let (slot, size_slot_ref) = self.find_empty(subpool_idx)?; - *size_slot_ref = data_len; - Ok(StoreAddr { - pool_idx: subpool_idx, - packet_idx: slot, - }) - } - - fn find_subpool(&self, req_size: usize, start_at_subpool: u16) -> Result { - for (i, &(_, elem_size)) in self.pool_cfg.cfg.iter().enumerate() { - if i < start_at_subpool as usize { - continue; - } - if elem_size >= req_size { - return Ok(i as u16); + /// This helper object + impl<'a> PoolGuard<'a> { + pub fn new(pool: &'a mut LocalPool, addr: StoreAddr) -> Self { + Self { + pool, + addr, + no_deletion: false, + deletion_failed_error: None, } } - Err(StoreError::DataTooLarge(req_size)) + + pub fn read(&self) -> Result<&[u8], StoreError> { + self.pool.read(&self.addr) + } + + /// Releasing the pool guard will disable the automatic deletion of the data when the guard + /// is dropped. + pub fn release(&mut self) { + self.no_deletion = true; + } } - fn write(&mut self, addr: &StoreAddr, data: &[u8]) -> Result<(), StoreError> { - let packet_pos = self.raw_pos(addr).ok_or_else(|| { - StoreError::InternalError(format!( - "write: Error in raw_pos func with address {addr:?}" - )) - })?; - let subpool = self.pool.get_mut(addr.pool_idx as usize).ok_or_else(|| { - StoreError::InternalError(format!( - "write: Error retrieving pool slice with address {addr:?}" - )) - })?; - let pool_slice = &mut subpool[packet_pos..packet_pos + data.len()]; - pool_slice.copy_from_slice(data); - Ok(()) - } - - fn find_empty(&mut self, subpool: u16) -> Result<(u16, &mut usize), StoreError> { - if let Some(size_list) = self.sizes_lists.get_mut(subpool as usize) { - for (i, elem_size) in size_list.iter_mut().enumerate() { - if *elem_size == Self::STORE_FREE { - return Ok((i as u16, elem_size)); + impl Drop for PoolGuard<'_> { + fn drop(&mut self) { + if !self.no_deletion { + if let Err(e) = self.pool.delete(self.addr) { + self.deletion_failed_error = Some(e); } } - } else { - return Err(StoreError::InvalidStoreId( - StoreIdError::InvalidSubpool(subpool), - None, - )); - } - Err(StoreError::StoreFull(subpool)) - } - - fn raw_pos(&self, addr: &StoreAddr) -> Option { - let (_, size) = self.pool_cfg.cfg.get(addr.pool_idx as usize)?; - Some(addr.packet_idx as usize * size) - } -} - -impl PoolProvider for LocalPool { - fn add(&mut self, data: &[u8]) -> Result { - let data_len = data.len(); - if data_len > Self::MAX_SIZE { - return Err(StoreError::DataTooLarge(data_len)); - } - let addr = self.reserve(data_len)?; - self.write(&addr, data)?; - Ok(addr) - } - - fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError> { - if len > Self::MAX_SIZE { - return Err(StoreError::DataTooLarge(len)); - } - let addr = self.reserve(len)?; - let raw_pos = self.raw_pos(&addr).unwrap(); - let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + len]; - Ok((addr, block)) - } - - fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError> { - let curr_size = self.addr_check(addr)?; - let raw_pos = self.raw_pos(addr).unwrap(); - let block = - &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + curr_size]; - Ok(block) - } - - fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard { - PoolRwGuard::new(self, addr) - } - - fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError> { - let curr_size = self.addr_check(addr)?; - let raw_pos = self.raw_pos(addr).unwrap(); - let block = &self.pool.get(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + curr_size]; - Ok(block) - } - - fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard { - PoolGuard::new(self, addr) - } - - fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError> { - self.addr_check(&addr)?; - let block_size = self.pool_cfg.cfg.get(addr.pool_idx as usize).unwrap().1; - let raw_pos = self.raw_pos(&addr).unwrap(); - let block = - &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + block_size]; - let size_list = self.sizes_lists.get_mut(addr.pool_idx as usize).unwrap(); - size_list[addr.packet_idx as usize] = Self::STORE_FREE; - block.fill(0); - Ok(()) - } - - fn has_element_at(&self, addr: &StoreAddr) -> Result { - self.validate_addr(addr)?; - let pool_idx = addr.pool_idx as usize; - let size_list = self.sizes_lists.get(pool_idx).unwrap(); - let curr_size = size_list[addr.packet_idx as usize]; - if curr_size == Self::STORE_FREE { - return Ok(false); - } - Ok(true) - } -} - -pub struct PoolGuard<'a> { - pool: &'a mut LocalPool, - pub addr: StoreAddr, - no_deletion: bool, - deletion_failed_error: Option, -} - -/// This helper object -impl<'a> PoolGuard<'a> { - pub fn new(pool: &'a mut LocalPool, addr: StoreAddr) -> Self { - Self { - pool, - addr, - no_deletion: false, - deletion_failed_error: None, } } - pub fn read(&self) -> Result<&[u8], StoreError> { - self.pool.read(&self.addr) + pub struct PoolRwGuard<'a> { + guard: PoolGuard<'a>, } - /// Releasing the pool guard will disable the automatic deletion of the data when the guard - /// is dropped. - pub fn release(&mut self) { - self.no_deletion = true; - } -} - -impl Drop for PoolGuard<'_> { - fn drop(&mut self) { - if !self.no_deletion { - if let Err(e) = self.pool.delete(self.addr) { - self.deletion_failed_error = Some(e); + impl<'a> PoolRwGuard<'a> { + pub fn new(pool: &'a mut LocalPool, addr: StoreAddr) -> Self { + Self { + guard: PoolGuard::new(pool, addr), } } + + pub fn modify(&mut self) -> Result<&mut [u8], StoreError> { + self.guard.pool.modify(&self.guard.addr) + } + + delegate!( + to self.guard { + pub fn read(&self) -> Result<&[u8], StoreError>; + /// Releasing the pool guard will disable the automatic deletion of the data when the guard + /// is dropped. + pub fn release(&mut self); + } + ); } -} -pub struct PoolRwGuard<'a> { - guard: PoolGuard<'a>, -} + pub trait PoolProvider { + /// Add new data to the pool. The provider should attempt to reserve a memory block with the + /// appropriate size and then copy the given data to the block. Yields a [StoreAddr] which can + /// be used to access the data stored in the pool + fn add(&mut self, data: &[u8]) -> Result; -impl<'a> PoolRwGuard<'a> { - pub fn new(pool: &'a mut LocalPool, addr: StoreAddr) -> Self { - Self { - guard: PoolGuard::new(pool, addr), + /// The provider should attempt to reserve a free memory block with the appropriate size and + /// then return a mutable reference to it. Yields a [StoreAddr] which can be used to access + /// the data stored in the pool + fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError>; + + /// Modify data added previously using a given [StoreAddr] by yielding a mutable reference + /// to it + fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError>; + + /// This function behaves like [Self::modify], but consumes the provided address and returns a + /// RAII conformant guard object. + /// + /// Unless the guard [PoolRwGuard::release] method is called, the data for the + /// given address will be deleted automatically when the guard is dropped. + /// This can prevent memory leaks. Users can read (and modify) the data and release the guard + /// if the data in the store is valid for further processing. If the data is faulty, no + /// manual deletion is necessary when returning from a processing function prematurely. + fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard; + + /// Read data by yielding a read-only reference given a [StoreAddr] + fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError>; + + /// This function behaves like [Self::read], but consumes the provided address and returns a + /// RAII conformant guard object. + /// + /// Unless the guard [PoolRwGuard::release] method is called, the data for the + /// given address will be deleted automatically when the guard is dropped. + /// This can prevent memory leaks. Users can read the data and release the guard + /// if the data in the store is valid for further processing. If the data is faulty, no + /// manual deletion is necessary when returning from a processing function prematurely. + fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard; + + /// Delete data inside the pool given a [StoreAddr] + fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError>; + fn has_element_at(&self, addr: &StoreAddr) -> Result; + + /// Retrieve the length of the data at the given store address. + fn len_of_data(&self, addr: &StoreAddr) -> Result { + if !self.has_element_at(addr)? { + return Err(StoreError::DataDoesNotExist(*addr)); + } + Ok(self.read(addr)?.len()) } } - pub fn modify(&mut self) -> Result<&mut [u8], StoreError> { - self.guard.pool.modify(&self.guard.addr) + /// Pool implementation providing sub-pools with fixed size memory blocks. More details in + /// the [module documentation][super::pool] + pub struct LocalPool { + pool_cfg: PoolCfg, + pool: Vec>, + sizes_lists: Vec>, } - delegate!( - to self.guard { - pub fn read(&self) -> Result<&[u8], StoreError>; - /// Releasing the pool guard will disable the automatic deletion of the data when the guard - /// is dropped. - pub fn release(&mut self); + impl LocalPool { + /// Create a new local pool from the [given configuration][PoolCfg]. This function will sanitize + /// the given configuration as well. + pub fn new(mut cfg: PoolCfg) -> LocalPool { + let subpools_num = cfg.sanitize(); + let mut local_pool = LocalPool { + pool_cfg: cfg, + pool: Vec::with_capacity(subpools_num), + sizes_lists: Vec::with_capacity(subpools_num), + }; + for &(num_elems, elem_size) in local_pool.pool_cfg.cfg.iter() { + let next_pool_len = elem_size * num_elems as usize; + local_pool.pool.push(vec![0; next_pool_len]); + let next_sizes_list_len = num_elems as usize; + local_pool + .sizes_lists + .push(vec![STORE_FREE; next_sizes_list_len]); + } + local_pool } - ); + + fn addr_check(&self, addr: &StoreAddr) -> Result { + self.validate_addr(addr)?; + let pool_idx = addr.pool_idx as usize; + let size_list = self.sizes_lists.get(pool_idx).unwrap(); + let curr_size = size_list[addr.packet_idx as usize]; + if curr_size == STORE_FREE { + return Err(StoreError::DataDoesNotExist(*addr)); + } + Ok(curr_size) + } + + fn validate_addr(&self, addr: &StoreAddr) -> Result<(), StoreError> { + let pool_idx = addr.pool_idx as usize; + if pool_idx >= self.pool_cfg.cfg.len() { + return Err(StoreError::InvalidStoreId( + StoreIdError::InvalidSubpool(addr.pool_idx), + Some(*addr), + )); + } + if addr.packet_idx >= self.pool_cfg.cfg[addr.pool_idx as usize].0 { + return Err(StoreError::InvalidStoreId( + StoreIdError::InvalidPacketIdx(addr.packet_idx), + Some(*addr), + )); + } + Ok(()) + } + + fn reserve(&mut self, data_len: usize) -> Result { + let subpool_idx = self.find_subpool(data_len, 0)?; + let (slot, size_slot_ref) = self.find_empty(subpool_idx)?; + *size_slot_ref = data_len; + Ok(StoreAddr { + pool_idx: subpool_idx, + packet_idx: slot, + }) + } + + fn find_subpool(&self, req_size: usize, start_at_subpool: u16) -> Result { + for (i, &(_, elem_size)) in self.pool_cfg.cfg.iter().enumerate() { + if i < start_at_subpool as usize { + continue; + } + if elem_size >= req_size { + return Ok(i as u16); + } + } + Err(StoreError::DataTooLarge(req_size)) + } + + fn write(&mut self, addr: &StoreAddr, data: &[u8]) -> Result<(), StoreError> { + let packet_pos = self.raw_pos(addr).ok_or(StoreError::InternalError(0))?; + let subpool = self + .pool + .get_mut(addr.pool_idx as usize) + .ok_or(StoreError::InternalError(1))?; + let pool_slice = &mut subpool[packet_pos..packet_pos + data.len()]; + pool_slice.copy_from_slice(data); + Ok(()) + } + + fn find_empty(&mut self, subpool: u16) -> Result<(u16, &mut usize), StoreError> { + if let Some(size_list) = self.sizes_lists.get_mut(subpool as usize) { + for (i, elem_size) in size_list.iter_mut().enumerate() { + if *elem_size == STORE_FREE { + return Ok((i as u16, elem_size)); + } + } + } else { + return Err(StoreError::InvalidStoreId( + StoreIdError::InvalidSubpool(subpool), + None, + )); + } + Err(StoreError::StoreFull(subpool)) + } + + fn raw_pos(&self, addr: &StoreAddr) -> Option { + let (_, size) = self.pool_cfg.cfg.get(addr.pool_idx as usize)?; + Some(addr.packet_idx as usize * size) + } + } + + impl PoolProvider for LocalPool { + fn add(&mut self, data: &[u8]) -> Result { + let data_len = data.len(); + if data_len > POOL_MAX_SIZE { + return Err(StoreError::DataTooLarge(data_len)); + } + let addr = self.reserve(data_len)?; + self.write(&addr, data)?; + Ok(addr) + } + + fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError> { + if len > POOL_MAX_SIZE { + return Err(StoreError::DataTooLarge(len)); + } + let addr = self.reserve(len)?; + let raw_pos = self.raw_pos(&addr).unwrap(); + let block = + &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + len]; + Ok((addr, block)) + } + + fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError> { + let curr_size = self.addr_check(addr)?; + let raw_pos = self.raw_pos(addr).unwrap(); + let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap() + [raw_pos..raw_pos + curr_size]; + Ok(block) + } + + fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard { + PoolRwGuard::new(self, addr) + } + + fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError> { + let curr_size = self.addr_check(addr)?; + let raw_pos = self.raw_pos(addr).unwrap(); + let block = + &self.pool.get(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + curr_size]; + Ok(block) + } + + fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard { + PoolGuard::new(self, addr) + } + + fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError> { + self.addr_check(&addr)?; + let block_size = self.pool_cfg.cfg.get(addr.pool_idx as usize).unwrap().1; + let raw_pos = self.raw_pos(&addr).unwrap(); + let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap() + [raw_pos..raw_pos + block_size]; + let size_list = self.sizes_lists.get_mut(addr.pool_idx as usize).unwrap(); + size_list[addr.packet_idx as usize] = STORE_FREE; + block.fill(0); + Ok(()) + } + + fn has_element_at(&self, addr: &StoreAddr) -> Result { + self.validate_addr(addr)?; + let pool_idx = addr.pool_idx as usize; + let size_list = self.sizes_lists.get(pool_idx).unwrap(); + let curr_size = size_list[addr.packet_idx as usize]; + if curr_size == STORE_FREE { + return Ok(false); + } + Ok(true) + } + } } #[cfg(test)] @@ -539,17 +546,17 @@ mod tests { // Values where number of buckets is 0 or size is too large should be removed let mut pool_cfg = PoolCfg::new(vec![(0, 0), (1, 0), (2, LocalPool::MAX_SIZE)]); pool_cfg.sanitize(); - assert_eq!(pool_cfg.cfg, vec![(1, 0)]); + assert_eq!(pool_cfg.cfg(), vec![(1, 0)]); // Entries should be ordered according to bucket size pool_cfg = PoolCfg::new(vec![(16, 6), (32, 3), (8, 12)]); pool_cfg.sanitize(); - assert_eq!(pool_cfg.cfg, vec![(32, 3), (16, 6), (8, 12)]); + assert_eq!(pool_cfg.cfg(), vec![(32, 3), (16, 6), (8, 12)]); // Unstable sort is used, so order of entries with same block length should not matter pool_cfg = PoolCfg::new(vec![(12, 12), (14, 16), (10, 12)]); pool_cfg.sanitize(); assert!( - pool_cfg.cfg == vec![(12, 12), (10, 12), (14, 16)] - || pool_cfg.cfg == vec![(10, 12), (12, 12), (14, 16)] + pool_cfg.cfg() == vec![(12, 12), (10, 12), (14, 16)] + || pool_cfg.cfg() == vec![(10, 12), (12, 12), (14, 16)] ); } diff --git a/satrs-core/src/pus/event_srv.rs b/satrs-core/src/pus/event_srv.rs index 8f34a0f..5f423f0 100644 --- a/satrs-core/src/pus/event_srv.rs +++ b/satrs-core/src/pus/event_srv.rs @@ -8,10 +8,10 @@ use crate::pus::{ EcssTcReceiver, EcssTmSender, PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHandler, }; +use alloc::boxed::Box; use spacepackets::ecss::event::Subservice; use spacepackets::ecss::tc::PusTcReader; use spacepackets::ecss::PusPacket; -use std::boxed::Box; use std::sync::mpsc::Sender; pub struct PusService5EventHandler { diff --git a/satrs-core/src/pus/mod.rs b/satrs-core/src/pus/mod.rs index f03ab0f..9f9107a 100644 --- a/satrs-core/src/pus/mod.rs +++ b/satrs-core/src/pus/mod.rs @@ -8,18 +8,22 @@ use core::fmt::{Display, Formatter}; use downcast_rs::{impl_downcast, Downcast}; #[cfg(feature = "alloc")] use dyn_clone::DynClone; +#[cfg(feature = "std")] +use std::error::Error; + use spacepackets::ecss::tc::{PusTcCreator, PusTcReader}; use spacepackets::ecss::tm::PusTmCreator; use spacepackets::ecss::PusError; use spacepackets::{ByteConversionError, SizeMissmatch, SpHeader}; -use std::error::Error; pub mod event; pub mod event_man; +#[cfg(feature = "std")] pub mod event_srv; pub mod hk; pub mod mode; pub mod scheduler; +#[cfg(feature = "std")] pub mod scheduler_srv; #[cfg(feature = "std")] pub mod test; diff --git a/satrs-core/src/pus/scheduler.rs b/satrs-core/src/pus/scheduler.rs index 2a91bf1..7b2ad43 100644 --- a/satrs-core/src/pus/scheduler.rs +++ b/satrs-core/src/pus/scheduler.rs @@ -2,22 +2,18 @@ //! //! The core data structure of this module is the [PusScheduler]. This structure can be used //! to perform the scheduling of telecommands like specified in the ECSS standard. -use crate::pool::{StoreAddr, StoreError}; use core::fmt::{Debug, Display, Formatter}; -use core::time::Duration; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use spacepackets::ecss::scheduling::TimeWindowType; use spacepackets::ecss::tc::{GenericPusTcSecondaryHeader, IsPusTelecommand}; use spacepackets::ecss::PusError; -use spacepackets::time::{CcsdsTimeProvider, TimestampError, UnixTimestamp}; +use spacepackets::time::{CcsdsTimeProvider, TimestampError}; use spacepackets::CcsdsPacket; #[cfg(feature = "std")] use std::error::Error; -//#[cfg(feature = "std")] -//pub use std_mod::*; - +use crate::pool::StoreAddr; #[cfg(feature = "alloc")] pub use alloc_mod::*; @@ -61,78 +57,6 @@ impl RequestId { } } -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum ScheduleError { - PusError(PusError), - /// The release time is within the time-margin added on top of the current time. - /// The first parameter is the current time, the second one the time margin, and the third one - /// the release time. - ReleaseTimeInTimeMargin(UnixTimestamp, Duration, UnixTimestamp), - /// Nested time-tagged commands are not allowed. - NestedScheduledTc, - StoreError(StoreError), - TcDataEmpty, - TimestampError(TimestampError), - WrongSubservice, - WrongService, -} - -impl Display for ScheduleError { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - ScheduleError::PusError(e) => { - write!(f, "Pus Error: {e}") - } - ScheduleError::ReleaseTimeInTimeMargin(current_time, margin, timestamp) => { - write!( - f, - "Error: time margin too short, current time: {current_time:?}, time margin: {margin:?}, release time: {timestamp:?}" - ) - } - ScheduleError::NestedScheduledTc => { - write!(f, "Error: nested scheduling is not allowed") - } - ScheduleError::StoreError(e) => { - write!(f, "Store Error: {e}") - } - ScheduleError::TcDataEmpty => { - write!(f, "Error: empty Tc Data field") - } - ScheduleError::TimestampError(e) => { - write!(f, "Timestamp Error: {e}") - } - ScheduleError::WrongService => { - write!(f, "Error: Service not 11.") - } - ScheduleError::WrongSubservice => { - write!(f, "Error: Subservice not 4.") - } - } - } -} - -impl From for ScheduleError { - fn from(e: PusError) -> Self { - ScheduleError::PusError(e) - } -} - -impl From for ScheduleError { - fn from(e: StoreError) -> Self { - ScheduleError::StoreError(e) - } -} - -impl From for ScheduleError { - fn from(e: TimestampError) -> Self { - ScheduleError::TimestampError(e) - } -} - -#[cfg(feature = "std")] -impl Error for ScheduleError {} - /// This is the format stored internally by the TC scheduler for each scheduled telecommand. /// It consists of the address of that telecommand in the TC pool and a request ID. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -156,11 +80,6 @@ impl TcInfo { } } -enum DeletionResult { - WithoutStoreDeletion(Option), - WithStoreDeletion(Result), -} - pub struct TimeWindow { time_window_type: TimeWindowType, start_time: Option, @@ -217,8 +136,8 @@ impl TimeWindow { #[cfg(feature = "alloc")] pub mod alloc_mod { + use super::*; use crate::pool::{PoolProvider, StoreAddr, StoreError}; - use crate::pus::scheduler::{DeletionResult, RequestId, ScheduleError, TcInfo, TimeWindow}; use alloc::collections::btree_map::{Entry, Range}; use alloc::collections::BTreeMap; use alloc::vec; @@ -235,6 +154,83 @@ pub mod alloc_mod { #[cfg(feature = "std")] use std::time::SystemTimeError; + enum DeletionResult { + WithoutStoreDeletion(Option), + WithStoreDeletion(Result), + } + + #[derive(Debug, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub enum ScheduleError { + PusError(PusError), + /// The release time is within the time-margin added on top of the current time. + /// The first parameter is the current time, the second one the time margin, and the third one + /// the release time. + ReleaseTimeInTimeMargin(UnixTimestamp, Duration, UnixTimestamp), + /// Nested time-tagged commands are not allowed. + NestedScheduledTc, + StoreError(StoreError), + TcDataEmpty, + TimestampError(TimestampError), + WrongSubservice, + WrongService, + } + + impl Display for ScheduleError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + ScheduleError::PusError(e) => { + write!(f, "Pus Error: {e}") + } + ScheduleError::ReleaseTimeInTimeMargin(current_time, margin, timestamp) => { + write!( + f, + "Error: time margin too short, current time: {current_time:?}, time margin: {margin:?}, release time: {timestamp:?}" + ) + } + ScheduleError::NestedScheduledTc => { + write!(f, "Error: nested scheduling is not allowed") + } + ScheduleError::StoreError(e) => { + write!(f, "Store Error: {e}") + } + ScheduleError::TcDataEmpty => { + write!(f, "Error: empty Tc Data field") + } + ScheduleError::TimestampError(e) => { + write!(f, "Timestamp Error: {e}") + } + ScheduleError::WrongService => { + write!(f, "Error: Service not 11.") + } + ScheduleError::WrongSubservice => { + write!(f, "Error: Subservice not 4.") + } + } + } + } + + impl From for ScheduleError { + fn from(e: PusError) -> Self { + ScheduleError::PusError(e) + } + } + + impl From for ScheduleError { + fn from(e: StoreError) -> Self { + ScheduleError::StoreError(e) + } + } + + impl From for ScheduleError { + fn from(e: TimestampError) -> Self { + ScheduleError::TimestampError(e) + } + } + + #[cfg(feature = "std")] + impl Error for ScheduleError {} + /// This is the core data structure for scheduling PUS telecommands with [alloc] support. /// /// It is assumed that the actual telecommand data is stored in a separate TC pool offering