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 25 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
14 changes: 14 additions & 0 deletions src/tmx/Messages/test/Main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Main.cpp
*
* Created on: May 10, 2016
dan-du-car marked this conversation as resolved.
Show resolved Hide resolved
* Author: ivp
*/

#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
51 changes: 35 additions & 16 deletions src/tmx/TmxUtils/src/SNMPClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,28 +165,47 @@ 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)
if ( request_type == request_type::GET ) {
for (auto vars = response->variables; vars; vars = vars->next_variable)
{
size_t str_len = vars->val_len;
for (size_t i = 0; i < str_len; ++i)
// 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.val_string.push_back(vars->val.string[i]);
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;
}
val.type = snmp_response_obj::response_type::STRING;
}
}
else if( request_type == request_type::SET){

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 ;
}
}
}
else {
log_error(status, request_type, response);
return false;
}
}
else
{
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
2 changes: 1 addition & 1 deletion src/v2i-hub/CDASimAdapter/scripts/send_timestep_udp.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def generate_time_sync():
count_num += 1
sock.sendto(encoded_msg,host)
print( encoded_msg.decode(encoding= 'UTF-8'), 'was sent to ', host)
time.sleep(5)
time.sleep(.1)
paulbourelly999 marked this conversation as resolved.
Show resolved Hide resolved
dan-du-car marked this conversation as resolved.
Show resolved Hide resolved
except socket.gaierror:

print ('There an error resolving the host')
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)
80 changes: 80 additions & 0 deletions src/v2i-hub/SpatPlugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# 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_Remote_SNMP_Port**: The destination port on the Traffic Signal Controller (TSC) for SNMP NTCIP communication."
dan-du-car marked this conversation as resolved.
Show resolved Hide resolved

**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.
18 changes: 9 additions & 9 deletions src/v2i-hub/SpatPlugin/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name":"SPAT",
"description":"Plugin that reads PTLM data from a configuration file, receives live data from the signal controller, and publishes a J2735 SPAT message.",
"description":"The SPaT plugin receives live Signal Phase and Timing data from the Traffic Signal Controller and publishes a J2735 SPAT message.",
"version":"@PROJECT_VERSION@",
"exeLocation":"/bin/SpatPlugin",
"coreIpAddr":"127.0.0.1",
Expand All @@ -10,28 +10,23 @@
"type":"J2735",
"subtype":"SPAT-P",
"description":"Signal Phase and Timing (SPAT) status for the signalized intersection."
},
{
"type":"SIGCONT",
"subtype":"ACT",
"description":"Current signal controller action"
}
],
"configuration":[
{
"key":"Intersection_Id",
"default":"1",
"description":"The intersection id for SPAT generated by this plugin."
"description":"The intersection id for SPAT generated by this plugin (Note Only used in SPAT MODE = BINARY)."
},
{
"key":"Intersection_Name",
"default":"Intersection",
"description":"The intersection name for SPAT generated by this plugin."
"description":"The intersection name for SPAT generated by this plugin (Note Only used in SPAT MODE = BINARY)."
},
{
"key":"SignalGroupMapping",
"default":"{\"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\":22,\"Phase\":2,\"Type\":\"pedestrian\"},{\"SignalGroupId\":24,\"Phase\":4,\"Type\":\"pedestrian\"},{\"SignalGroupId\":26,\"Phase\":6,\"Type\":\"pedestrian\"},{\"SignalGroupId\":28,\"Phase\":8,\"Type\":\"pedestrian\"}]}",
"description":"JSON data defining a list of SignalGroups and phases."
"description":"JSON data defining a list of SignalGroups and phases (Note Only used in SPAT MODE = BINARY)."
},
{
"key":"Local_IP",
Expand All @@ -53,6 +48,11 @@
"default":"<snmp port used in signal controller>",
"description":"The destination port on the Traffic Signal Controller (TSC) for SNMP NTCIP communication."
},
{
"key":"SPAT_Mode",
"default":"BINARY",
"description":"The format of received SPAT from Traffic Signal Controller (TSC). Acceptance values are BINARY and J2735_HEX."
},
{
"key":"LogLevel",
"default":"INFO",
Expand Down
Loading
Loading