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.
Files changed (38) hide show
  1. multimodalsim_viewer/__init__.py +0 -0
  2. multimodalsim_viewer/server/__init__.py +0 -0
  3. multimodalsim_viewer/server/http_routes.py +125 -0
  4. multimodalsim_viewer/server/log_manager.py +15 -0
  5. multimodalsim_viewer/server/scripts.py +72 -0
  6. multimodalsim_viewer/server/server.py +210 -0
  7. multimodalsim_viewer/server/server_utils.py +129 -0
  8. multimodalsim_viewer/server/simulation.py +154 -0
  9. multimodalsim_viewer/server/simulation_manager.py +607 -0
  10. multimodalsim_viewer/server/simulation_visualization_data_collector.py +756 -0
  11. multimodalsim_viewer/server/simulation_visualization_data_model.py +1693 -0
  12. multimodalsim_viewer/ui/__init__.py +0 -0
  13. multimodalsim_viewer/ui/cli.py +45 -0
  14. multimodalsim_viewer/ui/server.py +44 -0
  15. multimodalsim_viewer/ui/static/bitmap-fonts/custom-sans-serif.png +0 -0
  16. multimodalsim_viewer/ui/static/bitmap-fonts/custom-sans-serif.xml +1 -0
  17. multimodalsim_viewer/ui/static/chunk-MTC2LSCT.js +1 -0
  18. multimodalsim_viewer/ui/static/chunk-U5CGW4P4.js +7 -0
  19. multimodalsim_viewer/ui/static/favicon.ico +0 -0
  20. multimodalsim_viewer/ui/static/images/control-bar.png +0 -0
  21. multimodalsim_viewer/ui/static/images/sample-bus.png +0 -0
  22. multimodalsim_viewer/ui/static/images/sample-stop.png +0 -0
  23. multimodalsim_viewer/ui/static/images/sample-wait.png +0 -0
  24. multimodalsim_viewer/ui/static/images/simulation-control-bar.png +0 -0
  25. multimodalsim_viewer/ui/static/images/zoom-out-passenger.png +0 -0
  26. multimodalsim_viewer/ui/static/images/zoom-out-vehicle.png +0 -0
  27. multimodalsim_viewer/ui/static/index.html +15 -0
  28. multimodalsim_viewer/ui/static/main-X7OVCS3N.js +3648 -0
  29. multimodalsim_viewer/ui/static/media/layers-2x-TBM42ERR.png +0 -0
  30. multimodalsim_viewer/ui/static/media/layers-55W3Q4RM.png +0 -0
  31. multimodalsim_viewer/ui/static/media/marker-icon-2V3QKKVC.png +0 -0
  32. multimodalsim_viewer/ui/static/polyfills-FFHMD2TL.js +2 -0
  33. multimodalsim_viewer/ui/static/styles-KU7LTPET.css +1 -0
  34. multimodalsim_viewer-0.0.1.dist-info/METADATA +21 -0
  35. multimodalsim_viewer-0.0.1.dist-info/RECORD +38 -0
  36. multimodalsim_viewer-0.0.1.dist-info/WHEEL +5 -0
  37. multimodalsim_viewer-0.0.1.dist-info/entry_points.txt +8 -0
  38. 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()