1use crate::pus::{source_buffer_large_enough, EcssTmtcError};
2use spacepackets::ecss::tm::PusTmCreator;
3use spacepackets::ecss::tm::PusTmSecondaryHeader;
4use spacepackets::ecss::{EcssEnumeration, PusError};
5use spacepackets::{SpHeader, MAX_APID};
6
7use crate::pus::EcssTmSenderCore;
8#[cfg(feature = "alloc")]
9pub use alloc_mod::EventReporter;
10pub use spacepackets::ecss::event::*;
11
12pub struct EventReporterBase {
13 msg_count: u16,
14 apid: u16,
15 pub dest_id: u16,
16}
17
18impl EventReporterBase {
19 pub fn new(apid: u16) -> Option<Self> {
20 if apid > MAX_APID {
21 return None;
22 }
23 Some(Self {
24 msg_count: 0,
25 dest_id: 0,
26 apid,
27 })
28 }
29
30 pub fn event_info(
31 &mut self,
32 buf: &mut [u8],
33 sender: &mut (impl EcssTmSenderCore + ?Sized),
34 time_stamp: &[u8],
35 event_id: impl EcssEnumeration,
36 aux_data: Option<&[u8]>,
37 ) -> Result<(), EcssTmtcError> {
38 self.generate_and_send_generic_tm(
39 buf,
40 Subservice::TmInfoReport,
41 sender,
42 time_stamp,
43 event_id,
44 aux_data,
45 )
46 }
47
48 pub fn event_low_severity(
49 &mut self,
50 buf: &mut [u8],
51 sender: &mut (impl EcssTmSenderCore + ?Sized),
52 time_stamp: &[u8],
53 event_id: impl EcssEnumeration,
54 aux_data: Option<&[u8]>,
55 ) -> Result<(), EcssTmtcError> {
56 self.generate_and_send_generic_tm(
57 buf,
58 Subservice::TmLowSeverityReport,
59 sender,
60 time_stamp,
61 event_id,
62 aux_data,
63 )
64 }
65
66 pub fn event_medium_severity(
67 &mut self,
68 buf: &mut [u8],
69 sender: &mut (impl EcssTmSenderCore + ?Sized),
70 time_stamp: &[u8],
71 event_id: impl EcssEnumeration,
72 aux_data: Option<&[u8]>,
73 ) -> Result<(), EcssTmtcError> {
74 self.generate_and_send_generic_tm(
75 buf,
76 Subservice::TmMediumSeverityReport,
77 sender,
78 time_stamp,
79 event_id,
80 aux_data,
81 )
82 }
83
84 pub fn event_high_severity(
85 &mut self,
86 buf: &mut [u8],
87 sender: &mut (impl EcssTmSenderCore + ?Sized),
88 time_stamp: &[u8],
89 event_id: impl EcssEnumeration,
90 aux_data: Option<&[u8]>,
91 ) -> Result<(), EcssTmtcError> {
92 self.generate_and_send_generic_tm(
93 buf,
94 Subservice::TmHighSeverityReport,
95 sender,
96 time_stamp,
97 event_id,
98 aux_data,
99 )
100 }
101
102 fn generate_and_send_generic_tm(
103 &mut self,
104 buf: &mut [u8],
105 subservice: Subservice,
106 sender: &mut (impl EcssTmSenderCore + ?Sized),
107 time_stamp: &[u8],
108 event_id: impl EcssEnumeration,
109 aux_data: Option<&[u8]>,
110 ) -> Result<(), EcssTmtcError> {
111 let tm = self.generate_generic_event_tm(buf, subservice, time_stamp, event_id, aux_data)?;
112 sender.send_tm(tm.into())?;
113 self.msg_count += 1;
114 Ok(())
115 }
116
117 fn generate_generic_event_tm<'a>(
118 &'a self,
119 buf: &'a mut [u8],
120 subservice: Subservice,
121 time_stamp: &'a [u8],
122 event_id: impl EcssEnumeration,
123 aux_data: Option<&[u8]>,
124 ) -> Result<PusTmCreator, EcssTmtcError> {
125 let mut src_data_len = event_id.size();
126 if let Some(aux_data) = aux_data {
127 src_data_len += aux_data.len();
128 }
129 source_buffer_large_enough(buf.len(), src_data_len)?;
130 let mut sp_header = SpHeader::tm_unseg(self.apid, 0, 0).unwrap();
131 let sec_header = PusTmSecondaryHeader::new(
132 5,
133 subservice.into(),
134 self.msg_count,
135 self.dest_id,
136 Some(time_stamp),
137 );
138 let mut current_idx = 0;
139 event_id
140 .write_to_be_bytes(&mut buf[0..event_id.size()])
141 .map_err(PusError::ByteConversion)?;
142 current_idx += event_id.size();
143 if let Some(aux_data) = aux_data {
144 buf[current_idx..current_idx + aux_data.len()].copy_from_slice(aux_data);
145 current_idx += aux_data.len();
146 }
147 Ok(PusTmCreator::new(
148 &mut sp_header,
149 sec_header,
150 &buf[0..current_idx],
151 true,
152 ))
153 }
154}
155
156#[cfg(feature = "alloc")]
157mod alloc_mod {
158 use super::*;
159 use alloc::vec;
160 use alloc::vec::Vec;
161
162 pub struct EventReporter {
163 source_data_buf: Vec<u8>,
164 pub reporter: EventReporterBase,
165 }
166
167 impl EventReporter {
168 pub fn new(apid: u16, max_event_id_and_aux_data_size: usize) -> Option<Self> {
169 let reporter = EventReporterBase::new(apid)?;
170 Some(Self {
171 source_data_buf: vec![0; max_event_id_and_aux_data_size],
172 reporter,
173 })
174 }
175 pub fn event_info(
176 &mut self,
177 sender: &mut (impl EcssTmSenderCore + ?Sized),
178 time_stamp: &[u8],
179 event_id: impl EcssEnumeration,
180 aux_data: Option<&[u8]>,
181 ) -> Result<(), EcssTmtcError> {
182 self.reporter.event_info(
183 self.source_data_buf.as_mut_slice(),
184 sender,
185 time_stamp,
186 event_id,
187 aux_data,
188 )
189 }
190
191 pub fn event_low_severity(
192 &mut self,
193 sender: &mut (impl EcssTmSenderCore + ?Sized),
194 time_stamp: &[u8],
195 event_id: impl EcssEnumeration,
196 aux_data: Option<&[u8]>,
197 ) -> Result<(), EcssTmtcError> {
198 self.reporter.event_low_severity(
199 self.source_data_buf.as_mut_slice(),
200 sender,
201 time_stamp,
202 event_id,
203 aux_data,
204 )
205 }
206
207 pub fn event_medium_severity(
208 &mut self,
209 sender: &mut (impl EcssTmSenderCore + ?Sized),
210 time_stamp: &[u8],
211 event_id: impl EcssEnumeration,
212 aux_data: Option<&[u8]>,
213 ) -> Result<(), EcssTmtcError> {
214 self.reporter.event_medium_severity(
215 self.source_data_buf.as_mut_slice(),
216 sender,
217 time_stamp,
218 event_id,
219 aux_data,
220 )
221 }
222
223 pub fn event_high_severity(
224 &mut self,
225 sender: &mut (impl EcssTmSenderCore + ?Sized),
226 time_stamp: &[u8],
227 event_id: impl EcssEnumeration,
228 aux_data: Option<&[u8]>,
229 ) -> Result<(), EcssTmtcError> {
230 self.reporter.event_high_severity(
231 self.source_data_buf.as_mut_slice(),
232 sender,
233 time_stamp,
234 event_id,
235 aux_data,
236 )
237 }
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244 use crate::events::{EventU32, Severity};
245 use crate::pus::tests::CommonTmInfo;
246 use crate::pus::{EcssChannel, PusTmWrapper};
247 use crate::ChannelId;
248 use spacepackets::ByteConversionError;
249 use std::cell::RefCell;
250 use std::collections::VecDeque;
251 use std::vec::Vec;
252
253 const EXAMPLE_APID: u16 = 0xee;
254 const EXAMPLE_GROUP_ID: u16 = 2;
255 const EXAMPLE_EVENT_ID_0: u16 = 1;
256 #[allow(dead_code)]
257 const EXAMPLE_EVENT_ID_1: u16 = 2;
258
259 #[derive(Debug, Eq, PartialEq, Clone)]
260 struct TmInfo {
261 pub common: CommonTmInfo,
262 pub event: EventU32,
263 pub aux_data: Vec<u8>,
264 }
265
266 #[derive(Default, Clone)]
267 struct TestSender {
268 pub service_queue: RefCell<VecDeque<TmInfo>>,
269 }
270
271 impl EcssChannel for TestSender {
272 fn id(&self) -> ChannelId {
273 0
274 }
275 }
276
277 impl EcssTmSenderCore for TestSender {
278 fn send_tm(&self, tm: PusTmWrapper) -> Result<(), EcssTmtcError> {
279 match tm {
280 PusTmWrapper::InStore(_) => {
281 panic!("TestSender: unexpected call with address");
282 }
283 PusTmWrapper::Direct(tm) => {
284 assert!(!tm.source_data().is_empty());
285 let src_data = tm.source_data();
286 assert!(src_data.len() >= 4);
287 let event =
288 EventU32::from(u32::from_be_bytes(src_data[0..4].try_into().unwrap()));
289 let mut aux_data = Vec::new();
290 if src_data.len() > 4 {
291 aux_data.extend_from_slice(&src_data[4..]);
292 }
293 self.service_queue.borrow_mut().push_back(TmInfo {
294 common: CommonTmInfo::new_from_tm(&tm),
295 event,
296 aux_data,
297 });
298 Ok(())
299 }
300 }
301 }
302 }
303
304 fn severity_to_subservice(severity: Severity) -> Subservice {
305 match severity {
306 Severity::INFO => Subservice::TmInfoReport,
307 Severity::LOW => Subservice::TmLowSeverityReport,
308 Severity::MEDIUM => Subservice::TmMediumSeverityReport,
309 Severity::HIGH => Subservice::TmHighSeverityReport,
310 }
311 }
312
313 fn report_basic_event(
314 reporter: &mut EventReporter,
315 sender: &mut TestSender,
316 time_stamp: &[u8],
317 event: EventU32,
318 severity: Severity,
319 aux_data: Option<&[u8]>,
320 ) {
321 match severity {
322 Severity::INFO => {
323 reporter
324 .event_info(sender, time_stamp, event, aux_data)
325 .expect("Error reporting info event");
326 }
327 Severity::LOW => {
328 reporter
329 .event_low_severity(sender, time_stamp, event, aux_data)
330 .expect("Error reporting low event");
331 }
332 Severity::MEDIUM => {
333 reporter
334 .event_medium_severity(sender, time_stamp, event, aux_data)
335 .expect("Error reporting medium event");
336 }
337 Severity::HIGH => {
338 reporter
339 .event_high_severity(sender, time_stamp, event, aux_data)
340 .expect("Error reporting high event");
341 }
342 }
343 }
344
345 fn basic_event_test(
346 max_event_aux_data_buf: usize,
347 severity: Severity,
348 error_data: Option<&[u8]>,
349 ) {
350 let mut sender = TestSender::default();
351 let reporter = EventReporter::new(EXAMPLE_APID, max_event_aux_data_buf);
352 assert!(reporter.is_some());
353 let mut reporter = reporter.unwrap();
354 let time_stamp_empty: [u8; 7] = [0; 7];
355 let mut error_copy = Vec::new();
356 if let Some(err_data) = error_data {
357 error_copy.extend_from_slice(err_data);
358 }
359 let event = EventU32::new(severity, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0)
360 .expect("Error creating example event");
361 report_basic_event(
362 &mut reporter,
363 &mut sender,
364 &time_stamp_empty,
365 event,
366 severity,
367 error_data,
368 );
369 let mut service_queue = sender.service_queue.borrow_mut();
370 assert_eq!(service_queue.len(), 1);
371 let tm_info = service_queue.pop_front().unwrap();
372 assert_eq!(
373 tm_info.common.subservice,
374 severity_to_subservice(severity) as u8
375 );
376 assert_eq!(tm_info.common.dest_id, 0);
377 assert_eq!(tm_info.common.time_stamp, time_stamp_empty);
378 assert_eq!(tm_info.common.msg_counter, 0);
379 assert_eq!(tm_info.common.apid, EXAMPLE_APID);
380 assert_eq!(tm_info.event, event);
381 assert_eq!(tm_info.aux_data, error_copy);
382 }
383
384 #[test]
385 fn basic_info_event_generation() {
386 basic_event_test(4, Severity::INFO, None);
387 }
388
389 #[test]
390 fn basic_low_severity_event() {
391 basic_event_test(4, Severity::LOW, None);
392 }
393
394 #[test]
395 fn basic_medium_severity_event() {
396 basic_event_test(4, Severity::MEDIUM, None);
397 }
398
399 #[test]
400 fn basic_high_severity_event() {
401 basic_event_test(4, Severity::HIGH, None);
402 }
403
404 #[test]
405 fn event_with_info_string() {
406 let info_string = "Test Information";
407 basic_event_test(32, Severity::INFO, Some(info_string.as_bytes()));
408 }
409
410 #[test]
411 fn low_severity_with_raw_err_data() {
412 let raw_err_param: i32 = -1;
413 let raw_err = raw_err_param.to_be_bytes();
414 basic_event_test(8, Severity::LOW, Some(&raw_err))
415 }
416
417 fn check_buf_too_small(
418 reporter: &mut EventReporter,
419 sender: &mut TestSender,
420 expected_found_len: usize,
421 ) {
422 let time_stamp_empty: [u8; 7] = [0; 7];
423 let event = EventU32::new(Severity::INFO, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0)
424 .expect("Error creating example event");
425 let err = reporter.event_info(sender, &time_stamp_empty, event, None);
426 assert!(err.is_err());
427 let err = err.unwrap_err();
428 if let EcssTmtcError::Pus(PusError::ByteConversion(
429 ByteConversionError::ToSliceTooSmall { found, expected },
430 )) = err
431 {
432 assert_eq!(expected, 4);
433 assert_eq!(found, expected_found_len);
434 } else {
435 panic!("Unexpected error {:?}", err);
436 }
437 }
438
439 #[test]
440 fn insufficient_buffer() {
441 let mut sender = TestSender::default();
442 for i in 0..3 {
443 let reporter = EventReporter::new(EXAMPLE_APID, i);
444 assert!(reporter.is_some());
445 let mut reporter = reporter.unwrap();
446 check_buf_too_small(&mut reporter, &mut sender, i);
447 }
448 }
449}