multimodalsim-viewer 0.0.2__tar.gz → 0.1.0.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 (64) hide show
  1. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/PKG-INFO +6 -12
  2. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/README.md +6 -12
  3. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/common/environments/.env +2 -0
  4. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/common/utils.py +11 -48
  5. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/environment.py +70 -0
  6. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/leg.py +194 -0
  7. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/passenger.py +148 -0
  8. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/serializable.py +43 -0
  9. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/simulation_information.py +84 -0
  10. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/state.py +44 -0
  11. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/stop.py +114 -0
  12. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/update.py +616 -0
  13. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/vehicle.py +151 -0
  14. multimodalsim_viewer-0.0.2/multimodalsim_viewer/server/simulation_visualization_data_collector.py → multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/server/data_collector.py +185 -198
  15. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/server/data_manager.py +567 -0
  16. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/server/http_routes.py +4 -7
  17. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/server/log_manager.py +2 -2
  18. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/server/server.py +8 -10
  19. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/server/simulation.py +4 -5
  20. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/server/simulation_manager.py +22 -23
  21. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/__init__.py +0 -0
  22. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/chunk-BQ2VC5TN.js +7 -0
  23. multimodalsim_viewer-0.0.2/multimodalsim_viewer/ui/static/chunk-IGIP6IOU.js → multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/chunk-RHGMGEGM.js +1 -1
  24. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/environment.json +2 -0
  25. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/images/undefined-texture.png +0 -0
  26. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/images/zoomed-out-stop.png +0 -0
  27. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/index.html +2 -2
  28. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/main-EAYQBWLP.js +3648 -0
  29. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/scripts/load-environment.script.js +1 -1
  30. multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/styles-257KETL3.css +1 -0
  31. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer.egg-info/PKG-INFO +6 -12
  32. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer.egg-info/SOURCES.txt +23 -11
  33. multimodalsim_viewer-0.0.2/multimodalsim_viewer/server/simulation_visualization_data_model.py +0 -1552
  34. multimodalsim_viewer-0.0.2/multimodalsim_viewer/ui/static/chunk-6VAXIXEZ.js +0 -7
  35. multimodalsim_viewer-0.0.2/multimodalsim_viewer/ui/static/main-FGMGJ32M.js +0 -3648
  36. multimodalsim_viewer-0.0.2/multimodalsim_viewer/ui/static/styles-KU7LTPET.css +0 -1
  37. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/MANIFEST.in +0 -0
  38. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/__init__.py +0 -0
  39. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/common/__init__.py +0 -0
  40. {multimodalsim_viewer-0.0.2/multimodalsim_viewer/server → multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models}/__init__.py +0 -0
  41. {multimodalsim_viewer-0.0.2/multimodalsim_viewer/ui → multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/server}/__init__.py +0 -0
  42. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/server/scripts.py +0 -0
  43. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/angular_app.py +0 -0
  44. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/bitmap-fonts/custom-sans-serif.png +0 -0
  45. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/bitmap-fonts/custom-sans-serif.xml +0 -0
  46. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/favicon.ico +0 -0
  47. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/images/control-bar.png +0 -0
  48. /multimodalsim_viewer-0.0.2/multimodalsim_viewer/ui/static/images/sample-wait.png → /multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/images/passenger.png +0 -0
  49. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/images/simulation-control-bar.png +0 -0
  50. /multimodalsim_viewer-0.0.2/multimodalsim_viewer/ui/static/images/sample-stop.png → /multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/images/stop.png +0 -0
  51. /multimodalsim_viewer-0.0.2/multimodalsim_viewer/ui/static/images/sample-bus.png → /multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/images/vehicle.png +0 -0
  52. /multimodalsim_viewer-0.0.2/multimodalsim_viewer/ui/static/images/zoom-out-passenger.png → /multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/images/zoomed-out-passenger.png +0 -0
  53. /multimodalsim_viewer-0.0.2/multimodalsim_viewer/ui/static/images/zoom-out-vehicle.png → /multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/images/zoomed-out-vehicle.png +0 -0
  54. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/media/layers-2x-TBM42ERR.png +0 -0
  55. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/media/layers-55W3Q4RM.png +0 -0
  56. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/media/marker-icon-2V3QKKVC.png +0 -0
  57. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/polyfills-FFHMD2TL.js +0 -0
  58. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer.egg-info/dependency_links.txt +0 -0
  59. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer.egg-info/entry_points.txt +0 -0
  60. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer.egg-info/requires.txt +0 -0
  61. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer.egg-info/top_level.txt +0 -0
  62. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/pyproject.toml +0 -0
  63. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/setup.cfg +0 -0
  64. {multimodalsim_viewer-0.0.2 → multimodalsim_viewer-0.1.0.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multimodalsim_viewer
3
- Version: 0.0.2
3
+ Version: 0.1.0.0
4
4
  Summary: Multimodal simulation viewer
5
5
  Keywords: flask angular ui multimodal server
6
6
  Requires-Python: >=3.10
@@ -37,23 +37,13 @@ This package provides an interface to the [multimodalsim simulation project](htt
37
37
 
38
38
  ## Usage
39
39
 
40
- You have access to several commands that will allow you to run the project easily.
40
+ You have access to several commands that will allow you to run the project easily. You can use the `--help` flag to see a list of available options.
41
41
 
42
42
  ```bash
43
43
  viewer start
44
- viewer start --ui # only start the UI side
45
- viewer start --server # only start the server
46
44
 
47
45
  viewer stop
48
- viewer stop --ui # only stop the UI side
49
- viewer stop --server # only stop the server
50
- ```
51
-
52
- You can also run a simulation from the command line. This is useful for debugging, when you want to run a simulation without the web interface, and also for running simulations that uses a different version of the multimodalsim package.
53
-
54
- Several arguments are available to customize the simulation and can be found with the --help option, but the required arguments will be asked interactively if not provided. The command to run a simulation is:
55
46
 
56
- ```bash
57
47
  viewer simulate
58
48
  ```
59
49
 
@@ -68,3 +58,7 @@ To run a simulation, you need to provide input data. You can upload input data f
68
58
  ```bash
69
59
  git clone https://github.com/lab-core/multimodal-data.git
70
60
  ```
61
+
62
+ ## Environment variables
63
+
64
+ Some environment variables are available to customize the application. You can find a detailed list of these variables in the [README.md](https://github.com/lab-core/multimodal-simulator/blob/main/README.md#environment-variables) file of the multimodal-simulator repository.
@@ -4,23 +4,13 @@ This package provides an interface to the [multimodalsim simulation project](htt
4
4
 
5
5
  ## Usage
6
6
 
7
- You have access to several commands that will allow you to run the project easily.
7
+ You have access to several commands that will allow you to run the project easily. You can use the `--help` flag to see a list of available options.
8
8
 
9
9
  ```bash
10
10
  viewer start
11
- viewer start --ui # only start the UI side
12
- viewer start --server # only start the server
13
11
 
14
12
  viewer stop
15
- viewer stop --ui # only stop the UI side
16
- viewer stop --server # only stop the server
17
- ```
18
-
19
- You can also run a simulation from the command line. This is useful for debugging, when you want to run a simulation without the web interface, and also for running simulations that uses a different version of the multimodalsim package.
20
13
 
21
- Several arguments are available to customize the simulation and can be found with the --help option, but the required arguments will be asked interactively if not provided. The command to run a simulation is:
22
-
23
- ```bash
24
14
  viewer simulate
25
15
  ```
26
16
 
@@ -34,4 +24,8 @@ To run a simulation, you need to provide input data. You can upload input data f
34
24
 
35
25
  ```bash
36
26
  git clone https://github.com/lab-core/multimodal-data.git
37
- ```
27
+ ```
28
+
29
+ ## Environment variables
30
+
31
+ Some environment variables are available to customize the application. You can find a detailed list of these variables in the [README.md](https://github.com/lab-core/multimodal-simulator/blob/main/README.md#environment-variables) file of the multimodal-simulator repository.
@@ -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)
@@ -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")