1#[cfg(not(feature = "std"))]
7use alloc::{string::String, vec::Vec};
8
9use crate::NodeId;
10
11#[derive(Debug, Clone)]
16pub struct HivePeer {
17 pub node_id: NodeId,
19
20 pub identifier: String,
25
26 pub mesh_id: Option<String>,
28
29 pub name: Option<String>,
31
32 pub rssi: i8,
34
35 pub is_connected: bool,
37
38 pub last_seen_ms: u64,
40}
41
42impl HivePeer {
43 pub fn new(
45 node_id: NodeId,
46 identifier: String,
47 mesh_id: Option<String>,
48 name: Option<String>,
49 rssi: i8,
50 ) -> Self {
51 Self {
52 node_id,
53 identifier,
54 mesh_id,
55 name,
56 rssi,
57 is_connected: false,
58 last_seen_ms: 0,
59 }
60 }
61
62 pub fn touch(&mut self, now_ms: u64) {
64 self.last_seen_ms = now_ms;
65 }
66
67 pub fn is_stale(&self, now_ms: u64, timeout_ms: u64) -> bool {
69 if self.last_seen_ms == 0 {
70 return false; }
72 now_ms.saturating_sub(self.last_seen_ms) > timeout_ms
73 }
74
75 pub fn display_name(&self) -> &str {
77 self.name.as_deref().unwrap_or(self.identifier.as_str())
78 }
79
80 pub fn signal_strength(&self) -> SignalStrength {
82 match self.rssi {
83 r if r >= -50 => SignalStrength::Excellent,
84 r if r >= -70 => SignalStrength::Good,
85 r if r >= -85 => SignalStrength::Fair,
86 _ => SignalStrength::Weak,
87 }
88 }
89}
90
91impl Default for HivePeer {
92 fn default() -> Self {
93 Self {
94 node_id: NodeId::default(),
95 identifier: String::new(),
96 mesh_id: None,
97 name: None,
98 rssi: -100,
99 is_connected: false,
100 last_seen_ms: 0,
101 }
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum SignalStrength {
108 Excellent,
110 Good,
112 Fair,
114 Weak,
116}
117
118#[derive(Debug, Clone)]
123pub struct PeerManagerConfig {
124 pub peer_timeout_ms: u64,
126
127 pub cleanup_interval_ms: u64,
129
130 pub sync_interval_ms: u64,
132
133 pub sync_cooldown_ms: u64,
136
137 pub auto_connect: bool,
139
140 pub mesh_id: String,
142
143 pub max_peers: usize,
145}
146
147impl Default for PeerManagerConfig {
148 fn default() -> Self {
149 Self {
150 peer_timeout_ms: 45_000, cleanup_interval_ms: 10_000, sync_interval_ms: 5_000, sync_cooldown_ms: 30_000, auto_connect: true,
155 mesh_id: String::from("DEMO"),
156 max_peers: 8,
157 }
158 }
159}
160
161impl PeerManagerConfig {
162 pub fn with_mesh_id(mesh_id: impl Into<String>) -> Self {
164 Self {
165 mesh_id: mesh_id.into(),
166 ..Default::default()
167 }
168 }
169
170 pub fn peer_timeout(mut self, timeout_ms: u64) -> Self {
172 self.peer_timeout_ms = timeout_ms;
173 self
174 }
175
176 pub fn sync_interval(mut self, interval_ms: u64) -> Self {
178 self.sync_interval_ms = interval_ms;
179 self
180 }
181
182 pub fn auto_connect(mut self, enabled: bool) -> Self {
184 self.auto_connect = enabled;
185 self
186 }
187
188 pub fn max_peers(mut self, max: usize) -> Self {
190 self.max_peers = max;
191 self
192 }
193
194 pub fn matches_mesh(&self, device_mesh_id: Option<&str>) -> bool {
200 match device_mesh_id {
201 Some(id) => id == self.mesh_id,
202 None => true, }
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn test_peer_stale_detection() {
213 let mut peer = HivePeer::new(
214 NodeId::new(0x12345678),
215 "test-id".into(),
216 Some("DEMO".into()),
217 Some("HIVE_DEMO-12345678".into()),
218 -70,
219 );
220
221 peer.touch(1000);
223 assert!(!peer.is_stale(2000, 45_000));
224
225 assert!(peer.is_stale(50_000, 45_000));
227 }
228
229 #[test]
230 fn test_signal_strength() {
231 let peer_excellent = HivePeer {
232 rssi: -45,
233 ..Default::default()
234 };
235 assert_eq!(peer_excellent.signal_strength(), SignalStrength::Excellent);
236
237 let peer_good = HivePeer {
238 rssi: -65,
239 ..Default::default()
240 };
241 assert_eq!(peer_good.signal_strength(), SignalStrength::Good);
242
243 let peer_fair = HivePeer {
244 rssi: -80,
245 ..Default::default()
246 };
247 assert_eq!(peer_fair.signal_strength(), SignalStrength::Fair);
248
249 let peer_weak = HivePeer {
250 rssi: -95,
251 ..Default::default()
252 };
253 assert_eq!(peer_weak.signal_strength(), SignalStrength::Weak);
254 }
255
256 #[test]
257 fn test_mesh_matching() {
258 let config = PeerManagerConfig::with_mesh_id("ALPHA");
259
260 assert!(config.matches_mesh(Some("ALPHA")));
262
263 assert!(!config.matches_mesh(Some("BETA")));
265
266 assert!(config.matches_mesh(None));
268 }
269}