Skip to main content

nros_platform/board/
entry.rs

1//! [`BoardEntry`] — Phase 212.N.1.
2//!
3//! The single boot-driver trait every Entry pkg `main.rs` invokes:
4//!
5//! ```ignore
6//! fn main() {
7//!     let _ = <MyBoard as BoardEntry>::run(|runtime| {
8//!         run_plan(runtime)         // codegen-emitted (212.N.4)
9//!     });
10//! }
11//! ```
12//!
13//! `run` owns the full lifecycle:
14//!
15//! 1. [`super::BoardInit::init_hardware`]
16//! 2. [`super::TransportBringup::init_transport`] (if implemented)
17//! 3. [`super::NetworkWait::wait_link_up`] (if implemented)
18//! 4. Open executor, build `RuntimeCtx`, invoke `setup(runtime)`.
19//! 5. Spin executor to completion (or termination signal).
20//! 6. [`super::BoardExit::exit_success`] / `exit_failure`.
21//!
22//! The exact body lives in the family driver crates (212.N.2); the
23//! trait here pins the signature so codegen + user Entry pkg can
24//! call it without knowing the family.
25
26use super::runtime::RuntimeCtx;
27
28/// Deploy-metadata overlay threaded from `nros::main!()` into the board's
29/// boot config (issue #48 cause 1).
30///
31/// The `nros::main!()` macro reads the Entry pkg's
32/// `[package.metadata.nros.deploy.<board>]` block at expansion time and bakes
33/// the present keys here. Each field is `None` when the deploy block omitted
34/// it, so the board overlays only the supplied values onto its own
35/// `Config::default()` (the firmware's compiled-in default stays the source of
36/// truth for everything the deploy block does not name).
37///
38/// Boards whose `BoardEntry::run` ignores network/locator config (POSIX hosts,
39/// RTIC/Embassy MCUs that take their transport elsewhere) inherit the default
40/// [`BoardEntry::run_with_deploy`] body, which drops the overlay and calls
41/// [`BoardEntry::run`] — so adding fields here never touches those boards.
42#[derive(Clone, Copy, Default, Debug)]
43pub struct DeployOverlay {
44    /// `locator = "tcp/10.0.2.2:7451"` — the zenoh/RMW endpoint the firmware
45    /// dials. `None` → keep the board default.
46    pub locator: Option<&'static str>,
47    /// `ip = "10.0.2.15"` — static guest IP. `None` → keep the board default.
48    pub ip: Option<[u8; 4]>,
49    /// `gateway = "10.0.2.2"` — default route. `None` → keep the board default.
50    pub gateway: Option<[u8; 4]>,
51    /// `netmask = "255.255.255.0"`. `None` → keep the board default.
52    pub netmask: Option<[u8; 4]>,
53    /// `domain_id = 0` — ROS 2 domain. `None` → keep the board default.
54    pub domain_id: Option<u32>,
55    /// `transport = "xrce"` — select a board custom transport that must be
56    /// installed BEFORE the linked RMW registers (e.g. an XRCE-over-UART vtable).
57    /// `None` → the board's default transport. Honored by
58    /// [`BoardEntry::setup_transport`] (phase-244.D1).
59    pub transport: Option<&'static str>,
60}
61
62/// Per-board boot driver.
63///
64/// Implementations live in the family driver crates
65/// (`nros-board-posix`, `nros-board-freertos`, …). Per-board crates
66/// (`nros-board-mps2-an385-freertos`, …) plug the family.
67pub trait BoardEntry: super::Board {
68    /// Drive the full boot → user-closure → exit flow.
69    ///
70    /// `setup` receives a `&mut RuntimeCtx` with overlay knobs from
71    /// the launch file / CLI args. Returning `Err` from `setup` makes
72    /// `run` route to [`super::BoardExit::exit_failure`]; `Ok`
73    /// proceeds to executor spin + clean exit.
74    ///
75    /// **Returns `Result`, not `!`.** The legacy
76    /// `nros-board-common::board_init::BoardEntry::run` diverged;
77    /// 212.N keeps the option to return so unit tests can drive it
78    /// in a hosted process without `exit()` killing the test
79    /// harness. Production boards still call `exit_*` from inside
80    /// `run`'s body after spin returns.
81    fn run<F, E>(setup: F) -> Result<(), E>
82    where
83        F: FnOnce(&mut RuntimeCtx<'_>) -> Result<(), E>,
84        E: core::fmt::Debug;
85
86    /// Boot like [`run`](Self::run) but apply a deploy-metadata overlay to the
87    /// board's boot config first (issue #48 cause 1).
88    ///
89    /// The default body **ignores** `deploy` and forwards to
90    /// [`run`](Self::run); boards that compile a network/locator config (the
91    /// FreeRTOS / bare-metal firmware boards) override it to overlay the
92    /// supplied fields onto their `Config::default()`. `nros::main!()` calls
93    /// this (not `run`) for `target_os = "none"` OwnedSpin targets so the
94    /// `[package.metadata.nros.deploy.<board>]` block stops being inert.
95    fn run_with_deploy<F, E>(_deploy: &DeployOverlay, setup: F) -> Result<(), E>
96    where
97        F: FnOnce(&mut RuntimeCtx<'_>) -> Result<(), E>,
98        E: core::fmt::Debug,
99    {
100        Self::run(setup)
101    }
102
103    /// Install a board custom transport selected by `deploy.transport`, BEFORE
104    /// the linked RMW registers (phase-244.D1). `nros::main!()` calls this on
105    /// `target_os = "none"` immediately before `__register_linked_rmw()` — the
106    /// ordering an XRCE-over-UART vtable needs (`set_custom_transport_ops` must
107    /// precede `register`). The default body is a no-op (the board's built-in
108    /// transport needs no pre-register install); boards with a feature-gated
109    /// custom transport (e.g. mps2-an385 `xrce-transport`) override it. Failures
110    /// are the board's to handle (it owns `exit_failure`).
111    fn setup_transport(_deploy: &DeployOverlay) {}
112}