From 5166db7fa500efc2824baf8cee5a84517598d076 Mon Sep 17 00:00:00 2001 From: Saikrishna Bairamoni <84093461+SaikrishnaBairamoni@users.noreply.github.com> Date: Tue, 14 Feb 2023 10:19:35 -0500 Subject: [PATCH 001/124] install.sh: wget CARLA_0.9.10.tar.gz (#102) # PR Details Added wget "https://carla-releases.s3.eu-west-3.amazonaws.com/Linux/CARLA_0.9.10.tar.gz" line to download the CARKA_0.9.10.tar.gz file which required during build runtime. ## Description ## Related Issue ## Motivation and Context ## How Has This Been Tested? ## Types of changes - [ ] Defect fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that cause existing functionality to change) ## Checklist: - [ ] 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** document. [CARMA Contributing Guide](Contributing.md) - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. --- docker/install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/install.sh b/docker/install.sh index d2be8e09..3d7a3c2e 100755 --- a/docker/install.sh +++ b/docker/install.sh @@ -88,6 +88,7 @@ python3.7 -m pip install lxml==4.5.0 # Install CARLA cd /home/carma/src/ +wget "https://carla-releases.s3.eu-west-3.amazonaws.com/Linux/CARLA_0.9.10.tar.gz" if [[ ! -f '/home/carma/src/CARLA_0.9.10.tar.gz' ]]; then echo "!!! CARLA not present in the installation directy, please download CARLA_0.9.10.tar.gz into the work directory and rebuild. !!!" exit -1 From 7a888babe9257b516e2bec861ce7c3a7e80fadbc Mon Sep 17 00:00:00 2001 From: Cody Garver Date: Tue, 14 Feb 2023 12:55:10 -0500 Subject: [PATCH 002/124] Add GitHub workflows (#100) # PR Details ## Description ## Related Issue ## Motivation and Context ## How Has This Been Tested? ## Types of changes - [ ] Defect fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that cause existing functionality to change) ## Checklist: - [ ] 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** document. [CARMA Contributing Guide](Contributing.md) - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. --- .github/workflows/docker.yml | 13 +++++++++++++ .github/workflows/dockerhub.yml | 15 +++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/dockerhub.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 00000000..e344f4b8 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,13 @@ +name: Docker build + +on: + push: + branches-ignore: + - "develop" + - "release/*" + pull_request: + types: [opened, synchronize, reopened] + +jobs: + docker: + uses: usdot-fhwa-stol/actions/.github/workflows/docker.yml@main diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml new file mode 100644 index 00000000..0e2ff0ad --- /dev/null +++ b/.github/workflows/dockerhub.yml @@ -0,0 +1,15 @@ +name: Docker Hub build + +on: + push: + branches: + - "develop" + - "master" + - "release/*" + +jobs: + dockerhub: + uses: usdot-fhwa-stol/actions/.github/workflows/dockerhub.yml@main + secrets: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} From 8a3bf65f2762537e5b1fb2633c34eda49f75abcb Mon Sep 17 00:00:00 2001 From: Cody Garver Date: Tue, 14 Feb 2023 13:43:53 -0500 Subject: [PATCH 003/124] install.sh: conditonally download CARLA tarball --- docker/install.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/install.sh b/docker/install.sh index 3d7a3c2e..b5f7ac8e 100755 --- a/docker/install.sh +++ b/docker/install.sh @@ -87,16 +87,16 @@ python3.7 -m pip install pip python3.7 -m pip install lxml==4.5.0 # Install CARLA +CARLA_TAR="CARLA_0.9.10.tar.gz" cd /home/carma/src/ -wget "https://carla-releases.s3.eu-west-3.amazonaws.com/Linux/CARLA_0.9.10.tar.gz" -if [[ ! -f '/home/carma/src/CARLA_0.9.10.tar.gz' ]]; then - echo "!!! CARLA not present in the installation directy, please download CARLA_0.9.10.tar.gz into the work directory and rebuild. !!!" - exit -1 +if [[ ! -f "$CARLA_TAR" ]]; then + echo "!!! $CARLA_TAR not present in the installation directory, downloading automatically instead. This could take a long time, consider downloading the file manually and placing it in the installation directory. !!!" + wget "https://carla-releases.s3.eu-west-3.amazonaws.com/Linux/CARLA_0.9.10.tar.gz" fi sudo mkdir -p /opt/carla sudo chown -R carma:carma /opt/carla -tar xzvf CARLA_0.9.10.tar.gz -C /opt/carla +tar xzvf "$CARLA_TAR" -C /opt/carla # Installation of Co-Simulation Tool wget "https://archive.apache.org/dist/maven/maven-3/3.8.3/binaries/apache-maven-3.8.3-bin.tar.gz" From 5c1be9610e277ef94396164c9e366d18a3f4e3b9 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 17 Feb 2023 12:22:13 -0500 Subject: [PATCH 004/124] Add traci-call set order function for multi-client cases --- .../ambassador/AbstractSumoAmbassador.java | 3 ++ .../mosaic/fed/sumo/traci/TraciClient.java | 9 ++++ .../sumo/traci/commands/TraciSetOrder.java | 45 +++++++++++++++++++ .../constants/CommandSimulationControl.java | 6 ++- 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/commands/TraciSetOrder.java diff --git a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/ambassador/AbstractSumoAmbassador.java b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/ambassador/AbstractSumoAmbassador.java index 7e12f989..56641010 100644 --- a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/ambassador/AbstractSumoAmbassador.java +++ b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/ambassador/AbstractSumoAmbassador.java @@ -427,6 +427,9 @@ protected void initTraci() throws InternalFederateException { // just to be sure make a failsafe traci = new TraciClient(sumoConfig, socket); + // set traci client order number for multi-clinets case + traci.setOrder(1); + if (traci.getCurrentVersion().getApiVersion() < SumoVersion.LOWEST.getApiVersion()) { throw new InternalFederateException(String.format( "The installed version of SUMO ( <= %s) is not compatible with Eclipse MOSAIC." diff --git a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/TraciClient.java b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/TraciClient.java index 13621746..17a4f303 100644 --- a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/TraciClient.java +++ b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/TraciClient.java @@ -17,6 +17,7 @@ import org.eclipse.mosaic.fed.sumo.config.CSumo; import org.eclipse.mosaic.fed.sumo.traci.commands.SimulationClose; +import org.eclipse.mosaic.fed.sumo.traci.commands.TraciSetOrder; import org.eclipse.mosaic.fed.sumo.traci.commands.SimulationGetVersion; import org.eclipse.mosaic.fed.sumo.traci.commands.SimulationTraciRequest; import org.eclipse.mosaic.fed.sumo.traci.facades.TraciPoiFacade; @@ -174,6 +175,14 @@ public void close() { } } + public void setOrder(int orderNum) { + try { + commandRegister.getOrCreate(TraciSetOrder.class).execute(this, orderNum); + } catch (TraciCommandException e) { + throw new InternalFederateException(e); + } + } + @Override public SumoVersion getCurrentVersion() { if (currentVersion == null) { diff --git a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/commands/TraciSetOrder.java b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/commands/TraciSetOrder.java new file mode 100644 index 00000000..ffbea8ad --- /dev/null +++ b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/commands/TraciSetOrder.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 Fraunhofer FOKUS and others. All rights reserved. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contact: mosaic@fokus.fraunhofer.de + */ + +package org.eclipse.mosaic.fed.sumo.traci.commands; + +import org.eclipse.mosaic.fed.sumo.traci.AbstractTraciCommand; +import org.eclipse.mosaic.fed.sumo.traci.TraciCommandException; +import org.eclipse.mosaic.fed.sumo.traci.TraciConnection; +import org.eclipse.mosaic.fed.sumo.traci.TraciVersion; +import org.eclipse.mosaic.fed.sumo.traci.complex.Status; +import org.eclipse.mosaic.fed.sumo.traci.constants.CommandSimulationControl; +import org.eclipse.mosaic.rti.api.InternalFederateException; + + +public class TraciSetOrder extends AbstractTraciCommand { + + public TraciSetOrder() { + super(TraciVersion.LOWEST); + + write() + .command(CommandSimulationControl.COMMAND_SET_ORDER) + .writeIntParam(); + } + + public void execute(TraciConnection traciCon, int orderNum) throws TraciCommandException, InternalFederateException { + super.execute(traciCon, orderNum); + } + + @Override + protected Void constructResult(Status status, Object... objects) { + return null; + } +} diff --git a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/constants/CommandSimulationControl.java b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/constants/CommandSimulationControl.java index 7594c1d0..35efff6e 100644 --- a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/constants/CommandSimulationControl.java +++ b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/constants/CommandSimulationControl.java @@ -31,5 +31,9 @@ public class CommandSimulationControl { * The TraCI closes the connection to any client, stops simulation and shuts down SUMO. */ public final static int COMMAND_CLOSE = 0x7f; -} + /** + * Set SUMO client order for multi-clients case + */ + public final static int COMMAND_SET_ORDER = 0x03; +} From 96af44f9c8004dcaf0babea39e45a2938e96441b Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Sun, 19 Feb 2023 21:21:29 -0500 Subject: [PATCH 005/124] Set fix port for sumo simulation --- co-simulation/bundle/src/assembly/resources/etc/runtime.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/co-simulation/bundle/src/assembly/resources/etc/runtime.json b/co-simulation/bundle/src/assembly/resources/etc/runtime.json index 3638bc9d..78f582e6 100644 --- a/co-simulation/bundle/src/assembly/resources/etc/runtime.json +++ b/co-simulation/bundle/src/assembly/resources/etc/runtime.json @@ -203,7 +203,7 @@ "configuration": "sumo_config.json", "priority": 50, "host": "local", - "port": 0, + "port": 2010, "deploy": true, "start": true, "subscriptions": [ @@ -271,4 +271,4 @@ "javaClasspathEntries": [] } ] -} \ No newline at end of file +} From fcbba94e63ac0575e178b66a03fe7cfe85c41cfd Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Sun, 19 Feb 2023 21:21:47 -0500 Subject: [PATCH 006/124] commit --- .../eclipse/mosaic/fed/sumo/traci/TraciClient.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/TraciClient.java b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/TraciClient.java index 17a4f303..c368975c 100644 --- a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/TraciClient.java +++ b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/TraciClient.java @@ -176,11 +176,13 @@ public void close() { } public void setOrder(int orderNum) { - try { - commandRegister.getOrCreate(TraciSetOrder.class).execute(this, orderNum); - } catch (TraciCommandException e) { - throw new InternalFederateException(e); - } + try { + commandRegister.getOrCreate(TraciSetOrder.class).execute(this, orderNum); + } catch (TraciCommandException e) { + e.printStackTrace(); + } catch (InternalFederateException e) { + e.printStackTrace(); + } } @Override From 825136161ea7873d3a6acdd88199ce54ccfea765 Mon Sep 17 00:00:00 2001 From: Cody Garver Date: Mon, 20 Feb 2023 12:51:10 -0500 Subject: [PATCH 007/124] Revert from carma-system-4.3.0 to develop --- .circleci/config.yml | 2 +- docker/checkout.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c2ab86ba..aed14948 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,7 +28,7 @@ jobs: # Pull docker image from docker hub # XTERM used for better catkin_make output docker: - - image: usdotfhwastol/carma-base:carma-system-4.3.0 + - image: usdotfhwastoldev/carma-base:develop user: carma environment: TERM: xterm # use xterm to get full display output from build diff --git a/docker/checkout.sh b/docker/checkout.sh index 134e8939..ad545521 100755 --- a/docker/checkout.sh +++ b/docker/checkout.sh @@ -39,6 +39,6 @@ if [[ "$BRANCH" = "develop" ]]; then git clone https://github.com/usdot-fhwa-stol/carma-msgs.git ~/src/carma-msgs --branch $BRANCH --depth 1 git clone https://github.com/usdot-fhwa-stol/carma-utils.git ~/src/carma-utils --branch $BRANCH --depth 1 else - git clone https://github.com/usdot-fhwa-stol/carma-msgs.git ${dir}/src/carma-msgs --branch carma-system-4.3.0 --depth 1 - git clone https://github.com/usdot-fhwa-stol/carma-utils.git ${dir}/src/carma-utils --branch carma-system-4.3.0 --depth 1 + git clone https://github.com/usdot-fhwa-stol/carma-msgs.git ${dir}/src/carma-msgs --branch develop --depth 1 + git clone https://github.com/usdot-fhwa-stol/carma-utils.git ${dir}/src/carma-utils --branch develop --depth 1 fi From 70a766902b3f0334323424319ba46f6f919299af Mon Sep 17 00:00:00 2001 From: Cody Garver Date: Mon, 20 Feb 2023 14:49:18 -0500 Subject: [PATCH 008/124] Revert install.sh changes from master --- docker/install.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/install.sh b/docker/install.sh index 3d7a3c2e..b5f7ac8e 100755 --- a/docker/install.sh +++ b/docker/install.sh @@ -87,16 +87,16 @@ python3.7 -m pip install pip python3.7 -m pip install lxml==4.5.0 # Install CARLA +CARLA_TAR="CARLA_0.9.10.tar.gz" cd /home/carma/src/ -wget "https://carla-releases.s3.eu-west-3.amazonaws.com/Linux/CARLA_0.9.10.tar.gz" -if [[ ! -f '/home/carma/src/CARLA_0.9.10.tar.gz' ]]; then - echo "!!! CARLA not present in the installation directy, please download CARLA_0.9.10.tar.gz into the work directory and rebuild. !!!" - exit -1 +if [[ ! -f "$CARLA_TAR" ]]; then + echo "!!! $CARLA_TAR not present in the installation directory, downloading automatically instead. This could take a long time, consider downloading the file manually and placing it in the installation directory. !!!" + wget "https://carla-releases.s3.eu-west-3.amazonaws.com/Linux/CARLA_0.9.10.tar.gz" fi sudo mkdir -p /opt/carla sudo chown -R carma:carma /opt/carla -tar xzvf CARLA_0.9.10.tar.gz -C /opt/carla +tar xzvf "$CARLA_TAR" -C /opt/carla # Installation of Co-Simulation Tool wget "https://archive.apache.org/dist/maven/maven-3/3.8.3/binaries/apache-maven-3.8.3-bin.tar.gz" From 2e9e5e89cb5bd8946926dc912e00247590363d88 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Mon, 20 Feb 2023 21:21:16 -0500 Subject: [PATCH 009/124] Add get_traffic_light_live_state function in CARLA-MOSAIC bridge --- .../carla_integration/sumo_simulation.py | 30 +++++++++++++++++-- .../carla_integration/synchronization.py | 10 ++++++- co-simulation/bridge/carla_mosaic_bridge.py | 2 +- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/co-simulation/bridge/carla_integration/sumo_simulation.py b/co-simulation/bridge/carla_integration/sumo_simulation.py index 6db6695e..df036ff1 100644 --- a/co-simulation/bridge/carla_integration/sumo_simulation.py +++ b/co-simulation/bridge/carla_integration/sumo_simulation.py @@ -163,19 +163,20 @@ def __init__(self): self._current_phase = {} # {tlid: index_phase} for tlid in traci.trafficlight.getIDList(): - self.subscribe(tlid) self._tls[tlid] = {} for tllogic in traci.trafficlight.getAllProgramLogics(tlid): + states = [phase.state for phase in tllogic.getPhases()] parameters = tllogic.getParameters() tl = SumoTLLogic(tlid, states, parameters) self._tls[tlid][tllogic.programID] = tl + # Get current status of the traffic lights. self._current_program[tlid] = traci.trafficlight.getProgram(tlid) self._current_phase[tlid] = traci.trafficlight.getPhase(tlid) - + # print(self._tls) self._off = False @staticmethod @@ -191,6 +192,7 @@ def subscribe(tlid): traci.constants.TL_CURRENT_PHASE, ]) + @staticmethod def unsubscribe(tlid): """ @@ -226,6 +228,26 @@ def get_all_associated_signals(self, landmark_id): signals.update(self._tls[tlid][program_id].get_associated_signals(landmark_id)) return signals + def get_live_state(self, landmark_id): + link_index_list = [] + _tlid = -1 + for tlid, link_index in self.get_all_associated_signals(landmark_id): + _tlid = tlid + link_index_list.append(link_index) + + state_string = traci.trafficlight.getRedYellowGreenState(_tlid) + state_list = [] + for link_index in link_index_list: + state_list.append(state_string[link_index]) + for state in state_list: + if state == 'g' or state == 'G': + return SumoSignalState.GREEN_WITHOUT_PRIORITY + elif state == 'y': + return SumoSignalState.YELLOW + else: + return SumoSignalState.RED + + def get_state(self, landmark_id): """ Returns the traffic light state of the signals associated with the given landmark. @@ -402,6 +424,9 @@ def destroy_actor(actor_id): """ traci.vehicle.remove(actor_id) + def get_traffic_light_live_state(self, landmark_id): + return self.traffic_light_manager.get_live_state(landmark_id) + def get_traffic_light_state(self, landmark_id): """ Accessor for traffic light state. @@ -453,6 +478,7 @@ def tick(self): self.traffic_light_manager = SumoTLManager() self.firstTime = False traci.simulationStep() + # print(traci.trafficlight.getRedYellowGreenState('1008')) self.traffic_light_manager.tick() # Update data structures for the current frame. self.spawned_actors = set(traci.simulation.getDepartedIDList()) diff --git a/co-simulation/bridge/carla_integration/synchronization.py b/co-simulation/bridge/carla_integration/synchronization.py index 7c56ce5e..cb7ce917 100644 --- a/co-simulation/bridge/carla_integration/synchronization.py +++ b/co-simulation/bridge/carla_integration/synchronization.py @@ -66,6 +66,9 @@ def __init__(self, self.sumo.switch_off_traffic_lights() elif tls_manager == 'sumo': self.carla.switch_off_traffic_lights() + elif tls_manager == 'EVC': + # self.sumo.switch_off_traffic_lights() + self.carla.switch_off_traffic_lights() # Mapped actor ids. self.sumo2carla_ids = {} # Contains only actors controlled by sumo. @@ -134,7 +137,13 @@ def tick(self): carla_tl_state = BridgeHelper.get_carla_traffic_light_state(sumo_tl_state) self.carla.synchronize_traffic_light(landmark_id, carla_tl_state) + elif self.tls_manager == 'EVC': + common_landmarks = self.sumo.traffic_light_ids & self.carla.traffic_light_ids + for landmark_id in common_landmarks: + sumo_tl_state = self.sumo.get_traffic_light_live_state(landmark_id) + carla_tl_state = BridgeHelper.get_carla_traffic_light_state(sumo_tl_state) + self.carla.synchronize_traffic_light(landmark_id, carla_tl_state) # # ----------------- # # carla-->sumo sync # # ----------------- @@ -208,4 +217,3 @@ def close(self): # Closing sumo and carla client. self.carla.close() self.sumo.close() - diff --git a/co-simulation/bridge/carla_mosaic_bridge.py b/co-simulation/bridge/carla_mosaic_bridge.py index 3c4c76db..ebc3bd09 100644 --- a/co-simulation/bridge/carla_mosaic_bridge.py +++ b/co-simulation/bridge/carla_mosaic_bridge.py @@ -160,7 +160,7 @@ def main(args): help='synchronize all vehicle properties (default: False)') argparser.add_argument('--tls-manager', type=str, - choices=['none', 'sumo', 'carla'], + choices=['none', 'sumo', 'carla','EVC'], help="select traffic light manager (default: none)", default='none') argparser.add_argument('--debug', action='store_true', help='enable debug messages') From 391e1285ff57e14557febf0cd48339f71f3dfca2 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 23 Feb 2023 13:52:27 -0500 Subject: [PATCH 010/124] Push evc-sumo bridge scripts --- evc-sumo/evc_sumo_bridge/evc_connector.py | 78 ++++++++++++++++++ evc-sumo/evc_sumo_bridge/evc_sumo_bridge.py | 28 +++++++ .../resources/controller_cfg.json | 23 ++++++ .../evc_sumo_bridge/resources/skip_setup.cfg | Bin 0 -> 147474 bytes evc-sumo/evc_sumo_bridge/sumo_connector.py | 35 ++++++++ 5 files changed, 164 insertions(+) create mode 100644 evc-sumo/evc_sumo_bridge/evc_connector.py create mode 100644 evc-sumo/evc_sumo_bridge/evc_sumo_bridge.py create mode 100644 evc-sumo/evc_sumo_bridge/resources/controller_cfg.json create mode 100644 evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg create mode 100644 evc-sumo/evc_sumo_bridge/sumo_connector.py diff --git a/evc-sumo/evc_sumo_bridge/evc_connector.py b/evc-sumo/evc_sumo_bridge/evc_connector.py new file mode 100644 index 00000000..137dc693 --- /dev/null +++ b/evc-sumo/evc_sumo_bridge/evc_connector.py @@ -0,0 +1,78 @@ +from pyeos.virtual.factory import virtual_factory +from pyeos.virtual import VirtualControllerOptions +import json + +class EvcConnector: + + def tick(self): + for harness in self.harness_list: + harness.tick(1) + + def __init__(self, asc3app_path, controller_cfg_path): + self.asc3app_path = asc3app_path + self.controller_cfg_path = controller_cfg_path + self.harness_list = [] + self.controller_io_list = [] + + def detector_status_to_CIB(self): + ## TBD + pass + + def COB_to_traffic_light_status(self, controller_io, phase_id): + if controller_io.is_cob_on( (phase_id - 1) + (16 * 2) ): + return 'r' + elif controller_io.is_cob_on( (phase_id - 1) + (16 * 1) ): + return 'y' + elif controller_io.is_cob_on( (phase_id - 1) + (16 * 0) ): + return 'g' + + def get_traffic_light_status_from_EVC(self, controller_io, phases): + ## get number of characters in state string for SUMO + state_num = 0 + for phase in phases: + state_num = state_num + len(phase["index"]) + ## init sumo tl state string with all red states + state_string = ['r'] * state_num + + for phase in phases: + state_string = [self.COB_to_traffic_light_status(controller_io, phase['phaseId']) if i in phase['index'] else x for i,x in enumerate(state_string)] + return ''.join(state_string) + + def set_detector_status_to_EVC(self): + ## TBD + pass + + def get_controller_cfg_path_list(self): + with open(self.controller_cfg_path) as cfg_file: + self.config_json = json.load(cfg_file) + self.controller_cfg_path_list = [] + for controller_cfg_path in self.config_json['controllers']: + self.controller_cfg_path_list.append(VirtualControllerOptions(controller_cfg_path['cfgPath'])) + + def run(self, sumo_connector): + ## init eos factory + with sumo_connector.traci_handler() as traci_session: + with virtual_factory(self.asc3app_path) as eos_factory: + self.get_controller_cfg_path_list() + ## init eos controller(s) + with eos_factory.run_multiple(self.controller_cfg_path_list) as eos_controllers: + for i in range(len(eos_controllers)): + if self.config_json['controllers'][i]['enableWebPanel']: + eos_controllers[i].watch() + print("Launch EOS web pannel for controller ID:", self.config_json['controllers'][i]['controllerId'], ", SUMO TLID:", self.config_json['controllers'][i]['sumoTlId']) + else: + continue + ## init eos harness + with eos_factory.eos_harness(eos_controllers) as harnesses: + for i in range(len(harnesses)): + io = harnesses[i].io() + harnesses[i].tick(1) + self.controller_io_list.append(io) + self.harness_list.append(harnesses[i]) + + while True: + sumo_connector.tick() + for i in range(len(self.controller_io_list)): + tl_state_string = self.get_traffic_light_status_from_EVC(self.controller_io_list[i], self.config_json['controllers'][i]['phases']) + sumo_connector.set_traffic_light_status_to_SUMO(self.config_json['controllers'][i]['sumoTlId'], tl_state_string) + self.tick() diff --git a/evc-sumo/evc_sumo_bridge/evc_sumo_bridge.py b/evc-sumo/evc_sumo_bridge/evc_sumo_bridge.py new file mode 100644 index 00000000..cb0f3170 --- /dev/null +++ b/evc-sumo/evc_sumo_bridge/evc_sumo_bridge.py @@ -0,0 +1,28 @@ +import sys +import os +from pathlib import Path +import argparse + +from evc_connector import EvcConnector +from sumo_connector import SumoConnector + +def run(args): + sumo_connector = SumoConnector(args.traci_port, args.traci_order_num) + evc_connector = EvcConnector(args.asc3app_path, args.controller_cfg_path) + evc_connector.run(sumo_connector) + +if __name__ == "__main__": + arg = argparse.ArgumentParser(description='EVC-SUMO integration') + arg.add_argument('--asc3app-path', + required=True, + help='asc3app-application file path') + arg.add_argument('--traci-port', + default=2000, + help='Traci port to connect to SUMO') + arg.add_argument('--traci-order-num', + default=2, + help='Traci order number for SUMO multi-clients connection') + arg.add_argument('--controller-cfg-path', + default='resources/controller_cfg.json') + args = arg.parse_args() + run(args) diff --git a/evc-sumo/evc_sumo_bridge/resources/controller_cfg.json b/evc-sumo/evc_sumo_bridge/resources/controller_cfg.json new file mode 100644 index 00000000..a8fd4bbd --- /dev/null +++ b/evc-sumo/evc_sumo_bridge/resources/controller_cfg.json @@ -0,0 +1,23 @@ +{ + "controllers": [ + { + "controllerId": 1, + "sumoTlId": 1008, + "enableWebPanel": true, + "cfgPath": "/home/yuan/Documents/GitHub/EVC/src/resources/skip_setup.cfg", + "phases": [{"phaseId": 1, "index":[1,5]}, + {"phaseId": 2, "index":[3,4]}, + {"phaseId": 3, "index":[0,2]} ] + }, + { + "controllerId": 2, + "sumoTlId": 621, + "enableWebPanel": true, + "cfgPath": "/home/yuan/Documents/GitHub/EVC/src/resources/skip_setup.cfg", + "phases": [{"phaseId": 1, "index":[1,8,10]}, + {"phaseId": 2, "index":[0,5,11]}, + {"phaseId": 3, "index":[3,4,9]}, + {"phaseId": 4, "index":[2,6,7]} ] + } + ] +} diff --git a/evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg b/evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg new file mode 100644 index 0000000000000000000000000000000000000000..f81d546dedd00a4f40895857e5b6e7b459bedc29 GIT binary patch literal 147474 zcmeI5S#TWH6^2ju%t)ir*p|VpA1l}aAs+@4WO_aC2@dae{pmi``(`uu(S zoYVdF>DxVb#xrtph*KQab?*6@Y_6_8-%w~QPMA2UsV~lSxu(yik%~a&sO~rGRt|FiQI5_8 z5v?VY*pf|b$tAW#r-GiRKCvaA*pj@}$t}rSo!pYV)yXZ%Tbtt@_0#U1% zEfoU#IY&CA6B9TX6*7MMH(q~4lKc3PzduOcHkT_p`_Z@;(e5Bghjd~B2jeP}TyMPo z4Ad*T^ocILW%7Oe_&GiNrL6>jjR#i6mcTl3S8{ z%j6bbe+FWm%#G(ZuQD+KUS%Q@qxNni>f-n$1J(d*VBFO}w8Q;VL%->0RbItcJ6G=I zRM1PLeEl! zP+tP&BJ?a1Ak;TNxd=VWL?nT!fzG2ndD!4K$?`dX^)>8;=q+r4)LWqrm%E zP%c8xax{dR4$4L7S@5pqB$vS8qh~=z%fQ1$=vm$dq0R^8BJ?cBL8y;{auIr#w?nA4 zpj?EWWh#Wa9F&XDv%CXBT?fiV=vhh->dT;9gq{WOzrX1c7<}|Bc((5V4;P_lnE|18 zfpQUgmYERhIZ!S_&oT=_{Q{JW(6by5p?(L-Md(>hfKac3auIr#*%0b4pj?EWmS1m1Yx1WhT0p5>k3jaM6JN-6X#bHE$VQD{mj^epcJZ`?CzN-6X#CxiEL zP%c8xatef61{5hkAZR#dX`fmR4XVKp=X%~p{@YsBJ?ctA=LGtT!fzGGzj$- zP%c8xayo?i7AO~?XE_5x?F8i_^eksWs5?Qq2tCVL5bAkQE<(?8HiY^mC>Nn;c{ham zJt!BUXL%2VdJU9|(6hW3Lj4t#i_o*Y4?^LX95kgAdY1QtH;!aLQ%a#{`2cw1>py5p zDfBE0zNn;SqhsBeRE5qg$o5b73CE<(?;975d% z%0=i|@N9p@bnkVSz~G~2X@hnC4l1R95PBBux$tuln2HyD25;Ub0dX^6G#_@P)N-6X#mxDLnHA7QMp=Y@QyzyQGno5`CauIr#%@ArgC>Nn;>4s1*fpQUgmMswKH=taEo@Fb9`V%M@p=Y@YLcIaX zMd(?shEV?if=r>S|maLPQMj>DXf`FyUh2)mhI@b4I2s*CRUbR-M8wfg%W z?tiWR)%%~T%hxv+oA4{q;)A2i4?O<0`B(3M?fG&4YxS?*|GHehzS!u#|F!y8?|-fS z2Oj_0w%=>=%3n;^w0BRFn1?s981>|yu}wE&9#OCCOSx~&n7>hX8%=%vte9RlYBt?S zqj8O=Nlnq2+;>{Xi#c~CFj*#x%%2=x51SU0g6MkaW&M2FHZdbR3SD(M>_1NC&_q|m zLbf5}_a7#in4eHzmvh}H`W--^e4NbEw|XCwr1Gd*G3hwPt3~}%Y)A9xY_c`&Arhz@ zr(J{HU-CE&ku%*%fCNZ@1W14cNPq-LfCNZ@1W14cNPq-L;Qv4%C-uEwTrxVXDZ{qU zxp^~YPc06jRX(>~i|eIg<*R`aoz@`qo{AN^B=icj>OeR*+K^mpqOpIlw9Pm`45+%Q*s zqR#@97cY2;g+L~JPJBw~?(y$5GHfw`4Yy!5;+8_;^fNirjNW&&z+pG=JunE{UYlAdw0=CWCAPt*< zZL>B=!zN(ctPRqz3D`DkgEVXcw$0ig4V!>%vo=V>CScpF4brd)*fwi}G;9L4&DtOh zn}BVzHb}!JVB4$>(y$5GHfw`4Yy!5;+8_;^fNirjNW&&z+pG=JunE{UYlAdw0=CWC zAPt*B=!zN(ctPRqz3D`DkgEVXcw$0ig4V!>%vo=V>CScpF4brd)*fwi}G;9L4 z&DtOhn}BVzHb}!JVB4$>(y$5GHfw`4Yy!5;+8_;^fNirjNW&&z+pG=JunE{UYlAdw z0=CWCAPt*B=!zN(ctPRqz3D`DkgEVXcw$0ig4V!>%vo=V>CScpF4brd)*!IA+ z(PpnY4s3tGee}Gncz#j5jG1T6JeSRTb;Z0_Z|3=8-Z$=k);#MZ@8>0-G3#giVm@Qm z&t!}F#jRbPUF$o#+e-_(I=eS@tuL+E)YZ17wR`h`X~#-q^QwjOOU`%GoFt0YdFuTwjlG{)LK@Q{~JTEFmFj_eH@r`4pyNZ?B$nnz> z9g&Q=_sccZ#eKFkRSr}6B3Cv?XhH90a7F?oP(`5BFuieFen#IpTh)B%NN@hUGf$fr z{W)~DnOjN%BtQZrKmsH{0wh2JBtQZrKmsH{0wh2JDG7N0s;qc0=W(u-n>Yfit(dpb zdN*bSHeg()Y-!uj(b=)Nds9nym-dO4{k_x1Eo;|zv?e3sdO90&A(w~%>wtB@I?!u? zT!xQJawN8p011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kih67P)fNu+Inf1 zX(`hBYU?qch{h%DGV&w8s0LUsIs={`Iv3W9&V}`&b78&cTv#tU7uJi;h4rFyVZG>F zST8yk){D-C^`diOz35z6FFF_2i_V4hqH|%r=v-JYIv3W9&V}`&b78&cTv#tU7uJi; zh4rFyVZCUT!DS>s0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBrt9XRDOFkE52m>jEtS%E2!*Oro3M==Jv}Uqx%(|h8chC9j`IAUxg)w`R4oo#mv3v zX%5(&=%9K3tUA*?nJhUuLK@^4nILbMj2tfYa+Fu{rpfEFt^6bHQzR=#OJ0tXqN(b` zq)v{MW?3u?WvMsao8is$X33jUkYi<{OqG4|n7rZb^hmg?;jxJR(oX7vziP zmGiaodAUjU%B!-#yv9C976v~J9t~~}o(&!g?g*X>9uIZ}&j;TN?hIZCz8~Bbycj$Y z+#S3WJQ?f`eil3x+|%QS&xAX|o#8Fvt$iaBuXxKV5A&-08Z##GxcppRmOsl5S!Bj8 zmdMiZ-tfNg;qa00f$+ibp`KDtzNew5(9;<1EvdBuOdd`@ok zwt3sV9o|k^V_xsCHDds6(k|=dQdwcf6V5kd46Dt!!G&fl;dZ%0cFR5Cg0ML}CtMgV z3Kxeq{nHQp9DyN1W14cNPq-LfCNZ@1W14cNMP&`NIODNUT<+{ zcl)MNTl?nLO&uG%JGwecUF%A#SFJc@)xs5}4P9H?H?((lpWGWVVL+^=80Dd;12?gz z7-o=4n`(-YFq~CW3~eLKvf+r45SoigV5A8Y2AQJm8)TL8XVke)G66D7W)roL5BVe! zU~MFksVpZS@<}4V+DIb9bBDFT+DIb6+DIb9euK5a+DIZW8f)WfGt3$Nuinu!wH<^_ zVU8roo}bC)>gw|ig~sB9iIbxL(F{|zYPz~=f|Z9!aGTPBBqwr8M z0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLjA@lIg>{^*^s@%-$ofSiobH_6N{%&vTk zxtQ+=<~BqJgd8emIiei=_&-2Z$}?7G%SOJXf`@-efCNZ@1P)CCduAP)+dY)6wl1SX z2{T4~Ny9#nq(K@s0o!J6kcLgbwpkmbVH2=z)&^SsSEb z6R>U825Hy?Y@4+~8a4siBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J cBtQZrKmsH{0wh2JBtQZrKmuc*z<*}`53eSNYXATM literal 0 HcmV?d00001 diff --git a/evc-sumo/evc_sumo_bridge/sumo_connector.py b/evc-sumo/evc_sumo_bridge/sumo_connector.py new file mode 100644 index 00000000..f3ecb328 --- /dev/null +++ b/evc-sumo/evc_sumo_bridge/sumo_connector.py @@ -0,0 +1,35 @@ +import sys +import os +if 'SUMO_HOME' in os.environ: + sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools')) +else: + sys.exit("please declare environment variable 'SUMO_HOME'") + +# must be declare after getting SUMO_HOME env var +import traci +from contextlib import contextmanager + +class SumoConnector: + def __init__(self, port, order_num): + self.port = port + self.order_num = order_num + self.traci = traci + + @contextmanager + def traci_handler(self): + self.traci.init(int(self.port)) + self.traci.setOrder(self.order_num) + yield + self.traci.close() + + def tick(self): + self.traci.simulationStep() + + def set_traffic_light_status_to_SUMO(self, sumo_tl_id, tl_state_string): + traci.trafficlight.setRedYellowGreenState(str(sumo_tl_id), tl_state_string) + + def get_detector_status_from_SUMO(self): + ## TBD + pass + + From 6ed2d087e19a0ea6cc766b3848caac56e6fa87dd Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 23 Feb 2023 13:52:47 -0500 Subject: [PATCH 011/124] Push evc-sumo bridge unit test --- evc-sumo/test/resources/asc3app | 0 .../test/resources/test_controller_cfg.json | 23 ++++++ evc-sumo/test/unit_test.py | 76 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 evc-sumo/test/resources/asc3app create mode 100644 evc-sumo/test/resources/test_controller_cfg.json create mode 100644 evc-sumo/test/unit_test.py diff --git a/evc-sumo/test/resources/asc3app b/evc-sumo/test/resources/asc3app new file mode 100644 index 00000000..e69de29b diff --git a/evc-sumo/test/resources/test_controller_cfg.json b/evc-sumo/test/resources/test_controller_cfg.json new file mode 100644 index 00000000..a8fd4bbd --- /dev/null +++ b/evc-sumo/test/resources/test_controller_cfg.json @@ -0,0 +1,23 @@ +{ + "controllers": [ + { + "controllerId": 1, + "sumoTlId": 1008, + "enableWebPanel": true, + "cfgPath": "/home/yuan/Documents/GitHub/EVC/src/resources/skip_setup.cfg", + "phases": [{"phaseId": 1, "index":[1,5]}, + {"phaseId": 2, "index":[3,4]}, + {"phaseId": 3, "index":[0,2]} ] + }, + { + "controllerId": 2, + "sumoTlId": 621, + "enableWebPanel": true, + "cfgPath": "/home/yuan/Documents/GitHub/EVC/src/resources/skip_setup.cfg", + "phases": [{"phaseId": 1, "index":[1,8,10]}, + {"phaseId": 2, "index":[0,5,11]}, + {"phaseId": 3, "index":[3,4,9]}, + {"phaseId": 4, "index":[2,6,7]} ] + } + ] +} diff --git a/evc-sumo/test/unit_test.py b/evc-sumo/test/unit_test.py new file mode 100644 index 00000000..f279cca2 --- /dev/null +++ b/evc-sumo/test/unit_test.py @@ -0,0 +1,76 @@ +import unittest +import os +import sys +from pathlib import Path + +sys.path.append('../evc_sumo_bridge') +from evc_connector import EvcConnector + +class TestControllerIOGreen(object): + def is_cob_on(self, index): + green_list = [i for i in range(0, 16)] + if index in green_list: + return True + else: + return False + +class TestControllerIOYellow(object): + def is_cob_on(self, index): + yellow_list = [i for i in range(16, 32)] + if index in yellow_list: + return True + else: + return False + +class TestControllerIORed(object): + def is_cob_on(self, index): + red_list = [i for i in range(32, 48)] + if index in red_list: + return True + else: + return False + +class TestEvcConnector(unittest.TestCase): + def setUp(self): + self.asc3app_path = Path(os.path.abspath(os.path.join(__file__, "..", "asc3app"))) + self.controller_cfg_path = Path(os.path.abspath(os.path.join(__file__, "..", "resources", "test_controller_cfg.json"))) + + def test_get_controller_cfg_path_list(self): + evc_connector = EvcConnector(self.asc3app_path, self.controller_cfg_path) + evc_connector.get_controller_cfg_path_list() + self.assertEqual(len(evc_connector.controller_cfg_path_list), 2) + + def test_cob_to_traffic_light_status(self): + evc_connector = EvcConnector(self.asc3app_path, self.controller_cfg_path) + phase = 1 + ## get green light string + green_io = TestControllerIOGreen() + state = evc_connector.COB_to_traffic_light_status(green_io, phase) + self.assertEqual(state, "g") + ## get yellow light string + yellow_io = TestControllerIOYellow() + state = evc_connector.COB_to_traffic_light_status(yellow_io, phase) + self.assertEqual(state, "y") + ## get red light string + red_io = TestControllerIORed() + state = evc_connector.COB_to_traffic_light_status(red_io, phase) + self.assertEqual(state, "r") + + def test_get_traffic_light_status(self): + evc_connector = EvcConnector(self.asc3app_path, self.controller_cfg_path) + + ## get green light state string + green_io = TestControllerIOGreen() + state = evc_connector.get_traffic_light_status_from_EVC(green_io, [{"index": [0, 1], "phaseId": 1}, {"index": [2, 3], "phaseId": 2}]) + self.assertEqual(state, "gggg") + ## get yellow light state string + yellow_io = TestControllerIOYellow() + state = evc_connector.get_traffic_light_status_from_EVC(yellow_io, [{"index": [0, 1], "phaseId": 1}, {"index": [2, 3], "phaseId": 2}]) + self.assertEqual(state, "yyyy") + ## get red light state string + red_io = TestControllerIORed() + state = evc_connector.get_traffic_light_status_from_EVC(red_io, [{"index": [0, 1], "phaseId": 1}, {"index": [2, 3], "phaseId": 2}]) + self.assertEqual(state, "rrrr") + +if __name__ == '__main__': + unittest.main() From e9460a059efef4606ce0b76b9616c6981dbbbf63 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 23 Feb 2023 23:19:03 -0500 Subject: [PATCH 012/124] Commit --- evc-sumo/{evc_sumo_bridge => src}/evc_connector.py | 0 .../{evc_sumo_bridge => src}/evc_sumo_bridge.py | 0 .../resources/controller_cfg.json | 4 ++-- .../resources/skip_setup.cfg | Bin evc-sumo/{evc_sumo_bridge => src}/sumo_connector.py | 0 evc-sumo/test/resources/test_controller_cfg.json | 4 ++-- 6 files changed, 4 insertions(+), 4 deletions(-) rename evc-sumo/{evc_sumo_bridge => src}/evc_connector.py (100%) rename evc-sumo/{evc_sumo_bridge => src}/evc_sumo_bridge.py (100%) rename evc-sumo/{evc_sumo_bridge => src}/resources/controller_cfg.json (75%) rename evc-sumo/{evc_sumo_bridge => src}/resources/skip_setup.cfg (100%) rename evc-sumo/{evc_sumo_bridge => src}/sumo_connector.py (100%) diff --git a/evc-sumo/evc_sumo_bridge/evc_connector.py b/evc-sumo/src/evc_connector.py similarity index 100% rename from evc-sumo/evc_sumo_bridge/evc_connector.py rename to evc-sumo/src/evc_connector.py diff --git a/evc-sumo/evc_sumo_bridge/evc_sumo_bridge.py b/evc-sumo/src/evc_sumo_bridge.py similarity index 100% rename from evc-sumo/evc_sumo_bridge/evc_sumo_bridge.py rename to evc-sumo/src/evc_sumo_bridge.py diff --git a/evc-sumo/evc_sumo_bridge/resources/controller_cfg.json b/evc-sumo/src/resources/controller_cfg.json similarity index 75% rename from evc-sumo/evc_sumo_bridge/resources/controller_cfg.json rename to evc-sumo/src/resources/controller_cfg.json index a8fd4bbd..428ece4d 100644 --- a/evc-sumo/evc_sumo_bridge/resources/controller_cfg.json +++ b/evc-sumo/src/resources/controller_cfg.json @@ -4,7 +4,7 @@ "controllerId": 1, "sumoTlId": 1008, "enableWebPanel": true, - "cfgPath": "/home/yuan/Documents/GitHub/EVC/src/resources/skip_setup.cfg", + "cfgPath": "/opt/carma-simulation/evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg", "phases": [{"phaseId": 1, "index":[1,5]}, {"phaseId": 2, "index":[3,4]}, {"phaseId": 3, "index":[0,2]} ] @@ -13,7 +13,7 @@ "controllerId": 2, "sumoTlId": 621, "enableWebPanel": true, - "cfgPath": "/home/yuan/Documents/GitHub/EVC/src/resources/skip_setup.cfg", + "cfgPath": "/opt/carma-simulation/evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg", "phases": [{"phaseId": 1, "index":[1,8,10]}, {"phaseId": 2, "index":[0,5,11]}, {"phaseId": 3, "index":[3,4,9]}, diff --git a/evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg b/evc-sumo/src/resources/skip_setup.cfg similarity index 100% rename from evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg rename to evc-sumo/src/resources/skip_setup.cfg diff --git a/evc-sumo/evc_sumo_bridge/sumo_connector.py b/evc-sumo/src/sumo_connector.py similarity index 100% rename from evc-sumo/evc_sumo_bridge/sumo_connector.py rename to evc-sumo/src/sumo_connector.py diff --git a/evc-sumo/test/resources/test_controller_cfg.json b/evc-sumo/test/resources/test_controller_cfg.json index a8fd4bbd..ce20ce8e 100644 --- a/evc-sumo/test/resources/test_controller_cfg.json +++ b/evc-sumo/test/resources/test_controller_cfg.json @@ -4,7 +4,7 @@ "controllerId": 1, "sumoTlId": 1008, "enableWebPanel": true, - "cfgPath": "/home/yuan/Documents/GitHub/EVC/src/resources/skip_setup.cfg", + "cfgPath": "/opt/carma-simulation/evc-sumo/test/resources/skip_setup.cfg", "phases": [{"phaseId": 1, "index":[1,5]}, {"phaseId": 2, "index":[3,4]}, {"phaseId": 3, "index":[0,2]} ] @@ -13,7 +13,7 @@ "controllerId": 2, "sumoTlId": 621, "enableWebPanel": true, - "cfgPath": "/home/yuan/Documents/GitHub/EVC/src/resources/skip_setup.cfg", + "cfgPath": "/opt/carma-simulation/evc-sumo/test/resources/skip_setup.cfg", "phases": [{"phaseId": 1, "index":[1,8,10]}, {"phaseId": 2, "index":[0,5,11]}, {"phaseId": 3, "index":[3,4,9]}, From b15365755c3b60a6ceee3141a4b9a72e7a89493e Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 24 Feb 2023 23:37:41 -0500 Subject: [PATCH 013/124] Rename configuration file, old name makes confuse --- .../src/resources/{controller_cfg.json => evc_sumo_cfg.json} | 4 ++-- .../{test_controller_cfg.json => test_evc_sumo_cfg.json} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename evc-sumo/src/resources/{controller_cfg.json => evc_sumo_cfg.json} (73%) rename evc-sumo/test/resources/{test_controller_cfg.json => test_evc_sumo_cfg.json} (100%) diff --git a/evc-sumo/src/resources/controller_cfg.json b/evc-sumo/src/resources/evc_sumo_cfg.json similarity index 73% rename from evc-sumo/src/resources/controller_cfg.json rename to evc-sumo/src/resources/evc_sumo_cfg.json index 428ece4d..14c628b1 100644 --- a/evc-sumo/src/resources/controller_cfg.json +++ b/evc-sumo/src/resources/evc_sumo_cfg.json @@ -4,7 +4,7 @@ "controllerId": 1, "sumoTlId": 1008, "enableWebPanel": true, - "cfgPath": "/opt/carma-simulation/evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg", + "controllerCfgPath": "/opt/carma-simulation/evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg", "phases": [{"phaseId": 1, "index":[1,5]}, {"phaseId": 2, "index":[3,4]}, {"phaseId": 3, "index":[0,2]} ] @@ -13,7 +13,7 @@ "controllerId": 2, "sumoTlId": 621, "enableWebPanel": true, - "cfgPath": "/opt/carma-simulation/evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg", + "controllerCfgPath": "/opt/carma-simulation/evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg", "phases": [{"phaseId": 1, "index":[1,8,10]}, {"phaseId": 2, "index":[0,5,11]}, {"phaseId": 3, "index":[3,4,9]}, diff --git a/evc-sumo/test/resources/test_controller_cfg.json b/evc-sumo/test/resources/test_evc_sumo_cfg.json similarity index 100% rename from evc-sumo/test/resources/test_controller_cfg.json rename to evc-sumo/test/resources/test_evc_sumo_cfg.json From a4b117fdee3d1326b6fc7c2632df7fc0f7e8f05a Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 24 Feb 2023 23:39:11 -0500 Subject: [PATCH 014/124] Add comments for every functions --- evc-sumo/src/evc_connector.py | 74 ++++++++++++++++++++++++++++++---- evc-sumo/src/sumo_connector.py | 18 ++++++++- 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/evc-sumo/src/evc_connector.py b/evc-sumo/src/evc_connector.py index 137dc693..05c63423 100644 --- a/evc-sumo/src/evc_connector.py +++ b/evc-sumo/src/evc_connector.py @@ -2,15 +2,30 @@ from pyeos.virtual import VirtualControllerOptions import json +MAX_INTERSECTION_PHASES = 16 + class EvcConnector: def tick(self): + """ + Advance EVC controllers time via PyEOS + """ for harness in self.harness_list: harness.tick(1) - def __init__(self, asc3app_path, controller_cfg_path): + def __init__(self, asc3app_path, evc_sumo_cfg_path): + """ + Initialize + ------------- + asc3app_path: The directory to asc3app which provided by Econolite team + type: string + ------------- + evc_sumo_cfg_path: The directory to evc_sumo_cfg.json file, default path is "resources/evc_sumo_cfg.json" + type: string + ------------- + """ self.asc3app_path = asc3app_path - self.controller_cfg_path = controller_cfg_path + self.evc_sumo_cfg_path = evc_sumo_cfg_path self.harness_list = [] self.controller_io_list = [] @@ -19,14 +34,40 @@ def detector_status_to_CIB(self): pass def COB_to_traffic_light_status(self, controller_io, phase_id): - if controller_io.is_cob_on( (phase_id - 1) + (16 * 2) ): + """ + Convert COB status to string + ------------- + controller_io: The controller input/output (cib/cob) + type: + ------------- + phase_id: The EVC traffic light phase ID, start from 1 + type: int + ------------- + """ + + ## is_cob_on(0) is to check if phase 1 is or not in green state + ## is_cob_on(16) is to check if phase 1 is or not in yellow state + ## is_cob_on(32) is to check if phase 1 is or not in red state + if controller_io.is_cob_on( (phase_id - 1) + (MAX_INTERSECTION_PHASES * 2) ): return 'r' - elif controller_io.is_cob_on( (phase_id - 1) + (16 * 1) ): + elif controller_io.is_cob_on( (phase_id - 1) + (MAX_INTERSECTION_PHASES * 1) ): return 'y' - elif controller_io.is_cob_on( (phase_id - 1) + (16 * 0) ): + elif controller_io.is_cob_on( (phase_id - 1) + (MAX_INTERSECTION_PHASES * 0) ): return 'g' def get_traffic_light_status_from_EVC(self, controller_io, phases): + """ + Get traffic light status from EVC to SUMO state string + ------------- + controller_io: The controller input/output (cib/cob) + type: + ------------- + phases: Phases information defined in controller_cfg.json file + type: dict + ------------- + return: string + """ + ## get number of characters in state string for SUMO state_num = 0 for phase in phases: @@ -43,27 +84,44 @@ def set_detector_status_to_EVC(self): pass def get_controller_cfg_path_list(self): - with open(self.controller_cfg_path) as cfg_file: + """ + Store all controller config path into a list in order to pass to eos_factory to initialize controllers + """ + with open(self.evc_sumo_cfg_path) as cfg_file: self.config_json = json.load(cfg_file) self.controller_cfg_path_list = [] for controller_cfg_path in self.config_json['controllers']: - self.controller_cfg_path_list.append(VirtualControllerOptions(controller_cfg_path['cfgPath'])) + self.controller_cfg_path_list.append(VirtualControllerOptions(controller_cfg_path['controllerCfgPath'])) def run(self, sumo_connector): - ## init eos factory + """ + main loop + """ + ## init traci connection with sumo_connector.traci_handler() as traci_session: + + ## init EVC with virtual_factory(self.asc3app_path) as eos_factory: + self.get_controller_cfg_path_list() + ## init eos controller(s) with eos_factory.run_multiple(self.controller_cfg_path_list) as eos_controllers: + + ## enable/disable EVC web panel for i in range(len(eos_controllers)): if self.config_json['controllers'][i]['enableWebPanel']: eos_controllers[i].watch() print("Launch EOS web pannel for controller ID:", self.config_json['controllers'][i]['controllerId'], ", SUMO TLID:", self.config_json['controllers'][i]['sumoTlId']) else: continue + ## init eos harness with eos_factory.eos_harness(eos_controllers) as harnesses: + + ## store io and harness into list + ## harness needs to be used to advance controller time + ## io needs to be used to get CIB/COB for i in range(len(harnesses)): io = harnesses[i].io() harnesses[i].tick(1) diff --git a/evc-sumo/src/sumo_connector.py b/evc-sumo/src/sumo_connector.py index f3ecb328..c91e8e80 100644 --- a/evc-sumo/src/sumo_connector.py +++ b/evc-sumo/src/sumo_connector.py @@ -15,21 +15,35 @@ def __init__(self, port, order_num): self.order_num = order_num self.traci = traci + @contextmanager def traci_handler(self): + """ + Initialize traci + """ self.traci.init(int(self.port)) self.traci.setOrder(self.order_num) yield self.traci.close() def tick(self): + """ + Advance SUMO time via traci + """ self.traci.simulationStep() def set_traffic_light_status_to_SUMO(self, sumo_tl_id, tl_state_string): + """ + Set traffic light status to SUMO + ------------- + sumo_tl_id: SUMO traffic light ID (example: 1008) + type: int + ------------- + tl_state_string: traffic light state string for SUMO (example: "rrrrrr") + type: string + """ traci.trafficlight.setRedYellowGreenState(str(sumo_tl_id), tl_state_string) def get_detector_status_from_SUMO(self): ## TBD pass - - From b1881eda29eaf756a70815a96722743a3f84af06 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 24 Feb 2023 23:39:53 -0500 Subject: [PATCH 015/124] Rename parameters which was confused --- evc-sumo/src/evc_sumo_bridge.py | 4 ++-- evc-sumo/test/unit_test.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/evc-sumo/src/evc_sumo_bridge.py b/evc-sumo/src/evc_sumo_bridge.py index cb0f3170..a650e414 100644 --- a/evc-sumo/src/evc_sumo_bridge.py +++ b/evc-sumo/src/evc_sumo_bridge.py @@ -8,7 +8,7 @@ def run(args): sumo_connector = SumoConnector(args.traci_port, args.traci_order_num) - evc_connector = EvcConnector(args.asc3app_path, args.controller_cfg_path) + evc_connector = EvcConnector(args.asc3app_path, args.evc_sumo_cfg_path) evc_connector.run(sumo_connector) if __name__ == "__main__": @@ -22,7 +22,7 @@ def run(args): arg.add_argument('--traci-order-num', default=2, help='Traci order number for SUMO multi-clients connection') - arg.add_argument('--controller-cfg-path', + arg.add_argument('--evc-sumo-cfg-path', default='resources/controller_cfg.json') args = arg.parse_args() run(args) diff --git a/evc-sumo/test/unit_test.py b/evc-sumo/test/unit_test.py index f279cca2..a90508b3 100644 --- a/evc-sumo/test/unit_test.py +++ b/evc-sumo/test/unit_test.py @@ -33,15 +33,15 @@ def is_cob_on(self, index): class TestEvcConnector(unittest.TestCase): def setUp(self): self.asc3app_path = Path(os.path.abspath(os.path.join(__file__, "..", "asc3app"))) - self.controller_cfg_path = Path(os.path.abspath(os.path.join(__file__, "..", "resources", "test_controller_cfg.json"))) + self.evc_sumo_cfg_path = Path(os.path.abspath(os.path.join(__file__, "..", "resources", "test_controller_cfg.json"))) def test_get_controller_cfg_path_list(self): - evc_connector = EvcConnector(self.asc3app_path, self.controller_cfg_path) + evc_connector = EvcConnector(self.asc3app_path, self.evc_sumo_cfg_path) evc_connector.get_controller_cfg_path_list() self.assertEqual(len(evc_connector.controller_cfg_path_list), 2) def test_cob_to_traffic_light_status(self): - evc_connector = EvcConnector(self.asc3app_path, self.controller_cfg_path) + evc_connector = EvcConnector(self.asc3app_path, self.evc_sumo_cfg_path) phase = 1 ## get green light string green_io = TestControllerIOGreen() @@ -57,7 +57,7 @@ def test_cob_to_traffic_light_status(self): self.assertEqual(state, "r") def test_get_traffic_light_status(self): - evc_connector = EvcConnector(self.asc3app_path, self.controller_cfg_path) + evc_connector = EvcConnector(self.asc3app_path, self.evc_sumo_cfg_path) ## get green light state string green_io = TestControllerIOGreen() From 7ffbb5754e679b43b0c5e69e6ce0545d319076fa Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Sat, 25 Feb 2023 16:04:34 -0500 Subject: [PATCH 016/124] Complete comments for every functions --- evc-sumo/src/evc_connector.py | 55 ++++++++++++++++++++-------------- evc-sumo/src/sumo_connector.py | 24 +++++++++++---- 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/evc-sumo/src/evc_connector.py b/evc-sumo/src/evc_connector.py index 05c63423..781573f7 100644 --- a/evc-sumo/src/evc_connector.py +++ b/evc-sumo/src/evc_connector.py @@ -15,14 +15,14 @@ def tick(self): def __init__(self, asc3app_path, evc_sumo_cfg_path): """ - Initialize - ------------- - asc3app_path: The directory to asc3app which provided by Econolite team + Initialize EvcConnector object + + Parameters: + - asc3app_path: The directory to asc3app which is provided by the Econolite team type: string - ------------- - evc_sumo_cfg_path: The directory to evc_sumo_cfg.json file, default path is "resources/evc_sumo_cfg.json" + + - evc_sumo_cfg_path: The directory to evc_sumo_cfg.json file, default path is "resources/evc_sumo_cfg.json" type: string - ------------- """ self.asc3app_path = asc3app_path self.evc_sumo_cfg_path = evc_sumo_cfg_path @@ -30,20 +30,25 @@ def __init__(self, asc3app_path, evc_sumo_cfg_path): self.controller_io_list = [] def detector_status_to_CIB(self): - ## TBD + """ + Convert detector status to CIB + """ pass def COB_to_traffic_light_status(self, controller_io, phase_id): """ Convert COB status to string - ------------- - controller_io: The controller input/output (cib/cob) - type: - ------------- - phase_id: The EVC traffic light phase ID, start from 1 + + Parameters: + - controller_io: The controller input/output (cib/cob) + type: pyeos.common.harness + + - phase_id: The EVC traffic light phase ID, start from 1 type: int - ------------- """ + ## This function converts the COB status for a given traffic light phase to a string representing its state (red, yellow, or green). + ## The controller input/output (cib/cob) is passed as an argument along with the phase ID. + ## The function returns a string representing the state of the traffic light phase. ## is_cob_on(0) is to check if phase 1 is or not in green state ## is_cob_on(16) is to check if phase 1 is or not in yellow state @@ -58,14 +63,16 @@ def COB_to_traffic_light_status(self, controller_io, phase_id): def get_traffic_light_status_from_EVC(self, controller_io, phases): """ Get traffic light status from EVC to SUMO state string - ------------- - controller_io: The controller input/output (cib/cob) - type: - ------------- - phases: Phases information defined in controller_cfg.json file + + Parameters: + - controller_io: The controller input/output (cib/cob) + type: pyeos.common.harness + + - phases: Phases information defined in evc_sumo_cfg.json file type: dict - ------------- - return: string + + Returns: + - string: A string representing the current state of the traffic light. The possible characters are "r" for red, "y" for yellow, and "g" for green. """ ## get number of characters in state string for SUMO @@ -85,7 +92,7 @@ def set_detector_status_to_EVC(self): def get_controller_cfg_path_list(self): """ - Store all controller config path into a list in order to pass to eos_factory to initialize controllers + Get the controller config path list """ with open(self.evc_sumo_cfg_path) as cfg_file: self.config_json = json.load(cfg_file) @@ -95,7 +102,11 @@ def get_controller_cfg_path_list(self): def run(self, sumo_connector): """ - main loop + The main loop for the EVC connector + ------------- + sumo_connector: The SUMO connector instance + type: object + ------------- """ ## init traci connection with sumo_connector.traci_handler() as traci_session: diff --git a/evc-sumo/src/sumo_connector.py b/evc-sumo/src/sumo_connector.py index c91e8e80..727e1d61 100644 --- a/evc-sumo/src/sumo_connector.py +++ b/evc-sumo/src/sumo_connector.py @@ -1,5 +1,6 @@ import sys import os + if 'SUMO_HOME' in os.environ: sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools')) else: @@ -11,11 +12,20 @@ class SumoConnector: def __init__(self, port, order_num): + """ + Initialize SumoConnector object + + Parameters: + - port: The port number to connect to SUMO, which should be consistent with SUMO simulator setting + type: string + + - order_num: The priority order number of the connection to SUMO, which should be consistent with other SUMO connections if any + type: string + """ self.port = port self.order_num = order_num self.traci = traci - @contextmanager def traci_handler(self): """ @@ -35,15 +45,19 @@ def tick(self): def set_traffic_light_status_to_SUMO(self, sumo_tl_id, tl_state_string): """ Set traffic light status to SUMO - ------------- - sumo_tl_id: SUMO traffic light ID (example: 1008) + + Parameters: + - sumo_tl_id: SUMO traffic light ID (example: 1008) type: int - ------------- - tl_state_string: traffic light state string for SUMO (example: "rrrrrr") + + - tl_state_string: traffic light state string for SUMO (example: "rrrrrr") type: string """ traci.trafficlight.setRedYellowGreenState(str(sumo_tl_id), tl_state_string) def get_detector_status_from_SUMO(self): + """ + Get detector status from SUMO + """ ## TBD pass From b3495902caa545d25a587d586a0c8173805a7689 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 1 Mar 2023 20:48:36 -0500 Subject: [PATCH 017/124] Fix strange formatting --- .../mosaic/fed/sumo/traci/TraciClient.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/TraciClient.java b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/TraciClient.java index c368975c..96eb59ca 100644 --- a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/TraciClient.java +++ b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/TraciClient.java @@ -175,14 +175,17 @@ public void close() { } } + /** + * Set MOSAIC client order number to SUMO via traci + */ public void setOrder(int orderNum) { - try { - commandRegister.getOrCreate(TraciSetOrder.class).execute(this, orderNum); - } catch (TraciCommandException e) { - e.printStackTrace(); - } catch (InternalFederateException e) { - e.printStackTrace(); - } + try { + commandRegister.getOrCreate(TraciSetOrder.class).execute(this, orderNum); + } catch (TraciCommandException e) { + e.printStackTrace(); + } catch (InternalFederateException e) { + e.printStackTrace(); + } } @Override From a580a7db4b59f86de9c5f6f3172f3623aee9e3e1 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 1 Mar 2023 20:52:39 -0500 Subject: [PATCH 018/124] Add copy right --- .../sumo/traci/commands/TraciSetOrder.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/commands/TraciSetOrder.java b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/commands/TraciSetOrder.java index ffbea8ad..7cd4f7a8 100644 --- a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/commands/TraciSetOrder.java +++ b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/commands/TraciSetOrder.java @@ -1,17 +1,18 @@ -/* - * Copyright (c) 2020 Fraunhofer FOKUS and others. All rights reserved. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contact: mosaic@fokus.fraunhofer.de - */ + /* + * 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. + */ package org.eclipse.mosaic.fed.sumo.traci.commands; From b4a98a1df47c93d92ae90c599167c351a6a6799c Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 1 Mar 2023 23:59:21 -0500 Subject: [PATCH 019/124] Update sumo_simulation.py --- .../carla_integration/sumo_simulation.py | 47 +++++++++++++------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/co-simulation/bridge/carla_integration/sumo_simulation.py b/co-simulation/bridge/carla_integration/sumo_simulation.py index df036ff1..d4f71661 100644 --- a/co-simulation/bridge/carla_integration/sumo_simulation.py +++ b/co-simulation/bridge/carla_integration/sumo_simulation.py @@ -228,25 +228,42 @@ def get_all_associated_signals(self, landmark_id): signals.update(self._tls[tlid][program_id].get_associated_signals(landmark_id)) return signals - def get_live_state(self, landmark_id): + + def get_state_from_sumo(self, landmark_id): + """ + This function will send a request to get sumo current traffic light state via MOSAIC + Returns the traffic light state of the signals associated with the given landmark. + """ link_index_list = [] _tlid = -1 for tlid, link_index in self.get_all_associated_signals(landmark_id): _tlid = tlid link_index_list.append(link_index) - state_string = traci.trafficlight.getRedYellowGreenState(_tlid) - state_list = [] - for link_index in link_index_list: - state_list.append(state_string[link_index]) - for state in state_list: - if state == 'g' or state == 'G': - return SumoSignalState.GREEN_WITHOUT_PRIORITY - elif state == 'y': - return SumoSignalState.YELLOW - else: - return SumoSignalState.RED + ## SUMO traffic light state, ex 'rrggrr' in a intersection + sumo_tl_state_string = traci.trafficlight.getRedYellowGreenState(_tlid) + ## Collect state for the current phase from a intersection, + ## ex, if intersection tl state 'rrggrr' and current phase (west=>east) occupies index 1 and 2 + ## then phase_state_list will be ['g','g'] + phase_state_list = [] + for link_index in link_index_list: + phase_state_list.append(sumo_tl_state_string[link_index]) + + ## Since CARLA traffic light 3D model currently only have green, red and yellow states + ## which left and right turn arrow can not be shown. As a result, if one of state + ## in a phase is green or yellow then return green or yellow state for CARLA traffic light + ## when all states in a phase are red then set red to CARLA traffic light + + # if one of the state in the list is 'g' then return green for CARLA + if 'g' in phase_state_list or 'G' in phase_state_list: + return SumoSignalState.GREEN_WITHOUT_PRIORITY + # else if one of the state in the list is 'y' then return yellow for CARLA + elif 'y' in phase_state_list: + return SumoSignalState.YELLOW + # else if all of the states in the list are 'r' then return r for CARLA + else: + return SumoSignalState.RED def get_state(self, landmark_id): """ @@ -424,8 +441,8 @@ def destroy_actor(actor_id): """ traci.vehicle.remove(actor_id) - def get_traffic_light_live_state(self, landmark_id): - return self.traffic_light_manager.get_live_state(landmark_id) + def get_traffic_light_state_from_sumo(self, landmark_id): + return self.traffic_light_manager.get_state_from_sumo(landmark_id) def get_traffic_light_state(self, landmark_id): """ @@ -478,8 +495,8 @@ def tick(self): self.traffic_light_manager = SumoTLManager() self.firstTime = False traci.simulationStep() - # print(traci.trafficlight.getRedYellowGreenState('1008')) self.traffic_light_manager.tick() + # Update data structures for the current frame. self.spawned_actors = set(traci.simulation.getDepartedIDList()) self.destroyed_actors = set(traci.simulation.getArrivedIDList()) From 93ba50f9b82fabca87b239a8770ad25c3a781c68 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 2 Mar 2023 02:02:00 -0500 Subject: [PATCH 020/124] Added Leidos copyright and synchronize timestep management from MOSAIC --- evc-sumo/src/evc_connector.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/evc-sumo/src/evc_connector.py b/evc-sumo/src/evc_connector.py index 781573f7..ab4a3c01 100644 --- a/evc-sumo/src/evc_connector.py +++ b/evc-sumo/src/evc_connector.py @@ -1,3 +1,18 @@ + # 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. + # + from pyeos.virtual.factory import virtual_factory from pyeos.virtual import VirtualControllerOptions import json @@ -6,12 +21,12 @@ class EvcConnector: - def tick(self): + def tick(self, advance_steps): """ Advance EVC controllers time via PyEOS """ for harness in self.harness_list: - harness.tick(1) + harness.tick(advance_steps) def __init__(self, asc3app_path, evc_sumo_cfg_path): """ @@ -53,6 +68,7 @@ def COB_to_traffic_light_status(self, controller_io, phase_id): ## is_cob_on(0) is to check if phase 1 is or not in green state ## is_cob_on(16) is to check if phase 1 is or not in yellow state ## is_cob_on(32) is to check if phase 1 is or not in red state + ## MAX_INTERSECTION_PHASES default setting is value 16 as setting in line 20 if controller_io.is_cob_on( (phase_id - 1) + (MAX_INTERSECTION_PHASES * 2) ): return 'r' elif controller_io.is_cob_on( (phase_id - 1) + (MAX_INTERSECTION_PHASES * 1) ): @@ -135,7 +151,13 @@ def run(self, sumo_connector): ## io needs to be used to get CIB/COB for i in range(len(harnesses)): io = harnesses[i].io() - harnesses[i].tick(1) + + ## harness.tick is a fix step length, 0.1, with given value update times + ## if harness.tick(1) which means advance EVC 1 time within 0.1 second + ## if harness.tick(10) which means advance EVC 10 time within 0.1 second + ## By retrieving step length from SUMO (which is configured in MOSAIC configuration file) + ## the formula to convert the value for EVC will be 1 / (sumo_step_length * 10) + harnesses[i].tick( 1 / (sumo_connector.traci_get_step_length() * 10) ) self.controller_io_list.append(io) self.harness_list.append(harnesses[i]) @@ -144,4 +166,4 @@ def run(self, sumo_connector): for i in range(len(self.controller_io_list)): tl_state_string = self.get_traffic_light_status_from_EVC(self.controller_io_list[i], self.config_json['controllers'][i]['phases']) sumo_connector.set_traffic_light_status_to_SUMO(self.config_json['controllers'][i]['sumoTlId'], tl_state_string) - self.tick() + self.tick( 1 / (sumo_connector.traci_get_step_length() * 10) ) From d4010aaf37361fea7d5a41cfa2c461eeb99634c0 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 2 Mar 2023 02:02:16 -0500 Subject: [PATCH 021/124] Added copyright --- evc-sumo/test/unit_test.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/evc-sumo/test/unit_test.py b/evc-sumo/test/unit_test.py index a90508b3..5d659de7 100644 --- a/evc-sumo/test/unit_test.py +++ b/evc-sumo/test/unit_test.py @@ -1,3 +1,19 @@ + # 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 unittest import os import sys @@ -33,7 +49,7 @@ def is_cob_on(self, index): class TestEvcConnector(unittest.TestCase): def setUp(self): self.asc3app_path = Path(os.path.abspath(os.path.join(__file__, "..", "asc3app"))) - self.evc_sumo_cfg_path = Path(os.path.abspath(os.path.join(__file__, "..", "resources", "test_controller_cfg.json"))) + self.evc_sumo_cfg_path = Path(os.path.abspath(os.path.join(__file__, "..", "resources", "test_evc_sumo_cfg.json"))) def test_get_controller_cfg_path_list(self): evc_connector = EvcConnector(self.asc3app_path, self.evc_sumo_cfg_path) From 69403a6b385c58b415ac5fa282e25ffc0286c8b7 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 2 Mar 2023 02:02:44 -0500 Subject: [PATCH 022/124] Added copyright and function to get step length from SUMO via traci --- evc-sumo/src/sumo_connector.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/evc-sumo/src/sumo_connector.py b/evc-sumo/src/sumo_connector.py index 727e1d61..e66e9228 100644 --- a/evc-sumo/src/sumo_connector.py +++ b/evc-sumo/src/sumo_connector.py @@ -1,3 +1,18 @@ + # 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 sys import os @@ -26,6 +41,9 @@ def __init__(self, port, order_num): self.order_num = order_num self.traci = traci + def traci_get_step_length(self): + return traci.simulation.getDeltaT() + @contextmanager def traci_handler(self): """ From 771ed0e204b3d2730d8174a524585b858a8f6f47 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 2 Mar 2023 02:03:24 -0500 Subject: [PATCH 023/124] Updated controllerCfgPath value --- evc-sumo/src/resources/evc_sumo_cfg.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evc-sumo/src/resources/evc_sumo_cfg.json b/evc-sumo/src/resources/evc_sumo_cfg.json index 14c628b1..1d6ce22a 100644 --- a/evc-sumo/src/resources/evc_sumo_cfg.json +++ b/evc-sumo/src/resources/evc_sumo_cfg.json @@ -4,7 +4,7 @@ "controllerId": 1, "sumoTlId": 1008, "enableWebPanel": true, - "controllerCfgPath": "/opt/carma-simulation/evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg", + "controllerCfgPath": "resources/skip_setup.cfg", "phases": [{"phaseId": 1, "index":[1,5]}, {"phaseId": 2, "index":[3,4]}, {"phaseId": 3, "index":[0,2]} ] @@ -13,7 +13,7 @@ "controllerId": 2, "sumoTlId": 621, "enableWebPanel": true, - "controllerCfgPath": "/opt/carma-simulation/evc-sumo/evc_sumo_bridge/resources/skip_setup.cfg", + "controllerCfgPath": "resources/skip_setup.cfg", "phases": [{"phaseId": 1, "index":[1,8,10]}, {"phaseId": 2, "index":[0,5,11]}, {"phaseId": 3, "index":[3,4,9]}, From dd0a194ab9de41aabe744541797d04ff19fd652d Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 2 Mar 2023 02:04:06 -0500 Subject: [PATCH 024/124] Updated traci-port to 2010 which is the default setting of SUMO launched by MOSAIC --- evc-sumo/src/evc_sumo_bridge.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/evc-sumo/src/evc_sumo_bridge.py b/evc-sumo/src/evc_sumo_bridge.py index a650e414..03a31782 100644 --- a/evc-sumo/src/evc_sumo_bridge.py +++ b/evc-sumo/src/evc_sumo_bridge.py @@ -1,3 +1,19 @@ + # 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 sys import os from pathlib import Path @@ -17,12 +33,12 @@ def run(args): required=True, help='asc3app-application file path') arg.add_argument('--traci-port', - default=2000, + default=2010, help='Traci port to connect to SUMO') arg.add_argument('--traci-order-num', default=2, help='Traci order number for SUMO multi-clients connection') arg.add_argument('--evc-sumo-cfg-path', - default='resources/controller_cfg.json') + default='resources/evc_sumo_cfg.json') args = arg.parse_args() run(args) From 4d51eb982d0ce3769a5a03eb9713720933cf2d85 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 2 Mar 2023 22:00:22 -0500 Subject: [PATCH 025/124] Added EVC testing config file for unit test --- evc-sumo/test/resources/test.cfg | Bin 0 -> 147474 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 evc-sumo/test/resources/test.cfg diff --git a/evc-sumo/test/resources/test.cfg b/evc-sumo/test/resources/test.cfg new file mode 100644 index 0000000000000000000000000000000000000000..f81d546dedd00a4f40895857e5b6e7b459bedc29 GIT binary patch literal 147474 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 literal 0 HcmV?d00001 From 59499c9c5de05e0c5c410c4afee444be2569aed3 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 2 Mar 2023 22:06:40 -0500 Subject: [PATCH 026/124] Updated unit test to match with evc_sumo_cfg.json format --- evc-sumo/test/unit_test.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/evc-sumo/test/unit_test.py b/evc-sumo/test/unit_test.py index 5d659de7..6f5c0b08 100644 --- a/evc-sumo/test/unit_test.py +++ b/evc-sumo/test/unit_test.py @@ -19,7 +19,7 @@ import sys from pathlib import Path -sys.path.append('../evc_sumo_bridge') +sys.path.append('../src') from evc_connector import EvcConnector class TestControllerIOGreen(object): @@ -51,10 +51,9 @@ def setUp(self): self.asc3app_path = Path(os.path.abspath(os.path.join(__file__, "..", "asc3app"))) self.evc_sumo_cfg_path = Path(os.path.abspath(os.path.join(__file__, "..", "resources", "test_evc_sumo_cfg.json"))) - def test_get_controller_cfg_path_list(self): + def test_get_controller_cfg_list(self): evc_connector = EvcConnector(self.asc3app_path, self.evc_sumo_cfg_path) - evc_connector.get_controller_cfg_path_list() - self.assertEqual(len(evc_connector.controller_cfg_path_list), 2) + self.assertEqual(len(evc_connector.get_controller_cfg_list()), 2) def test_cob_to_traffic_light_status(self): evc_connector = EvcConnector(self.asc3app_path, self.evc_sumo_cfg_path) @@ -77,15 +76,15 @@ def test_get_traffic_light_status(self): ## get green light state string green_io = TestControllerIOGreen() - state = evc_connector.get_traffic_light_status_from_EVC(green_io, [{"index": [0, 1], "phaseId": 1}, {"index": [2, 3], "phaseId": 2}]) + state = evc_connector.get_traffic_light_status_from_EVC(green_io, [{"sumoTlStateIndex": [0, 1], "evcPhaseId": 1}, {"sumoTlStateIndex": [2, 3], "evcPhaseId": 2}]) self.assertEqual(state, "gggg") ## get yellow light state string yellow_io = TestControllerIOYellow() - state = evc_connector.get_traffic_light_status_from_EVC(yellow_io, [{"index": [0, 1], "phaseId": 1}, {"index": [2, 3], "phaseId": 2}]) + state = evc_connector.get_traffic_light_status_from_EVC(yellow_io, [{"sumoTlStateIndex": [0, 1], "evcPhaseId": 1}, {"sumoTlStateIndex": [2, 3], "evcPhaseId": 2}]) self.assertEqual(state, "yyyy") ## get red light state string red_io = TestControllerIORed() - state = evc_connector.get_traffic_light_status_from_EVC(red_io, [{"index": [0, 1], "phaseId": 1}, {"index": [2, 3], "phaseId": 2}]) + state = evc_connector.get_traffic_light_status_from_EVC(red_io, [{"sumoTlStateIndex": [0, 1], "evcPhaseId": 1}, {"sumoTlStateIndex": [2, 3], "evcPhaseId": 2}]) self.assertEqual(state, "rrrr") if __name__ == '__main__': From 9cb8984e03c86365ad39f7324c11bd309d83f959 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 2 Mar 2023 22:07:19 -0500 Subject: [PATCH 027/124] Added more configuration option for EVC controller --- evc-sumo/src/resources/evc_sumo_cfg.json | 30 +++++++++++++------ .../test/resources/test_evc_sumo_cfg.json | 30 +++++++++++++------ 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/evc-sumo/src/resources/evc_sumo_cfg.json b/evc-sumo/src/resources/evc_sumo_cfg.json index 1d6ce22a..2ab10d68 100644 --- a/evc-sumo/src/resources/evc_sumo_cfg.json +++ b/evc-sumo/src/resources/evc_sumo_cfg.json @@ -2,22 +2,34 @@ "controllers": [ { "controllerId": 1, + "controllerCfgPath": "resources/skip_setup.cfg", + "start_time": null, + "https_port": 0, + "web_port": 0, + "snmp_port": 0, + "harness_port": 0, + "controller_speed": null, "sumoTlId": 1008, "enableWebPanel": true, - "controllerCfgPath": "resources/skip_setup.cfg", - "phases": [{"phaseId": 1, "index":[1,5]}, - {"phaseId": 2, "index":[3,4]}, - {"phaseId": 3, "index":[0,2]} ] + "evcPhases": [{"evcPhaseId": 1, "sumoTlStateIndex":[1,5]}, + {"evcPhaseId": 2, "sumoTlStateIndex":[3,4]}, + {"evcPhaseId": 3, "sumoTlStateIndex":[0,2]} ] }, { "controllerId": 2, + "controllerCfgPath": "resources/skip_setup.cfg", + "start_time": null, + "https_port": 0, + "web_port": 0, + "snmp_port": 0, + "harness_port": 0, + "controller_speed": null, "sumoTlId": 621, "enableWebPanel": true, - "controllerCfgPath": "resources/skip_setup.cfg", - "phases": [{"phaseId": 1, "index":[1,8,10]}, - {"phaseId": 2, "index":[0,5,11]}, - {"phaseId": 3, "index":[3,4,9]}, - {"phaseId": 4, "index":[2,6,7]} ] + "evcPhases": [{"evcPhaseId": 1, "sumoTlStateIndex":[1,8,10]}, + {"evcPhaseId": 2, "sumoTlStateIndex":[0,5,11]}, + {"evcPhaseId": 3, "sumoTlStateIndex":[3,4,9]}, + {"evcPhaseId": 4, "sumoTlStateIndex":[2,6,7]} ] } ] } diff --git a/evc-sumo/test/resources/test_evc_sumo_cfg.json b/evc-sumo/test/resources/test_evc_sumo_cfg.json index ce20ce8e..2ab10d68 100644 --- a/evc-sumo/test/resources/test_evc_sumo_cfg.json +++ b/evc-sumo/test/resources/test_evc_sumo_cfg.json @@ -2,22 +2,34 @@ "controllers": [ { "controllerId": 1, + "controllerCfgPath": "resources/skip_setup.cfg", + "start_time": null, + "https_port": 0, + "web_port": 0, + "snmp_port": 0, + "harness_port": 0, + "controller_speed": null, "sumoTlId": 1008, "enableWebPanel": true, - "cfgPath": "/opt/carma-simulation/evc-sumo/test/resources/skip_setup.cfg", - "phases": [{"phaseId": 1, "index":[1,5]}, - {"phaseId": 2, "index":[3,4]}, - {"phaseId": 3, "index":[0,2]} ] + "evcPhases": [{"evcPhaseId": 1, "sumoTlStateIndex":[1,5]}, + {"evcPhaseId": 2, "sumoTlStateIndex":[3,4]}, + {"evcPhaseId": 3, "sumoTlStateIndex":[0,2]} ] }, { "controllerId": 2, + "controllerCfgPath": "resources/skip_setup.cfg", + "start_time": null, + "https_port": 0, + "web_port": 0, + "snmp_port": 0, + "harness_port": 0, + "controller_speed": null, "sumoTlId": 621, "enableWebPanel": true, - "cfgPath": "/opt/carma-simulation/evc-sumo/test/resources/skip_setup.cfg", - "phases": [{"phaseId": 1, "index":[1,8,10]}, - {"phaseId": 2, "index":[0,5,11]}, - {"phaseId": 3, "index":[3,4,9]}, - {"phaseId": 4, "index":[2,6,7]} ] + "evcPhases": [{"evcPhaseId": 1, "sumoTlStateIndex":[1,8,10]}, + {"evcPhaseId": 2, "sumoTlStateIndex":[0,5,11]}, + {"evcPhaseId": 3, "sumoTlStateIndex":[3,4,9]}, + {"evcPhaseId": 4, "sumoTlStateIndex":[2,6,7]} ] } ] } From 8e40af2aef59d38416e19b501526c9e404004eac Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 2 Mar 2023 22:08:04 -0500 Subject: [PATCH 028/124] Updated EVC connector --- evc-sumo/src/evc_connector.py | 40 +++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/evc-sumo/src/evc_connector.py b/evc-sumo/src/evc_connector.py index ab4a3c01..92a4ad7e 100644 --- a/evc-sumo/src/evc_connector.py +++ b/evc-sumo/src/evc_connector.py @@ -50,7 +50,7 @@ def detector_status_to_CIB(self): """ pass - def COB_to_traffic_light_status(self, controller_io, phase_id): + def COB_to_traffic_light_status(self, controller_io, evc_phase_id): """ Convert COB status to string @@ -58,7 +58,7 @@ def COB_to_traffic_light_status(self, controller_io, phase_id): - controller_io: The controller input/output (cib/cob) type: pyeos.common.harness - - phase_id: The EVC traffic light phase ID, start from 1 + - evc_phase_id: The EVC traffic light phase ID, start from 1 type: int """ ## This function converts the COB status for a given traffic light phase to a string representing its state (red, yellow, or green). @@ -69,11 +69,11 @@ def COB_to_traffic_light_status(self, controller_io, phase_id): ## is_cob_on(16) is to check if phase 1 is or not in yellow state ## is_cob_on(32) is to check if phase 1 is or not in red state ## MAX_INTERSECTION_PHASES default setting is value 16 as setting in line 20 - if controller_io.is_cob_on( (phase_id - 1) + (MAX_INTERSECTION_PHASES * 2) ): + if controller_io.is_cob_on( (evc_phase_id - 1) + (MAX_INTERSECTION_PHASES * 2) ): return 'r' - elif controller_io.is_cob_on( (phase_id - 1) + (MAX_INTERSECTION_PHASES * 1) ): + elif controller_io.is_cob_on( (evc_phase_id - 1) + (MAX_INTERSECTION_PHASES * 1) ): return 'y' - elif controller_io.is_cob_on( (phase_id - 1) + (MAX_INTERSECTION_PHASES * 0) ): + elif controller_io.is_cob_on( (evc_phase_id - 1) + (MAX_INTERSECTION_PHASES * 0) ): return 'g' def get_traffic_light_status_from_EVC(self, controller_io, phases): @@ -94,27 +94,37 @@ def get_traffic_light_status_from_EVC(self, controller_io, phases): ## get number of characters in state string for SUMO state_num = 0 for phase in phases: - state_num = state_num + len(phase["index"]) + state_num = state_num + len(phase["sumoTlStateIndex"]) ## init sumo tl state string with all red states state_string = ['r'] * state_num for phase in phases: - state_string = [self.COB_to_traffic_light_status(controller_io, phase['phaseId']) if i in phase['index'] else x for i,x in enumerate(state_string)] + state_string = [self.COB_to_traffic_light_status(controller_io, phase['evcPhaseId']) if i in phase['sumoTlStateIndex'] else x for i,x in enumerate(state_string)] return ''.join(state_string) def set_detector_status_to_EVC(self): ## TBD pass - def get_controller_cfg_path_list(self): + def get_controller_cfg_list(self): """ Get the controller config path list """ + controller_cfg_list = [] with open(self.evc_sumo_cfg_path) as cfg_file: self.config_json = json.load(cfg_file) - self.controller_cfg_path_list = [] for controller_cfg_path in self.config_json['controllers']: - self.controller_cfg_path_list.append(VirtualControllerOptions(controller_cfg_path['controllerCfgPath'])) + option = VirtualControllerOptions( + cfg_path=controller_cfg_path['controllerCfgPath'], + start_time=controller_cfg_path['start_time'], + https_port=controller_cfg_path['https_port'], + web_port=controller_cfg_path['web_port'], + snmp_port=controller_cfg_path['snmp_port'], + harness_port=controller_cfg_path['harness_port'], + controller_speed=controller_cfg_path['controller_speed']) + controller_cfg_list.append(option) + return controller_cfg_list + def run(self, sumo_connector): """ @@ -130,10 +140,8 @@ def run(self, sumo_connector): ## init EVC with virtual_factory(self.asc3app_path) as eos_factory: - self.get_controller_cfg_path_list() - ## init eos controller(s) - with eos_factory.run_multiple(self.controller_cfg_path_list) as eos_controllers: + with eos_factory.run_multiple(self.get_controller_cfg_list()) as eos_controllers: ## enable/disable EVC web panel for i in range(len(eos_controllers)): @@ -157,13 +165,13 @@ def run(self, sumo_connector): ## if harness.tick(10) which means advance EVC 10 time within 0.1 second ## By retrieving step length from SUMO (which is configured in MOSAIC configuration file) ## the formula to convert the value for EVC will be 1 / (sumo_step_length * 10) - harnesses[i].tick( 1 / (sumo_connector.traci_get_step_length() * 10) ) + harnesses[i].tick( int(1 / (sumo_connector.traci_get_step_length() * 10)) ) self.controller_io_list.append(io) self.harness_list.append(harnesses[i]) while True: sumo_connector.tick() for i in range(len(self.controller_io_list)): - tl_state_string = self.get_traffic_light_status_from_EVC(self.controller_io_list[i], self.config_json['controllers'][i]['phases']) + tl_state_string = self.get_traffic_light_status_from_EVC(self.controller_io_list[i], self.config_json['controllers'][i]['evcPhases']) sumo_connector.set_traffic_light_status_to_SUMO(self.config_json['controllers'][i]['sumoTlId'], tl_state_string) - self.tick( 1 / (sumo_connector.traci_get_step_length() * 10) ) + self.tick( int(1 / (sumo_connector.traci_get_step_length() * 10)) ) From 949049ef234a466c1fdcb4b6dcf8c2f8abc90286 Mon Sep 17 00:00:00 2001 From: Cody Garver Date: Mon, 6 Mar 2023 16:46:40 -0500 Subject: [PATCH 029/124] Delete CircleCi --- .circleci/config.yml | 96 -------------------------------------------- 1 file changed, 96 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index aed14948..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,96 +0,0 @@ -version: 2 -# Copyright (C) 2018-2020 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. -# - -# Configuration file for Circle CI -# CI will report failure if any executed command returns and error status -# Operations performed are as follows -# Build source code -# Run unit tests for C++ and Java -# Run static code analyzer for SourceCloud -# Upload test results -# Every run command should start with source ${INIT_ENV} to ensure all default dependancies are available - -jobs: - build: - # Pull docker image from docker hub - # XTERM used for better catkin_make output - docker: - - image: usdotfhwastoldev/carma-base:develop - user: carma - environment: - TERM: xterm # use xterm to get full display output from build - INIT_ENV: /home/carma/.base-image/init-env.sh - working_directory: "/opt/carma/" - # Execution steps - steps: - - run: - name: Create src folder - command: | - source ${INIT_ENV} - mkdir src - cd src - mkdir carma-simulation - - checkout: - path: src/carma-simulation - - run: - name: Pull Deps - command: | - source ${INIT_ENV} - ./src/carma-simulation/docker/checkout.sh -r ${PWD} - - run: - name: Build Driver - command: | - source ${INIT_ENV} - export ROS_PARALLEL_JOBS='-j1 -l1' # Try to reduce memory consumption on build - build-wrapper-linux-x86-64 --out-dir /opt/carma/bw-output bash make_with_coverage.bash -m -e /opt/carma/ -o ./coverage_reports/gcov - - run: - name: Run C++ Tests - command: | - source ${INIT_ENV} - export ROS_PARALLEL_JOBS='-j1 -l1' # Try to reduce memory consumption on build - bash make_with_coverage.bash -t -e /opt/carma/ -o ./coverage_reports/gcov - # Run SonarCloud analysis - # PR Branchs and number extracted from Circle variables and github api - # Circle CI seems to make a change to the base branch, so we must fetch --force to ensure correct git file change stats - # SONAR_SCANNER_TOKEN MUST be secured as an environment variable in Circle CI NOT in this file. - # The following sonar settings MUST be set in SonarCloud UI NOT in this file - # sonar.pullrequest.provider - # sonar.pullrequest.github.endpoint - # sonar.pullrequest.github.token.secured - # sonar.pullrequest.github.repository - # Use -X on sonar-scanner to enable debug output - - run: - name: Run Sonar Scanner - command: | - set -x - source ${INIT_ENV} - if [ -z "${CIRCLE_PULL_REQUEST}" ]; then - echo "Non-PR Build Detected. Running analysis on ${CIRCLE_BRANCH}" - cd src/carma-simulation - sonar-scanner -Dproject.settings=.sonarqube/sonar-scanner.properties -Dsonar.login=${SONAR_SCANNER_TOKEN} - exit 0; - fi - echo "PR branch ${CIRCLE_BRANCH}" - echo "Repo name ${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}" - echo "URL ${CIRCLE_PULL_REQUEST}" - export PR_NUM=`echo ${CIRCLE_PULL_REQUEST} | cut -d'/' -f7` - echo "PR number ${PR_NUM}" - export BASE_BRANCH_URL="https://api.github.com/repos/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/pulls/${PR_NUM}" - export TARGET_BRANCH=$(curl "$BASE_BRANCH_URL" | jq '.base.ref' | tr -d '"') - echo "Target Branch = ${TARGET_BRANCH}" - cd src/carma-simulation - git fetch --force origin ${TARGET_BRANCH}:${TARGET_BRANCH} - sonar-scanner -Dproject.settings=.sonarqube/sonar-scanner.properties -Dsonar.login=${SONAR_SCANNER_TOKEN} -Dsonar.pullrequest.base=${TARGET_BRANCH} -Dsonar.pullrequest.branch=${CIRCLE_BRANCH} -Dsonar.pullrequest.key=${PR_NUM} From d6c7a4d9477f63c6a19e37e42d23b0648db9a125 Mon Sep 17 00:00:00 2001 From: Saikrishna Bairamoni <84093461+SaikrishnaBairamoni@users.noreply.github.com> Date: Mon, 6 Mar 2023 17:43:17 -0500 Subject: [PATCH 030/124] Add CI for Sonar (#110) # PR Details This PR is to setup sonar cloud to run the C++ tests and publish the sonar cloud reports on sonarcloud.io ## Description ## Related Issue ## Motivation and Context ## How Has This Been Tested? ## Types of changes - [ ] Defect fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that cause existing functionality to change) ## Checklist: - [ ] 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** document. [CARMA Contributing Guide](Contributing.md) - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. --------- Co-authored-by: Cody Garver Co-authored-by: Cody Garver --- .github/workflows/runtest.yml | 21 +++++++++++++++++++++ co-simulation/pom.xml | 10 ++++++++++ 2 files changed, 31 insertions(+) create mode 100644 .github/workflows/runtest.yml diff --git a/.github/workflows/runtest.yml b/.github/workflows/runtest.yml new file mode 100644 index 00000000..f9b90955 --- /dev/null +++ b/.github/workflows/runtest.yml @@ -0,0 +1,21 @@ +name: "CI: Run tests" +on: + pull_request: + push: +jobs: + sonar: # sonar job is to setup and run sonar scan analysis on telematic cloud messaging code + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 8 + uses: actions/setup-java@v3 # The setup-java action provides the functionality for GitHub Actions runners for Downloading and setting up a requested version of Java + with: + java-version: 8 + distribution: "temurin" + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: | + cd co-simulation + mvn -e -X clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar -Dlicense.skip -Dmaven.test.failure.ignore=false -Pcoverage jacoco:report -Dsonar.projectKey=usdot-fhwa-stol_carma-simulation -Dsonar.projectName=carma-simulation -Dsonar.organization=usdot-fhwa-stol -Dsonar.host.url=https://sonarcloud.io diff --git a/co-simulation/pom.xml b/co-simulation/pom.xml index 89565bff..1f5dbaf0 100644 --- a/co-simulation/pom.xml +++ b/co-simulation/pom.xml @@ -83,6 +83,16 @@ + 8 + usdot-fhwa-stol + https://sonarcloud.io + + + 0.8.8 + jacoco + reuseReports + ${project.basedir}/target/site/jacoco/jacoco.xml + java 22.1-SNAPSHOT . UTF-8 From def6b99baa271c8d70b12c9d34843573164b2e0a Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 15 Mar 2023 22:35:23 -0400 Subject: [PATCH 031/124] Added loop detector function and config --- evc-sumo/src/evc_connector.py | 70 +++++++++++++++++------- evc-sumo/src/resources/evc_sumo_cfg.json | 25 ++------- evc-sumo/src/sumo_connector.py | 30 ++++++++-- 3 files changed, 80 insertions(+), 45 deletions(-) diff --git a/evc-sumo/src/evc_connector.py b/evc-sumo/src/evc_connector.py index 92a4ad7e..4857c75f 100644 --- a/evc-sumo/src/evc_connector.py +++ b/evc-sumo/src/evc_connector.py @@ -44,12 +44,6 @@ def __init__(self, asc3app_path, evc_sumo_cfg_path): self.harness_list = [] self.controller_io_list = [] - def detector_status_to_CIB(self): - """ - Convert detector status to CIB - """ - pass - def COB_to_traffic_light_status(self, controller_io, evc_phase_id): """ Convert COB status to string @@ -76,7 +70,7 @@ def COB_to_traffic_light_status(self, controller_io, evc_phase_id): elif controller_io.is_cob_on( (evc_phase_id - 1) + (MAX_INTERSECTION_PHASES * 0) ): return 'g' - def get_traffic_light_status_from_EVC(self, controller_io, phases): + def get_traffic_light_state_from_EVC(self, controller_io, phases): """ Get traffic light status from EVC to SUMO state string @@ -91,20 +85,44 @@ def get_traffic_light_status_from_EVC(self, controller_io, phases): - string: A string representing the current state of the traffic light. The possible characters are "r" for red, "y" for yellow, and "g" for green. """ - ## get number of characters in state string for SUMO - state_num = 0 - for phase in phases: - state_num = state_num + len(phase["sumoTlStateIndex"]) - ## init sumo tl state string with all red states - state_string = ['r'] * state_num + ## get number of characters in state string for SUMO and init sumo tl state string with all red + state_string = ['r'] * sum(len(phase['sumoTlStateIndex']) for phase in phases) for phase in phases: state_string = [self.COB_to_traffic_light_status(controller_io, phase['evcPhaseId']) if i in phase['sumoTlStateIndex'] else x for i,x in enumerate(state_string)] return ''.join(state_string) - def set_detector_status_to_EVC(self): - ## TBD - pass + def set_induction_loop_status_to_EVC(self, controller_io, evc_phase_id, sumo_induction_loop_status): + """ + Set induction loop status from SUMO to EVC via PyEOS CIB + + Parameters: + - controller_io: The controller input/output (cib/cob) + type: pyeos.common.harness + + - evc_phase_id: EVC phase ID + type: int + + - sumo_induction_loop_status: induction loop status retrieved from SumoConnector + type: int + + Returns: + - int: return a integer to represent cib is on or off. + cib on: 1 + cib off: 0 + """ + if sumo_induction_loop_status != 0: + if controller_io.is_cib_off(evc_phase_id - 1): + controller_io.cib_on(evc_phase_id - 1) + return 1 + else: + return 0 + else: + if controller_io.is_cib_on(evc_phase_id - 1): + controller_io.cib_off(evc_phase_id - 1) + return 1 + else: + return 0 def get_controller_cfg_list(self): """ @@ -147,7 +165,7 @@ def run(self, sumo_connector): for i in range(len(eos_controllers)): if self.config_json['controllers'][i]['enableWebPanel']: eos_controllers[i].watch() - print("Launch EOS web pannel for controller ID:", self.config_json['controllers'][i]['controllerId'], ", SUMO TLID:", self.config_json['controllers'][i]['sumoTlId']) + print("Launch EOS web panel for controller ID:", self.config_json['controllers'][i]['controllerId'], ", SUMO TLID:", self.config_json['controllers'][i]['sumoTlId']) else: continue @@ -169,9 +187,21 @@ def run(self, sumo_connector): self.controller_io_list.append(io) self.harness_list.append(harnesses[i]) + ground_truth_sumo_loop_detector_ids = sumo_connector.get_induction_loop_id_list() while True: sumo_connector.tick() - for i in range(len(self.controller_io_list)): - tl_state_string = self.get_traffic_light_status_from_EVC(self.controller_io_list[i], self.config_json['controllers'][i]['evcPhases']) - sumo_connector.set_traffic_light_status_to_SUMO(self.config_json['controllers'][i]['sumoTlId'], tl_state_string) + for i, controller in enumerate(self.config_json['controllers']): + ## Update EVC loop detector status + for evcPhase in controller['evcPhases']: + ## Some intersection or lane might not set loop detector + if 'sumoInductionLoopId' not in evcPhase: + continue + ## Check if induction loop id in config file exists in SUMO simulation + if evcPhase['sumoInductionLoopId'] in ground_truth_sumo_loop_detector_ids: + sumo_induction_loop_status = sumo_connector.get_induction_loop_status_from_SUMO(evcPhase['sumoInductionLoopId']) + self.set_induction_loop_status_to_EVC(self.controller_io_list[i], evcPhase['evcPhaseId'], sumo_induction_loop_status) + + ## Update SUMO traffic light state + tl_state_string = self.get_traffic_light_state_from_EVC(self.controller_io_list[i], controller['evcPhases']) + sumo_connector.set_traffic_light_state_to_SUMO(controller['sumoTlId'], tl_state_string) self.tick( int(1 / (sumo_connector.traci_get_step_length() * 10)) ) diff --git a/evc-sumo/src/resources/evc_sumo_cfg.json b/evc-sumo/src/resources/evc_sumo_cfg.json index 2ab10d68..aa3a7418 100644 --- a/evc-sumo/src/resources/evc_sumo_cfg.json +++ b/evc-sumo/src/resources/evc_sumo_cfg.json @@ -9,27 +9,12 @@ "snmp_port": 0, "harness_port": 0, "controller_speed": null, - "sumoTlId": 1008, + "sumoTlId": 139, "enableWebPanel": true, - "evcPhases": [{"evcPhaseId": 1, "sumoTlStateIndex":[1,5]}, - {"evcPhaseId": 2, "sumoTlStateIndex":[3,4]}, - {"evcPhaseId": 3, "sumoTlStateIndex":[0,2]} ] - }, - { - "controllerId": 2, - "controllerCfgPath": "resources/skip_setup.cfg", - "start_time": null, - "https_port": 0, - "web_port": 0, - "snmp_port": 0, - "harness_port": 0, - "controller_speed": null, - "sumoTlId": 621, - "enableWebPanel": true, - "evcPhases": [{"evcPhaseId": 1, "sumoTlStateIndex":[1,8,10]}, - {"evcPhaseId": 2, "sumoTlStateIndex":[0,5,11]}, - {"evcPhaseId": 3, "sumoTlStateIndex":[3,4,9]}, - {"evcPhaseId": 4, "sumoTlStateIndex":[2,6,7]} ] + "evcPhases": [{"evcPhaseId": 1, "sumoTlStateIndex":[6,7,8], "sumoInductionLoopId":"e1_0"}, + {"evcPhaseId": 2, "sumoTlStateIndex":[9,10,11], "sumoInductionLoopId":"e1_3"}, + {"evcPhaseId": 3, "sumoTlStateIndex":[3,4,5], "sumoInductionLoopId":"e1_1"}, + {"evcPhaseId": 4, "sumoTlStateIndex":[0,1,2], "sumoInductionLoopId":"e1_2"} ] } ] } diff --git a/evc-sumo/src/sumo_connector.py b/evc-sumo/src/sumo_connector.py index e66e9228..a0fe6f5f 100644 --- a/evc-sumo/src/sumo_connector.py +++ b/evc-sumo/src/sumo_connector.py @@ -42,6 +42,9 @@ def __init__(self, port, order_num): self.traci = traci def traci_get_step_length(self): + """ + Get sumo step length + """ return traci.simulation.getDeltaT() @contextmanager @@ -60,7 +63,7 @@ def tick(self): """ self.traci.simulationStep() - def set_traffic_light_status_to_SUMO(self, sumo_tl_id, tl_state_string): + def set_traffic_light_state_to_SUMO(self, sumo_tl_id, tl_state_string): """ Set traffic light status to SUMO @@ -73,9 +76,26 @@ def set_traffic_light_status_to_SUMO(self, sumo_tl_id, tl_state_string): """ traci.trafficlight.setRedYellowGreenState(str(sumo_tl_id), tl_state_string) - def get_detector_status_from_SUMO(self): + def get_induction_loop_id_list(self): """ - Get detector status from SUMO + Retrieves the induction loop id list from the SUMO simulation + + Returns: + - list: list of induction loop id in the SUMO simulation. + """ + return traci.inductionloop.getIDList() + + def get_induction_loop_status_from_SUMO(self, induction_loop_id): + """ + Retrieves the induction loop status from the SUMO simulation and uses the `getLastStepVehicleNumber` + method to get information about the vehicles that passed the detector. + If no vehicles passed the detector, the function returns 0. + Otherwise, it returns the number of vehicles that entered the detector. + + Parameters: + - detector_id (str): The detector ID in the SUMO simulation. + + Returns: + - int: The number of vehicles that entered the detector. """ - ## TBD - pass + return traci.inductionloop.getLastStepVehicleNumber(induction_loop_id) From 4eb4e811840910d0780c2ddec0926a52069cd637 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 17 Mar 2023 23:17:32 -0400 Subject: [PATCH 032/124] commit --- evc-sumo/src/evc_connector.py | 16 ++++- evc-sumo/src/resources/1008.cfg | Bin 0 -> 147474 bytes evc-sumo/src/resources/evc_sumo_cfg.json | 27 ++++++-- evc-sumo/test/unit_test.py | 81 +++++++++++++++++++++-- 4 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 evc-sumo/src/resources/1008.cfg diff --git a/evc-sumo/src/evc_connector.py b/evc-sumo/src/evc_connector.py index 4857c75f..03c17dd9 100644 --- a/evc-sumo/src/evc_connector.py +++ b/evc-sumo/src/evc_connector.py @@ -69,6 +69,8 @@ def COB_to_traffic_light_status(self, controller_io, evc_phase_id): return 'y' elif controller_io.is_cob_on( (evc_phase_id - 1) + (MAX_INTERSECTION_PHASES * 0) ): return 'g' + else: + return None def get_traffic_light_state_from_EVC(self, controller_io, phases): """ @@ -82,7 +84,8 @@ def get_traffic_light_state_from_EVC(self, controller_io, phases): type: dict Returns: - - string: A string representing the current state of the traffic light. The possible characters are "r" for red, "y" for yellow, and "g" for green. + - string: A string representing the current state of the traffic light. + The possible characters are "r" for red, "y" for yellow, and "g" for green and None for exception """ ## get number of characters in state string for SUMO and init sumo tl state string with all red @@ -90,7 +93,10 @@ def get_traffic_light_state_from_EVC(self, controller_io, phases): for phase in phases: state_string = [self.COB_to_traffic_light_status(controller_io, phase['evcPhaseId']) if i in phase['sumoTlStateIndex'] else x for i,x in enumerate(state_string)] - return ''.join(state_string) + if None in state_string: + return None + else: + return ''.join(state_string) def set_induction_loop_status_to_EVC(self, controller_io, evc_phase_id, sumo_induction_loop_status): """ @@ -203,5 +209,9 @@ def run(self, sumo_connector): ## Update SUMO traffic light state tl_state_string = self.get_traffic_light_state_from_EVC(self.controller_io_list[i], controller['evcPhases']) - sumo_connector.set_traffic_light_state_to_SUMO(controller['sumoTlId'], tl_state_string) + if tl_state_string is not None: + sumo_connector.set_traffic_light_state_to_SUMO(controller['sumoTlId'], tl_state_string) + else: + print("Error: Could not connect to EVC, please check EVC-SUMO config") + self.tick( int(1 / (sumo_connector.traci_get_step_length() * 10)) ) diff --git a/evc-sumo/src/resources/1008.cfg b/evc-sumo/src/resources/1008.cfg new file mode 100644 index 0000000000000000000000000000000000000000..0fa52a87c3d01fb957bb7542e75d7ef6409b0b7d GIT binary patch literal 147474 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 T36KB@kN^pg!1yQd-&y|$K;V9O literal 0 HcmV?d00001 diff --git a/evc-sumo/src/resources/evc_sumo_cfg.json b/evc-sumo/src/resources/evc_sumo_cfg.json index aa3a7418..96f8a031 100644 --- a/evc-sumo/src/resources/evc_sumo_cfg.json +++ b/evc-sumo/src/resources/evc_sumo_cfg.json @@ -2,6 +2,21 @@ "controllers": [ { "controllerId": 1, + "controllerCfgPath": "resources/1008.cfg", + "start_time": null, + "https_port": 0, + "web_port": 0, + "snmp_port": 0, + "harness_port": 0, + "controller_speed": null, + "sumoTlId": 1008, + "enableWebPanel": true, + "evcPhases": [{"evcPhaseId": 2, "sumoTlStateIndex":[1,5], "sumoInductionLoopId": "e1_0"}, + {"evcPhaseId": 6, "sumoTlStateIndex":[3,4], "sumoInductionLoopId": "e1_1"}, + {"evcPhaseId": 4, "sumoTlStateIndex":[0,2], "sumoInductionLoopId": "e1_2"} ] + }, + { + "controllerId": 2, "controllerCfgPath": "resources/skip_setup.cfg", "start_time": null, "https_port": 0, @@ -9,12 +24,12 @@ "snmp_port": 0, "harness_port": 0, "controller_speed": null, - "sumoTlId": 139, + "sumoTlId": 621, "enableWebPanel": true, - "evcPhases": [{"evcPhaseId": 1, "sumoTlStateIndex":[6,7,8], "sumoInductionLoopId":"e1_0"}, - {"evcPhaseId": 2, "sumoTlStateIndex":[9,10,11], "sumoInductionLoopId":"e1_3"}, - {"evcPhaseId": 3, "sumoTlStateIndex":[3,4,5], "sumoInductionLoopId":"e1_1"}, - {"evcPhaseId": 4, "sumoTlStateIndex":[0,1,2], "sumoInductionLoopId":"e1_2"} ] + "evcPhases": [{"evcPhaseId": 1, "sumoTlStateIndex":[1,8,10], "sumoInductionLoopId": "e1_6"}, + {"evcPhaseId": 2, "sumoTlStateIndex":[0,5,11], "sumoInductionLoopId": "e1_5"}, + {"evcPhaseId": 3, "sumoTlStateIndex":[3,4,9], "sumoInductionLoopId": "e1_4"}, + {"evcPhaseId": 4, "sumoTlStateIndex":[2,6,7], "sumoInductionLoopId": "e1_3"} ] } ] -} +} \ No newline at end of file diff --git a/evc-sumo/test/unit_test.py b/evc-sumo/test/unit_test.py index 6f5c0b08..b3e7287b 100644 --- a/evc-sumo/test/unit_test.py +++ b/evc-sumo/test/unit_test.py @@ -18,10 +18,31 @@ import os import sys from pathlib import Path +from unittest.mock import patch sys.path.append('../src') from evc_connector import EvcConnector +class TestControllerIOOff(object): + def is_cib_on(self, index): + return False + def cib_on(self, index): + return + def is_cib_off(self, index): + return True + def cib_off(self, index): + return + +class TestControllerIOOn(object): + def is_cib_on(self, index): + return True + def cib_on(self, index): + return + def is_cib_off(self, index): + return False + def cib_off(self, index): + return + class TestControllerIOGreen(object): def is_cob_on(self, index): green_list = [i for i in range(0, 16)] @@ -52,10 +73,24 @@ def setUp(self): self.evc_sumo_cfg_path = Path(os.path.abspath(os.path.join(__file__, "..", "resources", "test_evc_sumo_cfg.json"))) def test_get_controller_cfg_list(self): + """ + Test the functionality of the `get_controller_cfg_list` method of the `EvcConnector` class. + + This method tests if the `get_controller_cfg_list` method can correctly read and return the + controller configuration list from the specified path in the file system. + """ evc_connector = EvcConnector(self.asc3app_path, self.evc_sumo_cfg_path) self.assertEqual(len(evc_connector.get_controller_cfg_list()), 2) def test_cob_to_traffic_light_status(self): + """ + Test the functionality of the `COB_to_traffic_light_status` method of the `EvcConnector` class. + + This method tests if the `COB_to_traffic_light_status` method can correctly convert the + state of a Controller Output Bit (COB) to a traffic light state string ('g', 'y', 'r'). + The test cases use the `TestControllerIOGreen`, `TestControllerIOYellow`, and `TestControllerIORed` + classes to simulate the COB states. + """ evc_connector = EvcConnector(self.asc3app_path, self.evc_sumo_cfg_path) phase = 1 ## get green light string @@ -71,21 +106,59 @@ def test_cob_to_traffic_light_status(self): state = evc_connector.COB_to_traffic_light_status(red_io, phase) self.assertEqual(state, "r") - def test_get_traffic_light_status(self): + def test_get_traffic_light_state(self): + """ + Test the functionality of the `get_traffic_light_state_from_EVC` method of the `EvcConnector` class. + + This method tests if the `get_traffic_light_state_from_EVC` method can correctly read and return the + state of the traffic lights for the specified phases from the EVC. + The test cases use the `TestControllerIOGreen`, `TestControllerIOYellow`, and `TestControllerIORed` + classes to simulate the COB states. + """ evc_connector = EvcConnector(self.asc3app_path, self.evc_sumo_cfg_path) ## get green light state string green_io = TestControllerIOGreen() - state = evc_connector.get_traffic_light_status_from_EVC(green_io, [{"sumoTlStateIndex": [0, 1], "evcPhaseId": 1}, {"sumoTlStateIndex": [2, 3], "evcPhaseId": 2}]) + state = evc_connector.get_traffic_light_state_from_EVC(green_io, [{"sumoTlStateIndex": [0, 1], "evcPhaseId": 1}, {"sumoTlStateIndex": [2, 3], "evcPhaseId": 2}]) self.assertEqual(state, "gggg") ## get yellow light state string yellow_io = TestControllerIOYellow() - state = evc_connector.get_traffic_light_status_from_EVC(yellow_io, [{"sumoTlStateIndex": [0, 1], "evcPhaseId": 1}, {"sumoTlStateIndex": [2, 3], "evcPhaseId": 2}]) + state = evc_connector.get_traffic_light_state_from_EVC(yellow_io, [{"sumoTlStateIndex": [0, 1], "evcPhaseId": 1}, {"sumoTlStateIndex": [2, 3], "evcPhaseId": 2}]) self.assertEqual(state, "yyyy") ## get red light state string red_io = TestControllerIORed() - state = evc_connector.get_traffic_light_status_from_EVC(red_io, [{"sumoTlStateIndex": [0, 1], "evcPhaseId": 1}, {"sumoTlStateIndex": [2, 3], "evcPhaseId": 2}]) + state = evc_connector.get_traffic_light_state_from_EVC(red_io, [{"sumoTlStateIndex": [0, 1], "evcPhaseId": 1}, {"sumoTlStateIndex": [2, 3], "evcPhaseId": 2}]) self.assertEqual(state, "rrrr") + + def test_set_induction_loop_status_to_EVC(self): + """ + Test the functionality of the `set_induction_loop_status_to_EVC` method of the `EvcConnector` class. + + This method tests if the `set_induction_loop_status_to_EVC` method can correctly set the induction loop status + to the EVC for both on and off states. The test cases use the `TestControllerIOOn` and `TestControllerIOOff` + classes to simulate the controller inputs for the induction loop state. + """ + + evc_connector = EvcConnector(self.asc3app_path, self.evc_sumo_cfg_path) + + # Create an instance of the TestControllerIOOn and TestControllerIOOff class to + # simulate the controller input for the induction loop state being on and off + cib_on_io = TestControllerIOOn() + cib_off_io = TestControllerIOOff() + + # Check if the `set_induction_loop_status_to_EVC` method can correctly set the + # induction loop state to on for a given detector and lane index + ## on + self.assertEqual(evc_connector.set_induction_loop_status_to_EVC(cib_on_io, 1, 0), 1) + self.assertEqual(evc_connector.set_induction_loop_status_to_EVC(cib_on_io, 1, 1), 0) + ## off + self.assertEqual(evc_connector.set_induction_loop_status_to_EVC(cib_off_io, 1, 1), 1) + self.assertEqual(evc_connector.set_induction_loop_status_to_EVC(cib_off_io, 1, 0), 0) + + + + + if __name__ == '__main__': unittest.main() From 4eaf4bd416e8ddf32421b6262fc3c956f078f904 Mon Sep 17 00:00:00 2001 From: Misheel Bayartsengel Date: Tue, 21 Mar 2023 14:06:10 -0400 Subject: [PATCH 033/124] initial shell commit for infrastructure ambassador (#114) # PR Details ## Description This is initial commit to setup shell structure for infrastructure ambassador. The design mainly followed carma ambassador implementation at the time of this commit. ![image](https://user-images.githubusercontent.com/20613282/225988497-09f80692-72f0-4c9a-af5f-3edb7dc92f59.png) - It is assumed that there are multiple infrastructure components in the carma-simulation each being represented by `V2xInstance` class. - And all `V2xInstance` is managed by class called `V2xInstanceManager`. - Each infrastructure component is registered using `V2xRegistrationMessage` received by `V2xRegistrationReceiver` from `RegisrationAdapter`. - Time messages related to these instances are received by `V2xTimeMessageReceiver` using `V2xTimeMessage`. ## Related Issue ## Motivation and Context See above. ## How Has This Been Tested? local integration tested checking if the class builds in Eclipse IDE ## Types of changes - [ ] 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: - [X] I have added any new packages to the sonar-scanner.properties file - [ ] My change requires a change to the documentation. - [X] I have updated the documentation accordingly. - [X] I have read the **CONTRIBUTING** document. [CARMA Contributing Guide](Contributing.md) - [X] I have added tests to cover my changes. - [X] All new and existing tests passed. --------- Co-authored-by: Cheng Yuan --- .../ambassador/ApplicationAmbassadorTest.java | 6 +- .../ambassador/InfrastructureInstance.java | 74 ++++++++++++ .../InfrastructureInstanceManager.java | 44 +++++++ .../InfrastructureMessageAmbassador.java | 42 ++++++- .../InfrastructureRegistrationMessage.java | 34 ++++++ .../InfrastructureRegistrationReceiver.java | 105 +++++++++++++++++ .../ambassador/InfrastructureTimeMessage.java | 35 ++++++ .../InfrastructureTimeMessageReceiver.java | 108 ++++++++++++++++++ .../sumo/traci/commands/TraciSetOrder.java | 32 +++--- .../templates/leidos-license-header-2022.txt | 13 +++ .../templates/leidos-license-header-2023.txt | 13 +++ co-simulation/pom.xml | 2 + co-simulation/rti/mosaic-rti-core/pom.xml | 4 +- 13 files changed, 491 insertions(+), 21 deletions(-) create mode 100644 co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java create mode 100644 co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java create mode 100644 co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java create mode 100644 co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java create mode 100644 co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java create mode 100644 co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessageReceiver.java create mode 100644 co-simulation/legal/templates/leidos-license-header-2022.txt create mode 100644 co-simulation/legal/templates/leidos-license-header-2023.txt diff --git a/co-simulation/fed/mosaic-application/src/test/java/org/eclipse/mosaic/fed/application/ambassador/ApplicationAmbassadorTest.java b/co-simulation/fed/mosaic-application/src/test/java/org/eclipse/mosaic/fed/application/ambassador/ApplicationAmbassadorTest.java index e014db60..bf885300 100644 --- a/co-simulation/fed/mosaic-application/src/test/java/org/eclipse/mosaic/fed/application/ambassador/ApplicationAmbassadorTest.java +++ b/co-simulation/fed/mosaic-application/src/test/java/org/eclipse/mosaic/fed/application/ambassador/ApplicationAmbassadorTest.java @@ -158,7 +158,7 @@ public void loadJarOnStartup() throws Exception { try { //PRE-ASSERT, make sure it cannot found by class loader BEFORE actual jar loading SimulationKernel.SimulationKernel.getClassLoader().loadClass( - "load.from.jar.VehicleApplication" + "org.eclipse.mosaic.fed.application.app.api.VehicleApplication" ); fail(); } catch (Throwable e) { @@ -168,9 +168,9 @@ public void loadJarOnStartup() throws Exception { // RUN initialize Application, which searches for Jars in the same directory, where the ambassador configuration is in new ApplicationAmbassador(applicationParams); - // ASSERT if classes in Jar could be loaded successfully. This class is in "application-from-jar.jar" only. + // ASSERT if classes in Jar could be loaded successfully. This class is in "application-from-jar.jar" only. SimulationKernel.SimulationKernel.getClassLoader().loadClass( - "load.from.jar.VehicleApplication" + "org.eclipse.mosaic.fed.application.app.api.VehicleApplication" ); } diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java new file mode 100644 index 00000000..6e6b544f --- /dev/null +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 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. + */ + +package org.eclipse.mosaic.fed.infrastructure.ambassador; + +import org.eclipse.mosaic.lib.geo.GeoPoint; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; + +/** + * Connection manager and data object to associate with a single infrastructure instance in XIL + * NOTE: TODO See carma.ambassador for reference + */ +public class InfrastructureInstance { + + private DatagramSocket rxMsgsSocket = null; + private InetAddress targetAddress; + private int registrationPort; + private int timeSyncPort; + private GeoPoint location = null; + + public InfrastructureInstance() { + // TODO + // TODO Initialize Datagram Socket + } + + public InetAddress getTargetAddress() { + return targetAddress; + } + + public void setTargetAddress(InetAddress targetAddress) { + this.targetAddress = targetAddress; + } + + public void setLocation(GeoPoint location) { + this.location = location; + } + + public GeoPoint getLocation() { + return this.location; + } + + /** + * Sends the data to the Infrastructure Device communications interface configured at construction time. + * @param data The binary data to transmit + * @throws IOException If there is an issue with the underlying socket object or methods + */ + public void sendMsgs(byte[] data) throws IOException { + if (rxMsgsSocket == null) { + throw new IllegalStateException("Attempted to send data before opening socket"); + } + + DatagramPacket packet = new DatagramPacket(data, data.length, targetAddress, registrationPort); + rxMsgsSocket.send(packet); + + } +} diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java new file mode 100644 index 00000000..7afe44d9 --- /dev/null +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 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. + */ + +package org.eclipse.mosaic.fed.infrastructure.ambassador; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.mosaic.fed.infrastructure.ambassador.InfrastructureRegistrationMessage; +import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; +import org.eclipse.mosaic.interactions.traffic.VehicleUpdates; +import org.eclipse.mosaic.lib.enums.AdHocChannel; +import org.eclipse.mosaic.lib.objects.addressing.AdHocMessageRoutingBuilder; +import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xContent; +import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xMessage; +import org.eclipse.mosaic.lib.objects.v2x.MessageRouting; +import org.eclipse.mosaic.lib.objects.vehicle.VehicleData; + +/** + * Session management class for Infrastructure instances communicating with MOSAIC + * NOTE: TODO See carma.ambassador for reference + */ +public class InfrastructureInstanceManager { + private Map managedInstances = new HashMap<>(); + private double currentSimulationTime; + +} diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 344234c6..7eab5b2d 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -24,6 +24,13 @@ import org.eclipse.mosaic.interactions.application.ExternalMessage; import org.eclipse.mosaic.interactions.application.InfrastructureV2xMessageReception; +import org.eclipse.mosaic.fed.infrastructure.ambassador.InfrastructureRegistrationMessage; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + /** * Implementation of a {@link AbstractFederateAmbassador} for Infrastructure * message ambassador. @@ -40,6 +47,22 @@ public class InfrastructureMessageAmbassador extends AbstractFederateAmbassador */ InfrastructureConfiguration infrastructureConfiguration; + /** + * List of infrastructures that are controlled + */ + private final HashMap v2xMap = new HashMap<>(); + + /** + * The number of CARMA vehicles. + */ + int numberOfInfrastructureInstances = 0; + + private InfrastructureRegistrationReceiver InfrastructureRegistrationReceiver; + private Thread registrationRxBackgroundThread; + private InfrastructureTimeMessageReceiver InfrastructureTimeMessageReceiver; + private Thread v2xTimeRxBackgroundThread; + private InfrastructureInstanceManager InfrastructureInstanceManager = new InfrastructureInstanceManager(); + /** * Create a new {@link InfrastructureMessageAmbassador} object. * @@ -77,7 +100,6 @@ public InfrastructureMessageAmbassador(AmbassadorParameter ambassadorParameter) @Override public void initialize(long startTime, long endTime) throws InternalFederateException { super.initialize(startTime, endTime); - currentSimulationTime = startTime; try { rti.requestAdvanceTime(currentSimulationTime, 0, (byte) 1); @@ -85,6 +107,13 @@ public void initialize(long startTime, long endTime) throws InternalFederateExce log.error("Error during advanceTime request", e); throw new InternalFederateException(e); } + + // TODO Initialize listener socket and thread for Infrastructure Registration messages + + // TODO Initialize listener socket and thread for Infrastructure time sync messages + + // TODO Register any V2x infrastructures from config if any + } /** @@ -99,9 +128,13 @@ public void processInteraction(Interaction interaction) throws InternalFederateE String type = interaction.getTypeId(); long interactionTime = interaction.getTime(); log.trace("Process interaction with type '{}' at time: {}", type, interactionTime); + // Infrastructure message reception if (interaction.getTypeId().equals(InfrastructureV2xMessageReception.TYPE_ID)) { this.receiveInteraction((InfrastructureV2xMessageReception) interaction); } + // TODO Time sync message reception + + // TODO V2xHub infrastructure Registration reception } /** @@ -131,10 +164,17 @@ private synchronized void receiveInteraction(InfrastructureV2xMessageReception i public synchronized void processTimeAdvanceGrant(long time) throws InternalFederateException { if (time < currentSimulationTime) { + // process time advance only if time is equal or greater than the next + // simulation time step return; } try { + // TODO actions to do on queued Infrastructure instance registration attempts + + // TODO actions to do on queued Infrastructure time sync messages + + // TODO actions to do on queued v2x message receiver's received messages currentSimulationTime += infrastructureConfiguration.updateInterval * TIME.MILLI_SECOND; diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java new file mode 100644 index 00000000..7825ef71 --- /dev/null +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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. + */ + +package org.eclipse.mosaic.fed.infrastructure.ambassador; +import org.eclipse.mosaic.lib.geo.GeoPoint; + +/** + * A message to be sent by Infrastructure Device when it registers with the carma-mosaic ambassador + * NOTE: TODO See carma.ambassador for reference + */ +public class InfrastructureRegistrationMessage { + private String rxMessageIpAddress; + private int timeSyncListenPort = 1517; // TODO + private int regstratinoListenPort = 1516; // TODO + private GeoPoint location = null; + + public InfrastructureRegistrationMessage() { + + } + +} diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java new file mode 100644 index 00000000..885daa53 --- /dev/null +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023 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. + */ + +package org.eclipse.mosaic.fed.infrastructure.ambassador; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import com.google.gson.Gson; + +/** + * Worker thread Runnable for operating a listen socket to receive outbound Infrastructure Messages from Infrastructure Device instances + * This {@link Runnable} instance will operate a UDP socket to subscribe to packets from the V2x infrastructure's + * adapter. Upon receiving a packet, it will be enqueued for the primary thread to process the data once it ticks to a + * simulation processing step + * NOTE: TODO See carma.ambassador for reference + */ +public class InfrastructureRegistrationReceiver implements Runnable { + private Queue rxQueue = new LinkedList<>(); + private DatagramSocket listenSocket = null; + private static final int listenPort = 1515; // TODO + private boolean running = false; + private static final int UDP_MTU = 1535; // TODO Maximum Transmission Unit + + /** + * Initialize the listen socket for messages from the Infrastructure Device NS-3 Adapter + * @throws RuntimeException iff socket instantiation fails + */ + public void init() { + try { + listenSocket = new DatagramSocket(listenPort); + + } catch (SocketException e) { + throw new RuntimeException(e); + } + } + + @Override + public void run() { + byte[] buf = new byte[UDP_MTU]; + running = true; + while (running) { + DatagramPacket msg = new DatagramPacket(buf, buf.length); + try { + listenSocket.receive(msg); + } catch (IOException e) { + throw new RuntimeException(e); + } + + // parse message + String receivedPacket = new String(msg.getData()); + Gson gson = new Gson(); + InfrastructureRegistrationMessage parsedMessage = gson.fromJson(receivedPacket, InfrastructureRegistrationMessage.class); + + // Enqueue message for processing on main thread + synchronized (rxQueue) { + rxQueue.add(parsedMessage); + } + } + } + + /** + * Stop the runnable instance + */ + public void stop() { + if (listenSocket != null) { + listenSocket.close(); + } + + running = false; + } + + /** + * Query the current buffer of outbound messages. Clears the currently stored buffer once called. Thread-safe. + * @return The list of received outbound message from all Infrastructure Device instances since last call of this method + */ + public List getReceivedMessages() { + List output = new ArrayList<>(); + + synchronized (rxQueue) { + output.addAll(rxQueue); + rxQueue.clear(); + } + + return output; + } +} diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java new file mode 100644 index 00000000..b3d9a047 --- /dev/null +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 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. + */ + +package org.eclipse.mosaic.fed.infrastructure.ambassador; + +import java.net.InetAddress; +import java.nio.charset.StandardCharsets; + +/** + * Message to be sent or received by the Infrastructure Device Adapter interface + * NOTE: TODO See .ambassador for reference + + */ +public class InfrastructureTimeMessage { + private int timestep; + private int seq; + + + public InfrastructureTimeMessage() { + } + +} diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessageReceiver.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessageReceiver.java new file mode 100644 index 00000000..038f5b38 --- /dev/null +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessageReceiver.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 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. + */ + +package org.eclipse.mosaic.fed.infrastructure.ambassador; + +import org.eclipse.mosaic.lib.misc.Tuple; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * Worker thread Runnable for operating a listen socket to receive outbound Infrastructure Messages from Infrastructure Device instances + * This {@link Runnable} instance will operate a UDP socket to subscribe to packets from the V2x infrastructure + * adapter. Upon receiving a packet, it will be enqueued for the primary thread to process the data once it ticks to a + * simulation processing step + * NOTE: TODO See .ambassador for reference + */ +public class InfrastructureTimeMessageReceiver implements Runnable { + + private Queue> rxQueue = new LinkedList<>(); + private DatagramSocket listenSocket = null; + private static final int listenPort = 1516; // TODO + private boolean running = false; + private static final int UDP_MTU = 1535; // TOD OMaximum Transmission Unit + + /** + * Initialize the listen socket for messages from the Infrastructure Device NS-3 Adapter + * @throws RuntimeException iff socket instantiation fails + */ + public void init() { + try { + listenSocket = new DatagramSocket(listenPort); + + } catch (SocketException e) { + throw new RuntimeException(e); + } + } + + @Override + public void run() { + byte[] buf = new byte[UDP_MTU]; + running = true; + while (running) { + DatagramPacket msg = new DatagramPacket(buf, buf.length); + InetAddress senderAddr = msg.getAddress(); + + try { + listenSocket.receive(msg); + } catch (IOException e) { + throw new RuntimeException(e); + } + + // parse message + InfrastructureTimeMessage parsedMessage = null; /* = new InfrastructureTimeMessage(msg.getData()); */ + + // Enqueue message for processing on main thread + synchronized (rxQueue) { + rxQueue.add(new Tuple<>(senderAddr, parsedMessage)); + } + } + } + + /** + * Stop the runnable instance + */ + public void stop() { + if (listenSocket != null) { + listenSocket.close(); + } + + running = false; + } + + /** + * Query the current buffer of outbound messages. Clears the currently stored buffer once called. Thread-safe. + * @return The list of received outbound message from all Infrastructure Device instances since last call of this method + */ + public List> getReceivedMessages() { + List> output = new ArrayList<>(); + + synchronized (rxQueue) { + output.addAll(rxQueue); + rxQueue.clear(); + } + + return output; + } +} diff --git a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/commands/TraciSetOrder.java b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/commands/TraciSetOrder.java index 7cd4f7a8..3dc12d44 100644 --- a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/commands/TraciSetOrder.java +++ b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/traci/commands/TraciSetOrder.java @@ -1,18 +1,20 @@ - /* - * 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. - */ +/* + * Copyright (C) 2023 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. + */ + + package org.eclipse.mosaic.fed.sumo.traci.commands; diff --git a/co-simulation/legal/templates/leidos-license-header-2022.txt b/co-simulation/legal/templates/leidos-license-header-2022.txt new file mode 100644 index 00000000..6d6100a6 --- /dev/null +++ b/co-simulation/legal/templates/leidos-license-header-2022.txt @@ -0,0 +1,13 @@ +Copyright (C) 2019-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. \ No newline at end of file diff --git a/co-simulation/legal/templates/leidos-license-header-2023.txt b/co-simulation/legal/templates/leidos-license-header-2023.txt new file mode 100644 index 00000000..2652dd42 --- /dev/null +++ b/co-simulation/legal/templates/leidos-license-header-2023.txt @@ -0,0 +1,13 @@ +Copyright (C) 2023 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. \ No newline at end of file diff --git a/co-simulation/pom.xml b/co-simulation/pom.xml index 1f5dbaf0..24af598a 100644 --- a/co-simulation/pom.xml +++ b/co-simulation/pom.xml @@ -469,6 +469,8 @@
${parent.dir}/legal/templates/license-header-2020.txt
${parent.dir}/legal/templates/license-header-2021.txt
${parent.dir}/legal/templates/cosimulation-license-header-2021.txt
+
${parent.dir}/legal/templates/leidos-license-header-2022.txt
+
${parent.dir}/legal/templates/leidos-license-header-2023.txt
diff --git a/co-simulation/rti/mosaic-rti-core/pom.xml b/co-simulation/rti/mosaic-rti-core/pom.xml index d2488bae..2c844d04 100644 --- a/co-simulation/rti/mosaic-rti-core/pom.xml +++ b/co-simulation/rti/mosaic-rti-core/pom.xml @@ -14,7 +14,7 @@ Eclipse MOSAIC Runtime Infrastructure Implementation - true + false @@ -60,7 +60,7 @@ TestFederate - ${project.build.testSourceDirectory} + ${project.build.testOutputDirectory} test-jar From 9e928931953d4b00d20ed065e5175c2bbc5c28c0 Mon Sep 17 00:00:00 2001 From: paulbourelly999 Date: Tue, 21 Mar 2023 15:33:02 -0400 Subject: [PATCH 034/124] Updated Java version to 11 for maven sonar scanner plugin --- .github/workflows/runtest.yml | 4 ++-- Dockerfile | 2 -- co-simulation/pom.xml | 2 +- co-simulation/test/ci/ci-image-mvn-sumo/Dockerfile | 2 +- docker/install.sh | 5 ++--- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/runtest.yml b/.github/workflows/runtest.yml index f9b90955..5501dd99 100644 --- a/.github/workflows/runtest.yml +++ b/.github/workflows/runtest.yml @@ -7,10 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up JDK 8 + - name: Set up JDK 11 uses: actions/setup-java@v3 # The setup-java action provides the functionality for GitHub Actions runners for Downloading and setting up a requested version of Java with: - java-version: 8 + java-version: 11 distribution: "temurin" - name: Build and analyze env: diff --git a/Dockerfile b/Dockerfile index e6195008..f25a2a04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,8 +34,6 @@ LABEL org.label-schema.vcs-url="https://github.com/usdot-fhwa-stol/carma-simulat LABEL org.label-schema.vcs-ref=${VCS_REF} LABEL org.label-schema.build-date=${BUILD_DATE} -ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/ -RUN export JAVA_HOME # Env vars for the nvidia-container-runtime. ENV NVIDIA_VISIBLE_DEVICES all diff --git a/co-simulation/pom.xml b/co-simulation/pom.xml index 24af598a..34dc9d97 100644 --- a/co-simulation/pom.xml +++ b/co-simulation/pom.xml @@ -83,7 +83,7 @@ - 8 + 11 usdot-fhwa-stol https://sonarcloud.io diff --git a/co-simulation/test/ci/ci-image-mvn-sumo/Dockerfile b/co-simulation/test/ci/ci-image-mvn-sumo/Dockerfile index 1b4aef61..8b3c1bef 100644 --- a/co-simulation/test/ci/ci-image-mvn-sumo/Dockerfile +++ b/co-simulation/test/ci/ci-image-mvn-sumo/Dockerfile @@ -1,4 +1,4 @@ -FROM maven:3.6.3-adoptopenjdk-8 +FROM maven:3.8.3-adoptopenjdk-11 ENV DEBIAN_FRONTEND=noninteractive diff --git a/docker/install.sh b/docker/install.sh index b5f7ac8e..408070da 100755 --- a/docker/install.sh +++ b/docker/install.sh @@ -28,17 +28,16 @@ sudo apt-get update sudo apt-get install -y --allow-unauthenticated gcc-7 g++-7 python3.6 unzip tar python3.6-dev \ pkg-config sqlite3 autoconf libtool curl make libxml2 libsqlite3-dev \ libxml2-dev cmake libxerces-c-dev libfox-1.6-dev libgdal-dev libproj-dev \ - libgl2ps-dev python3.7 python3-pip automake openjdk-8-jdk ant python3.7-dev \ + libgl2ps-dev python3.7 python3-pip automake openjdk-11-jdk ant python3.7-dev \ python3.7-distutils x11-xserver-utils dconf-editor dbus-x11 libglvnd0 libgl1 \ libglx0 libegl1 libxext6 libx11-6 python3-dev \ build-essential pkg-config lbzip2 libprotobuf-dev protobuf-compiler patch rsync \ - wget vim nano xterm default-jdk libprotobuf-dev + wget vim nano xterm libprotobuf-dev sudo rm -rf /var/lib/apt/lists/* export JAVA_HOME="/usr/lib/jvm/java-11-openjdk-amd64" #update-alternatives --set python /usr/bin/python3.7 sudo apt-get clean -sudo rm -rf /var/cache/oracle-jdk8-installer sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 20 --slave /usr/bin/g++ g++ /usr/bin/g++-7 sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.7 1 sudo update-alternatives --set python /usr/bin/python3.7 From d89cfe59431a9d45986b83bc6f94fa171240a8d4 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Tue, 21 Mar 2023 21:47:27 -0400 Subject: [PATCH 035/124] Update unit_test.py --- evc-sumo/test/unit_test.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/evc-sumo/test/unit_test.py b/evc-sumo/test/unit_test.py index b3e7287b..0097525e 100644 --- a/evc-sumo/test/unit_test.py +++ b/evc-sumo/test/unit_test.py @@ -148,12 +148,15 @@ def test_set_induction_loop_status_to_EVC(self): # Check if the `set_induction_loop_status_to_EVC` method can correctly set the # induction loop state to on for a given detector and lane index + evc_phase_id = 1 + sumo_induction_loop_status = 1 ## on - self.assertEqual(evc_connector.set_induction_loop_status_to_EVC(cib_on_io, 1, 0), 1) - self.assertEqual(evc_connector.set_induction_loop_status_to_EVC(cib_on_io, 1, 1), 0) + self.assertEqual(evc_connector.set_induction_loop_status_to_EVC(cib_on_io, evc_phase_id, sumo_induction_loop_status), 1) + self.assertEqual(evc_connector.set_induction_loop_status_to_EVC(cib_on_io, evc_phase_id, sumo_induction_loop_status), 1) + sumo_induction_loop_status = 0 ## off - self.assertEqual(evc_connector.set_induction_loop_status_to_EVC(cib_off_io, 1, 1), 1) - self.assertEqual(evc_connector.set_induction_loop_status_to_EVC(cib_off_io, 1, 0), 0) + self.assertEqual(evc_connector.set_induction_loop_status_to_EVC(cib_off_io, evc_phase_id, sumo_induction_loop_status), 0) + self.assertEqual(evc_connector.set_induction_loop_status_to_EVC(cib_off_io, evc_phase_id, sumo_induction_loop_status), 0) From 0e332f0b1c98cd4429ded9bb1a02ef96a9e332ea Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Tue, 21 Mar 2023 21:48:19 -0400 Subject: [PATCH 036/124] Update evc_connector.py --- evc-sumo/src/evc_connector.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/evc-sumo/src/evc_connector.py b/evc-sumo/src/evc_connector.py index 03c17dd9..91cf6bf8 100644 --- a/evc-sumo/src/evc_connector.py +++ b/evc-sumo/src/evc_connector.py @@ -70,7 +70,8 @@ def COB_to_traffic_light_status(self, controller_io, evc_phase_id): elif controller_io.is_cob_on( (evc_phase_id - 1) + (MAX_INTERSECTION_PHASES * 0) ): return 'g' else: - return None + ## if state is not r, y or g then return O which is switched off state + return 'O' def get_traffic_light_state_from_EVC(self, controller_io, phases): """ @@ -93,10 +94,7 @@ def get_traffic_light_state_from_EVC(self, controller_io, phases): for phase in phases: state_string = [self.COB_to_traffic_light_status(controller_io, phase['evcPhaseId']) if i in phase['sumoTlStateIndex'] else x for i,x in enumerate(state_string)] - if None in state_string: - return None - else: - return ''.join(state_string) + return ''.join(state_string) def set_induction_loop_status_to_EVC(self, controller_io, evc_phase_id, sumo_induction_loop_status): """ @@ -113,21 +111,25 @@ def set_induction_loop_status_to_EVC(self, controller_io, evc_phase_id, sumo_ind type: int Returns: - - int: return a integer to represent cib is on or off. + - int: return an integer to represent cib is on or off. cib on: 1 cib off: 0 """ if sumo_induction_loop_status != 0: if controller_io.is_cib_off(evc_phase_id - 1): + ## CIB is currently off, turn to on controller_io.cib_on(evc_phase_id - 1) return 1 else: - return 0 + ## CIB is currently on + return 1 else: if controller_io.is_cib_on(evc_phase_id - 1): + ## CIB is currently on, turn to off controller_io.cib_off(evc_phase_id - 1) - return 1 + return 0 else: + ## CIB is currently off return 0 def get_controller_cfg_list(self): @@ -209,9 +211,7 @@ def run(self, sumo_connector): ## Update SUMO traffic light state tl_state_string = self.get_traffic_light_state_from_EVC(self.controller_io_list[i], controller['evcPhases']) - if tl_state_string is not None: - sumo_connector.set_traffic_light_state_to_SUMO(controller['sumoTlId'], tl_state_string) - else: - print("Error: Could not connect to EVC, please check EVC-SUMO config") + sumo_connector.set_traffic_light_state_to_SUMO(controller['sumoTlId'], tl_state_string) + self.tick( int(1 / (sumo_connector.traci_get_step_length() * 10)) ) From e9409b33d2180e698d4a42b104f44ff69c0ba05d Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 22 Mar 2023 11:41:54 -0400 Subject: [PATCH 037/124] Correct comment to match the change --- evc-sumo/src/evc_connector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evc-sumo/src/evc_connector.py b/evc-sumo/src/evc_connector.py index 91cf6bf8..357372b5 100644 --- a/evc-sumo/src/evc_connector.py +++ b/evc-sumo/src/evc_connector.py @@ -86,7 +86,7 @@ def get_traffic_light_state_from_EVC(self, controller_io, phases): Returns: - string: A string representing the current state of the traffic light. - The possible characters are "r" for red, "y" for yellow, and "g" for green and None for exception + The possible characters are "r" for red, "y" for yellow, "g" for green and "O" for switched off """ ## get number of characters in state string for SUMO and init sumo tl state string with all red From 78aa0e9e98c544b384bb5e36daed53297c2b35bd Mon Sep 17 00:00:00 2001 From: Eric Chen <122404059+EricChen-Lei@users.noreply.github.com> Date: Wed, 22 Mar 2023 16:51:17 -0400 Subject: [PATCH 038/124] upload evc docker upload evc essential files --- evc-sumo/Dockerfile | 42 +++++++++++ evc-sumo/docker/build-image.sh | 94 ++++++++++++++++++++++++ evc-sumo/docker/checkout.sh | 44 +++++++++++ evc-sumo/docker/entrypoint.sh | 22 ++++++ evc-sumo/docker/env.sh | 26 +++++++ evc-sumo/docker/get-component-version.sh | 21 ++++++ evc-sumo/docker/get-image-name.sh | 25 +++++++ evc-sumo/docker/get-repo-name.sh | 17 +++++ evc-sumo/docker/get-system-version.sh | 20 +++++ evc-sumo/docker/install.sh | 40 ++++++++++ evc-sumo/docker/token.txt | 1 + 11 files changed, 352 insertions(+) create mode 100644 evc-sumo/Dockerfile create mode 100755 evc-sumo/docker/build-image.sh create mode 100755 evc-sumo/docker/checkout.sh create mode 100755 evc-sumo/docker/entrypoint.sh create mode 100755 evc-sumo/docker/env.sh create mode 100755 evc-sumo/docker/get-component-version.sh create mode 100755 evc-sumo/docker/get-image-name.sh create mode 100755 evc-sumo/docker/get-repo-name.sh create mode 100755 evc-sumo/docker/get-system-version.sh create mode 100755 evc-sumo/docker/install.sh create mode 100644 evc-sumo/docker/token.txt diff --git a/evc-sumo/Dockerfile b/evc-sumo/Dockerfile new file mode 100644 index 00000000..895108f7 --- /dev/null +++ b/evc-sumo/Dockerfile @@ -0,0 +1,42 @@ +# Copyright (C) 2023 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. + +FROM ubuntu:20.04 + +# Set environment variables for non-interactive installation +ENV DEBIAN_FRONTEND=noninteractive +ARG DEBIAN_FRONTEND=noninteractive +ENV SUMO_HOME /usr/share/sumo +RUN apt-get update && apt-get install -y sudo + +# Create a group with GID 1000 +RUN groupadd -g 1000 carma + +# Create a user with UID 1000 and GID 1000 for running the browser +RUN useradd -m -u 1000 -g 1000 carma +RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + +# Set the user to be used for the following commands +WORKDIR /home/carma +COPY --chown=carma:carma /docker ./docker +COPY --chown=carma:carma /src ./src +COPY --chown=carma:carma /test ./test + +RUN docker/install.sh +USER carma +# Set the working directory for the browser user +WORKDIR /home/carma/src + +# Start the virtual framebuffer (Xvfb) and xterm +CMD x-terminal-emulator -e python3.8 evc_sumo_bridge.py --asc3app-path "../evcfile/asc3app-application.zip" \ No newline at end of file diff --git a/evc-sumo/docker/build-image.sh b/evc-sumo/docker/build-image.sh new file mode 100755 index 00000000..3d9fceb9 --- /dev/null +++ b/evc-sumo/docker/build-image.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +# Copyright (C) 2023 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. + +USERNAME=usdotfhwastol + +cd "$(dirname "$0")" +# IMAGE=$(./get-image-name.sh | tr '[:upper:]' '[:lower:]') +IMAGE=evc + +echo "" +echo "##### $IMAGE Docker Image Build Script #####" +echo "" + +while [[ $# -gt 0 ]]; do + arg="$1" + case $arg in + -v|--version) + COMPONENT_VERSION_STRING="$2" + shift + shift + ;; + --system-release) + SYSTEM_RELEASE=true + shift + ;; + -p|--push) + PUSH=true + shift + ;; + -d|--develop) + USERNAME=usdotfhwastoldev + COMPONENT_VERSION_STRING=develop + shift + ;; + esac +done + +if [[ -z "$COMPONENT_VERSION_STRING" ]]; then + COMPONENT_VERSION_STRING=$("./get-component-version.sh") +fi + +echo "Building docker image for $IMAGE version: $COMPONENT_VERSION_STRING" +echo "Final image name: $USERNAME/$IMAGE:$COMPONENT_VERSION_STRING" + +cd .. +if [[ $COMPONENT_VERSION_STRING = "develop" ]]; then + sed "s|usdotfhwastoldev/|$USERNAME/|g; s|usdotfhwastolcandidate/|$USERNAME/|g; s|usdotfhwastol/|$USERNAME/|g; s|:[0-9]*\.[0-9]*\.[0-9]*|:$COMPONENT_VERSION_STRING|g; s|checkout.sh|checkout.sh -d|g" \ + Dockerfile | docker build -f - --no-cache -t $USERNAME/$IMAGE:$COMPONENT_VERSION_STRING \ + --build-arg VERSION="$COMPONENT_VERSION_STRING" \ + --build-arg VCS_REF=`git rev-parse --short HEAD` \ + --build-arg BUILD_DATE=`date -u +”%Y-%m-%dT%H:%M:%SZ”` . +else + docker build --no-cache -t $USERNAME/$IMAGE:$COMPONENT_VERSION_STRING \ + --build-arg VERSION="$COMPONENT_VERSION_STRING" \ + --build-arg VCS_REF=`git rev-parse --short HEAD` \ + --build-arg BUILD_DATE=`date -u +”%Y-%m-%dT%H:%M:%SZ”` . +fi + +TAGS=() +TAGS+=("$USERNAME/$IMAGE:$COMPONENT_VERSION_STRING") + +docker tag $USERNAME/$IMAGE:$COMPONENT_VERSION_STRING $USERNAME/$IMAGE:latest +TAGS+=("$USERNAME/$IMAGE:latest") + +echo "Tagged $USERNAME/$IMAGE:$COMPONENT_VERSION_STRING as $USERNAME/$IMAGE:latest" + +if [ "$SYSTEM_RELEASE" = true ]; then + SYSTEM_VERSION_STRING=$("./get-system-version.sh") + docker tag $USERNAME/$IMAGE:$COMPONENT_VERSION_STRING $USERNAME/$IMAGE:$SYSTEM_VERSION_STRING + echo "Tagged $USERNAME/$IMAGE:$COMPONENT_VERSION_STRING as $USERNAME/$IMAGE:$SYSTEM_VERSION_STRING" + TAGS+=("$USERNAME/$IMAGE:$SYSTEM_VERSION_STRING") +fi + +if [ "$PUSH" = true ]; then + for tag in $TAGS; do + docker push "${tag}" + done +fi + +echo "" +echo "##### $IMAGE Docker Image Build Done! #####" diff --git a/evc-sumo/docker/checkout.sh b/evc-sumo/docker/checkout.sh new file mode 100755 index 00000000..4e4f1753 --- /dev/null +++ b/evc-sumo/docker/checkout.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Copyright (C) 2023 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. + +# CARMA packages checkout script +# Optional argument to set the root checkout directory with no ending '/' default is '~' + +set -ex + +dir=~ +while [[ $# -gt 0 ]]; do + arg="$1" + case $arg in + -d|--develop) + BRANCH=develop + shift + ;; + -r|--root) + dir=$2 + shift + shift + ;; + esac +done + +if [[ "$BRANCH" = "develop" ]]; then + git clone https://github.com/usdot-fhwa-stol/carma-msgs.git ~/src/carma-msgs --branch $BRANCH --depth 1 + git clone https://github.com/usdot-fhwa-stol/carma-utils.git ~/src/carma-utils --branch $BRANCH --depth 1 +else + git clone https://github.com/usdot-fhwa-stol/carma-msgs.git ${dir}/src/carma-msgs --branch develop --depth 1 + git clone https://github.com/usdot-fhwa-stol/carma-utils.git ${dir}/src/carma-utils --branch develop --depth 1 +fi diff --git a/evc-sumo/docker/entrypoint.sh b/evc-sumo/docker/entrypoint.sh new file mode 100755 index 00000000..f9623ef7 --- /dev/null +++ b/evc-sumo/docker/entrypoint.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Copyright (C) 2023 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. + +if [[ -z "$@" ]]; then + source ~/.base-image/env.sh; cd /opt/carma-simulation; exec "bash" +else + source ~/.base-image/env.sh; cd /opt/carma-simulation; exec "$@" +fi + diff --git a/evc-sumo/docker/env.sh b/evc-sumo/docker/env.sh new file mode 100755 index 00000000..c2960c00 --- /dev/null +++ b/evc-sumo/docker/env.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Copyright (C) 2023 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. + +# Set environment variables here. + +export NS3_HOME="/opt/ns-3/" +export JAVA_HOME="/usr/lib/jvm/java-11-jdk-amd64" +export SUMO_HOME="/opt/sumo/sumo-1_12_0" +export CARLA_HOME="/opt/carla" +export CARMA_SIMULATION_HOME="/opt/carma-simulation" +export PATH="/usr/local/share/sumo/bin:/usr/local/share/sumo/tools:$PATH" +export PATH="/opt/maven/bin:$PATH" +export PATH="/opt/carma-simulation:$PATH" diff --git a/evc-sumo/docker/get-component-version.sh b/evc-sumo/docker/get-component-version.sh new file mode 100755 index 00000000..e53c07f6 --- /dev/null +++ b/evc-sumo/docker/get-component-version.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Copyright (C) 2023 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. + +cd "$(dirname "$0")" +cd .. +COMPONENT_TAG_PREFIX="${PWD##*/}" +git describe --all --match="$COMPONENT_TAG_PREFIX*" --always --dirty="-SNAPSHOT" | awk -F "/" '{print $NF}' | sed "s/$COMPONENT_TAG_PREFIX\_//" + diff --git a/evc-sumo/docker/get-image-name.sh b/evc-sumo/docker/get-image-name.sh new file mode 100755 index 00000000..d1cbfdab --- /dev/null +++ b/evc-sumo/docker/get-image-name.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Copyright (C) 2023 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. + +cd "$(dirname "$0")" +REPO_NAME="$(./get-repo-name.sh)" + +# This sed command based on Stack Overflow answer: https://stackoverflow.com/questions/28795479/awk-sed-script-to-convert-a-file-from-camelcase-to-underscores +# Asked by Corentin Peuvrel: https://stackoverflow.com/users/4608146/corentin-peuvrel +# Answered by pachopepe: https://stackoverflow.com/users/641896/pachopepe +# Credited in accordance with Stack Overflow's CC-BY license +echo $REPO_NAME | sed -r 's/CARMA/carma/' | sed -r 's/([A-Z])/-\L\1/g' | sed 's/^_//' + diff --git a/evc-sumo/docker/get-repo-name.sh b/evc-sumo/docker/get-repo-name.sh new file mode 100755 index 00000000..549ce487 --- /dev/null +++ b/evc-sumo/docker/get-repo-name.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Copyright (C) 2023 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. + +basename -s .git `git config --get remote.origin.url` diff --git a/evc-sumo/docker/get-system-version.sh b/evc-sumo/docker/get-system-version.sh new file mode 100755 index 00000000..f43b07cd --- /dev/null +++ b/evc-sumo/docker/get-system-version.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Copyright (C) 2023 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. + +SYSTEM_TAG_PREFIX="CARMASystem" + +git describe --all --match="$SYSTEM_TAG_PREFIX*" --always --dirty="-SNAPSHOT" | awk -F "/" '{print $NF}' + diff --git a/evc-sumo/docker/install.sh b/evc-sumo/docker/install.sh new file mode 100755 index 00000000..d810b87d --- /dev/null +++ b/evc-sumo/docker/install.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Copyright (C) 2023 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. + +set -e +#user must replace the content in docker/token.txt with their own github token +file="docker/token.txt" +#download apt dependencies +sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends firefox dbus-x11 x11-apps x11-utils x11-xserver-utils \ + xserver-xorg-video-dummy xserver-xorg-input-void xvfb libgl1-mesa-dri libgl1-mesa-glx libpulse0 \ + alsa-utils xterm wget build-essential libssl-dev libffi-dev python3-dev python3-pip curl unzip \ + python3.8 python3.8-dev python3.8-distutils python3.8-venv cmake sumo sumo-tools sumo-doc software-properties-common + +sudo add-apt-repository -y ppa:sumo/stable + +sudo dpkg --add-architecture i386 +sudo apt-get update +sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 -y +#update sumo version +sudo apt-get install -y sumo sumo-tools sumo-doc +evc_token=$(cat "$file") +sudo curl -H "Authorization: token ${evc_token}" -H 'Accept: application/vnd.github.v4.raw' -O -L https://api.github.com/repos/usdot-fhwa-stol/CARMASensitive/contents/evcfile.zip +sudo unzip evcfile.zip + + + + +cd evcfile && python3.8 -m pip install pyeos-0.1.1-py3-none-any.whl diff --git a/evc-sumo/docker/token.txt b/evc-sumo/docker/token.txt new file mode 100644 index 00000000..a3e1f526 --- /dev/null +++ b/evc-sumo/docker/token.txt @@ -0,0 +1 @@ + \ No newline at end of file From ad657507ca5b047c684feba40c0ada72aba7ae7e Mon Sep 17 00:00:00 2001 From: Eric Chen <122404059+EricChen-Lei@users.noreply.github.com> Date: Thu, 23 Mar 2023 15:05:29 -0400 Subject: [PATCH 039/124] PR changes fixed fixed kyle PR fixes --- evc-sumo/Dockerfile | 7 ++--- evc-sumo/docker/build-image.sh | 20 +++++++++++--- evc-sumo/docker/checkout.sh | 7 ----- evc-sumo/docker/get-system-version.sh | 2 +- evc-sumo/docker/install.sh | 40 ++++++++++++++++++++++++++- evc-sumo/docker/token.txt | 1 - 6 files changed, 59 insertions(+), 18 deletions(-) delete mode 100644 evc-sumo/docker/token.txt diff --git a/evc-sumo/Dockerfile b/evc-sumo/Dockerfile index 895108f7..aec8f947 100644 --- a/evc-sumo/Dockerfile +++ b/evc-sumo/Dockerfile @@ -15,8 +15,7 @@ FROM ubuntu:20.04 # Set environment variables for non-interactive installation -ENV DEBIAN_FRONTEND=noninteractive -ARG DEBIAN_FRONTEND=noninteractive +ARG EVC_TOKEN="NULL" ENV SUMO_HOME /usr/share/sumo RUN apt-get update && apt-get install -y sudo @@ -33,8 +32,8 @@ COPY --chown=carma:carma /docker ./docker COPY --chown=carma:carma /src ./src COPY --chown=carma:carma /test ./test -RUN docker/install.sh -USER carma +RUN docker/install.sh ${EVC_TOKEN} +USER carma # Set the working directory for the browser user WORKDIR /home/carma/src diff --git a/evc-sumo/docker/build-image.sh b/evc-sumo/docker/build-image.sh index 3d9fceb9..082f4466 100755 --- a/evc-sumo/docker/build-image.sh +++ b/evc-sumo/docker/build-image.sh @@ -16,9 +16,10 @@ USERNAME=usdotfhwastol + cd "$(dirname "$0")" # IMAGE=$(./get-image-name.sh | tr '[:upper:]' '[:lower:]') -IMAGE=evc +IMAGE=econolite-virtual-controller echo "" echo "##### $IMAGE Docker Image Build Script #####" @@ -45,6 +46,10 @@ while [[ $# -gt 0 ]]; do COMPONENT_VERSION_STRING=develop shift ;; + *) + evc_token=${arg} + shift + ;; esac done @@ -55,23 +60,30 @@ fi echo "Building docker image for $IMAGE version: $COMPONENT_VERSION_STRING" echo "Final image name: $USERNAME/$IMAGE:$COMPONENT_VERSION_STRING" +if [ -z $evc_token ]; + then + echo "No argument provided for evc_token, this script needs to be run with token" + exit 1 +fi + cd .. if [[ $COMPONENT_VERSION_STRING = "develop" ]]; then sed "s|usdotfhwastoldev/|$USERNAME/|g; s|usdotfhwastolcandidate/|$USERNAME/|g; s|usdotfhwastol/|$USERNAME/|g; s|:[0-9]*\.[0-9]*\.[0-9]*|:$COMPONENT_VERSION_STRING|g; s|checkout.sh|checkout.sh -d|g" \ Dockerfile | docker build -f - --no-cache -t $USERNAME/$IMAGE:$COMPONENT_VERSION_STRING \ --build-arg VERSION="$COMPONENT_VERSION_STRING" \ --build-arg VCS_REF=`git rev-parse --short HEAD` \ - --build-arg BUILD_DATE=`date -u +”%Y-%m-%dT%H:%M:%SZ”` . + --build-arg BUILD_DATE=`date -u +”%Y-%m-%dT%H:%M:%SZ”` \ + --build-arg EVC_TOKEN=$evc_token . else docker build --no-cache -t $USERNAME/$IMAGE:$COMPONENT_VERSION_STRING \ --build-arg VERSION="$COMPONENT_VERSION_STRING" \ --build-arg VCS_REF=`git rev-parse --short HEAD` \ - --build-arg BUILD_DATE=`date -u +”%Y-%m-%dT%H:%M:%SZ”` . + --build-arg BUILD_DATE=`date -u +”%Y-%m-%dT%H:%M:%SZ”` \ + --build-arg EVC_TOKEN=$evc_token . fi TAGS=() TAGS+=("$USERNAME/$IMAGE:$COMPONENT_VERSION_STRING") - docker tag $USERNAME/$IMAGE:$COMPONENT_VERSION_STRING $USERNAME/$IMAGE:latest TAGS+=("$USERNAME/$IMAGE:latest") diff --git a/evc-sumo/docker/checkout.sh b/evc-sumo/docker/checkout.sh index 4e4f1753..a757537a 100755 --- a/evc-sumo/docker/checkout.sh +++ b/evc-sumo/docker/checkout.sh @@ -35,10 +35,3 @@ while [[ $# -gt 0 ]]; do esac done -if [[ "$BRANCH" = "develop" ]]; then - git clone https://github.com/usdot-fhwa-stol/carma-msgs.git ~/src/carma-msgs --branch $BRANCH --depth 1 - git clone https://github.com/usdot-fhwa-stol/carma-utils.git ~/src/carma-utils --branch $BRANCH --depth 1 -else - git clone https://github.com/usdot-fhwa-stol/carma-msgs.git ${dir}/src/carma-msgs --branch develop --depth 1 - git clone https://github.com/usdot-fhwa-stol/carma-utils.git ${dir}/src/carma-utils --branch develop --depth 1 -fi diff --git a/evc-sumo/docker/get-system-version.sh b/evc-sumo/docker/get-system-version.sh index f43b07cd..7ce43dda 100755 --- a/evc-sumo/docker/get-system-version.sh +++ b/evc-sumo/docker/get-system-version.sh @@ -14,7 +14,7 @@ # License for the specific language governing permissions and limitations under # the License. -SYSTEM_TAG_PREFIX="CARMASystem" +SYSTEM_TAG_PREFIX="carma-system"" git describe --all --match="$SYSTEM_TAG_PREFIX*" --always --dirty="-SNAPSHOT" | awk -F "/" '{print $NF}' diff --git a/evc-sumo/docker/install.sh b/evc-sumo/docker/install.sh index d810b87d..1d4560a0 100755 --- a/evc-sumo/docker/install.sh +++ b/evc-sumo/docker/install.sh @@ -15,6 +15,45 @@ # the License. set -e + +#This token is required to run the script +evc_token="" + +while [[ $# -gt 0 ]]; do + arg="$1" + case $arg in + -v|--version) + COMPONENT_VERSION_STRING="$2" + shift + shift + ;; + --system-release) + SYSTEM_RELEASE=true + shift + ;; + -p|--push) + PUSH=true + shift + ;; + -d|--develop) + USERNAME=usdotfhwastoldev + COMPONENT_VERSION_STRING=develop + shift + ;; + *) + evc_token=${arg} + shift + ;; + esac +done + +if [ -z $evc_token ]; + then + echo "No argument provided for evc_token, this script needs to be run with token" + exit 1 +fi + + #user must replace the content in docker/token.txt with their own github token file="docker/token.txt" #download apt dependencies @@ -30,7 +69,6 @@ sudo apt-get update sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 -y #update sumo version sudo apt-get install -y sumo sumo-tools sumo-doc -evc_token=$(cat "$file") sudo curl -H "Authorization: token ${evc_token}" -H 'Accept: application/vnd.github.v4.raw' -O -L https://api.github.com/repos/usdot-fhwa-stol/CARMASensitive/contents/evcfile.zip sudo unzip evcfile.zip diff --git a/evc-sumo/docker/token.txt b/evc-sumo/docker/token.txt deleted file mode 100644 index a3e1f526..00000000 --- a/evc-sumo/docker/token.txt +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 4c3dd569ffd5e88d08da17bd43ac3541beb35ed9 Mon Sep 17 00:00:00 2001 From: Eric Chen <122404059+EricChen-Lei@users.noreply.github.com> Date: Thu, 23 Mar 2023 15:10:29 -0400 Subject: [PATCH 040/124] change env.sh content take out the not used part --- evc-sumo/docker/env.sh | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/evc-sumo/docker/env.sh b/evc-sumo/docker/env.sh index c2960c00..3b6e6fa4 100755 --- a/evc-sumo/docker/env.sh +++ b/evc-sumo/docker/env.sh @@ -16,11 +16,6 @@ # Set environment variables here. -export NS3_HOME="/opt/ns-3/" -export JAVA_HOME="/usr/lib/jvm/java-11-jdk-amd64" -export SUMO_HOME="/opt/sumo/sumo-1_12_0" -export CARLA_HOME="/opt/carla" -export CARMA_SIMULATION_HOME="/opt/carma-simulation" +export SUMO_HOME="/usr/share/sumo" export PATH="/usr/local/share/sumo/bin:/usr/local/share/sumo/tools:$PATH" -export PATH="/opt/maven/bin:$PATH" -export PATH="/opt/carma-simulation:$PATH" + From 5a3994269a38ff70994cbb4297c9e83ba7b78824 Mon Sep 17 00:00:00 2001 From: EricChen-Lei <122404059+EricChen-Lei@users.noreply.github.com> Date: Tue, 28 Mar 2023 11:50:58 -0400 Subject: [PATCH 041/124] added ENV evc_token added ENV evc_token --- evc-sumo/Dockerfile | 3 ++- evc-sumo/docker/install.sh | 31 ------------------------------- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/evc-sumo/Dockerfile b/evc-sumo/Dockerfile index aec8f947..18a9a3aa 100644 --- a/evc-sumo/Dockerfile +++ b/evc-sumo/Dockerfile @@ -16,6 +16,7 @@ FROM ubuntu:20.04 # Set environment variables for non-interactive installation ARG EVC_TOKEN="NULL" +ENV evc_token=${EVC_TOKEN} ENV SUMO_HOME /usr/share/sumo RUN apt-get update && apt-get install -y sudo @@ -32,7 +33,7 @@ COPY --chown=carma:carma /docker ./docker COPY --chown=carma:carma /src ./src COPY --chown=carma:carma /test ./test -RUN docker/install.sh ${EVC_TOKEN} +RUN docker/install.sh evc_token USER carma # Set the working directory for the browser user WORKDIR /home/carma/src diff --git a/evc-sumo/docker/install.sh b/evc-sumo/docker/install.sh index 1d4560a0..e1384ea9 100755 --- a/evc-sumo/docker/install.sh +++ b/evc-sumo/docker/install.sh @@ -16,37 +16,6 @@ set -e -#This token is required to run the script -evc_token="" - -while [[ $# -gt 0 ]]; do - arg="$1" - case $arg in - -v|--version) - COMPONENT_VERSION_STRING="$2" - shift - shift - ;; - --system-release) - SYSTEM_RELEASE=true - shift - ;; - -p|--push) - PUSH=true - shift - ;; - -d|--develop) - USERNAME=usdotfhwastoldev - COMPONENT_VERSION_STRING=develop - shift - ;; - *) - evc_token=${arg} - shift - ;; - esac -done - if [ -z $evc_token ]; then echo "No argument provided for evc_token, this script needs to be run with token" From 6fd626aad0e9d7d8a1a9dd407085b33235c72b16 Mon Sep 17 00:00:00 2001 From: EricChen-Lei <122404059+EricChen-Lei@users.noreply.github.com> Date: Tue, 28 Mar 2023 14:55:41 -0400 Subject: [PATCH 042/124] update dockerfile updated a typo on dockerfile --- evc-sumo/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evc-sumo/Dockerfile b/evc-sumo/Dockerfile index 18a9a3aa..e27e7f24 100644 --- a/evc-sumo/Dockerfile +++ b/evc-sumo/Dockerfile @@ -33,7 +33,7 @@ COPY --chown=carma:carma /docker ./docker COPY --chown=carma:carma /src ./src COPY --chown=carma:carma /test ./test -RUN docker/install.sh evc_token +RUN docker/install.sh $evc_token USER carma # Set the working directory for the browser user WORKDIR /home/carma/src From e36745d9a9d5aa8901a36c95b9311022d240881d Mon Sep 17 00:00:00 2001 From: EricChen-Lei <122404059+EricChen-Lei@users.noreply.github.com> Date: Tue, 28 Mar 2023 15:49:33 -0400 Subject: [PATCH 043/124] update install.sh take out not used code --- evc-sumo/docker/install.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/evc-sumo/docker/install.sh b/evc-sumo/docker/install.sh index e1384ea9..24a46d4f 100755 --- a/evc-sumo/docker/install.sh +++ b/evc-sumo/docker/install.sh @@ -21,10 +21,7 @@ if [ -z $evc_token ]; echo "No argument provided for evc_token, this script needs to be run with token" exit 1 fi - -#user must replace the content in docker/token.txt with their own github token -file="docker/token.txt" #download apt dependencies sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends firefox dbus-x11 x11-apps x11-utils x11-xserver-utils \ xserver-xorg-video-dummy xserver-xorg-input-void xvfb libgl1-mesa-dri libgl1-mesa-glx libpulse0 \ From 9f0be896848d2a48621753066ea146b2c561a8c6 Mon Sep 17 00:00:00 2001 From: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> Date: Tue, 4 Apr 2023 15:29:36 -0400 Subject: [PATCH 044/124] Completed infrastructure registration message --- .../InfrastructureRegistrationMessage.java | 73 ++++++++++++++++++- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java index 7825ef71..bdb471be 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java @@ -23,12 +23,77 @@ */ public class InfrastructureRegistrationMessage { private String rxMessageIpAddress; - private int timeSyncListenPort = 1517; // TODO - private int regstratinoListenPort = 1516; // TODO + private String intersectionId = null; + private String infrastructureId; + + private int rxMessagePort = 1536; + private int timeSyncPort = 1517; // TODO private GeoPoint location = null; - public InfrastructureRegistrationMessage() { - + public InfrastructureRegistrationMessage(String rxMessageIpAddress, String intersectionId, String infrastructureId, + int rxMessagePort, int timeSyncPort, GeoPoint location) { + this.rxMessageIpAddress = rxMessageIpAddress; + this.intersectionId = intersectionId; + this.infrastructureId = infrastructureId; + this.rxMessagePort = rxMessagePort; + this.timeSyncPort = timeSyncPort; + this.location = location; + } + + public String getRxMessageIpAddress(){ + return this.rxMessageIpAddress; + } + + public String getIntersectionId(){ + return this.intersectionId; + } + + public String getInfrastructureId(){ + return this.infrastructureId; + } + + public int getRxMessagePort(){ + return this.rxMessagePort; + } + + public int getTimeSyncPort(){ + return this.timeSyncPort; + } + + public GeoPoint getLocation(){ + return this.location; + } + + public void setRxMessageIpAddress(String rxMessageIpAddress){ + this.rxMessageIpAddress = rxMessageIpAddress; + } + + public void setIntersectionId(String intersectionId){ + this.intersectionId = intersectionId; + } + + public void setInfrastructureId(String infrastructureId){ + this.infrastructureId = infrastructureId; + } + + public void setRxMessagePort(int rxMessagePort){ + this.rxMessagePort = rxMessagePort; + } + + public void setTimeSyncPort(int timeSyncPort){ + this.timeSyncPort = timeSyncPort; + } + + public void setLocation(GeoPoint location){ + this.location = location; + } + + @Override + public String toString() { + return "InfrastructureRegistrationMessage [rxMessageIpAddress=" + rxMessageIpAddress + ", intersectionId=" + intersectionId + + ", infrastructureId=" + infrastructureId + ", rxMessagePort=" + rxMessagePort + + ", timeSyncPort=" + timeSyncPort + ", location=" + location + "]"; } + } From 98fcc27813eba5b9d4a8ca6bd907cf865722c149 Mon Sep 17 00:00:00 2001 From: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> Date: Tue, 4 Apr 2023 15:30:19 -0400 Subject: [PATCH 045/124] Completed infrastructure instance --- .../ambassador/InfrastructureInstance.java | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java index 6e6b544f..e99da1a4 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java @@ -30,15 +30,21 @@ */ public class InfrastructureInstance { - private DatagramSocket rxMsgsSocket = null; + private String infrastructureId; private InetAddress targetAddress; - private int registrationPort; + private int rxMessagePort; private int timeSyncPort; private GeoPoint location = null; - public InfrastructureInstance() { - // TODO - // TODO Initialize Datagram Socket + private DatagramSocket rxMsgsSocket = null; + + public InfrastructureInstance(String infrastructureId, InetAddress targetAddress, + int rxMessagePort, int timeSyncPort, GeoPoint location) { + this.infrastructureId = infrastructureId; + this.targetAddress = targetAddress; + this.rxMessagePort = rxMessagePort; + this.timeSyncPort = timeSyncPort; + this.location = location; } public InetAddress getTargetAddress() { @@ -49,14 +55,43 @@ public void setTargetAddress(InetAddress targetAddress) { this.targetAddress = targetAddress; } + public GeoPoint getLocation() { + return this.location; + } + public void setLocation(GeoPoint location) { this.location = location; } - public GeoPoint getLocation() { - return this.location; + public String getInfrastructureId() { + return infrastructureId; + } + + public void setInfrastructureId(String infrastructureId) { + this.infrastructureId = infrastructureId; + } + + public int getRxMessagePort() { + return rxMessagePort; + } + + public void setRxMessagePort(int rxMessagePort) { + this.rxMessagePort = rxMessagePort; + } + + public int getTimeSyncPort() { + return timeSyncPort; + } + + public void setTimeSyncPort(int timeSyncPort) { + this.timeSyncPort = timeSyncPort; } + public void bind() throws IOException { + rxMsgsSocket = new DatagramSocket(); + } + + /** * Sends the data to the Infrastructure Device communications interface configured at construction time. * @param data The binary data to transmit @@ -66,8 +101,7 @@ public void sendMsgs(byte[] data) throws IOException { if (rxMsgsSocket == null) { throw new IllegalStateException("Attempted to send data before opening socket"); } - - DatagramPacket packet = new DatagramPacket(data, data.length, targetAddress, registrationPort); + DatagramPacket packet = new DatagramPacket(data, data.length, targetAddress, rxMessagePort); rxMsgsSocket.send(packet); } From e08c086833d575aaa5d91f63532fa1d6ec0be9b2 Mon Sep 17 00:00:00 2001 From: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> Date: Tue, 4 Apr 2023 16:12:31 -0400 Subject: [PATCH 046/124] commit --- .../InfrastructureInstanceManager.java | 39 +++++++++++++++---- .../InfrastructureMessageAmbassador.java | 18 +++++---- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 7afe44d9..32695781 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -25,20 +25,43 @@ import org.eclipse.mosaic.fed.infrastructure.ambassador.InfrastructureRegistrationMessage; import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; -import org.eclipse.mosaic.interactions.traffic.VehicleUpdates; import org.eclipse.mosaic.lib.enums.AdHocChannel; -import org.eclipse.mosaic.lib.objects.addressing.AdHocMessageRoutingBuilder; -import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xContent; -import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xMessage; -import org.eclipse.mosaic.lib.objects.v2x.MessageRouting; -import org.eclipse.mosaic.lib.objects.vehicle.VehicleData; +import org.eclipse.mosaic.lib.geo.GeoPoint; /** * Session management class for Infrastructure instances communicating with MOSAIC * NOTE: TODO See carma.ambassador for reference */ public class InfrastructureInstanceManager { - private Map managedInstances = new HashMap<>(); + private Map managedInstances = new HashMap<>(); private double currentSimulationTime; - + + public void onNewRegistration(InfrastructureRegistrationMessage registration){ + if (!managedInstances.containsKey(registration.getInfrastructureId())) { + try { + newInfrastructureInstance( + registration.getIntersectionId(), + registration.getInfrastructureId(), + InetAddress.getByName(registration.getRxMessageIpAddress()), + registration.getRxMessagePort(), + registration.getTimeSyncPort(), + registration.getLocation() + ); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } else { + // log warning + } + } + + private void newInfrastructureInstance(String intersectionId, String infrastructureId, InetAddress rxMessageIpAddress, int rxMessagePort, int timeSyncPort, GeoPoint location) { + InfrastructureInstance tmp = new InfrastructureInstance(intersectionId, infrastructureId, rxMessageIpAddress, rxMessagePort, timeSyncPort, location); + try { + tmp.bind(); + } catch (IOException e) { + throw new RuntimeException(e); + } + managedInstances.put(infrastructureId, tmp); + } } diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 7eab5b2d..7a8dd377 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -57,11 +57,11 @@ public class InfrastructureMessageAmbassador extends AbstractFederateAmbassador */ int numberOfInfrastructureInstances = 0; - private InfrastructureRegistrationReceiver InfrastructureRegistrationReceiver; + private InfrastructureRegistrationReceiver infrastructureRegistrationReceiver; private Thread registrationRxBackgroundThread; - private InfrastructureTimeMessageReceiver InfrastructureTimeMessageReceiver; + private InfrastructureTimeMessageReceiver infrastructureTimeMessageReceiver; private Thread v2xTimeRxBackgroundThread; - private InfrastructureInstanceManager InfrastructureInstanceManager = new InfrastructureInstanceManager(); + private InfrastructureInstanceManager infrastructureInstanceManager = new InfrastructureInstanceManager(); /** * Create a new {@link InfrastructureMessageAmbassador} object. @@ -109,7 +109,10 @@ public void initialize(long startTime, long endTime) throws InternalFederateExce } // TODO Initialize listener socket and thread for Infrastructure Registration messages - + infrastructureRegistrationReceiver = new InfrastructureRegistrationReceiver(); + infrastructureRegistrationReceiver.init(); + registrationRxBackgroundThread = new Thread(infrastructureRegistrationReceiver); + registrationRxBackgroundThread.start(); // TODO Initialize listener socket and thread for Infrastructure time sync messages // TODO Register any V2x infrastructures from config if any @@ -149,7 +152,6 @@ private synchronized void receiveInteraction(InfrastructureV2xMessageReception i log.info(infrastructureV2xMessageReception.getReceiverID() + " received V2X messages at time: " + infrastructureV2xMessageReception.getTime() + "."); log.info("The received message is " + infrastructureV2xMessageReception.getMessage() + " ."); - } /** @@ -170,8 +172,10 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder } try { - // TODO actions to do on queued Infrastructure instance registration attempts - + List newRegistrations = infrastructureRegistrationReceiver.getReceivedMessages(); + for (InfrastructureRegistrationMessage reg : newRegistrations) { + infrastructureInstanceManager.onNewRegistration(reg); + } // TODO actions to do on queued Infrastructure time sync messages // TODO actions to do on queued v2x message receiver's received messages From 6df77d0b69eb33df5df8b21976225d1dfd768610 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 6 Apr 2023 00:47:43 -0400 Subject: [PATCH 047/124] commit --- .../InfrastructureInstanceManager.java | 5 ++- .../InfrastructureMessageAmbassador.java | 22 +++++++++++- .../InfrastructureConfiguration.java | 10 +++++- .../configuration/RsuConfiguration.java | 36 +++++++++++++++++++ 4 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/RsuConfiguration.java diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 32695781..13491184 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -40,7 +40,6 @@ public void onNewRegistration(InfrastructureRegistrationMessage registration){ if (!managedInstances.containsKey(registration.getInfrastructureId())) { try { newInfrastructureInstance( - registration.getIntersectionId(), registration.getInfrastructureId(), InetAddress.getByName(registration.getRxMessageIpAddress()), registration.getRxMessagePort(), @@ -55,8 +54,8 @@ public void onNewRegistration(InfrastructureRegistrationMessage registration){ } } - private void newInfrastructureInstance(String intersectionId, String infrastructureId, InetAddress rxMessageIpAddress, int rxMessagePort, int timeSyncPort, GeoPoint location) { - InfrastructureInstance tmp = new InfrastructureInstance(intersectionId, infrastructureId, rxMessageIpAddress, rxMessagePort, timeSyncPort, location); + private void newInfrastructureInstance(String infrastructureId, InetAddress rxMessageIpAddress, int rxMessagePort, int timeSyncPort, GeoPoint location) { + InfrastructureInstance tmp = new InfrastructureInstance(infrastructureId, rxMessageIpAddress, rxMessagePort, timeSyncPort, location); try { tmp.bind(); } catch (IOException e) { diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 7a8dd377..b9a0ae8f 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -20,10 +20,11 @@ import org.eclipse.mosaic.rti.api.InternalFederateException; import org.eclipse.mosaic.rti.api.parameters.AmbassadorParameter; import org.eclipse.mosaic.fed.infrastructure.configuration.InfrastructureConfiguration; +import org.eclipse.mosaic.fed.infrastructure.configuration.RsuConfiguration; import org.eclipse.mosaic.lib.util.objects.ObjectInstantiation; import org.eclipse.mosaic.interactions.application.ExternalMessage; import org.eclipse.mosaic.interactions.application.InfrastructureV2xMessageReception; - +import org.eclipse.mosaic.interactions.mapping.RsuRegistration; import org.eclipse.mosaic.fed.infrastructure.ambassador.InfrastructureRegistrationMessage; import java.net.InetAddress; @@ -113,6 +114,21 @@ public void initialize(long startTime, long endTime) throws InternalFederateExce infrastructureRegistrationReceiver.init(); registrationRxBackgroundThread = new Thread(infrastructureRegistrationReceiver); registrationRxBackgroundThread.start(); + + // Read configuration file and register RSU onto MOSAIC + for (RsuConfiguration rsuConfiguration : infrastructureConfiguration.rsus) + { + RsuRegistration rsuRegistration = new RsuRegistration(currentSimulationTime, rsuConfiguration.name, + rsuConfiguration.group, rsuConfiguration.application, + rsuConfiguration.position); + try { + this.rti.triggerInteraction(rsuRegistration); + v2xMap.put(rsuConfiguration.name, false); + } catch (InternalFederateException | IllegalValueException e) { + log.error(e.getMessage()); + } + } + // TODO Initialize listener socket and thread for Infrastructure time sync messages // TODO Register any V2x infrastructures from config if any @@ -172,9 +188,13 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder } try { + List newRegistrations = infrastructureRegistrationReceiver.getReceivedMessages(); for (InfrastructureRegistrationMessage reg : newRegistrations) { infrastructureInstanceManager.onNewRegistration(reg); + if (!v2xMap.get(reg.getInfrastructureId())) + v2xMap.put(reg.getInfrastructureId(), true); + } // TODO actions to do on queued Infrastructure time sync messages diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/InfrastructureConfiguration.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/InfrastructureConfiguration.java index 5c4a4561..7e2694a7 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/InfrastructureConfiguration.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/InfrastructureConfiguration.java @@ -18,6 +18,7 @@ import com.google.gson.annotations.JsonAdapter; import java.io.Serializable; +import java.util.List; /** * The Infrastructure Message Ambassador configuration class. @@ -33,6 +34,13 @@ public class InfrastructureConfiguration implements Serializable { @JsonAdapter(TimeFieldAdapter.LegacyMilliSeconds.class) public Long updateInterval = 1000L; - // The RSU id which sends messages. + /** + * The list of RSU configurations in the infrastructure. + */ + public List rsus; + + /** + * The ID of the RSU that sends messages. + */ public String senderRSUId; } diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/RsuConfiguration.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/RsuConfiguration.java new file mode 100644 index 00000000..e9347317 --- /dev/null +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/RsuConfiguration.java @@ -0,0 +1,36 @@ +package org.eclipse.mosaic.fed.infrastructure.configuration; + +import java.util.List; + +import org.eclipse.mosaic.lib.geo.GeoPoint; + +/** + * Represents the configuration settings for a Road Side Unit (RSU) in a wireless communication infrastructure. + */ +public class RsuConfiguration { + + /** + * The timestamp associated with the configuration, in nanoseconds. + */ + public long time; + + /** + * The name of the RSU. + */ + public String name; + + /** + * A group identifier for the RSU, used for grouping and organizing RSUs. + */ + public String group; + + /** + * A list of application names installed on the RSU. + */ + public List application; + + /** + * The geographical position of the RSU on the map, represented as a GeoPoint object. + */ + public GeoPoint position; +} From a5a4acf3b5ff9e49e49ca001c0a59356c4330169 Mon Sep 17 00:00:00 2001 From: EricChen-Lei <122404059+EricChen-Lei@users.noreply.github.com> Date: Thu, 6 Apr 2023 15:48:48 -0400 Subject: [PATCH 048/124] updates for timesync and test implementation of time sync and unit testing --- .../ambassador/InfrastructureInstance.java | 10 +++ .../InfrastructureInstanceManager.java | 5 ++ .../InfrastructureMessageAmbassador.java | 21 ++++-- .../InfrastructureTimeInterface.java | 64 +++++++++++++++++++ .../ambassador/InfrastructureTimeMessage.java | 23 ++++++- .../InfrastructureTimeInterfaceTest.java | 63 ++++++++++++++++++ 6 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterface.java create mode 100644 co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterfaceTest.java diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java index 6e6b544f..f232251a 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java @@ -71,4 +71,14 @@ public void sendMsgs(byte[] data) throws IOException { rxMsgsSocket.send(packet); } + + public void sendTimesyncMsgs(byte[] data) throws IOException { + if (rxMsgsSocket == null) { + throw new IllegalStateException("Attempted to send data before opening socket"); + } + + DatagramPacket packet = new DatagramPacket(data, data.length, targetAddress, timeSyncPort); + rxMsgsSocket.send(packet); + + } } diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 7afe44d9..623b29af 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -40,5 +40,10 @@ public class InfrastructureInstanceManager { private Map managedInstances = new HashMap<>(); private double currentSimulationTime; + + public Map get_managedInstances() + { + return managedInstances; + } } diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 7eab5b2d..0133110b 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -26,6 +26,7 @@ import org.eclipse.mosaic.fed.infrastructure.ambassador.InfrastructureRegistrationMessage; +import java.io.IOException; import java.net.InetAddress; import java.util.ArrayList; import java.util.HashMap; @@ -62,6 +63,9 @@ public class InfrastructureMessageAmbassador extends AbstractFederateAmbassador private InfrastructureTimeMessageReceiver InfrastructureTimeMessageReceiver; private Thread v2xTimeRxBackgroundThread; private InfrastructureInstanceManager InfrastructureInstanceManager = new InfrastructureInstanceManager(); + private InfrastructureTimeInterface InfrastructureTimeInterface; + + int Timesync_seq = 0; /** * Create a new {@link InfrastructureMessageAmbassador} object. @@ -132,7 +136,7 @@ public void processInteraction(Interaction interaction) throws InternalFederateE if (interaction.getTypeId().equals(InfrastructureV2xMessageReception.TYPE_ID)) { this.receiveInteraction((InfrastructureV2xMessageReception) interaction); } - // TODO Time sync message reception + // TODO Time sync message reception: needs to be implemented when time regulat.... // TODO V2xHub infrastructure Registration reception } @@ -171,9 +175,16 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder try { // TODO actions to do on queued Infrastructure instance registration attempts - - // TODO actions to do on queued Infrastructure time sync messages - + + Timesync_seq += 1; + InfrastructureTimeMessage timesync_message = new InfrastructureTimeMessage(); + timesync_message.set_seq(Timesync_seq); + timesync_message.set_timestep(currentSimulationTime); + try { + InfrastructureTimeInterface.onTimestepUpdate(timesync_message); + } catch (IOException e) { + log.error(e.getMessage()); + } // TODO actions to do on queued v2x message receiver's received messages currentSimulationTime += infrastructureConfiguration.updateInterval * TIME.MILLI_SECOND; @@ -219,6 +230,6 @@ public boolean isTimeConstrained() { */ @Override public boolean isTimeRegulating() { - return true; + return false; } } diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterface.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterface.java new file mode 100644 index 00000000..539ebcce --- /dev/null +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterface.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 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. + */ + +package org.eclipse.mosaic.fed.infrastructure.ambassador; +import java.io.IOException; + +import org.apache.commons.logging.Log; + +import com.google.gson.Gson; +public class InfrastructureTimeInterface{ +public InfrastructureInstanceManager manager; +//private int target_port=12123; +//set to false on init release +private Boolean await_infrastructure_advance_request = false; +public InfrastructureTimeInterface(InfrastructureInstanceManager manager){ + this.manager = manager; +} + +/** + * This function implements the encoding of a json string consist of timestep and seq + * + * @param message This is the class that gets encoded, which include the data of timestep and seq + * @return return_json: encoded json string + */ +public byte[] encodeTimeMessage(InfrastructureTimeMessage message) +{ + Gson gson = new Gson(); + String json = gson.toJson(message); + byte[] return_json = json.getBytes(); + return return_json; +} + +/** + * This function is used to send out encoded timestep update to all registered instances the manager has on the managed instances map + * + * @param message This time message is used to store current seq and timestep from the ambassador side + * @throws IOException + */ +public void onTimestepUpdate(InfrastructureTimeMessage message) throws IOException +{ + if(this.manager.get_managedInstances().size() == 0) + { + throw new IllegalAccessError("There are no registered instances!"); + } + + for(InfrastructureInstance current_instance: this.manager.get_managedInstances().values()) + { + current_instance.sendTimesyncMsgs(encodeTimeMessage(message)); + } +} +} \ No newline at end of file diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java index b3d9a047..d5a67973 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java @@ -25,11 +25,32 @@ */ public class InfrastructureTimeMessage { - private int timestep; + private long timestep; private int seq; public InfrastructureTimeMessage() { } + public long get_timestep() + { + return timestep; + } + public int get_seq() + { + return seq; + } + public void set_timestep(long new_timestep) + { + this.timestep = new_timestep; + } + public void set_seq(int new_seq) + { + this.seq = new_seq; + } + @Override + public String toString() { + return "InfrastructureTimeMessage [timestep=" + timestep + ", seq=" + seq + "]"; + } + } diff --git a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterfaceTest.java b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterfaceTest.java new file mode 100644 index 00000000..51e06a6d --- /dev/null +++ b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterfaceTest.java @@ -0,0 +1,63 @@ +package org.eclipse.mosaic.fed.infrastructure.ambassador; + +import org.junit.Test; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import com.google.gson.Gson; + +public class InfrastructureTimeInterfaceTest { + @Mock + private InfrastructureInstanceManager manager; + @Mock + private InfrastructureInstance infrastructureInstanceMock1; + @Mock + private InfrastructureInstance infrastructureInstanceMock2; + private InfrastructureTimeInterface infrastructureTimeInterface; + + @Before + public void setup() + { + MockitoAnnotations.initMocks(this); + infrastructureTimeInterface = new InfrastructureTimeInterface(manager); + } + + @Test + public void testTimesyncUpdate() throws IOException + { + InfrastructureTimeMessage infrastructureTimeMessage = new InfrastructureTimeMessage(); + infrastructureTimeMessage.set_seq(1); + infrastructureTimeMessage.set_timestep((long)20230406); + + Map managed_instance_map = new HashMap<>(); + managed_instance_map.put("instance1", infrastructureInstanceMock1); + managed_instance_map.put("instance2", infrastructureInstanceMock2); + + when(manager.get_managedInstances()).thenReturn(managed_instance_map); + + infrastructureTimeInterface.onTimestepUpdate(infrastructureTimeMessage); + + infrastructureTimeMessage.set_seq(2); + infrastructureTimeMessage.set_timestep((long)20230407); + + infrastructureTimeInterface.onTimestepUpdate(infrastructureTimeMessage); + + Gson gson = new Gson(); + String temp = gson.toJson(infrastructureTimeMessage); + byte[] t_byte = temp.getBytes(); + + verify(infrastructureInstanceMock1, times(1)).sendTimesyncMsgs(eq((byte[])t_byte)); + verify(infrastructureInstanceMock2, times(1)).sendTimesyncMsgs(eq((byte[])t_byte)); + + } +} From e1adc05692f3ba727c612988e227d8d7fc8c1d0c Mon Sep 17 00:00:00 2001 From: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> Date: Thu, 6 Apr 2023 15:59:52 -0400 Subject: [PATCH 049/124] commit --- .../ambassador/InfrastructureInstance.java | 2 + .../InfrastructureMessageAmbassador.java | 84 +++++++++++-------- .../InfrastructureConfiguration.java | 5 -- .../configuration/RsuConfiguration.java | 36 -------- 4 files changed, 51 insertions(+), 76 deletions(-) delete mode 100644 co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/RsuConfiguration.java diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java index e99da1a4..9a3c7971 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java @@ -36,6 +36,8 @@ public class InfrastructureInstance { private int timeSyncPort; private GeoPoint location = null; + + private DatagramSocket rxMsgsSocket = null; public InfrastructureInstance(String infrastructureId, InetAddress targetAddress, diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index b9a0ae8f..5d563a12 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -20,15 +20,20 @@ import org.eclipse.mosaic.rti.api.InternalFederateException; import org.eclipse.mosaic.rti.api.parameters.AmbassadorParameter; import org.eclipse.mosaic.fed.infrastructure.configuration.InfrastructureConfiguration; -import org.eclipse.mosaic.fed.infrastructure.configuration.RsuConfiguration; +import org.eclipse.mosaic.lib.enums.AdHocChannel; +import org.eclipse.mosaic.lib.objects.communication.AdHocConfiguration; +import org.eclipse.mosaic.lib.objects.communication.InterfaceConfiguration; import org.eclipse.mosaic.lib.util.objects.ObjectInstantiation; import org.eclipse.mosaic.interactions.application.ExternalMessage; import org.eclipse.mosaic.interactions.application.InfrastructureV2xMessageReception; +import org.eclipse.mosaic.interactions.communication.AdHocCommunicationConfiguration; import org.eclipse.mosaic.interactions.mapping.RsuRegistration; import org.eclipse.mosaic.fed.infrastructure.ambassador.InfrastructureRegistrationMessage; +import java.net.Inet4Address; import java.net.InetAddress; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -48,11 +53,6 @@ public class InfrastructureMessageAmbassador extends AbstractFederateAmbassador */ InfrastructureConfiguration infrastructureConfiguration; - /** - * List of infrastructures that are controlled - */ - private final HashMap v2xMap = new HashMap<>(); - /** * The number of CARMA vehicles. */ @@ -109,30 +109,10 @@ public void initialize(long startTime, long endTime) throws InternalFederateExce throw new InternalFederateException(e); } - // TODO Initialize listener socket and thread for Infrastructure Registration messages infrastructureRegistrationReceiver = new InfrastructureRegistrationReceiver(); infrastructureRegistrationReceiver.init(); registrationRxBackgroundThread = new Thread(infrastructureRegistrationReceiver); registrationRxBackgroundThread.start(); - - // Read configuration file and register RSU onto MOSAIC - for (RsuConfiguration rsuConfiguration : infrastructureConfiguration.rsus) - { - RsuRegistration rsuRegistration = new RsuRegistration(currentSimulationTime, rsuConfiguration.name, - rsuConfiguration.group, rsuConfiguration.application, - rsuConfiguration.position); - try { - this.rti.triggerInteraction(rsuRegistration); - v2xMap.put(rsuConfiguration.name, false); - } catch (InternalFederateException | IllegalValueException e) { - log.error(e.getMessage()); - } - } - - // TODO Initialize listener socket and thread for Infrastructure time sync messages - - // TODO Register any V2x infrastructures from config if any - } /** @@ -151,9 +131,6 @@ public void processInteraction(Interaction interaction) throws InternalFederateE if (interaction.getTypeId().equals(InfrastructureV2xMessageReception.TYPE_ID)) { this.receiveInteraction((InfrastructureV2xMessageReception) interaction); } - // TODO Time sync message reception - - // TODO V2xHub infrastructure Registration reception } /** @@ -188,15 +165,52 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder } try { - - List newRegistrations = infrastructureRegistrationReceiver.getReceivedMessages(); + + // + List newRegistrations = infrastructureRegistrationReceiver + .getReceivedMessages(); for (InfrastructureRegistrationMessage reg : newRegistrations) { + + // new instance registration to store to infrastructure instance manager infrastructureInstanceManager.onNewRegistration(reg); - if (!v2xMap.get(reg.getInfrastructureId())) - v2xMap.put(reg.getInfrastructureId(), true); - + + // register RSU, currently one infrastructure pair with one RSU, the ID will be the same as result + // group and applications leave as null for now + RsuRegistration rsuRegistration = new RsuRegistration(currentSimulationTime, reg.getInfrastructureId(), + null, null, + reg.getLocation()); + try { + // trigger RTI interaction to MOSAIC + this.rti.triggerInteraction(rsuRegistration); + } catch (InternalFederateException | IllegalValueException e) { + log.error(e.getMessage()); + } + + //TODO actions to create DSRC parameter and then send to RTI interaction to MOSAIC + + // Create an AdHocConfiguration object to represent the configuration of the Ad-Hoc interface + InterfaceConfiguration interfaceConfig = new InterfaceConfiguration.Builder(AdHocChannel.SCH1) + .power(50) + .radius(100.0) + .secondChannel(AdHocChannel.SCH2) + .create(); + + AdHocConfiguration adHocConfig = new AdHocConfiguration.Builder(reg.getInfrastructureId()) + .addInterface(interfaceConfig) + .create(); + + AdHocCommunicationConfiguration communicationConfig = new AdHocCommunicationConfiguration(currentSimulationTime, adHocConfig); + + // Use the object to exchange the Ad-Hoc configuration with another vehicle or component + try { + // trigger RTI interaction to MOSAIC + this.rti.triggerInteraction(communicationConfig); + } catch (InternalFederateException | IllegalValueException e) { + log.error(e.getMessage()); + } } - // TODO actions to do on queued Infrastructure time sync messages + + // TODO actions to do on queued Infrastructure time sync messages // TODO actions to do on queued v2x message receiver's received messages diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/InfrastructureConfiguration.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/InfrastructureConfiguration.java index 7e2694a7..f819812b 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/InfrastructureConfiguration.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/InfrastructureConfiguration.java @@ -34,11 +34,6 @@ public class InfrastructureConfiguration implements Serializable { @JsonAdapter(TimeFieldAdapter.LegacyMilliSeconds.class) public Long updateInterval = 1000L; - /** - * The list of RSU configurations in the infrastructure. - */ - public List rsus; - /** * The ID of the RSU that sends messages. */ diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/RsuConfiguration.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/RsuConfiguration.java deleted file mode 100644 index e9347317..00000000 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/configuration/RsuConfiguration.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.eclipse.mosaic.fed.infrastructure.configuration; - -import java.util.List; - -import org.eclipse.mosaic.lib.geo.GeoPoint; - -/** - * Represents the configuration settings for a Road Side Unit (RSU) in a wireless communication infrastructure. - */ -public class RsuConfiguration { - - /** - * The timestamp associated with the configuration, in nanoseconds. - */ - public long time; - - /** - * The name of the RSU. - */ - public String name; - - /** - * A group identifier for the RSU, used for grouping and organizing RSUs. - */ - public String group; - - /** - * A list of application names installed on the RSU. - */ - public List application; - - /** - * The geographical position of the RSU on the map, represented as a GeoPoint object. - */ - public GeoPoint position; -} From 7fc6e9fe84f77d6ed0af4fcaf74066dfd3a9bea8 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 10:09:50 -0400 Subject: [PATCH 050/124] Added infrastructure registration message unit test java code --- ...nfrastructureRegistrationReceiverTest.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiverTest.java diff --git a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiverTest.java b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiverTest.java new file mode 100644 index 00000000..183db72b --- /dev/null +++ b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiverTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 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. + */ + + package org.eclipse.mosaic.fed.infrastructure.ambassador; + + import static org.junit.Assert.assertEquals; + + import java.net.DatagramPacket; + import java.net.DatagramSocket; + import java.net.InetAddress; + import java.util.List; + + import org.junit.After; + import org.junit.Before; + import org.junit.Test; + + public class InfrastructureRegistrationReceiverTest { + + private static final int TEST_PORT = 1515; + + private DatagramSocket sendSocket; + private InfrastructureRegistrationReceiver receiver; + + @Before + public void setup() throws Exception { + // Set up a UDP socket to send messages + sendSocket = new DatagramSocket(); + + // Initialize the receiver + receiver = new InfrastructureRegistrationReceiver(); + receiver.init(); + Thread receiverThread = new Thread(receiver); + receiverThread.start(); + } + + @After + public void teardown() throws Exception { + // Stop the receiver and close the send socket + receiver.stop(); + sendSocket.close(); + } + + @Test + public void testMessageReceive() throws Exception { + // Define a test message in JSON format + String json = "{\"rxMessageIpAddress\":\"192.168.0.1\",\"infrastructureId\":\"5678\",\"rxMessagePort\":1234,\"timeSyncPort\":5678,\"location\":{\"latitude\":37.3382,\"longitude\":121.8863}}"; + byte[] buffer = json.getBytes(); + + // Send the test message to the receiver + InetAddress address = InetAddress.getLocalHost(); + DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, TEST_PORT); + sendSocket.send(packet); + + // Wait for the message to be received + Thread.sleep(1000); + + // Verify that the message was received correctly + List msgs = receiver.getReceivedMessages(); + assertEquals(1, msgs.size()); + + InfrastructureRegistrationMessage msg = msgs.get(0); + double delta = 0.001; // maximum allowed difference for GeoLocation lat and lon + + assertEquals("192.168.0.1", msg.getRxMessageIpAddress()); + assertEquals("5678", msg.getInfrastructureId()); + assertEquals(1234, msg.getRxMessagePort()); + assertEquals(5678, msg.getTimeSyncPort()); + assertEquals(37.3382, msg.getLocation().getLatitude(), delta); + assertEquals(121.8863, msg.getLocation().getLongitude(), delta); + } + + } + \ No newline at end of file From 840c78ee02f6d4943d365fd4c78f5b190e75ee43 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 10:11:12 -0400 Subject: [PATCH 051/124] Update the data convert method to fix bug --- .../ambassador/CarmaRegistrationReceiver.java | 2 +- .../InfrastructureRegistrationReceiver.java | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationReceiver.java b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationReceiver.java index 878122da..6d419d5c 100644 --- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationReceiver.java +++ b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationReceiver.java @@ -64,7 +64,7 @@ public void run() { } // parse message - String receivedPacket = new String(msg.getData()); + String receivedPacket = new String(msg.getData(), 0, msg.getLength()); Gson gson = new Gson(); CarmaRegistrationMessage parsedMessage = gson.fromJson(receivedPacket, CarmaRegistrationMessage.class); diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java index 885daa53..bc6a34ee 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java @@ -60,22 +60,23 @@ public void run() { while (running) { DatagramPacket msg = new DatagramPacket(buf, buf.length); try { - listenSocket.receive(msg); + listenSocket.receive(msg); } catch (IOException e) { - throw new RuntimeException(e); + throw new RuntimeException(e); } - + // parse message - String receivedPacket = new String(msg.getData()); + String receivedPacket = new String(msg.getData(), 0, msg.getLength()); // Use length of message to create String Gson gson = new Gson(); InfrastructureRegistrationMessage parsedMessage = gson.fromJson(receivedPacket, InfrastructureRegistrationMessage.class); - + // Enqueue message for processing on main thread synchronized (rxQueue) { rxQueue.add(parsedMessage); } - } + } } + /** * Stop the runnable instance @@ -84,7 +85,6 @@ public void stop() { if (listenSocket != null) { listenSocket.close(); } - running = false; } From 820a2778d7fcd425b6b2953de78477e0421b334c Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 10:12:36 -0400 Subject: [PATCH 052/124] Remove intersection id parameter --- .../InfrastructureRegistrationMessage.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java index bdb471be..20782031 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java @@ -23,17 +23,15 @@ */ public class InfrastructureRegistrationMessage { private String rxMessageIpAddress; - private String intersectionId = null; private String infrastructureId; private int rxMessagePort = 1536; private int timeSyncPort = 1517; // TODO private GeoPoint location = null; - public InfrastructureRegistrationMessage(String rxMessageIpAddress, String intersectionId, String infrastructureId, + public InfrastructureRegistrationMessage(String rxMessageIpAddress, String infrastructureId, int rxMessagePort, int timeSyncPort, GeoPoint location) { this.rxMessageIpAddress = rxMessageIpAddress; - this.intersectionId = intersectionId; this.infrastructureId = infrastructureId; this.rxMessagePort = rxMessagePort; this.timeSyncPort = timeSyncPort; @@ -44,10 +42,6 @@ public String getRxMessageIpAddress(){ return this.rxMessageIpAddress; } - public String getIntersectionId(){ - return this.intersectionId; - } - public String getInfrastructureId(){ return this.infrastructureId; } @@ -68,10 +62,6 @@ public void setRxMessageIpAddress(String rxMessageIpAddress){ this.rxMessageIpAddress = rxMessageIpAddress; } - public void setIntersectionId(String intersectionId){ - this.intersectionId = intersectionId; - } - public void setInfrastructureId(String infrastructureId){ this.infrastructureId = infrastructureId; } @@ -90,7 +80,7 @@ public void setLocation(GeoPoint location){ @Override public String toString() { - return "InfrastructureRegistrationMessage [rxMessageIpAddress=" + rxMessageIpAddress + ", intersectionId=" + intersectionId + return "InfrastructureRegistrationMessage [rxMessageIpAddress=" + rxMessageIpAddress + ", infrastructureId=" + infrastructureId + ", rxMessagePort=" + rxMessagePort + ", timeSyncPort=" + timeSyncPort + ", location=" + location + "]"; } From 03bbb127cd7424a10520950b349d51b3ea57bd03 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 11:46:50 -0400 Subject: [PATCH 053/124] Create InfrastructureInstanceManagerTest.java --- .../InfrastructureInstanceManagerTest.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManagerTest.java diff --git a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManagerTest.java b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManagerTest.java new file mode 100644 index 00000000..9fa5cba4 --- /dev/null +++ b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManagerTest.java @@ -0,0 +1,57 @@ +package org.eclipse.mosaic.fed.infrastructure.ambassador; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +import java.net.InetAddress; + +import org.eclipse.mosaic.lib.geo.GeoPoint; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class InfrastructureInstanceManagerTest { + + private InfrastructureInstanceManager manager; + private InfrastructureRegistrationMessage registration; + private InetAddress ipAddress; + private GeoPoint location; + + @Before + public void setUp() throws Exception { + manager = new InfrastructureInstanceManager(); + registration = mock(InfrastructureRegistrationMessage.class); + ipAddress = mock(InetAddress.class); + location = mock(GeoPoint.class); + } + + @Test + public void testOnNewRegistration() { + // Set up the registration object + String infrastructureId = "infrastructure-123"; + int rxMessagePort = 1234; + int timeSyncPort = 5678; + String ipAddressString = "127.0.0.1"; + GeoPoint pt = GeoPoint.latLon(37.3382, -121.8863); + + // Mock the behavior of the registration object + mockRegistrationObject(infrastructureId, rxMessagePort, timeSyncPort, ipAddressString, pt); + + // Call the onNewRegistration method with the mocked registration object + manager.onNewRegistration(registration); + + // Verify that the infrastructure instance was added to the manager + assertEquals(true, manager.checkIfRegistered(infrastructureId)); + } + + private void mockRegistrationObject(String infrastructureId, int rxMessagePort, int timeSyncPort, + String ipAddressString, GeoPoint pt) { + // Mock the behavior of the registration object + Mockito.when(registration.getInfrastructureId()).thenReturn(infrastructureId); + Mockito.when(registration.getRxMessagePort()).thenReturn(rxMessagePort); + Mockito.when(registration.getTimeSyncPort()).thenReturn(timeSyncPort); + Mockito.when(registration.getRxMessageIpAddress()).thenReturn(ipAddressString); + Mockito.when(registration.getLocation()).thenReturn(pt); + Mockito.when(ipAddress.getHostAddress()).thenReturn(ipAddressString); + } +} From 2858bd14e6ff30c2d050d06923bdefa76ace8f9e Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 11:46:59 -0400 Subject: [PATCH 054/124] Update InfrastructureInstanceManager.java --- .../ambassador/InfrastructureInstanceManager.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 13491184..e6758a03 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -63,4 +63,14 @@ private void newInfrastructureInstance(String infrastructureId, InetAddress rxMe } managedInstances.put(infrastructureId, tmp); } + + /** + * External helper function to allow the ambassador to check if a given vehicle ID is a registered CARMA Platform + * instance + * @param mosiacVehicleId The id to check + * @return True if managed by this object (e.g., is a registered CARMA Platform vehicle). false o.w. + */ + public boolean checkIfRegistered(String infrastructureId) { + return managedInstances.keySet().contains(infrastructureId); + } } From cab00b7ee85ee83dd47b14c92356a9c60c12f850 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 11:47:20 -0400 Subject: [PATCH 055/124] Update InfrastructureMessageAmbassador.java --- .../InfrastructureMessageAmbassador.java | 141 +++++++++++------- 1 file changed, 84 insertions(+), 57 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 5d563a12..ee190750 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -28,13 +28,6 @@ import org.eclipse.mosaic.interactions.application.InfrastructureV2xMessageReception; import org.eclipse.mosaic.interactions.communication.AdHocCommunicationConfiguration; import org.eclipse.mosaic.interactions.mapping.RsuRegistration; -import org.eclipse.mosaic.fed.infrastructure.ambassador.InfrastructureRegistrationMessage; - -import java.net.Inet4Address; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; import java.util.List; /** @@ -148,83 +141,117 @@ private synchronized void receiveInteraction(InfrastructureV2xMessageReception i } /** - * This method is called by the AbstractFederateAmbassador when a time advance - * has been granted by the RTI. Before this call is placed, any unprocessed - * interaction is forwarded to the federate using the processInteraction method. + * Creates an Ad-Hoc configuration object to represent the configuration of the + * Ad-Hoc interface used for communication between the infrastructure instance + * and other vehicles or components, and sends it to the RTI for exchange. + * + * @param reg the infrastructure registration message received from the RTI + */ + private final void onDsrcRegistrationRequest(InfrastructureRegistrationMessage reg) { + // Create an InterfaceConfiguration object to represent the configuration of the + // Ad-Hoc interface + // Set the IP address and subnet mask to null for now + // Set the transmit power to 50 dBm and the maximum range to 100 meters + InterfaceConfiguration interfaceConfig = new InterfaceConfiguration.Builder(AdHocChannel.SCH1) + .ip(null) + .subnet(null) + .power(50) + .radius(100.0) + .create(); + + // Create an AdHocConfiguration object to associate the Ad-Hoc interface + // configuration + // with the infrastructure instance's ID + AdHocConfiguration adHocConfig = new AdHocConfiguration.Builder(reg.getInfrastructureId()) + .addInterface(interfaceConfig) + .create(); + + // Create an AdHocCommunicationConfiguration object to specify the time and + // Ad-Hoc + // configuration for exchange with another vehicle or component + AdHocCommunicationConfiguration communicationConfig = new AdHocCommunicationConfiguration(currentSimulationTime, + adHocConfig); + + try { + // Trigger RTI interaction to MOSAIC to exchange the Ad-Hoc configuration + this.rti.triggerInteraction(communicationConfig); + } catch (InternalFederateException | IllegalValueException e) { + // Log error message if there was an issue with the RTI interaction + log.error(e.getMessage()); + } + } + + /** + * Performs registration of a new infrastructure instance and sets up + * communication interfaces. + * + * @param reg The registration message of the new infrastructure instance. + */ + private final void onRsuRegistrationRequest(InfrastructureRegistrationMessage reg) { + + // Register the new infrastructure instance to the RTI as an RSU + RsuRegistration rsuRegistration = new RsuRegistration(currentSimulationTime, reg.getInfrastructureId(), + null, null, + reg.getLocation()); + try { + // Trigger RTI interaction to MOSAIC + this.rti.triggerInteraction(rsuRegistration); + } catch (InternalFederateException | IllegalValueException e) { + // Log error message if there was an issue with the RTI interaction + log.error(e.getMessage()); + } + + + } + + /** + * This method is called by the AbstractFederateAmbassador when the RTI grants a + * time advance to the federate. Any unprocessed interactions are forwarded to + * the federate using the processInteraction method before this call is made. * - * @param time The timestamp towards which the federate can advance it local - * time. + * @param time The timestamp indicating the time to which the federate can + * advance its local time. */ @Override public synchronized void processTimeAdvanceGrant(long time) throws InternalFederateException { + // Process the time advance only if the time is equal or greater than the next + // simulation time step if (time < currentSimulationTime) { - // process time advance only if time is equal or greater than the next - // simulation time step return; } - try { - - // + + // Handle any new infrastructure registration requests List newRegistrations = infrastructureRegistrationReceiver .getReceivedMessages(); for (InfrastructureRegistrationMessage reg : newRegistrations) { - - // new instance registration to store to infrastructure instance manager + // Store new instance registration to infrastructure instance manager infrastructureInstanceManager.onNewRegistration(reg); - - // register RSU, currently one infrastructure pair with one RSU, the ID will be the same as result - // group and applications leave as null for now - RsuRegistration rsuRegistration = new RsuRegistration(currentSimulationTime, reg.getInfrastructureId(), - null, null, - reg.getLocation()); - try { - // trigger RTI interaction to MOSAIC - this.rti.triggerInteraction(rsuRegistration); - } catch (InternalFederateException | IllegalValueException e) { - log.error(e.getMessage()); - } - - //TODO actions to create DSRC parameter and then send to RTI interaction to MOSAIC - - // Create an AdHocConfiguration object to represent the configuration of the Ad-Hoc interface - InterfaceConfiguration interfaceConfig = new InterfaceConfiguration.Builder(AdHocChannel.SCH1) - .power(50) - .radius(100.0) - .secondChannel(AdHocChannel.SCH2) - .create(); - - AdHocConfiguration adHocConfig = new AdHocConfiguration.Builder(reg.getInfrastructureId()) - .addInterface(interfaceConfig) - .create(); - - AdHocCommunicationConfiguration communicationConfig = new AdHocCommunicationConfiguration(currentSimulationTime, adHocConfig); - - // Use the object to exchange the Ad-Hoc configuration with another vehicle or component - try { - // trigger RTI interaction to MOSAIC - this.rti.triggerInteraction(communicationConfig); - } catch (InternalFederateException | IllegalValueException e) { - log.error(e.getMessage()); - } + // Process registration requests for RSUs and DSRCs + onRsuRegistrationRequest(reg); + onDsrcRegistrationRequest(reg); } - // TODO actions to do on queued Infrastructure time sync messages + // TODO: Handle any queued Infrastructure time sync messages - // TODO actions to do on queued v2x message receiver's received messages + // TODO: Handle any queued V2X message receiver's received messages + // Advance the simulation time currentSimulationTime += infrastructureConfiguration.updateInterval * TIME.MILLI_SECOND; + // Request the next time advance from the RTI rti.requestAdvanceTime(currentSimulationTime, 0, (byte) 2); - // send RSU external message + // Send an external message to the RSU String message = "External Message to RSU: RSU sent message at time: " + currentSimulationTime; ExternalMessage rsuMessage = new ExternalMessage(currentSimulationTime, message, infrastructureConfiguration.senderRSUId); try { + // Trigger RTI interaction to MOSAIC this.rti.triggerInteraction(rsuMessage); } catch (InternalFederateException | IllegalValueException e) { + // Log an error message if there was an issue with the RTI interaction log.error(e.getMessage()); } From 51821488ea922fa076473d053abde86eb1c02e1a Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 13:34:52 -0400 Subject: [PATCH 056/124] Added copyright --- .../InfrastructureInstanceManagerTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManagerTest.java b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManagerTest.java index 9fa5cba4..7bc7b19c 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManagerTest.java +++ b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManagerTest.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2023 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. + */ + package org.eclipse.mosaic.fed.infrastructure.ambassador; import static org.junit.Assert.assertEquals; From 4ad02e4eaa00963f9c337b12b372c6e6aac843a2 Mon Sep 17 00:00:00 2001 From: Eric Chen <122404059+EricChen-Lei@users.noreply.github.com> Date: Fri, 7 Apr 2023 14:15:09 -0400 Subject: [PATCH 057/124] code update updated suggested code and bugs fixed --- .../ambassador/InfrastructureInstance.java | 7 +- .../InfrastructureInstanceManager.java | 9 +-- .../InfrastructureMessageAmbassador.java | 38 ++++----- .../InfrastructureTimeInterface.java | 78 ++++++++++--------- .../ambassador/InfrastructureTimeMessage.java | 25 +++--- .../InfrastructureTimeInterfaceTest.java | 44 +++++++---- 6 files changed, 113 insertions(+), 88 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java index f232251a..c2793005 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java @@ -72,7 +72,12 @@ public void sendMsgs(byte[] data) throws IOException { } - public void sendTimesyncMsgs(byte[] data) throws IOException { + /** + * Sends time sync data to the Infrastructure Device communications interface configured at construction time. + * @param data The binary data to transmit + * @throws IOException If there is an issue with the underlying socket object or methods + */ + public void sendTimeSyncMsgs(byte[] data) throws IOException { if (rxMsgsSocket == null) { throw new IllegalStateException("Attempted to send data before opening socket"); } diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 623b29af..dc8ee393 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -34,16 +34,15 @@ import org.eclipse.mosaic.lib.objects.vehicle.VehicleData; /** - * Session management class for Infrastructure instances communicating with MOSAIC + * Session management class for Infrastructure instances communicating with + * MOSAIC * NOTE: TODO See carma.ambassador for reference */ public class InfrastructureInstanceManager { - private Map managedInstances = new HashMap<>(); + private Map managedInstances = new HashMap<>(); private double currentSimulationTime; - public Map get_managedInstances() - { + public Map getManagedInstances() { return managedInstances; } - } diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 0133110b..afda1244 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -63,9 +63,9 @@ public class InfrastructureMessageAmbassador extends AbstractFederateAmbassador private InfrastructureTimeMessageReceiver InfrastructureTimeMessageReceiver; private Thread v2xTimeRxBackgroundThread; private InfrastructureInstanceManager InfrastructureInstanceManager = new InfrastructureInstanceManager(); - private InfrastructureTimeInterface InfrastructureTimeInterface; + private InfrastructureTimeInterface infrastructureTimeInterface; - int Timesync_seq = 0; + private int timeSyncSeq = 0; /** * Create a new {@link InfrastructureMessageAmbassador} object. @@ -112,10 +112,12 @@ public void initialize(long startTime, long endTime) throws InternalFederateExce throw new InternalFederateException(e); } - // TODO Initialize listener socket and thread for Infrastructure Registration messages + // TODO Initialize listener socket and thread for Infrastructure Registration + // messages + + // TODO Initialize listener socket and thread for Infrastructure time sync + // messages - // TODO Initialize listener socket and thread for Infrastructure time sync messages - // TODO Register any V2x infrastructures from config if any } @@ -136,9 +138,10 @@ public void processInteraction(Interaction interaction) throws InternalFederateE if (interaction.getTypeId().equals(InfrastructureV2xMessageReception.TYPE_ID)) { this.receiveInteraction((InfrastructureV2xMessageReception) interaction); } - // TODO Time sync message reception: needs to be implemented when time regulat.... + // TODO Time sync message reception: needs to be implemented when time + // regulat.... - // TODO V2xHub infrastructure Registration reception + // TODO V2xHub infrastructure Registration reception } /** @@ -174,17 +177,14 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder } try { - // TODO actions to do on queued Infrastructure instance registration attempts - - Timesync_seq += 1; - InfrastructureTimeMessage timesync_message = new InfrastructureTimeMessage(); - timesync_message.set_seq(Timesync_seq); - timesync_message.set_timestep(currentSimulationTime); - try { - InfrastructureTimeInterface.onTimestepUpdate(timesync_message); - } catch (IOException e) { - log.error(e.getMessage()); - } + // TODO actions to do on queued Infrastructure instance registration attempts + + timeSyncSeq += 1; + InfrastructureTimeMessage timeSyncMessage = new InfrastructureTimeMessage(); + timeSyncMessage.setSeq(timeSyncSeq); + timeSyncMessage.setTimestep(currentSimulationTime); + infrastructureTimeInterface.onTimeStepUpdate(timeSyncMessage); + // TODO actions to do on queued v2x message receiver's received messages currentSimulationTime += infrastructureConfiguration.updateInterval * TIME.MILLI_SECOND; @@ -204,6 +204,8 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder } catch (IllegalValueException e) { log.error("Error during advanceTime(" + time + ")", e); throw new InternalFederateException(e); + } catch (IOException e1) { + log.error("Error during updating timestep :" + e1.getMessage()); } } diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterface.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterface.java index 539ebcce..2ebe2174 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterface.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterface.java @@ -15,50 +15,54 @@ */ package org.eclipse.mosaic.fed.infrastructure.ambassador; + import java.io.IOException; -import org.apache.commons.logging.Log; +import org.slf4j.Logger; import com.google.gson.Gson; -public class InfrastructureTimeInterface{ -public InfrastructureInstanceManager manager; -//private int target_port=12123; -//set to false on init release -private Boolean await_infrastructure_advance_request = false; -public InfrastructureTimeInterface(InfrastructureInstanceManager manager){ - this.manager = manager; -} -/** - * This function implements the encoding of a json string consist of timestep and seq - * - * @param message This is the class that gets encoded, which include the data of timestep and seq - * @return return_json: encoded json string - */ -public byte[] encodeTimeMessage(InfrastructureTimeMessage message) -{ - Gson gson = new Gson(); - String json = gson.toJson(message); - byte[] return_json = json.getBytes(); - return return_json; -} +public class InfrastructureTimeInterface { + private InfrastructureInstanceManager manager; + // set to false on init release + private boolean await_infrastructure_advance_request = false; -/** - * This function is used to send out encoded timestep update to all registered instances the manager has on the managed instances map - * - * @param message This time message is used to store current seq and timestep from the ambassador side - * @throws IOException - */ -public void onTimestepUpdate(InfrastructureTimeMessage message) throws IOException -{ - if(this.manager.get_managedInstances().size() == 0) - { - throw new IllegalAccessError("There are no registered instances!"); + protected Logger log; + + public InfrastructureTimeInterface(InfrastructureInstanceManager manager) { + this.manager = manager; } - for(InfrastructureInstance current_instance: this.manager.get_managedInstances().values()) - { - current_instance.sendTimesyncMsgs(encodeTimeMessage(message)); + /** + * This function implements the encoding of a json string consist of timestep + * and seq + * + * @param message This is the class that gets encoded, which include the data of + * timestep and seq + * @return return_json: encoded json string + */ + public byte[] encodeTimeMessage(InfrastructureTimeMessage message) { + Gson gson = new Gson(); + String json = gson.toJson(message); + byte[] returnJson = json.getBytes(); + return returnJson; + } + + /** + * This function is used to send out encoded timestep update to all registered + * instances the manager has on the managed instances map + * + * @param message This time message is used to store current seq and timestep + * from the ambassador side + * @throws IOException + */ + public void onTimeStepUpdate(InfrastructureTimeMessage message) throws IOException { + if (this.manager.getManagedInstances().size() == 0) { + log.info("There are no registered instances"); + } + + for (InfrastructureInstance currentInstance : manager.getManagedInstances().values()) { + currentInstance.sendTimeSyncMsgs(encodeTimeMessage(message)); + } } -} } \ No newline at end of file diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java index d5a67973..821aed0b 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java @@ -22,32 +22,31 @@ /** * Message to be sent or received by the Infrastructure Device Adapter interface * NOTE: TODO See .ambassador for reference - + * */ public class InfrastructureTimeMessage { private long timestep; private int seq; - - + public InfrastructureTimeMessage() { } - public long get_timestep() - { + public long getTimestep() { return timestep; } - public int get_seq() - { + + public int getSeq() { return seq; } - public void set_timestep(long new_timestep) - { - this.timestep = new_timestep; + + public void setTimestep(long newTimestep) { + this.timestep = newTimestep; } - public void set_seq(int new_seq) - { - this.seq = new_seq; + + public void setSeq(int newSeq) { + this.seq = newSeq; } + @Override public String toString() { return "InfrastructureTimeMessage [timestep=" + timestep + ", seq=" + seq + "]"; diff --git a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterfaceTest.java b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterfaceTest.java index 51e06a6d..5841789e 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterfaceTest.java +++ b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterfaceTest.java @@ -1,6 +1,21 @@ +/* + * Copyright (c) 2021 Old Dominion University. All rights reserved. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + package org.eclipse.mosaic.fed.infrastructure.ambassador; import org.junit.Test; + +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -26,38 +41,39 @@ public class InfrastructureTimeInterfaceTest { private InfrastructureTimeInterface infrastructureTimeInterface; @Before - public void setup() - { + public void setup() { MockitoAnnotations.initMocks(this); infrastructureTimeInterface = new InfrastructureTimeInterface(manager); } @Test - public void testTimesyncUpdate() throws IOException - { + public void testTimesyncUpdate() throws IOException { InfrastructureTimeMessage infrastructureTimeMessage = new InfrastructureTimeMessage(); - infrastructureTimeMessage.set_seq(1); - infrastructureTimeMessage.set_timestep((long)20230406); + infrastructureTimeMessage.setSeq(1); + infrastructureTimeMessage.setTimestep((long) 20230406); Map managed_instance_map = new HashMap<>(); managed_instance_map.put("instance1", infrastructureInstanceMock1); managed_instance_map.put("instance2", infrastructureInstanceMock2); - when(manager.get_managedInstances()).thenReturn(managed_instance_map); + when(manager.getManagedInstances()).thenReturn(managed_instance_map); - infrastructureTimeInterface.onTimestepUpdate(infrastructureTimeMessage); + infrastructureTimeInterface.onTimeStepUpdate(infrastructureTimeMessage); - infrastructureTimeMessage.set_seq(2); - infrastructureTimeMessage.set_timestep((long)20230407); + infrastructureTimeMessage.setSeq(2); + infrastructureTimeMessage.setTimestep((long) 20230407); - infrastructureTimeInterface.onTimestepUpdate(infrastructureTimeMessage); + infrastructureTimeInterface.onTimeStepUpdate(infrastructureTimeMessage); Gson gson = new Gson(); String temp = gson.toJson(infrastructureTimeMessage); byte[] t_byte = temp.getBytes(); - verify(infrastructureInstanceMock1, times(1)).sendTimesyncMsgs(eq((byte[])t_byte)); - verify(infrastructureInstanceMock2, times(1)).sendTimesyncMsgs(eq((byte[])t_byte)); - + verify(infrastructureInstanceMock1, times(1)).sendTimeSyncMsgs(eq((byte[]) t_byte)); + verify(infrastructureInstanceMock2, times(1)).sendTimeSyncMsgs(eq((byte[]) t_byte)); + + verify(infrastructureInstanceMock1, times(2)).sendTimeSyncMsgs(any()); + verify(infrastructureInstanceMock2, times(2)).sendTimeSyncMsgs(any()); + } } From 3af8f03579a500305907e040aba96eda2c5b829c Mon Sep 17 00:00:00 2001 From: Eric Chen <122404059+EricChen-Lei@users.noreply.github.com> Date: Fri, 7 Apr 2023 14:35:17 -0400 Subject: [PATCH 058/124] updated the copyright updated the copyright statement --- .../InfrastructureTimeInterfaceTest.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterfaceTest.java b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterfaceTest.java index 5841789e..d679aed8 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterfaceTest.java +++ b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterfaceTest.java @@ -1,14 +1,17 @@ /* - * Copyright (c) 2021 Old Dominion University. All rights reserved. + * Copyright (C) 2023 LEIDOS. * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. + * 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 * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * SPDX-License-Identifier: EPL-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. */ package org.eclipse.mosaic.fed.infrastructure.ambassador; From 1f45013397d19887b3b3f7c12c122eff730eedb2 Mon Sep 17 00:00:00 2001 From: Eric Chen <122404059+EricChen-Lei@users.noreply.github.com> Date: Fri, 7 Apr 2023 14:42:11 -0400 Subject: [PATCH 059/124] update the ambassador copyright update copyright from odu to leidos --- .../InfrastructureMessageAmbassador.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index afda1244..f388b8f3 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -1,14 +1,17 @@ /* - * Copyright (c) 2021 Old Dominion University. All rights reserved. + * Copyright (C) 2023 LEIDOS. * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. + * 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 * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * SPDX-License-Identifier: EPL-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. */ package org.eclipse.mosaic.fed.infrastructure.ambassador; From bba77edad05c2d3393366a1bd4abaaeb74c33fec Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 18:25:47 -0400 Subject: [PATCH 060/124] Added log and comments and adjusted indentation --- .../InfrastructureInstanceManager.java | 75 +++++++++++++++---- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index e6758a03..3be8fe03 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -28,34 +28,74 @@ import org.eclipse.mosaic.lib.enums.AdHocChannel; import org.eclipse.mosaic.lib.geo.GeoPoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** - * Session management class for Infrastructure instances communicating with MOSAIC - * NOTE: TODO See carma.ambassador for reference + * Session management class for Infrastructure instances communicating with + * MOSAIC. + * + * This class is responsible for managing instances of infrastructure registered + * with the MOSAIC system. It provides methods for registering new instances, + * checking if instances are registered, and storing and retrieving instances + * from a map. */ public class InfrastructureInstanceManager { private Map managedInstances = new HashMap<>(); private double currentSimulationTime; + private static final Logger log = LoggerFactory.getLogger(InfrastructureInstanceManager.class); - public void onNewRegistration(InfrastructureRegistrationMessage registration){ + /** + * Register a new infrastructure instance with the MOSAIC system. + * + * This method takes an InfrastructureRegistrationMessage, converts it to an + * InfrastructureInstance, and adds it to the managedInstances map if it is not + * already present. + * + * @param registration The InfrastructureRegistrationMessage to be registered. + * + * @throws RuntimeException if an error occurs while creating or adding the new + * instance. + */ + public void onNewRegistration(InfrastructureRegistrationMessage registration) { if (!managedInstances.containsKey(registration.getInfrastructureId())) { try { newInfrastructureInstance( - registration.getInfrastructureId(), - InetAddress.getByName(registration.getRxMessageIpAddress()), - registration.getRxMessagePort(), - registration.getTimeSyncPort(), - registration.getLocation() - ); + registration.getInfrastructureId(), + InetAddress.getByName(registration.getRxMessageIpAddress()), + registration.getRxMessagePort(), + registration.getTimeSyncPort(), + registration.getLocation()); } catch (UnknownHostException e) { + log.error("Failed to create infrastructure instance with ID '{}' due to an unknown host exception: {}", + registration.getInfrastructureId(), e.getMessage()); throw new RuntimeException(e); } } else { - // log warning + log.warn("Registration message received for already registered infrastructure with ID: {}", + registration.getInfrastructureId()); } } - private void newInfrastructureInstance(String infrastructureId, InetAddress rxMessageIpAddress, int rxMessagePort, int timeSyncPort, GeoPoint location) { - InfrastructureInstance tmp = new InfrastructureInstance(infrastructureId, rxMessageIpAddress, rxMessagePort, timeSyncPort, location); + /** + * Create a new InfrastructureInstance and add it to the managedInstances map. + * + * This method creates a new InfrastructureInstance with the provided parameters + * and adds it to the managedInstances map. + * + * @param infrastructureId The ID of the new instance. + * @param rxMessageIpAddress The IP address to receive messages on. + * @param rxMessagePort The port to receive messages on. + * @param timeSyncPort The port for time synchronization. + * @param location The location of the instance. + * + * @throws RuntimeException if an error occurs while creating or adding the new + * instance. + */ + private void newInfrastructureInstance(String infrastructureId, InetAddress rxMessageIpAddress, int rxMessagePort, + int timeSyncPort, GeoPoint location) { + InfrastructureInstance tmp = new InfrastructureInstance(infrastructureId, rxMessageIpAddress, rxMessagePort, + timeSyncPort, location); try { tmp.bind(); } catch (IOException e) { @@ -64,11 +104,14 @@ private void newInfrastructureInstance(String infrastructureId, InetAddress rxMe managedInstances.put(infrastructureId, tmp); } - /** - * External helper function to allow the ambassador to check if a given vehicle ID is a registered CARMA Platform + /** + * External helper function to allow the ambassador to check if a given vehicle + * ID is a registered CARMA Platform * instance - * @param mosiacVehicleId The id to check - * @return True if managed by this object (e.g., is a registered CARMA Platform vehicle). false o.w. + * + * @param infrastructureId The id to check + * @return True if managed by this object (e.g., is a registered CARMA Platform + * vehicle). false o.w. */ public boolean checkIfRegistered(String infrastructureId) { return managedInstances.keySet().contains(infrastructureId); From 247fbe9c47c58b37629d570f976e8597fad2b8c0 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 19:53:11 -0400 Subject: [PATCH 061/124] Added doc and clean up TODO --- .../ambassador/InfrastructureInstance.java | 94 ++++++++++++++++--- 1 file changed, 81 insertions(+), 13 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java index 9a3c7971..84efe04d 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ - + package org.eclipse.mosaic.fed.infrastructure.ambassador; import org.eclipse.mosaic.lib.geo.GeoPoint; @@ -22,26 +22,35 @@ import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; -import java.net.SocketException; /** - * Connection manager and data object to associate with a single infrastructure instance in XIL - * NOTE: TODO See carma.ambassador for reference + * InfrastructureInstance class represents a physical instance of an + * infrastructure node in the simulated environment. + * It contains information about the infrastructure node such as its ID, + * location, target address, and ports. */ public class InfrastructureInstance { - + private String infrastructureId; private InetAddress targetAddress; private int rxMessagePort; private int timeSyncPort; private GeoPoint location = null; - - - private DatagramSocket rxMsgsSocket = null; - public InfrastructureInstance(String infrastructureId, InetAddress targetAddress, - int rxMessagePort, int timeSyncPort, GeoPoint location) { + /** + * Constructor for InfrastructureInstance + * + * @param infrastructureId the ID of the infrastructure node + * @param targetAddress the target IP address of the infrastructure node + * @param rxMessagePort the receive message port of the infrastructure node + * @param timeSyncPort the time synchronization port of the infrastructure + * node + * @param location the location of the infrastructure node in the + * simulated environment + */ + public InfrastructureInstance(String infrastructureId, InetAddress targetAddress, + int rxMessagePort, int timeSyncPort, GeoPoint location) { this.infrastructureId = infrastructureId; this.targetAddress = targetAddress; this.rxMessagePort = rxMessagePort; @@ -49,55 +58,114 @@ public InfrastructureInstance(String infrastructureId, InetAddress targetAddress this.location = location; } + /** + * Returns the target IP address of the infrastructure node + * + * @return InetAddress the target IP address of the infrastructure node + */ public InetAddress getTargetAddress() { return targetAddress; } + /** + * Sets the target IP address of the infrastructure node + * + * @param targetAddress the target IP address to set + */ public void setTargetAddress(InetAddress targetAddress) { this.targetAddress = targetAddress; } + /** + * Returns the location of the infrastructure node in the simulated environment + * + * @return GeoPoint the location of the infrastructure node + */ public GeoPoint getLocation() { return this.location; } + /** + * Sets the location of the infrastructure node in the simulated environment + * + * @param location the location to set + */ public void setLocation(GeoPoint location) { this.location = location; } + /** + * Returns the ID of the infrastructure node + * + * @return String the ID of the infrastructure node + */ public String getInfrastructureId() { return infrastructureId; } + /** + * Sets the ID of the infrastructure node + * + * @param infrastructureId the ID to set + */ public void setInfrastructureId(String infrastructureId) { this.infrastructureId = infrastructureId; } + /** + * Returns the receive message port of the infrastructure node + * + * @return int the receive message port of the infrastructure node + */ public int getRxMessagePort() { return rxMessagePort; } + /** + * Sets the receive message port of the infrastructure node + * + * @param rxMessagePort the port to set + */ public void setRxMessagePort(int rxMessagePort) { this.rxMessagePort = rxMessagePort; } + /** + * Returns the time synchronization port of the infrastructure node + * + * @return int the time synchronization port of the infrastructure node + */ public int getTimeSyncPort() { return timeSyncPort; } + /** + * Sets the time synchronization port of the infrastructure node + * + * @param timeSyncPort the port to set + */ public void setTimeSyncPort(int timeSyncPort) { this.timeSyncPort = timeSyncPort; } + /** + * Creates a DatagramSocket object and binds it to this infrastructure + * instance's receive message port + * + * @throws IOException if there is an issue with the underlying socket object or + * methods + */ public void bind() throws IOException { rxMsgsSocket = new DatagramSocket(); } - /** - * Sends the data to the Infrastructure Device communications interface configured at construction time. + * Sends the data to the Infrastructure Device communications interface + * configured at construction time. + * * @param data The binary data to transmit - * @throws IOException If there is an issue with the underlying socket object or methods + * @throws IOException If there is an issue with the underlying socket object or + * methods */ public void sendMsgs(byte[] data) throws IOException { if (rxMsgsSocket == null) { From f89b9642986f5230cc8ee1c89c8756d220b4b0ca Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 19:55:51 -0400 Subject: [PATCH 062/124] Added doc and clean up TODO --- .../InfrastructureRegistrationReceiver.java | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java index bc6a34ee..61918f34 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java @@ -27,32 +27,41 @@ import com.google.gson.Gson; /** - * Worker thread Runnable for operating a listen socket to receive outbound Infrastructure Messages from Infrastructure Device instances - * This {@link Runnable} instance will operate a UDP socket to subscribe to packets from the V2x infrastructure's - * adapter. Upon receiving a packet, it will be enqueued for the primary thread to process the data once it ticks to a + * Worker thread Runnable for operating a listen socket to receive outbound + * Infrastructure Messages from Infrastructure Device instances + * This {@link Runnable} instance will operate a UDP socket to subscribe to + * packets from the V2x infrastructure's + * adapter. Upon receiving a packet, it will be enqueued for the primary thread + * to process the data once it ticks to a * simulation processing step - * NOTE: TODO See carma.ambassador for reference */ public class InfrastructureRegistrationReceiver implements Runnable { private Queue rxQueue = new LinkedList<>(); private DatagramSocket listenSocket = null; - private static final int listenPort = 1515; // TODO + private static final int listenPort = 1515; private boolean running = false; - private static final int UDP_MTU = 1535; // TODO Maximum Transmission Unit + private static final int UDP_MTU = 1535; /** - * Initialize the listen socket for messages from the Infrastructure Device NS-3 Adapter + * Initialize the listen socket for messages from the Infrastructure Device NS-3 + * Adapter + * * @throws RuntimeException iff socket instantiation fails */ public void init() { try { listenSocket = new DatagramSocket(listenPort); - + } catch (SocketException e) { throw new RuntimeException(e); } } + /** + * The main method of the worker thread. Listens for incoming messages and + * enqueues them for processing on the + * primary thread. + */ @Override public void run() { byte[] buf = new byte[UDP_MTU]; @@ -64,22 +73,23 @@ public void run() { } catch (IOException e) { throw new RuntimeException(e); } - + // parse message - String receivedPacket = new String(msg.getData(), 0, msg.getLength()); // Use length of message to create String + String receivedPacket = new String(msg.getData(), 0, msg.getLength()); // Use length of message to create + // String Gson gson = new Gson(); - InfrastructureRegistrationMessage parsedMessage = gson.fromJson(receivedPacket, InfrastructureRegistrationMessage.class); - + InfrastructureRegistrationMessage parsedMessage = gson.fromJson(receivedPacket, + InfrastructureRegistrationMessage.class); + // Enqueue message for processing on main thread synchronized (rxQueue) { rxQueue.add(parsedMessage); } } } - /** - * Stop the runnable instance + * Stop the runnable instance and close the listen socket. */ public void stop() { if (listenSocket != null) { @@ -89,8 +99,11 @@ public void stop() { } /** - * Query the current buffer of outbound messages. Clears the currently stored buffer once called. Thread-safe. - * @return The list of received outbound message from all Infrastructure Device instances since last call of this method + * Query the current buffer of outbound messages. Clears the currently stored + * buffer once called. Thread-safe. + * + * @return The list of received outbound message from all Infrastructure Device + * instances since last call of this method */ public List getReceivedMessages() { List output = new ArrayList<>(); @@ -99,7 +112,7 @@ public List getReceivedMessages() { output.addAll(rxQueue); rxQueue.clear(); } - + return output; } } From 91a10e3edf46df5bb5c9de055c90933c4ddbde20 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 20:06:45 -0400 Subject: [PATCH 063/124] Added doc and clean up TODO --- .../InfrastructureRegistrationMessage.java | 128 +++++++++++++++--- 1 file changed, 110 insertions(+), 18 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java index 20782031..5a8b9430 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java @@ -13,24 +13,49 @@ * License for the specific language governing permissions and limitations under * the License. */ - + package org.eclipse.mosaic.fed.infrastructure.ambassador; + import org.eclipse.mosaic.lib.geo.GeoPoint; /** - * A message to be sent by Infrastructure Device when it registers with the carma-mosaic ambassador - * NOTE: TODO See carma.ambassador for reference + * A message to be sent by Infrastructure Device when it registers with the + * carma-mosaic ambassador */ public class InfrastructureRegistrationMessage { + + // IP address where the Infrastructure Device will be listening for inbound + // messages private String rxMessageIpAddress; + // Unique identifier for the Infrastructure Device private String infrastructureId; + // Port number where the Infrastructure Device will be listening for inbound + // messages private int rxMessagePort = 1536; - private int timeSyncPort = 1517; // TODO + + // Port number where the Infrastructure Device will be listening for time + // synchronization messages + private int timeSyncPort = 1517; + + // Geo-coordinate of the Infrastructure Device location private GeoPoint location = null; + /** + * Constructor for an `InfrastructureRegistrationMessage` instance + * + * @param rxMessageIpAddress IP address where the Infrastructure Device will be + * listening for inbound messages + * @param infrastructureId Unique identifier for the Infrastructure Device + * @param rxMessagePort Port number where the Infrastructure Device will be + * listening for inbound messages + * @param timeSyncPort Port number where the Infrastructure Device will be + * listening for time synchronization messages + * @param location Geo-coordinate of the Infrastructure Device + * location + */ public InfrastructureRegistrationMessage(String rxMessageIpAddress, String infrastructureId, - int rxMessagePort, int timeSyncPort, GeoPoint location) { + int rxMessagePort, int timeSyncPort, GeoPoint location) { this.rxMessageIpAddress = rxMessageIpAddress; this.infrastructureId = infrastructureId; this.rxMessagePort = rxMessagePort; @@ -38,52 +63,119 @@ public InfrastructureRegistrationMessage(String rxMessageIpAddress, String infra this.location = location; } - public String getRxMessageIpAddress(){ + /** + * Returns the IP address where the Infrastructure Device will be listening for + * inbound messages + * + * @return The IP address where the Infrastructure Device will be listening for + * inbound messages + */ + public String getRxMessageIpAddress() { return this.rxMessageIpAddress; } - public String getInfrastructureId(){ + /** + * Returns the unique identifier for the Infrastructure Device + * + * @return The unique identifier for the Infrastructure Device + */ + public String getInfrastructureId() { return this.infrastructureId; } - public int getRxMessagePort(){ + /** + * Returns the port number where the Infrastructure Device will be listening for + * inbound messages + * + * @return The port number where the Infrastructure Device will be listening for + * inbound messages + */ + public int getRxMessagePort() { return this.rxMessagePort; } - public int getTimeSyncPort(){ + /** + * Returns the port number where the Infrastructure Device will be listening for + * time synchronization messages + * + * @return The port number where the Infrastructure Device will be listening for + * time synchronization messages + */ + public int getTimeSyncPort() { return this.timeSyncPort; } - public GeoPoint getLocation(){ + /** + * Returns the Geo-coordinate of the Infrastructure Device location + * + * @return The Geo-coordinate of the Infrastructure Device location + */ + public GeoPoint getLocation() { return this.location; } - public void setRxMessageIpAddress(String rxMessageIpAddress){ + /** + * Sets the IP address where the Infrastructure Device will be listening for + * inbound messages + * + * @param rxMessageIpAddress The IP address where the Infrastructure Device will + * be listening for inbound messages + */ + public void setRxMessageIpAddress(String rxMessageIpAddress) { this.rxMessageIpAddress = rxMessageIpAddress; } - public void setInfrastructureId(String infrastructureId){ + /** + * Sets the unique identifier for the Infrastructure Device + * It currently is the same with RSU ID + * + * @param infrastructureId The unique identifier for the Infrastructure Device + */ + public void setInfrastructureId(String infrastructureId) { this.infrastructureId = infrastructureId; } - public void setRxMessagePort(int rxMessagePort){ + /** + * Sets the port number where the Infrastructure Device will be listening for + * inbound messages + * + * @param rxMessagePort The port number where the Infrastructure Device will be + * listening for inbound messages + */ + public void setRxMessagePort(int rxMessagePort) { this.rxMessagePort = rxMessagePort; } - public void setTimeSyncPort(int timeSyncPort){ + /** + * Set the time sync port for the InfrastructureRegistrationMessage + * + * @param timeSyncPort the new time sync port to be set + */ + public void setTimeSyncPort(int timeSyncPort) { this.timeSyncPort = timeSyncPort; } - public void setLocation(GeoPoint location){ + /** + * Set the location for the InfrastructureRegistrationMessage + * + * @param location the new GeoPoint object representing the location of the + * Infrastructure Device + */ + public void setLocation(GeoPoint location) { this.location = location; } + /** + * Returns a string representation of the InfrastructureRegistrationMessage + * object + * + * @return a string representation of the object + */ @Override public String toString() { - return "InfrastructureRegistrationMessage [rxMessageIpAddress=" + rxMessageIpAddress - + ", infrastructureId=" + infrastructureId + ", rxMessagePort=" + rxMessagePort + return "InfrastructureRegistrationMessage [rxMessageIpAddress=" + rxMessageIpAddress + + ", infrastructureId=" + infrastructureId + ", rxMessagePort=" + rxMessagePort + ", timeSyncPort=" + timeSyncPort + ", location=" + location + "]"; } - } From cc376afb8d3b20b67eee32e706da41a636b9ced1 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 20:10:10 -0400 Subject: [PATCH 064/124] Update InfrastructureRegistrationReceiver.java --- .../ambassador/InfrastructureRegistrationReceiver.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java index 61918f34..7f8c2b06 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java @@ -59,8 +59,7 @@ public void init() { /** * The main method of the worker thread. Listens for incoming messages and - * enqueues them for processing on the - * primary thread. + * enqueues them for processing on the primary thread. */ @Override public void run() { From aad01362cb6aabe5f409783c09738462fd31abe5 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 20:10:16 -0400 Subject: [PATCH 065/124] Update InfrastructureInstanceManager.java --- .../ambassador/InfrastructureInstanceManager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 3be8fe03..fa3fac44 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -106,8 +106,7 @@ private void newInfrastructureInstance(String infrastructureId, InetAddress rxMe /** * External helper function to allow the ambassador to check if a given vehicle - * ID is a registered CARMA Platform - * instance + * ID is a registered CARMA Platform instance * * @param infrastructureId The id to check * @return True if managed by this object (e.g., is a registered CARMA Platform From 5b61435b8c0ceb88611acb7e0a0873e6d1fc73c3 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Fri, 7 Apr 2023 20:32:44 -0400 Subject: [PATCH 066/124] Update InfrastructureMessageAmbassador.java --- .../InfrastructureMessageAmbassador.java | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index ee190750..55db809b 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -21,6 +21,7 @@ import org.eclipse.mosaic.rti.api.parameters.AmbassadorParameter; import org.eclipse.mosaic.fed.infrastructure.configuration.InfrastructureConfiguration; import org.eclipse.mosaic.lib.enums.AdHocChannel; +import org.eclipse.mosaic.lib.geo.GeoPoint; import org.eclipse.mosaic.lib.objects.communication.AdHocConfiguration; import org.eclipse.mosaic.lib.objects.communication.InterfaceConfiguration; import org.eclipse.mosaic.lib.util.objects.ObjectInstantiation; @@ -28,6 +29,8 @@ import org.eclipse.mosaic.interactions.application.InfrastructureV2xMessageReception; import org.eclipse.mosaic.interactions.communication.AdHocCommunicationConfiguration; import org.eclipse.mosaic.interactions.mapping.RsuRegistration; + +import java.util.Collections; import java.util.List; /** @@ -46,11 +49,6 @@ public class InfrastructureMessageAmbassador extends AbstractFederateAmbassador */ InfrastructureConfiguration infrastructureConfiguration; - /** - * The number of CARMA vehicles. - */ - int numberOfInfrastructureInstances = 0; - private InfrastructureRegistrationReceiver infrastructureRegistrationReceiver; private Thread registrationRxBackgroundThread; private InfrastructureTimeMessageReceiver infrastructureTimeMessageReceiver; @@ -141,17 +139,22 @@ private synchronized void receiveInteraction(InfrastructureV2xMessageReception i } /** + * * Creates an Ad-Hoc configuration object to represent the configuration of the * Ad-Hoc interface used for communication between the infrastructure instance * and other vehicles or components, and sends it to the RTI for exchange. * * @param reg the infrastructure registration message received from the RTI + * + * Note: This function should be called after the + * onRsuRegistrationRequest */ - private final void onDsrcRegistrationRequest(InfrastructureRegistrationMessage reg) { + private void onDsrcRegistrationRequest(String infrastructureId) { // Create an InterfaceConfiguration object to represent the configuration of the // Ad-Hoc interface // Set the IP address and subnet mask to null for now // Set the transmit power to 50 dBm and the maximum range to 100 meters + // NOTE: TODO Setup the IP address and subnet InterfaceConfiguration interfaceConfig = new InterfaceConfiguration.Builder(AdHocChannel.SCH1) .ip(null) .subnet(null) @@ -160,15 +163,13 @@ private final void onDsrcRegistrationRequest(InfrastructureRegistrationMessage r .create(); // Create an AdHocConfiguration object to associate the Ad-Hoc interface - // configuration - // with the infrastructure instance's ID - AdHocConfiguration adHocConfig = new AdHocConfiguration.Builder(reg.getInfrastructureId()) + // configuration with the infrastructure instance's ID + AdHocConfiguration adHocConfig = new AdHocConfiguration.Builder(infrastructureId) .addInterface(interfaceConfig) .create(); // Create an AdHocCommunicationConfiguration object to specify the time and - // Ad-Hoc - // configuration for exchange with another vehicle or component + // Ad-Hoc configuration for exchange with another vehicle or component AdHocCommunicationConfiguration communicationConfig = new AdHocCommunicationConfiguration(currentSimulationTime, adHocConfig); @@ -187,12 +188,11 @@ private final void onDsrcRegistrationRequest(InfrastructureRegistrationMessage r * * @param reg The registration message of the new infrastructure instance. */ - private final void onRsuRegistrationRequest(InfrastructureRegistrationMessage reg) { + private void onRsuRegistrationRequest(String infrastructureId, GeoPoint location) { // Register the new infrastructure instance to the RTI as an RSU - RsuRegistration rsuRegistration = new RsuRegistration(currentSimulationTime, reg.getInfrastructureId(), - null, null, - reg.getLocation()); + RsuRegistration rsuRegistration = new RsuRegistration(currentSimulationTime, infrastructureId, "", + Collections.emptyList(), location); try { // Trigger RTI interaction to MOSAIC this.rti.triggerInteraction(rsuRegistration); @@ -200,8 +200,6 @@ private final void onRsuRegistrationRequest(InfrastructureRegistrationMessage re // Log error message if there was an issue with the RTI interaction log.error(e.getMessage()); } - - } /** @@ -229,8 +227,8 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder // Store new instance registration to infrastructure instance manager infrastructureInstanceManager.onNewRegistration(reg); // Process registration requests for RSUs and DSRCs - onRsuRegistrationRequest(reg); - onDsrcRegistrationRequest(reg); + onRsuRegistrationRequest(reg.getInfrastructureId(), reg.getLocation()); + onDsrcRegistrationRequest(reg.getInfrastructureId()); } // TODO: Handle any queued Infrastructure time sync messages From 812bde29669e396f1c3fe09700ed8b72a06a804a Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Mon, 10 Apr 2023 10:02:31 -0400 Subject: [PATCH 067/124] Correct copyright --- .../InfrastructureMessageAmbassador.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 55db809b..7c43880a 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -1,14 +1,18 @@ /* - * Copyright (c) 2021 Old Dominion University. All rights reserved. + * Copyright (C) 2023 LEIDOS. * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. + * 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 * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * SPDX-License-Identifier: EPL-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. + * */ package org.eclipse.mosaic.fed.infrastructure.ambassador; From d7b4c932ed70a0faf05d4209dfbf073e26cc956a Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Mon, 10 Apr 2023 10:03:44 -0400 Subject: [PATCH 068/124] Updated error logging in onNewRegistration function to log the actual exception --- .../ambassador/InfrastructureInstanceManager.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index fa3fac44..375dd07c 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -54,8 +54,6 @@ public class InfrastructureInstanceManager { * * @param registration The InfrastructureRegistrationMessage to be registered. * - * @throws RuntimeException if an error occurs while creating or adding the new - * instance. */ public void onNewRegistration(InfrastructureRegistrationMessage registration) { if (!managedInstances.containsKey(registration.getInfrastructureId())) { @@ -69,12 +67,12 @@ public void onNewRegistration(InfrastructureRegistrationMessage registration) { } catch (UnknownHostException e) { log.error("Failed to create infrastructure instance with ID '{}' due to an unknown host exception: {}", registration.getInfrastructureId(), e.getMessage()); - throw new RuntimeException(e); + log.error("Stack trace:", e); } } else { log.warn("Registration message received for already registered infrastructure with ID: {}", registration.getInfrastructureId()); - } + } } /** From b318a1bfc219ccefce98a6184917032c39255a46 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Mon, 10 Apr 2023 10:20:01 -0400 Subject: [PATCH 069/124] Logged the actual exception message for newInfrastructureInstance function --- .../ambassador/InfrastructureInstanceManager.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 375dd07c..da8e4fd5 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -72,7 +72,7 @@ public void onNewRegistration(InfrastructureRegistrationMessage registration) { } else { log.warn("Registration message received for already registered infrastructure with ID: {}", registration.getInfrastructureId()); - } + } } /** @@ -97,7 +97,9 @@ private void newInfrastructureInstance(String infrastructureId, InetAddress rxMe try { tmp.bind(); } catch (IOException e) { - throw new RuntimeException(e); + log.error("Failed to bind infrastructure instance with ID '{}' to its RX message socket: {}", + infrastructureId, e.getMessage()); + log.error("Stack trace:", e); } managedInstances.put(infrastructureId, tmp); } From c92f6fb9a1b792a794e7ec14ca67dd10b5823b32 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Mon, 10 Apr 2023 10:23:22 -0400 Subject: [PATCH 070/124] Update InfrastructureInstanceManager.java --- .../ambassador/InfrastructureInstanceManager.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index da8e4fd5..e6c8ddf5 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -87,8 +87,6 @@ public void onNewRegistration(InfrastructureRegistrationMessage registration) { * @param timeSyncPort The port for time synchronization. * @param location The location of the instance. * - * @throws RuntimeException if an error occurs while creating or adding the new - * instance. */ private void newInfrastructureInstance(String infrastructureId, InetAddress rxMessageIpAddress, int rxMessagePort, int timeSyncPort, GeoPoint location) { From dc8671817b58e74fbc6e70a3f3d6dc13b559e63f Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Mon, 10 Apr 2023 13:46:45 -0400 Subject: [PATCH 071/124] Update InfrastructureMessageAmbassador.java --- .../ambassador/InfrastructureMessageAmbassador.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 48e79861..69f0c9e7 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -64,7 +64,7 @@ public class InfrastructureMessageAmbassador extends AbstractFederateAmbassador private InfrastructureTimeMessageReceiver infrastructureTimeMessageReceiver; private Thread v2xTimeRxBackgroundThread; - private InfrastructureInstanceManager InfrastructureInstanceManager = new InfrastructureInstanceManager(); + private InfrastructureInstanceManager infrastructureInstanceManager = new InfrastructureInstanceManager(); private InfrastructureTimeInterface infrastructureTimeInterface; private int timeSyncSeq = 0; From 9d7a7792860d40c77794c1ae11b9bfc0a9b49627 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Mon, 10 Apr 2023 14:14:55 -0400 Subject: [PATCH 072/124] Update InfrastructureInstanceManager.java --- .../ambassador/InfrastructureInstanceManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 3d32024e..23e499dd 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -112,7 +112,7 @@ private void newInfrastructureInstance(String infrastructureId, InetAddress rxMe */ public boolean checkIfRegistered(String infrastructureId) { return managedInstances.keySet().contains(infrastructureId); - + } public Map getManagedInstances() { return managedInstances; } From 2ef5b0eefbe7761453e4a73295ba56da417e902e Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Mon, 10 Apr 2023 15:24:43 -0400 Subject: [PATCH 073/124] Correct wrong MOSAIC header --- .../InfrastructureMessageAmbassador.java | 1 - .../InfrastructureMessageAmbassadorTest.java | 18 +++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 69f0c9e7..356661e2 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -12,7 +12,6 @@ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. - * */ package org.eclipse.mosaic.fed.infrastructure.ambassador; diff --git a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassadorTest.java b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassadorTest.java index 2a5842cb..99c5e75f 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassadorTest.java +++ b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassadorTest.java @@ -1,15 +1,19 @@ /* - * Copyright (c) 2021 Old Dominion University. All rights reserved. + * Copyright (C) 2023 LEIDOS. * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. + * 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 * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * SPDX-License-Identifier: EPL-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. */ + package org.eclipse.mosaic.fed.infrastructure.ambassador; import org.junit.Test; From 878a4478797031d57b07ddebe2c40fecb0e69fbe Mon Sep 17 00:00:00 2001 From: Kyle Rush Date: Wed, 12 Apr 2023 07:59:50 -0700 Subject: [PATCH 074/124] Uncomment ns-3 build steps --- .gitignore | 1 + Dockerfile | 1 + .../src/assembly/resources/fed/ns3/ns3_installer.sh | 1 + docker/install.sh | 9 ++++----- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 601c80a1..810ae2da 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ CARLA_*.tar.gz +.idea/ diff --git a/Dockerfile b/Dockerfile index f25a2a04..fcc8f8af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,6 +42,7 @@ ENV NVIDIA_DRIVER_CAPABILITIES graphics,utility,compute RUN adduser $SUMO_USER --disabled-password # Enable sudo +RUN sed -i 's|http://archive.ubuntu.com|http://us.archive.ubuntu.com|g' /etc/apt/sources.list RUN apt-get update && apt-get install -y sudo RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers RUN adduser $SUMO_USER sudo --disabled-password diff --git a/co-simulation/bundle/src/assembly/resources/fed/ns3/ns3_installer.sh b/co-simulation/bundle/src/assembly/resources/fed/ns3/ns3_installer.sh index 0fb89d74..57bd9cb5 100644 --- a/co-simulation/bundle/src/assembly/resources/fed/ns3/ns3_installer.sh +++ b/co-simulation/bundle/src/assembly/resources/fed/ns3/ns3_installer.sh @@ -363,6 +363,7 @@ build_ns3() # ns-3 prior to 3.28.1 does not compile without warnings using g++ 10.2.0 CXXFLAGS="-Wno-error" python3.6 ./build.py --disable-netanim + cp -ar ns-3.28/build/ns /usr/include log "Build ns3-federate" cd ${current_dir}/federate mv src/ClientServerChannel.h . diff --git a/docker/install.sh b/docker/install.sh index 408070da..04e4defb 100755 --- a/docker/install.sh +++ b/docker/install.sh @@ -59,11 +59,10 @@ cd /home/carma/src #sudo make install # Install NS-3 -#cd "/home/carma/src/co-simulation tool/bundle/src/assembly/resources/fed/ns3/" -# Added sudo cp -ar ns-3.28/build/ns /usr/include before "Build ns3-federate" in ns3_installer.sh -#chmod +x ns3_installer.sh -#set -x -#./ns3_installer.sh -q +cd "/home/carma/src/co-simulation tool/bundle/src/assembly/resources/fed/ns3/" +chmod +x ns3_installer.sh +set -x +./ns3_installer.sh -q #TODO: Add expore NS3_HOME=path_to_run.sh to /bin/fed/ns3/run.sh # Install SUMO-1.12.0 From b1ba170692742282dd1be307060e8c3478908b53 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 12 Apr 2023 19:36:27 -0400 Subject: [PATCH 075/124] Removed redundant files --- .../assembly/resources/fed/ns3/connection.py | 413 ----- .../assembly/resources/fed/ns3/constants.py | 1353 ----------------- .../src/assembly/resources/fed/ns3/main.py | 301 ---- 3 files changed, 2067 deletions(-) delete mode 100644 co-simulation/bundle/src/assembly/resources/fed/ns3/connection.py delete mode 100644 co-simulation/bundle/src/assembly/resources/fed/ns3/constants.py delete mode 100644 co-simulation/bundle/src/assembly/resources/fed/ns3/main.py diff --git a/co-simulation/bundle/src/assembly/resources/fed/ns3/connection.py b/co-simulation/bundle/src/assembly/resources/fed/ns3/connection.py deleted file mode 100644 index 415409ba..00000000 --- a/co-simulation/bundle/src/assembly/resources/fed/ns3/connection.py +++ /dev/null @@ -1,413 +0,0 @@ -# -*- coding: utf-8 -*- -# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo -# Copyright (C) 2008-2020 German Aerospace Center (DLR) and others. -# This program and the accompanying materials are made available under the -# terms of the Eclipse Public License 2.0 which is available at -# https://www.eclipse.org/legal/epl-2.0/ -# This Source Code may also be made available under the following Secondary -# Licenses when the conditions for such availability set forth in the Eclipse -# Public License 2.0 are satisfied: GNU General Public License, version 2 -# or later which is available at -# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html -# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later - -# @file connection.py -# @author Michael Behrisch -# @author Lena Kalleske -# @author Mario Krumnow -# @author Daniel Krajzewicz -# @author Jakob Erdmann -# @date 2008-10-09 - -from __future__ import print_function -from __future__ import absolute_import -import socket -import struct -import sys -import warnings -import abc - -from . import constants as tc -from .exceptions import TraCIException, FatalTraCIError -from .domain import _defaultDomains -from .storage import Storage - -_RESULTS = {0x00: "OK", 0x01: "Not implemented", 0xFF: "Error"} - - -class Connection: - - """Contains the socket, the composed message string - together with a list of TraCI commands which are inside. - """ - - def __init__(self, host, port, process): - if sys.platform.startswith('java'): - # working around jython 2.7.0 bug #2273 - self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) - else: - self._socket = socket.socket() - self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - self._socket.connect((host, port)) - self._process = process - self._string = bytes() - self._queue = [] - self._subscriptionMapping = {} - self._stepListeners = {} - self._nextStepListenerID = 0 - for domain in _defaultDomains: - domain._register(self, self._subscriptionMapping) - - def _recvExact(self): - try: - result = bytes() - while len(result) < 4: - t = self._socket.recv(4 - len(result)) - if not t: - return None - result += t - length = struct.unpack("!i", result)[0] - 4 - result = bytes() - while len(result) < length: - t = self._socket.recv(length - len(result)) - if not t: - return None - result += t - return Storage(result) - except socket.error: - return None - - def _sendExact(self): - if self._socket is None: - raise FatalTraCIError("Connection already closed.") - length = struct.pack("!i", len(self._string) + 4) - # print("python_sendExact: '%s'" % ' '.join(map(lambda x : "%X" % ord(x), self._string))) - self._socket.send(length + self._string) - result = self._recvExact() - if not result: - self._socket.close() - del self._socket - raise FatalTraCIError("connection closed by SUMO") - for command in self._queue: - prefix = result.read("!BBB") - err = result.readString() - if prefix[2] or err: - self._string = bytes() - self._queue = [] - raise TraCIException(err, prefix[1], _RESULTS[prefix[2]]) - elif prefix[1] != command: - raise FatalTraCIError("Received answer %s for command %s." % (prefix[1], command)) - elif prefix[1] == tc.CMD_STOP: - length = result.read("!B")[0] - 1 - result.read("!%sx" % length) - self._string = bytes() - self._queue = [] - return result - - def _pack(self, format, *values): - packed = bytes() - for f, v in zip(format, values): - if f == "i": - packed += struct.pack("!Bi", tc.TYPE_INTEGER, int(v)) - elif f == "I": # raw int for setOrder - packed += struct.pack("!i", int(v)) - elif f == "d": - packed += struct.pack("!Bd", tc.TYPE_DOUBLE, float(v)) - elif f == "D": # raw double for some base commands like simstep - packed += struct.pack("!d", float(v)) - elif f == "b": - packed += struct.pack("!Bb", tc.TYPE_BYTE, int(v)) - elif f == "B": - packed += struct.pack("!BB", tc.TYPE_UBYTE, int(v)) - elif f == "u": # raw unsigned byte needed for distance command and subscribe - packed += struct.pack("!B", int(v)) - elif f == "s": - v = str(v) - packed += struct.pack("!Bi", tc.TYPE_STRING, len(v)) + v.encode("latin1") - elif f == "p": # polygon - if len(v) <= 255: - packed += struct.pack("!BB", tc.TYPE_POLYGON, len(v)) - else: - packed += struct.pack("!BBi", tc.TYPE_POLYGON, 0, len(v)) - for p in v: - packed += struct.pack("!dd", *p) - elif f == "t": # tuple aka compound - packed += struct.pack("!Bi", tc.TYPE_COMPOUND, v) - elif f == "c": # color - packed += struct.pack("!BBBBB", tc.TYPE_COLOR, int(v[0]), int(v[1]), int(v[2]), - int(v[3]) if len(v) > 3 else 255) - elif f == "l": # string list - packed += struct.pack("!Bi", tc.TYPE_STRINGLIST, len(v)) - for s in v: - packed += struct.pack("!i", len(s)) + s.encode("latin1") - elif f == "f": # float list - packed += struct.pack("!Bi", tc.TYPE_DOUBLELIST, len(v)) - for x in v: - packed += struct.pack("!d", x) - elif f == "o": - packed += struct.pack("!Bdd", tc.POSITION_2D, *v) - elif f == "O": - packed += struct.pack("!Bddd", tc.POSITION_3D, *v) - elif f == "g": - packed += struct.pack("!Bdd", tc.POSITION_LON_LAT, *v) - elif f == "G": - packed += struct.pack("!Bddd", tc.POSITION_LON_LAT_ALT, *v) - elif f == "r": - packed += struct.pack("!Bi", tc.POSITION_ROADMAP, len(v[0])) + v[0].encode("latin1") - packed += struct.pack("!dB", v[1], v[2]) - return packed - - def _sendCmd(self, cmdID, varID, objID, format="", *values): - self._queue.append(cmdID) - packed = self._pack(format, *values) - length = len(packed) + 1 + 1 # length and command - if varID is not None: - if isinstance(varID, tuple): # begin and end of a subscription - length += 8 + 8 + 4 + len(objID) - else: - length += 1 + 4 + len(objID) - if length <= 255: - self._string += struct.pack("!BB", length, cmdID) - else: - self._string += struct.pack("!BiB", 0, length + 4, cmdID) - if varID is not None: - if isinstance(varID, tuple): - self._string += struct.pack("!dd", *varID) - else: - self._string += struct.pack("!B", varID) - self._string += struct.pack("!i", len(objID)) + objID.encode("latin1") - self._string += packed - return self._sendExact() - - def _readSubscription(self, result): - # to enable this you also need to set _DEBUG to True in storage.py - # result.printDebug() - result.readLength() - response = result.read("!B")[0] - isVariableSubscription = ((response >= tc.RESPONSE_SUBSCRIBE_INDUCTIONLOOP_VARIABLE and - response <= tc.RESPONSE_SUBSCRIBE_BUSSTOP_VARIABLE) or - (response >= tc.RESPONSE_SUBSCRIBE_PARKINGAREA_VARIABLE and - response <= tc.RESPONSE_SUBSCRIBE_OVERHEADWIRE_VARIABLE)) - objectID = result.readString() - if not isVariableSubscription: - domain = result.read("!B")[0] - numVars = result.read("!B")[0] - if isVariableSubscription: - while numVars > 0: - varID, status = result.read("!BB") - if status: - print("Error!", result.readTypedString()) - elif response in self._subscriptionMapping: - self._subscriptionMapping[response].add(objectID, varID, result) - else: - raise FatalTraCIError( - "Cannot handle subscription response %02x for %s." % (response, objectID)) - numVars -= 1 - else: - objectNo = result.read("!i")[0] - for _ in range(objectNo): - oid = result.readString() - if numVars == 0: - self._subscriptionMapping[response].addContext( - objectID, self._subscriptionMapping[domain], oid) - for __ in range(numVars): - varID, status = result.read("!BB") - if status: - print("Error!", result.readTypedString()) - elif response in self._subscriptionMapping: - self._subscriptionMapping[response].addContext( - objectID, self._subscriptionMapping[domain], oid, varID, result) - else: - raise FatalTraCIError( - "Cannot handle subscription response %02x for %s." % (response, objectID)) - return objectID, response - - def _subscribe(self, cmdID, begin, end, objID, varIDs, parameters): - format = "u" - args = [len(varIDs)] - for v in varIDs: - format += "u" - args.append(v) - if parameters is not None and v in parameters: - if isinstance(parameters[v], tuple): - f, a = parameters[v] - elif isinstance(parameters[v], int): - f, a = "i", parameters[v] - elif isinstance(parameters[v], float): - f, a = "d", parameters[v] - else: - f, a = "s", parameters[v] - format += f - args.append(a) - result = self._sendCmd(cmdID, (begin, end), objID, format, *args) - if varIDs: - objectID, response = self._readSubscription(result) - if response - cmdID != 16 or objectID != objID: - raise FatalTraCIError("Received answer %02x,%s for subscription command %02x,%s." % ( - response, objectID, cmdID, objID)) - - def _getSubscriptionResults(self, cmdID): - return self._subscriptionMapping[cmdID] - - def _subscribeContext(self, cmdID, begin, end, objID, domain, dist, varIDs, parameters=None): - result = self._sendCmd(cmdID, (begin, end), objID, "uDu" + (len(varIDs) * "u"), - domain, dist, len(varIDs), *varIDs) - if varIDs: - objectID, response = self._readSubscription(result) - if response - cmdID != 16 or objectID != objID: - raise FatalTraCIError("Received answer %02x,%s for context subscription command %02x,%s." % ( - response, objectID, cmdID, objID)) - - def _addSubscriptionFilter(self, filterType, params=None): - if filterType in (tc.FILTER_TYPE_NONE, tc.FILTER_TYPE_NOOPPOSITE, - tc.FILTER_TYPE_TURN, tc.FILTER_TYPE_LEAD_FOLLOW): - # filter without parameter - assert params is None - self._sendCmd(tc.CMD_ADD_SUBSCRIPTION_FILTER, None, None, "u", filterType) - elif filterType in (tc.FILTER_TYPE_DOWNSTREAM_DIST, tc.FILTER_TYPE_UPSTREAM_DIST, - tc.FILTER_TYPE_FIELD_OF_VISION, tc.FILTER_TYPE_LATERAL_DIST): - # filter with float parameter - self._sendCmd(tc.CMD_ADD_SUBSCRIPTION_FILTER, None, None, "ud", filterType, params) - elif filterType in (tc.FILTER_TYPE_VCLASS, tc.FILTER_TYPE_VTYPE): - # filter with list(string) parameter - self._sendCmd(tc.CMD_ADD_SUBSCRIPTION_FILTER, None, None, "ul", filterType, params) - elif filterType == tc.FILTER_TYPE_LANES: - # filter with list(byte) parameter - # check uniqueness of given lanes in list - lanes = set() - for i in params: - lane = int(i) - if lane < 0: - lane += 256 - lanes.add(lane) - if len(lanes) < len(list(params)): - warnings.warn("Ignoring duplicate lane specification for subscription filter.") - self._sendCmd(tc.CMD_ADD_SUBSCRIPTION_FILTER, None, None, - (len(lanes) + 2) * "u", filterType, len(lanes), *lanes) - - def load(self, args): - """ - Load a simulation from the given arguments. - """ - self._sendCmd(tc.CMD_LOAD, None, None, "l", args) - - def simulationStep(self, step=0.): - """ - Make a simulation step and simulate up to the given second in sim time. - If the given value is 0 or absent, exactly one step is performed. - Values smaller than or equal to the current sim time result in no action. - """ - if type(step) is int and step >= 1000: - warnings.warn("API change now handles step as floating point seconds", stacklevel=2) - result = self._sendCmd(tc.CMD_SIMSTEP, None, None, "D", step) - for subscriptionResults in self._subscriptionMapping.values(): - subscriptionResults.reset() - numSubs = result.readInt() - responses = [] - while numSubs > 0: - responses.append(self._readSubscription(result)) - numSubs -= 1 - self._manageStepListeners(step) - return responses - - def _manageStepListeners(self, step): - listenersToRemove = [] - for (listenerID, listener) in self._stepListeners.items(): - keep = listener.step(step) - if not keep: - listenersToRemove.append(listenerID) - for listenerID in listenersToRemove: - self.removeStepListener(listenerID) - - def addStepListener(self, listener): - """addStepListener(traci.StepListener) -> int - - Append the step listener (its step function is called at the end of every call to traci.simulationStep()) - Returns the ID assigned to the listener if it was added successfully, None otherwise. - """ - if issubclass(type(listener), StepListener): - listener.setID(self._nextStepListenerID) - self._stepListeners[self._nextStepListenerID] = listener - self._nextStepListenerID += 1 - # print ("traci: Added stepListener %s\nlisteners: %s"%(_nextStepListenerID - 1, _stepListeners)) - return self._nextStepListenerID - 1 - warnings.warn( - "Proposed listener's type must inherit from traci.StepListener. Not adding object of type '%s'" % - type(listener)) - return None - - def removeStepListener(self, listenerID): - """removeStepListener(traci.StepListener) -> bool - - Remove the step listener from traci's step listener container. - Returns True if the listener was removed successfully, False if it wasn't registered. - """ - # print ("traci: removeStepListener %s\nlisteners: %s"%(listenerID, _stepListeners)) - if listenerID in self._stepListeners: - self._stepListeners[listenerID].cleanUp() - del self._stepListeners[listenerID] - # print ("traci: Removed stepListener %s"%(listenerID)) - return True - warnings.warn("Cannot remove unknown listener %s.\nlisteners:%s" % (listenerID, self._stepListeners)) - return False - - def getVersion(self): - command = tc.CMD_GETVERSION - result = self._sendCmd(command, None, None) - result.readLength() - response = result.read("!B")[0] - if response != command: - raise FatalTraCIError("Received answer %s for command %s." % (response, command)) - return result.readInt(), result.readString() - - def getV2xMessage(self): - command = tc.CMD_GET_V2X - result = self._sendCmd(command,None,None) - result.readLength() - response = result.read("!B")[0] - if response != command: - raise FatalTraCIError("Received answer %s for command %s." % (response, command)) - return result.readStringList() - - def setV2xMessage(self, message): - self._sendCmd(tc.CMD_SET_V2X, None, None, "s", message) - - def setOrder(self, order): - self._sendCmd(tc.CMD_SETORDER, None, None, "I", order) - - def close(self, wait=True): - for listenerID in list(self._stepListeners.keys()): - self.removeStepListener(listenerID) - if self._socket is not None: - self._sendCmd(tc.CMD_CLOSE, None, None) - self._socket.close() - self._socket = None - if wait and self._process is not None: - self._process.wait() - - -class StepListener(object): - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def step(self, t=0): - """step(int) -> bool - - After adding a StepListener 'listener' with traci.addStepListener(listener), - TraCI will call listener.step(t) after each call to traci.simulationStep(t) - The return value indicates whether the stepListener wants to stay active. - """ - return True - - def cleanUp(self): - """cleanUp() -> None - - This method is called at removal of the stepListener, allowing to schedule some final actions - """ - pass - - def setID(self, ID): - self._ID = ID - - def getID(self): - return self._ID diff --git a/co-simulation/bundle/src/assembly/resources/fed/ns3/constants.py b/co-simulation/bundle/src/assembly/resources/fed/ns3/constants.py deleted file mode 100644 index 38714578..00000000 --- a/co-simulation/bundle/src/assembly/resources/fed/ns3/constants.py +++ /dev/null @@ -1,1353 +0,0 @@ -# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo -# Copyright (C) 2009-2020 German Aerospace Center (DLR) and others. -# This program and the accompanying materials are made available under the -# terms of the Eclipse Public License 2.0 which is available at -# https://www.eclipse.org/legal/epl-2.0/ -# This Source Code may also be made available under the following Secondary -# Licenses when the conditions for such availability set forth in the Eclipse -# Public License 2.0 are satisfied: GNU General Public License, version 2 -# or later which is available at -# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html -# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later - -# @file constants.py -# @author generated by "rebuildConstants.py" -# @date 2020-11-11 13:02:15.607206 - -""" -This script contains TraCI constant definitions from /src/libsumo/TraCIConstants.h. -""" - - -# **************************************** -# VERSION -# **************************************** -TRACI_VERSION = 20 - -# **************************************** -# COMMANDS -# **************************************** -# command: get version -CMD_GETVERSION = 0x00 - -# command: load -CMD_LOAD = 0x01 - -# command: simulation step -CMD_SIMSTEP = 0x02 - -# command: set connection priority (execution order) -CMD_SETORDER = 0x03 - -# command: stop vehicle -CMD_STOP = 0x12 - -# command: reroute to parking area -CMD_REROUTE_TO_PARKING = 0xc2 - -# command: Resume from parking -CMD_RESUME = 0x19 - -# command: set lane -CMD_CHANGELANE = 0x13 - -# command: slow down -CMD_SLOWDOWN = 0x14 - -# command: set sublane (vehicle) -CMD_CHANGESUBLANE = 0x15 - -# command: open gap -CMD_OPENGAP = 0x16 - -# command: get V2X message -CMD_GET_V2X = 0x0d - -# command: set V2X message -CMD_SET_V2X = 0x2f - -# command: replace vehicle stop and updated route -CMD_REPLACE_STOP = 0x17 - -# command: retrieve information about the current taxi fleet and their status -VAR_TAXI_FLEET = 0x20 - -# command: send dispatch request for the given taxi -CMD_TAXI_DISPATCH = 0x21 - -# command: change target -CMD_CHANGETARGET = 0x31 - -# command: close sumo -CMD_CLOSE = 0x7F - -# command: add subscription filter -CMD_ADD_SUBSCRIPTION_FILTER = 0x7e - - -# command: subscribe induction loop (e1) context -CMD_SUBSCRIBE_INDUCTIONLOOP_CONTEXT = 0x80 -# response: subscribe induction loop (e1) context -RESPONSE_SUBSCRIBE_INDUCTIONLOOP_CONTEXT = 0x90 -# command: get induction loop (e1) variable -CMD_GET_INDUCTIONLOOP_VARIABLE = 0xa0 -# response: get induction loop (e1) variable -RESPONSE_GET_INDUCTIONLOOP_VARIABLE = 0xb0 -# command: set induction loop (e1) variable, not used yet -CMD_SET_INDUCTIONLOOP_VARIABLE = 0xc0 -# command: subscribe induction loop (e1) variable -CMD_SUBSCRIBE_INDUCTIONLOOP_VARIABLE = 0xd0 -# response: subscribe induction loop (e1) variable -RESPONSE_SUBSCRIBE_INDUCTIONLOOP_VARIABLE = 0xe0 - -# command: subscribe multi-entry/multi-exit detector (e3) context -CMD_SUBSCRIBE_MULTIENTRYEXIT_CONTEXT = 0x81 -# response: subscribe multi-entry/multi-exit detector (e3) context -RESPONSE_SUBSCRIBE_MULTIENTRYEXIT_CONTEXT = 0x91 -# command: get multi-entry/multi-exit detector (e3) variable -CMD_GET_MULTIENTRYEXIT_VARIABLE = 0xa1 -# response: get multi-entry/multi-exit detector (e3) variable -RESPONSE_GET_MULTIENTRYEXIT_VARIABLE = 0xb1 -# command: set multi-entry/multi-exit detector (e3) variable, not used yet -CMD_SET_MULTIENTRYEXIT_VARIABLE = 0xc1 -# command: subscribe multi-entry/multi-exit detector (e3) variable -CMD_SUBSCRIBE_MULTIENTRYEXIT_VARIABLE = 0xd1 -# response: subscribe multi-entry/multi-exit detector (e3) variable -RESPONSE_SUBSCRIBE_MULTIENTRYEXIT_VARIABLE = 0xe1 - -# command: subscribe traffic lights context -CMD_SUBSCRIBE_TL_CONTEXT = 0x82 -# response: subscribe traffic lights context -RESPONSE_SUBSCRIBE_TL_CONTEXT = 0x92 -# command: get traffic lights variable -CMD_GET_TL_VARIABLE = 0xa2 -# response: get traffic lights variable -RESPONSE_GET_TL_VARIABLE = 0xb2 -# command: set traffic lights variable -CMD_SET_TL_VARIABLE = 0xc2 -# command: subscribe traffic lights variable -CMD_SUBSCRIBE_TL_VARIABLE = 0xd2 -# response: subscribe traffic lights variable -RESPONSE_SUBSCRIBE_TL_VARIABLE = 0xe2 - -# command: subscribe lane context -CMD_SUBSCRIBE_LANE_CONTEXT = 0x83 -# response: subscribe lane context -RESPONSE_SUBSCRIBE_LANE_CONTEXT = 0x93 -# command: get lane variable -CMD_GET_LANE_VARIABLE = 0xa3 -# response: get lane variable -RESPONSE_GET_LANE_VARIABLE = 0xb3 -# command: set lane variable -CMD_SET_LANE_VARIABLE = 0xc3 -# command: subscribe lane variable -CMD_SUBSCRIBE_LANE_VARIABLE = 0xd3 -# response: subscribe lane variable -RESPONSE_SUBSCRIBE_LANE_VARIABLE = 0xe3 - -# command: subscribe vehicle context -CMD_SUBSCRIBE_VEHICLE_CONTEXT = 0x84 -# response: subscribe vehicle context -RESPONSE_SUBSCRIBE_VEHICLE_CONTEXT = 0x94 -# command: get vehicle variable -CMD_GET_VEHICLE_VARIABLE = 0xa4 -# response: get vehicle variable -RESPONSE_GET_VEHICLE_VARIABLE = 0xb4 -# command: set vehicle variable -CMD_SET_VEHICLE_VARIABLE = 0xc4 -# command: subscribe vehicle variable -CMD_SUBSCRIBE_VEHICLE_VARIABLE = 0xd4 -# response: subscribe vehicle variable -RESPONSE_SUBSCRIBE_VEHICLE_VARIABLE = 0xe4 - -# command: subscribe vehicle type context -CMD_SUBSCRIBE_VEHICLETYPE_CONTEXT = 0x85 -# response: subscribe vehicle type context -RESPONSE_SUBSCRIBE_VEHICLETYPE_CONTEXT = 0x95 -# command: get vehicle type variable -CMD_GET_VEHICLETYPE_VARIABLE = 0xa5 -# response: get vehicle type variable -RESPONSE_GET_VEHICLETYPE_VARIABLE = 0xb5 -# command: set vehicle type variable -CMD_SET_VEHICLETYPE_VARIABLE = 0xc5 -# command: subscribe vehicle type variable -CMD_SUBSCRIBE_VEHICLETYPE_VARIABLE = 0xd5 -# response: subscribe vehicle type variable -RESPONSE_SUBSCRIBE_VEHICLETYPE_VARIABLE = 0xe5 - -# command: subscribe route context -CMD_SUBSCRIBE_ROUTE_CONTEXT = 0x86 -# response: subscribe route context -RESPONSE_SUBSCRIBE_ROUTE_CONTEXT = 0x96 -# command: get route variable -CMD_GET_ROUTE_VARIABLE = 0xa6 -# response: get route variable -RESPONSE_GET_ROUTE_VARIABLE = 0xb6 -# command: set route variable -CMD_SET_ROUTE_VARIABLE = 0xc6 -# command: subscribe route variable -CMD_SUBSCRIBE_ROUTE_VARIABLE = 0xd6 -# response: subscribe route variable -RESPONSE_SUBSCRIBE_ROUTE_VARIABLE = 0xe6 - -# command: subscribe poi context -CMD_SUBSCRIBE_POI_CONTEXT = 0x87 -# response: subscribe poi context -RESPONSE_SUBSCRIBE_POI_CONTEXT = 0x97 -# command: get poi variable -CMD_GET_POI_VARIABLE = 0xa7 -# response: get poi variable -RESPONSE_GET_POI_VARIABLE = 0xb7 -# command: set poi variable -CMD_SET_POI_VARIABLE = 0xc7 -# command: subscribe poi variable -CMD_SUBSCRIBE_POI_VARIABLE = 0xd7 -# response: subscribe poi variable -RESPONSE_SUBSCRIBE_POI_VARIABLE = 0xe7 - -# command: subscribe polygon context -CMD_SUBSCRIBE_POLYGON_CONTEXT = 0x88 -# response: subscribe polygon context -RESPONSE_SUBSCRIBE_POLYGON_CONTEXT = 0x98 -# command: get polygon variable -CMD_GET_POLYGON_VARIABLE = 0xa8 -# response: get polygon variable -RESPONSE_GET_POLYGON_VARIABLE = 0xb8 -# command: set polygon variable -CMD_SET_POLYGON_VARIABLE = 0xc8 -# command: subscribe polygon variable -CMD_SUBSCRIBE_POLYGON_VARIABLE = 0xd8 -# response: subscribe polygon variable -RESPONSE_SUBSCRIBE_POLYGON_VARIABLE = 0xe8 - -# command: subscribe junction context -CMD_SUBSCRIBE_JUNCTION_CONTEXT = 0x89 -# response: subscribe junction context -RESPONSE_SUBSCRIBE_JUNCTION_CONTEXT = 0x99 -# command: get junction variable -CMD_GET_JUNCTION_VARIABLE = 0xa9 -# response: get junction variable -RESPONSE_GET_JUNCTION_VARIABLE = 0xb9 -# command: set junction variable -CMD_SET_JUNCTION_VARIABLE = 0xc9 -# command: subscribe junction variable -CMD_SUBSCRIBE_JUNCTION_VARIABLE = 0xd9 -# response: subscribe junction variable -RESPONSE_SUBSCRIBE_JUNCTION_VARIABLE = 0xe9 - -# command: subscribe edge context -CMD_SUBSCRIBE_EDGE_CONTEXT = 0x8a -# response: subscribe edge context -RESPONSE_SUBSCRIBE_EDGE_CONTEXT = 0x9a -# command: get edge variable -CMD_GET_EDGE_VARIABLE = 0xaa -# response: get edge variable -RESPONSE_GET_EDGE_VARIABLE = 0xba -# command: set edge variable -CMD_SET_EDGE_VARIABLE = 0xca -# command: subscribe edge variable -CMD_SUBSCRIBE_EDGE_VARIABLE = 0xda -# response: subscribe edge variable -RESPONSE_SUBSCRIBE_EDGE_VARIABLE = 0xea - -# command: subscribe simulation context -CMD_SUBSCRIBE_SIM_CONTEXT = 0x8b -# response: subscribe simulation context -RESPONSE_SUBSCRIBE_SIM_CONTEXT = 0x9b -# command: get simulation variable -CMD_GET_SIM_VARIABLE = 0xab -# response: get simulation variable -RESPONSE_GET_SIM_VARIABLE = 0xbb -# command: set simulation variable -CMD_SET_SIM_VARIABLE = 0xcb -# command: subscribe simulation variable -CMD_SUBSCRIBE_SIM_VARIABLE = 0xdb -# response: subscribe simulation variable -RESPONSE_SUBSCRIBE_SIM_VARIABLE = 0xeb - -# command: subscribe GUI context -CMD_SUBSCRIBE_GUI_CONTEXT = 0x8c -# response: subscribe GUI context -RESPONSE_SUBSCRIBE_GUI_CONTEXT = 0x9c -# command: get GUI variable -CMD_GET_GUI_VARIABLE = 0xac -# response: get GUI variable -RESPONSE_GET_GUI_VARIABLE = 0xbc -# command: set GUI variable -CMD_SET_GUI_VARIABLE = 0xcc -# command: subscribe GUI variable -CMD_SUBSCRIBE_GUI_VARIABLE = 0xdc -# response: subscribe GUI variable -RESPONSE_SUBSCRIBE_GUI_VARIABLE = 0xec - -# command: subscribe lane area detector (e2) context -CMD_SUBSCRIBE_LANEAREA_CONTEXT = 0x8d -# response: subscribe lane area detector (e2) context -RESPONSE_SUBSCRIBE_LANEAREA_CONTEXT = 0x9d -# command: get lane area detector (e2) variable -CMD_GET_LANEAREA_VARIABLE = 0xad -# response: get lane area detector (e2) variable -RESPONSE_GET_LANEAREA_VARIABLE = 0xbd -# command: set lane area detector (e2) variable, not used yet -CMD_SET_LANEAREA_VARIABLE = 0xcd -# command: subscribe lane area detector (e2) variable -CMD_SUBSCRIBE_LANEAREA_VARIABLE = 0xdd -# response: subscribe lane area detector (e2) variable -RESPONSE_SUBSCRIBE_LANEAREA_VARIABLE = 0xed - -# command: subscribe person context -CMD_SUBSCRIBE_PERSON_CONTEXT = 0x8e -# response: subscribe person context -RESPONSE_SUBSCRIBE_PERSON_CONTEXT = 0x9e -# command: get person variable -CMD_GET_PERSON_VARIABLE = 0xae -# response: get person variable -RESPONSE_GET_PERSON_VARIABLE = 0xbe -# command: set person variable -CMD_SET_PERSON_VARIABLE = 0xce -# command: subscribe person variable -CMD_SUBSCRIBE_PERSON_VARIABLE = 0xde -# response: subscribe person variable -RESPONSE_SUBSCRIBE_PERSON_VARIABLE = 0xee - -# command: subscribe busstop context -CMD_SUBSCRIBE_BUSSTOP_CONTEXT = 0x8f -# response: subscribe busstop context -RESPONSE_SUBSCRIBE_BUSSTOP_CONTEXT = 0x9f -# command: get busstop variable -CMD_GET_BUSSTOP_VARIABLE = 0xaf -# response: get busstop variable -RESPONSE_GET_BUSSTOP_VARIABLE = 0xbf -# command: set busstop variable, not used yet -CMD_SET_BUSSTOP_VARIABLE = 0xcf -# command: subscribe busstop variable -CMD_SUBSCRIBE_BUSSTOP_VARIABLE = 0xdf -# response: subscribe busstop variable -RESPONSE_SUBSCRIBE_BUSSTOP_VARIABLE = 0xef - -# command: subscribe parkingarea context -CMD_SUBSCRIBE_PARKINGAREA_CONTEXT = 0x04 -# response: subscribe parkingarea context -RESPONSE_SUBSCRIBE_PARKINGAREA_CONTEXT = 0x14 -# command: get parkingarea variable -CMD_GET_PARKINGAREA_VARIABLE = 0x24 -# response: get parkingarea variable -RESPONSE_GET_PARKINGAREA_VARIABLE = 0x34 -# command: set parkingarea variable -CMD_SET_PARKINGAREA_VARIABLE = 0x44 -# command: subscribe parkingarea variable -CMD_SUBSCRIBE_PARKINGAREA_VARIABLE = 0x54 -# response: subscribe parkingarea variable -RESPONSE_SUBSCRIBE_PARKINGAREA_VARIABLE = 0x64 - -# command: subscribe chargingstation context -CMD_SUBSCRIBE_CHARGINGSTATION_CONTEXT = 0x05 -# response: subscribe chargingstation context -RESPONSE_SUBSCRIBE_CHARGINGSTATION_CONTEXT = 0x15 -# command: get chargingstation variable -CMD_GET_CHARGINGSTATION_VARIABLE = 0x25 -# response: get chargingstation variable -RESPONSE_GET_CHARGINGSTATION_VARIABLE = 0x35 -# command: set chargingstation variable -CMD_SET_CHARGINGSTATION_VARIABLE = 0x45 -# command: subscribe chargingstation variable -CMD_SUBSCRIBE_CHARGINGSTATION_VARIABLE = 0x55 -# response: subscribe chargingstation variable -RESPONSE_SUBSCRIBE_CHARGINGSTATION_VARIABLE = 0x65 - -# command: subscribe routeprobe context -CMD_SUBSCRIBE_ROUTEPROBE_CONTEXT = 0x06 -# response: subscribe routeprobe context -RESPONSE_SUBSCRIBE_ROUTEPROBE_CONTEXT = 0x16 -# command: get routeprobe variable -CMD_GET_ROUTEPROBE_VARIABLE = 0x26 -# response: get routeprobe variable -RESPONSE_GET_ROUTEPROBE_VARIABLE = 0x36 -# command: set routeprobe variable -CMD_SET_ROUTEPROBE_VARIABLE = 0x46 -# command: subscribe routeprobe variable -CMD_SUBSCRIBE_ROUTEPROBE_VARIABLE = 0x56 -# response: subscribe routeprobe variable -RESPONSE_SUBSCRIBE_ROUTEPROBE_VARIABLE = 0x66 - -# command: subscribe calibrator context -CMD_SUBSCRIBE_CALIBRATOR_CONTEXT = 0x07 -# response: subscribe calibrator context -RESPONSE_SUBSCRIBE_CALIBRATOR_CONTEXT = 0x17 -# command: get calibrator variable -CMD_GET_CALIBRATOR_VARIABLE = 0x27 -# response: get calibrator variable -RESPONSE_GET_CALIBRATOR_VARIABLE = 0x37 -# command: set calibrator variable -CMD_SET_CALIBRATOR_VARIABLE = 0x47 -# command: subscribe calibrator variable -CMD_SUBSCRIBE_CALIBRATOR_VARIABLE = 0x57 -# response: subscribe calibrator variable -RESPONSE_SUBSCRIBE_CALIBRATOR_VARIABLE = 0x67 - -# command: subscribe rerouter context -CMD_SUBSCRIBE_REROUTER_CONTEXT = 0x08 -# response: subscribe rerouter context -RESPONSE_SUBSCRIBE_REROUTER_CONTEXT = 0x18 -# command: get rerouter variable -CMD_GET_REROUTER_VARIABLE = 0x28 -# response: get rerouter variable -RESPONSE_GET_REROUTER_VARIABLE = 0x38 -# command: set rerouter variable -CMD_SET_REROUTER_VARIABLE = 0x48 -# command: subscribe rerouter variable -CMD_SUBSCRIBE_REROUTER_VARIABLE = 0x58 -# response: subscribe rerouter variable -RESPONSE_SUBSCRIBE_REROUTER_VARIABLE = 0x68 - -# command: subscribe variablespeedsign context -CMD_SUBSCRIBE_VARIABLESPEEDSIGN_CONTEXT = 0x09 -# response: subscribe variablespeedsign context -RESPONSE_SUBSCRIBE_VARIABLESPEEDSIGN_CONTEXT = 0x19 -# command: get variablespeedsign variable -CMD_GET_VARIABLESPEEDSIGN_VARIABLE = 0x29 -# response: get variablespeedsign variable -RESPONSE_GET_VARIABLESPEEDSIGN_VARIABLE = 0x39 -# command: set variablespeedsign variable -CMD_SET_VARIABLESPEEDSIGN_VARIABLE = 0x49 -# command: subscribe variablespeedsign variable -CMD_SUBSCRIBE_VARIABLESPEEDSIGN_VARIABLE = 0x59 -# response: subscribe variablespeedsign variable -RESPONSE_SUBSCRIBE_VARIABLESPEEDSIGN_VARIABLE = 0x69 - -# command: subscribe meandata context -CMD_SUBSCRIBE_MEANDATA_CONTEXT = 0x0a -# response: subscribe meandata context -RESPONSE_SUBSCRIBE_MEANDATA_CONTEXT = 0x1a -# command: get meandata variable -CMD_GET_MEANDATA_VARIABLE = 0x2a -# response: get meandata variable -RESPONSE_GET_MEANDATA_VARIABLE = 0x3a -# command: set meandata variable, not used yet -CMD_SET_MEANDATA_VARIABLE = 0x4a -# command: subscribe meandata variable -CMD_SUBSCRIBE_MEANDATA_VARIABLE = 0x5a -# response: subscribe meandata variable -RESPONSE_SUBSCRIBE_MEANDATA_VARIABLE = 0x6a - -# command: subscribe overheadwire context -CMD_SUBSCRIBE_OVERHEADWIRE_CONTEXT = 0x0b -# response: subscribe overheadwire context -RESPONSE_SUBSCRIBE_OVERHEADWIRE_CONTEXT = 0x1b -# command: get overheadwire variable -CMD_GET_OVERHEADWIRE_VARIABLE = 0x2b -# response: get overheadwire variable -RESPONSE_GET_OVERHEADWIRE_VARIABLE = 0x3b -# command: set overheadwire variable -CMD_SET_OVERHEADWIRE_VARIABLE = 0x4b -# command: subscribe overheadwire variable -CMD_SUBSCRIBE_OVERHEADWIRE_VARIABLE = 0x5b -# response: subscribe overheadwire variable -RESPONSE_SUBSCRIBE_OVERHEADWIRE_VARIABLE = 0x6b - - -# **************************************** -# POSITION REPRESENTATIONS -# **************************************** -# Position in geo-coordinates -POSITION_LON_LAT = 0x00 -# 2D cartesian coordinates -POSITION_2D = 0x01 -# Position in geo-coordinates with altitude -POSITION_LON_LAT_ALT = 0x02 -# 3D cartesian coordinates -POSITION_3D = 0x03 -# Position on road map -POSITION_ROADMAP = 0x04 - - -# **************************************** -# DATA TYPES -# **************************************** -# Polygon (2*n doubles) -TYPE_POLYGON = 0x06 -# unsigned byte -TYPE_UBYTE = 0x07 -# signed byte -TYPE_BYTE = 0x08 -# 32 bit signed integer -TYPE_INTEGER = 0x09 -# double precision float -TYPE_DOUBLE = 0x0B -# 8 bit ASCII string -TYPE_STRING = 0x0C -# list of strings -TYPE_STRINGLIST = 0x0E -# compound object -TYPE_COMPOUND = 0x0F -# list of double precision floats -TYPE_DOUBLELIST = 0x10 -# color (four ubytes) -TYPE_COLOR = 0x11 - - -# **************************************** -# RESULT TYPES -# **************************************** -# result type: Ok -RTYPE_OK = 0x00 -# result type: not implemented -RTYPE_NOTIMPLEMENTED = 0x01 -# result type: error -RTYPE_ERR = 0xFF - -# **************************************** -# special return or parameter values -# **************************************** -# return value for invalid queries (especially vehicle is not on the road), see Position::INVALID -INVALID_DOUBLE_VALUE = -1073741824.0 -# return value for invalid queries (especially vehicle is not on the road), see Position::INVALID -INVALID_INT_VALUE = -1073741824 -# maximum value for client ordering (2 ^ 30) -MAX_ORDER = 1073741824 - - -# **************************************** -# DIFFERENT DISTANCE REQUESTS -# **************************************** -# air distance -REQUEST_AIRDIST = 0x00 -# driving distance -REQUEST_DRIVINGDIST = 0x01 - - -# **************************************** -# VEHICLE REMOVAL REASONS -# **************************************** -# vehicle started teleport -REMOVE_TELEPORT = 0x00 -# vehicle removed while parking -REMOVE_PARKING = 0x01 -# vehicle arrived -REMOVE_ARRIVED = 0x02 -# vehicle was vaporized -REMOVE_VAPORIZED = 0x03 -# vehicle finished route during teleport -REMOVE_TELEPORT_ARRIVED = 0x04 - -# **************************************** -# VEHICLE MOVE REASONS -# **************************************** -# infer reason from move distance -MOVE_AUTOMATIC = 0x00 -# vehicle teleports to another location -MOVE_TELEPORT = 0x01 -# vehicle moved normally -MOVE_NORMAL = 0x02 - -# **************************************** -# PERSON/CONTAINER STAGES -# **************************************** -# person / container stopping -STAGE_WAITING_FOR_DEPART = 0x00 -# person / container stopping -STAGE_WAITING = 0x01 -# person walking -STAGE_WALKING = 0x02 -# person riding / container being transported -STAGE_DRIVING = 0x03 -# person accessing stopping place -STAGE_ACCESS = 0x04 -# stage for encoding abstract travel demand -STAGE_TRIP = 0x05 -# person / container transhiping -STAGE_TRANSHIP = 0x06 - -# **************************************** -# Stop Flags -# **************************************** -STOP_DEFAULT = 0x00 -STOP_PARKING = 0x01 -STOP_TRIGGERED = 0x02 -STOP_CONTAINER_TRIGGERED = 0x04 -STOP_BUS_STOP = 0x08 -STOP_CONTAINER_STOP = 0x10 -STOP_CHARGING_STATION = 0x20 -STOP_PARKING_AREA = 0x40 -STOP_OVERHEAD_WIRE = 0x80 - -# **************************************** -# Departure Flags -# **************************************** -DEPARTFLAG_TRIGGERED = -0x01 -DEPARTFLAG_CONTAINER_TRIGGERED = -0x02 -DEPARTFLAG_NOW = -0x03 - -DEPARTFLAG_SPEED_RANDOM = -0x02 -DEPARTFLAG_SPEED_MAX = -0x03 - -DEPARTFLAG_LANE_RANDOM = -0x02 -DEPARTFLAG_LANE_FREE = -0x03 -DEPARTFLAG_LANE_ALLOWED_FREE = -0x04 -DEPARTFLAG_LANE_BEST_FREE = -0x05 -DEPARTFLAG_LANE_FIRST_ALLOWED = -0x06 - -DEPARTFLAG_POS_RANDOM = -0x02 -DEPARTFLAG_POS_FREE = -0x03 -DEPARTFLAG_POS_BASE = -0x04 -DEPARTFLAG_POS_LAST = -0x05 -DEPARTFLAG_POS_RANDOM_FREE = -0x06 - -ARRIVALFLAG_LANE_CURRENT = -0x02 -ARRIVALFLAG_SPEED_CURRENT = -0x02 - -ARRIVALFLAG_POS_RANDOM = -0x02 -ARRIVALFLAG_POS_MAX = -0x03 - -# **************************************** -# Routing modes -# **************************************** -# use custom weights if available, fall back to loaded weights and then to free-flow speed -ROUTING_MODE_DEFAULT = 0x00 -# use aggregated travel times from device.rerouting -ROUTING_MODE_AGGREGATED = 0x01 -# use loaded efforts -ROUTING_MODE_EFFORT = 0x02 -# use combined costs -ROUTING_MODE_COMBINED = 0x03 - -# **************************************** -# Traffic light types -# **************************************** -TRAFFICLIGHT_TYPE_STATIC = 0x00 -TRAFFICLIGHT_TYPE_ACTUATED = 0x03 -TRAFFICLIGHT_TYPE_DELAYBASED = 0x04 - -# **************************************** -# FILTER TYPES (for context subscription filters) -# **************************************** - -# Reset all filters -FILTER_TYPE_NONE = 0x00 - -# Filter by list of lanes relative to ego vehicle -FILTER_TYPE_LANES = 0x01 - -# Exclude vehicles on opposite (and other) lanes from context subscription result -FILTER_TYPE_NOOPPOSITE = 0x02 - -# Specify maximal downstream distance for vehicles in context subscription result -FILTER_TYPE_DOWNSTREAM_DIST = 0x03 - -# Specify maximal upstream distance for vehicles in context subscription result -FILTER_TYPE_UPSTREAM_DIST = 0x04 - -# Only return leader and follower on the specified lanes in context subscription result -FILTER_TYPE_LEAD_FOLLOW = 0x05 - -# Only return foes on upcoming junction in context subscription result -FILTER_TYPE_TURN = 0x07 - -# Only return vehicles of the given vClass in context subscription result -FILTER_TYPE_VCLASS = 0x08 - -# Only return vehicles of the given vType in context subscription result -FILTER_TYPE_VTYPE = 0x09 - -# Only return vehicles within field of vision in context subscription result -FILTER_TYPE_FIELD_OF_VISION = 0x0A - -# Only return vehicles within the given lateral distance in context subscription result -FILTER_TYPE_LATERAL_DIST = 0x0B - -# **************************************** -# VARIABLE TYPES (for CMD_GET_*_VARIABLE) -# **************************************** -# list of instances' ids (get: all) -TRACI_ID_LIST = 0x00 - -# count of instances (get: all) -ID_COUNT = 0x01 - -# subscribe object variables (get: all) -AUTOMATIC_VARIABLES_SUBSCRIPTION = 0x02 - -# subscribe context variables (get: all) -AUTOMATIC_CONTEXT_SUBSCRIPTION = 0x03 - -# generic attributes (get/set: all) -GENERIC_ATTRIBUTE = 0x03 - -# last step vehicle number (get: induction loops, multi-entry/multi-exit detector, lanes, edges) -LAST_STEP_VEHICLE_NUMBER = 0x10 - -# last step vehicle number (get: induction loops, multi-entry/multi-exit detector, lanes, edges) -LAST_STEP_MEAN_SPEED = 0x11 - -# last step vehicle list (get: induction loops, multi-entry/multi-exit detector, lanes, edges) -LAST_STEP_VEHICLE_ID_LIST = 0x12 - -# last step occupancy (get: induction loops, lanes, edges) -LAST_STEP_OCCUPANCY = 0x13 - -# last step vehicle halting number (get: multi-entry/multi-exit detector, lanes, edges) -LAST_STEP_VEHICLE_HALTING_NUMBER = 0x14 - -# last step mean vehicle length (get: induction loops, lanes, edges) -LAST_STEP_LENGTH = 0x15 - -# last step time since last detection (get: induction loops) -LAST_STEP_TIME_SINCE_DETECTION = 0x16 - -# entry times -LAST_STEP_VEHICLE_DATA = 0x17 - -# last step jam length in vehicles -JAM_LENGTH_VEHICLE = 0x18 - -# last step jam length in meters -JAM_LENGTH_METERS = 0x19 - -# last step person list (get: edges, vehicles) -LAST_STEP_PERSON_ID_LIST = 0x1a - -# full name (get: edges, simulation, trafficlight) -VAR_NAME = 0x1b - -# carFollowModel function followSpeed (get: vehicle) -VAR_FOLLOW_SPEED = 0x1c - -# carFollowModel function stopSpeed (get: vehicle) -VAR_STOP_SPEED = 0x1d - -# carFollowModel function getSecureGap (get: vehicle) -VAR_SECURE_GAP = 0x1e - -# estimated (depart) delay for next stop -VAR_STOP_DELAY = 0x1f - -# estimated arrival delay for next stop -VAR_STOP_ARRIVALDELAY = 0x22 - -# begin time(get: calibrator) -VAR_BEGIN = 0x1c - -# end time(get: calibrator) -VAR_END = 0x1d - -# vtype list (get: calibrator) -VAR_VTYPES = 0x1e - -# vehicles per hour (get: calibrator) -VAR_VEHSPERHOUR = 0x13 - -# passed vehicle count (get: calibrator) -VAR_PASSED = 0x14 - -# inserted vehicle count (get: calibrator) -VAR_INSERTED = 0x15 - -# removed vehicle count (get: calibrator) -VAR_REMOVED = 0x16 - -# routeProbe id (get: calibrator) -VAR_ROUTE_PROBE = 0x17 - -# routeProbe id (get: calibrator) -CMD_SET_FLOW = 0x18 - -# traffic light states, encoded as rRgGyYoO tuple (get: traffic lights) -TL_RED_YELLOW_GREEN_STATE = 0x20 - -# index of the phase (set: traffic lights) -TL_PHASE_INDEX = 0x22 - -# traffic light program (set: traffic lights) -TL_PROGRAM = 0x23 - -# phase duration (set: traffic lights) -TL_PHASE_DURATION = 0x24 - -# vehicles that block passing the given signal (get: traffic lights) -TL_BLOCKING_VEHICLES = 0x25 - -# controlled lanes (get: traffic lights) -TL_CONTROLLED_LANES = 0x26 - -# controlled links (get: traffic lights) -TL_CONTROLLED_LINKS = 0x27 - -# index of the current phase (get: traffic lights) -TL_CURRENT_PHASE = 0x28 - -# name of the current program (get: traffic lights) -TL_CURRENT_PROGRAM = 0x29 - -# vehicles that also wish to pass the given signal (get: traffic lights) -TL_RIVAL_VEHICLES = 0x30 - -# vehicles that also wish to pass the given signal and have higher priority (get: traffic lights) -TL_PRIORITY_VEHICLES = 0x31 - -# controlled junctions (get: traffic lights) -TL_CONTROLLED_JUNCTIONS = 0x2a - -# complete definition (get: traffic lights) -TL_COMPLETE_DEFINITION_RYG = 0x2b - -# complete program (set: traffic lights) -TL_COMPLETE_PROGRAM_RYG = 0x2c - -# assumed time to next switch (get: traffic lights) -TL_NEXT_SWITCH = 0x2d - -# current state, using external signal names (get: traffic lights) -TL_EXTERNAL_STATE = 0x2e - -# outgoing link number (get: lanes) -LANE_LINK_NUMBER = 0x30 - -# id of parent edge (get: lanes) -LANE_EDGE_ID = 0x31 - -# outgoing link definitions (get: lanes) -LANE_LINKS = 0x33 - -# list of allowed vehicle classes (get&set: lanes) -LANE_ALLOWED = 0x34 - -# list of not allowed vehicle classes (get&set: lanes) -LANE_DISALLOWED = 0x35 - -# list of foe lanes (get: lanes) -VAR_FOES = 0x37 - -# slope (get: edge, lane, vehicle, person) -VAR_SLOPE = 0x36 - -# speed (get: vehicle) -VAR_SPEED = 0x40 - -# adapt previous speed (set: vehicle) -VAR_PREV_SPEED = 0x3c - -# lateral speed (get: vehicle) -VAR_SPEED_LAT = 0x32 - -# maximum allowed/possible speed (get: vehicle types, lanes, set: edges, lanes) -VAR_MAXSPEED = 0x41 - -# position (2D) (get: vehicle, poi, inductionloop, lane area detector; set: poi) -VAR_POSITION = 0x42 - -# position (3D) (get: vehicle, poi, set: poi) -VAR_POSITION3D = 0x39 - -# angle (get: vehicle, poi; set: poi) -VAR_ANGLE = 0x43 - -# length (get: vehicle types, lanes, lane area detector, set: lanes) -VAR_LENGTH = 0x44 - -# color (get: vehicles, vehicle types, polygons, pois) -VAR_COLOR = 0x45 - -# max. acceleration (get: vehicles, vehicle types) -VAR_ACCEL = 0x46 - -# max. comfortable deceleration (get: vehicles, vehicle types) -VAR_DECEL = 0x47 - -# max. (physically possible) deceleration (get: vehicles, vehicle types) -VAR_EMERGENCY_DECEL = 0x7b - -# apparent deceleration (get: vehicles, vehicle types) -VAR_APPARENT_DECEL = 0x7c - -# action step length (get: vehicles, vehicle types) -VAR_ACTIONSTEPLENGTH = 0x7d - -# last action time (get: vehicles) -VAR_LASTACTIONTIME = 0x7f - -# driver's desired headway (get: vehicle types) -VAR_TAU = 0x48 - -# vehicle class (get: vehicle types) -VAR_VEHICLECLASS = 0x49 - -# emission class (get: vehicle types) -VAR_EMISSIONCLASS = 0x4a - -# shape class (get: vehicle types) -VAR_SHAPECLASS = 0x4b - -# minimum gap (get: vehicle types) -VAR_MINGAP = 0x4c - -# width (get: vehicle types, lanes, polygons, poi) -VAR_WIDTH = 0x4d - -# shape (get: polygons) -VAR_SHAPE = 0x4e - -# type id (get: vehicles, polygons, pois) -VAR_TYPE = 0x4f - -# road id (get: vehicles) -VAR_ROAD_ID = 0x50 - -# lane id (get: vehicles, inductionloop, lane area detector) -VAR_LANE_ID = 0x51 - -# lane index (get: vehicle, edge) -VAR_LANE_INDEX = 0x52 - -# route id (get & set: vehicles) -VAR_ROUTE_ID = 0x53 - -# edges (get: routes, vehicles) -VAR_EDGES = 0x54 - -# lanes (get: variablespeedsign) -VAR_LANES = 0x30 - -# update bestLanes (set: vehicle) -VAR_UPDATE_BESTLANES = 0x6a - -# filled? (get: polygons) -VAR_FILL = 0x55 - -# get/set image file (poi, poly, vehicle, person, simulation) -VAR_IMAGEFILE = 0x93 - -# position (1D along lane) (get: vehicle) -VAR_LANEPOSITION = 0x56 - -# route (set: vehicles) -VAR_ROUTE = 0x57 - -# travel time information (get&set: vehicle) -VAR_EDGE_TRAVELTIME = 0x58 - -# effort information (get&set: vehicle) -VAR_EDGE_EFFORT = 0x59 - -# last step travel time (get: edge, lane) -VAR_CURRENT_TRAVELTIME = 0x5a - -# signals state (get/set: vehicle) -VAR_SIGNALS = 0x5b - -# vehicle: new lane/position along (set: vehicle) -VAR_MOVE_TO = 0x5c - -# polygon: add dynamics (set: polygon) -VAR_ADD_DYNAMICS = 0x5c - -# vehicle: highlight (set: vehicle, poi) -VAR_HIGHLIGHT = 0x6c - -# driver imperfection (set: vehicle) -VAR_IMPERFECTION = 0x5d - -# speed factor (set: vehicle) -VAR_SPEED_FACTOR = 0x5e - -# speed deviation (set: vehicle) -VAR_SPEED_DEVIATION = 0x5f - -# routing mode (get/set: vehicle) -VAR_ROUTING_MODE = 0x89 - -# speed without TraCI influence (get: vehicle) -VAR_SPEED_WITHOUT_TRACI = 0xb1 - -# best lanes (get: vehicle) -VAR_BEST_LANES = 0xb2 - -# how speed is set (set: vehicle) -VAR_SPEEDSETMODE = 0xb3 - -# move vehicle to explicit (remote controlled) position (set: vehicle) -MOVE_TO_XY = 0xb4 - -# is the vehicle stopped, and if so parked and/or triggered? -# value = stopped + 2 * parking + 4 * triggered -VAR_STOPSTATE = 0xb5 - -# how lane changing is performed (get/set: vehicle) -VAR_LANECHANGE_MODE = 0xb6 - -# maximum speed regarding max speed on the current lane and speed factor (get: vehicle) -VAR_ALLOWED_SPEED = 0xb7 - -# position (1D lateral position relative to center of the current lane) (get: vehicle) -VAR_LANEPOSITION_LAT = 0xb8 - -# get/set prefered lateral alignment within the lane (vehicle) -VAR_LATALIGNMENT = 0xb9 - -# get/set maximum lateral speed (vehicle, vtypes) -VAR_MAXSPEED_LAT = 0xba - -# get/set minimum lateral gap (vehicle, vtypes) -VAR_MINGAP_LAT = 0xbb - -# get/set vehicle height (vehicle, vtypes, poi) -VAR_HEIGHT = 0xbc - -# get/set vehicle line -VAR_LINE = 0xbd - -# get/set vehicle via -VAR_VIA = 0xbe - -# get (lane change relevant) neighboring vehicles (vehicles) -VAR_NEIGHBORS = 0xbf - -# current CO2 emission of a node (get: vehicle, lane, edge) -VAR_CO2EMISSION = 0x60 - -# current CO emission of a node (get: vehicle, lane, edge) -VAR_COEMISSION = 0x61 - -# current HC emission of a node (get: vehicle, lane, edge) -VAR_HCEMISSION = 0x62 - -# current PMx emission of a node (get: vehicle, lane, edge) -VAR_PMXEMISSION = 0x63 - -# current NOx emission of a node (get: vehicle, lane, edge) -VAR_NOXEMISSION = 0x64 - -# current fuel consumption of a node (get: vehicle, lane, edge) -VAR_FUELCONSUMPTION = 0x65 - -# current noise emission of a node (get: vehicle, lane, edge) -VAR_NOISEEMISSION = 0x66 - -# current person number (get: vehicle, trafficlight) -VAR_PERSON_NUMBER = 0x67 - -# person capacity (vehicle , vehicle type) -VAR_PERSON_CAPACITY = 0x38 - -VAR_BUS_STOP_ID_LIST = 0x9f - -# number of persons waiting at a defined bus stop (get: simulation) -VAR_BUS_STOP_WAITING = 0x67 - -# ids of persons waiting at a defined bus stop (get: simulation) -VAR_BUS_STOP_WAITING_IDS = 0xef - -# current leader together with gap (get: vehicle) -VAR_LEADER = 0x68 - -# current leader together with gap (get: vehicle) -VAR_FOLLOWER = 0x78 - -# edge index in current route (get: vehicle) -VAR_ROUTE_INDEX = 0x69 - -# current waiting time (get: vehicle, lane) -VAR_WAITING_TIME = 0x7a - -# current waiting time (get: vehicle) -VAR_ACCUMULATED_WAITING_TIME = 0x87 - -# upcoming traffic lights (get: vehicle) -VAR_NEXT_TLS = 0x70 - -# upcoming stops (get: vehicle) -VAR_NEXT_STOPS = 0x73 - -# upcoming stops with selection (get: vehicle) -VAR_NEXT_STOPS2 = 0x74 - -# current acceleration (get: vehicle) -VAR_ACCELERATION = 0x72 - -# arrival position (get,set: vehicle) -VAR_ARRIVALPOS = 0x75 - -# arrival lane (get,set: vehicle) -VAR_ARRIVALLANE = 0x76 - -# arrival speed (get,set: vehicle) -VAR_ARRIVALSPEED = 0x77 - -# add log message (set: simulation) -CMD_MESSAGE = 0x65 - -# current time in seconds (get: simulation) -VAR_TIME = 0x66 - -# current time step (get: simulation) -VAR_TIME_STEP = 0x70 - -# current electricity consumption of a node (get: vehicle, lane, edge) -VAR_ELECTRICITYCONSUMPTION = 0x71 - -# number of loaded vehicles (get: simulation) -VAR_LOADED_VEHICLES_NUMBER = 0x71 - -# loaded vehicle ids (get: simulation) -VAR_LOADED_VEHICLES_IDS = 0x72 - -# number of departed vehicle (get: simulation) -VAR_DEPARTED_VEHICLES_NUMBER = 0x73 - -# departed vehicle ids (get: simulation) -VAR_DEPARTED_VEHICLES_IDS = 0x74 - -# number of vehicles starting to teleport (get: simulation) -VAR_TELEPORT_STARTING_VEHICLES_NUMBER = 0x75 - -# ids of vehicles starting to teleport (get: simulation) -VAR_TELEPORT_STARTING_VEHICLES_IDS = 0x76 - -# number of vehicles ending to teleport (get: simulation) -VAR_TELEPORT_ENDING_VEHICLES_NUMBER = 0x77 - -# ids of vehicles ending to teleport (get: simulation) -VAR_TELEPORT_ENDING_VEHICLES_IDS = 0x78 - -# number of arrived vehicles (get: simulation) -VAR_ARRIVED_VEHICLES_NUMBER = 0x79 - -# ids of arrived vehicles (get: simulation) -VAR_ARRIVED_VEHICLES_IDS = 0x7a - -# delta t (get: simulation) -VAR_DELTA_T = 0x7b - -# bounding box (get: simulation) -VAR_NET_BOUNDING_BOX = 0x7c - -# minimum number of expected vehicles (get: simulation) -VAR_MIN_EXPECTED_VEHICLES = 0x7d - -# number of vehicles starting to park (get: simulation) -VAR_STOP_STARTING_VEHICLES_NUMBER = 0x68 - -# ids of vehicles starting to park (get: simulation) -VAR_STOP_STARTING_VEHICLES_IDS = 0x69 - -# number of vehicles ending to park (get: simulation) -VAR_STOP_ENDING_VEHICLES_NUMBER = 0x6a - -# ids of vehicles ending to park (get: simulation) -VAR_STOP_ENDING_VEHICLES_IDS = 0x6b - -# number of vehicles starting to park (get: simulation) -VAR_PARKING_STARTING_VEHICLES_NUMBER = 0x6c - -# ids of vehicles starting to park (get: simulation) -VAR_PARKING_STARTING_VEHICLES_IDS = 0x6d - -# number of vehicles maneuvering (get: simulation) -VAR_PARKING_MANEUVERING_VEHICLES_NUMBER = 0x3a - -# ids of vehicles maneuvering (get: simulation) -VAR_PARKING_MANEUVERING_VEHICLES_IDS = 0x3b - -# number of vehicles ending to park (get: simulation) -VAR_PARKING_ENDING_VEHICLES_NUMBER = 0x6e - -# ids of vehicles ending to park (get: simulation) -VAR_PARKING_ENDING_VEHICLES_IDS = 0x6f - -# number of vehicles involved in a collision (get: simulation) -VAR_COLLIDING_VEHICLES_NUMBER = 0x80 - -# ids of vehicles involved in a collision (get: simulation) -VAR_COLLIDING_VEHICLES_IDS = 0x81 - -# number of vehicles involved in a collision (get: simulation) -VAR_EMERGENCYSTOPPING_VEHICLES_NUMBER = 0x89 - -# ids of vehicles involved in a collision (get: simulation) -VAR_EMERGENCYSTOPPING_VEHICLES_IDS = 0x8a - -# clears the simulation of all not inserted vehicles (set: simulation) -CMD_CLEAR_PENDING_VEHICLES = 0x94 - -# triggers saving simulation state (set: simulation) -CMD_SAVE_SIMSTATE = 0x95 - -# triggers saving simulation state (set: simulation) -CMD_LOAD_SIMSTATE = 0x96 - -# sets/retrieves abstract parameter -VAR_PARAMETER = 0x7e - -# retrieves abstract parameter and returns (key, value) tuple -VAR_PARAMETER_WITH_KEY = 0x3e - - -# add an instance (poi, polygon, vehicle, person, route) -ADD = 0x80 - -# remove an instance (poi, polygon, vehicle, person) -REMOVE = 0x81 - -# copy an instance (vehicle type, other TBD.) -COPY = 0x88 - -# convert coordinates -POSITION_CONVERSION = 0x82 - -# distance between points or vehicles -DISTANCE_REQUEST = 0x83 - -# the current driving distance -VAR_DISTANCE = 0x84 - -# add a fully specified instance (vehicle) -ADD_FULL = 0x85 - -# find a car based route -FIND_ROUTE = 0x86 - -# find an intermodal route -FIND_INTERMODAL_ROUTE = 0x87 - -# force rerouting based on travel time (vehicles) -CMD_REROUTE_TRAVELTIME = 0x90 - -# force rerouting based on effort (vehicles) -CMD_REROUTE_EFFORT = 0x91 - -# validates current route (vehicles) -VAR_ROUTE_VALID = 0x92 - -# retrieve information regarding the current person/container stage -VAR_STAGE = 0xc0 - -# retrieve information regarding the next edge including crossings and walkingAreas (pedestrians only) -VAR_NEXT_EDGE = 0xc1 - -# retrieve information regarding the number of remaining stages -VAR_STAGES_REMAINING = 0xc2 - -# retrieve the current vehicle id for the driving stage (person, container) -VAR_VEHICLE = 0xc3 - -# append a person stage (person) -APPEND_STAGE = 0xc4 - -# replace a person stage (person) -REPLACE_STAGE = 0xcd - -# append a person stage (person) -REMOVE_STAGE = 0xc5 - -# retrieve taxi reservation (person) -VAR_TAXI_RESERVATIONS = 0xc6 - -# sample last route (routeprobe) -VAR_SAMPLE_LAST = 0x20 - -# sample current route (routeprobe) -VAR_SAMPLE_CURRENT = 0x21 - -# zoom -VAR_VIEW_ZOOM = 0xa0 - -# view position -VAR_VIEW_OFFSET = 0xa1 - -# view schema -VAR_VIEW_SCHEMA = 0xa2 - -# view by boundary -VAR_VIEW_BOUNDARY = 0xa3 - -# select/deselect object (gui) -VAR_SELECT = 0xa4 - -# screenshot -VAR_SCREENSHOT = 0xa5 - -# track vehicle -VAR_TRACK_VEHICLE = 0xa6 - -# presence of view -VAR_HAS_VIEW = 0xa7 - -# @name currently wanted lane-change action -# @{ -# @brief No action desired -LCA_NONE = 0 -# @brief Needs to stay on the current lane -LCA_STAY = 1 << 0 -# @brief Wants go to the left -LCA_LEFT = 1 << 1 -# @brief Wants go to the right -LCA_RIGHT = 1 << 2 -# @brief The action is needed to follow the route (navigational lc) -LCA_STRATEGIC = 1 << 3 -# @brief The action is done to help someone else -LCA_COOPERATIVE = 1 << 4 -# @brief The action is due to the wish to be faster (tactical lc) -LCA_SPEEDGAIN = 1 << 5 -# @brief The action is due to the default of keeping right "Rechtsfahrgebot" -LCA_KEEPRIGHT = 1 << 6 -# @brief The action is due to a TraCI request -LCA_TRACI = 1 << 7 -# @brief The action is urgent (to be defined by lc-model) -LCA_URGENT = 1 << 8 -# @brief The action has not been determined -LCA_UNKNOWN = 1 << 30 -# @} - -# @name External state -# @{ -# @brief The vehicle is blocked by left leader -LCA_BLOCKED_BY_LEFT_LEADER = 1 << 9 -# @brief The vehicle is blocked by left follower -LCA_BLOCKED_BY_LEFT_FOLLOWER = 1 << 10 -# @brief The vehicle is blocked by right leader -LCA_BLOCKED_BY_RIGHT_LEADER = 1 << 11 -# @brief The vehicle is blocked by right follower -LCA_BLOCKED_BY_RIGHT_FOLLOWER = 1 << 12 -# @brief The vehicle is blocked being overlapping -LCA_OVERLAPPING = 1 << 13 -# @brief The vehicle does not have enough space to complete a continuous change before the next turn -LCA_INSUFFICIENT_SPACE = 1 << 14 -# @brief used by the sublane model -LCA_SUBLANE = 1 << 15 -# @brief Vehicle is too slow to complete a continuous lane change (in case that maxSpeedLatStanding==0) -LCA_INSUFFICIENT_SPEED = 1 << 28 -# @brief lane can change -LCA_WANTS_LANECHANGE = LCA_LEFT | LCA_RIGHT -# @brief lane can change or stay -LCA_WANTS_LANECHANGE_OR_STAY = LCA_WANTS_LANECHANGE | LCA_STAY -# @brief blocked left -LCA_BLOCKED_LEFT = LCA_BLOCKED_BY_LEFT_LEADER | LCA_BLOCKED_BY_LEFT_FOLLOWER -# @brief blocked right -LCA_BLOCKED_RIGHT = LCA_BLOCKED_BY_RIGHT_LEADER | LCA_BLOCKED_BY_RIGHT_FOLLOWER -# @brief blocked by leader -LCA_BLOCKED_BY_LEADER = LCA_BLOCKED_BY_LEFT_LEADER | LCA_BLOCKED_BY_RIGHT_LEADER -# @brief blocker by follower -LCA_BLOCKED_BY_FOLLOWER = LCA_BLOCKED_BY_LEFT_FOLLOWER | LCA_BLOCKED_BY_RIGHT_FOLLOWER -# @brief blocked in all directions -LCA_BLOCKED = LCA_BLOCKED_LEFT | LCA_BLOCKED_RIGHT | LCA_INSUFFICIENT_SPACE | LCA_INSUFFICIENT_SPEED -# @brief reasons of lane change -LCA_CHANGE_REASONS = (LCA_STRATEGIC | LCA_COOPERATIVE | LCA_SPEEDGAIN | LCA_KEEPRIGHT | LCA_SUBLANE | LCA_TRACI) -# LCA_BLOCKED_BY_CURRENT_LEADER = 1 << 28 -# LCA_BLOCKED_BY_CURRENT_FOLLOWER = 1 << 29 -# @} - -# @name originally model specific states (migrated here since -# they were duplicated in all current models) -# @{ -LCA_AMBLOCKINGLEADER = 1 << 16 -LCA_AMBLOCKINGFOLLOWER = 1 << 17 -LCA_MRIGHT = 1 << 18 -LCA_MLEFT = 1 << 19 -# !!! never set LCA_UNBLOCK = 1 << 20, -LCA_AMBLOCKINGFOLLOWER_DONTBRAKE = 1 << 21 -# !!! never used LCA_AMBLOCKINGSECONDFOLLOWER = 1 << 22, -LCA_CHANGE_TO_HELP = 1 << 23 -# !!! never read LCA_KEEP1 = 1 << 24, -# !!! never used LCA_KEEP2 = 1 << 25, -LCA_AMBACKBLOCKER = 1 << 26 -LCA_AMBACKBLOCKER_STANDING = 1 << 27 -# @} diff --git a/co-simulation/bundle/src/assembly/resources/fed/ns3/main.py b/co-simulation/bundle/src/assembly/resources/fed/ns3/main.py deleted file mode 100644 index 89a85dea..00000000 --- a/co-simulation/bundle/src/assembly/resources/fed/ns3/main.py +++ /dev/null @@ -1,301 +0,0 @@ -# -*- coding: utf-8 -*- -# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo -# Copyright (C) 2008-2020 German Aerospace Center (DLR) and others. -# This program and the accompanying materials are made available under the -# terms of the Eclipse Public License 2.0 which is available at -# https://www.eclipse.org/legal/epl-2.0/ -# This Source Code may also be made available under the following Secondary -# Licenses when the conditions for such availability set forth in the Eclipse -# Public License 2.0 are satisfied: GNU General Public License, version 2 -# or later which is available at -# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html -# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later - -# @file main.py -# @author Michael Behrisch -# @author Lena Kalleske -# @author Mario Krumnow -# @author Daniel Krajzewicz -# @author Jakob Erdmann -# @date 2008-10-09 - -# pylint: disable=E1101 - -from __future__ import print_function -from __future__ import absolute_import -import socket -import time -import subprocess -import warnings -import sys -import os -from functools import wraps - -if 'SUMO_HOME' in os.environ: - sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools')) -else: - sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -import sumolib # noqa -from sumolib.miscutils import getFreeSocketPort # noqa - -from .domain import _defaultDomains # noqa -# StepListener needs to be imported for backwards compatibility -from .connection import Connection, StepListener # noqa -from .exceptions import FatalTraCIError, TraCIException # noqa -from . import _inductionloop, _lanearea, _multientryexit, _trafficlight # noqa -from . import _variablespeedsign, _meandata # noqa -from . import _lane, _person, _route, _vehicle, _vehicletype # noqa -from . import _edge, _gui, _junction, _poi, _polygon, _simulation # noqa -from . import _calibrator, _routeprobe, _rerouter # noqa -from . import _busstop, _parkingarea, _chargingstation, _overheadwire # noqa - -inductionloop = _inductionloop.InductionLoopDomain() -lanearea = _lanearea.LaneAreaDomain() -multientryexit = _multientryexit.MultiEntryExitDomain() -trafficlight = _trafficlight.TrafficLightDomain() -variablespeedsign = _variablespeedsign.VariableSpeedSignDomain() -meandata = _meandata.MeanDataDomain() -lane = _lane.LaneDomain() -person = _person.PersonDomain() -route = _route.RouteDomain() -vehicle = _vehicle.VehicleDomain() -vehicletype = _vehicletype.VehicleTypeDomain() -edge = _edge.EdgeDomain() -gui = _gui.GuiDomain() -junction = _junction.JunctionDomain() -poi = _poi.PoiDomain() -polygon = _polygon.PolygonDomain() -simulation = _simulation.SimulationDomain() -calibrator = _calibrator.CalibratorDomain() -busstop = _busstop.BusStopDomain() -parkingarea = _parkingarea.ParkingAreaDomain() -chargingstation = _chargingstation.ChargingStationDomain() -overheadwire = _overheadwire.OverheadWireDomain() -routeprobe = _routeprobe.RouteProbeDomain() -rerouter = _rerouter.RerouterDomain() - -_connections = {} -_traceFile = {} -_traceGetters = {} -# cannot use immutable type as global variable -_currentLabel = [""] -_connectHook = None - - -def _STEPS2TIME(step): - """Conversion from time steps in milliseconds to seconds as float""" - return step / 1000. - - -def setConnectHook(hookFunc): - global _connectHook - _connectHook = hookFunc - - -def _addTracing(method): - @wraps(method) - def tracingWrapper(*args, **kwargs): - _traceFile[_currentLabel[0]].write("traci.%s(%s)\n" % ( - method.__name__, - ', '.join(list(map(repr, args)) + ["%s=%s" % (n, repr(v)) for n, v in kwargs.items()]))) - return method(*args, **kwargs) - return tracingWrapper - - -def connect(port=8813, numRetries=10, host="localhost", proc=None, waitBetweenRetries=1): - """ - Establish a connection to a TraCI-Server and return the - connection object. The connection is not saved in the pool and not - accessible via traci.switch. It should be safe to use different - connections established by this method in different threads. - """ - for retry in range(1, numRetries + 2): - try: - conn = Connection(host, port, proc) - if _connectHook is not None: - _connectHook(conn) - return conn - except socket.error as e: - if proc is not None and proc.poll() is not None: - raise TraCIException("TraCI server already finished") - if retry > 1: - print("Could not connect to TraCI server at %s:%s" % (host, port), e) - if retry < numRetries + 1: - print(" Retrying in %s seconds" % waitBetweenRetries) - time.sleep(waitBetweenRetries) - raise FatalTraCIError("Could not connect in %s tries" % (numRetries + 1)) - - -def init(port=8813, numRetries=10, host="localhost", label="default", proc=None): - """ - Establish a connection to a TraCI-Server and store it under the given - label. This method is not thread-safe. It accesses the connection - pool concurrently. - """ - _connections[label] = connect(port, numRetries, host, proc) - switch(label) - return getVersion() - - -def start(cmd, port=None, numRetries=10, label="default", verbose=False, - traceFile=None, traceGetters=True, stdout=None): - """ - Start a sumo server using cmd, establish a connection to it and - store it under the given label. This method is not thread-safe. - """ - if label in _connections: - raise TraCIException("Connection '%s' is already active." % label) - if traceFile is not None: - _startTracing(traceFile, cmd, port, label, traceGetters) - while numRetries >= 0 and label not in _connections: - sumoPort = sumolib.miscutils.getFreeSocketPort() if port is None else port - cmd2 = cmd + ["--remote-port", str(sumoPort)] - if verbose: - print("Calling " + ' '.join(cmd2)) - sumoProcess = subprocess.Popen(cmd2, stdout=stdout) - try: - return init(sumoPort, numRetries, "localhost", label, sumoProcess) - except TraCIException: - if port is not None: - break - warnings.warn("Could not connect to TraCI server using port %s. Retrying with different port." % sumoPort) - numRetries -= 1 - raise FatalTraCIError("Could not connect.") - - -def _startTracing(traceFile, cmd, port, label, traceGetters): - _traceFile[label] = open(traceFile, 'w') - _traceFile[label].write("traci.start(%s, port=%s, label=%s)\n" % ( - repr(cmd), repr(port), repr(label))) - _traceGetters[label] = traceGetters - - -def isLibsumo(): - return False - - -def isLibtraci(): - return False - - -def hasGUI(): - try: - gui.getIDList() - return True - except TraCIException: - return False - - -def load(args): - """load([optionOrParam, ...]) - Let sumo load a simulation using the given command line like options - Example: - load(['-c', 'run.sumocfg']) - load(['-n', 'net.net.xml', '-r', 'routes.rou.xml']) - """ - if "" not in _connections: - raise FatalTraCIError("Not connected.") - if _traceFile: - # cannot wrap because the method is import from __init__ - _traceFile[_currentLabel[0]].write("traci.load(%s)\n" % repr(args)) - return _connections[""].load(args) - - -def isLoaded(): - return "" in _connections - - -def simulationStep(step=0): - """ - Make a simulation step and simulate up to the given second in sim time. - If the given value is 0 or absent, exactly one step is performed. - Values smaller than or equal to the current sim time result in no action. - """ - if "" not in _connections: - raise FatalTraCIError("Not connected.") - if _traceFile: - # cannot wrap because the method is import from __init__ - args = "" if step == 0 else str(step) - _traceFile[_currentLabel[0]].write("traci.simulationStep(%s)\n" % args) - return _connections[""].simulationStep(step) - - -def addStepListener(listener): - """addStepListener(traci.StepListener) -> int - - Append the step listener (its step function is called at the end of every call to traci.simulationStep()) - to the current connection. - Returns the ID assigned to the listener if it was added successfully, None otherwise. - """ - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].addStepListener(listener) - - -def removeStepListener(listenerID): - """removeStepListener(traci.StepListener) -> bool - - Remove the step listener from the current connection's step listener container. - Returns True if the listener was removed successfully, False if it wasn't registered. - """ - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].removeStepListener(listenerID) - - -def getVersion(): - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].getVersion() - - -def setOrder(order): - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].setOrder(order) - -def getV2xMessage(): - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].getV2xMessage() - -def setV2xMessage(message): - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].setV2xMessage(message) - -def close(wait=True): - if "" not in _connections: - raise FatalTraCIError("Not connected.") - _connections[""].close(wait) - _connections[""].simulation._setConnection(None) - del _connections[_currentLabel[0]] - del _connections[""] - if _traceFile: - # cannot wrap because the method is import from __init__ - _traceFile[_currentLabel[0]].write("traci.close()\n") - _traceFile[_currentLabel[0]].close() - - -def switch(label): - _connections[""] = getConnection(label) - _currentLabel[0] = label - for domain in _defaultDomains: - domain._setConnection(_connections[""]) - if _traceFile: - domain._setTraceFile(_traceFile[label], _traceGetters[label]) - - -def getLabel(): - return _currentLabel[0] - - -def getConnection(label="default"): - if label not in _connections: - raise TraCIException("Connection '%s' is not known." % label) - return _connections[label] - - -def setLegacyGetLeader(enabled): - _vehicle._legacyGetLeader = enabled From 227a6282510ff98daa4e5e4b1098769b3309e839 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 12 Apr 2023 19:37:23 -0400 Subject: [PATCH 076/124] Added ns3 patch and renamed folder --- co-simulation/{traci_update => patch}/connection.py | 0 co-simulation/{traci_update => patch}/constants.py | 0 co-simulation/{traci_update => patch}/main.py | 0 co-simulation/patch/run.sh | 12 ++++++++++++ 4 files changed, 12 insertions(+) rename co-simulation/{traci_update => patch}/connection.py (100%) rename co-simulation/{traci_update => patch}/constants.py (100%) rename co-simulation/{traci_update => patch}/main.py (100%) create mode 100644 co-simulation/patch/run.sh diff --git a/co-simulation/traci_update/connection.py b/co-simulation/patch/connection.py similarity index 100% rename from co-simulation/traci_update/connection.py rename to co-simulation/patch/connection.py diff --git a/co-simulation/traci_update/constants.py b/co-simulation/patch/constants.py similarity index 100% rename from co-simulation/traci_update/constants.py rename to co-simulation/patch/constants.py diff --git a/co-simulation/traci_update/main.py b/co-simulation/patch/main.py similarity index 100% rename from co-simulation/traci_update/main.py rename to co-simulation/patch/main.py diff --git a/co-simulation/patch/run.sh b/co-simulation/patch/run.sh new file mode 100644 index 00000000..ff05c495 --- /dev/null +++ b/co-simulation/patch/run.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +port=$1 +cmdport=$2 + +if [[ -z $cmdport ]]; then + cmdport=0 +fi + +cd ns3 +LD_LIBRARY_PATH=/opt/carma-simulation/bin/fed/ns3/ns-allinone-3.28/ns-3.28/build /opt/carma-simulation/bin/fed/ns3/ns-allinone-3.28/ns-3.28/build/scratch/mosaic_starter --port=$port --cmdPort=$cmdport --configFile=scratch/ns3_federate_config.xml + From 2c3bbf999b4fdcc40aec39e20afa45d3ad1c31ca Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 12 Apr 2023 19:37:57 -0400 Subject: [PATCH 077/124] Enabled ns3 parameter for testing purpose --- .../resources/scenarios/Tiergarten/scenario_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Tiergarten/scenario_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Tiergarten/scenario_config.json index e533ed8b..bcd90c80 100644 --- a/co-simulation/bundle/src/assembly/resources/scenarios/Tiergarten/scenario_config.json +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Tiergarten/scenario_config.json @@ -28,7 +28,7 @@ "cell": false, "environment": true, "sns": true, - "ns3": false, + "ns3": true, "omnetpp": false, "output": true, "sumo": true From 7e46f5d03fa652a7e804b08c121119d9caeda5b8 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 12 Apr 2023 19:38:50 -0400 Subject: [PATCH 078/124] Completed NS3 installation and upgraded SUMO to 1.15.0 --- docker/env.sh | 5 +++-- docker/install.sh | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/docker/env.sh b/docker/env.sh index c00c8b66..2876e720 100755 --- a/docker/env.sh +++ b/docker/env.sh @@ -1,11 +1,12 @@ #!/bin/bash # Set environment variables here. -export NS3_HOME="/opt/ns-3/" +export NS3_HOME="/opt/carma-simulation/bin/fed/ns3" export JAVA_HOME="/usr/lib/jvm/java-11-jdk-amd64" -export SUMO_HOME="/opt/sumo/sumo-1_12_0" +export SUMO_HOME="/opt/sumo/sumo-1_15_0" export CARLA_HOME="/opt/carla" export CARMA_SIMULATION_HOME="/opt/carma-simulation" export PATH="/usr/local/share/sumo/bin:/usr/local/share/sumo/tools:$PATH" export PATH="/opt/maven/bin:$PATH" export PATH="/opt/carma-simulation:$PATH" + diff --git a/docker/install.sh b/docker/install.sh index 04e4defb..e953e71d 100755 --- a/docker/install.sh +++ b/docker/install.sh @@ -58,23 +58,16 @@ cd /home/carma/src #make check #sudo make install -# Install NS-3 -cd "/home/carma/src/co-simulation tool/bundle/src/assembly/resources/fed/ns3/" -chmod +x ns3_installer.sh -set -x -./ns3_installer.sh -q -#TODO: Add expore NS3_HOME=path_to_run.sh to /bin/fed/ns3/run.sh - # Install SUMO-1.12.0 cd /home/carma/src/ wget "https://github.com/eclipse/sumo/archive/refs/tags/v1_12_0.tar.gz" sudo mkdir -p /opt/sumo sudo chown -R carma:carma /opt/sumo -tar xvf v1_12_0.tar.gz -C /opt/sumo -sudo cp co-simulation/traci_update/constants.py /opt/sumo/sumo-1_12_0/tools/traci -sudo cp co-simulation/traci_update/connection.py /opt/sumo/sumo-1_12_0/tools/traci -sudo cp co-simulation/traci_update/main.py /opt/sumo/sumo-1_12_0/tools/traci -cd /opt/sumo/sumo-1_12_0 +tar xvf v1_15_0.tar.gz -C /opt/sumo +sudo cp co-simulation/patch/constants.py /opt/sumo/sumo-1_15_0/tools/traci +sudo cp co-simulation/patch/connection.py /opt/sumo/sumo-1_15_0/tools/traci +sudo cp co-simulation/patch/main.py /opt/sumo/sumo-1_15_0/tools/traci +cd /opt/sumo/sumo-1_15_0 mkdir -p build/cmake-build && cd build/cmake-build cmake ../.. make -j$(nproc) @@ -118,4 +111,11 @@ cp bundle-22.1-SNAPSHOT.jar /opt/carma-simulation cd /home/carma/src/co-simulation unzip sample_scenario.zip -d /opt/carma-simulation/scenarios +# Install NS-3 +cd "/opt/carma-simulation/bin/fed/ns3/" +chmod +x ns3_installer.sh +set -x +./ns3_installer.sh -q +sudo cp /home/carma/src/co-simulation/patch/run.sh /opt/carma-simulation/bin/fed/ns3 + echo "Build complete!!!" From 280dba5d8dfb693962e3b9c2c41c5de3a3bb7d12 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 12 Apr 2023 19:39:10 -0400 Subject: [PATCH 079/124] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index fcc8f8af..1ffcc7db 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,4 +54,4 @@ COPY --chown=carma:carma docker/env.sh /home/carma/.base-image/ RUN docker/install.sh ENTRYPOINT [ "/home/carma/src/docker/entrypoint.sh" ] -CMD [ "mosaic.sh", "-s", "HelloWorld" ] +CMD [ "mosaic.sh", "-s", "Tiergarten" ] From cf8d89f595b783ec5ac31ec0c51c6c76ff891e06 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 12 Apr 2023 19:39:23 -0400 Subject: [PATCH 080/124] Update ns3_installer.sh --- .../bundle/src/assembly/resources/fed/ns3/ns3_installer.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/co-simulation/bundle/src/assembly/resources/fed/ns3/ns3_installer.sh b/co-simulation/bundle/src/assembly/resources/fed/ns3/ns3_installer.sh index 57bd9cb5..bad64962 100644 --- a/co-simulation/bundle/src/assembly/resources/fed/ns3/ns3_installer.sh +++ b/co-simulation/bundle/src/assembly/resources/fed/ns3/ns3_installer.sh @@ -362,8 +362,8 @@ build_ns3() # ns-3 prior to 3.28.1 does not compile without warnings using g++ 10.2.0 CXXFLAGS="-Wno-error" python3.6 ./build.py --disable-netanim - - cp -ar ns-3.28/build/ns /usr/include + sudo cp -ar ns-3.28/build/ns3 /usr/include/ + log "Build ns3-federate" cd ${current_dir}/federate mv src/ClientServerChannel.h . From 4c0ecb970e03a9587dfa9c11624b0a8853f8da99 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 12 Apr 2023 21:16:01 -0400 Subject: [PATCH 081/124] Removed V2X function from traci and CARLA --- .../carla_integration/sumo_simulation.py | 13 - co-simulation/patch/connection.py | 435 ----- co-simulation/patch/constants.py | 1399 ----------------- co-simulation/patch/main.py | 326 ---- docker/install.sh | 7 +- 5 files changed, 2 insertions(+), 2178 deletions(-) delete mode 100644 co-simulation/patch/connection.py delete mode 100644 co-simulation/patch/constants.py delete mode 100644 co-simulation/patch/main.py diff --git a/co-simulation/bridge/carla_integration/sumo_simulation.py b/co-simulation/bridge/carla_integration/sumo_simulation.py index d4f71661..d5d892d0 100644 --- a/co-simulation/bridge/carla_integration/sumo_simulation.py +++ b/co-simulation/bridge/carla_integration/sumo_simulation.py @@ -501,19 +501,6 @@ def tick(self): self.spawned_actors = set(traci.simulation.getDepartedIDList()) self.destroyed_actors = set(traci.simulation.getArrivedIDList()) - # Show v2x messages received by CARLA vehicles - v2xMessageReceived = traci.getV2xMessage() - if v2xMessageReceived is not None: - for message in v2xMessageReceived: - print(message) - - # Send V2x message to CARLA ambassador - if self.sendV2xInterval == 10: - traci.setV2xMessage("carla_0; A V2X messages from CARLA simulator.") - self.sendV2xInterval = 0 - - self.sendV2xInterval += 1 - @staticmethod def close(): """ diff --git a/co-simulation/patch/connection.py b/co-simulation/patch/connection.py deleted file mode 100644 index a8dcec77..00000000 --- a/co-simulation/patch/connection.py +++ /dev/null @@ -1,435 +0,0 @@ -# -*- coding: utf-8 -*- -# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo -# Copyright (C) 2008-2022 German Aerospace Center (DLR) and others. -# This program and the accompanying materials are made available under the -# terms of the Eclipse Public License 2.0 which is available at -# https://www.eclipse.org/legal/epl-2.0/ -# This Source Code may also be made available under the following Secondary -# Licenses when the conditions for such availability set forth in the Eclipse -# Public License 2.0 are satisfied: GNU General Public License, version 2 -# or later which is available at -# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html -# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later - -# @file connection.py -# @author Michael Behrisch -# @author Lena Kalleske -# @author Mario Krumnow -# @author Daniel Krajzewicz -# @author Jakob Erdmann -# @date 2008-10-09 - -from __future__ import print_function -from __future__ import absolute_import -import socket -import struct -import sys -import warnings -import abc - -from . import constants as tc -from .exceptions import TraCIException, FatalTraCIError -from .domain import _defaultDomains -from .storage import Storage - -_RESULTS = {0x00: "OK", 0x01: "Not implemented", 0xFF: "Error"} - - -class Connection: - - """Contains the socket, the composed message string - together with a list of TraCI commands which are inside. - """ - - def __init__(self, host, port, process): - if sys.platform.startswith('java'): - # working around jython 2.7.0 bug #2273 - self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) - else: - self._socket = socket.socket() - self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - self._socket.connect((host, port)) - self._process = process - self._string = bytes() - self._queue = [] - self._subscriptionMapping = {} - self._stepListeners = {} - self._nextStepListenerID = 0 - self._traceFile = None - for domain in _defaultDomains: - domain._register(self, self._subscriptionMapping) - - def _recvExact(self): - try: - result = bytes() - while len(result) < 4: - t = self._socket.recv(4 - len(result)) - if not t: - return None - result += t - length = struct.unpack("!i", result)[0] - 4 - result = bytes() - while len(result) < length: - t = self._socket.recv(length - len(result)) - if not t: - return None - result += t - return Storage(result) - except socket.error: - return None - - def _sendExact(self): - if self._socket is None: - raise FatalTraCIError("Connection already closed.") - length = struct.pack("!i", len(self._string) + 4) - # print("python_sendExact: '%s'" % ' '.join(map(lambda x : "%X" % ord(x), self._string))) - self._socket.send(length + self._string) - result = self._recvExact() - if not result: - self._socket.close() - del self._socket - raise FatalTraCIError("connection closed by SUMO") - for command in self._queue: - prefix = result.read("!BBB") - err = result.readString() - if prefix[2] or err: - self._string = bytes() - self._queue = [] - raise TraCIException(err, prefix[1], _RESULTS[prefix[2]]) - elif prefix[1] != command: - raise FatalTraCIError("Received answer %s for command %s." % (prefix[1], command)) - elif prefix[1] == tc.CMD_STOP: - length = result.read("!B")[0] - 1 - result.read("!%sx" % length) - self._string = bytes() - self._queue = [] - return result - - def _pack(self, format, *values): - packed = bytes() - for f, v in zip(format, values): - if f == "i": - packed += struct.pack("!Bi", tc.TYPE_INTEGER, int(v)) - elif f == "I": # raw int for setOrder - packed += struct.pack("!i", int(v)) - elif f == "d": - packed += struct.pack("!Bd", tc.TYPE_DOUBLE, float(v)) - elif f == "D": # raw double for some base commands like simstep - packed += struct.pack("!d", float(v)) - elif f == "b": - packed += struct.pack("!Bb", tc.TYPE_BYTE, int(v)) - elif f == "B": - packed += struct.pack("!BB", tc.TYPE_UBYTE, int(v)) - elif f == "u": # raw unsigned byte needed for distance command and subscribe - packed += struct.pack("!B", int(v)) - elif f == "s": - v = str(v) - packed += struct.pack("!Bi", tc.TYPE_STRING, len(v)) + v.encode("latin1") - elif f == "p": # polygon - if len(v) <= 255: - packed += struct.pack("!BB", tc.TYPE_POLYGON, len(v)) - else: - packed += struct.pack("!BBi", tc.TYPE_POLYGON, 0, len(v)) - for p in v: - packed += struct.pack("!dd", *p) - elif f == "t": # tuple aka compound - packed += struct.pack("!Bi", tc.TYPE_COMPOUND, v) - elif f == "c": # color - packed += struct.pack("!BBBBB", tc.TYPE_COLOR, int(v[0]), int(v[1]), int(v[2]), - int(v[3]) if len(v) > 3 else 255) - elif f == "l": # string list - packed += struct.pack("!Bi", tc.TYPE_STRINGLIST, len(v)) - for s in v: - packed += struct.pack("!i", len(s)) + s.encode("latin1") - elif f == "f": # float list - packed += struct.pack("!Bi", tc.TYPE_DOUBLELIST, len(v)) - for x in v: - packed += struct.pack("!d", x) - elif f == "o": - packed += struct.pack("!Bdd", tc.POSITION_2D, *v) - elif f == "O": - packed += struct.pack("!Bddd", tc.POSITION_3D, *v) - elif f == "g": - packed += struct.pack("!Bdd", tc.POSITION_LON_LAT, *v) - elif f == "G": - packed += struct.pack("!Bddd", tc.POSITION_LON_LAT_ALT, *v) - elif f == "r": - packed += struct.pack("!Bi", tc.POSITION_ROADMAP, len(v[0])) + v[0].encode("latin1") - packed += struct.pack("!dB", v[1], v[2]) - return packed - - def _sendCmd(self, cmdID, varID, objID, format="", *values): - self._queue.append(cmdID) - packed = self._pack(format, *values) - length = len(packed) + 1 + 1 # length and command - if varID is not None: - if isinstance(varID, tuple): # begin and end of a subscription - length += 8 + 8 + 4 + len(objID) - else: - length += 1 + 4 + len(objID) - if length <= 255: - self._string += struct.pack("!BB", length, cmdID) - else: - self._string += struct.pack("!BiB", 0, length + 4, cmdID) - if varID is not None: - if isinstance(varID, tuple): - self._string += struct.pack("!dd", *varID) - else: - self._string += struct.pack("!B", varID) - self._string += struct.pack("!i", len(objID)) + objID.encode("latin1") - self._string += packed - return self._sendExact() - - def _readSubscription(self, result): - # to enable this you also need to set _DEBUG to True in storage.py - # result.printDebug() - result.readLength() - response = result.read("!B")[0] - isVariableSubscription = ((response >= tc.RESPONSE_SUBSCRIBE_INDUCTIONLOOP_VARIABLE and - response <= tc.RESPONSE_SUBSCRIBE_BUSSTOP_VARIABLE) or - (response >= tc.RESPONSE_SUBSCRIBE_PARKINGAREA_VARIABLE and - response <= tc.RESPONSE_SUBSCRIBE_OVERHEADWIRE_VARIABLE)) - objectID = result.readString() - if not isVariableSubscription: - domain = result.read("!B")[0] - numVars = result.read("!B")[0] - if isVariableSubscription: - while numVars > 0: - varID, status = result.read("!BB") - if status: - print("Error!", result.readTypedString()) - elif response in self._subscriptionMapping: - self._subscriptionMapping[response].add(objectID, varID, result) - else: - raise FatalTraCIError( - "Cannot handle subscription response %02x for %s." % (response, objectID)) - numVars -= 1 - else: - objectNo = result.read("!i")[0] - for _ in range(objectNo): - oid = result.readString() - if numVars == 0: - self._subscriptionMapping[response].addContext( - objectID, self._subscriptionMapping[domain], oid) - for __ in range(numVars): - varID, status = result.read("!BB") - if status: - print("Error!", result.readTypedString()) - elif response in self._subscriptionMapping: - self._subscriptionMapping[response].addContext( - objectID, self._subscriptionMapping[domain], oid, varID, result) - else: - raise FatalTraCIError( - "Cannot handle subscription response %02x for %s." % (response, objectID)) - return objectID, response - - def _subscribe(self, cmdID, begin, end, objID, varIDs, parameters): - format = "u" - args = [len(varIDs)] - for v in varIDs: - format += "u" - args.append(v) - if parameters is not None and v in parameters: - if isinstance(parameters[v], tuple): - format += parameters[v][0] - for a in parameters[v][1:]: - args.append(a) - elif isinstance(parameters[v], int): - format += "i" - args.append(parameters[v]) - elif isinstance(parameters[v], float): - format += "d" - args.append(parameters[v]) - else: - format += "s" - args.append(parameters[v]) - result = self._sendCmd(cmdID, (begin, end), objID, format, *args) - if varIDs: - objectID, response = self._readSubscription(result) - if response - cmdID != 16 or objectID != objID: - raise FatalTraCIError("Received answer %02x,%s for subscription command %02x,%s." % ( - response, objectID, cmdID, objID)) - - def _getSubscriptionResults(self, cmdID): - return self._subscriptionMapping[cmdID] - - def _subscribeContext(self, cmdID, begin, end, objID, domain, dist, varIDs, parameters=None): - result = self._sendCmd(cmdID, (begin, end), objID, "uDu" + (len(varIDs) * "u"), - domain, dist, len(varIDs), *varIDs) - if varIDs: - objectID, response = self._readSubscription(result) - if response - cmdID != 16 or objectID != objID: - raise FatalTraCIError("Received answer %02x,%s for context subscription command %02x,%s." % ( - response, objectID, cmdID, objID)) - - def _addSubscriptionFilter(self, filterType, params=None): - if filterType in (tc.FILTER_TYPE_NONE, tc.FILTER_TYPE_NOOPPOSITE, - tc.FILTER_TYPE_LEAD_FOLLOW): - # filter without parameter - assert params is None - self._sendCmd(tc.CMD_ADD_SUBSCRIPTION_FILTER, None, None, "u", filterType) - elif filterType in (tc.FILTER_TYPE_DOWNSTREAM_DIST, tc.FILTER_TYPE_UPSTREAM_DIST, - tc.FILTER_TYPE_TURN, tc.FILTER_TYPE_FIELD_OF_VISION, - tc.FILTER_TYPE_LATERAL_DIST): - # filter with float parameter - self._sendCmd(tc.CMD_ADD_SUBSCRIPTION_FILTER, None, None, "ud", filterType, params) - elif filterType in (tc.FILTER_TYPE_VCLASS, tc.FILTER_TYPE_VTYPE): - # filter with list(string) parameter - self._sendCmd(tc.CMD_ADD_SUBSCRIPTION_FILTER, None, None, "ul", filterType, params) - elif filterType == tc.FILTER_TYPE_LANES: - # filter with list(byte) parameter - # check uniqueness of given lanes in list - lanes = set() - for i in params: - lane = int(i) - if lane < 0: - lane += 256 - lanes.add(lane) - if len(lanes) < len(list(params)): - warnings.warn("Ignoring duplicate lane specification for subscription filter.") - self._sendCmd(tc.CMD_ADD_SUBSCRIPTION_FILTER, None, None, - (len(lanes) + 2) * "u", filterType, len(lanes), *lanes) - - def hasGUI(self): - try: - self.gui.getIDList() - return True - except TraCIException: - return False - - def load(self, args): - """ - Load a simulation from the given arguments. - """ - if self._traceFile: - self._traceFile.write("traci.load(%s)\n" % repr(args)) - self._sendCmd(tc.CMD_LOAD, None, None, "l", args) - - def simulationStep(self, step=0.): - """ - Make a simulation step and simulate up to the given second in sim time. - If the given value is 0 or absent, exactly one step is performed. - Values smaller than or equal to the current sim time result in no action. - """ - if self._traceFile: - args = "" if step == 0 else str(step) - self._traceFile.write("traci.simulationStep(%s)\n" % args) - if type(step) is int and step >= 1000: - warnings.warn("API change now handles step as floating point seconds", stacklevel=2) - result = self._sendCmd(tc.CMD_SIMSTEP, None, None, "D", step) - for subscriptionResults in self._subscriptionMapping.values(): - subscriptionResults.reset() - numSubs = result.readInt() - responses = [] - while numSubs > 0: - responses.append(self._readSubscription(result)) - numSubs -= 1 - self._manageStepListeners(step) - return responses - - def _manageStepListeners(self, step): - listenersToRemove = [] - for (listenerID, listener) in self._stepListeners.items(): - keep = listener.step(step) - if not keep: - listenersToRemove.append(listenerID) - for listenerID in listenersToRemove: - self.removeStepListener(listenerID) - - def addStepListener(self, listener): - """addStepListener(traci.StepListener) -> int - - Append the step listener (its step function is called at the end of every call to traci.simulationStep()) - Returns the ID assigned to the listener if it was added successfully, None otherwise. - """ - if issubclass(type(listener), StepListener): - listener.setID(self._nextStepListenerID) - self._stepListeners[self._nextStepListenerID] = listener - self._nextStepListenerID += 1 - # print ("traci: Added stepListener %s\nlisteners: %s"%(_nextStepListenerID - 1, _stepListeners)) - return self._nextStepListenerID - 1 - warnings.warn( - "Proposed listener's type must inherit from traci.StepListener. Not adding object of type '%s'" % - type(listener)) - return None - - def removeStepListener(self, listenerID): - """removeStepListener(traci.StepListener) -> bool - - Remove the step listener from traci's step listener container. - Returns True if the listener was removed successfully, False if it wasn't registered. - """ - # print ("traci: removeStepListener %s\nlisteners: %s"%(listenerID, _stepListeners)) - if listenerID in self._stepListeners: - self._stepListeners[listenerID].cleanUp() - del self._stepListeners[listenerID] - # print ("traci: Removed stepListener %s"%(listenerID)) - return True - warnings.warn("Cannot remove unknown listener %s.\nlisteners:%s" % (listenerID, self._stepListeners)) - return False - - def getVersion(self): - command = tc.CMD_GETVERSION - result = self._sendCmd(command, None, None) - result.readLength() - response = result.read("!B")[0] - if response != command: - raise FatalTraCIError("Received answer %s for command %s." % (response, command)) - return result.readInt(), result.readString() - - def setOrder(self, order): - self._sendCmd(tc.CMD_SETORDER, None, None, "I", order) - - def close(self, wait=True): - if self._traceFile: - self._traceFile.write("traci.close()\n") - self._traceFile.close() - for domain in _defaultDomains: - domain._setTraceFile(None, False) - for listenerID in list(self._stepListeners.keys()): - self.removeStepListener(listenerID) - if self._socket is not None: - self._sendCmd(tc.CMD_CLOSE, None, None) - self._socket.close() - self._socket = None - if wait and self._process is not None: - self._process.wait() - - def getV2xMessage(self): - command = tc.CMD_GET_V2X - result = self._sendCmd(command,None,None) - result.readLength() - response = result.read("!B")[0] - - if response != command: - raise FatalTraCIError("Received answer %s for command %s." % (response, command)) - return result.readStringList() - - def setV2xMessage(self, message): - self._sendCmd(tc.CMD_SET_V2X, None, None, "s", message) - -class StepListener(object): - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def step(self, t=0): - """step(int) -> bool - - After adding a StepListener 'listener' with traci.addStepListener(listener), - TraCI will call listener.step(t) after each call to traci.simulationStep(t) - The return value indicates whether the stepListener wants to stay active. - """ - return True - - def cleanUp(self): - """cleanUp() -> None - - This method is called at removal of the stepListener, allowing to schedule some final actions - """ - pass - - def setID(self, ID): - self._ID = ID - - def getID(self): - return self._ID diff --git a/co-simulation/patch/constants.py b/co-simulation/patch/constants.py deleted file mode 100644 index 3a2506df..00000000 --- a/co-simulation/patch/constants.py +++ /dev/null @@ -1,1399 +0,0 @@ -# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo -# Copyright (C) 2009-2022 German Aerospace Center (DLR) and others. -# This program and the accompanying materials are made available under the -# terms of the Eclipse Public License 2.0 which is available at -# https://www.eclipse.org/legal/epl-2.0/ -# This Source Code may also be made available under the following Secondary -# Licenses when the conditions for such availability set forth in the Eclipse -# Public License 2.0 are satisfied: GNU General Public License, version 2 -# or later which is available at -# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html -# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later - -# @file constants.py -# @author generated by "rebuildConstants.py" -# @date 2021-11-09 23:34:42.684719 - -""" -This script contains TraCI constant definitions from /src/libsumo/TraCIConstants.h. -""" - - -# **************************************** -# VERSION -# **************************************** -TRACI_VERSION = 20 - -# **************************************** -# COMMANDS -# **************************************** -# command: get version -CMD_GETVERSION = 0x00 - -# command: load -CMD_LOAD = 0x01 - -# command: simulation step -CMD_SIMSTEP = 0x02 - -# command: set connection priority (execution order) -CMD_SETORDER = 0x03 - -# command: stop vehicle -CMD_STOP = 0x12 - -# command: reroute to parking area -CMD_REROUTE_TO_PARKING = 0xc2 - -# command: Resume from parking -CMD_RESUME = 0x19 - -# command: set lane -CMD_CHANGELANE = 0x13 - -# command: slow down -CMD_SLOWDOWN = 0x14 - -# command: set sublane (vehicle) -CMD_CHANGESUBLANE = 0x15 - -# command: open gap -CMD_OPENGAP = 0x16 - -# command: replace vehicle stop and updated route -CMD_REPLACE_STOP = 0x17 - -# command: retrieve information about the current taxi fleet and their status -VAR_TAXI_FLEET = 0x20 - -# command: send dispatch request for the given taxi -CMD_TAXI_DISPATCH = 0x21 - -# command: change target -CMD_CHANGETARGET = 0x31 - -# command: close sumo -CMD_CLOSE = 0x7F - -# command: add subscription filter -CMD_ADD_SUBSCRIPTION_FILTER = 0x7e - -# command: get V2X message -CMD_GET_V2X = 0x0d - -# command: set V2X message -CMD_SET_V2X = 0x2f - -# command: subscribe induction loop (e1) context -CMD_SUBSCRIBE_INDUCTIONLOOP_CONTEXT = 0x80 -# response: subscribe induction loop (e1) context -RESPONSE_SUBSCRIBE_INDUCTIONLOOP_CONTEXT = 0x90 -# command: get induction loop (e1) variable -CMD_GET_INDUCTIONLOOP_VARIABLE = 0xa0 -# response: get induction loop (e1) variable -RESPONSE_GET_INDUCTIONLOOP_VARIABLE = 0xb0 -# command: set induction loop (e1) variable, not used yet -CMD_SET_INDUCTIONLOOP_VARIABLE = 0xc0 -# command: subscribe induction loop (e1) variable -CMD_SUBSCRIBE_INDUCTIONLOOP_VARIABLE = 0xd0 -# response: subscribe induction loop (e1) variable -RESPONSE_SUBSCRIBE_INDUCTIONLOOP_VARIABLE = 0xe0 - -# command: subscribe multi-entry/multi-exit detector (e3) context -CMD_SUBSCRIBE_MULTIENTRYEXIT_CONTEXT = 0x81 -# response: subscribe multi-entry/multi-exit detector (e3) context -RESPONSE_SUBSCRIBE_MULTIENTRYEXIT_CONTEXT = 0x91 -# command: get multi-entry/multi-exit detector (e3) variable -CMD_GET_MULTIENTRYEXIT_VARIABLE = 0xa1 -# response: get multi-entry/multi-exit detector (e3) variable -RESPONSE_GET_MULTIENTRYEXIT_VARIABLE = 0xb1 -# command: set multi-entry/multi-exit detector (e3) variable, not used yet -CMD_SET_MULTIENTRYEXIT_VARIABLE = 0xc1 -# command: subscribe multi-entry/multi-exit detector (e3) variable -CMD_SUBSCRIBE_MULTIENTRYEXIT_VARIABLE = 0xd1 -# response: subscribe multi-entry/multi-exit detector (e3) variable -RESPONSE_SUBSCRIBE_MULTIENTRYEXIT_VARIABLE = 0xe1 - -# command: subscribe traffic lights context -CMD_SUBSCRIBE_TL_CONTEXT = 0x82 -# response: subscribe traffic lights context -RESPONSE_SUBSCRIBE_TL_CONTEXT = 0x92 -# command: get traffic lights variable -CMD_GET_TL_VARIABLE = 0xa2 -# response: get traffic lights variable -RESPONSE_GET_TL_VARIABLE = 0xb2 -# command: set traffic lights variable -CMD_SET_TL_VARIABLE = 0xc2 -# command: subscribe traffic lights variable -CMD_SUBSCRIBE_TL_VARIABLE = 0xd2 -# response: subscribe traffic lights variable -RESPONSE_SUBSCRIBE_TL_VARIABLE = 0xe2 - -# command: subscribe lane context -CMD_SUBSCRIBE_LANE_CONTEXT = 0x83 -# response: subscribe lane context -RESPONSE_SUBSCRIBE_LANE_CONTEXT = 0x93 -# command: get lane variable -CMD_GET_LANE_VARIABLE = 0xa3 -# response: get lane variable -RESPONSE_GET_LANE_VARIABLE = 0xb3 -# command: set lane variable -CMD_SET_LANE_VARIABLE = 0xc3 -# command: subscribe lane variable -CMD_SUBSCRIBE_LANE_VARIABLE = 0xd3 -# response: subscribe lane variable -RESPONSE_SUBSCRIBE_LANE_VARIABLE = 0xe3 - -# command: subscribe vehicle context -CMD_SUBSCRIBE_VEHICLE_CONTEXT = 0x84 -# response: subscribe vehicle context -RESPONSE_SUBSCRIBE_VEHICLE_CONTEXT = 0x94 -# command: get vehicle variable -CMD_GET_VEHICLE_VARIABLE = 0xa4 -# response: get vehicle variable -RESPONSE_GET_VEHICLE_VARIABLE = 0xb4 -# command: set vehicle variable -CMD_SET_VEHICLE_VARIABLE = 0xc4 -# command: subscribe vehicle variable -CMD_SUBSCRIBE_VEHICLE_VARIABLE = 0xd4 -# response: subscribe vehicle variable -RESPONSE_SUBSCRIBE_VEHICLE_VARIABLE = 0xe4 - -# command: subscribe vehicle type context -CMD_SUBSCRIBE_VEHICLETYPE_CONTEXT = 0x85 -# response: subscribe vehicle type context -RESPONSE_SUBSCRIBE_VEHICLETYPE_CONTEXT = 0x95 -# command: get vehicle type variable -CMD_GET_VEHICLETYPE_VARIABLE = 0xa5 -# response: get vehicle type variable -RESPONSE_GET_VEHICLETYPE_VARIABLE = 0xb5 -# command: set vehicle type variable -CMD_SET_VEHICLETYPE_VARIABLE = 0xc5 -# command: subscribe vehicle type variable -CMD_SUBSCRIBE_VEHICLETYPE_VARIABLE = 0xd5 -# response: subscribe vehicle type variable -RESPONSE_SUBSCRIBE_VEHICLETYPE_VARIABLE = 0xe5 - -# command: subscribe route context -CMD_SUBSCRIBE_ROUTE_CONTEXT = 0x86 -# response: subscribe route context -RESPONSE_SUBSCRIBE_ROUTE_CONTEXT = 0x96 -# command: get route variable -CMD_GET_ROUTE_VARIABLE = 0xa6 -# response: get route variable -RESPONSE_GET_ROUTE_VARIABLE = 0xb6 -# command: set route variable -CMD_SET_ROUTE_VARIABLE = 0xc6 -# command: subscribe route variable -CMD_SUBSCRIBE_ROUTE_VARIABLE = 0xd6 -# response: subscribe route variable -RESPONSE_SUBSCRIBE_ROUTE_VARIABLE = 0xe6 - -# command: subscribe poi context -CMD_SUBSCRIBE_POI_CONTEXT = 0x87 -# response: subscribe poi context -RESPONSE_SUBSCRIBE_POI_CONTEXT = 0x97 -# command: get poi variable -CMD_GET_POI_VARIABLE = 0xa7 -# response: get poi variable -RESPONSE_GET_POI_VARIABLE = 0xb7 -# command: set poi variable -CMD_SET_POI_VARIABLE = 0xc7 -# command: subscribe poi variable -CMD_SUBSCRIBE_POI_VARIABLE = 0xd7 -# response: subscribe poi variable -RESPONSE_SUBSCRIBE_POI_VARIABLE = 0xe7 - -# command: subscribe polygon context -CMD_SUBSCRIBE_POLYGON_CONTEXT = 0x88 -# response: subscribe polygon context -RESPONSE_SUBSCRIBE_POLYGON_CONTEXT = 0x98 -# command: get polygon variable -CMD_GET_POLYGON_VARIABLE = 0xa8 -# response: get polygon variable -RESPONSE_GET_POLYGON_VARIABLE = 0xb8 -# command: set polygon variable -CMD_SET_POLYGON_VARIABLE = 0xc8 -# command: subscribe polygon variable -CMD_SUBSCRIBE_POLYGON_VARIABLE = 0xd8 -# response: subscribe polygon variable -RESPONSE_SUBSCRIBE_POLYGON_VARIABLE = 0xe8 - -# command: subscribe junction context -CMD_SUBSCRIBE_JUNCTION_CONTEXT = 0x89 -# response: subscribe junction context -RESPONSE_SUBSCRIBE_JUNCTION_CONTEXT = 0x99 -# command: get junction variable -CMD_GET_JUNCTION_VARIABLE = 0xa9 -# response: get junction variable -RESPONSE_GET_JUNCTION_VARIABLE = 0xb9 -# command: set junction variable -CMD_SET_JUNCTION_VARIABLE = 0xc9 -# command: subscribe junction variable -CMD_SUBSCRIBE_JUNCTION_VARIABLE = 0xd9 -# response: subscribe junction variable -RESPONSE_SUBSCRIBE_JUNCTION_VARIABLE = 0xe9 - -# command: subscribe edge context -CMD_SUBSCRIBE_EDGE_CONTEXT = 0x8a -# response: subscribe edge context -RESPONSE_SUBSCRIBE_EDGE_CONTEXT = 0x9a -# command: get edge variable -CMD_GET_EDGE_VARIABLE = 0xaa -# response: get edge variable -RESPONSE_GET_EDGE_VARIABLE = 0xba -# command: set edge variable -CMD_SET_EDGE_VARIABLE = 0xca -# command: subscribe edge variable -CMD_SUBSCRIBE_EDGE_VARIABLE = 0xda -# response: subscribe edge variable -RESPONSE_SUBSCRIBE_EDGE_VARIABLE = 0xea - -# command: subscribe simulation context -CMD_SUBSCRIBE_SIM_CONTEXT = 0x8b -# response: subscribe simulation context -RESPONSE_SUBSCRIBE_SIM_CONTEXT = 0x9b -# command: get simulation variable -CMD_GET_SIM_VARIABLE = 0xab -# response: get simulation variable -RESPONSE_GET_SIM_VARIABLE = 0xbb -# command: set simulation variable -CMD_SET_SIM_VARIABLE = 0xcb -# command: subscribe simulation variable -CMD_SUBSCRIBE_SIM_VARIABLE = 0xdb -# response: subscribe simulation variable -RESPONSE_SUBSCRIBE_SIM_VARIABLE = 0xeb - -# command: subscribe GUI context -CMD_SUBSCRIBE_GUI_CONTEXT = 0x8c -# response: subscribe GUI context -RESPONSE_SUBSCRIBE_GUI_CONTEXT = 0x9c -# command: get GUI variable -CMD_GET_GUI_VARIABLE = 0xac -# response: get GUI variable -RESPONSE_GET_GUI_VARIABLE = 0xbc -# command: set GUI variable -CMD_SET_GUI_VARIABLE = 0xcc -# command: subscribe GUI variable -CMD_SUBSCRIBE_GUI_VARIABLE = 0xdc -# response: subscribe GUI variable -RESPONSE_SUBSCRIBE_GUI_VARIABLE = 0xec - -# command: subscribe lane area detector (e2) context -CMD_SUBSCRIBE_LANEAREA_CONTEXT = 0x8d -# response: subscribe lane area detector (e2) context -RESPONSE_SUBSCRIBE_LANEAREA_CONTEXT = 0x9d -# command: get lane area detector (e2) variable -CMD_GET_LANEAREA_VARIABLE = 0xad -# response: get lane area detector (e2) variable -RESPONSE_GET_LANEAREA_VARIABLE = 0xbd -# command: set lane area detector (e2) variable, not used yet -CMD_SET_LANEAREA_VARIABLE = 0xcd -# command: subscribe lane area detector (e2) variable -CMD_SUBSCRIBE_LANEAREA_VARIABLE = 0xdd -# response: subscribe lane area detector (e2) variable -RESPONSE_SUBSCRIBE_LANEAREA_VARIABLE = 0xed - -# command: subscribe person context -CMD_SUBSCRIBE_PERSON_CONTEXT = 0x8e -# response: subscribe person context -RESPONSE_SUBSCRIBE_PERSON_CONTEXT = 0x9e -# command: get person variable -CMD_GET_PERSON_VARIABLE = 0xae -# response: get person variable -RESPONSE_GET_PERSON_VARIABLE = 0xbe -# command: set person variable -CMD_SET_PERSON_VARIABLE = 0xce -# command: subscribe person variable -CMD_SUBSCRIBE_PERSON_VARIABLE = 0xde -# response: subscribe person variable -RESPONSE_SUBSCRIBE_PERSON_VARIABLE = 0xee - -# command: subscribe busstop context -CMD_SUBSCRIBE_BUSSTOP_CONTEXT = 0x8f -# response: subscribe busstop context -RESPONSE_SUBSCRIBE_BUSSTOP_CONTEXT = 0x9f -# command: get busstop variable -CMD_GET_BUSSTOP_VARIABLE = 0xaf -# response: get busstop variable -RESPONSE_GET_BUSSTOP_VARIABLE = 0xbf -# command: set busstop variable, not used yet -CMD_SET_BUSSTOP_VARIABLE = 0xcf -# command: subscribe busstop variable -CMD_SUBSCRIBE_BUSSTOP_VARIABLE = 0xdf -# response: subscribe busstop variable -RESPONSE_SUBSCRIBE_BUSSTOP_VARIABLE = 0xef - -# command: subscribe parkingarea context -CMD_SUBSCRIBE_PARKINGAREA_CONTEXT = 0x04 -# response: subscribe parkingarea context -RESPONSE_SUBSCRIBE_PARKINGAREA_CONTEXT = 0x14 -# command: get parkingarea variable -CMD_GET_PARKINGAREA_VARIABLE = 0x24 -# response: get parkingarea variable -RESPONSE_GET_PARKINGAREA_VARIABLE = 0x34 -# command: set parkingarea variable -CMD_SET_PARKINGAREA_VARIABLE = 0x44 -# command: subscribe parkingarea variable -CMD_SUBSCRIBE_PARKINGAREA_VARIABLE = 0x54 -# response: subscribe parkingarea variable -RESPONSE_SUBSCRIBE_PARKINGAREA_VARIABLE = 0x64 - -# command: subscribe chargingstation context -CMD_SUBSCRIBE_CHARGINGSTATION_CONTEXT = 0x05 -# response: subscribe chargingstation context -RESPONSE_SUBSCRIBE_CHARGINGSTATION_CONTEXT = 0x15 -# command: get chargingstation variable -CMD_GET_CHARGINGSTATION_VARIABLE = 0x25 -# response: get chargingstation variable -RESPONSE_GET_CHARGINGSTATION_VARIABLE = 0x35 -# command: set chargingstation variable -CMD_SET_CHARGINGSTATION_VARIABLE = 0x45 -# command: subscribe chargingstation variable -CMD_SUBSCRIBE_CHARGINGSTATION_VARIABLE = 0x55 -# response: subscribe chargingstation variable -RESPONSE_SUBSCRIBE_CHARGINGSTATION_VARIABLE = 0x65 - -# command: subscribe routeprobe context -CMD_SUBSCRIBE_ROUTEPROBE_CONTEXT = 0x06 -# response: subscribe routeprobe context -RESPONSE_SUBSCRIBE_ROUTEPROBE_CONTEXT = 0x16 -# command: get routeprobe variable -CMD_GET_ROUTEPROBE_VARIABLE = 0x26 -# response: get routeprobe variable -RESPONSE_GET_ROUTEPROBE_VARIABLE = 0x36 -# command: set routeprobe variable -CMD_SET_ROUTEPROBE_VARIABLE = 0x46 -# command: subscribe routeprobe variable -CMD_SUBSCRIBE_ROUTEPROBE_VARIABLE = 0x56 -# response: subscribe routeprobe variable -RESPONSE_SUBSCRIBE_ROUTEPROBE_VARIABLE = 0x66 - -# command: subscribe calibrator context -CMD_SUBSCRIBE_CALIBRATOR_CONTEXT = 0x07 -# response: subscribe calibrator context -RESPONSE_SUBSCRIBE_CALIBRATOR_CONTEXT = 0x17 -# command: get calibrator variable -CMD_GET_CALIBRATOR_VARIABLE = 0x27 -# response: get calibrator variable -RESPONSE_GET_CALIBRATOR_VARIABLE = 0x37 -# command: set calibrator variable -CMD_SET_CALIBRATOR_VARIABLE = 0x47 -# command: subscribe calibrator variable -CMD_SUBSCRIBE_CALIBRATOR_VARIABLE = 0x57 -# response: subscribe calibrator variable -RESPONSE_SUBSCRIBE_CALIBRATOR_VARIABLE = 0x67 - -# command: subscribe rerouter context -CMD_SUBSCRIBE_REROUTER_CONTEXT = 0x08 -# response: subscribe rerouter context -RESPONSE_SUBSCRIBE_REROUTER_CONTEXT = 0x18 -# command: get rerouter variable -CMD_GET_REROUTER_VARIABLE = 0x28 -# response: get rerouter variable -RESPONSE_GET_REROUTER_VARIABLE = 0x38 -# command: set rerouter variable -CMD_SET_REROUTER_VARIABLE = 0x48 -# command: subscribe rerouter variable -CMD_SUBSCRIBE_REROUTER_VARIABLE = 0x58 -# response: subscribe rerouter variable -RESPONSE_SUBSCRIBE_REROUTER_VARIABLE = 0x68 - -# command: subscribe variablespeedsign context -CMD_SUBSCRIBE_VARIABLESPEEDSIGN_CONTEXT = 0x09 -# response: subscribe variablespeedsign context -RESPONSE_SUBSCRIBE_VARIABLESPEEDSIGN_CONTEXT = 0x19 -# command: get variablespeedsign variable -CMD_GET_VARIABLESPEEDSIGN_VARIABLE = 0x29 -# response: get variablespeedsign variable -RESPONSE_GET_VARIABLESPEEDSIGN_VARIABLE = 0x39 -# command: set variablespeedsign variable -CMD_SET_VARIABLESPEEDSIGN_VARIABLE = 0x49 -# command: subscribe variablespeedsign variable -CMD_SUBSCRIBE_VARIABLESPEEDSIGN_VARIABLE = 0x59 -# response: subscribe variablespeedsign variable -RESPONSE_SUBSCRIBE_VARIABLESPEEDSIGN_VARIABLE = 0x69 - -# command: subscribe meandata context -CMD_SUBSCRIBE_MEANDATA_CONTEXT = 0x0a -# response: subscribe meandata context -RESPONSE_SUBSCRIBE_MEANDATA_CONTEXT = 0x1a -# command: get meandata variable -CMD_GET_MEANDATA_VARIABLE = 0x2a -# response: get meandata variable -RESPONSE_GET_MEANDATA_VARIABLE = 0x3a -# command: set meandata variable, not used yet -CMD_SET_MEANDATA_VARIABLE = 0x4a -# command: subscribe meandata variable -CMD_SUBSCRIBE_MEANDATA_VARIABLE = 0x5a -# response: subscribe meandata variable -RESPONSE_SUBSCRIBE_MEANDATA_VARIABLE = 0x6a - -# command: subscribe overheadwire context -CMD_SUBSCRIBE_OVERHEADWIRE_CONTEXT = 0x0b -# response: subscribe overheadwire context -RESPONSE_SUBSCRIBE_OVERHEADWIRE_CONTEXT = 0x1b -# command: get overheadwire variable -CMD_GET_OVERHEADWIRE_VARIABLE = 0x2b -# response: get overheadwire variable -RESPONSE_GET_OVERHEADWIRE_VARIABLE = 0x3b -# command: set overheadwire variable -CMD_SET_OVERHEADWIRE_VARIABLE = 0x4b -# command: subscribe overheadwire variable -CMD_SUBSCRIBE_OVERHEADWIRE_VARIABLE = 0x5b -# response: subscribe overheadwire variable -RESPONSE_SUBSCRIBE_OVERHEADWIRE_VARIABLE = 0x6b - - -# **************************************** -# POSITION REPRESENTATIONS -# **************************************** -# Position in geo-coordinates -POSITION_LON_LAT = 0x00 -# 2D cartesian coordinates -POSITION_2D = 0x01 -# Position in geo-coordinates with altitude -POSITION_LON_LAT_ALT = 0x02 -# 3D cartesian coordinates -POSITION_3D = 0x03 -# Position on road map -POSITION_ROADMAP = 0x04 - - -# **************************************** -# DATA TYPES -# **************************************** -# Polygon (2*n doubles) -TYPE_POLYGON = 0x06 -# unsigned byte -TYPE_UBYTE = 0x07 -# signed byte -TYPE_BYTE = 0x08 -# 32 bit signed integer -TYPE_INTEGER = 0x09 -# double precision float -TYPE_DOUBLE = 0x0B -# 8 bit ASCII string -TYPE_STRING = 0x0C -# list of strings -TYPE_STRINGLIST = 0x0E -# compound object -TYPE_COMPOUND = 0x0F -# list of double precision floats -TYPE_DOUBLELIST = 0x10 -# color (four ubytes) -TYPE_COLOR = 0x11 - - -# **************************************** -# RESULT TYPES -# **************************************** -# result type: Ok -RTYPE_OK = 0x00 -# result type: not implemented -RTYPE_NOTIMPLEMENTED = 0x01 -# result type: error -RTYPE_ERR = 0xFF - -# **************************************** -# special return or parameter values -# **************************************** -# return value for invalid queries (especially vehicle is not on the road), see Position::INVALID -INVALID_DOUBLE_VALUE = -1073741824.0 -# return value for invalid queries (especially vehicle is not on the road), see Position::INVALID -INVALID_INT_VALUE = -1073741824 -# maximum value for client ordering (2 ^ 30) -MAX_ORDER = 1073741824 -# default number of connection attempts -DEFAULT_NUM_RETRIES = 60 - - -# **************************************** -# DIFFERENT DISTANCE REQUESTS -# **************************************** -# air distance -REQUEST_AIRDIST = 0x00 -# driving distance -REQUEST_DRIVINGDIST = 0x01 - - -# **************************************** -# VEHICLE REMOVAL REASONS -# **************************************** -# vehicle started teleport -REMOVE_TELEPORT = 0x00 -# vehicle removed while parking -REMOVE_PARKING = 0x01 -# vehicle arrived -REMOVE_ARRIVED = 0x02 -# vehicle was vaporized -REMOVE_VAPORIZED = 0x03 -# vehicle finished route during teleport -REMOVE_TELEPORT_ARRIVED = 0x04 - -# **************************************** -# VEHICLE MOVE REASONS -# **************************************** -# infer reason from move distance -MOVE_AUTOMATIC = 0x00 -# vehicle teleports to another location -MOVE_TELEPORT = 0x01 -# vehicle moved normally -MOVE_NORMAL = 0x02 - -# **************************************** -# PERSON/CONTAINER STAGES -# **************************************** -# person / container stopping -STAGE_WAITING_FOR_DEPART = 0x00 -# person / container stopping -STAGE_WAITING = 0x01 -# person walking -STAGE_WALKING = 0x02 -# person riding / container being transported -STAGE_DRIVING = 0x03 -# person accessing stopping place -STAGE_ACCESS = 0x04 -# stage for encoding abstract travel demand -STAGE_TRIP = 0x05 -# person / container transhiping -STAGE_TRANSHIP = 0x06 - -# **************************************** -# Stop Flags -# **************************************** -STOP_DEFAULT = 0x00 -STOP_PARKING = 0x01 -STOP_TRIGGERED = 0x02 -STOP_CONTAINER_TRIGGERED = 0x04 -STOP_BUS_STOP = 0x08 -STOP_CONTAINER_STOP = 0x10 -STOP_CHARGING_STATION = 0x20 -STOP_PARKING_AREA = 0x40 -STOP_OVERHEAD_WIRE = 0x80 - -# **************************************** -# Departure Flags -# **************************************** -DEPARTFLAG_TRIGGERED = -0x01 -DEPARTFLAG_CONTAINER_TRIGGERED = -0x02 -DEPARTFLAG_NOW = -0x03 - -DEPARTFLAG_SPEED_RANDOM = -0x02 -DEPARTFLAG_SPEED_MAX = -0x03 - -DEPARTFLAG_LANE_RANDOM = -0x02 -DEPARTFLAG_LANE_FREE = -0x03 -DEPARTFLAG_LANE_ALLOWED_FREE = -0x04 -DEPARTFLAG_LANE_BEST_FREE = -0x05 -DEPARTFLAG_LANE_FIRST_ALLOWED = -0x06 - -DEPARTFLAG_POS_RANDOM = -0x02 -DEPARTFLAG_POS_FREE = -0x03 -DEPARTFLAG_POS_BASE = -0x04 -DEPARTFLAG_POS_LAST = -0x05 -DEPARTFLAG_POS_RANDOM_FREE = -0x06 - -ARRIVALFLAG_LANE_CURRENT = -0x02 -ARRIVALFLAG_SPEED_CURRENT = -0x02 - -ARRIVALFLAG_POS_RANDOM = -0x02 -ARRIVALFLAG_POS_MAX = -0x03 - -# **************************************** -# Routing modes -# **************************************** -# use custom weights if available, fall back to loaded weights and then to free-flow speed -ROUTING_MODE_DEFAULT = 0x00 -# use aggregated travel times from device.rerouting -ROUTING_MODE_AGGREGATED = 0x01 -# use loaded efforts -ROUTING_MODE_EFFORT = 0x02 -# use combined costs -ROUTING_MODE_COMBINED = 0x03 - -# **************************************** -# Traffic light types -# **************************************** -TRAFFICLIGHT_TYPE_STATIC = 0x00 -TRAFFICLIGHT_TYPE_ACTUATED = 0x03 -TRAFFICLIGHT_TYPE_DELAYBASED = 0x04 - -# **************************************** -# FILTER TYPES (for context subscription filters) -# **************************************** - -# Reset all filters -FILTER_TYPE_NONE = 0x00 - -# Filter by list of lanes relative to ego vehicle -FILTER_TYPE_LANES = 0x01 - -# Exclude vehicles on opposite (and other) lanes from context subscription result -FILTER_TYPE_NOOPPOSITE = 0x02 - -# Specify maximal downstream distance for vehicles in context subscription result -FILTER_TYPE_DOWNSTREAM_DIST = 0x03 - -# Specify maximal upstream distance for vehicles in context subscription result -FILTER_TYPE_UPSTREAM_DIST = 0x04 - -# Only return leader and follower on the specified lanes in context subscription result -FILTER_TYPE_LEAD_FOLLOW = 0x05 - -# Only return foes on upcoming junctions in context subscription result -FILTER_TYPE_TURN = 0x07 - -# Only return vehicles of the given vClass in context subscription result -FILTER_TYPE_VCLASS = 0x08 - -# Only return vehicles of the given vType in context subscription result -FILTER_TYPE_VTYPE = 0x09 - -# Only return vehicles within field of vision in context subscription result -FILTER_TYPE_FIELD_OF_VISION = 0x0A - -# Only return vehicles within the given lateral distance in context subscription result -FILTER_TYPE_LATERAL_DIST = 0x0B - -# **************************************** -# VARIABLE TYPES (for CMD_GET_*_VARIABLE) -# **************************************** -# list of instances' ids (get: all) -TRACI_ID_LIST = 0x00 - -# count of instances (get: all) -ID_COUNT = 0x01 - -# subscribe object variables (get: all) -AUTOMATIC_VARIABLES_SUBSCRIPTION = 0x02 - -# subscribe context variables (get: all) -AUTOMATIC_CONTEXT_SUBSCRIPTION = 0x03 - -# generic attributes (get/set: all) -GENERIC_ATTRIBUTE = 0x03 - -# last step vehicle number (get: induction loops, multi-entry/multi-exit detector, lanes, edges) -LAST_STEP_VEHICLE_NUMBER = 0x10 - -# last step vehicle number (get: induction loops, multi-entry/multi-exit detector, lanes, edges) -LAST_STEP_MEAN_SPEED = 0x11 - -# last step vehicle list (get: induction loops, multi-entry/multi-exit detector, lanes, edges) -LAST_STEP_VEHICLE_ID_LIST = 0x12 - -# last step occupancy (get: induction loops, lanes, edges) -LAST_STEP_OCCUPANCY = 0x13 - -# last step vehicle halting number (get: multi-entry/multi-exit detector, lanes, edges) -LAST_STEP_VEHICLE_HALTING_NUMBER = 0x14 - -# last step mean vehicle length (get: induction loops, lanes, edges) -LAST_STEP_LENGTH = 0x15 - -# last step time since last detection (get: induction loops) -LAST_STEP_TIME_SINCE_DETECTION = 0x16 - -# entry times -LAST_STEP_VEHICLE_DATA = 0x17 - -# last step jam length in vehicles -JAM_LENGTH_VEHICLE = 0x18 - -# last step jam length in meters -JAM_LENGTH_METERS = 0x19 - -# last interval travel time (get: e3) -VAR_LAST_INTERVAL_TRAVELTIME = 0x58 - -# last step vehicle halting number (get: multi-entry/multi-exit detector) -VAR_LAST_INTERVAL_MEAN_HALTING_NUMBER = 0x20 - -# last interval vehicle count(get: e3) -VAR_LAST_INTERVAL_VEHICLE_NUMBER = 0x21 - -# last step person list (get: edges, vehicles) -LAST_STEP_PERSON_ID_LIST = 0x1a - -# full name (get: edges, simulation, trafficlight) -VAR_NAME = 0x1b - -# carFollowModel function followSpeed (get: vehicle) -VAR_FOLLOW_SPEED = 0x1c - -# carFollowModel function stopSpeed (get: vehicle) -VAR_STOP_SPEED = 0x1d - -# carFollowModel function getSecureGap (get: vehicle) -VAR_SECURE_GAP = 0x1e - -# estimated (depart) delay for next stop (get: vehicle) -VAR_STOP_DELAY = 0x1f - -# estimated arrival delay for next stop (get: vehicle) -VAR_STOP_ARRIVALDELAY = 0x22 - -# collected timeLoss since departure (get: vehicle, e3) -VAR_TIMELOSS = 0x8c - -# begin time(get: calibrator) -VAR_BEGIN = 0x1c - -# end time(get: calibrator, simulation) -VAR_END = 0x1d - -# vtype list (get: calibrator) -VAR_VTYPES = 0x1e - -# vehicles per hour (get: calibrator) -VAR_VEHSPERHOUR = 0x13 - -# passed vehicle count (get: calibrator) -VAR_PASSED = 0x14 - -# inserted vehicle count (get: calibrator) -VAR_INSERTED = 0x15 - -# removed vehicle count (get: calibrator) -VAR_REMOVED = 0x16 - -# routeProbe id (get: calibrator) -VAR_ROUTE_PROBE = 0x17 - -# routeProbe id (get: calibrator) -CMD_SET_FLOW = 0x18 - -# traffic light states, encoded as rRgGyYoO tuple (get: traffic lights) -TL_RED_YELLOW_GREEN_STATE = 0x20 - -# index of the phase (set: traffic lights) -TL_PHASE_INDEX = 0x22 - -# traffic light program (set: traffic lights) -TL_PROGRAM = 0x23 - -# phase duration (set: traffic lights) -TL_PHASE_DURATION = 0x24 - -# vehicles that block passing the given signal (get: traffic lights) -TL_BLOCKING_VEHICLES = 0x25 - -# controlled lanes (get: traffic lights) -TL_CONTROLLED_LANES = 0x26 - -# controlled links (get: traffic lights) -TL_CONTROLLED_LINKS = 0x27 - -# index of the current phase (get: traffic lights) -TL_CURRENT_PHASE = 0x28 - -# name of the current program (get: traffic lights) -TL_CURRENT_PROGRAM = 0x29 - -# vehicles that also wish to pass the given signal (get: traffic lights) -TL_RIVAL_VEHICLES = 0x30 - -# vehicles that also wish to pass the given signal and have higher priority (get: traffic lights) -TL_PRIORITY_VEHICLES = 0x31 - -# controlled junctions (get: traffic lights) -TL_CONTROLLED_JUNCTIONS = 0x2a - -# complete definition (get: traffic lights) -TL_COMPLETE_DEFINITION_RYG = 0x2b - -# complete program (set: traffic lights) -TL_COMPLETE_PROGRAM_RYG = 0x2c - -# assumed time to next switch (get: traffic lights) -TL_NEXT_SWITCH = 0x2d - -# current state, using external signal names (get: traffic lights) -TL_EXTERNAL_STATE = 0x2e - -# add/get rail signal constraints -TL_CONSTRAINT = 0x2f - -# switch order of trains encoded in rail signal constraints (set: traffic lights) -TL_CONSTRAINT_SWAP = 0x32 - -# add/get rail signal constraints by foeSignal (get: traffic lights) -TL_CONSTRAINT_BYFOE = 0x34 - -# add/get rail signal constraints by foeSignal (set: traffic lights) -TL_CONSTRAINT_REMOVE = 0x35 - -# outgoing link number (get: lanes) -LANE_LINK_NUMBER = 0x30 - -# id of parent edge (get: lanes) -LANE_EDGE_ID = 0x31 - -# outgoing link definitions (get: lanes) -LANE_LINKS = 0x33 - -# list of allowed vehicle classes (get&set: lanes) -LANE_ALLOWED = 0x34 - -# list of not allowed vehicle classes (get&set: lanes) -LANE_DISALLOWED = 0x35 - -# list of foe lanes (get: lanes) -VAR_FOES = 0x37 - -# slope (get: edge, lane, vehicle, person) -VAR_SLOPE = 0x36 - -# speed (get: vehicle) -VAR_SPEED = 0x40 - -# adapt previous speed (set: vehicle) -VAR_PREV_SPEED = 0x3c - -# lateral speed (get: vehicle) -VAR_SPEED_LAT = 0x32 - -# maximum allowed/possible speed (get: vehicle types, lanes, set: edges, lanes) -VAR_MAXSPEED = 0x41 - -# position (2D) (get: vehicle, poi, inductionloop, lane area detector; set: poi) -VAR_POSITION = 0x42 - -# position (3D) (get: vehicle, poi, set: poi) -VAR_POSITION3D = 0x39 - -# angle (get: vehicle, poi; set: poi) -VAR_ANGLE = 0x43 - -# length (get: vehicle types, lanes, lane area detector, set: lanes) -VAR_LENGTH = 0x44 - -# color (get: vehicles, vehicle types, polygons, pois) -VAR_COLOR = 0x45 - -# max. acceleration (get: vehicles, vehicle types) -VAR_ACCEL = 0x46 - -# max. comfortable deceleration (get: vehicles, vehicle types) -VAR_DECEL = 0x47 - -# max. (physically possible) deceleration (get: vehicles, vehicle types) -VAR_EMERGENCY_DECEL = 0x7b - -# apparent deceleration (get: vehicles, vehicle types) -VAR_APPARENT_DECEL = 0x7c - -# action step length (get: vehicles, vehicle types) -VAR_ACTIONSTEPLENGTH = 0x7d - -# last action time (get: vehicles) -VAR_LASTACTIONTIME = 0x7f - -# driver's desired headway (get: vehicle types) -VAR_TAU = 0x48 - -# vehicle class (get: vehicle types) -VAR_VEHICLECLASS = 0x49 - -# emission class (get: vehicle types) -VAR_EMISSIONCLASS = 0x4a - -# shape class (get: vehicle types) -VAR_SHAPECLASS = 0x4b - -# minimum gap (get: vehicle types) -VAR_MINGAP = 0x4c - -# width (get: vehicle types, lanes, polygons, poi) -VAR_WIDTH = 0x4d - -# shape (get: polygons) -VAR_SHAPE = 0x4e - -# type id (get: vehicles, polygons, pois) -VAR_TYPE = 0x4f - -# road id (get: vehicles) -VAR_ROAD_ID = 0x50 - -# lane id (get: vehicles, inductionloop, lane area detector) -VAR_LANE_ID = 0x51 - -# lane index (get: vehicle, edge) -VAR_LANE_INDEX = 0x52 - -# route id (get & set: vehicles) -VAR_ROUTE_ID = 0x53 - -# edges (get: routes, vehicles) -VAR_EDGES = 0x54 - -# lanes (get: variablespeedsign) -VAR_LANES = 0x30 - -# update bestLanes (set: vehicle) -VAR_UPDATE_BESTLANES = 0x6a - -# filled? (get: polygons) -VAR_FILL = 0x55 - -# get/set image file (poi, poly, vehicle, person, simulation) -VAR_IMAGEFILE = 0x93 - -# position (1D along lane) (get: vehicle) -VAR_LANEPOSITION = 0x56 - -# route (set: vehicles) -VAR_ROUTE = 0x57 - -# travel time information (get&set: vehicle) -VAR_EDGE_TRAVELTIME = 0x58 - -# effort information (get&set: vehicle) -VAR_EDGE_EFFORT = 0x59 - -# last step travel time (get: edge, lane, e3) -VAR_CURRENT_TRAVELTIME = 0x5a - -# signals state (get/set: vehicle) -VAR_SIGNALS = 0x5b - -# vehicle: new lane/position along (set: vehicle) -VAR_MOVE_TO = 0x5c - -# polygon: add dynamics (set: polygon) -VAR_ADD_DYNAMICS = 0x5c - -# vehicle: highlight (set: vehicle, poi) -VAR_HIGHLIGHT = 0x6c - -# driver imperfection (set: vehicle) -VAR_IMPERFECTION = 0x5d - -# speed factor (set: vehicle) -VAR_SPEED_FACTOR = 0x5e - -# speed deviation (set: vehicle) -VAR_SPEED_DEVIATION = 0x5f - -# routing mode (get/set: vehicle) -VAR_ROUTING_MODE = 0x89 - -# speed without TraCI influence (get: vehicle) -VAR_SPEED_WITHOUT_TRACI = 0xb1 - -# best lanes (get: vehicle) -VAR_BEST_LANES = 0xb2 - -# how speed is set (set: vehicle) -VAR_SPEEDSETMODE = 0xb3 - -# move vehicle to explicit (remote controlled) position (set: vehicle) -MOVE_TO_XY = 0xb4 - -# is the vehicle stopped, and if so parked and/or triggered? -# value = stopped + 2 * parking + 4 * triggered -VAR_STOPSTATE = 0xb5 - -# how lane changing is performed (get/set: vehicle) -VAR_LANECHANGE_MODE = 0xb6 - -# maximum speed regarding max speed on the current lane and speed factor (get: vehicle) -VAR_ALLOWED_SPEED = 0xb7 - -# position (1D lateral position relative to center of the current lane) (get: vehicle) -VAR_LANEPOSITION_LAT = 0xb8 - -# get/set prefered lateral alignment within the lane (vehicle) -VAR_LATALIGNMENT = 0xb9 - -# get/set maximum lateral speed (vehicle, vtypes) -VAR_MAXSPEED_LAT = 0xba - -# get/set minimum lateral gap (vehicle, vtypes) -VAR_MINGAP_LAT = 0xbb - -# get/set vehicle height (vehicle, vtypes, poi) -VAR_HEIGHT = 0xbc - -# get/set vehicle line -VAR_LINE = 0xbd - -# get/set vehicle via -VAR_VIA = 0xbe - -# get (lane change relevant) neighboring vehicles (vehicles) -VAR_NEIGHBORS = 0xbf - -# current CO2 emission of a node (get: vehicle, lane, edge) -VAR_CO2EMISSION = 0x60 - -# current CO emission of a node (get: vehicle, lane, edge) -VAR_COEMISSION = 0x61 - -# current HC emission of a node (get: vehicle, lane, edge) -VAR_HCEMISSION = 0x62 - -# current PMx emission of a node (get: vehicle, lane, edge) -VAR_PMXEMISSION = 0x63 - -# current NOx emission of a node (get: vehicle, lane, edge) -VAR_NOXEMISSION = 0x64 - -# current fuel consumption of a node (get: vehicle, lane, edge) -VAR_FUELCONSUMPTION = 0x65 - -# current noise emission of a node (get: vehicle, lane, edge) -VAR_NOISEEMISSION = 0x66 - -# current person number (get: vehicle, trafficlight) -VAR_PERSON_NUMBER = 0x67 - -# person capacity (vehicle , vehicle type) -VAR_PERSON_CAPACITY = 0x38 - -VAR_BUS_STOP_ID_LIST = 0x9f - -# number of persons waiting at a defined bus stop (get: simulation) -VAR_BUS_STOP_WAITING = 0x67 - -# ids of persons waiting at a defined bus stop (get: simulation) -VAR_BUS_STOP_WAITING_IDS = 0xef - -# current leader together with gap (get: vehicle) -VAR_LEADER = 0x68 - -# current leader together with gap (get: vehicle) -VAR_FOLLOWER = 0x78 - -# edge index in current route (get: vehicle) -VAR_ROUTE_INDEX = 0x69 - -# current waiting time (get: vehicle, lane) -VAR_WAITING_TIME = 0x7a - -# current waiting time (get: vehicle) -VAR_ACCUMULATED_WAITING_TIME = 0x87 - -# upcoming traffic lights (get: vehicle) -VAR_NEXT_TLS = 0x70 - -# upcoming stops (get: vehicle) -VAR_NEXT_STOPS = 0x73 - -# upcoming stops with selection (get: vehicle) -VAR_NEXT_STOPS2 = 0x74 - -# current acceleration (get: vehicle) -VAR_ACCELERATION = 0x72 - -# arrival position (get,set: vehicle) -VAR_ARRIVALPOS = 0x75 - -# arrival lane (get,set: vehicle) -VAR_ARRIVALLANE = 0x76 - -# arrival speed (get,set: vehicle) -VAR_ARRIVALSPEED = 0x77 - -# add log message (set: simulation) -CMD_MESSAGE = 0x65 - -# current time in seconds (get: simulation) -VAR_TIME = 0x66 - -# current time step (get: simulation) -VAR_TIME_STEP = 0x70 - -# current electricity consumption of a node (get: vehicle, lane, edge) -VAR_ELECTRICITYCONSUMPTION = 0x71 - -# number of loaded vehicles (get: simulation) -VAR_LOADED_VEHICLES_NUMBER = 0x71 - -# loaded vehicle ids (get: simulation) -VAR_LOADED_VEHICLES_IDS = 0x72 - -# number of departed vehicle (get: simulation) -VAR_DEPARTED_VEHICLES_NUMBER = 0x73 - -# departed vehicle ids (get: simulation) -VAR_DEPARTED_VEHICLES_IDS = 0x74 - -# number of vehicles starting to teleport (get: simulation) -VAR_TELEPORT_STARTING_VEHICLES_NUMBER = 0x75 - -# ids of vehicles starting to teleport (get: simulation) -VAR_TELEPORT_STARTING_VEHICLES_IDS = 0x76 - -# number of vehicles ending to teleport (get: simulation) -VAR_TELEPORT_ENDING_VEHICLES_NUMBER = 0x77 - -# ids of vehicles ending to teleport (get: simulation) -VAR_TELEPORT_ENDING_VEHICLES_IDS = 0x78 - -# number of arrived vehicles (get: simulation) -VAR_ARRIVED_VEHICLES_NUMBER = 0x79 - -# ids of arrived vehicles (get: simulation) -VAR_ARRIVED_VEHICLES_IDS = 0x7a - -# delta t (get: simulation) -VAR_DELTA_T = 0x7b - -# bounding box (get: simulation) -VAR_NET_BOUNDING_BOX = 0x7c - -# minimum number of expected vehicles (get: simulation) -VAR_MIN_EXPECTED_VEHICLES = 0x7d - -# number of departed persons (get: simulation) -VAR_DEPARTED_PERSONS_NUMBER = 0x24 - -# departed person ids (get: simulation) -VAR_DEPARTED_PERSONS_IDS = 0x25 - -# number of arrived persons (get: simulation) -VAR_ARRIVED_PERSONS_NUMBER = 0x26 - -# ids of arrived persons (get: simulation) -VAR_ARRIVED_PERSONS_IDS = 0x27 - -# number of vehicles starting to park (get: simulation) -VAR_STOP_STARTING_VEHICLES_NUMBER = 0x68 - -# ids of vehicles starting to park (get: simulation) -VAR_STOP_STARTING_VEHICLES_IDS = 0x69 - -# number of vehicles ending to park (get: simulation) -VAR_STOP_ENDING_VEHICLES_NUMBER = 0x6a - -# ids of vehicles ending to park (get: simulation) -VAR_STOP_ENDING_VEHICLES_IDS = 0x6b - -# number of vehicles starting to park (get: simulation) -VAR_PARKING_STARTING_VEHICLES_NUMBER = 0x6c - -# ids of vehicles starting to park (get: simulation) -VAR_PARKING_STARTING_VEHICLES_IDS = 0x6d - -# number of vehicles maneuvering (get: simulation) -VAR_PARKING_MANEUVERING_VEHICLES_NUMBER = 0x3a - -# ids of vehicles maneuvering (get: simulation) -VAR_PARKING_MANEUVERING_VEHICLES_IDS = 0x3b - -# number of vehicles ending to park (get: simulation) -VAR_PARKING_ENDING_VEHICLES_NUMBER = 0x6e - -# ids of vehicles ending to park (get: simulation) -VAR_PARKING_ENDING_VEHICLES_IDS = 0x6f - -# number of vehicles involved in a collision (get: simulation) -VAR_COLLIDING_VEHICLES_NUMBER = 0x80 - -# ids of vehicles involved in a collision (get: simulation) -VAR_COLLIDING_VEHICLES_IDS = 0x81 - -# number of vehicles involved in a collision (get: simulation) -VAR_EMERGENCYSTOPPING_VEHICLES_NUMBER = 0x89 - -# ids of vehicles involved in a collision (get: simulation) -VAR_EMERGENCYSTOPPING_VEHICLES_IDS = 0x8a - -# clears the simulation of all not inserted vehicles (set: simulation) -CMD_CLEAR_PENDING_VEHICLES = 0x94 - -# retrieve number of not inserted vehicles (get: simulation, edge, lane) -VAR_PENDING_VEHICLES = 0x94 - -# triggers saving simulation state (set: simulation) -CMD_SAVE_SIMSTATE = 0x95 - -# triggers saving simulation state (set: simulation) -CMD_LOAD_SIMSTATE = 0x96 - -# retrieve detail data for each collision -VAR_COLLISIONS = 0x23 - -# sets/retrieves abstract parameter -VAR_PARAMETER = 0x7e - -# retrieves abstract parameter and returns (key, value) tuple -VAR_PARAMETER_WITH_KEY = 0x3e - - -# add an instance (poi, polygon, vehicle, person, route) -ADD = 0x80 - -# remove an instance (poi, polygon, vehicle, person) -REMOVE = 0x81 - -# copy an instance (vehicle type, other TBD.) -COPY = 0x88 - -# convert coordinates -POSITION_CONVERSION = 0x82 - -# distance between points or vehicles -DISTANCE_REQUEST = 0x83 - -# the current driving distance -VAR_DISTANCE = 0x84 - -# add a fully specified instance (vehicle) -ADD_FULL = 0x85 - -# find a car based route -FIND_ROUTE = 0x86 - -# find an intermodal route -FIND_INTERMODAL_ROUTE = 0x87 - -# force rerouting based on travel time (vehicles) -CMD_REROUTE_TRAVELTIME = 0x90 - -# force rerouting based on effort (vehicles) -CMD_REROUTE_EFFORT = 0x91 - -# validates current route (vehicles) -VAR_ROUTE_VALID = 0x92 - -# retrieve information regarding the current person/container stage -VAR_STAGE = 0xc0 - -# retrieve information regarding the next edge including crossings and walkingAreas (pedestrians only) -VAR_NEXT_EDGE = 0xc1 - -# retrieve information regarding the number of remaining stages -VAR_STAGES_REMAINING = 0xc2 - -# retrieve the current vehicle id for the driving stage (person, container) -VAR_VEHICLE = 0xc3 - -# append a person stage (person) -APPEND_STAGE = 0xc4 - -# replace a person stage (person) -REPLACE_STAGE = 0xcd - -# append a person stage (person) -REMOVE_STAGE = 0xc5 - -# retrieve taxi reservation (person) -VAR_TAXI_RESERVATIONS = 0xc6 - -# manipulate taxi reservation (person) -SPLIT_TAXI_RESERVATIONS = 0xc7 - -# sample last route (routeprobe) -VAR_SAMPLE_LAST = 0x20 - -# sample current route (routeprobe) -VAR_SAMPLE_CURRENT = 0x21 - -# zoom -VAR_VIEW_ZOOM = 0xa0 - -# view position -VAR_VIEW_OFFSET = 0xa1 - -# view schema -VAR_VIEW_SCHEMA = 0xa2 - -# view by boundary -VAR_VIEW_BOUNDARY = 0xa3 - -# select/deselect object (gui) -VAR_SELECT = 0xa4 - -# screenshot -VAR_SCREENSHOT = 0xa5 - -# track vehicle -VAR_TRACK_VEHICLE = 0xa6 - -# presence of view -VAR_HAS_VIEW = 0xa7 - -# @name currently wanted lane-change action -# @{ -# @brief No action desired -LCA_NONE = 0 -# @brief Needs to stay on the current lane -LCA_STAY = 1 << 0 -# @brief Wants go to the left -LCA_LEFT = 1 << 1 -# @brief Wants go to the right -LCA_RIGHT = 1 << 2 -# @brief The action is needed to follow the route (navigational lc) -LCA_STRATEGIC = 1 << 3 -# @brief The action is done to help someone else -LCA_COOPERATIVE = 1 << 4 -# @brief The action is due to the wish to be faster (tactical lc) -LCA_SPEEDGAIN = 1 << 5 -# @brief The action is due to the default of keeping right "Rechtsfahrgebot" -LCA_KEEPRIGHT = 1 << 6 -# @brief The action is due to a TraCI request -LCA_TRACI = 1 << 7 -# @brief The action is urgent (to be defined by lc-model) -LCA_URGENT = 1 << 8 -# @brief The action has not been determined -LCA_UNKNOWN = 1 << 30 -# @} - -# @name External state -# @{ -# @brief The vehicle is blocked by left leader -LCA_BLOCKED_BY_LEFT_LEADER = 1 << 9 -# @brief The vehicle is blocked by left follower -LCA_BLOCKED_BY_LEFT_FOLLOWER = 1 << 10 -# @brief The vehicle is blocked by right leader -LCA_BLOCKED_BY_RIGHT_LEADER = 1 << 11 -# @brief The vehicle is blocked by right follower -LCA_BLOCKED_BY_RIGHT_FOLLOWER = 1 << 12 -# @brief The vehicle is blocked being overlapping -LCA_OVERLAPPING = 1 << 13 -# @brief The vehicle does not have enough space to complete a continuous change before the next turn -LCA_INSUFFICIENT_SPACE = 1 << 14 -# @brief used by the sublane model -LCA_SUBLANE = 1 << 15 -# @brief Vehicle is too slow to complete a continuous lane change (in case that maxSpeedLatStanding==0) -LCA_INSUFFICIENT_SPEED = 1 << 28 -# @brief lane can change -LCA_WANTS_LANECHANGE = LCA_LEFT | LCA_RIGHT -# @brief lane can change or stay -LCA_WANTS_LANECHANGE_OR_STAY = LCA_WANTS_LANECHANGE | LCA_STAY -# @brief blocked left -LCA_BLOCKED_LEFT = LCA_BLOCKED_BY_LEFT_LEADER | LCA_BLOCKED_BY_LEFT_FOLLOWER -# @brief blocked right -LCA_BLOCKED_RIGHT = LCA_BLOCKED_BY_RIGHT_LEADER | LCA_BLOCKED_BY_RIGHT_FOLLOWER -# @brief blocked by leader -LCA_BLOCKED_BY_LEADER = LCA_BLOCKED_BY_LEFT_LEADER | LCA_BLOCKED_BY_RIGHT_LEADER -# @brief blocker by follower -LCA_BLOCKED_BY_FOLLOWER = LCA_BLOCKED_BY_LEFT_FOLLOWER | LCA_BLOCKED_BY_RIGHT_FOLLOWER -# @brief blocked in all directions -LCA_BLOCKED = LCA_BLOCKED_LEFT | LCA_BLOCKED_RIGHT | LCA_INSUFFICIENT_SPACE | LCA_INSUFFICIENT_SPEED -# @brief reasons of lane change -LCA_CHANGE_REASONS = (LCA_STRATEGIC | LCA_COOPERATIVE | LCA_SPEEDGAIN | LCA_KEEPRIGHT | LCA_SUBLANE | LCA_TRACI) -# LCA_BLOCKED_BY_CURRENT_LEADER = 1 << 28 -# LCA_BLOCKED_BY_CURRENT_FOLLOWER = 1 << 29 -# @} - -# @name originally model specific states (migrated here since -# they were duplicated in all current models) -# @{ -LCA_AMBLOCKINGLEADER = 1 << 16 -LCA_AMBLOCKINGFOLLOWER = 1 << 17 -LCA_MRIGHT = 1 << 18 -LCA_MLEFT = 1 << 19 -# !!! never set LCA_UNBLOCK = 1 << 20, -LCA_AMBLOCKINGFOLLOWER_DONTBRAKE = 1 << 21 -# !!! never used LCA_AMBLOCKINGSECONDFOLLOWER = 1 << 22, -LCA_CHANGE_TO_HELP = 1 << 23 -# !!! never read LCA_KEEP1 = 1 << 24, -# !!! never used LCA_KEEP2 = 1 << 25, -LCA_AMBACKBLOCKER = 1 << 26 -LCA_AMBACKBLOCKER_STANDING = 1 << 27 -# @} diff --git a/co-simulation/patch/main.py b/co-simulation/patch/main.py deleted file mode 100644 index 33f2cb99..00000000 --- a/co-simulation/patch/main.py +++ /dev/null @@ -1,326 +0,0 @@ -# -*- coding: utf-8 -*- -# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo -# Copyright (C) 2008-2022 German Aerospace Center (DLR) and others. -# This program and the accompanying materials are made available under the -# terms of the Eclipse Public License 2.0 which is available at -# https://www.eclipse.org/legal/epl-2.0/ -# This Source Code may also be made available under the following Secondary -# Licenses when the conditions for such availability set forth in the Eclipse -# Public License 2.0 are satisfied: GNU General Public License, version 2 -# or later which is available at -# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html -# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later - -# @file main.py -# @author Michael Behrisch -# @author Lena Kalleske -# @author Mario Krumnow -# @author Daniel Krajzewicz -# @author Jakob Erdmann -# @date 2008-10-09 - -# pylint: disable=E1101 - -from __future__ import print_function -from __future__ import absolute_import -import socket -import time -import subprocess -import warnings -import sys -import os -from functools import wraps - -if 'SUMO_HOME' in os.environ: - sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools')) -else: - sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -import sumolib # noqa -from sumolib.miscutils import getFreeSocketPort # noqa - -from .domain import _defaultDomains # noqa -# StepListener needs to be imported for backwards compatibility -from .connection import Connection, StepListener # noqa -from .exceptions import FatalTraCIError, TraCIException # noqa -from . import _inductionloop, _lanearea, _multientryexit, _trafficlight # noqa -from . import _variablespeedsign, _meandata # noqa -from . import _lane, _person, _route, _vehicle, _vehicletype # noqa -from . import _edge, _gui, _junction, _poi, _polygon, _simulation # noqa -from . import _calibrator, _routeprobe, _rerouter # noqa -from . import _busstop, _parkingarea, _chargingstation, _overheadwire # noqa -from . import constants as tc # noqa - -inductionloop = _inductionloop.InductionLoopDomain() -lanearea = _lanearea.LaneAreaDomain() -multientryexit = _multientryexit.MultiEntryExitDomain() -trafficlight = _trafficlight.TrafficLightDomain() -variablespeedsign = _variablespeedsign.VariableSpeedSignDomain() -meandata = _meandata.MeanDataDomain() -lane = _lane.LaneDomain() -person = _person.PersonDomain() -route = _route.RouteDomain() -vehicle = _vehicle.VehicleDomain() -vehicletype = _vehicletype.VehicleTypeDomain() -edge = _edge.EdgeDomain() -gui = _gui.GuiDomain() -junction = _junction.JunctionDomain() -poi = _poi.PoiDomain() -polygon = _polygon.PolygonDomain() -simulation = _simulation.SimulationDomain() -calibrator = _calibrator.CalibratorDomain() -busstop = _busstop.BusStopDomain() -parkingarea = _parkingarea.ParkingAreaDomain() -chargingstation = _chargingstation.ChargingStationDomain() -overheadwire = _overheadwire.OverheadWireDomain() -routeprobe = _routeprobe.RouteProbeDomain() -rerouter = _rerouter.RerouterDomain() - -_connections = {} -_traceFile = {} -_traceGetters = {} -# cannot use immutable type as global variable -_currentLabel = [""] -_connectHook = None - - -def _STEPS2TIME(step): - """Conversion from time steps in milliseconds to seconds as float""" - return step / 1000. - - -def setConnectHook(hookFunc): - global _connectHook - _connectHook = hookFunc - - -def _addTracing(method): - @wraps(method) - def tracingWrapper(*args, **kwargs): - _traceFile[_currentLabel[0]].write("traci.%s(%s)\n" % ( - method.__name__, - ', '.join(list(map(repr, args)) + ["%s=%s" % (n, repr(v)) for n, v in kwargs.items()]))) - return method(*args, **kwargs) - return tracingWrapper - - -def connect(port=8813, numRetries=tc.DEFAULT_NUM_RETRIES, host="localhost", proc=None, waitBetweenRetries=1): - """ - Establish a connection to a TraCI-Server and return the - connection object. The connection is not saved in the pool and not - accessible via traci.switch. It should be safe to use different - connections established by this method in different threads. - """ - for retry in range(1, numRetries + 2): - try: - conn = Connection(host, port, proc) - if _connectHook is not None: - _connectHook(conn) - return conn - except socket.error as e: - if proc is not None and proc.poll() is not None: - raise TraCIException("TraCI server already finished") - if retry > 1: - print("Could not connect to TraCI server at %s:%s" % (host, port), e) - if retry < numRetries + 1: - print(" Retrying in %s seconds" % waitBetweenRetries) - time.sleep(waitBetweenRetries) - raise FatalTraCIError("Could not connect in %s tries" % (numRetries + 1)) - -def getV2xMessage(): - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].getV2xMessage() - -def setV2xMessage(message): - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].setV2xMessage(message) - - -def init(port=8813, numRetries=tc.DEFAULT_NUM_RETRIES, host="localhost", label="default", proc=None, doSwitch=True): - """ - Establish a connection to a TraCI-Server and store it under the given - label. This method is not thread-safe. It accesses the connection - pool concurrently. - """ - _connections[label] = connect(port, numRetries, host, proc) - if doSwitch: - switch(label) - return _connections[label].getVersion() - - -def start(cmd, port=None, numRetries=tc.DEFAULT_NUM_RETRIES, label="default", verbose=False, - traceFile=None, traceGetters=True, stdout=None, doSwitch=True): - """ - Start a sumo server using cmd, establish a connection to it and - store it under the given label. This method is not thread-safe. - - - cmd (list): uses the Popen syntax. i.e. ['sumo', '-c', 'run.sumocfg']. The remote - port option will be added automatically - - numRetries (int): retries on failing to connect to sumo (more retries are needed - if a big .net.xml file must be loaded) - - label (string) : distinguish multiple traci connections used in the same script - - verbose (bool): print complete cmd - - traceFile (string): write all traci commands to FILE for debugging - - traceGetters (bool): whether to include get-commands in traceFile - - stdout (iostream): where to pipe sumo process stdout - """ - if label in _connections: - raise TraCIException("Connection '%s' is already active." % label) - if traceFile is not None: - _startTracing(traceFile, cmd, port, label, traceGetters) - while numRetries >= 0 and label not in _connections: - sumoPort = sumolib.miscutils.getFreeSocketPort() if port is None else port - cmd2 = cmd + ["--remote-port", str(sumoPort)] - if verbose: - print("Calling " + ' '.join(cmd2)) - sumoProcess = subprocess.Popen(cmd2, stdout=stdout) - try: - return init(sumoPort, numRetries, "localhost", label, sumoProcess, doSwitch) - except TraCIException as e: - if port is not None: - break - warnings.warn(("Could not connect to TraCI server using port %s (%s)." + - " Retrying with different port.") % (sumoPort, e)) - numRetries -= 1 - raise FatalTraCIError("Could not connect.") - - -def _startTracing(traceFile, cmd, port, label, traceGetters): - _traceFile[label] = open(traceFile, 'w') - _traceFile[label].write("traci.start(%s, port=%s, label=%s)\n" % ( - repr(cmd), repr(port), repr(label))) - _traceGetters[label] = traceGetters - - -def isLibsumo(): - return False - - -def isLibtraci(): - return False - - -def hasGUI(): - """ - Return whether a GUI and the corresponding GUI commands are available for the current connection. - """ - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].hasGUI() - - -def load(args): - """load([optionOrParam, ...]) - Let sumo load a simulation using the given command line like options - Example: - load(['-c', 'run.sumocfg']) - load(['-n', 'net.net.xml', '-r', 'routes.rou.xml']) - """ - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].load(args) - - -def isLoaded(): - return "" in _connections - - -def simulationStep(step=0): - """ - Make a simulation step and simulate up to the given second in sim time. - If the given value is 0 or absent, exactly one step is performed. - Values smaller than or equal to the current sim time result in no action. - """ - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].simulationStep(step) - - -def addStepListener(listener): - """addStepListener(traci.StepListener) -> int - - Append the step listener (its step function is called at the end of every call to traci.simulationStep()) - to the current connection. - Returns the ID assigned to the listener if it was added successfully, None otherwise. - """ - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].addStepListener(listener) - - -def removeStepListener(listenerID): - """removeStepListener(traci.StepListener) -> bool - - Remove the step listener from the current connection's step listener container. - Returns True if the listener was removed successfully, False if it wasn't registered. - """ - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].removeStepListener(listenerID) - - -def getVersion(): - """getVersion() -> tuple - - Returns a tuple containing the TraCI API version number (integer) - and a string identifying the SUMO version running on the TraCI server in human-readable form. - """ - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].getVersion() - - -def setOrder(order): - """ - Tells TraCI to give the current client the given position in the - execution order. It is mandatory to send this as the first command after - connecting to the TraCI server when using multiple clients. Each client - must be assigned a unique integer but there are not further restrictions - on numbering. - """ - if "" not in _connections: - raise FatalTraCIError("Not connected.") - return _connections[""].setOrder(order) - - -def close(wait=True): - """ - Tells TraCI to close the connection. - """ - if "" not in _connections: - raise FatalTraCIError("Not connected.") - _connections[""].close(wait) - _connections[""].simulation._setConnection(None) - del _connections[_currentLabel[0]] - del _connections[""] - if _currentLabel[0] in _traceFile: - del _traceFile[_currentLabel[0]] - - -def switch(label): - con = getConnection(label) - _connections[""] = con - _currentLabel[0] = label - for domain in _defaultDomains: - domain._setConnection(con) - if label in _traceFile: - domain._setTraceFile(_traceFile[label], _traceGetters[label]) - # connection holds a copy of each domain - getattr(con, domain._name)._setTraceFile(_traceFile[label], _traceGetters[label]) - con._traceFile = _traceFile[label] - - -def getLabel(): - return _currentLabel[0] - - -def getConnection(label="default"): - if label not in _connections: - raise TraCIException("Connection '%s' is not known." % label) - return _connections[label] - - -def setLegacyGetLeader(enabled): - _vehicle._legacyGetLeader = enabled diff --git a/docker/install.sh b/docker/install.sh index e953e71d..a49939f0 100755 --- a/docker/install.sh +++ b/docker/install.sh @@ -58,15 +58,12 @@ cd /home/carma/src #make check #sudo make install -# Install SUMO-1.12.0 +# Install SUMO-1.15.0 cd /home/carma/src/ -wget "https://github.com/eclipse/sumo/archive/refs/tags/v1_12_0.tar.gz" +wget "https://github.com/eclipse/sumo/archive/refs/tags/v1_15_0.tar.gz" sudo mkdir -p /opt/sumo sudo chown -R carma:carma /opt/sumo tar xvf v1_15_0.tar.gz -C /opt/sumo -sudo cp co-simulation/patch/constants.py /opt/sumo/sumo-1_15_0/tools/traci -sudo cp co-simulation/patch/connection.py /opt/sumo/sumo-1_15_0/tools/traci -sudo cp co-simulation/patch/main.py /opt/sumo/sumo-1_15_0/tools/traci cd /opt/sumo/sumo-1_15_0 mkdir -p build/cmake-build && cd build/cmake-build cmake ../.. From fd00ade3416cf7afe4381c1d13fb366361c8107a Mon Sep 17 00:00:00 2001 From: Cody Garver Date: Thu, 13 Apr 2023 09:07:58 -0400 Subject: [PATCH 082/124] README.md: add status badge for CI and Sonar (#124) # PR Details ## Description ## Related Issue Closes usdot-fhwa-stol/devops#77 ## Motivation and Context ## How Has This Been Tested? ## Types of changes - [ ] Defect fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that cause existing functionality to change) ## Checklist: - [ ] 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** document. [CARMA Contributing Guide](Contributing.md) - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. --- .github/workflows/{runtest.yml => ci.yml} | 0 README.md | 3 +++ 2 files changed, 3 insertions(+) rename .github/workflows/{runtest.yml => ci.yml} (100%) diff --git a/.github/workflows/runtest.yml b/.github/workflows/ci.yml similarity index 100% rename from .github/workflows/runtest.yml rename to .github/workflows/ci.yml diff --git a/README.md b/README.md index c89a3f9b..c4de76fa 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +| CI Build Status | Docker Build | Docker Hub Push | Sonar Code Quality | +|----------------------|---------------------|---------------------|---------------------| +|[![CI](https://github.com/usdot-fhwa-stol/carma-simulation/actions/workflows/ci.yml/badge.svg)](https://github.com/usdot-fhwa-stol/carma-simulation/actions/workflows/ci.yml) | [![Docker](https://github.com/usdot-fhwa-stol/carma-simulation/actions/workflows/docker.yml/badge.svg)](https://github.com/usdot-fhwa-stol/carma-simulation/actions/workflows/docker.yml) | [![Docker Hub](https://github.com/usdot-fhwa-stol/carma-simulation/actions/workflows/dockerhub.yml/badge.svg)](https://github.com/usdot-fhwa-stol/carma-simulation/actions/workflows/dockerhub.yml) | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=usdot-fhwa-stol_carma-simulation&metric=alert_status)](https://sonarcloud.io/dashboard?id=usdot-fhwa-stol_carma-simulation)| # CARMASimulation This repository will host the CARMA Everything-in-the-Loop Co-Simulation tool. This XiL simulation platform will support CARLA and SUMO simulation environments as a co-simulation using the MOSAIC framework to facilitate coordination and data exchange. This co-simulation environment will also utilize NS-3 to simulate the communications used by the C-ADS systems. From aa890085d0f40c4855cabf36999fd9f2f01c6166 Mon Sep 17 00:00:00 2001 From: EricChen-Lei <122404059+EricChen-Lei@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:54:21 -0400 Subject: [PATCH 083/124] Correct func call and add file correct sync func call and add evc scenario file --- .../carla_integration/synchronization.py | 2 +- evc-sumo/src/resources/148.cfg | Bin 0 -> 147474 bytes evc-sumo/src/resources/278.cfg | Bin 0 -> 147474 bytes evc-sumo/src/resources/916.cfg | Bin 0 -> 147474 bytes evc-sumo/src/resources/evc_sumo_cfg.json | 38 +++++++++++++----- 5 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 evc-sumo/src/resources/148.cfg create mode 100644 evc-sumo/src/resources/278.cfg create mode 100644 evc-sumo/src/resources/916.cfg diff --git a/co-simulation/bridge/carla_integration/synchronization.py b/co-simulation/bridge/carla_integration/synchronization.py index cb7ce917..7627593a 100644 --- a/co-simulation/bridge/carla_integration/synchronization.py +++ b/co-simulation/bridge/carla_integration/synchronization.py @@ -140,7 +140,7 @@ def tick(self): elif self.tls_manager == 'EVC': common_landmarks = self.sumo.traffic_light_ids & self.carla.traffic_light_ids for landmark_id in common_landmarks: - sumo_tl_state = self.sumo.get_traffic_light_live_state(landmark_id) + sumo_tl_state = self.sumo.get_traffic_light_state_from_sumo(landmark_id) carla_tl_state = BridgeHelper.get_carla_traffic_light_state(sumo_tl_state) self.carla.synchronize_traffic_light(landmark_id, carla_tl_state) diff --git a/evc-sumo/src/resources/148.cfg b/evc-sumo/src/resources/148.cfg new file mode 100644 index 0000000000000000000000000000000000000000..85a4c9d1c72090ea7215fc863fd75c2fdc98fe07 GIT binary patch literal 147474 zcmeI5S#TWH6^2ju%t)irSeC)80cX5{Ar2UeHLH!h1e+u_Hd|K9mJ9-0GO}!VOSiJI zr?OUMA>{=K7%=;qK%9h-uqI@y%31~t6|xmCsZ{df+@4We_aC2@dahJRmi``(`}}?H zIj8&U)63i$&B!H9tMV$o+PQKs zA1|E}8_sh*OXYPz9Q2sjaGvW~aOrgC=&>Wli(d9G){Bc#nadhDoYDa6g?c_%0rp=W7?P}hNS5qg#) zg!(cl7olgF1fjkO%0=i|njqA6P%c8xG8sbM3Ccz2Sq45f7R7wlB{2BtS@2x{GI+QM zJi3{rgr4Pa2=xjm7olgF3ZdQvNn;IUYi- z1?3|2EYl#=6`)*%p5>ho>UvNvLeElyP+tM%BJ?bH|NSkOz~G~2!J~Z#c(@2X%S;Hh z3zUn{v&@1}&w+9gdY0J`>Q|s#gr4OD2=xb0E<(?8B7}Msl#9@_%z;pU1LY$0EGI!I zd<6haDTSWpWbnrOCTL12^epcJZ@k(-Q%a#{nG4={j6zdNp=WtFc;l8qQ%a#{IR(6z zgK`mimQx|rDo`#$&oU1}eH@gF(6gKdq1J(N5qg&S5b8=$E<(@J0-`Wh$~p=UV{LVX96i_o(ygHX4EauIr#!{T7sq z(6g+CP=5yHBJ?acy7#(EVDQniv_qeNf=VeMgq{V@T==<_lv$vgo@G7s!FT(iDW%Y} zTn66w9w{`X6nd7+!5iOMf~J&0&(aCrI35p8DTSWp3h>6eW@t(&^ek6`H{NSNQ%a#{ z*#O>nhK8n;LeH`hys@sKDW%Y}bb&W2Xi6#cEZyLJGbk6KXW0ayz5&Wb=vl6UP~QdR zBJ?ah5bAbNE<(?;8A9y_e*xtp^ek6Hs5d~l z2tCU+5b9r`T!fy*B@%r|fCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-} zBrsK`iT$j0m{GlengmFI1V~_n2zW#Px}axLMNfUo+f6g1a>m|e`j*TyFi+n+@k_^| zg<0P(Kg}$fJTpae)5OUTShT3juAjwQ(U-!SsT6UIo)Z1GVKk4Y=ywidJI&npGTB1I zq^7uUhF8mOrgQuKid9=!od31D-u_zstGB-{m#;52x^I82 z{?*%GtAF+O*Xr*zd*v^tYu>x3SL09%{Sv^{e0OrF(W$)U3EDe z*iUBHL|4T^wjtvW>?Uc-PpYrWxvmub4kA$APiE;`{g25~c~o^w+D~z};a->HIGiUC zsO+a*(eWgE%)=Z{QZ$dMiTkMv2qg)S011!)36KB@kN^pg011!)36KB@kN^pc0RlOx z@BiYGv1v^mF-EzmGv`bzB*tquWB$!U_YoPo&mkITehNNA_Az{h>|^*0*@tlkuT}CP zANAB&Igj!9QsJ0|tu$Vp- z0*O$^FSqEkUxQ5%n9Q_;KT8n<B=!zN(ctPRqz3D`DkgEVXc zw$0ig4V!>%vo=V>CScpF4brd)*fwi}G;9L4&DtOhn}BVzHb}!JVB4$>(y$5GHfw`4 zYy!5;+8_;^fNirjNW&&z+pG=JunE{UYlAdw0=CWCAPt*B=!zN(ctPRqz3D`Dk zgEVXcw$0ig4V!>%vo=V>CScpF4brd)*fwi}G;9L4&DtOhn}BVzHb}!JVB4$>(y$5G zHfw`4Yy!5;+8_;^fNirjNW&&z+pG=JunE{UYlAdw0=CWCAPt*B=!zN(ctPRqz z3D`DkgEVXcw$0ig4V!>%vo=V>CScpF4brd)*fwi}G;9L4&DtOhn}BVzHb}!JVB4$> z(y$5GHfw`4Yy!5;+8_;^fNirjNW&&z+k@9eyWMpH*#4l~=y_T3{GxanGtZiNE}Qr2 zig~Zz%=5*(Z`}Q?dDcnZ&r3dI`e*!NK4bc4vc>%3b=_Uv8#;SCO0C^py*=FzBjhS-jlmNVy-7N1f&zpb>QZNtX4 z(!#ABb4v@m+Ai|a8#4+kFdfGGh)7IOqeWGQ5 z|Fmh#+6|rSk`b|=&W2pbB_hB&U>&dy^coipai7g~R0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrFt!MkQm&5HFYPidMOt62ALEH=T+%KhKk|!efc2s?;PIhz zVZG>FST8yk){D-C^`diOz35z6FFF_2i_V4hqH|%r=v-JYIv3W9&V}`&b78&cTv#tU z7uJi;h4rFyVZG>FST8yk){D-C^`diOz35z6FFF_2i&hz2Mgk;20wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J6P7^bw^y^`OUBR0`1!qp%64VS+a=>} zyZmvwUD08f@yFl(8fV*8SW;*)-~TUW?nO_t!{$ai&HH!Nk><%1$;lDYAjilgIbJey zxYWy0Udfv-ugkXbkF-yftQ;+QIZleEst=PoIZ_tNVriA7-VAT1H_MwXZ%RRql_r@c z`{Z$X!`th<;=Ss<=I!%dkAH_?wCA!)T4bi#zKqN;Tj9$}IZdXU?f1-|`$xY$f3ch; zC(2P~`%f@iSSJ_C88X|j$;m8<81!$mIkbKGtZbHb(kDH#)_cZ#T0*%>+9Z%ovc~uP zlArNU@^+7v$51O`2(SuR1*`&|3(O^g=K@|4SOvT<*v)eR?+(o^EYAfz7qANWN-=sB z8SDw}3$_J63hocK2R{xT2zCTN2_6h~22TeM1-Ax24IU0|3!Vuc32qO57Cah!uTT2C za9g;&Jf5&kw##Gklo?mpC$Gt)@}zuGzGPlGUngIXn`N)ODhteO>~p0x_<8VHa7XZL z@OW@%@Lcdjuq${z_K#w4DQU&_n!SJ@$p%-F>eSsLCK-XA^^J{mq4J`_IOSL)06HS`tw z8pGY?M-YZ#UwCbJU3k5GPHr@>_-~NU%T3-kZ@ag{+bL_z>;1K644_>)WW8J_E6jMp z1!jz4wHY_K$c!c2A$Q7dxi?%8E)35NTf;@+;&4g0G+Ys`3@->*g{#90!;8X;!!==B zxHeoDwuc?z`tUNjPwtloUQ4CIL3A|;F!v1IfyGx@zPRslxKmsH{ z0wh2JBtQZrKmsH{0wh2J0sijgp!RZ|RYCCsvsh>;MQi%DR# z2^5ByqV*f1OZhYE?2}A@43pVJE#yN!Nd#CMNn|R^$%lNB2(UJi$ne-`d)%!>TYx5@gTMWOH@(`G!JcaZ=Od=r5XK%2rL+ zR87!%gap?q9Y}H_r*s6;^%{%yly1%0iv&o31W14cNPq-LfCNZ@1W14cNPq-LfCNZ@ z1SUFx{rjVL!Y1B=!zN(ctPRqz3D`DkgEVXcw$0ig4V!>%vo=V>CScpF4brd)&?W&AAOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8>{{;Rw>;DuM Bg0KJp literal 0 HcmV?d00001 diff --git a/evc-sumo/src/resources/278.cfg b/evc-sumo/src/resources/278.cfg new file mode 100644 index 0000000000000000000000000000000000000000..99e7b486787db43488cdf16416b77601f1a81f5e GIT binary patch literal 147474 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 literal 0 HcmV?d00001 diff --git a/evc-sumo/src/resources/916.cfg b/evc-sumo/src/resources/916.cfg new file mode 100644 index 0000000000000000000000000000000000000000..85a4c9d1c72090ea7215fc863fd75c2fdc98fe07 GIT binary patch literal 147474 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 Bg0KJp literal 0 HcmV?d00001 diff --git a/evc-sumo/src/resources/evc_sumo_cfg.json b/evc-sumo/src/resources/evc_sumo_cfg.json index 96f8a031..72f24771 100644 --- a/evc-sumo/src/resources/evc_sumo_cfg.json +++ b/evc-sumo/src/resources/evc_sumo_cfg.json @@ -2,34 +2,50 @@ "controllers": [ { "controllerId": 1, - "controllerCfgPath": "resources/1008.cfg", + "controllerCfgPath": "resources/278.cfg", "start_time": null, "https_port": 0, "web_port": 0, "snmp_port": 0, "harness_port": 0, "controller_speed": null, - "sumoTlId": 1008, + "sumoTlId": 278, "enableWebPanel": true, - "evcPhases": [{"evcPhaseId": 2, "sumoTlStateIndex":[1,5], "sumoInductionLoopId": "e1_0"}, - {"evcPhaseId": 6, "sumoTlStateIndex":[3,4], "sumoInductionLoopId": "e1_1"}, - {"evcPhaseId": 4, "sumoTlStateIndex":[0,2], "sumoInductionLoopId": "e1_2"} ] + "evcPhases": [{"evcPhaseId": 2, "sumoTlStateIndex":[0,3], "sumoInductionLoopId": "e1_0"}, + {"evcPhaseId": 6, "sumoTlStateIndex":[1,4], "sumoInductionLoopId": "e1_1"}, + {"evcPhaseId": 4, "sumoTlStateIndex":[2,5], "sumoInductionLoopId": "e1_2"} ] }, { "controllerId": 2, - "controllerCfgPath": "resources/skip_setup.cfg", + "controllerCfgPath": "resources/148.cfg", "start_time": null, "https_port": 0, "web_port": 0, "snmp_port": 0, "harness_port": 0, "controller_speed": null, - "sumoTlId": 621, + "sumoTlId": 148, "enableWebPanel": true, - "evcPhases": [{"evcPhaseId": 1, "sumoTlStateIndex":[1,8,10], "sumoInductionLoopId": "e1_6"}, - {"evcPhaseId": 2, "sumoTlStateIndex":[0,5,11], "sumoInductionLoopId": "e1_5"}, - {"evcPhaseId": 3, "sumoTlStateIndex":[3,4,9], "sumoInductionLoopId": "e1_4"}, - {"evcPhaseId": 4, "sumoTlStateIndex":[2,6,7], "sumoInductionLoopId": "e1_3"} ] + "evcPhases": [{"evcPhaseId": 2, "sumoTlStateIndex":[7,8,10], "sumoInductionLoopId": "e1_3"}, + {"evcPhaseId": 6, "sumoTlStateIndex":[4,5,6], "sumoInductionLoopId": "e1_4"}, + {"evcPhaseId": 4, "sumoTlStateIndex":[2,3,11], "sumoInductionLoopId": "e1_5"}, + {"evcPhaseId": 8, "sumoTlStateIndex":[0,1,9], "sumoInductionLoopId": "e1_6"} ] + }, + { + "controllerId": 3, + "controllerCfgPath": "resources/916.cfg", + "start_time": null, + "https_port": 0, + "web_port": 0, + "snmp_port": 0, + "harness_port": 0, + "controller_speed": null, + "sumoTlId": 916, + "enableWebPanel": true, + "evcPhases": [{"evcPhaseId": 2, "sumoTlStateIndex":[4,10,11], "sumoInductionLoopId": "e1_7"}, + {"evcPhaseId": 6, "sumoTlStateIndex":[0,2,6], "sumoInductionLoopId": "e1_8"}, + {"evcPhaseId": 4, "sumoTlStateIndex":[1,3,8], "sumoInductionLoopId": "e1_9"}, + {"evcPhaseId": 8, "sumoTlStateIndex":[5,7,9], "sumoInductionLoopId": "e1_10"} ] } ] } \ No newline at end of file From ed6feb22389ed285aec4c290b039aebeca6d602f Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 20 Apr 2023 03:48:42 -0400 Subject: [PATCH 084/124] Update Tiergarten scenario for testing infrastructure and NS3 --- .../Tiergarten/infrastructure/infrastructure_config.json | 4 ++++ .../resources/scenarios/Tiergarten/scenario_config.json | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Tiergarten/infrastructure/infrastructure_config.json diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Tiergarten/infrastructure/infrastructure_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Tiergarten/infrastructure/infrastructure_config.json new file mode 100644 index 00000000..179bfc13 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Tiergarten/infrastructure/infrastructure_config.json @@ -0,0 +1,4 @@ +{ + "updateInterval": 1000, + "senderRSUId": "rsu_0" +} diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Tiergarten/scenario_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Tiergarten/scenario_config.json index bcd90c80..b7d36544 100644 --- a/co-simulation/bundle/src/assembly/resources/scenarios/Tiergarten/scenario_config.json +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Tiergarten/scenario_config.json @@ -1,7 +1,7 @@ { "simulation": { "id": "Tiergarten", - "duration": "180s", + "duration": "9999s", "randomSeed": 268965854, "projection": { "centerCoordinates": { @@ -31,6 +31,7 @@ "ns3": true, "omnetpp": false, "output": true, - "sumo": true + "sumo": true, + "infrastructure": true } } From 8b10dfa986f6c05d9c63dbf382a4eba602e7c638 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 20 Apr 2023 03:49:24 -0400 Subject: [PATCH 085/124] Added log when receive new infrastructure registration --- .../ambassador/InfrastructureInstanceManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 23e499dd..5e8828ae 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -43,8 +43,7 @@ public class InfrastructureInstanceManager { private Map managedInstances = new HashMap<>(); private double currentSimulationTime; - private static final Logger log = LoggerFactory.getLogger(InfrastructureInstanceManager.class); - + private final Logger log = LoggerFactory.getLogger(this.getClass()); /** * Register a new infrastructure instance with the MOSAIC system. * @@ -94,6 +93,7 @@ private void newInfrastructureInstance(String infrastructureId, InetAddress rxMe timeSyncPort, location); try { tmp.bind(); + log.info("New infrastructure registration received with ID '{}'", infrastructureId); } catch (IOException e) { log.error("Failed to bind infrastructure instance with ID '{}' to its RX message socket: {}", infrastructureId, e.getMessage()); From 21a02ea74169fae74bf9107d9b24333fde76a675 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 20 Apr 2023 03:52:03 -0400 Subject: [PATCH 086/124] Updated missing initialize call --- .../ambassador/InfrastructureMessageAmbassador.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 356661e2..6f28a6c7 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -64,7 +64,7 @@ public class InfrastructureMessageAmbassador extends AbstractFederateAmbassador private Thread v2xTimeRxBackgroundThread; private InfrastructureInstanceManager infrastructureInstanceManager = new InfrastructureInstanceManager(); - private InfrastructureTimeInterface infrastructureTimeInterface; + private InfrastructureTimeInterface infrastructureTimeInterface = new InfrastructureTimeInterface(infrastructureInstanceManager); private int timeSyncSeq = 0; @@ -162,6 +162,7 @@ private synchronized void receiveInteraction(InfrastructureV2xMessageReception i * * Note: This function should be called after the * onRsuRegistrationRequest + * @throws UnknownHostException */ private void onDsrcRegistrationRequest(String infrastructureId) { // Create an InterfaceConfiguration object to represent the configuration of the From a199f8f0e826ebad843e05caaa3c6ac02605ff12 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 20 Apr 2023 03:52:30 -0400 Subject: [PATCH 087/124] Updated missing initialize call and adjusted log level --- .../ambassador/InfrastructureTimeInterface.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterface.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterface.java index 2ebe2174..e9b7df99 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterface.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeInterface.java @@ -19,6 +19,7 @@ import java.io.IOException; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.gson.Gson; @@ -27,7 +28,7 @@ public class InfrastructureTimeInterface { // set to false on init release private boolean await_infrastructure_advance_request = false; - protected Logger log; + private final Logger log = LoggerFactory.getLogger(this.getClass()); public InfrastructureTimeInterface(InfrastructureInstanceManager manager) { this.manager = manager; @@ -58,7 +59,7 @@ public byte[] encodeTimeMessage(InfrastructureTimeMessage message) { */ public void onTimeStepUpdate(InfrastructureTimeMessage message) throws IOException { if (this.manager.getManagedInstances().size() == 0) { - log.info("There are no registered instances"); + log.debug("There are no registered instances"); } for (InfrastructureInstance currentInstance : manager.getManagedInstances().values()) { From 81a64a9e8f76236fd0ad2802a6decca5b559b6d2 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Thu, 20 Apr 2023 10:07:50 -0400 Subject: [PATCH 088/124] Updated DSRC parameters --- .../ambassador/InfrastructureMessageAmbassador.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 6f28a6c7..dff13b70 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -37,7 +37,9 @@ import org.eclipse.mosaic.fed.infrastructure.ambassador.InfrastructureRegistrationMessage; import java.io.IOException; +import java.net.Inet4Address; import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -164,15 +166,15 @@ private synchronized void receiveInteraction(InfrastructureV2xMessageReception i * onRsuRegistrationRequest * @throws UnknownHostException */ - private void onDsrcRegistrationRequest(String infrastructureId) { + private void onDsrcRegistrationRequest(String infrastructureId) throws UnknownHostException { // Create an InterfaceConfiguration object to represent the configuration of the // Ad-Hoc interface // Set the IP address and subnet mask to null for now // Set the transmit power to 50 dBm and the maximum range to 100 meters // NOTE: TODO Setup the IP address and subnet InterfaceConfiguration interfaceConfig = new InterfaceConfiguration.Builder(AdHocChannel.SCH1) - .ip(null) - .subnet(null) + .ip( (Inet4Address) Inet4Address.getByName("192.168.0.1") ) //TODO + .subnet((Inet4Address) Inet4Address.getByName("255.255.255.0")) //TODO .power(50) .radius(100.0) .create(); From f012c0613ae12fcc3870d9e93d5d86ef8fd5c2fe Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Mon, 24 Apr 2023 23:12:38 -0400 Subject: [PATCH 089/124] Completed doc --- .../ambassador/InfrastructureMessageAmbassador.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index dff13b70..2184e0e4 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -171,10 +171,9 @@ private void onDsrcRegistrationRequest(String infrastructureId) throws UnknownHo // Ad-Hoc interface // Set the IP address and subnet mask to null for now // Set the transmit power to 50 dBm and the maximum range to 100 meters - // NOTE: TODO Setup the IP address and subnet InterfaceConfiguration interfaceConfig = new InterfaceConfiguration.Builder(AdHocChannel.SCH1) - .ip( (Inet4Address) Inet4Address.getByName("192.168.0.1") ) //TODO - .subnet((Inet4Address) Inet4Address.getByName("255.255.255.0")) //TODO + .ip( (Inet4Address) Inet4Address.getByName("192.168.0.1") ) //TODO: set IP address if needed + .subnet((Inet4Address) Inet4Address.getByName("255.255.255.0")) //TODO: set subnet if needed .power(50) .radius(100.0) .create(); From 2f6e030bc8a54feb2fd240b934dc7474c1673c8c Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Mon, 24 Apr 2023 23:39:55 -0400 Subject: [PATCH 090/124] Fix port conflict --- .../ambassador/InfrastructureRegistrationReceiver.java | 2 +- .../ambassador/InfrastructureTimeMessageReceiver.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java index 7f8c2b06..cb763f06 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java @@ -40,7 +40,7 @@ public class InfrastructureRegistrationReceiver implements Runnable { private DatagramSocket listenSocket = null; private static final int listenPort = 1515; private boolean running = false; - private static final int UDP_MTU = 1535; + private static final int UDP_MTU = 1534; /** * Initialize the listen socket for messages from the Infrastructure Device NS-3 diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessageReceiver.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessageReceiver.java index 038f5b38..3030f8c7 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessageReceiver.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessageReceiver.java @@ -39,9 +39,9 @@ public class InfrastructureTimeMessageReceiver implements Runnable { private Queue> rxQueue = new LinkedList<>(); private DatagramSocket listenSocket = null; - private static final int listenPort = 1516; // TODO + private static final int listenPort = 1514; // TODO private boolean running = false; - private static final int UDP_MTU = 1535; // TOD OMaximum Transmission Unit + private static final int UDP_MTU = 1533; // TOD OMaximum Transmission Unit /** * Initialize the listen socket for messages from the Infrastructure Device NS-3 Adapter From b08542fb70680cda02ad41a2a32a611bd5f7e7e9 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Tue, 25 Apr 2023 00:23:30 -0400 Subject: [PATCH 091/124] Assigned and fix port conflict --- .../mosaic/fed/carma/ambassador/CarmaV2xMessageReceiver.java | 2 +- .../ambassador/InfrastructureRegistrationReceiver.java | 2 +- .../ambassador/InfrastructureTimeMessageReceiver.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaV2xMessageReceiver.java b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaV2xMessageReceiver.java index 222ff5cb..3db371f3 100644 --- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaV2xMessageReceiver.java +++ b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaV2xMessageReceiver.java @@ -40,7 +40,7 @@ public class CarmaV2xMessageReceiver implements Runnable { private DatagramSocket listenSocket = null; private static final int listenPort = 1516; private boolean running = true; - private static final int UDP_MTU = 1535; + private static final int UDP_MTU = 1536; /** * Initialize the listen socket for messages from the CARMA Platform NS-3 Adapter diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java index cb763f06..75c93b62 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java @@ -38,7 +38,7 @@ public class InfrastructureRegistrationReceiver implements Runnable { private Queue rxQueue = new LinkedList<>(); private DatagramSocket listenSocket = null; - private static final int listenPort = 1515; + private static final int listenPort = 1615; private boolean running = false; private static final int UDP_MTU = 1534; diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessageReceiver.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessageReceiver.java index 3030f8c7..3a05c6ff 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessageReceiver.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessageReceiver.java @@ -39,9 +39,9 @@ public class InfrastructureTimeMessageReceiver implements Runnable { private Queue> rxQueue = new LinkedList<>(); private DatagramSocket listenSocket = null; - private static final int listenPort = 1514; // TODO + private static final int listenPort = 1616; private boolean running = false; - private static final int UDP_MTU = 1533; // TOD OMaximum Transmission Unit + private static final int UDP_MTU = 1636; /** * Initialize the listen socket for messages from the Infrastructure Device NS-3 Adapter From 3865f5744bd8536498c0607e23cb1731a26a7e8e Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Tue, 25 Apr 2023 00:48:40 -0400 Subject: [PATCH 092/124] Added MOSAIC Town04 scenario for K900 test --- .../scenarios/Town04/application/Town04.db | Bin 0 -> 419840 bytes .../scenarios/Town04/carla/bridge.sh | 6 + .../scenarios/Town04/carla/carla_config.json | 6 + .../scenarios/Town04/carma/carma_config.json | 25 + .../infrastructure/infrastructure_config.json | 4 + .../Town04/mapping/mapping_config.json | 6 + .../scenarios/Town04/ns3/ns3_config.json | 20 + .../Town04/ns3/ns3_federate_config.xml | 93 + .../scenarios/Town04/output/output_config.xml | 204 + .../scenarios/Town04/scenario_config.json | 40 + .../scenarios/Town04/sumo/Town04.net.xml | 5373 +++++++++++++++++ .../scenarios/Town04/sumo/Town04.rou.xml | 16 + .../scenarios/Town04/sumo/Town04.sumocfg | 17 + .../scenarios/Town04/sumo/detector.xml | 19 + .../scenarios/Town04/sumo/sumo_config.json | 4 + 15 files changed, 5833 insertions(+) create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/application/Town04.db create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/carla/bridge.sh create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/carla/carla_config.json create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/carma/carma_config.json create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/infrastructure/infrastructure_config.json create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/mapping/mapping_config.json create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/ns3/ns3_config.json create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/ns3/ns3_federate_config.xml create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/output/output_config.xml create mode 100755 co-simulation/bundle/src/assembly/resources/scenarios/Town04/scenario_config.json create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.net.xml create mode 100755 co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.rou.xml create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.sumocfg create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/detector.xml create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/sumo_config.json diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/application/Town04.db b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/application/Town04.db new file mode 100644 index 0000000000000000000000000000000000000000..627afa8dec3af4e6e94762085b60676e07cda12e GIT binary patch literal 419840 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%0D9t7 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 z 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

b420klLZ}%C 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/output/output_config.xml b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/output/output_config.xml new file mode 100644 index 00000000..5a71e921 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/output/output_config.xml @@ -0,0 +1,204 @@ + + + + + + output.csv + . + ; + + + + "VEHICLE_UPDATES" + Time + Updated:Name + Updated:Speed + Updated:Heading + Updated:Position.Latitude + Updated:Position.Longitude + Updated:Position.Altitude + Updated:DistanceDriven + Updated:LongitudinalAcceleration + Updated:Slope + Updated:Stopped + Updated:RouteId + Updated:RoadPosition.Connection.Id + Updated:RoadPosition.LaneIndex + Updated:VehicleEmissions.CurrentEmissions.Co2 + Updated:VehicleEmissions.CurrentEmissions.Co + Updated:VehicleEmissions.CurrentEmissions.Hc + Updated:VehicleEmissions.CurrentEmissions.Pmx + Updated:VehicleEmissions.CurrentEmissions.Nox + Updated:VehicleConsumptions.CurrentConsumptions.Fuel + Updated:VehicleEmissions.AllEmissions.Co2 + Updated:VehicleEmissions.AllEmissions.Co + Updated:VehicleEmissions.AllEmissions.Hc + Updated:VehicleEmissions.AllEmissions.Pmx + Updated:VehicleEmissions.AllEmissions.Nox + Updated:VehicleConsumptions.AllConsumptions.Fuel + Updated:VehicleSignals.BlinkerRight + Updated:VehicleSignals.BlinkerLeft + Updated:VehicleSignals.BrakeLight + + + + + "V2X_MESSAGE_RECEPTION" + Time + Type + MessageId + ReceiverName + ReceiverInformation.ReceiveSignalStrength + + + + + "V2X_MESSAGE_TRANSMISSION" + Time + Type + MessageId + SourceName + SourcePosition.Latitude + SourcePosition.Longitude + SourcePosition.Altitude + Message.Routing.Destination.Type + Message.Routing.Destination.Address.IPv4Address + Message.Routing.Destination.AdhocChannelId + + + + + "VEHICLE_REGISTRATION" + Time + Mapping.Name + Mapping.VehicleType.VehicleClass + Mapping.VehicleType.Color + Mapping.Group + Mapping.VehicleType.Length + Mapping.VehicleType.MinGap + Mapping.VehicleType.MaxSpeed + Mapping.VehicleType.Accel + Mapping.VehicleType.Decel + Mapping.VehicleType.Sigma + Mapping.VehicleType.Tau + Mapping.VehicleType.SpeedFactor + Departure.RouteId + Departure.DepartureLane + Departure.DeparturePos + + + + + "TRAFFICLIGHT_REGISTRATION" + Time + Mapping.Name + Mapping.Applications + TrafficLightGroup.FirstPosition.Latitude + TrafficLightGroup.FirstPosition.Longitude + + + + + "TRAFFICSIGN_REGISTRATION" + Time + TrafficSign.Id + TrafficSign.GeoPosition.Latitude + TrafficSign.GeoPosition.Longitude + TrafficSign.GeoPosition.Altitude + TrafficSign.Angle + TrafficSign.ConnectionId + TrafficSign.Lane + TrafficSign.TypeId + TrafficSign.SignContents + + + + + "CHANGE_SPEED" + Time + VehicleId + Type + Speed + Interval + Acceleration + + + + + "RSU_REGISTRATION" + Time + Mapping.Name + Mapping.Position.Latitude + Mapping.Position.Longitude + Mapping.Position.Altitude + Mapping.Group + Mapping.Applications + + + + + "ADHOC_CONFIGURATION" + Time + Configuration.NodeId + Configuration.RadioMode + Configuration.Conf0.NewIP + Configuration.Conf0.NewPower + Configuration.Conf0.Channel0 + Configuration.Conf0.Channel1 + Configuration.Conf1.NewIP + Configuration.Conf1.NewPower + Configuration.Conf1.Channel0 + Configuration.Conf1.Channel1 + + + + + "CELL_CONFIGURATION" + Time + Configuration.NodeId + Configuration.Enabled + Configuration.MaxDownlinkBitrate + Configuration.MaxUplinkBitrate + + + + + "DETECTOR_UPDATES" + Time + UpdatedInductionLoops:VehicleCount + UpdatedInductionLoops:MeanSpeed + UpdatedInductionLoops:TrafficFlow + UpdatedLaneAreaDetectors:Length + UpdatedLaneAreaDetectors:VehicleCount + UpdatedLaneAreaDetectors:MeanSpeed + + + + + "LANE_PROPERTY_CHANGE" + Time + EdgeId + LaneIndex + AllowedVehicleClasses + DisallowedVehicleClasses + MaxSpeed + + + + + + + true + 46587 + + + + + + + + + + + diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/scenario_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/scenario_config.json new file mode 100755 index 00000000..c70915d4 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/scenario_config.json @@ -0,0 +1,40 @@ +{ + "simulation": { + "id": "Town04", + "duration": "600s", + "randomSeed": 212323853, + "projection": { + "centerCoordinates": { + "latitude": 52.63, + "longitude": 13.56 + }, + "cartesianOffset": { + "x": 503.02, + "y": 423.76 + } + }, + "network": { + "netMask": "255.255.0.0", + "vehicleNet": "10.1.0.0", + "rsuNet": "10.2.0.0", + "tlNet": "10.3.0.0", + "csNet": "10.4.0.0", + "serverNet": "10.5.0.0", + "tmcNet": "10.6.0.0" + } + }, + "federates": { + "application": true, + "cell": false, + "environment": false, + "sns": false, + "ns3": true, + "omnetpp": false, + "output": false, + "sumo": true, + "carla": true, + "carma": true, + "infrastructure": true + + } +} diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.net.xml b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.net.xml new file mode 100644 index 00000000..46d8e7a9 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.net.xml @@ -0,0 +1,5373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.rou.xml b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.rou.xml new file mode 100755 index 00000000..351f1680 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.rou.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.sumocfg b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.sumocfg new file mode 100644 index 00000000..f22dda7c --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.sumocfg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/detector.xml b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/detector.xml new file mode 100644 index 00000000..d39b9527 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/detector.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/sumo_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/sumo_config.json new file mode 100644 index 00000000..413def8c --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/sumo_config.json @@ -0,0 +1,4 @@ +{ + "sumoConfigurationFile": "Town04.sumocfg", + "updateInterval": 100 +} From 55d7ae02263a041f14b1b1c978b681eb6cecef45 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Tue, 25 Apr 2023 00:49:20 -0400 Subject: [PATCH 093/124] Update InfrastructureRegistrationReceiver.java --- .../ambassador/InfrastructureRegistrationReceiver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java index 75c93b62..3437535f 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java @@ -40,7 +40,7 @@ public class InfrastructureRegistrationReceiver implements Runnable { private DatagramSocket listenSocket = null; private static final int listenPort = 1615; private boolean running = false; - private static final int UDP_MTU = 1534; + private static final int UDP_MTU = 1635; /** * Initialize the listen socket for messages from the Infrastructure Device NS-3 From 064633572e6ce9fe08747242b9d9765ca8ad3645 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Tue, 25 Apr 2023 00:57:39 -0400 Subject: [PATCH 094/124] Update InfrastructureRegistrationReceiverTest.java --- .../ambassador/InfrastructureRegistrationReceiverTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiverTest.java b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiverTest.java index 183db72b..2cd893c5 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiverTest.java +++ b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiverTest.java @@ -29,7 +29,7 @@ public class InfrastructureRegistrationReceiverTest { - private static final int TEST_PORT = 1515; + private static final int TEST_PORT = 1615; private DatagramSocket sendSocket; private InfrastructureRegistrationReceiver receiver; From 509d4a758e188afdbad1a1459e717f9fe61c5708 Mon Sep 17 00:00:00 2001 From: Kyle Rush Date: Tue, 25 Apr 2023 09:17:26 -0400 Subject: [PATCH 095/124] Migrate logic for v2x messaging from platform to infrastructure (#127) Refactored existing logic into new maven module to re-use for infrastructure ambassador # PR Details ## Description ## Related Issue ## Motivation and Context Migrating the v2x-message reception logic into a shared library so it can be easily incorporated into the infrastructure ambassador without code duplication. ## How Has This Been Tested? Hasn't been tested yet, original module wasn't unit tested so I'll write some tonight but wanted to get this review out before COB. ## Types of changes - [ ] 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: - [x] I have added any new packages to the sonar-scanner.properties file - [] My change requires a change to the documentation. - [x] I have updated the documentation accordingly. - [x] I have read the **CONTRIBUTING** document. [CARMA Contributing Guide](Contributing.md) - [ ] I have added tests to cover my changes. - [x] All new and existing tests passed. --- co-simulation/NOTICE-THIRD-PARTY.md | 8 +++ co-simulation/fed/mosaic-carma/pom.xml | 5 ++ .../ambassador/CarmaInstanceManager.java | 15 ++--- .../ambassador/CarmaMessageAmbassador.java | 35 +++++----- .../fed/mosaic-infrastructure/pom.xml | 5 ++ .../InfrastructureInstanceManager.java | 48 ++++++++++--- .../InfrastructureMessageAmbassador.java | 67 ++++++++++++------- .../InfrastructureMessageAmbassadorTest.java | 18 ++--- co-simulation/lib/mosaic-carma-utils/pom.xml | 53 +++++++++++++++ .../gov/dot/fhwa/saxton}/CarmaV2xMessage.java | 2 +- .../fhwa/saxton}/CarmaV2xMessageReceiver.java | 25 ++++++- co-simulation/pom.xml | 1 + 12 files changed, 211 insertions(+), 71 deletions(-) create mode 100644 co-simulation/lib/mosaic-carma-utils/pom.xml rename co-simulation/{fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador => lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton}/CarmaV2xMessage.java (99%) rename co-simulation/{fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador => lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton}/CarmaV2xMessageReceiver.java (79%) diff --git a/co-simulation/NOTICE-THIRD-PARTY.md b/co-simulation/NOTICE-THIRD-PARTY.md index 2f8c463d..3f1d33c3 100644 --- a/co-simulation/NOTICE-THIRD-PARTY.md +++ b/co-simulation/NOTICE-THIRD-PARTY.md @@ -245,6 +245,14 @@ Logback Core Module (1.2.3) * Source: https://github.com/ceki/logback/logback-core +mosaic-carma-utils (22.1-SNAPSHOT) + + * License: Eclipse Public License - v 2.0 + * Maven artifact: `gov.dot.fhwa.saxton:mosaic-carma-utils:22.1-SNAPSHOT` + * Project: not available + * Source: https://github.com/eclipse/mosaic/lib/mosaic-carma-utils + + org.leadpony.justify (1.1.0) * License: The Apache Software License, Version 2.0 diff --git a/co-simulation/fed/mosaic-carma/pom.xml b/co-simulation/fed/mosaic-carma/pom.xml index 59b560b4..8e14a5a6 100644 --- a/co-simulation/fed/mosaic-carma/pom.xml +++ b/co-simulation/fed/mosaic-carma/pom.xml @@ -25,6 +25,11 @@ mosaic-objects ${mosaic.version} + + gov.dot.fhwa.saxton + mosaic-carma-utils + ${mosaic.version} + com.google.code.gson gson diff --git a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceManager.java b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceManager.java index 409b49a1..02b511bf 100644 --- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceManager.java +++ b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceManager.java @@ -16,14 +16,7 @@ package org.eclipse.mosaic.fed.carma.ambassador; -import java.io.IOException; -import java.net.InetAddress; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.HashMap; -import java.util.Map; - -import org.eclipse.mosaic.fed.carma.ambassador.CarmaRegistrationMessage; +import gov.dot.fhwa.saxton.CarmaV2xMessage; import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; import org.eclipse.mosaic.interactions.traffic.VehicleUpdates; import org.eclipse.mosaic.lib.enums.AdHocChannel; @@ -33,6 +26,12 @@ import org.eclipse.mosaic.lib.objects.v2x.MessageRouting; import org.eclipse.mosaic.lib.objects.vehicle.VehicleData; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; + /** * Session management class for CARMA Platform instances communicating with MOSAIC */ diff --git a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaMessageAmbassador.java b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaMessageAmbassador.java index fe8fe979..5a1fa2ef 100644 --- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaMessageAmbassador.java +++ b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaMessageAmbassador.java @@ -13,42 +13,37 @@ package org.eclipse.mosaic.fed.carma.ambassador; +import gov.dot.fhwa.saxton.CarmaV2xMessage; +import gov.dot.fhwa.saxton.CarmaV2xMessageReceiver; +import org.eclipse.mosaic.fed.application.ambassador.SimulationKernel; +import org.eclipse.mosaic.fed.carma.configuration.CarmaConfiguration; +import org.eclipse.mosaic.fed.carma.configuration.CarmaVehicleConfiguration; +import org.eclipse.mosaic.interactions.application.CarmaV2xMessageReception; +import org.eclipse.mosaic.interactions.application.ExternalMessage; import org.eclipse.mosaic.interactions.communication.V2xMessageReception; import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; +import org.eclipse.mosaic.interactions.traffic.VehicleUpdates; +import org.eclipse.mosaic.interactions.vehicle.VehicleFederateAssignment; +import org.eclipse.mosaic.lib.enums.DriveDirection; import org.eclipse.mosaic.lib.misc.Tuple; -import org.eclipse.mosaic.lib.objects.addressing.DestinationAddressContainer; import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xMessage; -import org.eclipse.mosaic.lib.objects.v2x.MessageRouting; import org.eclipse.mosaic.lib.objects.v2x.V2xMessage; +import org.eclipse.mosaic.lib.objects.vehicle.VehicleData; +import org.eclipse.mosaic.lib.objects.vehicle.VehicleDeparture; +import org.eclipse.mosaic.lib.util.objects.ObjectInstantiation; import org.eclipse.mosaic.rti.TIME; import org.eclipse.mosaic.rti.api.AbstractFederateAmbassador; import org.eclipse.mosaic.rti.api.IllegalValueException; +import org.eclipse.mosaic.rti.api.Interaction; import org.eclipse.mosaic.rti.api.InternalFederateException; import org.eclipse.mosaic.rti.api.parameters.AmbassadorParameter; -import org.eclipse.mosaic.fed.carma.ambassador.CarmaRegistrationMessage; - -import org.eclipse.mosaic.fed.carma.configuration.CarmaConfiguration; -import org.eclipse.mosaic.fed.application.ambassador.SimulationKernel; -import org.eclipse.mosaic.fed.carma.configuration.CarmaVehicleConfiguration; - +import javax.xml.bind.DatatypeConverter; import java.net.InetAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import org.eclipse.mosaic.rti.api.Interaction; -import org.eclipse.mosaic.lib.objects.vehicle.VehicleData; -import org.eclipse.mosaic.lib.objects.vehicle.VehicleDeparture; -import org.eclipse.mosaic.lib.util.objects.ObjectInstantiation; -import org.eclipse.mosaic.interactions.application.CarmaV2xMessageReception; -import org.eclipse.mosaic.interactions.application.ExternalMessage; -import org.eclipse.mosaic.interactions.traffic.VehicleUpdates; -import org.eclipse.mosaic.interactions.vehicle.VehicleFederateAssignment; -import org.eclipse.mosaic.lib.enums.DriveDirection; - -import javax.xml.bind.DatatypeConverter; - /** * Implementation of a {@link AbstractFederateAmbassador} for CARMA message * ambassador. diff --git a/co-simulation/fed/mosaic-infrastructure/pom.xml b/co-simulation/fed/mosaic-infrastructure/pom.xml index 011094c7..49fe34bc 100644 --- a/co-simulation/fed/mosaic-infrastructure/pom.xml +++ b/co-simulation/fed/mosaic-infrastructure/pom.xml @@ -25,6 +25,11 @@ mosaic-objects ${mosaic.version} + + gov.dot.fhwa.saxton + mosaic-carma-utils + ${mosaic.version} + org.eclipse.mosaic mosaic-interactions diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 23e499dd..6539fde8 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -16,21 +16,23 @@ package org.eclipse.mosaic.fed.infrastructure.ambassador; -import java.io.IOException; -import java.net.InetAddress; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.HashMap; -import java.util.Map; - -import org.eclipse.mosaic.fed.infrastructure.ambassador.InfrastructureRegistrationMessage; +import gov.dot.fhwa.saxton.CarmaV2xMessage; import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; import org.eclipse.mosaic.lib.enums.AdHocChannel; import org.eclipse.mosaic.lib.geo.GeoPoint; - +import org.eclipse.mosaic.lib.objects.addressing.AdHocMessageRoutingBuilder; +import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xContent; +import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xMessage; +import org.eclipse.mosaic.lib.objects.v2x.MessageRouting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; + /** * Session management class for Infrastructure instances communicating with * MOSAIC. @@ -102,6 +104,34 @@ private void newInfrastructureInstance(String infrastructureId, InetAddress rxMe managedInstances.put(infrastructureId, tmp); } + /** + * Callback to be invoked when CARMA Platform receives a V2X Message from the NS-3 simulation + * @param sourceAddr The V2X Message received + * @param txMsg The Host ID of the vehicle receiving the data + * @throws RuntimeException If the socket used to communicate with the platform experiences failure + */ + public V2xMessageTransmission onV2XMessageTx(InetAddress sourceAddr, CarmaV2xMessage txMsg) { + InfrastructureInstance sender = null; + for (InfrastructureInstance ci : managedInstances.values()) { + if (ci.getTargetAddress().equals(sourceAddr)) { + sender = ci; + } + } + + if (sender == null) { + // Unregistered instance attempting to send messages + throw new IllegalStateException("Unregistered CARMA Platform instance attempting to send messages via MOSAIC"); + } + + AdHocMessageRoutingBuilder messageRoutingBuilder = new AdHocMessageRoutingBuilder( + sender.getInfrastructureId(), sender.getLocation()).viaChannel(AdHocChannel.CCH); + + MessageRouting routing = messageRoutingBuilder.topoBroadCast(1); + + return new V2xMessageTransmission((long) currentSimulationTime, new ExternalV2xMessage(routing, + new ExternalV2xContent((long) currentSimulationTime, sender.getLocation(), txMsg.getPayload()))); + } + /** * External helper function to allow the ambassador to check if a given vehicle * ID is a registered CARMA Platform instance diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 356661e2..74d9da70 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -16,30 +16,30 @@ package org.eclipse.mosaic.fed.infrastructure.ambassador; -import org.eclipse.mosaic.rti.TIME; -import org.eclipse.mosaic.rti.api.AbstractFederateAmbassador; -import org.eclipse.mosaic.rti.api.IllegalValueException; -import org.eclipse.mosaic.rti.api.Interaction; -import org.eclipse.mosaic.rti.api.InternalFederateException; -import org.eclipse.mosaic.rti.api.parameters.AmbassadorParameter; +import gov.dot.fhwa.saxton.CarmaV2xMessage; +import gov.dot.fhwa.saxton.CarmaV2xMessageReceiver; import org.eclipse.mosaic.fed.infrastructure.configuration.InfrastructureConfiguration; +import org.eclipse.mosaic.interactions.application.ExternalMessage; +import org.eclipse.mosaic.interactions.application.InfrastructureV2xMessageReception; +import org.eclipse.mosaic.interactions.communication.AdHocCommunicationConfiguration; +import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; +import org.eclipse.mosaic.interactions.mapping.RsuRegistration; import org.eclipse.mosaic.lib.enums.AdHocChannel; import org.eclipse.mosaic.lib.geo.GeoPoint; +import org.eclipse.mosaic.lib.misc.Tuple; import org.eclipse.mosaic.lib.objects.communication.AdHocConfiguration; import org.eclipse.mosaic.lib.objects.communication.InterfaceConfiguration; import org.eclipse.mosaic.lib.util.objects.ObjectInstantiation; -import org.eclipse.mosaic.interactions.application.ExternalMessage; -import org.eclipse.mosaic.interactions.application.InfrastructureV2xMessageReception; -import org.eclipse.mosaic.interactions.communication.AdHocCommunicationConfiguration; -import org.eclipse.mosaic.interactions.mapping.RsuRegistration; - -import java.util.Collections; -import org.eclipse.mosaic.fed.infrastructure.ambassador.InfrastructureRegistrationMessage; +import org.eclipse.mosaic.rti.TIME; +import org.eclipse.mosaic.rti.api.AbstractFederateAmbassador; +import org.eclipse.mosaic.rti.api.IllegalValueException; +import org.eclipse.mosaic.rti.api.Interaction; +import org.eclipse.mosaic.rti.api.InternalFederateException; +import org.eclipse.mosaic.rti.api.parameters.AmbassadorParameter; import java.io.IOException; import java.net.InetAddress; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collections; import java.util.List; /** @@ -60,8 +60,9 @@ public class InfrastructureMessageAmbassador extends AbstractFederateAmbassador private InfrastructureRegistrationReceiver infrastructureRegistrationReceiver; private Thread registrationRxBackgroundThread; - private InfrastructureTimeMessageReceiver infrastructureTimeMessageReceiver; - private Thread v2xTimeRxBackgroundThread; + + private CarmaV2xMessageReceiver v2xMessageReceiver = new CarmaV2xMessageReceiver(1515); + private Thread v2xMessageBackgroundThread; private InfrastructureInstanceManager infrastructureInstanceManager = new InfrastructureInstanceManager(); private InfrastructureTimeInterface infrastructureTimeInterface; @@ -106,18 +107,24 @@ public InfrastructureMessageAmbassador(AmbassadorParameter ambassadorParameter) public void initialize(long startTime, long endTime) throws InternalFederateException { super.initialize(startTime, endTime); currentSimulationTime = startTime; - try { - rti.requestAdvanceTime(currentSimulationTime, 0, (byte) 1); - } catch (IllegalValueException e) { - log.error("Error during advanceTime request", e); - throw new InternalFederateException(e); - } infrastructureRegistrationReceiver = new InfrastructureRegistrationReceiver(); infrastructureRegistrationReceiver.init(); registrationRxBackgroundThread = new Thread(infrastructureRegistrationReceiver); registrationRxBackgroundThread.start(); + // TODO: Port 1517 assumed to be available. Need to double check. + v2xMessageReceiver = new CarmaV2xMessageReceiver(1517); + v2xMessageReceiver.init(); + v2xMessageBackgroundThread = new Thread(v2xMessageReceiver); + v2xMessageBackgroundThread.start(); + + try { + rti.requestAdvanceTime(currentSimulationTime, 0, (byte) 1); + } catch (IllegalValueException e) { + log.error("Error during advanceTime request", e); + throw new InternalFederateException(e); + } } /** @@ -245,6 +252,12 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder onDsrcRegistrationRequest(reg.getInfrastructureId()); } + List> newMessages = v2xMessageReceiver.getReceivedMessages(); + for (Tuple msg : newMessages) { + V2xMessageTransmission msgInt = infrastructureInstanceManager.onV2XMessageTx(msg.getA(), msg.getB()); + this.rti.triggerInteraction(msgInt); + } + timeSyncSeq += 1; InfrastructureTimeMessage timeSyncMessage = new InfrastructureTimeMessage(); timeSyncMessage.setSeq(timeSyncSeq); @@ -304,4 +317,12 @@ public boolean isTimeConstrained() { public boolean isTimeRegulating() { return false; } + + /** + * Test helper function to cleanup sockets and threads. + */ + protected void close() { + v2xMessageReceiver.stop(); + infrastructureRegistrationReceiver.stop(); + } } diff --git a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassadorTest.java b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassadorTest.java index 99c5e75f..07014bb7 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassadorTest.java +++ b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassadorTest.java @@ -16,28 +16,25 @@ package org.eclipse.mosaic.fed.infrastructure.ambassador; -import org.junit.Test; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - import org.eclipse.mosaic.lib.util.junit.TestFileRule; import org.eclipse.mosaic.rti.TIME; import org.eclipse.mosaic.rti.api.RtiAmbassador; import org.eclipse.mosaic.rti.api.parameters.AmbassadorParameter; import org.eclipse.mosaic.rti.api.parameters.FederateDescriptor; import org.eclipse.mosaic.rti.config.CLocalHost; - +import org.junit.After; import org.junit.Before; import org.junit.Rule; +import org.junit.Test; import org.junit.rules.RuleChain; import org.junit.rules.TemporaryFolder; import java.io.File; import java.io.IOException; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + /** * Tests for {@link InfrastructureMessageAmbassador}. */ @@ -79,6 +76,11 @@ public void setup() throws IOException { ambassador.setFederateDescriptor(handleMock); } + @After + public void teardown() throws IOException { + ambassador.close(); + } + @Test public void initialize() throws Throwable { diff --git a/co-simulation/lib/mosaic-carma-utils/pom.xml b/co-simulation/lib/mosaic-carma-utils/pom.xml new file mode 100644 index 00000000..cd4b087c --- /dev/null +++ b/co-simulation/lib/mosaic-carma-utils/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + org.eclipse.mosaic + mosaic-parent + 22.1-SNAPSHOT + ../../pom.xml + + + gov.dot.fhwa.saxton + mosaic-carma-utils + + + + org.eclipse.mosaic + mosaic-rti-api + ${mosaic.version} + + + org.eclipse.mosaic + mosaic-objects + ${mosaic.version} + + + org.eclipse.mosaic + mosaic-interactions + ${mosaic.version} + + + org.eclipse.mosaic + mosaic-utils + ${mosaic.version} + + + org.eclipse.mosaic + mosaic-geomath + ${mosaic.version} + + + ch.qos.logback + logback-classic + + + junit + junit + 4.11 + test + + + \ No newline at end of file diff --git a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaV2xMessage.java b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java similarity index 99% rename from co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaV2xMessage.java rename to co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java index 9ce2cff4..91ce3e0c 100644 --- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaV2xMessage.java +++ b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java @@ -14,7 +14,7 @@ * the License. */ -package org.eclipse.mosaic.fed.carma.ambassador; +package gov.dot.fhwa.saxton; import java.net.InetAddress; import java.nio.charset.StandardCharsets; diff --git a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaV2xMessageReceiver.java b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessageReceiver.java similarity index 79% rename from co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaV2xMessageReceiver.java rename to co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessageReceiver.java index 222ff5cb..08ab0939 100644 --- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaV2xMessageReceiver.java +++ b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessageReceiver.java @@ -14,7 +14,7 @@ * the License. */ -package org.eclipse.mosaic.fed.carma.ambassador; +package gov.dot.fhwa.saxton; import org.eclipse.mosaic.lib.misc.Tuple; @@ -38,10 +38,31 @@ public class CarmaV2xMessageReceiver implements Runnable { private Queue> rxQueue = new LinkedList<>(); private DatagramSocket listenSocket = null; - private static final int listenPort = 1516; + private int listenPort; private boolean running = true; private static final int UDP_MTU = 1535; + /** + * Default constructor added to preserve prior behavior after refactor into new package for re-use in Infrastructure + * Ambassador. + * + * Assumes a desired listen port of 1516. + */ + public CarmaV2xMessageReceiver() { + this.listenPort = 1516; + } + + /** + * Recommended constructor for CarmaV2XMessageReceiver. + * + * @param listenPort The host port that should be used to bind a UDP socket for receiving V2X message transmission + * events from connected CARMA software instances. An exception will be thrown in init() if this + * port is unavailable or socket binding fails for other reasons. + */ + public CarmaV2xMessageReceiver(int listenPort) { + this.listenPort = listenPort; + } + /** * Initialize the listen socket for messages from the CARMA Platform NS-3 Adapter * @throws RuntimeException iff socket instantiation fails diff --git a/co-simulation/pom.xml b/co-simulation/pom.xml index 34dc9d97..6ca1e3c5 100644 --- a/co-simulation/pom.xml +++ b/co-simulation/pom.xml @@ -80,6 +80,7 @@ app/tutorials/traffic-light-communication app/tutorials/weather-warning app/tutorials/external-communication + lib/mosaic-carma-utils From 8d632d9aa7f9e17f07f7922d633d73468035c630 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Tue, 25 Apr 2023 10:12:31 -0400 Subject: [PATCH 096/124] Added log when infrastructure instance message being received --- .../ambassador/InfrastructureRegistrationReceiver.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java index 3437535f..246d8d38 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiver.java @@ -24,8 +24,13 @@ import java.util.LinkedList; import java.util.List; import java.util.Queue; + + import com.google.gson.Gson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Worker thread Runnable for operating a listen socket to receive outbound * Infrastructure Messages from Infrastructure Device instances @@ -41,6 +46,7 @@ public class InfrastructureRegistrationReceiver implements Runnable { private static final int listenPort = 1615; private boolean running = false; private static final int UDP_MTU = 1635; + private final Logger log = LoggerFactory.getLogger(this.getClass()); /** * Initialize the listen socket for messages from the Infrastructure Device NS-3 @@ -82,6 +88,7 @@ public void run() { // Enqueue message for processing on main thread synchronized (rxQueue) { + log.info("New Infrastructure instance '{}' received with Infrastructure Registration Receiver.", parsedMessage.getInfrastructureId()); rxQueue.add(parsedMessage); } } From bc15efab653d353bfcabdfc01fc9a4bb8fbfc895 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Tue, 25 Apr 2023 10:14:04 -0400 Subject: [PATCH 097/124] Rephrased log to make it clearer --- .../ambassador/InfrastructureInstanceManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 5e8828ae..0b84fd2b 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -93,7 +93,7 @@ private void newInfrastructureInstance(String infrastructureId, InetAddress rxMe timeSyncPort, location); try { tmp.bind(); - log.info("New infrastructure registration received with ID '{}'", infrastructureId); + log.info("New Infrastructure instance '{}' registered with Infrastructure Instance Manager.", infrastructureId); } catch (IOException e) { log.error("Failed to bind infrastructure instance with ID '{}' to its RX message socket: {}", infrastructureId, e.getMessage()); From eab7859a7d5b62997f0d518456ba96e6d511c2c4 Mon Sep 17 00:00:00 2001 From: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> Date: Tue, 25 Apr 2023 15:39:23 -0400 Subject: [PATCH 098/124] Added log to CARMA vehicle registration receiver --- .../fed/carma/ambassador/CarmaRegistrationReceiver.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationReceiver.java b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationReceiver.java index 6d419d5c..7ea8ae95 100644 --- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationReceiver.java +++ b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationReceiver.java @@ -26,6 +26,8 @@ import java.util.List; import java.util.Queue; import com.google.gson.Gson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Worker thread Runnable for operating a listen socket to receive outbound V2X Messages from CARMA Platform instances @@ -39,6 +41,7 @@ public class CarmaRegistrationReceiver implements Runnable { private static final int listenPort = 1515; private boolean running = true; private static final int UDP_MTU = 1535; + private final Logger log = LoggerFactory.getLogger(this.getClass()); /** * Initialize the listen socket for messages from the CARMA Platform NS-3 Adapter @@ -70,6 +73,7 @@ public void run() { // Enqueue message for processing on main thread synchronized (rxQueue) { + log.info("New Infrastructure instance '{}' received with Infrastructure Registration Receiver.", parsedMessage.getCarmaVehicleId()); rxQueue.add(parsedMessage); } } From 224ee63f4ff967ba16b3be9c90b0889a112e9c24 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 26 Apr 2023 12:06:06 -0400 Subject: [PATCH 099/124] Updated Town04 test scenario --- .../scenarios/Town04/carma/carma_config.json | 27 +++---------------- .../infrastructure/infrastructure_config.json | 3 +-- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/carma/carma_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/carma/carma_config.json index 7a49b85b..31046b6a 100644 --- a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/carma/carma_config.json +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/carma/carma_config.json @@ -1,25 +1,4 @@ { - "updateInterval": 100, - "carmaVehicles":[ -{ - "routeID": "0", - "lane": 1, - "position": 0, - "departSpeed": 0, - "vehicleType": "vehicle.chevrolet.impala", - "applications":["org.eclipse.mosaic.app.tutorial.VehicleCommunicationApp"], - "geoPosition": { - "latitude": 52.579272059028646, - "longitude": 13.467165499469328 - }, - "projectedPosition": - { - "x": 501.62, - "y": 116.95 - }, - - "heading": 24.204351784500364, - "slope": 0.0 -}], -"senderCarmaVehicleId":"carma_0" -} + "updateInterval": 100, + "carmaVehicles": [] +} \ No newline at end of file diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/infrastructure/infrastructure_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/infrastructure/infrastructure_config.json index 179bfc13..196145f7 100644 --- a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/infrastructure/infrastructure_config.json +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/infrastructure/infrastructure_config.json @@ -1,4 +1,3 @@ { - "updateInterval": 1000, - "senderRSUId": "rsu_0" + "updateInterval": 100 } From 49f0caa2420dee6d9f5383ddb2bb9edff14fdb3a Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 26 Apr 2023 12:07:10 -0400 Subject: [PATCH 100/124] Fixed the dependency issue (modified by Kyle) --- co-simulation/bundle/pom.xml | 7 ++++++- co-simulation/bundle/src/assembly/mosaic-bundle.xml | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/co-simulation/bundle/pom.xml b/co-simulation/bundle/pom.xml index 6b17b9df..a411a546 100644 --- a/co-simulation/bundle/pom.xml +++ b/co-simulation/bundle/pom.xml @@ -37,6 +37,11 @@ + + gov.dot.fhwa.saxton + mosaic-carma-utils + ${mosaic.version} + org.eclipse.mosaic mosaic-objects @@ -257,4 +262,4 @@ - + \ No newline at end of file diff --git a/co-simulation/bundle/src/assembly/mosaic-bundle.xml b/co-simulation/bundle/src/assembly/mosaic-bundle.xml index d7444c27..5381df6b 100644 --- a/co-simulation/bundle/src/assembly/mosaic-bundle.xml +++ b/co-simulation/bundle/src/assembly/mosaic-bundle.xml @@ -108,6 +108,7 @@ lib/mosaic org.eclipse.mosaic:mosaic-* + gov.dot.fhwa.saxton:mosaic-carma-utils From 2ae1ac50b9862fbba72cc84eeb9aab3aaabdc41f Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Wed, 26 Apr 2023 13:46:12 -0400 Subject: [PATCH 101/124] Add more descriptive comments to onDsrcRegistrationRequest method --- .../InfrastructureMessageAmbassador.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index cb059ed7..980ccda2 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -69,7 +69,8 @@ public class InfrastructureMessageAmbassador extends AbstractFederateAmbassador private Thread v2xMessageBackgroundThread; private InfrastructureInstanceManager infrastructureInstanceManager = new InfrastructureInstanceManager(); - private InfrastructureTimeInterface infrastructureTimeInterface = new InfrastructureTimeInterface(infrastructureInstanceManager); + private InfrastructureTimeInterface infrastructureTimeInterface = new InfrastructureTimeInterface( + infrastructureInstanceManager); private int timeSyncSeq = 0; @@ -178,11 +179,13 @@ private synchronized void receiveInteraction(InfrastructureV2xMessageReception i private void onDsrcRegistrationRequest(String infrastructureId) throws UnknownHostException { // Create an InterfaceConfiguration object to represent the configuration of the // Ad-Hoc interface - // Set the IP address and subnet mask to null for now - // Set the transmit power to 50 dBm and the maximum range to 100 meters + // TODO: Replace the IP address of the ad-hoc interface if necessary + // TODO: Replace the subnet mask of the ad-hoc interface if necessary + // TODO: Replace the transmit power of the ad-hoc interface (in dBm) if necessary + // TODO: Replace the communication range of the ad-hoc interface (in meters) if necessary InterfaceConfiguration interfaceConfig = new InterfaceConfiguration.Builder(AdHocChannel.SCH1) - .ip( (Inet4Address) Inet4Address.getByName("192.168.0.1") ) //TODO: set IP address if needed - .subnet((Inet4Address) Inet4Address.getByName("255.255.255.0")) //TODO: set subnet if needed + .ip((Inet4Address) Inet4Address.getByName("192.168.0.1")) + .subnet((Inet4Address) Inet4Address.getByName("255.255.255.0")) .power(50) .radius(100.0) .create(); @@ -207,6 +210,7 @@ private void onDsrcRegistrationRequest(String infrastructureId) throws UnknownHo } } + /** * Performs registration of a new infrastructure instance and sets up * communication interfaces. From 8655813597339691dff9e0410b7311118eb95f7d Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 26 Apr 2023 16:24:40 -0400 Subject: [PATCH 102/124] Fixed Infrastructure Time message to use timestep in milliseconds (#129) # PR Details ## Description Updated infrastructure time message to use millisecond timestep instead of nanosecond time step since that is what V2X-Hub and CARMA-Streets expect and we do not do any nanosecond level logic on infrastructure. ## Related Issue ## Motivation and Context Fix bugs related to V2X-Hub/CARMA Streets Time Synchronization ## How Has This Been Tested? ## Types of changes - [ x] Defect fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that cause existing functionality to change) ## Checklist: - [ ] I have added any new packages to the sonar-scanner.properties file - [ x] My change requires a change to the documentation. - [ x] I have updated the documentation accordingly. - [x ] I have read the **CONTRIBUTING** document. [CARMA Contributing Guide](Contributing.md) - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. --- .../ambassador/InfrastructureMessageAmbassador.java | 5 +++-- .../infrastructure/ambassador/InfrastructureTimeMessage.java | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 980ccda2..4b79d82f 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -236,7 +236,7 @@ private void onRsuRegistrationRequest(String infrastructureId, GeoPoint location * time advance to the federate. Any unprocessed interactions are forwarded to * the federate using the processInteraction method before this call is made. * - * @param time The timestamp indicating the time to which the federate can + * @param time The timestamp (in nanoseconds) indicating the time to which the federate can * advance its local time. */ @Override @@ -269,7 +269,8 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder timeSyncSeq += 1; InfrastructureTimeMessage timeSyncMessage = new InfrastructureTimeMessage(); timeSyncMessage.setSeq(timeSyncSeq); - timeSyncMessage.setTimestep(currentSimulationTime); + // nanoseconds to milliseconds for InfrastructureTimeMessage + timeSyncMessage.setTimestep(currentSimulationTime/1000000); infrastructureTimeInterface.onTimeStepUpdate(timeSyncMessage); // TODO: Handle any queued V2X message receiver's received messages diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java index 821aed0b..85eff91b 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureTimeMessage.java @@ -25,7 +25,9 @@ * */ public class InfrastructureTimeMessage { + // Timestamp in milliseconds private long timestep; + // Sequence number for time message private int seq; public InfrastructureTimeMessage() { From 4bedd5c68eec30da064dc1f2f3bc3aaee3892f67 Mon Sep 17 00:00:00 2001 From: Kyle Rush Date: Thu, 27 Apr 2023 12:40:57 -0700 Subject: [PATCH 103/124] Resolve parser tokenization error in CarmaV2xMessage. Parser for AMF format was incorrectly tokenizing on '=' instead of also on linebreaks --- .../gov/dot/fhwa/saxton/CarmaV2xMessage.java | 13 +++++++- .../dot/fhwa/saxton/CarmaV2xMessageTest.java | 32 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java diff --git a/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java index 91ce3e0c..7f78890d 100644 --- a/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java +++ b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java @@ -18,6 +18,8 @@ import java.net.InetAddress; import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.regex.Pattern; /** * Message to be sent or received by the CARMA Platform NS-3 Adapater interface @@ -122,7 +124,16 @@ public boolean isEncryption() { */ private void parseV2xMessage(byte[] buf) { String msg = new String(buf, StandardCharsets.UTF_8); - String[] msgParts = msg.split("="); + + // Modeled after Stackoverflow answer by user Eritrean: https://stackoverflow.com/users/5176992/eritrean + // On Question: https://stackoverflow.com/questions/61029164/how-to-split-string-by-comma-and-newline-n-in-java + // Asked by user: https://stackoverflow.com/users/12315939/kopite1905 + // Used under CC BY-SA 4.0 license: https://creativecommons.org/licenses/by-sa/4.0/ + String[] msgParts = Pattern.compile("\\R") + .splitAsStream(msg) + .map(s -> s.split("=")) + .flatMap(Arrays::stream) + .toArray(String[]::new); for (int i = 0; i < msgParts.length; i++) { if (msgParts[i].equals("Version")) { diff --git a/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java b/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java new file mode 100644 index 00000000..ecbb5300 --- /dev/null +++ b/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java @@ -0,0 +1,32 @@ +package gov.dot.fhwa.saxton; + +import org.junit.Before; +import org.junit.Test; + +public class CarmaV2xMessageTest { + + private final String sampleMessage = "Version=0.7\n" + +"Type=BSM\n" + +"PSID=0020\n" + +"Priority=6\n" + +"TxMode=ALT\n" + +"TxChannel=172\n" + +"TxInterval=0\n" + +"DeliveryStart=\n" + +"DeliveryStop=\n" + +"Signature=False\n" + +"Encryption=False\n" + +"Payload=00142500400000000f0e35a4e900eb49d20000007fffffff8ffff080fdfa1fa1007fff8000960fa0\n"; + + @Before + public void setUp() throws Exception { + //manager = new InfrastructureInstanceManager(); + //registration = mock(InfrastructureRegistrationMessage.class); + } + + @Test + public void testCarmaV2xMessageParse() { + CarmaV2xMessage test = new CarmaV2xMessage(sampleMessage.getBytes()); + } + +} From fea19145fcf09d689a9977487878f731425910de Mon Sep 17 00:00:00 2001 From: Kyle Rush Date: Thu, 27 Apr 2023 12:42:23 -0700 Subject: [PATCH 104/124] Cleanup test file --- .../dot/fhwa/saxton/CarmaV2xMessageTest.java | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java b/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java index ecbb5300..28a68dda 100644 --- a/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java +++ b/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2019-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. + */ + package gov.dot.fhwa.saxton; import org.junit.Before; @@ -5,28 +21,28 @@ public class CarmaV2xMessageTest { - private final String sampleMessage = "Version=0.7\n" + -"Type=BSM\n" + -"PSID=0020\n" + -"Priority=6\n" + -"TxMode=ALT\n" + -"TxChannel=172\n" + -"TxInterval=0\n" + -"DeliveryStart=\n" + -"DeliveryStop=\n" + -"Signature=False\n" + -"Encryption=False\n" + -"Payload=00142500400000000f0e35a4e900eb49d20000007fffffff8ffff080fdfa1fa1007fff8000960fa0\n"; + private final String sampleMessage = + "Version=0.7\n" + + "Type=BSM\n" + + "PSID=0020\n" + + "Priority=6\n" + + "TxMode=ALT\n" + + "TxChannel=172\n" + + "TxInterval=0\n" + + "DeliveryStart=\n" + + "DeliveryStop=\n" + + "Signature=False\n" + + "Encryption=False\n" + + "Payload=00142500400000000f0e35a4e900eb49d20000007fffffff8ffff080fdfa1fa1007fff8000960fa0\n"; @Before public void setUp() throws Exception { - //manager = new InfrastructureInstanceManager(); - //registration = mock(InfrastructureRegistrationMessage.class); } @Test public void testCarmaV2xMessageParse() { CarmaV2xMessage test = new CarmaV2xMessage(sampleMessage.getBytes()); + assert(test != null); } } From d492bd7da40d1dd1d71b344b10a0c8290fb00ffa Mon Sep 17 00:00:00 2001 From: Kyle Rush Date: Thu, 27 Apr 2023 14:03:33 -0700 Subject: [PATCH 105/124] Add logic to find start of AMF message --- .../src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java | 5 +++-- .../test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java index 7f78890d..298d0615 100644 --- a/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java +++ b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java @@ -123,7 +123,8 @@ public boolean isEncryption() { * @param buf A binary array containing the data sent from the CARMA Platform's ns3 adapter */ private void parseV2xMessage(byte[] buf) { - String msg = new String(buf, StandardCharsets.UTF_8); + String rawMsg = new String(buf, StandardCharsets.UTF_8); + String msg = rawMsg.substring(rawMsg.indexOf("Version"), rawMsg.length()); // Modeled after Stackoverflow answer by user Eritrean: https://stackoverflow.com/users/5176992/eritrean // On Question: https://stackoverflow.com/questions/61029164/how-to-split-string-by-comma-and-newline-n-in-java @@ -161,7 +162,7 @@ private void parseV2xMessage(byte[] buf) { } else if (msgParts[i].equals("Payload")) { payload = msgParts[++i]; } else { - throw new IllegalArgumentException("No such field in CarmaV2xMessage."); + throw new IllegalArgumentException("No such field in CarmaV2xMessage: " + msgParts[i]); } } } diff --git a/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java b/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java index 28a68dda..e5f28b58 100644 --- a/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java +++ b/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java @@ -45,4 +45,11 @@ public void testCarmaV2xMessageParse() { assert(test != null); } + @Test + public void testCarmaV2xMessageParse2() { + String sampleMessage2 = "jaksldfjkl;asdfjkl;asdfjkalsdfjkdjjsdjdf" + sampleMessage; + CarmaV2xMessage test = new CarmaV2xMessage(sampleMessage.getBytes()); + assert(test != null); + } + } From e87a2dcff020b2fda7c0f7fd6c736058823c9661 Mon Sep 17 00:00:00 2001 From: Kyle Rush Date: Thu, 27 Apr 2023 14:16:08 -0700 Subject: [PATCH 106/124] Add logs for message processing --- .../fhwa/saxton/CarmaV2xMessageReceiver.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessageReceiver.java b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessageReceiver.java index 07410c15..27c6303a 100644 --- a/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessageReceiver.java +++ b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessageReceiver.java @@ -17,6 +17,8 @@ package gov.dot.fhwa.saxton; import org.eclipse.mosaic.lib.misc.Tuple; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.DatagramPacket; @@ -42,6 +44,8 @@ public class CarmaV2xMessageReceiver implements Runnable { private boolean running = true; private static final int UDP_MTU = 1536; + private final Logger log = LoggerFactory.getLogger(this.getClass()); + /** * Default constructor added to preserve prior behavior after refactor into new package for re-use in Infrastructure * Ambassador. @@ -70,6 +74,7 @@ public CarmaV2xMessageReceiver(int listenPort) { public void init() { try { listenSocket = new DatagramSocket(listenPort); + log.info("CarmaV2xMessageReceiver started listening on UDP port: " + listenPort); } catch (SocketException e) { throw new RuntimeException(e); } @@ -86,17 +91,23 @@ public void run() { try { listenSocket.receive(msg); + log.info("CarmaV2xMessageReceiver received message of size: " + msg.getLength() + " from client " + msg.getAddress().toString() + "."); } catch (IOException e) { throw new RuntimeException(e); } // parse message - CarmaV2xMessage parsedMessage = new CarmaV2xMessage(msg.getData()); - - // Enqueue message for processing on main thread - synchronized (rxQueue) { - rxQueue.add(new Tuple<>(senderAddr, parsedMessage)); - } + try { + CarmaV2xMessage parsedMessage = new CarmaV2xMessage(msg.getData()); + + // Enqueue message for processing on main thread + synchronized (rxQueue) { + rxQueue.add(new Tuple<>(senderAddr, parsedMessage)); + log.info("CarmaV2xMessageReceiver enqueued message of size: " + msg.getLength() + " from client " + msg.getAddress().toString() + "."); + } + } catch (IllegalArgumentException parseError) { + log.warn("CarmaV2xMessageReceiver received malformed message with length: " + msg.getLength() + " from client " + msg.getAddress().toString() + "! Reason: " + parseError.getMessage() +". Discarding..."); + } } } From 316ddd3cf0a3f85adef07865ad1faec2519119a9 Mon Sep 17 00:00:00 2001 From: Kyle Rush Date: Thu, 27 Apr 2023 14:20:00 -0700 Subject: [PATCH 107/124] Fix typo in unit test --- .../src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java b/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java index e5f28b58..476f3fd9 100644 --- a/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java +++ b/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java @@ -48,7 +48,7 @@ public void testCarmaV2xMessageParse() { @Test public void testCarmaV2xMessageParse2() { String sampleMessage2 = "jaksldfjkl;asdfjkl;asdfjkalsdfjkdjjsdjdf" + sampleMessage; - CarmaV2xMessage test = new CarmaV2xMessage(sampleMessage.getBytes()); + CarmaV2xMessage test = new CarmaV2xMessage(sampleMessage2.getBytes()); assert(test != null); } From 955e396acf27a5501cb6b1b2677e7e40dd74f776 Mon Sep 17 00:00:00 2001 From: Kyle Rush Date: Fri, 28 Apr 2023 10:56:29 -0700 Subject: [PATCH 108/124] Break after parsing the final field of the message --- co-simulation/lib/mosaic-carma-utils/pom.xml | 6 +++++ .../gov/dot/fhwa/saxton/CarmaV2xMessage.java | 4 +-- .../dot/fhwa/saxton/CarmaV2xMessageTest.java | 27 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/co-simulation/lib/mosaic-carma-utils/pom.xml b/co-simulation/lib/mosaic-carma-utils/pom.xml index cd4b087c..3c605899 100644 --- a/co-simulation/lib/mosaic-carma-utils/pom.xml +++ b/co-simulation/lib/mosaic-carma-utils/pom.xml @@ -49,5 +49,11 @@ 4.11 test + + commons-codec + commons-codec + 1.15 + test + \ No newline at end of file diff --git a/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java index 298d0615..d29de5c7 100644 --- a/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java +++ b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java @@ -17,7 +17,6 @@ package gov.dot.fhwa.saxton; import java.net.InetAddress; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.regex.Pattern; @@ -123,7 +122,7 @@ public boolean isEncryption() { * @param buf A binary array containing the data sent from the CARMA Platform's ns3 adapter */ private void parseV2xMessage(byte[] buf) { - String rawMsg = new String(buf, StandardCharsets.UTF_8); + String rawMsg = new String(buf); String msg = rawMsg.substring(rawMsg.indexOf("Version"), rawMsg.length()); // Modeled after Stackoverflow answer by user Eritrean: https://stackoverflow.com/users/5176992/eritrean @@ -161,6 +160,7 @@ private void parseV2xMessage(byte[] buf) { encryption = Boolean.parseBoolean(msgParts[++i]); } else if (msgParts[i].equals("Payload")) { payload = msgParts[++i]; + break; // Break on the final field of the message } else { throw new IllegalArgumentException("No such field in CarmaV2xMessage: " + msgParts[i]); } diff --git a/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java b/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java index 476f3fd9..6e7e99be 100644 --- a/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java +++ b/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java @@ -16,8 +16,10 @@ package gov.dot.fhwa.saxton; +import org.apache.commons.codec.DecoderException; import org.junit.Before; import org.junit.Test; +import org.apache.commons.codec.binary.Hex; public class CarmaV2xMessageTest { @@ -35,6 +37,24 @@ public class CarmaV2xMessageTest { "Encryption=False\n" + "Payload=00142500400000000f0e35a4e900eb49d20000007fffffff8ffff080fdfa1fa1007fff8000960fa0\n"; + private final String sampleMessage2 = "450001039c69400040119f7e7f000001" + + "7f0000018fdc05ec00efff0256657273" + + "696f6e3d302e370a547970653d42534d" + + "0a505349443d303032300a5072696f72" + + "6974793d360a54784d6f64653d414c54" + + "0a54784368616e6e656c3d3137320a54" + + "78496e74657276616c3d300a44656c69" + + "7665727953746172743d0a44656c6976" + + "65727953746f703d0a5369676e617475" + + "72653d46616c73650a456e6372797074" + + "696f6e3d46616c73650a5061796c6f61" + + "643d3030313432353032633030303030" + + "30303239316562356134653930306562" + + "34396432303030303030376666666666" + + "66663866666666303830666466613166" + + "61313030376666663830303039363066" + + "61300a"; + @Before public void setUp() throws Exception { } @@ -52,4 +72,11 @@ public void testCarmaV2xMessageParse2() { assert(test != null); } + @Test + public void testCarmaV2xMessageParse3() throws DecoderException { + byte[] bytes = Hex.decodeHex(sampleMessage2.toCharArray()); + CarmaV2xMessage test = new CarmaV2xMessage(bytes); + assert(test != null); + } + } From a17a975db79cabdf8c9ad11be492721319eff787 Mon Sep 17 00:00:00 2001 From: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> Date: Fri, 12 May 2023 17:10:03 -0400 Subject: [PATCH 109/124] added ip argument for traci to connect with sumo --- evc-sumo/src/evc_sumo_bridge.py | 5 ++++- evc-sumo/src/sumo_connector.py | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/evc-sumo/src/evc_sumo_bridge.py b/evc-sumo/src/evc_sumo_bridge.py index 03a31782..a6f566f1 100644 --- a/evc-sumo/src/evc_sumo_bridge.py +++ b/evc-sumo/src/evc_sumo_bridge.py @@ -23,7 +23,7 @@ from sumo_connector import SumoConnector def run(args): - sumo_connector = SumoConnector(args.traci_port, args.traci_order_num) + sumo_connector = SumoConnector(arg.traci_ip, args.traci_port, args.traci_order_num) evc_connector = EvcConnector(args.asc3app_path, args.evc_sumo_cfg_path) evc_connector.run(sumo_connector) @@ -32,6 +32,9 @@ def run(args): arg.add_argument('--asc3app-path', required=True, help='asc3app-application file path') + arg.add_argument('--traci-ip', + default="localhost", + help='Traci ip to connect to SUMO') arg.add_argument('--traci-port', default=2010, help='Traci port to connect to SUMO') diff --git a/evc-sumo/src/sumo_connector.py b/evc-sumo/src/sumo_connector.py index a0fe6f5f..6a3fc049 100644 --- a/evc-sumo/src/sumo_connector.py +++ b/evc-sumo/src/sumo_connector.py @@ -26,7 +26,7 @@ from contextlib import contextmanager class SumoConnector: - def __init__(self, port, order_num): + def __init__(self, ip, port, order_num): """ Initialize SumoConnector object @@ -37,6 +37,7 @@ def __init__(self, port, order_num): - order_num: The priority order number of the connection to SUMO, which should be consistent with other SUMO connections if any type: string """ + self.ip = ip self.port = port self.order_num = order_num self.traci = traci @@ -52,7 +53,7 @@ def traci_handler(self): """ Initialize traci """ - self.traci.init(int(self.port)) + self.traci.init(self.ip, int(self.port)) self.traci.setOrder(self.order_num) yield self.traci.close() From 1c4a74fa78f9d57c0bb72a536fb83742f06d5ed2 Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Mon, 15 May 2023 13:51:31 -0400 Subject: [PATCH 110/124] Update evc_sumo_bridge.py --- evc-sumo/src/evc_sumo_bridge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evc-sumo/src/evc_sumo_bridge.py b/evc-sumo/src/evc_sumo_bridge.py index a6f566f1..138f013c 100644 --- a/evc-sumo/src/evc_sumo_bridge.py +++ b/evc-sumo/src/evc_sumo_bridge.py @@ -23,7 +23,7 @@ from sumo_connector import SumoConnector def run(args): - sumo_connector = SumoConnector(arg.traci_ip, args.traci_port, args.traci_order_num) + sumo_connector = SumoConnector(args.traci_ip, args.traci_port, args.traci_order_num) evc_connector = EvcConnector(args.asc3app_path, args.evc_sumo_cfg_path) evc_connector.run(sumo_connector) From e6b897d895fb2dcf5dd4f0ceb3957586f6648d0c Mon Sep 17 00:00:00 2001 From: Cheng Yuan Date: Mon, 15 May 2023 13:51:35 -0400 Subject: [PATCH 111/124] Update sumo_connector.py --- evc-sumo/src/sumo_connector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evc-sumo/src/sumo_connector.py b/evc-sumo/src/sumo_connector.py index 6a3fc049..d7a27a6d 100644 --- a/evc-sumo/src/sumo_connector.py +++ b/evc-sumo/src/sumo_connector.py @@ -53,7 +53,7 @@ def traci_handler(self): """ Initialize traci """ - self.traci.init(self.ip, int(self.port)) + self.traci.init(host=self.ip, port=int(self.port)) self.traci.setOrder(self.order_num) yield self.traci.close() From 17407f2ff1501815bee317b461c0f7e475a1b738 Mon Sep 17 00:00:00 2001 From: Kyle Rush Date: Wed, 17 May 2023 17:35:45 -0400 Subject: [PATCH 112/124] Fix/add missing msg rx logic for infra (#135) # PR Details ## Description ## Related Issue Fixes a variety of issues related to NS-3 integration with CARMA Streets and platform. Integration is not functional to the point that messages from streets and platform can enter NS-3 and be simulated, and then exit NS-3 and be received by their respective systems. ## How Has This Been Tested? Tested on local simulation PCs prior to full-system integration and verificaiton ## Types of changes - [x] Defect fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that cause existing functionality to change) ## Checklist: - [x] I have added any new packages to the sonar-scanner.properties file - [ ] My change requires a change to the documentation. - [x] I have updated the documentation accordingly. - [x] I have read the **CONTRIBUTING** document. [CARMA Contributing Guide](Contributing.md) - [x] I have added tests to cover my changes. - [x] All new and existing tests passed. --------- Co-authored-by: Cheng Yuan Co-authored-by: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> --- co-simulation/NOTICE-THIRD-PARTY.md | 58 +- .../src/assembly/resources/etc/logback.xml | 54 +- .../src/assembly/resources/etc/runtime.json | 7 +- .../Town04_test/application/Town04.db | Bin 0 -> 462848 bytes .../scenarios/Town04_test/carla/bridge.sh | 6 + .../Town04_test/carla/carla_config.json | 6 + .../Town04_test/carma/carma_config.json | 4 + .../infrastructure/infrastructure_config.json | 3 + .../Town04_test/mapping/mapping_config.json | 6 + .../scenarios/Town04_test/ns3/ns3_config.json | 20 + .../Town04_test/ns3/ns3_federate_config.xml | 93 + .../Town04_test/output/output_config.xml | 204 + .../Town04_test/scenario_config.json | 40 + .../scenarios/Town04_test/sumo/Town04.net.xml | 5373 +++++++++++++++++ .../scenarios/Town04_test/sumo/Town04.rou.xml | 16 + .../scenarios/Town04_test/sumo/Town04.sumocfg | 17 + .../scenarios/Town04_test/sumo/detector.xml | 19 + .../Town04_test/sumo/sumo_config.json | 4 + co-simulation/fed/mosaic-carla/pom.xml | 5 + .../fed/carla/ambassador/CarlaAmbassador.java | 36 +- co-simulation/fed/mosaic-carma/pom.xml | 15 + .../fed/carma/ambassador/CarmaInstance.java | 3 +- .../ambassador/CarmaInstanceManager.java | 31 +- .../ambassador/CarmaMessageAmbassador.java | 248 +- .../ambassador/CarmaRegistrationReceiver.java | 2 +- .../fed/mosaic-infrastructure/pom.xml | 16 + .../InfrastructureInstanceManager.java | 33 +- .../InfrastructureMessageAmbassador.java | 103 +- co-simulation/lib/mosaic-carma-utils/pom.xml | 11 +- .../gov/dot/fhwa/saxton/CarmaV2xMessage.java | 6 +- .../fhwa/saxton/CarmaV2xMessageReceiver.java | 7 +- .../dot/fhwa/saxton/CarmaV2xMessageTest.java | 84 + .../advanced/ExternalVehicleRegistration.java | 97 + .../coupling/AbstractNetworkAmbassador.java | 38 +- 34 files changed, 6437 insertions(+), 228 deletions(-) create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/application/Town04.db create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carla/bridge.sh create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carla/carla_config.json create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carma/carma_config.json create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/infrastructure/infrastructure_config.json create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/mapping/mapping_config.json create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/ns3/ns3_config.json create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/ns3/ns3_federate_config.xml create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/output/output_config.xml create mode 100755 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/scenario_config.json create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.net.xml create mode 100755 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.rou.xml create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.sumocfg create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/detector.xml create mode 100644 co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/sumo_config.json create mode 100644 co-simulation/lib/mosaic-interactions/src/main/java/org/eclipse/mosaic/interactions/mapping/advanced/ExternalVehicleRegistration.java diff --git a/co-simulation/NOTICE-THIRD-PARTY.md b/co-simulation/NOTICE-THIRD-PARTY.md index 3f1d33c3..43f89a85 100644 --- a/co-simulation/NOTICE-THIRD-PARTY.md +++ b/co-simulation/NOTICE-THIRD-PARTY.md @@ -13,6 +13,14 @@ Apache Commons CLI (1.4) * Source: https://svn.apache.org/viewvc/commons/proper/cli/trunk/ +Apache Commons Codec (1.15) + + * License: Apache License, Version 2.0 + * Maven artifact: `commons-codec:commons-codec:1.15` + * Project: https://commons.apache.org/proper/commons-codec/ + * Source: https://github.com/apache/commons-codec + + Apache Commons Configuration (2.5) * License: Apache License, Version 2.0 @@ -77,6 +85,22 @@ Commons JXPath (1.3) * Source: https://svn.apache.org/repos/asf/commons/proper/jxpath/trunk +Extended StAX API (1.8.1) + + * License: Eclipse Distribution License - v 1.0 + * Maven artifact: `org.jvnet.staxex:stax-ex:1.8.1` + * Project: https://projects.eclipse.org/projects/ee4j/stax-ex + * Source: https://github.com/eclipse-ee4j/jaxb-stax-ex + + +fastinfoset (1.2.16) + + * License: Apache License, Version 2.0, Eclipse Distribution License - v 1.0 + * Maven artifact: `com.sun.xml.fastinfoset:FastInfoset:1.2.16` + * Project: https://projects.eclipse.org/projects/ee4j.jaxb-impl/FastInfoset + * Source: https://github.com/eclipse-ee4j/jaxb-fi/FastInfoset + + Findbugs Annotations under Apache License (1.3.9-1) * License: Apache License, Version 2.0 @@ -133,6 +157,14 @@ HPPC Collections (0.8.1) * Source: https://github.com/carrotsearch/hppc/hppc +istack common utility code runtime (3.0.8) + + * License: Eclipse Distribution License - v 1.0 + * Maven artifact: `com.sun.istack:istack-commons-runtime:3.0.8` + * Project: https://projects.eclipse.org/projects/ee4j/istack-commons/istack-commons-runtime + * Source: https://github.com/eclipse-ee4j/jaxb-istack-commons/istack-commons-runtime + + Jackson module: JAXB Annotations (2.9.9) * License: The Apache Software License, Version 2.0 @@ -173,6 +205,14 @@ Jackson-dataformat-XML (2.9.9) * Source: https://github.com/FasterXML/jackson-dataformat-xml +jakarta.xml.bind-api (2.3.2) + + * License: Eclipse Distribution License - v 1.0 + * Maven artifact: `jakarta.xml.bind:jakarta.xml.bind-api:2.3.2` + * Project: https://github.com/eclipse-ee4j/jaxb-api/jakarta.xml.bind-api + * Source: https://github.com/eclipse-ee4j/jaxb-api/jakarta.xml.bind-api + + Janino (2.7.5) * License: New BSD License @@ -191,12 +231,20 @@ Java-WebSocket (1.3.9) JavaBeans Activation Framework API jar (1.2.0) - * License: CDDL/GPLv2+CE + * License: CDDL/GPLv2+CE, EDL 1.0 * Maven artifact: `javax.activation:javax.activation-api:1.2.0` * Project: http://java.net/all/javax.activation-api/ * Source: https://github.com/javaee/activation/javax.activation-api +JAXB Runtime (2.3.2) + + * License: Eclipse Distribution License - v 1.0 + * Maven artifact: `org.glassfish.jaxb:jaxb-runtime:2.3.2` + * Project: https://javaee.github.io/jaxb-v2/jaxb-runtime-parent/jaxb-runtime/ + * Source: https://github.com/eclipse-ee4j/jaxb-ri/jaxb-runtime-parent/jaxb-runtime + + jaxb-api (2.3.1) * License: CDDL 1.1, GPL2 w/ CPE @@ -293,6 +341,14 @@ Stax2 API (3.1.4) * Source: https://github.com/FasterXML/stax2-api +TXW2 Runtime (2.3.2) + + * License: Eclipse Distribution License - v 1.0 + * Maven artifact: `org.glassfish.jaxb:txw2:2.3.2` + * Project: https://javaee.github.io/jaxb-v2/jaxb-txw-parent/txw2/ + * Source: https://github.com/eclipse-ee4j/jaxb-ri/jaxb-txw-parent/txw2 + + Woodstox (5.1.0) * License: The Apache License, Version 2.0 diff --git a/co-simulation/bundle/src/assembly/resources/etc/logback.xml b/co-simulation/bundle/src/assembly/resources/etc/logback.xml index 638d6257..b3ffd0e3 100644 --- a/co-simulation/bundle/src/assembly/resources/etc/logback.xml +++ b/co-simulation/bundle/src/assembly/resources/etc/logback.xml @@ -138,95 +138,99 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + - + diff --git a/co-simulation/bundle/src/assembly/resources/etc/runtime.json b/co-simulation/bundle/src/assembly/resources/etc/runtime.json index 78f582e6..9fba1c56 100644 --- a/co-simulation/bundle/src/assembly/resources/etc/runtime.json +++ b/co-simulation/bundle/src/assembly/resources/etc/runtime.json @@ -63,7 +63,7 @@ "deploy": false, "start": false, "subscriptions": [ - "CarmaV2xMessageReception" + "V2xMessageReception" ], "javaClasspathEntries": [] }, @@ -114,7 +114,7 @@ "deploy": false, "start": false, "subscriptions": [ - "InfrastructureV2xMessageReception" + "V2xMessageReception" ], "javaClasspathEntries": [] }, @@ -174,7 +174,8 @@ "TrafficLightRegistration", "VehicleUpdates", "V2xMessageTransmission", - "AdHocCommunicationConfiguration" + "AdHocCommunicationConfiguration", + "ExternalVehicleRegistration" ], "javaClasspathEntries": [] }, diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/application/Town04.db b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/application/Town04.db new file mode 100644 index 0000000000000000000000000000000000000000..dbdf4a4c0bf9e399d82c44a9ebbefe9404ec9d72 GIT binary patch literal 462848 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Þ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 khzp0^*25k=4p_K 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 lh Date: Tue, 30 May 2023 17:48:30 -0400 Subject: [PATCH 122/124] Updated loglevel of rti/ns3/mosaic application to avoid simulation slow down and crash --- .../src/assembly/resources/etc/logback.xml | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) mode change 100644 => 100755 co-simulation/bundle/src/assembly/resources/etc/logback.xml diff --git a/co-simulation/bundle/src/assembly/resources/etc/logback.xml b/co-simulation/bundle/src/assembly/resources/etc/logback.xml old mode 100644 new mode 100755 index b3ffd0e3..5577b9c1 --- a/co-simulation/bundle/src/assembly/resources/etc/logback.xml +++ b/co-simulation/bundle/src/assembly/resources/etc/logback.xml @@ -144,34 +144,34 @@ - + - + - + - + - + - + - + - + - + @@ -209,7 +209,7 @@ - + @@ -217,20 +217,20 @@ - + - + - + - + From a9aa2448943e73ad250d31bcf69c2d1937fbb6cb Mon Sep 17 00:00:00 2001 From: Saikrishna Bairamoni <84093461+SaikrishnaBairamoni@users.noreply.github.com> Date: Fri, 16 Jun 2023 15:20:39 -0400 Subject: [PATCH 123/124] update checkout.sh to point 4.4.3 release images --- docker/checkout.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/checkout.sh b/docker/checkout.sh index ad545521..47d97242 100755 --- a/docker/checkout.sh +++ b/docker/checkout.sh @@ -39,6 +39,6 @@ if [[ "$BRANCH" = "develop" ]]; then git clone https://github.com/usdot-fhwa-stol/carma-msgs.git ~/src/carma-msgs --branch $BRANCH --depth 1 git clone https://github.com/usdot-fhwa-stol/carma-utils.git ~/src/carma-utils --branch $BRANCH --depth 1 else - git clone https://github.com/usdot-fhwa-stol/carma-msgs.git ${dir}/src/carma-msgs --branch develop --depth 1 - git clone https://github.com/usdot-fhwa-stol/carma-utils.git ${dir}/src/carma-utils --branch develop --depth 1 + git clone https://github.com/usdot-fhwa-stol/carma-msgs.git ${dir}/src/carma-msgs --branch carma-system-4.4.3 --depth 1 + git clone https://github.com/usdot-fhwa-stol/carma-utils.git ${dir}/src/carma-utils --branch carma-system-4.4.3 --depth 1 fi From 0861bed069dd224a703cc1b6c7a5aacbfab440f6 Mon Sep 17 00:00:00 2001 From: Saikrishna Bairamoni <84093461+SaikrishnaBairamoni@users.noreply.github.com> Date: Tue, 20 Jun 2023 10:33:43 -0400 Subject: [PATCH 124/124] Update Dockerfile with new reponame cdasim --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1ffcc7db..256cd1bb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,12 +25,12 @@ ARG VCS_REF ARG BUILD_DATE LABEL org.label-schema.schema-version="1.0" -LABEL org.label-schema.name="carma-simulation" +LABEL org.label-schema.name="cdasim" LABEL org.label-schema.description="XIL Simulation environment for evaluation and testing of the CARMA Platform" LABEL org.label-schema.vendor="Leidos" LABEL org.label-schema.version=${VERSION} LABEL org.label-schema.url="https://highways.dot.gov/research/research-programs/operations/CARMA" -LABEL org.label-schema.vcs-url="https://github.com/usdot-fhwa-stol/carma-simulation/" +LABEL org.label-schema.vcs-url="https://github.com/usdot-fhwa-stol/cdasim/" LABEL org.label-schema.vcs-ref=${VCS_REF} LABEL org.label-schema.build-date=${BUILD_DATE}

00M(PL8s4jdK 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 CDnB#; literal 0 HcmV?d00001 diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carla/bridge.sh b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carla/bridge.sh new file mode 100644 index 00000000..7e7ec854 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carla/bridge.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +#x-terminal-emulator +cd /opt/carma-simulation/bridge + +x-terminal-emulator -e python3.7 carla_mosaic_bridge.py --bridge-server-port 8913 -m Town04 /opt/carma-simulation/scenarios/Town04_test/sumo/Town04.net.xml --step-length 0.1 --tls-manager EVC diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carla/carla_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carla/carla_config.json new file mode 100644 index 00000000..51bc8ef2 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carla/carla_config.json @@ -0,0 +1,6 @@ +{ + "updateInterval": 100, + "carlaUE4Path": "/opt/carla/", + "bridgePath": "/opt/carma-simulation/scenarios/Town04_test/carla; bridge.sh", + "carlaConnectionPort": 8913 +} diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carma/carma_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carma/carma_config.json new file mode 100644 index 00000000..31046b6a --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carma/carma_config.json @@ -0,0 +1,4 @@ +{ + "updateInterval": 100, + "carmaVehicles": [] +} \ No newline at end of file diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/infrastructure/infrastructure_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/infrastructure/infrastructure_config.json new file mode 100644 index 00000000..196145f7 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/infrastructure/infrastructure_config.json @@ -0,0 +1,3 @@ +{ + "updateInterval": 100 +} diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/mapping/mapping_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/mapping/mapping_config.json new file mode 100644 index 00000000..c37205e6 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/mapping/mapping_config.json @@ -0,0 +1,6 @@ +{ + "prototypes": [], + "chargingStations": [], + "vehicles": [ + ] +} diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/ns3/ns3_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/ns3/ns3_config.json new file mode 100644 index 00000000..8e021047 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/ns3/ns3_config.json @@ -0,0 +1,20 @@ +{ + "messages": { + "destinationType": { + "AD_HOC_GEOCAST": true, + "AD_HOC_TOPOCAST": false, + "CELL_GEOCAST": false, + "CELL_GEOCAST_MBMS": false, + "CELL_TOPOCAST": false + }, + "destinationAddress": { + "ipv4UnicastAddress": false, + "ipv4BroadcastAddress": true, + "ipv4AnycastAddress" : false + }, + "protocolType": { + "UDP": true, + "TCP": false + } + } +} diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/ns3/ns3_federate_config.xml b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/ns3/ns3_federate_config.xml new file mode 100644 index 00000000..61332733 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/ns3/ns3_federate_config.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/output/output_config.xml b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/output/output_config.xml new file mode 100644 index 00000000..5a71e921 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/output/output_config.xml @@ -0,0 +1,204 @@ + + + + + + output.csv + . + ; + + + + "VEHICLE_UPDATES" + Time + Updated:Name + Updated:Speed + Updated:Heading + Updated:Position.Latitude + Updated:Position.Longitude + Updated:Position.Altitude + Updated:DistanceDriven + Updated:LongitudinalAcceleration + Updated:Slope + Updated:Stopped + Updated:RouteId + Updated:RoadPosition.Connection.Id + Updated:RoadPosition.LaneIndex + Updated:VehicleEmissions.CurrentEmissions.Co2 + Updated:VehicleEmissions.CurrentEmissions.Co + Updated:VehicleEmissions.CurrentEmissions.Hc + Updated:VehicleEmissions.CurrentEmissions.Pmx + Updated:VehicleEmissions.CurrentEmissions.Nox + Updated:VehicleConsumptions.CurrentConsumptions.Fuel + Updated:VehicleEmissions.AllEmissions.Co2 + Updated:VehicleEmissions.AllEmissions.Co + Updated:VehicleEmissions.AllEmissions.Hc + Updated:VehicleEmissions.AllEmissions.Pmx + Updated:VehicleEmissions.AllEmissions.Nox + Updated:VehicleConsumptions.AllConsumptions.Fuel + Updated:VehicleSignals.BlinkerRight + Updated:VehicleSignals.BlinkerLeft + Updated:VehicleSignals.BrakeLight + + + + + "V2X_MESSAGE_RECEPTION" + Time + Type + MessageId + ReceiverName + ReceiverInformation.ReceiveSignalStrength + + + + + "V2X_MESSAGE_TRANSMISSION" + Time + Type + MessageId + SourceName + SourcePosition.Latitude + SourcePosition.Longitude + SourcePosition.Altitude + Message.Routing.Destination.Type + Message.Routing.Destination.Address.IPv4Address + Message.Routing.Destination.AdhocChannelId + + + + + "VEHICLE_REGISTRATION" + Time + Mapping.Name + Mapping.VehicleType.VehicleClass + Mapping.VehicleType.Color + Mapping.Group + Mapping.VehicleType.Length + Mapping.VehicleType.MinGap + Mapping.VehicleType.MaxSpeed + Mapping.VehicleType.Accel + Mapping.VehicleType.Decel + Mapping.VehicleType.Sigma + Mapping.VehicleType.Tau + Mapping.VehicleType.SpeedFactor + Departure.RouteId + Departure.DepartureLane + Departure.DeparturePos + + + + + "TRAFFICLIGHT_REGISTRATION" + Time + Mapping.Name + Mapping.Applications + TrafficLightGroup.FirstPosition.Latitude + TrafficLightGroup.FirstPosition.Longitude + + + + + "TRAFFICSIGN_REGISTRATION" + Time + TrafficSign.Id + TrafficSign.GeoPosition.Latitude + TrafficSign.GeoPosition.Longitude + TrafficSign.GeoPosition.Altitude + TrafficSign.Angle + TrafficSign.ConnectionId + TrafficSign.Lane + TrafficSign.TypeId + TrafficSign.SignContents + + + + + "CHANGE_SPEED" + Time + VehicleId + Type + Speed + Interval + Acceleration + + + + + "RSU_REGISTRATION" + Time + Mapping.Name + Mapping.Position.Latitude + Mapping.Position.Longitude + Mapping.Position.Altitude + Mapping.Group + Mapping.Applications + + + + + "ADHOC_CONFIGURATION" + Time + Configuration.NodeId + Configuration.RadioMode + Configuration.Conf0.NewIP + Configuration.Conf0.NewPower + Configuration.Conf0.Channel0 + Configuration.Conf0.Channel1 + Configuration.Conf1.NewIP + Configuration.Conf1.NewPower + Configuration.Conf1.Channel0 + Configuration.Conf1.Channel1 + + + + + "CELL_CONFIGURATION" + Time + Configuration.NodeId + Configuration.Enabled + Configuration.MaxDownlinkBitrate + Configuration.MaxUplinkBitrate + + + + + "DETECTOR_UPDATES" + Time + UpdatedInductionLoops:VehicleCount + UpdatedInductionLoops:MeanSpeed + UpdatedInductionLoops:TrafficFlow + UpdatedLaneAreaDetectors:Length + UpdatedLaneAreaDetectors:VehicleCount + UpdatedLaneAreaDetectors:MeanSpeed + + + + + "LANE_PROPERTY_CHANGE" + Time + EdgeId + LaneIndex + AllowedVehicleClasses + DisallowedVehicleClasses + MaxSpeed + + + + + + + true + 46587 + + + + + + + + + + + diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/scenario_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/scenario_config.json new file mode 100755 index 00000000..2103391b --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/scenario_config.json @@ -0,0 +1,40 @@ +{ + "simulation": { + "id": "Town04", + "duration": "600s", + "randomSeed": 212323853, + "projection": { + "centerCoordinates": { + "latitude": 0, + "longitude": 0 + }, + "cartesianOffset": { + "x": 0, + "y": 0 + } + }, + "network": { + "netMask": "255.255.0.0", + "vehicleNet": "10.1.0.0", + "rsuNet": "10.2.0.0", + "tlNet": "10.3.0.0", + "csNet": "10.4.0.0", + "serverNet": "10.5.0.0", + "tmcNet": "10.6.0.0" + } + }, + "federates": { + "application": true, + "cell": false, + "environment": false, + "sns": false, + "ns3": true, + "omnetpp": false, + "output": false, + "sumo": true, + "carla": true, + "carma": true, + "infrastructure": true + + } +} diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.net.xml b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.net.xml new file mode 100644 index 00000000..46d8e7a9 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.net.xml @@ -0,0 +1,5373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.rou.xml b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.rou.xml new file mode 100755 index 00000000..351f1680 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.rou.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.sumocfg b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.sumocfg new file mode 100644 index 00000000..8ac4c13c --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.sumocfg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/detector.xml b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/detector.xml new file mode 100644 index 00000000..d39b9527 --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/detector.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/sumo_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/sumo_config.json new file mode 100644 index 00000000..413def8c --- /dev/null +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/sumo_config.json @@ -0,0 +1,4 @@ +{ + "sumoConfigurationFile": "Town04.sumocfg", + "updateInterval": 100 +} diff --git a/co-simulation/fed/mosaic-carla/pom.xml b/co-simulation/fed/mosaic-carla/pom.xml index 20a3e551..dd2e3d8f 100644 --- a/co-simulation/fed/mosaic-carla/pom.xml +++ b/co-simulation/fed/mosaic-carla/pom.xml @@ -59,6 +59,11 @@ 4.11 test + + commons-codec + commons-codec + 1.15 + diff --git a/co-simulation/fed/mosaic-carla/src/main/java/org/eclipse/mosaic/fed/carla/ambassador/CarlaAmbassador.java b/co-simulation/fed/mosaic-carla/src/main/java/org/eclipse/mosaic/fed/carla/ambassador/CarlaAmbassador.java index 75be4598..e5d3a47f 100644 --- a/co-simulation/fed/mosaic-carla/src/main/java/org/eclipse/mosaic/fed/carla/ambassador/CarlaAmbassador.java +++ b/co-simulation/fed/mosaic-carla/src/main/java/org/eclipse/mosaic/fed/carla/ambassador/CarlaAmbassador.java @@ -13,43 +13,33 @@ package org.eclipse.mosaic.fed.carla.ambassador; -import java.io.File; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.PriorityBlockingQueue; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nonnull; - import com.google.common.collect.Lists; - +import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.StringUtils; import org.eclipse.mosaic.fed.carla.carlaconnect.CarlaConnection; import org.eclipse.mosaic.fed.carla.config.CarlaConfiguration; import org.eclipse.mosaic.fed.sumo.traci.constants.CommandSimulationControl; import org.eclipse.mosaic.fed.sumo.traci.writer.ListTraciWriter; import org.eclipse.mosaic.fed.sumo.traci.writer.StringTraciWriter; -import org.eclipse.mosaic.interactions.application.CarlaTraciRequest; -import org.eclipse.mosaic.interactions.application.CarlaTraciResponse; -import org.eclipse.mosaic.interactions.application.CarlaV2xMessageReception; -import org.eclipse.mosaic.interactions.application.ExternalMessage; -import org.eclipse.mosaic.interactions.application.SimulationStep; -import org.eclipse.mosaic.interactions.application.SimulationStepResponse; +import org.eclipse.mosaic.interactions.application.*; import org.eclipse.mosaic.lib.util.ProcessLoggingThread; import org.eclipse.mosaic.lib.util.objects.ObjectInstantiation; import org.eclipse.mosaic.rti.TIME; -import org.eclipse.mosaic.rti.api.AbstractFederateAmbassador; -import org.eclipse.mosaic.rti.api.FederateExecutor; -import org.eclipse.mosaic.rti.api.IllegalValueException; -import org.eclipse.mosaic.rti.api.Interaction; -import org.eclipse.mosaic.rti.api.InternalFederateException; +import org.eclipse.mosaic.rti.api.*; import org.eclipse.mosaic.rti.api.federatestarter.ExecutableFederateExecutor; import org.eclipse.mosaic.rti.api.federatestarter.NopFederateExecutor; import org.eclipse.mosaic.rti.api.parameters.AmbassadorParameter; import org.eclipse.mosaic.rti.config.CLocalHost; +import javax.annotation.Nonnull; +import java.io.File; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.TimeUnit; + /** * Implementation of a {@link AbstractFederateAmbassador} for the vehicle * simulator CARLA. It is used to visualize the traffic simulation in 3D @@ -448,6 +438,8 @@ public synchronized void triggerInteraction(int length, byte[] command) throws I // message: {}", message[0], // message[1]); } + } else if (command[5] == 0x85) { + log.info("Received vehicle add command from CARLA " + Hex.encodeHex(command)); } else { rti.triggerInteraction(new CarlaTraciRequest(this.nextTimeStep, length, command)); } diff --git a/co-simulation/fed/mosaic-carma/pom.xml b/co-simulation/fed/mosaic-carma/pom.xml index 8e14a5a6..a3957af7 100644 --- a/co-simulation/fed/mosaic-carma/pom.xml +++ b/co-simulation/fed/mosaic-carma/pom.xml @@ -75,6 +75,21 @@ jaxb-api 2.3.1 + + commons-codec + commons-codec + 1.15 + + + jakarta.xml.bind + jakarta.xml.bind-api + 2.3.2 + + + org.glassfish.jaxb + jaxb-runtime + 2.3.2 + diff --git a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstance.java b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstance.java index 8ab40727..affba737 100644 --- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstance.java +++ b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstance.java @@ -22,7 +22,6 @@ import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; -import java.net.SocketException; /** * Connection manager and data object to associate with a single CARMA Platform instance in XIL @@ -34,7 +33,7 @@ public class CarmaInstance { private InetAddress targetAddress; private int port; - private GeoPoint location = null; + private GeoPoint location = GeoPoint.ORIGO; public CarmaInstance(String carmaVehicleId, String carlaRoleName, InetAddress targetAddress, int port) { this.carmaVehicleId = carmaVehicleId; diff --git a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceManager.java b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceManager.java index 02b511bf..68ca2810 100644 --- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceManager.java +++ b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceManager.java @@ -20,11 +20,14 @@ import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; import org.eclipse.mosaic.interactions.traffic.VehicleUpdates; import org.eclipse.mosaic.lib.enums.AdHocChannel; +import org.eclipse.mosaic.lib.geo.GeoCircle; import org.eclipse.mosaic.lib.objects.addressing.AdHocMessageRoutingBuilder; import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xContent; import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xMessage; import org.eclipse.mosaic.lib.objects.v2x.MessageRouting; import org.eclipse.mosaic.lib.objects.vehicle.VehicleData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.InetAddress; @@ -37,10 +40,10 @@ */ public class CarmaInstanceManager { private Map managedInstances = new HashMap<>(); - private double currentSimulationTime; // TODO: Verify actual port for CARMA Platform NS-3 adapter private static final int TARGET_PORT = 5374; + private final Logger log = LoggerFactory.getLogger(this.getClass()); /** * Callback to invoked when a new CARMA Platform instance registers with the mosaic-carma ambassador for the first time @@ -60,20 +63,24 @@ public void onNewRegistration(CarmaRegistrationMessage registration) { } } else { // log warning + log.warn("Received duplicate registration for vehicle " + registration.getCarlaVehicleRole()); } } /** * Callback to be invoked when CARMA Platform receives a V2X Message from the NS-3 simulation * @param sourceAddr The V2X Message received * @param txMsg The Host ID of the vehicle receiving the data + * @param time The timestamp at which the interaction occurs * @throws RuntimeException If the socket used to communicate with the platform experiences failure */ - public V2xMessageTransmission onV2XMessageTx(InetAddress sourceAddr, CarmaV2xMessage txMsg) { + public V2xMessageTransmission onV2XMessageTx(InetAddress sourceAddr, CarmaV2xMessage txMsg, long time) { CarmaInstance sender = null; for (CarmaInstance ci : managedInstances.values()) { if (ci.getTargetAddress().equals(sourceAddr)) { sender = ci; + break; } + log.info("Instance {} with target address {} can not match with source address {}", ci.getCarlaRoleName(), ci.getTargetAddress(), sourceAddr.toString()); } if (sender == null) { @@ -84,11 +91,17 @@ public V2xMessageTransmission onV2XMessageTx(InetAddress sourceAddr, CarmaV2xMes AdHocMessageRoutingBuilder messageRoutingBuilder = new AdHocMessageRoutingBuilder( sender.getCarlaRoleName(), sender.getLocation()).viaChannel(AdHocChannel.CCH); - MessageRouting routing = messageRoutingBuilder.topoBroadCast(1); - - return new V2xMessageTransmission((long) currentSimulationTime, new ExternalV2xMessage(routing, - new ExternalV2xContent((long) currentSimulationTime, sender.getLocation(), txMsg.getPayload()))); - + // TODO: Get maximum broadcast radius from configuration file. + MessageRouting routing = messageRoutingBuilder.geoBroadCast(new GeoCircle(sender.getLocation(), 300)); + + log.info("Preparing to generate V2XMessageTransmission interaction for transmission on MOSAIC event bus..."); + log.info("sim time: " + time); + log.info("sender id: " + sender.getCarlaRoleName()); + log.info("location: " + sender.getLocation().toString()); + log.info("txMsg non-null? " + (txMsg != null)); + log.info("payload: " + txMsg.getPayload()); + return new V2xMessageTransmission((long) time, new ExternalV2xMessage(routing, + new ExternalV2xContent((long) time, sender.getLocation(), txMsg.getPayload()))); } /** @@ -134,7 +147,11 @@ private void newCarmaInstance(String carmaVehId, String carlaRoleName, InetAddre CarmaInstance tmp = new CarmaInstance(carmaVehId, carlaRoleName, targetAddress, targetPort); try { tmp.bind(); + log.info("New CARMA instance '{}' registered with CARMA Instance Manager.", carlaRoleName); } catch (IOException e) { + log.error("Failed to bind CARMA instance with ID '{}' to its RX message socket: {}", + carlaRoleName, e.getMessage()); + log.error("Stack trace:", e); throw new RuntimeException(e); } managedInstances.put(carlaRoleName, tmp); diff --git a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaMessageAmbassador.java b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaMessageAmbassador.java index 5a1fa2ef..aa877b63 100644 --- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaMessageAmbassador.java +++ b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaMessageAmbassador.java @@ -17,19 +17,27 @@ import gov.dot.fhwa.saxton.CarmaV2xMessageReceiver; import org.eclipse.mosaic.fed.application.ambassador.SimulationKernel; import org.eclipse.mosaic.fed.carma.configuration.CarmaConfiguration; -import org.eclipse.mosaic.fed.carma.configuration.CarmaVehicleConfiguration; import org.eclipse.mosaic.interactions.application.CarmaV2xMessageReception; -import org.eclipse.mosaic.interactions.application.ExternalMessage; +import org.eclipse.mosaic.interactions.communication.AdHocCommunicationConfiguration; import org.eclipse.mosaic.interactions.communication.V2xMessageReception; import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; +import org.eclipse.mosaic.interactions.mapping.advanced.ExternalVehicleRegistration; import org.eclipse.mosaic.interactions.traffic.VehicleUpdates; -import org.eclipse.mosaic.interactions.vehicle.VehicleFederateAssignment; +import org.eclipse.mosaic.lib.enums.AdHocChannel; import org.eclipse.mosaic.lib.enums.DriveDirection; +import org.eclipse.mosaic.lib.geo.CartesianPoint; +import org.eclipse.mosaic.lib.geo.GeoPoint; import org.eclipse.mosaic.lib.misc.Tuple; +import org.eclipse.mosaic.lib.objects.addressing.IpResolver; +import org.eclipse.mosaic.lib.objects.communication.AdHocConfiguration; +import org.eclipse.mosaic.lib.objects.communication.InterfaceConfiguration; +import org.eclipse.mosaic.lib.objects.road.IRoadPosition; +import org.eclipse.mosaic.lib.objects.road.SimpleRoadPosition; import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xMessage; import org.eclipse.mosaic.lib.objects.v2x.V2xMessage; -import org.eclipse.mosaic.lib.objects.vehicle.VehicleData; -import org.eclipse.mosaic.lib.objects.vehicle.VehicleDeparture; +import org.eclipse.mosaic.lib.objects.vehicle.*; +import org.eclipse.mosaic.lib.objects.vehicle.sensor.DistanceSensor; +import org.eclipse.mosaic.lib.objects.vehicle.sensor.RadarSensor; import org.eclipse.mosaic.lib.util.objects.ObjectInstantiation; import org.eclipse.mosaic.rti.TIME; import org.eclipse.mosaic.rti.api.AbstractFederateAmbassador; @@ -39,9 +47,11 @@ import org.eclipse.mosaic.rti.api.parameters.AmbassadorParameter; import javax.xml.bind.DatatypeConverter; +import java.net.Inet4Address; import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Arrays; import java.util.List; /** @@ -60,16 +70,6 @@ public class CarmaMessageAmbassador extends AbstractFederateAmbassador { */ CarmaConfiguration carmaConfiguration; - /** - * List of vehicles that are controlled by CARMA platform. - */ - private final HashMap carmaVehicleMap = new HashMap<>(); - - /** - * The number of CARMA vehicles. - */ - int numberOfCarmaVehicle = 0; - private CarmaRegistrationReceiver carmaRegistrationReceiver; private Thread registrationRxBackgroundThread; private CarmaV2xMessageReceiver v2xMessageReceiver; @@ -133,28 +133,6 @@ public void initialize(long startTime, long endTime) throws InternalFederateExce v2xMessageReceiver.init(); v2xRxBackgroundThread = new Thread(v2xMessageReceiver); v2xRxBackgroundThread.start(); - - // Register CARMA vehicles - for (CarmaVehicleConfiguration carmaVehicleConfiguration : carmaConfiguration.carmaVehicles) { - VehicleDeparture vehicleDeparture = new VehicleDeparture.Builder(carmaVehicleConfiguration.routeID) - .departureLane(VehicleDeparture.LaneSelectionMode.BEST, carmaVehicleConfiguration.lane, - carmaVehicleConfiguration.position) - .departureSpeed(VehicleDeparture.DepartSpeedMode.MAXIMUM, carmaVehicleConfiguration.departSpeed) - .create(); - - VehicleFederateAssignment carmaVehicleRegistration = new VehicleFederateAssignment(currentSimulationTime, - "carma_" + numberOfCarmaVehicle, "carma", 50, carmaVehicleConfiguration.vehicleType, - vehicleDeparture, carmaVehicleConfiguration.applications); - - numberOfCarmaVehicle++; - - try { - this.rti.triggerInteraction(carmaVehicleRegistration); - carmaVehicleMap.put(carmaVehicleRegistration.getVehicleId(), false); - } catch (InternalFederateException | IllegalValueException e) { - log.error(e.getMessage()); - } - } } /** @@ -175,39 +153,34 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder } try { - List newRegistrations = carmaRegistrationReceiver.getReceivedMessages(); for (CarmaRegistrationMessage reg : newRegistrations) { carmaInstanceManager.onNewRegistration(reg); + onDsrcRegistrationRequest(reg.getCarlaVehicleRole()); } - List> newMessages = v2xMessageReceiver.getReceivedMessages(); - for (Tuple msg : newMessages) { - V2xMessageTransmission msgInt = carmaInstanceManager.onV2XMessageTx(msg.getA(), msg.getB()); - this.rti.triggerInteraction(msgInt); - } - // Update CARMA vehicle - updateCarmaVehicles(); + if (currentSimulationTime == 0) { + // For the first timestep, clear the message receive queues. + v2xMessageReceiver.getReceivedMessages(); // Automatically empties the queues. + } else { + List> newMessages = v2xMessageReceiver.getReceivedMessages(); + for (Tuple msg : newMessages) { + V2xMessageTransmission msgInt = carmaInstanceManager.onV2XMessageTx(msg.getA(), msg.getB(), time); + SimulationKernel.SimulationKernel.getV2xMessageCache().putItem(currentSimulationTime, msgInt.getMessage()); + rti.triggerInteraction(msgInt); + } + } currentSimulationTime += carmaConfiguration.updateInterval * TIME.MILLI_SECOND; rti.requestAdvanceTime(currentSimulationTime, 0, (byte) 2); - - // Send CARMA external messages - String message = "Message to CARMA vehicles: Carma Vehicles update at time: " + currentSimulationTime; - ExternalMessage externalMessage = new ExternalMessage(currentSimulationTime, message, - carmaConfiguration.senderCarmaVehicleId); - try { - this.rti.triggerInteraction(externalMessage); - } catch (InternalFederateException | IllegalValueException e) { - log.error(e.getMessage()); - } - log.debug("trigger external message interaction at time: " + currentSimulationTime + " ."); - } catch (IllegalValueException e) { log.error("Error during advanceTime(" + time + ")", e); throw new InternalFederateException(e); + } catch (UnknownHostException e) { + log.error("Error during advanceTime(" + time + ")", e); + throw new InternalFederateException(e); } } @@ -256,10 +229,14 @@ public void processInteraction(Interaction interaction) throws InternalFederateE receiveInteraction((CarmaV2xMessageReception) interaction); } if (interaction.getTypeId().equals(VehicleUpdates.TYPE_ID)) { - carmaInstanceManager.onVehicleUpdates((VehicleUpdates) interaction); + receiveVehicleUpdateInteraction((VehicleUpdates) interaction); } } + private synchronized void receiveVehicleUpdateInteraction(VehicleUpdates interaction) { + carmaInstanceManager.onVehicleUpdates(interaction); + } + /** * Helper function to retrieve previously transmitted messages by ID from the buffer * @param id The id of the message to return @@ -275,11 +252,11 @@ private V2xMessage lookupV2xMsgIdInBuffer(int id) { */ private synchronized void receiveV2xReceptionInteraction(V2xMessageReception interaction) { String carlaRoleName = interaction.getReceiverName(); - if (!carmaInstanceManager.checkIfRegistered(carlaRoleName)) { // Abort early as we only are concerned with CARMA Platform vehicles return; } + log.info("Processing V2X message reception event for " + interaction.getReceiverName() + " of msg id " + interaction.getMessageId()); int messageId = interaction.getMessageId(); V2xMessage msg = lookupV2xMsgIdInBuffer(messageId); @@ -287,65 +264,120 @@ private synchronized void receiveV2xReceptionInteraction(V2xMessageReception int if (msg != null && msg instanceof ExternalV2xMessage) { ExternalV2xMessage msg2 = (ExternalV2xMessage) msg; carmaInstanceManager.onV2XMessageRx(DatatypeConverter.parseHexBinary(msg2.getMessage()), carlaRoleName); + log.info("Sending V2X message reception event for " + interaction.getReceiverName() + " of msg id " + interaction.getMessageId() + " of size " + msg2.getPayLoad().getBytes().length); } else { - // TODO: Log warning as message was no longer in buffer to be received + log.warn("Message with id " + interaction.getMessageId() + " received by " + interaction.getReceiverName() + " is no longer in the message buffer to be retrieved! Message transmission failed!!!"); } } - /** - * Extract external message from received {@link CarmaV2xMessageReception} - * interaction. - * - * @param carmaV2xMessageReception Interaction indicates that the external - * message is received by a CARMA vehicle. - * @throws InternalFederateException Exception if a invalid value is used. - */ - private synchronized void receiveInteraction(CarmaV2xMessageReception carmaV2xMessageReception) - throws InternalFederateException { - log.info("CARMA vehicle: " + carmaV2xMessageReception.getReceiverID() - + " received an external message at time: " + carmaV2xMessageReception.getTime() + "."); - log.info("The received message is " + carmaV2xMessageReception.getMessage() + " ."); + private void onDsrcRegistrationRequest(String vehicleId) throws UnknownHostException { + ExternalVehicleRegistration tempRegistration = new ExternalVehicleRegistration( + currentSimulationTime, + vehicleId, + "carma", + null, + new VehicleType("carma")); - } + try { + // Trigger RTI interaction to MOSAIC to exchange the Ad-Hoc configuration + this.rti.triggerInteraction(tempRegistration); + } catch (InternalFederateException | IllegalValueException e) { + // Log error message if there was an issue with the RTI interaction + log.error(e.getMessage()); + } - /** - * Update CARMA vehicles position. - */ - private final void updateCarmaVehicles() { - - List addedVehicle = new ArrayList<>(); - List updatedVehicle = new ArrayList<>(); - List removedNames = new ArrayList<>(); - - for (String vehicleName : carmaVehicleMap.keySet()) { - final int firstUnderscorePosition = vehicleName.indexOf('_'); - final int vehicleNumber = Integer.parseInt(vehicleName.substring(firstUnderscorePosition + 1)); - - VehicleData vehicleData = new VehicleData.Builder(currentSimulationTime, vehicleName) - .position(carmaConfiguration.carmaVehicles.get(vehicleNumber).geoPosition, - carmaConfiguration.carmaVehicles.get(vehicleNumber).projectedPosition) - .orientation(DriveDirection.UNAVAILABLE, - carmaConfiguration.carmaVehicles.get(vehicleNumber).heading, - carmaConfiguration.carmaVehicles.get(vehicleNumber).slope) - .create(); - - if (!carmaVehicleMap.get(vehicleName)) { - // update the new added CARMA vehicles - addedVehicle.add(vehicleData); - carmaVehicleMap.put(vehicleName, true); - } else { - // update the registed CARMA vehicles - updatedVehicle.add(vehicleData); - } + VehicleSignals tmpSignals = new VehicleSignals( + false, + false, + false, + false, + false); + + VehicleEmissions tmpEmissions = new VehicleEmissions( + new Emissions( + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ), + new Emissions( + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + )); + + VehicleBatteryState tmpBattery = new VehicleBatteryState("", currentSimulationTime); + IRoadPosition tmpPos = new SimpleRoadPosition("", 0, 0.0, 0.0); + VehicleSensors tmpSensors = new VehicleSensors( + new DistanceSensor(0.0, + 0.0, + 0.0, + 0.0), + new RadarSensor(0.0)); + VehicleConsumptions tmpConsumptions = new VehicleConsumptions( + new Consumptions(0.0, 0.0), + new Consumptions(0.0, 0.0)); + VehicleData tmpVehicle = new VehicleData.Builder(currentSimulationTime, vehicleId) + .position(GeoPoint.ORIGO, CartesianPoint.ORIGO) + .movement(0.0, 0.0, 0.0) + .consumptions(tmpConsumptions) + .emissions(tmpEmissions) + .electric(tmpBattery) + .laneArea("") + .sensors(tmpSensors) + .road(tmpPos) + .signals(tmpSignals) + .orientation(DriveDirection.FORWARD, 0.0, 0.0) + .stopped(false) + .route("") + .create(); + VehicleUpdates tempUpdates = new VehicleUpdates( + currentSimulationTime, + new ArrayList<>(Arrays.asList(tmpVehicle)), + new ArrayList<>(), + new ArrayList<>()); + + try { + // Trigger RTI interaction to MOSAIC to exchange the Ad-Hoc configuration + this.rti.triggerInteraction(tempUpdates); + } catch (InternalFederateException | IllegalValueException e) { + // Log error message if there was an issue with the RTI interaction + log.error(e.getMessage()); } - VehicleUpdates vehicleUpdates = new VehicleUpdates(currentSimulationTime, addedVehicle, updatedVehicle, - removedNames); - // trigger VehicleUpdates interaction + // Create an InterfaceConfiguration object to represent the configuration of the + // Ad-Hoc interface + // TODO: Replace the transmit power of the ad-hoc interface (in dBm) if necessary + // TODO: Replace the communication range of the ad-hoc interface (in meters) if necessary + Inet4Address vehAddress = IpResolver.getSingleton().registerHost(vehicleId); + log.info("Assigned registered comms device " + vehicleId + " with IP address " + vehAddress.toString()); + InterfaceConfiguration interfaceConfig = new InterfaceConfiguration.Builder(AdHocChannel.CCH) + .ip(vehAddress) + .subnet(IpResolver.getSingleton().getNetMask()) + .power(50) + .radius(300.0) + .create(); + + // Create an AdHocConfiguration object to associate the Ad-Hoc interface + // configuration with the infrastructure instance's ID + AdHocConfiguration adHocConfig = new AdHocConfiguration.Builder(vehicleId) + .addInterface(interfaceConfig) + .create(); + + // Create an AdHocCommunicationConfiguration object to specify the time and + // Ad-Hoc configuration for exchange with another vehicle or component + AdHocCommunicationConfiguration communicationConfig = new AdHocCommunicationConfiguration(currentSimulationTime, + adHocConfig); + log.info("Communications comms device " + vehicleId + " with IP address " + vehAddress.toString() + " success!"); try { - this.rti.triggerInteraction(vehicleUpdates); + // Trigger RTI interaction to MOSAIC to exchange the Ad-Hoc configuration + this.rti.triggerInteraction(communicationConfig); } catch (InternalFederateException | IllegalValueException e) { + // Log error message if there was an issue with the RTI interaction log.error(e.getMessage()); } } diff --git a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationReceiver.java b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationReceiver.java index 7ea8ae95..7d609327 100644 --- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationReceiver.java +++ b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationReceiver.java @@ -73,7 +73,7 @@ public void run() { // Enqueue message for processing on main thread synchronized (rxQueue) { - log.info("New Infrastructure instance '{}' received with Infrastructure Registration Receiver.", parsedMessage.getCarmaVehicleId()); + log.info("New CARMA instance '{}' received with CARMA Registration Receiver.", parsedMessage.getCarmaVehicleId()); rxQueue.add(parsedMessage); } } diff --git a/co-simulation/fed/mosaic-infrastructure/pom.xml b/co-simulation/fed/mosaic-infrastructure/pom.xml index 49fe34bc..15a1bd00 100644 --- a/co-simulation/fed/mosaic-infrastructure/pom.xml +++ b/co-simulation/fed/mosaic-infrastructure/pom.xml @@ -59,6 +59,22 @@ 4.11 test + + org.eclipse.mosaic + mosaic-application + 22.1-SNAPSHOT + compile + + + jakarta.xml.bind + jakarta.xml.bind-api + 2.3.2 + + + org.glassfish.jaxb + jaxb-runtime + 2.3.2 + diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 5682f3c2..09889470 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -19,6 +19,7 @@ import gov.dot.fhwa.saxton.CarmaV2xMessage; import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; import org.eclipse.mosaic.lib.enums.AdHocChannel; +import org.eclipse.mosaic.lib.geo.GeoCircle; import org.eclipse.mosaic.lib.geo.GeoPoint; import org.eclipse.mosaic.lib.objects.addressing.AdHocMessageRoutingBuilder; import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xContent; @@ -44,7 +45,7 @@ */ public class InfrastructureInstanceManager { private Map managedInstances = new HashMap<>(); - private double currentSimulationTime; + private final Logger log = LoggerFactory.getLogger(this.getClass()); /** * Register a new infrastructure instance with the MOSAIC system. @@ -110,7 +111,7 @@ private void newInfrastructureInstance(String infrastructureId, InetAddress rxMe * @param txMsg The Host ID of the vehicle receiving the data * @throws RuntimeException If the socket used to communicate with the platform experiences failure */ - public V2xMessageTransmission onV2XMessageTx(InetAddress sourceAddr, CarmaV2xMessage txMsg) { + public V2xMessageTransmission onV2XMessageTx(InetAddress sourceAddr, CarmaV2xMessage txMsg, long time) { InfrastructureInstance sender = null; for (InfrastructureInstance ci : managedInstances.values()) { if (ci.getTargetAddress().equals(sourceAddr)) { @@ -120,16 +121,36 @@ public V2xMessageTransmission onV2XMessageTx(InetAddress sourceAddr, CarmaV2xMes if (sender == null) { // Unregistered instance attempting to send messages - throw new IllegalStateException("Unregistered CARMA Platform instance attempting to send messages via MOSAIC"); + throw new IllegalStateException("Unregistered CARMA Streets/V2XHub instance attempting to send messages via MOSAIC"); } AdHocMessageRoutingBuilder messageRoutingBuilder = new AdHocMessageRoutingBuilder( sender.getInfrastructureId(), sender.getLocation()).viaChannel(AdHocChannel.CCH); - MessageRouting routing = messageRoutingBuilder.topoBroadCast(1); + // TODO: Get maximum broadcast radius from configuration file. + MessageRouting routing = messageRoutingBuilder.geoBroadCast(new GeoCircle(sender.getLocation(), 300)); + + return new V2xMessageTransmission(time, new ExternalV2xMessage(routing, + new ExternalV2xContent(time, sender.getLocation(), txMsg.getPayload()))); + } + + /** + * Callback to be invoked when CARMA Platform receives a V2X Message from the NS-3 simulation + * @param rxMsg The V2X Message received + * @param rxRsuId The Host ID of the vehicle receiving the data + * @throws RuntimeException If the socket used to communicate with the platform experiences failure + */ + public void onV2XMessageRx(byte[] rxMsg, String rxRsuId) { + if (!managedInstances.containsKey(rxRsuId)) { + return; + } - return new V2xMessageTransmission((long) currentSimulationTime, new ExternalV2xMessage(routing, - new ExternalV2xContent((long) currentSimulationTime, sender.getLocation(), txMsg.getPayload()))); + InfrastructureInstance rsu = managedInstances.get(rxRsuId); + try { + rsu.sendMsgs(rxMsg); + } catch (IOException e) { + throw new RuntimeException(e); + } } /** diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 4b79d82f..7919b562 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -18,17 +18,22 @@ import gov.dot.fhwa.saxton.CarmaV2xMessage; import gov.dot.fhwa.saxton.CarmaV2xMessageReceiver; +import org.eclipse.mosaic.fed.application.ambassador.SimulationKernel; import org.eclipse.mosaic.fed.infrastructure.configuration.InfrastructureConfiguration; -import org.eclipse.mosaic.interactions.application.ExternalMessage; import org.eclipse.mosaic.interactions.application.InfrastructureV2xMessageReception; import org.eclipse.mosaic.interactions.communication.AdHocCommunicationConfiguration; +import org.eclipse.mosaic.interactions.communication.V2xMessageReception; import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; import org.eclipse.mosaic.interactions.mapping.RsuRegistration; import org.eclipse.mosaic.lib.enums.AdHocChannel; +import org.eclipse.mosaic.lib.geo.CartesianPoint; import org.eclipse.mosaic.lib.geo.GeoPoint; import org.eclipse.mosaic.lib.misc.Tuple; +import org.eclipse.mosaic.lib.objects.addressing.IpResolver; import org.eclipse.mosaic.lib.objects.communication.AdHocConfiguration; import org.eclipse.mosaic.lib.objects.communication.InterfaceConfiguration; +import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xMessage; +import org.eclipse.mosaic.lib.objects.v2x.V2xMessage; import org.eclipse.mosaic.lib.util.objects.ObjectInstantiation; import org.eclipse.mosaic.rti.TIME; import org.eclipse.mosaic.rti.api.AbstractFederateAmbassador; @@ -37,12 +42,11 @@ import org.eclipse.mosaic.rti.api.InternalFederateException; import org.eclipse.mosaic.rti.api.parameters.AmbassadorParameter; +import javax.xml.bind.DatatypeConverter; import java.io.IOException; import java.net.Inet4Address; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.HashMap; import java.util.Collections; import java.util.List; @@ -145,6 +149,9 @@ public void processInteraction(Interaction interaction) throws InternalFederateE long interactionTime = interaction.getTime(); log.trace("Process interaction with type '{}' at time: {}", type, interactionTime); // Infrastructure message reception + if (interaction.getTypeId().equals(V2xMessageReception.TYPE_ID)) { + this.receiveV2xReceptionInteraction((V2xMessageReception) interaction); + } if (interaction.getTypeId().equals(InfrastructureV2xMessageReception.TYPE_ID)) { this.receiveInteraction((InfrastructureV2xMessageReception) interaction); } @@ -154,14 +161,35 @@ public void processInteraction(Interaction interaction) throws InternalFederateE * Extract external message from received * {@link InfrastructureV2xMessageReception} interaction. * - * @param infrastructureV2xMessageReception Interaction indicates that the + * @param interaction Interaction indicates that the * external message is received by a * rsu. */ - private synchronized void receiveInteraction(InfrastructureV2xMessageReception infrastructureV2xMessageReception) { - log.info(infrastructureV2xMessageReception.getReceiverID() + " received V2X messages at time: " - + infrastructureV2xMessageReception.getTime() + "."); - log.info("The received message is " + infrastructureV2xMessageReception.getMessage() + " ."); + private synchronized void receiveV2xReceptionInteraction(V2xMessageReception interaction) { + String rsuId = interaction.getReceiverName(); + + if (!infrastructureInstanceManager.checkIfRegistered(rsuId)) { + // Abort early as we only are concerned with CARMA Platform vehicles + + log.info("Abort V2X message reception event for " + interaction.getReceiverName() + " of msg id " + interaction.getMessageId() + " from sender " + interaction.getSenderId()); + return; + } + log.info("Processing V2X message reception event for " + interaction.getReceiverName() + " of msg id " + interaction.getMessageId() + " from sender " + interaction.getSenderId()); + + int messageId = interaction.getMessageId(); + log.info("Querying v2x message cache for message id: {}", messageId); + V2xMessage msg = SimulationKernel.SimulationKernel.getV2xMessageCache().getItem(messageId); + + log.info("Is msg null ? {}", msg == null); + log.info("Is msg instanceof ExternalV2xMessage ? {}", msg instanceof ExternalV2xMessage); + + if (msg != null && msg instanceof ExternalV2xMessage) { + ExternalV2xMessage msg2 = (ExternalV2xMessage) msg; + infrastructureInstanceManager.onV2XMessageRx(DatatypeConverter.parseHexBinary(msg2.getMessage()), rsuId); + log.info("Sending V2X message reception event for " + interaction.getReceiverName() + " of msg id " + interaction.getMessageId() + " of size " + msg2.getPayLoad().getBytes().length); + } else { + log.warn("Message with id " + interaction.getMessageId() + " received by " + interaction.getReceiverName() + " is no longer in the message buffer to be retrieved! Message transmission failed!!!"); + } } /** @@ -170,7 +198,7 @@ private synchronized void receiveInteraction(InfrastructureV2xMessageReception i * Ad-Hoc interface used for communication between the infrastructure instance * and other vehicles or components, and sends it to the RTI for exchange. * - * @param reg the infrastructure registration message received from the RTI + * @param infrastructureId the infrastructure registration message received from the RTI * * Note: This function should be called after the * onRsuRegistrationRequest @@ -179,15 +207,15 @@ private synchronized void receiveInteraction(InfrastructureV2xMessageReception i private void onDsrcRegistrationRequest(String infrastructureId) throws UnknownHostException { // Create an InterfaceConfiguration object to represent the configuration of the // Ad-Hoc interface - // TODO: Replace the IP address of the ad-hoc interface if necessary - // TODO: Replace the subnet mask of the ad-hoc interface if necessary // TODO: Replace the transmit power of the ad-hoc interface (in dBm) if necessary // TODO: Replace the communication range of the ad-hoc interface (in meters) if necessary - InterfaceConfiguration interfaceConfig = new InterfaceConfiguration.Builder(AdHocChannel.SCH1) - .ip((Inet4Address) Inet4Address.getByName("192.168.0.1")) - .subnet((Inet4Address) Inet4Address.getByName("255.255.255.0")) + Inet4Address rsuAddress = IpResolver.getSingleton().registerHost(infrastructureId); + log.info("Assigned registered comms device " + infrastructureId + " with IP address " + rsuAddress.toString()); + InterfaceConfiguration interfaceConfig = new InterfaceConfiguration.Builder(AdHocChannel.CCH) + .ip(rsuAddress) + .subnet(IpResolver.getSingleton().getNetMask()) .power(50) - .radius(100.0) + .radius(300.0) .create(); // Create an AdHocConfiguration object to associate the Ad-Hoc interface @@ -200,7 +228,7 @@ private void onDsrcRegistrationRequest(String infrastructureId) throws UnknownHo // Ad-Hoc configuration for exchange with another vehicle or component AdHocCommunicationConfiguration communicationConfig = new AdHocCommunicationConfiguration(currentSimulationTime, adHocConfig); - + log.info("Communications comms device " +infrastructureId + " with IP address " + rsuAddress.toString() + " success!"); try { // Trigger RTI interaction to MOSAIC to exchange the Ad-Hoc configuration this.rti.triggerInteraction(communicationConfig); @@ -213,13 +241,12 @@ private void onDsrcRegistrationRequest(String infrastructureId) throws UnknownHo /** * Performs registration of a new infrastructure instance and sets up - * communication interfaces. - * - * @param reg The registration message of the new infrastructure instance. + * communication interfaces */ private void onRsuRegistrationRequest(String infrastructureId, GeoPoint location) { // Register the new infrastructure instance to the RTI as an RSU + log.info("Attemtping to register RSU ID: " + infrastructureId + " @ " + location.toString()); RsuRegistration rsuRegistration = new RsuRegistration(currentSimulationTime, infrastructureId, "", Collections.emptyList(), location); try { @@ -247,23 +274,37 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder if (time < currentSimulationTime) { return; } + log.info("Infrastructure message ambassador processing timestep to " + time); try { // Handle any new infrastructure registration requests List newRegistrations = infrastructureRegistrationReceiver .getReceivedMessages(); for (InfrastructureRegistrationMessage reg : newRegistrations) { + log.info("Processing new registration request for " + reg.getInfrastructureId()); // Store new instance registration to infrastructure instance manager infrastructureInstanceManager.onNewRegistration(reg); // Process registration requests for RSUs and DSRCs - onRsuRegistrationRequest(reg.getInfrastructureId(), reg.getLocation()); + double x = reg.getLocation().getLatitude(); + double y = reg.getLocation().getLongitude(); + double z = reg.getLocation().getAltitude(); + onRsuRegistrationRequest(reg.getInfrastructureId(), CartesianPoint.xyz(x, y,z).toGeo()); + log.info("RSU Registration for "+ reg.getInfrastructureId() + " @ x, y, z: (" + x + ", " + y + ", " + z+ ")"); onDsrcRegistrationRequest(reg.getInfrastructureId()); } - List> newMessages = v2xMessageReceiver.getReceivedMessages(); - for (Tuple msg : newMessages) { - V2xMessageTransmission msgInt = infrastructureInstanceManager.onV2XMessageTx(msg.getA(), msg.getB()); - this.rti.triggerInteraction(msgInt); + if (currentSimulationTime == 0) { + // For the first timestep, clear the message receive queues. + v2xMessageReceiver.getReceivedMessages(); // Automatically empties the queues. + } else { + List> newMessages = v2xMessageReceiver.getReceivedMessages(); + for (Tuple msg : newMessages) { + log.info("Processing new V2X transmit event of type " + msg.getB().getType()); + V2xMessageTransmission msgInt = infrastructureInstanceManager.onV2XMessageTx(msg.getA(), msg.getB(), currentSimulationTime); + SimulationKernel.SimulationKernel.getV2xMessageCache().putItem(currentSimulationTime, msgInt.getMessage()); + log.info("Inserted message ID {} into v2xmessage cache.", msgInt.getMessageId()); + this.rti.triggerInteraction(msgInt); + } } timeSyncSeq += 1; @@ -279,20 +320,8 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder currentSimulationTime += infrastructureConfiguration.updateInterval * TIME.MILLI_SECOND; // Request the next time advance from the RTI + log.info("Requesting timestep updated to " + currentSimulationTime); rti.requestAdvanceTime(currentSimulationTime, 0, (byte) 2); - - // Send an external message to the RSU - String message = "External Message to RSU: RSU sent message at time: " + currentSimulationTime; - ExternalMessage rsuMessage = new ExternalMessage(currentSimulationTime, message, - infrastructureConfiguration.senderRSUId); - try { - // Trigger RTI interaction to MOSAIC - this.rti.triggerInteraction(rsuMessage); - } catch (InternalFederateException | IllegalValueException e) { - // Log an error message if there was an issue with the RTI interaction - log.error(e.getMessage()); - } - } catch (IllegalValueException e) { log.error("Error during advanceTime(" + time + ")", e); throw new InternalFederateException(e); diff --git a/co-simulation/lib/mosaic-carma-utils/pom.xml b/co-simulation/lib/mosaic-carma-utils/pom.xml index 3c605899..9e6f73f2 100644 --- a/co-simulation/lib/mosaic-carma-utils/pom.xml +++ b/co-simulation/lib/mosaic-carma-utils/pom.xml @@ -39,6 +39,16 @@ mosaic-geomath ${mosaic.version} + + jakarta.xml.bind + jakarta.xml.bind-api + 2.3.2 + + + org.glassfish.jaxb + jaxb-runtime + 2.3.2 + ch.qos.logback logback-classic @@ -53,7 +63,6 @@ commons-codec commons-codec 1.15 - test \ No newline at end of file diff --git a/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java index d29de5c7..e3871b5e 100644 --- a/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java +++ b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessage.java @@ -141,7 +141,11 @@ private void parseV2xMessage(byte[] buf) { } else if (msgParts[i].equals("Type")) { type = msgParts[++i]; } else if (msgParts[i].equals("PSID")) { - psid = Integer.parseInt(msgParts[++i]); + if (msgParts[i + 1].startsWith("0x")) { + psid = Integer.parseInt(msgParts[++i].split("0x")[1]); + } else { + psid = Integer.parseInt(msgParts[++i]); + } } else if (msgParts[i].equals("Priority")) { priority = Integer.parseInt(msgParts[++i]); } else if (msgParts[i].equals("TxMode")) { diff --git a/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessageReceiver.java b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessageReceiver.java index 27c6303a..dc2d369d 100644 --- a/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessageReceiver.java +++ b/co-simulation/lib/mosaic-carma-utils/src/main/java/gov/dot/fhwa/saxton/CarmaV2xMessageReceiver.java @@ -85,10 +85,7 @@ public void run() { byte[] buf = new byte[UDP_MTU]; while (running) { DatagramPacket msg = new DatagramPacket(buf, buf.length); - InetAddress senderAddr = msg.getAddress(); - - - + try { listenSocket.receive(msg); log.info("CarmaV2xMessageReceiver received message of size: " + msg.getLength() + " from client " + msg.getAddress().toString() + "."); @@ -102,7 +99,7 @@ public void run() { // Enqueue message for processing on main thread synchronized (rxQueue) { - rxQueue.add(new Tuple<>(senderAddr, parsedMessage)); + rxQueue.add(new Tuple<>(msg.getAddress(), parsedMessage)); log.info("CarmaV2xMessageReceiver enqueued message of size: " + msg.getLength() + " from client " + msg.getAddress().toString() + "."); } } catch (IllegalArgumentException parseError) { diff --git a/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java b/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java index 6e7e99be..964aaeb3 100644 --- a/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java +++ b/co-simulation/lib/mosaic-carma-utils/src/test/java/gov/dot/fhwa/saxton/CarmaV2xMessageTest.java @@ -55,6 +55,77 @@ public class CarmaV2xMessageTest { "61313030376666663830303039363066" + "61300a"; + private final String sampleMessage3 = + "Version=0.7\n" + + "Type=BSM\n" + + "PSID=0x0020\n" + + "Priority=6\n" + + "TxMode=ALT\n" + + "TxChannel=172\n" + + "TxInterval=0\n" + + "DeliveryStart=\n" + + "DeliveryStop=\n" + + "Signature=False\n" + + "Encryption=False\n" + + "Payload=00142500400000000f0e35a4e900eb49d20000007fffffff8ffff080fdfa1fa1007fff8000960fa0\n"; + + private static final String sampleMessage5 = + "45000364c73f400040111842ac020001" + + "ac020002977b05ed03505b6956657273" + + "696f6e3d302e370a547970653d4d4150" + + "0a505349443d3078383030320a507269" + + "6f726974793d370a54784d6f64653d43" + + "4f4e540a54784368616e6e656c3d3138" + + "330a5478496e74657276616c3d300a44" + + "656c697665727953746172743d0a4465" + + "6c697665727953746f703d0a5369676e" + + "61747572653d46616c73650a456e6372" + + "797074696f6e3d46616c73650a506179" + + "6c6f61643d3030313238313533333831" + + "31333032303230344244413435344344" + + "43463831343344344443343838313138" + + "36303232343136343830323238303030" + + "38303032323937443442433830413041" + + "30413938323538323539323341393042" + + "32463245343138393836463431423730" + + "30363438303630353430333832413032" + + "30313434303135343830303130303034" + + "35323144394630303134313431363043" + + "37433432413138373938353836313935" + + "30324134324130363045393237313030" + + "36363230303034303031303542453642" + + "46343143384144454435383136454243" + + "30353035303744434238363045433537" + + "41454144353037394530323832383930" + + "30383930303031303030343137323233" + + "41353037323842373530463943364541" + + "39453841453438304130413046363837" + + "34364144343437433030323832383930" + + "30413130383037303834303430343038" + + "30334239303030323030303632423638" + + "44353330354431463932363941373235" + + "30323744383335324637323836374436" + + "43383234303333343030303430303043" + + "35334635423736314142424237443335" + + "44334330383133454331413342414143" + + "31364246433034383035304234303330" + + "35413032303243343032323038303031" + + "30303033313046453535463834394143" + + "44363038443841434531333642343430" + + "30303044464534383038383830303038" + + "30303230383633363543303031374431" + + "36313245423334303236303637343034" + + "38393533393039303742443834383035" + + "30323430333031323031433038303032" + + "34303030323030303030303930303236" + + "31383041304130463238353236303031" + + "34303030313030303030303136394643" + + "31353835424431444130303042303030" + + "30383030303030304133424232463433" + + "39343539413830303630303030343030" + + "30303030343644353543343136433637" + + "4634300a"; + @Before public void setUp() throws Exception { } @@ -79,4 +150,17 @@ public void testCarmaV2xMessageParse3() throws DecoderException { assert(test != null); } + @Test + public void testCarmaV2xMessageParse4() throws DecoderException { + CarmaV2xMessage test = new CarmaV2xMessage(sampleMessage3.getBytes()); + assert(test != null); + } + + @Test + public void testCarmaV2xMessageParse5() throws DecoderException { + byte[] bytes = Hex.decodeHex(sampleMessage5.toCharArray()); + CarmaV2xMessage test = new CarmaV2xMessage(bytes); + assert(test != null); + } + } diff --git a/co-simulation/lib/mosaic-interactions/src/main/java/org/eclipse/mosaic/interactions/mapping/advanced/ExternalVehicleRegistration.java b/co-simulation/lib/mosaic-interactions/src/main/java/org/eclipse/mosaic/interactions/mapping/advanced/ExternalVehicleRegistration.java new file mode 100644 index 00000000..64d3fc3e --- /dev/null +++ b/co-simulation/lib/mosaic-interactions/src/main/java/org/eclipse/mosaic/interactions/mapping/advanced/ExternalVehicleRegistration.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2019-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. + */ + +package org.eclipse.mosaic.interactions.mapping.advanced; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.eclipse.mosaic.lib.objects.mapping.VehicleMapping; +import org.eclipse.mosaic.lib.objects.vehicle.VehicleType; +import org.eclipse.mosaic.rti.api.Interaction; + +import java.util.List; + +import static org.apache.commons.lang3.builder.ToStringStyle.SHORT_PREFIX_STYLE; + +/** + * This interaction is used to define vehicle's controlled externally (i.e. not directly by a MOSAIC application or + * by a defined behavior and/or route in one of the attached simulators. + */ +public final class ExternalVehicleRegistration extends Interaction { + private static final long serialVersionUID = 1L; + + /** + * String identifying the type of this interaction. + */ + public final static String TYPE_ID = createTypeIdentifier(ExternalVehicleRegistration.class); + + /** + * The new vehicle. + */ + private final VehicleMapping vehicleMapping; + + /** + * Creates a new interaction that informs about a new added vehicle to the simulation. + * + * @param time Timestamp of this interaction, unit: [ns] + * @param name vehicle identifier + * @param group vehicle group identifier + * @param applications installed applications of the vehicle + * @param vehicleType vehicle type + */ + public ExternalVehicleRegistration(final long time, final String name, final String group, final List applications, final VehicleType vehicleType) { + super(time); + this.vehicleMapping = new VehicleMapping(name, group, applications, vehicleType); + } + + public VehicleMapping getMapping() { + return vehicleMapping; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(7, 19) + .append(vehicleMapping) + .toHashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (obj.getClass() != getClass()) { + return false; + } + + ExternalVehicleRegistration rhs = (ExternalVehicleRegistration) obj; + return new EqualsBuilder() + .append(this.vehicleMapping, rhs.vehicleMapping) + .isEquals(); + } + + @Override + public String toString() { + return new ToStringBuilder(this, SHORT_PREFIX_STYLE) + .appendSuper(super.toString()) + .append("vehicleMapping", vehicleMapping) + .toString(); + } +} diff --git a/co-simulation/lib/mosaic-network/src/main/java/org/eclipse/mosaic/lib/coupling/AbstractNetworkAmbassador.java b/co-simulation/lib/mosaic-network/src/main/java/org/eclipse/mosaic/lib/coupling/AbstractNetworkAmbassador.java index 0bc6b5e0..ab8f03dc 100644 --- a/co-simulation/lib/mosaic-network/src/main/java/org/eclipse/mosaic/lib/coupling/AbstractNetworkAmbassador.java +++ b/co-simulation/lib/mosaic-network/src/main/java/org/eclipse/mosaic/lib/coupling/AbstractNetworkAmbassador.java @@ -15,12 +15,14 @@ package org.eclipse.mosaic.lib.coupling; +import org.apache.commons.lang3.tuple.Pair; import org.eclipse.mosaic.interactions.communication.AdHocCommunicationConfiguration; import org.eclipse.mosaic.interactions.communication.V2xMessageReception; import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; import org.eclipse.mosaic.interactions.mapping.RsuRegistration; import org.eclipse.mosaic.interactions.mapping.TrafficLightRegistration; import org.eclipse.mosaic.interactions.mapping.VehicleRegistration; +import org.eclipse.mosaic.interactions.mapping.advanced.ExternalVehicleRegistration; import org.eclipse.mosaic.interactions.traffic.VehicleUpdates; import org.eclipse.mosaic.lib.coupling.ClientServerChannel.CMD; import org.eclipse.mosaic.lib.coupling.ClientServerChannel.NodeDataContainer; @@ -43,20 +45,12 @@ import org.eclipse.mosaic.rti.api.InternalFederateException; import org.eclipse.mosaic.rti.api.federatestarter.DockerFederateExecutor; import org.eclipse.mosaic.rti.api.parameters.AmbassadorParameter; - -import org.apache.commons.lang3.tuple.Pair; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Scanner; +import java.util.*; /** * The Ambassador for coupling a network simulator to MOSAIC RTI. @@ -283,6 +277,8 @@ protected void processInteraction(Interaction interaction) throws InternalFedera this.receiveTypedInteraction((V2xMessageTransmission) interaction); } else if (interaction.getTypeId().equals(AdHocCommunicationConfiguration.TYPE_ID)) { this.receiveTypedInteraction((AdHocCommunicationConfiguration) interaction); + } else if (interaction.getTypeId().equals((ExternalVehicleRegistration.TYPE_ID))) { + this.receiveTypedInteraction((ExternalVehicleRegistration) interaction); } } @@ -372,6 +368,30 @@ private synchronized void receiveTypedInteraction(VehicleRegistration interactio newVirtualVehicles.put(av.getName(), new VirtualNodeContainer(null, null)); } + /** + * Store nodes for later adding based on received vehicle mappings. + *
+ * The unique mapping of RTI string-ids to federate integer-ids + * is also done in the handling of the vehicle movements + * + * @param interaction interaction containing a mapping of added vehicles + */ + private synchronized void receiveTypedInteraction(ExternalVehicleRegistration interaction) { + this.log.debug( + "Received ExternalVehicleRegistration for vehicle {} at simulation time {} ", + interaction.getMapping().getName(), + interaction.getTime() + ); + VehicleMapping av = interaction.getMapping(); + // We have got this Vehicle already + if (idTransformer.containsInternalId(av.getName()) || newVirtualVehicles.containsKey(av.getName())) { + this.log.warn("A vehicle with ID {} was already added. Ignoring message.", av.getName()); + return; + } + // Add new virtual vehicle-container without config message or position + newVirtualVehicles.put(av.getName(), new VirtualNodeContainer(null, null)); + } + /** * Add nodes based on received rsu mappings. * From b4d990ca345a3781265392827350b6f925d143ff Mon Sep 17 00:00:00 2001 From: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> Date: Wed, 17 May 2023 22:02:48 -0400 Subject: [PATCH 113/124] Updated all GeoPoint to CartesianPoint for infrastructure --- .../ambassador/InfrastructureInstance.java | 12 ++++++------ .../ambassador/InfrastructureInstanceManager.java | 10 +++++----- .../ambassador/InfrastructureMessageAmbassador.java | 8 ++------ .../InfrastructureRegistrationMessage.java | 10 +++++----- .../InfrastructureInstanceManagerTest.java | 10 +++++----- .../InfrastructureRegistrationReceiverTest.java | 9 +++++---- 6 files changed, 28 insertions(+), 31 deletions(-) diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java index 373ea17b..87e602d4 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstance.java @@ -16,7 +16,7 @@ package org.eclipse.mosaic.fed.infrastructure.ambassador; -import org.eclipse.mosaic.lib.geo.GeoPoint; +import org.eclipse.mosaic.lib.geo.CartesianPoint; import java.io.IOException; import java.net.DatagramPacket; @@ -35,7 +35,7 @@ public class InfrastructureInstance { private InetAddress targetAddress; private int rxMessagePort; private int timeSyncPort; - private GeoPoint location = null; + private CartesianPoint location = null; private DatagramSocket rxMsgsSocket = null; /** @@ -50,7 +50,7 @@ public class InfrastructureInstance { * simulated environment */ public InfrastructureInstance(String infrastructureId, InetAddress targetAddress, - int rxMessagePort, int timeSyncPort, GeoPoint location) { + int rxMessagePort, int timeSyncPort, CartesianPoint location) { this.infrastructureId = infrastructureId; this.targetAddress = targetAddress; this.rxMessagePort = rxMessagePort; @@ -79,9 +79,9 @@ public void setTargetAddress(InetAddress targetAddress) { /** * Returns the location of the infrastructure node in the simulated environment * - * @return GeoPoint the location of the infrastructure node + * @return CartesianPoint the location of the infrastructure node */ - public GeoPoint getLocation() { + public CartesianPoint getLocation() { return this.location; } @@ -90,7 +90,7 @@ public GeoPoint getLocation() { * * @param location the location to set */ - public void setLocation(GeoPoint location) { + public void setLocation(CartesianPoint location) { this.location = location; } diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java index 09889470..f711fc16 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManager.java @@ -20,7 +20,7 @@ import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; import org.eclipse.mosaic.lib.enums.AdHocChannel; import org.eclipse.mosaic.lib.geo.GeoCircle; -import org.eclipse.mosaic.lib.geo.GeoPoint; +import org.eclipse.mosaic.lib.geo.CartesianPoint; import org.eclipse.mosaic.lib.objects.addressing.AdHocMessageRoutingBuilder; import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xContent; import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xMessage; @@ -91,7 +91,7 @@ public void onNewRegistration(InfrastructureRegistrationMessage registration) { * */ private void newInfrastructureInstance(String infrastructureId, InetAddress rxMessageIpAddress, int rxMessagePort, - int timeSyncPort, GeoPoint location) { + int timeSyncPort, CartesianPoint location) { InfrastructureInstance tmp = new InfrastructureInstance(infrastructureId, rxMessageIpAddress, rxMessagePort, timeSyncPort, location); try { @@ -125,13 +125,13 @@ public V2xMessageTransmission onV2XMessageTx(InetAddress sourceAddr, CarmaV2xMes } AdHocMessageRoutingBuilder messageRoutingBuilder = new AdHocMessageRoutingBuilder( - sender.getInfrastructureId(), sender.getLocation()).viaChannel(AdHocChannel.CCH); + sender.getInfrastructureId(), sender.getLocation().toGeo()).viaChannel(AdHocChannel.CCH); // TODO: Get maximum broadcast radius from configuration file. - MessageRouting routing = messageRoutingBuilder.geoBroadCast(new GeoCircle(sender.getLocation(), 300)); + MessageRouting routing = messageRoutingBuilder.geoBroadCast(new GeoCircle(sender.getLocation().toGeo(), 300)); return new V2xMessageTransmission(time, new ExternalV2xMessage(routing, - new ExternalV2xContent(time, sender.getLocation(), txMsg.getPayload()))); + new ExternalV2xContent(time, sender.getLocation().toGeo(), txMsg.getPayload()))); } /** diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java index 7919b562..a5dedc1c 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureMessageAmbassador.java @@ -26,7 +26,6 @@ import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission; import org.eclipse.mosaic.interactions.mapping.RsuRegistration; import org.eclipse.mosaic.lib.enums.AdHocChannel; -import org.eclipse.mosaic.lib.geo.CartesianPoint; import org.eclipse.mosaic.lib.geo.GeoPoint; import org.eclipse.mosaic.lib.misc.Tuple; import org.eclipse.mosaic.lib.objects.addressing.IpResolver; @@ -285,11 +284,8 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder // Store new instance registration to infrastructure instance manager infrastructureInstanceManager.onNewRegistration(reg); // Process registration requests for RSUs and DSRCs - double x = reg.getLocation().getLatitude(); - double y = reg.getLocation().getLongitude(); - double z = reg.getLocation().getAltitude(); - onRsuRegistrationRequest(reg.getInfrastructureId(), CartesianPoint.xyz(x, y,z).toGeo()); - log.info("RSU Registration for "+ reg.getInfrastructureId() + " @ x, y, z: (" + x + ", " + y + ", " + z+ ")"); + onRsuRegistrationRequest(reg.getInfrastructureId(), reg.getLocation().toGeo()); + log.info("RSU Registration for "+ reg.getInfrastructureId() + " @ x, y, z: (" + reg.getLocation().getX() + ", " + reg.getLocation().getY() + ", " + reg.getLocation().getZ() + ")"); onDsrcRegistrationRequest(reg.getInfrastructureId()); } diff --git a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java index 5a8b9430..b81bde84 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java +++ b/co-simulation/fed/mosaic-infrastructure/src/main/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationMessage.java @@ -16,7 +16,7 @@ package org.eclipse.mosaic.fed.infrastructure.ambassador; -import org.eclipse.mosaic.lib.geo.GeoPoint; +import org.eclipse.mosaic.lib.geo.CartesianPoint; /** * A message to be sent by Infrastructure Device when it registers with the @@ -39,7 +39,7 @@ public class InfrastructureRegistrationMessage { private int timeSyncPort = 1517; // Geo-coordinate of the Infrastructure Device location - private GeoPoint location = null; + private CartesianPoint location = null; /** * Constructor for an `InfrastructureRegistrationMessage` instance @@ -55,7 +55,7 @@ public class InfrastructureRegistrationMessage { * location */ public InfrastructureRegistrationMessage(String rxMessageIpAddress, String infrastructureId, - int rxMessagePort, int timeSyncPort, GeoPoint location) { + int rxMessagePort, int timeSyncPort, CartesianPoint location) { this.rxMessageIpAddress = rxMessageIpAddress; this.infrastructureId = infrastructureId; this.rxMessagePort = rxMessagePort; @@ -110,7 +110,7 @@ public int getTimeSyncPort() { * * @return The Geo-coordinate of the Infrastructure Device location */ - public GeoPoint getLocation() { + public CartesianPoint getLocation() { return this.location; } @@ -161,7 +161,7 @@ public void setTimeSyncPort(int timeSyncPort) { * @param location the new GeoPoint object representing the location of the * Infrastructure Device */ - public void setLocation(GeoPoint location) { + public void setLocation(CartesianPoint location) { this.location = location; } diff --git a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManagerTest.java b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManagerTest.java index 7bc7b19c..c49d4dfc 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManagerTest.java +++ b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureInstanceManagerTest.java @@ -21,7 +21,7 @@ import java.net.InetAddress; -import org.eclipse.mosaic.lib.geo.GeoPoint; +import org.eclipse.mosaic.lib.geo.CartesianPoint; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; @@ -31,14 +31,14 @@ public class InfrastructureInstanceManagerTest { private InfrastructureInstanceManager manager; private InfrastructureRegistrationMessage registration; private InetAddress ipAddress; - private GeoPoint location; + private CartesianPoint location; @Before public void setUp() throws Exception { manager = new InfrastructureInstanceManager(); registration = mock(InfrastructureRegistrationMessage.class); ipAddress = mock(InetAddress.class); - location = mock(GeoPoint.class); + location = mock(CartesianPoint.class); } @Test @@ -48,7 +48,7 @@ public void testOnNewRegistration() { int rxMessagePort = 1234; int timeSyncPort = 5678; String ipAddressString = "127.0.0.1"; - GeoPoint pt = GeoPoint.latLon(37.3382, -121.8863); + CartesianPoint pt = CartesianPoint.xyz(37.3382, -121.8863, 1.0); // Mock the behavior of the registration object mockRegistrationObject(infrastructureId, rxMessagePort, timeSyncPort, ipAddressString, pt); @@ -61,7 +61,7 @@ public void testOnNewRegistration() { } private void mockRegistrationObject(String infrastructureId, int rxMessagePort, int timeSyncPort, - String ipAddressString, GeoPoint pt) { + String ipAddressString, CartesianPoint pt) { // Mock the behavior of the registration object Mockito.when(registration.getInfrastructureId()).thenReturn(infrastructureId); Mockito.when(registration.getRxMessagePort()).thenReturn(rxMessagePort); diff --git a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiverTest.java b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiverTest.java index 2cd893c5..a93a24b5 100644 --- a/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiverTest.java +++ b/co-simulation/fed/mosaic-infrastructure/src/test/java/org/eclipse/mosaic/fed/infrastructure/ambassador/InfrastructureRegistrationReceiverTest.java @@ -56,7 +56,7 @@ public void teardown() throws Exception { @Test public void testMessageReceive() throws Exception { // Define a test message in JSON format - String json = "{\"rxMessageIpAddress\":\"192.168.0.1\",\"infrastructureId\":\"5678\",\"rxMessagePort\":1234,\"timeSyncPort\":5678,\"location\":{\"latitude\":37.3382,\"longitude\":121.8863}}"; + String json = "{\"rxMessageIpAddress\":\"192.168.0.1\",\"infrastructureId\":\"rsu_1\",\"rxMessagePort\":1234,\"timeSyncPort\":5678,\"location\":{\"x\":37.3382,\"y\":121.8863, \"z\":1.0}}"; byte[] buffer = json.getBytes(); // Send the test message to the receiver @@ -75,11 +75,12 @@ public void testMessageReceive() throws Exception { double delta = 0.001; // maximum allowed difference for GeoLocation lat and lon assertEquals("192.168.0.1", msg.getRxMessageIpAddress()); - assertEquals("5678", msg.getInfrastructureId()); + assertEquals("rsu_1", msg.getInfrastructureId()); assertEquals(1234, msg.getRxMessagePort()); assertEquals(5678, msg.getTimeSyncPort()); - assertEquals(37.3382, msg.getLocation().getLatitude(), delta); - assertEquals(121.8863, msg.getLocation().getLongitude(), delta); + assertEquals(37.3382, msg.getLocation().getX(), delta); + assertEquals(121.8863, msg.getLocation().getY(), delta); + assertEquals(1.0, msg.getLocation().getZ(), delta); } } From 460f443b470f5670dde3aa81c892b4420b2b6bcc Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 22 May 2023 17:56:58 -0400 Subject: [PATCH 114/124] update config to 1 controller update config to 1 controller --- evc-sumo/src/resources/evc_sumo_cfg.json | 34 +----------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/evc-sumo/src/resources/evc_sumo_cfg.json b/evc-sumo/src/resources/evc_sumo_cfg.json index 72f24771..65a27c9b 100644 --- a/evc-sumo/src/resources/evc_sumo_cfg.json +++ b/evc-sumo/src/resources/evc_sumo_cfg.json @@ -14,38 +14,6 @@ "evcPhases": [{"evcPhaseId": 2, "sumoTlStateIndex":[0,3], "sumoInductionLoopId": "e1_0"}, {"evcPhaseId": 6, "sumoTlStateIndex":[1,4], "sumoInductionLoopId": "e1_1"}, {"evcPhaseId": 4, "sumoTlStateIndex":[2,5], "sumoInductionLoopId": "e1_2"} ] - }, - { - "controllerId": 2, - "controllerCfgPath": "resources/148.cfg", - "start_time": null, - "https_port": 0, - "web_port": 0, - "snmp_port": 0, - "harness_port": 0, - "controller_speed": null, - "sumoTlId": 148, - "enableWebPanel": true, - "evcPhases": [{"evcPhaseId": 2, "sumoTlStateIndex":[7,8,10], "sumoInductionLoopId": "e1_3"}, - {"evcPhaseId": 6, "sumoTlStateIndex":[4,5,6], "sumoInductionLoopId": "e1_4"}, - {"evcPhaseId": 4, "sumoTlStateIndex":[2,3,11], "sumoInductionLoopId": "e1_5"}, - {"evcPhaseId": 8, "sumoTlStateIndex":[0,1,9], "sumoInductionLoopId": "e1_6"} ] - }, - { - "controllerId": 3, - "controllerCfgPath": "resources/916.cfg", - "start_time": null, - "https_port": 0, - "web_port": 0, - "snmp_port": 0, - "harness_port": 0, - "controller_speed": null, - "sumoTlId": 916, - "enableWebPanel": true, - "evcPhases": [{"evcPhaseId": 2, "sumoTlStateIndex":[4,10,11], "sumoInductionLoopId": "e1_7"}, - {"evcPhaseId": 6, "sumoTlStateIndex":[0,2,6], "sumoInductionLoopId": "e1_8"}, - {"evcPhaseId": 4, "sumoTlStateIndex":[1,3,8], "sumoInductionLoopId": "e1_9"}, - {"evcPhaseId": 8, "sumoTlStateIndex":[5,7,9], "sumoInductionLoopId": "e1_10"} ] } ] -} \ No newline at end of file +} From 9810b0d7832c2541d038181f6922019886770bc7 Mon Sep 17 00:00:00 2001 From: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> Date: Tue, 23 May 2023 15:20:42 -0400 Subject: [PATCH 115/124] Updated carla vehicle to be registered as role name, not id --- .../bridge/carla_integration/sumo_simulation.py | 10 +++------- .../bridge/carla_integration/synchronization.py | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/co-simulation/bridge/carla_integration/sumo_simulation.py b/co-simulation/bridge/carla_integration/sumo_simulation.py index d5d892d0..ade4b5d6 100644 --- a/co-simulation/bridge/carla_integration/sumo_simulation.py +++ b/co-simulation/bridge/carla_integration/sumo_simulation.py @@ -331,9 +331,6 @@ def __init__(self, sumo_net, step_length, host=None, port=None): self.net = sumo_net - # Variable to asign an id to new added actors. - self._sequential_id = 0 - # Structures to keep track of the spawned and destroyed vehicles at each time step. self.spawned_actors = set() self.destroyed_actors = set() @@ -411,15 +408,16 @@ def get_actor(actor_id): return SumoActor(type_id, vclass, transform, signals, extent, color) - def spawn_actor(self, type_id, color=None): + def spawn_actor(self, type_id, role_name, color=None): """ Spawns a new actor. :param type_id: vtype to be spawned. + :param role_name: vehicle role name to register to carla ambassador :param color: color attribute for this specific actor. :return: actor id if the actor is successfully spawned. Otherwise, INVALID_ACTOR_ID. """ - actor_id = 'carla_' + str(self._sequential_id) + actor_id = role_name try: traci.vehicle.add(actor_id, 'carla_route', typeID=type_id) except traci.exceptions.TraCIException as error: @@ -430,8 +428,6 @@ def spawn_actor(self, type_id, color=None): color = color.split(',') traci.vehicle.setColor(actor_id, color) - self._sequential_id += 1 - return actor_id @staticmethod diff --git a/co-simulation/bridge/carla_integration/synchronization.py b/co-simulation/bridge/carla_integration/synchronization.py index 7627593a..65e1b24e 100644 --- a/co-simulation/bridge/carla_integration/synchronization.py +++ b/co-simulation/bridge/carla_integration/synchronization.py @@ -153,11 +153,11 @@ def tick(self): carla_spawned_actors = self.carla.spawned_actors - set(self.sumo2carla_ids.values()) for carla_actor_id in carla_spawned_actors: carla_actor = self.carla.get_actor(carla_actor_id) - + role_name = self.carla.get_actor(carla_actor_id).attributes["role_name"] type_id = BridgeHelper.get_sumo_vtype(carla_actor) color = carla_actor.attributes.get('color', None) if self.sync_vehicle_color else None if type_id is not None: - sumo_actor_id = self.sumo.spawn_actor(type_id, color) + sumo_actor_id = self.sumo.spawn_actor(type_id, role_name, color) if sumo_actor_id != INVALID_ACTOR_ID: self.carla2sumo_ids[carla_actor_id] = sumo_actor_id From 413d4c866ea2c401a58359e0a09900a34ff389b2 Mon Sep 17 00:00:00 2001 From: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> Date: Tue, 23 May 2023 16:45:18 -0400 Subject: [PATCH 116/124] Update MosaicConformVehicleIdTransformer.java --- .../fed/sumo/util/MosaicConformVehicleIdTransformer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/util/MosaicConformVehicleIdTransformer.java b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/util/MosaicConformVehicleIdTransformer.java index ea7cab73..8555e625 100644 --- a/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/util/MosaicConformVehicleIdTransformer.java +++ b/co-simulation/fed/mosaic-sumo/src/main/java/org/eclipse/mosaic/fed/sumo/util/MosaicConformVehicleIdTransformer.java @@ -64,8 +64,9 @@ public String toExternalId(String mosaicVehicleId) { @Override public String fromExternalId(String sumoVehicleId) { // do not change Carla vehicle ID - if (sumoVehicleId.startsWith("carla")) { + if (sumoVehicleId.startsWith("carma")) { sumoToMosaicVehicleIdMap.put(sumoVehicleId, sumoVehicleId); + log.info("Assigned vehicle id {}", sumoVehicleId); return sumoVehicleId; } From 01faf1dd5f188f566e6c42eeb2bb5276c913d7f4 Mon Sep 17 00:00:00 2001 From: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> Date: Tue, 23 May 2023 18:38:17 -0400 Subject: [PATCH 117/124] Removed SUMO vehicle in Town04 and Town04_test scenario --- .../scenarios/Town04/sumo/Town04.rou.xml | 15 +++++---------- .../scenarios/Town04_test/sumo/Town04.rou.xml | 15 +++++---------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.rou.xml b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.rou.xml index 351f1680..5072fe20 100755 --- a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.rou.xml +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/sumo/Town04.rou.xml @@ -1,16 +1,11 @@ - + + - - - - - - - - - + + \ No newline at end of file diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.rou.xml b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.rou.xml index 351f1680..5072fe20 100755 --- a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.rou.xml +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.rou.xml @@ -1,16 +1,11 @@ - + + - - - - - - - - - + + \ No newline at end of file From 341c8f79239fd97cc603e70189f24a2f37cfe365 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 24 May 2023 15:32:27 -0400 Subject: [PATCH 118/124] Set snmp port to 5050 in config file (#138) # PR Details ## Description Added configuration to EVC to set SNMP port to 5050. ## Related Issue ## Motivation and Context Fixed SNMP port so that tsc_configuration files do not need to change. ## How Has This Been Tested? Tested locally by deploying config EVC and enabling SPAT and running TCPDUMP to confirm spat messages are sent to correct IP ## Types of changes - [ ] Defect fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that cause existing functionality to change) ## Checklist: - [ ] 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. - [x ] I have read the **CONTRIBUTING** document. [CARMA Contributing Guide](Contributing.md) - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. --- evc-sumo/src/resources/evc_sumo_cfg.json | 4 ++-- evc-sumo/src/resources/test_3.cfg | Bin 0 -> 147474 bytes 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 evc-sumo/src/resources/test_3.cfg diff --git a/evc-sumo/src/resources/evc_sumo_cfg.json b/evc-sumo/src/resources/evc_sumo_cfg.json index 65a27c9b..bf9ba81b 100644 --- a/evc-sumo/src/resources/evc_sumo_cfg.json +++ b/evc-sumo/src/resources/evc_sumo_cfg.json @@ -2,11 +2,11 @@ "controllers": [ { "controllerId": 1, - "controllerCfgPath": "resources/278.cfg", + "controllerCfgPath": "resources/test_3.cfg", "start_time": null, "https_port": 0, "web_port": 0, - "snmp_port": 0, + "snmp_port": 5050, "harness_port": 0, "controller_speed": null, "sumoTlId": 278, diff --git a/evc-sumo/src/resources/test_3.cfg b/evc-sumo/src/resources/test_3.cfg new file mode 100644 index 0000000000000000000000000000000000000000..21ca27094b86dfb4d12b6432381d4c2f9676584e GIT binary patch literal 147474 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 If&b3_ALM#|*8l(j literal 0 HcmV?d00001 From fd98b550780dd0a15ad5e2fe49206295272cd8d6 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 24 May 2023 17:44:53 -0400 Subject: [PATCH 119/124] Fix evc config spat forwarding (#141) # PR Details ## Description ## Related Issue ## Motivation and Context ## How Has This Been Tested? ## Types of changes - [ ] Defect fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that cause existing functionality to change) ## Checklist: - [ ] 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** document. [CARMA Contributing Guide](Contributing.md) - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. --- evc-sumo/src/resources/test_3.cfg | Bin 147474 -> 147474 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/evc-sumo/src/resources/test_3.cfg b/evc-sumo/src/resources/test_3.cfg index 21ca27094b86dfb4d12b6432381d4c2f9676584e..f8a10db4d12e65777b1e50caa6d7261a8d559582 100644 GIT binary patch delta 54 zcmbQ#z&WXbbAk-Zk9Bz|lO3y~8>_cgGYT$bS;NA>ynVt##w8*g8c!I2fSqZY6r;=b JhHZ?KIsqM~5?TNN delta 54 zcmbQ#z&WXbbAk+u*wnm~$&OXgjn!ML83h-zaJg|YZlAD_aft|r!Ab@oU}u^p#ptrV JVH@M5P5|TX5Yzww From 59c94194a7bf335ac14aacd8fbcfa3992c927907 Mon Sep 17 00:00:00 2001 From: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> Date: Thu, 25 May 2023 11:42:25 -0400 Subject: [PATCH 120/124] set center coordinates as 0,0 --- .../resources/scenarios/Town04/scenario_config.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/scenario_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/scenario_config.json index c70915d4..2103391b 100755 --- a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/scenario_config.json +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/scenario_config.json @@ -5,12 +5,12 @@ "randomSeed": 212323853, "projection": { "centerCoordinates": { - "latitude": 52.63, - "longitude": 13.56 + "latitude": 0, + "longitude": 0 }, "cartesianOffset": { - "x": 503.02, - "y": 423.76 + "x": 0, + "y": 0 } }, "network": { From 2c4ffff655209197819fa5238664285ea481731a Mon Sep 17 00:00:00 2001 From: Cheng Yuan <84340069+chengyuan0124@users.noreply.github.com> Date: Thu, 25 May 2023 11:42:29 -0400 Subject: [PATCH 121/124] Update Town04.db --- .../scenarios/Town04/application/Town04.db | Bin 419840 -> 462848 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/application/Town04.db b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/application/Town04.db index 627afa8dec3af4e6e94762085b60676e07cda12e..dbdf4a4c0bf9e399d82c44a9ebbefe9404ec9d72 100644 GIT binary patch delta 168396 zcmeFa2T)W?*DlQD91I|8qexId6fmNK8x<8LNX7^TOa#S@ahL#RMK_8$Vh+cE&Y(ae z4442>0WpK3qJoMEvv#sVeinh9+UTb%+4tqV_YtKn4Yn`+Q zIfn%KO`kj2ddke%@e}4+cQ=!pF&JiE*4AcbW|r_b3;zD~pLBzB_s*7PU0YU5WM9l= z{Jv(gSXqJesXo=*p1O zGa0SufPg_EQeG=JH-<7|?u3bPlLyY59v3}*#?(PGXUv&CXYQO4Q~uWy*`VOT140K| zhYlF%H`w}r#jPFaB-fTSi)BgMFfH4Ul<-SD>ER=6X@56M$WL)gWC-cUZjCf@uxAM9_1$ODXB~ReGZ>aMlM%<@ zL3b7~xbzFgGzNzbWTr9LXP-G)Fw9wCGrqxY68K}vs#vmjvZ}xB;am+y`By?)vpwl5 zp1rJFS2v-tZmpTDR`%(?s5^|Vm02t7WR3>iJ!RizpJZ=kFJ(_<4`p{`H)Yplg)%J5 zlAV>EkR6ikm2H=8lC77ml&NJ)Wea4pV7MmBqGY3FAu@m2P}u-kKUq(ii_B4!APN@s z5LpPn2=58Ag}a1F!ihp}VMiff@JdiDI4MXK%ocD~-}n#sid_C4zKS2sAHsLw z3wiatQr;=vM&4XrIM1EehWms2h?~dV$JKDBaDBK=TruYjr;Kxklg63P8OiC(LF`}b zYIZ*RAbSNnhV94h%9gXejZv#perf2Jp1Ba<iRkLK5{M2Q5NLUo@#v+^)I z3(z;_=ebE}feFyxP-tjrOQ8+L;5>x`LvF`7r9 z8mh$$!5O9qCjHB(zI{efl^sG92O zba_hKg%1IJVbdaJ8k$9*DynDBdM|qN6F{HIe_gFd2_{tc7q+SGd3@)$OSnlNUE<+8 zCJ@au!TP0?`=Jf%7F)dr;fKzTIn`)}3Dup%Ho=X^j{gdT#|`s+>x1G=sD266$8&t= zep#g;`#|gyFqigf_nmeM#hB!D$0@tEZ&Htir-4y5hc0DV zqv$pm_sa!5RJN3psV&}D1ZJ-YRrQOgzA;IKSAMxc8}AJoRX!I@B+!LaKgO+s^q@6>-rde@_O8tYKGV2D z_-uO|aG)`2ajY3`x>=L)skbE>Pe?AN`jnq>bg1Hhcn<^AZpz|otu`QZZl5c;|MO)E00!^g)ZMC~JW>S-(QNr^t=AyACRCj>tTqssg z3^RxBF5b`I6phCGK^4YTy@2v?ouB)8RK7uV{syHL8chgiP|h_goFZ>|f$*Ylr_}Gz zC=;rmO;I+2xBge}86MpCR=6b^X+m}TVMq8WwOKpMAgh{n@5ZJUA@C}D8S*m;p8rIJbl*Mx$lPv#-rDG~r4IQL=tnBVM`E^$84T#PW$a~^XgGoX zqJow3&GtXRYRB&2$@~F42fVPOXSmzmydF z*HkF;&BWFY9RO98Z93=ajl2l>6%}e}btYr-CV&^!&(HEi6oI~^Lc930oc&>^t(;>W z?H@-+h8la2udMpug*=I>@2QZ4IxpXj%b@Cog zN1*ShkZpD&qdk8DdUS1g-~iN@K;Kd!C*PNCeQFEnz70-#&rH;ZfZtFd1($p({({Y? za$DMOcW>lQpiimLzh)kOd1N%8-klyCt)gAS44ZPL$DGPC4IL0>>`sqE`slf+Cm~l& zg*NWjTpC>lRh>WNu1-Zg2=p-(#`_fMQTPc^drEgkj=B@*BPy)j(BAlp1)x@|&)lg; z-3YXf3LW^+*(?v1Vr8ty;9$=+$c=zsP@yB^LmxgH4)COr9mC%tR|0)bg~tC}s?CJ` zvU1Ft7OyjC>u_UtLh=}jG`fGdu{*vW-kPVOE`;1&Ds26vs*RR6aZ^Rph>OFoAZG%t zqQVYZteW!;T!MVvEw_2Wq^_uIbtP*naw5=5Dy-ad*VTE@or>zk zH)eUJAx8qPpu%1UIege!0B}{)lMA89fk4ZtaAx7^Nh?1BdUMWa&JtQ4Vdz1{)pr7a zH`I}+`j86iv9;Oo2|b|a=68<&v`6*?`hW`a^(|=4@ds3Dd&q7Dyruu`g`K}og-zJG z*cXh~?K<`muiGcx&2bRNgf4$D!Fvc3$5bzBuJW;gZOCn6{ieq0UalfDr z1X@akudhCH+5I-48EaHelaMummQdk`&Nb}%46nY5o!0yCR(gD(xdL|YicQ5goR6b+ zMBVFD_?=7rHn*G$=(_Q5g|*0vK(A5ZZ<_+nAMpcpxuoqP2ZRXpDiy)+GT~wEP(T$~ zLtn~KTLQg8MOaUoC$%{VXyW9#9uaHWwE5Q?nDvedUOfHj&69m$3!cAwXqq2tO^Dv2 z!rlB6+m3)0Q;{Es(*0=HAmadCsB@`)iCPg=%c$`2>E;tou%YVNB_nc2A`1d7q#~wY zj`Co)0d%5@>mG;92~&+m}1m| zfb*${v-RIvegZG2!mso+m57=X=p`zmxHjkYjRZgkZ+tb#12rSiJSyVZ1Ld`Su%0UV zN7xR@rmcew>#3r5_i6W6(EWpropqI+Oh8CV$mLKY-RusWEOLUX4zEA*Qjvr}FH;fj zTUQL}c@NN8&Gr-?Kw<(dq9TG^=M*#=25WjwNv}90BG7DVFsp$Fwa%zEsLfVBJG zv51saR4XStl7Ou@Tpc&xNsvTKMk*wJ5-&*)iGu`54WHr{x{tZRf4!DR{GZlXzjgp}ao4&O9rgjK|`BRd5@) z)!f_MB5oG-&++1P=XB(>=7?b?y=OmTSFx|NbJ?fa8SHfSD)v%#0(%lWoIQ-~!FFNW zu+7|`W`W? zxZ#JQAsu$Wux0q{4}x^)Uc<^AvbztYgLfJZe7v_yAsw_;0O`PV!)7sH69ZDOjq#9D zsUyumcmH+6Amh2l9a4`~T_EkZ0zuk0#RAeknpTjyt1JxXQW1hWGnmZ}U>56O_NKwC zO@P_zC+jP7kXgvs()ZG8X{j_@dQ`eanj)PmohS{I_Lp{*BB@aFMex93o%>tUQ{hA6=jQ# zinfSSL~}(GMS-IJB4<$>5nuROSS!3G%oCmz?hvjLCJLtrLxlr{uEKUgvEZAajyx7) zPhzAeZeYt5DX#Kx4uiqqIXLjVX1hG!KQ82rJj_^f;|9koVtL?FSz(4e)L2qh#_<|e zY%S}(qo+K?SaS0w$7@Q@(0i{sZ;%HYOK#obc*Pw4;xQ+`ULIsDsi@$1&FfLI`;z}@ zd7!c6&K-`|+66rmb*mNqewVeS4uicHBU@zp6~D|8U+YtpPYa$jSKogL3>^cDWOHPjvXaAS$BE$`3cgYU|R8B1(z zSY8R4hZlcoFHy*Sj71$fu)JnSvuhnf&dP@xOWL=GN?RP|d*eIFhZsxRwPSnvZC~M+ z7@?I9HkRDI%lXs9(W~X&#*%yYpc{rJR`TS7j3xK)bN)0jB2hljSn}Wj$JoTx_nqa6 z0mh;yPySXts$TA8EU~g;dxeZ!?JlZ{l2gVKgxFp~Ztc?f|BjXSHJsOsLWT(7u4`Y$J zImgQ@x+*)c{+hgxu|yzXc?A_s2yYs3Pws9k;qy6OL9IV;ozkaIx{hYsD42iAdm0fo zo9*TEGt0_NTEP45o?X~QV^+1bfZ{$wKl2@>E}CC!_&y&_trXUyJ&z7fyhFt|c{^jtuV3sxc2hXA_jXxC$0s}G$XN3G zH`{2sC6@x_ZH*;TDbs7faKV`g=VGL7jFw{?EO+aBZFhNVV-1Of>D6QJVzU+svE0&F zA{Mi~x}_ajQ8+tB-pW`a5`q00uG7Abu9qt;j735rC@L5!Fw=dIn;T1d^x$}n;~q^E zzt5MqG?sMl&hd&n{5JpE(R_IeV@X2;G+A1y;kMW;Z*DAk^M?7yVy8RFn;A>qzGeNf zkMyKmZY+8Cj`PPF{+vko#bL; z$;Xd>dUiRWwOnK@`Sj`Up84&O3yme8KmXma;5%}Gv81^8?~Y}CmGe0Y!#-M4@^{bH zR?2zCN~NV7uY`olV_&V?CFdGT+}wD^omILuRL(J$xVrNG?5sl^=zPsEI$!Zd=PSI_Wa$3~8h^09>#xQY)Zd-zBdl_a)aQ7lAnJkgSOW z-Vi4l3-h+0q_d=rL@53SZrFWs3DAY(;+;Si7K>xVqr^Vq-r`Q+h;c=qM9+Y#6#_-b z5T%LKqB){Tq7cy_QFoDzsJVzKY!uc2F~GvVgu8_s{)6i!Woj*^a9=UBi^p?UvY2La z5+&|7V>iN^_y12ITcKdsn8C2LBv0#2{DN|A_DUuwP ztOAP%NL(e&#h=Ah;tS#(;$`CT;(=m2ux_2`n&`M_-9MMB|6HzOxC{E{a`m6fRhS0< zT(0VM|6H#A9klY#<*MmK{ro?dtN&cCR{nFj`p@O+KbNcjT(15Xm#fr<`c;XDXL@Vs zuiJf(nkDU{6L%|-X&H5?;Ieu5wqapuTXSQv|KbzG)&|q z5((>tC2*ax9GFK+i#?ksLFw>!5b=M$%rqvdSjEQC9@eh|?6ll_Q|*?ZVZ z_C)p|wjG#W+T|IeXx>0NivtnepOi8B!8qv$}iO zsknNZ*-&rXR9G4Le7OKAiMZKZ37-DFq9i|OBuFnSJyhHQDapXuyV&X08~db}H$b}h z>$f?Fk#ar}z>zD&F`cw|FV5`)@eN6H8mj4m;_B^euAiJhLvIn55S06z6};AM~|Vq**X;?Sw@Q;M=7GNDXy+Pt*G1EeIPV{>^p zCb){_oD%^L&)7KIYXwpg;jy|3>{Mr!x^{9lXg{=h(%^SU8E2Y(1^sYnpw+NTiy^R~ zOgDYs##*F|HNm-j9OJ#9q1|*?6}NY}w2m6)g_L9vZ8>&&Z8yqlFvL6;?s8k;5RH^X z6m5Y!4&A&c(eCwX(7vy~SQ?0wL>O%@2gl5uS|FX}4%$!8a=qwHH^v(bUYJ(AS}>lL z&oE@lj$HbBLPxt8Gq;a^y6!Hg1B{u%)rw=Pk#zhF!?={u+*X_Xka8l?dKQjZz4>Tr zTq)?!o%;PESayO5*4@NT--fssr$Izwp{fmzGDAut47Q**4n1BR>b24alyBSDth@>- zi7?n)HjY`E9#>N)0p$v9{pS7|NJ#|3>dJ6uvk-kq3Pb=FCYC%dw?j%I61JcR4y_*@ z5XXh!yE1iHZKttFIo8y+Tn3Igw8_3ObR%fLzV7keB&7U17S<37tGj_ab0>^=?E?{< zg)^sph&nM6DT!d%f}S|^PW>^}pcv4;x-Z+g8Yzir*jy%#*}AFDxegwFTmL=&8rY2t zhb_gO#WU_N=mxu8;S|)e-C??Brg13dj9XRDrz58sGmB^GJMihH(+t+Xeaj}OMvs(4 z@N3Q|96Rjf8)-{eeC1ZdPCbBWPe#8MV5h+Al|M$@hldD!XLWCelw|m8K6bK>I=15D z7l6&yJ+v1iB@zEx;D*CGMcQ9qcLzGCh#eMmN{N(20Bp`L9CMSgwP_v%z}#iH5XS#Xz z2I&7yn(C&FY4TyU75yn|@)dT89}s;pVCB#AiLoF6!*zJK)4pCEp_!EExRM5H7F zWp&pK)_*g4Ana*{L*^_E{75%WHyV7BmFS_Emd6+}x1W35^NglljG02uT@SMKbbv8a zHoX1!@x^p}jB#AP?OxHh94U#QSKSrt)Y#4HXg2J)g*~45T!6+CQLhDEaoD&8OnKuD zXngRJ(XkFlIndPiobNd1>w~K?vu8saJ`}uLnTnJ|+^g=gVXrI6I$*b?#0{J_l4lo!Zyd(oPf=Y2#76k#9_w@zed(B2JM|HC70IFHL-@FD7WZu zW}ZMtMq3DUAldBgmyGfB(rDwRj&+Ils-Ck5BxS?L+=YctM8Fm} z;IOPhFWb@Y;3@4}FY$rho(O@}XW?-EoKDh~5CVJq$)zbukS1ba^*S8(JM!5$NoRl+ zcQf`~u188D3RaJCxNF1A&QDgr!`pNne;+nvA_`WYi^Dtb-P5H80*}k(H5z*_q$I*% z^_OtC`(xV?Pa)*F+_}0VUV@Y^rk?5ZaClFPbJ5jP0JdH18I_WalwAq@A`Z9I&T6p{ z-Zi&BHUBmn<|Ppu#g~Rs5 z{EqD53-HE>QDv~|iFjB2UpQ>T(Pjy+j{&S$d8YgVh}#g>IfKKp`Fk%fPXl~^*-{w{ zJQ4A#KaImq#(11>ng{Uq7O!WAAte#=sy~6lW@nVoiGX!=J5hN@z6B}U6UvX{unE6U zG}pfcI6)E{B?03SQLy?WIIN3cuIh*k@Cb*=`E^K1gu&_$JvlsoNSF_DjyMBu7^7u9xkiZr$2Hhka}a_ij*ccdgDSM@t7 ztLbN2Z-2l9*#CF!{4}H_LRa;BaOh0c=q{Q@cv7<#+0Po0Qcm=4Hx7+BVfSPHOMn#u z{6c*kDP;t{jp~p#=)|NY5N=%JxqkT2JftK7T=hGs_WrMratk43y2NK}V;pQ?M1-q; zJ7vA2b=1Sz7Xcpls?|DBPK3DX(-|)rza)N z>%OjlB}3AxRH}2i!|b8iwE#;WJxD4;N|INtr<`A`PcQk<9AII6vHVgSCF5TK*6hCE z`oC=BvK`h-X^32*oCxD+MhwIn%GEsXutPe0E>vR1vpkS#pj}HKgbrv|zHGlt1D{6@ zk#&;sr7xw|q{pOdr122(_TOb)ivNmsjS+YVtoXn9kNJ80z3?IBB)&J_p3mjI;9Y@m z$W^>po*%CZPs)AEE#sbssK+_nP;O7I1*eHq#mVAq<1FTk<#=-1K}2IU`x1K}Tg{GU z4`z2{^H_DPtE{7})ex-c&+4jR$(Zk$H<@RcG=$5BF?%svF}^VFGA=N-!&j8!82uUT z8D>aDd{vl#4~GP$$ZlH#aVS}NXl&F&q#`~m)bgpI%JPXHE;_?EH_<~v#feBY*JNHT zj|!^$@$SKyCjgIKG(d465UJ*v@cb$qVmW2mh8i_!U+VSnqz0)7My2IaLElp%*z;b2 zc<4zNY6nsgj7rO)f+Z)PvC>8X>~~E*qythBj7rOoe|NAH*2x{u){mY-+XzObWm3W3uc+mTT>9HK(XS2P3@9pQCT3UIAre!rk?#CM1KXEIbiDiB!aghgr?B;|cgM)C!#b%TI=l zeSZq6qD=#)uf}0IDz=;t$}9SXDHQozkZO_%&uWGp&&KRsHX#DUt7>QYmLU~E#j@ns z@%*gmhrbU5_=X~RUm8*oR80Q_hwao4jGi|FwEI0j5y3zzf{JCyuw(YJkGsCU2I-=1 z2UFAN#-)a>r^4F_-F2nq%M6*z*(DdA*3mA@ECjibJDYUBWDgy%%&_-dKHB!374(;& zP+21Ec=P1Adl9z)-gBcFYZ_7!3@S^A9q(MMdwE|DaC(e=z!#(<7?i#Ohg%J>&oti- z-pHL{?#YLdieOOshdAuM#HG7c1+-CNyuTZ&ia!52PAm(w@b@PMi)JYi}#S2lbaSMb^xD#b~0+1 zA5szAOJ9z|Z5k@x$H5D)yyVANUrVGSxR?Gm4)0V|acTEtfc5Ef=7V;Edg)6I?s?*w z>U{w3_WhI|NV_dEj)m*oUI`=VKx3vn{R&@@M9=(#bm|o0nN6?zgB;#+^L-|LVv%t{ zS>?wjz~c#iq`!f~yLanXl*k1A2X8#JeTGzhOs3KOz)n^X=dIrv#=GUbHGSNXiuhts z_Z>UgP96|k&H`8@-!nB6sd^LA->{S8?BazZ2Lr5N6kO^u7pVwVr7y=QzUWJ2MFdO=f>r6hV5e@&t;G#%0DklJWfIt(U{$&% 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