完成py_plan.md
This commit is contained in:
240
sim/main.py
Normal file
240
sim/main.py
Normal file
@@ -0,0 +1,240 @@
|
||||
"""
|
||||
Main simulation entry point.
|
||||
|
||||
Simulation flow:
|
||||
1. Create SimPy environment
|
||||
2. Create wireless channel
|
||||
3. Randomly deploy nodes
|
||||
4. Designate sink node
|
||||
5. Start node processes
|
||||
6. Run simulation
|
||||
7. Output metrics
|
||||
"""
|
||||
|
||||
import random
|
||||
import json
|
||||
import simpy
|
||||
|
||||
from sim.node.node import Node
|
||||
from sim.radio.channel import Channel
|
||||
from sim.core.metrics import MetricsCollector
|
||||
from sim import config
|
||||
|
||||
|
||||
def deploy_nodes(
|
||||
env: simpy.Environment,
|
||||
channel: Channel,
|
||||
num_nodes: int = None,
|
||||
area_size: float = None,
|
||||
) -> list:
|
||||
"""
|
||||
Deploy nodes randomly in the area.
|
||||
|
||||
Args:
|
||||
env: SimPy environment
|
||||
channel: Wireless channel
|
||||
num_nodes: Number of nodes (default from config)
|
||||
area_size: Area size (default from config)
|
||||
|
||||
Returns:
|
||||
List of Node objects
|
||||
"""
|
||||
if num_nodes is None:
|
||||
num_nodes = config.NODE_COUNT
|
||||
if area_size is None:
|
||||
area_size = config.AREA_SIZE
|
||||
|
||||
nodes = []
|
||||
|
||||
# Deploy sink node at center
|
||||
sink_x = area_size / 2
|
||||
sink_y = area_size / 2
|
||||
|
||||
sink = Node(
|
||||
env=env,
|
||||
node_id=config.SINK_NODE_ID,
|
||||
x=sink_x,
|
||||
y=sink_y,
|
||||
channel=channel,
|
||||
is_sink=True,
|
||||
)
|
||||
nodes.append(sink)
|
||||
|
||||
# Deploy other nodes randomly
|
||||
for i in range(1, num_nodes):
|
||||
x = random.uniform(0, area_size)
|
||||
y = random.uniform(0, area_size)
|
||||
|
||||
node = Node(env=env, node_id=i, x=x, y=y, channel=channel)
|
||||
nodes.append(node)
|
||||
|
||||
return nodes
|
||||
|
||||
|
||||
def setup_receive_callback(nodes: list, channel: Channel):
|
||||
"""
|
||||
Setup receive callback from channel to nodes.
|
||||
|
||||
Args:
|
||||
nodes: List of nodes
|
||||
channel: Wireless channel
|
||||
"""
|
||||
|
||||
def receive_dispatcher(node_id: int, received):
|
||||
"""Dispatch received packet to appropriate node."""
|
||||
for node in nodes:
|
||||
if node.node_id == node_id:
|
||||
node.on_receive(received)
|
||||
break
|
||||
|
||||
channel.receive_callback = receive_dispatcher
|
||||
|
||||
|
||||
def run_simulation(
|
||||
num_nodes: int = None,
|
||||
area_size: float = None,
|
||||
sim_time: float = None,
|
||||
seed: int = None,
|
||||
) -> dict:
|
||||
"""
|
||||
Run the LoRa network simulation.
|
||||
|
||||
Args:
|
||||
num_nodes: Number of nodes
|
||||
area_size: Area size in meters
|
||||
sim_time: Simulation time in seconds
|
||||
seed: Random seed for reproducibility
|
||||
|
||||
Returns:
|
||||
Simulation results including metrics
|
||||
"""
|
||||
# Set random seed
|
||||
if seed is not None:
|
||||
random.seed(seed)
|
||||
|
||||
# Create environment
|
||||
env = simpy.Environment()
|
||||
|
||||
# Create channel
|
||||
channel = Channel(env)
|
||||
|
||||
# Deploy nodes
|
||||
if num_nodes is None:
|
||||
num_nodes = config.NODE_COUNT
|
||||
if area_size is None:
|
||||
area_size = config.AREA_SIZE
|
||||
if sim_time is None:
|
||||
sim_time = config.SIM_TIME
|
||||
|
||||
nodes = deploy_nodes(env, channel, num_nodes, area_size)
|
||||
|
||||
# Setup receive callbacks
|
||||
setup_receive_callback(nodes, channel)
|
||||
|
||||
# Create metrics collector
|
||||
metrics = MetricsCollector()
|
||||
metrics.set_start_time(0.0)
|
||||
|
||||
# Add collision callback
|
||||
initial_collisions = channel.collision_count
|
||||
|
||||
# Start all nodes
|
||||
for node in nodes:
|
||||
node.start()
|
||||
|
||||
# Run simulation
|
||||
env.run(until=sim_time)
|
||||
|
||||
# Collect metrics
|
||||
convergence_time = config.HELLO_PERIOD * 3 # Estimate convergence
|
||||
metrics.set_convergence_time(convergence_time)
|
||||
|
||||
# First add stats for non-sink nodes
|
||||
for node in nodes:
|
||||
if node.is_sink:
|
||||
continue
|
||||
stats = node.get_stats()
|
||||
metrics.add_node_stats(node.node_id, stats)
|
||||
|
||||
# Then add sink stats
|
||||
for node in nodes:
|
||||
if node.is_sink:
|
||||
stats = node.get_stats()
|
||||
metrics.add_sink_stats(node.node_id, stats)
|
||||
break
|
||||
|
||||
metrics.add_collision(channel.collision_count - initial_collisions)
|
||||
|
||||
# Get results
|
||||
results = {
|
||||
"config": {
|
||||
"num_nodes": num_nodes,
|
||||
"area_size": area_size,
|
||||
"sim_time": sim_time,
|
||||
"seed": seed,
|
||||
},
|
||||
"metrics": metrics.get_metrics().get_summary(),
|
||||
"topology": [],
|
||||
}
|
||||
|
||||
# Add topology info
|
||||
for node in nodes:
|
||||
results["topology"].append(
|
||||
{
|
||||
"node_id": node.node_id,
|
||||
"is_sink": node.is_sink,
|
||||
"x": node.x,
|
||||
"y": node.y,
|
||||
"cost": node.routing.cost if node.routing.cost != float("inf") else -1,
|
||||
"parent": node.routing.parent,
|
||||
}
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point."""
|
||||
print("=" * 60)
|
||||
print("LoRa Multi-Hop Network Simulation")
|
||||
print("=" * 60)
|
||||
|
||||
# Run simulation
|
||||
results = run_simulation()
|
||||
|
||||
# Print results
|
||||
print("\n--- Configuration ---")
|
||||
print(f"Nodes: {results['config']['num_nodes']}")
|
||||
print(
|
||||
f"Area: {results['config']['area_size']}m x {results['config']['area_size']}m"
|
||||
)
|
||||
print(f"Simulation time: {results['config']['sim_time']}s")
|
||||
|
||||
print("\n--- Metrics ---")
|
||||
metrics = results["metrics"]
|
||||
print(f"Total sent: {metrics['total_sent']}")
|
||||
print(f"Total received: {metrics['total_received']}")
|
||||
print(f"Packet Delivery Ratio: {metrics['pdr']}%")
|
||||
print(f"Average hops: {metrics['avg_hop']}")
|
||||
print(f"Average retries: {metrics['avg_retries']}")
|
||||
print(f"Convergence time: {metrics['convergence_time']}s")
|
||||
print(f"Collisions: {metrics['collisions']}")
|
||||
|
||||
print("\n--- Topology ---")
|
||||
for node_info in results["topology"]:
|
||||
parent_str = (
|
||||
f"-> {node_info['parent']}" if node_info["parent"] is not None else ""
|
||||
)
|
||||
print(
|
||||
f"Node {node_info['node_id']:2d}: cost={node_info['cost']:3d} {parent_str}"
|
||||
)
|
||||
|
||||
# Save results
|
||||
with open("simulation_results.json", "w") as f:
|
||||
json.dump(results, f, indent=2)
|
||||
|
||||
print("\nResults saved to simulation_results.json")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user