multimodalsim-viewer 0.0.3__py3-none-any.whl → 0.1.0.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/environments/.env +2 -0
- multimodalsim_viewer/common/utils.py +11 -48
- multimodalsim_viewer/models/__init__.py +0 -0
- multimodalsim_viewer/models/environment.py +70 -0
- multimodalsim_viewer/models/leg.py +194 -0
- multimodalsim_viewer/models/passenger.py +148 -0
- multimodalsim_viewer/models/serializable.py +43 -0
- multimodalsim_viewer/models/simulation_information.py +84 -0
- multimodalsim_viewer/models/state.py +44 -0
- multimodalsim_viewer/models/stop.py +114 -0
- multimodalsim_viewer/models/update.py +616 -0
- multimodalsim_viewer/models/vehicle.py +151 -0
- multimodalsim_viewer/server/{simulation_visualization_data_collector.py → data_collector.py} +185 -198
- multimodalsim_viewer/server/data_manager.py +567 -0
- multimodalsim_viewer/server/http_routes.py +4 -7
- multimodalsim_viewer/server/log_manager.py +2 -2
- multimodalsim_viewer/server/server.py +8 -10
- multimodalsim_viewer/server/simulation.py +4 -5
- multimodalsim_viewer/server/simulation_manager.py +22 -23
- multimodalsim_viewer/ui/static/environment.json +2 -0
- multimodalsim_viewer/ui/static/index.html +2 -2
- multimodalsim_viewer/ui/static/{main-LUPJCMAF.js → main-EAYQBWLP.js} +173 -173
- multimodalsim_viewer/ui/static/scripts/load-environment.script.js +1 -1
- multimodalsim_viewer/ui/static/styles-257KETL3.css +1 -0
- {multimodalsim_viewer-0.0.3.dist-info → multimodalsim_viewer-0.1.0.0.dist-info}/METADATA +6 -12
- multimodalsim_viewer-0.1.0.0.dist-info/RECORD +53 -0
- multimodalsim_viewer/server/simulation_visualization_data_model.py +0 -1570
- multimodalsim_viewer/ui/static/styles-KU7LTPET.css +0 -1
- multimodalsim_viewer-0.0.3.dist-info/RECORD +0 -43
- {multimodalsim_viewer-0.0.3.dist-info → multimodalsim_viewer-0.1.0.0.dist-info}/WHEEL +0 -0
- {multimodalsim_viewer-0.0.3.dist-info → multimodalsim_viewer-0.1.0.0.dist-info}/entry_points.txt +0 -0
- {multimodalsim_viewer-0.0.3.dist-info → multimodalsim_viewer-0.1.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,616 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
|
3
|
+
from multimodalsim_viewer.models.environment import VisualizedEnvironment
|
4
|
+
from multimodalsim_viewer.models.leg import LegType, VisualizedLeg
|
5
|
+
from multimodalsim_viewer.models.passenger import (
|
6
|
+
VisualizedPassenger,
|
7
|
+
convert_passenger_status_to_string,
|
8
|
+
convert_string_to_passenger_status,
|
9
|
+
)
|
10
|
+
from multimodalsim_viewer.models.serializable import Serializable
|
11
|
+
from multimodalsim_viewer.models.stop import StopType, VisualizedStop
|
12
|
+
from multimodalsim_viewer.models.vehicle import (
|
13
|
+
VisualizedVehicle,
|
14
|
+
convert_string_to_vehicle_status,
|
15
|
+
convert_vehicle_status_to_string,
|
16
|
+
)
|
17
|
+
|
18
|
+
|
19
|
+
# MARK: UpdateType
|
20
|
+
class UpdateType(Enum):
|
21
|
+
PASSENGER = "passenger"
|
22
|
+
VEHICLE = "vehicle"
|
23
|
+
STATISTICS = "statistics"
|
24
|
+
|
25
|
+
|
26
|
+
# MARK: Update
|
27
|
+
class Update(Serializable):
|
28
|
+
"""
|
29
|
+
Base class for updates in the simulation viewer.
|
30
|
+
|
31
|
+
Represents differences in the simulation environment caused by an event.
|
32
|
+
|
33
|
+
Updates can be applied sequentially to the environment to recreate the evolution of the simulation.
|
34
|
+
"""
|
35
|
+
|
36
|
+
def __init__( # pylint: disable=too-many-arguments, too-many-positional-arguments
|
37
|
+
self, update_type: UpdateType, update_index: int, event_index: int, event_name: str, timestamp: float
|
38
|
+
):
|
39
|
+
self.__update_type: UpdateType = update_type
|
40
|
+
self.__update_index: int = update_index
|
41
|
+
self.__event_index: int = event_index
|
42
|
+
self.__event_name: str = event_name
|
43
|
+
self.__timestamp: float = timestamp
|
44
|
+
|
45
|
+
@property
|
46
|
+
def update_type(self) -> UpdateType:
|
47
|
+
return self.__update_type
|
48
|
+
|
49
|
+
@property
|
50
|
+
def update_index(self) -> int:
|
51
|
+
return self.__update_index
|
52
|
+
|
53
|
+
@update_index.setter
|
54
|
+
def update_index(self, value: int) -> None:
|
55
|
+
self.__update_index = value
|
56
|
+
|
57
|
+
@property
|
58
|
+
def event_index(self) -> int:
|
59
|
+
return self.__event_index
|
60
|
+
|
61
|
+
@property
|
62
|
+
def event_name(self) -> str:
|
63
|
+
return self.__event_name
|
64
|
+
|
65
|
+
@property
|
66
|
+
def timestamp(self) -> float:
|
67
|
+
return self.__timestamp
|
68
|
+
|
69
|
+
def apply(self, environment: VisualizedEnvironment) -> None:
|
70
|
+
"""
|
71
|
+
Apply the update to the given environment.
|
72
|
+
|
73
|
+
This method should be overridden by subclasses.
|
74
|
+
"""
|
75
|
+
|
76
|
+
def serialize(self) -> dict:
|
77
|
+
return {
|
78
|
+
"updateType": self.update_type.value,
|
79
|
+
"updateIndex": self.update_index,
|
80
|
+
"eventIndex": self.event_index,
|
81
|
+
"eventName": self.event_name,
|
82
|
+
"timestamp": self.timestamp,
|
83
|
+
}
|
84
|
+
|
85
|
+
@classmethod
|
86
|
+
def deserialize(cls, serialized_data: dict | str) -> "Update":
|
87
|
+
serialized_data = cls.serialized_data_to_dict(serialized_data)
|
88
|
+
|
89
|
+
required_fields = [
|
90
|
+
"updateType",
|
91
|
+
"updateIndex",
|
92
|
+
"eventIndex",
|
93
|
+
"eventName",
|
94
|
+
"timestamp",
|
95
|
+
]
|
96
|
+
cls.verify_required_fields(serialized_data, required_fields, "Update")
|
97
|
+
|
98
|
+
update_type = UpdateType(serialized_data.get("updateType"))
|
99
|
+
update_index = serialized_data.get("updateIndex")
|
100
|
+
event_index = serialized_data.get("eventIndex")
|
101
|
+
event_name = serialized_data.get("eventName")
|
102
|
+
timestamp = serialized_data.get("timestamp")
|
103
|
+
|
104
|
+
return cls(update_type, update_index, event_index, event_name, timestamp)
|
105
|
+
|
106
|
+
|
107
|
+
# MARK: PassengerUpdate
|
108
|
+
class PassengerUpdate(Update):
|
109
|
+
"""
|
110
|
+
Differences in a passenger before and after an event.
|
111
|
+
"""
|
112
|
+
|
113
|
+
def __init__( # pylint: disable=too-many-arguments, too-many-positional-arguments
|
114
|
+
self,
|
115
|
+
update_index: int,
|
116
|
+
event_index: int,
|
117
|
+
event_name: str,
|
118
|
+
timestamp: float,
|
119
|
+
old_passenger: VisualizedPassenger | None = None,
|
120
|
+
new_passenger: VisualizedPassenger | None = None,
|
121
|
+
should_compute_difference: bool = True,
|
122
|
+
):
|
123
|
+
super().__init__(UpdateType.PASSENGER, update_index, event_index, event_name, timestamp)
|
124
|
+
|
125
|
+
self.__passenger_id: str | None = None
|
126
|
+
|
127
|
+
# Dictionary containing the new values of the fields that have changed
|
128
|
+
self.__differences: dict = {}
|
129
|
+
|
130
|
+
# Legs are more complex and will be handled separately
|
131
|
+
self.__number_of_legs_to_remove: int = 0
|
132
|
+
self.__legs_to_add: list[VisualizedLeg] = []
|
133
|
+
|
134
|
+
self.__legs_differences: list[dict] = []
|
135
|
+
|
136
|
+
if should_compute_difference:
|
137
|
+
self.__compute_difference(old_passenger, new_passenger)
|
138
|
+
|
139
|
+
def __compute_difference(
|
140
|
+
self, old_passenger: VisualizedPassenger | None, new_passenger: VisualizedPassenger | None
|
141
|
+
) -> dict:
|
142
|
+
"""
|
143
|
+
Compute the difference between the old and new passenger.
|
144
|
+
"""
|
145
|
+
|
146
|
+
if new_passenger is None:
|
147
|
+
raise ValueError("New passenger cannot be None")
|
148
|
+
|
149
|
+
if old_passenger is not None and old_passenger.passenger_id != new_passenger.passenger_id:
|
150
|
+
raise ValueError("Old and new passenger must have the same ID")
|
151
|
+
|
152
|
+
self.__passenger_id = new_passenger.passenger_id
|
153
|
+
|
154
|
+
if old_passenger is None or old_passenger.name != new_passenger.name:
|
155
|
+
self.__differences["name"] = new_passenger.name
|
156
|
+
|
157
|
+
if old_passenger is None or old_passenger.status != new_passenger.status:
|
158
|
+
self.__differences["status"] = convert_passenger_status_to_string(new_passenger.status)
|
159
|
+
|
160
|
+
if old_passenger is None or old_passenger.number_of_passengers != new_passenger.number_of_passengers:
|
161
|
+
self.__differences["numberOfPassengers"] = new_passenger.number_of_passengers
|
162
|
+
|
163
|
+
if old_passenger is None or old_passenger.tags != new_passenger.tags:
|
164
|
+
self.__differences["tags"] = new_passenger.tags
|
165
|
+
|
166
|
+
all_old_legs = old_passenger.all_legs if old_passenger is not None else []
|
167
|
+
all_new_legs = new_passenger.all_legs
|
168
|
+
|
169
|
+
self.__number_of_legs_to_remove = max(0, len(all_old_legs) - len(all_new_legs))
|
170
|
+
self.__legs_to_add = all_new_legs[len(all_old_legs) :]
|
171
|
+
|
172
|
+
for index in range(min(len(all_old_legs), len(all_new_legs))):
|
173
|
+
old_leg = all_old_legs[index]
|
174
|
+
new_leg = all_new_legs[index]
|
175
|
+
|
176
|
+
leg_difference = self.__compute_leg_difference(old_leg, new_leg, index)
|
177
|
+
if leg_difference is not None:
|
178
|
+
self.__legs_differences.append(leg_difference)
|
179
|
+
|
180
|
+
def __compute_leg_difference(self, old_leg: VisualizedLeg, new_leg: VisualizedLeg, index: int) -> dict | None:
|
181
|
+
"""
|
182
|
+
Compute the difference between the old and new leg.
|
183
|
+
"""
|
184
|
+
leg_difference = {}
|
185
|
+
|
186
|
+
if old_leg.assigned_vehicle_id != new_leg.assigned_vehicle_id:
|
187
|
+
leg_difference["assignedVehicleId"] = new_leg.assigned_vehicle_id
|
188
|
+
|
189
|
+
if old_leg.boarding_stop_id != new_leg.boarding_stop_id:
|
190
|
+
leg_difference["boardingStopId"] = new_leg.boarding_stop_id
|
191
|
+
|
192
|
+
if old_leg.alighting_stop_id != new_leg.alighting_stop_id:
|
193
|
+
leg_difference["alightingStopId"] = new_leg.alighting_stop_id
|
194
|
+
|
195
|
+
if old_leg.boarding_stop_index != new_leg.boarding_stop_index:
|
196
|
+
leg_difference["boardingStopIndex"] = new_leg.boarding_stop_index
|
197
|
+
|
198
|
+
if old_leg.alighting_stop_index != new_leg.alighting_stop_index:
|
199
|
+
leg_difference["alightingStopIndex"] = new_leg.alighting_stop_index
|
200
|
+
|
201
|
+
if old_leg.boarding_time != new_leg.boarding_time:
|
202
|
+
leg_difference["boardingTime"] = new_leg.boarding_time
|
203
|
+
|
204
|
+
if old_leg.alighting_time != new_leg.alighting_time:
|
205
|
+
leg_difference["alightingTime"] = new_leg.alighting_time
|
206
|
+
|
207
|
+
if old_leg.tags != new_leg.tags:
|
208
|
+
leg_difference["tags"] = new_leg.tags
|
209
|
+
|
210
|
+
if old_leg.leg_type != new_leg.leg_type:
|
211
|
+
leg_difference["legType"] = new_leg.leg_type.value
|
212
|
+
|
213
|
+
if not leg_difference:
|
214
|
+
return None
|
215
|
+
|
216
|
+
leg_difference["index"] = index
|
217
|
+
|
218
|
+
return leg_difference
|
219
|
+
|
220
|
+
def apply(self, environment: VisualizedEnvironment) -> None:
|
221
|
+
passenger = environment.get_passenger(self.__passenger_id)
|
222
|
+
|
223
|
+
if passenger is None:
|
224
|
+
passenger = VisualizedPassenger(
|
225
|
+
self.__passenger_id,
|
226
|
+
self.__differences.get("name"),
|
227
|
+
convert_string_to_passenger_status(self.__differences.get("status")),
|
228
|
+
self.__differences.get("numberOfPassengers"),
|
229
|
+
[],
|
230
|
+
None,
|
231
|
+
[],
|
232
|
+
self.__differences.get("tags"),
|
233
|
+
)
|
234
|
+
|
235
|
+
environment.add_passenger(passenger)
|
236
|
+
|
237
|
+
else:
|
238
|
+
if "name" in self.__differences:
|
239
|
+
passenger.name = self.__differences.get("name")
|
240
|
+
if "status" in self.__differences:
|
241
|
+
passenger.status = convert_string_to_passenger_status(self.__differences.get("status"))
|
242
|
+
if "numberOfPassengers" in self.__differences:
|
243
|
+
passenger.number_of_passengers = self.__differences.get("numberOfPassengers")
|
244
|
+
if "tags" in self.__differences:
|
245
|
+
passenger.tags = self.__differences.get("tags")
|
246
|
+
|
247
|
+
self.__update_legs(passenger)
|
248
|
+
|
249
|
+
def __update_legs(self, passenger: VisualizedPassenger) -> None: # pylint: disable=too-many-branches
|
250
|
+
all_legs = passenger.all_legs
|
251
|
+
|
252
|
+
if self.__number_of_legs_to_remove > 0:
|
253
|
+
all_legs = all_legs[: -self.__number_of_legs_to_remove]
|
254
|
+
|
255
|
+
all_legs.extend(self.__legs_to_add)
|
256
|
+
|
257
|
+
for leg_difference in self.__legs_differences:
|
258
|
+
leg = all_legs[leg_difference.get("index")]
|
259
|
+
|
260
|
+
if "legType" in leg_difference:
|
261
|
+
leg.leg_type = LegType(leg_difference.get("legType"))
|
262
|
+
if "assignedVehicleId" in leg_difference:
|
263
|
+
leg.assigned_vehicle_id = leg_difference.get("assignedVehicleId")
|
264
|
+
if "boardingStopId" in leg_difference:
|
265
|
+
leg.boarding_stop_id = leg_difference.get("boardingStopId")
|
266
|
+
if "alightingStopId" in leg_difference:
|
267
|
+
leg.alighting_stop_id = leg_difference.get("alightingStopId")
|
268
|
+
if "boardingStopIndex" in leg_difference:
|
269
|
+
leg.boarding_stop_index = leg_difference.get("boardingStopIndex")
|
270
|
+
if "alightingStopIndex" in leg_difference:
|
271
|
+
leg.alighting_stop_index = leg_difference.get("alightingStopIndex")
|
272
|
+
if "boardingTime" in leg_difference:
|
273
|
+
leg.boarding_time = leg_difference.get("boardingTime")
|
274
|
+
if "alightingTime" in leg_difference:
|
275
|
+
leg.alighting_time = leg_difference.get("alightingTime")
|
276
|
+
|
277
|
+
passenger.previous_legs = []
|
278
|
+
passenger.current_leg = None
|
279
|
+
passenger.next_legs = []
|
280
|
+
|
281
|
+
for leg in all_legs:
|
282
|
+
if leg.leg_type == LegType.PREVIOUS:
|
283
|
+
passenger.previous_legs.append(leg)
|
284
|
+
elif leg.leg_type == LegType.CURRENT:
|
285
|
+
passenger.current_leg = leg
|
286
|
+
elif leg.leg_type == LegType.NEXT:
|
287
|
+
passenger.next_legs.append(leg)
|
288
|
+
|
289
|
+
def serialize(self) -> dict:
|
290
|
+
serialized_data = super().serialize()
|
291
|
+
|
292
|
+
serialized_data["passengerId"] = self.__passenger_id
|
293
|
+
|
294
|
+
if self.__differences:
|
295
|
+
serialized_data["differences"] = self.__differences
|
296
|
+
if self.__number_of_legs_to_remove > 0:
|
297
|
+
serialized_data["numberOfLegsToRemove"] = self.__number_of_legs_to_remove
|
298
|
+
if self.__legs_to_add:
|
299
|
+
serialized_data["legsToAdd"] = [leg.serialize() for leg in self.__legs_to_add]
|
300
|
+
if self.__legs_differences:
|
301
|
+
serialized_data["legsDifferences"] = self.__legs_differences
|
302
|
+
|
303
|
+
return serialized_data
|
304
|
+
|
305
|
+
@classmethod
|
306
|
+
def deserialize(cls, serialized_data: dict | str) -> "PassengerUpdate":
|
307
|
+
serialized_data = cls.serialized_data_to_dict(serialized_data)
|
308
|
+
|
309
|
+
update = Update.deserialize(serialized_data)
|
310
|
+
|
311
|
+
passenger_update = cls(
|
312
|
+
update.update_index,
|
313
|
+
update.event_index,
|
314
|
+
update.event_name,
|
315
|
+
update.timestamp,
|
316
|
+
should_compute_difference=False,
|
317
|
+
)
|
318
|
+
|
319
|
+
required_fields = [
|
320
|
+
"passengerId",
|
321
|
+
]
|
322
|
+
cls.verify_required_fields(serialized_data, required_fields, "PassengerUpdate")
|
323
|
+
|
324
|
+
# pylint: disable=unused-private-member
|
325
|
+
passenger_update.__passenger_id = serialized_data.get("passengerId")
|
326
|
+
passenger_update.__differences = serialized_data.get("differences", {})
|
327
|
+
passenger_update.__number_of_legs_to_remove = serialized_data.get("numberOfLegsToRemove", 0)
|
328
|
+
passenger_update.__legs_to_add = [
|
329
|
+
VisualizedLeg.deserialize(leg_data) for leg_data in serialized_data.get("legsToAdd", [])
|
330
|
+
]
|
331
|
+
passenger_update.__legs_differences = serialized_data.get("legsDifferences", [])
|
332
|
+
# pylint: enable=unused-private-member
|
333
|
+
|
334
|
+
return passenger_update
|
335
|
+
|
336
|
+
|
337
|
+
# MARK: VehicleUpdate
|
338
|
+
class VehicleUpdate(Update):
|
339
|
+
"""
|
340
|
+
Differences in a vehicle before and after an event.
|
341
|
+
"""
|
342
|
+
|
343
|
+
def __init__( # pylint: disable=too-many-arguments, too-many-positional-arguments
|
344
|
+
self,
|
345
|
+
update_index: int,
|
346
|
+
event_index: int,
|
347
|
+
event_name: str,
|
348
|
+
timestamp: float,
|
349
|
+
old_vehicle: VisualizedVehicle | None = None,
|
350
|
+
new_vehicle: VisualizedVehicle | None = None,
|
351
|
+
should_compute_difference: bool = True,
|
352
|
+
):
|
353
|
+
super().__init__(UpdateType.VEHICLE, update_index, event_index, event_name, timestamp)
|
354
|
+
|
355
|
+
self.__vehicle_id: str | None = None
|
356
|
+
|
357
|
+
# Dictionary containing the new values of the fields that have changed
|
358
|
+
self.__differences: dict = {}
|
359
|
+
|
360
|
+
# Stops are more complex and will be handled separately
|
361
|
+
self.__number_of_stops_to_remove: int = 0
|
362
|
+
self.__stops_to_add: list[VisualizedStop] = []
|
363
|
+
|
364
|
+
self.__stops_differences: list[dict] = []
|
365
|
+
|
366
|
+
# Polylines are only used on the server side to update the polylines file when the simulation is running.
|
367
|
+
# We only need to store the new polylines here, and use it when applying the update.
|
368
|
+
# In the future, if we want to apply this update in the server by reading the save file,
|
369
|
+
# we will need to change this.
|
370
|
+
self.__new_polylines: dict[str, tuple[str, list[float]]] | None = (
|
371
|
+
new_vehicle.polylines if new_vehicle is not None else None
|
372
|
+
)
|
373
|
+
|
374
|
+
if should_compute_difference:
|
375
|
+
self.__compute_difference(old_vehicle, new_vehicle)
|
376
|
+
|
377
|
+
@property
|
378
|
+
def vehicle_id(self) -> str | None:
|
379
|
+
return self.__vehicle_id
|
380
|
+
|
381
|
+
def __compute_difference(
|
382
|
+
self, old_vehicle: VisualizedVehicle | None, new_vehicle: VisualizedVehicle | None
|
383
|
+
) -> dict:
|
384
|
+
"""
|
385
|
+
Compute the difference between the old and new vehicle.
|
386
|
+
"""
|
387
|
+
|
388
|
+
if new_vehicle is None:
|
389
|
+
raise ValueError("New vehicle cannot be None")
|
390
|
+
|
391
|
+
if old_vehicle is not None and old_vehicle.vehicle_id != new_vehicle.vehicle_id:
|
392
|
+
raise ValueError("Old and new vehicle must have the same ID")
|
393
|
+
|
394
|
+
self.__vehicle_id = new_vehicle.vehicle_id
|
395
|
+
|
396
|
+
if old_vehicle is None or old_vehicle.mode != new_vehicle.mode:
|
397
|
+
self.__differences["mode"] = new_vehicle.mode
|
398
|
+
|
399
|
+
if old_vehicle is None or old_vehicle.status != new_vehicle.status:
|
400
|
+
self.__differences["status"] = convert_vehicle_status_to_string(new_vehicle.status)
|
401
|
+
|
402
|
+
if old_vehicle is None or old_vehicle.capacity != new_vehicle.capacity:
|
403
|
+
self.__differences["capacity"] = new_vehicle.capacity
|
404
|
+
|
405
|
+
if old_vehicle is None or old_vehicle.name != new_vehicle.name:
|
406
|
+
self.__differences["name"] = new_vehicle.name
|
407
|
+
|
408
|
+
if old_vehicle is None or old_vehicle.tags != new_vehicle.tags:
|
409
|
+
self.__differences["tags"] = new_vehicle.tags
|
410
|
+
|
411
|
+
all_old_stops = old_vehicle.all_stops if old_vehicle is not None else []
|
412
|
+
all_new_stops = new_vehicle.all_stops
|
413
|
+
|
414
|
+
self.__number_of_stops_to_remove = max(0, len(all_old_stops) - len(all_new_stops))
|
415
|
+
self.__stops_to_add = all_new_stops[len(all_old_stops) :]
|
416
|
+
|
417
|
+
for index in range(min(len(all_old_stops), len(all_new_stops))):
|
418
|
+
old_stop = all_old_stops[index]
|
419
|
+
new_stop = all_new_stops[index]
|
420
|
+
|
421
|
+
stop_difference = self.__compute_stop_difference(old_stop, new_stop, index)
|
422
|
+
if stop_difference is not None:
|
423
|
+
self.__stops_differences.append(stop_difference)
|
424
|
+
|
425
|
+
def __compute_stop_difference(self, old_stop: VisualizedStop, new_stop: VisualizedStop, index: int) -> dict | None:
|
426
|
+
"""
|
427
|
+
Compute the difference between the old and new stop.
|
428
|
+
"""
|
429
|
+
stop_difference = {}
|
430
|
+
|
431
|
+
if old_stop.arrival_time != new_stop.arrival_time:
|
432
|
+
stop_difference["arrivalTime"] = new_stop.arrival_time
|
433
|
+
|
434
|
+
if old_stop.departure_time != new_stop.departure_time:
|
435
|
+
stop_difference["departureTime"] = new_stop.departure_time
|
436
|
+
|
437
|
+
if old_stop.latitude != new_stop.latitude:
|
438
|
+
stop_difference["latitude"] = new_stop.latitude
|
439
|
+
|
440
|
+
if old_stop.longitude != new_stop.longitude:
|
441
|
+
stop_difference["longitude"] = new_stop.longitude
|
442
|
+
|
443
|
+
if old_stop.capacity != new_stop.capacity:
|
444
|
+
stop_difference["capacity"] = new_stop.capacity
|
445
|
+
|
446
|
+
if old_stop.label != new_stop.label:
|
447
|
+
stop_difference["label"] = new_stop.label
|
448
|
+
|
449
|
+
if old_stop.tags != new_stop.tags:
|
450
|
+
stop_difference["tags"] = new_stop.tags
|
451
|
+
|
452
|
+
if old_stop.stop_type != new_stop.stop_type:
|
453
|
+
stop_difference["stopType"] = new_stop.stop_type.value
|
454
|
+
|
455
|
+
if not stop_difference:
|
456
|
+
return None
|
457
|
+
|
458
|
+
stop_difference["index"] = index
|
459
|
+
|
460
|
+
return stop_difference
|
461
|
+
|
462
|
+
def apply(self, environment: VisualizedEnvironment) -> None:
|
463
|
+
vehicle = environment.get_vehicle(self.__vehicle_id)
|
464
|
+
|
465
|
+
if vehicle is None:
|
466
|
+
vehicle = VisualizedVehicle(
|
467
|
+
self.__vehicle_id,
|
468
|
+
self.__differences.get("mode"),
|
469
|
+
convert_string_to_vehicle_status(self.__differences.get("status")),
|
470
|
+
self.__new_polylines,
|
471
|
+
[],
|
472
|
+
None,
|
473
|
+
[],
|
474
|
+
self.__differences.get("capacity"),
|
475
|
+
self.__differences.get("name"),
|
476
|
+
self.__differences.get("tags"),
|
477
|
+
)
|
478
|
+
|
479
|
+
environment.add_vehicle(vehicle)
|
480
|
+
|
481
|
+
else:
|
482
|
+
vehicle.polylines = self.__new_polylines
|
483
|
+
|
484
|
+
if "mode" in self.__differences:
|
485
|
+
vehicle.mode = self.__differences.get("mode")
|
486
|
+
if "status" in self.__differences:
|
487
|
+
vehicle.status = convert_string_to_vehicle_status(self.__differences.get("status"))
|
488
|
+
if "capacity" in self.__differences:
|
489
|
+
vehicle.capacity = self.__differences.get("capacity")
|
490
|
+
if "name" in self.__differences:
|
491
|
+
vehicle.name = self.__differences.get("name")
|
492
|
+
if "tags" in self.__differences:
|
493
|
+
vehicle.tags = self.__differences.get("tags")
|
494
|
+
|
495
|
+
self.__update_stops(vehicle)
|
496
|
+
|
497
|
+
def __update_stops(self, vehicle: VisualizedVehicle) -> None: # pylint: disable=too-many-branches
|
498
|
+
all_stops = vehicle.all_stops
|
499
|
+
|
500
|
+
if self.__number_of_stops_to_remove > 0:
|
501
|
+
all_stops = all_stops[: -self.__number_of_stops_to_remove]
|
502
|
+
|
503
|
+
all_stops.extend(self.__stops_to_add)
|
504
|
+
|
505
|
+
for stop_difference in self.__stops_differences:
|
506
|
+
stop = all_stops[stop_difference.get("index")]
|
507
|
+
|
508
|
+
if "arrivalTime" in stop_difference:
|
509
|
+
stop.arrival_time = stop_difference.get("arrivalTime")
|
510
|
+
if "departureTime" in stop_difference:
|
511
|
+
stop.departure_time = stop_difference.get("departureTime")
|
512
|
+
if "latitude" in stop_difference:
|
513
|
+
stop.latitude = stop_difference.get("latitude")
|
514
|
+
if "longitude" in stop_difference:
|
515
|
+
stop.longitude = stop_difference.get("longitude")
|
516
|
+
if "capacity" in stop_difference:
|
517
|
+
stop.capacity = stop_difference.get("capacity")
|
518
|
+
if "label" in stop_difference:
|
519
|
+
stop.label = stop_difference.get("label")
|
520
|
+
if "tags" in stop_difference:
|
521
|
+
stop.tags = stop_difference.get("tags")
|
522
|
+
if "stopType" in stop_difference:
|
523
|
+
stop.stop_type = StopType(stop_difference.get("stopType"))
|
524
|
+
|
525
|
+
vehicle.previous_stops = []
|
526
|
+
vehicle.current_stop = None
|
527
|
+
vehicle.next_stops = []
|
528
|
+
|
529
|
+
for stop in all_stops:
|
530
|
+
if stop.stop_type == StopType.PREVIOUS:
|
531
|
+
vehicle.previous_stops.append(stop)
|
532
|
+
elif stop.stop_type == StopType.CURRENT:
|
533
|
+
vehicle.current_stop = stop
|
534
|
+
elif stop.stop_type == StopType.NEXT:
|
535
|
+
vehicle.next_stops.append(stop)
|
536
|
+
|
537
|
+
def serialize(self) -> dict:
|
538
|
+
serialized_data = super().serialize()
|
539
|
+
|
540
|
+
serialized_data["vehicleId"] = self.__vehicle_id
|
541
|
+
|
542
|
+
if self.__differences:
|
543
|
+
serialized_data["differences"] = self.__differences
|
544
|
+
if self.__number_of_stops_to_remove > 0:
|
545
|
+
serialized_data["numberOfStopsToRemove"] = self.__number_of_stops_to_remove
|
546
|
+
if self.__stops_to_add:
|
547
|
+
serialized_data["stopsToAdd"] = [stop.serialize() for stop in self.__stops_to_add]
|
548
|
+
if self.__stops_differences:
|
549
|
+
serialized_data["stopsDifferences"] = self.__stops_differences
|
550
|
+
|
551
|
+
return serialized_data
|
552
|
+
|
553
|
+
@classmethod
|
554
|
+
def deserialize(cls, serialized_data: dict | str) -> "VehicleUpdate":
|
555
|
+
serialized_data = cls.serialized_data_to_dict(serialized_data)
|
556
|
+
|
557
|
+
update = Update.deserialize(serialized_data)
|
558
|
+
|
559
|
+
vehicle_update = cls(
|
560
|
+
update.update_index,
|
561
|
+
update.event_index,
|
562
|
+
update.event_name,
|
563
|
+
update.timestamp,
|
564
|
+
should_compute_difference=False,
|
565
|
+
)
|
566
|
+
|
567
|
+
required_fields = [
|
568
|
+
"vehicleId",
|
569
|
+
]
|
570
|
+
cls.verify_required_fields(serialized_data, required_fields, "VehicleUpdate")
|
571
|
+
|
572
|
+
# pylint: disable=unused-private-member
|
573
|
+
vehicle_update.__vehicle_id = serialized_data.get("vehicleId")
|
574
|
+
vehicle_update.__differences = serialized_data.get("differences", {})
|
575
|
+
vehicle_update.__number_of_stops_to_remove = serialized_data.get("numberOfStopsToRemove", 0)
|
576
|
+
vehicle_update.__stops_to_add = [
|
577
|
+
VisualizedStop.deserialize(stop_data) for stop_data in serialized_data.get("stopsToAdd", [])
|
578
|
+
]
|
579
|
+
vehicle_update.__stops_differences = serialized_data.get("stopsDifferences", [])
|
580
|
+
# pylint: enable=unused-private-member
|
581
|
+
|
582
|
+
return vehicle_update
|
583
|
+
|
584
|
+
|
585
|
+
# MARK: StatisticsUpdate
|
586
|
+
class StatisticsUpdate(Update):
|
587
|
+
"""
|
588
|
+
New statistics computed by the simulation.
|
589
|
+
"""
|
590
|
+
|
591
|
+
def __init__( # pylint: disable=too-many-arguments, too-many-positional-arguments
|
592
|
+
self, update_index: int, event_index: int, event_name: str, timestamp: float, statistics: dict
|
593
|
+
):
|
594
|
+
super().__init__(UpdateType.STATISTICS, update_index, event_index, event_name, timestamp)
|
595
|
+
self.__statistics: dict = statistics
|
596
|
+
|
597
|
+
def apply(self, environment: VisualizedEnvironment) -> None:
|
598
|
+
environment.statistics = self.__statistics
|
599
|
+
|
600
|
+
def serialize(self) -> dict:
|
601
|
+
serialized_data = super().serialize()
|
602
|
+
serialized_data["statistics"] = self.__statistics
|
603
|
+
return serialized_data
|
604
|
+
|
605
|
+
@classmethod
|
606
|
+
def deserialize(cls, serialized_data: dict | str) -> "StatisticsUpdate":
|
607
|
+
serialized_data = cls.serialized_data_to_dict(serialized_data)
|
608
|
+
|
609
|
+
update = Update.deserialize(serialized_data)
|
610
|
+
|
611
|
+
required_fields = ["statistics"]
|
612
|
+
cls.verify_required_fields(serialized_data, required_fields, "StatisticsUpdate")
|
613
|
+
|
614
|
+
return cls(
|
615
|
+
update.update_index, update.event_index, update.event_name, update.timestamp, serialized_data["statistics"]
|
616
|
+
)
|