Skip to main content

cu29_runtime/
app.rs

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