From cea762d290c6107fbc20fcdaa6dd2391e0984c28 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:55:29 +0000 Subject: [PATCH] Optional SENSOR_CONFIG_JSON_FILE (#616) # PR Details ## Description This PR makes the **SENSOR_JSON_FILE_PATH** an optional environment variable in simulation since spawning sensors is not required for the base line functionality of V2X-Hub. Prior to this PR, the CDASimAdapter would accept empty **SENSOR_JSON_FILE_PATH** files but not missing ones. ## Related Issue [VH-1320 ](https://usdot-carma.atlassian.net/browse/VH-1320) ## Motivation and Context Improve usability by making CDASimAdapterPlugin work without **SENSOR_JSON_FILE_PATH** optional environment variable ## How Has This Been Tested? Tested in CDASim Cloud deployment ## Types of changes - [x] Defect fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that cause existing functionality to change) ## Checklist: - [ ] I have added any new packages to the sonar-scanner.properties file - [x] My change requires a change to the documentation. - [x] I have updated the documentation accordingly. - [x] I have read the **CONTRIBUTING** document. [V2XHUB Contributing Guide](https://github.com/usdot-fhwa-OPS/V2X-Hub/blob/develop/Contributing.md) - [x] I have added tests to cover my changes. - [x] All new and existing tests passed. --- configuration/README.md | 26 ++++--- .../src/simulation/SimulationEnvUtils.cpp | 12 ++-- .../src/simulation/SimulationEnvUtils.h | 10 ++- .../TmxUtils/test/TestSimulationEnvUtils.cpp | 71 +++++++++++++++++++ .../CDASimAdapter/src/CDASimAdapter.cpp | 5 +- .../CDASimAdapter/src/CDASimConnection.cpp | 22 +++--- .../src/include/CDASimConnection.hpp | 11 +-- ...onnection.cpp => TestCDASimConnection.cpp} | 41 +++++++---- 8 files changed, 151 insertions(+), 47 deletions(-) create mode 100644 src/tmx/TmxUtils/test/TestSimulationEnvUtils.cpp rename src/v2i-hub/CDASimAdapter/test/{TestCARMASimulationConnection.cpp => TestCDASimConnection.cpp} (74%) diff --git a/configuration/README.md b/configuration/README.md index 7b978c398..d1ff943de 100644 --- a/configuration/README.md +++ b/configuration/README.md @@ -44,14 +44,18 @@ Enter the login credentials you created in step 5b and login. Installation complete! ### Simulation Setup -To support execution in a simulated environment, V2X-Hub is in the process of integrating with CDASim, a Co-Simulation tool built as an extension of Eclipse Mosiac. This extension will incorporate integration with several other platforms including CARMA-Platform and CARLA. The setup for this simply requires setting environment variables inside the V2X-Hub docker container. - * **SIMULATION_MODE** – Environment variable for enabling simulation components for V2X-Hub. If set to "true" or "TRUE" simulation components will be enable. Otherwise, simulation components will not be enabled. - * **KAFKA_BROKER_ADDRESS** – Environment variable for storing Kafka broker connection string (including port). - * **TIME_SYNC_TOPIC** – Environment variable for storing Kafka time sync topic. - * **SIMULATION_IP** – Environment variable for storing IP address of CDASim application. - * **SIMULATION_REGISTRATION_PORT** – Environment variable for storing port on CDASim that handles registration attempts. - * **TIME_SYNC_PORT** – Environment varaible for storing port for receiving time sync messages from CDASim. - * **V2X_PORT** – Environment variable for storing port for receiving v2x messages from CDASim - * **SIM_V2X_PORT** – Environment variable for storing port for sending v2x messages to CDASim - * **LOCAL_IP** – Environment variable for storing local IP of V2X Hub. - * **INFRASTRUCTURE_ID** – Environment variable for storing infrastructure id of V2X Hub.. \ No newline at end of file + +To support execution in a simulated environment, V2X-Hub is in the process of integrating with CDASim, a Co-Simulation tool built as an extension of Eclipse Mosiac. This extension will incorporate integration with several other platforms including CARMA-Platform and CARLA. The setup for this simply requires setting environment variables for the V2X-Hub docker compose deployment. These can be set via the `initialization.sh` script and can be manually edited after. + +* **V2XHUB_VERSION** – Version of V2X-Hub to deloy ( Docker Tag/ GitHub Tag ) +* **SIMULATION_MODE** – Environment variable for enabling simulation components for V2X-Hub. If set to "true" or "TRUE" simulation components will be enable. Otherwise, simulation components will not be enabled. +* **KAFKA_BROKER_ADDRESS** – Environment variable for storing Kafka broker connection string (including port). +* **TIME_SYNC_TOPIC** – Environment variable for storing Kafka time sync topic. +* **SIMULATION_IP** – Environment variable for storing IP address of CDASim application. +* **SIMULATION_REGISTRATION_PORT** – Environment variable for storing port on CDASim that handles registration attempts. +* **TIME_SYNC_PORT** – Environment varaible for storing port for receiving time sync messages from CDASim. +* **V2X_PORT** – Environment variable for storing port for receiving v2x messages from CDASim +* **SIM_V2X_PORT** – Environment variable for storing port for sending v2x messages to CDASim +* **V2XHUB_IP** – Environment variable for storing IP address of V2X Hub. +* **INFRASTRUCTURE_ID** – Environment variable for storing infrastructure id of V2X Hub. +* **SENSOR_JSON_FILE_PATH** – Environment variable for storing path to sensor configuration file. This is an optional simulation environment variable that allows for setting up simulated sensor for a V2X-Hub instance. Example file can be found in the **CDASimAdapterPlugin** tests [here](../src/v2i-hub/CDASimAdapter/test/sensors.json). diff --git a/src/tmx/TmxUtils/src/simulation/SimulationEnvUtils.cpp b/src/tmx/TmxUtils/src/simulation/SimulationEnvUtils.cpp index 0913db6e7..a659b3502 100644 --- a/src/tmx/TmxUtils/src/simulation/SimulationEnvUtils.cpp +++ b/src/tmx/TmxUtils/src/simulation/SimulationEnvUtils.cpp @@ -1,4 +1,4 @@ -#include +#include "simulation/SimulationEnvUtils.h" namespace tmx::utils::sim{ bool is_simulation_mode() { @@ -13,15 +13,17 @@ namespace tmx::utils::sim{ } - std::string get_sim_config(const char *config_name) { + std::string get_sim_config(const char *config_name, bool required) { if (is_simulation_mode() && config_name) { - try { + if ( std::getenv(config_name) != nullptr) { std::string config = std::getenv(config_name); return config; } - catch(const std::logic_error &e) { + else { std::string config_name_str = config_name; - throw TmxException("Simulation Config " + config_name_str + " not set!"); + if ( required ) { + throw TmxException("Required simulation config " + config_name_str + " not set!"); + } } } else { throw TmxException("V2X-Hub not in sumulation mode or config param name is null pointer!"); diff --git a/src/tmx/TmxUtils/src/simulation/SimulationEnvUtils.h b/src/tmx/TmxUtils/src/simulation/SimulationEnvUtils.h index efffc4ffa..6b2822fd8 100644 --- a/src/tmx/TmxUtils/src/simulation/SimulationEnvUtils.h +++ b/src/tmx/TmxUtils/src/simulation/SimulationEnvUtils.h @@ -77,7 +77,13 @@ namespace tmx::utils::sim{ * @return true if SIMULATION_MODE is "true" or "TRUE" and false otherwise. */ bool is_simulation_mode(); - - std::string get_sim_config(const char *config_name); + /** + * @brief Get simulation configuration. + * @param config_name Name of configuration parameter + * @param required bool flag whether configuration paramter is required (Default: true) + * @throws tmx::TmxException if required configuration is unset or system not in simulation mode. + * @return string value of sim configuration + */ + std::string get_sim_config(const char *config_name, bool required = true); } \ No newline at end of file diff --git a/src/tmx/TmxUtils/test/TestSimulationEnvUtils.cpp b/src/tmx/TmxUtils/test/TestSimulationEnvUtils.cpp new file mode 100644 index 000000000..9232c0c62 --- /dev/null +++ b/src/tmx/TmxUtils/test/TestSimulationEnvUtils.cpp @@ -0,0 +1,71 @@ +#include +#include +#include + + +TEST(testSimulationEnvUtils, is_simulation_mode_unset) +{ + // Unset any environment set SIMULATION_MODE + unsetenv(tmx::utils::sim::SIMULATION_MODE); + EXPECT_FALSE(tmx::utils::sim::is_simulation_mode()); +} + +TEST(testSimulationEnvUtils, is_simulation_mode_true) { + setenv(tmx::utils::sim::SIMULATION_MODE, "true", 1); + EXPECT_TRUE(tmx::utils::sim::is_simulation_mode()); + setenv(tmx::utils::sim::SIMULATION_MODE, "TRUE", 1); + EXPECT_TRUE(tmx::utils::sim::is_simulation_mode()); +} + +TEST(testSimulationEnvUtils, is_simulation_mode_false) { + setenv(tmx::utils::sim::SIMULATION_MODE, "false", 1); + EXPECT_FALSE(tmx::utils::sim::is_simulation_mode()); + setenv(tmx::utils::sim::SIMULATION_MODE, "FALSE", 1); + EXPECT_FALSE(tmx::utils::sim::is_simulation_mode()); +} + +TEST(testSimulationEnvUtils, get_sim_config_non_simulation_mode) { + // Precondition for test (ASSERT) + ASSERT_FALSE(tmx::utils::sim::is_simulation_mode()); + EXPECT_THROW(tmx::utils::sim::get_sim_config(tmx::utils::sim::SIMULATION_IP), tmx::TmxException ); + +} + +TEST(testSimulationEnvUtils, get_sim_config_unset) { + // Precondition for test (ASSERT) + setenv(tmx::utils::sim::SIMULATION_MODE, "true", 1); + unsetenv(tmx::utils::sim::SIMULATION_IP); + ASSERT_TRUE(tmx::utils::sim::is_simulation_mode()); + + EXPECT_THROW(tmx::utils::sim::get_sim_config(tmx::utils::sim::SIMULATION_IP), tmx::TmxException ); +} + +TEST(testSimulationEnvUtils, get_sim_config_set) { + // Precondition for test (ASSERT) + std::string simulation_ip = "127.0.0.1"; + setenv(tmx::utils::sim::SIMULATION_MODE, "true", 1); + setenv(tmx::utils::sim::SIMULATION_IP, simulation_ip.c_str(), 1); + ASSERT_TRUE(tmx::utils::sim::is_simulation_mode()); + + EXPECT_EQ(tmx::utils::sim::get_sim_config(tmx::utils::sim::SIMULATION_IP), simulation_ip ); +} + +TEST(testSimulationEnvUtils, get_sim_config_optional_set) { + // Precondition for test (ASSERT) + std::string simulation_ip = "127.0.0.1"; + setenv(tmx::utils::sim::SIMULATION_MODE, "true", 1); + setenv(tmx::utils::sim::SIMULATION_IP, simulation_ip.c_str(), 1); + ASSERT_TRUE(tmx::utils::sim::is_simulation_mode()); + + EXPECT_EQ(tmx::utils::sim::get_sim_config(tmx::utils::sim::SIMULATION_IP,false), simulation_ip ); +} + +TEST(testSimulationEnvUtils, get_sim_config_optional_unset) { + // Precondition for test (ASSERT) + std::string simulation_ip = "127.0.0.1"; + setenv(tmx::utils::sim::SIMULATION_MODE, "true", 1); + unsetenv(tmx::utils::sim::SIMULATION_IP); + ASSERT_TRUE(tmx::utils::sim::is_simulation_mode()); + + EXPECT_TRUE(tmx::utils::sim::get_sim_config(tmx::utils::sim::SIMULATION_IP,false).empty()); +} \ No newline at end of file diff --git a/src/v2i-hub/CDASimAdapter/src/CDASimAdapter.cpp b/src/v2i-hub/CDASimAdapter/src/CDASimAdapter.cpp index 1986bb608..38eb994ab 100644 --- a/src/v2i-hub/CDASimAdapter/src/CDASimAdapter.cpp +++ b/src/v2i-hub/CDASimAdapter/src/CDASimAdapter.cpp @@ -103,7 +103,10 @@ namespace CDASimAdapter{ uint v2x_port = std::stoul(sim::get_sim_config(sim::V2X_PORT)); uint sim_v2x_port = std::stoul(sim::get_sim_config(sim::SIM_V2X_PORT)); std::string infrastructure_id = sim::get_sim_config(sim::INFRASTRUCTURE_ID); - std::string sensor_json_file_path = sim::get_sim_config(sim::SENSOR_JSON_FILE_PATH); + // Sensor JSON file path is an optional environment variable that allows configuration of + // simulated sensor if provided. + std::string sensor_json_file_path = ""; + sensor_json_file_path = sim::get_sim_config(sim::SENSOR_JSON_FILE_PATH, false); PLOG(logINFO) << "CDASim connecting " << simulation_ip << "\nUsing Registration Port : " << std::to_string( simulation_registration_port) << diff --git a/src/v2i-hub/CDASimAdapter/src/CDASimConnection.cpp b/src/v2i-hub/CDASimAdapter/src/CDASimConnection.cpp index 982ac5f64..07b26e516 100644 --- a/src/v2i-hub/CDASimAdapter/src/CDASimConnection.cpp +++ b/src/v2i-hub/CDASimAdapter/src/CDASimConnection.cpp @@ -6,7 +6,7 @@ using namespace tmx::utils; namespace CDASimAdapter{ CDASimConnection::CDASimConnection(const std::string &simulation_ip, const std::string &infrastructure_id, const uint simulation_registration_port, const uint sim_v2x_port, const std::string &local_ip, const uint time_sync_port,const uint simulated_interaction_port, const uint v2x_port, - const Point &location, const std::string &sensor_json_file_path) : + const Point &location, const std::string &sensor_json_file_path ) : _simulation_ip(simulation_ip), _infrastructure_id(infrastructure_id), _simulation_registration_port(simulation_registration_port), _simulation_v2x_port(sim_v2x_port), _local_ip(local_ip), _time_sync_port(time_sync_port), _simulated_interaction_port(simulated_interaction_port),_v2x_port(v2x_port), _location(location) ,_sensor_json_file_path(sensor_json_file_path) { @@ -52,12 +52,18 @@ namespace CDASimAdapter{ //Read local sensor file and populate the sensors JSON //Sample sensors.json: https://raw.githubusercontent.com/usdot-fhwa-OPS/V2X-Hub/develop/src/v2i-hub/CDASimAdapter/test/sensors.json - auto sensors_json_v = read_json_file(_sensor_json_file_path); - if(sensors_json_v.empty()) - { - PLOG(logWARNING) << "Sensors JSON is empty!" << std::endl; - } - message["sensors"] = sensors_json_v; + // Sensor configuration is an optional part of registration message. + if ( !_sensor_json_file_path.empty() ) { + auto sensors_json_v = read_json_file(_sensor_json_file_path); + if(sensors_json_v.empty()) + { + PLOG(logWARNING) << "Sensors JSON is empty!" << std::endl; + } + message["sensors"] = sensors_json_v; + } + else { + PLOG(logWARNING) << "No sensors where configured for this V2X-Hub instance."; + } Json::StyledWriter writer; message_str = writer.write(message); return message_str; @@ -97,10 +103,8 @@ namespace CDASimAdapter{ // Initialize V2X-Hub UDP Server and Client to foward V2X messages between CARMA Simulation Infrastructure // Adapter and V2X-Hub. // TODO: Using TMX Utils get immediate forward port - // TODO: Replace 0 with immediate forward port immediate_forward_listener = std::make_shared( local_ip, 5678); // TODO: Using TMX Utils get message receiver port - // TODO: Replace 0 with message receiver port message_receiver_publisher = std::make_shared( local_ip, 8765); // Initialize UDP Server for listening for incoming CARMA-Simulation time synchronization. PLOG(logDEBUG) << "Creating UDPServer for Time Sync Messages: " << local_ip << ":" << std::to_string(time_sync_port) << "\n" diff --git a/src/v2i-hub/CDASimAdapter/src/include/CDASimConnection.hpp b/src/v2i-hub/CDASimAdapter/src/include/CDASimConnection.hpp index b6bb55424..2c8a03bf5 100644 --- a/src/v2i-hub/CDASimAdapter/src/include/CDASimConnection.hpp +++ b/src/v2i-hub/CDASimAdapter/src/include/CDASimConnection.hpp @@ -35,7 +35,7 @@ namespace CDASimAdapter { */ explicit CDASimConnection( const std::string &simulation_ip, const std::string &infrastructure_id, const uint simulation_registration_port, const uint sim_v2x_port, const std::string &local_ip, const uint time_sync_port, const uint simulated_interaction_port, const uint v2x_port, - const tmx::utils::Point &location, const std::string &sensor_json_file_path); + const tmx::utils::Point &location, const std::string &sensor_json_file_path = ""); /** * @brief Method to forward v2x message to CARMA Simulation * @param v2x_message string @@ -173,9 +173,12 @@ namespace CDASimAdapter { std::shared_ptr time_sync_listener; std::shared_ptr sensor_detected_object_listener; - FRIEND_TEST(TestCARMASimulationConnection, get_handshake_json); - FRIEND_TEST(TestCARMASimulationConnection, read_json_file); - FRIEND_TEST(TestCARMASimulationConnection, string_to_json); + FRIEND_TEST(TestCDASimConnection, get_handshake_json); + FRIEND_TEST(TestCDASimConnection, get_handshake_json_no_sensor_config); + FRIEND_TEST(TestCDASimConnection, read_json_file); + FRIEND_TEST(TestCDASimConnection, string_to_json); + + }; } diff --git a/src/v2i-hub/CDASimAdapter/test/TestCARMASimulationConnection.cpp b/src/v2i-hub/CDASimAdapter/test/TestCDASimConnection.cpp similarity index 74% rename from src/v2i-hub/CDASimAdapter/test/TestCARMASimulationConnection.cpp rename to src/v2i-hub/CDASimAdapter/test/TestCDASimConnection.cpp index 2e992933e..c1ec10d4d 100644 --- a/src/v2i-hub/CDASimAdapter/test/TestCARMASimulationConnection.cpp +++ b/src/v2i-hub/CDASimAdapter/test/TestCDASimConnection.cpp @@ -19,7 +19,7 @@ using namespace tmx::utils; namespace CDASimAdapter { - class TestCARMASimulationConnection : public ::testing::Test { + class TestCDASimConnection : public ::testing::Test { protected: void SetUp() override { // Initialize CARMA Simulation connection with (0,0,0) location. @@ -35,11 +35,11 @@ namespace CDASimAdapter { }; - TEST_F( TestCARMASimulationConnection, initialize) { + TEST_F( TestCDASimConnection, initialize) { ASSERT_FALSE(connection->is_connected()); } - TEST_F( TestCARMASimulationConnection, forward_message) { + TEST_F( TestCDASimConnection, forward_message) { std::shared_ptr client = std::make_shared(); std::string test_message = "message"; EXPECT_CALL( *client, Send(test_message) ).Times(2).WillOnce(testing::DoAll(Return(-1))).WillRepeatedly(testing::DoAll(Return(test_message.size()))); @@ -47,7 +47,7 @@ namespace CDASimAdapter { connection->forward_message(test_message, client); } - TEST_F( TestCARMASimulationConnection, forward_message_invalid ) { + TEST_F( TestCDASimConnection, forward_message_invalid ) { std::shared_ptr client = std::make_shared(); std::string test_message = ""; // ASSERT that we never call Send message. @@ -59,11 +59,11 @@ namespace CDASimAdapter { connection->forward_message(test_message, client); } - TEST_F( TestCARMASimulationConnection, consume_msg){ + TEST_F( TestCDASimConnection, consume_msg){ std::shared_ptr server = std::make_shared(); char *msg_data = new char(); - char *test_string = "Test Message"; + char test_string[] = "Test Message"; EXPECT_CALL( *server, TimedReceive(_, _, _) ).Times(2). WillOnce(testing::DoAll(Return(-1))). WillRepeatedly( testing::DoAll( SetArrayArgument<0>(test_string, test_string + strlen(test_string) + 1),Return(10))); @@ -78,11 +78,11 @@ namespace CDASimAdapter { } - TEST_F( TestCARMASimulationConnection, setup_upd_connection) { + TEST_F( TestCDASimConnection, setup_upd_connection) { ASSERT_TRUE(connection->setup_udp_connection("127.0.0.1", "127.0.0.1", 4567, 4568, 4569, 4570)); } - TEST_F( TestCARMASimulationConnection, get_handshake_json) { + TEST_F( TestCDASimConnection, get_handshake_json) { Point location; location.X = 1000; location.Y = 38.955; @@ -91,23 +91,34 @@ namespace CDASimAdapter { in_strm.open(sensors_file_path, std::ifstream::binary); if(in_strm.is_open()) { - ASSERT_EQ(connection->get_handshake_json("4566", "127.0.0.1", 4567, 4568, 4569, location), + EXPECT_EQ(connection->get_handshake_json("4566", "127.0.0.1", 4567, 4568, 4569, location), "{\n \"infrastructureId\" : \"4566\",\n \"location\" : {\n \"x\" : 1000.0,\n \"y\" : 38.954999999999998,\n \"z\" : -77.149000000000001\n },\n \"rxMessageIpAddress\" : \"127.0.0.1\",\n \"rxMessagePort\" : 4569,\n \"sensors\" : [\n {\n \"location\" : {\n \"x\" : 0.0,\n \"y\" : 0.0,\n \"z\" : 0.0\n },\n \"orientation\" : {\n \"pitch\" : 0.0,\n \"roll\" : 0.0,\n \"yaw\" : 0.0\n },\n \"sensorId\" : \"SomeID\",\n \"type\" : \"SemanticLidar\"\n },\n {\n \"location\" : {\n \"x\" : 1.0,\n \"y\" : 2.0,\n \"z\" : 0.0\n },\n \"orientation\" : {\n \"pitch\" : 0.0,\n \"roll\" : 0.0,\n \"yaw\" : 23.0\n },\n \"sensorId\" : \"SomeID2\",\n \"type\" : \"SemanticLidar\"\n }\n ],\n \"simulatedInteractionPort\" : 4568,\n \"timeSyncPort\" : 4567\n}\n"); } } - TEST_F( TestCARMASimulationConnection, carma_simulation_handshake) { + TEST_F( TestCDASimConnection, get_handshake_json_no_sensor_config) { + Point location; + location.X = 1000; + location.Y = 38.955; + location.Z = -77.149; + connection.reset(new CDASimConnection("127.0.0.1", "1212", 4567, 4678, "127.0.0.1", 1213, 1214, 1215, location)); + // Test method when sensors_file_path is empty + EXPECT_EQ(connection->get_handshake_json("4566", "127.0.0.1", 4567, 4568, 4569, location), + "{\n \"infrastructureId\" : \"4566\",\n \"location\" : {\n \"x\" : 1000.0,\n \"y\" : 38.954999999999998,\n \"z\" : -77.149000000000001\n },\n \"rxMessageIpAddress\" : \"127.0.0.1\",\n \"rxMessagePort\" : 4569,\n \"simulatedInteractionPort\" : 4568,\n \"timeSyncPort\" : 4567\n}\n"); + } + + TEST_F( TestCDASimConnection, carma_simulation_handshake) { Point location; // UDP creation error - ASSERT_FALSE(connection->carma_simulation_handshake("", "45", NULL, + EXPECT_FALSE(connection->carma_simulation_handshake("", "45", 0, "", 45, 45, 45, location)); } - TEST_F(TestCARMASimulationConnection, connect) { - ASSERT_TRUE(connection->connect()); + TEST_F(TestCDASimConnection, connect) { + EXPECT_TRUE(connection->connect()); } - TEST_F(TestCARMASimulationConnection, read_json_file) + TEST_F(TestCDASimConnection, read_json_file) { auto sensorJsonV = connection->read_json_file("Invalid_file_path" ); ASSERT_TRUE(sensorJsonV.empty()); @@ -120,7 +131,7 @@ namespace CDASimAdapter { } } - TEST_F(TestCARMASimulationConnection, string_to_json) + TEST_F(TestCDASimConnection, string_to_json) { auto sensorJsonV = connection->string_to_json("Invalid Json"); ASSERT_TRUE(sensorJsonV.empty());