-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Common functionality for SUMO CARMA Messenger Vehicle Plugin (#226)
<!-- Thanks for the contribution, this is awesome. --> # PR Details ## Description This PR introduces the implementation of the CARMA-messenger vehicle plugin for CDASim, aligning with the [High-level Design for Freight ERV functionality](https://usdot-carma.atlassian.net/wiki/spaces/CRMSIM/pages/3107979271/High-level+Design+for+Freight+ERV+functionality) documentation ## Related GitHub Issue <!--- This project only accepts pull requests related to open GitHub issues or Jira Keys --> <!--- If suggesting a new feature or change, please discuss it in an issue first --> <!--- If fixing a bug, there should be an issue describing it with steps to reproduce --> <!--- Please DO NOT name partially fixed issues, instead open an issue specific to this fix --> <!--- Please link to the issue here: --> ## Related Jira Key [CXC-87](https://usdot-carma.atlassian.net/browse/CXC-87) ## Motivation and Context <!--- Why is this change required? What problem does it solve? --> ## How Has This Been Tested? <!--- Please describe in detail how you tested your changes. --> <!--- Include details of your testing environment, and the tests you ran to --> <!--- see how your change affects other areas of the code, etc. --> ## Types of changes <!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Defect fix (non-breaking change that fixes an issue) - [x] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that cause existing functionality to change) ## Checklist: <!--- Go over all the following points, and put an `x` in all the boxes that apply. --> <!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> - [ ] I have added any new packages to the sonar-scanner.properties file - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have read the [**CONTRIBUTING**](https://github.com/usdot-fhwa-stol/carma-platform/blob/develop/Contributing.md) document. - [x] I have added tests to cover my changes. - [x] All new and existing tests passed. [CXC-87]: https://usdot-carma.atlassian.net/browse/CXC-87?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
- Loading branch information
Showing
7 changed files
with
692 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
# Copyright (C) 2022 LEIDOS. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
# use this file except in compliance with the License. You may obtain a copy of | ||
# the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
# License for the specific language governing permissions and limitations under | ||
# the License. | ||
|
||
import json | ||
import logging | ||
from enum import Enum | ||
|
||
class VehicleState(Enum): | ||
NOT_CREATED = 0 | ||
CREATED_AND_DRIVING = 1 | ||
FINISHED_AND_DESTROYED = 2 | ||
|
||
|
||
class MsgerVehicleCfg: | ||
|
||
def _setup_logging(self, level): | ||
""" | ||
Set up logging with the specified level. | ||
Parameters: | ||
- level: The log level to set up. | ||
type: str | ||
""" | ||
numeric_level = getattr(logging, level.upper(), None) | ||
if not isinstance(numeric_level, int): | ||
raise ValueError('Invalid log level: %s' % level) | ||
logging.basicConfig(level=numeric_level, format='%(asctime)s - %(levelname)s - %(message)s') | ||
|
||
def __init__(self, cfg_path, log_level='INFO'): | ||
""" | ||
Initializes the vehicle configuration manager by loading vehicle data from a JSON file. | ||
Each vehicle's configuration is stored in the dictionary `_msger_veh_dict`, keyed by the vehicle's ID. This dictionary includes: | ||
- 'route': list of strings representing the sequence of edges the vehicle will travel over in the simulation. | ||
- 'speed': float, the maximum speed of the vehicle in meters per second. | ||
- 'departureTime': int, the simulation time at which the vehicle starts. | ||
- 'lcm': int, an integer representing the lane change mode using SUMO's encoding. | ||
- 'cfm': str, the car following model that determines how the vehicle follows other vehicles. | ||
Parameters: | ||
- cfg_path: The file path to the JSON configuration file containing messenger vehicle data. | ||
type: str | ||
""" | ||
|
||
self._setup_logging(log_level) | ||
self._msger_veh_dict = {} | ||
self._veh_state_dict = {} | ||
try: | ||
with open(cfg_path, 'r') as cfg_file: | ||
config_json = json.load(cfg_file) | ||
for veh in config_json["msgerVehs"]: | ||
veh_id = veh["id"] | ||
if veh_id in self._msger_veh_dict: | ||
logging.warning(f"Duplicate vehicle ID detected and skipped: {veh_id}") | ||
continue | ||
self._msger_veh_dict[veh_id] = { | ||
"route": veh["route"], | ||
"speed": veh["speed"], | ||
"departureTime": veh["departureTime"], | ||
"lcm": veh["lcm"], | ||
"cfm": veh["cfm"] | ||
} | ||
self._veh_state_dict[veh_id] = VehicleState.NOT_CREATED | ||
logging.info("Vehicle configuration loaded successfully.") | ||
except FileNotFoundError: | ||
logging.error("Configuration file not found. Please check the file path.") | ||
except json.JSONDecodeError: | ||
logging.error("Error decoding JSON. Please check the input JSON file for errors.") | ||
except Exception as e: | ||
logging.error(f"An unexpected error occurred: {e}") | ||
|
||
def get_veh_route(self, veh_id): | ||
""" | ||
Retrieves the route for a specified vehicle ID. | ||
Parameters: | ||
- veh_id: The ID of the vehicle. | ||
type: str | ||
Returns: | ||
- list of str: The route associated with the vehicle. | ||
""" | ||
return self._msger_veh_dict[veh_id]["route"] | ||
|
||
def get_veh_speed(self, veh_id): | ||
""" | ||
Retrieves the speed setting for a specified vehicle ID. | ||
Parameters: | ||
- veh_id: The ID of the vehicle. | ||
type: str | ||
Returns: | ||
- float: The speed of the vehicle. | ||
""" | ||
return self._msger_veh_dict[veh_id]["speed"] | ||
|
||
def get_veh_ids(self): | ||
""" | ||
Retrieves a list of all vehicle IDs managed by this configuration. | ||
Returns: | ||
- list of str: A list of vehicle IDs. | ||
""" | ||
return list(self._msger_veh_dict.keys()) | ||
|
||
def get_veh_departure_time(self, veh_id): | ||
""" | ||
Retrieves the departure time for a specified vehicle ID. | ||
Parameters: | ||
- veh_id: The ID of the vehicle. | ||
type: str | ||
Returns: | ||
- int: The departure time of the vehicle. | ||
""" | ||
return self._msger_veh_dict[veh_id]["departureTime"] | ||
|
||
def get_veh_lcm(self, veh_id): | ||
""" | ||
Retrieves the lane change mode setting for a specified vehicle ID. | ||
Parameters: | ||
- veh_id: The ID of the vehicle. | ||
type: str | ||
Returns: | ||
- int: The lane change mode of the vehicle. | ||
""" | ||
return self._msger_veh_dict[veh_id]["lcm"] | ||
|
||
def get_veh_cfm(self, veh_id): | ||
""" | ||
Retrieves the car-following model setting for a specified vehicle ID. | ||
Parameters: | ||
- veh_id: The ID of the vehicle. | ||
type: str | ||
Returns: | ||
- str: The car-following model of the vehicle. | ||
""" | ||
return self._msger_veh_dict[veh_id]["cfm"] | ||
|
||
def set_veh_state(self, veh_id, state): | ||
""" | ||
Sets the state of a specified vehicle ID. | ||
Parameters: | ||
- veh_id: The ID of the vehicle. | ||
- state: The new state to set for the vehicle. | ||
type: int | ||
""" | ||
if veh_id in self._veh_state_dict: | ||
self._veh_state_dict[veh_id] = state | ||
logging.info("Set vehicle " + str(veh_id) + " state to " + state.name) | ||
else: | ||
logging.warning(f"Attempted to set state for a non-existent vehicle ID: {veh_id}") | ||
|
||
def get_veh_state(self, veh_id): | ||
""" | ||
Retrieves the state of a specified vehicle ID. | ||
Parameters: | ||
- veh_id: The ID of the vehicle. | ||
type: str | ||
Returns: | ||
- int: The current state of the vehicle. | ||
""" | ||
return self._veh_state_dict.get(veh_id, None) # Returns None if the vehicle ID doesn't exist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# Copyright (C) 2022 LEIDOS. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
# use this file except in compliance with the License. You may obtain a copy of | ||
# the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
# License for the specific language governing permissions and limitations under | ||
# the License. | ||
|
||
import argparse | ||
import logging | ||
from sumo_connector import SumoConnector | ||
from msger_veh_cfg import MsgerVehicleCfg | ||
from msger_veh_cfg import VehicleState | ||
|
||
def setup_logging(level): | ||
numeric_level = getattr(logging, level.upper(), None) | ||
if not isinstance(numeric_level, int): | ||
raise ValueError('Invalid log level: %s' % level) | ||
logging.basicConfig(level=numeric_level, format='%(asctime)s - %(levelname)s - %(message)s') | ||
|
||
def run(args): | ||
""" | ||
Executes the vehicle simulation management. This function initializes the vehicle configurations, | ||
connects to SUMO via TraCI, and manages the simulation steps and vehicle states. | ||
Parameters: | ||
- args: Command line arguments containing settings for Traci connection and configuration path. | ||
type: Namespace | ||
""" | ||
setup_logging(args.log_level) | ||
try: | ||
msger_veh_cfg = MsgerVehicleCfg(args.msger_veh_cfg_path, log_level=args.log_level) | ||
sumo_connector = SumoConnector(args.traci_ip, args.traci_port, args.traci_order_num, log_level=args.log_level) | ||
msg_veh_ids = msger_veh_cfg.get_veh_ids() | ||
with sumo_connector.traci_handler(): | ||
while True: | ||
sumo_connector.tick() | ||
sumo_veh_ids = sumo_connector.get_veh_ids() | ||
for msg_veh_id in msg_veh_ids: | ||
|
||
if msg_veh_id not in sumo_veh_ids and \ | ||
msger_veh_cfg.get_veh_state(msg_veh_id) == VehicleState.NOT_CREATED and \ | ||
msger_veh_cfg.get_veh_departure_time(msg_veh_id) <= sumo_connector.get_sim_time(): | ||
### Init | ||
logging.info("Adding new vehicle with ID: " + msg_veh_id) | ||
sumo_connector.add_veh(msg_veh_id) | ||
sumo_connector.set_veh_route(msg_veh_id, msger_veh_cfg.get_veh_route(msg_veh_id)) | ||
sumo_connector.set_veh_speed(msg_veh_id, msger_veh_cfg.get_veh_speed(msg_veh_id)) | ||
sumo_connector.set_veh_lcm(msg_veh_id, msger_veh_cfg.get_veh_lcm(msg_veh_id)) | ||
sumo_connector.set_veh_type(msg_veh_id, msger_veh_cfg.get_veh_cfm(msg_veh_id)) | ||
msger_veh_cfg.set_veh_state(msg_veh_id, VehicleState.CREATED_AND_DRIVING) | ||
elif msg_veh_id not in sumo_veh_ids and msger_veh_cfg.get_veh_state(msg_veh_id) == VehicleState.CREATED_AND_DRIVING: | ||
### Remove | ||
msger_veh_cfg.set_veh_state(msg_veh_id, VehicleState.FINISHED_AND_DESTROYED) | ||
logging.info("Vehicle " + msg_veh_id + " finished route.") | ||
|
||
## TODO | ||
|
||
except Exception as e: | ||
logging.error(f"An error occurred during simulation: {e}") | ||
|
||
if __name__ == "__main__": | ||
parser = argparse.ArgumentParser(description='Script to manage messenger vehicles in a SUMO simulation.') | ||
parser.add_argument('--traci-ip', default="localhost", help='IP address for Traci to connect to SUMO.') | ||
parser.add_argument('--traci-port', default=2010, help='Port number for Traci to connect to SUMO.') | ||
parser.add_argument('--traci-order-num', default=2, help='Traci connection order number for SUMO multi-client setup.') | ||
parser.add_argument('--msger-veh-cfg-path', default="resources/msger_veh_cfg.json", help='Path to the messenger vehicle configuration JSON file.') | ||
parser.add_argument('--log-level', default='INFO', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], | ||
help='Set the logging level') | ||
args = parser.parse_args() | ||
run(args) |
33 changes: 33 additions & 0 deletions
33
carma_messenger_vehicle_plugin/src/resources/msger_veh_cfg.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"msgerVehs": | ||
[ | ||
{ | ||
"id": "vehicle_1", | ||
"route": [ "1_26704526_26704482", | ||
"32909782_26704482_26785753", | ||
"25185001_26785753_26704584", | ||
"25185007_26704584_21487146", | ||
"25185006_21487146_21487168", | ||
"4068038_21487168_251150126", | ||
"4068038_251150126_428788319"], | ||
"speed": 15, | ||
"departureTime": 5, | ||
"lcm" : 0, | ||
"cfm" : "FreightERV" | ||
}, | ||
{ | ||
"id": "vehicle_2", | ||
"route": [ "1_26704526_26704482", | ||
"32909782_26704482_26785753", | ||
"25185001_26785753_26704584", | ||
"25185007_26704584_21487146", | ||
"25185006_21487146_21487168", | ||
"4068038_21487168_251150126", | ||
"4068038_251150126_428788319"], | ||
"speed": 10, | ||
"departureTime": 10, | ||
"lcm" : 0, | ||
"cfm" : "FreightERV" | ||
} | ||
] | ||
} |
Oops, something went wrong.