satrs/pus/
mod.rs

1//! # PUS support modules
2//!
3//! This module contains structures to make working with the PUS C standard easier.
4//! The satrs-example application contains various usage examples of these components.
5use crate::pool::{PoolAddr, PoolError};
6use crate::pus::verification::{TcStateAccepted, TcStateToken, VerificationToken};
7use crate::queue::{GenericReceiveError, GenericSendError};
8use crate::request::{GenericMessage, MessageMetadata, RequestId};
9#[cfg(feature = "alloc")]
10use crate::tmtc::PacketAsVec;
11use crate::tmtc::PacketInPool;
12use crate::ComponentId;
13use core::fmt::{Display, Formatter};
14use core::time::Duration;
15#[cfg(feature = "alloc")]
16use downcast_rs::{impl_downcast, Downcast};
17#[cfg(feature = "alloc")]
18use dyn_clone::DynClone;
19#[cfg(feature = "std")]
20use std::error::Error;
21
22use spacepackets::ecss::tc::{PusTcCreator, PusTcReader};
23use spacepackets::ecss::tm::PusTmCreator;
24use spacepackets::ecss::PusError;
25use spacepackets::{ByteConversionError, SpHeader};
26
27pub mod action;
28pub mod event;
29pub mod event_man;
30#[cfg(feature = "std")]
31pub mod event_srv;
32pub mod mode;
33pub mod scheduler;
34#[cfg(feature = "std")]
35pub mod scheduler_srv;
36#[cfg(feature = "std")]
37pub mod test;
38pub mod verification;
39
40#[cfg(feature = "alloc")]
41pub use alloc_mod::*;
42
43#[cfg(feature = "std")]
44pub use std_mod::*;
45
46use self::verification::VerificationReportingProvider;
47
48/// Generic handling status for an object which is able to continuosly handle a queue to handle
49/// request or replies until the queue is empty.
50#[derive(Debug, PartialEq, Eq, Copy, Clone)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub enum HandlingStatus {
53    HandledOne,
54    Empty,
55}
56
57#[derive(Debug, PartialEq, Eq, Clone)]
58pub enum PusTmVariant<'time, 'src_data> {
59    InStore(PoolAddr),
60    Direct(PusTmCreator<'time, 'src_data>),
61}
62
63impl From<PoolAddr> for PusTmVariant<'_, '_> {
64    fn from(value: PoolAddr) -> Self {
65        Self::InStore(value)
66    }
67}
68
69impl<'time, 'src_data> From<PusTmCreator<'time, 'src_data>> for PusTmVariant<'time, 'src_data> {
70    fn from(value: PusTmCreator<'time, 'src_data>) -> Self {
71        Self::Direct(value)
72    }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub enum EcssTmtcError {
77    Store(PoolError),
78    ByteConversion(ByteConversionError),
79    Pus(PusError),
80    CantSendAddr(PoolAddr),
81    CantSendDirectTm,
82    Send(GenericSendError),
83    Receive(GenericReceiveError),
84}
85
86impl Display for EcssTmtcError {
87    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
88        match self {
89            EcssTmtcError::Store(store) => {
90                write!(f, "ecss tmtc error: {store}")
91            }
92            EcssTmtcError::ByteConversion(e) => {
93                write!(f, "ecss tmtc error: {e}")
94            }
95            EcssTmtcError::Pus(e) => {
96                write!(f, "ecss tmtc error: {e}")
97            }
98            EcssTmtcError::CantSendAddr(addr) => {
99                write!(f, "can not send address {addr}")
100            }
101            EcssTmtcError::CantSendDirectTm => {
102                write!(f, "can not send TM directly")
103            }
104            EcssTmtcError::Send(e) => {
105                write!(f, "ecss tmtc error: {e}")
106            }
107            EcssTmtcError::Receive(e) => {
108                write!(f, "ecss tmtc error {e}")
109            }
110        }
111    }
112}
113
114impl From<PoolError> for EcssTmtcError {
115    fn from(value: PoolError) -> Self {
116        Self::Store(value)
117    }
118}
119
120impl From<PusError> for EcssTmtcError {
121    fn from(value: PusError) -> Self {
122        Self::Pus(value)
123    }
124}
125
126impl From<GenericSendError> for EcssTmtcError {
127    fn from(value: GenericSendError) -> Self {
128        Self::Send(value)
129    }
130}
131
132impl From<ByteConversionError> for EcssTmtcError {
133    fn from(value: ByteConversionError) -> Self {
134        Self::ByteConversion(value)
135    }
136}
137
138impl From<GenericReceiveError> for EcssTmtcError {
139    fn from(value: GenericReceiveError) -> Self {
140        Self::Receive(value)
141    }
142}
143
144#[cfg(feature = "std")]
145impl Error for EcssTmtcError {
146    fn source(&self) -> Option<&(dyn Error + 'static)> {
147        match self {
148            EcssTmtcError::Store(e) => Some(e),
149            EcssTmtcError::ByteConversion(e) => Some(e),
150            EcssTmtcError::Pus(e) => Some(e),
151            EcssTmtcError::Send(e) => Some(e),
152            EcssTmtcError::Receive(e) => Some(e),
153            _ => None,
154        }
155    }
156}
157pub trait ChannelWithId: Send {
158    /// Each sender can have an ID associated with it
159    fn id(&self) -> ComponentId;
160    fn name(&self) -> &'static str {
161        "unset"
162    }
163}
164
165/// Generic trait for a user supplied sender object.
166///
167/// This sender object is responsible for sending PUS telemetry to a TM sink.
168pub trait EcssTmSender: Send {
169    fn send_tm(&self, sender_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError>;
170}
171
172/// Generic trait for a user supplied sender object.
173///
174/// This sender object is responsible for sending PUS telecommands to a TC recipient. Each
175/// telecommand can optionally have a token which contains its verification state.
176pub trait EcssTcSender {
177    fn send_tc(&self, tc: PusTcCreator, token: Option<TcStateToken>) -> Result<(), EcssTmtcError>;
178}
179
180/// Dummy object which can be useful for tests.
181#[derive(Default)]
182pub struct EcssTmDummySender {}
183
184impl EcssTmSender for EcssTmDummySender {
185    fn send_tm(&self, _source_id: ComponentId, _tm: PusTmVariant) -> Result<(), EcssTmtcError> {
186        Ok(())
187    }
188}
189
190/// A PUS telecommand packet can be stored in memory and sent using different methods. Right now,
191/// storage inside a pool structure like [crate::pool::StaticMemoryPool], and storage inside a
192/// `Vec<u8>` are supported.
193#[non_exhaustive]
194#[derive(Debug, Clone, PartialEq, Eq)]
195pub enum TcInMemory {
196    Pool(PacketInPool),
197    #[cfg(feature = "alloc")]
198    Vec(PacketAsVec),
199}
200
201impl From<PacketInPool> for TcInMemory {
202    fn from(value: PacketInPool) -> Self {
203        Self::Pool(value)
204    }
205}
206
207#[cfg(feature = "alloc")]
208impl From<PacketAsVec> for TcInMemory {
209    fn from(value: PacketAsVec) -> Self {
210        Self::Vec(value)
211    }
212}
213
214/// Generic structure for an ECSS PUS Telecommand and its correspoding verification token.
215#[derive(Debug, Clone, PartialEq, Eq)]
216pub struct EcssTcAndToken {
217    pub tc_in_memory: TcInMemory,
218    pub token: Option<TcStateToken>,
219}
220
221impl EcssTcAndToken {
222    pub fn new(tc_in_memory: impl Into<TcInMemory>, token: impl Into<TcStateToken>) -> Self {
223        Self {
224            tc_in_memory: tc_in_memory.into(),
225            token: Some(token.into()),
226        }
227    }
228}
229
230/// Generic abstraction for a telecommand being sent around after is has been accepted.
231pub struct AcceptedEcssTcAndToken {
232    pub tc_in_memory: TcInMemory,
233    pub token: VerificationToken<TcStateAccepted>,
234}
235
236impl From<AcceptedEcssTcAndToken> for EcssTcAndToken {
237    fn from(value: AcceptedEcssTcAndToken) -> Self {
238        EcssTcAndToken {
239            tc_in_memory: value.tc_in_memory,
240            token: Some(value.token.into()),
241        }
242    }
243}
244
245impl TryFrom<EcssTcAndToken> for AcceptedEcssTcAndToken {
246    type Error = ();
247
248    fn try_from(value: EcssTcAndToken) -> Result<Self, Self::Error> {
249        if let Some(TcStateToken::Accepted(token)) = value.token {
250            return Ok(AcceptedEcssTcAndToken {
251                tc_in_memory: value.tc_in_memory,
252                token,
253            });
254        }
255        Err(())
256    }
257}
258
259#[derive(Debug, Clone)]
260pub enum TryRecvTmtcError {
261    Tmtc(EcssTmtcError),
262    Empty,
263}
264
265impl From<EcssTmtcError> for TryRecvTmtcError {
266    fn from(value: EcssTmtcError) -> Self {
267        Self::Tmtc(value)
268    }
269}
270
271impl From<PusError> for TryRecvTmtcError {
272    fn from(value: PusError) -> Self {
273        Self::Tmtc(value.into())
274    }
275}
276
277impl From<PoolError> for TryRecvTmtcError {
278    fn from(value: PoolError) -> Self {
279        Self::Tmtc(value.into())
280    }
281}
282
283/// Generic trait for a user supplied receiver object.
284pub trait EcssTcReceiver {
285    fn recv_tc(&self) -> Result<EcssTcAndToken, TryRecvTmtcError>;
286}
287
288/// Generic trait for objects which can send ECSS PUS telecommands.
289pub trait PacketSenderPusTc: Send {
290    type Error;
291    fn send_pus_tc(
292        &self,
293        sender_id: ComponentId,
294        header: &SpHeader,
295        pus_tc: &PusTcReader,
296    ) -> Result<(), Self::Error>;
297}
298
299pub trait ActiveRequestMapProvider<V>: Sized {
300    fn insert(&mut self, request_id: &RequestId, request_info: V);
301    fn get(&self, request_id: RequestId) -> Option<&V>;
302    fn get_mut(&mut self, request_id: RequestId) -> Option<&mut V>;
303    fn remove(&mut self, request_id: RequestId) -> bool;
304
305    /// Call a user-supplied closure for each active request.
306    fn for_each<F: FnMut(&RequestId, &V)>(&self, f: F);
307
308    /// Call a user-supplied closure for each active request. Mutable variant.
309    fn for_each_mut<F: FnMut(&RequestId, &mut V)>(&mut self, f: F);
310}
311
312pub trait ActiveRequestProvider {
313    fn target_id(&self) -> ComponentId;
314    fn token(&self) -> TcStateToken;
315    fn set_token(&mut self, token: TcStateToken);
316    fn has_timed_out(&self) -> bool;
317    fn timeout(&self) -> Duration;
318}
319
320/// This trait is an abstraction for the routing of PUS request to a dedicated
321/// recipient using the generic [ComponentId].
322pub trait PusRequestRouter<Request> {
323    type Error;
324
325    fn route(
326        &self,
327        requestor_info: MessageMetadata,
328        target_id: ComponentId,
329        request: Request,
330    ) -> Result<(), Self::Error>;
331}
332
333pub trait PusReplyHandler<ActiveRequestInfo: ActiveRequestProvider, ReplyType> {
334    type Error;
335
336    /// This function handles a reply for a given PUS request and returns whether that request
337    /// is finished. A finished PUS request will be removed from the active request map.
338    fn handle_reply(
339        &mut self,
340        reply: &GenericMessage<ReplyType>,
341        active_request: &ActiveRequestInfo,
342        tm_sender: &impl EcssTmSender,
343        verification_handler: &impl VerificationReportingProvider,
344        time_stamp: &[u8],
345    ) -> Result<bool, Self::Error>;
346
347    fn handle_unrequested_reply(
348        &mut self,
349        reply: &GenericMessage<ReplyType>,
350        tm_sender: &impl EcssTmSender,
351    ) -> Result<(), Self::Error>;
352
353    /// Handle the timeout of an active request.
354    fn handle_request_timeout(
355        &mut self,
356        active_request: &ActiveRequestInfo,
357        tm_sender: &impl EcssTmSender,
358        verification_handler: &impl VerificationReportingProvider,
359        time_stamp: &[u8],
360    ) -> Result<(), Self::Error>;
361}
362
363#[cfg(feature = "alloc")]
364pub mod alloc_mod {
365    use hashbrown::HashMap;
366
367    use super::*;
368
369    /// Extension trait for [EcssTmSender].
370    ///
371    /// It provides additional functionality, for example by implementing the [Downcast] trait
372    /// and the [DynClone] trait.
373    ///
374    /// [Downcast] is implemented to allow passing the sender as a boxed trait object and still
375    /// retrieve the concrete type at a later point.
376    ///
377    /// [DynClone] allows cloning the trait object as long as the boxed object implements
378    /// [Clone].
379    #[cfg(feature = "alloc")]
380    pub trait EcssTmSenderExt: EcssTmSender + Downcast + DynClone {
381        // Remove this once trait upcasting coercion has been implemented.
382        // Tracking issue: https://github.com/rust-lang/rust/issues/65991
383        fn upcast(&self) -> &dyn EcssTmSender;
384        // Remove this once trait upcasting coercion has been implemented.
385        // Tracking issue: https://github.com/rust-lang/rust/issues/65991
386        fn upcast_mut(&mut self) -> &mut dyn EcssTmSender;
387    }
388
389    /// Blanket implementation for all types which implement [EcssTmSender] and are clonable.
390    impl<T> EcssTmSenderExt for T
391    where
392        T: EcssTmSender + Clone + 'static,
393    {
394        // Remove this once trait upcasting coercion has been implemented.
395        // Tracking issue: https://github.com/rust-lang/rust/issues/65991
396        fn upcast(&self) -> &dyn EcssTmSender {
397            self
398        }
399        // Remove this once trait upcasting coercion has been implemented.
400        // Tracking issue: https://github.com/rust-lang/rust/issues/65991
401        fn upcast_mut(&mut self) -> &mut dyn EcssTmSender {
402            self
403        }
404    }
405
406    dyn_clone::clone_trait_object!(EcssTmSenderExt);
407    impl_downcast!(EcssTmSenderExt);
408
409    /// Extension trait for [EcssTcSender].
410    ///
411    /// It provides additional functionality, for example by implementing the [Downcast] trait
412    /// and the [DynClone] trait.
413    ///
414    /// [Downcast] is implemented to allow passing the sender as a boxed trait object and still
415    /// retrieve the concrete type at a later point.
416    ///
417    /// [DynClone] allows cloning the trait object as long as the boxed object implements
418    /// [Clone].
419    #[cfg(feature = "alloc")]
420    pub trait EcssTcSenderExt: EcssTcSender + Downcast + DynClone {}
421
422    /// Blanket implementation for all types which implement [EcssTcSender] and are clonable.
423    impl<T> EcssTcSenderExt for T where T: EcssTcSender + Clone + 'static {}
424
425    dyn_clone::clone_trait_object!(EcssTcSenderExt);
426    impl_downcast!(EcssTcSenderExt);
427
428    /// Extension trait for [EcssTcReceiver].
429    ///
430    /// It provides additional functionality, for example by implementing the [Downcast] trait
431    /// and the [DynClone] trait.
432    ///
433    /// [Downcast] is implemented to allow passing the sender as a boxed trait object and still
434    /// retrieve the concrete type at a later point.
435    ///
436    /// [DynClone] allows cloning the trait object as long as the boxed object implements
437    /// [Clone].
438    #[cfg(feature = "alloc")]
439    pub trait EcssTcReceiverExt: EcssTcReceiver + Downcast {}
440
441    /// Blanket implementation for all types which implement [EcssTcReceiver] and are clonable.
442    impl<T> EcssTcReceiverExt for T where T: EcssTcReceiver + 'static {}
443
444    impl_downcast!(EcssTcReceiverExt);
445
446    /// This trait is an abstraction for the conversion of a PUS telecommand into a generic request
447    /// type.
448    ///
449    /// Having a dedicated trait for this allows maximum flexiblity and tailoring of the standard.
450    /// The only requirement is that a valid active request information instance and a request
451    /// are returned by the core conversion function. The active request type needs to fulfill
452    /// the [ActiveRequestProvider] trait bound.
453    ///
454    /// The user should take care of performing the error handling as well. Some of the following
455    /// aspects might be relevant:
456    ///
457    /// - Checking the validity of the APID, service ID, subservice ID.
458    /// - Checking the validity of the user data.
459    ///
460    /// A [VerificationReportingProvider] instance is passed to the user to also allow handling
461    /// of the verification process as part of the PUS standard requirements.
462    pub trait PusTcToRequestConverter<ActiveRequestInfo: ActiveRequestProvider, Request> {
463        type Error;
464        fn convert(
465            &mut self,
466            token: VerificationToken<TcStateAccepted>,
467            tc: &PusTcReader,
468            tm_sender: &(impl EcssTmSender + ?Sized),
469            verif_reporter: &impl VerificationReportingProvider,
470            time_stamp: &[u8],
471        ) -> Result<(ActiveRequestInfo, Request), Self::Error>;
472    }
473
474    #[derive(Clone, Debug)]
475    pub struct DefaultActiveRequestMap<V>(pub HashMap<RequestId, V>);
476
477    impl<V> Default for DefaultActiveRequestMap<V> {
478        fn default() -> Self {
479            Self(HashMap::new())
480        }
481    }
482
483    impl<V> ActiveRequestMapProvider<V> for DefaultActiveRequestMap<V> {
484        fn insert(&mut self, request_id: &RequestId, request: V) {
485            self.0.insert(*request_id, request);
486        }
487
488        fn get(&self, request_id: RequestId) -> Option<&V> {
489            self.0.get(&request_id)
490        }
491
492        fn get_mut(&mut self, request_id: RequestId) -> Option<&mut V> {
493            self.0.get_mut(&request_id)
494        }
495
496        fn remove(&mut self, request_id: RequestId) -> bool {
497            self.0.remove(&request_id).is_some()
498        }
499
500        fn for_each<F: FnMut(&RequestId, &V)>(&self, mut f: F) {
501            for (req_id, active_req) in &self.0 {
502                f(req_id, active_req);
503            }
504        }
505
506        fn for_each_mut<F: FnMut(&RequestId, &mut V)>(&mut self, mut f: F) {
507            for (req_id, active_req) in &mut self.0 {
508                f(req_id, active_req);
509            }
510        }
511    }
512
513    /*
514    /// Generic reply handler structure which can be used to handle replies for a specific PUS
515    /// service.
516    ///
517    /// This is done by keeping track of active requests using an internal map structure. An API
518    /// to register new active requests is exposed as well.
519    /// The reply handler performs boilerplate tasks like performing the verification handling and
520    /// timeout handling.
521    ///
522    /// This object is not useful by itself but serves as a common building block for high-level
523    /// PUS reply handlers. Concrete PUS handlers should constrain the [ActiveRequestProvider] and
524    /// the `ReplyType` generics to specific types tailored towards PUS services in addition to
525    /// providing an API which can process received replies and convert them into verification
526    /// completions or other operation like user hook calls. The framework also provides some
527    /// concrete PUS handlers for common PUS services like the mode, action and housekeeping
528    /// service.
529    ///
530    /// This object does not automatically update its internal time information used to check for
531    /// timeouts. The user should call the [Self::update_time] and [Self::update_time_from_now]
532    /// methods to do this.
533    pub struct PusServiceReplyHandler<
534        ActiveRequestMap: ActiveRequestMapProvider<ActiveRequestType>,
535        ReplyHook: ReplyHandlerHook<ActiveRequestType, ReplyType>,
536        ActiveRequestType: ActiveRequestProvider,
537        ReplyType,
538    > {
539        pub active_request_map: ActiveRequestMap,
540        pub tm_buf: alloc::vec::Vec<u8>,
541        pub current_time: UnixTimestamp,
542        pub user_hook: ReplyHook,
543        phantom: PhantomData<(ActiveRequestType, ReplyType)>,
544    }
545
546    impl<
547            ActiveRequestMap: ActiveRequestMapProvider<ActiveRequestType>,
548            ReplyHook: ReplyHandlerHook<ActiveRequestType, ReplyType>,
549            ActiveRequestType: ActiveRequestProvider,
550            ReplyType,
551        >
552        PusServiceReplyHandler<
553            ActiveRequestMap,
554            ReplyHook,
555            ActiveRequestType,
556            ReplyType,
557        >
558    {
559        #[cfg(feature = "std")]
560        pub fn new_from_now(
561            active_request_map: ActiveRequestMap,
562            fail_data_buf_size: usize,
563            user_hook: ReplyHook,
564        ) -> Result<Self, std::time::SystemTimeError> {
565            let current_time = UnixTimestamp::from_now()?;
566            Ok(Self::new(
567                active_request_map,
568                fail_data_buf_size,
569                user_hook,
570                tm_sender,
571                current_time,
572            ))
573        }
574
575        pub fn new(
576            active_request_map: ActiveRequestMap,
577            fail_data_buf_size: usize,
578            user_hook: ReplyHook,
579            tm_sender: TmSender,
580            init_time: UnixTimestamp,
581        ) -> Self {
582            Self {
583                active_request_map,
584                tm_buf: alloc::vec![0; fail_data_buf_size],
585                current_time: init_time,
586                user_hook,
587                tm_sender,
588                phantom: PhantomData,
589            }
590        }
591
592        pub fn add_routed_request(
593            &mut self,
594            request_id: verification::RequestId,
595            active_request_type: ActiveRequestType,
596        ) {
597            self.active_request_map
598                .insert(&request_id.into(), active_request_type);
599        }
600
601        pub fn request_active(&self, request_id: RequestId) -> bool {
602            self.active_request_map.get(request_id).is_some()
603        }
604
605        /// Check for timeouts across all active requests.
606        ///
607        /// It will call [Self::handle_timeout] for all active requests which have timed out.
608        pub fn check_for_timeouts(&mut self, time_stamp: &[u8]) -> Result<(), EcssTmtcError> {
609            let mut timed_out_commands = alloc::vec::Vec::new();
610            self.active_request_map.for_each(|request_id, active_req| {
611                let diff = self.current_time - active_req.start_time();
612                if diff.duration_absolute > active_req.timeout() {
613                    self.handle_timeout(active_req, time_stamp);
614                }
615                timed_out_commands.push(*request_id);
616            });
617            for timed_out_command in timed_out_commands {
618                self.active_request_map.remove(timed_out_command);
619            }
620            Ok(())
621        }
622
623        /// Handle the timeout for a given active request.
624        ///
625        /// This implementation will report a verification completion failure with a user-provided
626        /// error code. It supplies the configured request timeout in milliseconds as a [u64]
627        /// serialized in big-endian format as the failure data.
628        pub fn handle_timeout(&self, active_request: &ActiveRequestType, time_stamp: &[u8]) {
629            let timeout = active_request.timeout().as_millis() as u64;
630            let timeout_raw = timeout.to_be_bytes();
631            self.verification_reporter
632                .completion_failure(
633                    active_request.token(),
634                    FailParams::new(
635                        time_stamp,
636                        &self.user_hook.timeout_error_code(),
637                        &timeout_raw,
638                    ),
639                )
640                .unwrap();
641            self.user_hook.timeout_callback(active_request);
642        }
643
644        /// Update the current time used for timeout checks based on the current OS time.
645        #[cfg(feature = "std")]
646        pub fn update_time_from_now(&mut self) -> Result<(), std::time::SystemTimeError> {
647            self.current_time = UnixTimestamp::from_now()?;
648            Ok(())
649        }
650
651        /// Update the current time used for timeout checks.
652        pub fn update_time(&mut self, time: UnixTimestamp) {
653            self.current_time = time;
654        }
655    }
656    */
657}
658
659#[cfg(feature = "std")]
660pub mod std_mod {
661    use super::*;
662    use crate::pool::{
663        PoolAddr, PoolError, PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool,
664    };
665    use crate::pus::verification::{TcStateAccepted, VerificationToken};
666    use crate::tmtc::{PacketAsVec, PacketSenderWithSharedPool};
667    use crate::ComponentId;
668    use alloc::vec::Vec;
669    use core::time::Duration;
670    use spacepackets::ecss::tc::PusTcReader;
671    use spacepackets::ecss::WritablePusPacket;
672    use spacepackets::time::StdTimestampError;
673    use spacepackets::ByteConversionError;
674    use std::string::String;
675    use std::sync::mpsc;
676    use std::sync::mpsc::TryRecvError;
677    use thiserror::Error;
678
679    #[cfg(feature = "crossbeam")]
680    pub use cb_mod::*;
681
682    use super::verification::{TcStateToken, VerificationReportingProvider};
683    use super::{AcceptedEcssTcAndToken, ActiveRequestProvider, TcInMemory};
684    use crate::tmtc::PacketInPool;
685
686    impl From<mpsc::SendError<PoolAddr>> for EcssTmtcError {
687        fn from(_: mpsc::SendError<PoolAddr>) -> Self {
688            Self::Send(GenericSendError::RxDisconnected)
689        }
690    }
691
692    impl EcssTmSender for mpsc::Sender<PacketInPool> {
693        fn send_tm(&self, source_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError> {
694            match tm {
695                PusTmVariant::InStore(store_addr) => self
696                    .send(PacketInPool {
697                        sender_id: source_id,
698                        store_addr,
699                    })
700                    .map_err(|_| GenericSendError::RxDisconnected)?,
701                PusTmVariant::Direct(_) => return Err(EcssTmtcError::CantSendDirectTm),
702            };
703            Ok(())
704        }
705    }
706
707    impl EcssTmSender for mpsc::SyncSender<PacketInPool> {
708        fn send_tm(&self, source_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError> {
709            match tm {
710                PusTmVariant::InStore(store_addr) => self
711                    .try_send(PacketInPool {
712                        sender_id: source_id,
713                        store_addr,
714                    })
715                    .map_err(|e| EcssTmtcError::Send(e.into()))?,
716                PusTmVariant::Direct(_) => return Err(EcssTmtcError::CantSendDirectTm),
717            };
718            Ok(())
719        }
720    }
721
722    pub type MpscTmAsVecSender = mpsc::Sender<PacketAsVec>;
723
724    impl EcssTmSender for MpscTmAsVecSender {
725        fn send_tm(&self, source_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError> {
726            match tm {
727                PusTmVariant::InStore(addr) => return Err(EcssTmtcError::CantSendAddr(addr)),
728                PusTmVariant::Direct(tm) => self
729                    .send(PacketAsVec {
730                        sender_id: source_id,
731                        packet: tm.to_vec()?,
732                    })
733                    .map_err(|e| EcssTmtcError::Send(e.into()))?,
734            };
735            Ok(())
736        }
737    }
738
739    pub type MpscTmAsVecSenderBounded = mpsc::SyncSender<PacketAsVec>;
740
741    impl EcssTmSender for MpscTmAsVecSenderBounded {
742        fn send_tm(&self, source_id: ComponentId, tm: PusTmVariant) -> Result<(), EcssTmtcError> {
743            match tm {
744                PusTmVariant::InStore(addr) => return Err(EcssTmtcError::CantSendAddr(addr)),
745                PusTmVariant::Direct(tm) => self
746                    .send(PacketAsVec {
747                        sender_id: source_id,
748                        packet: tm.to_vec()?,
749                    })
750                    .map_err(|e| EcssTmtcError::Send(e.into()))?,
751            };
752            Ok(())
753        }
754    }
755
756    pub type MpscTcReceiver = mpsc::Receiver<EcssTcAndToken>;
757
758    impl EcssTcReceiver for MpscTcReceiver {
759        fn recv_tc(&self) -> Result<EcssTcAndToken, TryRecvTmtcError> {
760            self.try_recv().map_err(|e| match e {
761                TryRecvError::Empty => TryRecvTmtcError::Empty,
762                TryRecvError::Disconnected => TryRecvTmtcError::Tmtc(EcssTmtcError::from(
763                    GenericReceiveError::TxDisconnected(None),
764                )),
765            })
766        }
767    }
768
769    #[cfg(feature = "crossbeam")]
770    pub mod cb_mod {
771        use super::*;
772        use crossbeam_channel as cb;
773
774        impl From<cb::SendError<PoolAddr>> for EcssTmtcError {
775            fn from(_: cb::SendError<PoolAddr>) -> Self {
776                Self::Send(GenericSendError::RxDisconnected)
777            }
778        }
779
780        impl From<cb::TrySendError<PoolAddr>> for EcssTmtcError {
781            fn from(value: cb::TrySendError<PoolAddr>) -> Self {
782                match value {
783                    cb::TrySendError::Full(_) => Self::Send(GenericSendError::QueueFull(None)),
784                    cb::TrySendError::Disconnected(_) => {
785                        Self::Send(GenericSendError::RxDisconnected)
786                    }
787                }
788            }
789        }
790
791        impl EcssTmSender for cb::Sender<PacketInPool> {
792            fn send_tm(
793                &self,
794                sender_id: ComponentId,
795                tm: PusTmVariant,
796            ) -> Result<(), EcssTmtcError> {
797                match tm {
798                    PusTmVariant::InStore(addr) => self
799                        .try_send(PacketInPool::new(sender_id, addr))
800                        .map_err(|e| EcssTmtcError::Send(e.into()))?,
801                    PusTmVariant::Direct(_) => return Err(EcssTmtcError::CantSendDirectTm),
802                };
803                Ok(())
804            }
805        }
806        impl EcssTmSender for cb::Sender<PacketAsVec> {
807            fn send_tm(
808                &self,
809                sender_id: ComponentId,
810                tm: PusTmVariant,
811            ) -> Result<(), EcssTmtcError> {
812                match tm {
813                    PusTmVariant::InStore(addr) => return Err(EcssTmtcError::CantSendAddr(addr)),
814                    PusTmVariant::Direct(tm) => self
815                        .send(PacketAsVec::new(sender_id, tm.to_vec()?))
816                        .map_err(|e| EcssTmtcError::Send(e.into()))?,
817                };
818                Ok(())
819            }
820        }
821
822        pub type CrossbeamTcReceiver = cb::Receiver<EcssTcAndToken>;
823    }
824
825    #[derive(Debug, Clone, PartialEq, Eq)]
826    pub struct ActivePusRequestStd {
827        target_id: ComponentId,
828        token: TcStateToken,
829        start_time: std::time::Instant,
830        timeout: Duration,
831    }
832
833    impl ActivePusRequestStd {
834        pub fn new(
835            target_id: ComponentId,
836            token: impl Into<TcStateToken>,
837            timeout: Duration,
838        ) -> Self {
839            Self {
840                target_id,
841                token: token.into(),
842                start_time: std::time::Instant::now(),
843                timeout,
844            }
845        }
846    }
847
848    impl ActiveRequestProvider for ActivePusRequestStd {
849        fn target_id(&self) -> ComponentId {
850            self.target_id
851        }
852
853        fn token(&self) -> TcStateToken {
854            self.token
855        }
856
857        fn timeout(&self) -> Duration {
858            self.timeout
859        }
860        fn set_token(&mut self, token: TcStateToken) {
861            self.token = token;
862        }
863
864        fn has_timed_out(&self) -> bool {
865            std::time::Instant::now() - self.start_time > self.timeout
866        }
867    }
868
869    // TODO: All these types could probably be no_std if we implemented error handling ourselves..
870    // but thiserror is really nice, so keep it like this for simplicity for now. Maybe thiserror
871    // will be no_std soon, see https://github.com/rust-lang/rust/issues/103765 .
872
873    #[derive(Debug, Clone, Error)]
874    pub enum PusTcFromMemError {
875        #[error("generic PUS error: {0}")]
876        EcssTmtc(#[from] EcssTmtcError),
877        #[error("invalid format of TC in memory: {0:?}")]
878        InvalidFormat(TcInMemory),
879    }
880
881    #[derive(Debug, Clone, Error)]
882    pub enum GenericRoutingError {
883        // #[error("not enough application data, expected at least {expected}, found {found}")]
884        // NotEnoughAppData { expected: usize, found: usize },
885        #[error("Unknown target ID {0}")]
886        UnknownTargetId(ComponentId),
887        #[error("Sending action request failed: {0}")]
888        Send(GenericSendError),
889    }
890
891    /// This error can be used for generic conversions from PUS Telecommands to request types.
892    ///
893    /// Please note that this error can also be used if no request is generated and the PUS
894    /// service, subservice and application data is used directly to perform some request.
895    #[derive(Debug, Clone, Error)]
896    pub enum GenericConversionError {
897        #[error("wrong service number {0} for packet handler")]
898        WrongService(u8),
899        #[error("invalid subservice {0}")]
900        InvalidSubservice(u8),
901        #[error("not enough application data, expected at least {expected}, found {found}")]
902        NotEnoughAppData { expected: usize, found: usize },
903        #[error("invalid application data")]
904        InvalidAppData(String),
905    }
906
907    /// Wrapper type which tries to encapsulate all possible errors when handling PUS packets.
908    #[derive(Debug, Clone, Error)]
909    pub enum PusPacketHandlingError {
910        #[error("error polling PUS TC packet: {0}")]
911        TcPolling(#[from] EcssTmtcError),
912        #[error("error generating PUS reader from memory: {0}")]
913        TcFromMem(#[from] PusTcFromMemError),
914        #[error("generic request conversion error: {0}")]
915        RequestConversion(#[from] GenericConversionError),
916        #[error("request routing error: {0}")]
917        RequestRouting(#[from] GenericRoutingError),
918        #[error("invalid verification token")]
919        InvalidVerificationToken,
920        #[error("other error {0}")]
921        Other(String),
922    }
923
924    #[derive(Debug, Clone, Error)]
925    pub enum PartialPusHandlingError {
926        #[error("generic timestamp generation error")]
927        Time(#[from] StdTimestampError),
928        #[error("error sending telemetry: {0}")]
929        TmSend(EcssTmtcError),
930        #[error("error sending verification message")]
931        Verification(EcssTmtcError),
932        #[error("invalid verification token")]
933        NoVerificationToken,
934    }
935
936    /// Generic result type for handlers which can process PUS packets.
937    #[derive(Debug, Clone)]
938    pub enum DirectPusPacketHandlerResult {
939        Handled(HandlingStatus),
940        SubserviceNotImplemented(u8, VerificationToken<TcStateAccepted>),
941        CustomSubservice(u8, VerificationToken<TcStateAccepted>),
942    }
943
944    impl From<HandlingStatus> for DirectPusPacketHandlerResult {
945        fn from(value: HandlingStatus) -> Self {
946            Self::Handled(value)
947        }
948    }
949
950    pub trait EcssTcInMemConverter {
951        fn cache(&mut self, possible_packet: &TcInMemory) -> Result<(), PusTcFromMemError>;
952
953        fn tc_slice_raw(&self) -> &[u8];
954
955        fn sender_id(&self) -> Option<ComponentId>;
956
957        fn cache_and_convert(
958            &mut self,
959            possible_packet: &TcInMemory,
960        ) -> Result<PusTcReader<'_>, PusTcFromMemError> {
961            self.cache(possible_packet)?;
962            Ok(PusTcReader::new(self.tc_slice_raw())
963                .map_err(EcssTmtcError::Pus)?
964                .0)
965        }
966
967        fn convert(&self) -> Result<PusTcReader<'_>, PusTcFromMemError> {
968            Ok(PusTcReader::new(self.tc_slice_raw())
969                .map_err(EcssTmtcError::Pus)?
970                .0)
971        }
972    }
973
974    /// Converter structure for PUS telecommands which are stored inside a `Vec<u8>` structure.
975    /// Please note that this structure is not able to convert TCs which are stored inside a
976    /// [SharedStaticMemoryPool].
977    #[derive(Default, Clone)]
978    pub struct EcssTcInVecConverter {
979        sender_id: Option<ComponentId>,
980        pub pus_tc_raw: Option<Vec<u8>>,
981    }
982
983    impl EcssTcInMemConverter for EcssTcInVecConverter {
984        fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> {
985            self.pus_tc_raw = None;
986            match tc_in_memory {
987                super::TcInMemory::Pool(_packet_in_pool) => {
988                    return Err(PusTcFromMemError::InvalidFormat(tc_in_memory.clone()));
989                }
990                super::TcInMemory::Vec(packet_with_sender) => {
991                    self.pus_tc_raw = Some(packet_with_sender.packet.clone());
992                    self.sender_id = Some(packet_with_sender.sender_id);
993                }
994            };
995            Ok(())
996        }
997
998        fn sender_id(&self) -> Option<ComponentId> {
999            self.sender_id
1000        }
1001
1002        fn tc_slice_raw(&self) -> &[u8] {
1003            if self.pus_tc_raw.is_none() {
1004                return &[];
1005            }
1006            self.pus_tc_raw.as_ref().unwrap()
1007        }
1008    }
1009
1010    /// Converter structure for PUS telecommands which are stored inside
1011    /// [SharedStaticMemoryPool] structure. This is useful if run-time allocation for these
1012    /// packets should be avoided. Please note that this structure is not able to convert TCs which
1013    /// are stored as a `Vec<u8>`.
1014    pub struct EcssTcInSharedStoreConverter {
1015        sender_id: Option<ComponentId>,
1016        shared_tc_store: SharedStaticMemoryPool,
1017        pus_buf: Vec<u8>,
1018    }
1019
1020    impl EcssTcInSharedStoreConverter {
1021        pub fn new(shared_tc_store: SharedStaticMemoryPool, max_expected_tc_size: usize) -> Self {
1022            Self {
1023                sender_id: None,
1024                shared_tc_store,
1025                pus_buf: alloc::vec![0; max_expected_tc_size],
1026            }
1027        }
1028
1029        pub fn copy_tc_to_buf(&mut self, addr: PoolAddr) -> Result<(), PusTcFromMemError> {
1030            // Keep locked section as short as possible.
1031            let mut tc_pool = self.shared_tc_store.write().map_err(|_| {
1032                PusTcFromMemError::EcssTmtc(EcssTmtcError::Store(PoolError::LockError))
1033            })?;
1034            let tc_size = tc_pool.len_of_data(&addr).map_err(EcssTmtcError::Store)?;
1035            if tc_size > self.pus_buf.len() {
1036                return Err(
1037                    EcssTmtcError::ByteConversion(ByteConversionError::ToSliceTooSmall {
1038                        found: self.pus_buf.len(),
1039                        expected: tc_size,
1040                    })
1041                    .into(),
1042                );
1043            }
1044            let tc_guard = tc_pool.read_with_guard(addr);
1045            // TODO: Proper error handling.
1046            tc_guard.read(&mut self.pus_buf[0..tc_size]).unwrap();
1047            Ok(())
1048        }
1049    }
1050
1051    impl EcssTcInMemConverter for EcssTcInSharedStoreConverter {
1052        fn cache(&mut self, tc_in_memory: &TcInMemory) -> Result<(), PusTcFromMemError> {
1053            match tc_in_memory {
1054                super::TcInMemory::Pool(packet_in_pool) => {
1055                    self.copy_tc_to_buf(packet_in_pool.store_addr)?;
1056                    self.sender_id = Some(packet_in_pool.sender_id);
1057                }
1058                super::TcInMemory::Vec(_) => {
1059                    return Err(PusTcFromMemError::InvalidFormat(tc_in_memory.clone()));
1060                }
1061            };
1062            Ok(())
1063        }
1064
1065        fn tc_slice_raw(&self) -> &[u8] {
1066            self.pus_buf.as_ref()
1067        }
1068
1069        fn sender_id(&self) -> Option<ComponentId> {
1070            self.sender_id
1071        }
1072    }
1073
1074    pub struct PusServiceBase<
1075        TcReceiver: EcssTcReceiver,
1076        TmSender: EcssTmSender,
1077        VerificationReporter: VerificationReportingProvider,
1078    > {
1079        pub id: ComponentId,
1080        pub tc_receiver: TcReceiver,
1081        pub tm_sender: TmSender,
1082        pub verif_reporter: VerificationReporter,
1083    }
1084
1085    /// This is a high-level PUS packet handler helper.
1086    ///
1087    /// It performs some of the boilerplate acitivities involved when handling PUS telecommands and
1088    /// it can be used to implement the handling of PUS telecommands for certain PUS telecommands
1089    /// groups (for example individual services).
1090    ///
1091    /// This base class can handle PUS telecommands backed by different memory storage machanisms
1092    /// by using the [EcssTcInMemConverter] abstraction. This object provides some convenience
1093    /// methods to make the generic parts of TC handling easier.
1094    pub struct PusServiceHelper<
1095        TcReceiver: EcssTcReceiver,
1096        TmSender: EcssTmSender,
1097        TcInMemConverter: EcssTcInMemConverter,
1098        VerificationReporter: VerificationReportingProvider,
1099    > {
1100        pub common: PusServiceBase<TcReceiver, TmSender, VerificationReporter>,
1101        pub tc_in_mem_converter: TcInMemConverter,
1102    }
1103
1104    impl<
1105            TcReceiver: EcssTcReceiver,
1106            TmSender: EcssTmSender,
1107            TcInMemConverter: EcssTcInMemConverter,
1108            VerificationReporter: VerificationReportingProvider,
1109        > PusServiceHelper<TcReceiver, TmSender, TcInMemConverter, VerificationReporter>
1110    {
1111        pub fn new(
1112            id: ComponentId,
1113            tc_receiver: TcReceiver,
1114            tm_sender: TmSender,
1115            verification_handler: VerificationReporter,
1116            tc_in_mem_converter: TcInMemConverter,
1117        ) -> Self {
1118            Self {
1119                common: PusServiceBase {
1120                    id,
1121                    tc_receiver,
1122                    tm_sender,
1123                    verif_reporter: verification_handler,
1124                },
1125                tc_in_mem_converter,
1126            }
1127        }
1128
1129        pub fn id(&self) -> ComponentId {
1130            self.common.id
1131        }
1132
1133        pub fn tm_sender(&self) -> &TmSender {
1134            &self.common.tm_sender
1135        }
1136
1137        /// This function can be used to poll the internal [EcssTcReceiver] object for the next
1138        /// telecommand packet. It will return `Ok(None)` if there are not packets available.
1139        /// In any other case, it will perform the acceptance of the ECSS TC packet using the
1140        /// internal [VerificationReportingProvider] object. It will then return the telecommand
1141        /// and the according accepted token.
1142        pub fn retrieve_and_accept_next_packet(
1143            &mut self,
1144        ) -> Result<Option<AcceptedEcssTcAndToken>, PusPacketHandlingError> {
1145            match self.common.tc_receiver.recv_tc() {
1146                Ok(EcssTcAndToken {
1147                    tc_in_memory,
1148                    token,
1149                }) => {
1150                    if token.is_none() {
1151                        return Err(PusPacketHandlingError::InvalidVerificationToken);
1152                    }
1153                    let token = token.unwrap();
1154                    let accepted_token = VerificationToken::<TcStateAccepted>::try_from(token)
1155                        .map_err(|_| PusPacketHandlingError::InvalidVerificationToken)?;
1156                    Ok(Some(AcceptedEcssTcAndToken {
1157                        tc_in_memory,
1158                        token: accepted_token,
1159                    }))
1160                }
1161                Err(e) => match e {
1162                    TryRecvTmtcError::Tmtc(e) => Err(PusPacketHandlingError::TcPolling(e)),
1163                    TryRecvTmtcError::Empty => Ok(None),
1164                },
1165            }
1166        }
1167
1168        pub fn verif_reporter(&self) -> &VerificationReporter {
1169            &self.common.verif_reporter
1170        }
1171        pub fn verif_reporter_mut(&mut self) -> &mut VerificationReporter {
1172            &mut self.common.verif_reporter
1173        }
1174
1175        pub fn tc_in_mem_converter(&self) -> &TcInMemConverter {
1176            &self.tc_in_mem_converter
1177        }
1178
1179        pub fn tc_in_mem_converter_mut(&mut self) -> &mut TcInMemConverter {
1180            &mut self.tc_in_mem_converter
1181        }
1182    }
1183
1184    pub type PusServiceHelperDynWithMpsc<TcInMemConverter, VerificationReporter> =
1185        PusServiceHelper<MpscTcReceiver, MpscTmAsVecSender, TcInMemConverter, VerificationReporter>;
1186    pub type PusServiceHelperDynWithBoundedMpsc<TcInMemConverter, VerificationReporter> =
1187        PusServiceHelper<
1188            MpscTcReceiver,
1189            MpscTmAsVecSenderBounded,
1190            TcInMemConverter,
1191            VerificationReporter,
1192        >;
1193    pub type PusServiceHelperStaticWithMpsc<TcInMemConverter, VerificationReporter> =
1194        PusServiceHelper<
1195            MpscTcReceiver,
1196            PacketSenderWithSharedPool,
1197            TcInMemConverter,
1198            VerificationReporter,
1199        >;
1200    pub type PusServiceHelperStaticWithBoundedMpsc<TcInMemConverter, VerificationReporter> =
1201        PusServiceHelper<
1202            MpscTcReceiver,
1203            PacketSenderWithSharedPool,
1204            TcInMemConverter,
1205            VerificationReporter,
1206        >;
1207}
1208
1209pub(crate) fn source_buffer_large_enough(
1210    cap: usize,
1211    len: usize,
1212) -> Result<(), ByteConversionError> {
1213    if len > cap {
1214        return Err(ByteConversionError::ToSliceTooSmall {
1215            found: cap,
1216            expected: len,
1217        });
1218    }
1219    Ok(())
1220}
1221
1222#[cfg(any(feature = "test_util", test))]
1223pub mod test_util {
1224    use crate::request::UniqueApidTargetId;
1225    use spacepackets::ecss::{tc::PusTcCreator, tm::PusTmReader};
1226
1227    use super::{
1228        verification::{self, TcStateAccepted, VerificationToken},
1229        DirectPusPacketHandlerResult, PusPacketHandlingError,
1230    };
1231
1232    pub const TEST_APID: u16 = 0x101;
1233    pub const TEST_UNIQUE_ID_0: u32 = 0x05;
1234    pub const TEST_UNIQUE_ID_1: u32 = 0x06;
1235    pub const TEST_COMPONENT_ID_0: UniqueApidTargetId =
1236        UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_0);
1237    pub const TEST_COMPONENT_ID_1: UniqueApidTargetId =
1238        UniqueApidTargetId::new(TEST_APID, TEST_UNIQUE_ID_1);
1239
1240    pub trait PusTestHarness {
1241        fn init_verification(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
1242        fn send_tc(&self, token: &VerificationToken<TcStateAccepted>, tc: &PusTcCreator);
1243        fn read_next_tm(&mut self) -> PusTmReader<'_>;
1244        fn check_no_tm_available(&self) -> bool;
1245        fn check_next_verification_tm(
1246            &self,
1247            subservice: u8,
1248            expected_request_id: verification::RequestId,
1249        );
1250    }
1251
1252    pub trait SimplePusPacketHandler {
1253        fn handle_one_tc(&mut self)
1254            -> Result<DirectPusPacketHandlerResult, PusPacketHandlingError>;
1255    }
1256}
1257
1258#[cfg(test)]
1259pub mod tests {
1260    use core::cell::RefCell;
1261    use std::sync::mpsc::TryRecvError;
1262    use std::sync::{mpsc, RwLock};
1263
1264    use alloc::collections::VecDeque;
1265    use alloc::vec::Vec;
1266    use satrs_shared::res_code::ResultU16;
1267    use spacepackets::ecss::tc::{PusTcCreator, PusTcReader};
1268    use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator, PusTmReader};
1269    use spacepackets::ecss::{PusPacket, WritablePusPacket};
1270    use spacepackets::CcsdsPacket;
1271
1272    use crate::pool::{PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig};
1273    use crate::pus::verification::{RequestId, VerificationReporter};
1274    use crate::tmtc::{PacketAsVec, PacketInPool, PacketSenderWithSharedPool, SharedPacketPool};
1275    use crate::ComponentId;
1276
1277    use super::test_util::{TEST_APID, TEST_COMPONENT_ID_0};
1278
1279    use super::verification::test_util::TestVerificationReporter;
1280    use super::verification::{
1281        TcStateAccepted, VerificationReporterCfg, VerificationReportingProvider, VerificationToken,
1282    };
1283    use super::*;
1284
1285    #[derive(Debug, Eq, PartialEq, Clone)]
1286    pub(crate) struct CommonTmInfo {
1287        pub subservice: u8,
1288        pub apid: u16,
1289        pub seq_count: u16,
1290        pub msg_counter: u16,
1291        pub dest_id: u16,
1292        pub timestamp: Vec<u8>,
1293    }
1294
1295    impl CommonTmInfo {
1296        pub fn new(
1297            subservice: u8,
1298            apid: u16,
1299            seq_count: u16,
1300            msg_counter: u16,
1301            dest_id: u16,
1302            timestamp: &[u8],
1303        ) -> Self {
1304            Self {
1305                subservice,
1306                apid,
1307                seq_count,
1308                msg_counter,
1309                dest_id,
1310                timestamp: timestamp.to_vec(),
1311            }
1312        }
1313        pub fn new_zero_seq_count(
1314            subservice: u8,
1315            apid: u16,
1316            dest_id: u16,
1317            timestamp: &[u8],
1318        ) -> Self {
1319            Self::new(subservice, apid, 0, 0, dest_id, timestamp)
1320        }
1321
1322        pub fn new_from_tm(tm: &PusTmCreator) -> Self {
1323            let mut timestamp = [0; 7];
1324            timestamp.clone_from_slice(&tm.timestamp()[0..7]);
1325            Self {
1326                subservice: PusPacket::subservice(tm),
1327                apid: tm.apid(),
1328                seq_count: tm.seq_count(),
1329                msg_counter: tm.msg_counter(),
1330                dest_id: tm.dest_id(),
1331                timestamp: timestamp.to_vec(),
1332            }
1333        }
1334    }
1335
1336    /// Common fields for a PUS service test harness.
1337    pub struct PusServiceHandlerWithSharedStoreCommon {
1338        pus_buf: RefCell<[u8; 2048]>,
1339        tm_buf: [u8; 2048],
1340        tc_pool: SharedStaticMemoryPool,
1341        tm_pool: SharedPacketPool,
1342        tc_sender: mpsc::SyncSender<EcssTcAndToken>,
1343        tm_receiver: mpsc::Receiver<PacketInPool>,
1344    }
1345
1346    pub type PusServiceHelperStatic = PusServiceHelper<
1347        MpscTcReceiver,
1348        PacketSenderWithSharedPool,
1349        EcssTcInSharedStoreConverter,
1350        VerificationReporter,
1351    >;
1352
1353    impl PusServiceHandlerWithSharedStoreCommon {
1354        /// This function generates the structure in addition to the PUS service handler
1355        /// [PusServiceHandler] which might be required for a specific PUS service handler.
1356        ///
1357        /// The PUS service handler is instantiated with a [EcssTcInStoreConverter].
1358        pub fn new(id: ComponentId) -> (Self, PusServiceHelperStatic) {
1359            let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)], false);
1360            let tc_pool = StaticMemoryPool::new(pool_cfg.clone());
1361            let tm_pool = StaticMemoryPool::new(pool_cfg);
1362            let shared_tc_pool = SharedStaticMemoryPool::new(RwLock::new(tc_pool));
1363            let shared_tm_pool = SharedStaticMemoryPool::new(RwLock::new(tm_pool));
1364            let shared_tm_pool_wrapper = SharedPacketPool::new(&shared_tm_pool);
1365            let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::sync_channel(10);
1366            let (tm_tx, tm_rx) = mpsc::sync_channel(10);
1367
1368            let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
1369            let verification_handler =
1370                VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
1371            let test_srv_tm_sender =
1372                PacketSenderWithSharedPool::new(tm_tx, shared_tm_pool_wrapper.clone());
1373            let in_store_converter =
1374                EcssTcInSharedStoreConverter::new(shared_tc_pool.clone(), 2048);
1375            (
1376                Self {
1377                    pus_buf: RefCell::new([0; 2048]),
1378                    tm_buf: [0; 2048],
1379                    tc_pool: shared_tc_pool,
1380                    tm_pool: shared_tm_pool_wrapper,
1381                    tc_sender: test_srv_tc_tx,
1382                    tm_receiver: tm_rx,
1383                },
1384                PusServiceHelper::new(
1385                    id,
1386                    test_srv_tc_rx,
1387                    test_srv_tm_sender,
1388                    verification_handler,
1389                    in_store_converter,
1390                ),
1391            )
1392        }
1393        pub fn send_tc(
1394            &self,
1395            sender_id: ComponentId,
1396            token: &VerificationToken<TcStateAccepted>,
1397            tc: &PusTcCreator,
1398        ) {
1399            let mut mut_buf = self.pus_buf.borrow_mut();
1400            let tc_size = tc.write_to_bytes(mut_buf.as_mut_slice()).unwrap();
1401            let mut tc_pool = self.tc_pool.write().unwrap();
1402            let addr = tc_pool.add(&mut_buf[..tc_size]).unwrap();
1403            drop(tc_pool);
1404            // Send accepted TC to test service handler.
1405            self.tc_sender
1406                .send(EcssTcAndToken::new(
1407                    PacketInPool::new(sender_id, addr),
1408                    *token,
1409                ))
1410                .expect("sending tc failed");
1411        }
1412
1413        pub fn read_next_tm(&mut self) -> PusTmReader<'_> {
1414            let next_msg = self.tm_receiver.try_recv();
1415            assert!(next_msg.is_ok());
1416            let tm_in_pool = next_msg.unwrap();
1417            let tm_pool = self.tm_pool.0.read().unwrap();
1418            let tm_raw = tm_pool.read_as_vec(&tm_in_pool.store_addr).unwrap();
1419            self.tm_buf[0..tm_raw.len()].copy_from_slice(&tm_raw);
1420            PusTmReader::new(&self.tm_buf, 7).unwrap().0
1421        }
1422
1423        pub fn check_no_tm_available(&self) -> bool {
1424            let next_msg = self.tm_receiver.try_recv();
1425            if let TryRecvError::Empty = next_msg.unwrap_err() {
1426                return true;
1427            }
1428            false
1429        }
1430
1431        pub fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId) {
1432            let next_msg = self.tm_receiver.try_recv();
1433            assert!(next_msg.is_ok());
1434            let tm_in_pool = next_msg.unwrap();
1435            let tm_pool = self.tm_pool.0.read().unwrap();
1436            let tm_raw = tm_pool.read_as_vec(&tm_in_pool.store_addr).unwrap();
1437            let tm = PusTmReader::new(&tm_raw, 7).unwrap().0;
1438            assert_eq!(PusPacket::service(&tm), 1);
1439            assert_eq!(PusPacket::subservice(&tm), subservice);
1440            assert_eq!(tm.apid(), TEST_APID);
1441            let req_id =
1442                RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
1443            assert_eq!(req_id, expected_request_id);
1444        }
1445    }
1446
1447    pub struct PusServiceHandlerWithVecCommon {
1448        current_tm: Option<Vec<u8>>,
1449        tc_sender: mpsc::Sender<EcssTcAndToken>,
1450        tm_receiver: mpsc::Receiver<PacketAsVec>,
1451    }
1452    pub type PusServiceHelperDynamic = PusServiceHelper<
1453        MpscTcReceiver,
1454        MpscTmAsVecSender,
1455        EcssTcInVecConverter,
1456        VerificationReporter,
1457    >;
1458
1459    impl PusServiceHandlerWithVecCommon {
1460        pub fn new_with_standard_verif_reporter(
1461            id: ComponentId,
1462        ) -> (Self, PusServiceHelperDynamic) {
1463            let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
1464            let (tm_tx, tm_rx) = mpsc::channel();
1465
1466            let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
1467            let verification_handler =
1468                VerificationReporter::new(TEST_COMPONENT_ID_0.id(), &verif_cfg);
1469            let in_store_converter = EcssTcInVecConverter::default();
1470            (
1471                Self {
1472                    current_tm: None,
1473                    tc_sender: test_srv_tc_tx,
1474                    tm_receiver: tm_rx,
1475                },
1476                PusServiceHelper::new(
1477                    id,
1478                    test_srv_tc_rx,
1479                    tm_tx,
1480                    verification_handler,
1481                    in_store_converter,
1482                ),
1483            )
1484        }
1485    }
1486
1487    impl PusServiceHandlerWithVecCommon {
1488        pub fn new_with_test_verif_sender(
1489            id: ComponentId,
1490        ) -> (
1491            Self,
1492            PusServiceHelper<
1493                MpscTcReceiver,
1494                MpscTmAsVecSender,
1495                EcssTcInVecConverter,
1496                TestVerificationReporter,
1497            >,
1498        ) {
1499            let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
1500            let (tm_tx, tm_rx) = mpsc::channel();
1501
1502            let in_store_converter = EcssTcInVecConverter::default();
1503            let verification_handler = TestVerificationReporter::new(id);
1504            (
1505                Self {
1506                    current_tm: None,
1507                    tc_sender: test_srv_tc_tx,
1508                    tm_receiver: tm_rx,
1509                    //verification_handler: verification_handler.clone(),
1510                },
1511                PusServiceHelper::new(
1512                    id,
1513                    test_srv_tc_rx,
1514                    tm_tx,
1515                    verification_handler,
1516                    in_store_converter,
1517                ),
1518            )
1519        }
1520    }
1521
1522    impl PusServiceHandlerWithVecCommon {
1523        pub fn send_tc(
1524            &self,
1525            sender_id: ComponentId,
1526            token: &VerificationToken<TcStateAccepted>,
1527            tc: &PusTcCreator,
1528        ) {
1529            // Send accepted TC to test service handler.
1530            self.tc_sender
1531                .send(EcssTcAndToken::new(
1532                    TcInMemory::Vec(PacketAsVec::new(
1533                        sender_id,
1534                        tc.to_vec().expect("pus tc conversion to vec failed"),
1535                    )),
1536                    *token,
1537                ))
1538                .expect("sending tc failed");
1539        }
1540
1541        pub fn read_next_tm(&mut self) -> PusTmReader<'_> {
1542            let next_msg = self.tm_receiver.try_recv();
1543            assert!(next_msg.is_ok());
1544            self.current_tm = Some(next_msg.unwrap().packet);
1545            PusTmReader::new(self.current_tm.as_ref().unwrap(), 7)
1546                .unwrap()
1547                .0
1548        }
1549
1550        pub fn check_no_tm_available(&self) -> bool {
1551            let next_msg = self.tm_receiver.try_recv();
1552            if let TryRecvError::Empty = next_msg.unwrap_err() {
1553                return true;
1554            }
1555            false
1556        }
1557
1558        pub fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId) {
1559            let next_msg = self.tm_receiver.try_recv();
1560            assert!(next_msg.is_ok());
1561            let next_msg = next_msg.unwrap();
1562            let tm = PusTmReader::new(next_msg.packet.as_slice(), 7).unwrap().0;
1563            assert_eq!(PusPacket::service(&tm), 1);
1564            assert_eq!(PusPacket::subservice(&tm), subservice);
1565            assert_eq!(tm.apid(), TEST_APID);
1566            let req_id =
1567                RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
1568            assert_eq!(req_id, expected_request_id);
1569        }
1570    }
1571
1572    pub const APP_DATA_TOO_SHORT: ResultU16 = ResultU16::new(1, 1);
1573
1574    #[derive(Default)]
1575    pub struct TestConverter<const SERVICE: u8> {
1576        pub conversion_request: VecDeque<Vec<u8>>,
1577    }
1578
1579    impl<const SERVICE: u8> TestConverter<SERVICE> {
1580        pub fn check_service(&self, tc: &PusTcReader) -> Result<(), PusPacketHandlingError> {
1581            if tc.service() != SERVICE {
1582                return Err(PusPacketHandlingError::RequestConversion(
1583                    GenericConversionError::WrongService(tc.service()),
1584                ));
1585            }
1586            Ok(())
1587        }
1588
1589        pub fn is_empty(&self) {
1590            self.conversion_request.is_empty();
1591        }
1592
1593        pub fn check_next_conversion(&mut self, tc: &PusTcCreator) {
1594            assert!(!self.conversion_request.is_empty());
1595            assert_eq!(
1596                self.conversion_request.pop_front().unwrap(),
1597                tc.to_vec().unwrap()
1598            );
1599        }
1600    }
1601
1602    pub struct TestRouter<REQUEST> {
1603        pub routing_requests: RefCell<VecDeque<(ComponentId, REQUEST)>>,
1604        pub routing_errors: RefCell<VecDeque<(ComponentId, GenericRoutingError)>>,
1605        pub injected_routing_failure: RefCell<Option<GenericRoutingError>>,
1606    }
1607
1608    impl<REQUEST> Default for TestRouter<REQUEST> {
1609        fn default() -> Self {
1610            Self {
1611                routing_requests: Default::default(),
1612                routing_errors: Default::default(),
1613                injected_routing_failure: Default::default(),
1614            }
1615        }
1616    }
1617
1618    impl<REQUEST> TestRouter<REQUEST> {
1619        pub fn check_for_injected_error(&self) -> Result<(), GenericRoutingError> {
1620            if self.injected_routing_failure.borrow().is_some() {
1621                return Err(self.injected_routing_failure.borrow_mut().take().unwrap());
1622            }
1623            Ok(())
1624        }
1625
1626        pub fn handle_error(
1627            &self,
1628            target_id: ComponentId,
1629            _token: VerificationToken<TcStateAccepted>,
1630            _tc: &PusTcReader,
1631            error: GenericRoutingError,
1632            _time_stamp: &[u8],
1633            _verif_reporter: &impl VerificationReportingProvider,
1634        ) {
1635            self.routing_errors
1636                .borrow_mut()
1637                .push_back((target_id, error));
1638        }
1639
1640        pub fn no_routing_errors(&self) -> bool {
1641            self.routing_errors.borrow().is_empty()
1642        }
1643
1644        pub fn retrieve_next_routing_error(&mut self) -> (ComponentId, GenericRoutingError) {
1645            if self.routing_errors.borrow().is_empty() {
1646                panic!("no routing request available");
1647            }
1648            self.routing_errors.borrow_mut().pop_front().unwrap()
1649        }
1650
1651        pub fn inject_routing_error(&mut self, error: GenericRoutingError) {
1652            *self.injected_routing_failure.borrow_mut() = Some(error);
1653        }
1654
1655        pub fn is_empty(&self) -> bool {
1656            self.routing_requests.borrow().is_empty()
1657        }
1658
1659        pub fn retrieve_next_request(&mut self) -> (ComponentId, REQUEST) {
1660            if self.routing_requests.borrow().is_empty() {
1661                panic!("no routing request available");
1662            }
1663            self.routing_requests.borrow_mut().pop_front().unwrap()
1664        }
1665    }
1666}