Major refactoring and update of PUS module

This commit is contained in:
2024-04-04 15:18:53 +02:00
parent 344fe6a4c0
commit df2733a176
75 changed files with 9295 additions and 4764 deletions

View File

@ -8,8 +8,28 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
## Added
- Added `params::WritableToBeBytes::to_vec`.
- New `ComponentId` (`u64` typedef for now) which replaces former `TargetId` as a generic
way to identify components.
- Various abstraction and objects for targeted requests. This includes mode request/reply
types for actions, HK and modes.
- `VerificationReportingProvider::owner_id` method.
- Introduced generic `EventMessage` which is generic over the event type and the additional
parameter type. This message also contains the sender ID which can be useful for debugging
or application layer / FDIR logic.
## Changed
- `encoding::ccsds::PacketIdValidator` renamed to `ValidatorU16Id`, which lives in the crate root.
It can be used for both CCSDS packet ID and CCSDS APID validation.
- `EventManager::try_event_handling` not expects a mutable error handling closure instead of
returning the occured errors.
- Renamed `EventManagerBase` to `EventReportCreator`
- Renamed `VerificationReporterCore` to `VerificationReportCreator`.
- Removed `VerificationReporterCore`. The high-level API exposed by `VerificationReporter` and
the low level API exposed by `VerificationReportCreator` should be sufficient for all use-cases.
- Refactored `EventManager` to heavily use generics instead of trait objects.
- `SendEventProvider` -> `EventSendProvider`. `id` trait method renamed to `channel_id`.
- `ListenerTable` -> `ListenerMapProvider`
@ -18,16 +38,37 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Refactored ECSS TM sender abstractions to be generic over different message queue backends.
- Refactored Verification Reporter abstractions and implementation to be generic over the sender
instead of using trait objects.
- Renamed `WritableToBeBytes::raw_len` to `WritableToBeBytes::written_len` for consistency.
- `PusServiceProvider` renamed to `PusServiceDistributor` to make the purpose of the object
more clear
- `PusServiceProvider::handle_pus_tc_packet` renamed to `PusServiceDistributor::distribute_packet`.
- `PusServiceDistibutor` and `CcsdsDistributor` now use generics instead of trait objects.
This makes accessing the concrete trait implementations more easy as well.
- Major overhaul of the PUS handling module.
- Replace `TargetId` by `ComponentId`.
- Replace most usages of `ChannelId` by `ComponentId`. A dedicated channel ID has limited usage
due to the nature of typed channels in Rust.
- `CheckTimer` renamed to `CountdownProvider`.
- Renamed `TargetId` to `ComponentId`.
- Replaced most `ChannelId` occurences with `ComponentId`. For typed channels, there is generally
no need for dedicated channel IDs.
- Changed `params::WritableToBeBytes::raw_len` to `written_len` for consistency.
- `EventReporter` caches component ID.
- Renamed `PusService11SchedHandler` to `PusSchedServiceHandler`.
- Fixed general naming of PUS handlers from `handle_one_tc` to `poll_and_handle_next_tc`.
- Reworked verification module: The sender (`impl EcssTmSenderCore`)
now needs to be passed explicitely to the `VerificationReportingProvider` abstraction. This
allows easier sharing of the TM sender component.
## Fixed
- Update deprecated API for `PusScheduler::insert_wrapped_tc_cds_short`
and `PusScheduler::insert_wrapped_tc_cds_long`.
- `EventReporter` uses interior mutability pattern to allow non-mutable API.
## Removed
- Remove `objects` module.
# [v0.2.0-rc.0] 2024-02-21

View File

@ -19,7 +19,7 @@ smallvec = "1"
crc = "3"
[dependencies.satrs-shared]
version = "0.1.2"
version = "0.1.3"
path = "../satrs-shared"
[dependencies.num_enum]
@ -72,7 +72,7 @@ optional = true
[dependencies.spacepackets]
git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git"
version = "0.11.0-rc.0"
version = "0.11.0-rc.2"
branch = "main"
default-features = false
@ -117,6 +117,7 @@ alloc = [
serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"]
crossbeam = ["crossbeam-channel"]
heapless = ["dep:heapless"]
test_util = []
doc-images = []
[package.metadata.docs.rs]

View File

@ -4,11 +4,11 @@ Checklist for new releases
# Pre-Release
1. Make sure any new modules are documented sufficiently enough and check docs with
`cargo +nightly doc --all-features --config 'rustdocflags=["--cfg", "doc_cfg"]' --open`.
`cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]' --open`.
2. Bump version specifier in `Cargo.toml`.
3. Update `CHANGELOG.md`: Convert `unreleased` section into version section with date and add new
`unreleased` section.
4. Run `cargo test --all-features`.
4. Run `cargo test --all-features` or `cargo nextest r --all-features` and `cargo test --doc`.
5. Run `cargo fmt` and `cargo clippy`. Check `cargo msrv` against MSRV in `Cargo.toml`.
6. Wait for CI/CD results for EGit and Github. These also check cross-compilation for bare-metal
targets.

View File

@ -1,63 +1,68 @@
use crate::{pool::StoreAddr, TargetId};
use crate::{params::Params, pool::StoreAddr};
#[cfg(feature = "alloc")]
pub use alloc_mod::*;
pub type ActionId = u32;
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct ActionRequest {
pub action_id: ActionId,
pub variant: ActionRequestVariant,
}
impl ActionRequest {
pub fn new(action_id: ActionId, variant: ActionRequestVariant) -> Self {
Self { action_id, variant }
}
}
#[non_exhaustive]
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum ActionRequest {
UnsignedIdAndStoreData {
action_id: ActionId,
data_addr: StoreAddr,
},
pub enum ActionRequestVariant {
NoData,
StoreData(StoreAddr),
#[cfg(feature = "alloc")]
UnsignedIdAndVecData {
action_id: ActionId,
data: alloc::vec::Vec<u8>,
},
#[cfg(feature = "alloc")]
StringIdAndVecData {
action_id: alloc::string::String,
data: alloc::vec::Vec<u8>,
},
#[cfg(feature = "alloc")]
StringIdAndStoreData {
action_id: alloc::string::String,
data: StoreAddr,
},
VecData(alloc::vec::Vec<u8>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TargetedActionRequest {
target: TargetId,
action_request: ActionRequest,
}
impl TargetedActionRequest {
pub fn new(target: TargetId, action_request: ActionRequest) -> Self {
Self {
target,
action_request,
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ActionReply {
pub action_id: ActionId,
pub variant: ActionReplyVariant,
}
/// A reply to an action request.
#[non_exhaustive]
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum ActionReply {
CompletionFailed(ActionId),
StepFailed {
id: ActionId,
step: u32,
},
Completed(ActionId),
#[cfg(feature = "alloc")]
CompletedStringId(alloc::string::String),
#[cfg(feature = "alloc")]
CompletionFailedStringId(alloc::string::String),
#[cfg(feature = "alloc")]
StepFailedStringId {
id: alloc::string::String,
step: u32,
},
#[derive(Clone, Debug, PartialEq)]
pub enum ActionReplyVariant {
CompletionFailed(Params),
StepFailed { step: u32, reason: Params },
Completed,
}
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use super::*;
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct ActionRequestStringId {
pub action_id: alloc::string::String,
pub variant: ActionRequestVariant,
}
impl ActionRequestStringId {
pub fn new(action_id: alloc::string::String, variant: ActionRequestVariant) -> Self {
Self { action_id, variant }
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ActionReplyStringId {
pub action_id: alloc::string::String,
pub variant: ActionReplyVariant,
}
}
#[cfg(test)]
mod tests {}

View File

@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
use super::{
filestore::{FilestoreError, VirtualFilestore},
user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams},
CheckTimer, CheckTimerCreator, EntityType, LocalEntityConfig, PacketInfo, PacketTarget,
CheckTimerCreator, CountdownProvider, EntityType, LocalEntityConfig, PacketInfo, PacketTarget,
RemoteEntityConfig, RemoteEntityConfigProvider, State, TimerContext, TransactionId,
TransactionStep,
};
@ -54,7 +54,7 @@ struct TransferState {
completion_disposition: CompletionDisposition,
checksum: u32,
current_check_count: u32,
current_check_timer: Option<Box<dyn CheckTimer>>,
current_check_timer: Option<Box<dyn CountdownProvider>>,
}
impl Default for TransferState {
@ -799,9 +799,9 @@ mod tests {
};
use crate::cfdp::{
filestore::NativeFilestore, user::OwnedMetadataRecvdParams, CheckTimer, CheckTimerCreator,
DefaultFaultHandler, IndicationConfig, RemoteEntityConfig, StdRemoteEntityConfigProvider,
UserFaultHandler, CRC_32,
filestore::NativeFilestore, user::OwnedMetadataRecvdParams, CheckTimerCreator,
CountdownProvider, DefaultFaultHandler, IndicationConfig, RemoteEntityConfig,
StdRemoteEntityConfigProvider, UserFaultHandler, CRC_32,
};
use super::*;
@ -1057,7 +1057,7 @@ mod tests {
expired: Arc<AtomicBool>,
}
impl CheckTimer for TestCheckTimer {
impl CountdownProvider for TestCheckTimer {
fn has_expired(&self) -> bool {
self.expired.load(core::sync::atomic::Ordering::Relaxed)
}
@ -1088,7 +1088,10 @@ mod tests {
}
impl CheckTimerCreator for TestCheckTimerCreator {
fn get_check_timer_provider(&self, timer_context: TimerContext) -> Box<dyn CheckTimer> {
fn get_check_timer_provider(
&self,
timer_context: TimerContext,
) -> Box<dyn CountdownProvider> {
match timer_context {
TimerContext::CheckLimit { .. } => {
Box::new(TestCheckTimer::new(self.check_limit_expired_flag.clone()))

View File

@ -17,6 +17,8 @@ use alloc::boxed::Box;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::time::CountdownProvider;
#[cfg(feature = "std")]
pub mod dest;
#[cfg(feature = "alloc")]
@ -45,7 +47,15 @@ pub enum TimerContext {
},
}
/// Generic abstraction for a check timer which is used by 3 mechanisms of the CFDP protocol.
/// A generic trait which allows CFDP entities to create check timers which are required to
/// implement special procedures in unacknowledged transmission mode, as specified in 4.6.3.2
/// and 4.6.3.3.
///
/// This trait also allows the creation of different check timers depending on context and purpose
/// of the timer, the runtime environment (e.g. standard clock timer vs. timer using a RTC) or
/// other factors.
///
/// The countdown timer is used by 3 mechanisms of the CFDP protocol.
///
/// ## 1. Check limit handling
///
@ -74,22 +84,9 @@ pub enum TimerContext {
/// The timer will be used to perform the Positive Acknowledgement Procedures as specified in
/// 4.7. 1of the CFDP standard. The expiration period will be provided by the Positive ACK timer
/// interval of the remote entity configuration.
pub trait CheckTimer: Debug {
fn has_expired(&self) -> bool;
fn reset(&mut self);
}
/// A generic trait which allows CFDP entities to create check timers which are required to
/// implement special procedures in unacknowledged transmission mode, as specified in 4.6.3.2
/// and 4.6.3.3. The [CheckTimer] documentation provides more information about the purpose of the
/// check timer in the context of CFDP.
///
/// This trait also allows the creation of different check timers depending on context and purpose
/// of the timer, the runtime environment (e.g. standard clock timer vs. timer using a RTC) or
/// other factors.
#[cfg(feature = "alloc")]
pub trait CheckTimerCreator {
fn get_check_timer_provider(&self, timer_context: TimerContext) -> Box<dyn CheckTimer>;
fn get_check_timer_provider(&self, timer_context: TimerContext) -> Box<dyn CountdownProvider>;
}
/// Simple implementation of the [CheckTimerCreator] trait assuming a standard runtime.
@ -112,7 +109,7 @@ impl StdCheckTimer {
}
#[cfg(feature = "std")]
impl CheckTimer for StdCheckTimer {
impl CountdownProvider for StdCheckTimer {
fn has_expired(&self) -> bool {
let elapsed_time = self.start_time.elapsed();
if elapsed_time.as_secs() > self.expiry_time_seconds {

View File

@ -1,65 +1,4 @@
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use hashbrown::HashSet;
use spacepackets::PacketId;
use crate::tmtc::ReceivesTcCore;
pub trait PacketIdLookup {
fn validate(&self, packet_id: u16) -> bool;
}
#[cfg(feature = "alloc")]
impl PacketIdLookup for Vec<u16> {
fn validate(&self, packet_id: u16) -> bool {
self.contains(&packet_id)
}
}
#[cfg(feature = "alloc")]
impl PacketIdLookup for HashSet<u16> {
fn validate(&self, packet_id: u16) -> bool {
self.contains(&packet_id)
}
}
impl PacketIdLookup for [u16] {
fn validate(&self, packet_id: u16) -> bool {
self.binary_search(&packet_id).is_ok()
}
}
impl PacketIdLookup for &[u16] {
fn validate(&self, packet_id: u16) -> bool {
self.binary_search(&packet_id).is_ok()
}
}
#[cfg(feature = "alloc")]
impl PacketIdLookup for Vec<PacketId> {
fn validate(&self, packet_id: u16) -> bool {
self.contains(&PacketId::from(packet_id))
}
}
#[cfg(feature = "alloc")]
impl PacketIdLookup for HashSet<PacketId> {
fn validate(&self, packet_id: u16) -> bool {
self.contains(&PacketId::from(packet_id))
}
}
impl PacketIdLookup for [PacketId] {
fn validate(&self, packet_id: u16) -> bool {
self.binary_search(&PacketId::from(packet_id)).is_ok()
}
}
impl PacketIdLookup for &[PacketId] {
fn validate(&self, packet_id: u16) -> bool {
self.binary_search(&PacketId::from(packet_id)).is_ok()
}
}
use crate::{tmtc::ReceivesTcCore, ValidatorU16Id};
/// This function parses a given buffer for tightly packed CCSDS space packets. It uses the
/// [PacketId] field of the CCSDS packets to detect the start of a CCSDS space packet and then
@ -75,7 +14,7 @@ impl PacketIdLookup for &[PacketId] {
/// error will be returned.
pub fn parse_buffer_for_ccsds_space_packets<E>(
buf: &mut [u8],
packet_id_lookup: &(impl PacketIdLookup + ?Sized),
packet_id_validator: &(impl ValidatorU16Id + ?Sized),
tc_receiver: &mut (impl ReceivesTcCore<Error = E> + ?Sized),
next_write_idx: &mut usize,
) -> Result<u32, E> {
@ -88,7 +27,7 @@ pub fn parse_buffer_for_ccsds_space_packets<E>(
break;
}
let packet_id = u16::from_be_bytes(buf[current_idx..current_idx + 2].try_into().unwrap());
if packet_id_lookup.validate(packet_id) {
if packet_id_validator.validate(packet_id) {
let length_field =
u16::from_be_bytes(buf[current_idx + 4..current_idx + 6].try_into().unwrap());
let packet_size = length_field + 7;
@ -123,13 +62,13 @@ mod tests {
const TEST_APID_0: u16 = 0x02;
const TEST_APID_1: u16 = 0x10;
const TEST_PACKET_ID_0: PacketId = PacketId::const_tc(true, TEST_APID_0);
const TEST_PACKET_ID_1: PacketId = PacketId::const_tc(true, TEST_APID_1);
const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0);
const TEST_PACKET_ID_1: PacketId = PacketId::new_for_tc(true, TEST_APID_1);
#[test]
fn test_basic() {
let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap();
let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
let sph = SpHeader::new_from_apid(TEST_APID_0);
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
let mut buffer: [u8; 32] = [0; 32];
let packet_len = ping_tc
.write_to_bytes(&mut buffer)
@ -155,9 +94,9 @@ mod tests {
#[test]
fn test_multi_packet() {
let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap();
let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
let action_tc = PusTcCreator::new_simple(&mut sph, 8, 0, None, true);
let sph = SpHeader::new_from_apid(TEST_APID_0);
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], true);
let mut buffer: [u8; 32] = [0; 32];
let packet_len_ping = ping_tc
.write_to_bytes(&mut buffer)
@ -190,10 +129,10 @@ mod tests {
#[test]
fn test_multi_apid() {
let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap();
let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
sph = SpHeader::tc_unseg(TEST_APID_1, 0, 0).unwrap();
let action_tc = PusTcCreator::new_simple(&mut sph, 8, 0, None, true);
let sph = SpHeader::new_from_apid(TEST_APID_0);
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
let sph = SpHeader::new_from_apid(TEST_APID_1);
let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], true);
let mut buffer: [u8; 32] = [0; 32];
let packet_len_ping = ping_tc
.write_to_bytes(&mut buffer)
@ -226,10 +165,10 @@ mod tests {
#[test]
fn test_split_packet_multi() {
let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap();
let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
sph = SpHeader::tc_unseg(TEST_APID_1, 0, 0).unwrap();
let action_tc = PusTcCreator::new_simple(&mut sph, 8, 0, None, true);
let ping_tc =
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
let action_tc =
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 8, 0, &[], true);
let mut buffer: [u8; 32] = [0; 32];
let packet_len_ping = ping_tc
.write_to_bytes(&mut buffer)
@ -257,8 +196,8 @@ mod tests {
#[test]
fn test_one_split_packet() {
let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap();
let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
let ping_tc =
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
let mut buffer: [u8; 32] = [0; 32];
let packet_len_ping = ping_tc
.write_to_bytes(&mut buffer)

View File

@ -11,7 +11,7 @@
//! about events first:
//!
//! The event manager has a listener table abstracted by the [ListenerMapProvider], which maps
//! listener groups identified by [ListenerKey]s to a [sender ID][ChannelId].
//! listener groups identified by [ListenerKey]s to a [listener ID][ComponentId].
//! It also contains a sender table abstracted by the [SenderMapProvider] which maps these sender
//! IDs to concrete [EventSendProvider]s. A simple approach would be to use one send event provider
//! for each OBSW thread and then subscribe for all interesting events for a particular thread
@ -28,8 +28,8 @@
//! manager.
//! 3. The event manager receives the receiver component as part of a [EventReceiveProvider]
//! implementation so all events are routed to the manager.
//! 4. Create the [send event providers][EventSendProvider]s which allow routing events to
//! subscribers. You can now use their [sender IDs][EventSendProvider::channel_id] to subscribe
//! 4. Create the [event sender map][SenderMapProvider]s which allow routing events to
//! subscribers. You can now use the subscriber component IDs to subscribe
//! for event groups, for example by using the [EventManager::subscribe_single] method.
//! 5. Add the send provider as well using the [EventManager::add_sender] call so the event
//! manager can route listener groups to a the send provider.
@ -45,12 +45,13 @@
//! for a concrete example using multi-threading where events are routed to
//! different threads.
use crate::events::{EventU16, EventU32, GenericEvent, LargestEventRaw, LargestGroupIdRaw};
use crate::params::{Params, ParamsHeapless};
use crate::params::Params;
use crate::queue::GenericSendError;
use core::fmt::Debug;
use core::marker::PhantomData;
use core::slice::Iter;
use crate::ChannelId;
use crate::ComponentId;
#[cfg(feature = "alloc")]
pub use alloc_mod::*;
@ -65,87 +66,122 @@ pub enum ListenerKey {
All,
}
pub type EventWithHeaplessAuxData<Event> = (Event, Option<ParamsHeapless>);
pub type EventU32WithHeaplessAuxData = EventWithHeaplessAuxData<EventU32>;
pub type EventU16WithHeaplessAuxData = EventWithHeaplessAuxData<EventU16>;
#[derive(Debug)]
pub struct EventMessage<Event: GenericEvent, ParamProvider: Debug = Params> {
sender_id: ComponentId,
event: Event,
params: Option<ParamProvider>,
}
pub type EventWithAuxData<Event> = (Event, Option<Params>);
pub type EventU32WithAuxData = EventWithAuxData<EventU32>;
pub type EventU16WithAuxData = EventWithAuxData<EventU16>;
pub trait EventSendProvider<EV: GenericEvent, AuxDataProvider = Params> {
fn channel_id(&self) -> ChannelId;
fn send_no_data(&self, event: EV) -> Result<(), GenericSendError> {
self.send(event, None)
impl<Event: GenericEvent, ParamProvider: Debug + Clone> EventMessage<Event, ParamProvider> {
pub fn new_generic(
sender_id: ComponentId,
event: Event,
params: Option<&ParamProvider>,
) -> Self {
Self {
sender_id,
event,
params: params.cloned(),
}
}
fn send(&self, event: EV, aux_data: Option<AuxDataProvider>) -> Result<(), GenericSendError>;
pub fn sender_id(&self) -> ComponentId {
self.sender_id
}
pub fn event(&self) -> Event {
self.event
}
pub fn params(&self) -> Option<&ParamProvider> {
self.params.as_ref()
}
pub fn new(sender_id: ComponentId, event: Event) -> Self {
Self::new_generic(sender_id, event, None)
}
pub fn new_with_params(sender_id: ComponentId, event: Event, params: &ParamProvider) -> Self {
Self::new_generic(sender_id, event, Some(params))
}
}
pub type EventMessageU32 = EventMessage<EventU32, Params>;
pub type EventMessageU16 = EventMessage<EventU16, Params>;
/// Generic abstraction
pub trait EventSendProvider<Event: GenericEvent, ParamProvider: Debug = Params> {
type Error;
fn target_id(&self) -> ComponentId;
fn send(&self, message: EventMessage<Event, ParamProvider>) -> Result<(), Self::Error>;
}
/// Generic abstraction for an event receiver.
pub trait EventReceiveProvider<Event: GenericEvent, AuxDataProvider = Params> {
pub trait EventReceiveProvider<Event: GenericEvent, ParamsProvider: Debug = Params> {
type Error;
/// This function has to be provided by any event receiver. A call may or may not return
/// an event and optional auxiliary data.
fn try_recv_event(&self) -> Option<(Event, Option<AuxDataProvider>)>;
fn try_recv_event(&self) -> Result<Option<EventMessage<Event, ParamsProvider>>, Self::Error>;
}
pub trait ListenerMapProvider {
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
fn get_listeners(&self) -> alloc::vec::Vec<ListenerKey>;
fn contains_listener(&self, key: &ListenerKey) -> bool;
fn get_listener_ids(&self, key: &ListenerKey) -> Option<Iter<ChannelId>>;
fn add_listener(&mut self, key: ListenerKey, sender_id: ChannelId) -> bool;
fn get_listener_ids(&self, key: &ListenerKey) -> Option<Iter<ComponentId>>;
fn add_listener(&mut self, key: ListenerKey, listener_id: ComponentId) -> bool;
fn remove_duplicates(&mut self, key: &ListenerKey);
}
pub trait SenderMapProvider<
SP: EventSendProvider<EV, AUX>,
EV: GenericEvent = EventU32,
AUX = Params,
EventSender: EventSendProvider<Event, ParamProvider>,
Event: GenericEvent = EventU32,
ParamProvider: Debug = Params,
>
{
fn contains_send_event_provider(&self, id: &ChannelId) -> bool;
fn contains_send_event_provider(&self, target_id: &ComponentId) -> bool;
fn get_send_event_provider(&self, id: &ChannelId) -> Option<&SP>;
fn add_send_event_provider(&mut self, send_provider: SP) -> bool;
fn get_send_event_provider(&self, target_id: &ComponentId) -> Option<&EventSender>;
fn add_send_event_provider(&mut self, send_provider: EventSender) -> bool;
}
/// Generic event manager implementation.
///
/// # Generics
///
/// * `ERP`: [EventReceiveProvider] used to receive all events.
/// * `SMP`: [SenderMapProvider] which maps channel IDs to send providers.
/// * `LTR`: [ListenerMapProvider] which maps listener keys to channel IDs.
/// * `SP`: [EventSendProvider] contained within the sender map which sends the events.
/// * `EV`: The event type. This type must implement the [GenericEvent]. Currently only [EventU32]
/// * `EventReceiver`: [EventReceiveProvider] used to receive all events.
/// * `SenderMap`: [SenderMapProvider] which maps channel IDs to send providers.
/// * `ListenerMap`: [ListenerMapProvider] which maps listener keys to channel IDs.
/// * `EventSender`: [EventSendProvider] contained within the sender map which sends the events.
/// * `Ev`: The event type. This type must implement the [GenericEvent]. Currently only [EventU32]
/// and [EventU16] are supported.
/// * `AUX`: Auxiliary data which is sent with the event to provide optional context information
/// * `Data`: Auxiliary data which is sent with the event to provide optional context information
pub struct EventManager<
ERP: EventReceiveProvider<EV, AUX>,
SMP: SenderMapProvider<SP, EV, AUX>,
LTR: ListenerMapProvider,
SP: EventSendProvider<EV, AUX>,
EV: GenericEvent = EventU32,
AUX = Params,
EventReceiver: EventReceiveProvider<Event, ParamProvider>,
SenderMap: SenderMapProvider<EventSender, Event, ParamProvider>,
ListenerMap: ListenerMapProvider,
EventSender: EventSendProvider<Event, ParamProvider>,
Event: GenericEvent = EventU32,
ParamProvider: Debug = Params,
> {
event_receiver: ERP,
sender_map: SMP,
listener_map: LTR,
phantom: core::marker::PhantomData<(SP, EV, AUX)>,
event_receiver: EventReceiver,
sender_map: SenderMap,
listener_map: ListenerMap,
phantom: core::marker::PhantomData<(EventSender, Event, ParamProvider)>,
}
#[derive(Debug)]
pub enum EventRoutingResult<EV: GenericEvent, AUX> {
pub enum EventRoutingResult<Event: GenericEvent, ParamProvider: Debug> {
/// No event was received
Empty,
/// An event was received and routed to listeners.
Handled {
num_recipients: u32,
event: EV,
aux_data: Option<AUX>,
event_msg: EventMessage<Event, ParamProvider>,
},
}
@ -153,35 +189,29 @@ pub enum EventRoutingResult<EV: GenericEvent, AUX> {
pub enum EventRoutingError {
Send(GenericSendError),
NoSendersForKey(ListenerKey),
NoSenderForId(ChannelId),
}
#[derive(Debug)]
pub struct EventRoutingErrorsWithResult<EV: GenericEvent, AUX> {
pub result: EventRoutingResult<EV, AUX>,
pub errors: [Option<EventRoutingError>; 3],
NoSenderForId(ComponentId),
}
impl<
ER: EventReceiveProvider<EV, AUX>,
S: SenderMapProvider<SP, EV, AUX>,
L: ListenerMapProvider,
SP: EventSendProvider<EV, AUX>,
EV: GenericEvent + Copy,
AUX: Clone,
> EventManager<ER, S, L, SP, EV, AUX>
EventReceiver: EventReceiveProvider<Event, ParamProvider>,
SenderMap: SenderMapProvider<EventSender, Event, ParamProvider>,
ListenerMap: ListenerMapProvider,
EventSender: EventSendProvider<Event, ParamProvider>,
Event: GenericEvent + Copy,
ParamProvider: Debug,
> EventManager<EventReceiver, SenderMap, ListenerMap, EventSender, Event, ParamProvider>
{
pub fn remove_duplicates(&mut self, key: &ListenerKey) {
self.listener_map.remove_duplicates(key)
}
/// Subscribe for a unique event.
pub fn subscribe_single(&mut self, event: &EV, sender_id: ChannelId) {
pub fn subscribe_single(&mut self, event: &Event, sender_id: ComponentId) {
self.update_listeners(ListenerKey::Single(event.raw_as_largest_type()), sender_id);
}
/// Subscribe for an event group.
pub fn subscribe_group(&mut self, group_id: LargestGroupIdRaw, sender_id: ChannelId) {
pub fn subscribe_group(&mut self, group_id: LargestGroupIdRaw, sender_id: ComponentId) {
self.update_listeners(ListenerKey::Group(group_id), sender_id);
}
@ -189,21 +219,24 @@ impl<
///
/// For example, this can be useful for a handler component which sends every event as
/// a telemetry packet.
pub fn subscribe_all(&mut self, sender_id: ChannelId) {
pub fn subscribe_all(&mut self, sender_id: ComponentId) {
self.update_listeners(ListenerKey::All, sender_id);
}
}
impl<
ERP: EventReceiveProvider<EV, AUX>,
SMP: SenderMapProvider<SP, EV, AUX>,
LTR: ListenerMapProvider,
SP: EventSendProvider<EV, AUX>,
EV: GenericEvent + Copy,
AUX: Clone,
> EventManager<ERP, SMP, LTR, SP, EV, AUX>
EventReceiver: EventReceiveProvider<Event, ParamProvider>,
SenderMap: SenderMapProvider<EventSenderMap, Event, ParamProvider>,
ListenerMap: ListenerMapProvider,
EventSenderMap: EventSendProvider<Event, ParamProvider>,
Event: GenericEvent + Copy,
ParamProvider: Debug,
> EventManager<EventReceiver, SenderMap, ListenerMap, EventSenderMap, Event, ParamProvider>
{
pub fn new_with_custom_maps(event_receiver: ERP, sender_map: SMP, listener_map: LTR) -> Self {
pub fn new_with_custom_maps(
event_receiver: EventReceiver,
sender_map: SenderMap,
listener_map: ListenerMap,
) -> Self {
EventManager {
listener_map,
sender_map,
@ -213,81 +246,79 @@ impl<
}
/// Add a new sender component which can be used to send events to subscribers.
pub fn add_sender(&mut self, send_provider: SP) {
pub fn add_sender(&mut self, send_provider: EventSenderMap) {
if !self
.sender_map
.contains_send_event_provider(&send_provider.channel_id())
.contains_send_event_provider(&send_provider.target_id())
{
self.sender_map.add_send_event_provider(send_provider);
}
}
/// Generic function to update the event subscribers.
fn update_listeners(&mut self, key: ListenerKey, sender_id: ChannelId) {
fn update_listeners(&mut self, key: ListenerKey, sender_id: ComponentId) {
self.listener_map.add_listener(key, sender_id);
}
}
impl<
EventReceiver: EventReceiveProvider<Event, ParamProvider>,
SenderMap: SenderMapProvider<EventSenderMap, Event, ParamProvider>,
ListenerMap: ListenerMapProvider,
EventSenderMap: EventSendProvider<Event, ParamProvider, Error = GenericSendError>,
Event: GenericEvent + Copy,
ParamProvider: Clone + Debug,
> EventManager<EventReceiver, SenderMap, ListenerMap, EventSenderMap, Event, ParamProvider>
{
/// This function will use the cached event receiver and try to receive one event.
/// If an event was received, it will try to route that event to all subscribed event listeners.
/// If this works without any issues, the [EventRoutingResult] will contain context information
/// about the routed event.
///
/// This function will track up to 3 errors returned as part of the
/// [EventRoutingErrorsWithResult] error struct.
pub fn try_event_handling(
/// If an error occurs during the routing, the error handler will be called. The error handler
/// should take a reference to the event message as the first argument, and the routing error
/// as the second argument.
pub fn try_event_handling<E: FnMut(&EventMessage<Event, ParamProvider>, EventRoutingError)>(
&self,
) -> Result<EventRoutingResult<EV, AUX>, EventRoutingErrorsWithResult<EV, AUX>> {
let mut err_idx = 0;
let mut err_slice = [None, None, None];
mut error_handler: E,
) -> EventRoutingResult<Event, ParamProvider> {
let mut num_recipients = 0;
let mut add_error = |error: EventRoutingError| {
if err_idx < 3 {
err_slice[err_idx] = Some(error);
err_idx += 1;
}
};
let mut send_handler = |key: &ListenerKey, event: EV, aux_data: &Option<AUX>| {
if self.listener_map.contains_listener(key) {
if let Some(ids) = self.listener_map.get_listener_ids(key) {
for id in ids {
if let Some(sender) = self.sender_map.get_send_event_provider(id) {
if let Err(e) = sender.send(event, aux_data.clone()) {
add_error(EventRoutingError::Send(e));
let mut send_handler =
|key: &ListenerKey, event_msg: &EventMessage<Event, ParamProvider>| {
if self.listener_map.contains_listener(key) {
if let Some(ids) = self.listener_map.get_listener_ids(key) {
for id in ids {
if let Some(sender) = self.sender_map.get_send_event_provider(id) {
if let Err(e) = sender.send(EventMessage::new_generic(
*id,
event_msg.event,
event_msg.params.as_ref(),
)) {
error_handler(event_msg, EventRoutingError::Send(e));
} else {
num_recipients += 1;
}
} else {
num_recipients += 1;
error_handler(event_msg, EventRoutingError::NoSenderForId(*id));
}
} else {
add_error(EventRoutingError::NoSenderForId(*id));
}
} else {
error_handler(event_msg, EventRoutingError::NoSendersForKey(*key));
}
} else {
add_error(EventRoutingError::NoSendersForKey(*key));
}
}
};
if let Some((event, aux_data)) = self.event_receiver.try_recv_event() {
let single_key = ListenerKey::Single(event.raw_as_largest_type());
send_handler(&single_key, event, &aux_data);
let group_key = ListenerKey::Group(event.group_id_as_largest_type());
send_handler(&group_key, event, &aux_data);
send_handler(&ListenerKey::All, event, &aux_data);
if err_idx > 0 {
return Err(EventRoutingErrorsWithResult {
result: EventRoutingResult::Handled {
num_recipients,
event,
aux_data,
},
errors: err_slice,
});
}
return Ok(EventRoutingResult::Handled {
};
if let Ok(Some(event_msg)) = self.event_receiver.try_recv_event() {
let single_key = ListenerKey::Single(event_msg.event.raw_as_largest_type());
send_handler(&single_key, &event_msg);
let group_key = ListenerKey::Group(event_msg.event.group_id_as_largest_type());
send_handler(&group_key, &event_msg);
send_handler(&ListenerKey::All, &event_msg);
return EventRoutingResult::Handled {
num_recipients,
event,
aux_data,
});
event_msg,
};
}
Ok(EventRoutingResult::Empty)
EventRoutingResult::Empty
}
}
@ -311,23 +342,31 @@ pub mod alloc_mod {
/// and the [DefaultListenerMap]. It uses
/// [bounded mpsc senders](https://doc.rust-lang.org/std/sync/mpsc/struct.SyncSender.html) as the
/// message queue backend.
pub type EventManagerWithBoundedMpsc<EV = EventU32, AUX = Params> = EventManager<
pub type EventManagerWithBoundedMpsc<Event = EventU32, ParamProvider = Params> = EventManager<
MpscEventReceiver,
DefaultSenderMap<EventSenderMpscBounded<EV>, EV, AUX>,
DefaultSenderMap<EventSenderMpscBounded<Event>, Event, ParamProvider>,
DefaultListenerMap,
EventSenderMpscBounded<EV>,
EventSenderMpscBounded<Event>,
>;
impl<
ER: EventReceiveProvider<EV, AUX>,
SP: EventSendProvider<EV, AUX>,
EV: GenericEvent + Copy,
AUX: 'static,
> EventManager<ER, DefaultSenderMap<SP, EV, AUX>, DefaultListenerMap, SP, EV, AUX>
EventReceiver: EventReceiveProvider<Event, ParamProvider>,
EventSender: EventSendProvider<Event, ParamProvider>,
Event: GenericEvent + Copy,
ParamProvider: 'static + Debug,
>
EventManager<
EventReceiver,
DefaultSenderMap<EventSender, Event, ParamProvider>,
DefaultListenerMap,
EventSender,
Event,
ParamProvider,
>
{
/// Create an event manager where the sender table will be the [DefaultSenderMap]
/// and the listener table will be the [DefaultListenerMap].
pub fn new(event_receiver: ER) -> Self {
pub fn new(event_receiver: EventReceiver) -> Self {
Self {
listener_map: DefaultListenerMap::default(),
sender_map: DefaultSenderMap::default(),
@ -342,7 +381,7 @@ pub mod alloc_mod {
/// Simple implementation which uses a [HashMap] and a [Vec] internally.
#[derive(Default)]
pub struct DefaultListenerMap {
listeners: HashMap<ListenerKey, Vec<ChannelId>>,
listeners: HashMap<ListenerKey, Vec<ComponentId>>,
}
impl ListenerMapProvider for DefaultListenerMap {
@ -358,11 +397,11 @@ pub mod alloc_mod {
self.listeners.contains_key(key)
}
fn get_listener_ids(&self, key: &ListenerKey) -> Option<Iter<ChannelId>> {
fn get_listener_ids(&self, key: &ListenerKey) -> Option<Iter<ComponentId>> {
self.listeners.get(key).map(|vec| vec.iter())
}
fn add_listener(&mut self, key: ListenerKey, sender_id: ChannelId) -> bool {
fn add_listener(&mut self, key: ListenerKey, sender_id: ComponentId) -> bool {
if let Some(existing_list) = self.listeners.get_mut(&key) {
existing_list.push(sender_id);
} else {
@ -384,16 +423,19 @@ pub mod alloc_mod {
///
/// Simple implementation which uses a [HashMap] internally.
pub struct DefaultSenderMap<
SP: EventSendProvider<EV, AUX>,
EV: GenericEvent = EventU32,
AUX = Params,
EventSender: EventSendProvider<Event, ParamProvider>,
Event: GenericEvent = EventU32,
ParamProvider: Debug = Params,
> {
senders: HashMap<ChannelId, SP>,
phantom: PhantomData<(EV, AUX)>,
senders: HashMap<ComponentId, EventSender>,
phantom: PhantomData<(Event, ParamProvider)>,
}
impl<SP: EventSendProvider<EV, AUX>, EV: GenericEvent, AUX> Default
for DefaultSenderMap<SP, EV, AUX>
impl<
EventSender: EventSendProvider<Event, ParamProvider>,
Event: GenericEvent,
ParamProvider: Debug,
> Default for DefaultSenderMap<EventSender, Event, ParamProvider>
{
fn default() -> Self {
Self {
@ -403,21 +445,25 @@ pub mod alloc_mod {
}
}
impl<SP: EventSendProvider<EV, AUX>, EV: GenericEvent, AUX> SenderMapProvider<SP, EV, AUX>
for DefaultSenderMap<SP, EV, AUX>
impl<
EventSender: EventSendProvider<Event, ParamProvider>,
Event: GenericEvent,
ParamProvider: Debug,
> SenderMapProvider<EventSender, Event, ParamProvider>
for DefaultSenderMap<EventSender, Event, ParamProvider>
{
fn contains_send_event_provider(&self, id: &ChannelId) -> bool {
fn contains_send_event_provider(&self, id: &ComponentId) -> bool {
self.senders.contains_key(id)
}
fn get_send_event_provider(&self, id: &ChannelId) -> Option<&SP> {
fn get_send_event_provider(&self, id: &ComponentId) -> Option<&EventSender> {
self.senders
.get(id)
.filter(|sender| sender.channel_id() == *id)
.filter(|sender| sender.target_id() == *id)
}
fn add_send_event_provider(&mut self, send_provider: SP) -> bool {
let id = send_provider.channel_id();
fn add_send_event_provider(&mut self, send_provider: EventSender) -> bool {
let id = send_provider.target_id();
if self.senders.contains_key(&id) {
return false;
}
@ -428,26 +474,33 @@ pub mod alloc_mod {
#[cfg(feature = "std")]
pub mod std_mod {
use crate::queue::GenericReceiveError;
use super::*;
use std::sync::mpsc;
pub struct MpscEventReceiver<Event: GenericEvent + Send = EventU32> {
mpsc_receiver: mpsc::Receiver<(Event, Option<Params>)>,
receiver: mpsc::Receiver<EventMessage<Event>>,
}
impl<Event: GenericEvent + Send> MpscEventReceiver<Event> {
pub fn new(receiver: mpsc::Receiver<(Event, Option<Params>)>) -> Self {
Self {
mpsc_receiver: receiver,
}
pub fn new(receiver: mpsc::Receiver<EventMessage<Event>>) -> Self {
Self { receiver }
}
}
impl<Event: GenericEvent + Send> EventReceiveProvider<Event> for MpscEventReceiver<Event> {
fn try_recv_event(&self) -> Option<EventWithAuxData<Event>> {
if let Ok(event_and_data) = self.mpsc_receiver.try_recv() {
return Some(event_and_data);
type Error = GenericReceiveError;
fn try_recv_event(&self) -> Result<Option<EventMessage<Event>>, Self::Error> {
match self.receiver.try_recv() {
Ok(msg) => Ok(Some(msg)),
Err(e) => match e {
mpsc::TryRecvError::Empty => Ok(None),
mpsc::TryRecvError::Disconnected => {
Err(GenericReceiveError::TxDisconnected(None))
}
},
}
None
}
}
@ -458,23 +511,26 @@ pub mod std_mod {
/// send events.
#[derive(Clone)]
pub struct EventSenderMpsc<Event: GenericEvent + Send> {
id: u32,
sender: mpsc::Sender<(Event, Option<Params>)>,
target_id: ComponentId,
sender: mpsc::Sender<EventMessage<Event>>,
}
impl<Event: GenericEvent + Send> EventSenderMpsc<Event> {
pub fn new(id: u32, sender: mpsc::Sender<(Event, Option<Params>)>) -> Self {
Self { id, sender }
pub fn new(target_id: ComponentId, sender: mpsc::Sender<EventMessage<Event>>) -> Self {
Self { target_id, sender }
}
}
impl<Event: GenericEvent + Send> EventSendProvider<Event> for EventSenderMpsc<Event> {
fn channel_id(&self) -> u32 {
self.id
type Error = GenericSendError;
fn target_id(&self) -> ComponentId {
self.target_id
}
fn send(&self, event: Event, aux_data: Option<Params>) -> Result<(), GenericSendError> {
fn send(&self, event_msg: EventMessage<Event>) -> Result<(), GenericSendError> {
self.sender
.send((event, aux_data))
.send(event_msg)
.map_err(|_| GenericSendError::RxDisconnected)
}
}
@ -483,19 +539,19 @@ pub mod std_mod {
/// events. This has the advantage that the channel is bounded and thus more deterministic.
#[derive(Clone)]
pub struct EventSenderMpscBounded<Event: GenericEvent + Send> {
channel_id: u32,
sender: mpsc::SyncSender<(Event, Option<Params>)>,
target_id: ComponentId,
sender: mpsc::SyncSender<EventMessage<Event>>,
capacity: usize,
}
impl<Event: GenericEvent + Send> EventSenderMpscBounded<Event> {
pub fn new(
channel_id: u32,
sender: mpsc::SyncSender<(Event, Option<Params>)>,
target_id: ComponentId,
sender: mpsc::SyncSender<EventMessage<Event>>,
capacity: usize,
) -> Self {
Self {
channel_id,
target_id,
sender,
capacity,
}
@ -503,11 +559,14 @@ pub mod std_mod {
}
impl<Event: GenericEvent + Send> EventSendProvider<Event> for EventSenderMpscBounded<Event> {
fn channel_id(&self) -> u32 {
self.channel_id
type Error = GenericSendError;
fn target_id(&self) -> ComponentId {
self.target_id
}
fn send(&self, event: Event, aux_data: Option<Params>) -> Result<(), GenericSendError> {
if let Err(e) = self.sender.try_send((event, aux_data)) {
fn send(&self, event_msg: EventMessage<Event>) -> Result<(), Self::Error> {
if let Err(e) = self.sender.try_send(event_msg) {
return match e {
mpsc::TrySendError::Full(_) => {
Err(GenericSendError::QueueFull(Some(self.capacity as u32)))
@ -530,19 +589,20 @@ mod tests {
use super::*;
use crate::event_man::EventManager;
use crate::events::{EventU32, GenericEvent, Severity};
use crate::params::ParamsRaw;
use crate::params::{ParamsHeapless, ParamsRaw};
use crate::pus::test_util::{TEST_COMPONENT_ID_0, TEST_COMPONENT_ID_1};
use std::format;
use std::sync::mpsc::{self, channel, Receiver, Sender};
use std::sync::mpsc::{self};
const TEST_EVENT: EventU32 = EventU32::const_new(Severity::INFO, 0, 5);
fn check_next_event(
expected: EventU32,
receiver: &Receiver<EventU32WithAuxData>,
receiver: &mpsc::Receiver<EventMessageU32>,
) -> Option<Params> {
if let Ok(event) = receiver.try_recv() {
assert_eq!(event.0, expected);
return event.1;
if let Ok(event_msg) = receiver.try_recv() {
assert_eq!(event_msg.event, expected);
return event_msg.params;
}
None
}
@ -555,17 +615,16 @@ mod tests {
assert!(matches!(res, EventRoutingResult::Handled { .. }));
if let EventRoutingResult::Handled {
num_recipients,
event,
..
event_msg,
} = res
{
assert_eq!(event, expected);
assert_eq!(event_msg.event, expected);
assert_eq!(num_recipients, expected_num_sent);
}
}
fn generic_event_man() -> (Sender<EventU32WithAuxData>, EventManagerWithMpsc) {
let (event_sender, manager_queue) = channel();
fn generic_event_man() -> (mpsc::Sender<EventMessageU32>, EventManagerWithMpsc) {
let (event_sender, manager_queue) = mpsc::channel();
let event_man_receiver = MpscEventReceiver::new(manager_queue);
(event_sender, EventManager::new(event_man_receiver))
}
@ -575,48 +634,56 @@ mod tests {
let (event_sender, mut event_man) = generic_event_man();
let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap();
let event_grp_1_0 = EventU32::new(Severity::HIGH, 1, 0).unwrap();
let (single_event_sender, single_event_receiver) = channel();
let (single_event_sender, single_event_receiver) = mpsc::channel();
let single_event_listener = EventSenderMpsc::new(0, single_event_sender);
event_man.subscribe_single(&event_grp_0, single_event_listener.channel_id());
event_man.subscribe_single(&event_grp_0, single_event_listener.target_id());
event_man.add_sender(single_event_listener);
let (group_event_sender_0, group_event_receiver_0) = channel();
let (group_event_sender_0, group_event_receiver_0) = mpsc::channel();
let group_event_listener = EventU32SenderMpsc::new(1, group_event_sender_0);
event_man.subscribe_group(event_grp_1_0.group_id(), group_event_listener.channel_id());
event_man.subscribe_group(event_grp_1_0.group_id(), group_event_listener.target_id());
event_man.add_sender(group_event_listener);
let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
};
// Test event with one listener
event_sender
.send((event_grp_0, None))
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_grp_0))
.expect("Sending single error failed");
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_grp_0, 1);
let res = event_man.try_event_handling(&error_handler);
// assert!(res.is_ok());
check_handled_event(res, event_grp_0, 1);
check_next_event(event_grp_0, &single_event_receiver);
// Test event which is sent to all group listeners
event_sender
.send((event_grp_1_0, None))
.send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0))
.expect("Sending group error failed");
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_grp_1_0, 1);
let res = event_man.try_event_handling(&error_handler);
check_handled_event(res, event_grp_1_0, 1);
check_next_event(event_grp_1_0, &group_event_receiver_0);
}
#[test]
fn test_with_basic_aux_data() {
fn test_with_basic_params() {
let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
};
let (event_sender, mut event_man) = generic_event_man();
let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap();
let (single_event_sender, single_event_receiver) = channel();
let (single_event_sender, single_event_receiver) = mpsc::channel();
let single_event_listener = EventSenderMpsc::new(0, single_event_sender);
event_man.subscribe_single(&event_grp_0, single_event_listener.channel_id());
event_man.subscribe_single(&event_grp_0, single_event_listener.target_id());
event_man.add_sender(single_event_listener);
event_sender
.send((event_grp_0, Some(Params::Heapless((2_u32, 3_u32).into()))))
.send(EventMessage::new_with_params(
TEST_COMPONENT_ID_0.id(),
event_grp_0,
&Params::Heapless((2_u32, 3_u32).into()),
))
.expect("Sending group error failed");
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_grp_0, 1);
let res = event_man.try_event_handling(&error_handler);
check_handled_event(res, event_grp_0, 1);
let aux = check_next_event(event_grp_0, &single_event_receiver);
assert!(aux.is_some());
let aux = aux.unwrap();
@ -631,38 +698,37 @@ mod tests {
/// Test listening for multiple groups
#[test]
fn test_multi_group() {
let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
};
let (event_sender, mut event_man) = generic_event_man();
let res = event_man.try_event_handling();
assert!(res.is_ok());
let hres = res.unwrap();
assert!(matches!(hres, EventRoutingResult::Empty));
let res = event_man.try_event_handling(error_handler);
assert!(matches!(res, EventRoutingResult::Empty));
let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap();
let event_grp_1_0 = EventU32::new(Severity::HIGH, 1, 0).unwrap();
let (event_grp_0_sender, event_grp_0_receiver) = channel();
let (event_grp_0_sender, event_grp_0_receiver) = mpsc::channel();
let event_grp_0_and_1_listener = EventU32SenderMpsc::new(0, event_grp_0_sender);
event_man.subscribe_group(
event_grp_0.group_id(),
event_grp_0_and_1_listener.channel_id(),
event_grp_0_and_1_listener.target_id(),
);
event_man.subscribe_group(
event_grp_1_0.group_id(),
event_grp_0_and_1_listener.channel_id(),
event_grp_0_and_1_listener.target_id(),
);
event_man.add_sender(event_grp_0_and_1_listener);
event_sender
.send((event_grp_0, None))
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_grp_0))
.expect("Sending Event Group 0 failed");
event_sender
.send((event_grp_1_0, None))
.send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_grp_1_0))
.expect("Sendign Event Group 1 failed");
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_grp_0, 1);
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_grp_1_0, 1);
let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_grp_0, 1);
let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_grp_1_0, 1);
check_next_event(event_grp_0, &event_grp_0_receiver);
check_next_event(event_grp_1_0, &event_grp_0_receiver);
@ -672,42 +738,42 @@ mod tests {
/// to both group and single events from one listener
#[test]
fn test_listening_to_same_event_and_multi_type() {
let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
};
let (event_sender, mut event_man) = generic_event_man();
let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap();
let event_1 = EventU32::new(Severity::HIGH, 1, 0).unwrap();
let (event_0_tx_0, event_0_rx_0) = channel();
let (event_0_tx_1, event_0_rx_1) = channel();
let (event_0_tx_0, event_0_rx_0) = mpsc::channel();
let (event_0_tx_1, event_0_rx_1) = mpsc::channel();
let event_listener_0 = EventU32SenderMpsc::new(0, event_0_tx_0);
let event_listener_1 = EventU32SenderMpsc::new(1, event_0_tx_1);
let event_listener_0_sender_id = event_listener_0.channel_id();
let event_listener_0_sender_id = event_listener_0.target_id();
event_man.subscribe_single(&event_0, event_listener_0_sender_id);
event_man.add_sender(event_listener_0);
let event_listener_1_sender_id = event_listener_1.channel_id();
let event_listener_1_sender_id = event_listener_1.target_id();
event_man.subscribe_single(&event_0, event_listener_1_sender_id);
event_man.add_sender(event_listener_1);
event_sender
.send((event_0, None))
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0))
.expect("Triggering Event 0 failed");
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_0, 2);
let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_0, 2);
check_next_event(event_0, &event_0_rx_0);
check_next_event(event_0, &event_0_rx_1);
event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id);
event_sender
.send((event_0, None))
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0))
.expect("Triggering Event 0 failed");
event_sender
.send((event_1, None))
.send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_1))
.expect("Triggering Event 1 failed");
// 3 Events messages will be sent now
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_0, 2);
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_1, 1);
let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_0, 2);
let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_1, 1);
// Both the single event and the group event should arrive now
check_next_event(event_0, &event_0_rx_0);
check_next_event(event_1, &event_0_rx_0);
@ -716,36 +782,36 @@ mod tests {
event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id);
event_man.remove_duplicates(&ListenerKey::Group(event_1.group_id()));
event_sender
.send((event_1, None))
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_1))
.expect("Triggering Event 1 failed");
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_1, 1);
let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_1, 1);
}
#[test]
fn test_all_events_listener() {
let (event_sender, manager_queue) = channel();
let error_handler = |event_msg: &EventMessageU32, e: EventRoutingError| {
panic!("routing error occurred for event {:?}: {:?}", event_msg, e);
};
let (event_sender, manager_queue) = mpsc::channel();
let event_man_receiver = MpscEventReceiver::new(manager_queue);
let mut event_man = EventManagerWithMpsc::new(event_man_receiver);
let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap();
let event_1 = EventU32::new(Severity::HIGH, 1, 0).unwrap();
let (event_0_tx_0, all_events_rx) = channel();
let (event_0_tx_0, all_events_rx) = mpsc::channel();
let all_events_listener = EventU32SenderMpsc::new(0, event_0_tx_0);
event_man.subscribe_all(all_events_listener.channel_id());
event_man.subscribe_all(all_events_listener.target_id());
event_man.add_sender(all_events_listener);
event_sender
.send((event_0, None))
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), event_0))
.expect("Triggering event 0 failed");
event_sender
.send((event_1, None))
.send(EventMessage::new(TEST_COMPONENT_ID_1.id(), event_1))
.expect("Triggering event 1 failed");
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_0, 1);
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_1, 1);
let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_0, 1);
let res = event_man.try_event_handling(error_handler);
check_handled_event(res, event_1, 1);
check_next_event(event_0, &all_events_rx);
check_next_event(event_1, &all_events_rx);
}
@ -755,15 +821,15 @@ mod tests {
let (event_sender, _event_receiver) = mpsc::sync_channel(3);
let event_sender = EventU32SenderMpscBounded::new(1, event_sender, 3);
event_sender
.send_no_data(TEST_EVENT)
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT))
.expect("sending test event failed");
event_sender
.send_no_data(TEST_EVENT)
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT))
.expect("sending test event failed");
event_sender
.send_no_data(TEST_EVENT)
.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT))
.expect("sending test event failed");
let error = event_sender.send_no_data(TEST_EVENT);
let error = event_sender.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT));
if let Err(e) = error {
assert!(matches!(e, GenericSendError::QueueFull(Some(3))));
} else {
@ -775,7 +841,7 @@ mod tests {
let (event_sender, event_receiver) = mpsc::sync_channel(3);
let event_sender = EventU32SenderMpscBounded::new(1, event_sender, 3);
drop(event_receiver);
if let Err(e) = event_sender.send_no_data(TEST_EVENT) {
if let Err(e) = event_sender.send(EventMessage::new(TEST_COMPONENT_ID_0.id(), TEST_EVENT)) {
assert!(matches!(e, GenericSendError::RxDisconnected));
} else {
panic!("Expected error");

View File

@ -80,7 +80,7 @@ impl HasSeverity for SeverityHigh {
const SEVERITY: Severity = Severity::HIGH;
}
pub trait GenericEvent: EcssEnumeration {
pub trait GenericEvent: EcssEnumeration + Copy + Clone {
type Raw;
type GroupId;
type UniqueId;

View File

@ -1,4 +1,3 @@
//! # Hardware Abstraction Layer module
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub mod std;

View File

@ -4,11 +4,10 @@ use std::{
net::{SocketAddr, TcpListener, TcpStream},
};
use alloc::boxed::Box;
use crate::{
encoding::{ccsds::PacketIdLookup, parse_buffer_for_ccsds_space_packets},
encoding::parse_buffer_for_ccsds_space_packets,
tmtc::{ReceivesTc, TmPacketSource},
ValidatorU16Id,
};
use super::tcp_server::{
@ -16,17 +15,19 @@ use super::tcp_server::{
};
/// Concrete [TcpTcParser] implementation for the [TcpSpacepacketsServer].
pub struct SpacepacketsTcParser {
packet_id_lookup: Box<dyn PacketIdLookup + Send>,
pub struct SpacepacketsTcParser<PacketIdChecker: ValidatorU16Id> {
packet_id_lookup: PacketIdChecker,
}
impl SpacepacketsTcParser {
pub fn new(packet_id_lookup: Box<dyn PacketIdLookup + Send>) -> Self {
impl<PacketIdChecker: ValidatorU16Id> SpacepacketsTcParser<PacketIdChecker> {
pub fn new(packet_id_lookup: PacketIdChecker) -> Self {
Self { packet_id_lookup }
}
}
impl<TmError, TcError: 'static> TcpTcParser<TmError, TcError> for SpacepacketsTcParser {
impl<TmError, TcError: 'static, PacketIdChecker: ValidatorU16Id> TcpTcParser<TmError, TcError>
for SpacepacketsTcParser<PacketIdChecker>
{
fn handle_tc_parsing(
&mut self,
tc_buffer: &mut [u8],
@ -38,7 +39,7 @@ impl<TmError, TcError: 'static> TcpTcParser<TmError, TcError> for SpacepacketsTc
// Reader vec full, need to parse for packets.
conn_result.num_received_tcs += parse_buffer_for_ccsds_space_packets(
&mut tc_buffer[..current_write_idx],
self.packet_id_lookup.as_ref(),
&self.packet_id_lookup,
tc_receiver.upcast_mut(),
next_write_idx,
)
@ -95,6 +96,7 @@ pub struct TcpSpacepacketsServer<
TcError: 'static,
TmSource: TmPacketSource<Error = TmError>,
TcReceiver: ReceivesTc<Error = TcError>,
PacketIdChecker: ValidatorU16Id,
> {
generic_server: TcpTmtcGenericServer<
TmError,
@ -102,7 +104,7 @@ pub struct TcpSpacepacketsServer<
TmSource,
TcReceiver,
SpacepacketsTmSender,
SpacepacketsTcParser,
SpacepacketsTcParser<PacketIdChecker>,
>,
}
@ -111,7 +113,8 @@ impl<
TcError: 'static,
TmSource: TmPacketSource<Error = TmError>,
TcReceiver: ReceivesTc<Error = TcError>,
> TcpSpacepacketsServer<TmError, TcError, TmSource, TcReceiver>
PacketIdChecker: ValidatorU16Id,
> TcpSpacepacketsServer<TmError, TcError, TmSource, TcReceiver, PacketIdChecker>
{
///
/// ## Parameter
@ -127,12 +130,12 @@ impl<
cfg: ServerConfig,
tm_source: TmSource,
tc_receiver: TcReceiver,
packet_id_lookup: Box<dyn PacketIdLookup + Send>,
packet_id_checker: PacketIdChecker,
) -> Result<Self, std::io::Error> {
Ok(Self {
generic_server: TcpTmtcGenericServer::new(
cfg,
SpacepacketsTcParser::new(packet_id_lookup),
SpacepacketsTcParser::new(packet_id_checker),
SpacepacketsTmSender::default(),
tm_source,
tc_receiver,
@ -170,7 +173,7 @@ mod tests {
thread,
};
use alloc::{boxed::Box, sync::Arc};
use alloc::sync::Arc;
use hashbrown::HashSet;
use spacepackets::{
ecss::{tc::PusTcCreator, WritablePusPacket},
@ -185,21 +188,21 @@ mod tests {
use super::TcpSpacepacketsServer;
const TEST_APID_0: u16 = 0x02;
const TEST_PACKET_ID_0: PacketId = PacketId::const_tc(true, TEST_APID_0);
const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0);
const TEST_APID_1: u16 = 0x10;
const TEST_PACKET_ID_1: PacketId = PacketId::const_tc(true, TEST_APID_1);
const TEST_PACKET_ID_1: PacketId = PacketId::new_for_tc(true, TEST_APID_1);
fn generic_tmtc_server(
addr: &SocketAddr,
tc_receiver: SyncTcCacher,
tm_source: SyncTmSource,
packet_id_lookup: HashSet<PacketId>,
) -> TcpSpacepacketsServer<(), (), SyncTmSource, SyncTcCacher> {
) -> TcpSpacepacketsServer<(), (), SyncTmSource, SyncTcCacher, HashSet<PacketId>> {
TcpSpacepacketsServer::new(
ServerConfig::new(*addr, Duration::from_millis(2), 1024, 1024),
tm_source,
tc_receiver,
Box::new(packet_id_lookup),
packet_id_lookup,
)
.expect("TCP server generation failed")
}
@ -233,8 +236,8 @@ mod tests {
assert_eq!(conn_result.num_sent_tms, 0);
set_if_done.store(true, Ordering::Relaxed);
});
let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap();
let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
let ping_tc =
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
let tc_0 = ping_tc.to_vec().expect("packet generation failed");
let mut stream = TcpStream::connect(dest_addr).expect("connecting to TCP server failed");
stream
@ -265,13 +268,13 @@ mod tests {
// Add telemetry
let mut total_tm_len = 0;
let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap();
let verif_tm = PusTcCreator::new_simple(&mut sph, 1, 1, None, true);
let verif_tm =
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 1, 1, &[], true);
let tm_0 = verif_tm.to_vec().expect("writing packet failed");
total_tm_len += tm_0.len();
tm_source.add_tm(&tm_0);
let mut sph = SpHeader::tc_unseg(TEST_APID_1, 0, 0).unwrap();
let verif_tm = PusTcCreator::new_simple(&mut sph, 1, 3, None, true);
let verif_tm =
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 1, 3, &[], true);
let tm_1 = verif_tm.to_vec().expect("writing packet failed");
total_tm_len += tm_1.len();
tm_source.add_tm(&tm_1);
@ -312,14 +315,14 @@ mod tests {
.expect("setting reas timeout failed");
// Send telecommands
let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap();
let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
let ping_tc =
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
let tc_0 = ping_tc.to_vec().expect("ping tc creation failed");
stream
.write_all(&tc_0)
.expect("writing to TCP server failed");
let mut sph = SpHeader::tc_unseg(TEST_APID_1, 0, 0).unwrap();
let action_tc = PusTcCreator::new_simple(&mut sph, 8, 0, None, true);
let action_tc =
PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 8, 0, &[], true);
let tc_1 = action_tc.to_vec().expect("action tc creation failed");
stream
.write_all(&tc_1)

View File

@ -40,8 +40,8 @@ use std::vec::Vec;
/// let ping_receiver = PingReceiver::default();
/// let mut udp_tc_server = UdpTcServer::new(dest_addr, 2048, Box::new(ping_receiver))
/// .expect("Creating UDP TMTC server failed");
/// let mut sph = SpHeader::tc_unseg(0x02, 0, 0).unwrap();
/// let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
/// let sph = SpHeader::new_from_apid(0x02);
/// let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
/// let len = pus_tc
/// .write_to_bytes(&mut buf)
/// .expect("Error writing PUS TC packet");
@ -178,8 +178,8 @@ mod tests {
let mut udp_tc_server = UdpTcServer::new(dest_addr, 2048, Box::new(ping_receiver))
.expect("Creating UDP TMTC server failed");
is_send(&udp_tc_server);
let mut sph = SpHeader::tc_unseg(0x02, 0, 0).unwrap();
let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
let sph = SpHeader::new_from_apid(0x02);
let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
let len = pus_tc
.write_to_bytes(&mut buf)
.expect("Error writing PUS TC packet");

View File

@ -1,40 +1,40 @@
use crate::{
pus::verification::{TcStateAccepted, VerificationToken},
TargetId,
};
use crate::ComponentId;
pub type CollectionIntervalFactor = u32;
/// Unique Identifier for a certain housekeeping dataset.
pub type UniqueId = u32;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum HkRequest {
OneShot(UniqueId),
Enable(UniqueId),
Disable(UniqueId),
ModifyCollectionInterval(UniqueId, CollectionIntervalFactor),
pub struct HkRequest {
pub unique_id: UniqueId,
pub variant: HkRequestVariant,
}
impl HkRequest {
pub fn new(unique_id: UniqueId, variant: HkRequestVariant) -> Self {
Self { unique_id, variant }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum HkRequestVariant {
OneShot,
EnablePeriodic,
DisablePeriodic,
ModifyCollectionInterval(CollectionIntervalFactor),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct TargetedHkRequest {
pub target_id: TargetId,
pub hk_request: HkRequest,
pub target_id: ComponentId,
pub hk_request: HkRequestVariant,
}
impl TargetedHkRequest {
pub fn new(target_id: TargetId, hk_request: HkRequest) -> Self {
pub fn new(target_id: ComponentId, hk_request: HkRequestVariant) -> Self {
Self {
target_id,
hk_request,
}
}
}
pub trait PusHkRequestRouter {
type Error;
fn route(
&self,
target_id: TargetId,
hk_request: HkRequest,
token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error>;
}

View File

@ -14,7 +14,7 @@
//! - The [pus] module which provides special support for projects using
//! the [ECSS PUS C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
#![no_std]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
#![cfg_attr(docs_rs, feature(doc_auto_cfg))]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
@ -23,16 +23,15 @@ extern crate downcast_rs;
extern crate std;
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub mod cfdp;
pub mod encoding;
pub mod event_man;
pub mod events;
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub mod executable;
pub mod hal;
pub mod objects;
#[cfg(feature = "std")]
pub mod mode_tree;
pub mod pool;
pub mod power;
pub mod pus;
@ -40,6 +39,7 @@ pub mod queue;
pub mod request;
pub mod res_code;
pub mod seq_count;
pub mod time;
pub mod tmtc;
pub mod action;
@ -49,8 +49,70 @@ pub mod params;
pub use spacepackets;
/// Generic channel ID type.
pub type ChannelId = u32;
use spacepackets::PacketId;
/// Generic target ID type.
pub type TargetId = u64;
/// Generic component ID type.
pub type ComponentId = u64;
pub trait ValidatorU16Id {
fn validate(&self, id: u16) -> bool;
}
#[cfg(feature = "alloc")]
impl ValidatorU16Id for alloc::vec::Vec<u16> {
fn validate(&self, id: u16) -> bool {
self.contains(&id)
}
}
#[cfg(feature = "alloc")]
impl ValidatorU16Id for hashbrown::HashSet<u16> {
fn validate(&self, id: u16) -> bool {
self.contains(&id)
}
}
impl ValidatorU16Id for [u16] {
fn validate(&self, id: u16) -> bool {
self.binary_search(&id).is_ok()
}
}
impl ValidatorU16Id for &[u16] {
fn validate(&self, id: u16) -> bool {
self.binary_search(&id).is_ok()
}
}
#[cfg(feature = "alloc")]
impl ValidatorU16Id for alloc::vec::Vec<spacepackets::PacketId> {
fn validate(&self, packet_id: u16) -> bool {
self.contains(&PacketId::from(packet_id))
}
}
#[cfg(feature = "alloc")]
impl ValidatorU16Id for hashbrown::HashSet<spacepackets::PacketId> {
fn validate(&self, packet_id: u16) -> bool {
self.contains(&PacketId::from(packet_id))
}
}
#[cfg(feature = "std")]
impl ValidatorU16Id for std::collections::HashSet<PacketId> {
fn validate(&self, packet_id: u16) -> bool {
self.contains(&PacketId::from(packet_id))
}
}
impl ValidatorU16Id for [PacketId] {
fn validate(&self, packet_id: u16) -> bool {
self.binary_search(&PacketId::from(packet_id)).is_ok()
}
}
impl ValidatorU16Id for &[PacketId] {
fn validate(&self, packet_id: u16) -> bool {
self.binary_search(&PacketId::from(packet_id)).is_ok()
}
}

View File

@ -1,67 +1,95 @@
use core::mem::size_of;
use satrs_shared::res_code::ResultU16;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use spacepackets::ByteConversionError;
use crate::TargetId;
#[cfg(feature = "alloc")]
pub use alloc_mod::*;
#[cfg(feature = "std")]
pub use std_mod::*;
use crate::{
queue::GenericTargetedMessagingError,
request::{GenericMessage, MessageMetadata, MessageReceiver, MessageReceiverWithId, RequestId},
ComponentId,
};
pub type Mode = u32;
pub type Submode = u16;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ModeAndSubmode {
mode: u32,
submode: u16,
mode: Mode,
submode: Submode,
}
impl ModeAndSubmode {
pub const fn new_mode_only(mode: u32) -> Self {
pub const RAW_LEN: usize = size_of::<Mode>() + size_of::<Submode>();
pub const fn new_mode_only(mode: Mode) -> Self {
Self { mode, submode: 0 }
}
pub const fn new(mode: u32, submode: u16) -> Self {
pub const fn new(mode: Mode, submode: Submode) -> Self {
Self { mode, submode }
}
pub fn raw_len() -> usize {
size_of::<u32>() + size_of::<u16>()
}
pub fn from_be_bytes(buf: &[u8]) -> Result<Self, ByteConversionError> {
if buf.len() < 6 {
return Err(ByteConversionError::FromSliceTooSmall {
expected: 6,
expected: Self::RAW_LEN,
found: buf.len(),
});
}
Ok(Self {
mode: u32::from_be_bytes(buf[0..4].try_into().unwrap()),
submode: u16::from_be_bytes(buf[4..6].try_into().unwrap()),
mode: Mode::from_be_bytes(buf[0..size_of::<Mode>()].try_into().unwrap()),
submode: Submode::from_be_bytes(
buf[size_of::<Mode>()..size_of::<Mode>() + size_of::<Submode>()]
.try_into()
.unwrap(),
),
})
}
pub fn mode(&self) -> u32 {
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
if buf.len() < Self::RAW_LEN {
return Err(ByteConversionError::ToSliceTooSmall {
expected: Self::RAW_LEN,
found: buf.len(),
});
}
buf[0..size_of::<Mode>()].copy_from_slice(&self.mode.to_be_bytes());
buf[size_of::<Mode>()..Self::RAW_LEN].copy_from_slice(&self.submode.to_be_bytes());
Ok(Self::RAW_LEN)
}
pub fn mode(&self) -> Mode {
self.mode
}
pub fn submode(&self) -> u16 {
pub fn submode(&self) -> Submode {
self.submode
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TargetedModeCommand {
pub address: TargetId,
pub address: ComponentId,
pub mode_submode: ModeAndSubmode,
}
impl TargetedModeCommand {
pub const fn new(address: TargetId, mode_submode: ModeAndSubmode) -> Self {
pub const fn new(address: ComponentId, mode_submode: ModeAndSubmode) -> Self {
Self {
address,
mode_submode,
}
}
pub fn address(&self) -> TargetId {
pub fn address(&self) -> ComponentId {
self.address
}
@ -81,6 +109,8 @@ impl TargetedModeCommand {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ModeRequest {
/// Mode information. Can be used to notify other components of changed modes.
ModeInfo(ModeAndSubmode),
SetMode(ModeAndSubmode),
ReadMode,
AnnounceMode,
@ -90,6 +120,479 @@ pub enum ModeRequest {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TargetedModeRequest {
target_id: TargetId,
target_id: ComponentId,
mode_request: ModeRequest,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ModeReply {
/// Reply to a mode request to confirm the commanded mode was reached.
ModeReply(ModeAndSubmode),
// Can not reach the commanded mode. Contains a reason as a [ResultU16].
CantReachMode(ResultU16),
/// We are in the wrong mode for unknown reasons. Contains the expected and reached mode.
WrongMode {
expected: ModeAndSubmode,
reached: ModeAndSubmode,
},
}
pub type GenericModeReply = GenericMessage<ModeReply>;
pub trait ModeRequestSender {
fn local_channel_id(&self) -> ComponentId;
fn send_mode_request(
&self,
request_id: RequestId,
target_id: ComponentId,
request: ModeRequest,
) -> Result<(), GenericTargetedMessagingError>;
}
pub trait ModeRequestReceiver {
fn try_recv_mode_request(
&self,
) -> Result<Option<GenericMessage<ModeRequest>>, GenericTargetedMessagingError>;
}
impl<R: MessageReceiver<ModeRequest>> ModeRequestReceiver
for MessageReceiverWithId<ModeRequest, R>
{
fn try_recv_mode_request(
&self,
) -> Result<Option<GenericMessage<ModeRequest>>, GenericTargetedMessagingError> {
self.try_recv_message()
}
}
#[derive(Debug, Clone)]
pub enum ModeError {
Messaging(GenericTargetedMessagingError),
}
impl From<GenericTargetedMessagingError> for ModeError {
fn from(value: GenericTargetedMessagingError) -> Self {
Self::Messaging(value)
}
}
pub trait ModeProvider {
fn mode_and_submode(&self) -> ModeAndSubmode;
fn mode(&self) -> Mode {
self.mode_and_submode().mode()
}
fn submode(&self) -> Submode {
self.mode_and_submode().submode()
}
}
pub trait ModeRequestHandler: ModeProvider {
type Error;
fn start_transition(
&mut self,
requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode,
) -> Result<(), Self::Error>;
fn announce_mode(&self, requestor_info: Option<MessageMetadata>, recursive: bool);
fn handle_mode_reached(
&mut self,
requestor_info: Option<MessageMetadata>,
) -> Result<(), Self::Error>;
fn handle_mode_info(
&mut self,
requestor_info: MessageMetadata,
info: ModeAndSubmode,
) -> Result<(), Self::Error>;
fn send_mode_reply(
&self,
requestor_info: MessageMetadata,
reply: ModeReply,
) -> Result<(), Self::Error>;
fn handle_mode_request(
&mut self,
request: GenericMessage<ModeRequest>,
) -> Result<(), Self::Error> {
match request.message {
ModeRequest::SetMode(mode_and_submode) => {
self.start_transition(request.requestor_info, mode_and_submode)
}
ModeRequest::ReadMode => self.send_mode_reply(
request.requestor_info,
ModeReply::ModeReply(self.mode_and_submode()),
),
ModeRequest::AnnounceMode => {
self.announce_mode(Some(request.requestor_info), false);
Ok(())
}
ModeRequest::AnnounceModeRecursive => {
self.announce_mode(Some(request.requestor_info), true);
Ok(())
}
ModeRequest::ModeInfo(info) => self.handle_mode_info(request.requestor_info, info),
}
}
}
pub trait ModeReplyReceiver {
fn try_recv_mode_reply(
&self,
) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError>;
}
impl<R: MessageReceiver<ModeReply>> ModeReplyReceiver for MessageReceiverWithId<ModeReply, R> {
fn try_recv_mode_reply(
&self,
) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError> {
self.try_recv_message()
}
}
pub trait ModeReplySender {
fn local_channel_id(&self) -> ComponentId;
/// The requestor is assumed to be the target of the reply.
fn send_mode_reply(
&self,
requestor_info: MessageMetadata,
reply: ModeReply,
) -> Result<(), GenericTargetedMessagingError>;
}
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use crate::{
mode::ModeRequest,
queue::GenericTargetedMessagingError,
request::{
MessageMetadata, MessageSender, MessageSenderAndReceiver, MessageSenderMap,
RequestAndReplySenderAndReceiver, RequestId,
},
ComponentId,
};
use super::*;
impl<S: MessageSender<ModeReply>> MessageSenderMap<ModeReply, S> {
pub fn send_mode_reply(
&self,
requestor_info: MessageMetadata,
target_id: ComponentId,
request: ModeReply,
) -> Result<(), GenericTargetedMessagingError> {
self.send_message(requestor_info, target_id, request)
}
pub fn add_reply_target(&mut self, target_id: ComponentId, request_sender: S) {
self.add_message_target(target_id, request_sender)
}
}
impl<FROM, S: MessageSender<ModeReply>, R: MessageReceiver<FROM>> ModeReplySender
for MessageSenderAndReceiver<ModeReply, FROM, S, R>
{
fn local_channel_id(&self) -> ComponentId {
self.local_channel_id_generic()
}
fn send_mode_reply(
&self,
requestor_info: MessageMetadata,
request: ModeReply,
) -> Result<(), GenericTargetedMessagingError> {
self.message_sender_map.send_mode_reply(
MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()),
requestor_info.sender_id(),
request,
)
}
}
impl<TO, S: MessageSender<TO>, R: MessageReceiver<ModeReply>> ModeReplyReceiver
for MessageSenderAndReceiver<TO, ModeReply, S, R>
{
fn try_recv_mode_reply(
&self,
) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError> {
self.message_receiver.try_recv_message()
}
}
impl<
REQUEST,
S0: MessageSender<REQUEST>,
R0: MessageReceiver<ModeReply>,
S1: MessageSender<ModeReply>,
R1: MessageReceiver<REQUEST>,
> RequestAndReplySenderAndReceiver<REQUEST, ModeReply, S0, R0, S1, R1>
{
pub fn add_reply_target(&mut self, target_id: ComponentId, reply_sender: S1) {
self.reply_sender_map
.add_message_target(target_id, reply_sender)
}
}
impl<
REQUEST,
S0: MessageSender<REQUEST>,
R0: MessageReceiver<ModeReply>,
S1: MessageSender<ModeReply>,
R1: MessageReceiver<REQUEST>,
> ModeReplySender for RequestAndReplySenderAndReceiver<REQUEST, ModeReply, S0, R0, S1, R1>
{
fn local_channel_id(&self) -> ComponentId {
self.local_channel_id_generic()
}
fn send_mode_reply(
&self,
requestor_info: MessageMetadata,
request: ModeReply,
) -> Result<(), GenericTargetedMessagingError> {
self.reply_sender_map.send_mode_reply(
MessageMetadata::new(requestor_info.request_id(), self.local_channel_id()),
requestor_info.sender_id(),
request,
)
}
}
impl<
REQUEST,
S0: MessageSender<REQUEST>,
R0: MessageReceiver<ModeReply>,
S1: MessageSender<ModeReply>,
R1: MessageReceiver<REQUEST>,
> ModeReplyReceiver
for RequestAndReplySenderAndReceiver<REQUEST, ModeReply, S0, R0, S1, R1>
{
fn try_recv_mode_reply(
&self,
) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError> {
self.reply_receiver.try_recv_message()
}
}
/// Helper type definition for a mode handler which can handle mode requests.
pub type ModeRequestHandlerInterface<S, R> =
MessageSenderAndReceiver<ModeReply, ModeRequest, S, R>;
impl<S: MessageSender<ModeReply>, R: MessageReceiver<ModeRequest>>
ModeRequestHandlerInterface<S, R>
{
pub fn try_recv_mode_request(
&self,
) -> Result<Option<GenericMessage<ModeRequest>>, GenericTargetedMessagingError> {
self.try_recv_message()
}
pub fn send_mode_reply(
&self,
requestor_info: MessageMetadata,
reply: ModeReply,
) -> Result<(), GenericTargetedMessagingError> {
self.send_message(
requestor_info.request_id(),
requestor_info.sender_id(),
reply,
)
}
}
/// Helper type defintion for a mode handler object which can send mode requests and receive
/// mode replies.
pub type ModeRequestorInterface<S, R> = MessageSenderAndReceiver<ModeRequest, ModeReply, S, R>;
impl<S: MessageSender<ModeRequest>, R: MessageReceiver<ModeReply>> ModeRequestorInterface<S, R> {
pub fn try_recv_mode_reply(
&self,
) -> Result<Option<GenericMessage<ModeReply>>, GenericTargetedMessagingError> {
self.try_recv_message()
}
pub fn send_mode_request(
&self,
request_id: RequestId,
target_id: ComponentId,
reply: ModeRequest,
) -> Result<(), GenericTargetedMessagingError> {
self.send_message(request_id, target_id, reply)
}
}
/// Helper type defintion for a mode handler object which can both send mode requests and
/// process mode requests.
pub type ModeInterface<S0, R0, S1, R1> =
RequestAndReplySenderAndReceiver<ModeRequest, ModeReply, S0, R0, S1, R1>;
impl<S: MessageSender<ModeRequest>> MessageSenderMap<ModeRequest, S> {
pub fn send_mode_request(
&self,
requestor_info: MessageMetadata,
target_id: ComponentId,
request: ModeRequest,
) -> Result<(), GenericTargetedMessagingError> {
self.send_message(requestor_info, target_id, request)
}
pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: S) {
self.add_message_target(target_id, request_sender)
}
}
/*
impl<S: MessageSender<ModeRequest>> ModeRequestSender for MessageSenderMapWithId<ModeRequest, S> {
fn local_channel_id(&self) -> ComponentId {
self.local_channel_id
}
fn send_mode_request(
&self,
request_id: RequestId,
target_id: ComponentId,
request: ModeRequest,
) -> Result<(), GenericTargetedMessagingError> {
self.send_message(request_id, target_id, request)
}
}
*/
impl<TO, S: MessageSender<TO>, R: MessageReceiver<ModeRequest>> ModeRequestReceiver
for MessageSenderAndReceiver<TO, ModeRequest, S, R>
{
fn try_recv_mode_request(
&self,
) -> Result<Option<GenericMessage<ModeRequest>>, GenericTargetedMessagingError> {
self.message_receiver.try_recv_message()
}
}
impl<FROM, S: MessageSender<ModeRequest>, R: MessageReceiver<FROM>> ModeRequestSender
for MessageSenderAndReceiver<ModeRequest, FROM, S, R>
{
fn local_channel_id(&self) -> ComponentId {
self.local_channel_id_generic()
}
fn send_mode_request(
&self,
request_id: RequestId,
target_id: ComponentId,
request: ModeRequest,
) -> Result<(), GenericTargetedMessagingError> {
self.message_sender_map.send_mode_request(
MessageMetadata::new(request_id, self.local_channel_id()),
target_id,
request,
)
}
}
impl<
REPLY,
S0: MessageSender<ModeRequest>,
R0: MessageReceiver<REPLY>,
S1: MessageSender<REPLY>,
R1: MessageReceiver<ModeRequest>,
> RequestAndReplySenderAndReceiver<ModeRequest, REPLY, S0, R0, S1, R1>
{
pub fn add_request_target(&mut self, target_id: ComponentId, request_sender: S0) {
self.request_sender_map
.add_message_target(target_id, request_sender)
}
}
impl<
REPLY,
S0: MessageSender<ModeRequest>,
R0: MessageReceiver<REPLY>,
S1: MessageSender<REPLY>,
R1: MessageReceiver<ModeRequest>,
> ModeRequestSender
for RequestAndReplySenderAndReceiver<ModeRequest, REPLY, S0, R0, S1, R1>
{
fn local_channel_id(&self) -> ComponentId {
self.local_channel_id_generic()
}
fn send_mode_request(
&self,
request_id: RequestId,
target_id: ComponentId,
request: ModeRequest,
) -> Result<(), GenericTargetedMessagingError> {
self.request_sender_map.send_mode_request(
MessageMetadata::new(request_id, self.local_channel_id()),
target_id,
request,
)
}
}
impl<
REPLY,
S0: MessageSender<ModeRequest>,
R0: MessageReceiver<REPLY>,
S1: MessageSender<REPLY>,
R1: MessageReceiver<ModeRequest>,
> ModeRequestReceiver
for RequestAndReplySenderAndReceiver<ModeRequest, REPLY, S0, R0, S1, R1>
{
fn try_recv_mode_request(
&self,
) -> Result<Option<GenericMessage<ModeRequest>>, GenericTargetedMessagingError> {
self.request_receiver.try_recv_message()
}
}
}
#[cfg(feature = "std")]
pub mod std_mod {
use std::sync::mpsc;
use crate::request::GenericMessage;
use super::*;
pub type ModeRequestHandlerMpsc = ModeRequestHandlerInterface<
mpsc::Sender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
>;
pub type ModeRequestHandlerMpscBounded = ModeRequestHandlerInterface<
mpsc::SyncSender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
>;
pub type ModeRequestorMpsc = ModeRequestorInterface<
mpsc::Sender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
>;
pub type ModeRequestorBoundedMpsc = ModeRequestorInterface<
mpsc::SyncSender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
>;
pub type ModeRequestorAndHandlerMpsc = ModeInterface<
mpsc::Sender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
mpsc::Sender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
>;
pub type ModeRequestorAndHandlerMpscBounded = ModeInterface<
mpsc::SyncSender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
mpsc::SyncSender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
>;
}
#[cfg(test)]
mod tests {}

37
satrs/src/mode_tree.rs Normal file
View File

@ -0,0 +1,37 @@
use alloc::vec::Vec;
use hashbrown::HashMap;
use crate::{
mode::{Mode, ModeAndSubmode, Submode},
ComponentId,
};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TableEntryType {
/// Target table containing information of the expected children modes for given mode.
Target,
/// Sequence table which contains information about how to reach a target table, including
/// the order of the sequences.
Sequence,
}
pub struct ModeTableEntry {
/// Name of respective table entry.
pub name: &'static str,
/// Target channel ID.
pub channel_id: ComponentId,
pub mode_submode: ModeAndSubmode,
pub allowed_submode_mask: Option<Submode>,
pub check_success: bool,
}
pub struct ModeTableMapValue {
/// Name for a given mode table entry.
pub name: &'static str,
pub entries: Vec<ModeTableEntry>,
}
pub type ModeTable = HashMap<Mode, ModeTableMapValue>;
#[cfg(test)]
mod tests {}

View File

@ -1,308 +0,0 @@
//! # Module providing addressable object support and a manager for them
//!
//! Each addressable object can be identified using an [object ID][ObjectId].
//! The [system object][ManagedSystemObject] trait also allows storing these objects into the
//! [object manager][ObjectManager]. They can then be retrieved and casted back to a known type
//! using the object ID.
//!
//! # Examples
//!
//! ```rust
//! use std::any::Any;
//! use std::error::Error;
//! use satrs::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
//!
//! struct ExampleSysObj {
//! id: ObjectId,
//! dummy: u32,
//! was_initialized: bool,
//! }
//!
//! impl ExampleSysObj {
//! fn new(id: ObjectId, dummy: u32) -> ExampleSysObj {
//! ExampleSysObj {
//! id,
//! dummy,
//! was_initialized: false,
//! }
//! }
//! }
//!
//! impl SystemObject for ExampleSysObj {
//! type Error = ();
//! fn get_object_id(&self) -> &ObjectId {
//! &self.id
//! }
//!
//! fn initialize(&mut self) -> Result<(), Self::Error> {
//! self.was_initialized = true;
//! Ok(())
//! }
//! }
//!
//! impl ManagedSystemObject for ExampleSysObj {}
//!
//! let mut obj_manager = ObjectManager::default();
//! let obj_id = ObjectId { id: 0, name: "Example 0"};
//! let example_obj = ExampleSysObj::new(obj_id, 42);
//! obj_manager.insert(Box::new(example_obj));
//! let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get_ref(&obj_id);
//! let example_obj = obj_back_casted.unwrap();
//! assert_eq!(example_obj.id, obj_id);
//! assert_eq!(example_obj.dummy, 42);
//! ```
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
pub use alloc_mod::*;
#[cfg(feature = "alloc")]
use downcast_rs::Downcast;
#[cfg(feature = "alloc")]
use hashbrown::HashMap;
#[cfg(feature = "std")]
use std::error::Error;
use crate::TargetId;
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
pub struct ObjectId {
pub id: TargetId,
pub name: &'static str,
}
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use super::*;
/// Each object which is stored inside the [object manager][ObjectManager] needs to implemented
/// this trait
pub trait SystemObject: Downcast {
type Error;
fn get_object_id(&self) -> &ObjectId;
fn initialize(&mut self) -> Result<(), Self::Error>;
}
downcast_rs::impl_downcast!(SystemObject assoc Error);
pub trait ManagedSystemObject: SystemObject + Send {}
downcast_rs::impl_downcast!(ManagedSystemObject assoc Error);
/// Helper module to manage multiple [ManagedSystemObjects][ManagedSystemObject] by mapping them
/// using an [object ID][ObjectId]
#[cfg(feature = "alloc")]
pub struct ObjectManager<E> {
obj_map: HashMap<ObjectId, Box<dyn ManagedSystemObject<Error = E>>>,
}
#[cfg(feature = "alloc")]
impl<E: 'static> Default for ObjectManager<E> {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "alloc")]
impl<E: 'static> ObjectManager<E> {
pub fn new() -> Self {
ObjectManager {
obj_map: HashMap::new(),
}
}
pub fn insert(&mut self, sys_obj: Box<dyn ManagedSystemObject<Error = E>>) -> bool {
let obj_id = sys_obj.get_object_id();
if self.obj_map.contains_key(obj_id) {
return false;
}
self.obj_map.insert(*obj_id, sys_obj).is_none()
}
/// Initializes all System Objects in the hash map and returns the number of successful
/// initializations
pub fn initialize(&mut self) -> Result<u32, Box<dyn Error>> {
let mut init_success = 0;
for val in self.obj_map.values_mut() {
if val.initialize().is_ok() {
init_success += 1
}
}
Ok(init_success)
}
/// Retrieve a reference to an object stored inside the manager. The type to retrieve needs to
/// be explicitly passed as a generic parameter or specified on the left hand side of the
/// expression.
pub fn get_ref<T: ManagedSystemObject<Error = E>>(&self, key: &ObjectId) -> Option<&T> {
self.obj_map.get(key).and_then(|o| o.downcast_ref::<T>())
}
/// Retrieve a mutable reference to an object stored inside the manager. The type to retrieve
/// needs to be explicitly passed as a generic parameter or specified on the left hand side
/// of the expression.
pub fn get_mut<T: ManagedSystemObject<Error = E>>(
&mut self,
key: &ObjectId,
) -> Option<&mut T> {
self.obj_map
.get_mut(key)
.and_then(|o| o.downcast_mut::<T>())
}
}
}
#[cfg(test)]
mod tests {
use crate::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
use std::boxed::Box;
use std::string::String;
use std::sync::{Arc, Mutex};
use std::thread;
struct ExampleSysObj {
id: ObjectId,
dummy: u32,
was_initialized: bool,
}
impl ExampleSysObj {
fn new(id: ObjectId, dummy: u32) -> ExampleSysObj {
ExampleSysObj {
id,
dummy,
was_initialized: false,
}
}
}
impl SystemObject for ExampleSysObj {
type Error = ();
fn get_object_id(&self) -> &ObjectId {
&self.id
}
fn initialize(&mut self) -> Result<(), Self::Error> {
self.was_initialized = true;
Ok(())
}
}
impl ManagedSystemObject for ExampleSysObj {}
struct OtherExampleObject {
id: ObjectId,
string: String,
was_initialized: bool,
}
impl SystemObject for OtherExampleObject {
type Error = ();
fn get_object_id(&self) -> &ObjectId {
&self.id
}
fn initialize(&mut self) -> Result<(), Self::Error> {
self.was_initialized = true;
Ok(())
}
}
impl ManagedSystemObject for OtherExampleObject {}
#[test]
fn test_obj_manager_simple() {
let mut obj_manager = ObjectManager::default();
let expl_obj_id = ObjectId {
id: 0,
name: "Example 0",
};
let example_obj = ExampleSysObj::new(expl_obj_id, 42);
assert!(obj_manager.insert(Box::new(example_obj)));
let res = obj_manager.initialize();
assert!(res.is_ok());
assert_eq!(res.unwrap(), 1);
let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get_ref(&expl_obj_id);
assert!(obj_back_casted.is_some());
let expl_obj_back_casted = obj_back_casted.unwrap();
assert_eq!(expl_obj_back_casted.dummy, 42);
assert!(expl_obj_back_casted.was_initialized);
let second_obj_id = ObjectId {
id: 12,
name: "Example 1",
};
let second_example_obj = OtherExampleObject {
id: second_obj_id,
string: String::from("Hello Test"),
was_initialized: false,
};
assert!(obj_manager.insert(Box::new(second_example_obj)));
let res = obj_manager.initialize();
assert!(res.is_ok());
assert_eq!(res.unwrap(), 2);
let obj_back_casted: Option<&OtherExampleObject> = obj_manager.get_ref(&second_obj_id);
assert!(obj_back_casted.is_some());
let expl_obj_back_casted = obj_back_casted.unwrap();
assert_eq!(expl_obj_back_casted.string, String::from("Hello Test"));
assert!(expl_obj_back_casted.was_initialized);
let existing_obj_id = ObjectId {
id: 12,
name: "Example 1",
};
let invalid_obj = OtherExampleObject {
id: existing_obj_id,
string: String::from("Hello Test"),
was_initialized: false,
};
assert!(!obj_manager.insert(Box::new(invalid_obj)));
}
#[test]
fn object_man_threaded() {
let obj_manager = Arc::new(Mutex::new(ObjectManager::new()));
let expl_obj_id = ObjectId {
id: 0,
name: "Example 0",
};
let example_obj = ExampleSysObj::new(expl_obj_id, 42);
let second_obj_id = ObjectId {
id: 12,
name: "Example 1",
};
let second_example_obj = OtherExampleObject {
id: second_obj_id,
string: String::from("Hello Test"),
was_initialized: false,
};
let mut obj_man_handle = obj_manager.lock().expect("Mutex lock failed");
assert!(obj_man_handle.insert(Box::new(example_obj)));
assert!(obj_man_handle.insert(Box::new(second_example_obj)));
let res = obj_man_handle.initialize();
std::mem::drop(obj_man_handle);
assert!(res.is_ok());
assert_eq!(res.unwrap(), 2);
let obj_man_0 = obj_manager.clone();
let jh0 = thread::spawn(move || {
let locked_man = obj_man_0.lock().expect("Mutex lock failed");
let obj_back_casted: Option<&ExampleSysObj> = locked_man.get_ref(&expl_obj_id);
assert!(obj_back_casted.is_some());
let expl_obj_back_casted = obj_back_casted.unwrap();
assert_eq!(expl_obj_back_casted.dummy, 42);
assert!(expl_obj_back_casted.was_initialized);
std::mem::drop(locked_man)
});
let jh1 = thread::spawn(move || {
let locked_man = obj_manager.lock().expect("Mutex lock failed");
let obj_back_casted: Option<&OtherExampleObject> = locked_man.get_ref(&second_obj_id);
assert!(obj_back_casted.is_some());
let expl_obj_back_casted = obj_back_casted.unwrap();
assert_eq!(expl_obj_back_casted.string, String::from("Hello Test"));
assert!(expl_obj_back_casted.was_initialized);
std::mem::drop(locked_man)
});
jh0.join().expect("Joining thread 0 failed");
jh1.join().expect("Joining thread 1 failed");
}
}

View File

@ -60,21 +60,28 @@ use alloc::vec::Vec;
/// Generic trait which is used for objects which can be converted into a raw network (big) endian
/// byte format.
pub trait WritableToBeBytes {
fn raw_len(&self) -> usize;
fn written_len(&self) -> usize;
/// Writes the object to a raw buffer in network endianness (big)
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
#[cfg(feature = "alloc")]
fn to_vec(&self) -> Result<Vec<u8>, ByteConversionError> {
let mut vec = alloc::vec![0; self.written_len()];
self.write_to_be_bytes(&mut vec)?;
Ok(vec)
}
}
macro_rules! param_to_be_bytes_impl {
($Newtype: ident) => {
impl WritableToBeBytes for $Newtype {
#[inline]
fn raw_len(&self) -> usize {
fn written_len(&self) -> usize {
size_of::<<Self as ToBeBytes>::ByteArray>()
}
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
let raw_len = self.raw_len();
let raw_len = WritableToBeBytes::written_len(self);
if buf.len() < raw_len {
return Err(ByteConversionError::ToSliceTooSmall {
found: buf.len(),
@ -382,32 +389,32 @@ pub enum ParamsRaw {
}
impl WritableToBeBytes for ParamsRaw {
fn raw_len(&self) -> usize {
fn written_len(&self) -> usize {
match self {
ParamsRaw::U8(v) => v.raw_len(),
ParamsRaw::U8Pair(v) => v.raw_len(),
ParamsRaw::U8Triplet(v) => v.raw_len(),
ParamsRaw::I8(v) => v.raw_len(),
ParamsRaw::I8Pair(v) => v.raw_len(),
ParamsRaw::I8Triplet(v) => v.raw_len(),
ParamsRaw::U16(v) => v.raw_len(),
ParamsRaw::U16Pair(v) => v.raw_len(),
ParamsRaw::U16Triplet(v) => v.raw_len(),
ParamsRaw::I16(v) => v.raw_len(),
ParamsRaw::I16Pair(v) => v.raw_len(),
ParamsRaw::I16Triplet(v) => v.raw_len(),
ParamsRaw::U32(v) => v.raw_len(),
ParamsRaw::U32Pair(v) => v.raw_len(),
ParamsRaw::U32Triplet(v) => v.raw_len(),
ParamsRaw::I32(v) => v.raw_len(),
ParamsRaw::I32Pair(v) => v.raw_len(),
ParamsRaw::I32Triplet(v) => v.raw_len(),
ParamsRaw::F32(v) => v.raw_len(),
ParamsRaw::F32Pair(v) => v.raw_len(),
ParamsRaw::F32Triplet(v) => v.raw_len(),
ParamsRaw::U64(v) => v.raw_len(),
ParamsRaw::I64(v) => v.raw_len(),
ParamsRaw::F64(v) => v.raw_len(),
ParamsRaw::U8(v) => WritableToBeBytes::written_len(v),
ParamsRaw::U8Pair(v) => WritableToBeBytes::written_len(v),
ParamsRaw::U8Triplet(v) => WritableToBeBytes::written_len(v),
ParamsRaw::I8(v) => WritableToBeBytes::written_len(v),
ParamsRaw::I8Pair(v) => WritableToBeBytes::written_len(v),
ParamsRaw::I8Triplet(v) => WritableToBeBytes::written_len(v),
ParamsRaw::U16(v) => WritableToBeBytes::written_len(v),
ParamsRaw::U16Pair(v) => WritableToBeBytes::written_len(v),
ParamsRaw::U16Triplet(v) => WritableToBeBytes::written_len(v),
ParamsRaw::I16(v) => WritableToBeBytes::written_len(v),
ParamsRaw::I16Pair(v) => WritableToBeBytes::written_len(v),
ParamsRaw::I16Triplet(v) => WritableToBeBytes::written_len(v),
ParamsRaw::U32(v) => WritableToBeBytes::written_len(v),
ParamsRaw::U32Pair(v) => WritableToBeBytes::written_len(v),
ParamsRaw::U32Triplet(v) => WritableToBeBytes::written_len(v),
ParamsRaw::I32(v) => WritableToBeBytes::written_len(v),
ParamsRaw::I32Pair(v) => WritableToBeBytes::written_len(v),
ParamsRaw::I32Triplet(v) => WritableToBeBytes::written_len(v),
ParamsRaw::F32(v) => WritableToBeBytes::written_len(v),
ParamsRaw::F32Pair(v) => WritableToBeBytes::written_len(v),
ParamsRaw::F32Triplet(v) => WritableToBeBytes::written_len(v),
ParamsRaw::U64(v) => WritableToBeBytes::written_len(v),
ParamsRaw::I64(v) => WritableToBeBytes::written_len(v),
ParamsRaw::F64(v) => WritableToBeBytes::written_len(v),
}
}
@ -460,7 +467,7 @@ params_raw_from_newtype!(
);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum EcssEnumParams {
pub enum ParamsEcssEnum {
U8(EcssEnumU8),
U16(EcssEnumU16),
U32(EcssEnumU32),
@ -468,40 +475,46 @@ pub enum EcssEnumParams {
}
macro_rules! writable_as_be_bytes_ecss_enum_impl {
($EnumIdent: ident) => {
($EnumIdent: ident, $Ty: ident) => {
impl From<$EnumIdent> for ParamsEcssEnum {
fn from(e: $EnumIdent) -> Self {
Self::$Ty(e)
}
}
impl WritableToBeBytes for $EnumIdent {
fn raw_len(&self) -> usize {
fn written_len(&self) -> usize {
self.size()
}
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
<Self as UnsignedEnum>::write_to_be_bytes(self, buf).map(|_| self.raw_len())
<Self as UnsignedEnum>::write_to_be_bytes(self, buf).map(|_| self.written_len())
}
}
};
}
writable_as_be_bytes_ecss_enum_impl!(EcssEnumU8);
writable_as_be_bytes_ecss_enum_impl!(EcssEnumU16);
writable_as_be_bytes_ecss_enum_impl!(EcssEnumU32);
writable_as_be_bytes_ecss_enum_impl!(EcssEnumU64);
writable_as_be_bytes_ecss_enum_impl!(EcssEnumU8, U8);
writable_as_be_bytes_ecss_enum_impl!(EcssEnumU16, U16);
writable_as_be_bytes_ecss_enum_impl!(EcssEnumU32, U32);
writable_as_be_bytes_ecss_enum_impl!(EcssEnumU64, U64);
impl WritableToBeBytes for EcssEnumParams {
fn raw_len(&self) -> usize {
impl WritableToBeBytes for ParamsEcssEnum {
fn written_len(&self) -> usize {
match self {
EcssEnumParams::U8(e) => e.raw_len(),
EcssEnumParams::U16(e) => e.raw_len(),
EcssEnumParams::U32(e) => e.raw_len(),
EcssEnumParams::U64(e) => e.raw_len(),
ParamsEcssEnum::U8(e) => e.written_len(),
ParamsEcssEnum::U16(e) => e.written_len(),
ParamsEcssEnum::U32(e) => e.written_len(),
ParamsEcssEnum::U64(e) => e.written_len(),
}
}
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
match self {
EcssEnumParams::U8(e) => WritableToBeBytes::write_to_be_bytes(e, buf),
EcssEnumParams::U16(e) => WritableToBeBytes::write_to_be_bytes(e, buf),
EcssEnumParams::U32(e) => WritableToBeBytes::write_to_be_bytes(e, buf),
EcssEnumParams::U64(e) => WritableToBeBytes::write_to_be_bytes(e, buf),
ParamsEcssEnum::U8(e) => WritableToBeBytes::write_to_be_bytes(e, buf),
ParamsEcssEnum::U16(e) => WritableToBeBytes::write_to_be_bytes(e, buf),
ParamsEcssEnum::U32(e) => WritableToBeBytes::write_to_be_bytes(e, buf),
ParamsEcssEnum::U64(e) => WritableToBeBytes::write_to_be_bytes(e, buf),
}
}
}
@ -510,7 +523,19 @@ impl WritableToBeBytes for EcssEnumParams {
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ParamsHeapless {
Raw(ParamsRaw),
EcssEnum(EcssEnumParams),
EcssEnum(ParamsEcssEnum),
}
impl From<ParamsRaw> for ParamsHeapless {
fn from(v: ParamsRaw) -> Self {
Self::Raw(v)
}
}
impl From<ParamsEcssEnum> for ParamsHeapless {
fn from(v: ParamsEcssEnum) -> Self {
Self::EcssEnum(v)
}
}
macro_rules! from_conversions_for_raw {
@ -559,16 +584,14 @@ from_conversions_for_raw!(
/// Generic enumeration for additional parameters, including parameters which rely on heap
/// allocations.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Params {
Heapless(ParamsHeapless),
Store(StoreAddr),
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
Vec(Vec<u8>),
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
String(String),
}
@ -584,8 +607,13 @@ impl From<ParamsHeapless> for Params {
}
}
impl From<ParamsRaw> for Params {
fn from(x: ParamsRaw) -> Self {
Self::Heapless(ParamsHeapless::Raw(x))
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
impl From<Vec<u8>> for Params {
fn from(val: Vec<u8>) -> Self {
Self::Vec(val)
@ -594,7 +622,6 @@ impl From<Vec<u8>> for Params {
/// Converts a byte slice into the [Params::Vec] variant
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
impl From<&[u8]> for Params {
fn from(val: &[u8]) -> Self {
Self::Vec(val.to_vec())
@ -602,7 +629,6 @@ impl From<&[u8]> for Params {
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
impl From<String> for Params {
fn from(val: String) -> Self {
Self::String(val)
@ -610,7 +636,6 @@ impl From<String> for Params {
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
/// Converts a string slice into the [Params::String] variant
impl From<&str> for Params {
fn from(val: &str) -> Self {
@ -618,10 +643,90 @@ impl From<&str> for Params {
}
}
/// Please note while [WritableToBeBytes] is implemented for [Params], the default implementation
/// will not be able to process the [Params::Store] parameter variant.
impl WritableToBeBytes for Params {
fn written_len(&self) -> usize {
match self {
Params::Heapless(p) => match p {
ParamsHeapless::Raw(raw) => raw.written_len(),
ParamsHeapless::EcssEnum(enumeration) => enumeration.written_len(),
},
Params::Store(_) => 0,
#[cfg(feature = "alloc")]
Params::Vec(vec) => vec.len(),
#[cfg(feature = "alloc")]
Params::String(string) => string.len(),
}
}
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
match self {
Params::Heapless(p) => match p {
ParamsHeapless::Raw(raw) => raw.write_to_be_bytes(buf),
ParamsHeapless::EcssEnum(enumeration) => enumeration.write_to_be_bytes(buf),
},
Params::Store(_) => Ok(0),
#[cfg(feature = "alloc")]
Params::Vec(vec) => {
if buf.len() < vec.len() {
return Err(ByteConversionError::ToSliceTooSmall {
found: buf.len(),
expected: vec.len(),
});
}
buf[0..vec.len()].copy_from_slice(vec);
Ok(vec.len())
}
#[cfg(feature = "alloc")]
Params::String(string) => {
if buf.len() < string.len() {
return Err(ByteConversionError::ToSliceTooSmall {
found: buf.len(),
expected: string.len(),
});
}
buf[0..string.len()].copy_from_slice(string.as_bytes());
Ok(string.len())
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_cloning_works(param_raw: &impl WritableToBeBytes) {
let _new_param = param_raw;
}
fn test_writing_fails(param_raw: &(impl WritableToBeBytes + ToBeBytes)) {
let pair_size = WritableToBeBytes::written_len(param_raw);
assert_eq!(pair_size, ToBeBytes::written_len(param_raw));
let mut vec = alloc::vec![0; pair_size - 1];
let result = param_raw.write_to_be_bytes(&mut vec);
if let Err(ByteConversionError::ToSliceTooSmall { found, expected }) = result {
assert_eq!(found, pair_size - 1);
assert_eq!(expected, pair_size);
} else {
panic!("Expected ByteConversionError::ToSliceTooSmall");
}
}
fn test_writing(params_raw: &ParamsRaw, writeable: &impl WritableToBeBytes) {
assert_eq!(params_raw.written_len(), writeable.written_len());
let mut vec = alloc::vec![0; writeable.written_len()];
writeable
.write_to_be_bytes(&mut vec)
.expect("writing parameter to buffer failed");
let mut other_vec = alloc::vec![0; writeable.written_len()];
params_raw
.write_to_be_bytes(&mut other_vec)
.expect("writing parameter to buffer failed");
assert_eq!(vec, other_vec);
}
#[test]
fn test_basic_u32_pair() {
let u32_pair = U32Pair(4, 8);
@ -632,10 +737,32 @@ mod tests {
assert_eq!(u32_conv_back, 4);
u32_conv_back = u32::from_be_bytes(raw[4..8].try_into().unwrap());
assert_eq!(u32_conv_back, 8);
test_writing_fails(&u32_pair);
test_cloning_works(&u32_pair);
let u32_praw = ParamsRaw::from(u32_pair);
test_writing(&u32_praw, &u32_pair);
}
#[test]
fn basic_signed_test_pair() {
fn test_u16_pair_writing_fails() {
let u16_pair = U16Pair(4, 8);
test_writing_fails(&u16_pair);
test_cloning_works(&u16_pair);
let u16_praw = ParamsRaw::from(u16_pair);
test_writing(&u16_praw, &u16_pair);
}
#[test]
fn test_u8_pair_writing_fails() {
let u8_pair = U8Pair(4, 8);
test_writing_fails(&u8_pair);
test_cloning_works(&u8_pair);
let u8_praw = ParamsRaw::from(u8_pair);
test_writing(&u8_praw, &u8_pair);
}
#[test]
fn basic_i8_test() {
let i8_pair = I8Pair(-3, -16);
assert_eq!(i8_pair.0, -3);
assert_eq!(i8_pair.1, -16);
@ -644,10 +771,31 @@ mod tests {
assert_eq!(i8_conv_back, -3);
i8_conv_back = i8::from_be_bytes(raw[1..2].try_into().unwrap());
assert_eq!(i8_conv_back, -16);
test_writing_fails(&i8_pair);
test_cloning_works(&i8_pair);
let i8_praw = ParamsRaw::from(i8_pair);
test_writing(&i8_praw, &i8_pair);
}
#[test]
fn basic_signed_test_triplet() {
fn test_from_u32_triplet() {
let raw_params = U32Triplet::from((1, 2, 3));
assert_eq!(raw_params.0, 1);
assert_eq!(raw_params.1, 2);
assert_eq!(raw_params.2, 3);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 12);
assert_eq!(
raw_params.to_be_bytes(),
[0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]
);
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let u32_triplet = ParamsRaw::from(raw_params);
test_writing(&u32_triplet, &raw_params);
}
#[test]
fn test_i8_triplet() {
let i8_triplet = I8Triplet(-3, -16, -126);
assert_eq!(i8_triplet.0, -3);
assert_eq!(i8_triplet.1, -16);
@ -659,6 +807,10 @@ mod tests {
assert_eq!(i8_conv_back, -16);
i8_conv_back = i8::from_be_bytes(raw[2..3].try_into().unwrap());
assert_eq!(i8_conv_back, -126);
test_writing_fails(&i8_triplet);
test_cloning_works(&i8_triplet);
let i8_praw = ParamsRaw::from(i8_triplet);
test_writing(&i8_praw, &i8_triplet);
}
#[test]
@ -681,4 +833,352 @@ mod tests {
panic!("Params type is not a vector")
}
}
#[test]
fn test_params_written_len_raw() {
let param_raw = ParamsRaw::from((500_u32, 1000_u32));
let param: Params = Params::Heapless(param_raw.into());
assert_eq!(param.written_len(), 8);
let mut buf: [u8; 8] = [0; 8];
param
.write_to_be_bytes(&mut buf)
.expect("writing to buffer failed");
assert_eq!(u32::from_be_bytes(buf[0..4].try_into().unwrap()), 500);
assert_eq!(u32::from_be_bytes(buf[4..8].try_into().unwrap()), 1000);
}
#[test]
fn test_params_written_string() {
let string = "Test String".to_string();
let param = Params::String(string.clone());
assert_eq!(param.written_len(), string.len());
let vec = param.to_vec().unwrap();
let string_conv_back = String::from_utf8(vec).expect("conversion to string failed");
assert_eq!(string_conv_back, string);
}
#[test]
fn test_params_written_vec() {
let vec: Vec<u8> = alloc::vec![1, 2, 3, 4, 5];
let param = Params::Vec(vec.clone());
assert_eq!(param.written_len(), vec.len());
assert_eq!(param.to_vec().expect("writing vec params failed"), vec);
}
#[test]
fn test_u32_single() {
let raw_params = U32::from(20);
assert_eq!(raw_params.0, 20);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 4);
assert_eq!(raw_params.to_be_bytes(), [0, 0, 0, 20]);
let other = U32::from(20);
assert_eq!(raw_params, other);
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let u32_praw = ParamsRaw::from(raw_params);
test_writing(&u32_praw, &raw_params);
}
#[test]
fn test_i8_single() {
let neg_number: i8 = -5_i8;
let raw_params = I8::from(neg_number);
assert_eq!(raw_params.0, neg_number);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 1);
assert_eq!(raw_params.to_be_bytes(), neg_number.to_be_bytes());
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let u8_praw = ParamsRaw::from(raw_params);
test_writing(&u8_praw, &raw_params);
}
#[test]
fn test_u8_single() {
let raw_params = U8::from(20);
assert_eq!(raw_params.0, 20);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 1);
assert_eq!(raw_params.to_be_bytes(), [20]);
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let u32_praw = ParamsRaw::from(raw_params);
test_writing(&u32_praw, &raw_params);
}
#[test]
fn test_u16_single() {
let raw_params = U16::from(0x123);
assert_eq!(raw_params.0, 0x123);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 2);
assert_eq!(raw_params.to_be_bytes(), [0x01, 0x23]);
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let u16_praw = ParamsRaw::from(raw_params);
test_writing(&u16_praw, &raw_params);
}
#[test]
fn test_u16_triplet() {
let raw_params = U16Triplet::from((1, 2, 3));
assert_eq!(raw_params.0, 1);
assert_eq!(raw_params.1, 2);
assert_eq!(raw_params.2, 3);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 6);
assert_eq!(raw_params.to_be_bytes(), [0, 1, 0, 2, 0, 3]);
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let u16_praw = ParamsRaw::from(raw_params);
test_writing(&u16_praw, &raw_params);
}
#[test]
fn test_u8_triplet() {
let raw_params = U8Triplet::from((1, 2, 3));
assert_eq!(raw_params.0, 1);
assert_eq!(raw_params.1, 2);
assert_eq!(raw_params.2, 3);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 3);
assert_eq!(raw_params.to_be_bytes(), [1, 2, 3]);
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let u8_praw = ParamsRaw::from(raw_params);
test_writing(&u8_praw, &raw_params);
}
#[test]
fn test_i16_single() {
let value = -300_i16;
let raw_params = I16::from(value);
assert_eq!(raw_params.0, value);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 2);
assert_eq!(raw_params.to_be_bytes(), value.to_be_bytes());
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let i16_praw = ParamsRaw::from(raw_params);
test_writing(&i16_praw, &raw_params);
}
#[test]
fn test_i16_pair() {
let raw_params = I16Pair::from((-300, -400));
assert_eq!(raw_params.0, -300);
assert_eq!(raw_params.1, -400);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 4);
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let i16_praw = ParamsRaw::from(raw_params);
test_writing(&i16_praw, &raw_params);
}
#[test]
fn test_i16_triplet() {
let raw_params = I16Triplet::from((-300, -400, -350));
assert_eq!(raw_params.0, -300);
assert_eq!(raw_params.1, -400);
assert_eq!(raw_params.2, -350);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 6);
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let i16_praw = ParamsRaw::from(raw_params);
test_writing(&i16_praw, &raw_params);
}
#[test]
fn test_i32_single() {
let raw_params = I32::from(-80000);
assert_eq!(raw_params.0, -80000);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 4);
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let i32_praw = ParamsRaw::from(raw_params);
test_writing(&i32_praw, &raw_params);
}
#[test]
fn test_i32_pair() {
let raw_params = I32Pair::from((-80000, -200));
assert_eq!(raw_params.0, -80000);
assert_eq!(raw_params.1, -200);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 8);
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let i32_praw = ParamsRaw::from(raw_params);
test_writing(&i32_praw, &raw_params);
}
#[test]
fn test_i32_triplet() {
let raw_params = I32Triplet::from((-80000, -5, -200));
assert_eq!(raw_params.0, -80000);
assert_eq!(raw_params.1, -5);
assert_eq!(raw_params.2, -200);
assert_eq!(WritableToBeBytes::written_len(&raw_params), 12);
test_writing_fails(&raw_params);
test_cloning_works(&raw_params);
let i32_praw = ParamsRaw::from(raw_params);
test_writing(&i32_praw, &raw_params);
}
#[test]
fn test_f32_single() {
let param = F32::from(0.1);
assert_eq!(param.0, 0.1);
assert_eq!(WritableToBeBytes::written_len(&param), 4);
let f32_pair_raw = param.to_be_bytes();
let f32_0 = f32::from_be_bytes(f32_pair_raw[0..4].try_into().unwrap());
assert_eq!(f32_0, 0.1);
test_writing_fails(&param);
test_cloning_works(&param);
let praw = ParamsRaw::from(param);
test_writing(&praw, &param);
let p_try_from = F32::try_from(param.to_be_bytes().as_ref()).expect("try_from failed");
assert_eq!(p_try_from, param);
}
#[test]
fn test_f32_pair() {
let param = F32Pair::from((0.1, 0.2));
assert_eq!(param.0, 0.1);
assert_eq!(param.1, 0.2);
assert_eq!(WritableToBeBytes::written_len(&param), 8);
let f32_pair_raw = param.to_be_bytes();
let f32_0 = f32::from_be_bytes(f32_pair_raw[0..4].try_into().unwrap());
assert_eq!(f32_0, 0.1);
let f32_1 = f32::from_be_bytes(f32_pair_raw[4..8].try_into().unwrap());
assert_eq!(f32_1, 0.2);
let other_pair = F32Pair::from((0.1, 0.2));
assert_eq!(param, other_pair);
test_writing_fails(&param);
test_cloning_works(&param);
let praw = ParamsRaw::from(param);
test_writing(&praw, &param);
let p_try_from = F32Pair::try_from(param.to_be_bytes().as_ref()).expect("try_from failed");
assert_eq!(p_try_from, param);
}
#[test]
fn test_f32_triplet() {
let f32 = F32Triplet::from((0.1, -0.1, -5.2));
assert_eq!(f32.0, 0.1);
assert_eq!(f32.1, -0.1);
assert_eq!(f32.2, -5.2);
assert_eq!(WritableToBeBytes::written_len(&f32), 12);
let f32_pair_raw = f32.to_be_bytes();
let f32_0 = f32::from_be_bytes(f32_pair_raw[0..4].try_into().unwrap());
assert_eq!(f32_0, 0.1);
let f32_1 = f32::from_be_bytes(f32_pair_raw[4..8].try_into().unwrap());
assert_eq!(f32_1, -0.1);
let f32_2 = f32::from_be_bytes(f32_pair_raw[8..12].try_into().unwrap());
assert_eq!(f32_2, -5.2);
test_writing_fails(&f32);
test_cloning_works(&f32);
let f32_praw = ParamsRaw::from(f32);
test_writing(&f32_praw, &f32);
let f32_try_from =
F32Triplet::try_from(f32.to_be_bytes().as_ref()).expect("try_from failed");
assert_eq!(f32_try_from, f32);
}
#[test]
fn test_u64_single() {
let u64 = U64::from(0x1010101010);
assert_eq!(u64.0, 0x1010101010);
assert_eq!(WritableToBeBytes::written_len(&u64), 8);
test_writing_fails(&u64);
test_cloning_works(&u64);
let praw = ParamsRaw::from(u64);
test_writing(&praw, &u64);
}
#[test]
fn test_i64_single() {
let i64 = I64::from(-0xfffffffff);
assert_eq!(i64.0, -0xfffffffff);
assert_eq!(WritableToBeBytes::written_len(&i64), 8);
test_writing_fails(&i64);
test_cloning_works(&i64);
let praw = ParamsRaw::from(i64);
test_writing(&praw, &i64);
}
#[test]
fn test_f64_single() {
let value = 823_823_812_832.232_3;
let f64 = F64::from(value);
assert_eq!(f64.0, value);
assert_eq!(WritableToBeBytes::written_len(&f64), 8);
test_writing_fails(&f64);
test_cloning_works(&f64);
let praw = ParamsRaw::from(f64);
test_writing(&praw, &f64);
}
#[test]
fn test_f64_triplet() {
let f64_triplet = F64Triplet::from((0.1, 0.2, 0.3));
assert_eq!(f64_triplet.0, 0.1);
assert_eq!(f64_triplet.1, 0.2);
assert_eq!(f64_triplet.2, 0.3);
assert_eq!(WritableToBeBytes::written_len(&f64_triplet), 24);
let f64_triplet_raw = f64_triplet.to_be_bytes();
let f64_0 = f64::from_be_bytes(f64_triplet_raw[0..8].try_into().unwrap());
assert_eq!(f64_0, 0.1);
let f64_1 = f64::from_be_bytes(f64_triplet_raw[8..16].try_into().unwrap());
assert_eq!(f64_1, 0.2);
let f64_2 = f64::from_be_bytes(f64_triplet_raw[16..24].try_into().unwrap());
assert_eq!(f64_2, 0.3);
test_writing_fails(&f64_triplet);
test_cloning_works(&f64_triplet);
}
#[test]
fn test_u8_ecss_enum() {
let value = 200;
let u8p = EcssEnumU8::new(value);
test_cloning_works(&u8p);
let praw = ParamsEcssEnum::from(u8p);
assert_eq!(praw.written_len(), 1);
let mut buf = [0; 1];
praw.write_to_be_bytes(&mut buf)
.expect("writing to buffer failed");
buf[0] = 200;
}
#[test]
fn test_u16_ecss_enum() {
let value = 60000;
let u16p = EcssEnumU16::new(value);
test_cloning_works(&u16p);
let praw = ParamsEcssEnum::from(u16p);
assert_eq!(praw.written_len(), 2);
let mut buf = [0; 2];
praw.write_to_be_bytes(&mut buf)
.expect("writing to buffer failed");
assert_eq!(u16::from_be_bytes(buf), value);
}
#[test]
fn test_u32_ecss_enum() {
let value = 70000;
let u32p = EcssEnumU32::new(value);
test_cloning_works(&u32p);
let praw = ParamsEcssEnum::from(u32p);
assert_eq!(praw.written_len(), 4);
let mut buf = [0; 4];
praw.write_to_be_bytes(&mut buf)
.expect("writing to buffer failed");
assert_eq!(u32::from_be_bytes(buf), value);
}
#[test]
fn test_u64_ecss_enum() {
let value = 0xffffffffff;
let u64p = EcssEnumU64::new(value);
test_cloning_works(&u64p);
let praw = ParamsEcssEnum::from(u64p);
assert_eq!(praw.written_len(), 8);
let mut buf = [0; 8];
praw.write_to_be_bytes(&mut buf)
.expect("writing to buffer failed");
assert_eq!(u64::from_be_bytes(buf), value);
}
}

View File

@ -72,7 +72,6 @@
//! }
//! ```
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub use alloc_mod::*;
use core::fmt::{Display, Formatter};
use delegate::delegate;

View File

@ -1,6 +1,10 @@
use crate::{action::ActionRequest, TargetId};
use crate::{
action::{ActionId, ActionRequest},
params::Params,
request::{GenericMessage, MessageMetadata, RequestId},
};
use super::verification::{TcStateAccepted, VerificationToken};
use satrs_shared::res_code::ResultU16;
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
@ -8,219 +12,278 @@ pub use std_mod::*;
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[allow(unused_imports)]
pub use alloc_mod::*;
/// This trait is an abstraction for the routing of PUS service 8 action requests to a dedicated
/// recipient using the generic [TargetId].
pub trait PusActionRequestRouter {
type Error;
fn route(
&self,
target_id: TargetId,
hk_request: ActionRequest,
token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error>;
#[derive(Clone, Debug)]
pub struct ActionRequestWithId {
pub request_id: RequestId,
pub request: ActionRequest,
}
/// A reply to an action request, but tailored to the PUS standard verification process.
#[non_exhaustive]
#[derive(Clone, PartialEq, Debug)]
pub enum ActionReplyVariant {
Completed,
StepSuccess {
step: u16,
},
CompletionFailed {
error_code: ResultU16,
params: Option<Params>,
},
StepFailed {
error_code: ResultU16,
step: u16,
params: Option<Params>,
},
}
#[derive(Debug, PartialEq, Clone)]
pub struct PusActionReply {
pub action_id: ActionId,
pub variant: ActionReplyVariant,
}
impl PusActionReply {
pub fn new(action_id: ActionId, variant: ActionReplyVariant) -> Self {
Self { action_id, variant }
}
}
pub type GenericActionReplyPus = GenericMessage<PusActionReply>;
impl GenericActionReplyPus {
pub fn new_action_reply(
requestor_info: MessageMetadata,
action_id: ActionId,
reply: ActionReplyVariant,
) -> Self {
Self::new(requestor_info, PusActionReply::new(action_id, reply))
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub mod alloc_mod {
use spacepackets::ecss::tc::PusTcReader;
use crate::{
action::ActionRequest,
queue::GenericTargetedMessagingError,
request::{
GenericMessage, MessageReceiver, MessageSender, MessageSenderAndReceiver, RequestId,
},
ComponentId,
};
use crate::pus::verification::VerificationReportingProvider;
use super::PusActionReply;
use super::*;
/// Helper type definition for a mode handler which can handle mode requests.
pub type ActionRequestHandlerInterface<S, R> =
MessageSenderAndReceiver<PusActionReply, ActionRequest, S, R>;
/// This trait is an abstraction for the conversion of a PUS service 8 action telecommand into
/// an [ActionRequest].
///
/// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard.
/// The only requirement is that a valid [TargetId] and an [ActionRequest] are returned by the
/// core conversion function.
///
/// The user should take care of performing the error handling as well. Some of the following
/// aspects might be relevant:
///
/// - Checking the validity of the APID, service ID, subservice ID.
/// - Checking the validity of the user data.
///
/// A [VerificationReportingProvider] instance is passed to the user to also allow handling
/// of the verification process as part of the PUS standard requirements.
pub trait PusActionToRequestConverter {
type Error;
fn convert(
&mut self,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) -> Result<(TargetId, ActionRequest), Self::Error>;
impl<S: MessageSender<PusActionReply>, R: MessageReceiver<ActionRequest>>
ActionRequestHandlerInterface<S, R>
{
pub fn try_recv_action_request(
&self,
) -> Result<Option<GenericMessage<ActionRequest>>, GenericTargetedMessagingError> {
self.try_recv_message()
}
pub fn send_action_reply(
&self,
request_id: RequestId,
target_id: ComponentId,
reply: PusActionReply,
) -> Result<(), GenericTargetedMessagingError> {
self.send_message(request_id, target_id, reply)
}
}
/// Helper type defintion for a mode handler object which can send mode requests and receive
/// mode replies.
pub type ActionRequestorInterface<S, R> =
MessageSenderAndReceiver<ActionRequest, PusActionReply, S, R>;
impl<S: MessageSender<ActionRequest>, R: MessageReceiver<PusActionReply>>
ActionRequestorInterface<S, R>
{
pub fn try_recv_action_reply(
&self,
) -> Result<Option<GenericMessage<PusActionReply>>, GenericTargetedMessagingError> {
self.try_recv_message()
}
pub fn send_action_request(
&self,
request_id: RequestId,
target_id: ComponentId,
request: ActionRequest,
) -> Result<(), GenericTargetedMessagingError> {
self.send_message(request_id, target_id, request)
}
}
}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub mod std_mod {
use crate::pus::{
get_current_cds_short_timestamp, verification::VerificationReportingProvider,
EcssTcInMemConverter, EcssTcReceiverCore, EcssTmSenderCore, GenericRoutingError,
PusPacketHandlerResult, PusPacketHandlingError, PusRoutingErrorHandler, PusServiceHelper,
use std::sync::mpsc;
use crate::{
pus::{
verification::{self, TcStateToken},
ActivePusRequestStd, ActiveRequestProvider, DefaultActiveRequestMap,
},
ComponentId,
};
use super::*;
/// This is a high-level handler for the PUS service 8 action service.
///
/// It performs the following handling steps:
///
/// 1. Retrieve the next TC packet from the [PusServiceHelper]. The [EcssTcInMemConverter]
/// allows to configure the used telecommand memory backend.
/// 2. Convert the TC to a targeted action request using the provided
/// [PusActionToRequestConverter]. The generic error type is constrained to the
/// [PusPacketHandlingError] for the concrete implementation which offers a packet handler.
/// 3. Route the action request using the provided [PusActionRequestRouter].
/// 4. Handle all routing errors using the provided [PusRoutingErrorHandler].
pub struct PusService8ActionHandler<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
RequestConverter: PusActionToRequestConverter,
RequestRouter: PusActionRequestRouter<Error = RoutingError>,
RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
RoutingError = GenericRoutingError,
> {
service_helper:
PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
pub request_converter: RequestConverter,
pub request_router: RequestRouter,
pub routing_error_handler: RoutingErrorHandler,
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ActivePusActionRequestStd {
pub action_id: ActionId,
common: ActivePusRequestStd,
}
impl<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
RequestConverter: PusActionToRequestConverter<Error = PusPacketHandlingError>,
RequestRouter: PusActionRequestRouter<Error = RoutingError>,
RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
RoutingError: Clone,
>
PusService8ActionHandler<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
RequestConverter,
RequestRouter,
RoutingErrorHandler,
RoutingError,
>
where
PusPacketHandlingError: From<RoutingError>,
{
impl ActiveRequestProvider for ActivePusActionRequestStd {
delegate::delegate! {
to self.common {
fn target_id(&self) -> ComponentId;
fn token(&self) -> verification::TcStateToken;
fn set_token(&mut self, token: verification::TcStateToken);
fn has_timed_out(&self) -> bool;
fn timeout(&self) -> core::time::Duration;
}
}
}
impl ActivePusActionRequestStd {
pub fn new_from_common_req(action_id: ActionId, common: ActivePusRequestStd) -> Self {
Self { action_id, common }
}
pub fn new(
service_helper: PusServiceHelper<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
>,
request_converter: RequestConverter,
request_router: RequestRouter,
routing_error_handler: RoutingErrorHandler,
action_id: ActionId,
target_id: ComponentId,
token: TcStateToken,
timeout: core::time::Duration,
) -> Self {
Self {
service_helper,
request_converter,
request_router,
routing_error_handler,
action_id,
common: ActivePusRequestStd::new(target_id, token, timeout),
}
}
/// Core function to poll the next TC packet and try to handle it.
pub fn handle_one_tc(&mut self) -> 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
.service_helper
.tc_in_mem_converter
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
let mut partial_error = None;
let time_stamp = get_current_cds_short_timestamp(&mut partial_error);
let (target_id, action_request) = self.request_converter.convert(
ecss_tc_and_token.token,
&tc,
&time_stamp,
&self.service_helper.common.verification_handler,
)?;
if let Err(e) =
self.request_router
.route(target_id, action_request, ecss_tc_and_token.token)
{
self.routing_error_handler.handle_error(
target_id,
ecss_tc_and_token.token,
&tc,
e.clone(),
&time_stamp,
&self.service_helper.common.verification_handler,
);
return Err(e.into());
}
Ok(PusPacketHandlerResult::RequestHandled)
}
}
pub type DefaultActiveActionRequestMap = DefaultActiveRequestMap<ActivePusActionRequestStd>;
pub type ActionRequestHandlerMpsc = ActionRequestHandlerInterface<
mpsc::Sender<GenericMessage<PusActionReply>>,
mpsc::Receiver<GenericMessage<ActionRequest>>,
>;
pub type ActionRequestHandlerMpscBounded = ActionRequestHandlerInterface<
mpsc::SyncSender<GenericMessage<PusActionReply>>,
mpsc::Receiver<GenericMessage<ActionRequest>>,
>;
pub type ActionRequestorMpsc = ActionRequestorInterface<
mpsc::Sender<GenericMessage<ActionRequest>>,
mpsc::Receiver<GenericMessage<PusActionReply>>,
>;
pub type ActionRequestorBoundedMpsc = ActionRequestorInterface<
mpsc::SyncSender<GenericMessage<ActionRequest>>,
mpsc::Receiver<GenericMessage<PusActionReply>>,
>;
/*
pub type ModeRequestorAndHandlerMpsc = ModeInterface<
mpsc::Sender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
mpsc::Sender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
>;
pub type ModeRequestorAndHandlerMpscBounded = ModeInterface<
mpsc::SyncSender<GenericMessage<ModeRequest>>,
mpsc::Receiver<GenericMessage<ModeReply>>,
mpsc::SyncSender<GenericMessage<ModeReply>>,
mpsc::Receiver<GenericMessage<ModeRequest>>,
>;
*/
}
#[cfg(test)]
mod tests {
/*
use core::{cell::RefCell, time::Duration};
use std::{sync::mpsc, time::SystemTimeError};
use alloc::{collections::VecDeque, vec::Vec};
use delegate::delegate;
use spacepackets::{
ecss::{
tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader},
tc::{PusTcCreator, PusTcReader},
tm::PusTmReader,
PusPacket,
},
CcsdsPacket, SequenceFlags, SpHeader,
time::{cds, TimeWriter},
CcsdsPacket,
};
use crate::pus::{
tests::{
PusServiceHandlerWithVecCommon, PusTestHarness, SimplePusPacketHandler, TestConverter,
TestRouter, TestRoutingErrorHandler, APP_DATA_TOO_SHORT, TEST_APID,
use crate::{
action::ActionRequestVariant,
params::{self, ParamsRaw, WritableToBeBytes},
pus::{
tests::{
PusServiceHandlerWithVecCommon, PusTestHarness, SimplePusPacketHandler,
TestConverter, TestRouter, APP_DATA_TOO_SHORT,
},
verification::{
self,
tests::{SharedVerificationMap, TestVerificationReporter, VerificationStatus},
FailParams, TcStateAccepted, TcStateNone, TcStateStarted,
VerificationReportingProvider,
},
EcssTcInMemConverter, EcssTcInVecConverter, EcssTmtcError, GenericRoutingError,
MpscTcReceiver, PusPacketHandlerResult, PusPacketHandlingError, PusRequestRouter,
PusServiceHelper, PusTcToRequestConverter, TmAsVecSenderWithMpsc,
},
verification::{
tests::TestVerificationReporter, FailParams, RequestId, VerificationReportingProvider,
},
EcssTcInVecConverter, GenericRoutingError, MpscTcReceiver, PusPacketHandlerResult,
PusPacketHandlingError, TmAsVecSenderWithMpsc,
};
use super::*;
impl PusActionRequestRouter for TestRouter<ActionRequest> {
impl<Request> PusRequestRouter<Request> for TestRouter<Request> {
type Error = GenericRoutingError;
fn route(
&self,
target_id: TargetId,
hk_request: ActionRequest,
request: Request,
_token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error> {
self.routing_requests
.borrow_mut()
.push_back((target_id, hk_request));
.push_back((target_id, request));
self.check_for_injected_error()
}
fn handle_error(
&self,
target_id: TargetId,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
error: Self::Error,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) {
self.routing_errors
.borrow_mut()
.push_back((target_id, error));
}
}
impl PusActionToRequestConverter for TestConverter<8> {
impl PusTcToRequestConverter<ActionRequest> for TestConverter<8> {
type Error = PusPacketHandlingError;
fn convert(
&mut self,
@ -254,9 +317,9 @@ mod tests {
.expect("start success failure");
return Ok((
target_id.into(),
ActionRequest::UnsignedIdAndVecData {
ActionRequest {
action_id: u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()),
data: tc.user_data()[4..].to_vec(),
variant: ActionRequestVariant::VecData(tc.user_data()[4..].to_vec()),
},
));
}
@ -266,31 +329,32 @@ mod tests {
}
}
struct Pus8HandlerWithVecTester {
common: PusServiceHandlerWithVecCommon<TestVerificationReporter>,
handler: PusService8ActionHandler<
pub struct PusDynRequestHandler<const SERVICE: u8, Request> {
srv_helper: PusServiceHelper<
MpscTcReceiver,
TmAsVecSenderWithMpsc,
EcssTcInVecConverter,
TestVerificationReporter,
TestConverter<8>,
TestRouter<ActionRequest>,
TestRoutingErrorHandler,
>,
request_converter: TestConverter<SERVICE>,
request_router: TestRouter<Request>,
}
impl Pus8HandlerWithVecTester {
struct Pus8RequestTestbenchWithVec {
common: PusServiceHandlerWithVecCommon<TestVerificationReporter>,
handler: PusDynRequestHandler<8, ActionRequest>,
}
impl Pus8RequestTestbenchWithVec {
pub fn new() -> Self {
let (common, srv_handler) =
PusServiceHandlerWithVecCommon::new_with_test_verif_sender();
let (common, srv_helper) = PusServiceHandlerWithVecCommon::new_with_test_verif_sender();
Self {
common,
handler: PusService8ActionHandler::new(
srv_handler,
TestConverter::default(),
TestRouter::default(),
TestRoutingErrorHandler::default(),
),
handler: PusDynRequestHandler {
srv_helper,
request_converter: TestConverter::default(),
request_router: TestRouter::default(),
},
}
}
@ -305,13 +369,13 @@ mod tests {
}
}
delegate! {
to self.handler.routing_error_handler {
pub fn retrieve_next_error(&mut self) -> (TargetId, GenericRoutingError);
to self.handler.request_router {
pub fn retrieve_next_routing_error(&mut self) -> (TargetId, GenericRoutingError);
}
}
}
impl PusTestHarness for Pus8HandlerWithVecTester {
impl PusTestHarness for Pus8RequestTestbenchWithVec {
delegate! {
to self.common {
fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
@ -320,78 +384,421 @@ mod tests {
fn check_next_verification_tm(
&self,
subservice: u8,
expected_request_id: RequestId,
expected_request_id: verification::RequestId,
);
}
}
}
impl SimplePusPacketHandler for Pus8HandlerWithVecTester {
impl SimplePusPacketHandler for Pus8RequestTestbenchWithVec {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.handler.srv_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
.handler
.srv_helper
.tc_in_mem_converter
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
let time_stamp = cds::TimeProvider::from_now_with_u16_days()
.expect("timestamp generation failed")
.to_vec()
.unwrap();
let (target_id, action_request) = self.handler.request_converter.convert(
ecss_tc_and_token.token,
&tc,
&time_stamp,
&self.handler.srv_helper.common.verification_handler,
)?;
if let Err(e) = self.handler.request_router.route(
target_id,
action_request,
ecss_tc_and_token.token,
) {
self.handler.request_router.handle_error(
target_id,
ecss_tc_and_token.token,
&tc,
e.clone(),
&time_stamp,
&self.handler.srv_helper.common.verification_handler,
);
return Err(e.into());
}
Ok(PusPacketHandlerResult::RequestHandled)
}
}
const TIMEOUT_ERROR_CODE: ResultU16 = ResultU16::new(1, 2);
const COMPLETION_ERROR_CODE: ResultU16 = ResultU16::new(2, 0);
const COMPLETION_ERROR_CODE_STEP: ResultU16 = ResultU16::new(2, 1);
#[derive(Default)]
pub struct TestReplyHandlerHook {
pub unexpected_replies: VecDeque<GenericActionReplyPus>,
pub timeouts: RefCell<VecDeque<ActivePusActionRequest>>,
}
impl ReplyHandlerHook<ActivePusActionRequest, ActionReplyPusWithActionId> for TestReplyHandlerHook {
fn handle_unexpected_reply(&mut self, reply: &GenericActionReplyPus) {
self.unexpected_replies.push_back(reply.clone());
}
fn timeout_callback(&self, active_request: &ActivePusActionRequest) {
self.timeouts.borrow_mut().push_back(active_request.clone());
}
fn timeout_error_code(&self) -> ResultU16 {
TIMEOUT_ERROR_CODE
}
}
pub struct Pus8ReplyTestbench {
verif_reporter: TestVerificationReporter,
#[allow(dead_code)]
ecss_tm_receiver: mpsc::Receiver<Vec<u8>>,
handler: PusService8ReplyHandler<
TestVerificationReporter,
DefaultActiveActionRequestMap,
TestReplyHandlerHook,
mpsc::Sender<Vec<u8>>,
>,
}
impl Pus8ReplyTestbench {
pub fn new(normal_ctor: bool) -> Self {
let reply_handler_hook = TestReplyHandlerHook::default();
let shared_verif_map = SharedVerificationMap::default();
let test_verif_reporter = TestVerificationReporter::new(shared_verif_map.clone());
let (ecss_tm_sender, ecss_tm_receiver) = mpsc::channel();
let reply_handler = if normal_ctor {
PusService8ReplyHandler::new_from_now_with_default_map(
test_verif_reporter.clone(),
128,
reply_handler_hook,
ecss_tm_sender,
)
.expect("creating reply handler failed")
} else {
PusService8ReplyHandler::new_from_now(
test_verif_reporter.clone(),
DefaultActiveActionRequestMap::default(),
128,
reply_handler_hook,
ecss_tm_sender,
)
.expect("creating reply handler failed")
};
Self {
verif_reporter: test_verif_reporter,
ecss_tm_receiver,
handler: reply_handler,
}
}
pub fn init_handling_for_request(
&mut self,
request_id: RequestId,
_action_id: ActionId,
) -> VerificationToken<TcStateStarted> {
assert!(!self.handler.request_active(request_id));
// let action_req = ActionRequest::new(action_id, ActionRequestVariant::NoData);
let token = self.add_tc_with_req_id(request_id.into());
let token = self
.verif_reporter
.acceptance_success(token, &[])
.expect("acceptance success failure");
let token = self
.verif_reporter
.start_success(token, &[])
.expect("start success failure");
let verif_info = self
.verif_reporter
.verification_info(&verification::RequestId::from(request_id))
.expect("no verification info found");
assert!(verif_info.started.expect("request was not started"));
assert!(verif_info.accepted.expect("request was not accepted"));
token
}
pub fn next_unrequested_reply(&self) -> Option<GenericActionReplyPus> {
self.handler.user_hook.unexpected_replies.front().cloned()
}
pub fn assert_request_completion_success(&self, step: Option<u16>, request_id: RequestId) {
let verif_info = self
.verif_reporter
.verification_info(&verification::RequestId::from(request_id))
.expect("no verification info found");
self.assert_request_completion_common(request_id, &verif_info, step, true);
}
pub fn assert_request_completion_failure(
&self,
step: Option<u16>,
request_id: RequestId,
fail_enum: ResultU16,
fail_data: &[u8],
) {
let verif_info = self
.verif_reporter
.verification_info(&verification::RequestId::from(request_id))
.expect("no verification info found");
self.assert_request_completion_common(request_id, &verif_info, step, false);
assert_eq!(verif_info.fail_enum.unwrap(), fail_enum.raw() as u64);
assert_eq!(verif_info.failure_data.unwrap(), fail_data);
}
pub fn assert_request_completion_common(
&self,
request_id: RequestId,
verif_info: &VerificationStatus,
step: Option<u16>,
completion_success: bool,
) {
if let Some(step) = step {
assert!(verif_info.step_status.is_some());
assert!(verif_info.step_status.unwrap());
assert_eq!(step, verif_info.step);
}
assert_eq!(
verif_info.completed.expect("request is not completed"),
completion_success
);
assert!(!self.handler.request_active(request_id));
}
pub fn assert_request_step_failure(&self, step: u16, request_id: RequestId) {
let verif_info = self
.verif_reporter
.verification_info(&verification::RequestId::from(request_id))
.expect("no verification info found");
assert!(verif_info.step_status.is_some());
assert!(!verif_info.step_status.unwrap());
assert_eq!(step, verif_info.step);
}
pub fn add_routed_request(
&mut self,
request_id: verification::RequestId,
target_id: TargetId,
action_id: ActionId,
token: VerificationToken<TcStateStarted>,
timeout: Duration,
) {
if self.handler.request_active(request_id.into()) {
panic!("request already present");
}
self.handler
.add_routed_action_request(request_id, target_id, action_id, token, timeout);
if !self.handler.request_active(request_id.into()) {
panic!("request should be active now");
}
}
delegate! {
to self.handler {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
pub fn request_active(&self, request_id: RequestId) -> bool;
pub fn handle_action_reply(
&mut self,
action_reply_with_ids: GenericMessage<ActionReplyPusWithActionId>,
time_stamp: &[u8]
) -> Result<(), EcssTmtcError>;
pub fn update_time_from_now(&mut self) -> Result<(), SystemTimeError>;
pub fn check_for_timeouts(&mut self, time_stamp: &[u8]) -> Result<(), EcssTmtcError>;
}
to self.verif_reporter {
fn add_tc_with_req_id(&mut self, req_id: verification::RequestId) -> VerificationToken<TcStateNone>;
}
}
}
#[test]
fn basic_test() {
let mut action_handler = Pus8HandlerWithVecTester::new();
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
let sec_header = PusTcSecondaryHeader::new_simple(8, 1);
let action_id: u32 = 1;
let action_id_raw = action_id.to_be_bytes();
let tc = PusTcCreator::new(&mut sp_header, sec_header, action_id_raw.as_ref(), true);
action_handler.send_tc(&tc);
let result = action_handler.handle_one_tc();
assert!(result.is_ok());
action_handler.check_next_conversion(&tc);
let (target_id, action_req) = action_handler.retrieve_next_request();
assert_eq!(target_id, TEST_APID.into());
if let ActionRequest::UnsignedIdAndVecData { action_id, data } = action_req {
assert_eq!(action_id, 1);
assert_eq!(data, &[]);
}
fn test_reply_handler_completion_success() {
let mut reply_testbench = Pus8ReplyTestbench::new(true);
let sender_id = 0x06;
let request_id = 0x02;
let target_id = 0x05;
let action_id = 0x03;
let token = reply_testbench.init_handling_for_request(request_id, action_id);
reply_testbench.add_routed_request(
request_id.into(),
target_id,
action_id,
token,
Duration::from_millis(1),
);
assert!(reply_testbench.request_active(request_id));
let action_reply = GenericMessage::new(
request_id,
sender_id,
ActionReplyPusWithActionId {
action_id,
variant: ActionReplyPus::Completed,
},
);
reply_testbench
.handle_action_reply(action_reply, &[])
.expect("reply handling failure");
reply_testbench.assert_request_completion_success(None, request_id);
}
#[test]
fn test_routing_error() {
let mut action_handler = Pus8HandlerWithVecTester::new();
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
let sec_header = PusTcSecondaryHeader::new_simple(8, 1);
let action_id: u32 = 1;
let action_id_raw = action_id.to_be_bytes();
let tc = PusTcCreator::new(&mut sp_header, sec_header, action_id_raw.as_ref(), true);
let error = GenericRoutingError::UnknownTargetId(25);
action_handler
.handler
.request_router
.inject_routing_error(error);
action_handler.send_tc(&tc);
let result = action_handler.handle_one_tc();
assert!(result.is_err());
let check_error = |routing_error: GenericRoutingError| {
if let GenericRoutingError::UnknownTargetId(id) = routing_error {
assert_eq!(id, 25);
} else {
panic!("unexpected error type");
}
};
if let PusPacketHandlingError::RequestRoutingError(routing_error) = result.unwrap_err() {
check_error(routing_error);
} else {
panic!("unexpected error type");
}
action_handler.check_next_conversion(&tc);
let (target_id, action_req) = action_handler.retrieve_next_request();
assert_eq!(target_id, TEST_APID.into());
if let ActionRequest::UnsignedIdAndVecData { action_id, data } = action_req {
assert_eq!(action_id, 1);
assert_eq!(data, &[]);
}
let (target_id, found_error) = action_handler.retrieve_next_error();
assert_eq!(target_id, TEST_APID.into());
check_error(found_error);
fn test_reply_handler_step_success() {
let mut reply_testbench = Pus8ReplyTestbench::new(false);
let request_id = 0x02;
let target_id = 0x05;
let action_id = 0x03;
let token = reply_testbench.init_handling_for_request(request_id, action_id);
reply_testbench.add_routed_request(
request_id.into(),
target_id,
action_id,
token,
Duration::from_millis(1),
);
let action_reply = GenericActionReplyPus::new_action_reply(
request_id,
action_id,
action_id,
ActionReplyPus::StepSuccess { step: 1 },
);
reply_testbench
.handle_action_reply(action_reply, &[])
.expect("reply handling failure");
let action_reply = GenericActionReplyPus::new_action_reply(
request_id,
action_id,
action_id,
ActionReplyPus::Completed,
);
reply_testbench
.handle_action_reply(action_reply, &[])
.expect("reply handling failure");
reply_testbench.assert_request_completion_success(Some(1), request_id);
}
#[test]
fn test_reply_handler_completion_failure() {
let mut reply_testbench = Pus8ReplyTestbench::new(true);
let sender_id = 0x01;
let request_id = 0x02;
let target_id = 0x05;
let action_id = 0x03;
let token = reply_testbench.init_handling_for_request(request_id, action_id);
reply_testbench.add_routed_request(
request_id.into(),
target_id,
action_id,
token,
Duration::from_millis(1),
);
let params_raw = ParamsRaw::U32(params::U32(5));
let action_reply = GenericActionReplyPus::new_action_reply(
request_id,
sender_id,
action_id,
ActionReplyPus::CompletionFailed {
error_code: COMPLETION_ERROR_CODE,
params: params_raw.into(),
},
);
reply_testbench
.handle_action_reply(action_reply, &[])
.expect("reply handling failure");
reply_testbench.assert_request_completion_failure(
None,
request_id,
COMPLETION_ERROR_CODE,
&params_raw.to_vec().unwrap(),
);
}
#[test]
fn test_reply_handler_step_failure() {
let mut reply_testbench = Pus8ReplyTestbench::new(false);
let sender_id = 0x01;
let request_id = 0x02;
let target_id = 0x05;
let action_id = 0x03;
let token = reply_testbench.init_handling_for_request(request_id, action_id);
reply_testbench.add_routed_request(
request_id.into(),
target_id,
action_id,
token,
Duration::from_millis(1),
);
let action_reply = GenericActionReplyPus::new_action_reply(
request_id,
sender_id,
action_id,
ActionReplyPus::StepFailed {
error_code: COMPLETION_ERROR_CODE_STEP,
step: 2,
params: ParamsRaw::U32(crate::params::U32(5)).into(),
},
);
reply_testbench
.handle_action_reply(action_reply, &[])
.expect("reply handling failure");
reply_testbench.assert_request_step_failure(2, request_id);
}
#[test]
fn test_reply_handler_timeout_handling() {
let mut reply_testbench = Pus8ReplyTestbench::new(true);
let request_id = 0x02;
let target_id = 0x06;
let action_id = 0x03;
let token = reply_testbench.init_handling_for_request(request_id, action_id);
reply_testbench.add_routed_request(
request_id.into(),
target_id,
action_id,
token,
Duration::from_millis(1),
);
let timeout_param = Duration::from_millis(1).as_millis() as u64;
let timeout_param_raw = timeout_param.to_be_bytes();
std::thread::sleep(Duration::from_millis(2));
reply_testbench
.update_time_from_now()
.expect("time update failure");
reply_testbench.check_for_timeouts(&[]).unwrap();
reply_testbench.assert_request_completion_failure(
None,
request_id,
TIMEOUT_ERROR_CODE,
&timeout_param_raw,
);
}
#[test]
fn test_unrequested_reply() {
let mut reply_testbench = Pus8ReplyTestbench::new(true);
let sender_id = 0x01;
let request_id = 0x02;
let action_id = 0x03;
let action_reply = GenericActionReplyPus::new_action_reply(
request_id,
sender_id,
action_id,
ActionReplyPus::Completed,
);
reply_testbench
.handle_action_reply(action_reply, &[])
.expect("reply handling failure");
let reply = reply_testbench.next_unrequested_reply();
assert!(reply.is_some());
let reply = reply.unwrap();
assert_eq!(reply.message.action_id, action_id);
assert_eq!(reply.request_id, request_id);
assert_eq!(reply.message.variant, ActionReplyPus::Completed);
}
*/
}

View File

@ -6,8 +6,10 @@ use spacepackets::ByteConversionError;
use spacepackets::{SpHeader, MAX_APID};
use crate::pus::EcssTmSenderCore;
#[cfg(feature = "alloc")]
pub use alloc_mod::EventReporter;
pub use alloc_mod::*;
pub use spacepackets::ecss::event::*;
pub struct EventReportCreator {
@ -16,117 +18,112 @@ pub struct EventReportCreator {
}
impl EventReportCreator {
pub fn new(apid: u16) -> Option<Self> {
pub fn new(apid: u16, dest_id: u16) -> Option<Self> {
if apid > MAX_APID {
return None;
}
Some(Self {
// msg_count: 0,
dest_id: 0,
apid,
})
Some(Self { dest_id, apid })
}
pub fn event_info<'time, 'src_data>(
&mut self,
src_data_buf: &'src_data mut [u8],
&self,
time_stamp: &'time [u8],
event_id: impl EcssEnumeration,
aux_data: Option<&'src_data [u8]>,
params: Option<&'src_data [u8]>,
src_data_buf: &'src_data mut [u8],
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
self.generate_and_send_generic_tm(
src_data_buf,
Subservice::TmInfoReport,
time_stamp,
event_id,
aux_data,
params,
src_data_buf,
)
}
pub fn event_low_severity<'time, 'src_data>(
&mut self,
src_data_buf: &'src_data mut [u8],
&self,
time_stamp: &'time [u8],
event_id: impl EcssEnumeration,
aux_data: Option<&'src_data [u8]>,
params: Option<&'src_data [u8]>,
src_data_buf: &'src_data mut [u8],
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
self.generate_and_send_generic_tm(
src_data_buf,
Subservice::TmLowSeverityReport,
time_stamp,
event_id,
aux_data,
params,
src_data_buf,
)
}
pub fn event_medium_severity<'time, 'src_data>(
&mut self,
buf: &'src_data mut [u8],
&self,
time_stamp: &'time [u8],
event_id: impl EcssEnumeration,
aux_data: Option<&'src_data [u8]>,
params: Option<&'src_data [u8]>,
buf: &'src_data mut [u8],
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
self.generate_and_send_generic_tm(
buf,
Subservice::TmMediumSeverityReport,
time_stamp,
event_id,
aux_data,
params,
buf,
)
}
pub fn event_high_severity<'time, 'src_data>(
&mut self,
src_data_buf: &'src_data mut [u8],
&self,
time_stamp: &'time [u8],
event_id: impl EcssEnumeration,
aux_data: Option<&'src_data [u8]>,
params: Option<&'src_data [u8]>,
src_data_buf: &'src_data mut [u8],
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
self.generate_and_send_generic_tm(
src_data_buf,
Subservice::TmHighSeverityReport,
time_stamp,
event_id,
aux_data,
params,
src_data_buf,
)
}
fn generate_and_send_generic_tm<'time, 'src_data>(
&mut self,
src_data_buf: &'src_data mut [u8],
&self,
subservice: Subservice,
time_stamp: &'time [u8],
event_id: impl EcssEnumeration,
aux_data: Option<&'src_data [u8]>,
params: Option<&'src_data [u8]>,
src_data_buf: &'src_data mut [u8],
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
self.generate_generic_event_tm(src_data_buf, subservice, time_stamp, event_id, aux_data)
self.generate_generic_event_tm(subservice, time_stamp, event_id, params, src_data_buf)
}
fn generate_generic_event_tm<'time, 'src_data>(
&self,
src_data_buf: &'src_data mut [u8],
subservice: Subservice,
time_stamp: &'time [u8],
event_id: impl EcssEnumeration,
aux_data: Option<&'src_data [u8]>,
params: Option<&'src_data [u8]>,
src_data_buf: &'src_data mut [u8],
) -> Result<PusTmCreator<'time, 'src_data>, ByteConversionError> {
let mut src_data_len = event_id.size();
if let Some(aux_data) = aux_data {
if let Some(aux_data) = params {
src_data_len += aux_data.len();
}
source_buffer_large_enough(src_data_buf.len(), src_data_len)?;
let mut sp_header = SpHeader::tm_unseg(self.apid, 0, 0).unwrap();
let sec_header =
PusTmSecondaryHeader::new(5, subservice.into(), 0, self.dest_id, Some(time_stamp));
PusTmSecondaryHeader::new(5, subservice.into(), 0, self.dest_id, time_stamp);
let mut current_idx = 0;
event_id.write_to_be_bytes(&mut src_data_buf[0..event_id.size()])?;
current_idx += event_id.size();
if let Some(aux_data) = aux_data {
if let Some(aux_data) = params {
src_data_buf[current_idx..current_idx + aux_data.len()].copy_from_slice(aux_data);
current_idx += aux_data.len();
}
Ok(PusTmCreator::new(
&mut sp_header,
SpHeader::new_from_apid(self.apid),
sec_header,
&src_data_buf[0..current_idx],
true,
@ -137,99 +134,129 @@ impl EventReportCreator {
#[cfg(feature = "alloc")]
mod alloc_mod {
use super::*;
use crate::ComponentId;
use alloc::vec;
use alloc::vec::Vec;
use core::cell::RefCell;
pub struct EventReporter {
source_data_buf: Vec<u8>,
pub reporter: EventReportCreator,
pub trait EventTmHookProvider {
fn modify_tm(&self, tm: &mut PusTmCreator);
}
impl EventReporter {
pub fn new(apid: u16, max_event_id_and_aux_data_size: usize) -> Option<Self> {
let reporter = EventReportCreator::new(apid)?;
#[derive(Default)]
pub struct DummyEventHook {}
impl EventTmHookProvider for DummyEventHook {
fn modify_tm(&self, _tm: &mut PusTmCreator) {}
}
pub struct EventReporter<EventTmHook: EventTmHookProvider = DummyEventHook> {
id: ComponentId,
// Use interior mutability pattern here. This is just an intermediate buffer to the PUS event packet
// generation.
source_data_buf: RefCell<Vec<u8>>,
pub report_creator: EventReportCreator,
pub tm_hook: EventTmHook,
}
impl EventReporter<DummyEventHook> {
pub fn new(
id: ComponentId,
default_apid: u16,
default_dest_id: u16,
max_event_id_and_aux_data_size: usize,
) -> Option<Self> {
let reporter = EventReportCreator::new(default_apid, default_dest_id)?;
Some(Self {
source_data_buf: vec![0; max_event_id_and_aux_data_size],
reporter,
id,
source_data_buf: RefCell::new(vec![0; max_event_id_and_aux_data_size]),
report_creator: reporter,
tm_hook: DummyEventHook::default(),
})
}
}
impl<EventTmHook: EventTmHookProvider> EventReporter<EventTmHook> {
pub fn new_with_hook(
id: ComponentId,
default_apid: u16,
default_dest_id: u16,
max_event_id_and_aux_data_size: usize,
tm_hook: EventTmHook,
) -> Option<Self> {
let reporter = EventReportCreator::new(default_apid, default_dest_id)?;
Some(Self {
id,
source_data_buf: RefCell::new(vec![0; max_event_id_and_aux_data_size]),
report_creator: reporter,
tm_hook,
})
}
pub fn event_info(
&mut self,
sender: &mut (impl EcssTmSenderCore + ?Sized),
&self,
sender: &(impl EcssTmSenderCore + ?Sized),
time_stamp: &[u8],
event_id: impl EcssEnumeration,
aux_data: Option<&[u8]>,
params: Option<&[u8]>,
) -> Result<(), EcssTmtcError> {
let tm_creator = self
.reporter
.event_info(
self.source_data_buf.as_mut_slice(),
time_stamp,
event_id,
aux_data,
)
let mut mut_buf = self.source_data_buf.borrow_mut();
let mut tm_creator = self
.report_creator
.event_info(time_stamp, event_id, params, mut_buf.as_mut_slice())
.map_err(PusError::ByteConversion)?;
sender.send_tm(tm_creator.into())?;
self.tm_hook.modify_tm(&mut tm_creator);
sender.send_tm(self.id, tm_creator.into())?;
Ok(())
}
pub fn event_low_severity(
&mut self,
sender: &mut (impl EcssTmSenderCore + ?Sized),
&self,
sender: &(impl EcssTmSenderCore + ?Sized),
time_stamp: &[u8],
event_id: impl EcssEnumeration,
aux_data: Option<&[u8]>,
params: Option<&[u8]>,
) -> Result<(), EcssTmtcError> {
let tm_creator = self
.reporter
.event_low_severity(
self.source_data_buf.as_mut_slice(),
time_stamp,
event_id,
aux_data,
)
let mut mut_buf = self.source_data_buf.borrow_mut();
let mut tm_creator = self
.report_creator
.event_low_severity(time_stamp, event_id, params, mut_buf.as_mut_slice())
.map_err(PusError::ByteConversion)?;
sender.send_tm(tm_creator.into())?;
self.tm_hook.modify_tm(&mut tm_creator);
sender.send_tm(self.id, tm_creator.into())?;
Ok(())
}
pub fn event_medium_severity(
&mut self,
sender: &mut (impl EcssTmSenderCore + ?Sized),
&self,
sender: &(impl EcssTmSenderCore + ?Sized),
time_stamp: &[u8],
event_id: impl EcssEnumeration,
aux_data: Option<&[u8]>,
params: Option<&[u8]>,
) -> Result<(), EcssTmtcError> {
let tm_creator = self
.reporter
.event_medium_severity(
self.source_data_buf.as_mut_slice(),
time_stamp,
event_id,
aux_data,
)
let mut mut_buf = self.source_data_buf.borrow_mut();
let mut tm_creator = self
.report_creator
.event_medium_severity(time_stamp, event_id, params, mut_buf.as_mut_slice())
.map_err(PusError::ByteConversion)?;
sender.send_tm(tm_creator.into())?;
self.tm_hook.modify_tm(&mut tm_creator);
sender.send_tm(self.id, tm_creator.into())?;
Ok(())
}
pub fn event_high_severity(
&mut self,
sender: &mut (impl EcssTmSenderCore + ?Sized),
&self,
sender: &(impl EcssTmSenderCore + ?Sized),
time_stamp: &[u8],
event_id: impl EcssEnumeration,
aux_data: Option<&[u8]>,
params: Option<&[u8]>,
) -> Result<(), EcssTmtcError> {
let tm_creator = self
.reporter
.event_high_severity(
self.source_data_buf.as_mut_slice(),
time_stamp,
event_id,
aux_data,
)
let mut mut_buf = self.source_data_buf.borrow_mut();
let mut tm_creator = self
.report_creator
.event_high_severity(time_stamp, event_id, params, mut_buf.as_mut_slice())
.map_err(PusError::ByteConversion)?;
sender.send_tm(tm_creator.into())?;
self.tm_hook.modify_tm(&mut tm_creator);
sender.send_tm(self.id, tm_creator.into())?;
Ok(())
}
}
@ -239,9 +266,10 @@ mod alloc_mod {
mod tests {
use super::*;
use crate::events::{EventU32, Severity};
use crate::pus::test_util::TEST_COMPONENT_ID_0;
use crate::pus::tests::CommonTmInfo;
use crate::pus::{EcssChannel, PusTmWrapper};
use crate::ChannelId;
use crate::pus::{ChannelWithId, PusTmVariant};
use crate::ComponentId;
use spacepackets::ByteConversionError;
use std::cell::RefCell;
use std::collections::VecDeque;
@ -255,6 +283,7 @@ mod tests {
#[derive(Debug, Eq, PartialEq, Clone)]
struct TmInfo {
pub sender_id: ComponentId,
pub common: CommonTmInfo,
pub event: EventU32,
pub aux_data: Vec<u8>,
@ -265,19 +294,19 @@ mod tests {
pub service_queue: RefCell<VecDeque<TmInfo>>,
}
impl EcssChannel for TestSender {
fn channel_id(&self) -> ChannelId {
impl ChannelWithId for TestSender {
fn id(&self) -> ComponentId {
0
}
}
impl EcssTmSenderCore for TestSender {
fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> {
fn send_tm(&self, sender_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError> {
match tm {
PusTmWrapper::InStore(_) => {
PusTmVariant::InStore(_) => {
panic!("TestSender: unexpected call with address");
}
PusTmWrapper::Direct(tm) => {
PusTmVariant::Direct(tm) => {
assert!(!tm.source_data().is_empty());
let src_data = tm.source_data();
assert!(src_data.len() >= 4);
@ -288,6 +317,7 @@ mod tests {
aux_data.extend_from_slice(&src_data[4..]);
}
self.service_queue.borrow_mut().push_back(TmInfo {
sender_id,
common: CommonTmInfo::new_from_tm(&tm),
event,
aux_data,
@ -345,7 +375,12 @@ mod tests {
error_data: Option<&[u8]>,
) {
let mut sender = TestSender::default();
let reporter = EventReporter::new(EXAMPLE_APID, max_event_aux_data_buf);
let reporter = EventReporter::new(
TEST_COMPONENT_ID_0.id(),
EXAMPLE_APID,
0,
max_event_aux_data_buf,
);
assert!(reporter.is_some());
let mut reporter = reporter.unwrap();
let time_stamp_empty: [u8; 7] = [0; 7];
@ -375,6 +410,7 @@ mod tests {
assert_eq!(tm_info.common.msg_counter, 0);
assert_eq!(tm_info.common.apid, EXAMPLE_APID);
assert_eq!(tm_info.event, event);
assert_eq!(tm_info.sender_id, TEST_COMPONENT_ID_0.id());
assert_eq!(tm_info.aux_data, error_copy);
}
@ -437,7 +473,7 @@ mod tests {
fn insufficient_buffer() {
let mut sender = TestSender::default();
for i in 0..3 {
let reporter = EventReporter::new(EXAMPLE_APID, i);
let reporter = EventReporter::new(0, EXAMPLE_APID, 0, i);
assert!(reporter.is_some());
let mut reporter = reporter.unwrap();
check_buf_too_small(&mut reporter, &mut sender, i);

View File

@ -157,8 +157,8 @@ pub mod alloc_mod {
phantom: PhantomData<(E, EV)>,
}
impl<B: PusEventMgmtBackendProvider<EV, Error = E>, EV: GenericEvent, E>
PusEventDispatcher<B, EV, E>
impl<B: PusEventMgmtBackendProvider<Event, Error = E>, Event: GenericEvent, E>
PusEventDispatcher<B, Event, E>
{
pub fn new(reporter: EventReporter, backend: B) -> Self {
Self {
@ -168,20 +168,20 @@ pub mod alloc_mod {
}
}
pub fn enable_tm_for_event(&mut self, event: &EV) -> Result<bool, E> {
pub fn enable_tm_for_event(&mut self, event: &Event) -> Result<bool, E> {
self.backend.enable_event_reporting(event)
}
pub fn disable_tm_for_event(&mut self, event: &EV) -> Result<bool, E> {
pub fn disable_tm_for_event(&mut self, event: &Event) -> Result<bool, E> {
self.backend.disable_event_reporting(event)
}
pub fn generate_pus_event_tm_generic(
&mut self,
sender: &mut (impl EcssTmSenderCore + ?Sized),
&self,
sender: &(impl EcssTmSenderCore + ?Sized),
time_stamp: &[u8],
event: EV,
aux_data: Option<&[u8]>,
event: Event,
params: Option<&[u8]>,
) -> Result<bool, EventManError> {
if !self.backend.event_enabled(&event) {
return Ok(false);
@ -189,22 +189,22 @@ pub mod alloc_mod {
match event.severity() {
Severity::INFO => self
.reporter
.event_info(sender, time_stamp, event, aux_data)
.event_info(sender, time_stamp, event, params)
.map(|_| true)
.map_err(|e| e.into()),
Severity::LOW => self
.reporter
.event_low_severity(sender, time_stamp, event, aux_data)
.event_low_severity(sender, time_stamp, event, params)
.map(|_| true)
.map_err(|e| e.into()),
Severity::MEDIUM => self
.reporter
.event_medium_severity(sender, time_stamp, event, aux_data)
.event_medium_severity(sender, time_stamp, event, params)
.map(|_| true)
.map_err(|e| e.into()),
Severity::HIGH => self
.reporter
.event_high_severity(sender, time_stamp, event, aux_data)
.event_high_severity(sender, time_stamp, event, params)
.map(|_| true)
.map_err(|e| e.into()),
}
@ -239,8 +239,8 @@ pub mod alloc_mod {
}
pub fn generate_pus_event_tm<Severity: HasSeverity>(
&mut self,
sender: &mut (impl EcssTmSenderCore + ?Sized),
&self,
sender: &(impl EcssTmSenderCore + ?Sized),
time_stamp: &[u8],
event: EventU32TypedSev<Severity>,
aux_data: Option<&[u8]>,
@ -257,31 +257,36 @@ pub mod alloc_mod {
#[cfg(test)]
mod tests {
use super::*;
use crate::{events::SeverityInfo, pus::TmAsVecSenderWithMpsc};
use crate::events::SeverityInfo;
use crate::pus::PusTmAsVec;
use crate::request::UniqueApidTargetId;
use std::sync::mpsc::{self, TryRecvError};
const INFO_EVENT: EventU32TypedSev<SeverityInfo> =
EventU32TypedSev::<SeverityInfo>::const_new(1, 0);
const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5);
const EMPTY_STAMP: [u8; 7] = [0; 7];
const TEST_APID: u16 = 0x02;
const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
fn create_basic_man_1() -> DefaultPusEventU32Dispatcher<()> {
let reporter = EventReporter::new(0x02, 128).expect("Creating event repoter failed");
let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128)
.expect("Creating event repoter failed");
PusEventDispatcher::new_with_default_backend(reporter)
}
fn create_basic_man_2() -> DefaultPusEventU32Dispatcher<()> {
let reporter = EventReporter::new(0x02, 128).expect("Creating event repoter failed");
let reporter = EventReporter::new(TEST_ID.raw(), TEST_APID, 0, 128)
.expect("Creating event repoter failed");
let backend = DefaultPusEventMgmtBackend::default();
PusEventDispatcher::new(reporter, backend)
}
#[test]
fn test_basic() {
let mut event_man = create_basic_man_1();
let (event_tx, event_rx) = mpsc::channel();
let mut sender = TmAsVecSenderWithMpsc::new(0, "test_sender", event_tx);
let event_man = create_basic_man_1();
let (event_tx, event_rx) = mpsc::channel::<PusTmAsVec>();
let event_sent = event_man
.generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None)
.generate_pus_event_tm(&event_tx, &EMPTY_STAMP, INFO_EVENT, None)
.expect("Sending info event failed");
assert!(event_sent);
@ -292,13 +297,13 @@ mod tests {
#[test]
fn test_disable_event() {
let mut event_man = create_basic_man_2();
let (event_tx, event_rx) = mpsc::channel();
let mut sender = TmAsVecSenderWithMpsc::new(0, "test", event_tx);
let (event_tx, event_rx) = mpsc::channel::<PusTmAsVec>();
// let mut sender = TmAsVecSenderWithMpsc::new(0, "test", event_tx);
let res = event_man.disable_tm_for_event(&LOW_SEV_EVENT);
assert!(res.is_ok());
assert!(res.unwrap());
let mut event_sent = event_man
.generate_pus_event_tm_generic(&mut sender, &EMPTY_STAMP, LOW_SEV_EVENT, None)
.generate_pus_event_tm_generic(&event_tx, &EMPTY_STAMP, LOW_SEV_EVENT, None)
.expect("Sending low severity event failed");
assert!(!event_sent);
let res = event_rx.try_recv();
@ -306,7 +311,7 @@ mod tests {
assert!(matches!(res.unwrap_err(), TryRecvError::Empty));
// Check that only the low severity event was disabled
event_sent = event_man
.generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None)
.generate_pus_event_tm(&event_tx, &EMPTY_STAMP, INFO_EVENT, None)
.expect("Sending info event failed");
assert!(event_sent);
event_rx.try_recv().expect("No info event received");
@ -315,8 +320,7 @@ mod tests {
#[test]
fn test_reenable_event() {
let mut event_man = create_basic_man_1();
let (event_tx, event_rx) = mpsc::channel();
let mut sender = TmAsVecSenderWithMpsc::new(0, "test", event_tx);
let (event_tx, event_rx) = mpsc::channel::<PusTmAsVec>();
let mut res = event_man.disable_tm_for_event_with_sev(&INFO_EVENT);
assert!(res.is_ok());
assert!(res.unwrap());
@ -324,7 +328,7 @@ mod tests {
assert!(res.is_ok());
assert!(res.unwrap());
let event_sent = event_man
.generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None)
.generate_pus_event_tm(&event_tx, &EMPTY_STAMP, INFO_EVENT, None)
.expect("Sending info event failed");
assert!(event_sent);
event_rx.try_recv().expect("No info event received");

View File

@ -2,17 +2,18 @@ use crate::events::EventU32;
use crate::pus::event_man::{EventRequest, EventRequestWithToken};
use crate::pus::verification::TcStateToken;
use crate::pus::{PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError};
use crate::queue::GenericSendError;
use spacepackets::ecss::event::Subservice;
use spacepackets::ecss::PusPacket;
use std::sync::mpsc::Sender;
use super::verification::VerificationReportingProvider;
use super::{
get_current_cds_short_timestamp, EcssTcInMemConverter, EcssTcReceiverCore, EcssTmSenderCore,
PusServiceHelper,
EcssTcInMemConverter, EcssTcReceiverCore, EcssTmSenderCore, GenericConversionError,
GenericRoutingError, PusServiceHelper,
};
pub struct PusService5EventHandler<
pub struct PusEventServiceHandler<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
@ -28,7 +29,7 @@ impl<
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
> PusService5EventHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
> PusEventServiceHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
{
pub fn new(
service_helper: PusServiceHelper<
@ -45,16 +46,19 @@ impl<
}
}
pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
pub fn poll_and_handle_next_tc(
&mut self,
time_stamp: &[u8],
) -> 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
.service_helper
.tc_in_mem_converter
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
self.service_helper
.tc_in_mem_converter_mut()
.cache(&ecss_tc_and_token.tc_in_memory)?;
let tc = self.service_helper.tc_in_mem_converter().convert()?;
let subservice = tc.subservice();
let srv = Subservice::try_from(subservice);
if srv.is_err() {
@ -63,63 +67,73 @@ impl<
ecss_tc_and_token.token,
));
}
let handle_enable_disable_request = |enable: bool, stamp: [u8; 7]| {
if tc.user_data().len() < 4 {
return Err(PusPacketHandlingError::NotEnoughAppData {
expected: 4,
found: tc.user_data().len(),
});
}
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
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &stamp)
.map_err(|_| PartialPusHandlingError::Verification);
let partial_error = start_token.clone().err();
let mut token: TcStateToken = ecss_tc_and_token.token.into();
if let Ok(start_token) = start_token {
token = start_token.into();
}
let event_req_with_token = if enable {
EventRequestWithToken {
request: EventRequest::Enable(event_u32),
token,
let handle_enable_disable_request =
|enable: bool| -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
if tc.user_data().len() < 4 {
return Err(GenericConversionError::NotEnoughAppData {
expected: 4,
found: tc.user_data().len(),
}
.into());
}
} else {
EventRequestWithToken {
request: EventRequest::Disable(event_u32),
token,
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
.service_helper
.common
.verif_reporter
.start_success(
&self.service_helper.common.tm_sender,
ecss_tc_and_token.token,
time_stamp,
)
.map_err(|_| PartialPusHandlingError::Verification);
let partial_error = start_token.clone().err();
let mut token: TcStateToken = ecss_tc_and_token.token.into();
if let Ok(start_token) = start_token {
token = start_token.into();
}
let event_req_with_token = if enable {
EventRequestWithToken {
request: EventRequest::Enable(event_u32),
token,
}
} else {
EventRequestWithToken {
request: EventRequest::Disable(event_u32),
token,
}
};
self.event_request_tx
.send(event_req_with_token)
.map_err(|_| {
PusPacketHandlingError::RequestRouting(GenericRoutingError::Send(
GenericSendError::RxDisconnected,
))
})?;
if let Some(partial_error) = partial_error {
return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess(
partial_error,
));
}
Ok(PusPacketHandlerResult::RequestHandled)
};
self.event_request_tx
.send(event_req_with_token)
.map_err(|_| {
PusPacketHandlingError::Other("Forwarding event request failed".into())
})?;
if let Some(partial_error) = partial_error {
return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess(
partial_error,
));
}
Ok(PusPacketHandlerResult::RequestHandled)
};
let mut partial_error = None;
let time_stamp = get_current_cds_short_timestamp(&mut partial_error);
match srv.unwrap() {
Subservice::TmInfoReport
| Subservice::TmLowSeverityReport
| Subservice::TmMediumSeverityReport
| Subservice::TmHighSeverityReport => {
return Err(PusPacketHandlingError::InvalidSubservice(tc.subservice()))
return Err(PusPacketHandlingError::RequestConversion(
GenericConversionError::WrongService(tc.subservice()),
))
}
Subservice::TcEnableEventGeneration => {
handle_enable_disable_request(true, time_stamp)?;
handle_enable_disable_request(true)?;
}
Subservice::TcDisableEventGeneration => {
handle_enable_disable_request(false, time_stamp)?;
handle_enable_disable_request(false)?;
}
Subservice::TcReportDisabledList | Subservice::TmDisabledEventsReport => {
return Ok(PusPacketHandlerResult::SubserviceNotImplemented(
@ -137,60 +151,70 @@ impl<
mod tests {
use delegate::delegate;
use spacepackets::ecss::event::Subservice;
use spacepackets::time::{cds, TimeWriter};
use spacepackets::util::UnsignedEnum;
use spacepackets::{
ecss::{
tc::{PusTcCreator, PusTcSecondaryHeader},
tm::PusTmReader,
},
SequenceFlags, SpHeader,
SpHeader,
};
use std::sync::mpsc::{self, Sender};
use crate::pus::event_man::EventRequest;
use crate::pus::tests::SimplePusPacketHandler;
use crate::pus::test_util::{PusTestHarness, SimplePusPacketHandler, TEST_APID};
use crate::pus::verification::{
RequestId, VerificationReporterWithSharedPoolMpscBoundedSender,
RequestId, VerificationReporter, VerificationReportingProvider,
};
use crate::pus::{MpscTcReceiver, TmInSharedPoolSenderWithBoundedMpsc};
use crate::pus::{GenericConversionError, MpscTcReceiver, MpscTmInSharedPoolSenderBounded};
use crate::{
events::EventU32,
pus::{
event_man::EventRequestWithToken,
tests::{PusServiceHandlerWithSharedStoreCommon, PusTestHarness, TEST_APID},
tests::PusServiceHandlerWithSharedStoreCommon,
verification::{TcStateAccepted, VerificationToken},
EcssTcInSharedStoreConverter, PusPacketHandlerResult, PusPacketHandlingError,
},
};
use super::PusService5EventHandler;
use super::PusEventServiceHandler;
const TEST_EVENT_0: EventU32 = EventU32::const_new(crate::events::Severity::INFO, 5, 25);
struct Pus5HandlerWithStoreTester {
common: PusServiceHandlerWithSharedStoreCommon,
handler: PusService5EventHandler<
handler: PusEventServiceHandler<
MpscTcReceiver,
TmInSharedPoolSenderWithBoundedMpsc,
MpscTmInSharedPoolSenderBounded,
EcssTcInSharedStoreConverter,
VerificationReporterWithSharedPoolMpscBoundedSender,
VerificationReporter,
>,
}
impl Pus5HandlerWithStoreTester {
pub fn new(event_request_tx: Sender<EventRequestWithToken>) -> Self {
let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new();
let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new(0);
Self {
common,
handler: PusService5EventHandler::new(srv_handler, event_request_tx),
handler: PusEventServiceHandler::new(srv_handler, event_request_tx),
}
}
}
impl PusTestHarness for Pus5HandlerWithStoreTester {
fn init_verification(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted> {
let init_token = self.handler.service_helper.verif_reporter_mut().add_tc(tc);
self.handler
.service_helper
.verif_reporter()
.acceptance_success(self.handler.service_helper.tm_sender(), init_token, &[0; 7])
.expect("acceptance success failure")
}
delegate! {
to self.common {
fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
fn send_tc(&self, token: &VerificationToken<TcStateAccepted>, tc: &PusTcCreator);
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);
@ -200,10 +224,9 @@ mod tests {
}
impl SimplePusPacketHandler for Pus5HandlerWithStoreTester {
delegate! {
to self.handler {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
}
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap();
self.handler.poll_and_handle_next_tc(&time_stamp)
}
}
@ -213,15 +236,16 @@ mod tests {
expected_event_req: EventRequest,
event_req_receiver: mpsc::Receiver<EventRequestWithToken>,
) {
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
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();
let ping_tc = PusTcCreator::new(sp_header, sec_header, &app_data, true);
let token = test_harness.init_verification(&ping_tc);
test_harness.send_tc(&token, &ping_tc);
let request_id = token.request_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);
@ -274,10 +298,11 @@ mod tests {
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 sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
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 ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
let token = test_harness.init_verification(&ping_tc);
test_harness.send_tc(&token, &ping_tc);
let result = test_harness.handle_one_tc();
assert!(result.is_ok());
let result = result.unwrap();
@ -292,15 +317,19 @@ mod tests {
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 sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
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 ping_tc = PusTcCreator::new(sp_header, sec_header, &[0, 1, 2], true);
let token = test_harness.init_verification(&ping_tc);
test_harness.send_tc(&token, &ping_tc);
let result = test_harness.handle_one_tc();
assert!(result.is_err());
let result = result.unwrap_err();
if let PusPacketHandlingError::NotEnoughAppData { expected, found } = result {
if let PusPacketHandlingError::RequestConversion(
GenericConversionError::NotEnoughAppData { expected, found },
) = result
{
assert_eq!(expected, 4);
assert_eq!(found, 3);
} else {

View File

@ -1,406 +0,0 @@
pub use spacepackets::ecss::hk::*;
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub use std_mod::*;
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub use alloc_mod::*;
use crate::{hk::HkRequest, TargetId};
use super::verification::{TcStateAccepted, VerificationToken};
/// This trait is an abstraction for the routing of PUS service 3 housekeeping requests to a
/// dedicated recipient using the generic [TargetId].
pub trait PusHkRequestRouter {
type Error;
fn route(
&self,
target_id: TargetId,
hk_request: HkRequest,
token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error>;
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub mod alloc_mod {
use spacepackets::ecss::tc::PusTcReader;
use crate::pus::verification::VerificationReportingProvider;
use super::*;
/// This trait is an abstraction for the conversion of a PUS service 8 action telecommand into
/// a [HkRequest].
///
/// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard.
/// The only requirement is that a valid [TargetId] and a [HkRequest] are returned by the
/// core conversion function.
///
/// The user should take care of performing the error handling as well. Some of the following
/// aspects might be relevant:
///
/// - Checking the validity of the APID, service ID, subservice ID.
/// - Checking the validity of the user data.
///
/// A [VerificationReportingProvider] is passed to the user to also allow handling
/// of the verification process as part of the PUS standard requirements.
pub trait PusHkToRequestConverter {
type Error;
fn convert(
&mut self,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) -> Result<(TargetId, HkRequest), Self::Error>;
}
}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub mod std_mod {
use crate::pus::{
get_current_cds_short_timestamp, verification::VerificationReportingProvider,
EcssTcInMemConverter, EcssTcReceiverCore, EcssTmSenderCore, GenericRoutingError,
PusPacketHandlerResult, PusPacketHandlingError, PusRoutingErrorHandler, PusServiceHelper,
};
use super::*;
/// This is a generic high-level handler for the PUS service 3 housekeeping service.
///
/// It performs the following handling steps:
///
/// 1. Retrieve the next TC packet from the [PusServiceHelper]. The [EcssTcInMemConverter]
/// allows to configure the used telecommand memory backend.
/// 2. Convert the TC to a targeted action request using the provided
/// [PusHkToRequestConverter]. The generic error type is constrained to the
/// [PusPacketHandlerResult] for the concrete implementation which offers a packet handler.
/// 3. Route the action request using the provided [PusHkRequestRouter]. The generic error
/// type is constrained to the [GenericRoutingError] for the concrete implementation.
/// 4. Handle all routing errors using the provided [PusRoutingErrorHandler]. The generic error
/// type is constrained to the [GenericRoutingError] for the concrete implementation.
pub struct PusService3HkHandler<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
RequestConverter: PusHkToRequestConverter,
RequestRouter: PusHkRequestRouter<Error = RoutingError>,
RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
RoutingError = GenericRoutingError,
> {
service_helper:
PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>,
pub request_converter: RequestConverter,
pub request_router: RequestRouter,
pub routing_error_handler: RoutingErrorHandler,
}
impl<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
RequestConverter: PusHkToRequestConverter<Error = PusPacketHandlingError>,
RequestRouter: PusHkRequestRouter<Error = RoutingError>,
RoutingErrorHandler: PusRoutingErrorHandler<Error = RoutingError>,
RoutingError: Clone,
>
PusService3HkHandler<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
RequestConverter,
RequestRouter,
RoutingErrorHandler,
RoutingError,
>
where
PusPacketHandlingError: From<RoutingError>,
{
pub fn new(
service_helper: PusServiceHelper<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
>,
request_converter: RequestConverter,
request_router: RequestRouter,
routing_error_handler: RoutingErrorHandler,
) -> Self {
Self {
service_helper,
request_converter,
request_router,
routing_error_handler,
}
}
pub fn handle_one_tc(&mut self) -> 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
.service_helper
.tc_in_mem_converter
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
let mut partial_error = None;
let time_stamp = get_current_cds_short_timestamp(&mut partial_error);
let (target_id, hk_request) = self.request_converter.convert(
ecss_tc_and_token.token,
&tc,
&time_stamp,
&self.service_helper.common.verification_handler,
)?;
if let Err(e) =
self.request_router
.route(target_id, hk_request, ecss_tc_and_token.token)
{
self.routing_error_handler.handle_error(
target_id,
ecss_tc_and_token.token,
&tc,
e.clone(),
&time_stamp,
&self.service_helper.common.verification_handler,
);
return Err(e.into());
}
Ok(PusPacketHandlerResult::RequestHandled)
}
}
}
#[cfg(test)]
mod tests {
use delegate::delegate;
use spacepackets::ecss::hk::Subservice;
use spacepackets::{
ecss::{
tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader},
tm::PusTmReader,
PusPacket,
},
CcsdsPacket, SequenceFlags, SpHeader,
};
use crate::pus::{MpscTcReceiver, TmAsVecSenderWithMpsc};
use crate::{
hk::HkRequest,
pus::{
tests::{
PusServiceHandlerWithVecCommon, PusTestHarness, SimplePusPacketHandler,
TestConverter, TestRouter, TestRoutingErrorHandler, APP_DATA_TOO_SHORT, TEST_APID,
},
verification::{
tests::TestVerificationReporter, FailParams, RequestId, TcStateAccepted,
VerificationReportingProvider, VerificationToken,
},
EcssTcInVecConverter, GenericRoutingError, PusPacketHandlerResult,
PusPacketHandlingError,
},
TargetId,
};
use super::{PusHkRequestRouter, PusHkToRequestConverter, PusService3HkHandler};
impl PusHkRequestRouter for TestRouter<HkRequest> {
type Error = GenericRoutingError;
fn route(
&self,
target_id: TargetId,
hk_request: HkRequest,
_token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error> {
self.routing_requests
.borrow_mut()
.push_back((target_id, hk_request));
self.check_for_injected_error()
}
}
impl PusHkToRequestConverter for TestConverter<3> {
type Error = PusPacketHandlingError;
fn convert(
&mut self,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) -> Result<(TargetId, HkRequest), Self::Error> {
self.conversion_request.push_back(tc.raw_data().to_vec());
self.check_service(tc)?;
let target_id = tc.apid();
if tc.user_data().len() < 4 {
verif_reporter
.start_failure(
token,
FailParams::new(
time_stamp,
&APP_DATA_TOO_SHORT,
(tc.user_data().len() as u32).to_be_bytes().as_ref(),
),
)
.expect("start success failure");
return Err(PusPacketHandlingError::NotEnoughAppData {
expected: 4,
found: tc.user_data().len(),
});
}
if tc.subservice() == Subservice::TcGenerateOneShotHk as u8 {
verif_reporter
.start_success(token, time_stamp)
.expect("start success failure");
return Ok((
target_id.into(),
HkRequest::OneShot(u32::from_be_bytes(
tc.user_data()[0..4].try_into().unwrap(),
)),
));
}
Err(PusPacketHandlingError::InvalidAppData(
"unexpected app data".into(),
))
}
}
struct Pus3HandlerWithVecTester {
common: PusServiceHandlerWithVecCommon<TestVerificationReporter>,
handler: PusService3HkHandler<
MpscTcReceiver,
TmAsVecSenderWithMpsc,
EcssTcInVecConverter,
TestVerificationReporter,
TestConverter<3>,
TestRouter<HkRequest>,
TestRoutingErrorHandler,
>,
}
impl Pus3HandlerWithVecTester {
pub fn new() -> Self {
let (common, srv_handler) =
PusServiceHandlerWithVecCommon::new_with_test_verif_sender();
Self {
common,
handler: PusService3HkHandler::new(
srv_handler,
TestConverter::default(),
TestRouter::default(),
TestRoutingErrorHandler::default(),
),
}
}
delegate! {
to self.handler.request_converter {
pub fn check_next_conversion(&mut self, tc: &PusTcCreator);
}
}
delegate! {
to self.handler.request_router {
pub fn retrieve_next_request(&mut self) -> (TargetId, HkRequest);
}
}
delegate! {
to self.handler.routing_error_handler {
pub fn retrieve_next_error(&mut self) -> (TargetId, GenericRoutingError);
}
}
}
impl PusTestHarness for Pus3HandlerWithVecTester {
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 Pus3HandlerWithVecTester {
delegate! {
to self.handler {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
}
}
}
#[test]
fn basic_test() {
let mut hk_handler = Pus3HandlerWithVecTester::new();
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
let sec_header = PusTcSecondaryHeader::new_simple(3, Subservice::TcGenerateOneShotHk as u8);
let unique_id: u32 = 1;
let unique_id_raw = unique_id.to_be_bytes();
let tc = PusTcCreator::new(&mut sp_header, sec_header, unique_id_raw.as_ref(), true);
hk_handler.send_tc(&tc);
let result = hk_handler.handle_one_tc();
assert!(result.is_ok());
hk_handler.check_next_conversion(&tc);
let (target_id, hk_request) = hk_handler.retrieve_next_request();
assert_eq!(target_id, TEST_APID.into());
if let HkRequest::OneShot(id) = hk_request {
assert_eq!(id, unique_id);
} else {
panic!("unexpected request");
}
}
#[test]
fn test_routing_error() {
let mut hk_handler = Pus3HandlerWithVecTester::new();
let mut sp_header = SpHeader::tc(TEST_APID, SequenceFlags::Unsegmented, 0, 0).unwrap();
let sec_header = PusTcSecondaryHeader::new_simple(3, Subservice::TcGenerateOneShotHk as u8);
let unique_id: u32 = 1;
let unique_id_raw = unique_id.to_be_bytes();
let tc = PusTcCreator::new(&mut sp_header, sec_header, unique_id_raw.as_ref(), true);
let error = GenericRoutingError::UnknownTargetId(25);
hk_handler
.handler
.request_router
.inject_routing_error(error);
hk_handler.send_tc(&tc);
let result = hk_handler.handle_one_tc();
assert!(result.is_err());
let check_error = |routing_error: GenericRoutingError| {
if let GenericRoutingError::UnknownTargetId(id) = routing_error {
assert_eq!(id, 25);
} else {
panic!("unexpected error type");
}
};
if let PusPacketHandlingError::RequestRoutingError(routing_error) = result.unwrap_err() {
check_error(routing_error);
} else {
panic!("unexpected error type");
}
hk_handler.check_next_conversion(&tc);
let (target_id, hk_req) = hk_handler.retrieve_next_request();
assert_eq!(target_id, TEST_APID.into());
if let HkRequest::OneShot(unique_id) = hk_req {
assert_eq!(unique_id, 1);
}
let (target_id, found_error) = hk_handler.retrieve_next_error();
assert_eq!(target_id, TEST_APID.into());
check_error(found_error);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,16 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "alloc")]
#[allow(unused_imports)]
pub use alloc_mod::*;
#[cfg(feature = "std")]
#[allow(unused_imports)]
pub use std_mod::*;
pub const MODE_SERVICE_ID: u8 = 200;
#[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
@ -14,3 +24,134 @@ pub enum Subservice {
TmCantReachMode = 7,
TmWrongModeReply = 8,
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub mod alloc_mod {}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub mod std_mod {}
#[cfg(test)]
mod tests {
use std::sync::mpsc;
use crate::{
mode::{
ModeAndSubmode, ModeReply, ModeReplySender, ModeRequest, ModeRequestSender,
ModeRequestorAndHandlerMpsc, ModeRequestorMpsc,
},
request::{GenericMessage, MessageMetadata},
};
const TEST_COMPONENT_ID_0: u64 = 5;
const TEST_COMPONENT_ID_1: u64 = 6;
const TEST_COMPONENT_ID_2: u64 = 7;
#[test]
fn test_simple_mode_requestor() {
let (reply_sender, reply_receiver) = mpsc::channel();
let (request_sender, request_receiver) = mpsc::channel();
let mut mode_requestor = ModeRequestorMpsc::new(TEST_COMPONENT_ID_0, reply_receiver);
mode_requestor.add_message_target(TEST_COMPONENT_ID_1, request_sender);
// Send a request and verify it arrives at the receiver.
let request_id = 2;
let sent_request = ModeRequest::ReadMode;
mode_requestor
.send_mode_request(request_id, TEST_COMPONENT_ID_1, sent_request)
.expect("send failed");
let request = request_receiver.recv().expect("recv failed");
assert_eq!(request.request_id(), 2);
assert_eq!(request.sender_id(), TEST_COMPONENT_ID_0);
assert_eq!(request.message, sent_request);
// Send a reply and verify it arrives at the requestor.
let mode_reply = ModeReply::ModeReply(ModeAndSubmode::new(1, 5));
reply_sender
.send(GenericMessage::new(
MessageMetadata::new(request_id, TEST_COMPONENT_ID_1),
mode_reply,
))
.expect("send failed");
let reply = mode_requestor.try_recv_mode_reply().expect("recv failed");
assert!(reply.is_some());
let reply = reply.unwrap();
assert_eq!(reply.sender_id(), TEST_COMPONENT_ID_1);
assert_eq!(reply.request_id(), 2);
assert_eq!(reply.message, mode_reply);
}
#[test]
fn test_mode_requestor_and_request_handler_request_sending() {
let (_reply_sender_to_connector, reply_receiver_of_connector) = mpsc::channel();
let (_request_sender_to_connector, request_receiver_of_connector) = mpsc::channel();
let (request_sender_to_channel_1, request_receiver_channel_1) = mpsc::channel();
//let (reply_sender_to_channel_2, reply_receiver_channel_2) = mpsc::channel();
let mut mode_connector = ModeRequestorAndHandlerMpsc::new(
TEST_COMPONENT_ID_0,
request_receiver_of_connector,
reply_receiver_of_connector,
);
assert_eq!(
ModeRequestSender::local_channel_id(&mode_connector),
TEST_COMPONENT_ID_0
);
assert_eq!(
ModeReplySender::local_channel_id(&mode_connector),
TEST_COMPONENT_ID_0
);
assert_eq!(
mode_connector.local_channel_id_generic(),
TEST_COMPONENT_ID_0
);
mode_connector.add_request_target(TEST_COMPONENT_ID_1, request_sender_to_channel_1);
// Send a request and verify it arrives at the receiver.
let request_id = 2;
let sent_request = ModeRequest::ReadMode;
mode_connector
.send_mode_request(request_id, TEST_COMPONENT_ID_1, sent_request)
.expect("send failed");
let request = request_receiver_channel_1.recv().expect("recv failed");
assert_eq!(request.request_id(), 2);
assert_eq!(request.sender_id(), TEST_COMPONENT_ID_0);
assert_eq!(request.message, ModeRequest::ReadMode);
}
#[test]
fn test_mode_requestor_and_request_handler_reply_sending() {
let (_reply_sender_to_connector, reply_receiver_of_connector) = mpsc::channel();
let (_request_sender_to_connector, request_receiver_of_connector) = mpsc::channel();
let (reply_sender_to_channel_2, reply_receiver_channel_2) = mpsc::channel();
let mut mode_connector = ModeRequestorAndHandlerMpsc::new(
TEST_COMPONENT_ID_0,
request_receiver_of_connector,
reply_receiver_of_connector,
);
mode_connector.add_reply_target(TEST_COMPONENT_ID_2, reply_sender_to_channel_2);
// Send a reply and verify it arrives at the receiver.
let request_id = 2;
let sent_reply = ModeReply::ModeReply(ModeAndSubmode::new(3, 5));
mode_connector
.send_mode_reply(
MessageMetadata::new(request_id, TEST_COMPONENT_ID_2),
sent_reply,
)
.expect("send failed");
let reply = reply_receiver_channel_2.recv().expect("recv failed");
assert_eq!(reply.request_id(), 2);
assert_eq!(reply.sender_id(), TEST_COMPONENT_ID_0);
assert_eq!(reply.message, sent_reply);
}
#[test]
fn test_mode_reply_handler() {}
}

View File

@ -381,7 +381,7 @@ pub mod alloc_mod {
/// a [crate::pool::PoolProvider] API. This data structure just tracks the store
/// addresses and their release times and offers a convenient API to insert and release
/// telecommands and perform other functionality specified by the ECSS standard in section 6.11.
/// The time is tracked as a [spacepackets::time::UnixTimestamp] but the only requirement to
/// The time is tracked as a [spacepackets::time::UnixTime] but the only requirement to
/// the timekeeping of the user is that it is convertible to that timestamp.
///
/// The standard also specifies that the PUS scheduler can be enabled and disabled.
@ -871,28 +871,28 @@ mod tests {
cds::CdsTime::from_unix_time_with_u16_days(&timestamp, cds::SubmillisPrecision::Absent)
.unwrap();
let len_time_stamp = cds_time.write_to_bytes(buf).unwrap();
let len_packet = base_ping_tc_simple_ctor(0, None)
let len_packet = base_ping_tc_simple_ctor(0, &[])
.write_to_bytes(&mut buf[len_time_stamp..])
.unwrap();
(
SpHeader::tc_unseg(0x02, 0x34, len_packet as u16).unwrap(),
SpHeader::new_for_unseg_tc(0x02, 0x34, len_packet as u16),
len_packet + len_time_stamp,
)
}
fn scheduled_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator {
let (mut sph, len_app_data) = pus_tc_base(timestamp, buf);
PusTcCreator::new_simple(&mut sph, 11, 4, Some(&buf[..len_app_data]), true)
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
PusTcCreator::new_simple(sph, 11, 4, &buf[..len_app_data], true)
}
fn wrong_tc_service(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator {
let (mut sph, len_app_data) = pus_tc_base(timestamp, buf);
PusTcCreator::new_simple(&mut sph, 12, 4, Some(&buf[..len_app_data]), true)
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
PusTcCreator::new_simple(sph, 12, 4, &buf[..len_app_data], true)
}
fn wrong_tc_subservice(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator {
let (mut sph, len_app_data) = pus_tc_base(timestamp, buf);
PusTcCreator::new_simple(&mut sph, 11, 5, Some(&buf[..len_app_data]), true)
let (sph, len_app_data) = pus_tc_base(timestamp, buf);
PusTcCreator::new_simple(sph, 11, 5, &buf[..len_app_data], true)
}
fn double_wrapped_time_tagged_tc(timestamp: UnixTime, buf: &mut [u8]) -> PusTcCreator {
@ -900,40 +900,31 @@ mod tests {
cds::CdsTime::from_unix_time_with_u16_days(&timestamp, cds::SubmillisPrecision::Absent)
.unwrap();
let len_time_stamp = cds_time.write_to_bytes(buf).unwrap();
let mut sph = SpHeader::tc_unseg(0x02, 0x34, 0).unwrap();
let sph = SpHeader::new_for_unseg_tc(0x02, 0x34, 0);
// app data should not matter, double wrapped time-tagged commands should be rejected right
// away
let inner_time_tagged_tc = PusTcCreator::new_simple(&mut sph, 11, 4, None, true);
let inner_time_tagged_tc = PusTcCreator::new_simple(sph, 11, 4, &[], true);
let packet_len = inner_time_tagged_tc
.write_to_bytes(&mut buf[len_time_stamp..])
.expect("writing inner time tagged tc failed");
PusTcCreator::new_simple(
&mut sph,
11,
4,
Some(&buf[..len_time_stamp + packet_len]),
true,
)
PusTcCreator::new_simple(sph, 11, 4, &buf[..len_time_stamp + packet_len], true)
}
fn invalid_time_tagged_cmd() -> PusTcCreator<'static> {
let mut sph = SpHeader::tc_unseg(0x02, 0x34, 1).unwrap();
PusTcCreator::new_simple(&mut sph, 11, 4, None, true)
let sph = SpHeader::new_for_unseg_tc(0x02, 0x34, 1);
PusTcCreator::new_simple(sph, 11, 4, &[], true)
}
fn base_ping_tc_simple_ctor(
seq_count: u16,
app_data: Option<&'static [u8]>,
) -> PusTcCreator<'static> {
let mut sph = SpHeader::tc_unseg(0x02, seq_count, 0).unwrap();
PusTcCreator::new_simple(&mut sph, 17, 1, app_data, true)
fn base_ping_tc_simple_ctor(seq_count: u16, app_data: &'static [u8]) -> PusTcCreator<'static> {
let sph = SpHeader::new_for_unseg_tc(0x02, seq_count, 0);
PusTcCreator::new_simple(sph, 17, 1, app_data, true)
}
fn ping_tc_to_store(
pool: &mut StaticMemoryPool,
buf: &mut [u8],
seq_count: u16,
app_data: Option<&'static [u8]>,
app_data: &'static [u8],
) -> TcInfo {
let ping_tc = base_ping_tc_simple_ctor(seq_count, app_data);
let ping_size = ping_tc.write_to_bytes(buf).expect("writing ping TC failed");
@ -957,7 +948,7 @@ mod tests {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
scheduler
.insert_unwrapped_and_stored_tc(
@ -967,7 +958,7 @@ mod tests {
.unwrap();
let app_data = &[0, 1, 2];
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, Some(app_data));
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, app_data);
scheduler
.insert_unwrapped_and_stored_tc(
UnixTime::new_only_secs(200),
@ -976,7 +967,7 @@ mod tests {
.unwrap();
let app_data = &[0, 1, 2];
let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, Some(app_data));
let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, app_data);
scheduler
.insert_unwrapped_and_stored_tc(
UnixTime::new_only_secs(300),
@ -1087,10 +1078,10 @@ mod tests {
let src_id_to_set = 12;
let apid_to_set = 0x22;
let seq_count = 105;
let mut sp_header = SpHeader::tc_unseg(apid_to_set, 105, 0).unwrap();
let sp_header = SpHeader::new_for_unseg_tc(apid_to_set, 105, 0);
let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
sec_header.source_id = src_id_to_set;
let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true);
let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
let req_id = RequestId::from_tc(&ping_tc);
assert_eq!(req_id.source_id(), src_id_to_set);
assert_eq!(req_id.apid(), apid_to_set);
@ -1106,13 +1097,13 @@ mod tests {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("insertion failed");
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None);
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1)
.expect("insertion failed");
@ -1171,13 +1162,13 @@ mod tests {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("insertion failed");
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None);
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_1)
.expect("insertion failed");
@ -1230,13 +1221,13 @@ mod tests {
scheduler.disable();
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("insertion failed");
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None);
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1)
.expect("insertion failed");
@ -1294,7 +1285,7 @@ mod tests {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
let info = scheduler
.insert_unwrapped_tc(
@ -1309,7 +1300,7 @@ mod tests {
let mut read_buf: [u8; 64] = [0; 64];
pool.read(&tc_info_0.addr(), &mut read_buf).unwrap();
let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data");
assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, None));
assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, &[]));
assert_eq!(scheduler.num_scheduled_telecommands(), 1);
@ -1332,7 +1323,7 @@ mod tests {
let read_len = pool.read(&addr_vec[0], &mut read_buf).unwrap();
let check_tc = PusTcReader::new(&read_buf).expect("incorrect Pus tc raw data");
assert_eq!(read_len, check_tc.1);
assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, None));
assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, &[]));
}
#[test]
@ -1356,7 +1347,7 @@ mod tests {
let read_len = pool.read(&info.addr, &mut buf).unwrap();
let check_tc = PusTcReader::new(&buf).expect("incorrect Pus tc raw data");
assert_eq!(read_len, check_tc.1);
assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, None));
assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, &[]));
assert_eq!(scheduler.num_scheduled_telecommands(), 1);
@ -1381,7 +1372,7 @@ mod tests {
let read_len = pool.read(&addr_vec[0], &mut buf).unwrap();
let check_tc = PusTcReader::new(&buf).expect("incorrect PUS tc raw data");
assert_eq!(read_len, check_tc.1);
assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, None));
assert_eq!(check_tc.0, base_ping_tc_simple_ctor(0, &[]));
}
#[test]
@ -1506,7 +1497,7 @@ mod tests {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("insertion failed");
@ -1540,7 +1531,7 @@ mod tests {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("insertion failed");
@ -1563,7 +1554,7 @@ mod tests {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("inserting tc failed");
@ -1581,7 +1572,7 @@ mod tests {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("inserting tc failed");
@ -1599,15 +1590,15 @@ mod tests {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("inserting tc failed");
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None);
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_1)
.expect("inserting tc failed");
let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, None);
let tc_info_2 = ping_tc_to_store(&mut pool, &mut buf, 2, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_2)
.expect("inserting tc failed");
@ -1667,7 +1658,7 @@ mod tests {
release_secs: u64,
) -> TcInfo {
let mut buf: [u8; 32] = [0; 32];
let tc_info = ping_tc_to_store(pool, &mut buf, seq_count, None);
let tc_info = ping_tc_to_store(pool, &mut buf, seq_count, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(release_secs as i64), tc_info)
@ -1915,13 +1906,13 @@ mod tests {
let mut scheduler = PusScheduler::new(UnixTime::new_only_secs(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(100), tc_info_0)
.expect("insertion failed");
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, None);
let tc_info_1 = ping_tc_to_store(&mut pool, &mut buf, 1, &[]);
scheduler
.insert_unwrapped_and_stored_tc(UnixTime::new_only_secs(200), tc_info_1)
.expect("insertion failed");
@ -1949,13 +1940,13 @@ mod tests {
#[test]
fn test_generic_insert_app_data_test() {
let time_writer = cds::CdsTime::new_with_u16_days(1, 1);
let mut sph = SpHeader::new(
PacketId::const_new(PacketType::Tc, true, 0x002),
PacketSequenceCtrl::const_new(SequenceFlags::Unsegmented, 5),
let sph = SpHeader::new(
PacketId::new(PacketType::Tc, true, 0x002),
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 5),
0,
);
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
let ping_tc = PusTcCreator::new_no_app_data(&mut sph, sec_header, true);
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, true);
let mut buf: [u8; 64] = [0; 64];
let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
assert!(result.is_ok());
@ -1971,13 +1962,13 @@ mod tests {
#[test]
fn test_generic_insert_app_data_test_byte_conv_error() {
let time_writer = cds::CdsTime::new_with_u16_days(1, 1);
let mut sph = SpHeader::new(
PacketId::const_new(PacketType::Tc, true, 0x002),
PacketSequenceCtrl::const_new(SequenceFlags::Unsegmented, 5),
let sph = SpHeader::new(
PacketId::new(PacketType::Tc, true, 0x002),
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 5),
0,
);
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
let ping_tc = PusTcCreator::new_no_app_data(&mut sph, sec_header, true);
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, true);
let mut buf: [u8; 16] = [0; 16];
let result = generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc);
assert!(result.is_err());
@ -2000,13 +1991,13 @@ mod tests {
#[test]
fn test_generic_insert_app_data_test_as_vec() {
let time_writer = cds::CdsTime::new_with_u16_days(1, 1);
let mut sph = SpHeader::new(
PacketId::const_new(PacketType::Tc, true, 0x002),
PacketSequenceCtrl::const_new(SequenceFlags::Unsegmented, 5),
let sph = SpHeader::new(
PacketId::new(PacketType::Tc, true, 0x002),
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 5),
0,
);
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
let ping_tc = PusTcCreator::new_no_app_data(&mut sph, sec_header, true);
let ping_tc = PusTcCreator::new_no_app_data(sph, sec_header, true);
let mut buf: [u8; 64] = [0; 64];
generate_insert_telecommand_app_data(&mut buf, &time_writer, &ping_tc).unwrap();
let vec = generate_insert_telecommand_app_data_as_vec(&time_writer, &ping_tc)

View File

@ -1,20 +1,16 @@
use super::scheduler::PusSchedulerProvider;
use super::verification::{
VerificationReporterWithSharedPoolMpscBoundedSender,
VerificationReporterWithSharedPoolMpscSender, VerificationReporterWithVecMpscBoundedSender,
VerificationReporterWithVecMpscSender, VerificationReportingProvider,
};
use super::verification::{VerificationReporter, VerificationReportingProvider};
use super::{
get_current_cds_short_timestamp, EcssTcInMemConverter, EcssTcInSharedStoreConverter,
EcssTcInVecConverter, EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusServiceHelper,
TmAsVecSenderWithBoundedMpsc, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc,
TmInSharedPoolSenderWithMpsc,
EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiverCore,
EcssTmSenderCore, MpscTcReceiver, MpscTmInSharedPoolSender, MpscTmInSharedPoolSenderBounded,
PusServiceHelper, PusTmAsVec,
};
use crate::pool::PoolProvider;
use crate::pus::{PusPacketHandlerResult, PusPacketHandlingError};
use alloc::string::ToString;
use spacepackets::ecss::{scheduling, PusPacket};
use spacepackets::time::cds::CdsTime;
use std::sync::mpsc;
/// This is a helper class for [std] environments to handle generic PUS 11 (scheduling service)
/// packets. This handler is able to handle the most important PUS requests for a scheduling
@ -24,7 +20,7 @@ use spacepackets::time::cds::CdsTime;
/// 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<
pub struct PusSchedServiceHandler<
TcReceiver: EcssTcReceiverCore,
TmSender: EcssTmSenderCore,
TcInMemConverter: EcssTcInMemConverter,
@ -43,13 +39,7 @@ impl<
VerificationReporter: VerificationReportingProvider,
Scheduler: PusSchedulerProvider,
>
PusService11SchedHandler<
TcReceiver,
TmSender,
TcInMemConverter,
VerificationReporter,
Scheduler,
>
PusSchedServiceHandler<TcReceiver, TmSender, TcInMemConverter, VerificationReporter, Scheduler>
{
pub fn new(
service_helper: PusServiceHelper<
@ -74,8 +64,9 @@ impl<
&self.scheduler
}
pub fn handle_one_tc(
pub fn poll_and_handle_next_tc(
&mut self,
time_stamp: &[u8],
sched_tc_pool: &mut (impl PoolProvider + ?Sized),
) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
@ -83,10 +74,10 @@ impl<
return Ok(PusPacketHandlerResult::Empty);
}
let ecss_tc_and_token = possible_packet.unwrap();
let tc = self
.service_helper
.tc_in_mem_converter
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
self.service_helper
.tc_in_mem_converter_mut()
.cache(&ecss_tc_and_token.tc_in_memory)?;
let tc = self.service_helper.tc_in_mem_converter().convert()?;
let subservice = PusPacket::subservice(&tc);
let standard_subservice = scheduling::Subservice::try_from(subservice);
if standard_subservice.is_err() {
@ -95,23 +86,28 @@ impl<
ecss_tc_and_token.token,
));
}
let mut partial_error = None;
let time_stamp = get_current_cds_short_timestamp(&mut partial_error);
let partial_error = None;
match standard_subservice.unwrap() {
scheduling::Subservice::TcEnableScheduling => {
let start_token = self
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.verif_reporter()
.start_success(
&self.service_helper.common.tm_sender,
ecss_tc_and_token.token,
time_stamp,
)
.expect("Error sending start success");
self.scheduler.enable();
if self.scheduler.is_enabled() {
self.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.verif_reporter()
.completion_success(
&self.service_helper.common.tm_sender,
start_token,
time_stamp,
)
.expect("Error sending completion success");
} else {
return Err(PusPacketHandlingError::Other(
@ -122,17 +118,23 @@ impl<
scheduling::Subservice::TcDisableScheduling => {
let start_token = self
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.verif_reporter()
.start_success(
&self.service_helper.common.tm_sender,
ecss_tc_and_token.token,
time_stamp,
)
.expect("Error sending start success");
self.scheduler.disable();
if !self.scheduler.is_enabled() {
self.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.verif_reporter()
.completion_success(
&self.service_helper.common.tm_sender,
start_token,
time_stamp,
)
.expect("Error sending completion success");
} else {
return Err(PusPacketHandlingError::Other(
@ -143,9 +145,12 @@ impl<
scheduling::Subservice::TcResetScheduling => {
let start_token = self
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.verif_reporter()
.start_success(
&self.service_helper.common.tm_sender,
ecss_tc_and_token.token,
time_stamp,
)
.expect("Error sending start success");
self.scheduler
@ -153,17 +158,24 @@ impl<
.expect("Error resetting TC Pool");
self.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.verif_reporter()
.completion_success(
&self.service_helper.common.tm_sender,
start_token,
time_stamp,
)
.expect("Error sending completion success");
}
scheduling::Subservice::TcInsertActivity => {
let start_token = self
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.verif_reporter
.start_success(
&self.service_helper.common.tm_sender,
ecss_tc_and_token.token,
time_stamp,
)
.expect("error sending start success");
// let mut pool = self.sched_tc_pool.write().expect("locking pool failed");
@ -172,9 +184,12 @@ impl<
.expect("insertion of activity into pool failed");
self.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.verif_reporter()
.completion_success(
&self.service_helper.common.tm_sender,
start_token,
time_stamp,
)
.expect("sending completion success failed");
}
_ => {
@ -195,53 +210,57 @@ impl<
}
/// Helper type definition for a PUS 11 handler with a dynamic TMTC memory backend and regular
/// mpsc queues.
pub type PusService11SchedHandlerDynWithMpsc<PusScheduler> = PusService11SchedHandler<
pub type PusService11SchedHandlerDynWithMpsc<PusScheduler> = PusSchedServiceHandler<
MpscTcReceiver,
TmAsVecSenderWithMpsc,
mpsc::Sender<PusTmAsVec>,
EcssTcInVecConverter,
VerificationReporterWithVecMpscSender,
VerificationReporter,
PusScheduler,
>;
/// Helper type definition for a PUS 11 handler with a dynamic TMTC memory backend and bounded MPSC
/// queues.
pub type PusService11SchedHandlerDynWithBoundedMpsc<PusScheduler> = PusService11SchedHandler<
pub type PusService11SchedHandlerDynWithBoundedMpsc<PusScheduler> = PusSchedServiceHandler<
MpscTcReceiver,
TmAsVecSenderWithBoundedMpsc,
mpsc::SyncSender<PusTmAsVec>,
EcssTcInVecConverter,
VerificationReporterWithVecMpscBoundedSender,
VerificationReporter,
PusScheduler,
>;
/// Helper type definition for a PUS 11 handler with a shared store TMTC memory backend and regular
/// mpsc queues.
pub type PusService11SchedHandlerStaticWithMpsc<PusScheduler> = PusService11SchedHandler<
pub type PusService11SchedHandlerStaticWithMpsc<PusScheduler> = PusSchedServiceHandler<
MpscTcReceiver,
TmInSharedPoolSenderWithMpsc,
MpscTmInSharedPoolSender,
EcssTcInSharedStoreConverter,
VerificationReporterWithSharedPoolMpscSender,
VerificationReporter,
PusScheduler,
>;
/// Helper type definition for a PUS 11 handler with a shared store TMTC memory backend and bounded
/// mpsc queues.
pub type PusService11SchedHandlerStaticWithBoundedMpsc<PusScheduler> = PusService11SchedHandler<
pub type PusService11SchedHandlerStaticWithBoundedMpsc<PusScheduler> = PusSchedServiceHandler<
MpscTcReceiver,
TmInSharedPoolSenderWithBoundedMpsc,
MpscTmInSharedPoolSenderBounded,
EcssTcInSharedStoreConverter,
VerificationReporterWithSharedPoolMpscBoundedSender,
VerificationReporter,
PusScheduler,
>;
#[cfg(test)]
mod tests {
use crate::pool::{StaticMemoryPool, StaticPoolConfig};
use crate::pus::tests::TEST_APID;
use crate::pus::verification::VerificationReporterWithSharedPoolMpscBoundedSender;
use crate::pus::test_util::{PusTestHarness, TEST_APID};
use crate::pus::verification::{VerificationReporter, VerificationReportingProvider};
use crate::pus::{
scheduler::{self, PusSchedulerProvider, TcInfo},
tests::{PusServiceHandlerWithSharedStoreCommon, PusTestHarness},
tests::PusServiceHandlerWithSharedStoreCommon,
verification::{RequestId, TcStateAccepted, VerificationToken},
EcssTcInSharedStoreConverter,
};
use crate::pus::{MpscTcReceiver, TmInSharedPoolSenderWithBoundedMpsc};
use crate::pus::{
MpscTcReceiver, MpscTmInSharedPoolSenderBounded, PusPacketHandlerResult,
PusPacketHandlingError,
};
use alloc::collections::VecDeque;
use delegate::delegate;
use spacepackets::ecss::scheduling::Subservice;
@ -254,15 +273,15 @@ mod tests {
time::cds,
};
use super::PusService11SchedHandler;
use super::PusSchedServiceHandler;
struct Pus11HandlerWithStoreTester {
common: PusServiceHandlerWithSharedStoreCommon,
handler: PusService11SchedHandler<
handler: PusSchedServiceHandler<
MpscTcReceiver,
TmInSharedPoolSenderWithBoundedMpsc,
MpscTmInSharedPoolSenderBounded,
EcssTcInSharedStoreConverter,
VerificationReporterWithSharedPoolMpscBoundedSender,
VerificationReporter,
TestScheduler,
>,
sched_tc_pool: StaticMemoryPool,
@ -273,19 +292,34 @@ mod tests {
let test_scheduler = TestScheduler::default();
let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)], false);
let sched_tc_pool = StaticMemoryPool::new(pool_cfg.clone());
let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new();
let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new(0);
Self {
common,
handler: PusService11SchedHandler::new(srv_handler, test_scheduler),
handler: PusSchedServiceHandler::new(srv_handler, test_scheduler),
sched_tc_pool,
}
}
pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap();
self.handler
.poll_and_handle_next_tc(&time_stamp, &mut self.sched_tc_pool)
}
}
impl PusTestHarness for Pus11HandlerWithStoreTester {
fn init_verification(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted> {
let init_token = self.handler.service_helper.verif_reporter_mut().add_tc(tc);
self.handler
.service_helper
.verif_reporter()
.acceptance_success(self.handler.service_helper.tm_sender(), init_token, &[0; 7])
.expect("acceptance success failure")
}
delegate! {
to self.common {
fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
fn send_tc(&self, token: &VerificationToken<TcStateAccepted>, tc: &PusTcCreator);
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);
@ -341,15 +375,17 @@ mod tests {
test_harness: &mut Pus11HandlerWithStoreTester,
subservice: Subservice,
) {
let mut reply_header = SpHeader::tm_unseg(TEST_APID, 0, 0).unwrap();
let reply_header = SpHeader::new_for_unseg_tm(TEST_APID, 0, 0);
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 enable_scheduling = PusTcCreator::new(reply_header, tc_header, &[0; 7], true);
let token = test_harness.init_verification(&enable_scheduling);
test_harness.send_tc(&token, &enable_scheduling);
let request_id = token.req_id();
let request_id = token.request_id();
let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap();
test_harness
.handler
.handle_one_tc(&mut test_harness.sched_tc_pool)
.poll_and_handle_next_tc(&time_stamp, &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);
@ -386,9 +422,9 @@ mod tests {
#[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 reply_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
let mut sec_header = PusTcSecondaryHeader::new_simple(17, 1);
let ping_tc = PusTcCreator::new(&mut reply_header, sec_header, &[], true);
let ping_tc = PusTcCreator::new(reply_header, sec_header, &[], true);
let req_id_ping_tc = scheduler::RequestId::from_tc(&ping_tc);
let stamper = cds::CdsTime::now_with_u16_days().expect("time provider failed");
let mut sched_app_data: [u8; 64] = [0; 64];
@ -396,21 +432,19 @@ mod tests {
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();
reply_header = SpHeader::new_for_unseg_tc(TEST_APID, 1, 0);
sec_header = PusTcSecondaryHeader::new_simple(11, Subservice::TcInsertActivity as u8);
let enable_scheduling = PusTcCreator::new(
&mut reply_header,
reply_header,
sec_header,
&sched_app_data[..written_len],
true,
);
let token = test_harness.send_tc(&enable_scheduling);
let token = test_harness.init_verification(&enable_scheduling);
test_harness.send_tc(&token, &enable_scheduling);
let request_id = token.req_id();
test_harness
.handler
.handle_one_tc(&mut test_harness.sched_tc_pool)
.unwrap();
let request_id = token.request_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);
test_harness.check_next_verification_tm(7, request_id);

View File

@ -1,20 +1,17 @@
use crate::pus::{
PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError, PusTmWrapper,
PartialPusHandlingError, PusPacketHandlerResult, PusPacketHandlingError, PusTmAsVec,
PusTmInPool, PusTmVariant,
};
use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
use spacepackets::ecss::PusPacket;
use spacepackets::SpHeader;
use std::sync::mpsc;
use super::verification::{
VerificationReporterWithSharedPoolMpscBoundedSender,
VerificationReporterWithSharedPoolMpscSender, VerificationReporterWithVecMpscBoundedSender,
VerificationReporterWithVecMpscSender, VerificationReportingProvider,
};
use super::verification::{VerificationReporter, VerificationReportingProvider};
use super::{
get_current_cds_short_timestamp, EcssTcInMemConverter, EcssTcInSharedStoreConverter,
EcssTcInVecConverter, EcssTcReceiverCore, EcssTmSenderCore, MpscTcReceiver, PusServiceHelper,
TmAsVecSenderWithBoundedMpsc, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc,
TmInSharedPoolSenderWithMpsc,
EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter, EcssTcReceiverCore,
EcssTmSenderCore, GenericConversionError, MpscTcReceiver, MpscTmInSharedPoolSender,
MpscTmInSharedPoolSenderBounded, PusServiceHelper,
};
/// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets.
@ -47,27 +44,32 @@ impl<
Self { service_helper }
}
pub fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
pub fn poll_and_handle_next_tc(
&mut self,
time_stamp: &[u8],
) -> 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
.service_helper
.tc_in_mem_converter
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
self.service_helper
.tc_in_mem_converter_mut()
.cache(&ecss_tc_and_token.tc_in_memory)?;
let tc = self.service_helper.tc_in_mem_converter().convert()?;
if tc.service() != 17 {
return Err(PusPacketHandlingError::WrongService(tc.service()));
return Err(GenericConversionError::WrongService(tc.service()).into());
}
if tc.subservice() == 1 {
let mut partial_error = None;
let time_stamp = get_current_cds_short_timestamp(&mut partial_error);
let result = self
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.verif_reporter()
.start_success(
&self.service_helper.common.tm_sender,
ecss_tc_and_token.token,
time_stamp,
)
.map_err(|_| PartialPusHandlingError::Verification);
let start_token = if let Ok(result) = result {
Some(result)
@ -76,15 +78,17 @@ impl<
None
};
// Sequence count will be handled centrally in TM funnel.
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);
// It is assumed that the verification reporter was built with a valid APID, so we use
// the unchecked API here.
let reply_header =
SpHeader::new_for_unseg_tm(self.service_helper.verif_reporter().apid(), 0, 0);
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, time_stamp);
let ping_reply = PusTmCreator::new(reply_header, tc_header, &[], true);
let result = self
.service_helper
.common
.tm_sender
.send_tm(PusTmWrapper::Direct(ping_reply))
.send_tm(self.service_helper.id(), PusTmVariant::Direct(ping_reply))
.map_err(PartialPusHandlingError::TmSend);
if let Err(err) = result {
partial_error = Some(err);
@ -93,9 +97,12 @@ impl<
if let Some(start_token) = start_token {
if self
.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.verif_reporter()
.completion_success(
&self.service_helper.common.tm_sender,
start_token,
time_stamp,
)
.is_err()
{
partial_error = Some(PartialPusHandlingError::Verification)
@ -120,55 +127,57 @@ impl<
/// mpsc queues.
pub type PusService17TestHandlerDynWithMpsc = PusService17TestHandler<
MpscTcReceiver,
TmAsVecSenderWithMpsc,
mpsc::Sender<PusTmAsVec>,
EcssTcInVecConverter,
VerificationReporterWithVecMpscSender,
VerificationReporter,
>;
/// Helper type definition for a PUS 17 handler with a dynamic TMTC memory backend and bounded MPSC
/// queues.
pub type PusService17TestHandlerDynWithBoundedMpsc = PusService17TestHandler<
MpscTcReceiver,
TmAsVecSenderWithBoundedMpsc,
mpsc::SyncSender<PusTmInPool>,
EcssTcInVecConverter,
VerificationReporterWithVecMpscBoundedSender,
VerificationReporter,
>;
/// Helper type definition for a PUS 17 handler with a shared store TMTC memory backend and regular
/// mpsc queues.
pub type PusService17TestHandlerStaticWithMpsc = PusService17TestHandler<
MpscTcReceiver,
TmInSharedPoolSenderWithMpsc,
MpscTmInSharedPoolSender,
EcssTcInSharedStoreConverter,
VerificationReporterWithSharedPoolMpscSender,
VerificationReporter,
>;
/// Helper type definition for a PUS 17 handler with a shared store TMTC memory backend and bounded
/// mpsc queues.
pub type PusService17TestHandlerStaticWithBoundedMpsc = PusService17TestHandler<
MpscTcReceiver,
TmInSharedPoolSenderWithBoundedMpsc,
MpscTmInSharedPoolSenderBounded,
EcssTcInSharedStoreConverter,
VerificationReporterWithSharedPoolMpscBoundedSender,
VerificationReporter,
>;
#[cfg(test)]
mod tests {
use crate::pus::test_util::{PusTestHarness, SimplePusPacketHandler, TEST_APID};
use crate::pus::tests::{
PusServiceHandlerWithSharedStoreCommon, PusServiceHandlerWithVecCommon, PusTestHarness,
SimplePusPacketHandler, TEST_APID,
PusServiceHandlerWithSharedStoreCommon, PusServiceHandlerWithVecCommon,
};
use crate::pus::verification::std_mod::{
VerificationReporterWithSharedPoolMpscBoundedSender, VerificationReporterWithVecMpscSender,
use crate::pus::verification::{
RequestId, VerificationReporter, VerificationReportingProvider,
};
use crate::pus::verification::RequestId;
use crate::pus::verification::{TcStateAccepted, VerificationToken};
use crate::pus::{
EcssTcInSharedStoreConverter, EcssTcInVecConverter, MpscTcReceiver, PusPacketHandlerResult,
PusPacketHandlingError, TmAsVecSenderWithMpsc, TmInSharedPoolSenderWithBoundedMpsc,
EcssTcInSharedStoreConverter, EcssTcInVecConverter, GenericConversionError, MpscTcReceiver,
MpscTmAsVecSender, MpscTmInSharedPoolSenderBounded, PusPacketHandlerResult,
PusPacketHandlingError,
};
use crate::ComponentId;
use delegate::delegate;
use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
use spacepackets::ecss::tm::PusTmReader;
use spacepackets::ecss::PusPacket;
use spacepackets::{SequenceFlags, SpHeader};
use spacepackets::time::{cds, TimeWriter};
use spacepackets::SpHeader;
use super::PusService17TestHandler;
@ -176,15 +185,15 @@ mod tests {
common: PusServiceHandlerWithSharedStoreCommon,
handler: PusService17TestHandler<
MpscTcReceiver,
TmInSharedPoolSenderWithBoundedMpsc,
MpscTmInSharedPoolSenderBounded,
EcssTcInSharedStoreConverter,
VerificationReporterWithSharedPoolMpscBoundedSender,
VerificationReporter,
>,
}
impl Pus17HandlerWithStoreTester {
pub fn new() -> Self {
let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new();
pub fn new(id: ComponentId) -> Self {
let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new(id);
let pus_17_handler = PusService17TestHandler::new(srv_handler);
Self {
common,
@ -194,10 +203,19 @@ mod tests {
}
impl PusTestHarness for Pus17HandlerWithStoreTester {
fn init_verification(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted> {
let init_token = self.handler.service_helper.verif_reporter_mut().add_tc(tc);
self.handler
.service_helper
.verif_reporter()
.acceptance_success(self.handler.service_helper.tm_sender(), init_token, &[0; 7])
.expect("acceptance success failure")
}
delegate! {
to self.common {
fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
fn read_next_tm(&mut self) -> PusTmReader<'_>;
fn send_tc(&self, token: &VerificationToken<TcStateAccepted>, tc: &PusTcCreator);
fn check_no_tm_available(&self) -> bool;
fn check_next_verification_tm(
&self,
@ -208,27 +226,26 @@ mod tests {
}
}
impl SimplePusPacketHandler for Pus17HandlerWithStoreTester {
delegate! {
to self.handler {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
}
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap();
self.handler.poll_and_handle_next_tc(&time_stamp)
}
}
struct Pus17HandlerWithVecTester {
common: PusServiceHandlerWithVecCommon<VerificationReporterWithVecMpscSender>,
common: PusServiceHandlerWithVecCommon,
handler: PusService17TestHandler<
MpscTcReceiver,
TmAsVecSenderWithMpsc,
MpscTmAsVecSender,
EcssTcInVecConverter,
VerificationReporterWithVecMpscSender,
VerificationReporter,
>,
}
impl Pus17HandlerWithVecTester {
pub fn new() -> Self {
pub fn new(id: ComponentId) -> Self {
let (common, srv_handler) =
PusServiceHandlerWithVecCommon::new_with_standard_verif_reporter();
PusServiceHandlerWithVecCommon::new_with_standard_verif_reporter(id);
Self {
common,
handler: PusService17TestHandler::new(srv_handler),
@ -237,9 +254,18 @@ mod tests {
}
impl PusTestHarness for Pus17HandlerWithVecTester {
fn init_verification(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted> {
let init_token = self.handler.service_helper.verif_reporter_mut().add_tc(tc);
self.handler
.service_helper
.verif_reporter()
.acceptance_success(self.handler.service_helper.tm_sender(), init_token, &[0; 7])
.expect("acceptance success failure")
}
delegate! {
to self.common {
fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
fn send_tc(&self, token: &VerificationToken<TcStateAccepted>, tc: &PusTcCreator);
fn read_next_tm(&mut self) -> PusTmReader<'_>;
fn check_no_tm_available(&self) -> bool;
fn check_next_verification_tm(
@ -251,20 +277,20 @@ mod tests {
}
}
impl SimplePusPacketHandler for Pus17HandlerWithVecTester {
delegate! {
to self.handler {
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
}
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let time_stamp = cds::CdsTime::new_with_u16_days(0, 0).to_vec().unwrap();
self.handler.poll_and_handle_next_tc(&time_stamp)
}
}
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 sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
let ping_tc = PusTcCreator::new_no_app_data(&mut sp_header, sec_header, true);
let token = test_harness.send_tc(&ping_tc);
let request_id = token.req_id();
let ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
let token = test_harness.init_verification(&ping_tc);
test_harness.send_tc(&token, &ping_tc);
let request_id = token.request_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
@ -288,19 +314,19 @@ mod tests {
#[test]
fn test_basic_ping_processing_using_store() {
let mut test_harness = Pus17HandlerWithStoreTester::new();
let mut test_harness = Pus17HandlerWithStoreTester::new(0);
ping_test(&mut test_harness);
}
#[test]
fn test_basic_ping_processing_using_vec() {
let mut test_harness = Pus17HandlerWithVecTester::new();
let mut test_harness = Pus17HandlerWithVecTester::new(0);
ping_test(&mut test_harness);
}
#[test]
fn test_empty_tc_queue() {
let mut test_harness = Pus17HandlerWithStoreTester::new();
let mut test_harness = Pus17HandlerWithStoreTester::new(0);
let result = test_harness.handle_one_tc();
assert!(result.is_ok());
let result = result.unwrap();
@ -312,15 +338,19 @@ mod tests {
#[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 mut test_harness = Pus17HandlerWithStoreTester::new(0);
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
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 ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
let token = test_harness.init_verification(&ping_tc);
test_harness.send_tc(&token, &ping_tc);
let result = test_harness.handle_one_tc();
assert!(result.is_err());
let error = result.unwrap_err();
if let PusPacketHandlingError::WrongService(num) = error {
if let PusPacketHandlingError::RequestConversion(GenericConversionError::WrongService(
num,
)) = error
{
assert_eq!(num, 3);
} else {
panic!("unexpected error type {error}")
@ -329,11 +359,12 @@ mod tests {
#[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 mut test_harness = Pus17HandlerWithStoreTester::new(0);
let sp_header = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
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 ping_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
let token = test_harness.init_verification(&ping_tc);
test_harness.send_tc(&token, &ping_tc);
let result = test_harness.handle_one_tc();
assert!(result.is_ok());
let result = result.unwrap();

File diff suppressed because it is too large Load Diff

View File

@ -4,11 +4,17 @@ use std::error::Error;
#[cfg(feature = "std")]
use std::sync::mpsc;
use crate::ComponentId;
/// Generic channel ID type.
pub type ChannelId = u32;
/// Generic error type for sending something via a message queue.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum GenericSendError {
RxDisconnected,
QueueFull(Option<u32>),
TargetDoesNotExist(ComponentId),
}
impl Display for GenericSendError {
@ -20,6 +26,9 @@ impl Display for GenericSendError {
GenericSendError::QueueFull(max_cap) => {
write!(f, "queue with max capacity of {max_cap:?} is full")
}
GenericSendError::TargetDoesNotExist(target) => {
write!(f, "target queue with ID {target} does not exist")
}
}
}
}
@ -28,17 +37,17 @@ impl Display for GenericSendError {
impl Error for GenericSendError {}
/// Generic error type for sending something via a message queue.
#[derive(Debug, Copy, Clone)]
pub enum GenericRecvError {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum GenericReceiveError {
Empty,
TxDisconnected,
TxDisconnected(Option<ComponentId>),
}
impl Display for GenericRecvError {
impl Display for GenericReceiveError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::TxDisconnected => {
write!(f, "tx side has disconnected")
Self::TxDisconnected(channel_id) => {
write!(f, "tx side with id {channel_id:?} has disconnected")
}
Self::Empty => {
write!(f, "nothing to receive")
@ -48,7 +57,43 @@ impl Display for GenericRecvError {
}
#[cfg(feature = "std")]
impl Error for GenericRecvError {}
impl Error for GenericReceiveError {}
#[derive(Debug, Clone)]
pub enum GenericTargetedMessagingError {
Send(GenericSendError),
Receive(GenericReceiveError),
}
impl From<GenericSendError> for GenericTargetedMessagingError {
fn from(value: GenericSendError) -> Self {
Self::Send(value)
}
}
impl From<GenericReceiveError> for GenericTargetedMessagingError {
fn from(value: GenericReceiveError) -> Self {
Self::Receive(value)
}
}
impl Display for GenericTargetedMessagingError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::Send(err) => write!(f, "generic targeted messaging error: {}", err),
Self::Receive(err) => write!(f, "generic targeted messaging error: {}", err),
}
}
}
#[cfg(feature = "std")]
impl Error for GenericTargetedMessagingError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
GenericTargetedMessagingError::Send(send) => Some(send),
GenericTargetedMessagingError::Receive(receive) => Some(receive),
}
}
}
#[cfg(feature = "std")]
impl<T> From<mpsc::SendError<T>> for GenericSendError {

View File

@ -1,110 +1,586 @@
use core::fmt;
use core::{fmt, marker::PhantomData};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "alloc")]
pub use alloc_mod::*;
#[cfg(feature = "std")]
use std::error::Error;
pub use std_mod::*;
use spacepackets::{
ecss::{tc::IsPusTelecommand, PusPacket},
ByteConversionError, CcsdsPacket,
};
use crate::TargetId;
use crate::{queue::GenericTargetedMessagingError, ComponentId};
/// Generic request ID type. Requests can be associated with an ID to have a unique identifier
/// for them. This can be useful for tasks like tracking their progress.
pub type RequestId = u32;
/// CCSDS APID type definition. Please note that the APID is a 14 bit value.
pub type Apid = u16;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TargetIdCreationError {
ByteConversion(ByteConversionError),
NotEnoughAppData(usize),
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct UniqueApidTargetId {
pub apid: Apid,
pub unique_id: u32,
}
impl From<ByteConversionError> for TargetIdCreationError {
fn from(e: ByteConversionError) -> Self {
Self::ByteConversion(e)
impl UniqueApidTargetId {
pub const fn new(apid: Apid, target: u32) -> Self {
Self {
apid,
unique_id: target,
}
}
pub fn raw(&self) -> ComponentId {
((self.apid as u64) << 32) | (self.unique_id as u64)
}
pub fn id(&self) -> ComponentId {
self.raw()
}
/// This function attempts to build the ID from a PUS telecommand by extracting the APID
/// and the first four bytes of the application data field as the target field.
pub fn from_pus_tc(
tc: &(impl CcsdsPacket + PusPacket + IsPusTelecommand),
) -> Result<Self, ByteConversionError> {
if tc.user_data().len() < 4 {
return Err(ByteConversionError::FromSliceTooSmall {
found: tc.user_data().len(),
expected: 4,
});
}
Ok(Self::new(
tc.apid(),
u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()),
))
}
}
impl fmt::Display for TargetIdCreationError {
impl From<u64> for UniqueApidTargetId {
fn from(raw: u64) -> Self {
Self {
apid: (raw >> 32) as u16,
unique_id: raw as u32,
}
}
}
impl From<UniqueApidTargetId> for u64 {
fn from(target_and_apid_id: UniqueApidTargetId) -> Self {
target_and_apid_id.raw()
}
}
impl fmt::Display for UniqueApidTargetId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::ByteConversion(e) => write!(f, "target ID creation: {}", e),
Self::NotEnoughAppData(len) => {
write!(f, "not enough app data to generate target ID: {}", len)
write!(
f,
"Target and APID ID with APID {:#03x} and target {}",
self.apid, self.unique_id
)
}
}
/// This contains metadata information which might be useful when used together with a
/// generic message tpye.
///
/// This could for example be used to build request/reply patterns or state tracking for request.
#[derive(Debug, Copy, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MessageMetadata {
request_id: RequestId,
sender_id: ComponentId,
}
impl MessageMetadata {
pub const fn new(request_id: RequestId, sender_id: ComponentId) -> Self {
Self {
request_id,
sender_id,
}
}
pub fn request_id(&self) -> RequestId {
self.request_id
}
pub fn sender_id(&self) -> ComponentId {
self.sender_id
}
}
/// Generic message type which adds [metadata][MessageMetadata] to a generic message typ.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GenericMessage<Message> {
pub requestor_info: MessageMetadata,
pub message: Message,
}
impl<Message> GenericMessage<Message> {
pub fn new(requestor_info: MessageMetadata, message: Message) -> Self {
Self {
requestor_info,
message,
}
}
delegate::delegate! {
to self.requestor_info {
pub fn request_id(&self) -> RequestId;
pub fn sender_id(&self) -> ComponentId;
}
}
}
/// Generic trait for objects which can send targeted messages.
pub trait MessageSender<MSG>: Send {
fn send(&self, message: GenericMessage<MSG>) -> Result<(), GenericTargetedMessagingError>;
}
// Generic trait for objects which can receive targeted messages.
pub trait MessageReceiver<MSG> {
fn try_recv(&self) -> Result<Option<GenericMessage<MSG>>, GenericTargetedMessagingError>;
}
pub struct MessageWithSenderIdReceiver<MSG, R: MessageReceiver<MSG>>(pub R, PhantomData<MSG>);
impl<MSG, R: MessageReceiver<MSG>> From<R> for MessageWithSenderIdReceiver<MSG, R> {
fn from(receiver: R) -> Self {
MessageWithSenderIdReceiver(receiver, PhantomData)
}
}
impl<MSG, R: MessageReceiver<MSG>> MessageWithSenderIdReceiver<MSG, R> {
pub fn try_recv_message(
&self,
) -> Result<Option<GenericMessage<MSG>>, GenericTargetedMessagingError> {
self.0.try_recv()
}
}
pub struct MessageReceiverWithId<MSG, R: MessageReceiver<MSG>> {
local_channel_id: ComponentId,
reply_receiver: MessageWithSenderIdReceiver<MSG, R>,
}
impl<MSG, R: MessageReceiver<MSG>> MessageReceiverWithId<MSG, R> {
pub fn new(local_channel_id: ComponentId, reply_receiver: R) -> Self {
Self {
local_channel_id,
reply_receiver: MessageWithSenderIdReceiver::from(reply_receiver),
}
}
pub fn local_channel_id(&self) -> ComponentId {
self.local_channel_id
}
}
impl<MSG, R: MessageReceiver<MSG>> MessageReceiverWithId<MSG, R> {
pub fn try_recv_message(
&self,
) -> Result<Option<GenericMessage<MSG>>, GenericTargetedMessagingError> {
self.reply_receiver.0.try_recv()
}
}
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use core::marker::PhantomData;
use crate::queue::GenericSendError;
use super::*;
use hashbrown::HashMap;
pub struct MessageSenderMap<MSG, S: MessageSender<MSG>>(
pub HashMap<ComponentId, S>,
pub(crate) PhantomData<MSG>,
);
impl<MSG, S: MessageSender<MSG>> Default for MessageSenderMap<MSG, S> {
fn default() -> Self {
Self(Default::default(), PhantomData)
}
}
impl<MSG, S: MessageSender<MSG>> MessageSenderMap<MSG, S> {
pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: S) {
self.0.insert(target_id, message_sender);
}
pub fn send_message(
&self,
requestor_info: MessageMetadata,
target_channel_id: ComponentId,
message: MSG,
) -> Result<(), GenericTargetedMessagingError> {
if self.0.contains_key(&target_channel_id) {
return self
.0
.get(&target_channel_id)
.unwrap()
.send(GenericMessage::new(requestor_info, message));
}
Err(GenericSendError::TargetDoesNotExist(target_channel_id).into())
}
}
pub struct MessageSenderAndReceiver<TO, FROM, S: MessageSender<TO>, R: MessageReceiver<FROM>> {
pub local_channel_id: ComponentId,
pub message_sender_map: MessageSenderMap<TO, S>,
pub message_receiver: MessageWithSenderIdReceiver<FROM, R>,
}
impl<TO, FROM, S: MessageSender<TO>, R: MessageReceiver<FROM>>
MessageSenderAndReceiver<TO, FROM, S, R>
{
pub fn new(local_channel_id: ComponentId, message_receiver: R) -> Self {
Self {
local_channel_id,
message_sender_map: Default::default(),
message_receiver: MessageWithSenderIdReceiver::from(message_receiver),
}
}
pub fn add_message_target(&mut self, target_id: ComponentId, message_sender: S) {
self.message_sender_map
.add_message_target(target_id, message_sender)
}
pub fn local_channel_id_generic(&self) -> ComponentId {
self.local_channel_id
}
/// Try to send a message, which can be a reply or a request, depending on the generics.
pub fn send_message(
&self,
request_id: RequestId,
target_id: ComponentId,
message: TO,
) -> Result<(), GenericTargetedMessagingError> {
self.message_sender_map.send_message(
MessageMetadata::new(request_id, self.local_channel_id_generic()),
target_id,
message,
)
}
/// Try to receive a message, which can be a reply or a request, depending on the generics.
pub fn try_recv_message(
&self,
) -> Result<Option<GenericMessage<FROM>>, GenericTargetedMessagingError> {
self.message_receiver.try_recv_message()
}
}
pub struct RequestAndReplySenderAndReceiver<
REQUEST,
REPLY,
S0: MessageSender<REQUEST>,
R0: MessageReceiver<REPLY>,
S1: MessageSender<REPLY>,
R1: MessageReceiver<REQUEST>,
> {
pub local_channel_id: ComponentId,
// These 2 are a functional group.
pub request_sender_map: MessageSenderMap<REQUEST, S0>,
pub reply_receiver: MessageWithSenderIdReceiver<REPLY, R0>,
// These 2 are a functional group.
pub request_receiver: MessageWithSenderIdReceiver<REQUEST, R1>,
pub reply_sender_map: MessageSenderMap<REPLY, S1>,
}
impl<
REQUEST,
REPLY,
S0: MessageSender<REQUEST>,
R0: MessageReceiver<REPLY>,
S1: MessageSender<REPLY>,
R1: MessageReceiver<REQUEST>,
> RequestAndReplySenderAndReceiver<REQUEST, REPLY, S0, R0, S1, R1>
{
pub fn new(
local_channel_id: ComponentId,
request_receiver: R1,
reply_receiver: R0,
) -> Self {
Self {
local_channel_id,
request_receiver: request_receiver.into(),
reply_receiver: reply_receiver.into(),
request_sender_map: Default::default(),
reply_sender_map: Default::default(),
}
}
pub fn local_channel_id_generic(&self) -> ComponentId {
self.local_channel_id
}
}
}
#[cfg(feature = "std")]
impl Error for TargetIdCreationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
if let Self::ByteConversion(e) = self {
return Some(e);
pub mod std_mod {
use super::*;
use std::sync::mpsc;
use crate::queue::{GenericReceiveError, GenericSendError, GenericTargetedMessagingError};
impl<MSG: Send> MessageSender<MSG> for mpsc::Sender<GenericMessage<MSG>> {
fn send(&self, message: GenericMessage<MSG>) -> Result<(), GenericTargetedMessagingError> {
self.send(message)
.map_err(|_| GenericSendError::RxDisconnected)?;
Ok(())
}
None
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct TargetAndApidId {
pub apid: Apid,
pub target: u32,
}
impl TargetAndApidId {
pub fn new(apid: Apid, target: u32) -> Self {
Self { apid, target }
}
pub fn apid(&self) -> Apid {
self.apid
}
pub fn target(&self) -> u32 {
self.target
}
pub fn raw(&self) -> TargetId {
((self.apid as u64) << 32) | (self.target as u64)
}
pub fn target_id(&self) -> TargetId {
self.raw()
}
pub fn from_pus_tc(
tc: &(impl CcsdsPacket + PusPacket + IsPusTelecommand),
) -> Result<Self, TargetIdCreationError> {
if tc.user_data().len() < 4 {
return Err(ByteConversionError::FromSliceTooSmall {
found: tc.user_data().len(),
expected: 8,
impl<MSG: Send> MessageSender<MSG> for mpsc::SyncSender<GenericMessage<MSG>> {
fn send(&self, message: GenericMessage<MSG>) -> Result<(), GenericTargetedMessagingError> {
if let Err(e) = self.try_send(message) {
return match e {
mpsc::TrySendError::Full(_) => Err(GenericSendError::QueueFull(None).into()),
mpsc::TrySendError::Disconnected(_) => {
Err(GenericSendError::RxDisconnected.into())
}
};
}
.into());
Ok(())
}
Ok(Self {
apid: tc.apid(),
target: u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()),
})
}
pub type MessageSenderMapMpsc<MSG> = MessageReceiverWithId<MSG, mpsc::Sender<MSG>>;
pub type MessageSenderMapBoundedMpsc<MSG> = MessageReceiverWithId<MSG, mpsc::SyncSender<MSG>>;
impl<MSG> MessageReceiver<MSG> for mpsc::Receiver<GenericMessage<MSG>> {
fn try_recv(&self) -> Result<Option<GenericMessage<MSG>>, GenericTargetedMessagingError> {
match self.try_recv() {
Ok(msg) => Ok(Some(msg)),
Err(e) => match e {
mpsc::TryRecvError::Empty => Ok(None),
mpsc::TryRecvError::Disconnected => {
Err(GenericReceiveError::TxDisconnected(None).into())
}
},
}
}
}
pub type MessageReceiverWithIdMpsc<MSG> = MessageReceiverWithId<MSG, mpsc::Receiver<MSG>>;
}
impl From<u64> for TargetAndApidId {
fn from(raw: u64) -> Self {
Self {
apid: (raw >> 32) as u16,
target: raw as u32,
#[cfg(test)]
mod tests {
use std::sync::mpsc;
use alloc::string::ToString;
use spacepackets::{
ecss::tc::{PusTcCreator, PusTcSecondaryHeader},
ByteConversionError, SpHeader,
};
use crate::{
queue::{GenericReceiveError, GenericSendError, GenericTargetedMessagingError},
request::{MessageMetadata, MessageSenderMap},
};
use super::{GenericMessage, MessageReceiverWithId, UniqueApidTargetId};
const TEST_CHANNEL_ID_0: u64 = 1;
const TEST_CHANNEL_ID_1: u64 = 2;
const TEST_CHANNEL_ID_2: u64 = 3;
#[test]
fn test_basic_target_id_with_apid() {
let id = UniqueApidTargetId::new(0x111, 0x01);
assert_eq!(id.apid, 0x111);
assert_eq!(id.unique_id, 0x01);
assert_eq!(id.id(), id.raw());
assert_eq!(u64::from(id), id.raw());
let id_raw = id.raw();
let id_from_raw = UniqueApidTargetId::from(id_raw);
assert_eq!(id_from_raw, id);
assert_eq!(id.id(), (0x111 << 32) | 0x01);
let string = id.to_string();
assert_eq!(
string,
"Target and APID ID with APID 0x111 and target 1".to_string()
);
}
#[test]
fn test_basic_target_id_with_apid_from_pus_tc() {
let sp_header = SpHeader::new_for_unseg_tc(0x111, 5, 0);
let app_data = 1_u32.to_be_bytes();
let pus_tc = PusTcCreator::new_simple(sp_header, 17, 1, &app_data, true);
let id = UniqueApidTargetId::from_pus_tc(&pus_tc).unwrap();
assert_eq!(id.apid, 0x111);
assert_eq!(id.unique_id, 1);
}
#[test]
fn test_basic_target_id_with_apid_from_pus_tc_invalid_app_data() {
let sp_header = SpHeader::new_for_unseg_tc(0x111, 5, 0);
let sec_header = PusTcSecondaryHeader::new_simple(17, 1);
let pus_tc = PusTcCreator::new_no_app_data(sp_header, sec_header, true);
let error = UniqueApidTargetId::from_pus_tc(&pus_tc);
assert!(error.is_err());
let error = error.unwrap_err();
if let ByteConversionError::FromSliceTooSmall { found, expected } = error {
assert_eq!(found, 0);
assert_eq!(expected, 4);
} else {
panic!("Unexpected error type");
}
}
#[test]
fn test_receiver_only() {
let (sender, receiver) = mpsc::channel();
// Test structure with only a receiver which has a channel ID.
let receiver = MessageReceiverWithId::new(TEST_CHANNEL_ID_0, receiver);
let request_id = 5;
sender
.send(GenericMessage::new(
MessageMetadata::new(request_id, TEST_CHANNEL_ID_1),
5,
))
.unwrap();
let reply = receiver.try_recv_message().unwrap();
assert!(reply.is_some());
assert_eq!(receiver.local_channel_id(), TEST_CHANNEL_ID_0);
let reply = reply.unwrap();
assert_eq!(reply.requestor_info.request_id, request_id);
assert_eq!(reply.requestor_info.sender_id, TEST_CHANNEL_ID_1);
assert_eq!(reply.message, 5);
}
#[test]
fn test_receiver_empty() {
let (_sender, receiver) = mpsc::sync_channel::<GenericMessage<i32>>(2);
// Test structure with only a receiver which has a channel ID.
let receiver = MessageReceiverWithId::new(TEST_CHANNEL_ID_0, receiver);
let reply = receiver.try_recv_message().unwrap();
assert!(reply.is_none());
}
#[test]
fn test_all_tx_disconnected() {
let (sender, receiver) = mpsc::sync_channel::<GenericMessage<i32>>(2);
// Test structure with only a receiver which has a channel ID.
let receiver = MessageReceiverWithId::new(TEST_CHANNEL_ID_0, receiver);
drop(sender);
let reply = receiver.try_recv_message();
assert!(reply.is_err());
let error = reply.unwrap_err();
if let GenericTargetedMessagingError::Receive(GenericReceiveError::TxDisconnected(None)) =
error
{
} else {
panic!("unexpected error type");
}
}
#[test]
fn test_sender_map() {
let (sender0, receiver0) = mpsc::channel();
let (sender1, receiver1) = mpsc::channel();
let mut sender_map = MessageSenderMap::default();
sender_map.add_message_target(TEST_CHANNEL_ID_1, sender0);
sender_map.add_message_target(TEST_CHANNEL_ID_2, sender1);
sender_map
.send_message(
MessageMetadata::new(1, TEST_CHANNEL_ID_0),
TEST_CHANNEL_ID_1,
5,
)
.expect("sending message failed");
let mut reply = receiver0.recv().expect("receiving message failed");
assert_eq!(reply.request_id(), 1);
assert_eq!(reply.sender_id(), TEST_CHANNEL_ID_0);
assert_eq!(reply.message, 5);
sender_map
.send_message(
MessageMetadata::new(2, TEST_CHANNEL_ID_0),
TEST_CHANNEL_ID_2,
10,
)
.expect("sending message failed");
reply = receiver1.recv().expect("receiving message failed");
assert_eq!(reply.request_id(), 2);
assert_eq!(reply.sender_id(), TEST_CHANNEL_ID_0);
assert_eq!(reply.message, 10);
}
#[test]
fn test_sender_map_target_does_not_exist() {
let (sender0, _) = mpsc::channel();
let mut sender_map_with_id = MessageSenderMap::default();
sender_map_with_id.add_message_target(TEST_CHANNEL_ID_1, sender0);
let result = sender_map_with_id.send_message(
MessageMetadata::new(1, TEST_CHANNEL_ID_0),
TEST_CHANNEL_ID_2,
5,
);
assert!(result.is_err());
let error = result.unwrap_err();
if let GenericTargetedMessagingError::Send(GenericSendError::TargetDoesNotExist(target)) =
error
{
assert_eq!(target, TEST_CHANNEL_ID_2);
} else {
panic!("Unexpected error type");
}
}
#[test]
fn test_sender_map_queue_full() {
let (sender0, _receiver0) = mpsc::sync_channel(1);
let mut sender_map_with_id = MessageSenderMap::default();
sender_map_with_id.add_message_target(TEST_CHANNEL_ID_1, sender0);
sender_map_with_id
.send_message(
MessageMetadata::new(1, TEST_CHANNEL_ID_0),
TEST_CHANNEL_ID_1,
5,
)
.expect("sending message failed");
let result = sender_map_with_id.send_message(
MessageMetadata::new(1, TEST_CHANNEL_ID_0),
TEST_CHANNEL_ID_1,
5,
);
assert!(result.is_err());
let error = result.unwrap_err();
if let GenericTargetedMessagingError::Send(GenericSendError::QueueFull(capacity)) = error {
assert!(capacity.is_none());
} else {
panic!("Unexpected error type {}", error);
}
}
#[test]
fn test_sender_map_queue_receiver_disconnected() {
let (sender0, receiver0) = mpsc::sync_channel(1);
let mut sender_map_with_id = MessageSenderMap::default();
sender_map_with_id.add_message_target(TEST_CHANNEL_ID_1, sender0);
drop(receiver0);
let result = sender_map_with_id.send_message(
MessageMetadata::new(1, TEST_CHANNEL_ID_0),
TEST_CHANNEL_ID_1,
5,
);
assert!(result.is_err());
let error = result.unwrap_err();
if let GenericTargetedMessagingError::Send(GenericSendError::RxDisconnected) = error {
} else {
panic!("Unexpected error type {}", error);
}
}
}
impl From<TargetAndApidId> for u64 {
fn from(target_and_apid_id: TargetAndApidId) -> Self {
target_and_apid_id.raw()
}
}
impl fmt::Display for TargetAndApidId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}, {}", self.apid, self.target)
}
}

View File

@ -32,7 +32,7 @@ dyn_clone::clone_trait_object!(SequenceCountProvider<u16>);
#[cfg(feature = "alloc")]
impl<T, Raw> SequenceCountProvider<Raw> for T where T: SequenceCountProviderCore<Raw> + Clone {}
#[derive(Default, Clone)]
#[derive(Clone)]
pub struct SeqCountProviderSimple<T: Copy> {
seq_count: Cell<T>,
max_val: T,
@ -43,13 +43,12 @@ macro_rules! impl_for_primitives {
$(
paste! {
impl SeqCountProviderSimple<$ty> {
pub fn [<new_ $ty _max_val>](max_val: $ty) -> Self {
pub fn [<new_custom_max_val_ $ty>](max_val: $ty) -> Self {
Self {
seq_count: Cell::new(0),
max_val,
}
}
pub fn [<new_ $ty>]() -> Self {
Self {
seq_count: Cell::new(0),
@ -58,6 +57,12 @@ macro_rules! impl_for_primitives {
}
}
impl Default for SeqCountProviderSimple<$ty> {
fn default() -> Self {
Self::[<new_ $ty>]()
}
}
impl SequenceCountProviderCore<$ty> for SeqCountProviderSimple<$ty> {
fn get(&self) -> $ty {
self.seq_count.get()
@ -86,21 +91,16 @@ macro_rules! impl_for_primitives {
impl_for_primitives!(u8, u16, u32, u64,);
/// This is a sequence count provider which wraps around at [MAX_SEQ_COUNT].
#[derive(Clone)]
pub struct CcsdsSimpleSeqCountProvider {
provider: SeqCountProviderSimple<u16>,
}
impl CcsdsSimpleSeqCountProvider {
pub fn new() -> Self {
Self {
provider: SeqCountProviderSimple::new_u16_max_val(MAX_SEQ_COUNT),
}
}
}
impl Default for CcsdsSimpleSeqCountProvider {
fn default() -> Self {
Self::new()
Self {
provider: SeqCountProviderSimple::new_custom_max_val_u16(MAX_SEQ_COUNT),
}
}
}
@ -187,7 +187,7 @@ mod tests {
#[test]
fn test_u8_counter() {
let u8_counter = SeqCountProviderSimple::new_u8();
let u8_counter = SeqCountProviderSimple::<u8>::default();
assert_eq!(u8_counter.get(), 0);
assert_eq!(u8_counter.get_and_increment(), 0);
assert_eq!(u8_counter.get_and_increment(), 1);

7
satrs/src/time.rs Normal file
View File

@ -0,0 +1,7 @@
use core::fmt::Debug;
/// Generic abstraction for a check/countdown timer.
pub trait CountdownProvider: Debug {
fn has_expired(&self) -> bool;
fn reset(&mut self);
}

View File

@ -18,6 +18,7 @@
//! # Example
//!
//! ```rust
//! use satrs::ValidatorU16Id;
//! use satrs::tmtc::ccsds_distrib::{CcsdsPacketHandler, CcsdsDistributor};
//! use satrs::tmtc::{ReceivesTc, ReceivesTcCore};
//! use spacepackets::{CcsdsPacket, SpHeader};
@ -34,16 +35,19 @@
//! fn mutable_foo(&mut self) {}
//! }
//!
//! impl ValidatorU16Id for ConcreteApidHandler {
//! fn validate(&self, apid: u16) -> bool { apid == 0x0002 }
//! }
//!
//! impl CcsdsPacketHandler for ConcreteApidHandler {
//! type Error = ();
//! fn valid_apids(&self) -> &'static [u16] { &[0x002] }
//! fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> {
//! fn handle_packet_with_valid_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> {
//! assert_eq!(sp_header.apid(), 0x002);
//! assert_eq!(tc_raw.len(), 13);
//! self.known_call_count += 1;
//! Ok(())
//! }
//! fn handle_unknown_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> {
//! fn handle_packet_with_unknown_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> {
//! assert_eq!(sp_header.apid(), 0x003);
//! assert_eq!(tc_raw.len(), 13);
//! self.unknown_call_count += 1;
@ -55,8 +59,8 @@
//! let mut ccsds_distributor = CcsdsDistributor::new(apid_handler);
//!
//! // Create and pass PUS telecommand with a valid APID
//! let mut space_packet_header = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap();
//! let mut pus_tc = PusTcCreator::new_simple(&mut space_packet_header, 17, 1, None, true);
//! let sp_header = SpHeader::new_for_unseg_tc(0x002, 0x34, 0);
//! let mut pus_tc = PusTcCreator::new_simple(sp_header, 17, 1, &[], true);
//! let mut test_buf: [u8; 32] = [0; 32];
//! let mut size = pus_tc
//! .write_to_bytes(test_buf.as_mut_slice())
@ -81,7 +85,10 @@
//! let mutable_handler_ref = ccsds_distributor.packet_handler_mut();
//! mutable_handler_ref.mutable_foo();
//! ```
use crate::tmtc::{ReceivesCcsdsTc, ReceivesTcCore};
use crate::{
tmtc::{ReceivesCcsdsTc, ReceivesTcCore},
ValidatorU16Id,
};
use core::fmt::{Display, Formatter};
use spacepackets::{ByteConversionError, CcsdsPacket, SpHeader};
#[cfg(feature = "std")]
@ -92,14 +99,18 @@ use std::error::Error;
/// Users should implement this trait on their custom CCSDS packet handler and then pass a boxed
/// instance of this handler to the [CcsdsDistributor]. The distributor will use the trait
/// interface to dispatch received packets to the user based on the Application Process Identifier
/// (APID) field of the CCSDS packet.
pub trait CcsdsPacketHandler {
/// (APID) field of the CCSDS packet. The APID will be checked using the generic [ValidatorU16Id]
/// trait.
pub trait CcsdsPacketHandler: ValidatorU16Id {
type Error;
fn valid_apids(&self) -> &'static [u16];
fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8])
-> Result<(), Self::Error>;
fn handle_unknown_apid(
fn handle_packet_with_valid_apid(
&mut self,
sp_header: &SpHeader,
tc_raw: &[u8],
) -> Result<(), Self::Error>;
fn handle_packet_with_unknown_apid(
&mut self,
sp_header: &SpHeader,
tc_raw: &[u8],
@ -183,18 +194,15 @@ impl<PacketHandler: CcsdsPacketHandler<Error = E>, E: 'static> CcsdsDistributor<
}
fn dispatch_ccsds(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), CcsdsError<E>> {
let apid = sp_header.apid();
let valid_apids = self.packet_handler.valid_apids();
for &valid_apid in valid_apids {
if valid_apid == apid {
return self
.packet_handler
.handle_known_apid(sp_header, tc_raw)
.map_err(|e| CcsdsError::CustomError(e));
}
let valid_apid = self.packet_handler().validate(sp_header.apid());
if valid_apid {
self.packet_handler
.handle_packet_with_valid_apid(sp_header, tc_raw)
.map_err(|e| CcsdsError::CustomError(e))?;
return Ok(());
}
self.packet_handler
.handle_unknown_apid(sp_header, tc_raw)
.handle_packet_with_unknown_apid(sp_header, tc_raw)
.map_err(|e| CcsdsError::CustomError(e))
}
}
@ -213,8 +221,8 @@ pub(crate) mod tests {
fn is_send<T: Send>(_: &T) {}
pub fn generate_ping_tc(buf: &mut [u8]) -> &[u8] {
let mut sph = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap();
let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
let sph = SpHeader::new_for_unseg_tc(0x002, 0x34, 0);
let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
let size = pus_tc
.write_to_bytes(buf)
.expect("Error writing TC to buffer");
@ -223,8 +231,8 @@ pub(crate) mod tests {
}
pub fn generate_ping_tc_as_vec() -> Vec<u8> {
let mut sph = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap();
PusTcCreator::new_simple(&mut sph, 17, 1, None, true)
let sph = SpHeader::new_for_unseg_tc(0x002, 0x34, 0);
PusTcCreator::new_simple(sph, 17, 1, &[], true)
.to_vec()
.unwrap()
}
@ -241,13 +249,16 @@ pub(crate) mod tests {
pub unknown_packet_queue: VecDeque<(u16, Vec<u8>)>,
}
impl ValidatorU16Id for BasicApidHandlerSharedQueue {
fn validate(&self, packet_id: u16) -> bool {
[0x000, 0x002].contains(&packet_id)
}
}
impl CcsdsPacketHandler for BasicApidHandlerSharedQueue {
type Error = ();
fn valid_apids(&self) -> &'static [u16] {
&[0x000, 0x002]
}
fn handle_known_apid(
fn handle_packet_with_valid_apid(
&mut self,
sp_header: &SpHeader,
tc_raw: &[u8],
@ -261,7 +272,7 @@ pub(crate) mod tests {
Ok(())
}
fn handle_unknown_apid(
fn handle_packet_with_unknown_apid(
&mut self,
sp_header: &SpHeader,
tc_raw: &[u8],
@ -276,14 +287,16 @@ pub(crate) mod tests {
}
}
impl ValidatorU16Id for BasicApidHandlerOwnedQueue {
fn validate(&self, packet_id: u16) -> bool {
[0x000, 0x002].contains(&packet_id)
}
}
impl CcsdsPacketHandler for BasicApidHandlerOwnedQueue {
type Error = ();
fn valid_apids(&self) -> &'static [u16] {
&[0x000, 0x002]
}
fn handle_known_apid(
fn handle_packet_with_valid_apid(
&mut self,
sp_header: &SpHeader,
tc_raw: &[u8],
@ -294,7 +307,7 @@ pub(crate) mod tests {
Ok(())
}
fn handle_unknown_apid(
fn handle_packet_with_unknown_apid(
&mut self,
sp_header: &SpHeader,
tc_raw: &[u8],
@ -332,8 +345,8 @@ pub(crate) mod tests {
fn test_unknown_apid_handling() {
let apid_handler = BasicApidHandlerOwnedQueue::default();
let mut ccsds_distrib = CcsdsDistributor::new(apid_handler);
let mut sph = SpHeader::tc_unseg(0x004, 0x34, 0).unwrap();
let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
let sph = SpHeader::new_for_unseg_tc(0x004, 0x34, 0);
let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
let mut test_buf: [u8; 32] = [0; 32];
pus_tc
.write_to_bytes(test_buf.as_mut_slice())
@ -351,8 +364,8 @@ pub(crate) mod tests {
#[test]
fn test_ccsds_distribution() {
let mut ccsds_distrib = CcsdsDistributor::new(BasicApidHandlerOwnedQueue::default());
let mut sph = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap();
let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
let sph = SpHeader::new_for_unseg_tc(0x002, 0x34, 0);
let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
let tc_vec = pus_tc.to_vec().unwrap();
ccsds_distrib
.pass_ccsds(&sph, &tc_vec)
@ -370,8 +383,8 @@ pub(crate) mod tests {
#[test]
fn test_distribution_short_packet_fails() {
let mut ccsds_distrib = CcsdsDistributor::new(BasicApidHandlerOwnedQueue::default());
let mut sph = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap();
let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
let sph = SpHeader::new_for_unseg_tc(0x002, 0x34, 0);
let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
let tc_vec = pus_tc.to_vec().unwrap();
let result = ccsds_distrib.pass_tc(&tc_vec[0..6]);
assert!(result.is_err());

View File

@ -46,8 +46,8 @@
//! let mut pus_distributor = PusDistributor::new(service_handler);
//!
//! // Create and pass PUS ping telecommand with a valid APID
//! let mut space_packet_header = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap();
//! let mut pus_tc = PusTcCreator::new_simple(&mut space_packet_header, 17, 1, None, true);
//! let sp_header = SpHeader::new_for_unseg_tc(0x002, 0x34, 0);
//! let mut pus_tc = PusTcCreator::new_simple(sp_header, 17, 1, &[], true);
//! let mut test_buf: [u8; 32] = [0; 32];
//! let mut size = pus_tc
//! .write_to_bytes(test_buf.as_mut_slice())
@ -176,6 +176,7 @@ mod tests {
BasicApidHandlerSharedQueue,
};
use crate::tmtc::ccsds_distrib::{CcsdsDistributor, CcsdsPacketHandler};
use crate::ValidatorU16Id;
use alloc::format;
use alloc::vec::Vec;
use spacepackets::ecss::PusError;
@ -253,17 +254,13 @@ mod tests {
() => {
type Error = PusError;
fn valid_apids(&self) -> &'static [u16] {
&[0x000, 0x002]
}
fn handle_known_apid(
fn handle_packet_with_valid_apid(
&mut self,
sp_header: &SpHeader,
tc_raw: &[u8],
) -> Result<(), Self::Error> {
self.handler_base
.handle_known_apid(&sp_header, tc_raw)
.handle_packet_with_valid_apid(&sp_header, tc_raw)
.ok()
.expect("Unexpected error");
match self.pus_distrib.pass_ccsds(&sp_header, tc_raw) {
@ -275,13 +272,13 @@ mod tests {
}
}
fn handle_unknown_apid(
fn handle_packet_with_unknown_apid(
&mut self,
sp_header: &SpHeader,
tc_raw: &[u8],
) -> Result<(), Self::Error> {
self.handler_base
.handle_unknown_apid(&sp_header, tc_raw)
.handle_packet_with_unknown_apid(&sp_header, tc_raw)
.ok()
.expect("Unexpected error");
Ok(())
@ -289,6 +286,18 @@ mod tests {
};
}
impl ValidatorU16Id for ApidHandlerOwned {
fn validate(&self, packet_id: u16) -> bool {
[0x000, 0x002].contains(&packet_id)
}
}
impl ValidatorU16Id for ApidHandlerShared {
fn validate(&self, packet_id: u16) -> bool {
[0x000, 0x002].contains(&packet_id)
}
}
impl CcsdsPacketHandler for ApidHandlerOwned {
apid_handler_impl!();
}

View File

@ -8,7 +8,9 @@ pub use std_mod::*;
#[cfg(feature = "std")]
pub mod std_mod {
use crate::pool::{PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StoreAddr};
use crate::pool::{
PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StoreAddr, StoreError,
};
use crate::pus::EcssTmtcError;
use spacepackets::ecss::tm::PusTmCreator;
use spacepackets::ecss::WritablePusPacket;
@ -34,7 +36,7 @@ pub mod std_mod {
}
pub fn add_pus_tm(&self, pus_tm: &PusTmCreator) -> Result<StoreAddr, EcssTmtcError> {
let mut pg = self.0.write().map_err(|_| EcssTmtcError::StoreLock)?;
let mut pg = self.0.write().map_err(|_| StoreError::LockError)?;
let addr = pg.free_element(pus_tm.len_written(), |buf| {
pus_tm
.write_to_bytes(buf)
@ -90,9 +92,9 @@ impl PusTmWithCdsShortHelper {
source_data: &'a [u8],
seq_count: u16,
) -> PusTmCreator {
let mut reply_header = SpHeader::tm_unseg(self.apid, seq_count, 0).unwrap();
let reply_header = SpHeader::new_for_unseg_tm(self.apid, seq_count, 0);
let tc_header = PusTmSecondaryHeader::new_simple(service, subservice, &self.cds_short_buf);
PusTmCreator::new(&mut reply_header, tc_header, source_data, true)
PusTmCreator::new(reply_header, tc_header, source_data, true)
}
}

358
satrs/tests/mode_tree.rs Normal file
View File

@ -0,0 +1,358 @@
use core::cell::Cell;
use std::{println, sync::mpsc};
use satrs::mode::{
ModeError, ModeProvider, ModeReplyReceiver, ModeReplySender, ModeRequestHandler,
ModeRequestHandlerMpscBounded, ModeRequestReceiver, ModeRequestorAndHandlerMpscBounded,
ModeRequestorBoundedMpsc,
};
use satrs::request::MessageMetadata;
use satrs::{
mode::{ModeAndSubmode, ModeReply, ModeRequest},
queue::GenericTargetedMessagingError,
request::GenericMessage,
ComponentId,
};
use std::string::{String, ToString};
pub enum TestComponentId {
Device1 = 1,
Device2 = 2,
Assembly = 3,
PusModeService = 4,
}
struct PusModeService {
pub request_id_counter: Cell<u32>,
pub mode_node: ModeRequestorBoundedMpsc,
}
impl PusModeService {
pub fn send_announce_mode_cmd_to_assy(&self) {
self.mode_node
.send_mode_request(
self.request_id_counter.get(),
TestComponentId::Assembly as ComponentId,
ModeRequest::AnnounceModeRecursive,
)
.unwrap();
self.request_id_counter
.replace(self.request_id_counter.get() + 1);
}
}
struct TestDevice {
pub name: String,
pub mode_node: ModeRequestHandlerMpscBounded,
pub mode_and_submode: ModeAndSubmode,
}
impl TestDevice {
pub fn run(&mut self) {
self.check_mode_requests().expect("mode messaging error");
}
pub fn check_mode_requests(&mut self) -> Result<(), ModeError> {
if let Some(request) = self.mode_node.try_recv_mode_request()? {
self.handle_mode_request(request)?
}
Ok(())
}
}
impl ModeProvider for TestDevice {
fn mode_and_submode(&self) -> ModeAndSubmode {
self.mode_and_submode
}
}
impl ModeRequestHandler for TestDevice {
type Error = ModeError;
fn start_transition(
&mut self,
requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode,
) -> Result<(), ModeError> {
self.mode_and_submode = mode_and_submode;
self.handle_mode_reached(Some(requestor))?;
Ok(())
}
fn announce_mode(&self, _requestor_info: Option<MessageMetadata>, _recursive: bool) {
println!(
"{}: announcing mode: {:?}",
self.name, self.mode_and_submode
);
}
fn handle_mode_reached(&mut self, requestor: Option<MessageMetadata>) -> Result<(), ModeError> {
if let Some(requestor) = requestor {
self.send_mode_reply(requestor, ModeReply::ModeReply(self.mode_and_submode))?;
}
Ok(())
}
fn send_mode_reply(
&self,
requestor_info: MessageMetadata,
reply: ModeReply,
) -> Result<(), ModeError> {
self.mode_node.send_mode_reply(requestor_info, reply)?;
Ok(())
}
fn handle_mode_info(
&mut self,
requestor_info: MessageMetadata,
info: ModeAndSubmode,
) -> Result<(), ModeError> {
// A device is a leaf in the tree.. so this really should not happen
println!(
"{}: unexpected mode info from {:?} with mode: {:?}",
self.name,
requestor_info.sender_id(),
info
);
Ok(())
}
}
struct TestAssembly {
pub mode_node: ModeRequestorAndHandlerMpscBounded,
pub mode_requestor_info: Option<MessageMetadata>,
pub mode_and_submode: ModeAndSubmode,
pub target_mode_and_submode: Option<ModeAndSubmode>,
}
impl ModeProvider for TestAssembly {
fn mode_and_submode(&self) -> ModeAndSubmode {
self.mode_and_submode
}
}
impl TestAssembly {
pub fn run(&mut self) {
self.check_mode_requests().expect("mode messaging error");
self.check_mode_replies().expect("mode messaging error");
}
pub fn check_mode_requests(&mut self) -> Result<(), GenericTargetedMessagingError> {
if let Some(request) = self.mode_node.try_recv_mode_request()? {
match request.message {
ModeRequest::SetMode(mode_and_submode) => {
self.start_transition(request.requestor_info, mode_and_submode)
.unwrap();
}
ModeRequest::ReadMode => self
.mode_node
.send_mode_reply(
request.requestor_info,
ModeReply::ModeReply(self.mode_and_submode),
)
.unwrap(),
ModeRequest::AnnounceMode => {
self.announce_mode(Some(request.requestor_info), false)
}
ModeRequest::AnnounceModeRecursive => {
self.announce_mode(Some(request.requestor_info), true)
}
ModeRequest::ModeInfo(_) => todo!(),
}
}
Ok(())
}
pub fn check_mode_replies(&mut self) -> Result<(), GenericTargetedMessagingError> {
if let Some(reply_and_id) = self.mode_node.try_recv_mode_reply()? {
match reply_and_id.message {
ModeReply::ModeReply(reply) => {
println!(
"TestAssembly: Received mode reply from {:?}, reached: {:?}",
reply_and_id.sender_id(),
reply
);
}
ModeReply::CantReachMode(_) => todo!(),
ModeReply::WrongMode { expected, reached } => {
println!(
"TestAssembly: Wrong mode reply from {:?}, reached {:?}, expected {:?}",
reply_and_id.sender_id(),
reached,
expected
);
}
}
}
Ok(())
}
}
impl ModeRequestHandler for TestAssembly {
type Error = ModeError;
fn start_transition(
&mut self,
requestor: MessageMetadata,
mode_and_submode: ModeAndSubmode,
) -> Result<(), Self::Error> {
self.mode_requestor_info = Some(requestor);
self.target_mode_and_submode = Some(mode_and_submode);
Ok(())
}
fn announce_mode(&self, requestor_info: Option<MessageMetadata>, recursive: bool) {
println!(
"TestAssembly: Announcing mode (recursively: {}): {:?}",
recursive, self.mode_and_submode
);
// self.mode_requestor_info = Some((request_id, sender_id));
let mut mode_request = ModeRequest::AnnounceMode;
if recursive {
mode_request = ModeRequest::AnnounceModeRecursive;
}
let request_id = requestor_info.map_or(0, |info| info.request_id());
self.mode_node
.request_sender_map
.0
.iter()
.for_each(|(_, sender)| {
sender
.send(GenericMessage::new(
MessageMetadata::new(request_id, self.mode_node.local_channel_id_generic()),
mode_request,
))
.expect("sending mode request failed");
});
}
fn handle_mode_reached(
&mut self,
mode_requestor: Option<MessageMetadata>,
) -> Result<(), Self::Error> {
if let Some(requestor) = mode_requestor {
self.send_mode_reply(requestor, ModeReply::ModeReply(self.mode_and_submode))?;
}
Ok(())
}
fn send_mode_reply(
&self,
requestor: MessageMetadata,
reply: ModeReply,
) -> Result<(), Self::Error> {
self.mode_node.send_mode_reply(requestor, reply)?;
Ok(())
}
fn handle_mode_info(
&mut self,
_requestor_info: MessageMetadata,
_info: ModeAndSubmode,
) -> Result<(), Self::Error> {
// TODO: A proper assembly must reach to mode changes of its children..
Ok(())
}
}
fn main() {
// All request channel handles.
let (request_sender_to_dev1, request_receiver_dev1) = mpsc::sync_channel(10);
let (request_sender_to_dev2, request_receiver_dev2) = mpsc::sync_channel(10);
let (request_sender_to_assy, request_receiver_assy) = mpsc::sync_channel(10);
// All reply channel handles.
let (reply_sender_to_assy, reply_receiver_assy) = mpsc::sync_channel(10);
let (reply_sender_to_pus, reply_receiver_pus) = mpsc::sync_channel(10);
// Mode requestors and handlers.
let mut mode_node_assy = ModeRequestorAndHandlerMpscBounded::new(
TestComponentId::Assembly as ComponentId,
request_receiver_assy,
reply_receiver_assy,
);
// Mode requestors only.
let mut mode_node_pus = ModeRequestorBoundedMpsc::new(
TestComponentId::PusModeService as ComponentId,
reply_receiver_pus,
);
// Request handlers only.
let mut mode_node_dev1 = ModeRequestHandlerMpscBounded::new(
TestComponentId::Device1 as ComponentId,
request_receiver_dev1,
);
let mut mode_node_dev2 = ModeRequestHandlerMpscBounded::new(
TestComponentId::Device2 as ComponentId,
request_receiver_dev2,
);
// Set up mode request senders first.
mode_node_pus.add_message_target(
TestComponentId::Assembly as ComponentId,
request_sender_to_assy,
);
mode_node_pus.add_message_target(
TestComponentId::Device1 as ComponentId,
request_sender_to_dev1.clone(),
);
mode_node_pus.add_message_target(
TestComponentId::Device2 as ComponentId,
request_sender_to_dev2.clone(),
);
mode_node_assy.add_request_target(
TestComponentId::Device1 as ComponentId,
request_sender_to_dev1,
);
mode_node_assy.add_request_target(
TestComponentId::Device2 as ComponentId,
request_sender_to_dev2,
);
// Set up mode reply senders.
mode_node_dev1.add_message_target(
TestComponentId::Assembly as ComponentId,
reply_sender_to_assy.clone(),
);
mode_node_dev1.add_message_target(
TestComponentId::PusModeService as ComponentId,
reply_sender_to_pus.clone(),
);
mode_node_dev2.add_message_target(
TestComponentId::Assembly as ComponentId,
reply_sender_to_assy,
);
mode_node_dev2.add_message_target(
TestComponentId::PusModeService as ComponentId,
reply_sender_to_pus.clone(),
);
mode_node_assy.add_reply_target(
TestComponentId::PusModeService as ComponentId,
reply_sender_to_pus,
);
let mut device1 = TestDevice {
name: "Test Device 1".to_string(),
mode_node: mode_node_dev1,
mode_and_submode: ModeAndSubmode::new(0, 0),
};
let mut device2 = TestDevice {
name: "Test Device 2".to_string(),
mode_node: mode_node_dev2,
mode_and_submode: ModeAndSubmode::new(0, 0),
};
let mut assy = TestAssembly {
mode_node: mode_node_assy,
mode_requestor_info: None,
mode_and_submode: ModeAndSubmode::new(0, 0),
target_mode_and_submode: None,
};
let pus_service = PusModeService {
request_id_counter: Cell::new(0),
mode_node: mode_node_pus,
};
pus_service.send_announce_mode_cmd_to_assy();
assy.run();
device1.run();
device2.run();
assy.run();
}

View File

@ -1,11 +1,14 @@
use satrs::event_man::{
EventManagerWithMpsc, EventSendProvider, EventU32SenderMpsc, MpscEventU32Receiver,
EventManagerWithMpsc, EventMessage, EventMessageU32, EventRoutingError, EventSendProvider,
EventU32SenderMpsc, MpscEventU32Receiver,
};
use satrs::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
use satrs::params::U32Pair;
use satrs::params::{Params, ParamsHeapless, WritableToBeBytes};
use satrs::pus::event_man::{DefaultPusEventMgmtBackend, EventReporter, PusEventDispatcher};
use satrs::pus::TmAsVecSenderWithMpsc;
use satrs::pus::test_util::TEST_COMPONENT_ID_0;
use satrs::pus::PusTmAsVec;
use satrs::request::UniqueApidTargetId;
use spacepackets::ecss::tm::PusTmReader;
use spacepackets::ecss::{PusError, PusPacket};
use std::sync::mpsc::{self, SendError, TryRecvError};
@ -15,6 +18,8 @@ const INFO_EVENT: EventU32TypedSev<SeverityInfo> =
EventU32TypedSev::<SeverityInfo>::const_new(1, 0);
const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5);
const EMPTY_STAMP: [u8; 7] = [0; 7];
const TEST_APID: u16 = 0x02;
const TEST_ID: UniqueApidTargetId = UniqueApidTargetId::new(TEST_APID, 0x05);
#[derive(Debug, Clone)]
pub enum CustomTmSenderError {
@ -30,42 +35,43 @@ fn test_threaded_usage() {
let (pus_event_man_tx, pus_event_man_rx) = mpsc::channel();
let pus_event_man_send_provider = EventU32SenderMpsc::new(1, pus_event_man_tx);
event_man.subscribe_all(pus_event_man_send_provider.channel_id());
event_man.subscribe_all(pus_event_man_send_provider.target_id());
event_man.add_sender(pus_event_man_send_provider);
let (event_tx, event_rx) = mpsc::channel();
let reporter = EventReporter::new(0x02, 128).expect("Creating event reporter failed");
let mut pus_event_man =
PusEventDispatcher::new(reporter, DefaultPusEventMgmtBackend::default());
let (event_tx, event_rx) = mpsc::channel::<PusTmAsVec>();
let reporter =
EventReporter::new(TEST_ID.raw(), 0x02, 0, 128).expect("Creating event reporter failed");
let pus_event_man = PusEventDispatcher::new(reporter, DefaultPusEventMgmtBackend::default());
let error_handler = |event_msg: &EventMessageU32, error: EventRoutingError| {
panic!("received routing error for event {event_msg:?}: {error:?}");
};
// PUS + Generic event manager thread
let jh0 = thread::spawn(move || {
let mut sender = TmAsVecSenderWithMpsc::new(0, "event_sender", event_tx);
let mut event_cnt = 0;
let mut params_array: [u8; 128] = [0; 128];
loop {
let res = event_man.try_event_handling();
assert!(res.is_ok());
event_man.try_event_handling(error_handler);
match pus_event_man_rx.try_recv() {
Ok((event, aux_data)) => {
let mut gen_event = |aux_data| {
Ok(event_msg) => {
let gen_event = |aux_data| {
pus_event_man.generate_pus_event_tm_generic(
&mut sender,
&event_tx,
&EMPTY_STAMP,
event,
event_msg.event(),
aux_data,
)
};
let res = if let Some(aux_data) = aux_data {
let res = if let Some(aux_data) = event_msg.params() {
match aux_data {
Params::Heapless(heapless) => match heapless {
ParamsHeapless::Raw(raw) => {
raw.write_to_be_bytes(&mut params_array)
.expect("Writing raw parameter failed");
gen_event(Some(&params_array[0..raw.raw_len()]))
gen_event(Some(&params_array[0..raw.written_len()]))
}
ParamsHeapless::EcssEnum(e) => {
e.write_to_be_bytes(&mut params_array)
.expect("Writing ECSS enum failed");
gen_event(Some(&params_array[0..e.raw_len()]))
gen_event(Some(&params_array[0..e.written_len()]))
}
},
Params::Vec(vec) => gen_event(Some(vec.as_slice())),
@ -95,14 +101,17 @@ fn test_threaded_usage() {
// Event sender and TM checker thread
let jh1 = thread::spawn(move || {
event_sender
.send((INFO_EVENT.into(), None))
.send(EventMessage::new(
TEST_COMPONENT_ID_0.id(),
INFO_EVENT.into(),
))
.expect("Sending info event failed");
loop {
match event_rx.try_recv() {
// Event TM received successfully
Ok(event_tm) => {
let tm =
PusTmReader::new(event_tm.as_slice(), 7).expect("Deserializing TM failed");
let tm = PusTmReader::new(event_tm.packet.as_slice(), 7)
.expect("Deserializing TM failed");
assert_eq!(tm.0.service(), 5);
assert_eq!(tm.0.subservice(), 1);
let src_data = tm.0.source_data();
@ -121,14 +130,18 @@ fn test_threaded_usage() {
}
}
event_sender
.send((LOW_SEV_EVENT, Some(Params::Heapless((2_u32, 3_u32).into()))))
.send(EventMessage::new_with_params(
TEST_COMPONENT_ID_0.id(),
LOW_SEV_EVENT,
&Params::Heapless((2_u32, 3_u32).into()),
))
.expect("Sending low severity event failed");
loop {
match event_rx.try_recv() {
// Event TM received successfully
Ok(event_tm) => {
let tm =
PusTmReader::new(event_tm.as_slice(), 7).expect("Deserializing TM failed");
let tm = PusTmReader::new(event_tm.packet.as_slice(), 7)
.expect("Deserializing TM failed");
assert_eq!(tm.0.service(), 5);
assert_eq!(tm.0.subservice(), 2);
let src_data = tm.0.source_data();

View File

@ -1,9 +1,10 @@
#[cfg(feature = "crossbeam")]
// #[cfg(feature = "crossbeam")]
pub mod crossbeam_test {
use hashbrown::HashMap;
use satrs::pool::{PoolProvider, PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig};
use satrs::pus::test_util::{TEST_APID, TEST_COMPONENT_ID_0};
use satrs::pus::verification::{
FailParams, RequestId, VerificationReporterCfg, VerificationReporterWithSender,
FailParams, RequestId, VerificationReporter, VerificationReporterCfg,
VerificationReportingProvider,
};
use satrs::pus::TmInSharedPoolSenderWithCrossbeam;
@ -16,7 +17,6 @@ pub mod crossbeam_test {
use std::thread;
use std::time::Duration;
const TEST_APID: u16 = 0x03;
const FIXED_STAMP: [u8; 7] = [0; 7];
const PACKETS_SENT: u8 = 8;
@ -40,13 +40,9 @@ pub mod crossbeam_test {
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 = TmInSharedPoolSenderWithCrossbeam::new(
0,
"verif_sender",
shared_tm_pool.clone(),
tx.clone(),
);
let mut reporter_with_sender_0 = VerificationReporterWithSender::new(&cfg, sender);
let sender_0 = TmInSharedPoolSenderWithCrossbeam::new(shared_tm_pool.clone(), tx.clone());
let sender_1 = sender_0.clone();
let mut reporter_with_sender_0 = VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &cfg);
let mut reporter_with_sender_1 = reporter_with_sender_0.clone();
// For test purposes, we retrieve the request ID from the TCs and pass them to the receiver
// tread.
@ -57,9 +53,9 @@ pub mod crossbeam_test {
let (tx_tc_1, rx_tc_1) = crossbeam_channel::bounded(3);
{
let mut tc_guard = shared_tc_pool_0.write().unwrap();
let mut sph = SpHeader::tc_unseg(TEST_APID, 0, 0).unwrap();
let sph = SpHeader::new_for_unseg_tc(TEST_APID, 0, 0);
let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
let pus_tc_0 = PusTcCreator::new_no_app_data(&mut sph, tc_header, true);
let pus_tc_0 = PusTcCreator::new_no_app_data(sph, tc_header, true);
req_id_0 = RequestId::new(&pus_tc_0);
let addr = tc_guard
.free_element(pus_tc_0.len_written(), |buf| {
@ -67,9 +63,9 @@ pub mod crossbeam_test {
})
.unwrap();
tx_tc_0.send(addr).unwrap();
let mut sph = SpHeader::tc_unseg(TEST_APID, 1, 0).unwrap();
let sph = SpHeader::new_for_unseg_tc(TEST_APID, 1, 0);
let tc_header = PusTcSecondaryHeader::new_simple(5, 1);
let pus_tc_1 = PusTcCreator::new_no_app_data(&mut sph, tc_header, true);
let pus_tc_1 = PusTcCreator::new_no_app_data(sph, tc_header, true);
req_id_1 = RequestId::new(&pus_tc_1);
let addr = tc_guard
.free_element(pus_tc_0.len_written(), |buf| {
@ -93,24 +89,24 @@ pub mod crossbeam_test {
let token = reporter_with_sender_0.add_tc_with_req_id(req_id_0);
let accepted_token = reporter_with_sender_0
.acceptance_success(token, &FIXED_STAMP)
.acceptance_success(&sender_0, token, &FIXED_STAMP)
.expect("Acceptance success failed");
// Do some start handling here
let started_token = reporter_with_sender_0
.start_success(accepted_token, &FIXED_STAMP)
.start_success(&sender_0, accepted_token, &FIXED_STAMP)
.expect("Start success failed");
// Do some step handling here
reporter_with_sender_0
.step_success(&started_token, &FIXED_STAMP, EcssEnumU8::new(0))
.step_success(&sender_0, &started_token, &FIXED_STAMP, EcssEnumU8::new(0))
.expect("Start success failed");
// Finish up
reporter_with_sender_0
.step_success(&started_token, &FIXED_STAMP, EcssEnumU8::new(1))
.step_success(&sender_0, &started_token, &FIXED_STAMP, EcssEnumU8::new(1))
.expect("Start success failed");
reporter_with_sender_0
.completion_success(started_token, &FIXED_STAMP)
.completion_success(&sender_0, started_token, &FIXED_STAMP)
.expect("Completion success failed");
});
@ -128,15 +124,15 @@ pub mod crossbeam_test {
let (tc, _) = PusTcReader::new(&tc_buf[0..tc_len]).unwrap();
let token = reporter_with_sender_1.add_tc(&tc);
let accepted_token = reporter_with_sender_1
.acceptance_success(token, &FIXED_STAMP)
.acceptance_success(&sender_1, token, &FIXED_STAMP)
.expect("Acceptance success failed");
let started_token = reporter_with_sender_1
.start_success(accepted_token, &FIXED_STAMP)
.start_success(&sender_1, accepted_token, &FIXED_STAMP)
.expect("Start success failed");
let fail_code = EcssEnumU16::new(2);
let params = FailParams::new_no_fail_data(&FIXED_STAMP, &fail_code);
reporter_with_sender_1
.completion_failure(started_token, params)
.completion_failure(&sender_1, started_token, params)
.expect("Completion success failed");
});
@ -145,14 +141,14 @@ pub mod crossbeam_test {
let mut tm_buf: [u8; 1024] = [0; 1024];
let mut verif_map = HashMap::new();
while packet_counter < PACKETS_SENT {
let verif_addr = rx
let tm_in_pool = rx
.recv_timeout(Duration::from_millis(50))
.expect("Packet reception timeout");
let tm_len;
let shared_tm_store = shared_tm_pool.clone_backing_pool();
{
let mut rg = shared_tm_store.write().expect("Error locking shared pool");
let store_guard = rg.read_with_guard(verif_addr);
let store_guard = rg.read_with_guard(tm_in_pool.store_addr);
tm_len = store_guard
.read(&mut tm_buf)
.expect("Error reading TM slice");

View File

@ -31,7 +31,7 @@ use spacepackets::{
ecss::{tc::PusTcCreator, WritablePusPacket},
PacketId, SpHeader,
};
use std::{boxed::Box, collections::VecDeque, sync::Arc, vec::Vec};
use std::{collections::VecDeque, sync::Arc, vec::Vec};
#[derive(Default, Clone)]
struct SyncTcCacher {
@ -162,14 +162,14 @@ fn test_cobs_server() {
}
const TEST_APID_0: u16 = 0x02;
const TEST_PACKET_ID_0: PacketId = PacketId::const_tc(true, TEST_APID_0);
const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0);
#[test]
fn test_ccsds_server() {
let tc_receiver = SyncTcCacher::default();
let mut tm_source = SyncTmSource::default();
let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap();
let verif_tm = PusTcCreator::new_simple(&mut sph, 1, 1, None, true);
let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, 0, 0);
let verif_tm = PusTcCreator::new_simple(sph, 1, 1, &[], true);
let tm_0 = verif_tm.to_vec().expect("tm generation failed");
tm_source.add_tm(&tm_0);
let mut packet_id_lookup = HashSet::new();
@ -178,7 +178,7 @@ fn test_ccsds_server() {
ServerConfig::new(AUTO_PORT_ADDR, Duration::from_millis(2), 1024, 1024),
tm_source,
tc_receiver.clone(),
Box::new(packet_id_lookup),
packet_id_lookup,
)
.expect("TCP server generation failed");
let dest_addr = tcp_server
@ -203,8 +203,8 @@ fn test_ccsds_server() {
.expect("setting reas timeout failed");
// Send ping telecommand.
let mut sph = SpHeader::tc_unseg(TEST_APID_0, 0, 0).unwrap();
let ping_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
let sph = SpHeader::new_for_unseg_tc(TEST_APID_0, 0, 0);
let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
let tc_0 = ping_tc.to_vec().expect("packet creation failed");
stream
.write_all(&tc_0)