hdds_micro/transport/mesh/
neighbor.rs1#[derive(Debug, Clone, Copy, Default)]
8pub struct Neighbor {
9 pub node_id: u8,
11 pub rssi: i16,
13 pub last_seen: u32,
15 pub rx_count: u16,
17 valid: bool,
19}
20
21impl Neighbor {
22 pub fn new(node_id: u8, rssi: i16, tick: u32) -> Self {
24 Self {
25 node_id,
26 rssi,
27 last_seen: tick,
28 rx_count: 1,
29 valid: true,
30 }
31 }
32
33 pub fn update(&mut self, rssi: i16, tick: u32) {
35 self.rssi = ((self.rssi as i32 * 7 + rssi as i32) / 8) as i16;
37 self.last_seen = tick;
38 self.rx_count = self.rx_count.saturating_add(1);
39 }
40
41 pub fn is_valid(&self) -> bool {
43 self.valid
44 }
45
46 pub fn link_quality(&self) -> u8 {
48 let rssi = self.rssi.clamp(-100, -50);
52 ((rssi + 100) * 2).min(100) as u8
53 }
54}
55
56pub struct NeighborTable<const N: usize> {
58 entries: [Neighbor; N],
60 tick: u32,
62 lifetime: u32,
64}
65
66impl<const N: usize> NeighborTable<N> {
67 pub fn new(lifetime: u32) -> Self {
69 Self {
70 entries: [Neighbor::default(); N],
71 tick: 0,
72 lifetime,
73 }
74 }
75
76 pub fn tick(&mut self) {
78 self.tick = self.tick.wrapping_add(1);
79 }
80
81 pub fn update(&mut self, node_id: u8, rssi: i16) {
83 for entry in &mut self.entries {
85 if entry.valid && entry.node_id == node_id {
86 entry.update(rssi, self.tick);
87 return;
88 }
89 }
90
91 for entry in &mut self.entries {
94 if !entry.valid {
95 *entry = Neighbor::new(node_id, rssi, self.tick);
96 return;
97 }
98
99 let age = self.tick.wrapping_sub(entry.last_seen);
100 if age > self.lifetime {
101 *entry = Neighbor::new(node_id, rssi, self.tick);
102 return;
103 }
104 }
105
106 let mut worst_idx = 0;
108 let mut worst_quality = 255u8;
109
110 for (i, entry) in self.entries.iter().enumerate() {
111 if entry.valid {
112 let quality = entry.link_quality();
113 if quality < worst_quality {
114 worst_quality = quality;
115 worst_idx = i;
116 }
117 }
118 }
119
120 let new_quality = Neighbor::new(node_id, rssi, self.tick).link_quality();
122 if new_quality > worst_quality {
123 self.entries[worst_idx] = Neighbor::new(node_id, rssi, self.tick);
124 }
125 }
126
127 pub fn get(&self, node_id: u8) -> Option<&Neighbor> {
129 for entry in &self.entries {
130 if entry.valid && entry.node_id == node_id {
131 let age = self.tick.wrapping_sub(entry.last_seen);
132 if age <= self.lifetime {
133 return Some(entry);
134 }
135 }
136 }
137 None
138 }
139
140 pub fn best_neighbor(&self) -> Option<&Neighbor> {
142 let mut best: Option<&Neighbor> = None;
143 let mut best_quality = 0u8;
144
145 for entry in &self.entries {
146 if entry.valid {
147 let age = self.tick.wrapping_sub(entry.last_seen);
148 if age <= self.lifetime {
149 let quality = entry.link_quality();
150 if quality > best_quality {
151 best_quality = quality;
152 best = Some(entry);
153 }
154 }
155 }
156 }
157
158 best
159 }
160
161 pub fn iter(&self) -> impl Iterator<Item = &Neighbor> {
163 let tick = self.tick;
164 let lifetime = self.lifetime;
165
166 self.entries
167 .iter()
168 .filter(move |e| e.valid && tick.wrapping_sub(e.last_seen) <= lifetime)
169 }
170
171 pub fn len(&self) -> usize {
173 self.iter().count()
174 }
175
176 pub fn is_empty(&self) -> bool {
178 self.len() == 0
179 }
180
181 pub fn clear(&mut self) {
183 for entry in &mut self.entries {
184 entry.valid = false;
185 }
186 }
187
188 pub fn expire_old(&mut self) {
190 for entry in &mut self.entries {
191 if entry.valid {
192 let age = self.tick.wrapping_sub(entry.last_seen);
193 if age > self.lifetime {
194 entry.valid = false;
195 }
196 }
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_neighbor_creation() {
207 let neighbor = Neighbor::new(1, -70, 0);
208 assert_eq!(neighbor.node_id, 1);
209 assert_eq!(neighbor.rssi, -70);
210 assert!(neighbor.is_valid());
211 }
212
213 #[test]
214 fn test_neighbor_link_quality() {
215 let n1 = Neighbor::new(1, -50, 0);
217 assert_eq!(n1.link_quality(), 100);
218
219 let n2 = Neighbor::new(2, -70, 0);
221 assert_eq!(n2.link_quality(), 60);
222
223 let n3 = Neighbor::new(3, -95, 0);
225 assert_eq!(n3.link_quality(), 10);
226
227 let n4 = Neighbor::new(4, -120, 0);
229 assert_eq!(n4.link_quality(), 0);
230 }
231
232 #[test]
233 fn test_neighbor_table_basic() {
234 let mut table: NeighborTable<8> = NeighborTable::new(100);
235
236 table.update(1, -70);
237 table.update(2, -80);
238
239 assert_eq!(table.len(), 2);
240 assert!(table.get(1).is_some());
241 assert!(table.get(2).is_some());
242 assert!(table.get(3).is_none());
243 }
244
245 #[test]
246 fn test_neighbor_table_update() {
247 let mut table: NeighborTable<8> = NeighborTable::new(100);
248
249 table.update(1, -70);
250 assert_eq!(table.get(1).unwrap().rx_count, 1);
251
252 table.update(1, -65);
253 let neighbor = table.get(1).unwrap();
254 assert_eq!(neighbor.rx_count, 2);
255 assert!(neighbor.rssi > -70 && neighbor.rssi < -65);
257 }
258
259 #[test]
260 fn test_neighbor_table_expiry() {
261 let mut table: NeighborTable<8> = NeighborTable::new(10);
262
263 table.update(1, -70);
264 assert_eq!(table.len(), 1);
265
266 for _ in 0..15 {
268 table.tick();
269 }
270
271 assert!(table.get(1).is_none());
273 assert_eq!(table.len(), 0);
274 }
275
276 #[test]
277 fn test_neighbor_table_best() {
278 let mut table: NeighborTable<8> = NeighborTable::new(100);
279
280 table.update(1, -90); table.update(2, -60); table.update(3, -80); let best = table.best_neighbor().unwrap();
285 assert_eq!(best.node_id, 2);
286 }
287
288 #[test]
289 fn test_neighbor_table_full() {
290 let mut table: NeighborTable<4> = NeighborTable::new(1000);
291
292 table.update(1, -95);
294 table.update(2, -95);
295 table.update(3, -95);
296 table.update(4, -95);
297 assert_eq!(table.len(), 4);
298
299 table.update(5, -60);
301 assert_eq!(table.len(), 4);
302 assert!(table.get(5).is_some());
303 }
304
305 #[test]
306 fn test_neighbor_table_clear() {
307 let mut table: NeighborTable<8> = NeighborTable::new(100);
308
309 table.update(1, -70);
310 table.update(2, -80);
311 assert_eq!(table.len(), 2);
312
313 table.clear();
314 assert_eq!(table.len(), 0);
315 assert!(table.is_empty());
316 }
317}