# Rust Flight Executive (RFE) RFE is a framework for building real-time embedded applications in Rust, inspired by concepts from NASA's Core Flight Executive (cFE). Rather than being a direct port of cFE to Rust, RFE is designed as an alternative that embraces Rust's idioms and strengths. Unlike cFE which primarily targets computer systems, RFE extends support to microcontrollers and other resource-constrained embedded devices. It provides a message-passing architecture for inter-application communication, time management, and scheduling at different rates. I/O operations are handled either by the standard library (when the `std` feature is enabled) or by board support packages like `rp2040-hal` for embedded platforms. These board support packages can be used generically through the traits defined in the `embedded-hal` crate, allowing for portable code across different embedded platforms. ## Overview RFE is a framework that enables the development of modular, reusable flight software applications in Rust. While it shares some architectural concepts with NASA's cFE, RFE makes different design choices that are more ergonomic and better aligned with Rust's safety features, performance characteristics, and modern development ecosystem. By supporting both standard computers and microcontrollers, RFE offers greater flexibility for embedded systems development across a wide range of hardware platforms. ## 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) - **Platform abstraction** through feature flags: - `std`: Standard library support (Unix, Windows) - `rp2040`: Raspberry Pi Pico support - More platform abstractions planned for future releases - Support for custom platforms by implementing a few simple traits - **Runtime reflection**: - `reflect`: Runtime type information that can be used for dynamic command generation and telemetry processing - **Connectors**: A trait-based system for communication between instances, with built-in implementations (TCP, UDP, Memory) and support for custom user-defined connectors - **Core applications**: - Data Storage (DS): For telemetry storage - Health & Safety (HS): For system monitoring - Telemetry Output (TO): For telemetry management - Example app: Demonstrating application development ## Architecture RFE follows a modular architecture where applications communicate through a message bus. The core components include: - **RfeInstance**: The main framework instance that manages applications and scheduling. An instance is intended as a runner for apps - multiple apps can be added to a single instance, but thread/task priority is given to each instance rather than to individual apps. For each app to have its own task priority, you will need a separate instance for each app. - **App trait**: Interface that all applications must implement - **Rfe**: Interface for applications to interact with the framework - **Connectors**: A trait-based interface for communication between different RFE instances, allowing users to implement custom message transport mechanisms. Instances can communicate with each other through these connectors. ## Getting Started ### Prerequisites - Rust toolchain (latest stable recommended) - For RP2040 development: `thumbv6m-none-eabi` target ### Building ```bash # For standard desktop build cargo build --package example_build # For Raspberry Pi Pico cargo build --package rp_pico_example --target thumbv6m-none-eabi ``` ### Creating an Application Applications in RFE must implement the `App` trait: ```rust use rfe::*; use anyhow::Result; struct MyApp; impl App for MyApp { fn init(&mut self, rfe: &mut Rfe) -> Result<()> { // Initialize the application // Set up message subscriptions rfe.subscribe(TargetMsg::new(rfe.get_instance(), MsgKind::YourMsgKind)); Ok(()) } fn run(&mut self, rfe: &mut Rfe) { // Run the application logic // Process received messages while let Some(msg) = rfe.recv() { // Handle message } } fn hk(&mut self, rfe: &mut Rfe) { // Generate housekeeping data rfe.send(Msg::YourHkMsg(self.your_hk_data.clone())); } fn out_data(&mut self, rfe: &mut Rfe) { // Generate output data rfe.send(Msg::YourOutDataMsg(self.your_out_data.clone())); } fn get_app_rate(&self) -> Rate { // Set the intended rate at which your app runs Rate::Hz10 // Run at 10Hz } } ``` ### Running an RFE Instance ```rust use rfe::*; use rfe::connector::MemConnector; use std::thread; use std::sync::Arc; // Create a shared memory connector for inter-instance communication let (connector1, connector2) = Arc::new(MemConnector::new()); // First instance (you can set the priority of the thread you run the instance in) thread::spawn(move || { // Create a time driver for the first instance let time_driver = StdTimeDriver::new(); // Create the first RFE instance let mut instance1 = RfeInstance::new(Instance::Main, &time_driver); // Add the memory connector to the instance instance1.add_connector(connector1); // Add applications to the first instance let mut my_app1 = MyApp::new(); instance1.add_app("my_app1", &mut my_app1).unwrap(); // Run the first instance instance1.start(); }); // Second instance let time_driver2 = StdTimeDriver::new(); let mut instance2 = RfeInstance::new(Instance::Secondary, &time_driver2); // Add the memory connector to the second instance instance2.add_connector(connector_clone); // Add applications to the second instance let mut my_app2 = AnotherApp::new(); instance2.add_app("my_app2", &mut my_app2).unwrap(); // Run the second instance instance2.start(); ``` In this example: - Two RFE instances are created, each with its own application - The first instance runs in a separate thread. You can set the priority of this thread - The second instance runs in the main thread - A shared `MemConnector` allows the instances to communicate with each other - Each app can send messages that will be routed to the appropriate instance ## Project Structure - `rfe/`: Core framework implementation - `apps/`: Application implementations - `ds/`: Data Storage application - `hs/`: Health & Safety application - `to/`: Telemetry Output application - `example/`: Example application - `builds/`: Example builds for different platforms - `example_build/`: Standard desktop build - `rp_pico_example/`: Raspberry Pi Pico build - `ground/`: Ground system with GUI - `tools/`: Development tools - `decom/`: Telemetry decommutation tool ## Platform Support RFE supports multiple platforms through feature flags: - **Standard Desktop** (`std` feature): Unix, Windows - **Raspberry Pi Pico** (`rp2040` feature): Embedded ARM Cortex-M0+ - Users can add support for any platform by implementing just a few key traits (primarily the `TimeDriver` trait) - Board support packages utilize the `embedded-hal` traits for generic, portable hardware abstraction - Platform-specific functionality can be easily integrated through Rust's trait system - Additional platform support is planned for future releases The `reflect` feature can be enabled on any platform to provide runtime type information.