multimodalsim-viewer 0.1.1.0__tar.gz → 0.1.3.0__tar.gz

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 (62) hide show
  1. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/PKG-INFO +1 -1
  2. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/common/environments/.env +1 -0
  3. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/common/utils.py +6 -10
  4. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/server/data_manager.py +8 -37
  5. multimodalsim_viewer-0.1.3.0/multimodalsim_viewer/server/http_routes.py +156 -0
  6. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/server/scripts.py +1 -5
  7. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/server/server.py +8 -12
  8. multimodalsim_viewer-0.1.3.0/multimodalsim_viewer/server/simulation_manager.py +781 -0
  9. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/environment.json +1 -0
  10. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/index.html +1 -1
  11. multimodalsim_viewer-0.1.1.0/multimodalsim_viewer/ui/static/main-APJ3N7UX.js → multimodalsim_viewer-0.1.3.0/multimodalsim_viewer/ui/static/main-APOJBNRT.js +116 -116
  12. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer.egg-info/PKG-INFO +1 -1
  13. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer.egg-info/SOURCES.txt +1 -1
  14. multimodalsim_viewer-0.1.1.0/multimodalsim_viewer/server/http_routes.py +0 -132
  15. multimodalsim_viewer-0.1.1.0/multimodalsim_viewer/server/simulation_manager.py +0 -569
  16. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/MANIFEST.in +0 -0
  17. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/README.md +0 -0
  18. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/__init__.py +0 -0
  19. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/common/__init__.py +0 -0
  20. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/models/__init__.py +0 -0
  21. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/models/environment.py +0 -0
  22. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/models/leg.py +0 -0
  23. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/models/passenger.py +0 -0
  24. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/models/serializable.py +0 -0
  25. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/models/simulation_information.py +0 -0
  26. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/models/state.py +0 -0
  27. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/models/stop.py +0 -0
  28. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/models/update.py +0 -0
  29. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/models/vehicle.py +0 -0
  30. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/server/__init__.py +0 -0
  31. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/server/data_collector.py +0 -0
  32. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/server/log_manager.py +0 -0
  33. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/server/simulation.py +0 -0
  34. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/__init__.py +0 -0
  35. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/angular_app.py +0 -0
  36. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/bitmap-fonts/custom-sans-serif.png +0 -0
  37. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/bitmap-fonts/custom-sans-serif.xml +0 -0
  38. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/chunk-BQ2VC5TN.js +0 -0
  39. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/chunk-RHGMGEGM.js +0 -0
  40. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/favicon.ico +0 -0
  41. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/images/control-bar.png +0 -0
  42. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/images/passenger.png +0 -0
  43. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/images/simulation-control-bar.png +0 -0
  44. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/images/stop.png +0 -0
  45. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/images/undefined-texture.png +0 -0
  46. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/images/vehicle.png +0 -0
  47. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/images/zoomed-out-passenger.png +0 -0
  48. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/images/zoomed-out-stop.png +0 -0
  49. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/images/zoomed-out-vehicle.png +0 -0
  50. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/media/layers-2x-TBM42ERR.png +0 -0
  51. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/media/layers-55W3Q4RM.png +0 -0
  52. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/media/marker-icon-2V3QKKVC.png +0 -0
  53. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/polyfills-FFHMD2TL.js +0 -0
  54. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/scripts/load-environment.script.js +0 -0
  55. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer/ui/static/styles-257KETL3.css +0 -0
  56. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer.egg-info/dependency_links.txt +0 -0
  57. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer.egg-info/entry_points.txt +0 -0
  58. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer.egg-info/requires.txt +0 -0
  59. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/multimodalsim_viewer.egg-info/top_level.txt +0 -0
  60. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/pyproject.toml +0 -0
  61. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/setup.cfg +0 -0
  62. {multimodalsim_viewer-0.1.1.0 → multimodalsim_viewer-0.1.3.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multimodalsim_viewer
3
- Version: 0.1.1.0
3
+ Version: 0.1.3.0
4
4
  Summary: Multimodal simulation viewer
5
5
  Keywords: flask angular ui multimodal server
6
6
  Requires-Python: >=3.10
@@ -2,5 +2,6 @@ CLIENT_PORT=8085
2
2
  SERVER_PORT=8089
3
3
  SIMULATION_SAVE_FILE_SEPARATOR=---
4
4
  INPUT_DATA_DIRECTORY_PATH=data
5
+ OUTPUT_DATA_DIRECTORY_PATH=output
5
6
  NUMBER_OF_UPDATES_BETWEEN_STATES=1000
6
7
  NUMBER_OF_STATES_TO_SEND_AT_ONCE=1
@@ -9,7 +9,6 @@ from json import dumps
9
9
  from dotenv import dotenv_values
10
10
  from filelock import FileLock
11
11
  from flask import request
12
- from flask_socketio import emit
13
12
 
14
13
  environment = {}
15
14
 
@@ -79,6 +78,10 @@ class _Environment:
79
78
  def input_data_directory_path(self) -> str:
80
79
  return environment.get("INPUT_DATA_DIRECTORY_PATH")
81
80
 
81
+ @property
82
+ def output_data_directory_path(self) -> str:
83
+ return environment.get("OUTPUT_DATA_DIRECTORY_PATH")
84
+
82
85
  @property
83
86
  def number_of_updates_between_states(self) -> int:
84
87
  return int(environment.get("NUMBER_OF_UPDATES_BETWEEN_STATES"))
@@ -94,6 +97,7 @@ CLIENT_PORT = _environment.client_port
94
97
  HOST = _environment.host
95
98
  SIMULATION_SAVE_FILE_SEPARATOR = _environment.simulation_save_file_separator
96
99
  INPUT_DATA_DIRECTORY_PATH = _environment.input_data_directory_path
100
+ OUTPUT_DATA_DIRECTORY_PATH = _environment.output_data_directory_path
97
101
  NUMBER_OF_UPDATES_BETWEEN_STATES = _environment.number_of_updates_between_states
98
102
  NUMBER_OF_STATES_TO_SEND_AT_ONCE = _environment.number_of_states_to_send_at_once
99
103
 
@@ -142,19 +146,11 @@ def build_simulation_id(name: str) -> tuple[str, str]:
142
146
  return simulation_id, start_time
143
147
 
144
148
 
145
- def log(message: str, auth_type: str, level=logging.INFO, should_emit=True) -> None:
149
+ def log(message: str, auth_type: str, level=logging.INFO) -> None:
146
150
  if auth_type == "server":
147
151
  logging.log(level, "[%s] %s", auth_type, message)
148
- if should_emit:
149
- emit("log", f"{level} [{auth_type}] {message}", to=CLIENT_ROOM)
150
152
  else:
151
153
  logging.log(level, "[%s] %s %s", auth_type, get_session_id(), message)
152
- if should_emit:
153
- emit(
154
- "log",
155
- f"{level} [{auth_type}] {get_session_id()} {message}",
156
- to=CLIENT_ROOM,
157
- )
158
154
 
159
155
 
160
156
  def verify_simulation_name(name: str | None) -> str | None:
@@ -7,6 +7,7 @@ from filelock import FileLock
7
7
  from multimodalsim_viewer.common.utils import (
8
8
  INPUT_DATA_DIRECTORY_PATH,
9
9
  NUMBER_OF_STATES_TO_SEND_AT_ONCE,
10
+ OUTPUT_DATA_DIRECTORY_PATH,
10
11
  SIMULATION_SAVE_FILE_SEPARATOR,
11
12
  )
12
13
  from multimodalsim_viewer.models.environment import VisualizedEnvironment
@@ -47,7 +48,7 @@ class SimulationVisualizationDataManager: # pylint: disable=too-many-public-met
47
48
  @staticmethod
48
49
  def get_saved_simulations_directory_path() -> str:
49
50
  directory_path = os.path.join(
50
- SimulationVisualizationDataManager.get_data_directory_path(),
51
+ SimulationVisualizationDataManager.get_output_data_directory_path(),
51
52
  SimulationVisualizationDataManager.__SAVED_SIMULATIONS_DIRECTORY_NAME,
52
53
  )
53
54
 
@@ -498,49 +499,19 @@ class SimulationVisualizationDataManager: # pylint: disable=too-many-public-met
498
499
 
499
500
  return polylines, version
500
501
 
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
502
  # MARK: +- Simulation Data
530
503
  @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")
504
+ def get_output_data_directory_path() -> str:
505
+ output_data_directory = OUTPUT_DATA_DIRECTORY_PATH
535
506
 
536
- if not os.path.exists(data_directory_path):
537
- os.makedirs(data_directory_path)
507
+ if not os.path.exists(output_data_directory):
508
+ os.makedirs(output_data_directory)
538
509
 
539
- return data_directory_path
510
+ return output_data_directory
540
511
 
541
512
  @staticmethod
542
513
  def get_saved_logs_directory_path() -> str:
543
- data_directory_path = SimulationVisualizationDataManager.get_data_directory_path()
514
+ data_directory_path = SimulationVisualizationDataManager.get_output_data_directory_path()
544
515
  saved_logs_directory_path = os.path.join(data_directory_path, "saved_logs")
545
516
 
546
517
  if not os.path.exists(saved_logs_directory_path):
@@ -0,0 +1,156 @@
1
+ import logging
2
+ import os
3
+ import shutil
4
+ import tempfile
5
+ import zipfile
6
+ from typing import Callable
7
+
8
+ from flask import Blueprint, jsonify, request, send_file
9
+
10
+ from multimodalsim_viewer.server.data_manager import SimulationVisualizationDataManager
11
+ from multimodalsim_viewer.server.simulation_manager import SimulationManager
12
+
13
+
14
+ class InvalidFilesRequestError(Exception):
15
+ def __init__(self, error_message: str) -> None:
16
+ self.error_message = error_message
17
+
18
+
19
+ def http_routes(simulation_manager: SimulationManager): # pylint: disable=too-many-statements
20
+ blueprint = Blueprint("http_routes", __name__)
21
+
22
+ # MARK: Helpers
23
+ def get_unique_folder_name(base_path, folder_name):
24
+ counter = 1
25
+ original_name = folder_name
26
+ while os.path.exists(os.path.join(base_path, folder_name)):
27
+ folder_name = f"{original_name}_({counter})"
28
+ counter += 1
29
+ return folder_name
30
+
31
+ def zip_folder(folder_path, zip_name):
32
+ if not os.path.isdir(folder_path):
33
+ return None
34
+
35
+ zip_path = os.path.join(tempfile.gettempdir(), f"{zip_name}.zip")
36
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zip_file:
37
+ for root, _, files in os.walk(folder_path):
38
+ for file in files:
39
+ file_path = os.path.join(root, file)
40
+ zip_file.write(file_path, os.path.relpath(file_path, folder_path))
41
+
42
+ return zip_path
43
+
44
+ def save_and_extract_zip(path: str, files):
45
+ try:
46
+ if "file" not in files:
47
+ raise InvalidFilesRequestError("No file part")
48
+
49
+ file = files["file"]
50
+ if file.filename == "":
51
+ raise InvalidFilesRequestError("No selected file")
52
+
53
+ # Create temporary zip file
54
+ zip_path = os.path.join(tempfile.gettempdir(), file.filename)
55
+ file.save(zip_path)
56
+
57
+ # Extract files
58
+ with zipfile.ZipFile(zip_path, "r") as zip_ref:
59
+ zip_ref.extractall(path)
60
+ logging.info("Extracted files: %s", zip_ref.namelist())
61
+
62
+ # Let the exception propagate
63
+ finally:
64
+ # Remove temporary zip file
65
+ if os.path.exists(zip_path):
66
+ os.remove(zip_path)
67
+
68
+ def handle_zip_upload(folder_path, on_success: Callable | None = None):
69
+ parent_dir = os.path.dirname(folder_path)
70
+ base_folder_name = os.path.basename(folder_path)
71
+
72
+ unique_folder_name = get_unique_folder_name(parent_dir, base_folder_name)
73
+ actual_folder_path = os.path.join(parent_dir, unique_folder_name)
74
+
75
+ os.makedirs(actual_folder_path, exist_ok=True)
76
+
77
+ try:
78
+ save_and_extract_zip(actual_folder_path, request.files)
79
+ except InvalidFilesRequestError as error:
80
+ return jsonify({"error": error.error_message}), 400
81
+ except zipfile.BadZipFile:
82
+ return jsonify({"error": "Invalid ZIP file"}), 400
83
+
84
+ response_message = f"Folder '{unique_folder_name}' uploaded successfully"
85
+ if unique_folder_name != base_folder_name:
86
+ response_message += f" (renamed from '{base_folder_name}')"
87
+
88
+ if on_success:
89
+ on_success()
90
+
91
+ return (
92
+ jsonify({"message": response_message, "actual_folder_name": unique_folder_name}),
93
+ 201,
94
+ )
95
+
96
+ # MARK: Instances
97
+ @blueprint.route("/api/input_data/<folder_name>", methods=["GET"])
98
+ def export_input_data(folder_name):
99
+ folder_path = SimulationVisualizationDataManager.get_input_data_directory_path(folder_name)
100
+ logging.info("Requested folder: %s", folder_path)
101
+
102
+ zip_path = zip_folder(folder_path, folder_name)
103
+ if not zip_path:
104
+ return jsonify({"error": "Folder not found"}), 404
105
+
106
+ return send_file(zip_path, as_attachment=True)
107
+
108
+ @blueprint.route("/api/input_data/<folder_name>", methods=["POST"])
109
+ def import_input_data(folder_name):
110
+ folder_path = SimulationVisualizationDataManager.get_input_data_directory_path(folder_name)
111
+ return handle_zip_upload(folder_path)
112
+
113
+ @blueprint.route("/api/input_data/<folder_name>", methods=["DELETE"])
114
+ def delete_input_data(folder_name):
115
+ folder_path = SimulationVisualizationDataManager.get_input_data_directory_path(folder_name)
116
+ if not os.path.isdir(folder_path):
117
+ return jsonify({"error": "Folder not found"}), 404
118
+
119
+ shutil.rmtree(folder_path)
120
+ return jsonify({"message": f"Folder '{folder_name}' deleted successfully"})
121
+
122
+ # MARK: Visualizations
123
+ @blueprint.route("/api/simulation/<folder_name>", methods=["GET"])
124
+ def export_saved_simulation(folder_name):
125
+ folder_path = SimulationVisualizationDataManager.get_saved_simulation_directory_path(folder_name)
126
+ logging.info("Requested folder: %s", folder_path)
127
+
128
+ zip_path = zip_folder(folder_path, folder_name)
129
+ if not zip_path:
130
+ return jsonify({"error": "Folder not found"}), 404
131
+
132
+ return send_file(zip_path, as_attachment=True)
133
+
134
+ @blueprint.route("/api/simulation/<folder_name>", methods=["POST"])
135
+ def import_saved_simulation(folder_name):
136
+ folder_path = SimulationVisualizationDataManager.get_saved_simulation_directory_path(folder_name)
137
+
138
+ def on_success():
139
+ simulation_manager.emit_simulation(folder_name)
140
+
141
+ return handle_zip_upload(folder_path, on_success)
142
+
143
+ @blueprint.route("/api/simulation/<folder_name>", methods=["DELETE"])
144
+ def delete_saved_simulation(folder_name):
145
+ folder_path = SimulationVisualizationDataManager.get_saved_simulation_directory_path(folder_name)
146
+
147
+ if not os.path.isdir(folder_path):
148
+ return jsonify({"error": "Folder not found"}), 404
149
+
150
+ shutil.rmtree(folder_path)
151
+
152
+ simulation_manager.on_simulation_delete(folder_name)
153
+
154
+ return jsonify({"message": f"Folder '{folder_name}' deleted successfully"})
155
+
156
+ return blueprint
@@ -7,11 +7,7 @@ from requests.exceptions import ConnectionError as RequestsConnectionError
7
7
  from socketio import Client
8
8
  from socketio.exceptions import ConnectionError as SocketIOConnectionError
9
9
 
10
- from multimodalsim_viewer.common.utils import (
11
- CLIENT_PORT,
12
- HOST,
13
- SERVER_PORT,
14
- )
10
+ from multimodalsim_viewer.common.utils import CLIENT_PORT, HOST, SERVER_PORT
15
11
  from multimodalsim_viewer.server.server import configure_server
16
12
  from multimodalsim_viewer.server.simulation import (
17
13
  configure_simulation_parser,
@@ -14,17 +14,17 @@ from multimodalsim_viewer.server.simulation_manager import SimulationManager
14
14
  def configure_server() -> tuple[Flask, SocketIO]: # pylint: disable=too-many-statements, too-many-locals
15
15
  app = Flask(__name__)
16
16
 
17
+ socketio = SocketIO(app, cors_allowed_origins="*")
18
+
19
+ simulation_manager = SimulationManager(socketio)
20
+
17
21
  # Register HTTP routes
18
22
  CORS(app)
19
- app.register_blueprint(http_routes)
20
-
21
- socketio = SocketIO(app, cors_allowed_origins="*")
23
+ app.register_blueprint(http_routes(simulation_manager))
22
24
 
23
25
  # key = session id, value = auth type
24
26
  sockets_types_by_session_id = {}
25
27
 
26
- simulation_manager = SimulationManager()
27
-
28
28
  # MARK: Main events
29
29
  @socketio.on("connect")
30
30
  def on_connect(auth):
@@ -69,9 +69,9 @@ def configure_server() -> tuple[Flask, SocketIO]: # pylint: disable=too-many-st
69
69
  simulation_manager.resume_simulation(simulation_id)
70
70
 
71
71
  @socketio.on("get-simulations")
72
- def on_client_get_simulations():
73
- log("getting simulations", "client")
74
- simulation_manager.emit_simulations()
72
+ def on_client_get_simulations(loaded_simulations_ids: list[str]):
73
+ log(f"getting simulations with already loaded ids {loaded_simulations_ids}", "client")
74
+ simulation_manager.emit_simulations(loaded_simulations_ids)
75
75
 
76
76
  @socketio.on("get-available-data")
77
77
  def on_client_get_data():
@@ -136,10 +136,6 @@ def configure_server() -> tuple[Flask, SocketIO]: # pylint: disable=too-many-st
136
136
  log(f"simulation {simulation_id} resumed", "simulation")
137
137
  simulation_manager.on_simulation_resume(simulation_id)
138
138
 
139
- @socketio.on("log")
140
- def on_simulation_log(simulation_id, message):
141
- log(f"simulation {simulation_id}: {message}", "simulation", logging.DEBUG)
142
-
143
139
  @socketio.on("simulation-update-time")
144
140
  def on_simulation_update_time(simulation_id, timestamp):
145
141
  log(