Skip to content

Commit

Permalink
address comments and add unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
dan-du-car committed Nov 10, 2023
1 parent 1edaf32 commit d747a98
Show file tree
Hide file tree
Showing 9 changed files with 364 additions and 121 deletions.
69 changes: 69 additions & 0 deletions src/tmx/TmxUtils/src/RSU_MIB_4_1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#pragma once
namespace tmx
{
namespace utils
{
namespace rsu41
{
namespace mib
{
namespace oid
{
/**
* @brief This header file contains a subset of RSU MIB definition from https://github.com/certificationoperatingcouncil/COC_TestSpecs/blob/master/AppNotes/RSU/RSU-MIB.txt
*/
// Contains the ID given to this RSU.
static constexpr const char *RSU_ID_OID = "1.0.15628.4.1.17.4.0";

// Contains the version of this MIB supported by this RSU, e.g. rsuMIB 4.1 rev201812060000Z
static constexpr const char *RSU_MIB_VERSION = "1.0.15628.4.1.17.1.0";

// Contains the version of firmware running on this RSU.
static constexpr const char *RSU_FIRMWARE_VERSION = "1.0.15628.4.1.17.2.0";

// Contains the name of the manufacturer of this RSU.
static constexpr const char *RSU_MANUFACTURER = "1.0.15628.4.1.17.5.0";

// Contains GPS NMEA GPGGA output string.
static constexpr const char *RSU_GPS_OUTPUT_STRING = "1.0.15628.4.1.8.5.0";

// Immediate Forward Message Index
static constexpr const char *RSU_IFM_INDEX = "1.0.15628.4.1.5.1.1.0";

// Immediate Forward Message PSID.
static constexpr const char *RSU_IFM_PSID = "1.0.15628.4.1.5.1.2.0";

// Immediate Forward Message DSRC Message ID
static constexpr const char *RSU_IFM_DSRC_MSG_ID = "1.0.15628.4.1.5.1.3.0";

// Immediate Forward Message Transmit Mode
static constexpr const char *RSU_IFM_TX_MODE = "1.0.15628.4.1.5.1.4.0";

// DSRC channel set for Immediate Forward Message transmit
static constexpr const char *RSU_IFM_TX_CHANNEL = "1.0.15628.4.1.5.1.5.0";

// Set this bit to enable transmission of the message 0=off, 1=on
static constexpr const char *RSU_IFM_ENABLE = "1.0.15628.4.1.5.1.6.0";

// Create (4) or Destroy (6) row entry
static constexpr const char *RSU_IFM_STATUS = "1.0.15628.4.1.5.1.7.0";

// Specifies the current mode of operation of the RSU and provides capability to transition the device into a new mode, e.g. from the current mode to off, etc
static constexpr const char *RSU_MODE = "1.0.15628.4.1.99.0";

/*
SYNTAX INTEGER {
bothOp (0), --both Continuous and Alternating modes are operational
altOp (1), --Alternating mode is operational,
--Continuous mode is not operational
contOp (2), --Continuous mode is operational,
--Alternating mode is not operational
noneOp (3) --neither Continuous nor Alternating mode is operational
*/
static constexpr const char *RSU_CHAN_STATUS = "1.0.15628.4.1.19.1.0";
} // namespace oid
} // namespace mib
} // namespace rsu41

}
}
22 changes: 21 additions & 1 deletion src/v2i-hub/RSUHealthMonitorPlugin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,24 @@ find_library(libasn1c .)
BuildTmxPlugin()

TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PUBLIC ${NETSNMP_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC tmxutils ${NETSNMPAGENT} ${NETSNMPMIBS} ${NETSNMP} ${NETSNMP_LIBRARIES} jsoncpp NemaTode)
TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC tmxutils ${NETSNMPAGENT} ${NETSNMPMIBS} ${NETSNMP} ${NETSNMP_LIBRARIES} jsoncpp NemaTode)

#############
## Testing ##
#############
enable_testing()
include_directories(${PROJECT_SOURCE_DIR}/src)
add_library(${PROJECT_NAME}_lib src/RSUHealthMonitorWorker.cpp)
target_link_libraries(${PROJECT_NAME}_lib PUBLIC ${TMXAPI_LIBRARIES}
${ASN_J2735_LIBRARIES}
${MYSQL_LIBRARIES}
${MYSQLCPPCONN_LIBRARIES}
tmxutils
NemaTode
${UUID_LIBRARY})
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)
add_executable(${BINARY} ${TEST_SOURCES})
add_test(NAME ${BINARY} COMMAND ${BINARY})
target_link_libraries(${BINARY} PUBLIC ${PROJECT_NAME}_lib gtest NemaTode)
6 changes: 3 additions & 3 deletions src/v2i-hub/RSUHealthMonitorPlugin/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@
"description":"SNMP Security level"
},
{
"key":"RSUOIDConfigMap",
"default":"{\"RSUOIDConfig\":[{\"RsuField\":\"rsuGpsOutputString\",\"OID\":\"iso.0.15628.4.1.8.5.0\",\"Required\":false},{\"RsuField\":\"rsuID\",\"OID\":\"iso.0.15628.4.1.17.4.0\",\"Required\":false},{\"RsuField\":\"rsuMibVersion\",\"OID\":\"iso.0.15628.4.1.17.1.0\",\"Required\":false},{\"RsuField\":\"rsuFirmwareVersion\",\"OID\":\"iso.0.15628.4.1.17.2.0\",\"Required\":false},{\"RsuField\":\"rsuManufacturer\",\"OID\":\"iso.0.15628.4.1.17.5.0\",\"Required\":false},{\"RsuField\":\"rsuIFMIndex\",\"OID\":\"iso.3.6.1.2.1.1.7.0\",\"Required\":false},{\"RsuField\":\"rsuIFMPsid\",\"OID\":\"iso.3.6.1.2.1.1.7.0\",\"Required\":false},{\"RsuField\":\"rsuIFMDsrcMsgId\",\"OID\":\"iso.3.6.1.2.1.1.7.0\",\"Required\":false},{\"RsuField\":\"rsuIFMTxMode\",\"OID\":\"iso.3.6.1.2.1.1.7.0\",\"Required\":false},{\"RsuField\":\"rsuIFMTxChannel\",\"OID\":\"iso.3.6.1.2.1.1.7.0\",\"Required\":false},{\"RsuField\":\"rsuIFMEnable\",\"OID\":\"iso.3.6.1.2.1.1.7.0\",\"Required\":false},{\"RsuField\":\"rsuIFMStatus\",\"OID\":\"iso.3.6.1.2.1.1.7.0\",\"Required\":false},{\"RsuField\":\"rsuMode\",\"OID\":\"iso.0.15628.4.1.99.0\",\"Required\":false}]}",
"description":"OID (Object Identifier) uniquely identify managed objects in a MIB database."
"key":"RSUMIBVersion",
"default":"RSU4.1",
"description":"THe version of RSU MIB (Management Information Base). E.G. RSU4.1 or RSU1218"
}
]
}
111 changes: 25 additions & 86 deletions src/v2i-hub/RSUHealthMonitorPlugin/src/RSUHealthMonitorPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace RSUHealthMonitor

RSUHealthMonitorPlugin::RSUHealthMonitorPlugin(std::string name) : PluginClient(name)
{
_rsuWorker = std::make_shared<RSUHealthMonitorWorker>();
UpdateConfigSettings();
// Send SNMP call to RSU periodically at configurable interval.
std::thread rsuStatus_t(&RSUHealthMonitorPlugin::PeriodicRSUStatusReq, this);
Expand All @@ -25,44 +26,17 @@ namespace RSUHealthMonitor
GetConfigValue<string>("AuthPassPhrase", _authPassPhrase);
GetConfigValue<string>("SecurityUser", _securityUser);
GetConfigValue<string>("SecurityLevel", _securityLevel);

// Update the OID to RSU field mapping
string rsuOIDMapJsonStr;
GetConfigValue<string>("RSUOIDConfigMap", rsuOIDMapJsonStr);
UpdateRSUOIDConfig(rsuOIDMapJsonStr);
}

void RSUHealthMonitorPlugin::UpdateRSUOIDConfig(string &json_str)
{

if (json_str.length() == 0)
{
PLOG(logERROR) << "Error updating RSU OID config due to JSON is empty.";
return;
}
try
GetConfigValue<string>("RSUMIBVersion", _rsuMIBVersionStr);
boost::trim_left(_rsuMIBVersionStr);
boost::trim_right(_rsuMIBVersionStr);
// Support RSU MIB version 4.1
if (boost::iequals(_rsuMIBVersionStr, RSU4_1_str))
{
ptree pt;
istringstream iss(json_str);
read_json(iss, pt);

// Clear the RSU OID mapping variable
_rsuOIDConfigMap.clear();
BOOST_FOREACH (ptree::value_type &child, pt.get_child("RSUOIDConfig"))
{
// Array elements have no names.
assert(child.first.empty());
RSUOIDConfig config;
config.field = child.second.get<string>("RsuField");
config.oid = child.second.get<string>("OID");
config.required = child.second.get<bool>("Required");
// Add RSU OID to the map
_rsuOIDConfigMap.push_back(config);
}
_rsuMibVersion = RSUMIB_4_1;
}
catch (const std::exception &e)
else
{
PLOG(logERROR) << "Error updating RSU OID config " << e.what();
PLOG(logERROR) << "Unknow RSU Mib version: " + _rsuMIBVersionStr;
}
}

Expand All @@ -75,10 +49,10 @@ namespace RSUHealthMonitor
void RSUHealthMonitorPlugin::PeriodicRSUStatusReq()
{
while (true)
{
{
try
{
//SNMP call to get RSU status
// Periodic SNMP call to get RSU status based on RSU MIB version 4.1
auto rsuStatusJson = getRSUstatus();
// Broadcast the RSU status info when there are RSU responses.
if (!rsuStatusJson.empty())
Expand All @@ -89,7 +63,7 @@ namespace RSUHealthMonitor
rsuStatusFields.push_back(field);
}
// Only broadcast RSU status when all required fields are present.
if (isAllRequiredFieldsPresent(rsuStatusFields))
if (_rsuWorker && _rsuWorker->isAllRequiredFieldsPresent(_rsuMibVersion, rsuStatusFields))
{
Json::FastWriter fasterWirter;
string json_str = fasterWirter.write(rsuStatusJson);
Expand All @@ -111,13 +85,18 @@ namespace RSUHealthMonitor

Json::Value RSUHealthMonitorPlugin::getRSUstatus()
{
if (_rsuOIDConfigMap.size() == 0)
if (!_rsuWorker)
{
PLOG(logERROR) << "RSU status update call failed due to RSUOIDConfigMap is empty!";
PLOG(logERROR) << "RSU status update call failed due to fail to initialize RSU worker!";
return Json::nullValue;
}

auto rsuStatusConfigTbl = _rsuWorker->GetRSUStatusConfig(_rsuMibVersion);
if (rsuStatusConfigTbl.size() == 0)
{
PLOG(logERROR) << "RSU status update call failed due to RSU stataus config table is empty!";
return Json::nullValue;
}
PLOG(logDEBUG) << "RSU status update call at every " << _interval << " seconds!\n";

// Create SNMP client and use SNMP V3 protocol
PLOG(logINFO) << "Update SNMP client: RSU IP: " << _rsuIp << ", RSU port: " << _snmpPort << ", User: " << _securityUser << ", auth pass phrase: " << _authPassPhrase << ", security level: "
<< _securityLevel;
Expand All @@ -130,19 +109,18 @@ namespace RSUHealthMonitor

Json::Value rsuStatuJson;
// Sending RSU SNMP call for each field as each field has its own OID.
for (auto &config : _rsuOIDConfigMap)
for (auto &config : rsuStatusConfigTbl)
{
try
{
PLOG(logINFO) << "SNMP RSU status call for field:" << config.field << ", OID: " << config.oid;
snmp_response_obj responseVal;
auto success = _snmpClientPtr->process_snmp_request(config.oid, request_type::GET, responseVal);
if (!success)
{
// If any snmp request failed, stop any furthur snmp requests using the same current snmp session as the next OID will not be created.
break;
}
else if (success && responseVal.type == snmp_response_obj::response_type::INTEGER)

if (success && responseVal.type == snmp_response_obj::response_type::INTEGER)
{
rsuStatuJson[config.field] = responseVal.val_int;
}
Expand All @@ -153,7 +131,7 @@ namespace RSUHealthMonitor
// Proess GPS nmea string
if (boost::iequals("rsuGpsOutputString", config.field))
{
auto gps = ParseGPS(response_str);
auto gps = _rsuWorker->ParseRSUGPS(response_str);
rsuStatuJson["rsuGpsOutputStringLatitude"] = gps.begin()->first;
rsuStatuJson["rsuGpsOutputStringLongitude"] = gps.begin()->second;
}
Expand All @@ -171,47 +149,8 @@ namespace RSUHealthMonitor
return rsuStatuJson;
}

bool RSUHealthMonitorPlugin::isAllRequiredFieldsPresent(vector<string> fields)
{
bool isAllPresent = true;
for (auto &config : _rsuOIDConfigMap)
{
if (config.required && std::find(fields.begin(), fields.end(), config.field) == fields.end())
{
isAllPresent = false;
PLOG(logWARNING) << "No broadcast as required field " << config.field << " is not present!";
}
}
return isAllPresent;
}

std::map<double, double> RSUHealthMonitorPlugin::ParseGPS(const std::string &gps_nmea_data)
{
std::map<double, double> result;
nmea::NMEAParser parser;
nmea::GPSService gps(parser);
try
{
parser.readLine(gps_nmea_data);
std::stringstream ss;
ss << std::setprecision(8) << std::fixed << gps.fix.latitude << std::endl;
auto latitude_str = ss.str();
std::stringstream sss;
sss << std::setprecision(8) << std::fixed << gps.fix.longitude << std::endl;
auto longitude_str = sss.str();
result.insert({std::stod(latitude_str), std::stod(longitude_str)});
PLOG(logDEBUG) << "Parse GPS NMEA string: " << gps_nmea_data << ". Result (Latitude, Longitude): (" << latitude_str << "," << longitude_str << ")";
}
catch (nmea::NMEAParseError &e)
{
fprintf(stderr, "Error:%s\n", e.message.c_str());
}
return result;
}

RSUHealthMonitorPlugin::~RSUHealthMonitorPlugin()
{
_rsuOIDConfigMap.clear();
}

} // namespace RSUHealthMonitor
Expand Down
41 changes: 10 additions & 31 deletions src/v2i-hub/RSUHealthMonitorPlugin/src/RSUHealthMonitorPlugin.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@

#ifndef RSUHEALTHMONITORLUGIN_H_
#define RSUHEALTHMONITORLUGIN_H_
#pragma once

#include "PluginClient.h"
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "SNMPClient.h"
#include <jsoncpp/json/json.h>
#include "RSUStatusMessage.h"
#include <nmeaparse/nmea.h>
#include <algorithm>
#include "RSUHealthMonitorWorker.h"

using namespace tmx::utils;
using namespace std;
Expand All @@ -20,13 +14,6 @@ using namespace boost::property_tree;
namespace RSUHealthMonitor
{

struct RSUOIDConfig
{
string field;
string oid;
bool required; // Indicate whether this field is required to before broadcasting the RSU status.
};

class RSUHealthMonitorPlugin : public PluginClient
{
private:
Expand All @@ -37,7 +24,11 @@ namespace RSUHealthMonitor
string _authPassPhrase;
string _securityUser;
string _securityLevel;
vector<RSUOIDConfig> _rsuOIDConfigMap;
string _rsuMIBVersionStr;
RSUMibVersion _rsuMibVersion;
const char *RSU4_1_str = "RSU4.1";
const char *RSU1218_str = "RSU1218";
std::shared_ptr<RSUHealthMonitorWorker> _rsuWorker;
const long SEC_TO_MICRO = 1000000;
// std::shared_ptr<snmp_client> _snmpClientPtr;
/**
Expand All @@ -50,20 +41,10 @@ namespace RSUHealthMonitor
*/
void PeriodicRSUStatusReq();
/**
* @brief Sending SNMP requests to get info for each field in the _rsuOIDConfigMap, and return the RSU status in JSON
* @brief Sending SNMP requests to get info for each field in the RSUStatusConfigTable, and return the RSU status in JSON
* Use RSU Status configuration table include RSU field, OIDs, and whether fields are required or optional
*/
Json::Value getRSUstatus();
/**
* @brief Parse NMEA GPS sentense and return GPS related data
* @param gps_nmea_data NMEA GPS sentense
* @return map<double, double> A map of latitude and longitude
*/
std::map<double, double> ParseGPS(const std::string &gps_nmea_data);
/**
* @brief determine if all required fields in the RSU config map _rsuOIDConfigMap present in the input fields
* @return True if all required fields found. Otherwise, false.
*/
bool isAllRequiredFieldsPresent(vector<string> fields);

public:
RSUHealthMonitorPlugin(std::string name);
Expand All @@ -72,6 +53,4 @@ namespace RSUHealthMonitor
void OnConfigChanged(const char *key, const char *value);
};

} // namespace RSUHealthMonitorPlugin

#endif
} // namespace RSUHealthMonitorPlugin
Loading

0 comments on commit d747a98

Please sign in to comment.