完成py_plan.md
This commit is contained in:
5
sim/routing/__init__.py
Normal file
5
sim/routing/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Routing module."""
|
||||
|
||||
from sim.routing.gradient_routing import GradientRouting
|
||||
|
||||
__all__ = ["GradientRouting"]
|
||||
178
sim/routing/gradient_routing.py
Normal file
178
sim/routing/gradient_routing.py
Normal file
@@ -0,0 +1,178 @@
|
||||
"""
|
||||
Gradient-based routing protocol.
|
||||
|
||||
Implements:
|
||||
- Cost-based routing (gradient routing)
|
||||
- HELLO message handling for neighbor discovery
|
||||
- Parent selection based on cost + link penalty
|
||||
- Data forwarding to parent node
|
||||
"""
|
||||
|
||||
from typing import Dict, Optional
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from sim.core.packet import Packet, PacketType
|
||||
from sim.radio import propagation
|
||||
from sim import config
|
||||
|
||||
|
||||
@dataclass
|
||||
class NeighborInfo:
|
||||
"""Information about a neighbor node."""
|
||||
|
||||
node_id: int
|
||||
cost: int
|
||||
rssi: float
|
||||
last_hello_time: float
|
||||
|
||||
|
||||
class GradientRouting:
|
||||
"""
|
||||
Gradient routing protocol.
|
||||
|
||||
Each node maintains:
|
||||
- cost: Distance to sink (in hops + penalty)
|
||||
- parent: Next hop towards sink
|
||||
- neighbors: Dict of known neighbors with their costs
|
||||
"""
|
||||
|
||||
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.cost = 0 if is_sink else float("inf")
|
||||
self.parent: Optional[int] = None
|
||||
self.neighbors: Dict[int, NeighborInfo] = {}
|
||||
|
||||
# Sequence number for HELLO messages
|
||||
self.hello_seq = 0
|
||||
|
||||
def reset(self):
|
||||
"""Reset routing state."""
|
||||
self.cost = 0 if self.is_sink else float("inf")
|
||||
self.parent = None
|
||||
self.neighbors.clear()
|
||||
self.hello_seq = 0
|
||||
|
||||
def create_hello_packet(self) -> Packet:
|
||||
"""
|
||||
Create a HELLO packet for neighbor discovery.
|
||||
|
||||
Returns:
|
||||
HELLO packet with current cost
|
||||
"""
|
||||
packet = Packet(
|
||||
type=PacketType.HELLO,
|
||||
src=self.node_id,
|
||||
dst=-1, # Broadcast
|
||||
seq=self.hello_seq,
|
||||
hop=0,
|
||||
payload=str(int(self.cost)) if self.cost != float("inf") else "inf",
|
||||
)
|
||||
self.hello_seq += 1
|
||||
return packet
|
||||
|
||||
def process_hello(self, packet: Packet, rssi: float) -> bool:
|
||||
"""
|
||||
Process received HELLO packet.
|
||||
|
||||
Args:
|
||||
packet: Received HELLO packet
|
||||
rssi: RSSI of received signal
|
||||
|
||||
Returns:
|
||||
True if routing state changed (cost/parent updated)
|
||||
"""
|
||||
# Parse cost from payload
|
||||
try:
|
||||
neighbor_cost = int(packet.payload) if packet.payload else 0
|
||||
except ValueError:
|
||||
neighbor_cost = 0
|
||||
|
||||
# Calculate link penalty based on RSSI
|
||||
link_penalty = propagation.calculate_link_penalty(rssi)
|
||||
|
||||
# Calculate new cost to sink through this neighbor
|
||||
new_cost = neighbor_cost + 1 + int(link_penalty)
|
||||
|
||||
# Update neighbor info
|
||||
old_neighbor = self.neighbors.get(packet.src)
|
||||
self.neighbors[packet.src] = NeighborInfo(
|
||||
node_id=packet.src,
|
||||
cost=neighbor_cost,
|
||||
rssi=rssi,
|
||||
last_hello_time=packet.rssi, # Use rssi field to store time
|
||||
)
|
||||
|
||||
# Check if we should update our route
|
||||
# Update condition: new_cost < cost - 1
|
||||
old_cost = self.cost
|
||||
if new_cost < self.cost - config.ROUTE_UPDATE_THRESHOLD:
|
||||
self.cost = new_cost
|
||||
self.parent = packet.src
|
||||
return True
|
||||
|
||||
# Also update if we have no route yet
|
||||
if self.parent is None and not self.is_sink:
|
||||
if new_cost < float("inf"):
|
||||
self.cost = new_cost
|
||||
self.parent = packet.src
|
||||
return True
|
||||
|
||||
return old_cost != self.cost
|
||||
|
||||
def get_next_hop(self) -> Optional[int]:
|
||||
"""
|
||||
Get next hop towards sink.
|
||||
|
||||
Returns:
|
||||
Parent node ID, or None if no route
|
||||
"""
|
||||
return self.parent
|
||||
|
||||
def is_route_valid(self) -> bool:
|
||||
"""Check if current route is valid."""
|
||||
if self.is_sink:
|
||||
return True
|
||||
return self.parent is not None and self.cost < float("inf")
|
||||
|
||||
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]
|
||||
|
||||
# If our parent is stale, we need to find a new one
|
||||
if self.parent in stale:
|
||||
self.parent = None
|
||||
self.cost = float("inf")
|
||||
# Try to find new parent
|
||||
for nid, info in self.neighbors.items():
|
||||
if info.cost < self.cost:
|
||||
self.cost = info.cost + 1
|
||||
self.parent = 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": int(self.cost) if self.cost != float("inf") else -1,
|
||||
"parent": self.parent,
|
||||
"neighbors": {
|
||||
nid: {"cost": info.cost, "rssi": round(info.rssi, 2)}
|
||||
for nid, info in self.neighbors.items()
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user