Skip to main content

peat_mesh/topology/
autonomous.rs

1//! Autonomous operation mode for partitioned nodes
2//!
3//! This module provides autonomous operation capabilities for nodes that are
4//! isolated from all higher hierarchy levels (network partition). When a node
5//! enters autonomous mode, it continues local operations while buffering
6//! telemetry and data for eventual synchronization when connectivity is restored.
7//!
8//! ## Architecture
9//!
10//! ```text
11//! AutonomousOperationHandler
12//! ├── PartitionHandler trait implementation
13//! ├── Autonomous state management
14//! ├── Telemetry buffering
15//! └── Recovery and synchronization
16//! ```
17//!
18//! ## Usage
19//!
20//! ```rust
21//! use peat_mesh::topology::{AutonomousOperationHandler, PartitionDetector};
22//! use peat_mesh::beacon::HierarchyLevel;
23//! use std::sync::Arc;
24//!
25//! // Create autonomous operation handler
26//! let handler = Arc::new(AutonomousOperationHandler::new());
27//!
28//! // Create partition detector with handler
29//! let detector = PartitionDetector::new(
30//!     HierarchyLevel::Platform,
31//!     Default::default()
32//! ).with_handler(handler.clone());
33//!
34//! // Query autonomous state
35//! assert!(!handler.is_autonomous());
36//! ```
37
38use super::partition::{PartitionEvent, PartitionHandler};
39use std::sync::{Arc, RwLock};
40use std::time::Instant;
41use tracing::{info, warn};
42
43/// Autonomous operation state
44#[derive(Debug, Clone, PartialEq, Eq, Default)]
45pub enum AutonomousState {
46    /// Normal operation with connectivity to higher levels
47    #[default]
48    Normal,
49
50    /// Autonomous operation mode (partitioned from higher levels)
51    Autonomous {
52        /// When autonomous mode was entered
53        entered_at: Instant,
54
55        /// Number of detection attempts before partition was confirmed
56        detection_attempts: u32,
57    },
58}
59
60/// Handler for autonomous operation during network partitions
61///
62/// Implements the PartitionHandler trait to respond to partition detection
63/// and healing events. When a partition is detected, the handler enters
64/// autonomous mode, allowing the node to continue local operations while
65/// buffering data for eventual synchronization.
66#[derive(Debug)]
67pub struct AutonomousOperationHandler {
68    state: Arc<RwLock<AutonomousState>>,
69}
70
71impl AutonomousOperationHandler {
72    /// Create a new autonomous operation handler
73    pub fn new() -> Self {
74        Self {
75            state: Arc::new(RwLock::new(AutonomousState::default())),
76        }
77    }
78
79    /// Check if currently in autonomous operation mode
80    pub fn is_autonomous(&self) -> bool {
81        matches!(
82            *self.state.read().unwrap(),
83            AutonomousState::Autonomous { .. }
84        )
85    }
86
87    /// Get current autonomous state
88    pub fn get_state(&self) -> AutonomousState {
89        self.state.read().unwrap().clone()
90    }
91
92    /// Enter autonomous operation mode
93    fn enter_autonomous_mode(&self, detection_attempts: u32) {
94        let mut state = self.state.write().unwrap();
95
96        if matches!(*state, AutonomousState::Autonomous { .. }) {
97            warn!("Already in autonomous mode, ignoring duplicate partition detection");
98            return;
99        }
100
101        *state = AutonomousState::Autonomous {
102            entered_at: Instant::now(),
103            detection_attempts,
104        };
105
106        info!(
107            "Entered autonomous operation mode after {} detection attempts",
108            detection_attempts
109        );
110    }
111
112    /// Exit autonomous operation mode (return to normal operation)
113    fn exit_autonomous_mode(&self, autonomous_duration: std::time::Duration) {
114        let mut state = self.state.write().unwrap();
115
116        if matches!(*state, AutonomousState::Normal) {
117            warn!("Already in normal mode, ignoring duplicate partition healing");
118            return;
119        }
120
121        *state = AutonomousState::Normal;
122
123        info!(
124            "Exited autonomous operation mode after {:?} duration",
125            autonomous_duration
126        );
127    }
128}
129
130impl Default for AutonomousOperationHandler {
131    fn default() -> Self {
132        Self::new()
133    }
134}
135
136impl PartitionHandler for AutonomousOperationHandler {
137    fn on_partition_detected(&self, event: &PartitionEvent) {
138        if let PartitionEvent::PartitionDetected {
139            attempts,
140            detection_duration,
141        } = event
142        {
143            info!(
144                "Network partition detected after {} attempts over {:?}",
145                attempts, detection_duration
146            );
147
148            self.enter_autonomous_mode(*attempts);
149        }
150    }
151
152    fn on_partition_healed(&self, event: &PartitionEvent) {
153        if let PartitionEvent::PartitionHealed {
154            visible_beacons,
155            partition_duration,
156        } = event
157        {
158            info!(
159                "Network partition healed, {} higher-level beacons visible, partition lasted {:?}",
160                visible_beacons, partition_duration
161            );
162
163            self.exit_autonomous_mode(*partition_duration);
164        }
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171    use std::time::Duration;
172
173    #[test]
174    fn test_autonomous_handler_creation() {
175        let handler = AutonomousOperationHandler::new();
176        assert!(!handler.is_autonomous());
177        assert_eq!(handler.get_state(), AutonomousState::Normal);
178    }
179
180    #[test]
181    fn test_enter_autonomous_mode() {
182        let handler = AutonomousOperationHandler::new();
183
184        let event = PartitionEvent::PartitionDetected {
185            attempts: 3,
186            detection_duration: Duration::from_secs(6),
187        };
188
189        handler.on_partition_detected(&event);
190
191        assert!(handler.is_autonomous());
192
193        match handler.get_state() {
194            AutonomousState::Autonomous {
195                detection_attempts, ..
196            } => {
197                assert_eq!(detection_attempts, 3);
198            }
199            _ => panic!("Expected Autonomous state"),
200        }
201    }
202
203    #[test]
204    fn test_exit_autonomous_mode() {
205        let handler = AutonomousOperationHandler::new();
206
207        // Enter autonomous mode
208        let partition_event = PartitionEvent::PartitionDetected {
209            attempts: 3,
210            detection_duration: Duration::from_secs(6),
211        };
212        handler.on_partition_detected(&partition_event);
213        assert!(handler.is_autonomous());
214
215        // Exit autonomous mode
216        let healed_event = PartitionEvent::PartitionHealed {
217            visible_beacons: 2,
218            partition_duration: Duration::from_secs(120),
219        };
220        handler.on_partition_healed(&healed_event);
221
222        assert!(!handler.is_autonomous());
223        assert_eq!(handler.get_state(), AutonomousState::Normal);
224    }
225
226    #[test]
227    fn test_duplicate_partition_detection() {
228        let handler = AutonomousOperationHandler::new();
229
230        let event = PartitionEvent::PartitionDetected {
231            attempts: 3,
232            detection_duration: Duration::from_secs(6),
233        };
234
235        // First partition detection
236        handler.on_partition_detected(&event);
237        assert!(handler.is_autonomous());
238
239        let state1 = handler.get_state();
240
241        // Duplicate partition detection should be ignored
242        handler.on_partition_detected(&event);
243
244        let state2 = handler.get_state();
245        assert_eq!(state1, state2);
246    }
247
248    #[test]
249    fn test_duplicate_partition_healing() {
250        let handler = AutonomousOperationHandler::new();
251
252        let healed_event = PartitionEvent::PartitionHealed {
253            visible_beacons: 2,
254            partition_duration: Duration::from_secs(120),
255        };
256
257        // Healing when already in normal mode should be ignored
258        handler.on_partition_healed(&healed_event);
259        assert!(!handler.is_autonomous());
260        assert_eq!(handler.get_state(), AutonomousState::Normal);
261    }
262
263    #[test]
264    fn test_autonomous_state_transition() {
265        let handler = AutonomousOperationHandler::new();
266
267        // Normal → Autonomous
268        let partition_event = PartitionEvent::PartitionDetected {
269            attempts: 5,
270            detection_duration: Duration::from_secs(10),
271        };
272        handler.on_partition_detected(&partition_event);
273
274        assert!(handler.is_autonomous());
275
276        // Autonomous → Normal
277        let healed_event = PartitionEvent::PartitionHealed {
278            visible_beacons: 1,
279            partition_duration: Duration::from_secs(300),
280        };
281        handler.on_partition_healed(&healed_event);
282
283        assert!(!handler.is_autonomous());
284    }
285}