multimodalsim-viewer 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- multimodalsim_viewer/__init__.py +0 -0
- multimodalsim_viewer/server/__init__.py +0 -0
- multimodalsim_viewer/server/http_routes.py +125 -0
- multimodalsim_viewer/server/log_manager.py +15 -0
- multimodalsim_viewer/server/scripts.py +72 -0
- multimodalsim_viewer/server/server.py +210 -0
- multimodalsim_viewer/server/server_utils.py +129 -0
- multimodalsim_viewer/server/simulation.py +154 -0
- multimodalsim_viewer/server/simulation_manager.py +607 -0
- multimodalsim_viewer/server/simulation_visualization_data_collector.py +756 -0
- multimodalsim_viewer/server/simulation_visualization_data_model.py +1693 -0
- multimodalsim_viewer/ui/__init__.py +0 -0
- multimodalsim_viewer/ui/cli.py +45 -0
- multimodalsim_viewer/ui/server.py +44 -0
- multimodalsim_viewer/ui/static/bitmap-fonts/custom-sans-serif.png +0 -0
- multimodalsim_viewer/ui/static/bitmap-fonts/custom-sans-serif.xml +1 -0
- multimodalsim_viewer/ui/static/chunk-MTC2LSCT.js +1 -0
- multimodalsim_viewer/ui/static/chunk-U5CGW4P4.js +7 -0
- multimodalsim_viewer/ui/static/favicon.ico +0 -0
- multimodalsim_viewer/ui/static/images/control-bar.png +0 -0
- multimodalsim_viewer/ui/static/images/sample-bus.png +0 -0
- multimodalsim_viewer/ui/static/images/sample-stop.png +0 -0
- multimodalsim_viewer/ui/static/images/sample-wait.png +0 -0
- multimodalsim_viewer/ui/static/images/simulation-control-bar.png +0 -0
- multimodalsim_viewer/ui/static/images/zoom-out-passenger.png +0 -0
- multimodalsim_viewer/ui/static/images/zoom-out-vehicle.png +0 -0
- multimodalsim_viewer/ui/static/index.html +15 -0
- multimodalsim_viewer/ui/static/main-X7OVCS3N.js +3648 -0
- multimodalsim_viewer/ui/static/media/layers-2x-TBM42ERR.png +0 -0
- multimodalsim_viewer/ui/static/media/layers-55W3Q4RM.png +0 -0
- multimodalsim_viewer/ui/static/media/marker-icon-2V3QKKVC.png +0 -0
- multimodalsim_viewer/ui/static/polyfills-FFHMD2TL.js +2 -0
- multimodalsim_viewer/ui/static/styles-KU7LTPET.css +1 -0
- multimodalsim_viewer-0.0.1.dist-info/METADATA +21 -0
- multimodalsim_viewer-0.0.1.dist-info/RECORD +38 -0
- multimodalsim_viewer-0.0.1.dist-info/WHEEL +5 -0
- multimodalsim_viewer-0.0.1.dist-info/entry_points.txt +8 -0
- multimodalsim_viewer-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,756 @@
|
|
1
|
+
import threading
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from multimodalsim.observer.data_collector import DataCollector
|
5
|
+
from multimodalsim.simulator.environment import Environment
|
6
|
+
from multimodalsim.simulator.event import Event, RecurrentTimeSyncEvent
|
7
|
+
from multimodalsim.simulator.optimization_event import (
|
8
|
+
EnvironmentIdle,
|
9
|
+
EnvironmentUpdate,
|
10
|
+
Hold,
|
11
|
+
Optimize,
|
12
|
+
)
|
13
|
+
from multimodalsim.simulator.passenger_event import (
|
14
|
+
PassengerAlighting,
|
15
|
+
PassengerAssignment,
|
16
|
+
PassengerReady,
|
17
|
+
PassengerRelease,
|
18
|
+
PassengerToBoard,
|
19
|
+
)
|
20
|
+
from multimodalsim.simulator.simulation import Simulation
|
21
|
+
from multimodalsim.simulator.vehicle import Vehicle
|
22
|
+
from multimodalsim.simulator.vehicle_event import (
|
23
|
+
VehicleAlighted,
|
24
|
+
VehicleArrival,
|
25
|
+
VehicleBoarded,
|
26
|
+
VehicleBoarding,
|
27
|
+
VehicleComplete,
|
28
|
+
VehicleDeparture,
|
29
|
+
VehicleNotification,
|
30
|
+
VehicleReady,
|
31
|
+
VehicleUpdatePositionEvent,
|
32
|
+
VehicleWaiting,
|
33
|
+
)
|
34
|
+
from multimodalsim.statistics.data_analyzer import DataAnalyzer
|
35
|
+
from multimodalsim_viewer.server.log_manager import register_log
|
36
|
+
from multimodalsim_viewer.server.server_utils import (
|
37
|
+
HOST,
|
38
|
+
PORT,
|
39
|
+
STATE_SAVE_STEP,
|
40
|
+
SimulationStatus,
|
41
|
+
build_simulation_id,
|
42
|
+
)
|
43
|
+
from multimodalsim_viewer.server.simulation_visualization_data_model import (
|
44
|
+
PassengerLegsUpdate,
|
45
|
+
PassengerStatusUpdate,
|
46
|
+
SimulationInformation,
|
47
|
+
SimulationVisualizationDataManager,
|
48
|
+
StatisticUpdate,
|
49
|
+
Update,
|
50
|
+
UpdateType,
|
51
|
+
VehicleStatusUpdate,
|
52
|
+
VehicleStopsUpdate,
|
53
|
+
VisualizedEnvironment,
|
54
|
+
VisualizedPassenger,
|
55
|
+
VisualizedStop,
|
56
|
+
VisualizedVehicle,
|
57
|
+
)
|
58
|
+
from socketio import Client
|
59
|
+
|
60
|
+
|
61
|
+
# MARK: Data Collector
|
62
|
+
class SimulationVisualizationDataCollector(DataCollector):
|
63
|
+
simulation_id: str
|
64
|
+
update_counter: int
|
65
|
+
visualized_environment: VisualizedEnvironment
|
66
|
+
simulation_information: SimulationInformation
|
67
|
+
current_save_file_path: str
|
68
|
+
|
69
|
+
max_duration: float | None
|
70
|
+
"Maximum duration of the simulation in in-simulation time (seconds). The simulation will stop if it exceeds this duration."
|
71
|
+
|
72
|
+
# Special events
|
73
|
+
last_queued_event_time: float
|
74
|
+
passenger_assignment_event_queue: list[PassengerAssignment]
|
75
|
+
vehicle_notification_event_queue: list[VehicleNotification]
|
76
|
+
|
77
|
+
# Statistics
|
78
|
+
data_analyzer: DataAnalyzer
|
79
|
+
statistics_delta_time: int
|
80
|
+
last_statistics_update_time: int
|
81
|
+
|
82
|
+
# Communication
|
83
|
+
sio: Client | None = None
|
84
|
+
stop_event: threading.Event | None = None
|
85
|
+
connection_thread: threading.Thread | None = None
|
86
|
+
_simulation: Simulation | None = None
|
87
|
+
status: SimulationStatus | None = None
|
88
|
+
|
89
|
+
# Polylines
|
90
|
+
saved_polylines_coordinates_pairs: set[str] = set()
|
91
|
+
|
92
|
+
# Estimated end time
|
93
|
+
last_estimated_end_time: float | None = None
|
94
|
+
|
95
|
+
def __init__(
|
96
|
+
self,
|
97
|
+
data_analyzer: DataAnalyzer,
|
98
|
+
statistics_delta_time: int = 10,
|
99
|
+
name: str = "simulation",
|
100
|
+
input_data_description: str = "unknown",
|
101
|
+
simulation_id: str | None = None,
|
102
|
+
max_duration: float | None = None,
|
103
|
+
offline: bool = False,
|
104
|
+
stop_event: threading.Event | None = None,
|
105
|
+
) -> None:
|
106
|
+
if simulation_id is None:
|
107
|
+
simulation_id, _ = build_simulation_id(name)
|
108
|
+
|
109
|
+
self.simulation_id = simulation_id
|
110
|
+
self.update_counter = 0
|
111
|
+
self.visualized_environment = VisualizedEnvironment()
|
112
|
+
|
113
|
+
self.simulation_information = SimulationInformation(
|
114
|
+
simulation_id, input_data_description, None, None, None, None
|
115
|
+
)
|
116
|
+
|
117
|
+
self.current_save_file_path = None
|
118
|
+
|
119
|
+
self.max_duration = max_duration
|
120
|
+
|
121
|
+
self.passenger_assignment_event_queue = []
|
122
|
+
self.vehicle_notification_event_queue = []
|
123
|
+
self.last_queued_event_time = 0
|
124
|
+
|
125
|
+
self.data_analyzer = data_analyzer
|
126
|
+
self.statistics_delta_time = statistics_delta_time
|
127
|
+
self.last_statistics_update_time = None
|
128
|
+
|
129
|
+
self.stop_event = stop_event
|
130
|
+
|
131
|
+
if not offline:
|
132
|
+
self.initialize_communication()
|
133
|
+
|
134
|
+
@property
|
135
|
+
def isConnected(self) -> bool:
|
136
|
+
return self.sio is not None and self.sio.connected
|
137
|
+
|
138
|
+
# MARK: +- Communication
|
139
|
+
def initialize_communication(self) -> None:
|
140
|
+
sio = Client(reconnection_attempts=1)
|
141
|
+
|
142
|
+
self.sio = sio
|
143
|
+
self.status = SimulationStatus.RUNNING
|
144
|
+
|
145
|
+
@sio.on("pause-simulation")
|
146
|
+
def pauseSimulator():
|
147
|
+
if self._simulation is not None:
|
148
|
+
self._simulation.pause()
|
149
|
+
self.status = SimulationStatus.PAUSED
|
150
|
+
if self.isConnected:
|
151
|
+
self.sio.emit("simulation-pause", self.simulation_id)
|
152
|
+
|
153
|
+
@sio.on("resume-simulation")
|
154
|
+
def resumeSimulator():
|
155
|
+
if self._simulation is not None:
|
156
|
+
self._simulation.resume()
|
157
|
+
self.status = SimulationStatus.RUNNING
|
158
|
+
if self.isConnected:
|
159
|
+
self.sio.emit("simulation-resume", self.simulation_id)
|
160
|
+
|
161
|
+
@sio.on("stop-simulation")
|
162
|
+
def stopSimulator():
|
163
|
+
if self._simulation is not None:
|
164
|
+
self._simulation.stop()
|
165
|
+
self.status = SimulationStatus.STOPPING
|
166
|
+
|
167
|
+
@sio.on("connect")
|
168
|
+
def on_connect():
|
169
|
+
sio.emit(
|
170
|
+
"simulation-identification",
|
171
|
+
(
|
172
|
+
self.simulation_id,
|
173
|
+
self.simulation_information.data,
|
174
|
+
self.simulation_information.simulation_start_time,
|
175
|
+
self.visualized_environment.timestamp,
|
176
|
+
self.visualized_environment.estimated_end_time,
|
177
|
+
self.max_duration,
|
178
|
+
self.status.value,
|
179
|
+
),
|
180
|
+
)
|
181
|
+
|
182
|
+
@sio.on("edit-simulation-configuration")
|
183
|
+
def on_edit_simulation_configuration(max_duration: float | None):
|
184
|
+
self.max_duration = max_duration
|
185
|
+
|
186
|
+
if self.last_estimated_end_time is None:
|
187
|
+
return
|
188
|
+
|
189
|
+
# Notify the server if the estimated end time has changed
|
190
|
+
new_estimated_end_time = min(
|
191
|
+
self.last_estimated_end_time,
|
192
|
+
(
|
193
|
+
self.simulation_information.simulation_start_time
|
194
|
+
+ self.max_duration
|
195
|
+
if self.max_duration is not None
|
196
|
+
else self.last_estimated_end_time
|
197
|
+
),
|
198
|
+
)
|
199
|
+
|
200
|
+
if new_estimated_end_time != self.visualized_environment.estimated_end_time:
|
201
|
+
self.sio.emit(
|
202
|
+
"simulation-update-estimated-end-time",
|
203
|
+
(self.simulation_id, new_estimated_end_time),
|
204
|
+
)
|
205
|
+
self.visualized_environment.estimated_end_time = new_estimated_end_time
|
206
|
+
|
207
|
+
if self.stop_event is None:
|
208
|
+
self.stop_event = threading.Event()
|
209
|
+
|
210
|
+
self.connection_thread = threading.Thread(target=self.handle_connection)
|
211
|
+
self.connection_thread.start()
|
212
|
+
|
213
|
+
def handle_connection(self) -> None:
|
214
|
+
while not self.stop_event.is_set():
|
215
|
+
|
216
|
+
if not self.sio.connected:
|
217
|
+
try:
|
218
|
+
print("Trying to reconnect")
|
219
|
+
self.sio.connect(
|
220
|
+
f"http://{HOST}:{PORT}", auth={"type": "simulation"}
|
221
|
+
)
|
222
|
+
print("Connected")
|
223
|
+
except Exception as e:
|
224
|
+
print(f"Failed to connect to server: {e}")
|
225
|
+
print("Continuing in offline mode")
|
226
|
+
|
227
|
+
self.sio.sleep(5) # Check every 5 seconds
|
228
|
+
|
229
|
+
self.sio.disconnect()
|
230
|
+
self.sio.wait()
|
231
|
+
|
232
|
+
# MARK: +- Collect
|
233
|
+
def collect(
|
234
|
+
self,
|
235
|
+
env: Environment,
|
236
|
+
current_event: Optional[Event] = None,
|
237
|
+
event_index: Optional[int] = None,
|
238
|
+
event_priority: Optional[int] = None,
|
239
|
+
) -> None:
|
240
|
+
env.simulation_config.max_time = (
|
241
|
+
(
|
242
|
+
(
|
243
|
+
self.simulation_information.simulation_start_time
|
244
|
+
if self.simulation_information.simulation_start_time is not None
|
245
|
+
else env.current_time
|
246
|
+
)
|
247
|
+
+ self.max_duration
|
248
|
+
)
|
249
|
+
if self.max_duration is not None
|
250
|
+
else env.simulation_config.max_time
|
251
|
+
)
|
252
|
+
|
253
|
+
if current_event is None:
|
254
|
+
return
|
255
|
+
|
256
|
+
message = self.process_event(current_event, env)
|
257
|
+
register_log(self.simulation_id, message)
|
258
|
+
|
259
|
+
if self.isConnected:
|
260
|
+
self.sio.emit("log", (self.simulation_id, message))
|
261
|
+
|
262
|
+
if (
|
263
|
+
self.last_statistics_update_time == None
|
264
|
+
or current_event.time
|
265
|
+
>= self.last_statistics_update_time + self.statistics_delta_time
|
266
|
+
):
|
267
|
+
self.last_statistics_update_time = current_event.time
|
268
|
+
self.add_update(
|
269
|
+
Update(
|
270
|
+
UpdateType.UPDATE_STATISTIC,
|
271
|
+
StatisticUpdate(self.data_analyzer.get_statistics()),
|
272
|
+
current_event.time,
|
273
|
+
),
|
274
|
+
env,
|
275
|
+
)
|
276
|
+
|
277
|
+
# MARK: +- Add Update
|
278
|
+
def add_update(self, update: Update, environment: Environment) -> None:
|
279
|
+
update.order = self.update_counter
|
280
|
+
self.visualized_environment.order = self.update_counter
|
281
|
+
|
282
|
+
if self.update_counter == 0:
|
283
|
+
# Add the simulation start time to the simulation information
|
284
|
+
self.simulation_information.simulation_start_time = update.timestamp
|
285
|
+
|
286
|
+
# Save the simulation information
|
287
|
+
SimulationVisualizationDataManager.set_simulation_information(
|
288
|
+
self.simulation_id, self.simulation_information
|
289
|
+
)
|
290
|
+
|
291
|
+
# Notify the server that the simulation has started and send the simulation start time
|
292
|
+
if self.isConnected:
|
293
|
+
self.sio.emit(
|
294
|
+
"simulation-start", (self.simulation_id, update.timestamp)
|
295
|
+
)
|
296
|
+
|
297
|
+
if self.visualized_environment.timestamp != update.timestamp:
|
298
|
+
# Notify the server that the simulation time has been updated
|
299
|
+
if self.isConnected:
|
300
|
+
self.sio.emit(
|
301
|
+
"simulation-update-time",
|
302
|
+
(
|
303
|
+
self.simulation_id,
|
304
|
+
update.timestamp,
|
305
|
+
),
|
306
|
+
)
|
307
|
+
self.visualized_environment.timestamp = update.timestamp
|
308
|
+
|
309
|
+
# Remember the last estimated end time in case of max_duration updates
|
310
|
+
self.last_estimated_end_time = environment.estimated_end_time
|
311
|
+
estimated_end_time = min(
|
312
|
+
environment.estimated_end_time,
|
313
|
+
(
|
314
|
+
self.simulation_information.simulation_start_time + self.max_duration
|
315
|
+
if self.max_duration is not None
|
316
|
+
else environment.estimated_end_time
|
317
|
+
),
|
318
|
+
)
|
319
|
+
if estimated_end_time != self.visualized_environment.estimated_end_time:
|
320
|
+
# Notify the server that the simulation estimated end time has been updated
|
321
|
+
if self.isConnected:
|
322
|
+
self.sio.emit(
|
323
|
+
"simulation-update-estimated-end-time",
|
324
|
+
(self.simulation_id, estimated_end_time),
|
325
|
+
)
|
326
|
+
self.visualized_environment.estimated_end_time = estimated_end_time
|
327
|
+
|
328
|
+
# Save the state of the simulation every SAVE_STATE_STEP events before applying the update
|
329
|
+
if self.update_counter % STATE_SAVE_STEP == 0:
|
330
|
+
self.current_save_file_path = SimulationVisualizationDataManager.save_state(
|
331
|
+
self.simulation_id, self.visualized_environment
|
332
|
+
)
|
333
|
+
|
334
|
+
if update.type == UpdateType.CREATE_PASSENGER:
|
335
|
+
self.visualized_environment.add_passenger(update.data)
|
336
|
+
elif update.type == UpdateType.CREATE_VEHICLE:
|
337
|
+
self.visualized_environment.add_vehicle(update.data)
|
338
|
+
data: VisualizedVehicle = update.data
|
339
|
+
if data.polylines is not None:
|
340
|
+
self.update_polylines_if_needed(data)
|
341
|
+
elif update.type == UpdateType.UPDATE_PASSENGER_STATUS:
|
342
|
+
passenger = self.visualized_environment.get_passenger(
|
343
|
+
update.data.passenger_id
|
344
|
+
)
|
345
|
+
passenger.status = update.data.status
|
346
|
+
elif update.type == UpdateType.UPDATE_PASSENGER_LEGS:
|
347
|
+
passenger = self.visualized_environment.get_passenger(
|
348
|
+
update.data.passenger_id
|
349
|
+
)
|
350
|
+
legs_update: PassengerLegsUpdate = update.data
|
351
|
+
passenger.previous_legs = legs_update.previous_legs
|
352
|
+
passenger.next_legs = legs_update.next_legs
|
353
|
+
passenger.current_leg = legs_update.current_leg
|
354
|
+
elif update.type == UpdateType.UPDATE_VEHICLE_STATUS:
|
355
|
+
vehicle = self.visualized_environment.get_vehicle(update.data.vehicle_id)
|
356
|
+
vehicle.status = update.data.status
|
357
|
+
elif update.type == UpdateType.UPDATE_VEHICLE_STOPS:
|
358
|
+
vehicle = self.visualized_environment.get_vehicle(update.data.vehicle_id)
|
359
|
+
stops_update: VehicleStopsUpdate = update.data
|
360
|
+
vehicle.previous_stops = stops_update.previous_stops
|
361
|
+
vehicle.next_stops = stops_update.next_stops
|
362
|
+
vehicle.current_stop = stops_update.current_stop
|
363
|
+
if vehicle.polylines is not None:
|
364
|
+
self.update_polylines_if_needed(vehicle)
|
365
|
+
elif update.type == UpdateType.UPDATE_STATISTIC:
|
366
|
+
statistic_update: StatisticUpdate = update.data
|
367
|
+
self.visualized_environment.statistic = statistic_update.statistic
|
368
|
+
|
369
|
+
SimulationVisualizationDataManager.save_update(
|
370
|
+
self.current_save_file_path, update
|
371
|
+
)
|
372
|
+
|
373
|
+
self.update_counter += 1
|
374
|
+
|
375
|
+
# MARK: +- Polylines
|
376
|
+
def update_polylines_if_needed(self, vehicle: VisualizedVehicle) -> None:
|
377
|
+
polylines = vehicle.polylines
|
378
|
+
stops = vehicle.all_stops
|
379
|
+
|
380
|
+
# A polyline needs to have at least 2 points
|
381
|
+
if len(stops) < 2:
|
382
|
+
return
|
383
|
+
|
384
|
+
# Notify if their are not enough polylines
|
385
|
+
if len(polylines) < len(stops) - 1:
|
386
|
+
raise ValueError(
|
387
|
+
f"Vehicle {vehicle.vehicle_id} has not enough polylines for its stops"
|
388
|
+
)
|
389
|
+
|
390
|
+
stops_pairs: list[
|
391
|
+
tuple[tuple[VisualizedStop, VisualizedStop], tuple[str, list[float]]]
|
392
|
+
] = zip(
|
393
|
+
[(stops[i], stops[i + 1]) for i in range(len(stops) - 1)],
|
394
|
+
polylines.values(),
|
395
|
+
strict=False, # There may be more polylines than stops
|
396
|
+
)
|
397
|
+
|
398
|
+
polylines_to_save: dict[str, tuple[str, list[float]]] = {}
|
399
|
+
|
400
|
+
for stop_pair, polyline in stops_pairs:
|
401
|
+
first_stop, second_stop = stop_pair
|
402
|
+
|
403
|
+
if (
|
404
|
+
first_stop.latitude is None
|
405
|
+
or first_stop.longitude is None
|
406
|
+
or second_stop.latitude is None
|
407
|
+
or second_stop.longitude is None
|
408
|
+
):
|
409
|
+
raise ValueError(
|
410
|
+
f"Vehicle {vehicle.vehicle_id} has stops without coordinates"
|
411
|
+
)
|
412
|
+
|
413
|
+
coordinates_pair = f"{first_stop.latitude},{first_stop.longitude},{second_stop.latitude},{second_stop.longitude}"
|
414
|
+
|
415
|
+
if coordinates_pair not in self.saved_polylines_coordinates_pairs:
|
416
|
+
polylines_to_save[coordinates_pair] = polyline
|
417
|
+
self.saved_polylines_coordinates_pairs.add(coordinates_pair)
|
418
|
+
|
419
|
+
if len(polylines_to_save) > 0:
|
420
|
+
SimulationVisualizationDataManager.set_polylines(
|
421
|
+
self.simulation_id, polylines_to_save
|
422
|
+
)
|
423
|
+
|
424
|
+
if self.isConnected:
|
425
|
+
self.sio.emit(
|
426
|
+
"simulation-update-polylines-version",
|
427
|
+
self.simulation_id,
|
428
|
+
)
|
429
|
+
|
430
|
+
# MARK: +- Flush
|
431
|
+
def flush(self, environment) -> None:
|
432
|
+
for event in self.passenger_assignment_event_queue:
|
433
|
+
self.add_update(
|
434
|
+
Update(
|
435
|
+
UpdateType.UPDATE_PASSENGER_STATUS,
|
436
|
+
PassengerStatusUpdate.from_trip(
|
437
|
+
event.state_machine.owner,
|
438
|
+
),
|
439
|
+
event.time,
|
440
|
+
),
|
441
|
+
environment,
|
442
|
+
)
|
443
|
+
previous_passenger = self.visualized_environment.get_passenger(
|
444
|
+
event.state_machine.owner.id
|
445
|
+
)
|
446
|
+
self.add_update(
|
447
|
+
Update(
|
448
|
+
UpdateType.UPDATE_PASSENGER_LEGS,
|
449
|
+
PassengerLegsUpdate.from_trip_environment_and_previous_passenger(
|
450
|
+
event.state_machine.owner, environment, previous_passenger
|
451
|
+
),
|
452
|
+
event.time,
|
453
|
+
),
|
454
|
+
environment,
|
455
|
+
)
|
456
|
+
|
457
|
+
for event in self.vehicle_notification_event_queue:
|
458
|
+
vehicle = event._VehicleNotification__vehicle
|
459
|
+
route = event._VehicleNotification__route
|
460
|
+
existing_vehicle = self.visualized_environment.get_vehicle(vehicle.id)
|
461
|
+
if vehicle.polylines != existing_vehicle.polylines:
|
462
|
+
existing_vehicle.polylines = vehicle.polylines
|
463
|
+
|
464
|
+
self.add_update(
|
465
|
+
Update(
|
466
|
+
UpdateType.UPDATE_VEHICLE_STOPS,
|
467
|
+
VehicleStopsUpdate.from_vehicle_and_route(vehicle, route),
|
468
|
+
event.time,
|
469
|
+
),
|
470
|
+
environment,
|
471
|
+
)
|
472
|
+
|
473
|
+
self.passenger_assignment_event_queue = []
|
474
|
+
self.vehicle_notification_event_queue = []
|
475
|
+
|
476
|
+
@property
|
477
|
+
def hasToFlush(self) -> bool:
|
478
|
+
return (
|
479
|
+
len(self.passenger_assignment_event_queue) > 0
|
480
|
+
or len(self.vehicle_notification_event_queue) > 0
|
481
|
+
)
|
482
|
+
|
483
|
+
# MARK: +- Process Event
|
484
|
+
def process_event(self, event: Event, environment: Environment) -> str:
|
485
|
+
# In case that a queued event is not linked to EnvironmentIdle
|
486
|
+
if self.hasToFlush and event.time > self.last_queued_event_time:
|
487
|
+
self.flush(environment)
|
488
|
+
|
489
|
+
# Optimize
|
490
|
+
if isinstance(event, Optimize):
|
491
|
+
# Do nothing ?
|
492
|
+
return f"{event.time} TODO Optimize"
|
493
|
+
|
494
|
+
# EnvironmentUpdate
|
495
|
+
elif isinstance(event, EnvironmentUpdate):
|
496
|
+
# Do nothing ?
|
497
|
+
return f"{event.time} TODO EnvironmentUpdate"
|
498
|
+
|
499
|
+
# EnvironmentIdle
|
500
|
+
elif isinstance(event, EnvironmentIdle):
|
501
|
+
self.flush(environment)
|
502
|
+
return f"{event.time} TODO EnvironmentIdle"
|
503
|
+
|
504
|
+
# PassengerRelease
|
505
|
+
elif isinstance(event, PassengerRelease):
|
506
|
+
passenger = VisualizedPassenger.from_trip_and_environment(
|
507
|
+
event.trip, environment
|
508
|
+
)
|
509
|
+
self.add_update(
|
510
|
+
Update(
|
511
|
+
UpdateType.CREATE_PASSENGER,
|
512
|
+
passenger,
|
513
|
+
event.time,
|
514
|
+
),
|
515
|
+
environment,
|
516
|
+
)
|
517
|
+
return f"{event.time} TODO PassengerRelease"
|
518
|
+
|
519
|
+
# PassengerAssignment
|
520
|
+
elif isinstance(event, PassengerAssignment):
|
521
|
+
self.passenger_assignment_event_queue.append(event)
|
522
|
+
self.last_queued_event_time = event.time
|
523
|
+
return f"{event.time} TODO PassengerAssignment"
|
524
|
+
|
525
|
+
# PassengerReady
|
526
|
+
elif isinstance(event, PassengerReady):
|
527
|
+
self.add_update(
|
528
|
+
Update(
|
529
|
+
UpdateType.UPDATE_PASSENGER_STATUS,
|
530
|
+
PassengerStatusUpdate.from_trip(
|
531
|
+
event.state_machine.owner,
|
532
|
+
),
|
533
|
+
event.time,
|
534
|
+
),
|
535
|
+
environment,
|
536
|
+
)
|
537
|
+
return f"{event.time} TODO PassengerReady"
|
538
|
+
|
539
|
+
# PassengerToBoard
|
540
|
+
elif isinstance(event, PassengerToBoard):
|
541
|
+
self.add_update(
|
542
|
+
Update(
|
543
|
+
UpdateType.UPDATE_PASSENGER_STATUS,
|
544
|
+
PassengerStatusUpdate.from_trip(
|
545
|
+
event.state_machine.owner,
|
546
|
+
),
|
547
|
+
event.time,
|
548
|
+
),
|
549
|
+
environment,
|
550
|
+
)
|
551
|
+
previous_passenger = self.visualized_environment.get_passenger(
|
552
|
+
event.state_machine.owner.id
|
553
|
+
)
|
554
|
+
self.add_update(
|
555
|
+
Update(
|
556
|
+
UpdateType.UPDATE_PASSENGER_LEGS,
|
557
|
+
PassengerLegsUpdate.from_trip_environment_and_previous_passenger(
|
558
|
+
event.state_machine.owner, environment, previous_passenger
|
559
|
+
),
|
560
|
+
event.time,
|
561
|
+
),
|
562
|
+
environment,
|
563
|
+
)
|
564
|
+
return f"{event.time} TODO PassengerToBoard"
|
565
|
+
|
566
|
+
# PassengerAlighting
|
567
|
+
elif isinstance(event, PassengerAlighting):
|
568
|
+
self.add_update(
|
569
|
+
Update(
|
570
|
+
UpdateType.UPDATE_PASSENGER_STATUS,
|
571
|
+
PassengerStatusUpdate.from_trip(
|
572
|
+
event.state_machine.owner,
|
573
|
+
),
|
574
|
+
event.time,
|
575
|
+
),
|
576
|
+
environment,
|
577
|
+
)
|
578
|
+
previous_passenger = self.visualized_environment.get_passenger(
|
579
|
+
event.state_machine.owner.id
|
580
|
+
)
|
581
|
+
self.add_update(
|
582
|
+
Update(
|
583
|
+
UpdateType.UPDATE_PASSENGER_LEGS,
|
584
|
+
PassengerLegsUpdate.from_trip_environment_and_previous_passenger(
|
585
|
+
event.state_machine.owner, environment, previous_passenger
|
586
|
+
),
|
587
|
+
event.time,
|
588
|
+
),
|
589
|
+
environment,
|
590
|
+
)
|
591
|
+
return f"{event.time} TODO PassengerAlighting"
|
592
|
+
|
593
|
+
# VehicleWaiting
|
594
|
+
elif isinstance(event, VehicleWaiting):
|
595
|
+
self.add_update(
|
596
|
+
Update(
|
597
|
+
UpdateType.UPDATE_VEHICLE_STATUS,
|
598
|
+
VehicleStatusUpdate.from_vehicle(event.state_machine.owner),
|
599
|
+
event.time,
|
600
|
+
),
|
601
|
+
environment,
|
602
|
+
)
|
603
|
+
return f"{event.time} TODO VehicleWaiting"
|
604
|
+
|
605
|
+
# VehicleBoarding
|
606
|
+
elif isinstance(event, VehicleBoarding):
|
607
|
+
self.add_update(
|
608
|
+
Update(
|
609
|
+
UpdateType.UPDATE_VEHICLE_STATUS,
|
610
|
+
VehicleStatusUpdate.from_vehicle(
|
611
|
+
event.state_machine.owner,
|
612
|
+
),
|
613
|
+
event.time,
|
614
|
+
),
|
615
|
+
environment,
|
616
|
+
)
|
617
|
+
return f"{event.time} TODO VehicleBoarding"
|
618
|
+
|
619
|
+
# VehicleDeparture
|
620
|
+
elif isinstance(event, VehicleDeparture):
|
621
|
+
route = event._VehicleDeparture__route
|
622
|
+
vehicle = event.state_machine.owner
|
623
|
+
|
624
|
+
self.add_update(
|
625
|
+
Update(
|
626
|
+
UpdateType.UPDATE_VEHICLE_STATUS,
|
627
|
+
VehicleStatusUpdate.from_vehicle(
|
628
|
+
event.state_machine.owner,
|
629
|
+
),
|
630
|
+
event.time,
|
631
|
+
),
|
632
|
+
environment,
|
633
|
+
)
|
634
|
+
|
635
|
+
self.add_update(
|
636
|
+
Update(
|
637
|
+
UpdateType.UPDATE_VEHICLE_STOPS,
|
638
|
+
VehicleStopsUpdate.from_vehicle_and_route(vehicle, route),
|
639
|
+
event.time,
|
640
|
+
),
|
641
|
+
environment,
|
642
|
+
)
|
643
|
+
return f"{event.time} TODO VehicleDeparture"
|
644
|
+
|
645
|
+
# VehicleArrival
|
646
|
+
elif isinstance(event, VehicleArrival):
|
647
|
+
route = event._VehicleArrival__route
|
648
|
+
vehicle = event.state_machine.owner
|
649
|
+
|
650
|
+
self.add_update(
|
651
|
+
Update(
|
652
|
+
UpdateType.UPDATE_VEHICLE_STATUS,
|
653
|
+
VehicleStatusUpdate.from_vehicle(
|
654
|
+
event.state_machine.owner,
|
655
|
+
),
|
656
|
+
event.time,
|
657
|
+
),
|
658
|
+
environment,
|
659
|
+
)
|
660
|
+
|
661
|
+
self.add_update(
|
662
|
+
Update(
|
663
|
+
UpdateType.UPDATE_VEHICLE_STOPS,
|
664
|
+
VehicleStopsUpdate.from_vehicle_and_route(vehicle, route),
|
665
|
+
event.time,
|
666
|
+
),
|
667
|
+
environment,
|
668
|
+
)
|
669
|
+
|
670
|
+
return f"{event.time} TODO VehicleArrival"
|
671
|
+
|
672
|
+
# VehicleComplete
|
673
|
+
elif isinstance(event, VehicleComplete):
|
674
|
+
self.add_update(
|
675
|
+
Update(
|
676
|
+
UpdateType.UPDATE_VEHICLE_STATUS,
|
677
|
+
VehicleStatusUpdate.from_vehicle(
|
678
|
+
event.state_machine.owner,
|
679
|
+
),
|
680
|
+
event.time,
|
681
|
+
),
|
682
|
+
environment,
|
683
|
+
)
|
684
|
+
return f"{event.time} TODO VehicleComplete"
|
685
|
+
|
686
|
+
# VehicleReady
|
687
|
+
elif isinstance(event, VehicleReady):
|
688
|
+
vehicle = VisualizedVehicle.from_vehicle_and_route(
|
689
|
+
event.vehicle, event._VehicleReady__route
|
690
|
+
)
|
691
|
+
self.add_update(
|
692
|
+
Update(
|
693
|
+
UpdateType.CREATE_VEHICLE,
|
694
|
+
vehicle,
|
695
|
+
event.time,
|
696
|
+
),
|
697
|
+
environment,
|
698
|
+
)
|
699
|
+
return f"{event.time} TODO VehicleReady"
|
700
|
+
|
701
|
+
# VehicleNotification
|
702
|
+
elif isinstance(event, VehicleNotification):
|
703
|
+
self.vehicle_notification_event_queue.append(event)
|
704
|
+
self.last_queued_event_time = event.time
|
705
|
+
return f"{event.time} TODO VehicleNotification"
|
706
|
+
|
707
|
+
# VehicleBoarded
|
708
|
+
elif isinstance(event, VehicleBoarded):
|
709
|
+
return f"{event.time} TODO VehicleBoarded"
|
710
|
+
|
711
|
+
# VehicleAlighted
|
712
|
+
elif isinstance(event, VehicleAlighted):
|
713
|
+
return f"{event.time} TODO VehicleAlighted"
|
714
|
+
|
715
|
+
# VehicleUpdatePositionEvent
|
716
|
+
elif isinstance(event, VehicleUpdatePositionEvent):
|
717
|
+
# Do nothing ?
|
718
|
+
return f"{event.time} TODO VehicleUpdatePositionEvent"
|
719
|
+
|
720
|
+
# RecurrentTimeSyncEvent
|
721
|
+
elif isinstance(event, RecurrentTimeSyncEvent):
|
722
|
+
# Do nothing ?
|
723
|
+
return f"{event.time} TODO RecurrentTimeSyncEvent"
|
724
|
+
|
725
|
+
# Hold
|
726
|
+
elif isinstance(event, Hold):
|
727
|
+
# Do nothing ?
|
728
|
+
return f"{event.time} TODO Hold"
|
729
|
+
|
730
|
+
else:
|
731
|
+
raise NotImplementedError(f"Event {event} not implemented")
|
732
|
+
|
733
|
+
# MARK: +- Clean Up
|
734
|
+
def clean_up(self, env):
|
735
|
+
self.simulation_information.simulation_end_time = (
|
736
|
+
self.visualized_environment.timestamp
|
737
|
+
)
|
738
|
+
self.simulation_information.last_update_order = (
|
739
|
+
self.visualized_environment.order
|
740
|
+
)
|
741
|
+
|
742
|
+
SimulationVisualizationDataManager.set_simulation_information(
|
743
|
+
self.simulation_id, self.simulation_information
|
744
|
+
)
|
745
|
+
|
746
|
+
if self.stop_event is not None:
|
747
|
+
self.stop_event.set()
|
748
|
+
|
749
|
+
if self.connection_thread is not None:
|
750
|
+
self.connection_thread.join()
|
751
|
+
|
752
|
+
if self.isConnected:
|
753
|
+
self.sio.disconnect()
|
754
|
+
|
755
|
+
if self.sio is not None:
|
756
|
+
self.sio.wait()
|