Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Events

Events are an important mechanism used for remote systems to monitor unexpected or expected anomalies and events occuring on these systems. They can improve the observability of a system significantly and provide a “paper trail” of what is happening or has happened on a satellite where regular housekeeping packets might not be sufficient. They can also be used for fault detection, isolation and recovery (FDIR) purposes. For example, higher level system objects can listen on certain high criticality events to initiate custom system responses.

Event Severity

Generally, it also makes sense to classify events according to a severity system so operators can quickly judge the importance of an event. sat-rs does not constrain the severity classes or enforce their usage, but a severity classification like this can make sense:

  • INFO
  • LOW ERROR
  • MEDIUM ERROR
  • HIGH ERROR

Modelling Events with Rust

Usually, events will be associated with certain software objects or handlers. Oftentimes, developers and operators want to supply parameters or metadata associated with an event. This can all be done using the Rust enum type.

Let’s start with an example: a camera device handler might have the following events:

  • Image taken event
  • Communication error event including an error classifier
  • Communication timeout event with the configured timeout
  • Overheating event

You can model these events using the following data structure, also including a severity method.

#![allow(unused)]
fn main() {
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
pub enum Event {
    ImageTaken,
    CommunicationError(ErrorType),
    CommunicationTimeout(core::time::Duration),
    Overheating
}

impl Event {
    pub fn severity(&self) -> Severity {
        match self {
            Event::ImageTaken => Severity::Info,
            Event::CommunicationError(_) => Severity::Low,
            Event::CommunicationTimeout(_) => Severity::Low,
            Event::Overheating => Severity::High,
        } 
    }
}
}

Depending on the requirements of your system, you might want to filter which events are packaged and sent as telemetry. This requires an identification system. A simple scheme would be to add something like this:

#![allow(unused)]
fn main() {
impl Event {
    pub fn id(&self) -> u32 {
        match self {
            Event::ImageTaken => 0,
            Event::CommunicationError(_) => 1,
            Event::CommunicationTimeout(_) => 2,
            Event::Overheating => 3,
        } 
    }
}

}

Handling events

When an event occurs in the system, you want to trigger the event. This usually includes sending the event to a centralized event funnel. The funnel takes care of packing the event into a telemetry packet as well as forwarding the event to any other objects which are interested in the event. A message queue system is the best solution for this. For example, on an embedded Linux system, you might have an event sender handle like this inside your camera device handler:

#![allow(unused)]
fn main() {
pub struct CameraHandler {
    // (...)
    event_sender: std::sync::mpsc::SyncSender<Event>
}
}