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 crate::msg::MsgPacket;
|
||||
extern crate alloc;
|
||||
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 {
|
||||
/// Send messages to another instance
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `msgs` - The messages to send
|
||||
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>>;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,100 @@
|
||||
#![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")]
|
||||
extern crate std;
|
||||
|
||||
/// Connector module for inter-instance communication
|
||||
pub mod connector;
|
||||
use bincode::config::Configuration;
|
||||
/// Message module for defining message types
|
||||
pub mod msg;
|
||||
|
||||
/// Core RFE implementation
|
||||
mod rfe;
|
||||
pub use rfe::*;
|
||||
|
||||
/// Reflection module for runtime type information
|
||||
#[cfg(feature = "reflect")]
|
||||
pub mod reflect;
|
||||
|
||||
/// Macros for RFE
|
||||
pub use macros;
|
||||
/// Serial communication module
|
||||
pub mod serial;
|
||||
/// Time management module
|
||||
pub mod time;
|
||||
/// Utility functions and types
|
||||
pub mod utils;
|
||||
|
||||
/// Bincode configuration for serialization
|
||||
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_rules! unwrap_print_err {
|
||||
($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;
|
||||
use core::cell::RefCell;
|
||||
|
||||
@@ -13,99 +22,207 @@ use crate::{
|
||||
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 {}
|
||||
|
||||
/// Blanket implementation for all types that meet the requirements
|
||||
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 {}
|
||||
|
||||
/// Blanket implementation for all types that meet the requirements
|
||||
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)]
|
||||
pub enum Rate {
|
||||
/// 1Hz (once per second)
|
||||
Hz1,
|
||||
/// 5Hz (5 times per second)
|
||||
Hz5,
|
||||
/// 10Hz (10 times per second)
|
||||
Hz10,
|
||||
/// 20Hz (20 times per second)
|
||||
Hz20,
|
||||
/// 50Hz (50 times per second)
|
||||
Hz50,
|
||||
/// 100Hz (100 times per second, every tick)
|
||||
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>>>;
|
||||
|
||||
/// 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> {
|
||||
/// Time data including scheduler counter and time offset
|
||||
time_data: TimeData,
|
||||
/// Driver for time operations
|
||||
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> {
|
||||
/// List of applications registered with this instance
|
||||
app_list: HashMap<&'a str, AppRef<'a>>,
|
||||
/// Reference to the time management
|
||||
time: RfeTimeRef<'a>,
|
||||
/// The instance identifier
|
||||
#[allow(dead_code)]
|
||||
instance: Instance,
|
||||
/// List of connectors for inter-instance communication
|
||||
connectors: Vec<ConnectorState<'a>>,
|
||||
/// Scheduler counter, incremented on each tick
|
||||
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> {
|
||||
/// Reference to the application
|
||||
app: &'a mut dyn App,
|
||||
/// Rate at which the application's run method should be called
|
||||
app_rate: Rate,
|
||||
/// Rate at which the application's out_data method should be called
|
||||
out_data_rate: Rate,
|
||||
/// Rate at which the application's hk method should be called
|
||||
hk_rate: Rate,
|
||||
/// RFE instance for this application
|
||||
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> {
|
||||
/// Messages this application is subscribed to
|
||||
subscriptions: HashSet<TargetMsg>,
|
||||
/// Messages to be sent to other applications or connectors
|
||||
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,
|
||||
/// Reference to the time management
|
||||
time: RfeTimeRef<'a>,
|
||||
/// Flag indicating if subscriptions have been updated
|
||||
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)]
|
||||
pub struct ConnectorState<'a> {
|
||||
/// Reference to the connector
|
||||
connector: &'a mut dyn Connector,
|
||||
/// Messages this connector is subscribed to
|
||||
subscriptions: HashSet<TargetMsg>,
|
||||
/// Flag indicating if subscriptions have been received
|
||||
subs_received: bool,
|
||||
/// Scheduler counter value when subscriptions were last requested
|
||||
subs_last_requested: u64,
|
||||
}
|
||||
|
||||
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 {
|
||||
Self {
|
||||
subscriptions: HashSet::new(),
|
||||
msgs_to_send: Vec::new(),
|
||||
msgs_recevied: VecDeque::new(),
|
||||
msgs_received: VecDeque::new(),
|
||||
instance,
|
||||
subs_updated: false,
|
||||
time,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the instance this RFE belongs to
|
||||
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) {
|
||||
self.subscriptions.insert(msg);
|
||||
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) {
|
||||
self.subscriptions.extend(msgs.into_iter());
|
||||
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) {
|
||||
self.subscriptions.remove(msg);
|
||||
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) {
|
||||
self.subscriptions.clear();
|
||||
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) {
|
||||
self.msgs_to_send.push(MsgPacket::new(
|
||||
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) {
|
||||
self.msgs_to_send
|
||||
.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) {
|
||||
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> {
|
||||
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 {
|
||||
let time = self.time.borrow();
|
||||
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 {
|
||||
let time = self.time.borrow();
|
||||
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 {
|
||||
/// 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<()>;
|
||||
|
||||
/// 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);
|
||||
|
||||
/// 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);
|
||||
|
||||
/// 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);
|
||||
|
||||
/// Get the application rate
|
||||
///
|
||||
/// This method returns the rate at which the application should be run.
|
||||
fn get_app_rate(&self) -> Rate;
|
||||
}
|
||||
|
||||
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 {
|
||||
let time = Rc::new(RefCell::new(RfeTime {
|
||||
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<()> {
|
||||
if self.app_list.contains_key(name) {
|
||||
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();
|
||||
self.app_list.insert(
|
||||
name,
|
||||
AppRef {
|
||||
app: app,
|
||||
app_rate: app_rate,
|
||||
app,
|
||||
app_rate,
|
||||
hk_rate: Rate::Hz1,
|
||||
out_data_rate: app_rate,
|
||||
rfe: Rfe::new(self.instance, self.time.clone()),
|
||||
},
|
||||
);
|
||||
|
||||
// Initialize the application
|
||||
let appref = self.app_list.get_mut(name).unwrap();
|
||||
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) {
|
||||
self.connectors.push(ConnectorState {
|
||||
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) {
|
||||
let mut msgs = Vec::new();
|
||||
|
||||
// Run applications and collect messages
|
||||
for app in self.app_list.values_mut() {
|
||||
if app.app_rate == Rate::Hz100
|
||||
|| (self.sch_counter % 2 == 0 && app.app_rate == Rate::Hz50)
|
||||
|| (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)
|
||||
{
|
||||
// Check if the application should run at this tick
|
||||
if self.should_run_at_rate(app.app_rate) {
|
||||
app.app.run(&mut app.rfe);
|
||||
}
|
||||
|
||||
if app.hk_rate == Rate::Hz100
|
||||
|| (self.sch_counter % 2 == 0 && app.hk_rate == Rate::Hz50)
|
||||
|| (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)
|
||||
{
|
||||
// Check if housekeeping should run at this tick
|
||||
if self.should_run_at_rate(app.hk_rate) {
|
||||
app.app.hk(&mut app.rfe);
|
||||
}
|
||||
|
||||
if app.out_data_rate == Rate::Hz100
|
||||
|| (self.sch_counter % 2 == 0 && app.out_data_rate == Rate::Hz50)
|
||||
|| (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)
|
||||
{
|
||||
// Check if output data should run at this tick
|
||||
if self.should_run_at_rate(app.out_data_rate) {
|
||||
app.app.out_data(&mut app.rfe);
|
||||
}
|
||||
|
||||
// Collect messages from the application
|
||||
let new_msgs = core::mem::take(&mut app.rfe.msgs_to_send);
|
||||
msgs.extend(new_msgs);
|
||||
}
|
||||
@@ -365,7 +560,30 @@ impl<'a> RfeInstance<'a> {
|
||||
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")]
|
||||
/// 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) {
|
||||
use core::time::Duration;
|
||||
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
|
||||
///
|
||||
/// This type represents time in microseconds, either as a duration or
|
||||
/// as an absolute time relative to some epoch.
|
||||
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)]
|
||||
pub struct TimeData {
|
||||
/// Scheduler counter, incremented on each tick
|
||||
pub sch_counter: u64,
|
||||
/// Time offset in microseconds
|
||||
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 {
|
||||
/// 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;
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user