Skip to content

Commit

Permalink
Common functionality for SUMO CARMA Messenger Vehicle Plugin (#226)
Browse files Browse the repository at this point in the history
<!-- 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
kjrush authored Oct 1, 2024
2 parents a40be1a + 4d7922b commit aa079b5
Show file tree
Hide file tree
Showing 7 changed files with 692 additions and 0 deletions.
183 changes: 183 additions & 0 deletions carma_messenger_vehicle_plugin/src/msger_veh_cfg.py
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
77 changes: 77 additions & 0 deletions carma_messenger_vehicle_plugin/src/msger_veh_wrapper.py
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 carma_messenger_vehicle_plugin/src/resources/msger_veh_cfg.json
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"
}
]
}
Loading

0 comments on commit aa079b5

Please sign in to comment.