Finish PUS service optimizations
Some checks failed
Rust/sat-rs/pipeline/pr-main There was a failure building this commit
Some checks failed
Rust/sat-rs/pipeline/pr-main There was a failure building this commit
- Better naming for pool abstractions - Added last unittests for PUS helper services - Introduce new abstraction for PUS schedulers
This commit is contained in:
parent
6152c834d4
commit
ae56e206ba
@ -73,7 +73,7 @@ features = ["all"]
|
||||
optional = true
|
||||
|
||||
[dependencies.spacepackets]
|
||||
version = "0.7.0-beta.4"
|
||||
version = "0.7.0"
|
||||
default-features = false
|
||||
# git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git"
|
||||
# rev = "297cfad22637d3b07a1b27abe56d9a607b5b82a7"
|
||||
@ -123,4 +123,4 @@ doc-images = []
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "doc_cfg"]
|
||||
rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"]
|
||||
|
@ -1,49 +0,0 @@
|
||||
pub enum FsrcGroupIds {
|
||||
Tmtc = 0,
|
||||
}
|
||||
|
||||
pub struct FsrcErrorRaw {
|
||||
pub group_id: u8,
|
||||
pub unique_id: u8,
|
||||
pub group_name: &'static str,
|
||||
pub info: &'static str,
|
||||
}
|
||||
|
||||
pub trait FsrcErrorHandler {
|
||||
fn error(&mut self, e: FsrcErrorRaw);
|
||||
fn error_with_one_param(&mut self, e: FsrcErrorRaw, _p1: u32) {
|
||||
self.error(e);
|
||||
}
|
||||
fn error_with_two_params(&mut self, e: FsrcErrorRaw, _p1: u32, _p2: u32) {
|
||||
self.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
impl FsrcErrorRaw {
|
||||
pub const fn new(
|
||||
group_id: u8,
|
||||
unique_id: u8,
|
||||
group_name: &'static str,
|
||||
info: &'static str,
|
||||
) -> Self {
|
||||
FsrcErrorRaw {
|
||||
group_id,
|
||||
unique_id,
|
||||
group_name,
|
||||
info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct SimpleStdErrorHandler {}
|
||||
|
||||
#[cfg(feature = "use_std")]
|
||||
impl FsrcErrorHandler for SimpleStdErrorHandler {
|
||||
fn error(&mut self, e: FsrcErrorRaw) {
|
||||
println!(
|
||||
"Received error from group {} with ID ({},{}): {}",
|
||||
e.group_name, e.group_id, e.unique_id, e.info
|
||||
);
|
||||
}
|
||||
}
|
@ -24,7 +24,6 @@ extern crate std;
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
||||
pub mod cfdp;
|
||||
pub mod encoding;
|
||||
pub mod error;
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
||||
pub mod event_man;
|
||||
|
@ -1,23 +1,13 @@
|
||||
//! # Pool implementation providing pre-allocated sub-pools with fixed size memory blocks
|
||||
//! # Pool implementation providing memory pools for packet storage.
|
||||
//!
|
||||
//! This is a simple memory pool implementation which pre-allocates all sub-pools using a given pool
|
||||
//! configuration. After the pre-allocation, no dynamic memory allocation will be performed
|
||||
//! during run-time. This makes the implementation suitable for real-time applications and
|
||||
//! embedded environments. The pool implementation will also track the size of the data stored
|
||||
//! inside it.
|
||||
//!
|
||||
//! Transactions with the [pool][LocalPool] are done using a special [address][StoreAddr] type.
|
||||
//! Adding any data to the pool will yield a store address. Modification and read operations are
|
||||
//! done using a reference to a store address. Deletion will consume the store address.
|
||||
//!
|
||||
//! # Example
|
||||
//! # Example for the [StaticMemoryPool]
|
||||
//!
|
||||
//! ```
|
||||
//! use satrs_core::pool::{LocalPool, PoolCfg, PoolProvider};
|
||||
//! use satrs_core::pool::{PoolProviderMemInPlace, StaticMemoryPool, StaticPoolConfig};
|
||||
//!
|
||||
//! // 4 buckets of 4 bytes, 2 of 8 bytes and 1 of 16 bytes
|
||||
//! let pool_cfg = PoolCfg::new(vec![(4, 4), (2, 8), (1, 16)]);
|
||||
//! let mut local_pool = LocalPool::new(pool_cfg);
|
||||
//! let pool_cfg = StaticPoolConfig::new(vec![(4, 4), (2, 8), (1, 16)]);
|
||||
//! let mut local_pool = StaticMemoryPool::new(pool_cfg);
|
||||
//! let mut addr;
|
||||
//! {
|
||||
//! // Add new data to the pool
|
||||
@ -77,22 +67,24 @@
|
||||
#[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::error::Error;
|
||||
|
||||
type NumBlocks = u16;
|
||||
pub type StoreAddr = u64;
|
||||
|
||||
/// Simple address type used for transactions with the local pool.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct StoreAddr {
|
||||
pub struct StaticPoolAddr {
|
||||
pub(crate) pool_idx: u16,
|
||||
pub(crate) packet_idx: NumBlocks,
|
||||
}
|
||||
|
||||
impl StoreAddr {
|
||||
impl StaticPoolAddr {
|
||||
pub const INVALID_ADDR: u32 = 0xFFFFFFFF;
|
||||
|
||||
pub fn raw(&self) -> u32 {
|
||||
@ -100,7 +92,22 @@ impl StoreAddr {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StoreAddr {
|
||||
impl From<StaticPoolAddr> for StoreAddr {
|
||||
fn from(value: StaticPoolAddr) -> Self {
|
||||
((value.pool_idx as u64) << 16) | value.packet_idx as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StoreAddr> for StaticPoolAddr {
|
||||
fn from(value: StoreAddr) -> Self {
|
||||
Self {
|
||||
pool_idx: ((value >> 16) & 0xff) as u16,
|
||||
packet_idx: (value & 0xff) as u16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StaticPoolAddr {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
@ -180,64 +187,71 @@ impl Error for StoreError {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
mod alloc_mod {
|
||||
use crate::pool::{NumBlocks, StoreAddr, StoreError, StoreIdError};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use delegate::delegate;
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::{Arc, RwLock};
|
||||
/// Generic trait for pool providers where the data can be modified and read in-place. This
|
||||
/// generally means that a shared pool structure has to be wrapped inside a lock structure.
|
||||
pub trait PoolProviderMemInPlace {
|
||||
/// 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<StoreAddr, StoreError>;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub type ShareablePoolProvider = Box<dyn PoolProvider + Send + Sync>;
|
||||
#[cfg(feature = "std")]
|
||||
pub type SharedPool = Arc<RwLock<ShareablePoolProvider>>;
|
||||
/// 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>;
|
||||
|
||||
type PoolSize = usize;
|
||||
const STORE_FREE: PoolSize = PoolSize::MAX;
|
||||
pub const POOL_MAX_SIZE: PoolSize = STORE_FREE - 1;
|
||||
/// 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>;
|
||||
|
||||
/// Configuration structure of the [local pool][LocalPool]
|
||||
/// Read data by yielding a read-only reference given a [StoreAddr]
|
||||
fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError>;
|
||||
|
||||
/// Delete data inside the pool given a [StoreAddr]
|
||||
fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError>;
|
||||
fn has_element_at(&self, addr: &StoreAddr) -> Result<bool, StoreError>;
|
||||
|
||||
/// Retrieve the length of the data at the given store address.
|
||||
fn len_of_data(&self, addr: &StoreAddr) -> Result<usize, StoreError> {
|
||||
if !self.has_element_at(addr)? {
|
||||
return Err(StoreError::DataDoesNotExist(*addr));
|
||||
}
|
||||
Ok(self.read(addr)?.len())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PoolProviderMemInPlaceWithGuards: PoolProviderMemInPlace {
|
||||
/// This function behaves like [PoolProviderMemInPlace::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<Self>;
|
||||
|
||||
/// This function behaves like [PoolProviderMemInPlace::modify], but consumes the provided
|
||||
/// address and returns a RAII conformant guard object.
|
||||
///
|
||||
/// * `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)>,
|
||||
}
|
||||
/// 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<Self>;
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PoolGuard<'a> {
|
||||
pool: &'a mut LocalPool,
|
||||
pub struct PoolGuard<'a, MemProvider: PoolProviderMemInPlace + ?Sized> {
|
||||
pool: &'a mut MemProvider,
|
||||
pub addr: StoreAddr,
|
||||
no_deletion: bool,
|
||||
deletion_failed_error: Option<StoreError>,
|
||||
}
|
||||
}
|
||||
|
||||
/// This helper object
|
||||
impl<'a> PoolGuard<'a> {
|
||||
pub fn new(pool: &'a mut LocalPool, addr: StoreAddr) -> Self {
|
||||
/// This helper object
|
||||
impl<'a, MemProvider: PoolProviderMemInPlace> PoolGuard<'a, MemProvider> {
|
||||
pub fn new(pool: &'a mut MemProvider, addr: StoreAddr) -> Self {
|
||||
Self {
|
||||
pool,
|
||||
addr,
|
||||
@ -255,9 +269,9 @@ mod alloc_mod {
|
||||
pub fn release(&mut self) {
|
||||
self.no_deletion = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PoolGuard<'_> {
|
||||
impl<MemProvider: PoolProviderMemInPlace + ?Sized> Drop for PoolGuard<'_, MemProvider> {
|
||||
fn drop(&mut self) {
|
||||
if !self.no_deletion {
|
||||
if let Err(e) = self.pool.delete(self.addr) {
|
||||
@ -265,14 +279,14 @@ mod alloc_mod {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PoolRwGuard<'a> {
|
||||
guard: PoolGuard<'a>,
|
||||
}
|
||||
pub struct PoolRwGuard<'a, MemProvider: PoolProviderMemInPlace + ?Sized> {
|
||||
guard: PoolGuard<'a, MemProvider>,
|
||||
}
|
||||
|
||||
impl<'a> PoolRwGuard<'a> {
|
||||
pub fn new(pool: &'a mut LocalPool, addr: StoreAddr) -> Self {
|
||||
impl<'a, MemProvider: PoolProviderMemInPlace> PoolRwGuard<'a, MemProvider> {
|
||||
pub fn new(pool: &'a mut MemProvider, addr: StoreAddr) -> Self {
|
||||
Self {
|
||||
guard: PoolGuard::new(pool, addr),
|
||||
}
|
||||
@ -290,73 +304,80 @@ mod alloc_mod {
|
||||
pub fn release(&mut self);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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<StoreAddr, StoreError>;
|
||||
#[cfg(feature = "alloc")]
|
||||
mod alloc_mod {
|
||||
use super::{
|
||||
PoolGuard, PoolProviderMemInPlace, PoolProviderMemInPlaceWithGuards, PoolRwGuard,
|
||||
StaticPoolAddr,
|
||||
};
|
||||
use crate::pool::{NumBlocks, StoreAddr, StoreError, StoreIdError};
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// 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>;
|
||||
#[cfg(feature = "std")]
|
||||
pub type SharedStaticMemoryPool = Arc<RwLock<StaticMemoryPool>>;
|
||||
|
||||
/// 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>;
|
||||
type PoolSize = usize;
|
||||
const STORE_FREE: PoolSize = PoolSize::MAX;
|
||||
pub const POOL_MAX_SIZE: PoolSize = STORE_FREE - 1;
|
||||
|
||||
/// This function behaves like [Self::modify], but consumes the provided address and returns a
|
||||
/// RAII conformant guard object.
|
||||
/// Configuration structure of the [static memory pool][StaticMemoryPool]
|
||||
///
|
||||
/// 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<bool, StoreError>;
|
||||
|
||||
/// Retrieve the length of the data at the given store address.
|
||||
fn len_of_data(&self, addr: &StoreAddr) -> Result<usize, StoreError> {
|
||||
if !self.has_element_at(addr)? {
|
||||
return Err(StoreError::DataDoesNotExist(*addr));
|
||||
/// * `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 StaticPoolConfig {
|
||||
cfg: Vec<(NumBlocks, usize)>,
|
||||
}
|
||||
Ok(self.read(addr)?.len())
|
||||
|
||||
impl StaticPoolConfig {
|
||||
pub fn new(cfg: Vec<(NumBlocks, usize)>) -> Self {
|
||||
StaticPoolConfig { 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()
|
||||
}
|
||||
}
|
||||
|
||||
/// Pool implementation providing sub-pools with fixed size memory blocks. More details in
|
||||
/// the [module documentation][crate::pool]
|
||||
pub struct LocalPool {
|
||||
pool_cfg: PoolCfg,
|
||||
/// Pool implementation providing sub-pools with fixed size memory blocks.
|
||||
///
|
||||
/// This is a simple memory pool implementation which pre-allocates all sub-pools using a given pool
|
||||
/// configuration. After the pre-allocation, no dynamic memory allocation will be performed
|
||||
/// during run-time. This makes the implementation suitable for real-time applications and
|
||||
/// embedded environments. The pool implementation will also track the size of the data stored
|
||||
/// inside it.
|
||||
///
|
||||
/// Transactions with the [pool][StaticMemoryPool] are done using a generic
|
||||
/// [address][StoreAddr] type.
|
||||
/// Adding any data to the pool will yield a store address. Modification and read operations are
|
||||
/// done using a reference to a store address. Deletion will consume the store address.
|
||||
pub struct StaticMemoryPool {
|
||||
pool_cfg: StaticPoolConfig,
|
||||
pool: Vec<Vec<u8>>,
|
||||
sizes_lists: Vec<Vec<PoolSize>>,
|
||||
}
|
||||
|
||||
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 {
|
||||
impl StaticMemoryPool {
|
||||
/// Create a new local pool from the [given configuration][StaticPoolConfig]. This function
|
||||
/// will sanitize the given configuration as well.
|
||||
pub fn new(mut cfg: StaticPoolConfig) -> StaticMemoryPool {
|
||||
let subpools_num = cfg.sanitize();
|
||||
let mut local_pool = LocalPool {
|
||||
let mut local_pool = StaticMemoryPool {
|
||||
pool_cfg: cfg,
|
||||
pool: Vec::with_capacity(subpools_num),
|
||||
sizes_lists: Vec::with_capacity(subpools_num),
|
||||
@ -372,39 +393,39 @@ mod alloc_mod {
|
||||
local_pool
|
||||
}
|
||||
|
||||
fn addr_check(&self, addr: &StoreAddr) -> Result<usize, StoreError> {
|
||||
fn addr_check(&self, addr: &StaticPoolAddr) -> Result<usize, StoreError> {
|
||||
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));
|
||||
return Err(StoreError::DataDoesNotExist(StoreAddr::from(*addr)));
|
||||
}
|
||||
Ok(curr_size)
|
||||
}
|
||||
|
||||
fn validate_addr(&self, addr: &StoreAddr) -> Result<(), StoreError> {
|
||||
fn validate_addr(&self, addr: &StaticPoolAddr) -> 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),
|
||||
Some(StoreAddr::from(*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),
|
||||
Some(StoreAddr::from(*addr)),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reserve(&mut self, data_len: usize) -> Result<StoreAddr, StoreError> {
|
||||
fn reserve(&mut self, data_len: usize) -> Result<StaticPoolAddr, StoreError> {
|
||||
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 {
|
||||
Ok(StaticPoolAddr {
|
||||
pool_idx: subpool_idx,
|
||||
packet_idx: slot,
|
||||
})
|
||||
@ -422,7 +443,7 @@ mod alloc_mod {
|
||||
Err(StoreError::DataTooLarge(req_size))
|
||||
}
|
||||
|
||||
fn write(&mut self, addr: &StoreAddr, data: &[u8]) -> Result<(), StoreError> {
|
||||
fn write(&mut self, addr: &StaticPoolAddr, data: &[u8]) -> Result<(), StoreError> {
|
||||
let packet_pos = self.raw_pos(addr).ok_or(StoreError::InternalError(0))?;
|
||||
let subpool = self
|
||||
.pool
|
||||
@ -449,13 +470,13 @@ mod alloc_mod {
|
||||
Err(StoreError::StoreFull(subpool))
|
||||
}
|
||||
|
||||
fn raw_pos(&self, addr: &StoreAddr) -> Option<usize> {
|
||||
fn raw_pos(&self, addr: &StaticPoolAddr) -> Option<usize> {
|
||||
let (_, size) = self.pool_cfg.cfg.get(addr.pool_idx as usize)?;
|
||||
Some(addr.packet_idx as usize * size)
|
||||
}
|
||||
}
|
||||
|
||||
impl PoolProvider for LocalPool {
|
||||
impl PoolProviderMemInPlace for StaticMemoryPool {
|
||||
fn add(&mut self, data: &[u8]) -> Result<StoreAddr, StoreError> {
|
||||
let data_len = data.len();
|
||||
if data_len > POOL_MAX_SIZE {
|
||||
@ -463,7 +484,7 @@ mod alloc_mod {
|
||||
}
|
||||
let addr = self.reserve(data_len)?;
|
||||
self.write(&addr, data)?;
|
||||
Ok(addr)
|
||||
Ok(addr.into())
|
||||
}
|
||||
|
||||
fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError> {
|
||||
@ -474,34 +495,29 @@ mod alloc_mod {
|
||||
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))
|
||||
Ok((addr.into(), 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 addr = StaticPoolAddr::from(*addr);
|
||||
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 addr = StaticPoolAddr::from(*addr);
|
||||
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> {
|
||||
let addr = StaticPoolAddr::from(addr);
|
||||
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();
|
||||
@ -514,7 +530,8 @@ mod alloc_mod {
|
||||
}
|
||||
|
||||
fn has_element_at(&self, addr: &StoreAddr) -> Result<bool, StoreError> {
|
||||
self.validate_addr(addr)?;
|
||||
let addr = StaticPoolAddr::from(*addr);
|
||||
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];
|
||||
@ -524,34 +541,45 @@ mod alloc_mod {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl PoolProviderMemInPlaceWithGuards for StaticMemoryPool {
|
||||
fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard<Self> {
|
||||
PoolRwGuard::new(self, addr)
|
||||
}
|
||||
|
||||
fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard<Self> {
|
||||
PoolGuard::new(self, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::pool::{
|
||||
LocalPool, PoolCfg, PoolGuard, PoolProvider, PoolRwGuard, StoreAddr, StoreError,
|
||||
StoreIdError, POOL_MAX_SIZE,
|
||||
PoolGuard, PoolProviderMemInPlace, PoolProviderMemInPlaceWithGuards, PoolRwGuard,
|
||||
StaticMemoryPool, StaticPoolAddr, StaticPoolConfig, StoreError, StoreIdError,
|
||||
POOL_MAX_SIZE,
|
||||
};
|
||||
use std::vec;
|
||||
|
||||
fn basic_small_pool() -> LocalPool {
|
||||
fn basic_small_pool() -> StaticMemoryPool {
|
||||
// 4 buckets of 4 bytes, 2 of 8 bytes and 1 of 16 bytes
|
||||
let pool_cfg = PoolCfg::new(vec![(4, 4), (2, 8), (1, 16)]);
|
||||
LocalPool::new(pool_cfg)
|
||||
let pool_cfg = StaticPoolConfig::new(vec![(4, 4), (2, 8), (1, 16)]);
|
||||
StaticMemoryPool::new(pool_cfg)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cfg() {
|
||||
// Values where number of buckets is 0 or size is too large should be removed
|
||||
let mut pool_cfg = PoolCfg::new(vec![(0, 0), (1, 0), (2, POOL_MAX_SIZE)]);
|
||||
let mut pool_cfg = StaticPoolConfig::new(vec![(0, 0), (1, 0), (2, POOL_MAX_SIZE)]);
|
||||
pool_cfg.sanitize();
|
||||
assert_eq!(*pool_cfg.cfg(), vec![(1, 0)]);
|
||||
// Entries should be ordered according to bucket size
|
||||
pool_cfg = PoolCfg::new(vec![(16, 6), (32, 3), (8, 12)]);
|
||||
pool_cfg = StaticPoolConfig::new(vec![(16, 6), (32, 3), (8, 12)]);
|
||||
pool_cfg.sanitize();
|
||||
assert_eq!(*pool_cfg.cfg(), vec![(32, 3), (16, 6), (8, 12)]);
|
||||
// Unstable sort is used, so order of entries with same block length should not matter
|
||||
pool_cfg = PoolCfg::new(vec![(12, 12), (14, 16), (10, 12)]);
|
||||
pool_cfg = StaticPoolConfig::new(vec![(12, 12), (14, 16), (10, 12)]);
|
||||
pool_cfg.sanitize();
|
||||
assert!(
|
||||
*pool_cfg.cfg() == vec![(12, 12), (10, 12), (14, 16)]
|
||||
@ -600,10 +628,10 @@ mod tests {
|
||||
let (addr, buf_ref) = res.unwrap();
|
||||
assert_eq!(
|
||||
addr,
|
||||
StoreAddr {
|
||||
u64::from(StaticPoolAddr {
|
||||
pool_idx: 2,
|
||||
packet_idx: 0
|
||||
}
|
||||
})
|
||||
);
|
||||
assert_eq!(buf_ref.len(), 12);
|
||||
}
|
||||
@ -655,10 +683,13 @@ mod tests {
|
||||
fn test_read_does_not_exist() {
|
||||
let local_pool = basic_small_pool();
|
||||
// Try to access data which does not exist
|
||||
let res = local_pool.read(&StoreAddr {
|
||||
let res = local_pool.read(
|
||||
&StaticPoolAddr {
|
||||
packet_idx: 0,
|
||||
pool_idx: 0,
|
||||
});
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
assert!(res.is_err());
|
||||
assert!(matches!(
|
||||
res.unwrap_err(),
|
||||
@ -684,10 +715,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_invalid_pool_idx() {
|
||||
let local_pool = basic_small_pool();
|
||||
let addr = StoreAddr {
|
||||
let addr = StaticPoolAddr {
|
||||
pool_idx: 3,
|
||||
packet_idx: 0,
|
||||
};
|
||||
}
|
||||
.into();
|
||||
let res = local_pool.read(&addr);
|
||||
assert!(res.is_err());
|
||||
let err = res.unwrap_err();
|
||||
@ -700,12 +732,12 @@ mod tests {
|
||||
#[test]
|
||||
fn test_invalid_packet_idx() {
|
||||
let local_pool = basic_small_pool();
|
||||
let addr = StoreAddr {
|
||||
let addr = StaticPoolAddr {
|
||||
pool_idx: 2,
|
||||
packet_idx: 1,
|
||||
};
|
||||
assert_eq!(addr.raw(), 0x00020001);
|
||||
let res = local_pool.read(&addr);
|
||||
let res = local_pool.read(&addr.into());
|
||||
assert!(res.is_err());
|
||||
let err = res.unwrap_err();
|
||||
assert!(matches!(
|
||||
|
@ -82,7 +82,7 @@ pub mod heapless_mod {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum EventRequest<Event: GenericEvent = EventU32> {
|
||||
Enable(Event),
|
||||
Disable(Event),
|
||||
|
@ -1,54 +1,39 @@
|
||||
use crate::events::EventU32;
|
||||
use crate::pus::event_man::{EventRequest, EventRequestWithToken};
|
||||
use crate::pus::verification::TcStateToken;
|
||||
use crate::pus::{
|
||||
EcssTcReceiver, EcssTmSender, PartialPusHandlingError, PusPacketHandlerResult,
|
||||
PusPacketHandlingError,
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use crate::pus::{PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError};
|
||||
use spacepackets::ecss::event::Subservice;
|
||||
use spacepackets::ecss::PusPacket;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use super::verification::VerificationReporterWithSender;
|
||||
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHandler};
|
||||
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHelper};
|
||||
|
||||
pub struct PusService5EventHandler<TcInMemConverter: EcssTcInMemConverter> {
|
||||
pub psb: PusServiceHandler<TcInMemConverter>,
|
||||
pub service_helper: PusServiceHelper<TcInMemConverter>,
|
||||
event_request_tx: Sender<EventRequestWithToken>,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> PusService5EventHandler<TcInMemConverter> {
|
||||
pub fn new(
|
||||
tc_receiver: Box<dyn EcssTcReceiver>,
|
||||
tm_sender: Box<dyn EcssTmSender>,
|
||||
tm_apid: u16,
|
||||
verification_handler: VerificationReporterWithSender,
|
||||
tc_in_mem_converter: TcInMemConverter,
|
||||
service_handler: PusServiceHelper<TcInMemConverter>,
|
||||
event_request_tx: Sender<EventRequestWithToken>,
|
||||
) -> Self {
|
||||
Self {
|
||||
psb: PusServiceHandler::new(
|
||||
tc_receiver,
|
||||
tm_sender,
|
||||
tm_apid,
|
||||
verification_handler,
|
||||
tc_in_mem_converter,
|
||||
),
|
||||
service_helper: service_handler,
|
||||
event_request_tx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
|
||||
let possible_packet = self.psb.retrieve_and_accept_next_packet()?;
|
||||
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
|
||||
if possible_packet.is_none() {
|
||||
return Ok(PusPacketHandlerResult::Empty);
|
||||
}
|
||||
let ecss_tc_and_token = possible_packet.unwrap();
|
||||
let tc = self
|
||||
.psb
|
||||
.service_helper
|
||||
.tc_in_mem_converter
|
||||
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token)?;
|
||||
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
|
||||
let subservice = tc.subservice();
|
||||
let srv = Subservice::try_from(subservice);
|
||||
if srv.is_err() {
|
||||
@ -60,13 +45,13 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService5EventHandler<TcInMemConv
|
||||
let handle_enable_disable_request = |enable: bool, stamp: [u8; 7]| {
|
||||
if tc.user_data().len() < 4 {
|
||||
return Err(PusPacketHandlingError::NotEnoughAppData(
|
||||
"At least 4 bytes event ID expected".into(),
|
||||
"at least 4 bytes event ID expected".into(),
|
||||
));
|
||||
}
|
||||
let user_data = tc.user_data();
|
||||
let event_u32 = EventU32::from(u32::from_be_bytes(user_data[0..4].try_into().unwrap()));
|
||||
let start_token = self
|
||||
.psb
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.borrow_mut()
|
||||
@ -126,3 +111,170 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService5EventHandler<TcInMemConv
|
||||
Ok(PusPacketHandlerResult::RequestHandled)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use delegate::delegate;
|
||||
use spacepackets::ecss::event::Subservice;
|
||||
use spacepackets::util::UnsignedEnum;
|
||||
use spacepackets::{
|
||||
ecss::{
|
||||
tc::{PusTcCreator, PusTcSecondaryHeader},
|
||||
tm::PusTmReader,
|
||||
},
|
||||
SequenceFlags, SpHeader,
|
||||
};
|
||||
use std::sync::mpsc::{self, Sender};
|
||||
|
||||
use crate::pus::event_man::EventRequest;
|
||||
use crate::pus::tests::SimplePusPacketHandler;
|
||||
use crate::pus::verification::RequestId;
|
||||
use crate::{
|
||||
events::EventU32,
|
||||
pus::{
|
||||
event_man::EventRequestWithToken,
|
||||
tests::{PusServiceHandlerWithSharedStoreCommon, PusTestHarness, TEST_APID},
|
||||
verification::{TcStateAccepted, VerificationToken},
|
||||
EcssTcInSharedStoreConverter, PusPacketHandlerResult, PusPacketHandlingError,
|
||||
},
|
||||
};
|
||||
|
||||
use super::PusService5EventHandler;
|
||||
|
||||
const TEST_EVENT_0: EventU32 = EventU32::const_new(crate::events::Severity::INFO, 5, 25);
|
||||
|
||||
struct Pus5HandlerWithStoreTester {
|
||||
common: PusServiceHandlerWithSharedStoreCommon,
|
||||
handler: PusService5EventHandler<EcssTcInSharedStoreConverter>,
|
||||
}
|
||||
|
||||
impl Pus5HandlerWithStoreTester {
|
||||
pub fn new(event_request_tx: Sender<EventRequestWithToken>) -> Self {
|
||||
let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new();
|
||||
Self {
|
||||
common,
|
||||
handler: PusService5EventHandler::new(srv_handler, event_request_tx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PusTestHarness for Pus5HandlerWithStoreTester {
|
||||
delegate! {
|
||||
to self.common {
|
||||
fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
|
||||
fn read_next_tm(&mut self) -> PusTmReader<'_>;
|
||||
fn check_no_tm_available(&self) -> bool;
|
||||
fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl SimplePusPacketHandler for Pus5HandlerWithStoreTester {
|
||||
delegate! {
|
||||
to self.handler {
|
||||
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn event_test(
|
||||
test_harness: &mut (impl PusTestHarness + SimplePusPacketHandler),
|
||||
subservice: Subservice,
|
||||
expected_event_req: EventRequest,
|
||||
event_req_receiver: mpsc::Receiver<EventRequestWithToken>,
|
||||
) {
|
||||
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(5, subservice as u8);
|
||||
let mut app_data = [0; 4];
|
||||
TEST_EVENT_0
|
||||
.write_to_be_bytes(&mut app_data)
|
||||
.expect("writing test event failed");
|
||||
let ping_tc = PusTcCreator::new(&mut sp_header, sec_header, &app_data, true);
|
||||
let token = test_harness.send_tc(&ping_tc);
|
||||
let request_id = token.req_id();
|
||||
test_harness.handle_one_tc().unwrap();
|
||||
test_harness.check_next_verification_tm(1, request_id);
|
||||
test_harness.check_next_verification_tm(3, request_id);
|
||||
// Completion TM is not generated for us.
|
||||
assert!(test_harness.check_no_tm_available());
|
||||
let event_request = event_req_receiver
|
||||
.try_recv()
|
||||
.expect("no event request received");
|
||||
assert_eq!(expected_event_req, event_request.request);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enabling_event_reporting() {
|
||||
let (event_request_tx, event_request_rx) = mpsc::channel();
|
||||
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
||||
event_test(
|
||||
&mut test_harness,
|
||||
Subservice::TcEnableEventGeneration,
|
||||
EventRequest::Enable(TEST_EVENT_0),
|
||||
event_request_rx,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_disabling_event_reporting() {
|
||||
let (event_request_tx, event_request_rx) = mpsc::channel();
|
||||
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
||||
event_test(
|
||||
&mut test_harness,
|
||||
Subservice::TcDisableEventGeneration,
|
||||
EventRequest::Disable(TEST_EVENT_0),
|
||||
event_request_rx,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_tc_queue() {
|
||||
let (event_request_tx, _) = mpsc::channel();
|
||||
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
||||
let result = test_harness.handle_one_tc();
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
if let PusPacketHandlerResult::Empty = result {
|
||||
} else {
|
||||
panic!("unexpected result type {result:?}")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sending_custom_subservice() {
|
||||
let (event_request_tx, _) = mpsc::channel();
|
||||
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
||||
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(5, 200);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true);
|
||||
test_harness.send_tc(&ping_tc);
|
||||
let result = test_harness.handle_one_tc();
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
if let PusPacketHandlerResult::CustomSubservice(subservice, _) = result {
|
||||
assert_eq!(subservice, 200);
|
||||
} else {
|
||||
panic!("unexpected result type {result:?}")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sending_invalid_app_data() {
|
||||
let (event_request_tx, _) = mpsc::channel();
|
||||
let mut test_harness = Pus5HandlerWithStoreTester::new(event_request_tx);
|
||||
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
|
||||
let sec_header =
|
||||
PusTcSecondaryHeader::new_simple(5, Subservice::TcEnableEventGeneration as u8);
|
||||
let ping_tc = PusTcCreator::new(&mut sp_header, sec_header, &[0, 1, 2], true);
|
||||
test_harness.send_tc(&ping_tc);
|
||||
let result = test_harness.handle_one_tc();
|
||||
assert!(result.is_err());
|
||||
let result = result.unwrap_err();
|
||||
if let PusPacketHandlingError::NotEnoughAppData(string) = result {
|
||||
assert_eq!(string, "at least 4 bytes event ID expected");
|
||||
} else {
|
||||
panic!("unexpected result type {result:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,6 +194,9 @@ pub trait EcssTcSenderCore: EcssChannel {
|
||||
fn send_tc(&self, tc: PusTcCreator, token: Option<TcStateToken>) -> Result<(), EcssTmtcError>;
|
||||
}
|
||||
|
||||
/// A PUS telecommand packet can be stored in memory using different methods. Right now,
|
||||
/// storage inside a pool structure like [crate::pool::StaticMemoryPool], and storage inside a
|
||||
/// `Vec<u8>` are supported.
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum TcInMemory {
|
||||
@ -215,6 +218,7 @@ impl From<alloc::vec::Vec<u8>> for TcInMemory {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic structure for an ECSS PUS Telecommand and its correspoding verification token.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EcssTcAndToken {
|
||||
pub tc_in_memory: TcInMemory,
|
||||
@ -363,8 +367,9 @@ mod alloc_mod {
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
||||
pub mod std_mod {
|
||||
use crate::pool::{SharedPool, StoreAddr};
|
||||
use crate::pool::{PoolProviderMemInPlaceWithGuards, SharedStaticMemoryPool, StoreAddr};
|
||||
use crate::pus::verification::{
|
||||
StdVerifReporterWithSender, TcStateAccepted, VerificationToken,
|
||||
};
|
||||
@ -469,13 +474,13 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MpscTcInStoreReceiver {
|
||||
pub struct MpscTcReceiver {
|
||||
id: ChannelId,
|
||||
name: &'static str,
|
||||
receiver: mpsc::Receiver<EcssTcAndToken>,
|
||||
}
|
||||
|
||||
impl EcssChannel for MpscTcInStoreReceiver {
|
||||
impl EcssChannel for MpscTcReceiver {
|
||||
fn id(&self) -> ChannelId {
|
||||
self.id
|
||||
}
|
||||
@ -485,7 +490,7 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
|
||||
impl EcssTcReceiverCore for MpscTcInStoreReceiver {
|
||||
impl EcssTcReceiverCore for MpscTcReceiver {
|
||||
fn recv_tc(&self) -> Result<EcssTcAndToken, TryRecvTmtcError> {
|
||||
self.receiver.try_recv().map_err(|e| match e {
|
||||
TryRecvError::Empty => TryRecvTmtcError::Empty,
|
||||
@ -496,7 +501,7 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
|
||||
impl MpscTcInStoreReceiver {
|
||||
impl MpscTcReceiver {
|
||||
pub fn new(
|
||||
id: ChannelId,
|
||||
name: &'static str,
|
||||
@ -513,8 +518,8 @@ pub mod std_mod {
|
||||
#[derive(Clone)]
|
||||
pub struct MpscTmAsVecSender {
|
||||
id: ChannelId,
|
||||
sender: mpsc::Sender<Vec<u8>>,
|
||||
name: &'static str,
|
||||
sender: mpsc::Sender<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl From<mpsc::SendError<Vec<u8>>> for EcssTmtcError {
|
||||
@ -599,13 +604,13 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CrossbeamTcInStoreReceiver {
|
||||
pub struct CrossbeamTcReceiver {
|
||||
id: ChannelId,
|
||||
name: &'static str,
|
||||
receiver: cb::Receiver<EcssTcAndToken>,
|
||||
}
|
||||
|
||||
impl CrossbeamTcInStoreReceiver {
|
||||
impl CrossbeamTcReceiver {
|
||||
pub fn new(
|
||||
id: ChannelId,
|
||||
name: &'static str,
|
||||
@ -615,7 +620,7 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
|
||||
impl EcssChannel for CrossbeamTcInStoreReceiver {
|
||||
impl EcssChannel for CrossbeamTcReceiver {
|
||||
fn id(&self) -> ChannelId {
|
||||
self.id
|
||||
}
|
||||
@ -625,7 +630,7 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
|
||||
impl EcssTcReceiverCore for CrossbeamTcInStoreReceiver {
|
||||
impl EcssTcReceiverCore for CrossbeamTcReceiver {
|
||||
fn recv_tc(&self) -> Result<EcssTcAndToken, TryRecvTmtcError> {
|
||||
self.receiver.try_recv().map_err(|e| match e {
|
||||
cb::TryRecvError::Empty => TryRecvTmtcError::Empty,
|
||||
@ -689,36 +694,40 @@ pub mod std_mod {
|
||||
}
|
||||
|
||||
pub trait EcssTcInMemConverter {
|
||||
fn cache_ecss_tc_in_memory<'a>(
|
||||
&'a mut self,
|
||||
possible_packet: &'a AcceptedEcssTcAndToken,
|
||||
fn cache_ecss_tc_in_memory(
|
||||
&mut self,
|
||||
possible_packet: &TcInMemory,
|
||||
) -> Result<(), PusPacketHandlingError>;
|
||||
|
||||
fn tc_slice_raw(&self) -> &[u8];
|
||||
|
||||
fn convert_ecss_tc_in_memory_to_reader<'a>(
|
||||
&'a mut self,
|
||||
possible_packet: &'a AcceptedEcssTcAndToken,
|
||||
) -> Result<PusTcReader<'a>, PusPacketHandlingError> {
|
||||
fn convert_ecss_tc_in_memory_to_reader(
|
||||
&mut self,
|
||||
possible_packet: &TcInMemory,
|
||||
) -> Result<PusTcReader<'_>, PusPacketHandlingError> {
|
||||
self.cache_ecss_tc_in_memory(possible_packet)?;
|
||||
Ok(PusTcReader::new(self.tc_slice_raw())?.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Converter structure for PUS telecommands which are stored inside a `Vec<u8>` structure.
|
||||
/// Please note that this structure is not able to convert TCs which are stored inside a
|
||||
/// [SharedStaticMemoryPool].
|
||||
#[derive(Default, Clone)]
|
||||
pub struct EcssTcInVecConverter {
|
||||
pub pus_tc_raw: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl EcssTcInMemConverter for EcssTcInVecConverter {
|
||||
fn cache_ecss_tc_in_memory<'a>(
|
||||
&'a mut self,
|
||||
possible_packet: &'a AcceptedEcssTcAndToken,
|
||||
fn cache_ecss_tc_in_memory(
|
||||
&mut self,
|
||||
tc_in_memory: &TcInMemory,
|
||||
) -> Result<(), PusPacketHandlingError> {
|
||||
self.pus_tc_raw = None;
|
||||
match &possible_packet.tc_in_memory {
|
||||
match tc_in_memory {
|
||||
super::TcInMemory::StoreAddr(_) => {
|
||||
return Err(PusPacketHandlingError::InvalidTcInMemoryFormat(
|
||||
possible_packet.tc_in_memory.clone(),
|
||||
tc_in_memory.clone(),
|
||||
));
|
||||
}
|
||||
super::TcInMemory::Vec(vec) => {
|
||||
@ -736,13 +745,17 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EcssTcInStoreConverter {
|
||||
pub shared_tc_store: SharedPool,
|
||||
pub pus_buf: Vec<u8>,
|
||||
/// Converter structure for PUS telecommands which are stored inside
|
||||
/// [SharedStaticMemoryPool] structure. This is useful if run-time allocation for these
|
||||
/// packets should be avoided. Please note that this structure is not able to convert TCs which
|
||||
/// are stored as a `Vec<u8>`.
|
||||
pub struct EcssTcInSharedStoreConverter {
|
||||
shared_tc_store: SharedStaticMemoryPool,
|
||||
pus_buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl EcssTcInStoreConverter {
|
||||
pub fn new(shared_tc_store: SharedPool, max_expected_tc_size: usize) -> Self {
|
||||
impl EcssTcInSharedStoreConverter {
|
||||
pub fn new(shared_tc_store: SharedStaticMemoryPool, max_expected_tc_size: usize) -> Self {
|
||||
Self {
|
||||
shared_tc_store,
|
||||
pus_buf: alloc::vec![0; max_expected_tc_size],
|
||||
@ -765,18 +778,18 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
|
||||
impl EcssTcInMemConverter for EcssTcInStoreConverter {
|
||||
fn cache_ecss_tc_in_memory<'a>(
|
||||
&'a mut self,
|
||||
possible_packet: &'a AcceptedEcssTcAndToken,
|
||||
impl EcssTcInMemConverter for EcssTcInSharedStoreConverter {
|
||||
fn cache_ecss_tc_in_memory(
|
||||
&mut self,
|
||||
tc_in_memory: &TcInMemory,
|
||||
) -> Result<(), PusPacketHandlingError> {
|
||||
match &possible_packet.tc_in_memory {
|
||||
match tc_in_memory {
|
||||
super::TcInMemory::StoreAddr(addr) => {
|
||||
self.copy_tc_to_buf(*addr)?;
|
||||
}
|
||||
super::TcInMemory::Vec(_) => {
|
||||
return Err(PusPacketHandlingError::InvalidTcInMemoryFormat(
|
||||
possible_packet.tc_in_memory.clone(),
|
||||
tc_in_memory.clone(),
|
||||
));
|
||||
}
|
||||
};
|
||||
@ -821,18 +834,21 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
|
||||
/// Base class for handlers which can handle PUS TC packets. Right now, the verification
|
||||
/// reporter is constrained to the [StdVerifReporterWithSender] and the service handler
|
||||
/// relies on TMTC packets being exchanged via a [SharedPool]. Please note that this variant
|
||||
/// of the PUS service base is not optimized for handling packets sent as a `Vec<u8>` and
|
||||
/// might perform additional copies to the internal buffer as well. The class should
|
||||
/// still behave correctly.
|
||||
pub struct PusServiceHandler<TcInMemConverter: EcssTcInMemConverter> {
|
||||
/// This is a high-level PUS packet handler helper.
|
||||
///
|
||||
/// It performs some of the boilerplate acitivities involved when handling PUS telecommands and
|
||||
/// it can be used to implement the handling of PUS telecommands for certain PUS telecommands
|
||||
/// groups (for example individual services).
|
||||
///
|
||||
/// This base class can handle PUS telecommands backed by different memory storage machanisms
|
||||
/// by using the [EcssTcInMemConverter] abstraction. This object provides some convenience
|
||||
/// methods to make the generic parts of TC handling easier.
|
||||
pub struct PusServiceHelper<TcInMemConverter: EcssTcInMemConverter> {
|
||||
pub common: PusServiceBase,
|
||||
pub tc_in_mem_converter: TcInMemConverter,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> PusServiceHandler<TcInMemConverter> {
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> PusServiceHelper<TcInMemConverter> {
|
||||
pub fn new(
|
||||
tc_receiver: Box<dyn EcssTcReceiver>,
|
||||
tm_sender: Box<dyn EcssTmSender>,
|
||||
@ -851,6 +867,11 @@ pub mod std_mod {
|
||||
}
|
||||
}
|
||||
|
||||
/// This function can be used to poll the internal [EcssTcReceiver] object for the next
|
||||
/// telecommand packet. It will return `Ok(None)` if there are not packets available.
|
||||
/// In any other case, it will perform the acceptance of the ECSS TC packet using the
|
||||
/// internal [VerificationReporterWithSender] object. It will then return the telecommand
|
||||
/// and the according accepted token.
|
||||
pub fn retrieve_and_accept_next_packet(
|
||||
&mut self,
|
||||
) -> Result<Option<AcceptedEcssTcAndToken>, PusPacketHandlingError> {
|
||||
@ -893,10 +914,35 @@ pub(crate) fn source_buffer_large_enough(cap: usize, len: usize) -> Result<(), E
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator};
|
||||
pub mod tests {
|
||||
use std::sync::mpsc::TryRecvError;
|
||||
use std::sync::{mpsc, RwLock};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec;
|
||||
use spacepackets::ecss::tc::PusTcCreator;
|
||||
use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator, PusTmReader};
|
||||
use spacepackets::ecss::{PusPacket, WritablePusPacket};
|
||||
use spacepackets::CcsdsPacket;
|
||||
|
||||
use crate::pool::{
|
||||
PoolProviderMemInPlace, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig,
|
||||
StoreAddr,
|
||||
};
|
||||
use crate::pus::verification::RequestId;
|
||||
use crate::tmtc::tm_helper::SharedTmStore;
|
||||
|
||||
use super::verification::{
|
||||
TcStateAccepted, VerificationReporterCfg, VerificationReporterWithSender, VerificationToken,
|
||||
};
|
||||
use super::{
|
||||
EcssTcAndToken, EcssTcInSharedStoreConverter, EcssTcInVecConverter, MpscTcReceiver,
|
||||
MpscTmAsVecSender, MpscTmInStoreSender, PusPacketHandlerResult, PusPacketHandlingError,
|
||||
PusServiceHelper, TcInMemory,
|
||||
};
|
||||
|
||||
pub const TEST_APID: u16 = 0x101;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub(crate) struct CommonTmInfo {
|
||||
pub subservice: u8,
|
||||
@ -906,12 +952,23 @@ pub(crate) mod tests {
|
||||
pub time_stamp: [u8; 7],
|
||||
}
|
||||
|
||||
pub trait PusTestHarness {
|
||||
fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
|
||||
fn read_next_tm(&mut self) -> PusTmReader<'_>;
|
||||
fn check_no_tm_available(&self) -> bool;
|
||||
fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId);
|
||||
}
|
||||
|
||||
pub trait SimplePusPacketHandler {
|
||||
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
|
||||
}
|
||||
|
||||
impl CommonTmInfo {
|
||||
pub fn new_from_tm(tm: &PusTmCreator) -> Self {
|
||||
let mut time_stamp = [0; 7];
|
||||
time_stamp.clone_from_slice(&tm.timestamp()[0..7]);
|
||||
Self {
|
||||
subservice: tm.subservice(),
|
||||
subservice: PusPacket::subservice(tm),
|
||||
apid: tm.apid(),
|
||||
msg_counter: tm.msg_counter(),
|
||||
dest_id: tm.dest_id(),
|
||||
@ -919,4 +976,192 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Common fields for a PUS service test harness.
|
||||
pub struct PusServiceHandlerWithSharedStoreCommon {
|
||||
pus_buf: [u8; 2048],
|
||||
tm_buf: [u8; 2048],
|
||||
tc_pool: SharedStaticMemoryPool,
|
||||
tm_pool: SharedTmStore,
|
||||
tc_sender: mpsc::Sender<EcssTcAndToken>,
|
||||
tm_receiver: mpsc::Receiver<StoreAddr>,
|
||||
verification_handler: VerificationReporterWithSender,
|
||||
}
|
||||
|
||||
impl PusServiceHandlerWithSharedStoreCommon {
|
||||
/// This function generates the structure in addition to the PUS service handler
|
||||
/// [PusServiceHandler] which might be required for a specific PUS service handler.
|
||||
///
|
||||
/// The PUS service handler is instantiated with a [EcssTcInStoreConverter].
|
||||
pub fn new() -> (Self, PusServiceHelper<EcssTcInSharedStoreConverter>) {
|
||||
let pool_cfg = StaticPoolConfig::new(vec![(16, 16), (8, 32), (4, 64)]);
|
||||
let tc_pool = StaticMemoryPool::new(pool_cfg.clone());
|
||||
let tm_pool = StaticMemoryPool::new(pool_cfg);
|
||||
let shared_tc_pool = SharedStaticMemoryPool::new(RwLock::new(tc_pool));
|
||||
let shared_tm_pool = SharedTmStore::new(tm_pool);
|
||||
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
|
||||
let (tm_tx, tm_rx) = mpsc::channel();
|
||||
|
||||
let verif_sender =
|
||||
MpscTmInStoreSender::new(0, "verif_sender", shared_tm_pool.clone(), tm_tx.clone());
|
||||
let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
|
||||
let verification_handler =
|
||||
VerificationReporterWithSender::new(&verif_cfg, Box::new(verif_sender));
|
||||
let test_srv_tm_sender =
|
||||
MpscTmInStoreSender::new(0, "TEST_SENDER", shared_tm_pool.clone(), tm_tx);
|
||||
let test_srv_tc_receiver = MpscTcReceiver::new(0, "TEST_RECEIVER", test_srv_tc_rx);
|
||||
let in_store_converter =
|
||||
EcssTcInSharedStoreConverter::new(shared_tc_pool.clone(), 2048);
|
||||
(
|
||||
Self {
|
||||
pus_buf: [0; 2048],
|
||||
tm_buf: [0; 2048],
|
||||
tc_pool: shared_tc_pool,
|
||||
tm_pool: shared_tm_pool,
|
||||
tc_sender: test_srv_tc_tx,
|
||||
tm_receiver: tm_rx,
|
||||
verification_handler: verification_handler.clone(),
|
||||
},
|
||||
PusServiceHelper::new(
|
||||
Box::new(test_srv_tc_receiver),
|
||||
Box::new(test_srv_tm_sender),
|
||||
TEST_APID,
|
||||
verification_handler,
|
||||
in_store_converter,
|
||||
),
|
||||
)
|
||||
}
|
||||
pub fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted> {
|
||||
let token = self.verification_handler.add_tc(tc);
|
||||
let token = self
|
||||
.verification_handler
|
||||
.acceptance_success(token, Some(&[0; 7]))
|
||||
.unwrap();
|
||||
let tc_size = tc.write_to_bytes(&mut self.pus_buf).unwrap();
|
||||
let mut tc_pool = self.tc_pool.write().unwrap();
|
||||
let addr = tc_pool.add(&self.pus_buf[..tc_size]).unwrap();
|
||||
drop(tc_pool);
|
||||
// Send accepted TC to test service handler.
|
||||
self.tc_sender
|
||||
.send(EcssTcAndToken::new(addr, token))
|
||||
.expect("sending tc failed");
|
||||
token
|
||||
}
|
||||
|
||||
pub fn read_next_tm(&mut self) -> PusTmReader<'_> {
|
||||
let next_msg = self.tm_receiver.try_recv();
|
||||
assert!(next_msg.is_ok());
|
||||
let tm_addr = next_msg.unwrap();
|
||||
let tm_pool = self.tm_pool.shared_pool.read().unwrap();
|
||||
let tm_raw = tm_pool.read(&tm_addr).unwrap();
|
||||
self.tm_buf[0..tm_raw.len()].copy_from_slice(tm_raw);
|
||||
PusTmReader::new(&self.tm_buf, 7).unwrap().0
|
||||
}
|
||||
|
||||
pub fn check_no_tm_available(&self) -> bool {
|
||||
let next_msg = self.tm_receiver.try_recv();
|
||||
if let TryRecvError::Empty = next_msg.unwrap_err() {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId) {
|
||||
let next_msg = self.tm_receiver.try_recv();
|
||||
assert!(next_msg.is_ok());
|
||||
let tm_addr = next_msg.unwrap();
|
||||
let tm_pool = self.tm_pool.shared_pool.read().unwrap();
|
||||
let tm_raw = tm_pool.read(&tm_addr).unwrap();
|
||||
let tm = PusTmReader::new(tm_raw, 7).unwrap().0;
|
||||
assert_eq!(PusPacket::service(&tm), 1);
|
||||
assert_eq!(PusPacket::subservice(&tm), subservice);
|
||||
assert_eq!(tm.apid(), TEST_APID);
|
||||
let req_id =
|
||||
RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
|
||||
assert_eq!(req_id, expected_request_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PusServiceHandlerWithVecCommon {
|
||||
current_tm: Option<alloc::vec::Vec<u8>>,
|
||||
tc_sender: mpsc::Sender<EcssTcAndToken>,
|
||||
tm_receiver: mpsc::Receiver<alloc::vec::Vec<u8>>,
|
||||
verification_handler: VerificationReporterWithSender,
|
||||
}
|
||||
|
||||
impl PusServiceHandlerWithVecCommon {
|
||||
pub fn new() -> (Self, PusServiceHelper<EcssTcInVecConverter>) {
|
||||
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
|
||||
let (tm_tx, tm_rx) = mpsc::channel();
|
||||
|
||||
let verif_sender = MpscTmAsVecSender::new(0, "verififcatio-sender", tm_tx.clone());
|
||||
let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
|
||||
let verification_handler =
|
||||
VerificationReporterWithSender::new(&verif_cfg, Box::new(verif_sender));
|
||||
let test_srv_tm_sender = MpscTmAsVecSender::new(0, "test-sender", tm_tx);
|
||||
let test_srv_tc_receiver = MpscTcReceiver::new(0, "test-receiver", test_srv_tc_rx);
|
||||
let in_store_converter = EcssTcInVecConverter::default();
|
||||
(
|
||||
Self {
|
||||
current_tm: None,
|
||||
tc_sender: test_srv_tc_tx,
|
||||
tm_receiver: tm_rx,
|
||||
verification_handler: verification_handler.clone(),
|
||||
},
|
||||
PusServiceHelper::new(
|
||||
Box::new(test_srv_tc_receiver),
|
||||
Box::new(test_srv_tm_sender),
|
||||
TEST_APID,
|
||||
verification_handler,
|
||||
in_store_converter,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted> {
|
||||
let token = self.verification_handler.add_tc(tc);
|
||||
let token = self
|
||||
.verification_handler
|
||||
.acceptance_success(token, Some(&[0; 7]))
|
||||
.unwrap();
|
||||
// Send accepted TC to test service handler.
|
||||
self.tc_sender
|
||||
.send(EcssTcAndToken::new(
|
||||
TcInMemory::Vec(tc.to_vec().expect("pus tc conversion to vec failed")),
|
||||
token,
|
||||
))
|
||||
.expect("sending tc failed");
|
||||
token
|
||||
}
|
||||
|
||||
pub fn read_next_tm(&mut self) -> PusTmReader<'_> {
|
||||
let next_msg = self.tm_receiver.try_recv();
|
||||
assert!(next_msg.is_ok());
|
||||
self.current_tm = Some(next_msg.unwrap());
|
||||
PusTmReader::new(self.current_tm.as_ref().unwrap(), 7)
|
||||
.unwrap()
|
||||
.0
|
||||
}
|
||||
|
||||
pub fn check_no_tm_available(&self) -> bool {
|
||||
let next_msg = self.tm_receiver.try_recv();
|
||||
if let TryRecvError::Empty = next_msg.unwrap_err() {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId) {
|
||||
let next_msg = self.tm_receiver.try_recv();
|
||||
assert!(next_msg.is_ok());
|
||||
let next_msg = next_msg.unwrap();
|
||||
let tm = PusTmReader::new(next_msg.as_slice(), 7).unwrap().0;
|
||||
assert_eq!(PusPacket::service(&tm), 1);
|
||||
assert_eq!(PusPacket::subservice(&tm), subservice);
|
||||
assert_eq!(tm.apid(), TEST_APID);
|
||||
let req_id =
|
||||
RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
|
||||
assert_eq!(req_id, expected_request_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,82 +1,72 @@
|
||||
use crate::pool::SharedPool;
|
||||
use crate::pus::scheduler::PusScheduler;
|
||||
use crate::pus::{EcssTcReceiver, EcssTmSender, PusPacketHandlerResult, PusPacketHandlingError};
|
||||
use super::scheduler::PusSchedulerInterface;
|
||||
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHelper};
|
||||
use crate::pool::PoolProviderMemInPlace;
|
||||
use crate::pus::{PusPacketHandlerResult, PusPacketHandlingError};
|
||||
use alloc::string::ToString;
|
||||
use spacepackets::ecss::{scheduling, PusPacket};
|
||||
use spacepackets::time::cds::TimeProvider;
|
||||
use std::boxed::Box;
|
||||
|
||||
use super::verification::VerificationReporterWithSender;
|
||||
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHandler};
|
||||
|
||||
/// This is a helper class for [std] environments to handle generic PUS 11 (scheduling service)
|
||||
/// packets. This handler is constrained to using the [PusScheduler], but is able to process
|
||||
/// the most important PUS requests for a scheduling service.
|
||||
/// packets. This handler is able to handle the most important PUS requests for a scheduling
|
||||
/// service which provides the [PusSchedulerInterface].
|
||||
///
|
||||
/// Please note that this class does not do the regular periodic handling like releasing any
|
||||
/// telecommands inside the scheduler. The user can retrieve the wrapped scheduler via the
|
||||
/// [Self::scheduler] and [Self::scheduler_mut] function and then use the scheduler API to release
|
||||
/// telecommands when applicable.
|
||||
pub struct PusService11SchedHandler<TcInMemConverter: EcssTcInMemConverter> {
|
||||
pub psb: PusServiceHandler<TcInMemConverter>,
|
||||
shared_tc_store: SharedPool,
|
||||
scheduler: PusScheduler,
|
||||
pub struct PusService11SchedHandler<
|
||||
TcInMemConverter: EcssTcInMemConverter,
|
||||
Scheduler: PusSchedulerInterface,
|
||||
> {
|
||||
pub service_helper: PusServiceHelper<TcInMemConverter>,
|
||||
scheduler: Scheduler,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> PusService11SchedHandler<TcInMemConverter> {
|
||||
pub fn new(
|
||||
tc_receiver: Box<dyn EcssTcReceiver>,
|
||||
tm_sender: Box<dyn EcssTmSender>,
|
||||
tm_apid: u16,
|
||||
verification_handler: VerificationReporterWithSender,
|
||||
tc_in_mem_converter: TcInMemConverter,
|
||||
shared_tc_store: SharedPool,
|
||||
scheduler: PusScheduler,
|
||||
) -> Self {
|
||||
impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerInterface>
|
||||
PusService11SchedHandler<TcInMemConverter, Scheduler>
|
||||
{
|
||||
pub fn new(service_helper: PusServiceHelper<TcInMemConverter>, scheduler: Scheduler) -> Self {
|
||||
Self {
|
||||
psb: PusServiceHandler::new(
|
||||
tc_receiver,
|
||||
tm_sender,
|
||||
tm_apid,
|
||||
verification_handler,
|
||||
tc_in_mem_converter,
|
||||
),
|
||||
shared_tc_store,
|
||||
service_helper,
|
||||
scheduler,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scheduler_mut(&mut self) -> &mut PusScheduler {
|
||||
pub fn scheduler_mut(&mut self) -> &mut Scheduler {
|
||||
&mut self.scheduler
|
||||
}
|
||||
|
||||
pub fn scheduler(&self) -> &PusScheduler {
|
||||
pub fn scheduler(&self) -> &Scheduler {
|
||||
&self.scheduler
|
||||
}
|
||||
|
||||
pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
|
||||
let possible_packet = self.psb.retrieve_and_accept_next_packet()?;
|
||||
pub fn handle_one_tc(
|
||||
&mut self,
|
||||
sched_tc_pool: &mut (impl PoolProviderMemInPlace + ?Sized),
|
||||
) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
|
||||
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
|
||||
if possible_packet.is_none() {
|
||||
return Ok(PusPacketHandlerResult::Empty);
|
||||
}
|
||||
let ecss_tc_and_token = possible_packet.unwrap();
|
||||
let tc = self
|
||||
.psb
|
||||
.service_helper
|
||||
.tc_in_mem_converter
|
||||
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token)?;
|
||||
let subservice = tc.subservice();
|
||||
let std_service = scheduling::Subservice::try_from(subservice);
|
||||
if std_service.is_err() {
|
||||
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
|
||||
let subservice = PusPacket::subservice(&tc);
|
||||
let standard_subservice = scheduling::Subservice::try_from(subservice);
|
||||
if standard_subservice.is_err() {
|
||||
return Ok(PusPacketHandlerResult::CustomSubservice(
|
||||
tc.subservice(),
|
||||
subservice,
|
||||
ecss_tc_and_token.token,
|
||||
));
|
||||
}
|
||||
let mut partial_error = None;
|
||||
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
|
||||
match std_service.unwrap() {
|
||||
match standard_subservice.unwrap() {
|
||||
scheduling::Subservice::TcEnableScheduling => {
|
||||
let start_token = self
|
||||
.psb
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
@ -85,19 +75,21 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService11SchedHandler<TcInMemCon
|
||||
|
||||
self.scheduler.enable();
|
||||
if self.scheduler.is_enabled() {
|
||||
self.psb
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.completion_success(start_token, Some(&time_stamp))
|
||||
.expect("Error sending completion success");
|
||||
} else {
|
||||
panic!("Failed to enable scheduler");
|
||||
return Err(PusPacketHandlingError::Other(
|
||||
"failed to enabled scheduler".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
scheduling::Subservice::TcDisableScheduling => {
|
||||
let start_token = self
|
||||
.psb
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
@ -106,32 +98,32 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService11SchedHandler<TcInMemCon
|
||||
|
||||
self.scheduler.disable();
|
||||
if !self.scheduler.is_enabled() {
|
||||
self.psb
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.completion_success(start_token, Some(&time_stamp))
|
||||
.expect("Error sending completion success");
|
||||
} else {
|
||||
panic!("Failed to disable scheduler");
|
||||
return Err(PusPacketHandlingError::Other(
|
||||
"failed to disable scheduler".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
scheduling::Subservice::TcResetScheduling => {
|
||||
let start_token = self
|
||||
.psb
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
|
||||
.expect("Error sending start success");
|
||||
|
||||
let mut pool = self.shared_tc_store.write().expect("Locking pool failed");
|
||||
|
||||
self.scheduler
|
||||
.reset(pool.as_mut())
|
||||
.reset(sched_tc_pool)
|
||||
.expect("Error resetting TC Pool");
|
||||
|
||||
self.psb
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
@ -140,19 +132,19 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService11SchedHandler<TcInMemCon
|
||||
}
|
||||
scheduling::Subservice::TcInsertActivity => {
|
||||
let start_token = self
|
||||
.psb
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
|
||||
.expect("error sending start success");
|
||||
|
||||
let mut pool = self.shared_tc_store.write().expect("locking pool failed");
|
||||
// let mut pool = self.sched_tc_pool.write().expect("locking pool failed");
|
||||
self.scheduler
|
||||
.insert_wrapped_tc::<TimeProvider>(&tc, pool.as_mut())
|
||||
.insert_wrapped_tc::<TimeProvider>(&tc, sched_tc_pool)
|
||||
.expect("insertion of activity into pool failed");
|
||||
|
||||
self.psb
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
@ -162,7 +154,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService11SchedHandler<TcInMemCon
|
||||
_ => {
|
||||
// Treat unhandled standard subservices as custom subservices for now.
|
||||
return Ok(PusPacketHandlerResult::CustomSubservice(
|
||||
tc.subservice(),
|
||||
subservice,
|
||||
ecss_tc_and_token.token,
|
||||
));
|
||||
}
|
||||
@ -175,3 +167,189 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService11SchedHandler<TcInMemCon
|
||||
Ok(PusPacketHandlerResult::RequestHandled)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::pool::{StaticMemoryPool, StaticPoolConfig};
|
||||
use crate::pus::tests::TEST_APID;
|
||||
use crate::pus::{
|
||||
scheduler::{self, PusSchedulerInterface, TcInfo},
|
||||
tests::{PusServiceHandlerWithSharedStoreCommon, PusTestHarness},
|
||||
verification::{RequestId, TcStateAccepted, VerificationToken},
|
||||
EcssTcInSharedStoreConverter,
|
||||
};
|
||||
use alloc::collections::VecDeque;
|
||||
use delegate::delegate;
|
||||
use spacepackets::ecss::scheduling::Subservice;
|
||||
use spacepackets::ecss::tc::PusTcSecondaryHeader;
|
||||
use spacepackets::ecss::WritablePusPacket;
|
||||
use spacepackets::time::TimeWriter;
|
||||
use spacepackets::SpHeader;
|
||||
use spacepackets::{
|
||||
ecss::{tc::PusTcCreator, tm::PusTmReader},
|
||||
time::cds,
|
||||
};
|
||||
|
||||
use super::PusService11SchedHandler;
|
||||
|
||||
struct Pus11HandlerWithStoreTester {
|
||||
common: PusServiceHandlerWithSharedStoreCommon,
|
||||
handler: PusService11SchedHandler<EcssTcInSharedStoreConverter, TestScheduler>,
|
||||
sched_tc_pool: StaticMemoryPool,
|
||||
}
|
||||
|
||||
impl Pus11HandlerWithStoreTester {
|
||||
pub fn new() -> Self {
|
||||
let test_scheduler = TestScheduler::default();
|
||||
let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)]);
|
||||
let sched_tc_pool = StaticMemoryPool::new(pool_cfg.clone());
|
||||
let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new();
|
||||
Self {
|
||||
common,
|
||||
handler: PusService11SchedHandler::new(srv_handler, test_scheduler),
|
||||
sched_tc_pool,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PusTestHarness for Pus11HandlerWithStoreTester {
|
||||
delegate! {
|
||||
to self.common {
|
||||
fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
|
||||
fn read_next_tm(&mut self) -> PusTmReader<'_>;
|
||||
fn check_no_tm_available(&self) -> bool;
|
||||
fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TestScheduler {
|
||||
reset_count: u32,
|
||||
enabled: bool,
|
||||
enabled_count: u32,
|
||||
disabled_count: u32,
|
||||
inserted_tcs: VecDeque<TcInfo>,
|
||||
}
|
||||
|
||||
impl PusSchedulerInterface for TestScheduler {
|
||||
type TimeProvider = cds::TimeProvider;
|
||||
|
||||
fn reset(
|
||||
&mut self,
|
||||
_store: &mut (impl crate::pool::PoolProviderMemInPlace + ?Sized),
|
||||
) -> Result<(), crate::pool::StoreError> {
|
||||
self.reset_count += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_enabled(&self) -> bool {
|
||||
self.enabled
|
||||
}
|
||||
|
||||
fn enable(&mut self) {
|
||||
self.enabled_count += 1;
|
||||
self.enabled = true;
|
||||
}
|
||||
|
||||
fn disable(&mut self) {
|
||||
self.disabled_count += 1;
|
||||
self.enabled = false;
|
||||
}
|
||||
|
||||
fn insert_unwrapped_and_stored_tc(
|
||||
&mut self,
|
||||
_time_stamp: spacepackets::time::UnixTimestamp,
|
||||
info: crate::pus::scheduler::TcInfo,
|
||||
) -> Result<(), crate::pus::scheduler::ScheduleError> {
|
||||
self.inserted_tcs.push_back(info);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn generic_subservice_send(
|
||||
test_harness: &mut Pus11HandlerWithStoreTester,
|
||||
subservice: Subservice,
|
||||
) {
|
||||
let mut reply_header = SpHeader::tm_unseg(TEST_APID, 0, 0).unwrap();
|
||||
let tc_header = PusTcSecondaryHeader::new_simple(11, subservice as u8);
|
||||
let enable_scheduling = PusTcCreator::new(&mut reply_header, tc_header, &[0; 7], true);
|
||||
let token = test_harness.send_tc(&enable_scheduling);
|
||||
|
||||
let request_id = token.req_id();
|
||||
test_harness
|
||||
.handler
|
||||
.handle_one_tc(&mut test_harness.sched_tc_pool)
|
||||
.unwrap();
|
||||
test_harness.check_next_verification_tm(1, request_id);
|
||||
test_harness.check_next_verification_tm(3, request_id);
|
||||
test_harness.check_next_verification_tm(7, request_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scheduling_enabling_tc() {
|
||||
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
||||
test_harness.handler.scheduler_mut().disable();
|
||||
assert!(!test_harness.handler.scheduler().is_enabled());
|
||||
generic_subservice_send(&mut test_harness, Subservice::TcEnableScheduling);
|
||||
assert!(test_harness.handler.scheduler().is_enabled());
|
||||
assert_eq!(test_harness.handler.scheduler().enabled_count, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scheduling_disabling_tc() {
|
||||
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
||||
test_harness.handler.scheduler_mut().enable();
|
||||
assert!(test_harness.handler.scheduler().is_enabled());
|
||||
generic_subservice_send(&mut test_harness, Subservice::TcDisableScheduling);
|
||||
assert!(!test_harness.handler.scheduler().is_enabled());
|
||||
assert_eq!(test_harness.handler.scheduler().disabled_count, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reset_scheduler_tc() {
|
||||
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
||||
generic_subservice_send(&mut test_harness, Subservice::TcResetScheduling);
|
||||
assert_eq!(test_harness.handler.scheduler().reset_count, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_activity_tc() {
|
||||
let mut test_harness = Pus11HandlerWithStoreTester::new();
|
||||
let mut reply_header = SpHeader::tm_unseg(TEST_APID, 0, 0).unwrap();
|
||||
let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||
let ping_tc = PusTcCreator::new(&mut reply_header, sec_header, &[], true);
|
||||
let req_id_ping_tc = scheduler::RequestId::from_tc(&ping_tc);
|
||||
let stamper = cds::TimeProvider::from_now_with_u16_days().expect("time provider failed");
|
||||
let mut sched_app_data: [u8; 64] = [0; 64];
|
||||
let mut written_len = stamper.write_to_bytes(&mut sched_app_data).unwrap();
|
||||
let ping_raw = ping_tc.to_vec().expect("generating raw tc failed");
|
||||
sched_app_data[written_len..written_len + ping_raw.len()].copy_from_slice(&ping_raw);
|
||||
written_len += ping_raw.len();
|
||||
reply_header = SpHeader::tm_unseg(TEST_APID, 1, 0).unwrap();
|
||||
sec_header = PusTcSecondaryHeader::new_simple(11, Subservice::TcInsertActivity as u8);
|
||||
let enable_scheduling = PusTcCreator::new(
|
||||
&mut reply_header,
|
||||
sec_header,
|
||||
&sched_app_data[..written_len],
|
||||
true,
|
||||
);
|
||||
let token = test_harness.send_tc(&enable_scheduling);
|
||||
|
||||
let request_id = token.req_id();
|
||||
test_harness
|
||||
.handler
|
||||
.handle_one_tc(&mut test_harness.sched_tc_pool)
|
||||
.unwrap();
|
||||
test_harness.check_next_verification_tm(1, request_id);
|
||||
test_harness.check_next_verification_tm(3, request_id);
|
||||
test_harness.check_next_verification_tm(7, request_id);
|
||||
let tc_info = test_harness
|
||||
.handler
|
||||
.scheduler_mut()
|
||||
.inserted_tcs
|
||||
.pop_front()
|
||||
.unwrap();
|
||||
assert_eq!(tc_info.request_id(), req_id_ping_tc);
|
||||
}
|
||||
}
|
||||
|
@ -1,50 +1,33 @@
|
||||
use crate::pus::{
|
||||
EcssTcReceiver, EcssTmSender, PartialPusHandlingError, PusPacketHandlerResult,
|
||||
PusPacketHandlingError, PusTmWrapper,
|
||||
PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError, PusTmWrapper,
|
||||
};
|
||||
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
|
||||
use spacepackets::ecss::PusPacket;
|
||||
use spacepackets::SpHeader;
|
||||
use std::boxed::Box;
|
||||
|
||||
use super::verification::VerificationReporterWithSender;
|
||||
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHandler};
|
||||
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHelper};
|
||||
|
||||
/// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets.
|
||||
/// This handler only processes ping requests and generates a ping reply for them accordingly.
|
||||
pub struct PusService17TestHandler<TcInMemConverter: EcssTcInMemConverter> {
|
||||
pub psb: PusServiceHandler<TcInMemConverter>,
|
||||
pub service_helper: PusServiceHelper<TcInMemConverter>,
|
||||
}
|
||||
|
||||
impl<TcInMemConverter: EcssTcInMemConverter> PusService17TestHandler<TcInMemConverter> {
|
||||
pub fn new(
|
||||
tc_receiver: Box<dyn EcssTcReceiver>,
|
||||
tm_sender: Box<dyn EcssTmSender>,
|
||||
tm_apid: u16,
|
||||
verification_handler: VerificationReporterWithSender,
|
||||
tc_in_mem_converter: TcInMemConverter,
|
||||
) -> Self {
|
||||
Self {
|
||||
psb: PusServiceHandler::new(
|
||||
tc_receiver,
|
||||
tm_sender,
|
||||
tm_apid,
|
||||
verification_handler,
|
||||
tc_in_mem_converter,
|
||||
),
|
||||
}
|
||||
pub fn new(service_helper: PusServiceHelper<TcInMemConverter>) -> Self {
|
||||
Self { service_helper }
|
||||
}
|
||||
|
||||
pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
|
||||
let possible_packet = self.psb.retrieve_and_accept_next_packet()?;
|
||||
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
|
||||
if possible_packet.is_none() {
|
||||
return Ok(PusPacketHandlerResult::Empty);
|
||||
}
|
||||
let ecss_tc_and_token = possible_packet.unwrap();
|
||||
let tc = self
|
||||
.psb
|
||||
.service_helper
|
||||
.tc_in_mem_converter
|
||||
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token)?;
|
||||
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
|
||||
if tc.service() != 17 {
|
||||
return Err(PusPacketHandlingError::WrongService(tc.service()));
|
||||
}
|
||||
@ -52,7 +35,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService17TestHandler<TcInMemConv
|
||||
let mut partial_error = None;
|
||||
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
|
||||
let result = self
|
||||
.psb
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
@ -65,11 +48,12 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService17TestHandler<TcInMemConv
|
||||
None
|
||||
};
|
||||
// Sequence count will be handled centrally in TM funnel.
|
||||
let mut reply_header = SpHeader::tm_unseg(self.psb.common.tm_apid, 0, 0).unwrap();
|
||||
let mut reply_header =
|
||||
SpHeader::tm_unseg(self.service_helper.common.tm_apid, 0, 0).unwrap();
|
||||
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, &time_stamp);
|
||||
let ping_reply = PusTmCreator::new(&mut reply_header, tc_header, &[], true);
|
||||
let result = self
|
||||
.psb
|
||||
.service_helper
|
||||
.common
|
||||
.tm_sender
|
||||
.send_tm(PusTmWrapper::Direct(ping_reply))
|
||||
@ -80,7 +64,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService17TestHandler<TcInMemConv
|
||||
|
||||
if let Some(start_token) = start_token {
|
||||
if self
|
||||
.psb
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
@ -107,115 +91,182 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService17TestHandler<TcInMemConv
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::pool::{LocalPool, PoolCfg, SharedPool};
|
||||
use crate::pus::test::PusService17TestHandler;
|
||||
use crate::pus::verification::{
|
||||
RequestId, VerificationReporterCfg, VerificationReporterWithSender,
|
||||
use crate::pus::tests::{
|
||||
PusServiceHandlerWithSharedStoreCommon, PusServiceHandlerWithVecCommon, PusTestHarness,
|
||||
SimplePusPacketHandler, TEST_APID,
|
||||
};
|
||||
use crate::pus::verification::RequestId;
|
||||
use crate::pus::verification::{TcStateAccepted, VerificationToken};
|
||||
use crate::pus::{
|
||||
EcssTcAndToken, EcssTcInStoreConverter, MpscTcInStoreReceiver, MpscTmInStoreSender,
|
||||
EcssTcInSharedStoreConverter, EcssTcInVecConverter, PusPacketHandlerResult,
|
||||
PusPacketHandlingError,
|
||||
};
|
||||
use crate::tmtc::tm_helper::SharedTmStore;
|
||||
use delegate::delegate;
|
||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
|
||||
use spacepackets::ecss::tm::PusTmReader;
|
||||
use spacepackets::ecss::{PusPacket, WritablePusPacket};
|
||||
use spacepackets::ecss::PusPacket;
|
||||
use spacepackets::{SequenceFlags, SpHeader};
|
||||
use std::boxed::Box;
|
||||
use std::sync::{mpsc, RwLock};
|
||||
use std::vec;
|
||||
|
||||
const TEST_APID: u16 = 0x101;
|
||||
use super::PusService17TestHandler;
|
||||
|
||||
#[test]
|
||||
fn test_basic_ping_processing() {
|
||||
let mut pus_buf: [u8; 64] = [0; 64];
|
||||
let pool_cfg = PoolCfg::new(vec![(16, 16), (8, 32), (4, 64)]);
|
||||
let tc_pool = LocalPool::new(pool_cfg.clone());
|
||||
let tm_pool = LocalPool::new(pool_cfg);
|
||||
let tc_pool_shared = SharedPool::new(RwLock::new(Box::new(tc_pool)));
|
||||
let shared_tm_store = SharedTmStore::new(Box::new(tm_pool));
|
||||
let tm_pool_shared = shared_tm_store.clone_backing_pool();
|
||||
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
|
||||
let (tm_tx, tm_rx) = mpsc::channel();
|
||||
let verif_sender =
|
||||
MpscTmInStoreSender::new(0, "verif_sender", shared_tm_store.clone(), tm_tx.clone());
|
||||
let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
|
||||
let mut verification_handler =
|
||||
VerificationReporterWithSender::new(&verif_cfg, Box::new(verif_sender));
|
||||
let test_srv_tm_sender = MpscTmInStoreSender::new(0, "TEST_SENDER", shared_tm_store, tm_tx);
|
||||
let test_srv_tc_receiver = MpscTcInStoreReceiver::new(0, "TEST_RECEIVER", test_srv_tc_rx);
|
||||
let in_store_converter = EcssTcInStoreConverter::new(tc_pool_shared.clone(), 2048);
|
||||
let mut pus_17_handler = PusService17TestHandler::new(
|
||||
Box::new(test_srv_tc_receiver),
|
||||
Box::new(test_srv_tm_sender),
|
||||
TEST_APID,
|
||||
verification_handler.clone(),
|
||||
in_store_converter,
|
||||
struct Pus17HandlerWithStoreTester {
|
||||
common: PusServiceHandlerWithSharedStoreCommon,
|
||||
handler: PusService17TestHandler<EcssTcInSharedStoreConverter>,
|
||||
}
|
||||
|
||||
impl Pus17HandlerWithStoreTester {
|
||||
pub fn new() -> Self {
|
||||
let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new();
|
||||
let pus_17_handler = PusService17TestHandler::new(srv_handler);
|
||||
Self {
|
||||
common,
|
||||
handler: pus_17_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PusTestHarness for Pus17HandlerWithStoreTester {
|
||||
delegate! {
|
||||
to self.common {
|
||||
fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
|
||||
fn read_next_tm(&mut self) -> PusTmReader<'_>;
|
||||
fn check_no_tm_available(&self) -> bool;
|
||||
fn check_next_verification_tm(
|
||||
&self,
|
||||
subservice: u8,
|
||||
expected_request_id: RequestId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SimplePusPacketHandler for Pus17HandlerWithStoreTester {
|
||||
delegate! {
|
||||
to self.handler {
|
||||
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Pus17HandlerWithVecTester {
|
||||
common: PusServiceHandlerWithVecCommon,
|
||||
handler: PusService17TestHandler<EcssTcInVecConverter>,
|
||||
}
|
||||
|
||||
impl Pus17HandlerWithVecTester {
|
||||
pub fn new() -> Self {
|
||||
let (common, srv_handler) = PusServiceHandlerWithVecCommon::new();
|
||||
Self {
|
||||
common,
|
||||
handler: PusService17TestHandler::new(srv_handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PusTestHarness for Pus17HandlerWithVecTester {
|
||||
delegate! {
|
||||
to self.common {
|
||||
fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
|
||||
fn read_next_tm(&mut self) -> PusTmReader<'_>;
|
||||
fn check_no_tm_available(&self) -> bool;
|
||||
fn check_next_verification_tm(
|
||||
&self,
|
||||
subservice: u8,
|
||||
expected_request_id: RequestId,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SimplePusPacketHandler for Pus17HandlerWithVecTester {
|
||||
delegate! {
|
||||
to self.handler {
|
||||
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ping_test(test_harness: &mut (impl PusTestHarness + SimplePusPacketHandler)) {
|
||||
// Create a ping TC, verify acceptance.
|
||||
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true);
|
||||
let token = verification_handler.add_tc(&ping_tc);
|
||||
let token = verification_handler
|
||||
.acceptance_success(token, None)
|
||||
.unwrap();
|
||||
let tc_size = ping_tc.write_to_bytes(&mut pus_buf).unwrap();
|
||||
let mut tc_pool = tc_pool_shared.write().unwrap();
|
||||
let addr = tc_pool.add(&pus_buf[..tc_size]).unwrap();
|
||||
drop(tc_pool);
|
||||
// Send accepted TC to test service handler.
|
||||
test_srv_tc_tx
|
||||
.send(EcssTcAndToken::new(addr, token))
|
||||
.unwrap();
|
||||
let result = pus_17_handler.handle_one_tc();
|
||||
let token = test_harness.send_tc(&ping_tc);
|
||||
let request_id = token.req_id();
|
||||
let result = test_harness.handle_one_tc();
|
||||
assert!(result.is_ok());
|
||||
// We should see 4 replies in the TM queue now: Acceptance TM, Start TM, ping reply and
|
||||
// Completion TM
|
||||
let mut next_msg = tm_rx.try_recv();
|
||||
assert!(next_msg.is_ok());
|
||||
let mut tm_addr = next_msg.unwrap();
|
||||
let tm_pool = tm_pool_shared.read().unwrap();
|
||||
let tm_raw = tm_pool.read(&tm_addr).unwrap();
|
||||
let (tm, _) = PusTmReader::new(tm_raw, 0).unwrap();
|
||||
assert_eq!(tm.service(), 1);
|
||||
assert_eq!(tm.subservice(), 1);
|
||||
let req_id = RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
|
||||
assert_eq!(req_id, token.req_id());
|
||||
|
||||
// Acceptance TM
|
||||
next_msg = tm_rx.try_recv();
|
||||
assert!(next_msg.is_ok());
|
||||
tm_addr = next_msg.unwrap();
|
||||
let tm_raw = tm_pool.read(&tm_addr).unwrap();
|
||||
// Is generated with CDS short timestamp.
|
||||
let (tm, _) = PusTmReader::new(tm_raw, 7).unwrap();
|
||||
assert_eq!(tm.service(), 1);
|
||||
assert_eq!(tm.subservice(), 3);
|
||||
let req_id = RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
|
||||
assert_eq!(req_id, token.req_id());
|
||||
test_harness.check_next_verification_tm(1, request_id);
|
||||
|
||||
// Start TM
|
||||
test_harness.check_next_verification_tm(3, request_id);
|
||||
|
||||
// Ping reply
|
||||
next_msg = tm_rx.try_recv();
|
||||
assert!(next_msg.is_ok());
|
||||
tm_addr = next_msg.unwrap();
|
||||
let tm_raw = tm_pool.read(&tm_addr).unwrap();
|
||||
// Is generated with CDS short timestamp.
|
||||
let (tm, _) = PusTmReader::new(tm_raw, 7).unwrap();
|
||||
let tm = test_harness.read_next_tm();
|
||||
assert_eq!(tm.service(), 17);
|
||||
assert_eq!(tm.subservice(), 2);
|
||||
assert!(tm.user_data().is_empty());
|
||||
|
||||
// TM completion
|
||||
next_msg = tm_rx.try_recv();
|
||||
assert!(next_msg.is_ok());
|
||||
tm_addr = next_msg.unwrap();
|
||||
let tm_raw = tm_pool.read(&tm_addr).unwrap();
|
||||
// Is generated with CDS short timestamp.
|
||||
let (tm, _) = PusTmReader::new(tm_raw, 7).unwrap();
|
||||
assert_eq!(tm.service(), 1);
|
||||
assert_eq!(tm.subservice(), 7);
|
||||
let req_id = RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
|
||||
assert_eq!(req_id, token.req_id());
|
||||
test_harness.check_next_verification_tm(7, request_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_ping_processing_using_store() {
|
||||
let mut test_harness = Pus17HandlerWithStoreTester::new();
|
||||
ping_test(&mut test_harness);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_ping_processing_using_vec() {
|
||||
let mut test_harness = Pus17HandlerWithVecTester::new();
|
||||
ping_test(&mut test_harness);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_tc_queue() {
|
||||
let mut test_harness = Pus17HandlerWithStoreTester::new();
|
||||
let result = test_harness.handle_one_tc();
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
if let PusPacketHandlerResult::Empty = result {
|
||||
} else {
|
||||
panic!("unexpected result type {result:?}")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sending_unsupported_service() {
|
||||
let mut test_harness = Pus17HandlerWithStoreTester::new();
|
||||
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(3, 1);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true);
|
||||
test_harness.send_tc(&ping_tc);
|
||||
let result = test_harness.handle_one_tc();
|
||||
assert!(result.is_err());
|
||||
let error = result.unwrap_err();
|
||||
if let PusPacketHandlingError::WrongService(num) = error {
|
||||
assert_eq!(num, 3);
|
||||
} else {
|
||||
panic!("unexpected error type {error}")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sending_custom_subservice() {
|
||||
let mut test_harness = Pus17HandlerWithStoreTester::new();
|
||||
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
|
||||
let sec_header = PusTcSecondaryHeader::new_simple(17, 200);
|
||||
let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true);
|
||||
test_harness.send_tc(&ping_tc);
|
||||
let result = test_harness.handle_one_tc();
|
||||
assert!(result.is_ok());
|
||||
let result = result.unwrap();
|
||||
if let PusPacketHandlerResult::CustomSubservice(subservice, _) = result {
|
||||
assert_eq!(subservice, 200);
|
||||
} else {
|
||||
panic!("unexpected result type {result:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
//! ```
|
||||
//! use std::sync::{Arc, mpsc, RwLock};
|
||||
//! use std::time::Duration;
|
||||
//! use satrs_core::pool::{LocalPool, PoolCfg, PoolProvider, SharedPool};
|
||||
//! use satrs_core::pool::{PoolProviderMemInPlaceWithGuards, StaticMemoryPool, StaticPoolConfig};
|
||||
//! use satrs_core::pus::verification::{VerificationReporterCfg, VerificationReporterWithSender};
|
||||
//! use satrs_core::seq_count::SeqCountProviderSimple;
|
||||
//! use satrs_core::pus::MpscTmInStoreSender;
|
||||
@ -28,9 +28,9 @@
|
||||
//! const EMPTY_STAMP: [u8; 7] = [0; 7];
|
||||
//! const TEST_APID: u16 = 0x02;
|
||||
//!
|
||||
//! let pool_cfg = PoolCfg::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]);
|
||||
//! let tm_pool = LocalPool::new(pool_cfg.clone());
|
||||
//! let shared_tm_store = SharedTmStore::new(Box::new(tm_pool));
|
||||
//! let pool_cfg = StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]);
|
||||
//! let tm_pool = StaticMemoryPool::new(pool_cfg.clone());
|
||||
//! let shared_tm_store = SharedTmStore::new(tm_pool);
|
||||
//! let tm_store = shared_tm_store.clone_backing_pool();
|
||||
//! let (verif_tx, verif_rx) = mpsc::channel();
|
||||
//! let sender = MpscTmInStoreSender::new(0, "Test Sender", shared_tm_store, verif_tx);
|
||||
@ -208,6 +208,8 @@ impl WasAtLeastAccepted for TcStateAccepted {}
|
||||
impl WasAtLeastAccepted for TcStateStarted {}
|
||||
impl WasAtLeastAccepted for TcStateCompleted {}
|
||||
|
||||
/// Token wrapper to model all possible verification tokens. These tokens are used to
|
||||
/// enforce the correct order for the verification steps when doing verification reporting.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum TcStateToken {
|
||||
None(VerificationToken<TcStateNone>),
|
||||
@ -1323,7 +1325,7 @@ mod std_mod {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::pool::{LocalPool, PoolCfg};
|
||||
use crate::pool::{PoolProviderMemInPlaceWithGuards, StaticMemoryPool, StaticPoolConfig};
|
||||
use crate::pus::tests::CommonTmInfo;
|
||||
use crate::pus::verification::{
|
||||
EcssTmSenderCore, EcssTmtcError, FailParams, FailParamsWithStep, RequestId, TcStateNone,
|
||||
@ -1447,12 +1449,11 @@ mod tests {
|
||||
fn base_init(api_sel: bool) -> (TestBase<'static>, VerificationToken<TcStateNone>) {
|
||||
let mut reporter = base_reporter();
|
||||
let (tc, req_id) = base_tc_init(None);
|
||||
let init_tok;
|
||||
if api_sel {
|
||||
init_tok = reporter.add_tc_with_req_id(req_id);
|
||||
let init_tok = if api_sel {
|
||||
reporter.add_tc_with_req_id(req_id)
|
||||
} else {
|
||||
init_tok = reporter.add_tc(&tc);
|
||||
}
|
||||
reporter.add_tc(&tc)
|
||||
};
|
||||
(TestBase { vr: reporter, tc }, init_tok)
|
||||
}
|
||||
|
||||
@ -1475,7 +1476,7 @@ mod tests {
|
||||
time_stamp: EMPTY_STAMP,
|
||||
},
|
||||
additional_data: None,
|
||||
req_id: req_id.clone(),
|
||||
req_id: *req_id,
|
||||
};
|
||||
let mut service_queue = sender.service_queue.borrow_mut();
|
||||
assert_eq!(service_queue.len(), 1);
|
||||
@ -1485,9 +1486,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_mpsc_verif_send_sync() {
|
||||
let pool = LocalPool::new(PoolCfg::new(vec![(8, 8)]));
|
||||
let tm_store = Box::new(pool);
|
||||
let shared_tm_store = SharedTmStore::new(tm_store);
|
||||
let pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(8, 8)]));
|
||||
let shared_tm_store = SharedTmStore::new(pool);
|
||||
let (tx, _) = mpsc::channel();
|
||||
let mpsc_verif_sender = MpscTmInStoreSender::new(0, "verif_sender", shared_tm_store, tx);
|
||||
is_send(&mpsc_verif_sender);
|
||||
@ -1505,7 +1505,7 @@ mod tests {
|
||||
fn test_basic_acceptance_success() {
|
||||
let (b, tok) = base_init(false);
|
||||
let mut sender = TestSender::default();
|
||||
b.vr.acceptance_success(tok, &mut sender, Some(&EMPTY_STAMP))
|
||||
b.vr.acceptance_success(tok, &sender, Some(&EMPTY_STAMP))
|
||||
.expect("Sending acceptance success failed");
|
||||
acceptance_check(&mut sender, &tok.req_id);
|
||||
}
|
||||
@ -1605,7 +1605,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_basic_acceptance_failure_with_fail_data() {
|
||||
let (b, tok) = base_init(false);
|
||||
let mut sender = TestSender::default();
|
||||
let sender = TestSender::default();
|
||||
let fail_code = EcssEnumU8::new(10);
|
||||
let fail_data = EcssEnumU32::new(12);
|
||||
let mut fail_data_raw = [0; 4];
|
||||
@ -1615,7 +1615,7 @@ mod tests {
|
||||
&fail_code,
|
||||
Some(fail_data_raw.as_slice()),
|
||||
);
|
||||
b.vr.acceptance_failure(tok, &mut sender, fail_params)
|
||||
b.vr.acceptance_failure(tok, &sender, fail_params)
|
||||
.expect("Sending acceptance success failed");
|
||||
let cmp_info = TmInfo {
|
||||
common: CommonTmInfo {
|
||||
@ -1784,8 +1784,7 @@ mod tests {
|
||||
.rep()
|
||||
.start_success(accepted_token, &mut sender, Some(&[0, 1, 0, 1, 0, 1, 0]))
|
||||
.expect("Sending start success failed");
|
||||
let mut empty = b
|
||||
.rep()
|
||||
b.rep()
|
||||
.step_success(
|
||||
&started_token,
|
||||
&mut sender,
|
||||
@ -1793,8 +1792,6 @@ mod tests {
|
||||
EcssEnumU8::new(0),
|
||||
)
|
||||
.expect("Sending step 0 success failed");
|
||||
assert_eq!(empty, ());
|
||||
empty =
|
||||
b.vr.step_success(
|
||||
&started_token,
|
||||
&mut sender,
|
||||
@ -1802,7 +1799,6 @@ mod tests {
|
||||
EcssEnumU8::new(1),
|
||||
)
|
||||
.expect("Sending step 1 success failed");
|
||||
assert_eq!(empty, ());
|
||||
assert_eq!(sender.service_queue.borrow().len(), 4);
|
||||
step_success_check(&mut sender, tok.req_id);
|
||||
}
|
||||
@ -1818,16 +1814,12 @@ mod tests {
|
||||
.helper
|
||||
.start_success(accepted_token, Some(&[0, 1, 0, 1, 0, 1, 0]))
|
||||
.expect("Sending start success failed");
|
||||
let mut empty = b
|
||||
.helper
|
||||
b.helper
|
||||
.step_success(&started_token, Some(&EMPTY_STAMP), EcssEnumU8::new(0))
|
||||
.expect("Sending step 0 success failed");
|
||||
assert_eq!(empty, ());
|
||||
empty = b
|
||||
.helper
|
||||
b.helper
|
||||
.step_success(&started_token, Some(&EMPTY_STAMP), EcssEnumU8::new(1))
|
||||
.expect("Sending step 1 success failed");
|
||||
assert_eq!(empty, ());
|
||||
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
|
||||
assert_eq!(sender.service_queue.borrow().len(), 4);
|
||||
step_success_check(sender, tok.req_id);
|
||||
@ -2122,10 +2114,8 @@ mod tests {
|
||||
let started_token =
|
||||
b.vr.start_success(accepted_token, &mut sender, Some(&[0, 1, 0, 1, 0, 1, 0]))
|
||||
.expect("Sending start success failed");
|
||||
let empty =
|
||||
b.vr.completion_success(started_token, &mut sender, Some(&EMPTY_STAMP))
|
||||
.expect("Sending completion success failed");
|
||||
assert_eq!(empty, ());
|
||||
completion_success_check(&mut sender, tok.req_id);
|
||||
}
|
||||
|
||||
@ -2140,11 +2130,9 @@ mod tests {
|
||||
.helper
|
||||
.start_success(accepted_token, Some(&[0, 1, 0, 1, 0, 1, 0]))
|
||||
.expect("Sending start success failed");
|
||||
let empty = b
|
||||
.helper
|
||||
b.helper
|
||||
.completion_success(started_token, Some(&EMPTY_STAMP))
|
||||
.expect("Sending completion success failed");
|
||||
assert_eq!(empty, ());
|
||||
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
|
||||
completion_success_check(sender, tok.req_id);
|
||||
}
|
||||
@ -2152,8 +2140,8 @@ mod tests {
|
||||
#[test]
|
||||
// TODO: maybe a bit more extensive testing, all I have time for right now
|
||||
fn test_seq_count_increment() {
|
||||
let pool_cfg = PoolCfg::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]);
|
||||
let tm_pool = Box::new(LocalPool::new(pool_cfg.clone()));
|
||||
let pool_cfg = StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]);
|
||||
let tm_pool = StaticMemoryPool::new(pool_cfg.clone());
|
||||
let shared_tm_store = SharedTmStore::new(tm_pool);
|
||||
let shared_tm_pool = shared_tm_store.clone_backing_pool();
|
||||
let (verif_tx, verif_rx) = mpsc::channel();
|
||||
|
@ -8,7 +8,9 @@ pub use std_mod::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod std_mod {
|
||||
use crate::pool::{ShareablePoolProvider, SharedPool, StoreAddr};
|
||||
use crate::pool::{
|
||||
PoolProviderMemInPlace, SharedStaticMemoryPool, StaticMemoryPool, StoreAddr,
|
||||
};
|
||||
use crate::pus::EcssTmtcError;
|
||||
use spacepackets::ecss::tm::PusTmCreator;
|
||||
use spacepackets::ecss::WritablePusPacket;
|
||||
@ -16,22 +18,25 @@ pub mod std_mod {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SharedTmStore {
|
||||
pool: SharedPool,
|
||||
pub shared_pool: SharedStaticMemoryPool,
|
||||
}
|
||||
|
||||
impl SharedTmStore {
|
||||
pub fn new(backing_pool: ShareablePoolProvider) -> Self {
|
||||
pub fn new(shared_pool: StaticMemoryPool) -> Self {
|
||||
Self {
|
||||
pool: Arc::new(RwLock::new(backing_pool)),
|
||||
shared_pool: Arc::new(RwLock::new(shared_pool)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clone_backing_pool(&self) -> SharedPool {
|
||||
self.pool.clone()
|
||||
pub fn clone_backing_pool(&self) -> SharedStaticMemoryPool {
|
||||
self.shared_pool.clone()
|
||||
}
|
||||
|
||||
pub fn add_pus_tm(&self, pus_tm: &PusTmCreator) -> Result<StoreAddr, EcssTmtcError> {
|
||||
let mut pg = self.pool.write().map_err(|_| EcssTmtcError::StoreLock)?;
|
||||
let mut pg = self
|
||||
.shared_pool
|
||||
.write()
|
||||
.map_err(|_| EcssTmtcError::StoreLock)?;
|
||||
let (addr, buf) = pg.free_element(pus_tm.len_written())?;
|
||||
pus_tm
|
||||
.write_to_bytes(buf)
|
||||
@ -91,3 +96,33 @@ impl PusTmWithCdsShortHelper {
|
||||
PusTmCreator::new(&mut reply_header, tc_header, source_data, true)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use spacepackets::{ecss::PusPacket, time::cds::TimeProvider, CcsdsPacket};
|
||||
|
||||
use super::PusTmWithCdsShortHelper;
|
||||
|
||||
#[test]
|
||||
fn test_helper_with_stamper() {
|
||||
let mut pus_tm_helper = PusTmWithCdsShortHelper::new(0x123);
|
||||
let stamper = TimeProvider::new_with_u16_days(0, 0);
|
||||
let tm = pus_tm_helper.create_pus_tm_with_stamper(17, 1, &[1, 2, 3, 4], &stamper, 25);
|
||||
assert_eq!(tm.service(), 17);
|
||||
assert_eq!(tm.subservice(), 1);
|
||||
assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
|
||||
assert_eq!(tm.seq_count(), 25);
|
||||
assert_eq!(tm.timestamp(), [64, 0, 0, 0, 0, 0, 0])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_helper_from_now() {
|
||||
let mut pus_tm_helper = PusTmWithCdsShortHelper::new(0x123);
|
||||
let tm = pus_tm_helper.create_pus_tm_timestamp_now(17, 1, &[1, 2, 3, 4], 25);
|
||||
assert_eq!(tm.service(), 17);
|
||||
assert_eq!(tm.subservice(), 1);
|
||||
assert_eq!(tm.user_data(), &[1, 2, 3, 4]);
|
||||
assert_eq!(tm.seq_count(), 25);
|
||||
assert_eq!(tm.timestamp().len(), 7);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
use satrs_core::pool::{LocalPool, PoolCfg, PoolGuard, PoolProvider, StoreAddr};
|
||||
use satrs_core::pool::{
|
||||
PoolGuard, PoolProviderMemInPlace, StaticMemoryPool, StaticPoolConfig, StoreAddr,
|
||||
};
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
@ -9,8 +11,8 @@ const DUMMY_DATA: [u8; 4] = [0, 1, 2, 3];
|
||||
|
||||
#[test]
|
||||
fn threaded_usage() {
|
||||
let pool_cfg = PoolCfg::new(vec![(16, 6), (32, 3), (8, 12)]);
|
||||
let shared_pool = Arc::new(RwLock::new(LocalPool::new(pool_cfg)));
|
||||
let pool_cfg = StaticPoolConfig::new(vec![(16, 6), (32, 3), (8, 12)]);
|
||||
let shared_pool = Arc::new(RwLock::new(StaticMemoryPool::new(pool_cfg)));
|
||||
let shared_clone = shared_pool.clone();
|
||||
let (tx, rx): (Sender<StoreAddr>, Receiver<StoreAddr>) = mpsc::channel();
|
||||
let jh0 = thread::spawn(move || {
|
||||
|
@ -1,7 +1,7 @@
|
||||
#[cfg(feature = "crossbeam")]
|
||||
pub mod crossbeam_test {
|
||||
use hashbrown::HashMap;
|
||||
use satrs_core::pool::{LocalPool, PoolCfg, PoolProvider};
|
||||
use satrs_core::pool::{PoolProviderMemInPlace, StaticMemoryPool, StaticPoolConfig};
|
||||
use satrs_core::pus::verification::{
|
||||
FailParams, RequestId, VerificationReporterCfg, VerificationReporterWithSender,
|
||||
};
|
||||
@ -33,9 +33,9 @@ pub mod crossbeam_test {
|
||||
// each reporter have an own sequence count provider.
|
||||
let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
|
||||
// Shared pool object to store the verification PUS telemetry
|
||||
let pool_cfg = PoolCfg::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]);
|
||||
let shared_tm_store = SharedTmStore::new(Box::new(LocalPool::new(pool_cfg.clone())));
|
||||
let shared_tc_pool_0 = Arc::new(RwLock::new(LocalPool::new(pool_cfg)));
|
||||
let pool_cfg = StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]);
|
||||
let shared_tm_store = SharedTmStore::new(Box::new(StaticMemoryPool::new(pool_cfg.clone())));
|
||||
let shared_tc_pool_0 = Arc::new(RwLock::new(StaticMemoryPool::new(pool_cfg)));
|
||||
let shared_tc_pool_1 = shared_tc_pool_0.clone();
|
||||
let (tx, rx) = crossbeam_channel::bounded(10);
|
||||
let sender =
|
||||
|
@ -29,7 +29,7 @@ use satrs_core::event_man::{
|
||||
};
|
||||
use satrs_core::events::EventU32;
|
||||
use satrs_core::hk::HkRequest;
|
||||
use satrs_core::pool::{LocalPool, PoolCfg};
|
||||
use satrs_core::pool::{PoolProviderMemInPlace, StaticMemoryPool, StaticPoolConfig};
|
||||
use satrs_core::pus::event_man::{
|
||||
DefaultPusMgmtBackendProvider, EventReporter, EventRequest, EventRequestWithToken,
|
||||
PusEventDispatcher,
|
||||
@ -42,7 +42,9 @@ use satrs_core::pus::test::PusService17TestHandler;
|
||||
use satrs_core::pus::verification::{
|
||||
TcStateStarted, VerificationReporterCfg, VerificationReporterWithSender, VerificationToken,
|
||||
};
|
||||
use satrs_core::pus::{EcssTcInStoreConverter, MpscTcInStoreReceiver, MpscTmInStoreSender};
|
||||
use satrs_core::pus::{
|
||||
EcssTcInSharedStoreConverter, MpscTcReceiver, MpscTmInStoreSender, PusServiceHelper,
|
||||
};
|
||||
use satrs_core::seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProviderCore};
|
||||
use satrs_core::spacepackets::ecss::tm::{PusTmCreator, PusTmZeroCopyWriter};
|
||||
use satrs_core::spacepackets::{
|
||||
@ -66,7 +68,7 @@ use std::time::Duration;
|
||||
fn main() {
|
||||
setup_logger().expect("setting up logging with fern failed");
|
||||
println!("Running OBSW example");
|
||||
let tm_pool = LocalPool::new(PoolCfg::new(vec![
|
||||
let tm_pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![
|
||||
(30, 32),
|
||||
(15, 64),
|
||||
(15, 128),
|
||||
@ -74,9 +76,9 @@ fn main() {
|
||||
(15, 1024),
|
||||
(15, 2048),
|
||||
]));
|
||||
let shared_tm_store = SharedTmStore::new(Box::new(tm_pool));
|
||||
let shared_tm_store = SharedTmStore::new(tm_pool);
|
||||
let tm_store_event = shared_tm_store.clone();
|
||||
let tc_pool = LocalPool::new(PoolCfg::new(vec![
|
||||
let tc_pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![
|
||||
(30, 32),
|
||||
(15, 64),
|
||||
(15, 128),
|
||||
@ -85,8 +87,16 @@ fn main() {
|
||||
(15, 2048),
|
||||
]));
|
||||
let tc_store = TcStore {
|
||||
pool: Arc::new(RwLock::new(Box::new(tc_pool))),
|
||||
pool: Arc::new(RwLock::new(tc_pool)),
|
||||
};
|
||||
let sched_tc_pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![
|
||||
(30, 32),
|
||||
(15, 64),
|
||||
(15, 128),
|
||||
(15, 256),
|
||||
(15, 1024),
|
||||
(15, 2048),
|
||||
]));
|
||||
|
||||
let seq_count_provider = CcsdsSimpleSeqCountProvider::new();
|
||||
let mut msg_counter_map: HashMap<u8, u16> = HashMap::new();
|
||||
@ -172,18 +182,18 @@ fn main() {
|
||||
shared_tm_store.clone(),
|
||||
tm_funnel_tx.clone(),
|
||||
);
|
||||
let test_srv_receiver = MpscTcInStoreReceiver::new(
|
||||
let test_srv_receiver = MpscTcReceiver::new(
|
||||
TcReceiverId::PusTest as ChannelId,
|
||||
"PUS_17_TC_RECV",
|
||||
pus_test_rx,
|
||||
);
|
||||
let pus17_handler = PusService17TestHandler::new(
|
||||
let pus17_handler = PusService17TestHandler::new(PusServiceHelper::new(
|
||||
Box::new(test_srv_receiver),
|
||||
Box::new(test_srv_tm_sender),
|
||||
PUS_APID,
|
||||
verif_reporter.clone(),
|
||||
EcssTcInStoreConverter::new(tc_store.pool.clone(), 2048),
|
||||
);
|
||||
EcssTcInSharedStoreConverter::new(tc_store.pool.clone(), 2048),
|
||||
));
|
||||
let mut pus_17_wrapper = Service17CustomWrapper {
|
||||
pus17_handler,
|
||||
test_srv_event_sender,
|
||||
@ -195,7 +205,7 @@ fn main() {
|
||||
shared_tm_store.clone(),
|
||||
tm_funnel_tx.clone(),
|
||||
);
|
||||
let sched_srv_receiver = MpscTcInStoreReceiver::new(
|
||||
let sched_srv_receiver = MpscTcReceiver::new(
|
||||
TcReceiverId::PusSched as ChannelId,
|
||||
"PUS_11_TC_RECV",
|
||||
pus_sched_rx,
|
||||
@ -203,16 +213,18 @@ fn main() {
|
||||
let scheduler = PusScheduler::new_with_current_init_time(Duration::from_secs(5))
|
||||
.expect("Creating PUS Scheduler failed");
|
||||
let pus_11_handler = PusService11SchedHandler::new(
|
||||
PusServiceHelper::new(
|
||||
Box::new(sched_srv_receiver),
|
||||
Box::new(sched_srv_tm_sender),
|
||||
PUS_APID,
|
||||
verif_reporter.clone(),
|
||||
EcssTcInStoreConverter::new(tc_store.pool.clone(), 2048),
|
||||
tc_store.pool.clone(),
|
||||
EcssTcInSharedStoreConverter::new(tc_store.pool.clone(), 2048),
|
||||
),
|
||||
scheduler,
|
||||
);
|
||||
let mut pus_11_wrapper = Pus11Wrapper {
|
||||
pus_11_handler,
|
||||
sched_tc_pool,
|
||||
tc_source_wrapper,
|
||||
};
|
||||
|
||||
@ -222,17 +234,19 @@ fn main() {
|
||||
shared_tm_store.clone(),
|
||||
tm_funnel_tx.clone(),
|
||||
);
|
||||
let event_srv_receiver = MpscTcInStoreReceiver::new(
|
||||
let event_srv_receiver = MpscTcReceiver::new(
|
||||
TcReceiverId::PusEvent as ChannelId,
|
||||
"PUS_5_TC_RECV",
|
||||
pus_event_rx,
|
||||
);
|
||||
let pus_5_handler = PusService5EventHandler::new(
|
||||
PusServiceHelper::new(
|
||||
Box::new(event_srv_receiver),
|
||||
Box::new(event_srv_tm_sender),
|
||||
PUS_APID,
|
||||
verif_reporter.clone(),
|
||||
EcssTcInStoreConverter::new(tc_store.pool.clone(), 2048),
|
||||
EcssTcInSharedStoreConverter::new(tc_store.pool.clone(), 2048),
|
||||
),
|
||||
event_request_tx,
|
||||
);
|
||||
let mut pus_5_wrapper = Pus5Wrapper { pus_5_handler };
|
||||
@ -243,7 +257,7 @@ fn main() {
|
||||
shared_tm_store.clone(),
|
||||
tm_funnel_tx.clone(),
|
||||
);
|
||||
let action_srv_receiver = MpscTcInStoreReceiver::new(
|
||||
let action_srv_receiver = MpscTcReceiver::new(
|
||||
TcReceiverId::PusAction as ChannelId,
|
||||
"PUS_8_TC_RECV",
|
||||
pus_action_rx,
|
||||
@ -253,7 +267,7 @@ fn main() {
|
||||
Box::new(action_srv_tm_sender),
|
||||
PUS_APID,
|
||||
verif_reporter.clone(),
|
||||
EcssTcInStoreConverter::new(tc_store.pool.clone(), 2048),
|
||||
EcssTcInSharedStoreConverter::new(tc_store.pool.clone(), 2048),
|
||||
request_map.clone(),
|
||||
);
|
||||
let mut pus_8_wrapper = Pus8Wrapper { pus_8_handler };
|
||||
@ -265,13 +279,13 @@ fn main() {
|
||||
tm_funnel_tx.clone(),
|
||||
);
|
||||
let hk_srv_receiver =
|
||||
MpscTcInStoreReceiver::new(TcReceiverId::PusHk as ChannelId, "PUS_8_TC_RECV", pus_hk_rx);
|
||||
MpscTcReceiver::new(TcReceiverId::PusHk as ChannelId, "PUS_8_TC_RECV", pus_hk_rx);
|
||||
let pus_3_handler = PusService3HkHandler::new(
|
||||
Box::new(hk_srv_receiver),
|
||||
Box::new(hk_srv_tm_sender),
|
||||
PUS_APID,
|
||||
verif_reporter.clone(),
|
||||
EcssTcInStoreConverter::new(tc_store.pool.clone(), 2048),
|
||||
EcssTcInSharedStoreConverter::new(tc_store.pool.clone(), 2048),
|
||||
request_map,
|
||||
);
|
||||
let mut pus_3_wrapper = Pus3Wrapper { pus_3_handler };
|
||||
|
@ -4,8 +4,8 @@ use satrs_core::pus::verification::{
|
||||
FailParams, TcStateAccepted, VerificationReporterWithSender, VerificationToken,
|
||||
};
|
||||
use satrs_core::pus::{
|
||||
EcssTcInMemConverter, EcssTcInStoreConverter, EcssTcReceiver, EcssTmSender,
|
||||
PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHandler,
|
||||
EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcReceiver, EcssTmSender,
|
||||
PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHelper,
|
||||
};
|
||||
use satrs_core::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs_core::spacepackets::ecss::PusPacket;
|
||||
@ -14,7 +14,7 @@ use std::collections::HashMap;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
pub struct PusService8ActionHandler<TcInMemConverter: EcssTcInMemConverter> {
|
||||
psb: PusServiceHandler<TcInMemConverter>,
|
||||
service_helper: PusServiceHelper<TcInMemConverter>,
|
||||
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService8ActionHandler<TcInMemCon
|
||||
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
psb: PusServiceHandler::new(
|
||||
service_helper: PusServiceHelper::new(
|
||||
tc_receiver,
|
||||
tm_sender,
|
||||
tm_apid,
|
||||
@ -47,7 +47,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService8ActionHandler<TcInMemCon
|
||||
) -> Result<(), PusPacketHandlingError> {
|
||||
let user_data = tc.user_data();
|
||||
if user_data.len() < 8 {
|
||||
self.psb
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.borrow_mut()
|
||||
@ -77,7 +77,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService8ActionHandler<TcInMemCon
|
||||
} else {
|
||||
let mut fail_data: [u8; 4] = [0; 4];
|
||||
fail_data.copy_from_slice(&target_id.target.to_be_bytes());
|
||||
self.psb
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.borrow_mut()
|
||||
@ -98,15 +98,15 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService8ActionHandler<TcInMemCon
|
||||
}
|
||||
|
||||
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
|
||||
let possible_packet = self.psb.retrieve_and_accept_next_packet()?;
|
||||
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
|
||||
if possible_packet.is_none() {
|
||||
return Ok(PusPacketHandlerResult::Empty);
|
||||
}
|
||||
let ecss_tc_and_token = possible_packet.unwrap();
|
||||
self.psb
|
||||
self.service_helper
|
||||
.tc_in_mem_converter
|
||||
.cache_ecss_tc_in_memory(&ecss_tc_and_token)?;
|
||||
let tc = PusTcReader::new(self.psb.tc_in_mem_converter.tc_slice_raw())?.0;
|
||||
.cache_ecss_tc_in_memory(&ecss_tc_and_token.tc_in_memory)?;
|
||||
let tc = PusTcReader::new(self.service_helper.tc_in_mem_converter.tc_slice_raw())?.0;
|
||||
let subservice = tc.subservice();
|
||||
let mut partial_error = None;
|
||||
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
|
||||
@ -116,7 +116,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService8ActionHandler<TcInMemCon
|
||||
}
|
||||
_ => {
|
||||
let fail_data = [subservice];
|
||||
self.psb
|
||||
self.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
@ -142,7 +142,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService8ActionHandler<TcInMemCon
|
||||
}
|
||||
|
||||
pub struct Pus8Wrapper {
|
||||
pub(crate) pus_8_handler: PusService8ActionHandler<EcssTcInStoreConverter>,
|
||||
pub(crate) pus_8_handler: PusService8ActionHandler<EcssTcInSharedStoreConverter>,
|
||||
}
|
||||
|
||||
impl Pus8Wrapper {
|
||||
|
@ -1,9 +1,9 @@
|
||||
use log::{error, warn};
|
||||
use satrs_core::pus::event_srv::PusService5EventHandler;
|
||||
use satrs_core::pus::{EcssTcInStoreConverter, PusPacketHandlerResult};
|
||||
use satrs_core::pus::{EcssTcInSharedStoreConverter, PusPacketHandlerResult};
|
||||
|
||||
pub struct Pus5Wrapper {
|
||||
pub pus_5_handler: PusService5EventHandler<EcssTcInStoreConverter>,
|
||||
pub pus_5_handler: PusService5EventHandler<EcssTcInSharedStoreConverter>,
|
||||
}
|
||||
|
||||
impl Pus5Wrapper {
|
||||
|
@ -3,8 +3,8 @@ use log::{error, warn};
|
||||
use satrs_core::hk::{CollectionIntervalFactor, HkRequest};
|
||||
use satrs_core::pus::verification::{FailParams, StdVerifReporterWithSender};
|
||||
use satrs_core::pus::{
|
||||
EcssTcInMemConverter, EcssTcInStoreConverter, EcssTcReceiver, EcssTmSender,
|
||||
PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHandler,
|
||||
EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcReceiver, EcssTmSender,
|
||||
PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHelper,
|
||||
};
|
||||
use satrs_core::spacepackets::ecss::{hk, PusPacket};
|
||||
use satrs_example::{hk_err, tmtc_err, TargetIdWithApid};
|
||||
@ -12,7 +12,7 @@ use std::collections::HashMap;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
pub struct PusService3HkHandler<TcInMemConverter: EcssTcInMemConverter> {
|
||||
psb: PusServiceHandler<TcInMemConverter>,
|
||||
psb: PusServiceHelper<TcInMemConverter>,
|
||||
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService3HkHandler<TcInMemConvert
|
||||
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
psb: PusServiceHandler::new(
|
||||
psb: PusServiceHelper::new(
|
||||
tc_receiver,
|
||||
tm_sender,
|
||||
tm_apid,
|
||||
@ -46,7 +46,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService3HkHandler<TcInMemConvert
|
||||
let tc = self
|
||||
.psb
|
||||
.tc_in_mem_converter
|
||||
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token)?;
|
||||
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
|
||||
let subservice = tc.subservice();
|
||||
let mut partial_error = None;
|
||||
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
|
||||
@ -148,7 +148,7 @@ impl<TcInMemConverter: EcssTcInMemConverter> PusService3HkHandler<TcInMemConvert
|
||||
}
|
||||
|
||||
pub struct Pus3Wrapper {
|
||||
pub(crate) pus_3_handler: PusService3HkHandler<EcssTcInStoreConverter>,
|
||||
pub(crate) pus_3_handler: PusService3HkHandler<EcssTcInSharedStoreConverter>,
|
||||
}
|
||||
|
||||
impl Pus3Wrapper {
|
||||
|
@ -1,50 +1,54 @@
|
||||
use crate::tmtc::PusTcSource;
|
||||
use log::{error, info, warn};
|
||||
use satrs_core::pus::scheduler::TcInfo;
|
||||
use satrs_core::pool::{PoolProviderMemInPlace, StaticMemoryPool};
|
||||
use satrs_core::pus::scheduler::{PusScheduler, TcInfo};
|
||||
use satrs_core::pus::scheduler_srv::PusService11SchedHandler;
|
||||
use satrs_core::pus::{EcssTcInStoreConverter, PusPacketHandlerResult};
|
||||
use satrs_core::pus::{EcssTcInSharedStoreConverter, PusPacketHandlerResult};
|
||||
|
||||
pub struct Pus11Wrapper {
|
||||
pub pus_11_handler: PusService11SchedHandler<EcssTcInStoreConverter>,
|
||||
pub pus_11_handler: PusService11SchedHandler<EcssTcInSharedStoreConverter, PusScheduler>,
|
||||
pub sched_tc_pool: StaticMemoryPool,
|
||||
pub tc_source_wrapper: PusTcSource,
|
||||
}
|
||||
|
||||
impl Pus11Wrapper {
|
||||
pub fn release_tcs(&mut self) {
|
||||
let releaser = |enabled: bool, info: &TcInfo| -> bool {
|
||||
let releaser = |enabled: bool, _info: &TcInfo, tc: &[u8]| -> bool {
|
||||
if enabled {
|
||||
// Transfer TC from scheduler TC pool to shared TC pool.
|
||||
let released_tc_addr = self
|
||||
.tc_source_wrapper
|
||||
.tc_store
|
||||
.pool
|
||||
.write()
|
||||
.expect("locking pool failed")
|
||||
.add(tc)
|
||||
.expect("adding TC to shared pool failed");
|
||||
|
||||
self.tc_source_wrapper
|
||||
.tc_source
|
||||
.send(info.addr())
|
||||
.send(released_tc_addr)
|
||||
.expect("sending TC to TC source failed");
|
||||
}
|
||||
true
|
||||
};
|
||||
|
||||
let mut pool = self
|
||||
.tc_source_wrapper
|
||||
.tc_store
|
||||
.pool
|
||||
.write()
|
||||
.expect("error locking pool");
|
||||
|
||||
self.pus_11_handler
|
||||
.scheduler_mut()
|
||||
.update_time_from_now()
|
||||
.unwrap();
|
||||
if let Ok(released_tcs) = self
|
||||
let released_tcs = self
|
||||
.pus_11_handler
|
||||
.scheduler_mut()
|
||||
.release_telecommands(releaser, pool.as_mut())
|
||||
{
|
||||
.release_telecommands(releaser, &mut self.sched_tc_pool)
|
||||
.expect("releasing TCs failed");
|
||||
if released_tcs > 0 {
|
||||
info!("{released_tcs} TC(s) released from scheduler");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_next_packet(&mut self) -> bool {
|
||||
match self.pus_11_handler.handle_one_tc() {
|
||||
match self.pus_11_handler.handle_one_tc(&mut self.sched_tc_pool) {
|
||||
Ok(result) => match result {
|
||||
PusPacketHandlerResult::RequestHandled => {}
|
||||
PusPacketHandlerResult::RequestHandledPartialSuccess(e) => {
|
||||
|
@ -7,12 +7,12 @@ use satrs_core::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs_core::spacepackets::ecss::PusPacket;
|
||||
use satrs_core::spacepackets::time::cds::TimeProvider;
|
||||
use satrs_core::spacepackets::time::TimeWriter;
|
||||
use satrs_core::{events::EventU32, pus::EcssTcInStoreConverter};
|
||||
use satrs_core::{events::EventU32, pus::EcssTcInSharedStoreConverter};
|
||||
use satrs_example::{tmtc_err, TEST_EVENT};
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
pub struct Service17CustomWrapper {
|
||||
pub pus17_handler: PusService17TestHandler<EcssTcInStoreConverter>,
|
||||
pub pus17_handler: PusService17TestHandler<EcssTcInSharedStoreConverter>,
|
||||
pub test_srv_event_sender: Sender<(EventU32, Option<Params>)>,
|
||||
}
|
||||
|
||||
@ -38,8 +38,12 @@ impl Service17CustomWrapper {
|
||||
warn!("PUS17: Subservice {subservice} not implemented")
|
||||
}
|
||||
PusPacketHandlerResult::CustomSubservice(subservice, token) => {
|
||||
let (tc, _) =
|
||||
PusTcReader::new(self.pus17_handler.psb.tc_in_mem_converter.tc_slice_raw())
|
||||
let (tc, _) = PusTcReader::new(
|
||||
self.pus17_handler
|
||||
.service_helper
|
||||
.tc_in_mem_converter
|
||||
.tc_slice_raw(),
|
||||
)
|
||||
.unwrap();
|
||||
let time_stamper = TimeProvider::from_now_with_u16_days().unwrap();
|
||||
let mut stamp_buf: [u8; 7] = [0; 7];
|
||||
@ -51,14 +55,14 @@ impl Service17CustomWrapper {
|
||||
.expect("Sending test event failed");
|
||||
let start_token = self
|
||||
.pus17_handler
|
||||
.psb
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
.start_success(token, Some(&stamp_buf))
|
||||
.expect("Error sending start success");
|
||||
self.pus17_handler
|
||||
.psb
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
@ -67,7 +71,7 @@ impl Service17CustomWrapper {
|
||||
} else {
|
||||
let fail_data = [tc.subservice()];
|
||||
self.pus17_handler
|
||||
.psb
|
||||
.service_helper
|
||||
.common
|
||||
.verification_handler
|
||||
.get_mut()
|
||||
|
@ -5,7 +5,7 @@ use std::sync::mpsc::{Receiver, SendError, Sender, TryRecvError};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::pus::PusReceiver;
|
||||
use satrs_core::pool::{SharedPool, StoreAddr, StoreError};
|
||||
use satrs_core::pool::{PoolProviderMemInPlace, SharedStaticMemoryPool, StoreAddr, StoreError};
|
||||
use satrs_core::spacepackets::ecss::tc::PusTcReader;
|
||||
use satrs_core::spacepackets::ecss::PusPacket;
|
||||
use satrs_core::tmtc::tm_helper::SharedTmStore;
|
||||
@ -41,7 +41,7 @@ pub enum MpscStoreAndSendError {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TcStore {
|
||||
pub pool: SharedPool,
|
||||
pub pool: SharedStaticMemoryPool,
|
||||
}
|
||||
|
||||
impl TcStore {
|
||||
|
@ -3,7 +3,7 @@ use std::{net::SocketAddr, sync::mpsc::Receiver};
|
||||
use log::{info, warn};
|
||||
use satrs_core::{
|
||||
hal::std::udp_server::{ReceiveResult, UdpTcServer},
|
||||
pool::{SharedPool, StoreAddr},
|
||||
pool::{PoolProviderMemInPlaceWithGuards, SharedStaticMemoryPool, StoreAddr},
|
||||
tmtc::CcsdsError,
|
||||
};
|
||||
|
||||
@ -12,7 +12,7 @@ use crate::tmtc::MpscStoreAndSendError;
|
||||
pub struct UdpTmtcServer {
|
||||
pub udp_tc_server: UdpTcServer<CcsdsError<MpscStoreAndSendError>>,
|
||||
pub tm_rx: Receiver<StoreAddr>,
|
||||
pub tm_store: SharedPool,
|
||||
pub tm_store: SharedStaticMemoryPool,
|
||||
}
|
||||
impl UdpTmtcServer {
|
||||
pub fn periodic_operation(&mut self) {
|
||||
|
Loading…
Reference in New Issue
Block a user