Skip to main content

cu29_runtime/
app.rs

1use crate::curuntime::KeyFrame;
2use cu29_traits::CopperListTuple;
3use cu29_traits::CuResult;
4use cu29_unifiedlog::{SectionStorage, UnifiedLogWrite};
5
6#[cfg(feature = "std")]
7use crate::copperlist::CopperList;
8#[cfg(not(feature = "std"))]
9use alloc::vec::Vec;
10#[cfg(feature = "std")]
11use cu29_clock::RobotClockMock;
12#[cfg(feature = "std")]
13use std::vec::Vec;
14
15#[cfg(not(feature = "std"))]
16mod imp {
17    pub use alloc::string::String;
18}
19
20#[cfg(feature = "std")]
21mod imp {
22    pub use crate::config::CuConfig;
23    pub use crate::simulation::SimOverride;
24    pub use cu29_clock::RobotClock;
25    pub use cu29_unifiedlog::memmap::MmapSectionStorage;
26    pub use std::sync::{Arc, Mutex};
27}
28
29use imp::*;
30
31/// Convenience trait for CuApplication when it is just a std App
32#[cfg(feature = "std")]
33pub trait CuStdApplication:
34    CuApplication<MmapSectionStorage, cu29_unifiedlog::UnifiedLoggerWrite>
35{
36}
37
38#[cfg(feature = "std")]
39impl<T> CuStdApplication for T where
40    T: CuApplication<MmapSectionStorage, cu29_unifiedlog::UnifiedLoggerWrite>
41{
42}
43
44/// Compile-time subsystem identity embedded in generated Copper applications.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46pub struct Subsystem {
47    id: Option<&'static str>,
48    code: u16,
49}
50
51impl Subsystem {
52    #[inline]
53    pub const fn new(id: Option<&'static str>, code: u16) -> Self {
54        Self { id, code }
55    }
56
57    #[inline]
58    pub const fn id(self) -> Option<&'static str> {
59        self.id
60    }
61
62    #[inline]
63    pub const fn code(self) -> u16 {
64        self.code
65    }
66}
67
68/// Compile-time subsystem identity embedded in generated Copper applications.
69pub trait CuSubsystemMetadata {
70    /// Multi-Copper subsystem identity for this generated application.
71    fn subsystem() -> Subsystem;
72}
73
74/// A trait that defines the structure and behavior of a CuApplication.
75///
76/// CuApplication is the normal, running on robot version of an application and its runtime.
77///
78/// The `CuApplication` trait outlines the necessary functions required for managing an application lifecycle,
79/// including configuration management, initialization, task execution, and runtime control. It is meant to be
80/// implemented by types that represent specific applications, providing them with unified control and execution features.
81///
82/// This is the more generic version that allows you to specify a custom unified logger.
83pub trait CuApplication<S: SectionStorage, L: UnifiedLogWrite<S> + 'static> {
84    /// Returns the original configuration as a string, typically loaded from a RON file.
85    /// This configuration represents the default settings for the application before any overrides.
86    fn get_original_config() -> String;
87
88    /// Starts all tasks managed by the application/runtime.
89    ///
90    /// # Returns
91    /// * `Ok(())` - If all tasks are started successfully.
92    /// * `Err(CuResult)` - If an error occurs while attempting to start one
93    ///   or more tasks.
94    fn start_all_tasks(&mut self) -> CuResult<()>;
95
96    /// Executes a single iteration of copper-generated runtime (generating and logging one copperlist)
97    ///
98    /// # Returns
99    ///
100    /// * `CuResult<()>` - Returns `Ok(())` if the iteration completes successfully, or an error
101    ///   wrapped in `CuResult` if something goes wrong during execution.
102    ///
103    fn run_one_iteration(&mut self) -> CuResult<()>;
104
105    /// Runs indefinitely looping over run_one_iteration
106    ///
107    /// # Returns
108    ///
109    /// Returns a `CuResult<()>`, which indicates the success or failure of the
110    /// operation.
111    /// - On success, the result is `Ok(())`.
112    /// - On failure, an appropriate error wrapped in `CuResult` is returned.
113    fn run(&mut self) -> CuResult<()>;
114
115    /// Stops all tasks managed by the application/runtime.
116    ///
117    /// # Returns
118    ///
119    /// Returns a `CuResult<()>`, which indicates the success or failure of the
120    /// operation.
121    /// - On success, the result is `Ok(())`.
122    /// - On failure, an appropriate error wrapped in `CuResult` is returned.
123    ///
124    fn stop_all_tasks(&mut self) -> CuResult<()>;
125
126    /// Restore all tasks from the given frozen state
127    fn restore_keyframe(&mut self, freezer: &KeyFrame) -> CuResult<()>;
128}
129
130/// A trait that defines the structure and behavior of a simulation-enabled CuApplication.
131///
132/// CuSimApplication is the simulation version of an application and its runtime, allowing
133/// overriding of steps with simulated behavior.
134///
135/// The `CuSimApplication` trait outlines the necessary functions required for managing an application lifecycle
136/// in simulation mode, including configuration management, initialization, task execution, and runtime control.
137#[cfg(feature = "std")]
138pub trait CuSimApplication<S: SectionStorage, L: UnifiedLogWrite<S> + 'static> {
139    /// The type representing a simulation step that can be overridden
140    type Step<'z>;
141
142    /// Returns the original configuration as a string, typically loaded from a RON file.
143    /// This configuration represents the default settings for the application before any overrides.
144    fn get_original_config() -> String;
145
146    /// Returns the mission id this generated application is bound to, when applicable.
147    fn mission_id() -> Option<&'static str> {
148        None
149    }
150
151    /// Starts all tasks managed by the application/runtime in simulation mode.
152    ///
153    /// # Arguments
154    /// * `sim_callback` - A mutable function reference that allows overriding individual simulation steps.
155    ///
156    /// # Returns
157    /// * `Ok(())` - If all tasks are started successfully.
158    /// * `Err(CuResult)` - If an error occurs while attempting to start one
159    ///   or more tasks.
160    fn start_all_tasks(
161        &mut self,
162        sim_callback: &mut impl for<'z> FnMut(Self::Step<'z>) -> SimOverride,
163    ) -> CuResult<()>;
164
165    /// Executes a single iteration of copper-generated runtime in simulation mode.
166    ///
167    /// # Arguments
168    /// * `sim_callback` - A mutable function reference that allows overriding individual simulation steps.
169    ///
170    /// # Returns
171    ///
172    /// * `CuResult<()>` - Returns `Ok(())` if the iteration completes successfully, or an error
173    ///   wrapped in `CuResult` if something goes wrong during execution.
174    fn run_one_iteration(
175        &mut self,
176        sim_callback: &mut impl for<'z> FnMut(Self::Step<'z>) -> SimOverride,
177    ) -> CuResult<()>;
178
179    /// Runs indefinitely looping over run_one_iteration in simulation mode
180    ///
181    /// # Arguments
182    /// * `sim_callback` - A mutable function reference that allows overriding individual simulation steps.
183    ///
184    /// # Returns
185    ///
186    /// Returns a `CuResult<()>`, which indicates the success or failure of the
187    /// operation.
188    /// - On success, the result is `Ok(())`.
189    /// - On failure, an appropriate error wrapped in `CuResult` is returned.
190    fn run(
191        &mut self,
192        sim_callback: &mut impl for<'z> FnMut(Self::Step<'z>) -> SimOverride,
193    ) -> CuResult<()>;
194
195    /// Stops all tasks managed by the application/runtime in simulation mode.
196    ///
197    /// # Arguments
198    /// * `sim_callback` - A mutable function reference that allows overriding individual simulation steps.
199    ///
200    /// # Returns
201    ///
202    /// Returns a `CuResult<()>`, which indicates the success or failure of the
203    /// operation.
204    /// - On success, the result is `Ok(())`.
205    /// - On failure, an appropriate error wrapped in `CuResult` is returned.
206    fn stop_all_tasks(
207        &mut self,
208        sim_callback: &mut impl for<'z> FnMut(Self::Step<'z>) -> SimOverride,
209    ) -> CuResult<()>;
210
211    /// Restore all tasks from the given frozen state
212    fn restore_keyframe(&mut self, freezer: &KeyFrame) -> CuResult<()>;
213}
214
215/// Optional introspection hook exposing the latest runtime-generated CopperList snapshot.
216///
217/// This is remote-debug-only: debugger conveniences must not add unconditional
218/// runtime-path overhead to normal Copper builds. Non-`remote-debug` builds
219/// should implement this as a cheap `None`.
220pub trait CurrentRuntimeCopperList<P: CopperListTuple> {
221    fn current_runtime_copperlist_bytes(&self) -> Option<&[u8]>;
222
223    fn set_current_runtime_copperlist_bytes(&mut self, snapshot: Option<Vec<u8>>) {
224        let _ = snapshot;
225    }
226}
227
228/// Simulation-enabled applications that can replay a recorded CopperList verbatim.
229///
230/// This is the exact-output replay primitive used by deterministic re-sim flows:
231/// task outputs and bridge receives are overridden from the recorded CopperList,
232/// bridge sends are skipped, and an optional recorded keyframe can be injected
233/// verbatim when the current CL is expected to capture one.
234#[cfg(feature = "std")]
235pub trait CuRecordedReplayApplication<S: SectionStorage, L: UnifiedLogWrite<S> + 'static>:
236    CuSimApplication<S, L>
237{
238    /// The generated recorded CopperList payload set for this application.
239    type RecordedDataSet: CopperListTuple;
240
241    /// Replay one recorded CopperList exactly as logged.
242    fn replay_recorded_copperlist(
243        &mut self,
244        clock_mock: &RobotClockMock,
245        copperlist: &CopperList<Self::RecordedDataSet>,
246        keyframe: Option<&KeyFrame>,
247    ) -> CuResult<()>;
248}
249
250/// Simulation-enabled applications that can be instantiated for distributed replay.
251///
252/// This extends exact-output replay with the one extra capability the
253/// distributed engine needs: build a replayable app for a specific
254/// deployment `instance_id` while keeping app construction type-safe.
255#[cfg(feature = "std")]
256pub trait CuDistributedReplayApplication<S: SectionStorage, L: UnifiedLogWrite<S> + 'static>:
257    CuRecordedReplayApplication<S, L> + CuSubsystemMetadata
258{
259    /// Build this app for deterministic distributed replay.
260    fn build_distributed_replay(
261        clock: RobotClock,
262        unified_logger: Arc<Mutex<L>>,
263        instance_id: u32,
264        config_override: Option<CuConfig>,
265    ) -> CuResult<Self>
266    where
267        Self: Sized;
268}