#[derive(Debug, Clone, Copy, Default)]
pub struct Neighbor {
pub node_id: u8,
pub rssi: i16,
pub last_seen: u32,
pub rx_count: u16,
valid: bool,
}
impl Neighbor {
pub fn new(node_id: u8, rssi: i16, tick: u32) -> Self {
Self {
node_id,
rssi,
last_seen: tick,
rx_count: 1,
valid: true,
}
}
pub fn update(&mut self, rssi: i16, tick: u32) {
self.rssi = ((self.rssi as i32 * 7 + rssi as i32) / 8) as i16;
self.last_seen = tick;
self.rx_count = self.rx_count.saturating_add(1);
}
pub fn is_valid(&self) -> bool {
self.valid
}
pub fn link_quality(&self) -> u8 {
let rssi = self.rssi.clamp(-100, -50);
((rssi + 100) * 2).min(100) as u8
}
}
pub struct NeighborTable<const N: usize> {
entries: [Neighbor; N],
tick: u32,
lifetime: u32,
}
impl<const N: usize> NeighborTable<N> {
pub fn new(lifetime: u32) -> Self {
Self {
entries: [Neighbor::default(); N],
tick: 0,
lifetime,
}
}
pub fn tick(&mut self) {
self.tick = self.tick.wrapping_add(1);
}
pub fn update(&mut self, node_id: u8, rssi: i16) {
for entry in &mut self.entries {
if entry.valid && entry.node_id == node_id {
entry.update(rssi, self.tick);
return;
}
}
for entry in &mut self.entries {
if !entry.valid {
*entry = Neighbor::new(node_id, rssi, self.tick);
return;
}
let age = self.tick.wrapping_sub(entry.last_seen);
if age > self.lifetime {
*entry = Neighbor::new(node_id, rssi, self.tick);
return;
}
}
let mut worst_idx = 0;
let mut worst_quality = 255u8;
for (i, entry) in self.entries.iter().enumerate() {
if entry.valid {
let quality = entry.link_quality();
if quality < worst_quality {
worst_quality = quality;
worst_idx = i;
}
}
}
let new_quality = Neighbor::new(node_id, rssi, self.tick).link_quality();
if new_quality > worst_quality {
self.entries[worst_idx] = Neighbor::new(node_id, rssi, self.tick);
}
}
pub fn get(&self, node_id: u8) -> Option<&Neighbor> {
for entry in &self.entries {
if entry.valid && entry.node_id == node_id {
let age = self.tick.wrapping_sub(entry.last_seen);
if age <= self.lifetime {
return Some(entry);
}
}
}
None
}
pub fn best_neighbor(&self) -> Option<&Neighbor> {
let mut best: Option<&Neighbor> = None;
let mut best_quality = 0u8;
for entry in &self.entries {
if entry.valid {
let age = self.tick.wrapping_sub(entry.last_seen);
if age <= self.lifetime {
let quality = entry.link_quality();
if quality > best_quality {
best_quality = quality;
best = Some(entry);
}
}
}
}
best
}
pub fn iter(&self) -> impl Iterator<Item = &Neighbor> {
let tick = self.tick;
let lifetime = self.lifetime;
self.entries
.iter()
.filter(move |e| e.valid && tick.wrapping_sub(e.last_seen) <= lifetime)
}
pub fn len(&self) -> usize {
self.iter().count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clear(&mut self) {
for entry in &mut self.entries {
entry.valid = false;
}
}
pub fn expire_old(&mut self) {
for entry in &mut self.entries {
if entry.valid {
let age = self.tick.wrapping_sub(entry.last_seen);
if age > self.lifetime {
entry.valid = false;
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_neighbor_creation() {
let neighbor = Neighbor::new(1, -70, 0);
assert_eq!(neighbor.node_id, 1);
assert_eq!(neighbor.rssi, -70);
assert!(neighbor.is_valid());
}
#[test]
fn test_neighbor_link_quality() {
let n1 = Neighbor::new(1, -50, 0);
assert_eq!(n1.link_quality(), 100);
let n2 = Neighbor::new(2, -70, 0);
assert_eq!(n2.link_quality(), 60);
let n3 = Neighbor::new(3, -95, 0);
assert_eq!(n3.link_quality(), 10);
let n4 = Neighbor::new(4, -120, 0);
assert_eq!(n4.link_quality(), 0);
}
#[test]
fn test_neighbor_table_basic() {
let mut table: NeighborTable<8> = NeighborTable::new(100);
table.update(1, -70);
table.update(2, -80);
assert_eq!(table.len(), 2);
assert!(table.get(1).is_some());
assert!(table.get(2).is_some());
assert!(table.get(3).is_none());
}
#[test]
fn test_neighbor_table_update() {
let mut table: NeighborTable<8> = NeighborTable::new(100);
table.update(1, -70);
assert_eq!(table.get(1).unwrap().rx_count, 1);
table.update(1, -65);
let neighbor = table.get(1).unwrap();
assert_eq!(neighbor.rx_count, 2);
assert!(neighbor.rssi > -70 && neighbor.rssi < -65);
}
#[test]
fn test_neighbor_table_expiry() {
let mut table: NeighborTable<8> = NeighborTable::new(10);
table.update(1, -70);
assert_eq!(table.len(), 1);
for _ in 0..15 {
table.tick();
}
assert!(table.get(1).is_none());
assert_eq!(table.len(), 0);
}
#[test]
fn test_neighbor_table_best() {
let mut table: NeighborTable<8> = NeighborTable::new(100);
table.update(1, -90); table.update(2, -60); table.update(3, -80);
let best = table.best_neighbor().unwrap();
assert_eq!(best.node_id, 2);
}
#[test]
fn test_neighbor_table_full() {
let mut table: NeighborTable<4> = NeighborTable::new(1000);
table.update(1, -95);
table.update(2, -95);
table.update(3, -95);
table.update(4, -95);
assert_eq!(table.len(), 4);
table.update(5, -60);
assert_eq!(table.len(), 4);
assert!(table.get(5).is_some());
}
#[test]
fn test_neighbor_table_clear() {
let mut table: NeighborTable<8> = NeighborTable::new(100);
table.update(1, -70);
table.update(2, -80);
assert_eq!(table.len(), 2);
table.clear();
assert_eq!(table.len(), 0);
assert!(table.is_empty());
}
}