1#[cfg(not(feature = "std"))]
22use alloc::vec::Vec;
23
24use crate::{HierarchyLevel, NodeId};
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
28pub enum ConnectionState {
29 #[default]
31 Disconnected,
32 Connecting,
34 Connected,
36 Disconnecting,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum PeerRole {
43 Parent,
45 Child,
47 Peer,
49}
50
51#[derive(Debug, Clone)]
53pub struct PeerInfo {
54 pub node_id: NodeId,
56 pub role: PeerRole,
58 pub state: ConnectionState,
60 pub hierarchy_level: HierarchyLevel,
62 pub rssi: Option<i8>,
64 pub connected_at: Option<u64>,
66 pub messages_received: u32,
68 pub messages_sent: u32,
70 pub failure_count: u8,
72 pub last_seen_ms: u64,
74}
75
76impl PeerInfo {
77 pub fn new(node_id: NodeId, role: PeerRole, hierarchy_level: HierarchyLevel) -> Self {
79 Self {
80 node_id,
81 role,
82 state: ConnectionState::Disconnected,
83 hierarchy_level,
84 rssi: None,
85 connected_at: None,
86 messages_received: 0,
87 messages_sent: 0,
88 failure_count: 0,
89 last_seen_ms: 0,
90 }
91 }
92
93 pub fn is_connected(&self) -> bool {
95 self.state == ConnectionState::Connected
96 }
97
98 pub fn update_rssi(&mut self, rssi: i8) {
100 self.rssi = Some(rssi);
101 }
102
103 pub fn record_failure(&mut self) {
105 self.failure_count = self.failure_count.saturating_add(1);
106 }
107
108 pub fn reset_failures(&mut self) {
110 self.failure_count = 0;
111 }
112}
113
114#[derive(Debug, Clone, Default)]
116pub struct MeshTopology {
117 pub parent: Option<NodeId>,
119 pub children: Vec<NodeId>,
121 pub peers: Vec<NodeId>,
123 pub my_level: HierarchyLevel,
125 pub max_children: u8,
127 pub max_connections: u8,
129}
130
131impl MeshTopology {
132 pub fn new(my_level: HierarchyLevel, max_children: u8, max_connections: u8) -> Self {
134 Self {
135 parent: None,
136 children: Vec::new(),
137 peers: Vec::new(),
138 my_level,
139 max_children,
140 max_connections,
141 }
142 }
143
144 pub fn connection_count(&self) -> usize {
146 let parent_count = if self.parent.is_some() { 1 } else { 0 };
147 parent_count + self.children.len() + self.peers.len()
148 }
149
150 pub fn can_accept_connection(&self) -> bool {
152 self.connection_count() < self.max_connections as usize
153 }
154
155 pub fn can_accept_child(&self) -> bool {
157 self.children.len() < self.max_children as usize && self.can_accept_connection()
158 }
159
160 pub fn has_parent(&self) -> bool {
162 self.parent.is_some()
163 }
164
165 pub fn set_parent(&mut self, node_id: NodeId) -> bool {
167 if self.parent.is_some() {
168 return false;
169 }
170 if !self.can_accept_connection() {
171 return false;
172 }
173 self.parent = Some(node_id);
174 true
175 }
176
177 pub fn clear_parent(&mut self) -> Option<NodeId> {
179 self.parent.take()
180 }
181
182 pub fn add_child(&mut self, node_id: NodeId) -> bool {
184 if !self.can_accept_child() {
185 return false;
186 }
187 if self.children.contains(&node_id) {
188 return false;
189 }
190 self.children.push(node_id);
191 true
192 }
193
194 pub fn remove_child(&mut self, node_id: &NodeId) -> bool {
196 if let Some(pos) = self.children.iter().position(|n| n == node_id) {
197 self.children.remove(pos);
198 true
199 } else {
200 false
201 }
202 }
203
204 pub fn add_peer(&mut self, node_id: NodeId) -> bool {
206 if !self.can_accept_connection() {
207 return false;
208 }
209 if self.peers.contains(&node_id) {
210 return false;
211 }
212 self.peers.push(node_id);
213 true
214 }
215
216 pub fn remove_peer(&mut self, node_id: &NodeId) -> bool {
218 if let Some(pos) = self.peers.iter().position(|n| n == node_id) {
219 self.peers.remove(pos);
220 true
221 } else {
222 false
223 }
224 }
225
226 pub fn all_connected(&self) -> Vec<NodeId> {
228 let mut nodes = Vec::with_capacity(self.connection_count());
229 if let Some(ref parent) = self.parent {
230 nodes.push(*parent);
231 }
232 nodes.extend(self.children.iter().cloned());
233 nodes.extend(self.peers.iter().cloned());
234 nodes
235 }
236
237 pub fn is_connected(&self, node_id: &NodeId) -> bool {
239 self.parent.as_ref() == Some(node_id)
240 || self.children.contains(node_id)
241 || self.peers.contains(node_id)
242 }
243
244 pub fn get_role(&self, node_id: &NodeId) -> Option<PeerRole> {
246 if self.parent.as_ref() == Some(node_id) {
247 Some(PeerRole::Parent)
248 } else if self.children.contains(node_id) {
249 Some(PeerRole::Child)
250 } else if self.peers.contains(node_id) {
251 Some(PeerRole::Peer)
252 } else {
253 None
254 }
255 }
256}
257
258#[derive(Debug, Clone)]
260pub enum TopologyEvent {
261 ParentConnected {
263 node_id: NodeId,
265 level: HierarchyLevel,
267 rssi: Option<i8>,
269 },
270 ParentDisconnected {
272 node_id: NodeId,
274 reason: DisconnectReason,
276 },
277 ChildConnected {
279 node_id: NodeId,
281 level: HierarchyLevel,
283 },
284 ChildDisconnected {
286 node_id: NodeId,
288 reason: DisconnectReason,
290 },
291 PeerConnected {
293 node_id: NodeId,
295 },
296 PeerDisconnected {
298 node_id: NodeId,
300 reason: DisconnectReason,
302 },
303 TopologyChanged {
305 child_count: usize,
307 peer_count: usize,
309 has_parent: bool,
311 },
312 ParentFailoverStarted {
314 old_parent: NodeId,
316 },
317 ParentFailoverCompleted {
319 old_parent: NodeId,
321 new_parent: Option<NodeId>,
323 },
324 ConnectionQualityChanged {
326 node_id: NodeId,
328 rssi: i8,
330 },
331}
332
333#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
335pub enum DisconnectReason {
336 Requested,
338 Timeout,
340 RemoteDisconnect,
342 SupervisionTimeout,
344 LinkLoss,
346 LocalError,
348 #[default]
350 Unknown,
351}
352
353#[derive(Debug, Clone)]
355pub struct ParentCandidate {
356 pub node_id: NodeId,
358 pub level: HierarchyLevel,
360 pub rssi: i8,
362 pub age_ms: u64,
364 pub failure_count: u8,
366}
367
368impl ParentCandidate {
369 pub fn score(&self, my_level: HierarchyLevel) -> i32 {
377 let mut score = 0i32;
378
379 score += (self.rssi as i32 + 100) * 2; if self.age_ms < 1000 {
385 score += 20;
386 } else if self.age_ms < 3000 {
387 score += 10;
388 } else if self.age_ms < 5000 {
389 score += 5;
390 }
391 score -= (self.failure_count as i32) * 15;
395
396 let ideal_level = match my_level {
398 HierarchyLevel::Platform => HierarchyLevel::Squad,
399 HierarchyLevel::Squad => HierarchyLevel::Platoon,
400 HierarchyLevel::Platoon => HierarchyLevel::Company,
401 HierarchyLevel::Company => HierarchyLevel::Company, };
403
404 if self.level == ideal_level {
405 score += 30;
406 } else if self.level > my_level {
407 score += 15;
408 }
409 score
412 }
413}
414
415#[derive(Debug, Clone)]
417pub struct TopologyConfig {
418 pub max_children: u8,
420 pub max_connections: u8,
422 pub min_parent_rssi: i8,
424 pub max_beacon_age_ms: u64,
426 pub parent_timeout_ms: u64,
428 pub connect_timeout_ms: u64,
430 pub max_failures: u8,
432 pub failover_delay_ms: u64,
434 pub rssi_hysteresis: u8,
436}
437
438impl Default for TopologyConfig {
439 fn default() -> Self {
440 Self {
441 max_children: 3,
442 max_connections: 7,
443 min_parent_rssi: -85,
444 max_beacon_age_ms: 10_000,
445 parent_timeout_ms: 5_000,
446 connect_timeout_ms: 10_000,
447 max_failures: 3,
448 failover_delay_ms: 1_000,
449 rssi_hysteresis: 6,
450 }
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 use super::*;
457
458 #[test]
459 fn test_mesh_topology_new() {
460 let topology = MeshTopology::new(HierarchyLevel::Squad, 3, 7);
461 assert_eq!(topology.my_level, HierarchyLevel::Squad);
462 assert_eq!(topology.max_children, 3);
463 assert_eq!(topology.max_connections, 7);
464 assert!(topology.parent.is_none());
465 assert!(topology.children.is_empty());
466 }
467
468 #[test]
469 fn test_set_parent() {
470 let mut topology = MeshTopology::new(HierarchyLevel::Platform, 3, 7);
471 let parent_id = NodeId::new(0x1234);
472
473 assert!(topology.set_parent(parent_id));
474 assert_eq!(topology.parent, Some(parent_id));
475 assert_eq!(topology.connection_count(), 1);
476
477 assert!(!topology.set_parent(NodeId::new(0x5678)));
479 }
480
481 #[test]
482 fn test_add_children() {
483 let mut topology = MeshTopology::new(HierarchyLevel::Squad, 2, 7);
484
485 assert!(topology.add_child(NodeId::new(0x1111)));
486 assert!(topology.add_child(NodeId::new(0x2222)));
487 assert!(!topology.add_child(NodeId::new(0x3333)));
489
490 assert_eq!(topology.children.len(), 2);
491 }
492
493 #[test]
494 fn test_connection_limit() {
495 let mut topology = MeshTopology::new(HierarchyLevel::Squad, 5, 3);
496
497 assert!(topology.set_parent(NodeId::new(0x0001)));
498 assert!(topology.add_child(NodeId::new(0x0002)));
499 assert!(topology.add_peer(NodeId::new(0x0003)));
500 assert!(!topology.add_child(NodeId::new(0x0004)));
502 assert!(!topology.add_peer(NodeId::new(0x0005)));
503
504 assert_eq!(topology.connection_count(), 3);
505 }
506
507 #[test]
508 fn test_remove_child() {
509 let mut topology = MeshTopology::new(HierarchyLevel::Squad, 3, 7);
510 let child_id = NodeId::new(0x1111);
511
512 topology.add_child(child_id);
513 assert!(topology.remove_child(&child_id));
514 assert!(!topology.remove_child(&child_id)); assert!(topology.children.is_empty());
516 }
517
518 #[test]
519 fn test_get_role() {
520 let mut topology = MeshTopology::new(HierarchyLevel::Squad, 3, 7);
521 let parent_id = NodeId::new(0x0001);
522 let child_id = NodeId::new(0x0002);
523 let peer_id = NodeId::new(0x0003);
524 let unknown_id = NodeId::new(0x9999);
525
526 topology.set_parent(parent_id);
527 topology.add_child(child_id);
528 topology.add_peer(peer_id);
529
530 assert_eq!(topology.get_role(&parent_id), Some(PeerRole::Parent));
531 assert_eq!(topology.get_role(&child_id), Some(PeerRole::Child));
532 assert_eq!(topology.get_role(&peer_id), Some(PeerRole::Peer));
533 assert_eq!(topology.get_role(&unknown_id), None);
534 }
535
536 #[test]
537 fn test_all_connected() {
538 let mut topology = MeshTopology::new(HierarchyLevel::Squad, 3, 7);
539 topology.set_parent(NodeId::new(0x0001));
540 topology.add_child(NodeId::new(0x0002));
541 topology.add_peer(NodeId::new(0x0003));
542
543 let all = topology.all_connected();
544 assert_eq!(all.len(), 3);
545 }
546
547 #[test]
548 fn test_parent_candidate_score() {
549 let candidate = ParentCandidate {
550 node_id: NodeId::new(0x1234),
551 level: HierarchyLevel::Squad,
552 rssi: -50,
553 age_ms: 500,
554 failure_count: 0,
555 };
556
557 let score = candidate.score(HierarchyLevel::Platform);
559 assert_eq!(score, 150);
564 }
565
566 #[test]
567 fn test_parent_candidate_score_with_failures() {
568 let candidate = ParentCandidate {
569 node_id: NodeId::new(0x1234),
570 level: HierarchyLevel::Squad,
571 rssi: -50,
572 age_ms: 500,
573 failure_count: 2,
574 };
575
576 let score = candidate.score(HierarchyLevel::Platform);
577 assert_eq!(score, 120);
579 }
580
581 #[test]
582 fn test_peer_info() {
583 let mut peer = PeerInfo::new(
584 NodeId::new(0x1234),
585 PeerRole::Child,
586 HierarchyLevel::Platform,
587 );
588
589 assert!(!peer.is_connected());
590 assert_eq!(peer.failure_count, 0);
591
592 peer.record_failure();
593 peer.record_failure();
594 assert_eq!(peer.failure_count, 2);
595
596 peer.reset_failures();
597 assert_eq!(peer.failure_count, 0);
598 }
599
600 #[test]
601 fn test_topology_config_default() {
602 let config = TopologyConfig::default();
603 assert_eq!(config.max_children, 3);
604 assert_eq!(config.max_connections, 7);
605 assert_eq!(config.min_parent_rssi, -85);
606 }
607}