149 lines
4.0 KiB
Python
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",
|
|
}
|