satrs_core/pus/
event_man.rs

1use crate::events::{EventU32, GenericEvent, Severity};
2#[cfg(feature = "alloc")]
3use crate::events::{EventU32TypedSev, HasSeverity};
4#[cfg(feature = "alloc")]
5use alloc::boxed::Box;
6#[cfg(feature = "alloc")]
7use core::hash::Hash;
8#[cfg(feature = "alloc")]
9use hashbrown::HashSet;
10
11#[cfg(feature = "alloc")]
12pub use crate::pus::event::EventReporter;
13use crate::pus::verification::TcStateToken;
14#[cfg(feature = "alloc")]
15use crate::pus::EcssTmSenderCore;
16use crate::pus::EcssTmtcError;
17#[cfg(feature = "alloc")]
18#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
19pub use alloc_mod::*;
20#[cfg(feature = "heapless")]
21#[cfg_attr(doc_cfg, doc(cfg(feature = "heapless")))]
22pub use heapless_mod::*;
23
24/// This trait allows the PUS event manager implementation to stay generic over various types
25/// of backend containers.
26///
27/// These backend containers keep track on whether a particular event is enabled or disabled for
28/// reporting and also expose a simple API to enable or disable the event reporting.
29///
30/// For example, a straight forward implementation for host systems could use a
31/// [hash set](https://docs.rs/hashbrown/latest/hashbrown/struct.HashSet.html)
32/// structure to track disabled events. A more primitive and embedded friendly
33/// solution could track this information in a static or pre-allocated list which contains
34/// the disabled events.
35pub trait PusEventMgmtBackendProvider<Provider: GenericEvent> {
36    type Error;
37
38    fn event_enabled(&self, event: &Provider) -> bool;
39    fn enable_event_reporting(&mut self, event: &Provider) -> Result<bool, Self::Error>;
40    fn disable_event_reporting(&mut self, event: &Provider) -> Result<bool, Self::Error>;
41}
42
43#[cfg(feature = "heapless")]
44pub mod heapless_mod {
45    use super::*;
46    use crate::events::{GenericEvent, LargestEventRaw};
47    use std::marker::PhantomData;
48
49    #[cfg_attr(doc_cfg, doc(cfg(feature = "heapless")))]
50    // TODO: After a new version of heapless is released which uses hash32 version 0.3, try using
51    //       regular Event type again.
52    #[derive(Default)]
53    pub struct HeaplessPusMgmtBackendProvider<const N: usize, Provider: GenericEvent> {
54        disabled: heapless::FnvIndexSet<LargestEventRaw, N>,
55        phantom: PhantomData<Provider>,
56    }
57
58    /// Safety: All contained field are [Send] as well
59    unsafe impl<const N: usize, Event: GenericEvent + Send> Send
60        for HeaplessPusMgmtBackendProvider<N, Event>
61    {
62    }
63
64    impl<const N: usize, Provider: GenericEvent> PusEventMgmtBackendProvider<Provider>
65        for HeaplessPusMgmtBackendProvider<N, Provider>
66    {
67        type Error = ();
68
69        fn event_enabled(&self, event: &Provider) -> bool {
70            self.disabled.contains(&event.raw_as_largest_type())
71        }
72
73        fn enable_event_reporting(&mut self, event: &Provider) -> Result<bool, Self::Error> {
74            self.disabled
75                .insert(event.raw_as_largest_type())
76                .map_err(|_| ())
77        }
78
79        fn disable_event_reporting(&mut self, event: &Provider) -> Result<bool, Self::Error> {
80            Ok(self.disabled.remove(&event.raw_as_largest_type()))
81        }
82    }
83}
84
85#[derive(Debug, PartialEq, Eq)]
86pub enum EventRequest<Event: GenericEvent = EventU32> {
87    Enable(Event),
88    Disable(Event),
89}
90
91#[derive(Debug)]
92pub struct EventRequestWithToken<Event: GenericEvent = EventU32> {
93    pub request: EventRequest<Event>,
94    pub token: TcStateToken,
95}
96
97#[derive(Debug)]
98pub enum EventManError {
99    EcssTmtcError(EcssTmtcError),
100    SeverityMissmatch(Severity, Severity),
101}
102
103impl From<EcssTmtcError> for EventManError {
104    fn from(v: EcssTmtcError) -> Self {
105        Self::EcssTmtcError(v)
106    }
107}
108
109#[cfg(feature = "alloc")]
110pub mod alloc_mod {
111    use super::*;
112
113    /// Default backend provider which uses a hash set as the event reporting status container
114    /// like mentioned in the example of the [PusEventMgmtBackendProvider] documentation.
115    ///
116    /// This provider is a good option for host systems or larger embedded systems where
117    /// the expected occasional memory allocation performed by the [HashSet] is not an issue.
118    pub struct DefaultPusMgmtBackendProvider<Event: GenericEvent = EventU32> {
119        disabled: HashSet<Event>,
120    }
121
122    /// Safety: All contained field are [Send] as well
123    unsafe impl<Event: GenericEvent + Send> Send for DefaultPusMgmtBackendProvider<Event> {}
124
125    impl<Event: GenericEvent> Default for DefaultPusMgmtBackendProvider<Event> {
126        fn default() -> Self {
127            Self {
128                disabled: HashSet::default(),
129            }
130        }
131    }
132
133    impl<Provider: GenericEvent + PartialEq + Eq + Hash + Copy + Clone>
134        PusEventMgmtBackendProvider<Provider> for DefaultPusMgmtBackendProvider<Provider>
135    {
136        type Error = ();
137        fn event_enabled(&self, event: &Provider) -> bool {
138            !self.disabled.contains(event)
139        }
140
141        fn enable_event_reporting(&mut self, event: &Provider) -> Result<bool, Self::Error> {
142            Ok(self.disabled.remove(event))
143        }
144
145        fn disable_event_reporting(&mut self, event: &Provider) -> Result<bool, Self::Error> {
146            Ok(self.disabled.insert(*event))
147        }
148    }
149
150    pub struct PusEventDispatcher<BackendError, Provider: GenericEvent> {
151        reporter: EventReporter,
152        backend: Box<dyn PusEventMgmtBackendProvider<Provider, Error = BackendError>>,
153    }
154
155    /// Safety: All contained fields are send as well.
156    unsafe impl<E: Send, Event: GenericEvent + Send> Send for PusEventDispatcher<E, Event> {}
157
158    impl<BackendError, Provider: GenericEvent> PusEventDispatcher<BackendError, Provider> {
159        pub fn new(
160            reporter: EventReporter,
161            backend: Box<dyn PusEventMgmtBackendProvider<Provider, Error = BackendError>>,
162        ) -> Self {
163            Self { reporter, backend }
164        }
165    }
166
167    impl<BackendError, Event: GenericEvent> PusEventDispatcher<BackendError, Event> {
168        pub fn enable_tm_for_event(&mut self, event: &Event) -> Result<bool, BackendError> {
169            self.backend.enable_event_reporting(event)
170        }
171
172        pub fn disable_tm_for_event(&mut self, event: &Event) -> Result<bool, BackendError> {
173            self.backend.disable_event_reporting(event)
174        }
175
176        pub fn generate_pus_event_tm_generic(
177            &mut self,
178            sender: &mut (impl EcssTmSenderCore + ?Sized),
179            time_stamp: &[u8],
180            event: Event,
181            aux_data: Option<&[u8]>,
182        ) -> Result<bool, EventManError> {
183            if !self.backend.event_enabled(&event) {
184                return Ok(false);
185            }
186            match event.severity() {
187                Severity::INFO => self
188                    .reporter
189                    .event_info(sender, time_stamp, event, aux_data)
190                    .map(|_| true)
191                    .map_err(|e| e.into()),
192                Severity::LOW => self
193                    .reporter
194                    .event_low_severity(sender, time_stamp, event, aux_data)
195                    .map(|_| true)
196                    .map_err(|e| e.into()),
197                Severity::MEDIUM => self
198                    .reporter
199                    .event_medium_severity(sender, time_stamp, event, aux_data)
200                    .map(|_| true)
201                    .map_err(|e| e.into()),
202                Severity::HIGH => self
203                    .reporter
204                    .event_high_severity(sender, time_stamp, event, aux_data)
205                    .map(|_| true)
206                    .map_err(|e| e.into()),
207            }
208        }
209    }
210
211    impl<BackendError> PusEventDispatcher<BackendError, EventU32> {
212        pub fn enable_tm_for_event_with_sev<Severity: HasSeverity>(
213            &mut self,
214            event: &EventU32TypedSev<Severity>,
215        ) -> Result<bool, BackendError> {
216            self.backend.enable_event_reporting(event.as_ref())
217        }
218
219        pub fn disable_tm_for_event_with_sev<Severity: HasSeverity>(
220            &mut self,
221            event: &EventU32TypedSev<Severity>,
222        ) -> Result<bool, BackendError> {
223            self.backend.disable_event_reporting(event.as_ref())
224        }
225
226        pub fn generate_pus_event_tm<Severity: HasSeverity>(
227            &mut self,
228            sender: &mut (impl EcssTmSenderCore + ?Sized),
229            time_stamp: &[u8],
230            event: EventU32TypedSev<Severity>,
231            aux_data: Option<&[u8]>,
232        ) -> Result<bool, EventManError> {
233            self.generate_pus_event_tm_generic(sender, time_stamp, event.into(), aux_data)
234        }
235    }
236}
237#[cfg(test)]
238mod tests {
239    use super::*;
240    use crate::events::SeverityInfo;
241    use crate::pus::MpscTmAsVecSender;
242    use std::sync::mpsc::{channel, TryRecvError};
243
244    const INFO_EVENT: EventU32TypedSev<SeverityInfo> =
245        EventU32TypedSev::<SeverityInfo>::const_new(1, 0);
246    const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5);
247    const EMPTY_STAMP: [u8; 7] = [0; 7];
248
249    fn create_basic_man() -> PusEventDispatcher<(), EventU32> {
250        let reporter = EventReporter::new(0x02, 128).expect("Creating event repoter failed");
251        let backend = DefaultPusMgmtBackendProvider::<EventU32>::default();
252        PusEventDispatcher::new(reporter, Box::new(backend))
253    }
254
255    #[test]
256    fn test_basic() {
257        let mut event_man = create_basic_man();
258        let (event_tx, event_rx) = channel();
259        let mut sender = MpscTmAsVecSender::new(0, "test_sender", event_tx);
260        let event_sent = event_man
261            .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None)
262            .expect("Sending info event failed");
263
264        assert!(event_sent);
265        // Will not check packet here, correctness of packet was tested somewhere else
266        event_rx.try_recv().expect("Receiving event TM failed");
267    }
268
269    #[test]
270    fn test_disable_event() {
271        let mut event_man = create_basic_man();
272        let (event_tx, event_rx) = channel();
273        let mut sender = MpscTmAsVecSender::new(0, "test", event_tx);
274        let res = event_man.disable_tm_for_event(&LOW_SEV_EVENT);
275        assert!(res.is_ok());
276        assert!(res.unwrap());
277        let mut event_sent = event_man
278            .generate_pus_event_tm_generic(&mut sender, &EMPTY_STAMP, LOW_SEV_EVENT, None)
279            .expect("Sending low severity event failed");
280        assert!(!event_sent);
281        let res = event_rx.try_recv();
282        assert!(res.is_err());
283        assert!(matches!(res.unwrap_err(), TryRecvError::Empty));
284        // Check that only the low severity event was disabled
285        event_sent = event_man
286            .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None)
287            .expect("Sending info event failed");
288        assert!(event_sent);
289        event_rx.try_recv().expect("No info event received");
290    }
291
292    #[test]
293    fn test_reenable_event() {
294        let mut event_man = create_basic_man();
295        let (event_tx, event_rx) = channel();
296        let mut sender = MpscTmAsVecSender::new(0, "test", event_tx);
297        let mut res = event_man.disable_tm_for_event_with_sev(&INFO_EVENT);
298        assert!(res.is_ok());
299        assert!(res.unwrap());
300        res = event_man.enable_tm_for_event_with_sev(&INFO_EVENT);
301        assert!(res.is_ok());
302        assert!(res.unwrap());
303        let event_sent = event_man
304            .generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None)
305            .expect("Sending info event failed");
306        assert!(event_sent);
307        event_rx.try_recv().expect("No info event received");
308    }
309}