Skip to content

Commit

Permalink
Optional SENSOR_CONFIG_JSON_FILE (#616)
Browse files Browse the repository at this point in the history
<!-- Thanks for the contribution, this is awesome. -->

# 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.
<!--- Describe your changes in detail -->

## Related Issue
[VH-1320
](https://usdot-carma.atlassian.net/browse/VH-1320)<!--- This project
only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an
issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps
to reproduce -->
<!--- Please link to the issue here: -->

## Motivation and Context
Improve usability by making CDASimAdapterPlugin work without
**SENSOR_JSON_FILE_PATH** optional environment variable
<!--- Why is this change required? What problem does it solve? -->

## How Has This Been Tested?
Tested in CDASim Cloud deployment 
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->

- [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:

<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->

- [ ] I have added any new packages to the sonar-scanner.properties file
- [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.
  • Loading branch information
paulbourelly999 authored Jun 12, 2024
1 parent 165cb55 commit cea762d
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 47 deletions.
26 changes: 15 additions & 11 deletions configuration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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..

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).
12 changes: 7 additions & 5 deletions src/tmx/TmxUtils/src/simulation/SimulationEnvUtils.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include <simulation/SimulationEnvUtils.h>
#include "simulation/SimulationEnvUtils.h"

namespace tmx::utils::sim{
bool is_simulation_mode() {
Expand All @@ -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!");
Expand Down
10 changes: 8 additions & 2 deletions src/tmx/TmxUtils/src/simulation/SimulationEnvUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

}
71 changes: 71 additions & 0 deletions src/tmx/TmxUtils/test/TestSimulationEnvUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include <simulation/SimulationEnvUtils.h>
#include <gtest/gtest.h>
#include <stdlib.h>


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());
}
5 changes: 4 additions & 1 deletion src/v2i-hub/CDASimAdapter/src/CDASimAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) <<
Expand Down
22 changes: 13 additions & 9 deletions src/v2i-hub/CDASimAdapter/src/CDASimConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<UdpServer>( local_ip, 5678);
// TODO: Using TMX Utils get message receiver port
// TODO: Replace 0 with message receiver port
message_receiver_publisher = std::make_shared<UdpClient>( 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"
Expand Down
11 changes: 7 additions & 4 deletions src/v2i-hub/CDASimAdapter/src/include/CDASimConnection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -173,9 +173,12 @@ namespace CDASimAdapter {
std::shared_ptr<tmx::utils::UdpServer> time_sync_listener;
std::shared_ptr<tmx::utils::UdpServer> 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);


};

}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -35,19 +35,19 @@ 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<MockUpdClient> client = std::make_shared<MockUpdClient>();
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())));
ASSERT_THROW(connection->forward_message(test_message, client), UdpClientRuntimeError);
connection->forward_message(test_message, client);
}

TEST_F( TestCARMASimulationConnection, forward_message_invalid ) {
TEST_F( TestCDASimConnection, forward_message_invalid ) {
std::shared_ptr<MockUpdClient> client = std::make_shared<MockUpdClient>();
std::string test_message = "";
// ASSERT that we never call Send message.
Expand All @@ -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<MockUpdServer> server = std::make_shared<MockUpdServer>();
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)));
Expand All @@ -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;
Expand All @@ -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());
Expand All @@ -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());
Expand Down

0 comments on commit cea762d

Please sign in to comment.