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,607 @@
1
+ import inspect
2
+ import logging
3
+ import multiprocessing
4
+
5
+ from flask_socketio import emit
6
+ from multimodalsim_viewer.server.server_utils import (
7
+ CLIENT_ROOM,
8
+ RUNNING_SIMULATION_STATUSES,
9
+ SAVE_VERSION,
10
+ SIMULATION_SAVE_FILE_SEPARATOR,
11
+ SimulationStatus,
12
+ build_simulation_id,
13
+ get_session_id,
14
+ log,
15
+ )
16
+ from multimodalsim_viewer.server.simulation import run_simulation
17
+ from multimodalsim_viewer.server.simulation_visualization_data_model import (
18
+ SimulationVisualizationDataManager,
19
+ )
20
+
21
+
22
+ class SimulationHandler:
23
+ simulation_id: str
24
+ name: str
25
+ start_time: float
26
+ data: str
27
+ process: multiprocessing.Process | None
28
+ status: SimulationStatus
29
+ socket_id: str | None
30
+
31
+ simulation_start_time: float | None
32
+ simulation_end_time: float | None
33
+
34
+ simulation_time: float | None
35
+ simulation_estimated_end_time: float | None
36
+
37
+ max_duration: float | None
38
+
39
+ polylines_version: int | None
40
+
41
+ def __init__(
42
+ self,
43
+ simulation_id: str,
44
+ name: str,
45
+ start_time: float,
46
+ data: str,
47
+ status: SimulationStatus,
48
+ max_duration: float | None,
49
+ process: multiprocessing.Process | None,
50
+ ) -> None:
51
+ self.simulation_id = simulation_id
52
+ self.name = name
53
+ self.start_time = start_time
54
+ self.data = data
55
+ self.process = process
56
+ self.status = status
57
+
58
+ self.socket_id = None
59
+
60
+ self.simulation_start_time = None
61
+ self.simulation_end_time = None
62
+ self.simulation_time = None
63
+ self.simulation_estimated_end_time = None
64
+
65
+ self.max_duration = max_duration
66
+
67
+ self.polylines_version = None
68
+
69
+
70
+ class SimulationManager:
71
+ simulations: dict[str, SimulationHandler]
72
+
73
+ def __init__(self):
74
+ self.simulations = {}
75
+
76
+ def start_simulation(
77
+ self, name: str, data: str, response_event: str, max_duration: float | None
78
+ ) -> SimulationHandler:
79
+ simulation_id, start_time = build_simulation_id(name)
80
+
81
+ simulation_process = multiprocessing.Process(
82
+ target=run_simulation, args=(simulation_id, data, max_duration)
83
+ )
84
+
85
+ simulation_handler = SimulationHandler(
86
+ simulation_id,
87
+ name,
88
+ start_time,
89
+ data,
90
+ SimulationStatus.STARTING,
91
+ max_duration,
92
+ simulation_process,
93
+ )
94
+
95
+ self.simulations[simulation_id] = simulation_handler
96
+
97
+ simulation_process.start()
98
+
99
+ self.emit_simulations()
100
+
101
+ log(f'Emitting response event "{response_event}"', "server")
102
+ emit(response_event, simulation_id, to=CLIENT_ROOM)
103
+
104
+ return simulation_handler
105
+
106
+ def on_simulation_start(self, simulation_id, socket_id, simulation_start_time):
107
+ if simulation_id not in self.simulations:
108
+ log(
109
+ f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
110
+ "server",
111
+ logging.ERROR,
112
+ )
113
+ return
114
+
115
+ simulation = self.simulations[simulation_id]
116
+
117
+ simulation.socket_id = socket_id
118
+ simulation.status = SimulationStatus.RUNNING
119
+ simulation.simulation_start_time = simulation_start_time
120
+
121
+ self.emit_simulations()
122
+
123
+ def stop_simulation(self, simulation_id):
124
+ if simulation_id not in self.simulations:
125
+ log(
126
+ f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
127
+ "server",
128
+ logging.ERROR,
129
+ )
130
+ return
131
+
132
+ simulation = self.simulations[simulation_id]
133
+ simulation.status = SimulationStatus.STOPPING
134
+
135
+ emit("stop-simulation", to=simulation.socket_id)
136
+
137
+ def pause_simulation(self, simulation_id):
138
+ if simulation_id not in self.simulations:
139
+ log(
140
+ f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
141
+ "server",
142
+ logging.ERROR,
143
+ )
144
+ return
145
+
146
+ simulation = self.simulations[simulation_id]
147
+
148
+ emit("pause-simulation", to=simulation.socket_id)
149
+
150
+ def on_simulation_pause(self, simulation_id):
151
+ if simulation_id not in self.simulations:
152
+ log(
153
+ f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
154
+ "server",
155
+ logging.ERROR,
156
+ )
157
+ return
158
+
159
+ simulation = self.simulations[simulation_id]
160
+
161
+ simulation.status = SimulationStatus.PAUSED
162
+
163
+ self.emit_simulations()
164
+
165
+ def resume_simulation(self, simulation_id):
166
+ if simulation_id not in self.simulations:
167
+ log(
168
+ f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
169
+ "server",
170
+ logging.ERROR,
171
+ )
172
+ return
173
+
174
+ simulation = self.simulations[simulation_id]
175
+
176
+ emit("resume-simulation", to=simulation.socket_id)
177
+
178
+ def on_simulation_resume(self, simulation_id):
179
+ if simulation_id not in self.simulations:
180
+ log(
181
+ f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
182
+ "server",
183
+ logging.ERROR,
184
+ )
185
+ return
186
+
187
+ simulation = self.simulations[simulation_id]
188
+
189
+ simulation.status = SimulationStatus.RUNNING
190
+
191
+ self.emit_simulations()
192
+
193
+ def edit_simulation_configuration(
194
+ self, simulation_id: str, max_duration: float | None
195
+ ) -> None:
196
+ if simulation_id not in self.simulations:
197
+ log(
198
+ f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
199
+ "server",
200
+ logging.ERROR,
201
+ )
202
+ return
203
+
204
+ simulation = self.simulations[simulation_id]
205
+
206
+ simulation.max_duration = max_duration
207
+
208
+ emit("edit-simulation-configuration", (max_duration,), to=simulation.socket_id)
209
+
210
+ self.emit_simulations()
211
+
212
+ log(
213
+ f"Emitted simulations with new max duration {max_duration} for simulation {simulation_id}",
214
+ "server",
215
+ logging.WARN,
216
+ )
217
+
218
+ def on_simulation_disconnect(self, socket_id):
219
+ matching_simulation_ids = [
220
+ simulation_id
221
+ for simulation_id, simulation in self.simulations.items()
222
+ if simulation.socket_id == socket_id
223
+ ]
224
+
225
+ if len(matching_simulation_ids) != 1:
226
+ # The simulation has already been disconnected properly
227
+ return
228
+
229
+ simulation_id = matching_simulation_ids[0]
230
+
231
+ # Get the simulation information from the save file
232
+ simulation_information = (
233
+ SimulationVisualizationDataManager.get_simulation_information(simulation_id)
234
+ )
235
+
236
+ simulation = self.simulations[simulation_id]
237
+
238
+ if simulation.status in RUNNING_SIMULATION_STATUSES:
239
+ if simulation_information.simulation_end_time is None:
240
+ # The simulation has been lost
241
+ simulation.status = SimulationStatus.LOST
242
+ else:
243
+ # The simulation has been completed
244
+ simulation.status = SimulationStatus.COMPLETED
245
+
246
+ simulation.socket_id = None
247
+
248
+ self.emit_simulations()
249
+
250
+ def on_simulation_update_time(self, simulation_id, timestamp):
251
+ if simulation_id not in self.simulations:
252
+ log(
253
+ f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
254
+ "server",
255
+ logging.ERROR,
256
+ )
257
+ return
258
+
259
+ simulation = self.simulations[simulation_id]
260
+
261
+ simulation.simulation_time = timestamp
262
+
263
+ self.emit_simulations()
264
+
265
+ def on_simulation_update_estimated_end_time(
266
+ self, simulation_id, estimated_end_time
267
+ ):
268
+ if simulation_id not in self.simulations:
269
+ log(
270
+ f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
271
+ "server",
272
+ logging.ERROR,
273
+ )
274
+ return
275
+
276
+ simulation = self.simulations[simulation_id]
277
+
278
+ simulation.simulation_estimated_end_time = estimated_end_time
279
+
280
+ self.emit_simulations()
281
+
282
+ def on_simulation_update_polylines_version(self, simulation_id):
283
+ if simulation_id not in self.simulations:
284
+ log(
285
+ f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
286
+ "server",
287
+ logging.ERROR,
288
+ )
289
+ return
290
+
291
+ simulation = self.simulations[simulation_id]
292
+
293
+ simulation.polylines_version = (
294
+ SimulationVisualizationDataManager.get_polylines_version_with_lock(
295
+ simulation_id
296
+ )
297
+ )
298
+
299
+ self.emit_simulations()
300
+
301
+ def on_simulation_identification(
302
+ self,
303
+ simulation_id,
304
+ data,
305
+ simulation_start_time,
306
+ simulation_time,
307
+ simulation_estimated_end_time,
308
+ max_duration,
309
+ status,
310
+ socket_id,
311
+ ):
312
+
313
+ log(
314
+ f"Identifying simulation {simulation_id}",
315
+ "simulation",
316
+ )
317
+
318
+ start_time, name = simulation_id.split(SIMULATION_SAVE_FILE_SEPARATOR)
319
+
320
+ if simulation_id in self.simulations:
321
+ simulation = self.simulations[simulation_id]
322
+ else:
323
+ start_time, name = simulation_id.split(SIMULATION_SAVE_FILE_SEPARATOR)
324
+
325
+ simulation = SimulationHandler(
326
+ simulation_id,
327
+ name,
328
+ start_time,
329
+ data,
330
+ SimulationStatus(status),
331
+ max_duration,
332
+ None,
333
+ )
334
+
335
+ self.simulations[simulation_id] = simulation
336
+
337
+ simulation.name = name
338
+ simulation.start_time = start_time
339
+ simulation.data = data
340
+ simulation.simulation_start_time = simulation_start_time
341
+ simulation.simulation_time = simulation_time
342
+ simulation.simulation_estimated_end_time = simulation_estimated_end_time
343
+ simulation.max_duration = max_duration
344
+ simulation.status = SimulationStatus(status)
345
+ simulation.socket_id = socket_id
346
+
347
+ simulation.polylines_version = (
348
+ SimulationVisualizationDataManager.get_polylines_version_with_lock(
349
+ simulation_id
350
+ )
351
+ )
352
+
353
+ self.emit_simulations()
354
+
355
+ def emit_simulations(self):
356
+ self.query_simulations()
357
+
358
+ serialized_simulations = []
359
+
360
+ for simulation_id, simulation in self.simulations.items():
361
+ serialized_simulation = {
362
+ "id": simulation_id,
363
+ "name": simulation.name,
364
+ "status": simulation.status.value,
365
+ "startTime": simulation.start_time,
366
+ "data": simulation.data,
367
+ }
368
+
369
+ if simulation.simulation_start_time is not None:
370
+ serialized_simulation["simulationStartTime"] = (
371
+ simulation.simulation_start_time
372
+ )
373
+
374
+ if simulation.simulation_end_time is not None:
375
+ serialized_simulation["simulationEndTime"] = (
376
+ simulation.simulation_end_time
377
+ )
378
+
379
+ if simulation.simulation_time is not None:
380
+ serialized_simulation["simulationTime"] = simulation.simulation_time
381
+
382
+ if simulation.simulation_estimated_end_time is not None:
383
+ serialized_simulation["simulationEstimatedEndTime"] = (
384
+ simulation.simulation_estimated_end_time
385
+ )
386
+
387
+ if simulation.max_duration is not None:
388
+ serialized_simulation["configuration"] = {
389
+ "maxDuration": simulation.max_duration
390
+ }
391
+
392
+ if simulation.polylines_version is not None:
393
+ serialized_simulation["polylinesVersion"] = simulation.polylines_version
394
+
395
+ serialized_simulations.append(serialized_simulation)
396
+
397
+ emit(
398
+ "simulations",
399
+ serialized_simulations,
400
+ to=CLIENT_ROOM,
401
+ )
402
+
403
+ log("Emitting simulations", "server")
404
+
405
+ def emit_missing_simulation_states(
406
+ self,
407
+ simulation_id: str,
408
+ visualization_time: float,
409
+ loaded_state_orders: list[int],
410
+ ) -> None:
411
+ if simulation_id not in self.simulations:
412
+ log(
413
+ f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
414
+ "server",
415
+ logging.ERROR,
416
+ )
417
+ return
418
+
419
+ simulation = self.simulations[simulation_id]
420
+
421
+ try:
422
+ (
423
+ missing_states,
424
+ missing_updates,
425
+ state_orders_to_keep,
426
+ should_request_more_states,
427
+ first_continuous_state_order,
428
+ last_continuous_state_order,
429
+ necessary_state_order,
430
+ ) = SimulationVisualizationDataManager.get_missing_states(
431
+ simulation_id,
432
+ visualization_time,
433
+ loaded_state_orders,
434
+ simulation.status not in RUNNING_SIMULATION_STATUSES,
435
+ )
436
+
437
+ emit(
438
+ "missing-simulation-states",
439
+ (
440
+ missing_states,
441
+ missing_updates,
442
+ state_orders_to_keep,
443
+ should_request_more_states,
444
+ first_continuous_state_order,
445
+ last_continuous_state_order,
446
+ necessary_state_order,
447
+ ),
448
+ to=get_session_id(),
449
+ )
450
+
451
+ except Exception as e:
452
+ log(
453
+ f"Error while emitting missing simulation states for {simulation_id}: {e}",
454
+ "server",
455
+ logging.ERROR,
456
+ )
457
+ log(
458
+ f"Marking simulation {simulation_id} as corrupted",
459
+ "server",
460
+ logging.ERROR,
461
+ )
462
+
463
+ simulation.status = SimulationStatus.CORRUPTED
464
+
465
+ SimulationVisualizationDataManager.mark_simulation_as_corrupted(
466
+ simulation_id
467
+ )
468
+
469
+ self.emit_simulations()
470
+
471
+ def emit_simulation_polylines(self, simulation_id):
472
+ if simulation_id not in self.simulations:
473
+ log(
474
+ f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
475
+ "server",
476
+ logging.ERROR,
477
+ )
478
+ return
479
+
480
+ polylines, version = SimulationVisualizationDataManager.get_polylines(
481
+ simulation_id
482
+ )
483
+
484
+ emit(f"polylines-{simulation_id}", (polylines, version), to=CLIENT_ROOM)
485
+
486
+ def query_simulations(self):
487
+ all_simulation_ids = (
488
+ SimulationVisualizationDataManager.get_all_saved_simulation_ids()
489
+ )
490
+
491
+ for simulation_id, _ in list(self.simulations.items()):
492
+ if simulation_id not in all_simulation_ids and self.simulations[
493
+ simulation_id
494
+ ].status not in [
495
+ SimulationStatus.RUNNING,
496
+ SimulationStatus.PAUSED,
497
+ SimulationStatus.STOPPING,
498
+ SimulationStatus.STARTING,
499
+ SimulationStatus.LOST,
500
+ ]:
501
+ del self.simulations[simulation_id]
502
+
503
+ for simulation_id in all_simulation_ids:
504
+ # Non valid save files might throw an exception
505
+ self.query_simulation(simulation_id)
506
+
507
+ def query_simulation(self, simulation_id) -> None:
508
+ if simulation_id in self.simulations and self.simulations[
509
+ simulation_id
510
+ ].status in [
511
+ SimulationStatus.RUNNING,
512
+ SimulationStatus.PAUSED,
513
+ SimulationStatus.STOPPING,
514
+ SimulationStatus.STARTING,
515
+ SimulationStatus.LOST,
516
+ ]:
517
+ return
518
+
519
+ is_corrupted = SimulationVisualizationDataManager.is_simulation_corrupted(
520
+ simulation_id
521
+ )
522
+
523
+ if not is_corrupted:
524
+ # Non valid save files throw an exception
525
+ try:
526
+ # Get the simulation information from the save file
527
+ simulation_information = (
528
+ SimulationVisualizationDataManager.get_simulation_information(
529
+ simulation_id
530
+ )
531
+ )
532
+
533
+ # Get the version of the polylines
534
+ polylines_version = (
535
+ SimulationVisualizationDataManager.get_polylines_version_with_lock(
536
+ simulation_id
537
+ )
538
+ )
539
+
540
+ # Verify the version of the save file
541
+ version = simulation_information.version
542
+
543
+ status = SimulationStatus.COMPLETED
544
+ if version < SAVE_VERSION:
545
+ status = SimulationStatus.OUTDATED
546
+ elif version > SAVE_VERSION:
547
+ status = SimulationStatus.FUTURE
548
+
549
+ if status == SimulationStatus.OUTDATED:
550
+ log(
551
+ f"Simulation {simulation_id} version is outdated",
552
+ "server",
553
+ logging.DEBUG,
554
+ )
555
+ if status == SimulationStatus.FUTURE:
556
+ log(
557
+ f"Simulation {simulation_id} version is future",
558
+ "server",
559
+ logging.DEBUG,
560
+ )
561
+
562
+ simulation = SimulationHandler(
563
+ simulation_id,
564
+ simulation_information.name,
565
+ simulation_information.start_time,
566
+ simulation_information.data,
567
+ status,
568
+ None,
569
+ None,
570
+ )
571
+
572
+ simulation.simulation_start_time = (
573
+ simulation_information.simulation_start_time
574
+ )
575
+ simulation.simulation_end_time = (
576
+ simulation_information.simulation_end_time
577
+ )
578
+
579
+ simulation.polylines_version = polylines_version
580
+
581
+ if simulation_information.simulation_end_time is None:
582
+ # The simulation is not running but the end time is not set
583
+ raise Exception("Simulation is corrupted")
584
+
585
+ self.simulations[simulation_id] = simulation
586
+
587
+ except:
588
+ is_corrupted = True
589
+
590
+ if is_corrupted:
591
+ log(f"Simulation {simulation_id} is corrupted", "server", logging.DEBUG)
592
+
593
+ simulation = SimulationHandler(
594
+ simulation_id,
595
+ "unknown",
596
+ "unknown",
597
+ "unknown",
598
+ SimulationStatus.CORRUPTED,
599
+ None,
600
+ None,
601
+ )
602
+
603
+ self.simulations[simulation_id] = simulation
604
+
605
+ SimulationVisualizationDataManager.mark_simulation_as_corrupted(
606
+ simulation_id
607
+ )