Skip to main content

zerodds_rtc/
semantics.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3
4//! Execution-Semantics-Profile — Spec §5.3.
5//!
6//! Erweiterungen ueber Lightweight RTC hinaus, die domain-spezifische
7//! Verhaltens-Pattern definieren:
8//! * §5.3.1 Periodic Sampled Data Processing → `DataFlowComponentAction`.
9//! * §5.3.2 Stimulus Response Processing → `FsmComponentAction`.
10//! * §5.3.3 Modes of Operation → `ModeOfOperation` +
11//!   `MultiModeComponentAction`.
12
13use crate::lifecycle::ComponentAction;
14use crate::return_code::ReturnCode;
15
16/// Spec §5.3.1 — `DataFlowComponentAction`. Erweitert
17/// `ComponentAction` um Periodic-Sampled-Data-Processing-Callbacks.
18///
19/// Pro Tick (mit Frequenz `ExecutionContext::get_rate`) werden
20/// `on_execute` (Read-Only-Phase) gefolgt von `on_state_update`
21/// (State-Mutation-Phase) invoked.
22pub trait DataFlowComponentAction: ComponentAction {
23    /// Spec §5.3.1.x — Read-Only-Phase eines Periodic-Tick.
24    fn on_execute(&mut self, _exec_handle: u32) -> ReturnCode {
25        ReturnCode::Ok
26    }
27    /// Spec §5.3.1.x — State-Mutation-Phase eines Periodic-Tick.
28    fn on_state_update(&mut self, _exec_handle: u32) -> ReturnCode {
29        ReturnCode::Ok
30    }
31    /// Spec §5.3.1.x — `on_rate_changed` wenn `ExecutionContext::
32    /// set_rate` invoked.
33    fn on_rate_changed(&mut self, _exec_handle: u32) -> ReturnCode {
34        ReturnCode::Ok
35    }
36}
37
38/// Spec §5.3.2 — `FsmComponentAction`. Stimulus-Response-Processing
39/// als Finite-State-Machine.
40pub trait FsmComponentAction: ComponentAction {
41    /// Spec §5.3.2 — wird invoked wenn Event eintrifft (Caller-defined
42    /// Event-Type).
43    fn on_action(&mut self, _exec_handle: u32) -> ReturnCode {
44        ReturnCode::Ok
45    }
46}
47
48/// `ModeOfOperation` (Spec §5.3.3) — abstrakte Mode-Identifikation.
49/// Implementor definiert konkretes Mode-Set (z.B. `enum AutonomousMode
50/// { Idle, Driving, Charging }`).
51pub trait ModeOfOperation: core::fmt::Debug + Copy + PartialEq + Eq {
52    /// Mode-Name (Spec-konform stringbasiert in Introspection).
53    fn name(&self) -> &str;
54}
55
56/// Spec §5.3.3 — `MultiModeComponentAction`. Erweitert
57/// `ComponentAction` um Mode-Wechsel-Callbacks.
58pub trait MultiModeComponentAction<M: ModeOfOperation>: ComponentAction {
59    /// Spec §5.3.3.x — `on_mode_changed`. Wird beim Wechsel von
60    /// `from_mode` nach `to_mode` invoked.
61    fn on_mode_changed(&mut self, _from: M, _to: M, _exec_handle: u32) -> ReturnCode {
62        ReturnCode::Ok
63    }
64}
65
66#[cfg(test)]
67#[allow(clippy::expect_used)]
68mod tests {
69    use super::*;
70
71    struct DataFlowStub {
72        executes: u32,
73        state_updates: u32,
74        rate_changes: u32,
75    }
76    impl ComponentAction for DataFlowStub {}
77    impl DataFlowComponentAction for DataFlowStub {
78        fn on_execute(&mut self, _h: u32) -> ReturnCode {
79            self.executes += 1;
80            ReturnCode::Ok
81        }
82        fn on_state_update(&mut self, _h: u32) -> ReturnCode {
83            self.state_updates += 1;
84            ReturnCode::Ok
85        }
86        fn on_rate_changed(&mut self, _h: u32) -> ReturnCode {
87            self.rate_changes += 1;
88            ReturnCode::Ok
89        }
90    }
91
92    #[test]
93    fn data_flow_callbacks_are_invoked_independently() {
94        // Spec §5.3.1 — getrennte execute / state_update Phasen.
95        let mut s = DataFlowStub {
96            executes: 0,
97            state_updates: 0,
98            rate_changes: 0,
99        };
100        s.on_execute(0);
101        s.on_execute(0);
102        s.on_state_update(0);
103        s.on_rate_changed(0);
104        assert_eq!(s.executes, 2);
105        assert_eq!(s.state_updates, 1);
106        assert_eq!(s.rate_changes, 1);
107    }
108
109    struct FsmStub {
110        actions: u32,
111    }
112    impl ComponentAction for FsmStub {}
113    impl FsmComponentAction for FsmStub {
114        fn on_action(&mut self, _h: u32) -> ReturnCode {
115            self.actions += 1;
116            ReturnCode::Ok
117        }
118    }
119
120    #[test]
121    fn fsm_on_action_is_invoked_per_event() {
122        // Spec §5.3.2.
123        let mut s = FsmStub { actions: 0 };
124        s.on_action(0);
125        s.on_action(0);
126        s.on_action(0);
127        assert_eq!(s.actions, 3);
128    }
129
130    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
131    enum AutoMode {
132        Idle,
133        Driving,
134        Charging,
135    }
136
137    impl ModeOfOperation for AutoMode {
138        fn name(&self) -> &str {
139            match self {
140                Self::Idle => "Idle",
141                Self::Driving => "Driving",
142                Self::Charging => "Charging",
143            }
144        }
145    }
146
147    #[test]
148    fn mode_of_operation_provides_string_name() {
149        // Spec §5.3.3.
150        assert_eq!(AutoMode::Idle.name(), "Idle");
151        assert_eq!(AutoMode::Driving.name(), "Driving");
152        assert_eq!(AutoMode::Charging.name(), "Charging");
153    }
154
155    struct ModeStub {
156        transitions: alloc::vec::Vec<(AutoMode, AutoMode)>,
157    }
158    impl ComponentAction for ModeStub {}
159    impl MultiModeComponentAction<AutoMode> for ModeStub {
160        fn on_mode_changed(&mut self, from: AutoMode, to: AutoMode, _h: u32) -> ReturnCode {
161            self.transitions.push((from, to));
162            ReturnCode::Ok
163        }
164    }
165
166    #[test]
167    fn multi_mode_on_mode_changed_records_transition() {
168        // Spec §5.3.3.x.
169        let mut s = ModeStub {
170            transitions: alloc::vec::Vec::new(),
171        };
172        s.on_mode_changed(AutoMode::Idle, AutoMode::Driving, 1);
173        s.on_mode_changed(AutoMode::Driving, AutoMode::Charging, 1);
174        assert_eq!(
175            s.transitions,
176            alloc::vec![
177                (AutoMode::Idle, AutoMode::Driving),
178                (AutoMode::Driving, AutoMode::Charging),
179            ]
180        );
181    }
182}