docs
This commit is contained in:
@@ -0,0 +1,56 @@
|
|||||||
|
# Real-time Framework for Embedded systems (RFE)
|
||||||
|
|
||||||
|
RFE is a framework for building real-time embedded applications in Rust. It provides a message-passing architecture for inter-application communication, time management, and scheduling at different rates.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Message-passing architecture for inter-application communication
|
||||||
|
- Time management for both system and monotonic time
|
||||||
|
- Scheduling of applications at different rates (1Hz to 100Hz)
|
||||||
|
- Support for different platforms through feature flags
|
||||||
|
- Connectors for communication between instances (TCP, UDP, Memory)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To use RFE, you need to create an RfeInstance, add applications to it, and then run it at 100Hz. Applications must implement the App trait.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rfe::*;
|
||||||
|
|
||||||
|
struct MyApp;
|
||||||
|
|
||||||
|
impl App for MyApp {
|
||||||
|
fn init(&mut self, rfe: &mut Rfe) -> anyhow::Result<()> {
|
||||||
|
// Initialize the application
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self, rfe: &mut Rfe) {
|
||||||
|
// Run the application
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hk(&mut self, rfe: &mut Rfe) {
|
||||||
|
// Generate housekeeping data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn out_data(&mut self, rfe: &mut Rfe) {
|
||||||
|
// Generate output data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_app_rate(&self) -> Rate {
|
||||||
|
Rate::Hz10 // Run at 10Hz
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Platform Support
|
||||||
|
|
||||||
|
RFE supports multiple platforms through feature flags:
|
||||||
|
|
||||||
|
- `std`: Standard library support (Unix, Windows)
|
||||||
|
- `rp2040`: Raspberry Pi Pico support
|
||||||
|
- `reflect`: Runtime type information for debugging
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License - see the LICENSE file for details.
|
||||||
@@ -1,11 +1,35 @@
|
|||||||
|
/*!
|
||||||
|
* Connector module for RFE
|
||||||
|
*
|
||||||
|
* This module provides the Connector trait and implementations for various
|
||||||
|
* communication methods between RFE instances, including:
|
||||||
|
* - Memory connectors for inter-process communication
|
||||||
|
* - TCP connectors for network communication
|
||||||
|
* - UDP connectors for network communication
|
||||||
|
*/
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use crate::msg::MsgPacket;
|
use crate::msg::MsgPacket;
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
/// Connector trait for inter-instance communication
|
||||||
|
///
|
||||||
|
/// This trait defines the interface for connectors that enable communication
|
||||||
|
/// between RFE instances. Connectors are responsible for sending and receiving
|
||||||
|
/// messages between instances.
|
||||||
pub trait Connector: Debug {
|
pub trait Connector: Debug {
|
||||||
|
/// Send messages to another instance
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `msgs` - The messages to send
|
||||||
fn send(&mut self, msgs: Vec<MsgPacket>);
|
fn send(&mut self, msgs: Vec<MsgPacket>);
|
||||||
|
|
||||||
|
/// Receive messages from another instance
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * `Some(Vec<MsgPacket>)` - If messages are available
|
||||||
|
/// * `None` - If no messages are available
|
||||||
fn recv(&mut self) -> Option<Vec<MsgPacket>>;
|
fn recv(&mut self) -> Option<Vec<MsgPacket>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,100 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Real-time Framework for Embedded systems (RFE)
|
||||||
|
*
|
||||||
|
* RFE is a framework for building real-time embedded applications in Rust.
|
||||||
|
* It provides a message-passing architecture for inter-application communication,
|
||||||
|
* time management, and scheduling at different rates.
|
||||||
|
*
|
||||||
|
* # Features
|
||||||
|
*
|
||||||
|
* - Message-passing architecture for inter-application communication
|
||||||
|
* - Time management for both system and monotonic time
|
||||||
|
* - Scheduling of applications at different rates (1Hz to 100Hz)
|
||||||
|
* - Support for different platforms through feature flags
|
||||||
|
* - Connectors for communication between instances (TCP, UDP, Memory)
|
||||||
|
*
|
||||||
|
* # Usage
|
||||||
|
*
|
||||||
|
* To use RFE, you need to create an RfeInstance, add applications to it,
|
||||||
|
* and then run it at 100Hz. Applications must implement the App trait.
|
||||||
|
*
|
||||||
|
* ```rust
|
||||||
|
* use rfe::*;
|
||||||
|
*
|
||||||
|
* struct MyApp;
|
||||||
|
*
|
||||||
|
* impl App for MyApp {
|
||||||
|
* fn init(&mut self, rfe: &mut Rfe) -> anyhow::Result<()> {
|
||||||
|
* // Initialize the application
|
||||||
|
* Ok(())
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* fn run(&mut self, rfe: &mut Rfe) {
|
||||||
|
* // Run the application
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* fn hk(&mut self, rfe: &mut Rfe) {
|
||||||
|
* // Generate housekeeping data
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* fn out_data(&mut self, rfe: &mut Rfe) {
|
||||||
|
* // Generate output data
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* fn get_app_rate(&self) -> Rate {
|
||||||
|
* Rate::Hz10 // Run at 10Hz
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
|
/// Connector module for inter-instance communication
|
||||||
pub mod connector;
|
pub mod connector;
|
||||||
use bincode::config::Configuration;
|
use bincode::config::Configuration;
|
||||||
|
/// Message module for defining message types
|
||||||
pub mod msg;
|
pub mod msg;
|
||||||
|
|
||||||
|
/// Core RFE implementation
|
||||||
mod rfe;
|
mod rfe;
|
||||||
pub use rfe::*;
|
pub use rfe::*;
|
||||||
|
|
||||||
|
/// Reflection module for runtime type information
|
||||||
#[cfg(feature = "reflect")]
|
#[cfg(feature = "reflect")]
|
||||||
pub mod reflect;
|
pub mod reflect;
|
||||||
|
|
||||||
|
/// Macros for RFE
|
||||||
pub use macros;
|
pub use macros;
|
||||||
|
/// Serial communication module
|
||||||
pub mod serial;
|
pub mod serial;
|
||||||
|
/// Time management module
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
/// Utility functions and types
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
/// Bincode configuration for serialization
|
||||||
pub const BINCODE_CONFIG: Configuration = bincode::config::standard();
|
pub const BINCODE_CONFIG: Configuration = bincode::config::standard();
|
||||||
|
|
||||||
|
/// Macro for unwrapping a Result and printing an error message if it fails
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `$x` - The Result to unwrap
|
||||||
|
/// * `$msg` - The error message to print if the Result is an Err
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// use rfe::unwrap_print_err;
|
||||||
|
/// use log::error;
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let result: Result<(), &str> = Err("error");
|
||||||
|
/// unwrap_print_err!(result, "Failed to do something");
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! unwrap_print_err {
|
macro_rules! unwrap_print_err {
|
||||||
($x:expr, $msg: tt) => {
|
($x:expr, $msg: tt) => {
|
||||||
|
|||||||
+252
-34
@@ -1,3 +1,12 @@
|
|||||||
|
/*!
|
||||||
|
* Real-time Framework for Embedded systems (RFE)
|
||||||
|
*
|
||||||
|
* This module provides the core functionality for the RFE framework:
|
||||||
|
* - Application scheduling at different rates (1Hz to 100Hz)
|
||||||
|
* - Message passing between applications
|
||||||
|
* - Time management for both system and monotonic time
|
||||||
|
* - Connector management for inter-instance communication
|
||||||
|
*/
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
@@ -13,99 +22,207 @@ use crate::{
|
|||||||
time::{TimeData, TimeDriver},
|
time::{TimeData, TimeDriver},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Housekeeping data trait
|
||||||
|
///
|
||||||
|
/// This trait is used to mark types that can be used as housekeeping data.
|
||||||
|
/// Housekeeping data is used to monitor the health and status of applications.
|
||||||
pub trait Hk: Sized + Clone + Copy + 'static + Send + Sync {}
|
pub trait Hk: Sized + Clone + Copy + 'static + Send + Sync {}
|
||||||
|
|
||||||
|
/// Blanket implementation for all types that meet the requirements
|
||||||
impl<T> Hk for T where T: Sized + Clone + Copy + 'static + Send + Sync {}
|
impl<T> Hk for T where T: Sized + Clone + Copy + 'static + Send + Sync {}
|
||||||
|
|
||||||
|
/// Output data trait
|
||||||
|
///
|
||||||
|
/// This trait is used to mark types that can be used as output data.
|
||||||
|
/// Output data is the primary data produced by applications.
|
||||||
pub trait OutData: Sized + Clone + Copy + 'static + Send + Sync {}
|
pub trait OutData: Sized + Clone + Copy + 'static + Send + Sync {}
|
||||||
|
|
||||||
|
/// Blanket implementation for all types that meet the requirements
|
||||||
impl<T> OutData for T where T: Sized + Clone + Copy + 'static + Send + Sync {}
|
impl<T> OutData for T where T: Sized + Clone + Copy + 'static + Send + Sync {}
|
||||||
|
|
||||||
|
/// Application execution rates
|
||||||
|
///
|
||||||
|
/// This enum defines the rates at which applications can be scheduled.
|
||||||
|
/// The RFE framework runs at 100Hz, and applications can be scheduled
|
||||||
|
/// at various rates derived from this base rate.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum Rate {
|
pub enum Rate {
|
||||||
|
/// 1Hz (once per second)
|
||||||
Hz1,
|
Hz1,
|
||||||
|
/// 5Hz (5 times per second)
|
||||||
Hz5,
|
Hz5,
|
||||||
|
/// 10Hz (10 times per second)
|
||||||
Hz10,
|
Hz10,
|
||||||
|
/// 20Hz (20 times per second)
|
||||||
Hz20,
|
Hz20,
|
||||||
|
/// 50Hz (50 times per second)
|
||||||
Hz50,
|
Hz50,
|
||||||
|
/// 100Hz (100 times per second, every tick)
|
||||||
Hz100,
|
Hz100,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reference to an RfeTime instance
|
||||||
|
///
|
||||||
|
/// This type alias is used to share a single time reference between multiple RFE instances.
|
||||||
type RfeTimeRef<'a> = Rc<RefCell<RfeTime<'a>>>;
|
type RfeTimeRef<'a> = Rc<RefCell<RfeTime<'a>>>;
|
||||||
|
|
||||||
|
/// Time management for RFE
|
||||||
|
///
|
||||||
|
/// This struct manages time for the RFE framework, providing both system time
|
||||||
|
/// and monotonic time through a TimeDriver implementation.
|
||||||
pub struct RfeTime<'a> {
|
pub struct RfeTime<'a> {
|
||||||
|
/// Time data including scheduler counter and time offset
|
||||||
time_data: TimeData,
|
time_data: TimeData,
|
||||||
|
/// Driver for time operations
|
||||||
time_driver: &'a dyn TimeDriver,
|
time_driver: &'a dyn TimeDriver,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Main RFE instance
|
||||||
|
///
|
||||||
|
/// This struct represents a single RFE instance, which can contain multiple applications
|
||||||
|
/// and connectors. It manages the scheduling of applications and the routing of messages.
|
||||||
pub struct RfeInstance<'a> {
|
pub struct RfeInstance<'a> {
|
||||||
|
/// List of applications registered with this instance
|
||||||
app_list: HashMap<&'a str, AppRef<'a>>,
|
app_list: HashMap<&'a str, AppRef<'a>>,
|
||||||
|
/// Reference to the time management
|
||||||
time: RfeTimeRef<'a>,
|
time: RfeTimeRef<'a>,
|
||||||
|
/// The instance identifier
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
instance: Instance,
|
instance: Instance,
|
||||||
|
/// List of connectors for inter-instance communication
|
||||||
connectors: Vec<ConnectorState<'a>>,
|
connectors: Vec<ConnectorState<'a>>,
|
||||||
|
/// Scheduler counter, incremented on each tick
|
||||||
sch_counter: u64,
|
sch_counter: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reference to an application
|
||||||
|
///
|
||||||
|
/// This struct holds a reference to an application and its associated RFE instance.
|
||||||
|
/// It also stores the rates at which the application should be run.
|
||||||
pub struct AppRef<'a> {
|
pub struct AppRef<'a> {
|
||||||
|
/// Reference to the application
|
||||||
app: &'a mut dyn App,
|
app: &'a mut dyn App,
|
||||||
|
/// Rate at which the application's run method should be called
|
||||||
app_rate: Rate,
|
app_rate: Rate,
|
||||||
|
/// Rate at which the application's out_data method should be called
|
||||||
out_data_rate: Rate,
|
out_data_rate: Rate,
|
||||||
|
/// Rate at which the application's hk method should be called
|
||||||
hk_rate: Rate,
|
hk_rate: Rate,
|
||||||
|
/// RFE instance for this application
|
||||||
rfe: Rfe<'a>,
|
rfe: Rfe<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Core RFE interface for applications
|
||||||
|
///
|
||||||
|
/// This struct provides the interface for applications to interact with the RFE framework.
|
||||||
|
/// It handles message subscription, sending, and receiving, as well as time management.
|
||||||
pub struct Rfe<'a> {
|
pub struct Rfe<'a> {
|
||||||
|
/// Messages this application is subscribed to
|
||||||
subscriptions: HashSet<TargetMsg>,
|
subscriptions: HashSet<TargetMsg>,
|
||||||
|
/// Messages to be sent to other applications or connectors
|
||||||
msgs_to_send: Vec<MsgPacket>,
|
msgs_to_send: Vec<MsgPacket>,
|
||||||
msgs_recevied: VecDeque<MsgPacket>,
|
/// Messages received from other applications or connectors
|
||||||
|
msgs_received: VecDeque<MsgPacket>,
|
||||||
|
/// The instance this RFE belongs to
|
||||||
instance: Instance,
|
instance: Instance,
|
||||||
|
/// Reference to the time management
|
||||||
time: RfeTimeRef<'a>,
|
time: RfeTimeRef<'a>,
|
||||||
|
/// Flag indicating if subscriptions have been updated
|
||||||
subs_updated: bool,
|
subs_updated: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// State of a connector
|
||||||
|
///
|
||||||
|
/// This struct holds a reference to a connector and its associated state.
|
||||||
|
/// It tracks subscriptions and subscription request timing.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ConnectorState<'a> {
|
pub struct ConnectorState<'a> {
|
||||||
|
/// Reference to the connector
|
||||||
connector: &'a mut dyn Connector,
|
connector: &'a mut dyn Connector,
|
||||||
|
/// Messages this connector is subscribed to
|
||||||
subscriptions: HashSet<TargetMsg>,
|
subscriptions: HashSet<TargetMsg>,
|
||||||
|
/// Flag indicating if subscriptions have been received
|
||||||
subs_received: bool,
|
subs_received: bool,
|
||||||
|
/// Scheduler counter value when subscriptions were last requested
|
||||||
subs_last_requested: u64,
|
subs_last_requested: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Rfe<'a> {
|
impl<'a> Rfe<'a> {
|
||||||
|
/// Creates a new RFE instance
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `instance` - The instance this RFE belongs to
|
||||||
|
/// * `time` - Reference to the time management
|
||||||
pub fn new(instance: Instance, time: RfeTimeRef<'a>) -> Self {
|
pub fn new(instance: Instance, time: RfeTimeRef<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
subscriptions: HashSet::new(),
|
subscriptions: HashSet::new(),
|
||||||
msgs_to_send: Vec::new(),
|
msgs_to_send: Vec::new(),
|
||||||
msgs_recevied: VecDeque::new(),
|
msgs_received: VecDeque::new(),
|
||||||
instance,
|
instance,
|
||||||
subs_updated: false,
|
subs_updated: false,
|
||||||
time,
|
time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the instance this RFE belongs to
|
||||||
pub fn get_instance(&self) -> Instance {
|
pub fn get_instance(&self) -> Instance {
|
||||||
return self.instance;
|
self.instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Subscribe to a message
|
||||||
|
///
|
||||||
|
/// This method subscribes the application to a specific message type.
|
||||||
|
/// The application will receive messages of this type from other applications
|
||||||
|
/// and connectors.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `msg` - The message type to subscribe to
|
||||||
pub fn subscribe(&mut self, msg: TargetMsg) {
|
pub fn subscribe(&mut self, msg: TargetMsg) {
|
||||||
self.subscriptions.insert(msg);
|
self.subscriptions.insert(msg);
|
||||||
self.subs_updated = true;
|
self.subs_updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Subscribe to multiple messages
|
||||||
|
///
|
||||||
|
/// This method subscribes the application to multiple message types.
|
||||||
|
/// The application will receive messages of these types from other applications
|
||||||
|
/// and connectors.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `msgs` - The message types to subscribe to
|
||||||
pub fn subscribe_all<T: IntoIterator<Item = TargetMsg>>(&mut self, msgs: T) {
|
pub fn subscribe_all<T: IntoIterator<Item = TargetMsg>>(&mut self, msgs: T) {
|
||||||
self.subscriptions.extend(msgs.into_iter());
|
self.subscriptions.extend(msgs.into_iter());
|
||||||
self.subs_updated = true;
|
self.subs_updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unsubscribe from a message
|
||||||
|
///
|
||||||
|
/// This method unsubscribes the application from a specific message type.
|
||||||
|
/// The application will no longer receive messages of this type.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `msg` - The message type to unsubscribe from
|
||||||
pub fn unsubscribe(&mut self, msg: &TargetMsg) {
|
pub fn unsubscribe(&mut self, msg: &TargetMsg) {
|
||||||
self.subscriptions.remove(msg);
|
self.subscriptions.remove(msg);
|
||||||
self.subs_updated = true;
|
self.subs_updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unsubscribe from all messages
|
||||||
|
///
|
||||||
|
/// This method unsubscribes the application from all message types.
|
||||||
|
/// The application will no longer receive any messages.
|
||||||
pub fn unsubscribe_all(&mut self) {
|
pub fn unsubscribe_all(&mut self) {
|
||||||
self.subscriptions.clear();
|
self.subscriptions.clear();
|
||||||
self.subs_updated = true;
|
self.subs_updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send a message
|
||||||
|
///
|
||||||
|
/// This method sends a message from this application to other applications
|
||||||
|
/// and connectors that are subscribed to this message type.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `msg` - The message to send
|
||||||
pub fn send(&mut self, msg: Msg) {
|
pub fn send(&mut self, msg: Msg) {
|
||||||
self.msgs_to_send.push(MsgPacket::new(
|
self.msgs_to_send.push(MsgPacket::new(
|
||||||
self.get_instance(),
|
self.get_instance(),
|
||||||
@@ -114,41 +231,110 @@ impl<'a> Rfe<'a> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send a command to a specific instance
|
||||||
|
///
|
||||||
|
/// This method sends a command to a specific instance.
|
||||||
|
/// The command will be received by applications in that instance
|
||||||
|
/// that are subscribed to this message type.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `msg` - The command to send
|
||||||
|
/// * `target` - The target instance to send the command to
|
||||||
pub fn send_cmd(&mut self, msg: Msg, target: Instance) {
|
pub fn send_cmd(&mut self, msg: Msg, target: Instance) {
|
||||||
self.msgs_to_send
|
self.msgs_to_send
|
||||||
.push(MsgPacket::new(target, msg, self.get_system_time()));
|
.push(MsgPacket::new(target, msg, self.get_system_time()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Posts a message to this RFE's receive queue
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `msg` - The message to post
|
||||||
pub fn post_message(&mut self, msg: MsgPacket) {
|
pub fn post_message(&mut self, msg: MsgPacket) {
|
||||||
self.msgs_recevied.push_back(msg);
|
self.msgs_received.push_back(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Receives a message from this RFE's receive queue
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * `Some(MsgPacket)` - If a message is available
|
||||||
|
/// * `None` - If no message is available
|
||||||
pub fn recv(&mut self) -> Option<MsgPacket> {
|
pub fn recv(&mut self) -> Option<MsgPacket> {
|
||||||
self.msgs_recevied.pop_front()
|
self.msgs_received.pop_front()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// time starting from power on or program start
|
/// Get mission elapsed time
|
||||||
|
///
|
||||||
|
/// This method returns the time in microseconds since power on or program start.
|
||||||
|
/// It is useful for measuring durations and scheduling events.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * Time in microseconds since power on or program start
|
||||||
pub fn get_met_time(&self) -> u64 {
|
pub fn get_met_time(&self) -> u64 {
|
||||||
let time = self.time.borrow();
|
let time = self.time.borrow();
|
||||||
time.time_driver.get_monotonic_time(time.time_data)
|
time.time_driver.get_monotonic_time(time.time_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Time in microseconds relative to system epoch
|
/// Get system time
|
||||||
|
///
|
||||||
|
/// This method returns the time in microseconds relative to the system epoch.
|
||||||
|
/// It is useful for timestamping events and correlating with external systems.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * Time in microseconds relative to the system epoch
|
||||||
pub fn get_system_time(&self) -> u64 {
|
pub fn get_system_time(&self) -> u64 {
|
||||||
let time = self.time.borrow();
|
let time = self.time.borrow();
|
||||||
time.time_driver.get_system_time(time.time_data)
|
time.time_driver.get_system_time(time.time_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Application trait for RFE applications
|
||||||
|
///
|
||||||
|
/// This trait defines the interface for applications to interact with the RFE framework.
|
||||||
|
/// Applications must implement this trait to be scheduled by the RFE framework.
|
||||||
pub trait App {
|
pub trait App {
|
||||||
|
/// Initialize the application
|
||||||
|
///
|
||||||
|
/// This method is called once when the application is added to the RFE instance.
|
||||||
|
/// Use this method to set up subscriptions and initialize the application state.
|
||||||
fn init(&mut self, rfe: &mut Rfe) -> Result<()>;
|
fn init(&mut self, rfe: &mut Rfe) -> Result<()>;
|
||||||
|
|
||||||
|
/// Run the application
|
||||||
|
///
|
||||||
|
/// This method is called at the rate specified by `get_app_rate()`.
|
||||||
|
/// Use this method to perform the main application logic.
|
||||||
fn run(&mut self, rfe: &mut Rfe);
|
fn run(&mut self, rfe: &mut Rfe);
|
||||||
|
|
||||||
|
/// Generate housekeeping data
|
||||||
|
///
|
||||||
|
/// This method is called at the rate specified by the RFE instance.
|
||||||
|
/// Use this method to generate housekeeping data for telemetry.
|
||||||
fn hk(&mut self, rfe: &mut Rfe);
|
fn hk(&mut self, rfe: &mut Rfe);
|
||||||
|
|
||||||
|
/// Generate output data
|
||||||
|
///
|
||||||
|
/// This method is called at the rate specified by the RFE instance.
|
||||||
|
/// Use this method to generate output data for telemetry.
|
||||||
fn out_data(&mut self, rfe: &mut Rfe);
|
fn out_data(&mut self, rfe: &mut Rfe);
|
||||||
|
|
||||||
|
/// Get the application rate
|
||||||
|
///
|
||||||
|
/// This method returns the rate at which the application should be run.
|
||||||
fn get_app_rate(&self) -> Rate;
|
fn get_app_rate(&self) -> Rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RfeInstance<'a> {
|
impl<'a> RfeInstance<'a> {
|
||||||
|
/// Create a new RFE instance
|
||||||
|
///
|
||||||
|
/// This method creates a new RFE instance with the specified instance identifier
|
||||||
|
/// and time driver. The instance identifier is used to identify this instance
|
||||||
|
/// when communicating with other instances.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `instance` - The instance identifier
|
||||||
|
/// * `time_driver` - The time driver to use for time management
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * A new RFE instance
|
||||||
pub fn new(instance: Instance, time_driver: &'a dyn TimeDriver) -> Self {
|
pub fn new(instance: Instance, time_driver: &'a dyn TimeDriver) -> Self {
|
||||||
let time = Rc::new(RefCell::new(RfeTime {
|
let time = Rc::new(RefCell::new(RfeTime {
|
||||||
time_data: TimeData {
|
time_data: TimeData {
|
||||||
@@ -166,32 +352,50 @@ impl<'a> RfeInstance<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add an application to the RFE instance
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `name` - The name of the application
|
||||||
|
/// * `app` - The application to add
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * `Ok(())` - If the application was added successfully
|
||||||
|
/// * `Err(...)` - If the application could not be added
|
||||||
pub fn add_app(&mut self, name: &'a str, app: &'a mut dyn App) -> Result<()> {
|
pub fn add_app(&mut self, name: &'a str, app: &'a mut dyn App) -> Result<()> {
|
||||||
if self.app_list.contains_key(name) {
|
if self.app_list.contains_key(name) {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"failed to add app {name}, already added an app with that name"
|
"Failed to add app '{name}': an app with that name already exists"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let app_rate = app.get_app_rate();
|
let app_rate = app.get_app_rate();
|
||||||
self.app_list.insert(
|
self.app_list.insert(
|
||||||
name,
|
name,
|
||||||
AppRef {
|
AppRef {
|
||||||
app: app,
|
app,
|
||||||
app_rate: app_rate,
|
app_rate,
|
||||||
hk_rate: Rate::Hz1,
|
hk_rate: Rate::Hz1,
|
||||||
out_data_rate: app_rate,
|
out_data_rate: app_rate,
|
||||||
rfe: Rfe::new(self.instance, self.time.clone()),
|
rfe: Rfe::new(self.instance, self.time.clone()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Initialize the application
|
||||||
let appref = self.app_list.get_mut(name).unwrap();
|
let appref = self.app_list.get_mut(name).unwrap();
|
||||||
if let Err(e) = appref.app.init(&mut appref.rfe) {
|
if let Err(e) = appref.app.init(&mut appref.rfe) {
|
||||||
error!("app {name} failed to initialize {e}");
|
error!("App '{name}' failed to initialize: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a connector to the RFE instance
|
||||||
|
///
|
||||||
|
/// Connectors are used to communicate with other RFE instances.
|
||||||
|
/// They can be used to send and receive messages between instances.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `connector` - The connector to add
|
||||||
pub fn add_connector(&mut self, connector: &'a mut dyn Connector) {
|
pub fn add_connector(&mut self, connector: &'a mut dyn Connector) {
|
||||||
self.connectors.push(ConnectorState {
|
self.connectors.push(ConnectorState {
|
||||||
connector,
|
connector,
|
||||||
@@ -201,40 +405,31 @@ impl<'a> RfeInstance<'a> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expected to be called at 100Hz
|
/// Run the RFE instance
|
||||||
|
///
|
||||||
|
/// This method should be called at 100Hz to schedule applications and process messages.
|
||||||
|
/// It runs applications, collects messages, and routes them to the appropriate destinations.
|
||||||
pub fn run(&mut self) {
|
pub fn run(&mut self) {
|
||||||
let mut msgs = Vec::new();
|
let mut msgs = Vec::new();
|
||||||
|
|
||||||
|
// Run applications and collect messages
|
||||||
for app in self.app_list.values_mut() {
|
for app in self.app_list.values_mut() {
|
||||||
if app.app_rate == Rate::Hz100
|
// Check if the application should run at this tick
|
||||||
|| (self.sch_counter % 2 == 0 && app.app_rate == Rate::Hz50)
|
if self.should_run_at_rate(app.app_rate) {
|
||||||
|| (self.sch_counter % 5 == 0 && app.app_rate == Rate::Hz20)
|
|
||||||
|| (self.sch_counter % 10 == 0 && app.app_rate == Rate::Hz10)
|
|
||||||
|| (self.sch_counter % 20 == 0 && app.app_rate == Rate::Hz5)
|
|
||||||
|| (self.sch_counter % 100 == 0 && app.app_rate == Rate::Hz1)
|
|
||||||
{
|
|
||||||
app.app.run(&mut app.rfe);
|
app.app.run(&mut app.rfe);
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.hk_rate == Rate::Hz100
|
// Check if housekeeping should run at this tick
|
||||||
|| (self.sch_counter % 2 == 0 && app.hk_rate == Rate::Hz50)
|
if self.should_run_at_rate(app.hk_rate) {
|
||||||
|| (self.sch_counter % 5 == 0 && app.hk_rate == Rate::Hz20)
|
|
||||||
|| (self.sch_counter % 10 == 0 && app.hk_rate == Rate::Hz10)
|
|
||||||
|| (self.sch_counter % 20 == 0 && app.hk_rate == Rate::Hz5)
|
|
||||||
|| (self.sch_counter % 100 == 0 && app.hk_rate == Rate::Hz1)
|
|
||||||
{
|
|
||||||
app.app.hk(&mut app.rfe);
|
app.app.hk(&mut app.rfe);
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.out_data_rate == Rate::Hz100
|
// Check if output data should run at this tick
|
||||||
|| (self.sch_counter % 2 == 0 && app.out_data_rate == Rate::Hz50)
|
if self.should_run_at_rate(app.out_data_rate) {
|
||||||
|| (self.sch_counter % 5 == 0 && app.out_data_rate == Rate::Hz20)
|
|
||||||
|| (self.sch_counter % 10 == 0 && app.out_data_rate == Rate::Hz10)
|
|
||||||
|| (self.sch_counter % 20 == 0 && app.out_data_rate == Rate::Hz5)
|
|
||||||
|| (self.sch_counter % 100 == 0 && app.out_data_rate == Rate::Hz1)
|
|
||||||
{
|
|
||||||
app.app.out_data(&mut app.rfe);
|
app.app.out_data(&mut app.rfe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect messages from the application
|
||||||
let new_msgs = core::mem::take(&mut app.rfe.msgs_to_send);
|
let new_msgs = core::mem::take(&mut app.rfe.msgs_to_send);
|
||||||
msgs.extend(new_msgs);
|
msgs.extend(new_msgs);
|
||||||
}
|
}
|
||||||
@@ -365,7 +560,30 @@ impl<'a> RfeInstance<'a> {
|
|||||||
self.sch_counter += 1;
|
self.sch_counter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper method to determine if a task should run at the given rate
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `rate` - The rate to check
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * `true` - If the task should run at this tick
|
||||||
|
/// * `false` - If the task should not run at this tick
|
||||||
|
fn should_run_at_rate(&self, rate: Rate) -> bool {
|
||||||
|
match rate {
|
||||||
|
Rate::Hz100 => true,
|
||||||
|
Rate::Hz50 => self.sch_counter % 2 == 0,
|
||||||
|
Rate::Hz20 => self.sch_counter % 5 == 0,
|
||||||
|
Rate::Hz10 => self.sch_counter % 10 == 0,
|
||||||
|
Rate::Hz5 => self.sch_counter % 20 == 0,
|
||||||
|
Rate::Hz1 => self.sch_counter % 100 == 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
|
/// Start the RFE instance
|
||||||
|
///
|
||||||
|
/// This method starts the RFE instance and runs it at 100Hz.
|
||||||
|
/// It blocks the current thread and never returns.
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
use std::{thread::sleep, time::Instant};
|
use std::{thread::sleep, time::Instant};
|
||||||
|
|||||||
+44
-2
@@ -1,17 +1,59 @@
|
|||||||
|
/*!
|
||||||
|
* Time management module for RFE
|
||||||
|
*
|
||||||
|
* This module provides time management functionality for the RFE framework,
|
||||||
|
* including:
|
||||||
|
* - Timestamp type for representing time
|
||||||
|
* - TimeData struct for storing time-related data
|
||||||
|
* - TimeDriver trait for platform-specific time implementations
|
||||||
|
* - Various TimeDriver implementations for different platforms
|
||||||
|
*/
|
||||||
|
|
||||||
/// Microseconds timestamp
|
/// Microseconds timestamp
|
||||||
|
///
|
||||||
|
/// This type represents time in microseconds, either as a duration or
|
||||||
|
/// as an absolute time relative to some epoch.
|
||||||
pub type Timestamp = u64;
|
pub type Timestamp = u64;
|
||||||
|
|
||||||
|
/// Time data for RFE
|
||||||
|
///
|
||||||
|
/// This struct stores time-related data for the RFE framework, including
|
||||||
|
/// the scheduler counter and time offset.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct TimeData {
|
pub struct TimeData {
|
||||||
|
/// Scheduler counter, incremented on each tick
|
||||||
pub sch_counter: u64,
|
pub sch_counter: u64,
|
||||||
|
/// Time offset in microseconds
|
||||||
pub time_offset: Timestamp,
|
pub time_offset: Timestamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Time driver trait
|
||||||
|
///
|
||||||
|
/// This trait defines the interface for platform-specific time implementations.
|
||||||
|
/// It provides methods for getting both system time and monotonic time.
|
||||||
pub trait TimeDriver {
|
pub trait TimeDriver {
|
||||||
/// Time in microseconds relative to system epoch
|
/// Get system time
|
||||||
|
///
|
||||||
|
/// This method returns the time in microseconds relative to the system epoch.
|
||||||
|
/// It is useful for timestamping events and correlating with external systems.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `time_data` - The time data to use
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * Time in microseconds relative to the system epoch
|
||||||
fn get_system_time(&self, time_data: TimeData) -> Timestamp;
|
fn get_system_time(&self, time_data: TimeData) -> Timestamp;
|
||||||
|
|
||||||
/// Time in microseconds since program start or power on
|
/// Get monotonic time
|
||||||
|
///
|
||||||
|
/// This method returns the time in microseconds since program start or power on.
|
||||||
|
/// It is useful for measuring durations and scheduling events.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `time_data` - The time data to use
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * Time in microseconds since program start or power on
|
||||||
fn get_monotonic_time(&self, time_data: TimeData) -> Timestamp;
|
fn get_monotonic_time(&self, time_data: TimeData) -> Timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user