satrs_core/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::ChannelId;
6use core::fmt::{Display, Formatter};
7#[cfg(feature = "alloc")]
8use downcast_rs::{impl_downcast, Downcast};
9#[cfg(feature = "alloc")]
10use dyn_clone::DynClone;
11#[cfg(feature = "std")]
12use std::error::Error;
13
14use spacepackets::ecss::tc::{PusTcCreator, PusTcReader};
15use spacepackets::ecss::tm::PusTmCreator;
16use spacepackets::ecss::PusError;
17use spacepackets::{ByteConversionError, SpHeader};
18
19pub mod event;
20pub mod event_man;
21#[cfg(feature = "std")]
22pub mod event_srv;
23pub mod hk;
24pub mod mode;
25pub mod scheduler;
26#[cfg(feature = "std")]
27pub mod scheduler_srv;
28#[cfg(feature = "std")]
29pub mod test;
30pub mod verification;
31
32#[cfg(feature = "alloc")]
33pub use alloc_mod::*;
34
35use crate::pool::{StoreAddr, StoreError};
36use crate::pus::verification::{TcStateAccepted, TcStateToken, VerificationToken};
37#[cfg(feature = "std")]
38pub use std_mod::*;
39
40#[derive(Debug, PartialEq, Eq, Clone)]
41pub enum PusTmWrapper<'tm> {
42    InStore(StoreAddr),
43    Direct(PusTmCreator<'tm>),
44}
45
46impl From<StoreAddr> for PusTmWrapper<'_> {
47    fn from(value: StoreAddr) -> Self {
48        Self::InStore(value)
49    }
50}
51
52impl<'tm> From<PusTmCreator<'tm>> for PusTmWrapper<'tm> {
53    fn from(value: PusTmCreator<'tm>) -> Self {
54        Self::Direct(value)
55    }
56}
57
58/// Generic error type for sending something via a message queue.
59#[derive(Debug, Copy, Clone)]
60pub enum GenericSendError {
61    RxDisconnected,
62    QueueFull(Option<u32>),
63}
64
65impl Display for GenericSendError {
66    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
67        match self {
68            GenericSendError::RxDisconnected => {
69                write!(f, "rx side has disconnected")
70            }
71            GenericSendError::QueueFull(max_cap) => {
72                write!(f, "queue with max capacity of {max_cap:?} is full")
73            }
74        }
75    }
76}
77
78#[cfg(feature = "std")]
79impl Error for GenericSendError {}
80
81/// Generic error type for sending something via a message queue.
82#[derive(Debug, Copy, Clone)]
83pub enum GenericRecvError {
84    Empty,
85    TxDisconnected,
86}
87
88impl Display for GenericRecvError {
89    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
90        match self {
91            Self::TxDisconnected => {
92                write!(f, "tx side has disconnected")
93            }
94            Self::Empty => {
95                write!(f, "nothing to receive")
96            }
97        }
98    }
99}
100
101#[cfg(feature = "std")]
102impl Error for GenericRecvError {}
103
104#[derive(Debug, Clone)]
105pub enum EcssTmtcError {
106    StoreLock,
107    Store(StoreError),
108    Pus(PusError),
109    CantSendAddr(StoreAddr),
110    Send(GenericSendError),
111    Recv(GenericRecvError),
112}
113
114impl Display for EcssTmtcError {
115    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
116        match self {
117            EcssTmtcError::StoreLock => {
118                write!(f, "store lock error")
119            }
120            EcssTmtcError::Store(store) => {
121                write!(f, "store error: {store}")
122            }
123            EcssTmtcError::Pus(pus_e) => {
124                write!(f, "PUS error: {pus_e}")
125            }
126            EcssTmtcError::CantSendAddr(addr) => {
127                write!(f, "can not send address {addr}")
128            }
129            EcssTmtcError::Send(send_e) => {
130                write!(f, "send error {send_e}")
131            }
132            EcssTmtcError::Recv(recv_e) => {
133                write!(f, "recv error {recv_e}")
134            }
135        }
136    }
137}
138
139impl From<StoreError> for EcssTmtcError {
140    fn from(value: StoreError) -> Self {
141        Self::Store(value)
142    }
143}
144
145impl From<PusError> for EcssTmtcError {
146    fn from(value: PusError) -> Self {
147        Self::Pus(value)
148    }
149}
150
151impl From<GenericSendError> for EcssTmtcError {
152    fn from(value: GenericSendError) -> Self {
153        Self::Send(value)
154    }
155}
156
157impl From<GenericRecvError> for EcssTmtcError {
158    fn from(value: GenericRecvError) -> Self {
159        Self::Recv(value)
160    }
161}
162
163#[cfg(feature = "std")]
164impl Error for EcssTmtcError {
165    fn source(&self) -> Option<&(dyn Error + 'static)> {
166        match self {
167            EcssTmtcError::Store(e) => Some(e),
168            EcssTmtcError::Pus(e) => Some(e),
169            EcssTmtcError::Send(e) => Some(e),
170            _ => None,
171        }
172    }
173}
174pub trait EcssChannel: Send {
175    /// Each sender can have an ID associated with it
176    fn id(&self) -> ChannelId;
177    fn name(&self) -> &'static str {
178        "unset"
179    }
180}
181
182/// Generic trait for a user supplied sender object.
183///
184/// This sender object is responsible for sending PUS telemetry to a TM sink.
185pub trait EcssTmSenderCore: EcssChannel {
186    fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError>;
187}
188
189/// Generic trait for a user supplied sender object.
190///
191/// This sender object is responsible for sending PUS telecommands to a TC recipient. Each
192/// telecommand can optionally have a token which contains its verification state.
193pub trait EcssTcSenderCore: EcssChannel {
194    fn send_tc(&self, tc: PusTcCreator, token: Option<TcStateToken>) -> Result<(), EcssTmtcError>;
195}
196
197/// A PUS telecommand packet can be stored in memory using different methods. Right now,
198/// storage inside a pool structure like [crate::pool::StaticMemoryPool], and storage inside a
199/// `Vec<u8>` are supported.
200#[non_exhaustive]
201#[derive(Debug, Clone, PartialEq, Eq)]
202pub enum TcInMemory {
203    StoreAddr(StoreAddr),
204    #[cfg(feature = "alloc")]
205    Vec(alloc::vec::Vec<u8>),
206}
207
208impl From<StoreAddr> for TcInMemory {
209    fn from(value: StoreAddr) -> Self {
210        Self::StoreAddr(value)
211    }
212}
213
214#[cfg(feature = "alloc")]
215impl From<alloc::vec::Vec<u8>> for TcInMemory {
216    fn from(value: alloc::vec::Vec<u8>) -> Self {
217        Self::Vec(value)
218    }
219}
220
221/// Generic structure for an ECSS PUS Telecommand and its correspoding verification token.
222#[derive(Debug, Clone, PartialEq, Eq)]
223pub struct EcssTcAndToken {
224    pub tc_in_memory: TcInMemory,
225    pub token: Option<TcStateToken>,
226}
227
228impl EcssTcAndToken {
229    pub fn new(tc_in_memory: impl Into<TcInMemory>, token: impl Into<TcStateToken>) -> Self {
230        Self {
231            tc_in_memory: tc_in_memory.into(),
232            token: Some(token.into()),
233        }
234    }
235}
236
237/// Generic abstraction for a telecommand being sent around after is has been accepted.
238pub struct AcceptedEcssTcAndToken {
239    pub tc_in_memory: TcInMemory,
240    pub token: VerificationToken<TcStateAccepted>,
241}
242
243impl From<AcceptedEcssTcAndToken> for EcssTcAndToken {
244    fn from(value: AcceptedEcssTcAndToken) -> Self {
245        EcssTcAndToken {
246            tc_in_memory: value.tc_in_memory,
247            token: Some(value.token.into()),
248        }
249    }
250}
251
252impl TryFrom<EcssTcAndToken> for AcceptedEcssTcAndToken {
253    type Error = ();
254
255    fn try_from(value: EcssTcAndToken) -> Result<Self, Self::Error> {
256        if let Some(TcStateToken::Accepted(token)) = value.token {
257            return Ok(AcceptedEcssTcAndToken {
258                tc_in_memory: value.tc_in_memory,
259                token,
260            });
261        }
262        Err(())
263    }
264}
265
266#[derive(Debug, Clone)]
267pub enum TryRecvTmtcError {
268    Error(EcssTmtcError),
269    Empty,
270}
271
272impl From<EcssTmtcError> for TryRecvTmtcError {
273    fn from(value: EcssTmtcError) -> Self {
274        Self::Error(value)
275    }
276}
277
278impl From<PusError> for TryRecvTmtcError {
279    fn from(value: PusError) -> Self {
280        Self::Error(value.into())
281    }
282}
283
284impl From<StoreError> for TryRecvTmtcError {
285    fn from(value: StoreError) -> Self {
286        Self::Error(value.into())
287    }
288}
289
290/// Generic trait for a user supplied receiver object.
291pub trait EcssTcReceiverCore: EcssChannel {
292    fn recv_tc(&self) -> Result<EcssTcAndToken, TryRecvTmtcError>;
293}
294
295/// Generic trait for objects which can receive ECSS PUS telecommands. This trait is
296/// implemented by the [crate::tmtc::pus_distrib::PusDistributor] objects to allow passing PUS TC
297/// packets into it. It is generally assumed that the telecommand is stored in some pool structure,
298/// and the store address is passed as well. This allows efficient zero-copy forwarding of
299/// telecommands.
300pub trait ReceivesEcssPusTc {
301    type Error;
302    fn pass_pus_tc(&mut self, header: &SpHeader, pus_tc: &PusTcReader) -> Result<(), Self::Error>;
303}
304
305#[cfg(feature = "alloc")]
306mod alloc_mod {
307    use super::*;
308
309    /// Extension trait for [EcssTmSenderCore].
310    ///
311    /// It provides additional functionality, for example by implementing the [Downcast] trait
312    /// and the [DynClone] trait.
313    ///
314    /// [Downcast] is implemented to allow passing the sender as a boxed trait object and still
315    /// retrieve the concrete type at a later point.
316    ///
317    /// [DynClone] allows cloning the trait object as long as the boxed object implements
318    /// [Clone].
319    #[cfg(feature = "alloc")]
320    #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
321    pub trait EcssTmSender: EcssTmSenderCore + Downcast + DynClone {
322        // Remove this once trait upcasting coercion has been implemented.
323        // Tracking issue: https://github.com/rust-lang/rust/issues/65991
324        fn upcast(&self) -> &dyn EcssTmSenderCore;
325        // Remove this once trait upcasting coercion has been implemented.
326        // Tracking issue: https://github.com/rust-lang/rust/issues/65991
327        fn upcast_mut(&mut self) -> &mut dyn EcssTmSenderCore;
328    }
329
330    /// Blanket implementation for all types which implement [EcssTmSenderCore] and are clonable.
331    impl<T> EcssTmSender for T
332    where
333        T: EcssTmSenderCore + Clone + 'static,
334    {
335        // Remove this once trait upcasting coercion has been implemented.
336        // Tracking issue: https://github.com/rust-lang/rust/issues/65991
337        fn upcast(&self) -> &dyn EcssTmSenderCore {
338            self
339        }
340        // Remove this once trait upcasting coercion has been implemented.
341        // Tracking issue: https://github.com/rust-lang/rust/issues/65991
342        fn upcast_mut(&mut self) -> &mut dyn EcssTmSenderCore {
343            self
344        }
345    }
346
347    dyn_clone::clone_trait_object!(EcssTmSender);
348    impl_downcast!(EcssTmSender);
349
350    /// Extension trait for [EcssTcSenderCore].
351    ///
352    /// It provides additional functionality, for example by implementing the [Downcast] trait
353    /// and the [DynClone] trait.
354    ///
355    /// [Downcast] is implemented to allow passing the sender as a boxed trait object and still
356    /// retrieve the concrete type at a later point.
357    ///
358    /// [DynClone] allows cloning the trait object as long as the boxed object implements
359    /// [Clone].
360    #[cfg(feature = "alloc")]
361    #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
362    pub trait EcssTcSender: EcssTcSenderCore + Downcast + DynClone {}
363
364    /// Blanket implementation for all types which implement [EcssTcSenderCore] and are clonable.
365    impl<T> EcssTcSender for T where T: EcssTcSenderCore + Clone + 'static {}
366
367    dyn_clone::clone_trait_object!(EcssTcSender);
368    impl_downcast!(EcssTcSender);
369
370    /// Extension trait for [EcssTcReceiverCore].
371    ///
372    /// It provides additional functionality, for example by implementing the [Downcast] trait
373    /// and the [DynClone] trait.
374    ///
375    /// [Downcast] is implemented to allow passing the sender as a boxed trait object and still
376    /// retrieve the concrete type at a later point.
377    ///
378    /// [DynClone] allows cloning the trait object as long as the boxed object implements
379    /// [Clone].
380    #[cfg(feature = "alloc")]
381    #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
382    pub trait EcssTcReceiver: EcssTcReceiverCore + Downcast {}
383
384    /// Blanket implementation for all types which implement [EcssTcReceiverCore] and are clonable.
385    impl<T> EcssTcReceiver for T where T: EcssTcReceiverCore + 'static {}
386
387    impl_downcast!(EcssTcReceiver);
388}
389
390#[cfg(feature = "std")]
391#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
392pub mod std_mod {
393    use crate::pool::{PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool, StoreAddr};
394    use crate::pus::verification::{
395        StdVerifReporterWithSender, TcStateAccepted, VerificationToken,
396    };
397    use crate::pus::{
398        EcssChannel, EcssTcAndToken, EcssTcReceiver, EcssTcReceiverCore, EcssTmSender,
399        EcssTmSenderCore, EcssTmtcError, GenericRecvError, GenericSendError, PusTmWrapper,
400        TryRecvTmtcError,
401    };
402    use crate::tmtc::tm_helper::SharedTmPool;
403    use crate::ChannelId;
404    use alloc::boxed::Box;
405    use alloc::vec::Vec;
406    use crossbeam_channel as cb;
407    use spacepackets::ecss::tc::PusTcReader;
408    use spacepackets::ecss::tm::PusTmCreator;
409    use spacepackets::ecss::PusError;
410    use spacepackets::time::cds::TimeProvider;
411    use spacepackets::time::StdTimestampError;
412    use spacepackets::time::TimeWriter;
413    use std::cell::RefCell;
414    use std::string::String;
415    use std::sync::mpsc;
416    use std::sync::mpsc::TryRecvError;
417    use thiserror::Error;
418
419    use super::verification::VerificationReporterWithSender;
420    use super::{AcceptedEcssTcAndToken, TcInMemory};
421
422    impl From<mpsc::SendError<StoreAddr>> for EcssTmtcError {
423        fn from(_: mpsc::SendError<StoreAddr>) -> Self {
424            Self::Send(GenericSendError::RxDisconnected)
425        }
426    }
427
428    impl From<cb::SendError<StoreAddr>> for EcssTmtcError {
429        fn from(_: cb::SendError<StoreAddr>) -> Self {
430            Self::Send(GenericSendError::RxDisconnected)
431        }
432    }
433
434    impl From<cb::TrySendError<StoreAddr>> for EcssTmtcError {
435        fn from(value: cb::TrySendError<StoreAddr>) -> Self {
436            match value {
437                cb::TrySendError::Full(_) => Self::Send(GenericSendError::QueueFull(None)),
438                cb::TrySendError::Disconnected(_) => Self::Send(GenericSendError::RxDisconnected),
439            }
440        }
441    }
442
443    #[derive(Clone)]
444    pub struct MpscTmInSharedPoolSender {
445        id: ChannelId,
446        name: &'static str,
447        shared_tm_store: SharedTmPool,
448        sender: mpsc::Sender<StoreAddr>,
449    }
450
451    impl EcssChannel for MpscTmInSharedPoolSender {
452        fn id(&self) -> ChannelId {
453            self.id
454        }
455
456        fn name(&self) -> &'static str {
457            self.name
458        }
459    }
460
461    impl MpscTmInSharedPoolSender {
462        pub fn send_direct_tm(&self, tm: PusTmCreator) -> Result<(), EcssTmtcError> {
463            let addr = self.shared_tm_store.add_pus_tm(&tm)?;
464            self.sender
465                .send(addr)
466                .map_err(|_| EcssTmtcError::Send(GenericSendError::RxDisconnected))
467        }
468    }
469
470    impl EcssTmSenderCore for MpscTmInSharedPoolSender {
471        fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> {
472            match tm {
473                PusTmWrapper::InStore(addr) => {
474                    self.sender.send(addr)?;
475                    Ok(())
476                }
477                PusTmWrapper::Direct(tm) => self.send_direct_tm(tm),
478            }
479        }
480    }
481
482    impl MpscTmInSharedPoolSender {
483        pub fn new(
484            id: ChannelId,
485            name: &'static str,
486            shared_tm_store: SharedTmPool,
487            sender: mpsc::Sender<StoreAddr>,
488        ) -> Self {
489            Self {
490                id,
491                name,
492                shared_tm_store,
493                sender,
494            }
495        }
496    }
497
498    pub struct MpscTcReceiver {
499        id: ChannelId,
500        name: &'static str,
501        receiver: mpsc::Receiver<EcssTcAndToken>,
502    }
503
504    impl EcssChannel for MpscTcReceiver {
505        fn id(&self) -> ChannelId {
506            self.id
507        }
508
509        fn name(&self) -> &'static str {
510            self.name
511        }
512    }
513
514    impl EcssTcReceiverCore for MpscTcReceiver {
515        fn recv_tc(&self) -> Result<EcssTcAndToken, TryRecvTmtcError> {
516            self.receiver.try_recv().map_err(|e| match e {
517                TryRecvError::Empty => TryRecvTmtcError::Empty,
518                TryRecvError::Disconnected => {
519                    TryRecvTmtcError::Error(EcssTmtcError::from(GenericRecvError::TxDisconnected))
520                }
521            })
522        }
523    }
524
525    impl MpscTcReceiver {
526        pub fn new(
527            id: ChannelId,
528            name: &'static str,
529            receiver: mpsc::Receiver<EcssTcAndToken>,
530        ) -> Self {
531            Self { id, name, receiver }
532        }
533    }
534
535    /// This class can be used if frequent heap allocations during run-time are not an issue.
536    /// PUS TM packets will be sent around as [Vec]s. Please note that the current implementation
537    /// of this class can not deal with store addresses, so it is assumed that is is always
538    /// going to be called with direct packets.
539    #[derive(Clone)]
540    pub struct MpscTmAsVecSender {
541        id: ChannelId,
542        name: &'static str,
543        sender: mpsc::Sender<Vec<u8>>,
544    }
545
546    impl From<mpsc::SendError<Vec<u8>>> for EcssTmtcError {
547        fn from(_: mpsc::SendError<Vec<u8>>) -> Self {
548            Self::Send(GenericSendError::RxDisconnected)
549        }
550    }
551
552    impl MpscTmAsVecSender {
553        pub fn new(id: u32, name: &'static str, sender: mpsc::Sender<Vec<u8>>) -> Self {
554            Self { id, sender, name }
555        }
556    }
557
558    impl EcssChannel for MpscTmAsVecSender {
559        fn id(&self) -> ChannelId {
560            self.id
561        }
562        fn name(&self) -> &'static str {
563            self.name
564        }
565    }
566
567    impl EcssTmSenderCore for MpscTmAsVecSender {
568        fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> {
569            match tm {
570                PusTmWrapper::InStore(addr) => Err(EcssTmtcError::CantSendAddr(addr)),
571                PusTmWrapper::Direct(tm) => {
572                    let mut vec = Vec::new();
573                    tm.append_to_vec(&mut vec).map_err(EcssTmtcError::Pus)?;
574                    self.sender.send(vec)?;
575                    Ok(())
576                }
577            }
578        }
579    }
580
581    #[derive(Clone)]
582    pub struct CrossbeamTmInStoreSender {
583        id: ChannelId,
584        name: &'static str,
585        shared_tm_store: SharedTmPool,
586        sender: crossbeam_channel::Sender<StoreAddr>,
587    }
588
589    impl CrossbeamTmInStoreSender {
590        pub fn new(
591            id: ChannelId,
592            name: &'static str,
593            shared_tm_store: SharedTmPool,
594            sender: crossbeam_channel::Sender<StoreAddr>,
595        ) -> Self {
596            Self {
597                id,
598                name,
599                shared_tm_store,
600                sender,
601            }
602        }
603    }
604
605    impl EcssChannel for CrossbeamTmInStoreSender {
606        fn id(&self) -> ChannelId {
607            self.id
608        }
609
610        fn name(&self) -> &'static str {
611            self.name
612        }
613    }
614
615    impl EcssTmSenderCore for CrossbeamTmInStoreSender {
616        fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> {
617            match tm {
618                PusTmWrapper::InStore(addr) => self.sender.try_send(addr)?,
619                PusTmWrapper::Direct(tm) => {
620                    let addr = self.shared_tm_store.add_pus_tm(&tm)?;
621                    self.sender.try_send(addr)?;
622                }
623            }
624            Ok(())
625        }
626    }
627
628    pub struct CrossbeamTcReceiver {
629        id: ChannelId,
630        name: &'static str,
631        receiver: cb::Receiver<EcssTcAndToken>,
632    }
633
634    impl CrossbeamTcReceiver {
635        pub fn new(
636            id: ChannelId,
637            name: &'static str,
638            receiver: cb::Receiver<EcssTcAndToken>,
639        ) -> Self {
640            Self { id, name, receiver }
641        }
642    }
643
644    impl EcssChannel for CrossbeamTcReceiver {
645        fn id(&self) -> ChannelId {
646            self.id
647        }
648
649        fn name(&self) -> &'static str {
650            self.name
651        }
652    }
653
654    impl EcssTcReceiverCore for CrossbeamTcReceiver {
655        fn recv_tc(&self) -> Result<EcssTcAndToken, TryRecvTmtcError> {
656            self.receiver.try_recv().map_err(|e| match e {
657                cb::TryRecvError::Empty => TryRecvTmtcError::Empty,
658                cb::TryRecvError::Disconnected => {
659                    TryRecvTmtcError::Error(EcssTmtcError::from(GenericRecvError::TxDisconnected))
660                }
661            })
662        }
663    }
664
665    #[derive(Debug, Clone, Error)]
666    pub enum PusPacketHandlingError {
667        #[error("generic PUS error: {0}")]
668        Pus(#[from] PusError),
669        #[error("wrong service number {0} for packet handler")]
670        WrongService(u8),
671        #[error("invalid subservice {0}")]
672        InvalidSubservice(u8),
673        #[error("not enough application data available: {0}")]
674        NotEnoughAppData(String),
675        #[error("PUS packet too large, does not fit in buffer: {0}")]
676        PusPacketTooLarge(usize),
677        #[error("invalid application data")]
678        InvalidAppData(String),
679        #[error("invalid format of TC in memory: {0:?}")]
680        InvalidTcInMemoryFormat(TcInMemory),
681        #[error("generic ECSS tmtc error: {0}")]
682        EcssTmtc(#[from] EcssTmtcError),
683        #[error("invalid verification token")]
684        InvalidVerificationToken,
685        #[error("other error {0}")]
686        Other(String),
687    }
688
689    #[derive(Debug, Clone, Error)]
690    pub enum PartialPusHandlingError {
691        #[error("generic timestamp generation error")]
692        Time(#[from] StdTimestampError),
693        #[error("error sending telemetry: {0}")]
694        TmSend(#[from] EcssTmtcError),
695        #[error("error sending verification message")]
696        Verification,
697        #[error("invalid verification token")]
698        NoVerificationToken,
699    }
700
701    /// Generic result type for handlers which can process PUS packets.
702    #[derive(Debug, Clone)]
703    pub enum PusPacketHandlerResult {
704        RequestHandled,
705        RequestHandledPartialSuccess(PartialPusHandlingError),
706        SubserviceNotImplemented(u8, VerificationToken<TcStateAccepted>),
707        CustomSubservice(u8, VerificationToken<TcStateAccepted>),
708        Empty,
709    }
710
711    impl From<PartialPusHandlingError> for PusPacketHandlerResult {
712        fn from(value: PartialPusHandlingError) -> Self {
713            Self::RequestHandledPartialSuccess(value)
714        }
715    }
716
717    pub trait EcssTcInMemConverter {
718        fn cache_ecss_tc_in_memory(
719            &mut self,
720            possible_packet: &TcInMemory,
721        ) -> Result<(), PusPacketHandlingError>;
722
723        fn tc_slice_raw(&self) -> &[u8];
724
725        fn convert_ecss_tc_in_memory_to_reader(
726            &mut self,
727            possible_packet: &TcInMemory,
728        ) -> Result<PusTcReader<'_>, PusPacketHandlingError> {
729            self.cache_ecss_tc_in_memory(possible_packet)?;
730            Ok(PusTcReader::new(self.tc_slice_raw())?.0)
731        }
732    }
733
734    /// Converter structure for PUS telecommands which are stored inside a `Vec<u8>` structure.
735    /// Please note that this structure is not able to convert TCs which are stored inside a
736    /// [SharedStaticMemoryPool].
737    #[derive(Default, Clone)]
738    pub struct EcssTcInVecConverter {
739        pub pus_tc_raw: Option<Vec<u8>>,
740    }
741
742    impl EcssTcInMemConverter for EcssTcInVecConverter {
743        fn cache_ecss_tc_in_memory(
744            &mut self,
745            tc_in_memory: &TcInMemory,
746        ) -> Result<(), PusPacketHandlingError> {
747            self.pus_tc_raw = None;
748            match tc_in_memory {
749                super::TcInMemory::StoreAddr(_) => {
750                    return Err(PusPacketHandlingError::InvalidTcInMemoryFormat(
751                        tc_in_memory.clone(),
752                    ));
753                }
754                super::TcInMemory::Vec(vec) => {
755                    self.pus_tc_raw = Some(vec.clone());
756                }
757            };
758            Ok(())
759        }
760
761        fn tc_slice_raw(&self) -> &[u8] {
762            if self.pus_tc_raw.is_none() {
763                return &[];
764            }
765            self.pus_tc_raw.as_ref().unwrap()
766        }
767    }
768
769    /// Converter structure for PUS telecommands which are stored inside
770    /// [SharedStaticMemoryPool] structure. This is useful if run-time allocation for these
771    /// packets should be avoided. Please note that this structure is not able to convert TCs which
772    /// are stored as a `Vec<u8>`.
773    pub struct EcssTcInSharedStoreConverter {
774        shared_tc_store: SharedStaticMemoryPool,
775        pus_buf: Vec<u8>,
776    }
777
778    impl EcssTcInSharedStoreConverter {
779        pub fn new(shared_tc_store: SharedStaticMemoryPool, max_expected_tc_size: usize) -> Self {
780            Self {
781                shared_tc_store,
782                pus_buf: alloc::vec![0; max_expected_tc_size],
783            }
784        }
785
786        pub fn copy_tc_to_buf(&mut self, addr: StoreAddr) -> Result<(), PusPacketHandlingError> {
787            // Keep locked section as short as possible.
788            let mut tc_pool = self
789                .shared_tc_store
790                .write()
791                .map_err(|_| PusPacketHandlingError::EcssTmtc(EcssTmtcError::StoreLock))?;
792            let tc_size = tc_pool
793                .len_of_data(&addr)
794                .map_err(|e| PusPacketHandlingError::EcssTmtc(EcssTmtcError::Store(e)))?;
795            if tc_size > self.pus_buf.len() {
796                return Err(PusPacketHandlingError::PusPacketTooLarge(tc_size));
797            }
798            let tc_guard = tc_pool.read_with_guard(addr);
799            // TODO: Proper error handling.
800            tc_guard.read(&mut self.pus_buf[0..tc_size]).unwrap();
801            Ok(())
802        }
803    }
804
805    impl EcssTcInMemConverter for EcssTcInSharedStoreConverter {
806        fn cache_ecss_tc_in_memory(
807            &mut self,
808            tc_in_memory: &TcInMemory,
809        ) -> Result<(), PusPacketHandlingError> {
810            match tc_in_memory {
811                super::TcInMemory::StoreAddr(addr) => {
812                    self.copy_tc_to_buf(*addr)?;
813                }
814                super::TcInMemory::Vec(_) => {
815                    return Err(PusPacketHandlingError::InvalidTcInMemoryFormat(
816                        tc_in_memory.clone(),
817                    ));
818                }
819            };
820            Ok(())
821        }
822
823        fn tc_slice_raw(&self) -> &[u8] {
824            self.pus_buf.as_ref()
825        }
826    }
827
828    pub struct PusServiceBase {
829        pub tc_receiver: Box<dyn EcssTcReceiver>,
830        pub tm_sender: Box<dyn EcssTmSender>,
831        pub tm_apid: u16,
832        /// The verification handler is wrapped in a [RefCell] to allow the interior mutability
833        /// pattern. This makes writing methods which are not mutable a lot easier.
834        pub verification_handler: RefCell<StdVerifReporterWithSender>,
835    }
836
837    impl PusServiceBase {
838        #[cfg(feature = "std")]
839        pub fn get_current_timestamp(
840            partial_error: &mut Option<PartialPusHandlingError>,
841        ) -> [u8; 7] {
842            let mut time_stamp: [u8; 7] = [0; 7];
843            let time_provider =
844                TimeProvider::from_now_with_u16_days().map_err(PartialPusHandlingError::Time);
845            if let Ok(time_provider) = time_provider {
846                // Can't fail, we have a buffer with the exact required size.
847                time_provider.write_to_bytes(&mut time_stamp).unwrap();
848            } else {
849                *partial_error = Some(time_provider.unwrap_err());
850            }
851            time_stamp
852        }
853
854        #[cfg(feature = "std")]
855        pub fn get_current_timestamp_ignore_error() -> [u8; 7] {
856            let mut dummy = None;
857            Self::get_current_timestamp(&mut dummy)
858        }
859    }
860
861    /// This is a high-level PUS packet handler helper.
862    ///
863    /// It performs some of the boilerplate acitivities involved when handling PUS telecommands and
864    /// it can be used to implement the handling of PUS telecommands for certain PUS telecommands
865    /// groups (for example individual services).
866    ///
867    /// This base class can handle PUS telecommands backed by different memory storage machanisms
868    /// by using the [EcssTcInMemConverter] abstraction. This object provides some convenience
869    /// methods to make the generic parts of TC handling easier.
870    pub struct PusServiceHelper<TcInMemConverter: EcssTcInMemConverter> {
871        pub common: PusServiceBase,
872        pub tc_in_mem_converter: TcInMemConverter,
873    }
874
875    impl<TcInMemConverter: EcssTcInMemConverter> PusServiceHelper<TcInMemConverter> {
876        pub fn new(
877            tc_receiver: Box<dyn EcssTcReceiver>,
878            tm_sender: Box<dyn EcssTmSender>,
879            tm_apid: u16,
880            verification_handler: VerificationReporterWithSender,
881            tc_in_mem_converter: TcInMemConverter,
882        ) -> Self {
883            Self {
884                common: PusServiceBase {
885                    tc_receiver,
886                    tm_sender,
887                    tm_apid,
888                    verification_handler: RefCell::new(verification_handler),
889                },
890                tc_in_mem_converter,
891            }
892        }
893
894        /// This function can be used to poll the internal [EcssTcReceiver] object for the next
895        /// telecommand packet. It will return `Ok(None)` if there are not packets available.
896        /// In any other case, it will perform the acceptance of the ECSS TC packet using the
897        /// internal [VerificationReporterWithSender] object. It will then return the telecommand
898        /// and the according accepted token.
899        pub fn retrieve_and_accept_next_packet(
900            &mut self,
901        ) -> Result<Option<AcceptedEcssTcAndToken>, PusPacketHandlingError> {
902            match self.common.tc_receiver.recv_tc() {
903                Ok(EcssTcAndToken {
904                    tc_in_memory,
905                    token,
906                }) => {
907                    if token.is_none() {
908                        return Err(PusPacketHandlingError::InvalidVerificationToken);
909                    }
910                    let token = token.unwrap();
911                    let accepted_token = VerificationToken::<TcStateAccepted>::try_from(token)
912                        .map_err(|_| PusPacketHandlingError::InvalidVerificationToken)?;
913                    Ok(Some(AcceptedEcssTcAndToken {
914                        tc_in_memory,
915                        token: accepted_token,
916                    }))
917                }
918                Err(e) => match e {
919                    TryRecvTmtcError::Error(e) => Err(PusPacketHandlingError::EcssTmtc(e)),
920                    TryRecvTmtcError::Empty => Ok(None),
921                },
922            }
923        }
924    }
925}
926
927pub(crate) fn source_buffer_large_enough(cap: usize, len: usize) -> Result<(), EcssTmtcError> {
928    if len > cap {
929        return Err(
930            PusError::ByteConversion(ByteConversionError::ToSliceTooSmall {
931                found: cap,
932                expected: len,
933            })
934            .into(),
935        );
936    }
937    Ok(())
938}
939
940#[cfg(test)]
941pub mod tests {
942    use std::sync::mpsc::TryRecvError;
943    use std::sync::{mpsc, RwLock};
944
945    use alloc::boxed::Box;
946    use alloc::vec;
947    use spacepackets::ecss::tc::PusTcCreator;
948    use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator, PusTmReader};
949    use spacepackets::ecss::{PusPacket, WritablePusPacket};
950    use spacepackets::CcsdsPacket;
951
952    use crate::pool::{
953        PoolProvider, SharedStaticMemoryPool, StaticMemoryPool, StaticPoolConfig, StoreAddr,
954    };
955    use crate::pus::verification::RequestId;
956    use crate::tmtc::tm_helper::SharedTmPool;
957
958    use super::verification::{
959        TcStateAccepted, VerificationReporterCfg, VerificationReporterWithSender, VerificationToken,
960    };
961    use super::{
962        EcssTcAndToken, EcssTcInSharedStoreConverter, EcssTcInVecConverter, MpscTcReceiver,
963        MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
964        PusPacketHandlingError, PusServiceHelper, TcInMemory,
965    };
966
967    pub const TEST_APID: u16 = 0x101;
968
969    #[derive(Debug, Eq, PartialEq, Clone)]
970    pub(crate) struct CommonTmInfo {
971        pub subservice: u8,
972        pub apid: u16,
973        pub msg_counter: u16,
974        pub dest_id: u16,
975        pub time_stamp: [u8; 7],
976    }
977
978    pub trait PusTestHarness {
979        fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted>;
980        fn read_next_tm(&mut self) -> PusTmReader<'_>;
981        fn check_no_tm_available(&self) -> bool;
982        fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId);
983    }
984
985    pub trait SimplePusPacketHandler {
986        fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError>;
987    }
988
989    impl CommonTmInfo {
990        pub fn new_from_tm(tm: &PusTmCreator) -> Self {
991            let mut time_stamp = [0; 7];
992            time_stamp.clone_from_slice(&tm.timestamp()[0..7]);
993            Self {
994                subservice: PusPacket::subservice(tm),
995                apid: tm.apid(),
996                msg_counter: tm.msg_counter(),
997                dest_id: tm.dest_id(),
998                time_stamp,
999            }
1000        }
1001    }
1002
1003    /// Common fields for a PUS service test harness.
1004    pub struct PusServiceHandlerWithSharedStoreCommon {
1005        pus_buf: [u8; 2048],
1006        tm_buf: [u8; 2048],
1007        tc_pool: SharedStaticMemoryPool,
1008        tm_pool: SharedTmPool,
1009        tc_sender: mpsc::Sender<EcssTcAndToken>,
1010        tm_receiver: mpsc::Receiver<StoreAddr>,
1011        verification_handler: VerificationReporterWithSender,
1012    }
1013
1014    impl PusServiceHandlerWithSharedStoreCommon {
1015        /// This function generates the structure in addition to the PUS service handler
1016        /// [PusServiceHandler] which might be required for a specific PUS service handler.
1017        ///
1018        /// The PUS service handler is instantiated with a [EcssTcInStoreConverter].
1019        pub fn new() -> (Self, PusServiceHelper<EcssTcInSharedStoreConverter>) {
1020            let pool_cfg = StaticPoolConfig::new(vec![(16, 16), (8, 32), (4, 64)], false);
1021            let tc_pool = StaticMemoryPool::new(pool_cfg.clone());
1022            let tm_pool = StaticMemoryPool::new(pool_cfg);
1023            let shared_tc_pool = SharedStaticMemoryPool::new(RwLock::new(tc_pool));
1024            let shared_tm_pool = SharedTmPool::new(tm_pool);
1025            let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
1026            let (tm_tx, tm_rx) = mpsc::channel();
1027
1028            let verif_sender = MpscTmInSharedPoolSender::new(
1029                0,
1030                "verif_sender",
1031                shared_tm_pool.clone(),
1032                tm_tx.clone(),
1033            );
1034            let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
1035            let verification_handler =
1036                VerificationReporterWithSender::new(&verif_cfg, Box::new(verif_sender));
1037            let test_srv_tm_sender =
1038                MpscTmInSharedPoolSender::new(0, "TEST_SENDER", shared_tm_pool.clone(), tm_tx);
1039            let test_srv_tc_receiver = MpscTcReceiver::new(0, "TEST_RECEIVER", test_srv_tc_rx);
1040            let in_store_converter =
1041                EcssTcInSharedStoreConverter::new(shared_tc_pool.clone(), 2048);
1042            (
1043                Self {
1044                    pus_buf: [0; 2048],
1045                    tm_buf: [0; 2048],
1046                    tc_pool: shared_tc_pool,
1047                    tm_pool: shared_tm_pool,
1048                    tc_sender: test_srv_tc_tx,
1049                    tm_receiver: tm_rx,
1050                    verification_handler: verification_handler.clone(),
1051                },
1052                PusServiceHelper::new(
1053                    Box::new(test_srv_tc_receiver),
1054                    Box::new(test_srv_tm_sender),
1055                    TEST_APID,
1056                    verification_handler,
1057                    in_store_converter,
1058                ),
1059            )
1060        }
1061        pub fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted> {
1062            let token = self.verification_handler.add_tc(tc);
1063            let token = self
1064                .verification_handler
1065                .acceptance_success(token, Some(&[0; 7]))
1066                .unwrap();
1067            let tc_size = tc.write_to_bytes(&mut self.pus_buf).unwrap();
1068            let mut tc_pool = self.tc_pool.write().unwrap();
1069            let addr = tc_pool.add(&self.pus_buf[..tc_size]).unwrap();
1070            drop(tc_pool);
1071            // Send accepted TC to test service handler.
1072            self.tc_sender
1073                .send(EcssTcAndToken::new(addr, token))
1074                .expect("sending tc failed");
1075            token
1076        }
1077
1078        pub fn read_next_tm(&mut self) -> PusTmReader<'_> {
1079            let next_msg = self.tm_receiver.try_recv();
1080            assert!(next_msg.is_ok());
1081            let tm_addr = next_msg.unwrap();
1082            let tm_pool = self.tm_pool.0.read().unwrap();
1083            let tm_raw = tm_pool.read_as_vec(&tm_addr).unwrap();
1084            self.tm_buf[0..tm_raw.len()].copy_from_slice(&tm_raw);
1085            PusTmReader::new(&self.tm_buf, 7).unwrap().0
1086        }
1087
1088        pub fn check_no_tm_available(&self) -> bool {
1089            let next_msg = self.tm_receiver.try_recv();
1090            if let TryRecvError::Empty = next_msg.unwrap_err() {
1091                return true;
1092            }
1093            false
1094        }
1095
1096        pub fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId) {
1097            let next_msg = self.tm_receiver.try_recv();
1098            assert!(next_msg.is_ok());
1099            let tm_addr = next_msg.unwrap();
1100            let tm_pool = self.tm_pool.0.read().unwrap();
1101            let tm_raw = tm_pool.read_as_vec(&tm_addr).unwrap();
1102            let tm = PusTmReader::new(&tm_raw, 7).unwrap().0;
1103            assert_eq!(PusPacket::service(&tm), 1);
1104            assert_eq!(PusPacket::subservice(&tm), subservice);
1105            assert_eq!(tm.apid(), TEST_APID);
1106            let req_id =
1107                RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
1108            assert_eq!(req_id, expected_request_id);
1109        }
1110    }
1111
1112    pub struct PusServiceHandlerWithVecCommon {
1113        current_tm: Option<alloc::vec::Vec<u8>>,
1114        tc_sender: mpsc::Sender<EcssTcAndToken>,
1115        tm_receiver: mpsc::Receiver<alloc::vec::Vec<u8>>,
1116        verification_handler: VerificationReporterWithSender,
1117    }
1118
1119    impl PusServiceHandlerWithVecCommon {
1120        pub fn new() -> (Self, PusServiceHelper<EcssTcInVecConverter>) {
1121            let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
1122            let (tm_tx, tm_rx) = mpsc::channel();
1123
1124            let verif_sender = MpscTmAsVecSender::new(0, "verififcatio-sender", tm_tx.clone());
1125            let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
1126            let verification_handler =
1127                VerificationReporterWithSender::new(&verif_cfg, Box::new(verif_sender));
1128            let test_srv_tm_sender = MpscTmAsVecSender::new(0, "test-sender", tm_tx);
1129            let test_srv_tc_receiver = MpscTcReceiver::new(0, "test-receiver", test_srv_tc_rx);
1130            let in_store_converter = EcssTcInVecConverter::default();
1131            (
1132                Self {
1133                    current_tm: None,
1134                    tc_sender: test_srv_tc_tx,
1135                    tm_receiver: tm_rx,
1136                    verification_handler: verification_handler.clone(),
1137                },
1138                PusServiceHelper::new(
1139                    Box::new(test_srv_tc_receiver),
1140                    Box::new(test_srv_tm_sender),
1141                    TEST_APID,
1142                    verification_handler,
1143                    in_store_converter,
1144                ),
1145            )
1146        }
1147
1148        pub fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted> {
1149            let token = self.verification_handler.add_tc(tc);
1150            let token = self
1151                .verification_handler
1152                .acceptance_success(token, Some(&[0; 7]))
1153                .unwrap();
1154            // Send accepted TC to test service handler.
1155            self.tc_sender
1156                .send(EcssTcAndToken::new(
1157                    TcInMemory::Vec(tc.to_vec().expect("pus tc conversion to vec failed")),
1158                    token,
1159                ))
1160                .expect("sending tc failed");
1161            token
1162        }
1163
1164        pub fn read_next_tm(&mut self) -> PusTmReader<'_> {
1165            let next_msg = self.tm_receiver.try_recv();
1166            assert!(next_msg.is_ok());
1167            self.current_tm = Some(next_msg.unwrap());
1168            PusTmReader::new(self.current_tm.as_ref().unwrap(), 7)
1169                .unwrap()
1170                .0
1171        }
1172
1173        pub fn check_no_tm_available(&self) -> bool {
1174            let next_msg = self.tm_receiver.try_recv();
1175            if let TryRecvError::Empty = next_msg.unwrap_err() {
1176                return true;
1177            }
1178            false
1179        }
1180
1181        pub fn check_next_verification_tm(&self, subservice: u8, expected_request_id: RequestId) {
1182            let next_msg = self.tm_receiver.try_recv();
1183            assert!(next_msg.is_ok());
1184            let next_msg = next_msg.unwrap();
1185            let tm = PusTmReader::new(next_msg.as_slice(), 7).unwrap().0;
1186            assert_eq!(PusPacket::service(&tm), 1);
1187            assert_eq!(PusPacket::subservice(&tm), subservice);
1188            assert_eq!(tm.apid(), TEST_APID);
1189            let req_id =
1190                RequestId::from_bytes(tm.user_data()).expect("generating request ID failed");
1191            assert_eq!(req_id, expected_request_id);
1192        }
1193    }
1194}