multimodalsim-viewer 0.0.3__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.
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/PKG-INFO +6 -12
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/README.md +6 -12
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/common/environments/.env +2 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/common/utils.py +11 -48
- multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/environment.py +70 -0
- multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/leg.py +194 -0
- multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/passenger.py +148 -0
- multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/serializable.py +43 -0
- multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/simulation_information.py +84 -0
- multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/state.py +44 -0
- multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/stop.py +114 -0
- multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/update.py +616 -0
- multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models/vehicle.py +151 -0
- multimodalsim_viewer-0.0.3/multimodalsim_viewer/server/simulation_visualization_data_collector.py → multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/server/data_collector.py +185 -198
- multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/server/data_manager.py +567 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/server/http_routes.py +4 -7
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/server/log_manager.py +2 -2
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/server/server.py +8 -10
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/server/simulation.py +4 -5
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/server/simulation_manager.py +22 -23
- multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/__init__.py +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/environment.json +2 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/index.html +2 -2
- multimodalsim_viewer-0.0.3/multimodalsim_viewer/ui/static/main-LUPJCMAF.js → multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/main-EAYQBWLP.js +173 -173
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/scripts/load-environment.script.js +1 -1
- multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/ui/static/styles-257KETL3.css +1 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer.egg-info/PKG-INFO +6 -12
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer.egg-info/SOURCES.txt +14 -4
- multimodalsim_viewer-0.0.3/multimodalsim_viewer/server/simulation_visualization_data_model.py +0 -1570
- multimodalsim_viewer-0.0.3/multimodalsim_viewer/ui/static/styles-KU7LTPET.css +0 -1
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/MANIFEST.in +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/__init__.py +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/common/__init__.py +0 -0
- {multimodalsim_viewer-0.0.3/multimodalsim_viewer/server → multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/models}/__init__.py +0 -0
- {multimodalsim_viewer-0.0.3/multimodalsim_viewer/ui → multimodalsim_viewer-0.1.0.0/multimodalsim_viewer/server}/__init__.py +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/server/scripts.py +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/angular_app.py +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/bitmap-fonts/custom-sans-serif.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/bitmap-fonts/custom-sans-serif.xml +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/chunk-BQ2VC5TN.js +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/chunk-RHGMGEGM.js +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/favicon.ico +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/images/control-bar.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/images/passenger.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/images/simulation-control-bar.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/images/stop.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/images/undefined-texture.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/images/vehicle.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/images/zoomed-out-passenger.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/images/zoomed-out-stop.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/images/zoomed-out-vehicle.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/media/layers-2x-TBM42ERR.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/media/layers-55W3Q4RM.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/media/marker-icon-2V3QKKVC.png +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/ui/static/polyfills-FFHMD2TL.js +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer.egg-info/dependency_links.txt +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer.egg-info/entry_points.txt +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer.egg-info/requires.txt +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer.egg-info/top_level.txt +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/pyproject.toml +0 -0
- {multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/setup.cfg +0 -0
- {multimodalsim_viewer-0.0.3 → 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.
|
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.
|
{multimodalsim_viewer-0.0.3 → multimodalsim_viewer-0.1.0.0}/multimodalsim_viewer/common/utils.py
RENAMED
@@ -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 =
|
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")
|