1use std::time::{Duration, Instant};
40
41use async_trait::async_trait;
42use serde::{Deserialize, Serialize};
43
44use crate::device::DeviceState;
45use crate::error::Result;
46
47#[async_trait]
52pub trait DeviceLifecycle: Send + Sync {
53 fn state(&self) -> DeviceState;
55
56 fn is_operational(&self) -> bool {
58 self.state().is_operational()
59 }
60
61 fn can_accept_requests(&self) -> bool {
63 self.state().can_accept_requests()
64 }
65
66 async fn initialize(&mut self) -> Result<()>;
71
72 async fn start(&mut self) -> Result<()>;
76
77 async fn stop(&mut self) -> Result<()>;
81
82 async fn on_error(&mut self, _error: &crate::error::Error) -> Result<()> {
87 Ok(())
88 }
89
90 async fn recover(&mut self) -> Result<bool> {
94 Ok(false)
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub enum LifecycleEvent {
101 Initialized {
103 device_id: String,
104 timestamp: chrono::DateTime<chrono::Utc>,
105 },
106 Started {
108 device_id: String,
109 timestamp: chrono::DateTime<chrono::Utc>,
110 },
111 Stopped {
113 device_id: String,
114 timestamp: chrono::DateTime<chrono::Utc>,
115 reason: StopReason,
116 },
117 StateChanged {
119 device_id: String,
120 old_state: DeviceState,
121 new_state: DeviceState,
122 timestamp: chrono::DateTime<chrono::Utc>,
123 },
124 Error {
126 device_id: String,
127 error: String,
128 timestamp: chrono::DateTime<chrono::Utc>,
129 },
130 RecoveryAttempted {
132 device_id: String,
133 success: bool,
134 timestamp: chrono::DateTime<chrono::Utc>,
135 },
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
140pub enum StopReason {
141 UserRequested,
143 Error,
145 Maintenance,
147 SystemShutdown,
149 Timeout,
151}
152
153#[derive(Debug, Clone)]
155pub struct LifecycleStateMachine {
156 current_state: DeviceState,
157 last_transition: Option<Instant>,
158 error_count: u32,
159 max_retries: u32,
160 retry_delay: Duration,
161}
162
163impl LifecycleStateMachine {
164 pub fn new() -> Self {
166 Self {
167 current_state: DeviceState::Uninitialized,
168 last_transition: None,
169 error_count: 0,
170 max_retries: 3,
171 retry_delay: Duration::from_secs(1),
172 }
173 }
174
175 pub fn with_retries(mut self, max_retries: u32, retry_delay: Duration) -> Self {
177 self.max_retries = max_retries;
178 self.retry_delay = retry_delay;
179 self
180 }
181
182 pub fn state(&self) -> DeviceState {
184 self.current_state
185 }
186
187 pub fn can_transition_to(&self, target: DeviceState) -> bool {
189 match (self.current_state, target) {
190 (DeviceState::Uninitialized, DeviceState::Initializing) => true,
192 (DeviceState::Uninitialized, DeviceState::Error) => true,
193
194 (DeviceState::Initializing, DeviceState::Offline) => true,
196 (DeviceState::Initializing, DeviceState::Online) => true,
197 (DeviceState::Initializing, DeviceState::Error) => true,
198
199 (DeviceState::Offline, DeviceState::Online) => true,
201 (DeviceState::Offline, DeviceState::Error) => true,
202
203 (DeviceState::Online, DeviceState::Offline) => true,
205 (DeviceState::Online, DeviceState::ShuttingDown) => true,
206 (DeviceState::Online, DeviceState::Error) => true,
207
208 (DeviceState::ShuttingDown, DeviceState::Offline) => true,
210 (DeviceState::ShuttingDown, DeviceState::Error) => true,
211
212 (DeviceState::Error, DeviceState::Offline) => true,
214 (DeviceState::Error, DeviceState::Initializing) => true,
215 (DeviceState::Error, DeviceState::Uninitialized) => true,
216
217 _ => false,
218 }
219 }
220
221 pub fn transition_to(&mut self, target: DeviceState) -> Result<DeviceState> {
223 if !self.can_transition_to(target) {
224 return Err(crate::error::Error::Engine(format!(
225 "Invalid state transition: {:?} -> {:?}",
226 self.current_state, target
227 )));
228 }
229
230 let old_state = self.current_state;
231 self.current_state = target;
232 self.last_transition = Some(Instant::now());
233
234 if target != DeviceState::Error {
236 self.error_count = 0;
237 }
238
239 Ok(old_state)
240 }
241
242 pub fn record_error(&mut self) -> bool {
244 self.error_count += 1;
245 self.error_count > self.max_retries
246 }
247
248 pub fn error_count(&self) -> u32 {
250 self.error_count
251 }
252
253 pub fn reset_errors(&mut self) {
255 self.error_count = 0;
256 }
257
258 pub fn time_in_state(&self) -> Option<Duration> {
260 self.last_transition.map(|t| t.elapsed())
261 }
262
263 pub fn can_retry(&self) -> bool {
265 self.last_transition
266 .map(|t| t.elapsed() >= self.retry_delay)
267 .unwrap_or(true)
268 }
269}
270
271impl Default for LifecycleStateMachine {
272 fn default() -> Self {
273 Self::new()
274 }
275}
276
277#[async_trait]
281pub trait LifecycleHook: Send + Sync {
282 async fn before_init(&self) -> Result<()> {
284 Ok(())
285 }
286
287 async fn after_init(&self) -> Result<()> {
289 Ok(())
290 }
291
292 async fn before_start(&self) -> Result<()> {
294 Ok(())
295 }
296
297 async fn after_start(&self) -> Result<()> {
299 Ok(())
300 }
301
302 async fn before_stop(&self) -> Result<()> {
304 Ok(())
305 }
306
307 async fn after_stop(&self) -> Result<()> {
309 Ok(())
310 }
311
312 async fn on_error(&self, _error: &crate::error::Error) -> Result<()> {
314 Ok(())
315 }
316}
317
318pub struct NoOpLifecycleHook;
320
321#[async_trait]
322impl LifecycleHook for NoOpLifecycleHook {}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327
328 #[test]
329 fn test_lifecycle_state_machine_transitions() {
330 let mut sm = LifecycleStateMachine::new();
331
332 assert_eq!(sm.state(), DeviceState::Uninitialized);
333
334 sm.transition_to(DeviceState::Initializing).unwrap();
336 assert_eq!(sm.state(), DeviceState::Initializing);
337
338 sm.transition_to(DeviceState::Offline).unwrap();
339 assert_eq!(sm.state(), DeviceState::Offline);
340
341 sm.transition_to(DeviceState::Online).unwrap();
342 assert_eq!(sm.state(), DeviceState::Online);
343 }
344
345 #[test]
346 fn test_lifecycle_state_machine_invalid_transition() {
347 let mut sm = LifecycleStateMachine::new();
348
349 let result = sm.transition_to(DeviceState::Online);
351 assert!(result.is_err());
352 }
353
354 #[test]
355 fn test_lifecycle_state_machine_error_handling() {
356 let mut sm = LifecycleStateMachine::new().with_retries(3, Duration::from_millis(100));
357
358 assert!(!sm.record_error()); assert!(!sm.record_error()); assert!(!sm.record_error()); assert!(sm.record_error()); assert_eq!(sm.error_count(), 4);
365
366 sm.reset_errors();
367 assert_eq!(sm.error_count(), 0);
368 }
369
370 #[test]
371 fn test_lifecycle_valid_transitions() {
372 let sm = LifecycleStateMachine::new();
373
374 assert!(sm.can_transition_to(DeviceState::Initializing));
375 assert!(sm.can_transition_to(DeviceState::Error));
376 assert!(!sm.can_transition_to(DeviceState::Online));
377 assert!(!sm.can_transition_to(DeviceState::ShuttingDown));
378 }
379}