satrs_core/tmtc/
ccsds_distrib.rs

1//! CCSDS packet routing components.
2//!
3//! The routing components consist of two core components:
4//!  1. [CcsdsDistributor] component which dispatches received packets to a user-provided handler
5//!  2. [CcsdsPacketHandler] trait which should be implemented by the user-provided packet handler.
6//!
7//! The [CcsdsDistributor] implements the [ReceivesCcsdsTc] and [ReceivesTcCore] trait which allows to
8//! pass raw or CCSDS packets to it. Upon receiving a packet, it performs the following steps:
9//!
10//! 1. It tries to identify the target Application Process Identifier (APID) based on the
11//!    respective CCSDS space packet header field. If that process fails, a [ByteConversionError] is
12//!    returned to the user
13//! 2. If a valid APID is found and matches one of the APIDs provided by
14//!    [CcsdsPacketHandler::valid_apids], it will pass the packet to the user provided
15//!    [CcsdsPacketHandler::handle_known_apid] function. If no valid APID is found, the packet
16//!    will be passed to the [CcsdsPacketHandler::handle_unknown_apid] function.
17//!
18//! # Example
19//!
20//! ```rust
21//! use satrs_core::tmtc::ccsds_distrib::{CcsdsPacketHandler, CcsdsDistributor};
22//! use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore};
23//! use spacepackets::{CcsdsPacket, SpHeader};
24//! use spacepackets::ecss::WritablePusPacket;
25//! use spacepackets::ecss::tc::{PusTc, PusTcCreator};
26//!
27//! #[derive (Default)]
28//! struct ConcreteApidHandler {
29//!     known_call_count: u32,
30//!     unknown_call_count: u32
31//! }
32//!
33//! impl ConcreteApidHandler {
34//!     fn mutable_foo(&mut self) {}
35//! }
36//!
37//! impl CcsdsPacketHandler for ConcreteApidHandler {
38//!     type Error = ();
39//!     fn valid_apids(&self) -> &'static [u16] { &[0x002] }
40//!     fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> {
41//!         assert_eq!(sp_header.apid(), 0x002);
42//!         assert_eq!(tc_raw.len(), 13);
43//!         self.known_call_count += 1;
44//!         Ok(())
45//!     }
46//!     fn handle_unknown_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> {
47//!         assert_eq!(sp_header.apid(), 0x003);
48//!         assert_eq!(tc_raw.len(), 13);
49//!         self.unknown_call_count += 1;
50//!         Ok(())
51//!     }
52//! }
53//!
54//! let apid_handler = ConcreteApidHandler::default();
55//! let mut ccsds_distributor = CcsdsDistributor::new(Box::new(apid_handler));
56//!
57//! // Create and pass PUS telecommand with a valid APID
58//! let mut space_packet_header = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap();
59//! let mut pus_tc = PusTcCreator::new_simple(&mut space_packet_header, 17, 1, None, true);
60//! let mut test_buf: [u8; 32] = [0; 32];
61//! let mut size = pus_tc
62//!     .write_to_bytes(test_buf.as_mut_slice())
63//!     .expect("Error writing TC to buffer");
64//! let tc_slice = &test_buf[0..size];
65//! ccsds_distributor.pass_tc(&tc_slice).expect("Passing TC slice failed");
66//!
67//! // Now pass a packet with an unknown APID to the distributor
68//! pus_tc.set_apid(0x003);
69//! size = pus_tc
70//!     .write_to_bytes(test_buf.as_mut_slice())
71//!     .expect("Error writing TC to buffer");
72//! let tc_slice = &test_buf[0..size];
73//! ccsds_distributor.pass_tc(&tc_slice).expect("Passing TC slice failed");
74//!
75//! // User helper function to retrieve concrete class
76//! let concrete_handler_ref: &ConcreteApidHandler = ccsds_distributor
77//!     .apid_handler_ref()
78//!     .expect("Casting back to concrete type failed");
79//! assert_eq!(concrete_handler_ref.known_call_count, 1);
80//! assert_eq!(concrete_handler_ref.unknown_call_count, 1);
81//!
82//! // It's also possible to retrieve a mutable reference
83//! let mutable_ref: &mut ConcreteApidHandler = ccsds_distributor
84//!     .apid_handler_mut()
85//!     .expect("Casting back to concrete type failed");
86//! mutable_ref.mutable_foo();
87//! ```
88use crate::tmtc::{ReceivesCcsdsTc, ReceivesTcCore};
89use alloc::boxed::Box;
90use core::fmt::{Display, Formatter};
91use downcast_rs::Downcast;
92use spacepackets::{ByteConversionError, CcsdsPacket, SpHeader};
93#[cfg(feature = "std")]
94use std::error::Error;
95
96/// Generic trait for a handler or dispatcher object handling CCSDS packets.
97///
98/// Users should implement this trait on their custom CCSDS packet handler and then pass a boxed
99/// instance of this handler to the [CcsdsDistributor]. The distributor will use the trait
100/// interface to dispatch received packets to the user based on the Application Process Identifier
101/// (APID) field of the CCSDS packet.
102///
103/// This trait automatically implements the [downcast_rs::Downcast] to allow a more convenient API
104/// to cast trait objects back to their concrete type after the handler was passed to the
105/// distributor.
106pub trait CcsdsPacketHandler: Downcast {
107    type Error;
108
109    fn valid_apids(&self) -> &'static [u16];
110    fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8])
111        -> Result<(), Self::Error>;
112    fn handle_unknown_apid(
113        &mut self,
114        sp_header: &SpHeader,
115        tc_raw: &[u8],
116    ) -> Result<(), Self::Error>;
117}
118
119downcast_rs::impl_downcast!(CcsdsPacketHandler assoc Error);
120
121pub trait SendableCcsdsPacketHandler: CcsdsPacketHandler + Send {}
122
123impl<T: CcsdsPacketHandler + Send> SendableCcsdsPacketHandler for T {}
124
125downcast_rs::impl_downcast!(SendableCcsdsPacketHandler assoc Error);
126
127/// The CCSDS distributor dispatches received CCSDS packets to a user provided packet handler.
128///
129/// The passed APID handler is required to be [Send]able to allow more ergonomic usage with
130/// threads.
131pub struct CcsdsDistributor<E> {
132    /// User provided APID handler stored as a generic trait object.
133    /// It can be cast back to the original concrete type using the [Self::apid_handler_ref] or
134    /// the [Self::apid_handler_mut] method.
135    pub apid_handler: Box<dyn SendableCcsdsPacketHandler<Error = E>>,
136}
137
138#[derive(Debug, Copy, Clone, PartialEq, Eq)]
139pub enum CcsdsError<E> {
140    CustomError(E),
141    ByteConversionError(ByteConversionError),
142}
143
144impl<E: Display> Display for CcsdsError<E> {
145    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
146        match self {
147            Self::CustomError(e) => write!(f, "{e}"),
148            Self::ByteConversionError(e) => write!(f, "{e}"),
149        }
150    }
151}
152
153#[cfg(feature = "std")]
154impl<E: Error> Error for CcsdsError<E> {
155    fn source(&self) -> Option<&(dyn Error + 'static)> {
156        match self {
157            Self::CustomError(e) => e.source(),
158            Self::ByteConversionError(e) => e.source(),
159        }
160    }
161}
162
163impl<E: 'static> ReceivesCcsdsTc for CcsdsDistributor<E> {
164    type Error = CcsdsError<E>;
165
166    fn pass_ccsds(&mut self, header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> {
167        self.dispatch_ccsds(header, tc_raw)
168    }
169}
170
171impl<E: 'static> ReceivesTcCore for CcsdsDistributor<E> {
172    type Error = CcsdsError<E>;
173
174    fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> {
175        if tc_raw.len() < 7 {
176            return Err(CcsdsError::ByteConversionError(
177                ByteConversionError::FromSliceTooSmall {
178                    found: tc_raw.len(),
179                    expected: 7,
180                },
181            ));
182        }
183        let (sp_header, _) =
184            SpHeader::from_be_bytes(tc_raw).map_err(|e| CcsdsError::ByteConversionError(e))?;
185        self.dispatch_ccsds(&sp_header, tc_raw)
186    }
187}
188
189impl<E: 'static> CcsdsDistributor<E> {
190    pub fn new(apid_handler: Box<dyn SendableCcsdsPacketHandler<Error = E>>) -> Self {
191        CcsdsDistributor { apid_handler }
192    }
193
194    /// This function can be used to retrieve a reference to the concrete instance of the APID
195    /// handler after it was passed to the distributor. See the
196    /// [module documentation][crate::tmtc::ccsds_distrib] for an fsrc-example.
197    pub fn apid_handler_ref<T: SendableCcsdsPacketHandler<Error = E>>(&self) -> Option<&T> {
198        self.apid_handler.downcast_ref::<T>()
199    }
200
201    /// This function can be used to retrieve a mutable reference to the concrete instance of the
202    /// APID handler after it was passed to the distributor.
203    pub fn apid_handler_mut<T: SendableCcsdsPacketHandler<Error = E>>(&mut self) -> Option<&mut T> {
204        self.apid_handler.downcast_mut::<T>()
205    }
206
207    fn dispatch_ccsds(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), CcsdsError<E>> {
208        let apid = sp_header.apid();
209        let valid_apids = self.apid_handler.valid_apids();
210        for &valid_apid in valid_apids {
211            if valid_apid == apid {
212                return self
213                    .apid_handler
214                    .handle_known_apid(sp_header, tc_raw)
215                    .map_err(|e| CcsdsError::CustomError(e));
216            }
217        }
218        self.apid_handler
219            .handle_unknown_apid(sp_header, tc_raw)
220            .map_err(|e| CcsdsError::CustomError(e))
221    }
222}
223
224#[cfg(test)]
225pub(crate) mod tests {
226    use super::*;
227    use crate::tmtc::ccsds_distrib::{CcsdsDistributor, CcsdsPacketHandler};
228    use spacepackets::ecss::tc::PusTcCreator;
229    use spacepackets::ecss::WritablePusPacket;
230    use spacepackets::CcsdsPacket;
231    use std::collections::VecDeque;
232    use std::sync::{Arc, Mutex};
233    use std::vec::Vec;
234
235    fn is_send<T: Send>(_: &T) {}
236
237    pub fn generate_ping_tc(buf: &mut [u8]) -> &[u8] {
238        let mut sph = SpHeader::tc_unseg(0x002, 0x34, 0).unwrap();
239        let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
240        let size = pus_tc
241            .write_to_bytes(buf)
242            .expect("Error writing TC to buffer");
243        assert_eq!(size, 13);
244        &buf[0..size]
245    }
246
247    type SharedPacketQueue = Arc<Mutex<VecDeque<(u16, Vec<u8>)>>>;
248    pub struct BasicApidHandlerSharedQueue {
249        pub known_packet_queue: SharedPacketQueue,
250        pub unknown_packet_queue: SharedPacketQueue,
251    }
252
253    #[derive(Default)]
254    pub struct BasicApidHandlerOwnedQueue {
255        pub known_packet_queue: VecDeque<(u16, Vec<u8>)>,
256        pub unknown_packet_queue: VecDeque<(u16, Vec<u8>)>,
257    }
258
259    impl CcsdsPacketHandler for BasicApidHandlerSharedQueue {
260        type Error = ();
261        fn valid_apids(&self) -> &'static [u16] {
262            &[0x000, 0x002]
263        }
264
265        fn handle_known_apid(
266            &mut self,
267            sp_header: &SpHeader,
268            tc_raw: &[u8],
269        ) -> Result<(), Self::Error> {
270            let mut vec = Vec::new();
271            vec.extend_from_slice(tc_raw);
272            self.known_packet_queue
273                .lock()
274                .unwrap()
275                .push_back((sp_header.apid(), vec));
276            Ok(())
277        }
278
279        fn handle_unknown_apid(
280            &mut self,
281            sp_header: &SpHeader,
282            tc_raw: &[u8],
283        ) -> Result<(), Self::Error> {
284            let mut vec = Vec::new();
285            vec.extend_from_slice(tc_raw);
286            self.unknown_packet_queue
287                .lock()
288                .unwrap()
289                .push_back((sp_header.apid(), vec));
290            Ok(())
291        }
292    }
293
294    impl CcsdsPacketHandler for BasicApidHandlerOwnedQueue {
295        type Error = ();
296
297        fn valid_apids(&self) -> &'static [u16] {
298            &[0x000, 0x002]
299        }
300
301        fn handle_known_apid(
302            &mut self,
303            sp_header: &SpHeader,
304            tc_raw: &[u8],
305        ) -> Result<(), Self::Error> {
306            let mut vec = Vec::new();
307            vec.extend_from_slice(tc_raw);
308            Ok(self.known_packet_queue.push_back((sp_header.apid(), vec)))
309        }
310
311        fn handle_unknown_apid(
312            &mut self,
313            sp_header: &SpHeader,
314            tc_raw: &[u8],
315        ) -> Result<(), Self::Error> {
316            let mut vec = Vec::new();
317            vec.extend_from_slice(tc_raw);
318            Ok(self.unknown_packet_queue.push_back((sp_header.apid(), vec)))
319        }
320    }
321
322    #[test]
323    fn test_distribs_known_apid() {
324        let known_packet_queue = Arc::new(Mutex::default());
325        let unknown_packet_queue = Arc::new(Mutex::default());
326        let apid_handler = BasicApidHandlerSharedQueue {
327            known_packet_queue: known_packet_queue.clone(),
328            unknown_packet_queue: unknown_packet_queue.clone(),
329        };
330        let mut ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler));
331        is_send(&ccsds_distrib);
332        let mut test_buf: [u8; 32] = [0; 32];
333        let tc_slice = generate_ping_tc(test_buf.as_mut_slice());
334
335        ccsds_distrib.pass_tc(tc_slice).expect("Passing TC failed");
336        let recvd = known_packet_queue.lock().unwrap().pop_front();
337        assert!(unknown_packet_queue.lock().unwrap().is_empty());
338        assert!(recvd.is_some());
339        let (apid, packet) = recvd.unwrap();
340        assert_eq!(apid, 0x002);
341        assert_eq!(packet, tc_slice);
342    }
343
344    #[test]
345    fn test_distribs_unknown_apid() {
346        let known_packet_queue = Arc::new(Mutex::default());
347        let unknown_packet_queue = Arc::new(Mutex::default());
348        let apid_handler = BasicApidHandlerSharedQueue {
349            known_packet_queue: known_packet_queue.clone(),
350            unknown_packet_queue: unknown_packet_queue.clone(),
351        };
352        let mut ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler));
353        let mut sph = SpHeader::tc_unseg(0x004, 0x34, 0).unwrap();
354        let pus_tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, true);
355        let mut test_buf: [u8; 32] = [0; 32];
356        pus_tc
357            .write_to_bytes(test_buf.as_mut_slice())
358            .expect("Error writing TC to buffer");
359        ccsds_distrib.pass_tc(&test_buf).expect("Passing TC failed");
360        let recvd = unknown_packet_queue.lock().unwrap().pop_front();
361        assert!(known_packet_queue.lock().unwrap().is_empty());
362        assert!(recvd.is_some());
363        let (apid, packet) = recvd.unwrap();
364        assert_eq!(apid, 0x004);
365        assert_eq!(packet.as_slice(), test_buf);
366    }
367}