diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index fe5ecef7..ba3eb807 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -2,7 +2,8 @@ name: Docker build
on:
pull_request:
types: [opened, synchronize, reopened]
-
jobs:
docker:
uses: usdot-fhwa-stol/actions/.github/workflows/docker.yml@main
+ with:
+ runner: ubuntu-latest-16-cores
diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml
index abdd8134..09bf8bbf 100644
--- a/.github/workflows/dockerhub.yml
+++ b/.github/workflows/dockerhub.yml
@@ -2,8 +2,8 @@ name: Docker Hub build
on:
push:
branches:
- - "develop"
- - "master"
+ - develop
+ - master
- "release/*"
tags:
- "carma-system-*"
@@ -14,3 +14,5 @@ jobs:
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
+ with:
+ runner: ubuntu-latest-16-cores
diff --git a/README.md b/README.md
index d19a2b73..423087f9 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-| CI Build Status | Docker Build | Docker Hub Push | Sonar Code Quality |
+| Docker Image Build (develop) | Docker Image Build (release) | Sonar Code Quality | Sonar Workflow |
|----------------------|---------------------|---------------------|---------------------|
-|[![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)|
+|[![Docker Hub build](https://github.com/usdot-fhwa-stol/cdasim/actions/workflows/dockerhub.yml/badge.svg?branch=develop)](https://github.com/usdot-fhwa-stol/cdasim/actions/workflows/dockerhub.yml) | [![Docker Hub build](https://github.com/usdot-fhwa-stol/cdasim/actions/workflows/dockerhub.yml/badge.svg?branch=master)](https://github.com/usdot-fhwa-stol/cdasim/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)| [![CI: Run tests](https://github.com/usdot-fhwa-stol/cdasim/actions/workflows/ci.yml/badge.svg)](https://github.com/usdot-fhwa-stol/cdasim/actions/workflows/ci.yml)|
# 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.
diff --git a/co-simulation/bundle/pom.xml b/co-simulation/bundle/pom.xml
index 024b663c..f83b3d30 100644
--- a/co-simulation/bundle/pom.xml
+++ b/co-simulation/bundle/pom.xml
@@ -144,6 +144,11 @@
mosaic-infrastructure
${mosaic.version}
+
+ org.eclipse.mosaic
+ mosaic-carma-cloud
+ ${mosaic.version}
+
@@ -262,4 +267,4 @@
-
\ No newline at end of file
+
diff --git a/co-simulation/bundle/src/assembly/resources/etc/logback.xml b/co-simulation/bundle/src/assembly/resources/etc/logback.xml
index 4ab190bd..1cd6764d 100755
--- a/co-simulation/bundle/src/assembly/resources/etc/logback.xml
+++ b/co-simulation/bundle/src/assembly/resources/etc/logback.xml
@@ -98,6 +98,13 @@
%date %-5level %C{0}:%line - %msg%n
+
+ UTF-8
+ ${logDirectory}/CarmaCloud.log
+
+ %date %-5level %C{0}:%line - %msg%n
+
+
UTF-8
${logDirectory}/Environment.log
@@ -209,6 +216,10 @@
+
+
+
+
diff --git a/co-simulation/bundle/src/assembly/resources/etc/runtime.json b/co-simulation/bundle/src/assembly/resources/etc/runtime.json
index 4350f1eb..28c175e1 100644
--- a/co-simulation/bundle/src/assembly/resources/etc/runtime.json
+++ b/co-simulation/bundle/src/assembly/resources/etc/runtime.json
@@ -121,6 +121,20 @@
],
"javaClasspathEntries": []
},
+ {
+ "id": "carma-cloud",
+ "classname": "org.eclipse.mosaic.fed.carmacloud.ambassador.CarmaCloudMessageAmbassador",
+ "configuration": "carma-cloud_config.json",
+ "priority": 50,
+ "host": "local",
+ "port": 0,
+ "deploy": false,
+ "start": false,
+ "subscriptions": [
+ "V2xMessageReception"
+ ],
+ "javaClasspathEntries": []
+ },
{
"id": "mapping",
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/carma-cloud/carma-cloud_config.json
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/infrastructure/infrastructure_config.json
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04/carma-cloud/carma-cloud_config.json
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 2103391b..03a52f54 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
@@ -34,7 +34,8 @@
"sumo": true,
"carla": true,
"carma": true,
- "infrastructure": true
+ "infrastructure": true,
+ "carma-cloud": true
}
}
diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/application/Town04.db b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/application/Town04.db
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/application/Town04.db
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/application/Town04.db
diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carla/bridge.sh b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/carla/bridge.sh
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carla/bridge.sh
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/carla/bridge.sh
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_carma_cloud/carla/carla_config.json
similarity index 63%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carla/carla_config.json
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/carla/carla_config.json
index aff6f191..7f16c9ba 100644
--- a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carla/carla_config.json
+++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/carla/carla_config.json
@@ -1,7 +1,7 @@
{
"updateInterval": 100,
"carlaUE4Path": "/opt/carla/",
- "bridgePath": "/opt/carma-simulation/scenarios/Town04_test/carla; bridge.sh",
+ "bridgePath": "/opt/carma-simulation/scenarios/Town04_carma_cloud/carla; bridge.sh",
"carlaConnectionPort": 8913,
"carlaCDASimAdapterUrl":"http://127.0.0.1:8090/RPC2"
}
diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/carma-cloud/carma-cloud_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/carma-cloud/carma-cloud_config.json
new file mode 100644
index 00000000..196145f7
--- /dev/null
+++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/carma-cloud/carma-cloud_config.json
@@ -0,0 +1,3 @@
+{
+ "updateInterval": 100
+}
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_carma_cloud/carma/carma_config.json
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/carma/carma_config.json
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/carma/carma_config.json
diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/infrastructure/infrastructure_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/infrastructure/infrastructure_config.json
new file mode 100644
index 00000000..196145f7
--- /dev/null
+++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/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_carma_cloud/mapping/mapping_config.json
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/mapping/mapping_config.json
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/mapping/mapping_config.json
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_carma_cloud/ns3/ns3_config.json
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/ns3/ns3_config.json
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/ns3/ns3_config.json
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_carma_cloud/ns3/ns3_federate_config.xml
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/ns3/ns3_federate_config.xml
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/ns3/ns3_federate_config.xml
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_carma_cloud/output/output_config.xml
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/output/output_config.xml
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/output/output_config.xml
diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/scenario_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/scenario_config.json
similarity index 93%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/scenario_config.json
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/scenario_config.json
index 2103391b..03a52f54 100755
--- a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/scenario_config.json
+++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/scenario_config.json
@@ -34,7 +34,8 @@
"sumo": true,
"carla": true,
"carma": true,
- "infrastructure": true
+ "infrastructure": true,
+ "carma-cloud": 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_carma_cloud/sumo/Town04.net.xml
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.net.xml
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/sumo/Town04.net.xml
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_carma_cloud/sumo/Town04.rou.xml
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.rou.xml
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/sumo/Town04.rou.xml
diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.sumocfg b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/sumo/Town04.sumocfg
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/Town04.sumocfg
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/sumo/Town04.sumocfg
diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/detector.xml b/co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/sumo/detector.xml
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/detector.xml
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/sumo/detector.xml
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_carma_cloud/sumo/sumo_config.json
similarity index 100%
rename from co-simulation/bundle/src/assembly/resources/scenarios/Town04_test/sumo/sumo_config.json
rename to co-simulation/bundle/src/assembly/resources/scenarios/Town04_carma_cloud/sumo/sumo_config.json
diff --git a/co-simulation/fed/mosaic-carma-cloud/pom.xml b/co-simulation/fed/mosaic-carma-cloud/pom.xml
new file mode 100644
index 00000000..4c71b10c
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-cloud/pom.xml
@@ -0,0 +1,105 @@
+
+
+
+ 4.0.0
+
+
+ org.eclipse.mosaic
+ mosaic-parent
+ 22.1-SNAPSHOT
+ ../../pom.xml
+
+
+ mosaic-carma-cloud
+ CARMA Cloud Ambassador
+
+
+
+ org.eclipse.mosaic
+ mosaic-rti-api
+ ${mosaic.version}
+
+
+ org.eclipse.mosaic
+ mosaic-objects
+ ${mosaic.version}
+
+
+ org.eclipse.mosaic
+ mosaic-objects
+ ${mosaic.version}
+ test-jar
+ test
+
+
+ gov.dot.fhwa.saxton
+ mosaic-carma-utils
+ ${mosaic.version}
+
+
+ org.eclipse.mosaic
+ mosaic-interactions
+ ${mosaic.version}
+
+
+ org.eclipse.mosaic
+ mosaic-utils
+ ${mosaic.version}
+ test-jar
+ test
+
+
+ org.eclipse.mosaic
+ mosaic-geomath
+ ${mosaic.version}
+ test-jar
+ test
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ junit
+ junit
+ 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
+
+
+
+
+
+ skip-carma-cloud-tests
+
+
+
+
+
+ maven-surefire-plugin
+
+ true
+
+
+
+
+
+
+
+
diff --git a/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudInstance.java b/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudInstance.java
new file mode 100644
index 00000000..0dc5333e
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudInstance.java
@@ -0,0 +1,93 @@
+/*
+ * 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.carmacloud.ambassador;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.HttpURLConnection;
+import com.google.gson.Gson;
+import gov.dot.fhwa.saxton.TimeSyncMessage;
+
+
+/**
+ * CarmaCloudInstance class represents a physical instance of an
+ * CARMA Cloud in the simulated environment.
+ * It contains information about the instance such as its id and target address
+ */
+public class CarmaCloudInstance
+{
+ // unique simulation identifier for the CARMA Cloud instance
+ private final String carmaCloudId;
+ // URL endpoint where to send simulation time sync messages
+ private final String carmaCloudUrl;
+
+
+ /**
+ * Constructor for CarmaCloudInstance
+ *
+ * @param sId the ID of the CARMA Cloud instance.
+ * @param sUrl the receive time synchronization message port of the infrastructure.
+ */
+ public CarmaCloudInstance(String sId, String sUrl)
+ {
+ carmaCloudId = sId;
+ carmaCloudUrl = sUrl;
+ }
+
+
+ /**
+ * Returns the URL endpoint of the CARMA Cloud instance
+ *
+ * @return String URL endpoint of the CARMA Cloud instance
+ */
+ public String getCarmaCloudUrl()
+ {
+ return carmaCloudUrl;
+ }
+
+
+ /**
+ * Returns the ID of the CARMA Cloud instance
+ *
+ * @return String the ID of the CARMA Cloud instance
+ */
+ public String getCarmaCloudId()
+ {
+ return carmaCloudId;
+ }
+
+
+ /**
+ * Sends time sync data to the CARMA Cloud Instance
+ *
+ * @param oMsg the JSON time sync state to transmit
+ * @throws IOException if there is an issue with the underlying URL connection
+ */
+ public void sendTimeSyncMsg(TimeSyncMessage oMsg)
+ throws IOException
+ {
+ HttpURLConnection oHttp = (HttpURLConnection)new URL(carmaCloudUrl).openConnection();
+ oHttp.setRequestMethod("POST");
+ oHttp.setDoOutput(true);
+ try (DataOutputStream oOut = new DataOutputStream(oHttp.getOutputStream()))
+ {
+ oOut.write(new Gson().toJson(oMsg).getBytes());
+ }
+ if (oHttp.getResponseCode() != 200)
+ throw new IOException(String.format("CARMA Cloud failure %d %s", oHttp.getResponseCode(), oHttp.getResponseMessage()));
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudInstanceManager.java b/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudInstanceManager.java
new file mode 100644
index 00000000..94e55253
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudInstanceManager.java
@@ -0,0 +1,99 @@
+/*
+ * 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.carmacloud.ambassador;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import gov.dot.fhwa.saxton.TimeSyncMessage;
+
+
+/**
+ * Session management class for Infrastructure instances communicating with
+ * MOSAIC.
+ *
+ * This class is responsible for managing instances of CARMA Cloud 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 CarmaCloudInstanceManager
+{
+ private final Map managedInstances = new HashMap<>();
+ private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+
+ /**
+ * Register a new CARMA Cloud instance with the MOSAIC system.
+ *
+ * This method takes a CarmaCloudRegistrationMessage, converts it to a
+ * CarmaCloudInstance, and adds it to the managedInstances map
+ * if it is not already present.
+ *
+ * @param registration The CarmaCloudRegistrationMessage to be registered.
+ *
+ */
+ public void onNewRegistration(CarmaCloudRegistrationMessage registration)
+ {
+ if (!managedInstances.containsKey(registration.getId()))
+ {
+ CarmaCloudInstance tmp = new CarmaCloudInstance(registration.getId(), registration.getUrl());
+ managedInstances.put(registration.getId(), tmp);
+ }
+ else
+ log.warn("Registration message received for already registered CARMA Cloud with ID: {}", registration.getId());
+ }
+
+
+ /**
+ * This function is used to send out encoded time step 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 time step
+ * from the ambassador side
+ * @throws IOException
+ */
+ public void onTimeStepUpdate(TimeSyncMessage message)
+ throws IOException
+ {
+ if (managedInstances.isEmpty())
+ log.debug("There are no registered instances");
+ else
+ {
+ log.debug("onTimeStepUpdate instance count {}", managedInstances.size());
+ for (CarmaCloudInstance currentInstance : managedInstances.values())
+ {
+ currentInstance.sendTimeSyncMsg(message);
+ log.debug("Sent time message to CARMA-Cloud " + message.toString());
+ }
+ }
+ }
+
+
+ /**
+ * Returns Map of managed CARMA Cloud instances with CARMA Cloud ID as the
+ * String Key.
+ *
+ * @return map of managed CARMA Cloud instances.
+ */
+ public Map getManagedInstances()
+ {
+ return managedInstances;
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudMessageAmbassador.java b/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudMessageAmbassador.java
new file mode 100644
index 00000000..3bc42cc1
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudMessageAmbassador.java
@@ -0,0 +1,208 @@
+/*
+ * 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.carmacloud.ambassador;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.mosaic.fed.carmacloud.configuration.CarmaCloudConfiguration;
+import org.eclipse.mosaic.interactions.communication.V2xMessageReception;
+import org.eclipse.mosaic.lib.util.objects.ObjectInstantiation;
+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.rti.TIME;
+
+import gov.dot.fhwa.saxton.TimeSyncMessage;
+
+/**
+ * Implementation of a {@link AbstractFederateAmbassador} for CarmaCloud
+ * message ambassador.
+ */
+public class CarmaCloudMessageAmbassador extends AbstractFederateAmbassador
+{
+ /**
+ * Simulation time.
+ */
+ long currentSimulationTime;
+
+ /**
+ * CarmaCloudMessageAmbassador configuration file.
+ */
+ CarmaCloudConfiguration carmaCloudConfiguration;
+
+ private CarmaCloudRegistrationReceiver carmaCloudRegistrationReceiver;
+ private final CarmaCloudInstanceManager carmaCloudInstanceManager = new CarmaCloudInstanceManager();
+ private int timeSyncSeq;
+
+
+ /**
+ * Create a new {@link CarmaCloudMessageAmbassador} object.
+ *
+ * @param ambassadorParameter includes parameters for the
+ * CarmaCloudMessageAmbassador.
+ */
+ public CarmaCloudMessageAmbassador(AmbassadorParameter ambassadorParameter) throws RuntimeException
+ {
+ super(ambassadorParameter);
+ try // load configuration file
+ {
+ carmaCloudConfiguration = new ObjectInstantiation<>(CarmaCloudConfiguration.class, log)
+ .readFile(ambassadorParameter.configuration);
+ }
+ catch (InstantiationException e)
+ {
+ log.error("Configuration object could not be instantiated: ", e);
+ }
+
+ log.info("The update interval of CARMA Cloud message ambassador is {}.", carmaCloudConfiguration.updateInterval);
+
+ if (carmaCloudConfiguration.updateInterval <= 0L)
+ {
+ throw new RuntimeException("Invalid update interval for CARMA Cloud message ambassador, should be > 0.");
+ }
+ log.info("CARMA Cloud message ambassador is generated.");
+ }
+
+
+ /**
+ * This method is called to tell the federate the start time and the end time.
+ *
+ * @param startTime Start time of the simulation run in nanoseconds.
+ * @param endTime End time of the simulation run in nanoseconds.
+ * @throws InternalFederateException Exception is thrown if an error is occurred
+ * while execute of a federate.
+ */
+ @Override
+ public void initialize(long startTime, long endTime)
+ throws InternalFederateException
+ {
+ super.initialize(startTime, endTime);
+ currentSimulationTime = startTime;
+
+ carmaCloudRegistrationReceiver = new CarmaCloudRegistrationReceiver();
+ carmaCloudRegistrationReceiver.init();
+ new Thread(carmaCloudRegistrationReceiver).start();
+
+ try
+ {
+ rti.requestAdvanceTime(currentSimulationTime, 0, (byte) 1);
+ }
+ catch (IllegalValueException e)
+ {
+ log.error("Error during advanceTime request", e);
+ throw new InternalFederateException(e);
+ }
+ }
+
+
+ /**
+ * 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 (in nanoseconds) indicating the time to which the federate can
+ * advance its local time.
+ * @throws InternalFederateException
+ */
+ @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
+ log.debug("Process time advance grant from {} to {}.", currentSimulationTime, time);
+ if (time < currentSimulationTime)
+ {
+ return;
+ }
+
+ currentSimulationTime = time;
+ try
+ {
+ // Handle any new carmaCloud registration requests
+ List newRegistrations = carmaCloudRegistrationReceiver.getReceivedMessages();
+ for (CarmaCloudRegistrationMessage reg : newRegistrations)
+ {
+ log.info("Processing new registration request for {}.", reg.getId());
+ // Store new instance registration to carmaCloud instance manager
+ carmaCloudInstanceManager.onNewRegistration(reg);
+ }
+
+ timeSyncSeq += 1;
+ // nanoseconds to milliseconds for CarmaCloudTimeMessage
+ TimeSyncMessage timeSyncMessage = new TimeSyncMessage(currentSimulationTime / 1000000L, timeSyncSeq);
+ carmaCloudInstanceManager.onTimeStepUpdate(timeSyncMessage);
+
+ // Advance the simulation time
+ currentSimulationTime += carmaCloudConfiguration.updateInterval* TIME.MILLI_SECOND;
+
+ // Request the next time advance from the RTI
+ log.debug("Requesting timestep updated to {}.", currentSimulationTime);
+ rti.requestAdvanceTime(currentSimulationTime, 0, (byte) 2);
+ }
+ catch (IllegalValueException e)
+ {
+ throw new InternalFederateException(e);
+ }
+ catch (IOException e)
+ {
+ log.error("Error during updating timestep :", e);
+ }
+ }
+
+
+ /**
+ * Return whether this federate is time constrained. Is set if the federate is
+ * sensitive towards the correct ordering of events. The federate ambassador
+ * will ensure that the message processing happens in time stamp order. If set
+ * to false, interactions will be processed in receive order.
+ *
+ * @return {@code true} if this federate is time constrained, else
+ * {@code false}.
+ */
+ @Override
+ public boolean isTimeConstrained()
+ {
+ return true;
+ }
+
+
+ /**
+ * Return whether this federate is time regulating. Is set if the federate
+ * influences other federates and can prevent them from advancing their local
+ * time.
+ *
+ * @return {@code true} if this federate is time regulating, {@code false} else.
+ */
+ @Override
+ public boolean isTimeRegulating()
+ {
+ return false;
+ }
+
+
+ /**
+ * Test helper function to cleanup sockets and threads.
+ */
+ protected void close()
+ {
+ carmaCloudRegistrationReceiver.stop();
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudRegistrationMessage.java b/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudRegistrationMessage.java
new file mode 100644
index 00000000..82c5c25a
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudRegistrationMessage.java
@@ -0,0 +1,71 @@
+/*
+ * 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.carmacloud.ambassador;
+
+
+/**
+ * A message to be sent by CARMA Cloud instance when it registers with the
+ * carmacloud-mosaic ambassador
+ */
+public class CarmaCloudRegistrationMessage
+{
+ // unique simulation identifier for the CARMA Cloud instance
+ private final String id;
+ // URL endpoint where to send simulation time sync messages
+ private final String url;
+
+
+ /**
+ * Constructor for a CarmaCloudRegistrationMessage
+ *
+ * @param sId the ID of the CARMA Cloud instance.
+ * @param sUrl the receive time synchronization message port of the infrastructure.
+ */
+ public CarmaCloudRegistrationMessage(String sId, String sUrl)
+ {
+ id = sId;
+ url = sUrl;
+ }
+
+
+ /**
+ * Returns the URL endpoint of the CARMA Cloud instance
+ *
+ * @return String URL endpoint of the CARMA Cloud instance
+ */
+ public String getUrl()
+ {
+ return url;
+ }
+
+
+ /**
+ * Returns the ID of the CARMA Cloud instance
+ *
+ * @return String the ID of the CARMA Cloud instance
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ return String.format("CarmaCloudRegistrationMessage id %s url %s", id, url);
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudRegistrationReceiver.java b/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudRegistrationReceiver.java
new file mode 100644
index 00000000..1bc0834d
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudRegistrationReceiver.java
@@ -0,0 +1,139 @@
+/*
+ * 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.carmacloud.ambassador;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.google.gson.Gson;
+import java.io.DataInputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Worker thread Runnable for operating a listen socket to receive outbound
+ * CARMA Cloud Messages from CARMA Cloud instances
+ * This {@link Runnable} instance will operate a UDP socket to subscribe to
+ * packets from a CARMA Cloud adapter.
+ * Upon receiving a packet, it will be queued for the primary thread
+ * to process the data once it ticks to a simulation processing step
+ */
+public class CarmaCloudRegistrationReceiver implements Runnable
+{
+ private static final int LISTEN_PORT = 1617;
+ private final AtomicBoolean running = new AtomicBoolean(false);
+ private final Logger log = LoggerFactory.getLogger(this.getClass());
+ private final Queue rxQueue = new LinkedList<>();
+ private ServerSocket srvr;
+
+
+ /**
+ * Initialize the listen socket for messages from the CARMA Cloud instance adapter
+ *
+ * @throws RuntimeException if socket instantiation fails
+ */
+ public void init() throws RuntimeException
+ {
+ try
+ {
+ srvr = new ServerSocket(LISTEN_PORT);
+ }
+ catch (Exception oEx)
+ {
+ throw new RuntimeException("server socket instantiation failure", oEx);
+ }
+ }
+
+
+ /**
+ * The main method of the worker thread. Listens for incoming messages and
+ * queues them for processing on the primary thread.
+ */
+ @Override
+ public void run()
+ {
+ running.set(true);
+ try
+ {
+ while (running.get())
+ {
+ Socket oSock = srvr.accept();
+ DataInputStream oIn = new DataInputStream(oSock.getInputStream());
+
+ // parse message
+ CarmaCloudRegistrationMessage parsedMessage =
+ new Gson().fromJson(oIn.readUTF(), CarmaCloudRegistrationMessage.class);
+
+ // Enqueue message for processing on main thread
+ synchronized (rxQueue)
+ {
+ rxQueue.add(parsedMessage);
+ }
+ log.info("New CARMA Cloud instance '{}' received with CARMA Cloud Registration Receiver.", parsedMessage.getId());
+ }
+ }
+ catch (Exception oEx)
+ {
+ log.error("Error occurred", oEx);
+ }
+ }
+
+
+ /**
+ * Stop the runnable instance and close the listen socket.
+ */
+ public void stop()
+ {
+ running.set(false);
+ if (srvr != null)
+ {
+ try
+ {
+ srvr.close();
+ }
+ catch (Exception oEx)
+ {
+ log.error("Error occurred", oEx);
+ }
+ }
+ }
+
+
+ /**
+ * 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-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/configuration/CarmaCloudConfiguration.java b/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/configuration/CarmaCloudConfiguration.java
new file mode 100644
index 00000000..bbdaf761
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-cloud/src/main/java/org/eclipse/mosaic/fed/carmacloud/configuration/CarmaCloudConfiguration.java
@@ -0,0 +1,31 @@
+/*
+ * 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.carmacloud.configuration;
+
+import java.io.Serializable;
+import com.google.gson.annotations.JsonAdapter;
+import org.eclipse.mosaic.lib.util.gson.TimeFieldAdapter;
+
+
+/**
+ * The CARMA Cloud Message Ambassador configuration class.
+ */
+public class CarmaCloudConfiguration implements Serializable
+{
+ private static final long serialVersionUID = 1705520136000000000L;
+
+
+ @JsonAdapter(TimeFieldAdapter.LegacyMilliSeconds.class)
+ public Long updateInterval = 100L;
+}
diff --git a/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudInstanceManagerTest.java b/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudInstanceManagerTest.java
new file mode 100644
index 00000000..98300e91
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudInstanceManagerTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.carmacloud.ambassador;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.io.IOException;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+
+import gov.dot.fhwa.saxton.TimeSyncMessage;
+
+
+
+public class CarmaCloudInstanceManagerTest {
+
+ private CarmaCloudInstanceManager manager;
+ private CarmaCloudInstance instance1;
+ private static final String carmacloudId = "carma-cloud";
+ private static final String carmacloudUrl = "carma-cloud-url";
+
+ @Before
+ public void setUp() throws Exception {
+ manager = new CarmaCloudInstanceManager();
+ instance1 = mock(CarmaCloudInstance.class);
+ }
+
+ @Test
+ public void testOnNewRegistration() {
+ // Set up the registration object
+ CarmaCloudRegistrationMessage registration = new CarmaCloudRegistrationMessage(carmacloudId, carmacloudUrl);
+
+ // Call the onNewRegistration method with the registration object
+ manager.onNewRegistration(registration);
+
+ // Verify that the infrastructure instance was added to the manager
+ assertFalse(manager.getManagedInstances().isEmpty());
+ assertNotNull(manager.getManagedInstances().get(carmacloudId));
+ }
+
+ @Test
+ public void testOnTimeStepUpdate() throws IOException {
+ // replace registered CARMA Cloud instance with mock instance
+ manager.getManagedInstances().put(carmacloudId, instance1);
+
+ TimeSyncMessage message = new TimeSyncMessage(999L, 11);
+ manager.onTimeStepUpdate(message);
+ // Verify that all instances sendTimeSyncMsgs was called.
+ verify(instance1).sendTimeSyncMsg(message);
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudInstanceTest.java b/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudInstanceTest.java
new file mode 100644
index 00000000..5f6d74e5
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudInstanceTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.carmacloud.ambassador;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.BufferedInputStream;
+import java.net.InetSocketAddress;
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+import com.google.gson.Gson;
+import java.io.IOException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+
+
+import gov.dot.fhwa.saxton.TimeSyncMessage;
+
+public class CarmaCloudInstanceTest {
+
+ private CarmaCloudInstance instance;
+ private StringBuilder messageBuf = new StringBuilder();
+ private HttpServer oSrvr;
+
+ class TimeSyncHandler implements HttpHandler {
+ @Override
+ public void handle(HttpExchange oExch) throws IOException {
+ int nChar;
+ BufferedInputStream oIn = new BufferedInputStream(oExch.getRequestBody());
+ while ((nChar = oIn.read()) >= 0)
+ messageBuf.append((char)nChar);
+
+ oExch.sendResponseHeaders(200, -1L);
+ oExch.close();
+ }
+ }
+
+ @Before
+ public void setUp() throws IOException {
+ instance = new CarmaCloudInstance("carma-cloud", "http://localhost:8080/carmacloud/simulation");
+ oSrvr = HttpServer.create(new InetSocketAddress("localhost", 8080), 0);
+ HttpContext oCtx = oSrvr.createContext("/carmacloud/simulation");
+ oCtx.setHandler(new TimeSyncHandler());
+ oSrvr.start();
+ }
+
+ @After
+ public void tearDown() throws IOException {
+ oSrvr.stop(0);
+ }
+
+ @Test
+ public void testGetterSetterConstructor() {
+ // Test getters and constructor for setting and retrieving class members
+ assertEquals("carma-cloud", instance.getCarmaCloudId());
+ assertEquals("http://localhost:8080/carmacloud/simulation", instance.getCarmaCloudUrl());
+ }
+
+ @Test
+ public void testSendTimeSyncMsg() throws IOException {
+ // Test SendTimeSyncMsg method
+ TimeSyncMessage test_msg = new TimeSyncMessage(999L, 11);
+ instance.sendTimeSyncMsg(test_msg);
+
+ assertTrue(messageBuf.length() > 0);
+ if (messageBuf.length() > 0)
+ {
+ TimeSyncMessage parsedMessage = new Gson().fromJson(messageBuf.toString(), TimeSyncMessage.class);
+ assertEquals(999L, parsedMessage.getTimestep());
+ assertEquals(11, parsedMessage.getSeq());
+ }
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudMessageAmbassadorTest.java b/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudMessageAmbassadorTest.java
new file mode 100644
index 00000000..bde0faa1
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudMessageAmbassadorTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.carmacloud.ambassador;
+
+import org.eclipse.mosaic.lib.util.junit.TestFileRule;
+import org.eclipse.mosaic.rti.api.IllegalValueException;
+import org.eclipse.mosaic.rti.api.InternalFederateException;
+import org.eclipse.mosaic.rti.api.Interaction;
+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.eclipse.mosaic.rti.TIME;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+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.mockito.internal.util.reflection.FieldSetter;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TemporaryFolder;
+
+
+/**
+ * Tests for {@link CarmaCloudMessageAmbassador}.
+ */
+public class CarmaCloudMessageAmbassadorTest {
+
+ private final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private final TestFileRule testFileRule = new TestFileRule(temporaryFolder).basedir("carmacloud");
+
+ @Rule
+ public RuleChain chain = RuleChain.outerRule(temporaryFolder).around(testFileRule);
+
+ /**
+ * {@link RtiAmbassador} mock.
+ */
+ private RtiAmbassador rtiMock;
+
+ private CarmaCloudMessageAmbassador ambassador;
+ /**
+ * {@link CarmaCloudInstanceManager} mock.
+ */
+ private CarmaCloudInstanceManager instanceManagerMock;
+ /**
+ * {@link CarmaCloudRegistrationReceiver} mock.
+ */
+ private CarmaCloudRegistrationReceiver receiverMock;
+
+ private ArrayList registrationMessages;
+
+ @Before
+ public void setUp() throws IOException, NoSuchFieldException, InternalFederateException, IllegalValueException {
+ // Initialize Mocks
+ rtiMock = mock(RtiAmbassador.class);
+ FederateDescriptor handleMock = mock(FederateDescriptor.class);
+ instanceManagerMock = mock(CarmaCloudInstanceManager.class);
+ receiverMock = mock(CarmaCloudRegistrationReceiver.class);
+ File workingDir = temporaryFolder.getRoot();
+ CLocalHost testHostConfig = new CLocalHost();
+ testHostConfig.workingDirectory = workingDir.getAbsolutePath();
+
+ // Mock methods
+ when(handleMock.getHost()).thenReturn(testHostConfig);
+ when(handleMock.getId()).thenReturn("carmacloud");
+ CarmaCloudRegistrationMessage message = new CarmaCloudRegistrationMessage("carmacloud-id", "carmacloud-url");
+ registrationMessages = new ArrayList<>();
+ registrationMessages.add(message);
+ when(receiverMock.getReceivedMessages()).thenReturn(registrationMessages);
+
+ // Set mocks as ambassador members through reflection or setters
+ ambassador = new CarmaCloudMessageAmbassador(new AmbassadorParameter("carmacloud",
+ temporaryFolder.newFile("carmacloud/carma-cloud_config.json")));
+
+ ambassador.setRtiAmbassador(rtiMock);
+ ambassador.setFederateDescriptor(handleMock);
+ FieldSetter.setField(ambassador, ambassador.getClass().getDeclaredField("carmaCloudRegistrationReceiver"), receiverMock);
+ FieldSetter.setField(ambassador, ambassador.getClass().getDeclaredField("carmaCloudInstanceManager"), instanceManagerMock);
+ }
+
+ @Test
+ public void testInitialize() throws InternalFederateException, IllegalValueException {
+ // Test initialize method
+ ambassador.initialize(0, 100 * TIME.SECOND);
+ verify(rtiMock, times(1)).requestAdvanceTime(0L, 0L, ((byte) 1));
+ // cleanup threads and sockets
+ ambassador.close();
+ }
+
+ @Test
+ public void testProcessTimeAdvanceGrant() throws InternalFederateException, IllegalValueException, NoSuchFieldException, SecurityException
+ {
+ //Test processTimeAdvanceGrant for CARMA Cloud Registration
+ ambassador.processTimeAdvanceGrant(100000000L);
+ // Verify received messages were attempted to be pulled from CARMA Cloud Registration Receiver mock
+ verify(receiverMock, times(1)).getReceivedMessages();
+ verify(rtiMock, times(1)).requestAdvanceTime(200000000L, 0L, ((byte) 2));
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudRegistrationMessageTest.java b/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudRegistrationMessageTest.java
new file mode 100644
index 00000000..b48449b4
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudRegistrationMessageTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.carmacloud.ambassador;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class CarmaCloudRegistrationMessageTest {
+ @Test
+ public void testGetterSettersConstructor() {
+ // Test Constructor
+ CarmaCloudRegistrationMessage message = new CarmaCloudRegistrationMessage(
+ "carma-cloud",
+ "http://someaddress:8080/carmacloud/simulation");
+
+ // Test Getter
+ assertEquals("carma-cloud", message.getId());
+ assertEquals("http://someaddress:8080/carmacloud/simulation", message.getUrl() );
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudRegistrationReceiverTest.java b/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudRegistrationReceiverTest.java
new file mode 100644
index 00000000..2a0341a6
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-cloud/src/test/java/org/eclipse/mosaic/fed/carmacloud/ambassador/CarmaCloudRegistrationReceiverTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.carmacloud.ambassador;
+
+ import static org.junit.Assert.assertEquals;
+ import static org.mockito.Mockito.mock;
+ import static org.mockito.Mockito.when;
+ import org.mockito.internal.util.reflection.FieldSetter;
+
+ import java.io.ByteArrayInputStream;
+ import java.io.ByteArrayOutputStream;
+ import java.io.DataOutputStream;
+ import java.net.InetAddress;
+ import java.net.InetSocketAddress;
+ import java.net.ServerSocket;
+ import java.net.Socket;
+ import java.util.List;
+
+ import org.junit.After;
+ import org.junit.Before;
+ import org.junit.Test;
+
+ public class CarmaCloudRegistrationReceiverTest {
+
+ @Test
+ public void testMessageReceive() throws Exception {
+ // Define a test message in JSON format
+ String json = "{\"id\":\"carma-cloud\",\"url\":\"http://someaddress:8080/carmacloud/simulation\"}";
+ ByteArrayOutputStream oBytes = new ByteArrayOutputStream();
+ DataOutputStream oOut = new DataOutputStream(oBytes);
+ oOut.writeUTF(json);
+ oOut.close(); // flush contents
+
+ ServerSocket MockServer = mock(ServerSocket.class);
+ Socket MockSock = mock(Socket.class);
+
+ // mock socket server, socket, and inputstream
+ when(MockServer.accept()).thenReturn(MockSock);
+ when(MockSock.getInputStream()).thenReturn(new ByteArrayInputStream(oBytes.toByteArray()));
+
+ // Setup the registration receiver
+ CarmaCloudRegistrationReceiver receiver = new CarmaCloudRegistrationReceiver();
+ FieldSetter.setField(receiver, receiver.getClass().getDeclaredField("srvr"), MockServer);
+ receiver.run(); // multi-threading not needed here
+
+ // Verify that the message was received correctly
+ List msgs = receiver.getReceivedMessages();
+ assertEquals(1, msgs.size());
+
+ CarmaCloudRegistrationMessage msg = msgs.get(0);
+ assertEquals("carma-cloud", msg.getId());
+ assertEquals("http://someaddress:8080/carmacloud/simulation", msg.getUrl());
+ }
+ }
+
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 59aaee9d..e53d049b 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
@@ -96,10 +96,11 @@ public V2xMessageTransmission onV2XMessageTx(InetAddress sourceAddr, CarmaV2xMes
sender.getCarlaRoleName(), sender.getLocation()).viaChannel(AdHocChannel.CCH);
// TODO: Get maximum broadcast radius from configuration file.
MessageRouting routing = messageRoutingBuilder.geoBroadCast(new GeoCircle(sender.getLocation(), 300));
- log.debug("Generating V2XMessageTransmission interaction sim time: {}, sender id: {}, location: {}, payload: {}",
+ log.debug("Generating V2XMessageTransmission interaction sim time: {}, sender id: {}, location: {}, type: {}, payload: {}",
time,
sender.getCarmaVehicleId(),
sender.getLocation(),
+ txMsg.getType(),
txMsg.getPayload()
);
return new V2xMessageTransmission( time, new ExternalV2xMessage(routing,
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 eec5c94f..5215000c 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
@@ -174,6 +174,7 @@ public synchronized void processTimeAdvanceGrant(long time) throws InternalFeder
List> newMessages = v2xMessageReceiver.getReceivedMessages();
for (Tuple msg : newMessages) {
V2xMessageTransmission msgInt = carmaInstanceManager.onV2XMessageTx(msg.getA(), msg.getB(), currentSimulationTime);
+ log.debug("Generated a message with ID: {}", msgInt.getMessageId());
SimulationKernel.SimulationKernel.getV2xMessageCache().putItem(currentSimulationTime, msgInt.getMessage());
rti.triggerInteraction(msgInt);
}
diff --git a/co-simulation/pom.xml b/co-simulation/pom.xml
index 8844c0c0..cb71b377 100644
--- a/co-simulation/pom.xml
+++ b/co-simulation/pom.xml
@@ -72,6 +72,7 @@
fed/mosaic-carla
fed/mosaic-carma
fed/mosaic-infrastructure
+ fed/mosaic-carma-cloud
test/mosaic-integration-tests
diff --git a/docker/checkout.sh b/docker/checkout.sh
deleted file mode 100755
index 06391349..00000000
--- a/docker/checkout.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/bash
-
-# 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.
-
-# 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 carma-system-4.5.0 --depth 1
- git clone https://github.com/usdot-fhwa-stol/carma-utils.git ${dir}/src/carma-utils --branch carma-system-4.5.0 --depth 1
-fi