Repository navigation
Expand file tree
/
Copy pathmain.py
More file actions
120 lines (95 loc) · 4.05 KB
/
Copy pathmain.py
File metadata and controls
120 lines (95 loc) · 4.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import json
import time
import math
import argparse
import logging
import sys
import paho.mqtt.client as mqtt
from geopy.distance import geodesic
logger = logging.getLogger(__name__)
def load_geojson(file_path):
try:
with open(file_path, 'r') as f:
return json.load(f)
except Exception as e:
logger.error(f"Failed to load {file_path}: {e}")
sys.exit(1)
def get_continuous_path(segments_data):
path = []
for feature in segments_data['features']:
# GeoJSON is [lon, lat]
path.extend(feature['geometry']['coordinates'])
return path
def run_simulation(args):
client = mqtt.Client()
try:
client.connect(args.host, args.port, 60)
client.loop_start()
logger.info(f"Connected to MQTT Broker at {args.host}:{args.port}")
except Exception as e:
logger.error(f"MQTT Connection failed: {e}")
sys.exit(1)
segments = load_geojson(args.segments)
stations = load_geojson(args.stations)
path = get_continuous_path(segments)
stop_coords = [(f['geometry']['coordinates'][1], f['geometry']['coordinates'][0], f['properties']['tags'].get('name', 'Unknown'))
for f in stations['features']]
speed_mps = (args.speed * 1000) / 3600
topic = f"telemetry/bus/{args.bus_id}"
logger.info(f"Starting simulation for {args.bus_id}. Route has {len(path)} nodes.")
curr_idx = 0
while curr_idx < len(path) - 1:
start = path[curr_idx]
end = path[curr_idx + 1]
segment_dist = geodesic((start[1], start[0]), (end[1], end[0])).meters
if segment_dist < 0.5:
curr_idx += 1
continue
steps = math.ceil(segment_dist / (speed_mps * args.interval))
for step in range(steps):
t = step / steps
lat = start[1] + (end[1] - start[1]) * t
lon = start[0] + (end[0] - start[0]) * t
# Check for Station Proximity
for s_lat, s_lon, s_name in stop_coords[:]:
if geodesic((lat, lon), (s_lat, s_lon)).meters < args.proximity:
logger.info(f"DWELLING at station: {s_name} for {args.dwell}s")
time.sleep(args.dwell)
stop_coords.remove((s_lat, s_lon, s_name))
break
# Construct Payload
payload = {
"id": args.bus_id,
"lat": round(lat, 6),
"lon": round(lon, 6),
"speed": args.speed,
"timestamp": time.time()
}
client.publish(topic, json.dumps(payload))
logger.info(f"PUB to {topic}: {lat:.6f}, {lon:.6f}")
time.sleep(args.interval)
curr_idx += 1
logger.info("Simulation path completed. Disconnecting.")
client.loop_stop()
client.disconnect()
if __name__ == "__main__":
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
datefmt='%d %b %H:%M:%S'
)
parser = argparse.ArgumentParser(description="Roataway Vehicle Simulator")
parser.add_argument("--segments", required=True, help="Path to segments GeoJSON")
parser.add_argument("--stations", required=True, help="Path to stations GeoJSON")
parser.add_argument("--host", default="localhost", help="MQTT Broker host")
parser.add_argument("--port", type=int, default=1883, help="MQTT Broker port")
parser.add_argument("--bus-id", default="B68", help="Vehicle Identifier")
parser.add_argument("--speed", type=float, default=40.0, help="Speed in km/h")
parser.add_argument("--interval", type=float, default=2.0, help="Update interval in seconds")
parser.add_argument("--dwell", type=float, default=10.0, help="Station dwell time in seconds")
parser.add_argument("--proximity", type=float, default=25.0, help="Station detection radius in meters")
args = parser.parse_args()
try:
run_simulation(args)
except KeyboardInterrupt:
logger.warning("Simulation stopped by user.")