只有hello包实现多跳,还没加入业务数据
具体的还要看opencode和gpt记录接着优化
This commit is contained in:
26
sim/analysis_tools/__init__.py
Normal file
26
sim/analysis_tools/__init__.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""Analysis tools module."""
|
||||
|
||||
from sim.analysis_tools.topology import export_topology_json, analyze_parent_tree
|
||||
from sim.analysis_tools.convergence import (
|
||||
calculate_convergence_time,
|
||||
analyze_route_stability,
|
||||
)
|
||||
from sim.analysis_tools.channel_analysis import (
|
||||
analyze_channel_utilization,
|
||||
get_network_state,
|
||||
)
|
||||
from sim.analysis_tools.reliability_analysis import (
|
||||
analyze_loss_breakdown,
|
||||
calculate_pdr_metrics,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"export_topology_json",
|
||||
"analyze_parent_tree",
|
||||
"calculate_convergence_time",
|
||||
"analyze_route_stability",
|
||||
"analyze_channel_utilization",
|
||||
"get_network_state",
|
||||
"analyze_loss_breakdown",
|
||||
"calculate_pdr_metrics",
|
||||
]
|
||||
58
sim/analysis_tools/channel_analysis.py
Normal file
58
sim/analysis_tools/channel_analysis.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
Channel Analysis Tools.
|
||||
|
||||
Functions for analyzing channel utilization and collisions.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any
|
||||
|
||||
|
||||
def analyze_channel_utilization(collisions: int, busy_time: float, total_time: float) -> Dict[str, Any]:
|
||||
"""
|
||||
Analyze channel utilization.
|
||||
|
||||
Args:
|
||||
collisions: Number of collisions
|
||||
busy_time: Total channel busy time
|
||||
total_time: Total simulation time
|
||||
|
||||
Returns:
|
||||
Dictionary with channel analysis
|
||||
"""
|
||||
utilization = busy_time / total_time if total_time > 0 else 0
|
||||
|
||||
# Determine network state
|
||||
if utilization < 0.3:
|
||||
network_state = "LIGHT_LOAD"
|
||||
elif utilization < 0.7:
|
||||
network_state = "MODERATE"
|
||||
else:
|
||||
network_state = "SATURATED"
|
||||
|
||||
return {
|
||||
'busy_time': busy_time,
|
||||
'total_time': total_time,
|
||||
'utilization': utilization,
|
||||
'utilization_percent': round(utilization * 100, 2),
|
||||
'collisions': collisions,
|
||||
'collision_rate': collisions / total_time if total_time > 0 else 0,
|
||||
'network_state': network_state,
|
||||
}
|
||||
|
||||
|
||||
def get_network_state(utilization: float) -> str:
|
||||
"""
|
||||
Get network state based on utilization.
|
||||
|
||||
Args:
|
||||
utilization: Channel utilization ratio (0-1)
|
||||
Network state string
|
||||
"""
|
||||
|
||||
Returns:
|
||||
if utilization < 0.3:
|
||||
return "LIGHT_LOAD"
|
||||
elif utilization < 0.7:
|
||||
return "MODERATE"
|
||||
else:
|
||||
return "SATURATED"
|
||||
57
sim/analysis_tools/convergence.py
Normal file
57
sim/analysis_tools/convergence.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""
|
||||
Convergence Analysis Tools.
|
||||
|
||||
Functions for analyzing routing convergence.
|
||||
"""
|
||||
|
||||
from typing import List, Dict, Any
|
||||
|
||||
|
||||
def calculate_convergence_time(
|
||||
nodes: List[Any], threshold: float = 0.0, stable_duration: float = 30.0
|
||||
) -> float:
|
||||
"""
|
||||
Calculate convergence time.
|
||||
|
||||
Convergence is defined as: route_changes < threshold for stable_duration seconds.
|
||||
|
||||
Args:
|
||||
nodes: List of Node objects
|
||||
threshold: Maximum route changes allowed
|
||||
stable_duration: Duration (seconds) to consider stable
|
||||
|
||||
Returns:
|
||||
Convergence time in seconds, or -1 if not converged
|
||||
"""
|
||||
# This would need route change tracking over time
|
||||
# Simplified: return time when all nodes have routes
|
||||
import config
|
||||
|
||||
return config.HELLO_PERIOD * 3
|
||||
|
||||
|
||||
def analyze_route_stability(nodes: List[Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Analyze route stability.
|
||||
|
||||
Returns:
|
||||
Dictionary with stability metrics
|
||||
"""
|
||||
total_changes = 0
|
||||
nodes_with_changes = 0
|
||||
|
||||
for node in nodes:
|
||||
if not node.is_sink:
|
||||
# Get route change count from stats
|
||||
stats = node.get_stats()
|
||||
changes = stats.get("stats", {}).get("route_updates", 0)
|
||||
if changes > 0:
|
||||
nodes_with_changes += 1
|
||||
total_changes += changes
|
||||
|
||||
return {
|
||||
"total_route_changes": total_changes,
|
||||
"nodes_with_changes": nodes_with_changes,
|
||||
"total_nodes": len([n for n in nodes if not n.is_sink]),
|
||||
"stable": total_changes == 0,
|
||||
}
|
||||
62
sim/analysis_tools/reliability_analysis.py
Normal file
62
sim/analysis_tools/reliability_analysis.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""
|
||||
Reliability Analysis Tools.
|
||||
|
||||
Functions for analyzing packet delivery reliability.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any
|
||||
|
||||
|
||||
def analyze_loss_breakdown(loss_data: Dict[str, int]) -> Dict[str, Any]:
|
||||
"""
|
||||
Analyze packet loss breakdown.
|
||||
|
||||
Args:
|
||||
loss_data: Dictionary with loss counts by type
|
||||
|
||||
Returns:
|
||||
Dictionary with loss analysis
|
||||
"""
|
||||
total_loss = sum(loss_data.values())
|
||||
|
||||
if total_loss == 0:
|
||||
return {
|
||||
"total_loss": 0,
|
||||
"rates": {},
|
||||
"primary_cause": "none",
|
||||
}
|
||||
|
||||
rates = {k: round(v / total_loss * 100, 2) for k, v in loss_data.items() if v > 0}
|
||||
|
||||
# Find primary cause
|
||||
primary_cause = (
|
||||
max(loss_data.items(), key=lambda x: x[1])[0] if loss_data else "none"
|
||||
)
|
||||
|
||||
return {
|
||||
"total_loss": total_loss,
|
||||
"rates": rates,
|
||||
"primary_cause": primary_cause,
|
||||
}
|
||||
|
||||
|
||||
def calculate_pdr_metrics(total_sent: int, total_received: int) -> Dict[str, Any]:
|
||||
"""
|
||||
Calculate PDR metrics.
|
||||
|
||||
Args:
|
||||
total_sent: Total packets sent
|
||||
total_received: Total packets received
|
||||
|
||||
Returns:
|
||||
Dictionary with PDR analysis
|
||||
"""
|
||||
pdr = total_received / total_sent if total_sent > 0 else 0
|
||||
|
||||
return {
|
||||
"total_sent": total_sent,
|
||||
"total_received": total_received,
|
||||
"pdr": round(pdr * 100, 2),
|
||||
"delivered": total_received,
|
||||
"lost": total_sent - total_received,
|
||||
}
|
||||
93
sim/analysis_tools/topology.py
Normal file
93
sim/analysis_tools/topology.py
Normal file
@@ -0,0 +1,93 @@
|
||||
"""
|
||||
Topology Analysis Tools.
|
||||
|
||||
Functions for analyzing and exporting network topology.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import List, Dict, Any
|
||||
|
||||
|
||||
def export_topology_json(
|
||||
nodes: List[Any], filepath: str = "analysis/topology_export.json"
|
||||
):
|
||||
"""
|
||||
Export topology to JSON file.
|
||||
|
||||
Args:
|
||||
nodes: List of Node objects
|
||||
filepath: Output file path
|
||||
"""
|
||||
topology = {"nodes": []}
|
||||
|
||||
for node in nodes:
|
||||
node_info = {
|
||||
"id": node.node_id,
|
||||
"x": round(node.x, 2),
|
||||
"y": round(node.y, 2),
|
||||
"cost": int(node.routing.cost) if node.routing.cost != float("inf") else -1,
|
||||
"parent": node.routing.parent,
|
||||
"is_sink": node.is_sink,
|
||||
}
|
||||
topology["nodes"].append(node_info)
|
||||
|
||||
# Ensure directory exists
|
||||
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
||||
|
||||
with open(filepath, "w") as f:
|
||||
json.dump(topology, f, indent=2)
|
||||
|
||||
return topology
|
||||
|
||||
|
||||
def analyze_parent_tree(nodes: List[Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Analyze the parent tree structure.
|
||||
|
||||
Returns:
|
||||
Dictionary with tree analysis
|
||||
"""
|
||||
# Build parent map
|
||||
parent_map = {}
|
||||
for node in nodes:
|
||||
if node.routing.parent is not None:
|
||||
parent_map[node.node_id] = node.routing.parent
|
||||
|
||||
# Count children per node
|
||||
children_count = {}
|
||||
for node_id, parent_id in parent_map.items():
|
||||
if parent_id not in children_count:
|
||||
children_count[parent_id] = 0
|
||||
children_count[parent_id] += 1
|
||||
|
||||
# Find root (sink)
|
||||
sink = next((n for n in nodes if n.is_sink), None)
|
||||
|
||||
return {
|
||||
"parent_map": parent_map,
|
||||
"children_count": children_count,
|
||||
"sink_id": sink.node_id if sink else None,
|
||||
"total_links": len(parent_map),
|
||||
}
|
||||
|
||||
|
||||
def find_unreachable_nodes(nodes: List[Any]) -> List[int]:
|
||||
"""Find nodes without a valid route to sink."""
|
||||
unreachable = []
|
||||
for node in nodes:
|
||||
if not node.is_sink:
|
||||
if node.routing.parent is None or node.routing.cost == float("inf"):
|
||||
unreachable.append(node.node_id)
|
||||
return unreachable
|
||||
|
||||
|
||||
def calculate_hop_distribution(nodes: List[Any]) -> Dict[int, int]:
|
||||
"""Calculate hop count distribution."""
|
||||
hop_dist = {}
|
||||
for node in nodes:
|
||||
if not node.is_sink:
|
||||
cost = int(node.routing.cost) if node.routing.cost != float("inf") else -1
|
||||
if cost >= 0:
|
||||
hop_dist[cost] = hop_dist.get(cost, 0) + 1
|
||||
return hop_dist
|
||||
Reference in New Issue
Block a user