multimodalsim-viewer 0.1.2.0__py3-none-any.whl → 0.1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- multimodalsim_viewer/common/utils.py +1 -10
- multimodalsim_viewer/server/data_manager.py +0 -28
- multimodalsim_viewer/server/http_routes.py +113 -89
- multimodalsim_viewer/server/scripts.py +1 -5
- multimodalsim_viewer/server/server.py +8 -12
- multimodalsim_viewer/server/simulation_manager.py +456 -244
- multimodalsim_viewer/ui/static/index.html +1 -1
- multimodalsim_viewer/ui/static/{main-APJ3N7UX.js → main-APOJBNRT.js} +116 -116
- {multimodalsim_viewer-0.1.2.0.dist-info → multimodalsim_viewer-0.1.3.0.dist-info}/METADATA +1 -1
- {multimodalsim_viewer-0.1.2.0.dist-info → multimodalsim_viewer-0.1.3.0.dist-info}/RECORD +13 -13
- {multimodalsim_viewer-0.1.2.0.dist-info → multimodalsim_viewer-0.1.3.0.dist-info}/WHEEL +0 -0
- {multimodalsim_viewer-0.1.2.0.dist-info → multimodalsim_viewer-0.1.3.0.dist-info}/entry_points.txt +0 -0
- {multimodalsim_viewer-0.1.2.0.dist-info → multimodalsim_viewer-0.1.3.0.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,11 @@
|
|
1
|
+
import dataclasses
|
1
2
|
import inspect
|
2
3
|
import logging
|
3
4
|
import multiprocessing
|
5
|
+
import time
|
6
|
+
from threading import Lock, Thread
|
4
7
|
|
5
|
-
from flask_socketio import
|
8
|
+
from flask_socketio import SocketIO
|
6
9
|
|
7
10
|
from multimodalsim_viewer.common.utils import (
|
8
11
|
CLIENT_ROOM,
|
@@ -69,15 +72,203 @@ class SimulationHandler: # pylint: disable=too-many-instance-attributes, too-fe
|
|
69
72
|
self.polylines_version = None
|
70
73
|
|
71
74
|
|
75
|
+
@dataclasses.dataclass
|
76
|
+
class ScheduledTask:
|
77
|
+
task: Thread | None
|
78
|
+
last_run: float | None = None
|
79
|
+
|
80
|
+
|
81
|
+
class SimulationNotFoundError(Exception):
|
82
|
+
def __init__(self, simulation_id: str):
|
83
|
+
super().__init__(f"Simulation {simulation_id} not found")
|
84
|
+
|
85
|
+
|
86
|
+
class MultipleSimulationsMatchingSocketIdError(Exception):
|
87
|
+
def __init__(self, socket_id: str, simulation_ids: list[str]):
|
88
|
+
super().__init__(f"Multiple simulations matching socket id {socket_id} : {', '.join(simulation_ids)}")
|
89
|
+
|
90
|
+
|
91
|
+
# MARK: SimulationManager
|
72
92
|
class SimulationManager:
|
73
|
-
|
93
|
+
def __init__(self, socketio: SocketIO):
|
94
|
+
self.socketio = socketio
|
74
95
|
|
75
|
-
def __init__(self):
|
76
96
|
self.simulations = {}
|
77
97
|
|
78
|
-
|
79
|
-
|
80
|
-
|
98
|
+
self.task_by_simulation_id: dict[str, ScheduledTask] = {}
|
99
|
+
|
100
|
+
self.lock = Lock()
|
101
|
+
|
102
|
+
# MARK: +- Multi-threading
|
103
|
+
def __add_simulation_handler(self, simulation_handler: SimulationHandler) -> None:
|
104
|
+
with self.lock:
|
105
|
+
self.simulations[simulation_handler.simulation_id] = simulation_handler
|
106
|
+
|
107
|
+
def __set_socket_id(self, simulation_id: str, socket_id: str) -> None:
|
108
|
+
with self.lock:
|
109
|
+
if simulation_id not in self.simulations:
|
110
|
+
raise SimulationNotFoundError(simulation_id)
|
111
|
+
|
112
|
+
self.simulations[simulation_id].socket_id = socket_id
|
113
|
+
|
114
|
+
def __set_status(self, simulation_id: str, status: SimulationStatus) -> None:
|
115
|
+
with self.lock:
|
116
|
+
if simulation_id not in self.simulations:
|
117
|
+
raise SimulationNotFoundError(simulation_id)
|
118
|
+
|
119
|
+
self.simulations[simulation_id].status = status
|
120
|
+
|
121
|
+
def __set_start_time(self, simulation_id: str, start_time: float) -> None:
|
122
|
+
with self.lock:
|
123
|
+
if simulation_id not in self.simulations:
|
124
|
+
raise SimulationNotFoundError(simulation_id)
|
125
|
+
|
126
|
+
self.simulations[simulation_id].start_time = start_time
|
127
|
+
|
128
|
+
def __set_max_duration(self, simulation_id: str, max_duration: float | None) -> None:
|
129
|
+
with self.lock:
|
130
|
+
if simulation_id not in self.simulations:
|
131
|
+
raise SimulationNotFoundError(simulation_id)
|
132
|
+
|
133
|
+
self.simulations[simulation_id].max_duration = max_duration
|
134
|
+
|
135
|
+
def __set_simulation_time(self, simulation_id: str, simulation_time: float | None) -> None:
|
136
|
+
with self.lock:
|
137
|
+
if simulation_id not in self.simulations:
|
138
|
+
raise SimulationNotFoundError(simulation_id)
|
139
|
+
|
140
|
+
self.simulations[simulation_id].simulation_time = simulation_time
|
141
|
+
|
142
|
+
def __set_simulation_estimated_end_time(
|
143
|
+
self, simulation_id: str, simulation_estimated_end_time: float | None
|
144
|
+
) -> None:
|
145
|
+
with self.lock:
|
146
|
+
if simulation_id not in self.simulations:
|
147
|
+
raise SimulationNotFoundError(simulation_id)
|
148
|
+
|
149
|
+
self.simulations[simulation_id].simulation_estimated_end_time = simulation_estimated_end_time
|
150
|
+
|
151
|
+
def __set_polylines_version(self, simulation_id: str, polylines_version: int | None) -> None:
|
152
|
+
with self.lock:
|
153
|
+
if simulation_id not in self.simulations:
|
154
|
+
raise SimulationNotFoundError(simulation_id)
|
155
|
+
|
156
|
+
self.simulations[simulation_id].polylines_version = polylines_version
|
157
|
+
|
158
|
+
def __set_name(self, simulation_id: str, name: str) -> None:
|
159
|
+
with self.lock:
|
160
|
+
if simulation_id not in self.simulations:
|
161
|
+
raise SimulationNotFoundError(simulation_id)
|
162
|
+
|
163
|
+
self.simulations[simulation_id].name = name
|
164
|
+
|
165
|
+
def __set_data(self, simulation_id: str, data: str) -> None:
|
166
|
+
with self.lock:
|
167
|
+
if simulation_id not in self.simulations:
|
168
|
+
raise SimulationNotFoundError(simulation_id)
|
169
|
+
|
170
|
+
self.simulations[simulation_id].data = data
|
171
|
+
|
172
|
+
def __set_simulation_start_time(self, simulation_id: str, simulation_start_time: float) -> None:
|
173
|
+
with self.lock:
|
174
|
+
if simulation_id not in self.simulations:
|
175
|
+
raise SimulationNotFoundError(simulation_id)
|
176
|
+
|
177
|
+
self.simulations[simulation_id].simulation_start_time = simulation_start_time
|
178
|
+
|
179
|
+
def __set_size(self, simulation_id: str, size: int) -> None:
|
180
|
+
with self.lock:
|
181
|
+
if simulation_id not in self.simulations:
|
182
|
+
raise SimulationNotFoundError(simulation_id)
|
183
|
+
|
184
|
+
self.simulations[simulation_id].size = size
|
185
|
+
|
186
|
+
def __get_socket_id(self, simulation_id: str) -> str | None:
|
187
|
+
with self.lock:
|
188
|
+
if simulation_id in self.simulations:
|
189
|
+
return self.simulations[simulation_id].socket_id
|
190
|
+
raise SimulationNotFoundError(simulation_id)
|
191
|
+
|
192
|
+
def __get_status(self, simulation_id: str) -> SimulationStatus:
|
193
|
+
with self.lock:
|
194
|
+
if simulation_id in self.simulations:
|
195
|
+
return self.simulations[simulation_id].status
|
196
|
+
raise SimulationNotFoundError(simulation_id)
|
197
|
+
|
198
|
+
def __get_status_if_exists(self, simulation_id: str) -> SimulationStatus | None:
|
199
|
+
with self.lock:
|
200
|
+
if simulation_id in self.simulations:
|
201
|
+
return self.simulations[simulation_id].status
|
202
|
+
return None
|
203
|
+
|
204
|
+
def __get_matching_simulation_id_by_socket_id(self, socket_id: str) -> str | None:
|
205
|
+
with self.lock:
|
206
|
+
matching_simulation_ids = [
|
207
|
+
simulation_id
|
208
|
+
for simulation_id, simulation in self.simulations.items()
|
209
|
+
if simulation.socket_id == socket_id
|
210
|
+
]
|
211
|
+
|
212
|
+
if len(matching_simulation_ids) == 1:
|
213
|
+
return matching_simulation_ids[0]
|
214
|
+
|
215
|
+
if len(matching_simulation_ids) > 1:
|
216
|
+
raise MultipleSimulationsMatchingSocketIdError(socket_id, matching_simulation_ids)
|
217
|
+
return None
|
218
|
+
|
219
|
+
def __get_all_simulation_ids(self) -> list[str]:
|
220
|
+
with self.lock:
|
221
|
+
return list(self.simulations.keys())
|
222
|
+
|
223
|
+
def __get_serialized_simulation(self, simulation_id: str) -> dict:
|
224
|
+
with self.lock:
|
225
|
+
if simulation_id not in self.simulations:
|
226
|
+
raise SimulationNotFoundError(simulation_id)
|
227
|
+
|
228
|
+
simulation = self.simulations[simulation_id]
|
229
|
+
|
230
|
+
serialized_simulation = {
|
231
|
+
"id": simulation_id,
|
232
|
+
"name": simulation.name,
|
233
|
+
"status": simulation.status.value,
|
234
|
+
"startTime": simulation.start_time,
|
235
|
+
"data": simulation.data,
|
236
|
+
}
|
237
|
+
|
238
|
+
if simulation.simulation_start_time is not None:
|
239
|
+
serialized_simulation["simulationStartTime"] = simulation.simulation_start_time
|
240
|
+
|
241
|
+
if simulation.simulation_end_time is not None:
|
242
|
+
serialized_simulation["simulationEndTime"] = simulation.simulation_end_time
|
243
|
+
|
244
|
+
if simulation.simulation_time is not None:
|
245
|
+
serialized_simulation["simulationTime"] = simulation.simulation_time
|
246
|
+
|
247
|
+
if simulation.simulation_estimated_end_time is not None:
|
248
|
+
serialized_simulation["simulationEstimatedEndTime"] = simulation.simulation_estimated_end_time
|
249
|
+
|
250
|
+
if simulation.max_duration is not None:
|
251
|
+
serialized_simulation["configuration"] = {"maxDuration": simulation.max_duration}
|
252
|
+
|
253
|
+
if simulation.polylines_version is not None:
|
254
|
+
serialized_simulation["polylinesVersion"] = simulation.polylines_version
|
255
|
+
|
256
|
+
if simulation.size is not None:
|
257
|
+
serialized_simulation["size"] = simulation.size
|
258
|
+
|
259
|
+
return serialized_simulation
|
260
|
+
|
261
|
+
def __does_simulation_exist(self, simulation_id: str) -> bool:
|
262
|
+
with self.lock:
|
263
|
+
return simulation_id in self.simulations
|
264
|
+
|
265
|
+
def __delete_simulation_handler_if_exists(self, simulation_id: str) -> None:
|
266
|
+
with self.lock:
|
267
|
+
if simulation_id in self.simulations:
|
268
|
+
del self.simulations[simulation_id]
|
269
|
+
|
270
|
+
# MARK: +- Simulation control
|
271
|
+
def start_simulation(self, name: str, data: str, response_event: str, max_duration: float | None):
|
81
272
|
simulation_id, start_time = build_simulation_id(name)
|
82
273
|
|
83
274
|
simulation_process = multiprocessing.Process(
|
@@ -96,199 +287,194 @@ class SimulationManager:
|
|
96
287
|
simulation_process,
|
97
288
|
)
|
98
289
|
|
99
|
-
self.
|
290
|
+
self.__add_simulation_handler(simulation_handler)
|
100
291
|
|
101
292
|
simulation_process.start()
|
102
293
|
|
103
|
-
self.emit_simulations()
|
104
|
-
|
105
294
|
log(f'Emitting response event "{response_event}"', "server")
|
106
|
-
emit(response_event, simulation_id, to=CLIENT_ROOM)
|
107
295
|
|
108
|
-
|
296
|
+
self.socketio.emit(response_event, simulation_id, to=CLIENT_ROOM)
|
109
297
|
|
110
|
-
|
111
|
-
if simulation_id not in self.simulations:
|
112
|
-
log(
|
113
|
-
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
114
|
-
"server",
|
115
|
-
logging.ERROR,
|
116
|
-
)
|
117
|
-
return
|
298
|
+
log(f"Started simulation {simulation_id}", "server")
|
118
299
|
|
119
|
-
|
300
|
+
self.emit_simulation(simulation_id)
|
301
|
+
|
302
|
+
def stop_simulation(self, simulation_id):
|
303
|
+
try:
|
304
|
+
self.__set_status(simulation_id, SimulationStatus.STOPPING)
|
120
305
|
|
121
|
-
|
122
|
-
simulation.status = SimulationStatus.RUNNING
|
123
|
-
simulation.simulation_start_time = simulation_start_time
|
306
|
+
self.socketio.emit("stop-simulation", to=self.__get_socket_id(simulation_id))
|
124
307
|
|
125
|
-
|
308
|
+
log(f"Stopping simulation {simulation_id}", "server")
|
126
309
|
|
127
|
-
|
128
|
-
|
310
|
+
self.emit_simulation(simulation_id)
|
311
|
+
except SimulationNotFoundError:
|
129
312
|
log(
|
130
313
|
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
131
314
|
"server",
|
132
315
|
logging.ERROR,
|
133
316
|
)
|
134
|
-
return
|
135
|
-
|
136
|
-
simulation = self.simulations[simulation_id]
|
137
|
-
simulation.status = SimulationStatus.STOPPING
|
138
|
-
|
139
|
-
emit("stop-simulation", to=simulation.socket_id)
|
140
317
|
|
141
318
|
def pause_simulation(self, simulation_id):
|
142
|
-
|
319
|
+
try:
|
320
|
+
self.socketio.emit("pause-simulation", to=self.__get_socket_id(simulation_id))
|
321
|
+
|
322
|
+
log(f"Pausing simulation {simulation_id}", "server")
|
323
|
+
except SimulationNotFoundError:
|
143
324
|
log(
|
144
325
|
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
145
326
|
"server",
|
146
327
|
logging.ERROR,
|
147
328
|
)
|
148
|
-
return
|
149
|
-
|
150
|
-
simulation = self.simulations[simulation_id]
|
151
329
|
|
152
|
-
|
330
|
+
def resume_simulation(self, simulation_id):
|
331
|
+
try:
|
332
|
+
self.socketio.emit("resume-simulation", to=self.__get_socket_id(simulation_id))
|
153
333
|
|
154
|
-
|
155
|
-
|
334
|
+
log(f"Resuming simulation {simulation_id}", "server")
|
335
|
+
except SimulationNotFoundError:
|
156
336
|
log(
|
157
337
|
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
158
338
|
"server",
|
159
339
|
logging.ERROR,
|
160
340
|
)
|
161
|
-
return
|
162
341
|
|
163
|
-
|
342
|
+
def edit_simulation_configuration(self, simulation_id: str, max_duration: float | None) -> None:
|
343
|
+
try:
|
344
|
+
self.__set_max_duration(simulation_id, max_duration)
|
164
345
|
|
165
|
-
|
346
|
+
self.socketio.emit("edit-simulation-configuration", (max_duration,), to=self.__get_socket_id(simulation_id))
|
166
347
|
|
167
|
-
|
348
|
+
log(f"Edited simulation {simulation_id} configuration", "server")
|
168
349
|
|
169
|
-
|
170
|
-
|
350
|
+
self.emit_simulation(simulation_id)
|
351
|
+
except SimulationNotFoundError:
|
171
352
|
log(
|
172
353
|
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
173
354
|
"server",
|
174
355
|
logging.ERROR,
|
175
356
|
)
|
176
|
-
return
|
177
357
|
|
178
|
-
|
358
|
+
# MARK: +- Simulation events
|
359
|
+
def on_simulation_start(self, simulation_id, socket_id, simulation_start_time):
|
360
|
+
try:
|
361
|
+
self.__set_socket_id(simulation_id, socket_id)
|
362
|
+
self.__set_status(simulation_id, SimulationStatus.RUNNING)
|
363
|
+
self.__set_simulation_start_time(simulation_id, simulation_start_time)
|
179
364
|
|
180
|
-
|
365
|
+
log(f"Simulation {simulation_id} started", "server")
|
181
366
|
|
182
|
-
|
183
|
-
|
367
|
+
self.emit_simulation(simulation_id)
|
368
|
+
except SimulationNotFoundError:
|
184
369
|
log(
|
185
370
|
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
186
371
|
"server",
|
187
372
|
logging.ERROR,
|
188
373
|
)
|
189
|
-
return
|
190
|
-
|
191
|
-
simulation = self.simulations[simulation_id]
|
192
374
|
|
193
|
-
|
375
|
+
def on_simulation_pause(self, simulation_id):
|
376
|
+
try:
|
377
|
+
self.__set_status(simulation_id, SimulationStatus.PAUSED)
|
194
378
|
|
195
|
-
|
379
|
+
log(f"Simulation {simulation_id} paused", "server")
|
196
380
|
|
197
|
-
|
198
|
-
|
381
|
+
self.emit_simulation(simulation_id)
|
382
|
+
except SimulationNotFoundError:
|
199
383
|
log(
|
200
384
|
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
201
385
|
"server",
|
202
386
|
logging.ERROR,
|
203
387
|
)
|
204
|
-
return
|
205
388
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
emit("edit-simulation-configuration", (max_duration,), to=simulation.socket_id)
|
389
|
+
def on_simulation_resume(self, simulation_id):
|
390
|
+
try:
|
391
|
+
self.__set_status(simulation_id, SimulationStatus.RUNNING)
|
211
392
|
|
212
|
-
|
393
|
+
log(f"Simulation {simulation_id} resumed", "server")
|
213
394
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
395
|
+
self.emit_simulation(simulation_id)
|
396
|
+
except SimulationNotFoundError:
|
397
|
+
log(
|
398
|
+
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
399
|
+
"server",
|
400
|
+
logging.ERROR,
|
401
|
+
)
|
219
402
|
|
220
403
|
def on_simulation_disconnect(self, socket_id):
|
221
|
-
|
222
|
-
simulation_id
|
223
|
-
]
|
224
|
-
|
225
|
-
if len(matching_simulation_ids) != 1:
|
226
|
-
# The simulation has already been disconnected properly
|
227
|
-
return
|
404
|
+
try:
|
405
|
+
simulation_id = self.__get_matching_simulation_id_by_socket_id(socket_id)
|
228
406
|
|
229
|
-
|
407
|
+
if simulation_id is None:
|
408
|
+
# Simulation already disconnected properly
|
409
|
+
return
|
230
410
|
|
231
|
-
|
232
|
-
simulation_information = SimulationVisualizationDataManager.get_simulation_information(simulation_id)
|
411
|
+
status = self.__get_status(simulation_id)
|
233
412
|
|
234
|
-
|
413
|
+
simulation_information = SimulationVisualizationDataManager.get_simulation_information(simulation_id)
|
235
414
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
415
|
+
if status in RUNNING_SIMULATION_STATUSES:
|
416
|
+
if simulation_information.simulation_end_time is None:
|
417
|
+
# The simulation has been lost
|
418
|
+
self.__set_status(simulation_id, SimulationStatus.LOST)
|
419
|
+
else:
|
420
|
+
# The simulation has been completed
|
421
|
+
self.__set_status(simulation_id, SimulationStatus.COMPLETED)
|
243
422
|
|
244
|
-
|
423
|
+
self.__set_socket_id(simulation_id, None)
|
245
424
|
|
246
|
-
|
425
|
+
log(f"Simulation {simulation_id} disconnected", "server")
|
247
426
|
|
248
|
-
|
249
|
-
|
427
|
+
self.emit_simulation(simulation_id)
|
428
|
+
except SimulationNotFoundError:
|
250
429
|
log(
|
251
|
-
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {
|
430
|
+
f"{__file__} {inspect.currentframe().f_lineno}: Simulation with socket ID {socket_id} not found",
|
252
431
|
"server",
|
253
432
|
logging.ERROR,
|
254
433
|
)
|
255
|
-
return
|
256
434
|
|
257
|
-
|
258
|
-
|
259
|
-
|
435
|
+
def on_simulation_update_time(self, simulation_id, timestamp):
|
436
|
+
try:
|
437
|
+
self.__set_simulation_time(simulation_id, timestamp)
|
260
438
|
|
261
|
-
|
439
|
+
log(f"Simulation {simulation_id} time updated to {timestamp}", "server")
|
262
440
|
|
263
|
-
|
264
|
-
|
441
|
+
self.emit_simulation(simulation_id)
|
442
|
+
except SimulationNotFoundError:
|
265
443
|
log(
|
266
444
|
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
267
445
|
"server",
|
268
446
|
logging.ERROR,
|
269
447
|
)
|
270
|
-
return
|
271
|
-
|
272
|
-
simulation = self.simulations[simulation_id]
|
273
448
|
|
274
|
-
|
449
|
+
def on_simulation_update_estimated_end_time(self, simulation_id, estimated_end_time):
|
450
|
+
try:
|
451
|
+
self.__set_simulation_estimated_end_time(simulation_id, estimated_end_time)
|
275
452
|
|
276
|
-
|
453
|
+
log(f"Simulation {simulation_id} estimated end time updated to {estimated_end_time}", "server")
|
277
454
|
|
278
|
-
|
279
|
-
|
455
|
+
self.emit_simulation(simulation_id)
|
456
|
+
except SimulationNotFoundError:
|
280
457
|
log(
|
281
458
|
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
282
459
|
"server",
|
283
460
|
logging.ERROR,
|
284
461
|
)
|
285
|
-
return
|
286
462
|
|
287
|
-
|
463
|
+
def on_simulation_update_polylines_version(self, simulation_id):
|
464
|
+
try:
|
465
|
+
self.__set_polylines_version(
|
466
|
+
simulation_id, SimulationVisualizationDataManager.get_polylines_version_with_lock(simulation_id)
|
467
|
+
)
|
288
468
|
|
289
|
-
|
469
|
+
log(f"Simulation {simulation_id} polylines version updated", "server")
|
290
470
|
|
291
|
-
|
471
|
+
self.emit_simulation(simulation_id)
|
472
|
+
except SimulationNotFoundError:
|
473
|
+
log(
|
474
|
+
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
475
|
+
"server",
|
476
|
+
logging.ERROR,
|
477
|
+
)
|
292
478
|
|
293
479
|
def on_simulation_identification( # pylint: disable=too-many-arguments, too-many-positional-arguments
|
294
480
|
self,
|
@@ -301,89 +487,66 @@ class SimulationManager:
|
|
301
487
|
status,
|
302
488
|
socket_id,
|
303
489
|
):
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
)
|
309
|
-
|
310
|
-
start_time, name = simulation_id.split(SIMULATION_SAVE_FILE_SEPARATOR)
|
311
|
-
|
312
|
-
if simulation_id in self.simulations:
|
313
|
-
simulation = self.simulations[simulation_id]
|
314
|
-
else:
|
315
|
-
start_time, name = simulation_id.split(SIMULATION_SAVE_FILE_SEPARATOR)
|
316
|
-
|
317
|
-
simulation = SimulationHandler(
|
318
|
-
simulation_id,
|
319
|
-
name,
|
320
|
-
start_time,
|
321
|
-
data,
|
322
|
-
SimulationStatus(status),
|
323
|
-
max_duration,
|
324
|
-
None,
|
490
|
+
try:
|
491
|
+
log(
|
492
|
+
f"Identifying simulation {simulation_id}",
|
493
|
+
"simulation",
|
325
494
|
)
|
326
495
|
|
327
|
-
|
328
|
-
|
329
|
-
simulation.name = name
|
330
|
-
simulation.start_time = start_time
|
331
|
-
simulation.data = data
|
332
|
-
simulation.simulation_start_time = simulation_start_time
|
333
|
-
simulation.simulation_time = simulation_time
|
334
|
-
simulation.simulation_estimated_end_time = simulation_estimated_end_time
|
335
|
-
simulation.max_duration = max_duration
|
336
|
-
simulation.status = SimulationStatus(status)
|
337
|
-
simulation.socket_id = socket_id
|
338
|
-
|
339
|
-
simulation.polylines_version = SimulationVisualizationDataManager.get_polylines_version_with_lock(simulation_id)
|
340
|
-
|
341
|
-
self.emit_simulations()
|
342
|
-
|
343
|
-
def emit_simulations(self):
|
344
|
-
self.query_simulations()
|
345
|
-
|
346
|
-
serialized_simulations = []
|
347
|
-
|
348
|
-
for simulation_id, simulation in self.simulations.items():
|
349
|
-
serialized_simulation = {
|
350
|
-
"id": simulation_id,
|
351
|
-
"name": simulation.name,
|
352
|
-
"status": simulation.status.value,
|
353
|
-
"startTime": simulation.start_time,
|
354
|
-
"data": simulation.data,
|
355
|
-
}
|
356
|
-
|
357
|
-
if simulation.simulation_start_time is not None:
|
358
|
-
serialized_simulation["simulationStartTime"] = simulation.simulation_start_time
|
359
|
-
|
360
|
-
if simulation.simulation_end_time is not None:
|
361
|
-
serialized_simulation["simulationEndTime"] = simulation.simulation_end_time
|
496
|
+
start_time, name = simulation_id.split(SIMULATION_SAVE_FILE_SEPARATOR)
|
362
497
|
|
363
|
-
if
|
364
|
-
|
498
|
+
if not self.__does_simulation_exist(simulation_id):
|
499
|
+
start_time, name = simulation_id.split(SIMULATION_SAVE_FILE_SEPARATOR)
|
365
500
|
|
366
|
-
|
367
|
-
|
501
|
+
simulation = SimulationHandler(
|
502
|
+
simulation_id,
|
503
|
+
name,
|
504
|
+
start_time,
|
505
|
+
data,
|
506
|
+
SimulationStatus(status),
|
507
|
+
max_duration,
|
508
|
+
None,
|
509
|
+
)
|
368
510
|
|
369
|
-
|
370
|
-
|
511
|
+
self.__add_simulation_handler(simulation)
|
512
|
+
|
513
|
+
self.__set_name(simulation_id, name)
|
514
|
+
self.__set_start_time(simulation_id, start_time)
|
515
|
+
self.__set_data(simulation_id, data)
|
516
|
+
self.__set_simulation_start_time(simulation_id, simulation_start_time)
|
517
|
+
self.__set_simulation_time(simulation_id, simulation_time)
|
518
|
+
self.__set_simulation_estimated_end_time(simulation_id, simulation_estimated_end_time)
|
519
|
+
self.__set_max_duration(simulation_id, max_duration)
|
520
|
+
self.__set_status(simulation_id, SimulationStatus(status))
|
521
|
+
self.__set_socket_id(simulation_id, socket_id)
|
522
|
+
self.__set_polylines_version(
|
523
|
+
simulation_id, SimulationVisualizationDataManager.get_polylines_version_with_lock(simulation_id)
|
524
|
+
)
|
371
525
|
|
372
|
-
|
373
|
-
|
526
|
+
self.emit_simulation(simulation_id)
|
527
|
+
except SimulationNotFoundError as error:
|
528
|
+
print(error)
|
529
|
+
log(
|
530
|
+
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
531
|
+
"server",
|
532
|
+
logging.ERROR,
|
533
|
+
)
|
374
534
|
|
375
|
-
|
376
|
-
|
535
|
+
# MARK: +- Visualization
|
536
|
+
def emit_simulation_polylines(self, simulation_id):
|
537
|
+
if not self.__does_simulation_exist(simulation_id):
|
538
|
+
log(
|
539
|
+
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
540
|
+
"server",
|
541
|
+
logging.ERROR,
|
542
|
+
)
|
543
|
+
return
|
377
544
|
|
378
|
-
|
545
|
+
polylines, version = SimulationVisualizationDataManager.get_polylines(simulation_id)
|
379
546
|
|
380
|
-
emit(
|
381
|
-
"simulations",
|
382
|
-
serialized_simulations,
|
383
|
-
to=CLIENT_ROOM,
|
384
|
-
)
|
547
|
+
self.socketio.emit(f"polylines-{simulation_id}", (polylines, version), to=CLIENT_ROOM)
|
385
548
|
|
386
|
-
log("
|
549
|
+
log(f"Emitted polylines for simulation {simulation_id}", "server")
|
387
550
|
|
388
551
|
def emit_missing_simulation_states(
|
389
552
|
self,
|
@@ -391,30 +554,28 @@ class SimulationManager:
|
|
391
554
|
visualization_time: float,
|
392
555
|
complete_state_update_indexes: list[int],
|
393
556
|
) -> None:
|
394
|
-
if simulation_id not in self.simulations:
|
395
|
-
log(
|
396
|
-
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
397
|
-
"server",
|
398
|
-
logging.ERROR,
|
399
|
-
)
|
400
|
-
return
|
401
|
-
|
402
|
-
simulation = self.simulations[simulation_id]
|
403
557
|
|
404
558
|
try:
|
405
559
|
(missing_states, missing_updates, has_all_states) = SimulationVisualizationDataManager.get_missing_states(
|
406
560
|
simulation_id,
|
407
561
|
visualization_time,
|
408
562
|
complete_state_update_indexes,
|
409
|
-
|
563
|
+
self.__get_status(simulation_id) not in RUNNING_SIMULATION_STATUSES,
|
410
564
|
)
|
411
565
|
|
412
|
-
emit(
|
566
|
+
self.socketio.emit(
|
413
567
|
"missing-simulation-states",
|
414
568
|
(missing_states, missing_updates, has_all_states),
|
415
569
|
to=get_session_id(),
|
416
570
|
)
|
417
571
|
|
572
|
+
except SimulationNotFoundError:
|
573
|
+
log(
|
574
|
+
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
575
|
+
"server",
|
576
|
+
logging.ERROR,
|
577
|
+
)
|
578
|
+
return
|
418
579
|
except Exception as e: # pylint: disable=broad-exception-caught
|
419
580
|
log(
|
420
581
|
f"Error while emitting missing simulation states for {simulation_id}: {e}",
|
@@ -427,52 +588,120 @@ class SimulationManager:
|
|
427
588
|
logging.ERROR,
|
428
589
|
)
|
429
590
|
|
430
|
-
|
591
|
+
self.__set_status(simulation_id, SimulationStatus.CORRUPTED)
|
431
592
|
|
432
593
|
SimulationVisualizationDataManager.mark_simulation_as_corrupted(simulation_id)
|
433
594
|
|
434
|
-
self.
|
595
|
+
self.emit_simulation(simulation_id)
|
435
596
|
|
436
|
-
|
437
|
-
|
597
|
+
# MARK: +- Simulation list
|
598
|
+
def on_simulation_delete(self, simulation_id: str) -> None:
|
599
|
+
self.__delete_simulation_handler_if_exists(simulation_id)
|
600
|
+
|
601
|
+
self.socketio.emit("delete-simulation", simulation_id, to=CLIENT_ROOM)
|
602
|
+
|
603
|
+
log(f"Deleted simulation {simulation_id} from simulation manager", "server")
|
604
|
+
|
605
|
+
def emit_simulations(self, loaded_simulations_ids: list[str]):
|
606
|
+
try:
|
607
|
+
all_simulation_ids = SimulationVisualizationDataManager.get_all_saved_simulation_ids()
|
608
|
+
|
609
|
+
log("Emitting simulations", "server")
|
610
|
+
|
611
|
+
simulation_ids_to_delete: set[str] = set()
|
612
|
+
|
613
|
+
for simulation_id in self.__get_all_simulation_ids():
|
614
|
+
if simulation_id not in all_simulation_ids and self.__get_status(simulation_id) not in [
|
615
|
+
SimulationStatus.RUNNING,
|
616
|
+
SimulationStatus.PAUSED,
|
617
|
+
SimulationStatus.STOPPING,
|
618
|
+
SimulationStatus.STARTING,
|
619
|
+
SimulationStatus.LOST,
|
620
|
+
]:
|
621
|
+
simulation_ids_to_delete.add(simulation_id)
|
622
|
+
|
623
|
+
for loaded_simulation_id in loaded_simulations_ids:
|
624
|
+
if not loaded_simulation_id in all_simulation_ids:
|
625
|
+
simulation_ids_to_delete.add(loaded_simulation_id)
|
626
|
+
|
627
|
+
for simulation_id in simulation_ids_to_delete:
|
628
|
+
self.on_simulation_delete(simulation_id)
|
629
|
+
|
630
|
+
for simulation_id in all_simulation_ids:
|
631
|
+
self.emit_simulation(simulation_id)
|
632
|
+
|
633
|
+
log("Emitted simulations", "server")
|
634
|
+
except SimulationNotFoundError:
|
438
635
|
log(
|
439
|
-
f"{__file__} {inspect.currentframe().f_lineno}:
|
636
|
+
f"{__file__} {inspect.currentframe().f_lineno}: One or more simulations not found",
|
440
637
|
"server",
|
441
638
|
logging.ERROR,
|
442
639
|
)
|
443
|
-
return
|
444
640
|
|
445
|
-
|
641
|
+
def emit_simulation(self, simulation_id: str) -> None:
|
642
|
+
scheduled_task = self.task_by_simulation_id.get(simulation_id, None)
|
643
|
+
if scheduled_task is None:
|
644
|
+
scheduled_task = ScheduledTask(None)
|
645
|
+
self.task_by_simulation_id[simulation_id] = scheduled_task
|
646
|
+
|
647
|
+
minimum_debounce_time = 1
|
648
|
+
actual_debounce_time = 0
|
649
|
+
now = time.monotonic()
|
650
|
+
|
651
|
+
if scheduled_task.last_run is not None and scheduled_task.last_run + minimum_debounce_time > now:
|
652
|
+
actual_debounce_time = scheduled_task.last_run + minimum_debounce_time - now
|
446
653
|
|
447
|
-
|
654
|
+
def action():
|
655
|
+
self.socketio.sleep(actual_debounce_time)
|
656
|
+
|
657
|
+
try:
|
658
|
+
self.query_simulation(simulation_id)
|
448
659
|
|
449
|
-
|
450
|
-
all_simulation_ids = SimulationVisualizationDataManager.get_all_saved_simulation_ids()
|
660
|
+
log(f"Emitting simulation {simulation_id}", "server")
|
451
661
|
|
452
|
-
|
453
|
-
|
662
|
+
serialized_simulation = self.__get_serialized_simulation(simulation_id)
|
663
|
+
|
664
|
+
self.socketio.emit("simulation", serialized_simulation, to=CLIENT_ROOM)
|
665
|
+
|
666
|
+
log(f"Emitted simulation {simulation_id}", "server")
|
667
|
+
|
668
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
669
|
+
log(
|
670
|
+
f"Error while emitting simulation {simulation_id}: {e}",
|
671
|
+
"server",
|
672
|
+
logging.ERROR,
|
673
|
+
)
|
674
|
+
finally:
|
675
|
+
scheduled_task.last_run = time.monotonic()
|
676
|
+
scheduled_task.task = None
|
677
|
+
|
678
|
+
if scheduled_task.task is None:
|
679
|
+
if actual_debounce_time > 0:
|
680
|
+
scheduled_task.task = self.socketio.start_background_task(action)
|
681
|
+
log(f"Scheduled emit of simulation {simulation_id} in {actual_debounce_time}s", "server")
|
682
|
+
else:
|
683
|
+
action()
|
684
|
+
log(f"Emitted simulation {simulation_id} immediately", "server")
|
685
|
+
else:
|
686
|
+
log(f"Simulation {simulation_id} is already scheduled", "server")
|
687
|
+
|
688
|
+
def query_simulation(self, simulation_id) -> None:
|
689
|
+
log(f"Querying simulation {simulation_id}", "server")
|
690
|
+
|
691
|
+
try:
|
692
|
+
if self.__get_status_if_exists(simulation_id) in [
|
454
693
|
SimulationStatus.RUNNING,
|
455
694
|
SimulationStatus.PAUSED,
|
456
695
|
SimulationStatus.STOPPING,
|
457
696
|
SimulationStatus.STARTING,
|
458
697
|
SimulationStatus.LOST,
|
459
698
|
]:
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
def query_simulation(self, simulation_id) -> None:
|
467
|
-
if simulation_id in self.simulations and self.simulations[simulation_id].status in [
|
468
|
-
SimulationStatus.RUNNING,
|
469
|
-
SimulationStatus.PAUSED,
|
470
|
-
SimulationStatus.STOPPING,
|
471
|
-
SimulationStatus.STARTING,
|
472
|
-
SimulationStatus.LOST,
|
473
|
-
]:
|
474
|
-
simulation = self.simulations[simulation_id]
|
475
|
-
simulation.size = SimulationVisualizationDataManager.get_saved_simulation_size(simulation_id)
|
699
|
+
self.__set_size(
|
700
|
+
simulation_id, SimulationVisualizationDataManager.get_saved_simulation_size(simulation_id)
|
701
|
+
)
|
702
|
+
return
|
703
|
+
except SimulationNotFoundError:
|
704
|
+
log(f"Simulation {simulation_id} not found", "server", logging.ERROR)
|
476
705
|
return
|
477
706
|
|
478
707
|
is_corrupted = SimulationVisualizationDataManager.is_simulation_corrupted(simulation_id)
|
@@ -499,13 +728,11 @@ class SimulationManager:
|
|
499
728
|
log(
|
500
729
|
f"Simulation {simulation_id} version is outdated",
|
501
730
|
"server",
|
502
|
-
logging.DEBUG,
|
503
731
|
)
|
504
732
|
if status == SimulationStatus.FUTURE:
|
505
733
|
log(
|
506
734
|
f"Simulation {simulation_id} version is future",
|
507
735
|
"server",
|
508
|
-
logging.DEBUG,
|
509
736
|
)
|
510
737
|
|
511
738
|
simulation = SimulationHandler(
|
@@ -529,13 +756,13 @@ class SimulationManager:
|
|
529
756
|
# The simulation is not running but the end time is not set
|
530
757
|
raise Exception("Simulation is corrupted") # pylint: disable=broad-exception-raised
|
531
758
|
|
532
|
-
self.
|
759
|
+
self.__add_simulation_handler(simulation)
|
533
760
|
|
534
761
|
except Exception: # pylint: disable=broad-exception-caught
|
535
762
|
is_corrupted = True
|
536
763
|
|
537
764
|
if is_corrupted:
|
538
|
-
log(f"Simulation {simulation_id} is corrupted", "server"
|
765
|
+
log(f"Simulation {simulation_id} is corrupted", "server")
|
539
766
|
|
540
767
|
simulation = SimulationHandler(
|
541
768
|
simulation_id,
|
@@ -547,23 +774,8 @@ class SimulationManager:
|
|
547
774
|
None,
|
548
775
|
)
|
549
776
|
|
550
|
-
self.
|
777
|
+
self.__add_simulation_handler(simulation)
|
551
778
|
|
552
779
|
SimulationVisualizationDataManager.mark_simulation_as_corrupted(simulation_id)
|
553
|
-
|
554
|
-
|
555
|
-
if simulation_id not in self.simulations:
|
556
|
-
log(
|
557
|
-
f"{__file__} {inspect.currentframe().f_lineno}: Simulation {simulation_id} not found",
|
558
|
-
"server",
|
559
|
-
logging.ERROR,
|
560
|
-
)
|
561
|
-
return
|
562
|
-
|
563
|
-
states, updates = SimulationVisualizationDataManager.get_all_simulation_states(simulation_id)
|
564
|
-
|
565
|
-
emit(
|
566
|
-
"all-simulation-states",
|
567
|
-
(states, updates),
|
568
|
-
to=CLIENT_ROOM,
|
569
|
-
)
|
780
|
+
else:
|
781
|
+
log(f"Simulation {simulation_id} queried successfully", "server")
|