diff --git a/co-simulation/fed/mosaic-application/pom.xml b/co-simulation/fed/mosaic-application/pom.xml
index ddeb0881..f1cadd9f 100644
--- a/co-simulation/fed/mosaic-application/pom.xml
+++ b/co-simulation/fed/mosaic-application/pom.xml
@@ -54,6 +54,12 @@
${mosaic.version}
test-jar
test
+
+
+ org.eclipse.mosaic
+ mosaic-application
+
+
diff --git a/co-simulation/fed/mosaic-carma-messenger/pom.xml b/co-simulation/fed/mosaic-carma-messenger/pom.xml
new file mode 100644
index 00000000..78d8c6bb
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-messenger/pom.xml
@@ -0,0 +1,138 @@
+
+
+
+ 4.0.0
+
+
+ org.eclipse.mosaic
+ mosaic-parent
+ 22.1-SNAPSHOT
+ ../../pom.xml
+
+
+ mosaic-carma-messenger
+ CARMA Messenger Ambassador
+
+
+
+ org.eclipse.mosaic
+ mosaic-rti-api
+ ${mosaic.version}
+
+
+ org.eclipse.mosaic
+ mosaic-objects
+ ${mosaic.version}
+
+
+ gov.dot.fhwa.saxton
+ mosaic-carma-utils
+ ${mosaic.version}
+
+
+ com.google.code.gson
+ gson
+ 2.8.9
+
+
+ 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
+
+
+ org.eclipse.mosaic
+ mosaic-objects
+ ${mosaic.version}
+ test-jar
+ test
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+ org.eclipse.mosaic
+ mosaic-application
+ 22.1-SNAPSHOT
+ compile
+
+
+ javax.xml.bind
+ 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
+
+
+ org.eclipse.mosaic
+ mosaic-utils
+ ${mosaic.version}
+ compile
+
+
+ org.eclipse.mosaic
+ mosaic-carma
+ 22.1-SNAPSHOT
+ compile
+
+
+ org.eclipse.mosaic
+ mosaic-common-utils
+ 22.1-SNAPSHOT
+ compile
+
+
+
+
+
+ skip-carma-messenger-tests
+
+
+
+
+
+ maven-surefire-plugin
+
+ true
+
+
+
+
+
+
+
+
diff --git a/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerInstance.java b/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerInstance.java
new file mode 100644
index 00000000..1134e8db
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerInstance.java
@@ -0,0 +1,63 @@
+/*
+ * 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.carmamessenger.ambassador;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+
+import org.eclipse.mosaic.lib.CommonUtil.ambassador.CommonInstance;
+
+
+public class CarmaMessengerInstance extends CommonInstance{
+
+ private String messengerEmergencyState;
+ private int rxBridgeMessagePort;
+
+ public CarmaMessengerInstance(String carmaMessengerVehicleId, String sumoRoleName, InetAddress targetAddress, int v2xPort, int timeSyncPort, String messengerEmergencyState, int rxBridgeMessagePort) {
+ super(carmaMessengerVehicleId, sumoRoleName, targetAddress, v2xPort, timeSyncPort);
+ this.messengerEmergencyState = messengerEmergencyState;
+ this.rxBridgeMessagePort = rxBridgeMessagePort;
+ }
+ /**
+ * Carma Messenger Emergency state
+ */
+
+ public String getMessengerEmergencyState() {
+ return messengerEmergencyState;
+ }
+
+ public void setMessengerEmergencyState(String messengerEmergencyState) {
+ this.messengerEmergencyState = messengerEmergencyState;
+ }
+
+ public int getRxBridgeMessagePort() {
+ return rxBridgeMessagePort;
+ }
+
+ public void setRxBridgeMessagePort(int rxBridgeMessagePort) {
+ this.rxBridgeMessagePort = rxBridgeMessagePort;
+ }
+
+ public void sendVehStatusMsgs(byte[] data) throws IOException {
+ if (super.rxMsgsSocket == null) {
+ throw new IllegalStateException("Attempted to send data before opening socket");
+ }
+
+ DatagramPacket packet = new DatagramPacket(data, data.length, super.getTargetAddress(), rxBridgeMessagePort);
+ super.rxMsgsSocket.send(packet);
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerInstanceManager.java b/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerInstanceManager.java
new file mode 100644
index 00000000..cbc930c5
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerInstanceManager.java
@@ -0,0 +1,111 @@
+/*
+ * 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.carmamessenger.ambassador;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.eclipse.mosaic.lib.CommonUtil.ambassador.CommonInstanceManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+import gov.dot.fhwa.saxton.TimeSyncMessage;
+
+public class CarmaMessengerInstanceManager extends CommonInstanceManager{
+
+ private static final int BRIDGE_TARGET_PORT = 5500;
+ 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
+ * @param registration The new instance's registration data
+ */
+ public void onNewRegistration(CarmaMessengerRegistrationMessage registration) {
+ super.setTargetPort(5600);
+ if (!managedInstances.containsKey(registration.getVehicleRole())) {
+ try {
+ newCarmaMessengerInstance(
+ registration.getVehicleId(),
+ registration.getVehicleRole(),
+ InetAddress.getByName(registration.getRxMessageIpAddress()),
+ registration.getRxMessagePort(),
+ registration.getRxTimeSyncPort(),
+ registration.getMessengerEmergencyState(),
+ registration.getRxBridgeMessagePort()
+ );
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ // log warning
+ log.warn("Received duplicate registration for vehicle " + registration.getVehicleRole());
+ }
+ }
+
+ /**
+ * 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
+ */
+ @Override
+ public void onTimeStepUpdate(TimeSyncMessage message) throws IOException {
+ if (managedInstances.size() == 0) {
+ log.debug("There are no registered instances");
+ }
+ else {
+ Gson gson = new Gson();
+ byte[] bytes = gson.toJson(message).getBytes();
+ for (CarmaMessengerInstance currentInstance : managedInstances.values()) {
+ log.debug("Sending CARMA Messenger instance {} at {}:{} time sync message for time {}!" ,
+ currentInstance.getVehicleId(),
+ currentInstance.getTargetAddress(),
+ currentInstance.getTimeSyncPort(),
+ currentInstance.getMessengerEmergencyState(),
+ message.getTimestep());
+ currentInstance.sendTimeSyncMsg(bytes);
+ }
+ }
+ }
+
+
+ /**
+ * Helper function to configure a new CARMA Platform instance object upon registration
+ * @param carmaMessengerVehId The CARMA Platform vehicle ID (e.g. it's license plate number)
+ * @param sumoRoleName The Role Name associated with the CARMA Platform's ego vehicle in CARLA
+ * @param targetAddress The IP address to which received simulated V2X messages should be sent
+ * @param v2xPort The port to which received simulated V2X messages should be sent
+ * @param timeSyncPort The port to which to send time sync messages.
+ */
+ private void newCarmaMessengerInstance(String carmaMessengerVehId, String sumoRoleName, InetAddress targetAddress, int v2xPort, int timeSyncPort, String messengerEmergencyState, int rxBridgeMessagePort) {
+ CarmaMessengerInstance tmp = new CarmaMessengerInstance(carmaMessengerVehId, sumoRoleName, targetAddress, v2xPort, timeSyncPort, messengerEmergencyState, rxBridgeMessagePort);
+ try {
+ tmp.bind();
+ log.info("New CARMA Messenger instance '{}' registered with CARMA Instance Manager.", sumoRoleName);
+ } catch (IOException e) {
+ log.error("Failed to bind CARMA Messenger instance with ID '{}' to its RX message socket: {}",
+ sumoRoleName, e.getMessage());
+ log.error("Stack trace:", e);
+ throw new RuntimeException(e);
+ }
+ managedInstances.put(sumoRoleName, tmp);
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerMessageAmbassador.java b/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerMessageAmbassador.java
new file mode 100644
index 00000000..ff93d4fe
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerMessageAmbassador.java
@@ -0,0 +1,52 @@
+/*
+ * 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.carmamessenger.ambassador;
+
+import org.eclipse.mosaic.lib.CommonUtil.ambassador.CommonMessageAmbassador;
+import org.eclipse.mosaic.lib.CommonUtil.configuration.CommonConfiguration;
+import org.eclipse.mosaic.lib.util.objects.ObjectInstantiation;
+import org.eclipse.mosaic.rti.api.parameters.AmbassadorParameter;
+
+public class CarmaMessengerMessageAmbassador extends CommonMessageAmbassador{
+
+
+ /**
+ * Create a new {@link CarmaMessengerMessageAmbassador} object.
+ *
+ * @param ambassadorParameter includes parameters for the
+ * CarmaMessageAmbassador.
+ */
+ public CarmaMessengerMessageAmbassador(AmbassadorParameter ambassadorParameter) {
+ super(ambassadorParameter, CarmaMessengerRegistrationMessage.class, CommonConfiguration.class);
+
+ try {
+ // Read the CARMA message ambassador configuration file
+ commonConfiguration = new ObjectInstantiation<>(CommonConfiguration.class, log)
+ .readFile(ambassadorParameter.configuration);
+ } catch (InstantiationException e) {
+ log.error("Configuration object could not be instantiated: ", e);
+ }
+
+ log.info("The update interval of CARMA message ambassador is " + commonConfiguration.updateInterval + " .");
+
+ // Check the CARMA update interval
+ if (commonConfiguration.updateInterval <= 0) {
+ throw new RuntimeException("Invalid update interval for CARMA message ambassador, should be >0.");
+ }
+ log.info("CARMA message ambassador is generated.");
+ }
+
+}
diff --git a/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerRegistrationMessage.java b/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerRegistrationMessage.java
new file mode 100644
index 00000000..4516f15c
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerRegistrationMessage.java
@@ -0,0 +1,55 @@
+/*
+ * 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.carmamessenger.ambassador;
+
+import org.eclipse.mosaic.lib.CommonUtil.ambassador.CommonRegistrationMessage;
+
+public class CarmaMessengerRegistrationMessage extends CommonRegistrationMessage{
+
+ private String messengerEmergencyState;
+ private int rxBridgeMessagePort;
+
+ public CarmaMessengerRegistrationMessage(String carmaMessengerVehicleId, String sumoVehicleRole, String rxMessageIpAddress,
+ int rxMessagePort, int rxTimeSyncPort, String messengerEmergencyState, int rxBridgeMessagePort) {
+ super(carmaMessengerVehicleId, sumoVehicleRole, rxMessageIpAddress, rxMessagePort, rxTimeSyncPort);
+
+ this.messengerEmergencyState = messengerEmergencyState;
+ this.rxBridgeMessagePort = rxBridgeMessagePort;
+ }
+
+ public String getMessengerEmergencyState() {
+ return messengerEmergencyState;
+ }
+
+ public void setMessengerEmergencyState(String messengerEmergencyState) {
+ this.messengerEmergencyState = messengerEmergencyState;
+ }
+
+ public int getRxBridgeMessagePort(){
+ return rxBridgeMessagePort;
+ }
+
+ public void setRxBridgeMessagePort(int rxBridgeMessagePort){
+ this.rxBridgeMessagePort = rxBridgeMessagePort;
+ }
+
+ @Override
+ public String toString() {
+ return "CarmaMessengerRegistrationMessage [carmaMessengerVehicleId=" + super.getVehicleId() + ", sumoVehicleRole=" + super.getVehicleRole()
+ + ", rxMessageIpAddress=" + super.getRxMessageIpAddress() + ", rxMessagePort=" + super.getRxMessagePort()
+ + ", rxTimeSyncPort=" + super.getRxTimeSyncPort() + ", MessengerEmergencyState=" + messengerEmergencyState + ", rxBridgeMessagePort=" + rxBridgeMessagePort +"]";
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerRegistrationReceiver.java b/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerRegistrationReceiver.java
new file mode 100644
index 00000000..57ee5b9c
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-messenger/src/main/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerRegistrationReceiver.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.carmamessenger.ambassador;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.util.LinkedList;
+import java.util.Queue;
+
+import org.eclipse.mosaic.lib.CommonUtil.ambassador.CommonRegistrationReceiver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+public class CarmaMessengerRegistrationReceiver extends CommonRegistrationReceiver{
+
+ public CarmaMessengerRegistrationReceiver(Class type) {
+ super(type);
+ //TODO Auto-generated constructor stub
+ }
+
+ private Queue rxQueue = new LinkedList<>();
+ private DatagramSocket listenSocket = null;
+ private static final int listenPort = 1715;
+ private boolean running = true;
+ private static final int UDP_MTU = 1535;
+ private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+
+
+ @Override
+ public void run() {
+ byte[] buf = new byte[UDP_MTU];
+ 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(), 0, msg.getLength());
+ log.debug("Registration JSON received: {}", receivedPacket);
+ Gson gson = new Gson();
+ CarmaMessengerRegistrationMessage parsedMessage = gson.fromJson(receivedPacket, CarmaMessengerRegistrationMessage.class);
+
+ // Enqueue message for processing on main thread
+ synchronized (rxQueue) {
+ log.info("New CARMA messenger instance '{}' received with CARMA messenger Registration Receiver.", parsedMessage.getVehicleId());
+ rxQueue.add(parsedMessage);
+ }
+ }
+ }
+
+}
diff --git a/co-simulation/fed/mosaic-carma-messenger/src/test/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerInstanceManagerTest.java b/co-simulation/fed/mosaic-carma-messenger/src/test/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerInstanceManagerTest.java
new file mode 100644
index 00000000..e5b5d8ab
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-messenger/src/test/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerInstanceManagerTest.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.carmamessenger.ambassador;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.mosaic.lib.junit.IpResolverRule;
+import org.eclipse.mosaic.lib.objects.addressing.IpResolver;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import static org.mockito.Mockito.mock;
+import org.mockito.internal.util.reflection.FieldSetter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CarmaMessengerInstanceManagerTest {
+ private CarmaMessengerInstanceManager manager;
+ private CarmaMessengerInstance instance1;
+ private CarmaMessengerInstance instance2;
+ private CarmaMessengerInstance instance3;
+ 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 Logger log = LoggerFactory.getLogger(this.getClass());
+
+ /**
+ * Rule to initialize {@link IpResolver} Singleton.
+ */
+ @Rule
+ public IpResolverRule ipResolverRule = new IpResolverRule();
+
+ @Before
+ public void setUp() throws Exception {
+ manager = new CarmaMessengerInstanceManager();
+ instance1 = mock(CarmaMessengerInstance.class);
+ instance2 = mock(CarmaMessengerInstance.class);
+ instance3 = mock(CarmaMessengerInstance.class);
+ Map managedInstances = new HashMap<>();
+ managedInstances.put("instance1", instance1);
+ managedInstances.put("instance2", instance2);
+ managedInstances.put("instance3", instance3);
+
+ // Set private instance field to mock using reflection
+ FieldSetter.setField(manager, manager.getClass().getSuperclass().getDeclaredField("managedInstances"), managedInstances);
+ }
+
+ @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";
+ String emergencyState = "MockState";
+ int rxBridgeMessagePort = 5600;
+
+ // Mock the behavior of the registration object
+ CarmaMessengerRegistrationMessage registration = new CarmaMessengerRegistrationMessage(
+ infrastructureId,
+ infrastructureId,
+ ipAddressString,
+ rxMessagePort,
+ timeSyncPort,
+ emergencyState,
+ rxBridgeMessagePort);
+ // Ensure checkIfRegistered returns false for infrastructure ID before registering
+ assertFalse( manager.checkIfRegistered(infrastructureId) );
+
+ // Call the onNewRegistration method with the mocked registration object
+ manager.onNewRegistration(registration);
+
+ // Verify that the infrastructure instance was added to the manager
+ assertTrue( manager.checkIfRegistered(infrastructureId) );
+ // Ensure checkIfRegistered returns false for other Ids
+ assertFalse( manager.checkIfRegistered(infrastructureId + "something") );
+ }
+
+}
diff --git a/co-simulation/fed/mosaic-carma-messenger/src/test/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerInstanceTest.java b/co-simulation/fed/mosaic-carma-messenger/src/test/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerInstanceTest.java
new file mode 100644
index 00000000..04f0cf0b
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-messenger/src/test/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerInstanceTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.carmamessenger.ambassador;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import org.mockito.internal.util.reflection.FieldSetter;
+
+public class CarmaMessengerInstanceTest{
+ /**
+ * Mock Datagram socket
+ */
+ private DatagramSocket socket;
+ private CarmaMessengerInstance instance;
+ /**
+ * Mock InetAddress
+ */
+ private InetAddress address;
+
+ @Before
+ public void setup() throws NoSuchFieldException {
+ //init mocks
+ address = mock(InetAddress.class);
+ socket = mock(DatagramSocket.class);
+
+ //init infra instance
+ instance = new CarmaMessengerInstance(
+ "MockID",
+ "MockRolename",
+ address,
+ 3456,
+ 7890,
+ "MockState",
+ 5600
+ );
+ FieldSetter.setField(instance, instance.getClass().getSuperclass().getDeclaredField("rxMsgsSocket"), socket);
+ }
+
+ @Test
+ public void testGetterSetterConstructor() {
+ assertEquals("MockState", instance.getMessengerEmergencyState());
+ assertEquals(5600, instance.getRxBridgeMessagePort());
+ instance.setMessengerEmergencyState("NewState");
+ assertEquals("NewState", instance.getMessengerEmergencyState());
+ instance.setRxBridgeMessagePort(5700);
+ assertEquals(5700, instance.getRxBridgeMessagePort());
+ }
+
+ @Test
+ public void testSendVehStatusMsg() throws IOException {
+ // Test SendV2xMsg method
+ String test_msg = "test message";
+ instance.sendVehStatusMsgs(test_msg.getBytes());
+ // ArgumentCaptor to capture parameters passed to mock on method calls
+ ArgumentCaptor packet = ArgumentCaptor.forClass(DatagramPacket.class);
+ // Verify socket.send(DatagramPacket packet) is called and capture packet
+ // parameter
+ verify(socket, times(1)).send(packet.capture());
+
+ // Verify parameter members
+ assertArrayEquals(test_msg.getBytes(), packet.getValue().getData());
+ assertEquals(instance.getRxBridgeMessagePort(), packet.getValue().getPort());
+ assertEquals(address, packet.getValue().getAddress());
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-messenger/src/test/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerMessageAmbassadorTest.java b/co-simulation/fed/mosaic-carma-messenger/src/test/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerMessageAmbassadorTest.java
new file mode 100644
index 00000000..82376c04
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-messenger/src/test/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerMessageAmbassadorTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.carmamessenger.ambassador;
+
+import java.io.File;
+import java.io.IOException;
+
+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.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TemporaryFolder;
+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;
+
+public class CarmaMessengerMessageAmbassadorTest {
+ private final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private final TestFileRule testFileRule = new TestFileRule(temporaryFolder).basedir("carma")
+ .with("carma_config.json", "/carma_config.json");
+
+ @Rule
+ public RuleChain chain = RuleChain.outerRule(temporaryFolder).around(testFileRule);
+
+ private RtiAmbassador rtiMock;
+
+ private CarmaMessengerMessageAmbassador ambassador;
+
+ @Before
+ public void setup() throws IOException {
+
+ rtiMock = mock(RtiAmbassador.class);
+
+ FederateDescriptor handleMock = mock(FederateDescriptor.class);
+
+ File workingDir = temporaryFolder.getRoot();
+
+ CLocalHost testHostConfig = new CLocalHost();
+
+ testHostConfig.workingDirectory = workingDir.getAbsolutePath();
+
+ when(handleMock.getHost()).thenReturn(testHostConfig);
+
+ when(handleMock.getId()).thenReturn("carma");
+
+ ambassador = new CarmaMessengerMessageAmbassador(
+ new AmbassadorParameter("carma", testFileRule.get("carma_config.json")));
+
+ ambassador.setRtiAmbassador(rtiMock);
+
+ ambassador.setFederateDescriptor(handleMock);
+ }
+
+ @Test
+ public void initialize() throws Throwable {
+
+ // RUN
+ ambassador.initialize(0, 100 * TIME.SECOND);
+ // ASSERT
+ verify(rtiMock, times(1)).requestAdvanceTime(eq(0L), eq(0L), eq((byte) 1));
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-messenger/src/test/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerRegistrationMessageTest.java b/co-simulation/fed/mosaic-carma-messenger/src/test/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerRegistrationMessageTest.java
new file mode 100644
index 00000000..7c111f4d
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-messenger/src/test/java/org/eclipse/mosaic/fed/carmamessenger/ambassador/CarmaMessengerRegistrationMessageTest.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.carmamessenger.ambassador;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class CarmaMessengerRegistrationMessageTest {
+ @Test
+ public void testGetterSettersConstructor() {
+
+ CarmaMessengerRegistrationMessage message = new CarmaMessengerRegistrationMessage(
+ "MockID",
+ "MockRole",
+ "127.0.0.1",
+ 5678,
+ 1234,
+ "MockState",
+ 5600);
+ // Test Getter
+ assertEquals("MockState", message.getMessengerEmergencyState());
+ assertEquals(5600, message.getRxBridgeMessagePort());
+
+ message.setMessengerEmergencyState("NewState");
+ message.setRxBridgeMessagePort(5700);
+
+
+ assertEquals("NewState", message.getMessengerEmergencyState());
+ assertEquals(5700, message.getRxBridgeMessagePort());
+ }
+}
diff --git a/co-simulation/fed/mosaic-carma-messenger/src/test/resources/carma_config.json b/co-simulation/fed/mosaic-carma-messenger/src/test/resources/carma_config.json
new file mode 100644
index 00000000..f12b548c
--- /dev/null
+++ b/co-simulation/fed/mosaic-carma-messenger/src/test/resources/carma_config.json
@@ -0,0 +1,25 @@
+{
+ "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"
+}
diff --git a/co-simulation/fed/mosaic-carma/pom.xml b/co-simulation/fed/mosaic-carma/pom.xml
index 155576a3..e4ec0832 100644
--- a/co-simulation/fed/mosaic-carma/pom.xml
+++ b/co-simulation/fed/mosaic-carma/pom.xml
@@ -97,6 +97,12 @@
jaxb-runtime
2.3.2
+
+ org.eclipse.mosaic
+ mosaic-common-utils
+ 22.1-SNAPSHOT
+ compile
+
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 04523ac6..c922586c 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
@@ -16,117 +16,17 @@
package org.eclipse.mosaic.fed.carma.ambassador;
-import org.eclipse.mosaic.lib.geo.GeoPoint;
+import org.eclipse.mosaic.lib.CommonUtil.ambassador.CommonInstance;
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* Connection manager and data object to associate with a single CARMA Platform instance in XIL
*/
-public class CarmaInstance {
- private String carmaVehicleId;
- private String carlaRoleName;
- private DatagramSocket rxMsgsSocket = null;
-
- private InetAddress targetAddress;
- private int v2xPort;
- private int timeSyncPort;
- private GeoPoint location = GeoPoint.ORIGO;
+public class CarmaInstance extends CommonInstance{
public CarmaInstance(String carmaVehicleId, String carlaRoleName, InetAddress targetAddress, int v2xPort, int timeSyncPort ) {
- this.carmaVehicleId = carmaVehicleId;
- this.carlaRoleName = carlaRoleName;
- this.targetAddress = targetAddress;
- this.v2xPort = v2xPort;
- this.timeSyncPort = timeSyncPort;
- }
-
- 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;
- }
-
-
- public int getV2xPort() {
- return v2xPort;
- }
-
- public void setV2xPort(int v2xPort) {
- this.v2xPort = v2xPort;
- }
-
- public int getTimeSyncPort() {
- return timeSyncPort;
- }
-
- public void setTimeSyncPort(int timeSyncPort) {
- this.timeSyncPort = timeSyncPort;
+ super(carmaVehicleId, carlaRoleName, targetAddress, v2xPort, timeSyncPort);
}
- /**
- * Sends the V2X message to the CARMA Platform 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 sendV2xMsgs(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, v2xPort);
-
- rxMsgsSocket.send(packet);
- }
- /**
- * Sends the time sync messages to the CARMA Platform to synchronize ros clock with simulation clock.
- * @param data The binary data encoding of json time sync message
- * @throws IOException If there is an issue with the underlying socket object or methods
- */
- public void sendTimeSyncMsg(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);
- }
-
- /**
- * Connects the sockt to receive messages from the CARMA Platform instance
- * @throws IOException If the socket creation fails
- */
- public void bind() throws IOException {
- rxMsgsSocket = new DatagramSocket();
- }
-
- public String getCarmaVehicleId() {
- return carmaVehicleId;
- }
-
- public void setCarmaVehicleId(String carmaVehicleId) {
- this.carmaVehicleId = carmaVehicleId;
- }
-
- public String getCarlaRoleName() {
- return carlaRoleName;
- }
-
- public void setCarlaRoleName(String carlaRoleName) {
- this.carlaRoleName = carlaRoleName;
- }
}
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 e53d049b..a0442386 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,156 +16,20 @@
package org.eclipse.mosaic.fed.carma.ambassador;
-import gov.dot.fhwa.saxton.CarmaV2xMessage;
-import gov.dot.fhwa.saxton.TimeSyncMessage;
-
-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 com.google.gson.Gson;
-
import java.io.IOException;
import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.HashMap;
-import java.util.Map;
+
+import org.eclipse.mosaic.lib.CommonUtil.ambassador.CommonInstanceManager;
/**
* Session management class for CARMA Platform instances communicating with MOSAIC
*/
-public class CarmaInstanceManager {
- private Map managedInstances = new HashMap<>();
-
- // 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
- * @param registration The new instance's registration data
- */
- public void onNewRegistration(CarmaRegistrationMessage registration) {
- if (!managedInstances.containsKey(registration.getCarlaVehicleRole())) {
- try {
- newCarmaInstance(
- registration.getCarmaVehicleId(),
- registration.getCarlaVehicleRole(),
- InetAddress.getByName(registration.getRxMessageIpAddress()),
- registration.getRxMessagePort(),
- registration.getRxTimeSyncPort()
- );
- } catch (UnknownHostException e) {
- throw new RuntimeException(e);
- }
- } else {
- // log warning
- log.warn("Received duplicate registration for vehicle " + registration.getCarlaVehicleRole());
- }
- }
- /**
- * Method to be invoked when CARMA Ambassador receives a V2X message from CARMA Platform. Creates
- * V2xMessageTransmission interaction to be sent on the MOSIAC RTI.
- * @param sourceAddr the ip address of the CARMA Platform instance that sent the message.
- * @param txMsg The V2X Message received.
- * @param time The timestamp at which the interaction occurs.
- * @throws IllegalStateException if sourceAddr does not match any address in the managed instances.
- */
- public V2xMessageTransmission onV2XMessageTx(InetAddress sourceAddr, CarmaV2xMessage txMsg, long time) {
- CarmaInstance sender = null;
- // Find the CarmaInstance with sourceAddr.
- for (CarmaInstance ci : managedInstances.values()) {
- if (ci.getTargetAddress().equals(sourceAddr)) {
- sender = ci;
- break;
- }
- }
- // Unregistered instance attempting to send messages
- if (sender == null) {
- throw new IllegalStateException("Unregistered CARMA Platform instance attempting to send messages via MOSAIC");
- }
- AdHocMessageRoutingBuilder messageRoutingBuilder = new AdHocMessageRoutingBuilder(
- 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: {}, type: {}, payload: {}",
- time,
- sender.getCarmaVehicleId(),
- sender.getLocation(),
- txMsg.getType(),
- txMsg.getPayload()
- );
- return new V2xMessageTransmission( time, new ExternalV2xMessage(routing,
- new ExternalV2xContent( time, sender.getLocation(), txMsg.getPayload())));
- }
-
- /**
- * Callback to be invoked when the simulation emits a VehicleUpdates event, used to track the location of CARMA
- * Platform instances in this manager.
- * @param vui The vehicle update information
- */
- public void onVehicleUpdates(VehicleUpdates vui) {
- for (VehicleData veh : vui.getUpdated()) {
- if (managedInstances.containsKey(veh.getName())) {
- managedInstances.get(veh.getName()).setLocation(veh.getPosition());
- }
- }
+public class CarmaInstanceManager extends CommonInstanceManager{
+
+ public CarmaInstanceManager(){
+ setTargetPort(5374);
}
- /**
- * Callback to be invoked when CARMA Platform receives a V2X Message from the NS-3 simulation
- * @param rxMsg The V2X Message received
- * @param rxVehicleId 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 rxVehicleId) {
- if (!managedInstances.containsKey(rxVehicleId)) {
- return;
- }
-
- CarmaInstance carma = managedInstances.get(rxVehicleId);
- try {
- carma.sendV2xMsgs(rxMsg);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * 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(TimeSyncMessage message) throws IOException {
- if (managedInstances.size() == 0) {
- log.debug("There are no registered instances");
- }
- else {
- Gson gson = new Gson();
- byte[] bytes = gson.toJson(message).getBytes();
- for (CarmaInstance currentInstance : managedInstances.values()) {
- log.debug("Sending CARMA Platform instance {} at {}:{} time sync message for time {}!" ,
- currentInstance.getCarmaVehicleId(),
- currentInstance.getTargetAddress(),
- currentInstance.getTimeSyncPort(),
- message.getTimestep());
- currentInstance.sendTimeSyncMsg(bytes);
- }
- }
- }
-
-
/**
* Helper function to configure a new CARMA Platform instance object upon registration
* @param carmaVehId The CARMA Platform vehicle ID (e.g. it's license plate number)
@@ -174,7 +38,8 @@ public void onTimeStepUpdate(TimeSyncMessage message) throws IOException {
* @param v2xPort The port to which received simulated V2X messages should be sent
* @param timeSyncPort The port to which to send time sync messages.
*/
- private void newCarmaInstance(String carmaVehId, String carlaRoleName, InetAddress targetAddress, int v2xPort, int timeSyncPort) {
+ @Override
+ protected void newCommonInstance(String carmaVehId, String carlaRoleName, InetAddress targetAddress, int v2xPort, int timeSyncPort) {
CarmaInstance tmp = new CarmaInstance(carmaVehId, carlaRoleName, targetAddress, v2xPort, timeSyncPort);
try {
tmp.bind();
@@ -188,13 +53,4 @@ private void newCarmaInstance(String carmaVehId, String carlaRoleName, InetAddre
managedInstances.put(carlaRoleName, 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 mosiacVehicleId) {
- return managedInstances.keySet().contains(mosiacVehicleId);
- }
}
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 5215000c..ddbcb1c3 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,61 +13,18 @@
package org.eclipse.mosaic.fed.carma.ambassador;
-import gov.dot.fhwa.saxton.CarmaV2xMessage;
-import gov.dot.fhwa.saxton.CarmaV2xMessageReceiver;
-import gov.dot.fhwa.saxton.TimeSyncMessage;
-
-import org.eclipse.mosaic.fed.application.ambassador.SimulationKernel;
import org.eclipse.mosaic.fed.carma.configuration.CarmaConfiguration;
-import org.eclipse.mosaic.interactions.application.CarmaV2xMessageReception;
-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.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.*;
-import org.eclipse.mosaic.lib.objects.vehicle.sensor.DistanceSensor;
-import org.eclipse.mosaic.lib.objects.vehicle.sensor.RadarSensor;
+import org.eclipse.mosaic.lib.CommonUtil.ambassador.CommonMessageAmbassador;
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 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.Arrays;
-import java.util.List;
-
+import gov.dot.fhwa.saxton.CarmaV2xMessageReceiver;
/**
* Implementation of a {@link AbstractFederateAmbassador} for CARMA message
* ambassador.
*/
-public class CarmaMessageAmbassador extends AbstractFederateAmbassador {
-
- /**
- * Simulation time.
- */
- long currentSimulationTime;
+public class CarmaMessageAmbassador extends CommonMessageAmbassador {
/**
* CarmaMessageAmbassador configuration file.
@@ -89,7 +46,7 @@ public class CarmaMessageAmbassador extends AbstractFederateAmbassador {
* CarmaMessageAmbassador.
*/
public CarmaMessageAmbassador(AmbassadorParameter ambassadorParameter) {
- super(ambassadorParameter);
+ super(ambassadorParameter, CarmaRegistrationMessage.class, CarmaConfiguration.class);
try {
// Read the CARMA message ambassador configuration file
@@ -107,294 +64,4 @@ public CarmaMessageAmbassador(AmbassadorParameter ambassadorParameter) {
}
log.info("CARMA 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 nano seconds.
- * @param endTime End time of the simulation run in nano seconds.
- * @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;
- try {
- rti.requestAdvanceTime(currentSimulationTime, 0, (byte) 1);
- } catch (IllegalValueException e) {
- log.error("Error during advanceTime request", e);
- throw new InternalFederateException(e);
- }
-
- // Initialize listener socket and thread for CARMA Registration messages
- carmaRegistrationReceiver = new CarmaRegistrationReceiver();
- carmaRegistrationReceiver.init();
- registrationRxBackgroundThread = new Thread(carmaRegistrationReceiver);
- registrationRxBackgroundThread.start();
-
- // Initialize listener socket and thread for CARMA NS-3 Adapter messages
- v2xMessageReceiver = new CarmaV2xMessageReceiver();
- v2xMessageReceiver.init();
- v2xRxBackgroundThread = new Thread(v2xMessageReceiver);
- v2xRxBackgroundThread.start();
- }
-
- /**
- * 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.
- *
- * @param time The timestamp towards which the federate can advance it local
- * time.
- */
- @Override
- 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;
- }
- log.info("Carma message ambassador processing timestep to {}.", time);
-
- try {
- List newRegistrations = carmaRegistrationReceiver.getReceivedMessages();
- for (CarmaRegistrationMessage reg : newRegistrations) {
- carmaInstanceManager.onNewRegistration(reg);
- onDsrcRegistrationRequest(reg.getCarlaVehicleRole());
- }
- // Set current simulation time to most recent time update
- currentSimulationTime = time;
- 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(), currentSimulationTime);
- log.debug("Generated a message with ID: {}", msgInt.getMessageId());
- SimulationKernel.SimulationKernel.getV2xMessageCache().putItem(currentSimulationTime, msgInt.getMessage());
- rti.triggerInteraction(msgInt);
- }
- }
- // Time Syncmessage in nano seconds
- TimeSyncMessage timeSyncMessage = new TimeSyncMessage(currentSimulationTime, timeSyncSeq);
- carmaInstanceManager.onTimeStepUpdate(timeSyncMessage);
- // Increment time
- currentSimulationTime += carmaConfiguration.updateInterval * TIME.MILLI_SECOND;
- timeSyncSeq += 1;
-
- rti.requestAdvanceTime(currentSimulationTime, 0, (byte) 2);
- } 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);
- } catch (IOException e) {
- log.error("Error during advanceTime(" + time + ")", e);
- throw new InternalFederateException(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 true;
- }
-
- /**
- * This method processes the interactions.
- *
- * @param interaction The interaction that can be processed.
- * @throws InternalFederateException Exception is thrown if an error is occurred
- * while execute of a federate.
- */
- @Override
- public void processInteraction(Interaction interaction) throws InternalFederateException {
- String type = interaction.getTypeId();
- long interactionTime = interaction.getTime();
- log.trace("Process interaction with type '{}' at time: {}", type, interactionTime);
- if (interaction.getTypeId().equals(V2xMessageReception.TYPE_ID)) {
- receiveV2xReceptionInteraction((V2xMessageReception) interaction);
- }
- if (interaction.getTypeId().equals(CarmaV2xMessageReception.TYPE_ID)) {
- receiveInteraction((CarmaV2xMessageReception) interaction);
- }
- if (interaction.getTypeId().equals(VehicleUpdates.TYPE_ID)) {
- 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
- * @return The {@link V2xMessage} object if the id exists in the buffer, null o.w.
- */
- private V2xMessage lookupV2xMsgIdInBuffer(int id) {
- return SimulationKernel.SimulationKernel.getV2xMessageCache().getItem(id);
- }
-
- /**
- * Callback to be invoked when the network simulator determines that a simulated radio has received a V2X message
- * @param interaction The v2x message receipt data
- */
- 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);
-
- 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 {
- log.warn("Message with id " + interaction.getMessageId() + " received by " + interaction.getReceiverName() + " is no longer in the message buffer to be retrieved! Message transmission failed!!!");
- }
- }
-
- 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());
- }
-
- 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());
- }
-
-
- // 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 {
- // 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/CarmaRegistrationMessage.java b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationMessage.java
index 84310c16..9094e23d 100644
--- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationMessage.java
+++ b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationMessage.java
@@ -16,11 +16,12 @@
package org.eclipse.mosaic.fed.carma.ambassador;
+import org.eclipse.mosaic.lib.CommonUtil.ambassador.CommonRegistrationMessage;
/**
* JSON compatible message to be sent by CARMA Platform when it registers with
* the carma-mosaic ambassador
*/
-public class CarmaRegistrationMessage {
+public class CarmaRegistrationMessage extends CommonRegistrationMessage{
private String carmaVehicleId;
private String carlaVehicleRole;
private String rxMessageIpAddress;
@@ -29,6 +30,7 @@ public class CarmaRegistrationMessage {
public CarmaRegistrationMessage(String carmaVehicleId, String carlaVehicleRole, String rxMessageIpAddress,
int rxMessagePort, int rxTimeSyncPort) {
+ super(carmaVehicleId, carlaVehicleRole, rxMessageIpAddress, rxMessagePort, rxTimeSyncPort);
this.carmaVehicleId = carmaVehicleId;
this.carlaVehicleRole = carlaVehicleRole;
this.rxMessageIpAddress = rxMessageIpAddress;
@@ -36,46 +38,6 @@ public CarmaRegistrationMessage(String carmaVehicleId, String carlaVehicleRole,
this.rxTimeSyncPort = rxTimeSyncPort;
}
- public String getCarmaVehicleId() {
- return carmaVehicleId;
- }
-
- public void setCarmaVehicleId(String carmaVehicleId) {
- this.carmaVehicleId = carmaVehicleId;
- }
-
- public String getCarlaVehicleRole() {
- return carlaVehicleRole;
- }
-
- public void setCarlaVehicleRole(String carlaVehicleRole) {
- this.carlaVehicleRole = carlaVehicleRole;
- }
-
- public String getRxMessageIpAddress() {
- return rxMessageIpAddress;
- }
-
- public void setRxMessageIpAddress(String rxMessageIpAddress) {
- this.rxMessageIpAddress = rxMessageIpAddress;
- }
-
- public int getRxMessagePort() {
- return rxMessagePort;
- }
-
- public void setRxMessagePort(int rxMessagePort) {
- this.rxMessagePort = rxMessagePort;
- }
-
- public int getRxTimeSyncPort() {
- return rxTimeSyncPort;
- }
-
- public void setRxTimeSyncPort(int rxTimeSyncPort) {
- this.rxTimeSyncPort = rxTimeSyncPort;
- }
-
@Override
public String toString() {
return "CarmaRegistrationMessage [carmaVehicleId=" + carmaVehicleId + ", carlaVehicleRole=" + carlaVehicleRole
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 3e995a65..6e7b8d98 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
@@ -20,14 +20,10 @@
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;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
+import org.eclipse.mosaic.lib.CommonUtil.ambassador.CommonRegistrationReceiver;
/**
* Worker thread Runnable for operating a listen socket to receive outbound V2X Messages from CARMA Platform instances
@@ -35,25 +31,17 @@
* 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
*/
-public class CarmaRegistrationReceiver implements Runnable {
- private Queue rxQueue = new LinkedList<>();
+public class CarmaRegistrationReceiver extends CommonRegistrationReceiver{
+
+ public CarmaRegistrationReceiver(Class type) {
+ super(type);
+ }
+
private DatagramSocket listenSocket = null;
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
- * @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() {
@@ -74,35 +62,9 @@ public void run() {
// Enqueue message for processing on main thread
synchronized (rxQueue) {
- log.info("New CARMA instance '{}' received with CARMA Registration Receiver.", parsedMessage.getCarmaVehicleId());
+ log.info("New CARMA instance '{}' received with CARMA Registration Receiver.", parsedMessage.getVehicleId());
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 CARMA Platform 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/src/main/java/org/eclipse/mosaic/fed/carma/configuration/CarmaConfiguration.java b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/configuration/CarmaConfiguration.java
index a7fb62cf..207e752d 100644
--- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/configuration/CarmaConfiguration.java
+++ b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/configuration/CarmaConfiguration.java
@@ -13,32 +13,13 @@
package org.eclipse.mosaic.fed.carma.configuration;
-import org.eclipse.mosaic.lib.util.gson.TimeFieldAdapter;
-import com.google.gson.annotations.JsonAdapter;
-import java.io.Serializable;
-import java.util.List;
+import org.eclipse.mosaic.lib.CommonUtil.configuration.CommonConfiguration;
/**
* The CARMA Message Ambassador configuration class.
*/
-public class CarmaConfiguration implements Serializable {
+public class CarmaConfiguration extends CommonConfiguration {
private static final long serialVersionUID = 1479294781446446539L;
- /**
- * The time step that the CARMA message ambassador advances time. The default
- * value is 1000 (1s). Unit: [ms].
- */
- @JsonAdapter(TimeFieldAdapter.LegacyMilliSeconds.class)
- public Long updateInterval = 1000L;
-
- /**
- * Configruation for CARMA vehicles.
- */
- public List carmaVehicles;
-
- /**
- * ID of CARMA vehicle that sends external messages.
- */
- public String senderCarmaVehicleId;
}
diff --git a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/configuration/CarmaVehicleConfiguration.java b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/configuration/CarmaVehicleConfiguration.java
index f6b8bc77..067ce7a6 100644
--- a/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/configuration/CarmaVehicleConfiguration.java
+++ b/co-simulation/fed/mosaic-carma/src/main/java/org/eclipse/mosaic/fed/carma/configuration/CarmaVehicleConfiguration.java
@@ -13,63 +13,12 @@
package org.eclipse.mosaic.fed.carma.configuration;
-import java.util.List;
-
-import org.eclipse.mosaic.lib.geo.GeoPoint;
-import org.eclipse.mosaic.lib.geo.CartesianPoint;
+import org.eclipse.mosaic.lib.CommonUtil.configuration.CommonVehicleConfiguration;
/**
* Define CARMA vehicle information
*/
-public class CarmaVehicleConfiguration {
-
- /**
- * The route ID on which the vehicle will be spawned.
- */
- public String routeID;
-
- /**
- * The lane on which the vehicle will be spawned.
- */
- public int lane;
-
- /**
- * Position within the route where the vehicle should be spawned.
- */
- public double position;
-
- /**
- * The speed at which the vehicle is supposed to depart.
- */
- public double departSpeed;
-
- /**
- * The vehicle type
- */
- public String vehicleType;
-
- /**
- * Specify the applications to be used for this vehicle.
- */
- public List applications;
-
- /**
- * The geo position at which the vehicle is currently located.
- */
- public GeoPoint geoPosition;
-
- /**
- * The projected position at which currently the vehicle is located.
- */
- public CartesianPoint projectedPosition;
-
- /**
- * Vehicle heading in degrees
- */
- public Double heading;
+public class CarmaVehicleConfiguration extends CommonVehicleConfiguration {
- /**
- * The slope of vehicle in degrees
- */
- public double slope;
+
}
diff --git a/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceManagerTest.java b/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceManagerTest.java
index a7f84ccd..07412908 100644
--- a/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceManagerTest.java
+++ b/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceManagerTest.java
@@ -15,20 +15,8 @@
*/
package org.eclipse.mosaic.fed.carma.ambassador;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
import java.io.IOException;
-import java.net.DatagramSocket;
import java.net.InetAddress;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -36,12 +24,17 @@
import org.eclipse.mosaic.lib.geo.GeoPoint;
import org.eclipse.mosaic.lib.junit.IpResolverRule;
import org.eclipse.mosaic.lib.objects.addressing.IpResolver;
-import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xContent;
-import org.eclipse.mosaic.lib.objects.v2x.ExternalV2xMessage;
-import org.eclipse.mosaic.lib.objects.v2x.V2xMessage;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import org.mockito.internal.util.reflection.FieldSetter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -89,7 +82,7 @@ public void setUp() throws Exception {
managedInstances.put("instance3", instance3);
// Set private instance field to mock using reflection
- FieldSetter.setField(manager, manager.getClass().getDeclaredField("managedInstances"), managedInstances);
+ FieldSetter.setField(manager, manager.getClass().getSuperclass().getDeclaredField("managedInstances"), managedInstances);
}
@Test
@@ -139,7 +132,7 @@ public void testOnTimeStepUpdateWithoutRegisteredIntstances() throws NoSuchField
Map managedInstances = new HashMap<>();
// Set private instance field to mock using reflection
- FieldSetter.setField(manager, manager.getClass().getDeclaredField("managedInstances"), managedInstances);
+ FieldSetter.setField(manager, manager.getClass().getSuperclass().getDeclaredField("managedInstances"), managedInstances);
TimeSyncMessage message = new TimeSyncMessage(300, 3);
manager.onTimeStepUpdate(message);
@@ -162,7 +155,7 @@ public void testonV2XMessageTx() {
// Register host with IpResolver singleton
IpResolver.getSingleton().registerHost("veh_0");
// Set CarlaRoleName to veh_0 to macth registered host
- when(instance1.getCarlaRoleName()).thenReturn("veh_0");
+ when(instance1.getRoleName()).thenReturn("veh_0");
// Set location to origin
when(instance1.getLocation()).thenReturn(GeoPoint.ORIGO);
@@ -184,7 +177,7 @@ public void testonV2XMessageTxUnregisteredCarmaPlatform() {
when(instance2.getTargetAddress()).thenReturn(address2);
when(instance3.getTargetAddress()).thenReturn(address3);
IpResolver.getSingleton().registerHost("veh_0");
- when(instance1.getCarlaRoleName()).thenReturn("veh_0");
+ when(instance1.getRoleName()).thenReturn("veh_0");
when(instance1.getLocation()).thenReturn(GeoPoint.ORIGO);
// Attempt to create V2X Message Transmission for unregistered address.
// Throws IllegalStateException
diff --git a/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceTest.java b/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceTest.java
index 5c69adc6..b3f17709 100644
--- a/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceTest.java
+++ b/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaInstanceTest.java
@@ -15,21 +15,20 @@
*/
package org.eclipse.mosaic.fed.carma.ambassador;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import org.eclipse.mosaic.lib.geo.GeoPoint;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import org.mockito.internal.util.reflection.FieldSetter;
import com.google.gson.Gson;
@@ -78,20 +77,20 @@ public void setup() throws NoSuchFieldException {
3456,
5667);
// Set private instance field to mock using reflection
- FieldSetter.setField(instance, instance.getClass().getDeclaredField("rxMsgsSocket"), socket);
+ FieldSetter.setField(instance, instance.getClass().getSuperclass().getDeclaredField("rxMsgsSocket"), socket);
}
@Test
public void testGetterSetterConstructor() {
// Test getters and constructor for setting and retrieving class members
- assertEquals("SomeID", instance.getCarmaVehicleId());
+ assertEquals("SomeID", instance.getVehicleId());
assertEquals( GeoPoint.ORIGO, instance.getLocation());
assertEquals(3456, instance.getV2xPort());
assertEquals(5667, instance.getTimeSyncPort());
// Test Setter
- instance.setCarmaVehicleId("DifferentID");
- assertEquals("DifferentID", instance.getCarmaVehicleId());
+ instance.setVehicleId("DifferentID");
+ assertEquals("DifferentID", instance.getVehicleId());
instance.setTimeSyncPort(4321);
assertEquals(4321, instance.getTimeSyncPort());
instance.setV2xPort(5678);
diff --git a/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationMessageTest.java b/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationMessageTest.java
index 0ead758a..b41bae33 100644
--- a/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationMessageTest.java
+++ b/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/ambassador/CarmaRegistrationMessageTest.java
@@ -30,19 +30,19 @@ public void testGetterSettersConstructor() {
5678,
8642);
// Test Getter
- assertEquals("ID", message.getCarmaVehicleId());
- assertEquals("role", message.getCarlaVehicleRole());
+ assertEquals("ID", message.getVehicleId());
+ assertEquals("role", message.getVehicleRole());
assertEquals("127.0.0.1", message.getRxMessageIpAddress());
assertEquals(5678, message.getRxMessagePort());
assertEquals(8642, message.getRxTimeSyncPort());
message.setRxMessagePort(7777);
message.setRxTimeSyncPort(6666);
- message.setCarmaVehicleId("SOMEID");
- message.setCarlaVehicleRole("SOMEROLL");
+ message.setVehicleId("SOMEID");
+ message.setVehicleRole("SOMEROLL");
message.setRxMessageIpAddress("someIP");
- assertEquals("SOMEID", message.getCarmaVehicleId());
- assertEquals("SOMEROLL", message.getCarlaVehicleRole());
+ assertEquals("SOMEID", message.getVehicleId());
+ assertEquals("SOMEROLL", message.getVehicleRole());
assertEquals("someIP", message.getRxMessageIpAddress());
assertEquals(7777, message.getRxMessagePort());
diff --git a/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/configuration/CarmaConfigurationTest.java b/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/configuration/CarmaConfigurationTest.java
index ad6cd629..f30ba04c 100644
--- a/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/configuration/CarmaConfigurationTest.java
+++ b/co-simulation/fed/mosaic-carma/src/test/java/org/eclipse/mosaic/fed/carma/configuration/CarmaConfigurationTest.java
@@ -13,15 +13,18 @@
package org.eclipse.mosaic.fed.carma.configuration;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import java.io.InputStreamReader;
+import java.lang.reflect.Type;
import org.eclipse.mosaic.lib.geo.MutableCartesianPoint;
import org.eclipse.mosaic.lib.geo.MutableGeoPoint;
-import org.eclipse.mosaic.lib.util.objects.ObjectInstantiation;
-
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import org.junit.Test;
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+
/**
* Test for {@link CarmaConfiguration}.
*
@@ -44,20 +47,20 @@ public void readConfig_assertProperties() throws InstantiationException {
// ASSERT
assertNotNull(carmaConfiguration); // assert that configuration is created
assertEquals(Long.valueOf(100L), carmaConfiguration.updateInterval);
- assertEquals("0", carmaConfiguration.carmaVehicles.get(0).routeID);
- assertEquals(1, carmaConfiguration.carmaVehicles.get(0).lane);
- assertEquals(Double.valueOf(0.0D), carmaConfiguration.carmaVehicles.get(0).position, 0.001);
- assertEquals(Double.valueOf(0.0D), carmaConfiguration.carmaVehicles.get(0).departSpeed, 0.001);
- assertEquals("vehicle.chevrolet.impala", carmaConfiguration.carmaVehicles.get(0).vehicleType);
+ assertEquals("0", carmaConfiguration.Vehicles.get(0).routeID);
+ assertEquals(1, carmaConfiguration.Vehicles.get(0).lane);
+ assertEquals(Double.valueOf(0.0D), carmaConfiguration.Vehicles.get(0).position, 0.001);
+ assertEquals(Double.valueOf(0.0D), carmaConfiguration.Vehicles.get(0).departSpeed, 0.001);
+ assertEquals("vehicle.chevrolet.impala", carmaConfiguration.Vehicles.get(0).vehicleType);
assertEquals("org.eclipse.mosaic.app.tutorial.VehicleCommunicationApp",
- carmaConfiguration.carmaVehicles.get(0).applications.get(0));
+ carmaConfiguration.Vehicles.get(0).applications.get(0));
assertEquals(new MutableGeoPoint(52.579272059028646, 13.467165499469328),
- carmaConfiguration.carmaVehicles.get(0).geoPosition);
+ carmaConfiguration.Vehicles.get(0).geoPosition);
assertEquals(new MutableCartesianPoint(501.62, 116.95, 0.0),
- carmaConfiguration.carmaVehicles.get(0).projectedPosition);
- assertEquals(Double.valueOf(24.204351784500364D), carmaConfiguration.carmaVehicles.get(0).heading);
- assertEquals(Double.valueOf(0.0), carmaConfiguration.carmaVehicles.get(0).slope, 0.001);
- assertEquals("carma_0", carmaConfiguration.senderCarmaVehicleId);
+ carmaConfiguration.Vehicles.get(0).projectedPosition);
+ assertEquals(Double.valueOf(24.204351784500364D), carmaConfiguration.Vehicles.get(0).heading);
+ assertEquals(Double.valueOf(0.0), carmaConfiguration.Vehicles.get(0).slope, 0.001);
+ assertEquals("carma_0", carmaConfiguration.senderVehicleId);
}
/**
@@ -70,7 +73,8 @@ public void readConfig_assertProperties() throws InstantiationException {
* deserialization/instantiation.
*/
private CarmaConfiguration getCarmaConfiguration(String filePath) throws InstantiationException {
- return new ObjectInstantiation<>(CarmaConfiguration.class).read(getClass().getResourceAsStream(filePath));
+ Type type = new TypeToken() {}.getType();
+ return new Gson().fromJson(new InputStreamReader(getClass().getResourceAsStream(filePath)), type);
}
}
\ No newline at end of file
diff --git a/co-simulation/fed/mosaic-carma/src/test/resources/carma_config.json b/co-simulation/fed/mosaic-carma/src/test/resources/carma_config.json
index f12b548c..b40a8049 100644
--- a/co-simulation/fed/mosaic-carma/src/test/resources/carma_config.json
+++ b/co-simulation/fed/mosaic-carma/src/test/resources/carma_config.json
@@ -1,6 +1,6 @@
{
"updateInterval": 100,
- "carmaVehicles":[
+ "Vehicles":[
{
"routeID": "0",
"lane": 1,
@@ -21,5 +21,5 @@
"heading": 24.204351784500364,
"slope": 0.0
}],
-"senderCarmaVehicleId":"carma_0"
+"senderVehicleId":"carma_0"
}
diff --git a/co-simulation/lib/mosaic-common-utils/pom.xml b/co-simulation/lib/mosaic-common-utils/pom.xml
new file mode 100644
index 00000000..9c0af4dc
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/pom.xml
@@ -0,0 +1,112 @@
+
+
+ 4.0.0
+
+
+ org.eclipse.mosaic
+ mosaic-parent
+ 22.1-SNAPSHOT
+ ../../pom.xml
+
+
+ mosaic-common-utils
+ Common Utilities
+
+
+
+ com.google.code.gson
+ gson
+
+
+ org.eclipse.mosaic
+ mosaic-rti-api
+ ${mosaic.version}
+
+
+ org.eclipse.mosaic
+ mosaic-geomath
+ ${mosaic.version}
+
+
+ com.google.guava
+ guava
+
+
+ org.apache.commons
+ commons-configuration2
+
+
+
+ commons-jxpath
+ commons-jxpath
+ runtime
+
+
+ commons-cli
+ commons-cli
+
+
+ org.leadpony.justify
+ justify
+
+
+ com.ibm.icu
+ icu4j
+
+
+
+
+ org.apache.johnzon
+ johnzon-core
+ compile
+
+
+ gov.dot.fhwa.saxton
+ mosaic-carma-utils
+ ${mosaic.version}
+
+
+ org.eclipse.mosaic
+ mosaic-application
+ ${mosaic.version}
+
+
+ org.eclipse.mosaic
+ mosaic-objects
+ ${mosaic.version}
+
+
+ org.eclipse.mosaic
+ mosaic-objects
+ ${mosaic.version}
+ test-jar
+ test
+
+
+ org.eclipse.mosaic
+ mosaic-utils
+ ${mosaic.version}
+ test-jar
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
+
+
+
+
+
diff --git a/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonInstance.java b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonInstance.java
new file mode 100644
index 00000000..b438c3b0
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonInstance.java
@@ -0,0 +1,128 @@
+/*
+ * 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.lib.CommonUtil.ambassador;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+
+import org.eclipse.mosaic.lib.geo.GeoPoint;
+
+public class CommonInstance {
+
+ private String VehicleId;
+ private String RoleName;
+ private InetAddress targetAddress;
+ private int v2xPort;
+ private int timeSyncPort;
+ private GeoPoint location = GeoPoint.ORIGO;
+ protected DatagramSocket rxMsgsSocket = null;
+
+ public CommonInstance(String VehicleId, String RoleName, InetAddress targetAddress, int v2xPort, int timeSyncPort){
+ this.VehicleId = VehicleId;
+ this.RoleName = RoleName;
+ this.targetAddress = targetAddress;
+ this.v2xPort = v2xPort;
+ this.timeSyncPort = timeSyncPort;
+ }
+
+ public String getVehicleId() {
+ return VehicleId;
+ }
+
+ public void setVehicleId(String VehicleId) {
+ this.VehicleId = VehicleId;
+ }
+
+ public String getRoleName() {
+ return RoleName;
+ }
+
+ public void setRoleName(String RoleName) {
+ this.RoleName = RoleName;
+ }
+
+ public InetAddress getTargetAddress() {
+ return targetAddress;
+ }
+
+ public void setTargetAddress(InetAddress targetAddress) {
+ this.targetAddress = targetAddress;
+ }
+
+ public int getV2xPort() {
+ return v2xPort;
+ }
+
+ public void setV2xPort(int v2xPort) {
+ this.v2xPort = v2xPort;
+ }
+
+ public int getTimeSyncPort() {
+ return timeSyncPort;
+ }
+
+ public void setTimeSyncPort(int timeSyncPort) {
+ this.timeSyncPort = timeSyncPort;
+ }
+
+ public void setLocation(GeoPoint location) {
+ this.location = location;
+ }
+
+ public GeoPoint getLocation() {
+ return this.location;
+ }
+
+ /**
+ * Sends the V2X message to the CARMA Platform 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 sendV2xMsgs(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, v2xPort);
+
+ rxMsgsSocket.send(packet);
+ }
+
+ /**
+ * Sends the time sync messages to the CARMA Platform to synchronize ros clock with simulation clock.
+ * @param data The binary data encoding of json time sync message
+ * @throws IOException If there is an issue with the underlying socket object or methods
+ */
+ public void sendTimeSyncMsg(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);
+ }
+
+ /**
+ * Connects the sockt to receive messages from the CARMA Platform instance
+ * @throws IOException If the socket creation fails
+ */
+ public void bind() throws IOException {
+ rxMsgsSocket = new DatagramSocket();
+ }
+}
diff --git a/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonInstanceManager.java b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonInstanceManager.java
new file mode 100644
index 00000000..a19ff08e
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonInstanceManager.java
@@ -0,0 +1,159 @@
+/*
+ * 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.lib.CommonUtil.ambassador;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+
+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 com.google.gson.Gson;
+
+import gov.dot.fhwa.saxton.CarmaV2xMessage;
+import gov.dot.fhwa.saxton.TimeSyncMessage;
+
+
+public class CommonInstanceManager {
+ protected Map managedInstances = new HashMap<>();
+
+ private int TARGET_PORT = 5374;
+ protected final Logger log = LoggerFactory.getLogger(this.getClass());
+
+ public void onNewRegistration(R registration) {
+ if (!managedInstances.containsKey(registration.getVehicleRole())) {
+ try {
+ newCommonInstance(
+ registration.getVehicleId(),
+ registration.getVehicleRole(),
+ InetAddress.getByName(registration.getRxMessageIpAddress()),
+ registration.getRxMessagePort(),
+ registration.getRxTimeSyncPort()
+ );
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ // log warning
+ log.warn("Received duplicate registration for vehicle " + registration.getVehicleRole());
+ }
+ }
+
+ protected void newCommonInstance(String VehId, String RoleName, InetAddress targetAddress, int v2xPort, int timeSyncPort) {
+ CommonInstance tmp = new CommonInstance(VehId, RoleName, targetAddress, v2xPort, timeSyncPort);
+ try {
+ tmp.bind();
+ log.info("New Common instance '{}' registered with CARMA Instance Manager.", RoleName);
+ } catch (IOException e) {
+ log.error("Failed to bind Common instance with ID '{}' to its RX message socket: {}",
+ RoleName, e.getMessage());
+ log.error("Stack trace:", e);
+ throw new RuntimeException(e);
+ }
+ managedInstances.put(RoleName, (T) tmp);
+ }
+
+ public boolean checkIfRegistered(String mosiacVehicleId) {
+ return managedInstances.keySet().contains(mosiacVehicleId);
+ }
+
+ public V2xMessageTransmission onV2XMessageTx(InetAddress sourceAddr, CarmaV2xMessage txMsg, long time) {
+ T sender = null;
+ // Find the CarmaInstance with sourceAddr.
+ for (T ci : managedInstances.values()) {
+ if (ci.getTargetAddress().equals(sourceAddr)) {
+ sender = ci;
+ break;
+ }
+ }
+ // Unregistered instance attempting to send messages
+ if (sender == null) {
+ throw new IllegalStateException("Unregistered Common instance attempting to send messages via MOSAIC");
+ }
+ AdHocMessageRoutingBuilder messageRoutingBuilder = new AdHocMessageRoutingBuilder(
+ sender.getRoleName(), 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: {}, type: {}, payload: {}",
+ time,
+ sender.getVehicleId(),
+ sender.getLocation(),
+ txMsg.getType(),
+ txMsg.getPayload()
+ );
+ return new V2xMessageTransmission( time, new ExternalV2xMessage(routing,
+ new ExternalV2xContent( time, sender.getLocation(), txMsg.getPayload())));
+ }
+
+ public void onV2XMessageRx(byte[] rxMsg, String rxVehicleId) {
+ if (!managedInstances.containsKey(rxVehicleId)) {
+ return;
+ }
+
+ T common = managedInstances.get(rxVehicleId);
+ try {
+ common.sendV2xMsgs(rxMsg);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void onTimeStepUpdate(TimeSyncMessage message) throws IOException {
+ if (managedInstances.size() == 0) {
+ log.debug("There are no registered instances");
+ }
+ else {
+ Gson gson = new Gson();
+ byte[] bytes = gson.toJson(message).getBytes();
+ for (T currentInstance : managedInstances.values()) {
+ log.debug("Sending Common instance {} at {}:{} time sync message for time {}!" ,
+ currentInstance.getVehicleId(),
+ currentInstance.getTargetAddress(),
+ currentInstance.getTimeSyncPort(),
+ message.getTimestep());
+ currentInstance.sendTimeSyncMsg(bytes);
+ }
+ }
+ }
+
+ public int getTargetPort(){
+ return TARGET_PORT;
+ }
+
+ public void setTargetPort(int targetPort){
+ this.TARGET_PORT = targetPort;
+ }
+
+ public void onVehicleUpdates(VehicleUpdates vui) {
+ for (VehicleData veh : vui.getUpdated()) {
+ if (managedInstances.containsKey(veh.getName())) {
+ managedInstances.get(veh.getName()).setLocation(veh.getPosition());
+ }
+ }
+ }
+}
diff --git a/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonMessageAmbassador.java b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonMessageAmbassador.java
new file mode 100644
index 00000000..a5b7df8f
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonMessageAmbassador.java
@@ -0,0 +1,349 @@
+/*
+ * 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.lib.CommonUtil.ambassador;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.eclipse.mosaic.fed.application.ambassador.SimulationKernel;
+import org.eclipse.mosaic.interactions.application.CarmaV2xMessageReception;
+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.lib.CommonUtil.configuration.CommonConfiguration;
+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.Consumptions;
+import org.eclipse.mosaic.lib.objects.vehicle.Emissions;
+import org.eclipse.mosaic.lib.objects.vehicle.VehicleBatteryState;
+import org.eclipse.mosaic.lib.objects.vehicle.VehicleConsumptions;
+import org.eclipse.mosaic.lib.objects.vehicle.VehicleData;
+import org.eclipse.mosaic.lib.objects.vehicle.VehicleEmissions;
+import org.eclipse.mosaic.lib.objects.vehicle.VehicleSensors;
+import org.eclipse.mosaic.lib.objects.vehicle.VehicleSignals;
+import org.eclipse.mosaic.lib.objects.vehicle.VehicleType;
+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;
+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 gov.dot.fhwa.saxton.TimeSyncMessage;
+
+
+public class CommonMessageAmbassador extends AbstractFederateAmbassador{
+
+ protected long currentSimulationTime;
+ protected C commonConfiguration;
+
+ protected R commonRegistrationReceiver;
+ private Thread registrationRxBackgroundThread;
+ private CarmaV2xMessageReceiver v2xMessageReceiver;
+ private Thread v2xRxBackgroundThread;
+ protected M commonInstanceManager = (M) new CommonInstanceManager();
+ protected int timeSyncSeq = 0;
+ protected Class messageClass;
+ protected Class configClass;
+
+ @Override
+ public boolean isTimeRegulating() {
+ return true;
+ }
+
+ @Override
+ public boolean isTimeConstrained() {
+ return true;
+ }
+
+ public CommonMessageAmbassador(AmbassadorParameter ambassadorParameter, Class messageClass, Class configClass) {
+ super(ambassadorParameter);
+ this.messageClass = messageClass;
+ this.configClass = configClass;
+ try {
+ // Read the CARMA message ambassador configuration file
+ commonConfiguration = new ObjectInstantiation<>(configClass, log)
+ .readFile(ambassadorParameter.configuration);
+ } catch (InstantiationException e) {
+ log.error("Configuration object could not be instantiated: ", e);
+ }
+
+ log.info("The update interval of "+ this.getClass().getSimpleName() + "is " + commonConfiguration.updateInterval + " .");
+
+ // Check the CARMA update interval
+ if (commonConfiguration.updateInterval <= 0) {
+ throw new RuntimeException("Invalid update interval for " + this.getClass().getSimpleName() + ", should be >0.");
+ }
+ log.info( this.getClass().getSimpleName() + " is generated.");
+ }
+
+ @Override
+ 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);
+ }
+
+ // Initialize listener socket and thread for Common Registration messages
+ commonRegistrationReceiver = (R) new CommonRegistrationReceiver(messageClass);
+ commonRegistrationReceiver.init();
+ registrationRxBackgroundThread = new Thread(commonRegistrationReceiver);
+ registrationRxBackgroundThread.start();
+
+ // Initialize listener socket and thread for Common NS-3 Adapter messages
+ v2xMessageReceiver = new CarmaV2xMessageReceiver();
+ v2xMessageReceiver.init();
+ v2xRxBackgroundThread = new Thread(v2xMessageReceiver);
+ v2xRxBackgroundThread.start();
+ }
+
+ @Override
+ 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;
+ }
+ log.info(this.getClass().getSimpleName()+ " processing timestep to {}.", time);
+
+ try {
+ List newRegistrations = commonRegistrationReceiver.getReceivedMessages();
+ for (T reg : newRegistrations) {
+ commonInstanceManager.onNewRegistration(reg);
+ onDsrcRegistrationRequest(reg.getVehicleRole());
+ }
+ // Set current simulation time to most recent time update
+ currentSimulationTime = time;
+ 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 = commonInstanceManager.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);
+ }
+ }
+ // Time Syncmessage in nano seconds
+ TimeSyncMessage timeSyncMessage = new TimeSyncMessage(currentSimulationTime, timeSyncSeq);
+ commonInstanceManager.onTimeStepUpdate(timeSyncMessage);
+ // Increment time
+ currentSimulationTime += commonConfiguration.updateInterval * TIME.MILLI_SECOND;
+ timeSyncSeq += 1;
+
+ rti.requestAdvanceTime(currentSimulationTime, 0, (byte) 2);
+ } 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);
+ } catch (IOException e) {
+ log.error("Error during advanceTime(" + time + ")", e);
+ throw new InternalFederateException(e);
+ }
+
+ }
+
+ @Override
+ public void processInteraction(Interaction interaction) throws InternalFederateException {
+ String type = interaction.getTypeId();
+ long interactionTime = interaction.getTime();
+ log.trace("Process interaction with type '{}' at time: {}", type, interactionTime);
+ if (interaction.getTypeId().equals(V2xMessageReception.TYPE_ID)) {
+ receiveV2xReceptionInteraction((V2xMessageReception) interaction);
+ }
+ if (interaction.getTypeId().equals(CarmaV2xMessageReception.TYPE_ID)) {
+ receiveInteraction((CarmaV2xMessageReception) interaction);
+ }
+ if (interaction.getTypeId().equals(VehicleUpdates.TYPE_ID)) {
+ receiveVehicleUpdateInteraction((VehicleUpdates) interaction);
+ }
+ }
+
+ private synchronized void receiveVehicleUpdateInteraction(VehicleUpdates interaction) {
+ commonInstanceManager.onVehicleUpdates(interaction);
+ }
+
+ protected V2xMessage lookupV2xMsgIdInBuffer(int id) {
+ return SimulationKernel.SimulationKernel.getV2xMessageCache().getItem(id);
+ }
+
+ private synchronized void receiveV2xReceptionInteraction(V2xMessageReception interaction) {
+ String carlaRoleName = interaction.getReceiverName();
+ if (!commonInstanceManager.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);
+
+ if (msg != null && msg instanceof ExternalV2xMessage) {
+ ExternalV2xMessage msg2 = (ExternalV2xMessage) msg;
+ commonInstanceManager.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 {
+ log.warn("Message with id " + interaction.getMessageId() + " received by " + interaction.getReceiverName() + " is no longer in the message buffer to be retrieved! Message transmission failed!!!");
+ }
+ }
+
+ 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());
+ }
+
+ 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());
+ }
+
+
+ // 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 {
+ // 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/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonRegistrationMessage.java b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonRegistrationMessage.java
new file mode 100644
index 00000000..e58ad2be
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonRegistrationMessage.java
@@ -0,0 +1,81 @@
+/*
+ * 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.lib.CommonUtil.ambassador;
+
+public class CommonRegistrationMessage {
+
+ private String vehicleId;
+ private String vehicleRole;
+ private String rxMessageIpAddress;
+ private int rxMessagePort;
+ private int rxTimeSyncPort;
+
+ public CommonRegistrationMessage(String vehicleId, String vehicleRole, String rxMessageIpAddress,int rxMessagePort, int rxTimeSyncPort) {
+ this.vehicleId = vehicleId;
+ this.vehicleRole = vehicleRole;
+ this.rxMessageIpAddress = rxMessageIpAddress;
+ this.rxMessagePort = rxMessagePort;
+ this.rxTimeSyncPort = rxTimeSyncPort;
+ }
+
+ public String getVehicleId() {
+ return vehicleId;
+ }
+
+ public void setVehicleId(String vehicleId) {
+ this.vehicleId = vehicleId;
+ }
+
+ public String getVehicleRole() {
+ return vehicleRole;
+ }
+
+ public void setVehicleRole(String vehicleRole) {
+ this.vehicleRole = vehicleRole;
+ }
+
+ public String getRxMessageIpAddress() {
+ return rxMessageIpAddress;
+ }
+
+ public void setRxMessageIpAddress(String rxMessageIpAddress) {
+ this.rxMessageIpAddress = rxMessageIpAddress;
+ }
+
+ public int getRxMessagePort() {
+ return rxMessagePort;
+ }
+
+ public void setRxMessagePort(int rxMessagePort) {
+ this.rxMessagePort = rxMessagePort;
+ }
+
+ public int getRxTimeSyncPort() {
+ return rxTimeSyncPort;
+ }
+
+ public void setRxTimeSyncPort(int rxTimeSyncPort) {
+ this.rxTimeSyncPort = rxTimeSyncPort;
+ }
+
+ @Override
+ public String toString() {
+ return "CommonRegistrationMessage [VehicleId=" + vehicleId + ", VehicleRole=" + vehicleRole
+ + ", rxMessageIpAddress=" + rxMessageIpAddress + ", rxMessagePort=" + rxMessagePort
+ + ", rxTimeSyncPort=" + rxTimeSyncPort + "]";
+ }
+
+}
diff --git a/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonRegistrationReceiver.java b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonRegistrationReceiver.java
new file mode 100644
index 00000000..3d642217
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonRegistrationReceiver.java
@@ -0,0 +1,97 @@
+/*
+ * 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.lib.CommonUtil.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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+public class CommonRegistrationReceiver implements Runnable{
+
+ public CommonRegistrationReceiver(java.lang.Class type) {
+ this.type = type;
+ }
+
+ protected Queue rxQueue = new LinkedList<>();
+ private DatagramSocket listenSocket = null;
+ private static final int listenPort = 1515;
+ private boolean running = true;
+ private static final int UDP_MTU = 1535;
+ protected final Logger log = LoggerFactory.getLogger(this.getClass());
+ protected final Class type;
+
+ 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];
+ 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(), 0, msg.getLength());
+ log.debug("Registration JSON received: {}", receivedPacket);
+ Gson gson = new Gson();
+ T parsedMessage = gson.fromJson(receivedPacket, type);
+
+ // Enqueue message for processing on main thread
+ synchronized (rxQueue) {
+ log.info("New Common instance '{}' received with Common Registration Receiver.", parsedMessage.getVehicleId());
+ rxQueue.add(parsedMessage);
+ }
+ }
+ }
+
+ public void stop() {
+ if (listenSocket != null) {
+ listenSocket.close();
+ }
+
+ running = false;
+ }
+
+ public List getReceivedMessages() {
+ List output = new ArrayList<>();
+
+ synchronized (rxQueue) {
+ output.addAll(rxQueue);
+ rxQueue.clear();
+ }
+
+ return output;
+ }
+}
diff --git a/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/configuration/CommonConfiguration.java b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/configuration/CommonConfiguration.java
new file mode 100644
index 00000000..4afbffac
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/configuration/CommonConfiguration.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.lib.CommonUtil.configuration;
+
+import java.util.List;
+
+import org.eclipse.mosaic.lib.util.gson.TimeFieldAdapter;
+
+import com.google.gson.annotations.JsonAdapter;
+
+public class CommonConfiguration {
+
+ private static final long serialVersionUID = 1479294781446446539L;
+
+ /**
+ * The time step that the CARMA message ambassador advances time. The default
+ * value is 1000 (1s). Unit: [ms].
+ */
+ @JsonAdapter(TimeFieldAdapter.LegacyMilliSeconds.class)
+ public Long updateInterval = 1000L;
+
+ /**
+ * Configruation for CARMA vehicles.
+ */
+ public List Vehicles;
+
+ /**
+ * ID of CARMA vehicle that sends external messages.
+ */
+ public String senderVehicleId;
+}
diff --git a/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/configuration/CommonVehicleConfiguration.java b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/configuration/CommonVehicleConfiguration.java
new file mode 100644
index 00000000..5d8a6e3d
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/main/java/org/eclipse/mosaic/lib/CommonUtil/configuration/CommonVehicleConfiguration.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.lib.CommonUtil.configuration;
+
+import java.util.List;
+
+import org.eclipse.mosaic.lib.geo.CartesianPoint;
+import org.eclipse.mosaic.lib.geo.GeoPoint;
+
+public class CommonVehicleConfiguration {
+ /**
+ * The route ID on which the vehicle will be spawned.
+ */
+ public String routeID;
+
+ /**
+ * The lane on which the vehicle will be spawned.
+ */
+ public int lane;
+
+ /**
+ * Position within the route where the vehicle should be spawned.
+ */
+ public double position;
+
+ /**
+ * The speed at which the vehicle is supposed to depart.
+ */
+ public double departSpeed;
+
+ /**
+ * The vehicle type
+ */
+ public String vehicleType;
+
+ /**
+ * Specify the applications to be used for this vehicle.
+ */
+ public List applications;
+
+ /**
+ * The geo position at which the vehicle is currently located.
+ */
+ public GeoPoint geoPosition;
+
+ /**
+ * The projected position at which currently the vehicle is located.
+ */
+ public CartesianPoint projectedPosition;
+
+ /**
+ * Vehicle heading in degrees
+ */
+ public Double heading;
+
+ /**
+ * The slope of vehicle in degrees
+ */
+ public double slope;
+
+}
diff --git a/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonInstanceManagerTest.java b/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonInstanceManagerTest.java
new file mode 100644
index 00000000..4e9040b7
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonInstanceManagerTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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.lib.CommonUtil.ambassador;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission;
+import org.eclipse.mosaic.lib.geo.GeoPoint;
+import org.eclipse.mosaic.lib.junit.IpResolverRule;
+import org.eclipse.mosaic.lib.objects.addressing.IpResolver;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import org.mockito.internal.util.reflection.FieldSetter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+import gov.dot.fhwa.saxton.CarmaV2xMessage;
+import gov.dot.fhwa.saxton.TimeSyncMessage;
+
+public class CommonInstanceManagerTest {
+
+ private CommonInstanceManager manager;
+ private CommonInstance instance1;
+ private CommonInstance instance2;
+ private CommonInstance instance3;
+ 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 Logger log = LoggerFactory.getLogger(this.getClass());
+
+ /**
+ * Rule to initialize {@link IpResolver} Singleton.
+ */
+ @Rule
+ public IpResolverRule ipResolverRule = new IpResolverRule();
+
+ @Before
+ public void setUp() throws Exception {
+ manager = new CommonInstanceManager();
+ instance1 = mock(CommonInstance.class);
+ instance2 = mock(CommonInstance.class);
+ instance3 = mock(CommonInstance.class);
+ Map managedInstances = new HashMap<>();
+ managedInstances.put("instance1", instance1);
+ managedInstances.put("instance2", instance2);
+ managedInstances.put("instance3", instance3);
+
+ // Set private instance field to mock using reflection
+ FieldSetter.setField(manager, manager.getClass().getDeclaredField("managedInstances"), managedInstances);
+ }
+
+ @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";
+
+ // Mock the behavior of the registration object
+ CommonRegistrationMessage registration = new CommonRegistrationMessage(
+ infrastructureId,
+ infrastructureId,
+ ipAddressString,
+ rxMessagePort,
+ timeSyncPort);
+ // Ensure checkIfRegistered returns false for infrastructure ID before registering
+ assertFalse( manager.checkIfRegistered(infrastructureId) );
+
+ // Call the onNewRegistration method with the mocked registration object
+ manager.onNewRegistration(registration);
+
+ // Verify that the infrastructure instance was added to the manager
+ assertTrue( manager.checkIfRegistered(infrastructureId) );
+ // Ensure checkIfRegistered returns false for other Ids
+ assertFalse( manager.checkIfRegistered(infrastructureId + "something") );
+ }
+
+ @Test
+ public void testOnTimeStepUpdate() throws IOException {
+ TimeSyncMessage message = new TimeSyncMessage(300, 3);
+
+ Gson gson = new Gson();
+ byte[] message_bytes = gson.toJson(message).getBytes();
+
+ manager.onTimeStepUpdate(message);
+ // Verify that all instances sendTimeSyncMsgs was called.
+ verify(instance1).sendTimeSyncMsg(message_bytes);
+ verify(instance2).sendTimeSyncMsg(message_bytes);
+ verify(instance3).sendTimeSyncMsg(message_bytes);
+
+ }
+ @Test
+ public void testOnTimeStepUpdateWithoutRegisteredIntstances() throws NoSuchFieldException, SecurityException, IOException{
+ // Verify that with no managed instances nothing is called and no exception is thrown.
+ Map managedInstances = new HashMap<>();
+
+ // Set private instance field to mock using reflection
+ FieldSetter.setField(manager, manager.getClass().getDeclaredField("managedInstances"), managedInstances);
+ TimeSyncMessage message = new TimeSyncMessage(300, 3);
+
+ manager.onTimeStepUpdate(message);
+
+ verify(instance1, never()).sendTimeSyncMsg(any());
+ verify(instance2, never()).sendTimeSyncMsg(any());
+ verify(instance3, never()).sendTimeSyncMsg(any());
+
+ }
+ @Test
+ public void testonV2XMessageTx() {
+ CarmaV2xMessage message = new CarmaV2xMessage(sampleMessage.getBytes());
+ // Setup mock addresses for registered carma platform instances
+ InetAddress address1 = mock(InetAddress.class);
+ InetAddress address2 = mock(InetAddress.class);
+ InetAddress address3 = mock(InetAddress.class);
+ when(instance1.getTargetAddress()).thenReturn(address1);
+ when(instance2.getTargetAddress()).thenReturn(address2);
+ when(instance3.getTargetAddress()).thenReturn(address3);
+ // Register host with IpResolver singleton
+ IpResolver.getSingleton().registerHost("veh_0");
+ // Set CarlaRoleName to veh_0 to macth registered host
+ when(instance1.getRoleName()).thenReturn("veh_0");
+ // Set location to origin
+ when(instance1.getLocation()).thenReturn(GeoPoint.ORIGO);
+
+ V2xMessageTransmission messageTx = manager.onV2XMessageTx(address1, message, 1000);
+ assertEquals(1000, messageTx.getTime());
+ assertEquals(GeoPoint.ORIGO, messageTx.getSourcePosition());
+ assertEquals("veh_0", messageTx.getMessage().getRouting().getSource().getSourceName());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testonV2XMessageTxUnregisteredCarmaPlatform() {
+ CarmaV2xMessage message = new CarmaV2xMessage(sampleMessage.getBytes());
+ InetAddress address1 = mock(InetAddress.class);
+ InetAddress address2 = mock(InetAddress.class);
+ InetAddress address3 = mock(InetAddress.class);
+ InetAddress unregisteredAddress = mock(InetAddress.class);
+
+ when(instance1.getTargetAddress()).thenReturn(address1);
+ when(instance2.getTargetAddress()).thenReturn(address2);
+ when(instance3.getTargetAddress()).thenReturn(address3);
+ IpResolver.getSingleton().registerHost("veh_0");
+ when(instance1.getRoleName()).thenReturn("veh_0");
+ when(instance1.getLocation()).thenReturn(GeoPoint.ORIGO);
+ // Attempt to create V2X Message Transmission for unregistered address.
+ // Throws IllegalStateException
+ manager.onV2XMessageTx(unregisteredAddress, message, 1000);
+
+ }
+
+}
diff --git a/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonInstanceTest.java b/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonInstanceTest.java
new file mode 100644
index 00000000..23330fb9
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonInstanceTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.lib.CommonUtil.ambassador;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+
+import org.eclipse.mosaic.lib.geo.GeoPoint;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import org.mockito.internal.util.reflection.FieldSetter;
+
+import com.google.gson.Gson;
+
+import gov.dot.fhwa.saxton.TimeSyncMessage;
+
+public class CommonInstanceTest {
+
+ private DatagramSocket socket;
+
+ private CommonInstance instance;
+
+ private InetAddress address;
+
+ @Before
+ public void setup() throws NoSuchFieldException {
+ // Initialize Mocks
+ address = mock(InetAddress.class);
+ socket = mock(DatagramSocket.class);
+ // Initialize Infrastructure Instance
+
+ instance = new CommonInstance(
+ "SomeID",
+ "SomeRole",
+ address,
+ 3456,
+ 5667);
+ // Set private instance field to mock using reflection
+ FieldSetter.setField(instance, instance.getClass().getDeclaredField("rxMsgsSocket"), socket);
+
+ }
+
+ @Test
+ public void testGetterSetterConstructor() {
+ // Test getters and constructor for setting and retrieving class members
+ assertEquals("SomeID", instance.getVehicleId());
+ assertEquals("SomeRole", instance.getRoleName());
+ assertEquals( GeoPoint.ORIGO, instance.getLocation());
+ assertEquals(3456, instance.getV2xPort());
+ assertEquals(5667, instance.getTimeSyncPort());
+ // Test Setter
+ instance.setVehicleId("DifferentID");
+ assertEquals("DifferentID", instance.getVehicleId());
+ instance.setRoleName("DifferentRole");
+ assertEquals("DifferentRole", instance.getRoleName());
+ instance.setTimeSyncPort(4321);
+ assertEquals(4321, instance.getTimeSyncPort());
+ instance.setV2xPort(5678);
+ assertEquals(5678, instance.getV2xPort());
+ instance.setTargetAddress(address);
+ assertEquals(address, instance.getTargetAddress());
+ }
+
+ @Test
+ public void testSendV2xMsg() throws IOException {
+ // Test SendV2xMsg method
+ String test_msg = "test message";
+ instance.sendV2xMsgs(test_msg.getBytes());
+ // ArgumentCaptor to capture parameters passed to mock on method calls
+ ArgumentCaptor packet = ArgumentCaptor.forClass(DatagramPacket.class);
+ // Verify socket.send(DatagramPacket packet) is called and capture packet
+ // parameter
+ verify(socket, times(1)).send(packet.capture());
+
+ // Verify parameter members
+ assertArrayEquals(test_msg.getBytes(), packet.getValue().getData());
+ assertEquals(instance.getV2xPort(), packet.getValue().getPort());
+ assertEquals(address, packet.getValue().getAddress());
+ }
+
+ @Test
+ public void testSendTimeSyncMsg() throws IOException {
+ // Test SendTimeSyncMsg method
+ TimeSyncMessage test_msg = new TimeSyncMessage(1,100);
+ Gson gson = new Gson();
+ byte[] bytes = gson.toJson(test_msg).getBytes();
+ instance.sendTimeSyncMsg(bytes);
+
+ // ArgumentCaptor to capture parameters passed to mock on method calls
+ ArgumentCaptor packet = ArgumentCaptor.forClass(DatagramPacket.class);
+ // Verify socket.send(DatagramPacket packet) is called and capture packet
+ // parameter
+ verify(socket, times(1)).send(packet.capture());
+ // Convert message to bytes
+ byte[] message_bytes = gson.toJson(test_msg).getBytes();
+ // Verify parameter members
+ assertArrayEquals(message_bytes, packet.getValue().getData());
+ assertEquals(instance.getTimeSyncPort(), packet.getValue().getPort());
+ assertEquals(address, packet.getValue().getAddress());
+ }
+}
diff --git a/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonMessageAmbassadorTest.java b/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonMessageAmbassadorTest.java
new file mode 100644
index 00000000..09ccdfc1
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonMessageAmbassadorTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.lib.CommonUtil.ambassador;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.mosaic.lib.CommonUtil.configuration.CommonConfiguration;
+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.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TemporaryFolder;
+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;
+
+
+public class CommonMessageAmbassadorTest {
+ private final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private final TestFileRule testFileRule = new TestFileRule(temporaryFolder).basedir("carma")
+ .with("carma_config.json", "/carma_config.json");
+
+ @Rule
+ public RuleChain chain = RuleChain.outerRule(temporaryFolder).around(testFileRule);
+
+ private RtiAmbassador rtiMock;
+
+ private CommonMessageAmbassador ambassador;
+
+ @Before
+ public void setup() throws IOException {
+
+ rtiMock = mock(RtiAmbassador.class);
+
+ FederateDescriptor handleMock = mock(FederateDescriptor.class);
+
+ File workingDir = temporaryFolder.getRoot();
+
+ CLocalHost testHostConfig = new CLocalHost();
+
+ testHostConfig.workingDirectory = workingDir.getAbsolutePath();
+
+ when(handleMock.getHost()).thenReturn(testHostConfig);
+
+ when(handleMock.getId()).thenReturn("carma");
+
+ ambassador = new CommonMessageAmbassador(
+ new AmbassadorParameter("carma", testFileRule.get("carma_config.json")), CommonRegistrationMessage.class, CommonConfiguration.class);
+
+ ambassador.setRtiAmbassador(rtiMock);
+
+ ambassador.setFederateDescriptor(handleMock);
+ }
+
+ @Test
+ public void initialize() throws Throwable {
+
+ // RUN
+ ambassador.initialize(0, 100 * TIME.SECOND);
+ // ASSERT
+ verify(rtiMock, times(1)).requestAdvanceTime(eq(0L), eq(0L), eq((byte) 1));
+ }
+
+}
diff --git a/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonRegistrationMessageTest.java b/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonRegistrationMessageTest.java
new file mode 100644
index 00000000..a9ca5cf5
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/ambassador/CommonRegistrationMessageTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.lib.CommonUtil.ambassador;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class CommonRegistrationMessageTest {
+ @Test
+ public void testGetterSettersConstructor() {
+
+ CommonRegistrationMessage message = new CommonRegistrationMessage(
+ "ID",
+ "role",
+ "127.0.0.1",
+ 5678,
+ 8642);
+ // Test Getter
+ assertEquals("ID", message.getVehicleId());
+ assertEquals("role", message.getVehicleRole());
+
+ assertEquals("127.0.0.1", message.getRxMessageIpAddress());
+ assertEquals(5678, message.getRxMessagePort());
+ assertEquals(8642, message.getRxTimeSyncPort());
+ message.setRxMessagePort(7777);
+ message.setRxTimeSyncPort(6666);
+ message.setVehicleId("SOMEID");
+ message.setVehicleRole("SOMEROLL");
+ message.setRxMessageIpAddress("someIP");
+ assertEquals("SOMEID", message.getVehicleId());
+ assertEquals("SOMEROLL", message.getVehicleRole());
+
+ assertEquals("someIP", message.getRxMessageIpAddress());
+ assertEquals(7777, message.getRxMessagePort());
+ assertEquals(6666, message.getRxTimeSyncPort());
+
+ }
+}
diff --git a/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/configuration/CommonConfigurationTest.java b/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/configuration/CommonConfigurationTest.java
new file mode 100644
index 00000000..8bccc76f
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/test/java/org/eclipse/mosaic/lib/CommonUtil/configuration/CommonConfigurationTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.lib.CommonUtil.configuration;
+
+import java.io.InputStreamReader;
+import java.lang.reflect.Type;
+
+import org.eclipse.mosaic.lib.geo.MutableCartesianPoint;
+import org.eclipse.mosaic.lib.geo.MutableGeoPoint;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import org.junit.Test;
+
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+
+public class CommonConfigurationTest {
+
+ @Test
+ public void readConfig_assertProperties() throws InstantiationException{
+
+ String validConfig = "/carma_config.json";
+ CommonConfiguration commonConfiguration = geCommonConfiguration(validConfig);
+
+ assertNotNull(commonConfiguration); // assert that configuration is created
+ assertEquals(Long.valueOf(100L), commonConfiguration.updateInterval);
+ assertEquals("0", commonConfiguration.Vehicles.get(0).routeID);
+ assertEquals(1, commonConfiguration.Vehicles.get(0).lane);
+ assertEquals(Double.valueOf(0.0D), commonConfiguration.Vehicles.get(0).position, 0.001);
+ assertEquals(Double.valueOf(0.0D), commonConfiguration.Vehicles.get(0).departSpeed, 0.001);
+ assertEquals("vehicle.chevrolet.impala", commonConfiguration.Vehicles.get(0).vehicleType);
+ assertEquals("org.eclipse.mosaic.app.tutorial.VehicleCommunicationApp",
+ commonConfiguration.Vehicles.get(0).applications.get(0));
+ assertEquals(new MutableGeoPoint(52.579272059028646, 13.467165499469328),
+ commonConfiguration.Vehicles.get(0).geoPosition);
+ assertEquals(new MutableCartesianPoint(501.62, 116.95, 0.0),
+ commonConfiguration.Vehicles.get(0).projectedPosition);
+ assertEquals(Double.valueOf(24.204351784500364D), commonConfiguration.Vehicles.get(0).heading);
+ assertEquals(Double.valueOf(0.0), commonConfiguration.Vehicles.get(0).slope, 0.001);
+ assertEquals("carma_0", commonConfiguration.senderVehicleId);
+ }
+
+ private CommonConfiguration geCommonConfiguration(String filePath) throws InstantiationException{
+ Type type = new TypeToken>() {}.getType();
+ return new Gson().fromJson(new InputStreamReader(getClass().getResourceAsStream(filePath)), type);
+ }
+}
diff --git a/co-simulation/lib/mosaic-common-utils/src/test/resources/carma_config.json b/co-simulation/lib/mosaic-common-utils/src/test/resources/carma_config.json
new file mode 100644
index 00000000..b40a8049
--- /dev/null
+++ b/co-simulation/lib/mosaic-common-utils/src/test/resources/carma_config.json
@@ -0,0 +1,25 @@
+{
+ "updateInterval": 100,
+ "Vehicles":[
+{
+ "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
+}],
+"senderVehicleId":"carma_0"
+}
diff --git a/co-simulation/lib/mosaic-utils/pom.xml b/co-simulation/lib/mosaic-utils/pom.xml
index ebe6ccc5..20affba4 100644
--- a/co-simulation/lib/mosaic-utils/pom.xml
+++ b/co-simulation/lib/mosaic-utils/pom.xml
@@ -81,4 +81,4 @@
-
+
\ No newline at end of file
diff --git a/co-simulation/pom.xml b/co-simulation/pom.xml
index 5faa7301..c54156ad 100644
--- a/co-simulation/pom.xml
+++ b/co-simulation/pom.xml
@@ -51,6 +51,7 @@
rti/mosaic-starter
lib/mosaic-communication
+ lib/mosaic-common-utils
lib/mosaic-database
lib/mosaic-docker
lib/mosaic-geomath
@@ -73,6 +74,7 @@
fed/mosaic-carma
fed/mosaic-infrastructure
fed/mosaic-carma-cloud
+ fed/mosaic-carma-messenger
test/mosaic-integration-tests
@@ -501,6 +503,7 @@
**/ClientServerChannelProtos.java
fed/mosaic-carma/**
fed/mosaic-carla/**
+ fed/mosaic-carma-messenger/**
true
${project.build.sourceEncoding}
@@ -595,28 +598,28 @@
true
-
- org.jacoco
- jacoco-maven-plugin
- 0.8.6
-
- ${skip.coverage}
-
-
-
-
- prepare-agent
-
-
-
- report
- prepare-package
-
- report
-
-
-
-
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.6
+
+ ${skip.coverage}
+
+
+
+
+ prepare-agent
+
+
+
+ report
+ prepare-package
+
+ report
+
+
+
+