rename subfolders, add more READMEs
This commit is contained in:
477
satrs-core/src/pus/event.rs
Normal file
477
satrs-core/src/pus/event.rs
Normal file
@ -0,0 +1,477 @@
|
||||
use crate::pus::{source_buffer_large_enough, EcssTmError, EcssTmSender};
|
||||
use spacepackets::ecss::EcssEnumeration;
|
||||
use spacepackets::tm::PusTm;
|
||||
use spacepackets::tm::PusTmSecondaryHeader;
|
||||
use spacepackets::{SpHeader, MAX_APID};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use allocvec::EventReporter;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
pub enum Subservices {
|
||||
TmInfoReport = 1,
|
||||
TmLowSeverityReport = 2,
|
||||
TmMediumSeverityReport = 3,
|
||||
TmHighSeverityReport = 4,
|
||||
TcEnableEventGeneration = 5,
|
||||
TcDisableEventGeneration = 6,
|
||||
TcReportDisabledList = 7,
|
||||
TmDisabledEventsReport = 8,
|
||||
}
|
||||
|
||||
impl From<Subservices> for u8 {
|
||||
fn from(enumeration: Subservices) -> Self {
|
||||
enumeration as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Subservices {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
x if x == Subservices::TmInfoReport as u8 => Ok(Subservices::TmInfoReport),
|
||||
x if x == Subservices::TmLowSeverityReport as u8 => {
|
||||
Ok(Subservices::TmLowSeverityReport)
|
||||
}
|
||||
x if x == Subservices::TmMediumSeverityReport as u8 => {
|
||||
Ok(Subservices::TmMediumSeverityReport)
|
||||
}
|
||||
x if x == Subservices::TmHighSeverityReport as u8 => {
|
||||
Ok(Subservices::TmHighSeverityReport)
|
||||
}
|
||||
x if x == Subservices::TcEnableEventGeneration as u8 => {
|
||||
Ok(Subservices::TcEnableEventGeneration)
|
||||
}
|
||||
x if x == Subservices::TcDisableEventGeneration as u8 => {
|
||||
Ok(Subservices::TcDisableEventGeneration)
|
||||
}
|
||||
x if x == Subservices::TcReportDisabledList as u8 => {
|
||||
Ok(Subservices::TcReportDisabledList)
|
||||
}
|
||||
x if x == Subservices::TmDisabledEventsReport as u8 => {
|
||||
Ok(Subservices::TmDisabledEventsReport)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct EventReporterBase {
|
||||
msg_count: u16,
|
||||
apid: u16,
|
||||
pub dest_id: u16,
|
||||
}
|
||||
|
||||
impl EventReporterBase {
|
||||
pub fn new(apid: u16) -> Option<Self> {
|
||||
if apid > MAX_APID {
|
||||
return None;
|
||||
}
|
||||
Some(Self {
|
||||
msg_count: 0,
|
||||
dest_id: 0,
|
||||
apid,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn event_info<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<(), EcssTmError<E>> {
|
||||
self.generate_and_send_generic_tm(
|
||||
buf,
|
||||
Subservices::TmInfoReport,
|
||||
sender,
|
||||
time_stamp,
|
||||
event_id,
|
||||
aux_data,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn event_low_severity<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<(), EcssTmError<E>> {
|
||||
self.generate_and_send_generic_tm(
|
||||
buf,
|
||||
Subservices::TmLowSeverityReport,
|
||||
sender,
|
||||
time_stamp,
|
||||
event_id,
|
||||
aux_data,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn event_medium_severity<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<(), EcssTmError<E>> {
|
||||
self.generate_and_send_generic_tm(
|
||||
buf,
|
||||
Subservices::TmMediumSeverityReport,
|
||||
sender,
|
||||
time_stamp,
|
||||
event_id,
|
||||
aux_data,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn event_high_severity<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<(), EcssTmError<E>> {
|
||||
self.generate_and_send_generic_tm(
|
||||
buf,
|
||||
Subservices::TmHighSeverityReport,
|
||||
sender,
|
||||
time_stamp,
|
||||
event_id,
|
||||
aux_data,
|
||||
)
|
||||
}
|
||||
|
||||
fn generate_and_send_generic_tm<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
subservice: Subservices,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<(), EcssTmError<E>> {
|
||||
let tm = self.generate_generic_event_tm(buf, subservice, time_stamp, event_id, aux_data)?;
|
||||
sender.send_tm(tm)?;
|
||||
self.msg_count += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_generic_event_tm<'a, E>(
|
||||
&'a self,
|
||||
buf: &'a mut [u8],
|
||||
subservice: Subservices,
|
||||
time_stamp: &'a [u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<PusTm, EcssTmError<E>> {
|
||||
let mut src_data_len = event_id.byte_width();
|
||||
if let Some(aux_data) = aux_data {
|
||||
src_data_len += aux_data.len();
|
||||
}
|
||||
source_buffer_large_enough(buf.len(), src_data_len)?;
|
||||
let mut sp_header = SpHeader::tm(self.apid, 0, 0).unwrap();
|
||||
let sec_header = PusTmSecondaryHeader::new(
|
||||
5,
|
||||
subservice.into(),
|
||||
self.msg_count,
|
||||
self.dest_id,
|
||||
time_stamp,
|
||||
);
|
||||
let mut current_idx = 0;
|
||||
event_id.write_to_be_bytes(&mut buf[0..event_id.byte_width()])?;
|
||||
current_idx += event_id.byte_width();
|
||||
if let Some(aux_data) = aux_data {
|
||||
buf[current_idx..current_idx + aux_data.len()].copy_from_slice(aux_data);
|
||||
current_idx += aux_data.len();
|
||||
}
|
||||
Ok(PusTm::new(
|
||||
&mut sp_header,
|
||||
sec_header,
|
||||
Some(&buf[0..current_idx]),
|
||||
true,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
mod allocvec {
|
||||
use super::*;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub struct EventReporter {
|
||||
source_data_buf: Vec<u8>,
|
||||
pub reporter: EventReporterBase,
|
||||
}
|
||||
|
||||
impl EventReporter {
|
||||
pub fn new(apid: u16, max_event_id_and_aux_data_size: usize) -> Option<Self> {
|
||||
let reporter = EventReporterBase::new(apid)?;
|
||||
Some(Self {
|
||||
source_data_buf: vec![0; max_event_id_and_aux_data_size],
|
||||
reporter,
|
||||
})
|
||||
}
|
||||
pub fn event_info<E>(
|
||||
&mut self,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<(), EcssTmError<E>> {
|
||||
self.reporter.event_info(
|
||||
self.source_data_buf.as_mut_slice(),
|
||||
sender,
|
||||
time_stamp,
|
||||
event_id,
|
||||
aux_data,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn event_low_severity<E>(
|
||||
&mut self,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<(), EcssTmError<E>> {
|
||||
self.reporter.event_low_severity(
|
||||
self.source_data_buf.as_mut_slice(),
|
||||
sender,
|
||||
time_stamp,
|
||||
event_id,
|
||||
aux_data,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn event_medium_severity<E>(
|
||||
&mut self,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<(), EcssTmError<E>> {
|
||||
self.reporter.event_medium_severity(
|
||||
self.source_data_buf.as_mut_slice(),
|
||||
sender,
|
||||
time_stamp,
|
||||
event_id,
|
||||
aux_data,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn event_high_severity<E>(
|
||||
&mut self,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<(), EcssTmError<E>> {
|
||||
self.reporter.event_high_severity(
|
||||
self.source_data_buf.as_mut_slice(),
|
||||
sender,
|
||||
time_stamp,
|
||||
event_id,
|
||||
aux_data,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::events::{EventU32, Severity};
|
||||
use crate::pus::tests::CommonTmInfo;
|
||||
use spacepackets::ByteConversionError;
|
||||
use std::collections::VecDeque;
|
||||
use std::vec::Vec;
|
||||
|
||||
const EXAMPLE_APID: u16 = 0xee;
|
||||
const EXAMPLE_GROUP_ID: u16 = 2;
|
||||
const EXAMPLE_EVENT_ID_0: u16 = 1;
|
||||
#[allow(dead_code)]
|
||||
const EXAMPLE_EVENT_ID_1: u16 = 2;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
struct TmInfo {
|
||||
pub common: CommonTmInfo,
|
||||
pub event: EventU32,
|
||||
pub aux_data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct TestSender {
|
||||
pub service_queue: VecDeque<TmInfo>,
|
||||
}
|
||||
|
||||
impl EcssTmSender for TestSender {
|
||||
type Error = ();
|
||||
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<()>> {
|
||||
assert!(tm.source_data().is_some());
|
||||
let src_data = tm.source_data().unwrap();
|
||||
assert!(src_data.len() >= 4);
|
||||
let event = EventU32::from(u32::from_be_bytes(src_data[0..4].try_into().unwrap()));
|
||||
let mut aux_data = Vec::new();
|
||||
if src_data.len() > 4 {
|
||||
aux_data.extend_from_slice(&src_data[4..]);
|
||||
}
|
||||
self.service_queue.push_back(TmInfo {
|
||||
common: CommonTmInfo::new_from_tm(&tm),
|
||||
event,
|
||||
aux_data,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn severity_to_subservice(severity: Severity) -> Subservices {
|
||||
match severity {
|
||||
Severity::INFO => Subservices::TmInfoReport,
|
||||
Severity::LOW => Subservices::TmLowSeverityReport,
|
||||
Severity::MEDIUM => Subservices::TmMediumSeverityReport,
|
||||
Severity::HIGH => Subservices::TmHighSeverityReport,
|
||||
}
|
||||
}
|
||||
|
||||
fn report_basic_event(
|
||||
reporter: &mut EventReporter,
|
||||
sender: &mut TestSender,
|
||||
time_stamp: &[u8],
|
||||
event: EventU32,
|
||||
severity: Severity,
|
||||
aux_data: Option<&[u8]>,
|
||||
) {
|
||||
match severity {
|
||||
Severity::INFO => {
|
||||
reporter
|
||||
.event_info(sender, time_stamp, event, aux_data)
|
||||
.expect("Error reporting info event");
|
||||
}
|
||||
Severity::LOW => {
|
||||
reporter
|
||||
.event_low_severity(sender, time_stamp, event, aux_data)
|
||||
.expect("Error reporting low event");
|
||||
}
|
||||
Severity::MEDIUM => {
|
||||
reporter
|
||||
.event_medium_severity(sender, time_stamp, event, aux_data)
|
||||
.expect("Error reporting medium event");
|
||||
}
|
||||
Severity::HIGH => {
|
||||
reporter
|
||||
.event_high_severity(sender, time_stamp, event, aux_data)
|
||||
.expect("Error reporting high event");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn basic_event_test(
|
||||
max_event_aux_data_buf: usize,
|
||||
severity: Severity,
|
||||
error_data: Option<&[u8]>,
|
||||
) {
|
||||
let mut sender = TestSender::default();
|
||||
let reporter = EventReporter::new(EXAMPLE_APID, max_event_aux_data_buf);
|
||||
assert!(reporter.is_some());
|
||||
let mut reporter = reporter.unwrap();
|
||||
let time_stamp_empty: [u8; 7] = [0; 7];
|
||||
let mut error_copy = Vec::new();
|
||||
if let Some(err_data) = error_data {
|
||||
error_copy.extend_from_slice(err_data);
|
||||
}
|
||||
let event = EventU32::new(severity, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0)
|
||||
.expect("Error creating example event");
|
||||
report_basic_event(
|
||||
&mut reporter,
|
||||
&mut sender,
|
||||
&time_stamp_empty,
|
||||
event,
|
||||
severity,
|
||||
error_data,
|
||||
);
|
||||
assert_eq!(sender.service_queue.len(), 1);
|
||||
let tm_info = sender.service_queue.pop_front().unwrap();
|
||||
assert_eq!(
|
||||
tm_info.common.subservice,
|
||||
severity_to_subservice(severity) as u8
|
||||
);
|
||||
assert_eq!(tm_info.common.dest_id, 0);
|
||||
assert_eq!(tm_info.common.time_stamp, time_stamp_empty);
|
||||
assert_eq!(tm_info.common.msg_counter, 0);
|
||||
assert_eq!(tm_info.common.apid, EXAMPLE_APID);
|
||||
assert_eq!(tm_info.event, event);
|
||||
assert_eq!(tm_info.aux_data, error_copy);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_info_event_generation() {
|
||||
basic_event_test(4, Severity::INFO, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_low_severity_event() {
|
||||
basic_event_test(4, Severity::LOW, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_medium_severity_event() {
|
||||
basic_event_test(4, Severity::MEDIUM, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_high_severity_event() {
|
||||
basic_event_test(4, Severity::HIGH, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn event_with_info_string() {
|
||||
let info_string = "Test Information";
|
||||
basic_event_test(32, Severity::INFO, Some(info_string.as_bytes()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn low_severity_with_raw_err_data() {
|
||||
let raw_err_param: i32 = -1;
|
||||
let raw_err = raw_err_param.to_be_bytes();
|
||||
basic_event_test(8, Severity::LOW, Some(&raw_err))
|
||||
}
|
||||
|
||||
fn check_buf_too_small(
|
||||
reporter: &mut EventReporter,
|
||||
sender: &mut TestSender,
|
||||
expected_found_len: usize,
|
||||
) {
|
||||
let time_stamp_empty: [u8; 7] = [0; 7];
|
||||
let event = EventU32::new(Severity::INFO, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0)
|
||||
.expect("Error creating example event");
|
||||
let err = reporter.event_info(sender, &time_stamp_empty, event, None);
|
||||
assert!(err.is_err());
|
||||
let err = err.unwrap_err();
|
||||
if let EcssTmError::ByteConversionError(ByteConversionError::ToSliceTooSmall(missmatch)) =
|
||||
err
|
||||
{
|
||||
assert_eq!(missmatch.expected, 4);
|
||||
assert_eq!(missmatch.found, expected_found_len);
|
||||
} else {
|
||||
panic!("Unexpected error {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insufficient_buffer() {
|
||||
let mut sender = TestSender::default();
|
||||
for i in 0..3 {
|
||||
let reporter = EventReporter::new(EXAMPLE_APID, i);
|
||||
assert!(reporter.is_some());
|
||||
let mut reporter = reporter.unwrap();
|
||||
check_buf_too_small(&mut reporter, &mut sender, i);
|
||||
}
|
||||
}
|
||||
}
|
310
satrs-core/src/pus/event_man.rs
Normal file
310
satrs-core/src/pus/event_man.rs
Normal file
@ -0,0 +1,310 @@
|
||||
use crate::events::{EventU32, EventU32TypedSev, GenericEvent, HasSeverity, Severity};
|
||||
use alloc::boxed::Box;
|
||||
use core::hash::Hash;
|
||||
use hashbrown::HashSet;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use crate::pus::event::EventReporter;
|
||||
use crate::pus::verification::{TcStateStarted, VerificationToken};
|
||||
use crate::pus::{EcssTmError, EcssTmSender};
|
||||
#[cfg(feature = "heapless")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "heapless")))]
|
||||
pub use heapless_mod::*;
|
||||
|
||||
/// This trait allows the PUS event manager implementation to stay generic over various types
|
||||
/// of backend containers.
|
||||
///
|
||||
/// These backend containers keep track on whether a particular event is enabled or disabled for
|
||||
/// reporting and also expose a simple API to enable or disable the event reporting.
|
||||
///
|
||||
/// For example, a straight forward implementation for host systems could use a
|
||||
/// [hash set](https://docs.rs/hashbrown/latest/hashbrown/struct.HashSet.html)
|
||||
/// structure to track disabled events. A more primitive and embedded friendly
|
||||
/// solution could track this information in a static or pre-allocated list which contains
|
||||
/// the disabled events.
|
||||
pub trait PusEventMgmtBackendProvider<Provider: GenericEvent> {
|
||||
type Error;
|
||||
|
||||
fn event_enabled(&self, event: &Provider) -> bool;
|
||||
fn enable_event_reporting(&mut self, event: &Provider) -> Result<bool, Self::Error>;
|
||||
fn disable_event_reporting(&mut self, event: &Provider) -> Result<bool, Self::Error>;
|
||||
}
|
||||
|
||||
/// Default backend provider which uses a hash set as the event reporting status container
|
||||
/// like mentioned in the example of the [PusEventMgmtBackendProvider] documentation.
|
||||
///
|
||||
/// This provider is a good option for host systems or larger embedded systems where
|
||||
/// the expected occasional memory allocation performed by the [HashSet] is not an issue.
|
||||
pub struct DefaultPusMgmtBackendProvider<Event: GenericEvent = EventU32> {
|
||||
disabled: HashSet<Event>,
|
||||
}
|
||||
|
||||
/// Safety: All contained field are [Send] as well
|
||||
unsafe impl<Event: GenericEvent + Send> Send for DefaultPusMgmtBackendProvider<Event> {}
|
||||
|
||||
impl<Event: GenericEvent> Default for DefaultPusMgmtBackendProvider<Event> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
disabled: HashSet::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Provider: GenericEvent + PartialEq + Eq + Hash + Copy + Clone>
|
||||
PusEventMgmtBackendProvider<Provider> for DefaultPusMgmtBackendProvider<Provider>
|
||||
{
|
||||
type Error = ();
|
||||
fn event_enabled(&self, event: &Provider) -> bool {
|
||||
!self.disabled.contains(event)
|
||||
}
|
||||
|
||||
fn enable_event_reporting(&mut self, event: &Provider) -> Result<bool, Self::Error> {
|
||||
Ok(self.disabled.remove(event))
|
||||
}
|
||||
|
||||
fn disable_event_reporting(&mut self, event: &Provider) -> Result<bool, Self::Error> {
|
||||
Ok(self.disabled.insert(*event))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "heapless")]
|
||||
pub mod heapless_mod {
|
||||
use super::*;
|
||||
use crate::events::{GenericEvent, LargestEventRaw};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "heapless")))]
|
||||
// TODO: After a new version of heapless is released which uses hash32 version 0.3, try using
|
||||
// regular Event type again.
|
||||
#[derive(Default)]
|
||||
pub struct HeaplessPusMgmtBackendProvider<const N: usize, Provider: GenericEvent> {
|
||||
disabled: heapless::FnvIndexSet<LargestEventRaw, N>,
|
||||
phantom: PhantomData<Provider>,
|
||||
}
|
||||
|
||||
/// Safety: All contained field are [Send] as well
|
||||
unsafe impl<const N: usize, Event: GenericEvent + Send> Send
|
||||
for HeaplessPusMgmtBackendProvider<N, Event>
|
||||
{
|
||||
}
|
||||
|
||||
impl<const N: usize, Provider: GenericEvent> PusEventMgmtBackendProvider<Provider>
|
||||
for HeaplessPusMgmtBackendProvider<N, Provider>
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
fn event_enabled(&self, event: &Provider) -> bool {
|
||||
self.disabled.contains(&event.raw_as_largest_type())
|
||||
}
|
||||
|
||||
fn enable_event_reporting(&mut self, event: &Provider) -> Result<bool, Self::Error> {
|
||||
self.disabled
|
||||
.insert(event.raw_as_largest_type())
|
||||
.map_err(|_| ())
|
||||
}
|
||||
|
||||
fn disable_event_reporting(&mut self, event: &Provider) -> Result<bool, Self::Error> {
|
||||
Ok(self.disabled.remove(&event.raw_as_largest_type()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EventRequest<Event: GenericEvent = EventU32> {
|
||||
Enable(Event),
|
||||
Disable(Event),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventRequestWithToken<Event: GenericEvent = EventU32> {
|
||||
pub request: EventRequest<Event>,
|
||||
pub token: VerificationToken<TcStateStarted>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EventManError<SenderE> {
|
||||
EcssTmError(EcssTmError<SenderE>),
|
||||
SeverityMissmatch(Severity, Severity),
|
||||
}
|
||||
|
||||
impl<SenderE> From<EcssTmError<SenderE>> for EventManError<SenderE> {
|
||||
fn from(v: EcssTmError<SenderE>) -> Self {
|
||||
Self::EcssTmError(v)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PusEventDispatcher<BackendError, Provider: GenericEvent> {
|
||||
reporter: EventReporter,
|
||||
backend: Box<dyn PusEventMgmtBackendProvider<Provider, Error = BackendError>>,
|
||||
}
|
||||
|
||||
/// Safety: All contained fields are send as well.
|
||||
unsafe impl<E: Send, Event: GenericEvent + Send> Send for PusEventDispatcher<E, Event> {}
|
||||
|
||||
impl<BackendError, Provider: GenericEvent> PusEventDispatcher<BackendError, Provider> {
|
||||
pub fn new(
|
||||
reporter: EventReporter,
|
||||
backend: Box<dyn PusEventMgmtBackendProvider<Provider, Error = BackendError>>,
|
||||
) -> Self {
|
||||
Self { reporter, backend }
|
||||
}
|
||||
}
|
||||
|
||||
impl<BackendError, Event: GenericEvent> PusEventDispatcher<BackendError, Event> {
|
||||
pub fn enable_tm_for_event(&mut self, event: &Event) -> Result<bool, BackendError> {
|
||||
self.backend.enable_event_reporting(event)
|
||||
}
|
||||
|
||||
pub fn disable_tm_for_event(&mut self, event: &Event) -> Result<bool, BackendError> {
|
||||
self.backend.disable_event_reporting(event)
|
||||
}
|
||||
|
||||
pub fn generate_pus_event_tm_generic<E>(
|
||||
&mut self,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event: Event,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<bool, EventManError<E>> {
|
||||
if !self.backend.event_enabled(&event) {
|
||||
return Ok(false);
|
||||
}
|
||||
match event.severity() {
|
||||
Severity::INFO => self
|
||||
.reporter
|
||||
.event_info(sender, time_stamp, event, aux_data)
|
||||
.map(|_| true)
|
||||
.map_err(|e| e.into()),
|
||||
Severity::LOW => self
|
||||
.reporter
|
||||
.event_low_severity(sender, time_stamp, event, aux_data)
|
||||
.map(|_| true)
|
||||
.map_err(|e| e.into()),
|
||||
Severity::MEDIUM => self
|
||||
.reporter
|
||||
.event_medium_severity(sender, time_stamp, event, aux_data)
|
||||
.map(|_| true)
|
||||
.map_err(|e| e.into()),
|
||||
Severity::HIGH => self
|
||||
.reporter
|
||||
.event_high_severity(sender, time_stamp, event, aux_data)
|
||||
.map(|_| true)
|
||||
.map_err(|e| e.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<BackendError> PusEventDispatcher<BackendError, EventU32> {
|
||||
pub fn enable_tm_for_event_with_sev<Severity: HasSeverity>(
|
||||
&mut self,
|
||||
event: &EventU32TypedSev<Severity>,
|
||||
) -> Result<bool, BackendError> {
|
||||
self.backend.enable_event_reporting(event.as_ref())
|
||||
}
|
||||
|
||||
pub fn disable_tm_for_event_with_sev<Severity: HasSeverity>(
|
||||
&mut self,
|
||||
event: &EventU32TypedSev<Severity>,
|
||||
) -> Result<bool, BackendError> {
|
||||
self.backend.disable_event_reporting(event.as_ref())
|
||||
}
|
||||
|
||||
pub fn generate_pus_event_tm<E, Severity: HasSeverity>(
|
||||
&mut self,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event: EventU32TypedSev<Severity>,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<bool, EventManError<E>> {
|
||||
self.generate_pus_event_tm_generic(sender, time_stamp, event.into(), aux_data)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::events::SeverityInfo;
|
||||
use spacepackets::tm::PusTm;
|
||||
use std::sync::mpsc::{channel, SendError, TryRecvError};
|
||||
use std::vec::Vec;
|
||||
|
||||
const INFO_EVENT: EventU32TypedSev<SeverityInfo> =
|
||||
EventU32TypedSev::<SeverityInfo>::const_new(1, 0);
|
||||
const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5);
|
||||
const EMPTY_STAMP: [u8; 7] = [0; 7];
|
||||
|
||||
struct EventTmSender {
|
||||
sender: std::sync::mpsc::Sender<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl EcssTmSender for EventTmSender {
|
||||
type Error = SendError<Vec<u8>>;
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<Self::Error>> {
|
||||
let mut vec = Vec::new();
|
||||
tm.append_to_vec(&mut vec)?;
|
||||
self.sender.send(vec).map_err(EcssTmError::SendError)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn create_basic_man() -> PusEventDispatcher<(), EventU32> {
|
||||
let reporter = EventReporter::new(0x02, 128).expect("Creating event repoter failed");
|
||||
let backend = DefaultPusMgmtBackendProvider::<EventU32>::default();
|
||||
PusEventDispatcher::new(reporter, Box::new(backend))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic() {
|
||||
let mut event_man = create_basic_man();
|
||||
let (event_tx, event_rx) = channel();
|
||||
let mut sender = EventTmSender { sender: event_tx };
|
||||
let event_sent = event_man
|
||||
.generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None)
|
||||
.expect("Sending info event failed");
|
||||
|
||||
assert!(event_sent);
|
||||
// Will not check packet here, correctness of packet was tested somewhere else
|
||||
event_rx.try_recv().expect("Receiving event TM failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_disable_event() {
|
||||
let mut event_man = create_basic_man();
|
||||
let (event_tx, event_rx) = channel();
|
||||
let mut sender = EventTmSender { sender: event_tx };
|
||||
let res = event_man.disable_tm_for_event(&LOW_SEV_EVENT);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
let mut event_sent = event_man
|
||||
.generate_pus_event_tm_generic(&mut sender, &EMPTY_STAMP, LOW_SEV_EVENT, None)
|
||||
.expect("Sending low severity event failed");
|
||||
assert!(!event_sent);
|
||||
let res = event_rx.try_recv();
|
||||
assert!(res.is_err());
|
||||
assert!(matches!(res.unwrap_err(), TryRecvError::Empty));
|
||||
// Check that only the low severity event was disabled
|
||||
event_sent = event_man
|
||||
.generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None)
|
||||
.expect("Sending info event failed");
|
||||
assert!(event_sent);
|
||||
event_rx.try_recv().expect("No info event received");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reenable_event() {
|
||||
let mut event_man = create_basic_man();
|
||||
let (event_tx, event_rx) = channel();
|
||||
let mut sender = EventTmSender { sender: event_tx };
|
||||
let mut res = event_man.disable_tm_for_event_with_sev(&INFO_EVENT);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
res = event_man.enable_tm_for_event_with_sev(&INFO_EVENT);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
let event_sent = event_man
|
||||
.generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None)
|
||||
.expect("Sending info event failed");
|
||||
assert!(event_sent);
|
||||
event_rx.try_recv().expect("No info event received");
|
||||
}
|
||||
}
|
93
satrs-core/src/pus/mod.rs
Normal file
93
satrs-core/src/pus/mod.rs
Normal file
@ -0,0 +1,93 @@
|
||||
//! All PUS support modules
|
||||
//!
|
||||
//! Currenty includes:
|
||||
//!
|
||||
//! 1. PUS Verification Service 1 module inside [verification]. Requires [alloc] support.
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
use spacepackets::ecss::PusError;
|
||||
use spacepackets::time::TimestampError;
|
||||
use spacepackets::tm::PusTm;
|
||||
use spacepackets::{ByteConversionError, SizeMissmatch};
|
||||
|
||||
pub mod event;
|
||||
pub mod event_man;
|
||||
pub mod verification;
|
||||
|
||||
/// Generic error type which is also able to wrap a user send error with the user supplied type E.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EcssTmError<E> {
|
||||
/// Errors related to sending the verification telemetry to a TM recipient
|
||||
SendError(E),
|
||||
/// Errors related to the time stamp format of the telemetry
|
||||
TimestampError(TimestampError),
|
||||
/// Errors related to byte conversion, for example insufficient buffer size for given data
|
||||
ByteConversionError(ByteConversionError),
|
||||
/// Errors related to PUS packet format
|
||||
PusError(PusError),
|
||||
}
|
||||
|
||||
impl<E> From<PusError> for EcssTmError<E> {
|
||||
fn from(e: PusError) -> Self {
|
||||
EcssTmError::PusError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<ByteConversionError> for EcssTmError<E> {
|
||||
fn from(e: ByteConversionError) -> Self {
|
||||
EcssTmError::ByteConversionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic trait for a user supplied sender object.
|
||||
///
|
||||
/// This sender object is responsible for sending telemetry to a TM sink. The [Downcast] trait
|
||||
/// is implemented to allow passing the sender as a boxed trait object and still retrieve the
|
||||
/// concrete type at a later point.
|
||||
pub trait EcssTmSender: Downcast + Send {
|
||||
type Error;
|
||||
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<Self::Error>>;
|
||||
}
|
||||
|
||||
impl_downcast!(EcssTmSender assoc Error);
|
||||
|
||||
pub(crate) fn source_buffer_large_enough<E>(cap: usize, len: usize) -> Result<(), EcssTmError<E>> {
|
||||
if len > cap {
|
||||
return Err(EcssTmError::ByteConversionError(
|
||||
ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
||||
found: cap,
|
||||
expected: len,
|
||||
}),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use spacepackets::tm::{PusTm, PusTmSecondaryHeaderT};
|
||||
use spacepackets::CcsdsPacket;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub(crate) struct CommonTmInfo {
|
||||
pub subservice: u8,
|
||||
pub apid: u16,
|
||||
pub msg_counter: u16,
|
||||
pub dest_id: u16,
|
||||
pub time_stamp: [u8; 7],
|
||||
}
|
||||
|
||||
impl CommonTmInfo {
|
||||
pub fn new_from_tm(tm: &PusTm) -> Self {
|
||||
let mut time_stamp = [0; 7];
|
||||
time_stamp.clone_from_slice(&tm.time_stamp()[0..7]);
|
||||
Self {
|
||||
subservice: tm.subservice(),
|
||||
apid: tm.apid(),
|
||||
msg_counter: tm.msg_counter(),
|
||||
dest_id: tm.dest_id(),
|
||||
time_stamp,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1892
satrs-core/src/pus/verification.rs
Normal file
1892
satrs-core/src/pus/verification.rs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user