Files
lora_route_py/sim/routing/random_forward.py

149 lines
4.0 KiB
Python

"""
Random forwarding routing protocol.
Baseline algorithm: randomly select ONE neighbor to forward to.
This demonstrates the value of gradient-based routing.
"""
import random
from typing import Dict, Optional
from dataclasses import dataclass
from sim.core.packet import Packet, PacketType
from sim import config
@dataclass
class NeighborInfo:
"""Information about a neighbor node."""
node_id: int
rssi: float
last_hello_time: float
class RandomForwardRouting:
"""
Random forwarding routing protocol.
Each node maintains:
- neighbors: Dict of known neighbors
- For each packet, randomly selects ONE neighbor to forward to
This is a baseline to demonstrate why gradient routing is better.
"""
def __init__(self, node_id: int, is_sink: bool = False):
"""
Initialize routing.
Args:
node_id: This node's ID
is_sink: Whether this node is the sink
"""
self.node_id = node_id
self.is_sink = is_sink
# Routing state
self.parent: Optional[int] = None # Randomly selected per packet
self.neighbors: Dict[int, NeighborInfo] = {}
# Sequence number for HELLO messages
self.hello_seq = 0
# Cost (for compatibility with metrics)
self.cost = 0 if is_sink else 1
def reset(self):
"""Reset routing state."""
self.neighbors.clear()
self.hello_seq = 0
self.cost = 0 if self.is_sink else 1
self.parent = None
def create_hello_packet(self) -> Packet:
"""
Create a HELLO packet for neighbor discovery.
Returns:
HELLO packet with node ID
"""
packet = Packet(
type=PacketType.HELLO,
src=self.node_id,
dst=-1, # Broadcast
seq=self.hello_seq,
hop=0,
payload=str(self.node_id),
)
self.hello_seq += 1
return packet
def process_hello(self, packet: Packet, rssi: float) -> bool:
"""
Process received HELLO packet.
Track neighbors but don't calculate cost.
Args:
packet: Received HELLO packet
rssi: RSSI of received signal
Returns:
True if neighbor list changed
"""
# Update neighbor info
old_neighbors = len(self.neighbors)
self.neighbors[packet.src] = NeighborInfo(
node_id=packet.src,
rssi=rssi,
last_hello_time=rssi, # Store time in rssi field
)
return len(self.neighbors) != old_neighbors
def get_next_hop(self, packet: Packet = None) -> Optional[int]:
"""
Randomly select ONE neighbor to forward to.
Args:
packet: The packet to forward (unused in random routing)
Returns:
Random neighbor ID, or None if no neighbors
"""
if not self.neighbors:
return None
# Randomly select one neighbor
neighbor_ids = list(self.neighbors.keys())
return random.choice(neighbor_ids)
def is_route_valid(self) -> bool:
"""Check if routing is valid."""
return len(self.neighbors) > 0
def cleanup_stale_neighbors(self, current_time: float, timeout: float = 30.0):
"""Remove neighbors that haven't sent HELLO recently."""
stale = [
nid
for nid, info in self.neighbors.items()
if current_time - info.last_hello_time > timeout
]
for nid in stale:
del self.neighbors[nid]
def get_routing_table(self) -> dict:
"""Get routing table for debugging/visualization."""
return {
"node_id": self.node_id,
"is_sink": self.is_sink,
"cost": self.cost,
"parent": self.parent,
"neighbors": {
nid: {"rssi": round(info.rssi, 2)}
for nid, info in self.neighbors.items()
},
"algorithm": "random_forward",
}