multimodalsim-viewer 0.0.3__py3-none-any.whl → 0.1.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 (32) hide show
  1. multimodalsim_viewer/common/environments/.env +2 -0
  2. multimodalsim_viewer/common/utils.py +11 -48
  3. multimodalsim_viewer/models/__init__.py +0 -0
  4. multimodalsim_viewer/models/environment.py +70 -0
  5. multimodalsim_viewer/models/leg.py +194 -0
  6. multimodalsim_viewer/models/passenger.py +148 -0
  7. multimodalsim_viewer/models/serializable.py +43 -0
  8. multimodalsim_viewer/models/simulation_information.py +84 -0
  9. multimodalsim_viewer/models/state.py +44 -0
  10. multimodalsim_viewer/models/stop.py +114 -0
  11. multimodalsim_viewer/models/update.py +616 -0
  12. multimodalsim_viewer/models/vehicle.py +151 -0
  13. multimodalsim_viewer/server/{simulation_visualization_data_collector.py → data_collector.py} +185 -198
  14. multimodalsim_viewer/server/data_manager.py +572 -0
  15. multimodalsim_viewer/server/http_routes.py +4 -7
  16. multimodalsim_viewer/server/log_manager.py +2 -2
  17. multimodalsim_viewer/server/server.py +8 -10
  18. multimodalsim_viewer/server/simulation.py +4 -5
  19. multimodalsim_viewer/server/simulation_manager.py +22 -23
  20. multimodalsim_viewer/ui/static/environment.json +2 -0
  21. multimodalsim_viewer/ui/static/index.html +2 -2
  22. multimodalsim_viewer/ui/static/{main-LUPJCMAF.js → main-7DV4COXP.js} +173 -173
  23. multimodalsim_viewer/ui/static/scripts/load-environment.script.js +1 -1
  24. multimodalsim_viewer/ui/static/styles-257KETL3.css +1 -0
  25. {multimodalsim_viewer-0.0.3.dist-info → multimodalsim_viewer-0.1.0.1.dist-info}/METADATA +6 -12
  26. multimodalsim_viewer-0.1.0.1.dist-info/RECORD +53 -0
  27. multimodalsim_viewer/server/simulation_visualization_data_model.py +0 -1570
  28. multimodalsim_viewer/ui/static/styles-KU7LTPET.css +0 -1
  29. multimodalsim_viewer-0.0.3.dist-info/RECORD +0 -43
  30. {multimodalsim_viewer-0.0.3.dist-info → multimodalsim_viewer-0.1.0.1.dist-info}/WHEEL +0 -0
  31. {multimodalsim_viewer-0.0.3.dist-info → multimodalsim_viewer-0.1.0.1.dist-info}/entry_points.txt +0 -0
  32. {multimodalsim_viewer-0.0.3.dist-info → multimodalsim_viewer-0.1.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,572 @@
1
+ import os
2
+ from io import TextIOWrapper
3
+ from json import dump, loads
4
+
5
+ from filelock import FileLock
6
+
7
+ from multimodalsim_viewer.common.utils import (
8
+ INPUT_DATA_DIRECTORY_PATH,
9
+ NUMBER_OF_STATES_TO_SEND_AT_ONCE,
10
+ SIMULATION_SAVE_FILE_SEPARATOR,
11
+ )
12
+ from multimodalsim_viewer.models.environment import VisualizedEnvironment
13
+ from multimodalsim_viewer.models.simulation_information import SimulationInformation
14
+ from multimodalsim_viewer.models.update import Update
15
+
16
+
17
+ # MARK: Data Manager
18
+ class SimulationVisualizationDataManager: # pylint: disable=too-many-public-methods
19
+ """
20
+ This class manage reads and writes of simulation data for visualization.
21
+ """
22
+
23
+ __CORRUPTED_FILE_NAME = ".corrupted"
24
+ __SAVED_SIMULATIONS_DIRECTORY_NAME = "saved_simulations"
25
+ __SIMULATION_INFORMATION_FILE_NAME = "simulation_information.json"
26
+ __STATES_DIRECTORY_NAME = "states"
27
+ __POLYLINES_DIRECTORY_NAME = "polylines"
28
+ __POLYLINES_FILE_NAME = "polylines"
29
+ __POLYLINES_VERSION_FILE_NAME = "version"
30
+
31
+ __STATES_UPDATE_INDEX_MINIMUM_LENGTH = 8
32
+ __STATES_TIMESTAMP_MINIMUM_LENGTH = 8
33
+
34
+ # MARK: +- Format
35
+ @staticmethod
36
+ def __format_json_readable(data: dict, file: str) -> str:
37
+ return dump(data, file, indent=2, separators=(",", ": "), sort_keys=True)
38
+
39
+ @staticmethod
40
+ def __format_json_one_line(data: dict | str | int | float | bool, file: str) -> str:
41
+ # Add new line before if not empty
42
+ if file.tell() != 0:
43
+ file.write("\n")
44
+ return dump(data, file, separators=(",", ":"))
45
+
46
+ # MARK: +- File paths
47
+ @staticmethod
48
+ def get_saved_simulations_directory_path() -> str:
49
+ directory_path = os.path.join(
50
+ SimulationVisualizationDataManager.get_data_directory_path(),
51
+ SimulationVisualizationDataManager.__SAVED_SIMULATIONS_DIRECTORY_NAME,
52
+ )
53
+
54
+ if not os.path.exists(directory_path):
55
+ os.makedirs(directory_path)
56
+
57
+ return directory_path
58
+
59
+ @staticmethod
60
+ def get_all_saved_simulation_ids() -> list[str]:
61
+ directory_path = SimulationVisualizationDataManager.get_saved_simulations_directory_path()
62
+ return os.listdir(directory_path)
63
+
64
+ @staticmethod
65
+ def get_saved_simulation_directory_path(simulation_id: str, should_create=False) -> str:
66
+ directory_path = SimulationVisualizationDataManager.get_saved_simulations_directory_path()
67
+ simulation_directory_path = f"{directory_path}/{simulation_id}"
68
+
69
+ if should_create and not os.path.exists(simulation_directory_path):
70
+ os.makedirs(simulation_directory_path)
71
+
72
+ return simulation_directory_path
73
+
74
+ # MARK: +- Folder size
75
+ @staticmethod
76
+ def _get_folder_size(start_path: str) -> int:
77
+ total_size = 0
78
+ for directory_path, _, file_names in os.walk(start_path):
79
+ file_names = [name for name in file_names if not name.endswith(".lock")]
80
+ for file_name in file_names:
81
+ file_path = os.path.join(directory_path, file_name)
82
+ lock = FileLock(f"{file_path}.lock")
83
+ with lock:
84
+ total_size += os.path.getsize(file_path)
85
+ return total_size
86
+
87
+ @staticmethod
88
+ def get_saved_simulation_size(simulation_id: str) -> int:
89
+ simulation_directory_path = SimulationVisualizationDataManager.get_saved_simulation_directory_path(
90
+ simulation_id
91
+ )
92
+ return SimulationVisualizationDataManager._get_folder_size(simulation_directory_path)
93
+
94
+ # MARK: +- Corrupted
95
+ @staticmethod
96
+ def is_simulation_corrupted(simulation_id: str) -> bool:
97
+ simulation_directory_path = SimulationVisualizationDataManager.get_saved_simulation_directory_path(
98
+ simulation_id, True
99
+ )
100
+
101
+ return os.path.exists(f"{simulation_directory_path}/{SimulationVisualizationDataManager.__CORRUPTED_FILE_NAME}")
102
+
103
+ @staticmethod
104
+ def mark_simulation_as_corrupted(simulation_id: str) -> None:
105
+ simulation_directory_path = SimulationVisualizationDataManager.get_saved_simulation_directory_path(
106
+ simulation_id, True
107
+ )
108
+
109
+ file_path = f"{simulation_directory_path}/{SimulationVisualizationDataManager.__CORRUPTED_FILE_NAME}"
110
+
111
+ with open(file_path, "w", encoding="utf-8") as file:
112
+ file.write("")
113
+
114
+ # MARK: +- Simulation Information
115
+ @staticmethod
116
+ def get_saved_simulation_information_file_path(simulation_id: str) -> str:
117
+ simulation_directory_path = SimulationVisualizationDataManager.get_saved_simulation_directory_path(
118
+ simulation_id, True
119
+ )
120
+ file_path = (
121
+ f"{simulation_directory_path}/{SimulationVisualizationDataManager.__SIMULATION_INFORMATION_FILE_NAME}"
122
+ )
123
+
124
+ if not os.path.exists(file_path):
125
+ with open(file_path, "w", encoding="utf-8") as file:
126
+ file.write("")
127
+
128
+ return file_path
129
+
130
+ @staticmethod
131
+ def set_simulation_information(simulation_id: str, simulation_information: SimulationInformation) -> None:
132
+ file_path = SimulationVisualizationDataManager.get_saved_simulation_information_file_path(simulation_id)
133
+
134
+ lock = FileLock(f"{file_path}.lock")
135
+
136
+ with lock:
137
+ with open(file_path, "w", encoding="utf-8") as file:
138
+ SimulationVisualizationDataManager.__format_json_readable(simulation_information.serialize(), file)
139
+
140
+ @staticmethod
141
+ def get_simulation_information(simulation_id: str) -> SimulationInformation:
142
+ file_path = SimulationVisualizationDataManager.get_saved_simulation_information_file_path(simulation_id)
143
+
144
+ lock = FileLock(f"{file_path}.lock")
145
+
146
+ simulation_information = None
147
+ should_update_simulation_information = False
148
+
149
+ with lock:
150
+ with open(file_path, "r", encoding="utf-8") as file:
151
+ data = file.read()
152
+
153
+ simulation_information = SimulationInformation.deserialize(data)
154
+
155
+ # Handle mismatched simulation_id, name, or start_time because of uploads
156
+ # where the simulation folder has been renamed due to duplicates.
157
+ start_time, name = simulation_id.split(SIMULATION_SAVE_FILE_SEPARATOR)
158
+
159
+ if (
160
+ simulation_id != simulation_information.simulation_id
161
+ or name != simulation_information.name
162
+ or start_time != simulation_information.start_time
163
+ ):
164
+ simulation_information.simulation_id = simulation_id
165
+ simulation_information.name = name
166
+ simulation_information.start_time = start_time
167
+
168
+ if simulation_information is not None and should_update_simulation_information:
169
+ SimulationVisualizationDataManager.set_simulation_information(simulation_id, simulation_information)
170
+
171
+ return simulation_information
172
+
173
+ # MARK: +- States and updates
174
+ @staticmethod
175
+ def get_saved_simulation_states_folder_path(simulation_id: str) -> str:
176
+ simulation_directory_path = SimulationVisualizationDataManager.get_saved_simulation_directory_path(
177
+ simulation_id, True
178
+ )
179
+ folder_path = f"{simulation_directory_path}/{SimulationVisualizationDataManager.__STATES_DIRECTORY_NAME}"
180
+
181
+ if not os.path.exists(folder_path):
182
+ os.makedirs(folder_path)
183
+
184
+ return folder_path
185
+
186
+ @staticmethod
187
+ def get_saved_simulation_state_file_path(simulation_id: str, update_index: int, timestamp: float) -> str:
188
+ folder_path = SimulationVisualizationDataManager.get_saved_simulation_states_folder_path(simulation_id)
189
+
190
+ padded_update_index = str(update_index).zfill(
191
+ SimulationVisualizationDataManager.__STATES_UPDATE_INDEX_MINIMUM_LENGTH
192
+ )
193
+ padded_timestamp = str(int(timestamp)).zfill(
194
+ SimulationVisualizationDataManager.__STATES_TIMESTAMP_MINIMUM_LENGTH
195
+ )
196
+
197
+ # States and updates are stored in a .jsonl file to speed up reads and writes
198
+ # Each line is a state (the first line) or an update (the following lines)
199
+ file_path = f"{folder_path}/{padded_update_index}-{padded_timestamp}.jsonl"
200
+
201
+ if not os.path.exists(file_path):
202
+ with open(file_path, "w", encoding="utf-8") as file:
203
+ file.write("")
204
+
205
+ return file_path
206
+
207
+ @staticmethod
208
+ def get_sorted_states(simulation_id: str) -> list[tuple[int, float]]:
209
+ folder_path = SimulationVisualizationDataManager.get_saved_simulation_states_folder_path(simulation_id)
210
+
211
+ all_states_files = [
212
+ path for path in os.listdir(folder_path) if path.endswith(".jsonl")
213
+ ] # Filter out lock files
214
+
215
+ states = []
216
+ for state_file in all_states_files:
217
+ update_index, timestamp = state_file.split("-")
218
+ states.append((int(update_index), float(timestamp.split(".")[0])))
219
+
220
+ return sorted(states, key=lambda x: (x[1], x[0]))
221
+
222
+ @staticmethod
223
+ def save_state(simulation_id: str, environment: VisualizedEnvironment) -> str:
224
+ file_path = SimulationVisualizationDataManager.get_saved_simulation_state_file_path(
225
+ simulation_id, environment.update_index, environment.timestamp
226
+ )
227
+
228
+ lock = FileLock(f"{file_path}.lock")
229
+
230
+ with lock:
231
+ with open(file_path, "w", encoding="utf-8") as file:
232
+ # Store timestamp
233
+ SimulationVisualizationDataManager.__format_json_one_line(environment.timestamp, file)
234
+
235
+ # Store update index
236
+ SimulationVisualizationDataManager.__format_json_one_line(environment.update_index, file)
237
+
238
+ # Store statistics
239
+ SimulationVisualizationDataManager.__format_json_one_line(
240
+ environment.statistics if environment.statistics else {}, file
241
+ )
242
+
243
+ # Store total number of passengers
244
+ SimulationVisualizationDataManager.__format_json_one_line(len(environment.passengers), file)
245
+
246
+ # Store each passenger
247
+ for passenger in environment.passengers.values():
248
+ SimulationVisualizationDataManager.__format_json_one_line(passenger.serialize(), file)
249
+
250
+ # Store total number of vehicles
251
+ SimulationVisualizationDataManager.__format_json_one_line(len(environment.vehicles), file)
252
+
253
+ # Store each vehicle
254
+ for vehicle in environment.vehicles.values():
255
+ SimulationVisualizationDataManager.__format_json_one_line(vehicle.serialize(), file)
256
+
257
+ return file_path
258
+
259
+ @staticmethod
260
+ def get_state_to_send(file: TextIOWrapper) -> dict:
261
+ state = {}
262
+
263
+ state["timestamp"] = loads(file.readline().strip())
264
+ state["updateIndex"] = loads(file.readline().strip())
265
+ state["statistics"] = file.readline().strip()
266
+
267
+ state["passengers"] = []
268
+
269
+ num_passengers = loads(file.readline().strip())
270
+ for _ in range(num_passengers):
271
+ state["passengers"].append(file.readline().strip())
272
+
273
+ state["vehicles"] = []
274
+
275
+ num_vehicles = loads(file.readline().strip())
276
+ for _ in range(num_vehicles):
277
+ state["vehicles"].append(file.readline().strip())
278
+
279
+ return state
280
+
281
+ @staticmethod
282
+ def save_update(file_path: str, update: Update) -> None:
283
+ lock = FileLock(f"{file_path}.lock")
284
+ with lock:
285
+ with open(file_path, "a", encoding="utf-8") as file:
286
+ SimulationVisualizationDataManager.__format_json_one_line(update.serialize(), file)
287
+
288
+ @staticmethod
289
+ def get_missing_states( # pylint: disable=too-many-locals, too-many-branches, too-many-statements
290
+ simulation_id: str,
291
+ visualization_time: float,
292
+ complete_state_update_indexes: list[int],
293
+ is_simulation_complete: bool,
294
+ ) -> tuple[list[dict], dict[list[str]], bool]:
295
+ sorted_states = SimulationVisualizationDataManager.get_sorted_states(simulation_id)
296
+
297
+ if len(sorted_states) == 0:
298
+ return [], {}, False
299
+
300
+ if len(complete_state_update_indexes) == len(sorted_states):
301
+ # If the client has all states, no need to request more
302
+ return [], {}, True
303
+
304
+ necessary_state_index = None
305
+
306
+ for index, (update_index, state_timestamp) in enumerate(sorted_states):
307
+ if necessary_state_index is None and state_timestamp > visualization_time:
308
+ necessary_state_index = index
309
+ break
310
+
311
+ if necessary_state_index is None:
312
+ # If the visualization time is after the last state then
313
+ # The last state is necessary
314
+ necessary_state_index = len(sorted_states) - 1
315
+ else:
316
+ # Else we need the state before the first state with greater timestamp
317
+ necessary_state_index -= 1
318
+
319
+ # Handle negative indexes
320
+ necessary_state_index = max(0, necessary_state_index)
321
+
322
+ missing_states = []
323
+ missing_updates = {}
324
+ has_incomplete_states = False
325
+
326
+ # We want to load the necessary state first, followed by
327
+ # the next states and then the previous states in reverse order.
328
+ indexes_to_load = (
329
+ [necessary_state_index]
330
+ # All next states
331
+ + list(range(necessary_state_index + 1, len(sorted_states)))
332
+ # All previous states
333
+ + list(range(necessary_state_index - 1, -1, -1))
334
+ )
335
+
336
+ for index in indexes_to_load:
337
+ update_index, state_timestamp = sorted_states[index]
338
+
339
+ # If the client already has the state, skip it.
340
+ if update_index in complete_state_update_indexes:
341
+ continue
342
+
343
+ # Don't add states if the max number of states is reached
344
+ # but continue the loop to know which states need to be kept
345
+ if len(missing_states) >= NUMBER_OF_STATES_TO_SEND_AT_ONCE:
346
+ continue
347
+
348
+ state_file_path = SimulationVisualizationDataManager.get_saved_simulation_state_file_path(
349
+ simulation_id, update_index, state_timestamp
350
+ )
351
+
352
+ lock = FileLock(f"{state_file_path}.lock")
353
+
354
+ with lock:
355
+ with open(state_file_path, "r", encoding="utf-8") as file:
356
+ state = SimulationVisualizationDataManager.get_state_to_send(file)
357
+
358
+ isComplete = is_simulation_complete or (index < len(sorted_states) - 1)
359
+ if not isComplete:
360
+ has_incomplete_states = True
361
+ state["isComplete"] = isComplete
362
+
363
+ missing_states.append(state)
364
+
365
+ updates_data = file.readlines()
366
+ current_state_updates = []
367
+ for update_data in updates_data:
368
+ current_state_updates.append(update_data)
369
+
370
+ missing_updates[update_index] = current_state_updates
371
+
372
+ has_all_states = (
373
+ len(missing_states) + len(complete_state_update_indexes) == len(sorted_states) and not has_incomplete_states
374
+ )
375
+
376
+ return (missing_states, missing_updates, has_all_states)
377
+
378
+ # MARK: +- Polylines
379
+
380
+ # The polylines are saved with the following structure :
381
+ # polylines/
382
+ # version
383
+ # polylines.jsonl
384
+ # { "coordinatesString": "string", "encodedPolyline": "string", "coefficients": [float] }
385
+
386
+ @staticmethod
387
+ def get_saved_simulation_polylines_lock(simulation_id: str) -> FileLock:
388
+ simulation_directory_path = SimulationVisualizationDataManager.get_saved_simulation_directory_path(
389
+ simulation_id, True
390
+ )
391
+ return FileLock(f"{simulation_directory_path}/polylines.lock")
392
+
393
+ @staticmethod
394
+ def get_saved_simulation_polylines_directory_path(simulation_id: str) -> str:
395
+ simulation_directory_path = SimulationVisualizationDataManager.get_saved_simulation_directory_path(
396
+ simulation_id, True
397
+ )
398
+ directory_path = f"{simulation_directory_path}/{SimulationVisualizationDataManager.__POLYLINES_DIRECTORY_NAME}"
399
+
400
+ if not os.path.exists(directory_path):
401
+ os.makedirs(directory_path)
402
+
403
+ return directory_path
404
+
405
+ @staticmethod
406
+ def get_saved_simulation_polylines_version_file_path(simulation_id: str) -> str:
407
+ directory_path = SimulationVisualizationDataManager.get_saved_simulation_polylines_directory_path(simulation_id)
408
+ file_path = f"{directory_path}/{SimulationVisualizationDataManager.__POLYLINES_VERSION_FILE_NAME}"
409
+
410
+ if not os.path.exists(file_path):
411
+ with open(file_path, "w", encoding="utf-8") as file:
412
+ file.write(str(0))
413
+
414
+ return file_path
415
+
416
+ @staticmethod
417
+ def set_polylines_version(simulation_id: str, version: int) -> None:
418
+ """
419
+ Should always be called in a lock.
420
+ """
421
+ file_path = SimulationVisualizationDataManager.get_saved_simulation_polylines_version_file_path(simulation_id)
422
+
423
+ with open(file_path, "w", encoding="utf-8") as file:
424
+ file.write(str(version))
425
+
426
+ @staticmethod
427
+ def get_polylines_version(simulation_id: str) -> int:
428
+ """
429
+ Should always be called in a lock.
430
+ """
431
+ file_path = SimulationVisualizationDataManager.get_saved_simulation_polylines_version_file_path(simulation_id)
432
+
433
+ with open(file_path, "r", encoding="utf-8") as file:
434
+ return int(file.read())
435
+
436
+ @staticmethod
437
+ def get_polylines_version_with_lock(simulation_id: str) -> int:
438
+ lock = SimulationVisualizationDataManager.get_saved_simulation_polylines_lock(simulation_id)
439
+ with lock:
440
+ return SimulationVisualizationDataManager.get_polylines_version(simulation_id)
441
+
442
+ @staticmethod
443
+ def get_saved_simulation_polylines_file_path(simulation_id: str) -> str:
444
+ directory_path = SimulationVisualizationDataManager.get_saved_simulation_polylines_directory_path(simulation_id)
445
+
446
+ file_path = f"{directory_path}/{SimulationVisualizationDataManager.__POLYLINES_FILE_NAME}.jsonl"
447
+
448
+ if not os.path.exists(file_path):
449
+ with open(file_path, "w", encoding="utf-8") as file:
450
+ file.write("")
451
+
452
+ return file_path
453
+
454
+ @staticmethod
455
+ def set_polylines(simulation_id: str, polylines: dict[str, tuple[str, list[float]]]) -> None:
456
+
457
+ file_path = SimulationVisualizationDataManager.get_saved_simulation_polylines_file_path(simulation_id)
458
+
459
+ lock = SimulationVisualizationDataManager.get_saved_simulation_polylines_lock(simulation_id)
460
+
461
+ with lock:
462
+ # Increment the version to notify the client that the polylines have changed
463
+ version = SimulationVisualizationDataManager.get_polylines_version(simulation_id)
464
+ version += 1
465
+ SimulationVisualizationDataManager.set_polylines_version(simulation_id, version)
466
+
467
+ with open(file_path, "a", encoding="utf-8") as file:
468
+ for coordinates_string, (
469
+ encoded_polyline,
470
+ coefficients,
471
+ ) in polylines.items():
472
+ data = {
473
+ "coordinatesString": coordinates_string,
474
+ "encodedPolyline": encoded_polyline,
475
+ "coefficients": coefficients,
476
+ }
477
+ SimulationVisualizationDataManager.__format_json_one_line(data, file)
478
+
479
+ @staticmethod
480
+ def get_polylines(
481
+ simulation_id: str,
482
+ ) -> tuple[list[str], int]:
483
+
484
+ polylines = []
485
+
486
+ lock = SimulationVisualizationDataManager.get_saved_simulation_polylines_lock(simulation_id)
487
+
488
+ version = 0
489
+
490
+ with lock:
491
+ version = SimulationVisualizationDataManager.get_polylines_version(simulation_id)
492
+
493
+ file_path = SimulationVisualizationDataManager.get_saved_simulation_polylines_file_path(simulation_id)
494
+
495
+ with open(file_path, "r", encoding="utf-8") as file:
496
+ for line in file:
497
+ polylines.append(line)
498
+
499
+ return polylines, version
500
+
501
+ @staticmethod
502
+ def get_all_simulation_states(simulation_id: str) -> tuple[list[str], dict[list[str]]]:
503
+ states = []
504
+ updates = {}
505
+
506
+ sorted_states = SimulationVisualizationDataManager.get_sorted_states(simulation_id)
507
+
508
+ for update_index, timestamp in sorted_states:
509
+ file_path = SimulationVisualizationDataManager.get_saved_simulation_state_file_path(
510
+ simulation_id, update_index, timestamp
511
+ )
512
+
513
+ lock = FileLock(f"{file_path}.lock")
514
+
515
+ with lock:
516
+ with open(file_path, "r", encoding="utf-8") as file:
517
+ state_data = file.readline()
518
+ states.append(state_data)
519
+
520
+ updates_data = file.readlines()
521
+ current_state_updates = []
522
+ for update_data in updates_data:
523
+ current_state_updates.append(update_data)
524
+
525
+ updates[update_index] = current_state_updates
526
+
527
+ return states, updates
528
+
529
+ # MARK: +- Simulation Data
530
+ @staticmethod
531
+ def get_data_directory_path() -> str:
532
+ current_file_path = os.path.abspath(__file__)
533
+ current_file_dir = os.path.dirname(current_file_path)
534
+ data_directory_path = os.path.join(current_file_dir, "..", "data")
535
+
536
+ if not os.path.exists(data_directory_path):
537
+ os.makedirs(data_directory_path)
538
+
539
+ return data_directory_path
540
+
541
+ @staticmethod
542
+ def get_saved_logs_directory_path() -> str:
543
+ data_directory_path = SimulationVisualizationDataManager.get_data_directory_path()
544
+ saved_logs_directory_path = os.path.join(data_directory_path, "saved_logs")
545
+
546
+ if not os.path.exists(saved_logs_directory_path):
547
+ os.makedirs(saved_logs_directory_path)
548
+
549
+ return saved_logs_directory_path
550
+
551
+ @staticmethod
552
+ def get_input_data_directory_path(data: str | None = None) -> str:
553
+ input_data_directory = INPUT_DATA_DIRECTORY_PATH
554
+
555
+ if data is not None:
556
+ input_data_directory = os.path.join(input_data_directory, data)
557
+
558
+ return input_data_directory
559
+
560
+ @staticmethod
561
+ def get_available_data():
562
+ input_data_directory = SimulationVisualizationDataManager.get_input_data_directory_path()
563
+
564
+ if not os.path.exists(input_data_directory):
565
+ return []
566
+
567
+ # List all directories in the input data directory
568
+ return [
569
+ name
570
+ for name in os.listdir(input_data_directory)
571
+ if os.path.isdir(os.path.join(input_data_directory, name)) and not name.startswith(".")
572
+ ]
@@ -6,10 +6,7 @@ import zipfile
6
6
 
7
7
  from flask import Blueprint, jsonify, request, send_file
8
8
 
9
- from multimodalsim_viewer.common.utils import get_input_data_directory_path
10
- from multimodalsim_viewer.server.simulation_visualization_data_model import (
11
- SimulationVisualizationDataManager,
12
- )
9
+ from multimodalsim_viewer.server.data_manager import SimulationVisualizationDataManager
13
10
 
14
11
  http_routes = Blueprint("http_routes", __name__)
15
12
 
@@ -80,7 +77,7 @@ def handle_zip_upload(folder_path):
80
77
  # MARK: Input Data Routes
81
78
  @http_routes.route("/api/input_data/<folder_name>", methods=["GET"])
82
79
  def export_input_data(folder_name):
83
- folder_path = get_input_data_directory_path(folder_name)
80
+ folder_path = SimulationVisualizationDataManager.get_input_data_directory_path(folder_name)
84
81
  logging.info("Requested folder: %s", folder_path)
85
82
 
86
83
  zip_path = zip_folder(folder_path, folder_name)
@@ -92,13 +89,13 @@ def export_input_data(folder_name):
92
89
 
93
90
  @http_routes.route("/api/input_data/<folder_name>", methods=["POST"])
94
91
  def import_input_data(folder_name):
95
- folder_path = get_input_data_directory_path(folder_name)
92
+ folder_path = SimulationVisualizationDataManager.get_input_data_directory_path(folder_name)
96
93
  return handle_zip_upload(folder_path)
97
94
 
98
95
 
99
96
  @http_routes.route("/api/input_data/<folder_name>", methods=["DELETE"])
100
97
  def delete_input_data(folder_name):
101
- folder_path = get_input_data_directory_path(folder_name)
98
+ folder_path = SimulationVisualizationDataManager.get_input_data_directory_path(folder_name)
102
99
  if not os.path.isdir(folder_path):
103
100
  return jsonify({"error": "Folder not found"}), 404
104
101
 
@@ -1,8 +1,8 @@
1
- from multimodalsim_viewer.common.utils import get_saved_logs_directory_path
1
+ from multimodalsim_viewer.server.data_manager import SimulationVisualizationDataManager
2
2
 
3
3
 
4
4
  def register_log(simulation_id, message):
5
- saved_logs_directory_path = get_saved_logs_directory_path()
5
+ saved_logs_directory_path = SimulationVisualizationDataManager.get_saved_logs_directory_path()
6
6
  file_name = f"{simulation_id}.txt"
7
7
  file_path = f"{saved_logs_directory_path}/{file_name}"
8
8
 
@@ -5,12 +5,8 @@ from flask import Flask
5
5
  from flask_cors import CORS
6
6
  from flask_socketio import SocketIO, emit, join_room, leave_room
7
7
 
8
- from multimodalsim_viewer.common.utils import (
9
- CLIENT_ROOM,
10
- get_available_data,
11
- get_session_id,
12
- log,
13
- )
8
+ from multimodalsim_viewer.common.utils import CLIENT_ROOM, get_session_id, log
9
+ from multimodalsim_viewer.server.data_manager import SimulationVisualizationDataManager
14
10
  from multimodalsim_viewer.server.http_routes import http_routes
15
11
  from multimodalsim_viewer.server.simulation_manager import SimulationManager
16
12
 
@@ -80,17 +76,19 @@ def configure_server() -> tuple[Flask, SocketIO]: # pylint: disable=too-many-st
80
76
  @socketio.on("get-available-data")
81
77
  def on_client_get_data():
82
78
  log("getting available data", "client")
83
- emit("available-data", get_available_data(), to=CLIENT_ROOM)
79
+ emit("available-data", SimulationVisualizationDataManager.get_available_data(), to=CLIENT_ROOM)
84
80
 
85
81
  @socketio.on("get-missing-simulation-states")
86
- def on_client_get_missing_simulation_states(simulation_id, visualization_time, loaded_state_orders):
82
+ def on_client_get_missing_simulation_states(simulation_id, visualization_time, complete_state_update_indexes):
87
83
  log(
88
84
  f"getting missing simulation states for {simulation_id} "
89
85
  f"with visualization time {visualization_time} "
90
- f"and {len(loaded_state_orders)} loaded state orders",
86
+ f"and {len(complete_state_update_indexes)} complete state update indexes",
91
87
  "client",
92
88
  )
93
- simulation_manager.emit_missing_simulation_states(simulation_id, visualization_time, loaded_state_orders)
89
+ simulation_manager.emit_missing_simulation_states(
90
+ simulation_id, visualization_time, complete_state_update_indexes
91
+ )
94
92
 
95
93
  @socketio.on("get-polylines")
96
94
  def on_client_get_polylines(simulation_id):