From f933ff7678ce0fde14b3d8a862d3c63d8f70f432 Mon Sep 17 00:00:00 2001 From: Will Martin Date: Thu, 27 Jun 2024 15:59:14 -0400 Subject: [PATCH] Fix map (#609) # PR Details ## Description Changes were made to handle files that could be expected. This includes: Files with newline characters Files with only MapData Files containing a MessageFrame along with MapData Plugin was also cleaned up with a header file. ## Related Issue #608 ## Motivation and Context These changes make the MAP plugin more robust because it's able to handle MAP files that could be expected to be generated by a user. ## How Has This Been Tested? Tested using files with and without newline characters, files with and without MessageFrames, and a combination of the two. ## 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 - [ ] My change requires a change to the documentation. - [ ] 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) - [ ] I have added tests to cover my changes. - [X] All new and existing tests passed. --- src/v2i-hub/MapPlugin/manifest.json | 2 +- src/v2i-hub/MapPlugin/src/MapPlugin.cpp | 652 +++++++++++------------- src/v2i-hub/MapPlugin/src/MapPlugin.h | 114 +++++ 3 files changed, 411 insertions(+), 357 deletions(-) create mode 100644 src/v2i-hub/MapPlugin/src/MapPlugin.h diff --git a/src/v2i-hub/MapPlugin/manifest.json b/src/v2i-hub/MapPlugin/manifest.json index c63f1c718..8cd561b34 100644 --- a/src/v2i-hub/MapPlugin/manifest.json +++ b/src/v2i-hub/MapPlugin/manifest.json @@ -31,7 +31,7 @@ { "key":"MAP_Files", "default":"{ \"MapFiles\": [ {\"Action\":0, \"FilePath\":\"/var/www/plugins/MAP/MAP_9709_UPER.txt\"}] }", - "description":"JSON data defining a list of map files. One map file for each action set specified by the TSC." + "description":"JSON data defining a list of map files. One map file for each action set specified by the TSC." } ] } diff --git a/src/v2i-hub/MapPlugin/src/MapPlugin.cpp b/src/v2i-hub/MapPlugin/src/MapPlugin.cpp index 2317e7800..7ee8d4ae7 100644 --- a/src/v2i-hub/MapPlugin/src/MapPlugin.cpp +++ b/src/v2i-hub/MapPlugin/src/MapPlugin.cpp @@ -1,457 +1,397 @@ +#include "MapPlugin.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include "XmlMapParser.h" -#include "ConvertToJ2735r41.h" -#include "inputs/isd/ISDToJ2735r41.h" - -#define USE_STD_CHRONO -#include -#include - -#include "utils/common.h" -#include "utils/map.h" - -#include -using namespace std; -using namespace tmx; -using namespace tmx::messages; using namespace tmx::utils; namespace MapPlugin { -#if SAEJ2735_SPEC < 63 -UPERframe _uperFrameMessage; -#endif - -class MapFile: public tmx::message { -public: - MapFile(): tmx::message() {} - virtual ~MapFile() {} - - std_attribute(this->msg, int, Action, -1, ); - std_attribute(this->msg, std::string, FilePath, "", ); - std_attribute(this->msg, std::string, InputType, "", ); - std_attribute(this->msg, std::string, Bytes, "", ); -public: - static tmx::message_tree_type to_tree(MapFile m) { - return tmx::message::to_tree(static_cast(m)); + MapPlugin::MapPlugin(const std::string &name) : PluginClientClockAware(name) { + AddMessageFilter(IVPMSG_TYPE_SIGCONT, "ACT", IvpMsgFlags_None); + SubscribeToMessages(); + errThrottle.set_Frequency(std::chrono::minutes(30)); } - static MapFile from_tree(tmx::message_tree_type tree) { - MapFile m; - m.set_contents(tree); - return m; - } -}; - -//int _mapAction = -1; -//bool _isMapFilesNew = false; -//bool _isMapLoaded = false; - -volatile int gMessageCount = 0; - -class MapPlugin: public PluginClientClockAware { -public: - MapPlugin(string name); - virtual ~MapPlugin(); - - virtual int Main(); -protected: - void UpdateConfigSettings(); - - // Virtual method overrides. - void OnConfigChanged(const char *key, const char *value); - void OnMessageReceived(IvpMessage *msg); - void OnStateChange(IvpPluginState state); - -private: - std::atomic _mapAction {-1}; - std::atomic _isMapFileNew {false}; - std::atomic _cohdaR63 {false}; - - std::map _mapFiles; - std::mutex data_lock; - - J2735MessageFactory factory; + void MapPlugin::UpdateConfigSettings() { + GetConfigValue("Frequency", sendFrequency); - int sendFrequency = 1000; - FrequencyThrottle errThrottle; + message_tree_type rawMapFiles; + GetConfigValue("MAP_Files", rawMapFiles); - bool LoadMapFiles(); - void DebugPrintMapFiles(); -}; - -MapPlugin::MapPlugin(string name) : - PluginClientClockAware(name) { - AddMessageFilter(IVPMSG_TYPE_SIGCONT, "ACT", IvpMsgFlags_None); - SubscribeToMessages(); - errThrottle.set_Frequency(std::chrono::minutes(30)); -} - -MapPlugin::~MapPlugin() { - -} + if (!rawMapFiles.empty()) { + try + { + lock_guard lock(data_lock); + _mapFiles.clear(); -void MapPlugin::UpdateConfigSettings() { - GetConfigValue("Frequency", sendFrequency); + tmx::message mapFiles; + mapFiles.set_contents(rawMapFiles); - message_tree_type rawMapFiles; - GetConfigValue("MAP_Files", rawMapFiles); + PLOG(logDEBUG) << "Got MAP_Files: " << mapFiles; - if (!rawMapFiles.empty()) { - try - { - lock_guard lock(data_lock); - _mapFiles.clear(); + for (auto mapFile : mapFiles.template get_array("MapFiles")) + { + if (mapFile.get_Action() < 0) + continue; - tmx::message mapFiles; - mapFiles.set_contents(rawMapFiles); + _mapFiles[mapFile.get_Action()] = mapFile; + _isMapFileNew = true; + } - PLOG(logDEBUG) << "Got MAP_Files: " << mapFiles; + // Check to see if the active map was lost + if (!_mapFiles.count(_mapAction)) + { + if (_mapAction > 0) + { + PLOG(logINFO) << "New configuration does not contain a map for active action " << + _mapAction << ". Using default action."; + } + _mapAction = -1; + } - for (auto mapFile : mapFiles.template get_array("MapFiles")) - { - if (mapFile.get_Action() < 0) - continue; + if (_mapFiles.size() > 0 && _mapAction < 0) + _mapAction = _mapFiles.begin()->first; - _mapFiles[mapFile.get_Action()] = mapFile; - _isMapFileNew = true; } - - // Check to see if the active map was lost - if (!_mapFiles.count(_mapAction)) + catch (exception &ex) { - if (_mapAction > 0) - { - PLOG(logINFO) << "New configuration does not contain a map for active action " << - _mapAction << ". Using default action."; - } - _mapAction = -1; + PLOG(logERROR) << "Unable to parse map file input: " << ex.what(); } - if (_mapFiles.size() > 0 && _mapAction < 0) - _mapAction = _mapFiles.begin()->first; - - } - catch (exception &ex) - { - PLOG(logERROR) << "Unable to parse map file input: " << ex.what(); + DebugPrintMapFiles(); } - DebugPrintMapFiles(); } -} - -void MapPlugin::OnConfigChanged(const char *key, const char *value) { - PluginClient::OnConfigChanged(key, value); + void MapPlugin::OnConfigChanged(const char *key, const char *value) { + PluginClient::OnConfigChanged(key, value); - if (_plugin->state == IvpPluginState_registered) - { - // Check for special case Cohda R63 messages - if (strcmp("Cohda R63", key)) + if (_plugin->state == IvpPluginState_registered) { - string strValue(value); - - if (boost::iequals(strValue, "1") - || boost::iequals(strValue, "true") - || boost::iequals(strValue, "t") - || boost::iequals(strValue, "on")) + // Check for special case Cohda R63 messages + if (strcmp("Cohda R63", key)) { - _cohdaR63 = true; + std::string strValue(value); + + if (boost::iequals(strValue, "1") + || boost::iequals(strValue, "true") + || boost::iequals(strValue, "t") + || boost::iequals(strValue, "on")) + { + _cohdaR63 = true; + } + else + { + _cohdaR63 = false; + } } else { - _cohdaR63 = false; + UpdateConfigSettings(); } } - else - { - UpdateConfigSettings(); - } } -} -void MapPlugin::OnStateChange(IvpPluginState state) { - PluginClientClockAware::OnStateChange(state); + void MapPlugin::OnStateChange(IvpPluginState state) { + PluginClientClockAware::OnStateChange(state); - if (state == IvpPluginState_registered) { - UpdateConfigSettings(); + if (state == IvpPluginState_registered) { + UpdateConfigSettings(); + } } -} -void MapPlugin::OnMessageReceived(IvpMessage *msg) { - PluginClient::OnMessageReceived(msg); + void MapPlugin::OnMessageReceived(IvpMessage *msg) { + PluginClient::OnMessageReceived(msg); - if ((strcmp(msg->type, IVPMSG_TYPE_SIGCONT) == 0) - && (strcmp(msg->subtype, "ACT") == 0) - && (msg->payload->type == cJSON_String)) { - int action = ivpSigCont_getIvpSignalControllerAction(msg); + if ((strcmp(msg->type, IVPMSG_TYPE_SIGCONT) == 0) + && (strcmp(msg->subtype, "ACT") == 0) + && (msg->payload->type == cJSON_String)) { + int action = ivpSigCont_getIvpSignalControllerAction(msg); - if (action != _mapAction) - { - // Ignore if there is no map for this action - lock_guard lock(data_lock); - if (_mapFiles.count(action) <= 0) + if (action != _mapAction) { - if (errThrottle.Monitor(action)) + // Ignore if there is no map for this action + lock_guard lock(data_lock); + if (_mapFiles.count(action) <= 0) { - PLOG(logERROR) << "Missing map for Action " << action; + if (errThrottle.Monitor(action)) + { + PLOG(logERROR) << "Missing map for Action " << action; + } + return; } - return; - } - _isMapFileNew = _mapAction.exchange(action) != action; + _isMapFileNew = _mapAction.exchange(action) != action; + } } } -} -int MapPlugin::Main() { - PLOG(logINFO) << "Starting plugin."; + int MapPlugin::Main() { + PLOG(logINFO) << "Starting plugin."; - bool mapFilesOk = false; + bool mapFilesOk = false; - std::unique_ptr msg; - int activeAction = -1; - - // wait for the clock to be initialized - getClock()->wait_for_initialization(); + std::unique_ptr msg; + int activeAction = -1; + + // wait for the clock to be initialized + getClock()->wait_for_initialization(); - while (_plugin->state != IvpPluginState_error) { - if (_isMapFileNew) { - msg.reset(); - activeAction = -1; + while (_plugin->state != IvpPluginState_error) { + if (_isMapFileNew) { + msg.reset(); + activeAction = -1; - mapFilesOk = LoadMapFiles(); - _isMapFileNew = false; - } + mapFilesOk = LoadMapFiles(); + _isMapFileNew = false; + } - int temp = _mapAction; - if (temp < 0) - { - // No action set yet, so just wait - sleep(1); - continue; - } + int temp = _mapAction; + if (temp < 0) + { + // No action set yet, so just wait + sleep(1); + continue; + } - // Action has changed, so retrieve the correct map - if (temp != activeAction) - { - lock_guard lock(data_lock); - string byteStr = _mapFiles[temp].get_Bytes(); - if (!byteStr.empty()) + // Action has changed, so retrieve the correct map + if (temp != activeAction) { - msg.reset(dynamic_cast(factory.NewMessage(api::MSGSUBTYPE_MAPDATA_STRING))); - if (!msg) - { if (errThrottle.Monitor(temp)) - { - PLOG(logERROR) << "Unable to create map from bytes " << byteStr << ": " << factory.get_event(); + lock_guard lock(data_lock); + std::string byteStr = _mapFiles[temp].get_Bytes(); + if (!byteStr.empty()) + { + msg.reset(dynamic_cast(factory.NewMessage(tmx::messages::api::MSGSUBTYPE_MAPDATA_STRING))); + if (!msg) + { if (errThrottle.Monitor(temp)) + { + PLOG(logERROR) << "Unable to create map from bytes " << byteStr << ": " << factory.get_event(); + } + sleep(1); + continue; } - sleep(1); - continue; - } - string enc = msg->get_encoding(); - msg->refresh_timestamp(); - msg->set_payload(byteStr); - msg->set_encoding(enc); - msg->set_flags(IvpMsgFlags_RouteDSRC); - msg->addDsrcMetadata(0x8002); + std::string enc = msg->get_encoding(); + msg->refresh_timestamp(); + msg->set_payload(byteStr); + msg->set_encoding(enc); + msg->set_flags(IvpMsgFlags_RouteDSRC); + msg->addDsrcMetadata(tmx::messages::api::mapData_PSID); - activeAction = temp; - PLOG(logINFO) << "Map for action " << activeAction << " will be sent"; + activeAction = temp; + PLOG(logINFO) << "Map for action " << activeAction << " will be sent"; + } } - } - if (mapFilesOk) - { - // Time to send a new message - routeable_message *rMsg = dynamic_cast(msg.get()); - if (_cohdaR63) + if (mapFilesOk) { - auto bytes = rMsg->get_payload_bytes(); - rMsg->set_payload_bytes(bytes); // TODO: Translate to R63 bytes - } + // Time to send a new message + routeable_message *rMsg = dynamic_cast(msg.get()); + if (_cohdaR63) + { + auto bytes = rMsg->get_payload_bytes(); + rMsg->set_payload_bytes(bytes); // TODO: Translate to R63 bytes + } - if (rMsg) { - rMsg->refresh_timestamp(); - BroadcastMessage(*rMsg); + if (rMsg) { + rMsg->refresh_timestamp(); + BroadcastMessage(*rMsg); + } } + + auto sleepUntil = getClock()->nowInMilliseconds() + sendFrequency; + getClock()->sleep_until(sleepUntil); } - auto sleepUntil = getClock()->nowInMilliseconds() + sendFrequency; - getClock()->sleep_until(sleepUntil); + return (EXIT_SUCCESS); } - return (EXIT_SUCCESS); -} + std::string MapPlugin::enum_to_hex_string() + { + std::snprintf(mapID_buffer.data(), mapID_buffer.size(), "%04X", tmx::messages::api::mapData); + std::string map_messageID(mapID_buffer.data()); -bool MapPlugin::LoadMapFiles() -{ - if (_mapFiles.empty()) - return false; + return map_messageID; + } - lock_guard lock(data_lock); - for (auto &mapPair : _mapFiles) + std::string MapPlugin::removeMessageFrame(const std::string &fileContent) { - MapFile &mapFile = mapPair.second; - if (mapFile.get_Bytes() == "") - { - // Fill in the bytes for each map file - string inType = mapFile.get_InputType(); - if (inType.empty()) - { - try - { - string fn = mapFile.get_FilePath(); - - if (fn.substr(fn.size() - 5) == ".json") - inType = "ISD"; - else if (fn.substr(fn.size() - 4) == ".txt") - inType = "TXT"; - else if (fn.substr(fn.size()- 5) == ".uper") - inType ="UPER"; - else - inType = "XML"; - - if (inType == "ISD") - { - ISDToJ2735r41 converter(fn); - mapFile.set_Bytes(converter.to_encoded_message().get_payload_str()); - - PLOG(logINFO) << fn << " ISD file encoded as " << mapFile.get_Bytes(); - } - else if (inType == "TXT") - { - byte_stream bytes; - ifstream in(fn); - in >> bytes; + std::string map_messageID = enum_to_hex_string(); - PLOG(logINFO) << fn << " MAP encoded bytes are " << bytes; + // Check for and remove MessageFrame + if (fileContent.size() >= 4 && fileContent.substr(0, 4) == map_messageID) + { + // Check if message is hex size > 255, remove appropriate header + std::string tempFrame = fileContent; + std::string newFrame = fileContent; + tempFrame.erase(0, 6); + PLOG(logDEBUG4) << "Checking size of: " << tempFrame; + auto headerSize = (tempFrame.size() > 510) ? 8 : 6; + newFrame.erase(0, headerSize); + + PLOG(logDEBUG4) << "Payload without MessageFrame: " << newFrame; + return newFrame; + } + else + { + return fileContent; + } + } - MapDataMessage *mapMsg = MapDataEncodedMessage::decode_j2735_message >(bytes); - if (mapMsg) { - PLOG(logDEBUG) << "Map is " << *mapMsg; + std::string MapPlugin::checkMapContent(const std::string &fn) + { + PLOG(logDEBUG4) << "In MapPlugin :: checkMapContent"; + try + { + std::ifstream in(fn.c_str(), std::ios::binary); + if (!in) + { + PLOG(logERROR) << "Failed to open file: " << fn.c_str(); + throw std::ios_base::failure("Failed to open file: " + fn); + } + else + { + std::string content((std::istreambuf_iterator(in)), std::istreambuf_iterator()); + in.close(); + // Remove any newline characters + content.erase(remove(content.begin(), content.end(), '\n'), content.end()); + PLOG(logDEBUG4) << "Map without newline " << content; + std::string payload = removeMessageFrame(content); + + return payload; + } + } + catch (const std::ios_base::failure& e) + { + PLOG(logERROR) << "Exception encountered while reading file: " << e.what(); + throw; + } + } - MapDataEncodedMessage mapEnc; - mapEnc.encode_j2735_message(*mapMsg); - mapFile.set_Bytes(mapEnc.get_payload_str()); + bool MapPlugin::LoadMapFiles() + { + if (_mapFiles.empty()) + return false; - PLOG(logINFO) << fn << " J2735 message bytes encoded as " << mapFile.get_Bytes(); - } - } - else if (inType == "UPER") - { - PLOG(logDEBUG) << "Reading MAP file as UPER encoded hex bytes including MessageFrame." << std::endl; - std::ifstream in; - try { - in.open(fn, std::ios::in | std::ios::binary ); - if (in.is_open()) { - in.seekg(0, std::ios::end); - int fileSize = in.tellg(); - in.seekg(0, std::ios::beg); - PLOG(logDEBUG) << "File size is " << fileSize <(in)), std::istreambuf_iterator()); - PLOG(logDEBUG) << "File contents : " << bytes_string << std::endl; - mapFile.set_Bytes(bytes_string); - } - else { - PLOG(logERROR) << "Failed to open file " << fn << "." << std::endl; - } - } - catch( const ios_base::failure &e) { - PLOG(logERROR) << "Exception Encountered : \n" << e.what(); - } - } - else if (inType == "XML") + lock_guard lock(data_lock); + for (auto &mapPair : _mapFiles) + { + MapFile &mapFile = mapPair.second; + if (mapFile.get_Bytes() == "") + { + // Fill in the bytes for each map file + std::string inType = mapFile.get_InputType(); + if (inType.empty()) + { + try { - tmx::message_container_type container; - container.load(fn); + std::string fn = mapFile.get_FilePath(); + + if (fn.substr(fn.size() - 5) == ".json") + inType = "ISD"; + else if (fn.substr(fn.size() - 4) == ".txt") + inType = "TXT"; + else if (fn.substr(fn.size() - 4) == ".xml") + inType = "XML"; + else + PLOG(logWARNING) << "Incorrect MapFile extension entered!"; + + if (inType == "ISD") + { + ISDToJ2735r41 converter(fn); + mapFile.set_Bytes(converter.to_encoded_message().get_payload_str()); - if (container.get_storage().get_tree().begin()->first == "MapData") + PLOG(logINFO) << fn << " ISD file encoded as " << mapFile.get_Bytes(); + } + else if (inType == "TXT") { - MapDataMessage mapMsg; - mapMsg.set_contents(container.get_storage().get_tree()); + std::string payload = checkMapContent(fn); + byte_stream bytes; + std::istringstream streamableContent(payload); + streamableContent >> bytes; + PLOG(logINFO) << "MAP encoded bytes are " << bytes; + tmx::messages::MapDataMessage *mapMsg = tmx::messages::MapDataEncodedMessage::decode_j2735_message>(bytes); + + if (mapMsg) + { + PLOG(logDEBUG) << "Map is " << *mapMsg; - PLOG(logDEBUG) << "Encoding " << mapMsg; - MapDataEncodedMessage mapEnc; - mapEnc.encode_j2735_message(mapMsg); - mapFile.set_Bytes(mapEnc.get_payload_str()); + tmx::messages::MapDataEncodedMessage mapEnc; + mapEnc.encode_j2735_message(*mapMsg); + mapFile.set_Bytes(mapEnc.get_payload_str()); - PLOG(logINFO) << fn << " XML file encoded as " << mapFile.get_Bytes(); + PLOG(logINFO) << "J2735 message bytes encoded as " << mapFile.get_Bytes(); + } } - else + else if (inType == "XML") { - ConvertToJ2735r41 mapConverter; - XmlMapParser mapParser; - map theMap; + tmx::message_container_type container; + container.load(fn); - if (mapParser.ReadGidFile(fn, &theMap)) + if (container.get_storage().get_tree().begin()->first == "MapData") { - mapConverter.convertMap(&theMap); + tmx::messages::MapDataMessage mapMsg; + mapMsg.set_contents(container.get_storage().get_tree()); - PLOG(logDEBUG) << "Encoded Bytes:" << mapConverter.encodedByteCount; + PLOG(logDEBUG) << "Encoding " << mapMsg; + tmx::messages::MapDataEncodedMessage mapEnc; + mapEnc.encode_j2735_message(mapMsg); + mapFile.set_Bytes(mapEnc.get_payload_str()); - if (mapConverter.encodedByteCount > 0) + PLOG(logINFO) << fn << " XML file encoded as: " << mapFile.get_Bytes(); + } + else + { + ConvertToJ2735r41 mapConverter; + XmlMapParser mapParser; + map theMap; + + if (mapParser.ReadGidFile(fn, &theMap)) { - byte_stream bytes(mapConverter.encodedByteCount); - memcpy(bytes.data(), mapConverter.encoded, mapConverter.encodedByteCount); + mapConverter.convertMap(&theMap); - auto *mapEnc = factory.NewMessage(bytes); - if (!mapEnc) - return false; + PLOG(logDEBUG) << "Encoded Bytes:" << mapConverter.encodedByteCount; - mapFile.set_Bytes(mapEnc->get_payload_str()); + if (mapConverter.encodedByteCount > 0) + { + byte_stream bytes(mapConverter.encodedByteCount); + memcpy(bytes.data(), mapConverter.encoded, mapConverter.encodedByteCount); - PLOG(logINFO) << fn << " input file encoded as " << mapEnc->get_payload_str(); - } - else - { - return false; + auto *mapEnc = factory.NewMessage(bytes); + if (!mapEnc) + return false; + + mapFile.set_Bytes(mapEnc->get_payload_str()); + + PLOG(logINFO) << fn << " input file encoded as: " << mapEnc->get_payload_str(); + } + else + { + return false; + } } } } } - } - catch (exception &ex) - { - PLOG(logERROR) << "Unable to convert " << mapFile.get_FilePath() << ": " << ex.what(); - return false; + catch (exception &ex) + { + PLOG(logERROR) << "Unable to convert " << mapFile.get_FilePath() << ": " << ex.what(); + return false; + } } } } - } - return true; -} + return true; + } -void MapPlugin::DebugPrintMapFiles() { - PLOG(logDEBUG) << _mapFiles.size() - << " map files specified by configuration settings:"; + void MapPlugin::DebugPrintMapFiles() { + PLOG(logDEBUG) << _mapFiles.size() + << " map files specified by configuration settings:"; - for (auto iter = _mapFiles.begin(); iter != _mapFiles.end(); iter++) { - int key = iter->first; - PLOG(logDEBUG) << "-- Action " << key << " file is " << iter->second.get_FilePath(); + for (auto iter = _mapFiles.begin(); iter != _mapFiles.end(); iter++) { + int key = iter->first; + PLOG(logDEBUG) << "-- Action " << key << " file is " << iter->second.get_FilePath(); + } } -} } /* End namespace MapPlugin */ diff --git a/src/v2i-hub/MapPlugin/src/MapPlugin.h b/src/v2i-hub/MapPlugin/src/MapPlugin.h new file mode 100644 index 000000000..f0da14270 --- /dev/null +++ b/src/v2i-hub/MapPlugin/src/MapPlugin.h @@ -0,0 +1,114 @@ +//============================================================================ +// Name : MapPlugin.cpp +// Author : FHWA Saxton Transportation Operations Laboratory +// Version : 7.6.0 +// Copyright : Copyright (c) 2024 FHWA Saxton Transportation Operations Laboratory. All rights reserved. +// Description : MAP Plugin +//============================================================================ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "XmlMapParser.h" +#include "ConvertToJ2735r41.h" +#include "inputs/isd/ISDToJ2735r41.h" + +#define USE_STD_CHRONO +#include +#include + +#include "utils/common.h" +#include "utils/map.h" + +#include + +namespace MapPlugin { + +#if SAEJ2735_SPEC < 63 +UPERframe _uperFrameMessage; +#endif + +class MapFile: public tmx::message { +public: + using tmx::message::message; + ~MapFile() override = default; + + MapFile(MapFile&&) noexcept = default; + MapFile& operator=(MapFile&&) noexcept = default; + + MapFile(const MapFile&) = default; + MapFile& operator=(const MapFile&) = default; + + std_attribute(this->msg, int, Action, -1, ); + std_attribute(this->msg, std::string, FilePath, "", ); + std_attribute(this->msg, std::string, InputType, "", ); + std_attribute(this->msg, std::string, Bytes, "", ); + + static tmx::message_tree_type to_tree(const MapFile &m) { + return tmx::message::to_tree(static_cast(m)); + } + + static MapFile from_tree(const tmx::message_tree_type &tree) { + MapFile m; + m.set_contents(tree); + return m; + } +}; + +class MapPlugin: public tmx::utils::PluginClientClockAware { +public: + explicit MapPlugin(const std::string &name); + int Main() override; + +protected: + void UpdateConfigSettings(); + + // Virtual method overrides. + void OnConfigChanged(const char *key, const char *value) override; + void OnMessageReceived(IvpMessage *msg) override; + void OnStateChange(IvpPluginState state) override; + + bool LoadMapFiles(); + void DebugPrintMapFiles(); + + std::string enum_to_hex_string(); + std::string removeMessageFrame(const std::string &fileContent); + std::string checkMapContent(const std::string &fn); + +private: + tmx::messages::J2735MessageFactory factory; + tmx::utils::FrequencyThrottle errThrottle; + + std::atomic _mapAction {-1}; + std::atomic _isMapFileNew {false}; + std::atomic _cohdaR63 {false}; + + std::mutex data_lock; + std::map _mapFiles; + int sendFrequency = 1000; + + std::array mapID_buffer; +}; + +} // namespace MapPlugin