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.
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 +567 -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-EAYQBWLP.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.0.dist-info}/METADATA +6 -12
  26. multimodalsim_viewer-0.1.0.0.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.0.dist-info}/WHEEL +0 -0
  31. {multimodalsim_viewer-0.0.3.dist-info → multimodalsim_viewer-0.1.0.0.dist-info}/entry_points.txt +0 -0
  32. {multimodalsim_viewer-0.0.3.dist-info → multimodalsim_viewer-0.1.0.0.dist-info}/top_level.txt +0 -0
@@ -2,3 +2,5 @@ CLIENT_PORT=8085
2
2
  SERVER_PORT=8089
3
3
  SIMULATION_SAVE_FILE_SEPARATOR=---
4
4
  INPUT_DATA_DIRECTORY_PATH=data
5
+ NUMBER_OF_UPDATES_BETWEEN_STATES=1000
6
+ NUMBER_OF_STATES_TO_SEND_AT_ONCE=1
@@ -79,6 +79,14 @@ class _Environment:
79
79
  def input_data_directory_path(self) -> str:
80
80
  return environment.get("INPUT_DATA_DIRECTORY_PATH")
81
81
 
82
+ @property
83
+ def number_of_updates_between_states(self) -> int:
84
+ return int(environment.get("NUMBER_OF_UPDATES_BETWEEN_STATES"))
85
+
86
+ @property
87
+ def number_of_states_to_send_at_once(self) -> int:
88
+ return int(environment.get("NUMBER_OF_STATES_TO_SEND_AT_ONCE"))
89
+
82
90
 
83
91
  _environment = _Environment()
84
92
  SERVER_PORT = _environment.server_port
@@ -86,17 +94,16 @@ CLIENT_PORT = _environment.client_port
86
94
  HOST = _environment.host
87
95
  SIMULATION_SAVE_FILE_SEPARATOR = _environment.simulation_save_file_separator
88
96
  INPUT_DATA_DIRECTORY_PATH = _environment.input_data_directory_path
97
+ NUMBER_OF_UPDATES_BETWEEN_STATES = _environment.number_of_updates_between_states
98
+ NUMBER_OF_STATES_TO_SEND_AT_ONCE = _environment.number_of_states_to_send_at_once
89
99
 
90
100
 
91
101
  CLIENT_ROOM = "client"
92
102
  SIMULATION_ROOM = "simulation"
93
103
  SCRIPT_ROOM = "script"
94
104
 
95
- # Save the state of the simulation every STATE_SAVE_STEP events
96
- STATE_SAVE_STEP = 1000
97
-
98
105
  # If the version is identical, the save file can be loaded
99
- SAVE_VERSION = 9
106
+ SAVE_VERSION = 12
100
107
 
101
108
 
102
109
  class SimulationStatus(Enum):
@@ -135,50 +142,6 @@ def build_simulation_id(name: str) -> tuple[str, str]:
135
142
  return simulation_id, start_time
136
143
 
137
144
 
138
- def get_data_directory_path() -> str:
139
- current_file_path = os.path.abspath(__file__)
140
- current_file_dir = os.path.dirname(current_file_path)
141
- data_directory_path = os.path.join(current_file_dir, "..", "data")
142
-
143
- if not os.path.exists(data_directory_path):
144
- os.makedirs(data_directory_path)
145
-
146
- return data_directory_path
147
-
148
-
149
- def get_saved_logs_directory_path() -> str:
150
- data_directory_path = get_data_directory_path()
151
- saved_logs_directory_path = os.path.join(data_directory_path, "saved_logs")
152
-
153
- if not os.path.exists(saved_logs_directory_path):
154
- os.makedirs(saved_logs_directory_path)
155
-
156
- return saved_logs_directory_path
157
-
158
-
159
- def get_input_data_directory_path(data: str | None = None) -> str:
160
- input_data_directory = INPUT_DATA_DIRECTORY_PATH
161
-
162
- if data is not None:
163
- input_data_directory = os.path.join(input_data_directory, data)
164
-
165
- return input_data_directory
166
-
167
-
168
- def get_available_data():
169
- input_data_directory = get_input_data_directory_path()
170
-
171
- if not os.path.exists(input_data_directory):
172
- return []
173
-
174
- # List all directories in the input data directory
175
- return [
176
- name
177
- for name in os.listdir(input_data_directory)
178
- if os.path.isdir(os.path.join(input_data_directory, name)) and not name.startswith(".")
179
- ]
180
-
181
-
182
145
  def log(message: str, auth_type: str, level=logging.INFO, should_emit=True) -> None:
183
146
  if auth_type == "server":
184
147
  logging.log(level, "[%s] %s", auth_type, message)
File without changes
@@ -0,0 +1,70 @@
1
+ from multimodalsim_viewer.models.passenger import VisualizedPassenger
2
+ from multimodalsim_viewer.models.serializable import Serializable
3
+ from multimodalsim_viewer.models.vehicle import VisualizedVehicle
4
+
5
+
6
+ # MARK: Environment
7
+ class VisualizedEnvironment(Serializable):
8
+
9
+ def __init__(self) -> None:
10
+ self.passengers: dict[str, VisualizedPassenger] = {}
11
+ self.vehicles: dict[str, VisualizedVehicle] = {}
12
+ self.timestamp: float = 0.0
13
+ self.estimated_end_time: float = 0.0
14
+ self.update_index: int = 0
15
+ self.statistics: dict | None = None
16
+
17
+ def add_passenger(self, passenger: VisualizedPassenger) -> None:
18
+ self.passengers[passenger.passenger_id] = passenger
19
+
20
+ def get_passenger(self, passenger_id: str) -> VisualizedPassenger | None:
21
+ if passenger_id in self.passengers:
22
+ return self.passengers[passenger_id]
23
+ return None
24
+
25
+ def add_vehicle(self, vehicle: VisualizedVehicle) -> None:
26
+ self.vehicles[vehicle.vehicle_id] = vehicle
27
+
28
+ def get_vehicle(self, vehicle_id: str) -> VisualizedVehicle | None:
29
+ if vehicle_id in self.vehicles:
30
+ return self.vehicles[vehicle_id]
31
+ return None
32
+
33
+ def serialize(self) -> dict:
34
+ return {
35
+ "passengers": [passenger.serialize() for passenger in self.passengers.values()],
36
+ "vehicles": [vehicle.serialize() for vehicle in self.vehicles.values()],
37
+ "timestamp": self.timestamp,
38
+ "statistics": self.statistics if self.statistics is not None else {},
39
+ "updateIndex": self.update_index,
40
+ }
41
+
42
+ @classmethod
43
+ def deserialize(cls, serialized_data: dict | str) -> "VisualizedEnvironment":
44
+ serialized_data = cls.serialized_data_to_dict(serialized_data)
45
+
46
+ required_keys = [
47
+ "passengers",
48
+ "vehicles",
49
+ "timestamp",
50
+ "statistics",
51
+ "updateIndex",
52
+ ]
53
+
54
+ cls.verify_required_fields(serialized_data, required_keys, "VisualizedEnvironment")
55
+
56
+ environment = VisualizedEnvironment()
57
+ for passenger_data in serialized_data["passengers"]:
58
+ passenger = VisualizedPassenger.deserialize(passenger_data)
59
+ environment.add_passenger(passenger)
60
+
61
+ for vehicle_data in serialized_data["vehicles"]:
62
+ vehicle = VisualizedVehicle.deserialize(vehicle_data)
63
+ environment.add_vehicle(vehicle)
64
+
65
+ environment.timestamp = serialized_data["timestamp"]
66
+ environment.estimated_end_time = serialized_data["estimatedEndTime"]
67
+ environment.statistics = serialized_data["statistics"]
68
+ environment.update_index = serialized_data["updateIndex"]
69
+
70
+ return environment
@@ -0,0 +1,194 @@
1
+ from enum import Enum
2
+
3
+ from multimodalsim.simulator.environment import Environment
4
+ from multimodalsim.simulator.request import Leg, Trip
5
+ from multimodalsim.simulator.stop import Stop
6
+
7
+ from multimodalsim_viewer.models.serializable import Serializable
8
+
9
+
10
+ # MARK: get_stop_id
11
+ def get_stop_id(stop: Stop) -> str:
12
+ return f"{stop.location.lat},{stop.location.lon}"
13
+
14
+
15
+ # MARK: LegType
16
+ class LegType(Enum):
17
+ PREVIOUS = "previous"
18
+ CURRENT = "current"
19
+ NEXT = "next"
20
+
21
+
22
+ # MARK: Leg
23
+ class VisualizedLeg(Serializable): # pylint: disable=too-many-instance-attributes
24
+
25
+ def __init__( # pylint: disable=too-many-arguments, too-many-positional-arguments
26
+ self,
27
+ assigned_vehicle_id: str | None,
28
+ boarding_stop_id: str | None,
29
+ alighting_stop_id: str | None,
30
+ boarding_stop_index: int | None,
31
+ alighting_stop_index: int | None,
32
+ boarding_time: float | None,
33
+ alighting_time: float | None,
34
+ tags: list[str],
35
+ leg_type: LegType,
36
+ ) -> None:
37
+ self.assigned_vehicle_id: str | None = assigned_vehicle_id
38
+
39
+ self.boarding_stop_id: str | None = boarding_stop_id
40
+ self.alighting_stop_id: str | None = alighting_stop_id
41
+ self.boarding_stop_index: int | None = boarding_stop_index
42
+ self.alighting_stop_index: int | None = alighting_stop_index
43
+ self.boarding_time: float | None = boarding_time
44
+ self.alighting_time: float | None = alighting_time
45
+
46
+ self.tags: list[str] = tags
47
+ self.leg_type: LegType = leg_type
48
+
49
+ @classmethod
50
+ def from_leg_environment_and_trip( # pylint: disable=too-many-locals, too-many-branches, too-many-arguments, too-many-positional-arguments
51
+ cls,
52
+ leg: Leg,
53
+ environment: Environment,
54
+ trip: Trip,
55
+ leg_type: LegType,
56
+ ) -> "VisualizedLeg":
57
+ boarding_stop_id = None
58
+ alighting_stop_id = None
59
+ boarding_stop_index = None
60
+ alighting_stop_index = None
61
+
62
+ route = (
63
+ environment.get_route_by_vehicle_id(leg.assigned_vehicle.id) if leg.assigned_vehicle is not None else None
64
+ )
65
+
66
+ all_legs = trip.previous_legs + ([trip.current_leg] if trip.current_leg else []) + trip.next_legs
67
+
68
+ same_vehicle_leg_index = 0
69
+ for other_leg in all_legs:
70
+ if other_leg.assigned_vehicle == leg.assigned_vehicle:
71
+ if other_leg == leg:
72
+ break
73
+ same_vehicle_leg_index += 1
74
+
75
+ if route is not None:
76
+ all_stops = route.previous_stops.copy()
77
+ if route.current_stop is not None:
78
+ all_stops.append(route.current_stop)
79
+ all_stops += route.next_stops
80
+
81
+ trip_found_count = 0
82
+
83
+ for i, stop in enumerate(all_stops):
84
+ if boarding_stop_index is None and trip in (
85
+ stop.passengers_to_board + stop.boarding_passengers + stop.boarded_passengers
86
+ ):
87
+ if trip_found_count == same_vehicle_leg_index:
88
+ boarding_stop_id = get_stop_id(stop)
89
+ boarding_stop_index = i
90
+ break
91
+ trip_found_count += 1
92
+
93
+ trip_found_count = 0
94
+
95
+ for i, stop in enumerate(all_stops):
96
+ if alighting_stop_index is None and trip in (
97
+ stop.passengers_to_alight + stop.alighting_passengers + stop.alighted_passengers
98
+ ):
99
+ if trip_found_count == same_vehicle_leg_index:
100
+ alighting_stop_id = get_stop_id(stop)
101
+ alighting_stop_index = i
102
+ break
103
+ trip_found_count += 1
104
+
105
+ assigned_vehicle_id = leg.assigned_vehicle.id if leg.assigned_vehicle is not None else None
106
+
107
+ return cls(
108
+ assigned_vehicle_id,
109
+ boarding_stop_id,
110
+ alighting_stop_id,
111
+ boarding_stop_index,
112
+ alighting_stop_index,
113
+ leg.boarding_time,
114
+ leg.alighting_time,
115
+ leg.tags,
116
+ leg_type,
117
+ )
118
+
119
+ def serialize(self) -> dict:
120
+ serialized = {}
121
+
122
+ serialized["legType"] = self.leg_type.value
123
+
124
+ if self.assigned_vehicle_id is not None:
125
+ serialized["assignedVehicleId"] = self.assigned_vehicle_id
126
+
127
+ if self.boarding_stop_id is not None:
128
+ serialized["boardingStopId"] = self.boarding_stop_id
129
+
130
+ if self.alighting_stop_id is not None:
131
+ serialized["alightingStopId"] = self.alighting_stop_id
132
+
133
+ if self.boarding_stop_index is not None:
134
+ serialized["boardingStopIndex"] = self.boarding_stop_index
135
+
136
+ if self.alighting_stop_index is not None:
137
+ serialized["alightingStopIndex"] = self.alighting_stop_index
138
+
139
+ if self.boarding_time is not None:
140
+ serialized["boardingTime"] = self.boarding_time
141
+
142
+ if self.alighting_time is not None:
143
+ serialized["alightingTime"] = self.alighting_time
144
+
145
+ if len(self.tags) > 0:
146
+ serialized["tags"] = self.tags
147
+
148
+ return serialized
149
+
150
+ @classmethod
151
+ def deserialize(cls, serialized_data: dict | str) -> "VisualizedLeg":
152
+ serialized_data = cls.serialized_data_to_dict(serialized_data)
153
+
154
+ required_keys = ["legType"]
155
+
156
+ cls.verify_required_fields(serialized_data, required_keys, "VisualizedLeg")
157
+
158
+ assigned_vehicle_id = serialized_data.get("assignedVehicleId", None)
159
+
160
+ boarding_stop_id = serialized_data.get("boardingStopId", None)
161
+
162
+ alighting_stop_id = serialized_data.get("alightingStopId", None)
163
+
164
+ boarding_stop_index = serialized_data.get("boardingStopIndex", None)
165
+ if boarding_stop_index is not None:
166
+ boarding_stop_index = int(boarding_stop_index)
167
+
168
+ alighting_stop_index = serialized_data.get("alightingStopIndex", None)
169
+ if alighting_stop_index is not None:
170
+ alighting_stop_index = int(alighting_stop_index)
171
+
172
+ boarding_time = serialized_data.get("boardingTime", None)
173
+ if boarding_time is not None:
174
+ boarding_time = float(boarding_time)
175
+
176
+ alighting_time = serialized_data.get("alightingTime", None)
177
+ if alighting_time is not None:
178
+ alighting_time = float(alighting_time)
179
+
180
+ tags = serialized_data.get("tags", [])
181
+
182
+ leg_type = serialized_data.get("legType")
183
+
184
+ return cls(
185
+ assigned_vehicle_id,
186
+ boarding_stop_id,
187
+ alighting_stop_id,
188
+ boarding_stop_index,
189
+ alighting_stop_index,
190
+ boarding_time,
191
+ alighting_time,
192
+ tags,
193
+ LegType(leg_type),
194
+ )
@@ -0,0 +1,148 @@
1
+ from multimodalsim.optimization.dispatcher import ( # To avoid circular import issues. pylint: disable=unused-import;
2
+ Dispatcher,
3
+ )
4
+ from multimodalsim.simulator.environment import Environment
5
+ from multimodalsim.simulator.request import Trip
6
+ from multimodalsim.state_machine.status import PassengerStatus
7
+
8
+ from multimodalsim_viewer.models.leg import LegType, VisualizedLeg
9
+ from multimodalsim_viewer.models.serializable import Serializable
10
+
11
+
12
+ # MARK: Enums
13
+ def convert_passenger_status_to_string(status: PassengerStatus) -> str:
14
+ if status == PassengerStatus.RELEASE:
15
+ return "release"
16
+ if status == PassengerStatus.ASSIGNED:
17
+ return "assigned"
18
+ if status == PassengerStatus.READY:
19
+ return "ready"
20
+ if status == PassengerStatus.ONBOARD:
21
+ return "onboard"
22
+ if status == PassengerStatus.COMPLETE:
23
+ return "complete"
24
+ raise ValueError(f"Unknown PassengerStatus {status}")
25
+
26
+
27
+ def convert_string_to_passenger_status(status: str) -> PassengerStatus:
28
+ if status == "release":
29
+ return PassengerStatus.RELEASE
30
+ if status == "assigned":
31
+ return PassengerStatus.ASSIGNED
32
+ if status == "ready":
33
+ return PassengerStatus.READY
34
+ if status == "onboard":
35
+ return PassengerStatus.ONBOARD
36
+ if status == "complete":
37
+ return PassengerStatus.COMPLETE
38
+ raise ValueError(f"Unknown PassengerStatus {status}")
39
+
40
+
41
+ # MARK: Passenger
42
+ class VisualizedPassenger(Serializable): # pylint: disable=too-many-instance-attributes
43
+
44
+ def __init__( # pylint: disable=too-many-arguments, too-many-positional-arguments
45
+ self,
46
+ passenger_id: str,
47
+ name: str | None,
48
+ status: PassengerStatus,
49
+ number_of_passengers: int,
50
+ previous_legs: list[VisualizedLeg],
51
+ current_leg: VisualizedLeg | None,
52
+ next_legs: list[VisualizedLeg],
53
+ tags: list[str],
54
+ ) -> None:
55
+ self.passenger_id: str = passenger_id
56
+ self.name: str | None = name
57
+ self.status: PassengerStatus = status
58
+ self.number_of_passengers: int = number_of_passengers
59
+
60
+ self.previous_legs: list[VisualizedLeg] = previous_legs
61
+ self.current_leg: VisualizedLeg | None = current_leg
62
+ self.next_legs: list[VisualizedLeg] = next_legs
63
+
64
+ self.tags: list[str] = tags
65
+
66
+ @classmethod
67
+ def from_trip_and_environment(cls, trip: Trip, environment: Environment) -> "VisualizedPassenger":
68
+ previous_legs = [
69
+ VisualizedLeg.from_leg_environment_and_trip(leg, environment, trip, LegType.PREVIOUS)
70
+ for leg in trip.previous_legs
71
+ ]
72
+ current_leg = (
73
+ VisualizedLeg.from_leg_environment_and_trip(trip.current_leg, environment, trip, LegType.CURRENT)
74
+ if trip.current_leg is not None
75
+ else None
76
+ )
77
+ next_legs = [
78
+ VisualizedLeg.from_leg_environment_and_trip(leg, environment, trip, LegType.NEXT) for leg in trip.next_legs
79
+ ]
80
+
81
+ return cls(
82
+ trip.id, trip.name, trip.status, trip.nb_passengers, previous_legs, current_leg, next_legs, trip.tags
83
+ )
84
+
85
+ @property
86
+ def all_legs(self) -> list[VisualizedLeg]:
87
+ """
88
+ Returns all legs of the passenger, including previous, current, and next legs.
89
+ """
90
+ return self.previous_legs + ([self.current_leg] if self.current_leg is not None else []) + self.next_legs
91
+
92
+ # Similar to VisualizedVehicle
93
+ # pylint: disable=duplicate-code
94
+ def serialize(self) -> dict:
95
+ serialized = {
96
+ "id": self.passenger_id,
97
+ "status": convert_passenger_status_to_string(self.status),
98
+ "numberOfPassengers": self.number_of_passengers,
99
+ }
100
+
101
+ if self.name is not None:
102
+ serialized["name"] = self.name
103
+
104
+ serialized["previousLegs"] = [leg.serialize() for leg in self.previous_legs]
105
+
106
+ if self.current_leg is not None:
107
+ serialized["currentLeg"] = self.current_leg.serialize()
108
+
109
+ serialized["nextLegs"] = [leg.serialize() for leg in self.next_legs]
110
+
111
+ if len(self.tags) > 0:
112
+ serialized["tags"] = self.tags
113
+
114
+ return serialized
115
+
116
+ @classmethod
117
+ def deserialize(cls, serialized_data: dict | str) -> "VisualizedPassenger":
118
+ serialized_data = cls.serialized_data_to_dict(serialized_data)
119
+
120
+ required_keys = [
121
+ "id",
122
+ "status",
123
+ "previousLegs",
124
+ "nextLegs",
125
+ "numberOfPassengers",
126
+ ]
127
+
128
+ cls.verify_required_fields(serialized_data, required_keys, "VisualizedPassenger")
129
+
130
+ passenger_id = str(serialized_data["id"])
131
+ name = serialized_data.get("name", None)
132
+ status = convert_string_to_passenger_status(serialized_data["status"])
133
+ number_of_passengers = int(serialized_data["numberOfPassengers"])
134
+
135
+ previous_legs = [VisualizedLeg.deserialize(leg_data) for leg_data in serialized_data["previousLegs"]]
136
+ next_legs = [VisualizedLeg.deserialize(leg_data) for leg_data in serialized_data["nextLegs"]]
137
+
138
+ current_leg = serialized_data.get("currentLeg", None)
139
+ if current_leg is not None:
140
+ current_leg = VisualizedLeg.deserialize(current_leg)
141
+
142
+ tags = serialized_data.get("tags", [])
143
+
144
+ return VisualizedPassenger(
145
+ passenger_id, name, status, number_of_passengers, previous_legs, current_leg, next_legs, tags
146
+ )
147
+
148
+ # pylint: enable=duplicate-code
@@ -0,0 +1,43 @@
1
+ from json import loads
2
+
3
+
4
+ # MARK: Serializable
5
+ class Serializable:
6
+ def serialize(self) -> dict:
7
+ """
8
+ Serialize the instance into a dictionary.
9
+
10
+ This method should be implemented by subclasses.
11
+ """
12
+
13
+ @classmethod
14
+ def deserialize(cls, serialized_data: dict | str) -> "Serializable":
15
+ """
16
+ Deserialize a dictionary into an instance of the class.
17
+
18
+ If the dictionary is not valid, raise a `ValueError`.
19
+
20
+ This method should be implemented by subclasses.
21
+ """
22
+
23
+ @staticmethod
24
+ def serialized_data_to_dict(serialized_data: dict | str) -> dict:
25
+ """
26
+ Parse the serialized data into a dictionary if it is a string.
27
+
28
+ This method should be called before each deserialization.
29
+ """
30
+ if isinstance(serialized_data, str):
31
+ return loads(serialized_data)
32
+ return serialized_data
33
+
34
+ @staticmethod
35
+ def verify_required_fields(serialized_data: dict, required_fields: list[str], class_name: str) -> None:
36
+ """
37
+ Verify that the serialized data contains all required fields.
38
+
39
+ If any field is missing, raise a `ValueError`.
40
+ """
41
+ for field in required_fields:
42
+ if field not in serialized_data:
43
+ raise ValueError(f"Serialized data of {class_name} must contain '{field}' key")
@@ -0,0 +1,84 @@
1
+ from multimodalsim_viewer.common.utils import (
2
+ SAVE_VERSION,
3
+ SIMULATION_SAVE_FILE_SEPARATOR,
4
+ )
5
+ from multimodalsim_viewer.models.serializable import Serializable
6
+
7
+
8
+ # MARK: SimulationInformation
9
+ class SimulationInformation(Serializable): # pylint: disable=too-many-instance-attributes
10
+
11
+ def __init__( # pylint: disable=too-many-arguments, too-many-positional-arguments
12
+ self,
13
+ simulation_id: str,
14
+ data: str,
15
+ simulation_start_time: float | None,
16
+ simulation_end_time: float | None,
17
+ last_update_index: int | None,
18
+ version: int | None,
19
+ ) -> None:
20
+ self.version: int = version
21
+ if self.version is None:
22
+ self.version = SAVE_VERSION
23
+
24
+ self.simulation_id: str = simulation_id
25
+
26
+ self.name: str = simulation_id.split(SIMULATION_SAVE_FILE_SEPARATOR)[1]
27
+ self.start_time: str = simulation_id.split(SIMULATION_SAVE_FILE_SEPARATOR)[0]
28
+ self.data: str = data
29
+
30
+ self.simulation_start_time: float | None = simulation_start_time
31
+ self.simulation_end_time: float | None = simulation_end_time
32
+ self.last_update_index: int | None = last_update_index
33
+
34
+ def serialize(self) -> dict:
35
+ serialized = {
36
+ "version": self.version,
37
+ "simulationId": self.simulation_id,
38
+ "name": self.name,
39
+ "startTime": self.start_time,
40
+ "data": self.data,
41
+ }
42
+
43
+ if self.simulation_start_time is not None:
44
+ serialized["simulationStartTime"] = self.simulation_start_time
45
+
46
+ if self.simulation_end_time is not None:
47
+ serialized["simulationEndTime"] = self.simulation_end_time
48
+
49
+ if self.last_update_index is not None:
50
+ serialized["lastUpdateIndex"] = self.last_update_index
51
+
52
+ return serialized
53
+
54
+ @classmethod
55
+ def deserialize(cls, serialized_data: dict | str) -> "SimulationInformation":
56
+ serialized_data = cls.serialized_data_to_dict(serialized_data)
57
+
58
+ required_keys = ["version", "simulationId", "data"]
59
+ cls.verify_required_fields(serialized_data, required_keys, "SimulationInformation")
60
+
61
+ version = int(serialized_data["version"])
62
+ simulation_id = str(serialized_data["simulationId"])
63
+ simulation_data = str(serialized_data["data"])
64
+
65
+ simulation_start_time = serialized_data.get("simulationStartTime", None)
66
+ if simulation_start_time is not None:
67
+ simulation_start_time = float(simulation_start_time)
68
+
69
+ simulation_end_time = serialized_data.get("simulationEndTime", None)
70
+ if simulation_end_time is not None:
71
+ simulation_end_time = float(simulation_end_time)
72
+
73
+ last_update_index = serialized_data.get("lastUpdateIndex", None)
74
+ if last_update_index is not None:
75
+ last_update_index = int(last_update_index)
76
+
77
+ return SimulationInformation(
78
+ simulation_id,
79
+ simulation_data,
80
+ simulation_start_time,
81
+ simulation_end_time,
82
+ last_update_index,
83
+ version,
84
+ )
@@ -0,0 +1,44 @@
1
+ from multimodalsim_viewer.models.environment import VisualizedEnvironment
2
+ from multimodalsim_viewer.models.update import Update
3
+
4
+
5
+ # MARK: State
6
+ class VisualizedState(VisualizedEnvironment):
7
+ def __init__(self) -> None:
8
+ super().__init__()
9
+ self.updates: list[Update] = []
10
+
11
+ @classmethod
12
+ def from_environment(cls, environment: VisualizedEnvironment) -> "VisualizedState":
13
+ state = cls()
14
+ state.passengers = environment.passengers
15
+ state.vehicles = environment.vehicles
16
+ state.timestamp = environment.timestamp
17
+ state.estimated_end_time = environment.estimated_end_time
18
+ state.update_index = environment.update_index
19
+ return state
20
+
21
+ def serialize(self) -> dict:
22
+ serialized = super().serialize()
23
+
24
+ serialized["updates"] = [update.serialize() for update in self.updates]
25
+
26
+ return serialized
27
+
28
+ @classmethod
29
+ def deserialize(cls, serialized_data: dict | str) -> "VisualizedState":
30
+ serialized_data = cls.serialized_data_to_dict(serialized_data)
31
+
32
+ environment = VisualizedEnvironment.deserialize(serialized_data)
33
+
34
+ required_keys = ["updates"]
35
+
36
+ cls.verify_required_fields(serialized_data, required_keys, "VisualizedState")
37
+
38
+ state = cls.from_environment(environment)
39
+
40
+ for update_data in serialized_data["updates"]:
41
+ update = Update.deserialize(update_data)
42
+ state.updates.append(update)
43
+
44
+ return state