zerodds_rtc/lifecycle.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3
4//! Lifecycle-Modell — Spec §5.2.2.3 / §5.2.2.4 / §5.2.2.7.
5
6use crate::return_code::ReturnCode;
7
8/// `LifeCycleState` (Spec §5.2.2.3, S. 19) — Lifecycle-States eines
9/// RTC innerhalb eines `ExecutionContext`.
10///
11/// Spec-Zitat (§5.2.2.3.1-§5.2.2.3.4): "CREATED — instantiated but not
12/// yet initialized; INACTIVE — Alive but not invoked; ACTIVE — Alive
13/// and will be invoked when context is Running; ERROR — encountered
14/// problem and cannot continue without reset."
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub enum LifeCycleState {
17 /// `CREATED` (§5.2.2.3.1).
18 Created,
19 /// `INACTIVE` (§5.2.2.3.2).
20 Inactive,
21 /// `ACTIVE` (§5.2.2.3.3).
22 Active,
23 /// `ERROR` (§5.2.2.3.4).
24 Error,
25}
26
27/// `ExecutionKind` (Spec §5.2.2.7, S. 30-31) — definiert wann der
28/// Execution-Context die Component-Callbacks invoked.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30pub enum ExecutionKind {
31 /// `PERIODIC` (§5.2.2.7.1) — `on_execute` + `on_state_update` an
32 /// jedem Tick.
33 Periodic,
34 /// `EVENT_DRIVEN` (§5.2.2.7.2) — `on_action` bei diskretem Event.
35 EventDriven,
36 /// `OTHER` (§5.2.2.7.3) — Implementor-spezifisch.
37 Other,
38}
39
40/// `ComponentAction` Trait — Spec §5.2.2.4 (S. 20-21).
41///
42/// Spec-Zitat: "ComponentAction interface provides callbacks
43/// corresponding to the execution of the lifecycle operations of
44/// LightweightRTObject and ExecutionContext."
45///
46/// Default-Implementations geben `ReturnCode::Ok` zurueck — Anwender
47/// implementieren nur die Callbacks, die fuer ihre Domaene relevant
48/// sind (Spec §5.2.2.4: "An RTC developer may implement these callback
49/// operations in order to execute application-specific logic").
50pub trait ComponentAction {
51 /// Spec §5.2.2.4.1 — RTC has been initialized and entered Alive
52 /// state.
53 fn on_initialize(&mut self) -> ReturnCode {
54 ReturnCode::Ok
55 }
56 /// Spec §5.2.2.4.2 — RTC is being destroyed.
57 fn on_finalize(&mut self) -> ReturnCode {
58 ReturnCode::Ok
59 }
60 /// Spec §5.2.2.4.3 — Execution-Context transitioned Stopped →
61 /// Running.
62 fn on_startup(&mut self, _exec_handle: u32) -> ReturnCode {
63 ReturnCode::Ok
64 }
65 /// Spec §5.2.2.4.4 — Execution-Context transitioned Running →
66 /// Stopped.
67 fn on_shutdown(&mut self, _exec_handle: u32) -> ReturnCode {
68 ReturnCode::Ok
69 }
70 /// Spec §5.2.2.4.5 — RTC has been activated.
71 fn on_activated(&mut self, _exec_handle: u32) -> ReturnCode {
72 ReturnCode::Ok
73 }
74 /// Spec §5.2.2.4.6 — RTC has been deactivated.
75 fn on_deactivated(&mut self, _exec_handle: u32) -> ReturnCode {
76 ReturnCode::Ok
77 }
78 /// Spec §5.2.2.4.7 — RTC transitions Active → Error (single
79 /// invocation).
80 fn on_aborting(&mut self, _exec_handle: u32) -> ReturnCode {
81 ReturnCode::Ok
82 }
83 /// Spec §5.2.2.4.8 — RTC remains in Error state (repeated per
84 /// tick wenn `ExecutionKind::Periodic`).
85 fn on_error(&mut self, _exec_handle: u32) -> ReturnCode {
86 ReturnCode::Ok
87 }
88 /// Spec §5.2.2.4.9 — Reset-Versuch aus Error-State zurueck zu
89 /// Inactive.
90 fn on_reset(&mut self, _exec_handle: u32) -> ReturnCode {
91 ReturnCode::Ok
92 }
93}
94
95/// Spec §5.2.2.3 (Figure 5.4 + 5.5) — gueltige State-Transitions im
96/// Lifecycle. Liefert `true` wenn die Transition nach Spec erlaubt
97/// ist, sonst `false`.
98///
99/// Transitions:
100/// * `Created → Inactive` via `initialize`/`attach_context`.
101/// * `Inactive → Active` via `activate_component`.
102/// * `Active → Inactive` via `deactivate_component`.
103/// * `Active → Error` automatisch bei `on_*`-Return != Ok.
104/// * `Error → Inactive` via `reset_component` (wenn `on_reset`
105/// `ReturnCode::Ok` liefert).
106/// * Keine Transition aus `Created` ausser via `initialize`.
107#[must_use]
108pub const fn is_valid_transition(from: LifeCycleState, to: LifeCycleState) -> bool {
109 use LifeCycleState::{Active, Created, Error, Inactive};
110 matches!(
111 (from, to),
112 (Created, Inactive)
113 | (Inactive, Active)
114 | (Active, Inactive)
115 | (Active, Error)
116 | (Error, Inactive)
117 | (Error, Error)
118 )
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn valid_transitions_match_spec_state_machine() {
127 // Spec §5.2.2.3 Fig 5.4 + §5.2.2.5 Fig 5.5.
128 for (from, to, expected) in [
129 (LifeCycleState::Created, LifeCycleState::Inactive, true),
130 (LifeCycleState::Inactive, LifeCycleState::Active, true),
131 (LifeCycleState::Active, LifeCycleState::Inactive, true),
132 (LifeCycleState::Active, LifeCycleState::Error, true),
133 (LifeCycleState::Error, LifeCycleState::Inactive, true),
134 // Forbidden:
135 (LifeCycleState::Created, LifeCycleState::Active, false),
136 (LifeCycleState::Created, LifeCycleState::Error, false),
137 (LifeCycleState::Inactive, LifeCycleState::Created, false),
138 (LifeCycleState::Inactive, LifeCycleState::Error, false),
139 (LifeCycleState::Active, LifeCycleState::Created, false),
140 (LifeCycleState::Error, LifeCycleState::Active, false),
141 (LifeCycleState::Error, LifeCycleState::Created, false),
142 ] {
143 assert_eq!(
144 is_valid_transition(from, to),
145 expected,
146 "{from:?} -> {to:?}"
147 );
148 }
149 }
150
151 #[test]
152 fn default_component_action_returns_ok_for_all_callbacks() {
153 // Spec §5.2.2.4 — Default callbacks are no-ops returning OK.
154 struct Stub;
155 impl ComponentAction for Stub {}
156 let mut s = Stub;
157 assert_eq!(s.on_initialize(), ReturnCode::Ok);
158 assert_eq!(s.on_finalize(), ReturnCode::Ok);
159 assert_eq!(s.on_startup(0), ReturnCode::Ok);
160 assert_eq!(s.on_shutdown(0), ReturnCode::Ok);
161 assert_eq!(s.on_activated(0), ReturnCode::Ok);
162 assert_eq!(s.on_deactivated(0), ReturnCode::Ok);
163 assert_eq!(s.on_aborting(0), ReturnCode::Ok);
164 assert_eq!(s.on_error(0), ReturnCode::Ok);
165 assert_eq!(s.on_reset(0), ReturnCode::Ok);
166 }
167
168 #[test]
169 fn execution_kind_distinguishes_three_modes() {
170 // Spec §5.2.2.7.
171 let kinds = [
172 ExecutionKind::Periodic,
173 ExecutionKind::EventDriven,
174 ExecutionKind::Other,
175 ];
176 // Sicher unterschiedlich.
177 assert_ne!(kinds[0], kinds[1]);
178 assert_ne!(kinds[1], kinds[2]);
179 assert_ne!(kinds[0], kinds[2]);
180 }
181
182 #[test]
183 fn error_self_loop_allowed() {
184 // Spec §5.2.2.4.8: on_error wird repeatedly fuer Error-State
185 // invoked.
186 assert!(is_valid_transition(
187 LifeCycleState::Error,
188 LifeCycleState::Error
189 ));
190 }
191}