1#[cfg(not(feature = "std"))]
7use alloc::vec::Vec;
8
9use crate::{HierarchyLevel, NodeId};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
13pub enum ConnectionState {
14 #[default]
16 Disconnected,
17 Connecting,
19 Connected,
21 Disconnecting,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum PeerRole {
28 Parent,
30 Child,
32 Peer,
34}
35
36#[derive(Debug, Clone)]
38pub struct PeerInfo {
39 pub node_id: NodeId,
41 pub role: PeerRole,
43 pub state: ConnectionState,
45 pub hierarchy_level: HierarchyLevel,
47 pub rssi: Option<i8>,
49 pub connected_at: Option<u64>,
51 pub messages_received: u32,
53 pub messages_sent: u32,
55 pub failure_count: u8,
57 pub last_seen_ms: u64,
59}
60
61impl PeerInfo {
62 pub fn new(node_id: NodeId, role: PeerRole, hierarchy_level: HierarchyLevel) -> Self {
64 Self {
65 node_id,
66 role,
67 state: ConnectionState::Disconnected,
68 hierarchy_level,
69 rssi: None,
70 connected_at: None,
71 messages_received: 0,
72 messages_sent: 0,
73 failure_count: 0,
74 last_seen_ms: 0,
75 }
76 }
77
78 pub fn is_connected(&self) -> bool {
80 self.state == ConnectionState::Connected
81 }
82
83 pub fn update_rssi(&mut self, rssi: i8) {
85 self.rssi = Some(rssi);
86 }
87
88 pub fn record_failure(&mut self) {
90 self.failure_count = self.failure_count.saturating_add(1);
91 }
92
93 pub fn reset_failures(&mut self) {
95 self.failure_count = 0;
96 }
97}
98
99#[derive(Debug, Clone, Default)]
101pub struct MeshTopology {
102 pub parent: Option<NodeId>,
104 pub children: Vec<NodeId>,
106 pub peers: Vec<NodeId>,
108 pub my_level: HierarchyLevel,
110 pub max_children: u8,
112 pub max_connections: u8,
114}
115
116impl MeshTopology {
117 pub fn new(my_level: HierarchyLevel, max_children: u8, max_connections: u8) -> Self {
119 Self {
120 parent: None,
121 children: Vec::new(),
122 peers: Vec::new(),
123 my_level,
124 max_children,
125 max_connections,
126 }
127 }
128
129 pub fn connection_count(&self) -> usize {
131 let parent_count = if self.parent.is_some() { 1 } else { 0 };
132 parent_count + self.children.len() + self.peers.len()
133 }
134
135 pub fn can_accept_connection(&self) -> bool {
137 self.connection_count() < self.max_connections as usize
138 }
139
140 pub fn can_accept_child(&self) -> bool {
142 self.children.len() < self.max_children as usize && self.can_accept_connection()
143 }
144
145 pub fn has_parent(&self) -> bool {
147 self.parent.is_some()
148 }
149
150 pub fn set_parent(&mut self, node_id: NodeId) -> bool {
152 if self.parent.is_some() {
153 return false;
154 }
155 if !self.can_accept_connection() {
156 return false;
157 }
158 self.parent = Some(node_id);
159 true
160 }
161
162 pub fn clear_parent(&mut self) -> Option<NodeId> {
164 self.parent.take()
165 }
166
167 pub fn add_child(&mut self, node_id: NodeId) -> bool {
169 if !self.can_accept_child() {
170 return false;
171 }
172 if self.children.contains(&node_id) {
173 return false;
174 }
175 self.children.push(node_id);
176 true
177 }
178
179 pub fn remove_child(&mut self, node_id: &NodeId) -> bool {
181 if let Some(pos) = self.children.iter().position(|n| n == node_id) {
182 self.children.remove(pos);
183 true
184 } else {
185 false
186 }
187 }
188
189 pub fn add_peer(&mut self, node_id: NodeId) -> bool {
191 if !self.can_accept_connection() {
192 return false;
193 }
194 if self.peers.contains(&node_id) {
195 return false;
196 }
197 self.peers.push(node_id);
198 true
199 }
200
201 pub fn remove_peer(&mut self, node_id: &NodeId) -> bool {
203 if let Some(pos) = self.peers.iter().position(|n| n == node_id) {
204 self.peers.remove(pos);
205 true
206 } else {
207 false
208 }
209 }
210
211 pub fn all_connected(&self) -> Vec<NodeId> {
213 let mut nodes = Vec::with_capacity(self.connection_count());
214 if let Some(ref parent) = self.parent {
215 nodes.push(*parent);
216 }
217 nodes.extend(self.children.iter().cloned());
218 nodes.extend(self.peers.iter().cloned());
219 nodes
220 }
221
222 pub fn is_connected(&self, node_id: &NodeId) -> bool {
224 self.parent.as_ref() == Some(node_id)
225 || self.children.contains(node_id)
226 || self.peers.contains(node_id)
227 }
228
229 pub fn get_role(&self, node_id: &NodeId) -> Option<PeerRole> {
231 if self.parent.as_ref() == Some(node_id) {
232 Some(PeerRole::Parent)
233 } else if self.children.contains(node_id) {
234 Some(PeerRole::Child)
235 } else if self.peers.contains(node_id) {
236 Some(PeerRole::Peer)
237 } else {
238 None
239 }
240 }
241}
242
243#[derive(Debug, Clone)]
245pub enum TopologyEvent {
246 ParentConnected {
248 node_id: NodeId,
250 level: HierarchyLevel,
252 rssi: Option<i8>,
254 },
255 ParentDisconnected {
257 node_id: NodeId,
259 reason: DisconnectReason,
261 },
262 ChildConnected {
264 node_id: NodeId,
266 level: HierarchyLevel,
268 },
269 ChildDisconnected {
271 node_id: NodeId,
273 reason: DisconnectReason,
275 },
276 PeerConnected {
278 node_id: NodeId,
280 },
281 PeerDisconnected {
283 node_id: NodeId,
285 reason: DisconnectReason,
287 },
288 TopologyChanged {
290 child_count: usize,
292 peer_count: usize,
294 has_parent: bool,
296 },
297 ParentFailoverStarted {
299 old_parent: NodeId,
301 },
302 ParentFailoverCompleted {
304 old_parent: NodeId,
306 new_parent: Option<NodeId>,
308 },
309 ConnectionQualityChanged {
311 node_id: NodeId,
313 rssi: i8,
315 },
316}
317
318#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
320pub enum DisconnectReason {
321 Requested,
323 Timeout,
325 RemoteDisconnect,
327 SupervisionTimeout,
329 LinkLoss,
331 LocalError,
333 #[default]
335 Unknown,
336}
337
338#[derive(Debug, Clone)]
340pub struct ParentCandidate {
341 pub node_id: NodeId,
343 pub level: HierarchyLevel,
345 pub rssi: i8,
347 pub age_ms: u64,
349 pub failure_count: u8,
351}
352
353impl ParentCandidate {
354 pub fn score(&self, my_level: HierarchyLevel) -> i32 {
362 let mut score = 0i32;
363
364 score += (self.rssi as i32 + 100) * 2; if self.age_ms < 1000 {
370 score += 20;
371 } else if self.age_ms < 3000 {
372 score += 10;
373 } else if self.age_ms < 5000 {
374 score += 5;
375 }
376 score -= (self.failure_count as i32) * 15;
380
381 let ideal_level = match my_level {
383 HierarchyLevel::Platform => HierarchyLevel::Squad,
384 HierarchyLevel::Squad => HierarchyLevel::Platoon,
385 HierarchyLevel::Platoon => HierarchyLevel::Company,
386 HierarchyLevel::Company => HierarchyLevel::Company, };
388
389 if self.level == ideal_level {
390 score += 30;
391 } else if self.level > my_level {
392 score += 15;
393 }
394 score
397 }
398}
399
400#[derive(Debug, Clone)]
402pub struct TopologyConfig {
403 pub max_children: u8,
405 pub max_connections: u8,
407 pub min_parent_rssi: i8,
409 pub max_beacon_age_ms: u64,
411 pub parent_timeout_ms: u64,
413 pub connect_timeout_ms: u64,
415 pub max_failures: u8,
417 pub failover_delay_ms: u64,
419 pub rssi_hysteresis: u8,
421}
422
423impl Default for TopologyConfig {
424 fn default() -> Self {
425 Self {
426 max_children: 3,
427 max_connections: 7,
428 min_parent_rssi: -85,
429 max_beacon_age_ms: 10_000,
430 parent_timeout_ms: 5_000,
431 connect_timeout_ms: 10_000,
432 max_failures: 3,
433 failover_delay_ms: 1_000,
434 rssi_hysteresis: 6,
435 }
436 }
437}
438
439#[cfg(test)]
440mod tests {
441 use super::*;
442
443 #[test]
444 fn test_mesh_topology_new() {
445 let topology = MeshTopology::new(HierarchyLevel::Squad, 3, 7);
446 assert_eq!(topology.my_level, HierarchyLevel::Squad);
447 assert_eq!(topology.max_children, 3);
448 assert_eq!(topology.max_connections, 7);
449 assert!(topology.parent.is_none());
450 assert!(topology.children.is_empty());
451 }
452
453 #[test]
454 fn test_set_parent() {
455 let mut topology = MeshTopology::new(HierarchyLevel::Platform, 3, 7);
456 let parent_id = NodeId::new(0x1234);
457
458 assert!(topology.set_parent(parent_id));
459 assert_eq!(topology.parent, Some(parent_id));
460 assert_eq!(topology.connection_count(), 1);
461
462 assert!(!topology.set_parent(NodeId::new(0x5678)));
464 }
465
466 #[test]
467 fn test_add_children() {
468 let mut topology = MeshTopology::new(HierarchyLevel::Squad, 2, 7);
469
470 assert!(topology.add_child(NodeId::new(0x1111)));
471 assert!(topology.add_child(NodeId::new(0x2222)));
472 assert!(!topology.add_child(NodeId::new(0x3333)));
474
475 assert_eq!(topology.children.len(), 2);
476 }
477
478 #[test]
479 fn test_connection_limit() {
480 let mut topology = MeshTopology::new(HierarchyLevel::Squad, 5, 3);
481
482 assert!(topology.set_parent(NodeId::new(0x0001)));
483 assert!(topology.add_child(NodeId::new(0x0002)));
484 assert!(topology.add_peer(NodeId::new(0x0003)));
485 assert!(!topology.add_child(NodeId::new(0x0004)));
487 assert!(!topology.add_peer(NodeId::new(0x0005)));
488
489 assert_eq!(topology.connection_count(), 3);
490 }
491
492 #[test]
493 fn test_remove_child() {
494 let mut topology = MeshTopology::new(HierarchyLevel::Squad, 3, 7);
495 let child_id = NodeId::new(0x1111);
496
497 topology.add_child(child_id);
498 assert!(topology.remove_child(&child_id));
499 assert!(!topology.remove_child(&child_id)); assert!(topology.children.is_empty());
501 }
502
503 #[test]
504 fn test_get_role() {
505 let mut topology = MeshTopology::new(HierarchyLevel::Squad, 3, 7);
506 let parent_id = NodeId::new(0x0001);
507 let child_id = NodeId::new(0x0002);
508 let peer_id = NodeId::new(0x0003);
509 let unknown_id = NodeId::new(0x9999);
510
511 topology.set_parent(parent_id);
512 topology.add_child(child_id);
513 topology.add_peer(peer_id);
514
515 assert_eq!(topology.get_role(&parent_id), Some(PeerRole::Parent));
516 assert_eq!(topology.get_role(&child_id), Some(PeerRole::Child));
517 assert_eq!(topology.get_role(&peer_id), Some(PeerRole::Peer));
518 assert_eq!(topology.get_role(&unknown_id), None);
519 }
520
521 #[test]
522 fn test_all_connected() {
523 let mut topology = MeshTopology::new(HierarchyLevel::Squad, 3, 7);
524 topology.set_parent(NodeId::new(0x0001));
525 topology.add_child(NodeId::new(0x0002));
526 topology.add_peer(NodeId::new(0x0003));
527
528 let all = topology.all_connected();
529 assert_eq!(all.len(), 3);
530 }
531
532 #[test]
533 fn test_parent_candidate_score() {
534 let candidate = ParentCandidate {
535 node_id: NodeId::new(0x1234),
536 level: HierarchyLevel::Squad,
537 rssi: -50,
538 age_ms: 500,
539 failure_count: 0,
540 };
541
542 let score = candidate.score(HierarchyLevel::Platform);
544 assert_eq!(score, 150);
549 }
550
551 #[test]
552 fn test_parent_candidate_score_with_failures() {
553 let candidate = ParentCandidate {
554 node_id: NodeId::new(0x1234),
555 level: HierarchyLevel::Squad,
556 rssi: -50,
557 age_ms: 500,
558 failure_count: 2,
559 };
560
561 let score = candidate.score(HierarchyLevel::Platform);
562 assert_eq!(score, 120);
564 }
565
566 #[test]
567 fn test_peer_info() {
568 let mut peer = PeerInfo::new(
569 NodeId::new(0x1234),
570 PeerRole::Child,
571 HierarchyLevel::Platform,
572 );
573
574 assert!(!peer.is_connected());
575 assert_eq!(peer.failure_count, 0);
576
577 peer.record_failure();
578 peer.record_failure();
579 assert_eq!(peer.failure_count, 2);
580
581 peer.reset_failures();
582 assert_eq!(peer.failure_count, 0);
583 }
584
585 #[test]
586 fn test_topology_config_default() {
587 let config = TopologyConfig::default();
588 assert_eq!(config.max_children, 3);
589 assert_eq!(config.max_connections, 7);
590 assert_eq!(config.min_parent_rssi, -85);
591 }
592}