Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vh 1216 fix spat plugin segfault simulation #621

Merged
merged 31 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/tmx/Messages/test/Main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Copyright (C) 2024 LEIDOS.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this 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.
*/

#include <gtest/gtest.h>

int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
10 changes: 10 additions & 0 deletions src/tmx/Messages/test/TestTimeSyncMessage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <gtest/gtest.h>
#include "TimeSyncMessage.h"
namespace tmx::messages {

TEST(TestTimeSyncMessage, to_string) {
TimeSyncMessage msg(20, 30);
std::string json = "{ \"timestep\":20, \"seq\":30}";
EXPECT_EQ( json, msg.to_string());
}
}
2 changes: 1 addition & 1 deletion src/tmx/TmxUtils/src/MockUdpServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ namespace tmx::utils {
MOCK_METHOD(std::string, GetAddress, (), (const, override));
MOCK_METHOD(int, Receive, (char *msg, size_t maxSize), (override));
MOCK_METHOD(int, GetSocket, (), (override, const));
MOCK_METHOD(std::string, stringTimedReceive, (int maxWait_ms), (override));
};

}
13 changes: 9 additions & 4 deletions src/tmx/TmxUtils/src/PluginClientClockAware.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ namespace tmx::utils {
clock = std::make_shared<CarmaClock>(_simulation_mode);
if (_simulation_mode) {
AddMessageFilter<tmx::messages::TimeSyncMessage>(this, &PluginClientClockAware::HandleTimeSyncMessage);

SubscribeToMessages();
}

}


void PluginClientClockAware::HandleTimeSyncMessage(tmx::messages::TimeSyncMessage &msg, routeable_message &routeableMsg ) {
PLOG(logDEBUG) << "Message Received " << msg.to_string() << std::endl;
this->getClock()->update( msg.get_timestep() );
if (sim::is_simulation_mode() ) {
if (_simulation_mode ) {
PLOG(logDEBUG) << "Message Received " << msg.to_string() << std::endl;
clock->update( msg.get_timestep() );
paulbourelly999 marked this conversation as resolved.
Show resolved Hide resolved
dan-du-car marked this conversation as resolved.
Show resolved Hide resolved
SetStatus(Key_Simulation_Time_Step, Clock::ToUtcPreciseTimeString(msg.get_timestep()));
}
}
Expand All @@ -39,4 +39,9 @@ namespace tmx::utils {
return _simulation_mode;
}

std::shared_ptr<fwha_stol::lib::time::CarmaClock> PluginClientClockAware::getClock() const {
clock->wait_for_initialization(); // Blocks until first call to update when in sim mode.
return clock;
}

}
11 changes: 5 additions & 6 deletions src/tmx/TmxUtils/src/PluginClientClockAware.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,14 @@ class PluginClientClockAware : public PluginClient {

protected:
/**
* @brief Method for child classes to use to retrieve the clock object and get the simulation or real time.
* @return
* @brief Method for child classes to use to retrieve the clock object and get the simulation or real time.
dan-du-car marked this conversation as resolved.
Show resolved Hide resolved
* In simulation mode method will block until first time update has been received.
* @return clock
*/
inline std::shared_ptr<fwha_stol::lib::time::CarmaClock> getClock() const {
return clock;
}
std::shared_ptr<fwha_stol::lib::time::CarmaClock> getClock() const ;

void OnStateChange(IvpPluginState state) override;

bool isSimulationMode() const;


Expand Down
66 changes: 46 additions & 20 deletions src/tmx/TmxUtils/src/SNMPClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,27 +165,17 @@ namespace tmx::utils
PLOG(logINFO) << "Response request status: " << status << " (=" << (status == STAT_SUCCESS ? "SUCCESS" : "FAILED") << ")";

// Check GET response
if (status == STAT_SUCCESS && response && response->errstat == SNMP_ERR_NOERROR && request_type == request_type::GET)
if (status == STAT_SUCCESS && response && response->errstat == SNMP_ERR_NOERROR )
{
for (auto vars = response->variables; vars; vars = vars->next_variable)
{
// Get value of variable depending on ASN.1 type
// Variable could be a integer, string, bitstring, ojbid, counter : defined here https://github.com/net-snmp/net-snmp/blob/master/include/net-snmp/types.h
// get Integer value
if (vars->type == ASN_INTEGER && vars->val.integer)
{
val.type = snmp_response_obj::response_type::INTEGER;
val.val_int = *vars->val.integer;
}
else if (vars->type == ASN_OCTET_STR && vars->val.string)
{
size_t str_len = vars->val_len;
for (size_t i = 0; i < str_len; ++i)
{
val.val_string.push_back(vars->val.string[i]);
}
val.type = snmp_response_obj::response_type::STRING;
}
if ( request_type == request_type::GET ) {
process_snmp_get_response(val, *response);
dan-du-car marked this conversation as resolved.
Show resolved Hide resolved
}
else if( request_type == request_type::SET){
process_snmp_set_response(val, input_oid);
dan-du-car marked this conversation as resolved.
Show resolved Hide resolved
}
else {
log_error(status, request_type, response);
return false;
}
}
else
Expand All @@ -208,6 +198,42 @@ namespace tmx::utils
return port_;
}

void snmp_client::process_snmp_get_response(snmp_response_obj &val, const snmp_pdu &response) const {
for (auto vars = response.variables; vars; vars = vars->next_variable)
{
// Get value of variable depending on ASN.1 type
// Variable could be a integer, string, bitstring, ojbid, counter : defined here https://github.com/net-snmp/net-snmp/blob/master/include/net-snmp/types.h
// get Integer value
if (vars->type == ASN_INTEGER && vars->val.integer)
{
val.type = snmp_response_obj::response_type::INTEGER;
val.val_int = *vars->val.integer;
}
else if (vars->type == ASN_OCTET_STR && vars->val.string)
{
size_t str_len = vars->val_len;
for (size_t i = 0; i < str_len; ++i)
{
val.val_string.push_back(vars->val.string[i]);
}
val.type = snmp_response_obj::response_type::STRING;
}
}
}

void snmp_client::process_snmp_set_response( const snmp_response_obj &val, const std::string &input_oid) const {
if(val.type == snmp_response_obj::response_type::INTEGER){
FILE_LOG(logDEBUG) << "Success in SET for OID: " << input_oid << " Value: " << val.val_int << std::endl;
}

else if(val.type == snmp_response_obj::response_type::STRING){
FILE_LOG(logDEBUG) << "Success in SET for OID: " << input_oid << " Value:" << std::endl;
for(auto data : val.val_string){
FILE_LOG(logDEBUG) << data ;
}
}
}

void snmp_client::log_error(const int &status, const request_type &request_type, const snmp_pdu *response) const
{

Expand Down
12 changes: 12 additions & 0 deletions src/tmx/TmxUtils/src/SNMPClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ namespace tmx::utils
int snmp_version_ = 3; // default to 3 since previous versions not compatable currently
/*Time after which the the snmp request times out*/
int timeout_ = 10000;
/**
* @brief Helper method for populating snmp_respons_obj with SNMP get response.
* @param val response object
* @param response pdu
*/
void process_snmp_get_response(snmp_response_obj &val, const snmp_pdu &response) const;
/**
* @brief Helper method for logging successful SNMP set responses
* @param val response object
* @param input_oid OID
*/
void process_snmp_set_response( const snmp_response_obj &val, const std::string &input_oid) const;

public:
/** @brief Constructor for Traffic Signal Controller Service client.
Expand Down
2 changes: 1 addition & 1 deletion src/tmx/TmxUtils/test/J2735MessageTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ TEST_F(J2735MessageTest, EncodeSDSM)
tmx::messages::SdsmEncodedMessage SdsmEncodeMessage;
auto _sdsmMessage = new tmx::messages::SdsmMessage(message);
tmx::messages::MessageFrameMessage frame_msg(_sdsmMessage->get_j2735_data());
SdsmEncodeMessage.set_data(TmxJ2735EncodedMessage<SignalRequestMessage>::encode_j2735_message<codec::uper<MessageFrameMessage>>(frame_msg));
SdsmEncodeMessage.set_data(TmxJ2735EncodedMessage<SdsmMessage>::encode_j2735_message<codec::uper<MessageFrameMessage>>(frame_msg));
free(message);
free(frame_msg.get_j2735_data().get());
ASSERT_EQ(41, SdsmEncodeMessage.get_msgId());
Expand Down
6 changes: 3 additions & 3 deletions src/tmx/TmxUtils/test/test_SNMPClient.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

#include "MockSNMPClient.h"
#include "gtest/gtest.h"
#include "RSU_MIB_4_1.h"
#include <MockSNMPClient.h>
#include <gtest/gtest.h>
#include <RSU_MIB_4_1.h>

using namespace tmx::utils;
using namespace std;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ namespace MUSTSensorDriverPlugin {
MUSTSensorDriverPlugin::MUSTSensorDriverPlugin(const string &name): PluginClientClockAware(name)
{
mustSensorPacketReceiverThread = std::make_unique<tmx::utils::ThreadTimer>(std::chrono::milliseconds(5));
if (PluginClientClockAware::isSimulationMode()) {
PLOG(tmx::utils::logINFO) << "Simulation mode on " << std::endl;
SubscribeToMessages();
}
}

void MUSTSensorDriverPlugin::OnStateChange(IvpPluginState state) {
Expand Down
3 changes: 1 addition & 2 deletions src/v2i-hub/MapPlugin/src/MapPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,7 @@ namespace MapPlugin {
std::unique_ptr<tmx::messages::MapDataEncodedMessage> msg;
int activeAction = -1;

// wait for the clock to be initialized
getClock()->wait_for_initialization();


while (_plugin->state != IvpPluginState_error) {
if (_isMapFileNew) {
Expand Down
50 changes: 13 additions & 37 deletions src/v2i-hub/SpatPlugin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,44 +1,20 @@
PROJECT ( SpatPlugin VERSION 7.6.0 LANGUAGES CXX )

SET (TMX_PLUGIN_NAME "SPAT")

FIND_PACKAGE (XercesC REQUIRED)
FIND_PACKAGE (NetSNMP REQUIRED)
FIND_PACKAGE (carma-clock REQUIRED)

BuildTmxPlugin ()

TARGET_INCLUDE_DIRECTORIES ( ${PROJECT_NAME} PUBLIC ${XercesC_INCLUDE_DIRS} ${NETSNMP_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES ( ${PROJECT_NAME} tmxutils ::carma-clock rdkafka++ jsoncpp ${XercesC_LIBRARY} ${NETSNMP_LIBRARIES})

################################
# GTest
################################
enable_testing()

add_library(${PROJECT_NAME}_spat_lib src/NTCIP1202.cpp src/signalController.cpp src/PedestrianDetectionForSPAT.cpp)
target_include_directories( ${PROJECT_NAME}_spat_lib PUBLIC
${PROJECT_SOURCE_DIR}/src
${XercesC_INCLUDE_DIRS} ${NETSNMP_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME}_spat_lib PUBLIC
tmxutils ::carma-clock ${XercesC_LIBRARY} ${NETSNMP_LIBRARIES})
project( SpatPlugin VERSION 7.6.0 LANGUAGES CXX )
set(TMX_PLUGIN_NAME "SPAT")
set(CMAKE_CXX_STANDARD 17)
find_package(carma-clock REQUIRED)
dan-du-car marked this conversation as resolved.
Show resolved Hide resolved
BuildTmxPlugin()
target_link_libraries(${PROJECT_NAME} tmxutils ::carma-clock)
add_library(${PROJECT_NAME}_lib src/NTCIP1202.cpp
src/SignalControllerConnection.cpp)
target_link_libraries(${PROJECT_NAME}_lib PUBLIC tmxutils ::carma-clock )
#############
## Testing ##
#############
enable_testing()
set(BINARY ${PROJECT_NAME}_test)

file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false test/*.h test/*.cpp)

set(SOURCES ${TEST_SOURCES}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test)

set(SOURCES ${TEST_SOURCES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test)
add_executable(${BINARY} ${TEST_SOURCES})

add_test(NAME ${BINARY} COMMAND ${BINARY})
target_link_libraries(${BINARY} PUBLIC ${PROJECT_NAME}_spat_lib ${TMXAPI_LIBRARIES}
${ASN_J2735_LIBRARIES}
${MYSQL_LIBRARIES}
${MYSQLCPPCONN_LIBRARIES}
tmxutils
${UUID_LIBRARY}
gtest)
target_include_directories(${BINARY} PUBLIC src/)
target_link_libraries(${BINARY} PUBLIC ${PROJECT_NAME}_lib gtest)
82 changes: 82 additions & 0 deletions src/v2i-hub/SpatPlugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# SPaT Plugin Documentation

## Introduction

The SPaT Plugin is responsible for receiving information from the Traffic Signal Controller (TSC or SC) necessary for broadcasting Signal Phase and Timing (SPaT) messages. This includes querying any SNMP objects to determine TSC state and listen for any broadcast SPaT information from the TSC.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this received information used to populate self-defined Ntcip1202 https://github.com/usdot-fhwa-OPS/V2X-Hub/blob/develop/src/v2i-hub/SpatPlugin/src/NTCIP1202.h? Can we add additional details about the received information


## Related Plugins

A list of plugins related to the SPaT Plugin.

### Immediate Forward Plugin

For RSU Immediate Message Forwarding (IMF) functionality forward SPaT (Signal Phase and Timing Messages).

### Map Plugin

The SPaT and MAP messages are strongly related in that MAP messages are required to be able to understand SPaT messages. Map
messages offer data about which signal groups impact which lanes in a given intersection.

## Configuration/Deployment

This plugin has several configuration parameters. Below these are listed out as together with descriptions on how to set them.

**Intersection_ID**: The intersection id for SPAT generated by this plugin (Note Only used in SPAT MODE = BINARY).

**Intersection_Name**: The intersection name for SPAT generated by this plugin (Note Only used in SPAT MODE = BINARY).

**SignalGroupMapping**: JSON data defining a list of SignalGroups and phases (Note Only used in SPAT MODE = BINARY).

**Local_IP**: The IPv4 address of the local computer for receiving Traffic Signal Controller Broadcast Messages.

**Local_UDP_Port**: The local UDP port for reception of Traffic Signal Controller Broadcast Messages from the TSC.

**TSC_IP**: The IPv4 address of the destination Traffic Signal Controller (TSC).

**TSC_SNMP_Port**: The destination port on the Traffic Signal Controller (TSC) for SNMP NTCIP 1202 communication."

**TSC_SNMP_Community**: The SNMP Community used for sending SNMP NTCIP 1202 communication to Traffic Signal Controller (TSC). Please refer TSC vendor documentation for SNMP Community.

**SPAT_Mode**: The format of received SPAT from Traffic Signal Controller (TSC). Acceptance values are BINARY and J2735_HEX.
> [!NOTE]
> **J2735_HEX** is a new added SPAT format. If your TSC is able to send UPER SPAT directly to an RSU, this is the format in which it is being sent. Below is a screen shot of J2735_HEX SPaT via TCP Dump
![Alt text](docs/hex_tcpdump.png)

## Design

![Alt text](docs/spat_plugin_design.png)
The diagram above illustrates roughly how the SPaT Plugin functions. The SPaT Plugin is able to get and set Traffic Signal Controller (TSC or SC) configuration via SNMP requests. The defined objects and behaviour are standardize via NTCIP 1202, the National Transportation Communications for ITS Protocol Object Definitions for Actuated Signal Controllers (ASC) Interface. It also receives live Signal Phase and Timing Data from the TSC via UDP packets. Using this information, the SPaT plugin generates J2725 SPaT messags which are eventually forwarded to and Road Side Unit (RSU) radio for broadcast to actors at or near the intersection.

### Messages

**SPAT**: This message contains information from the traffic signal controller about Signal Phase and Timing (SPaT). To use this information for vehicle control the MAP message is also required for mapping signal phase to lanes in an intersection.

## Functionality Testing

Testing the functionality of a configured instance of the SPaT plugin requires access to a Traffic Signal Controller (TSC) or Virtual Traffic Signal Controller.

1) Configure the TSC to broadcast SPAT to an open port your edge device.
1) Take note of the format of this data. This can be done by consulting the TSC manual or by inspecting it using `tcpdump -i any port <open_port> -X`.**BINARY** payloads will be complete human unreadable collection of bytes and **J2735_HEX** will include human readable header information like `Type=SPAT`, `PSID=0x8002`, and a `Payload` followed by string HEX of the UPER encoded J2735 SPaT message.
2) Inspect the configurations of your TSC to determine Signal Group to phase mapping and the SNMP port. This can be found either at the **Channel Table** or the **Load Switch Configuration** on the TSC. Using these configurations create your signal group mapping JSON (Example shown below)
```json

{"SignalGroups":
[
{"SignalGroupId":1,"Phase":1,"Type":"vehicle"},
{"SignalGroupId":2,"Phase":2,"Type":"vehicle"},
{"SignalGroupId":3,"Phase":3,"Type":"vehicle"},
{"SignalGroupId":4,"Phase":4,"Type":"vehicle"},
{"SignalGroupId":5,"Phase":5,"Type":"vehicle"},
{"SignalGroupId":6,"Phase":6,"Type":"vehicle"},
{"SignalGroupId":7,"Phase":7,"Type":"vehicle"},
{"SignalGroupId":8,"Phase":8,"Type":"vehicle"},
{"SignalGroupId":9,"Phase":2,"Type":"pedestrian"},
{"SignalGroupId":10,"Phase":4,"Type":"pedestrian"},
{"SignalGroupId":11,"Phase":6,"Type":"pedestrian"},
{"SignalGroupId":12,"Phase":8,"Type":"pedestrian"}
]
}
```

3) Configure your SPaT Plugin with the information gathers in steps 1 and 2.
4) Check the Status and Messages tabs on the plugin after enabling it. Once enabled the SPAT Plugin should be receive SPAT messages at 10Hz (visible in the Messages tab) and have a status of "CONNECTED"
Binary file added src/v2i-hub/SpatPlugin/docs/hex_tcpdump.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading