diff --git a/src/tmx/TmxApi/tmx/TmxApiMessages.h b/src/tmx/TmxApi/tmx/TmxApiMessages.h index 81875fba8..4d5cf3a71 100644 --- a/src/tmx/TmxApi/tmx/TmxApiMessages.h +++ b/src/tmx/TmxApi/tmx/TmxApiMessages.h @@ -323,7 +323,7 @@ static CONSTEXPR const char *MSGPSID_SIGNALCONTROLANDPRIORITIZATIONREQUEST_PSID_ static CONSTEXPR const char *MSGPSID_SIGNALCONTROLANDPRIORITIZATIONSTATUS_PSID_STRING = "0xE0000015"; static CONSTEXPR const char *MSGPSID_ROADUSERCHARGINGCONFIGMESSAGE_PSID_STRING = "0x800F"; static CONSTEXPR const char *MSGPSID_ROADUSERCHARGINGREPORTMESSAGE_PSID_STRING = "0x800F"; -static CONSTEXPR const char *MSGPSID_TRAFFICLIGHTSTATUSMESSAGE_PSID_STRING = "TLSM"; +static CONSTEXPR const char *MSGPSID_TRAFFICLIGHTSTATUSMESSAGE_PSID_STRING = "0x8002"; static CONSTEXPR const char *MSGPSID_TESTMESSAGE00_PSID_STRING = "0xBFEE"; static CONSTEXPR const char *MSGPSID_TESTMESSAGE01_PSID_STRING = "0xBFEE"; static CONSTEXPR const char *MSGPSID_TESTMESSAGE02_PSID_STRING = "0xBFEE"; diff --git a/src/v2i-hub/ImmediateForwardPlugin/manifest.json b/src/v2i-hub/ImmediateForwardPlugin/manifest.json index b4842c489..a7ede2aaa 100644 --- a/src/v2i-hub/ImmediateForwardPlugin/manifest.json +++ b/src/v2i-hub/ImmediateForwardPlugin/manifest.json @@ -14,7 +14,7 @@ }, { "key": "Messages_Destination_1", - "default": "{ \"Messages\": [ { \"TmxType\": \"SPAT-P\", \"SendType\": \"SPAT\", \"PSID\": \"0x8002\", \"Channel\": \"183\" }, { \"TmxType\": \"MAP-P\", \"SendType\": \"MAP\", \"PSID\": \"0x8002\", \"Channel\": \"183\" }, { \"TmxType\": \"PSM-P\", \"SendType\": \"PSM\", \"PSID\": \"0x27\", \"Channel\": \"183\" } ,{ \"TmxType\": \"TMSG07-P\", \"SendType\": \"TMSG07\", \"PSID\": \"0x8002\", \"Channel\": \"183\" },{ \"TmxType\": \"TMSG03-P\", \"SendType\": \"TMSG03\", \"PSID\": \"0xBFEE\", \"Channel\": \"183\" },{ \"TmxType\": \"TMSG05-P\", \"SendType\": \"TMSG05\", \"PSID\": \"0x8003\", \"Channel\": \"183\" }, { \"TmxType\": \"SSM-P\", \"SendType\": \"SSM\", \"PSID\": \"0x8002\", \"Channel\": \"183\" },{ \"TmxType\": \"SDSM\", \"SendType\": \"SensorDataSharingMessage\", \"PSID\": \"0x8010\", \"Channel\": \"183\" }] }", + "default": "{ \"Messages\": [ { \"TmxType\": \"SPAT-P\", \"SendType\": \"SPAT\", \"PSID\": \"0x8002\", \"Channel\": \"183\" }, { \"TmxType\": \"MAP-P\", \"SendType\": \"MAP\", \"PSID\": \"0x8002\", \"Channel\": \"183\" }, { \"TmxType\": \"PSM-P\", \"SendType\": \"PSM\", \"PSID\": \"0x27\", \"Channel\": \"183\" }, { \"TmxType\": \"RSM\", \"SendType\": \"RSM\", \"PSID\": \"0x8003\", \"Channel\": \"183\" }, { \"TmxType\": \"TIM\", \"SendType\": \"TIM\", \"PSID\": \"0x8003\", \"Channel\": \"183\" }, { \"TmxType\": \"TMSG07-P\", \"SendType\": \"TMSG07\", \"PSID\": \"0x8002\", \"Channel\": \"183\" },{ \"TmxType\": \"TMSG03-P\", \"SendType\": \"TMSG03\", \"PSID\": \"0xBFEE\", \"Channel\": \"183\" },{ \"TmxType\": \"TMSG05-P\", \"SendType\": \"TMSG05\", \"PSID\": \"0x8003\", \"Channel\": \"183\" }, { \"TmxType\": \"SSM-P\", \"SendType\": \"SSM\", \"PSID\": \"0x8002\", \"Channel\": \"183\" },{ \"TmxType\": \"SDSM\", \"SendType\": \"SensorDataSharingMessage\", \"PSID\": \"0x8010\", \"Channel\": \"183\" }] }", "description": "JSON data defining the message types, PSIDs, and channel number for messages forwarded to the V2X radio at destination 1." }, { diff --git a/src/v2i-hub/RsmPlugin/CMakeLists.txt b/src/v2i-hub/RsmPlugin/CMakeLists.txt new file mode 100644 index 000000000..ccc07c2c3 --- /dev/null +++ b/src/v2i-hub/RsmPlugin/CMakeLists.txt @@ -0,0 +1,35 @@ +PROJECT ( RsmPlugin VERSION 7.5.1 LANGUAGES CXX ) + +SET (TMX_PLUGIN_NAME "RSM") +add_compile_options(-fPIC) +FIND_PACKAGE (XercesC REQUIRED) + +find_package(Qt5Core REQUIRED) +find_package(Qt5Widgets REQUIRED) +find_package(Qt5Network REQUIRED) + +find_package(qserverPedestrian REQUIRED) + +include_directories(${Qt5Widgets_INCLUDE_DIRS}) + +include_directories(${EXTERNAL_INSTALL_LOCATION}/include) +link_directories(${EXTERNAL_INSTALL_LOCATION}/lib) + +find_library(libasn1c .) + +include_directories( + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Network_INCLUDE_DIRS} +) + + +BuildTmxPlugin () + + + +TARGET_INCLUDE_DIRECTORIES ( ${PROJECT_NAME} PUBLIC ${XercesC_INCLUDE_DIRS} ${NETSNMP_INCLUDE_DIRS} ${Qt5Core_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS}) + +TARGET_LINK_LIBRARIES ( ${PROJECT_NAME} PUBLIC tmxutils ${XercesC_LIBRARY} ${NETSNMP_LIBRARIES} ${QHttpEngine_LIBRARY} Qt5Widgets Qt5Core Qt5Network ssl crypto qhttpengine qserverPedestrian) + + +link_directories(${CMAKE_PREFIX_PATH}/lib) diff --git a/src/v2i-hub/RsmPlugin/manifest.json b/src/v2i-hub/RsmPlugin/manifest.json new file mode 100644 index 000000000..7423fe90f --- /dev/null +++ b/src/v2i-hub/RsmPlugin/manifest.json @@ -0,0 +1,38 @@ +{ + "name":"RSM", + "description":"Provides Road Safety Message (RSM).", + "version":"@PROJECT_VERSION@", + "exeLocation":"/bin/RsmPlugin", + "coreIpAddr":"127.0.0.1", + "corePort":24601, + "messageTypes":[ + { + "type":"J2735", + "subtype":"RSM", + "description":"Road Safety Message used to provide road safety information to travelers." + } + ], + "configuration":[ + { + "key":"Interval", + "default":"1000", + "description":"The interval to send the RSM, in milliseconds." + }, + { + "key":"WebServiceIP", + "default":"127.0.0.1", + "description":"IP address at which the web service exists" + }, + { + "key":"WebServicePort", + "default":"12000", + "description":"Port at which Web service exists" + }, + { + "key":"LogLevel", + "default":"DEBUG", + "description": "RSM Plugin Log Level controls which log statements are printed to the terminal." + } + + ] +} diff --git a/src/v2i-hub/RsmPlugin/src/RsmPlugin.cpp b/src/v2i-hub/RsmPlugin/src/RsmPlugin.cpp new file mode 100644 index 000000000..f37c1244a --- /dev/null +++ b/src/v2i-hub/RsmPlugin/src/RsmPlugin.cpp @@ -0,0 +1,232 @@ +//========================================================================== +// Name : RsmPlugin.cpp +// Author : FHWA Saxton Transportation Operations Laboratory +// Version : +// Copyright : Copyright (c) 2023 FHWA Saxton Transportation Operations Laboratory. All rights reserved. +// Description : Rsm Plugin +//========================================================================== + +#include "include/RsmPlugin.hpp" + +namespace RsmPlugin +{ +/** + * Construct a new RsmPlugin with the given name. + * + * @param name The name to give the plugin for identification purposes. + */ +RsmPlugin::RsmPlugin(string name): PluginClient(name) +{ + // The log level can be changed from the default here. + FILELog::ReportingLevel() = FILELog::FromString("DEBUG"); + + AddMessageFilter(this, &RsmPlugin::HandleRoadSafetyMessage); + + // Subscribe to all messages specified by the filters above. + SubscribeToMessages(); +} + +void RsmPlugin::RsmRequestHandler(QHttpEngine::Socket *socket) +{ + if(socket->bytesAvailable() == 0) + { + PLOG(logERROR) << "RSM Plugin does not receive web service request content!" << endl; + writeResponse(QHttpEngine::Socket::BadRequest, socket); + return; + } + + // should read from the websocket and parse + QString st; + while(socket->bytesAvailable()>0) + { + st.append(socket->readAll()); + } + QByteArray array = st.toLocal8Bit(); + + char* rsmMsgdef = array.data(); + // Catch parse exceptions + + stringstream ss; + ss << rsmMsgdef; + PLOG(logDEBUG) << "Received from webservice: " << ss.str() << endl; + + try { + BroadcastRsm(rsmMsgdef); + writeResponse(QHttpEngine::Socket::Created, socket); + } + catch (TmxException &ex) { + PLOG(logERROR) << "Failed to encode message : " << ex.what(); + writeResponse(QHttpEngine::Socket::BadRequest, socket); + } +} + + +int RsmPlugin::StartWebService() +{ + //Web services + char *placeholderX[1]={0}; + int placeholderC=1; + QCoreApplication a(placeholderC,placeholderX); + + QHostAddress address = QHostAddress(QString::fromStdString (webip)); + quint16 port = static_cast(webport); + + + QSharedPointer handler(new OpenAPI::OAIApiRequestHandler()); + handler = QSharedPointer (new OpenAPI::OAIApiRequestHandler()); + + auto router = QSharedPointer::create(); + router->setUpRoutes(); + + QObject::connect(handler.data(), &OpenAPI::OAIApiRequestHandler::requestReceived, [&](QHttpEngine::Socket *socket) { + + this->RsmRequestHandler(socket); + }); + + QObject::connect(handler.data(), &OpenAPI::OAIApiRequestHandler::requestReceived, [&](QHttpEngine::Socket *socket) { + router->processRequest(socket); + }); + + QHttpEngine::Server server(handler.data()); + + if (!server.listen(address, port)) { + qCritical("RsmPlugin:: Unable to listen on the specified port."); + return 1; + } + PLOG(logINFO)<<"RsmPlugin:: Started web service"; + return a.exec(); + +} + +RsmPlugin::~RsmPlugin() +{ + if (_signSimClient != NULL) + delete _signSimClient; +} + +void RsmPlugin::UpdateConfigSettings() +{ + // Configuration settings are retrieved from the API using the GetConfigValue template class. + // This method does NOT execute in the main thread, so variables must be protected + // (e.g. using atomic, mutex, etc.). + + lock_guard lock(_cfgLock); + + GetConfigValue("WebServiceIP", webip); + GetConfigValue("WebServicePort", webport); +} + +void RsmPlugin::OnConfigChanged(const char *key, const char *value) +{ + PluginClient::OnConfigChanged(key, value); + UpdateConfigSettings(); +} + +void RsmPlugin::OnStateChange(IvpPluginState state) +{ + PluginClient::OnStateChange(state); + + if (state == IvpPluginState_registered) + { + UpdateConfigSettings(); + // Start webservice needs to occur after the first updateConfigSettings call to acquire port and ip configurations. + // Also needs to be called from Main thread to work. + thread webthread(&RsmPlugin::StartWebService, this); + webthread.detach(); // wait for the thread to finish + } +} + +void RsmPlugin::HandleRoadSafetyMessage(RsmMessage &msg, routeable_message &routeableMsg) +{ + PLOG(logDEBUG)<<"HandleRoadSafetyMessage"; +} + +void RsmPlugin::BroadcastRsm(char * rsmJson) +{ + + RsmMessage rsmmessage; + RsmEncodedMessage rsmENC; + tmx::message_container_type container; + unique_ptr msg; + + try + { + stringstream ss; + ss << rsmJson; + + container.load(ss); + rsmmessage.set_contents(container.get_storage().get_tree()); + + const string rsmString(rsmJson); + + rsmENC.encode_j2735_message(rsmmessage); + + msg.reset(); + msg.reset(dynamic_cast(factory.NewMessage(api::MSGSUBTYPE_ROADSAFETYMESSAGE_STRING))); + + string enc = rsmENC.get_encoding(); + msg->refresh_timestamp(); + msg->set_payload(rsmENC.get_payload_str()); + msg->set_encoding(enc); + msg->set_flags(IvpMsgFlags_RouteDSRC); + msg->addDsrcMetadata(0x8003); + msg->refresh_timestamp(); + + routeable_message *rMsg = dynamic_cast(msg.get()); + BroadcastMessage(*rMsg); + + PLOG(logINFO) << " RSM Plugin :: Broadcast RSM:: " << rsmENC.get_payload_str(); + } + catch(const exception& e) + { + PLOG(logWARNING) << "Error: " << e.what() << " broadcasting RSM for xml: " << rsmJson << endl; + } + + + +} + +/** + * Write HTTP response. + */ +void RsmPlugin::writeResponse(int responseCode , QHttpEngine::Socket *socket) { + socket->setStatusCode(responseCode); + socket->writeHeaders(); + if(socket->isOpen()){ + socket->close(); + } + +} + + +int RsmPlugin::Main() +{ + PLOG(logINFO) << "RsmPlugin:: Starting plugin.\n"; + + uint msCount = 0; + while (_plugin->state != IvpPluginState_error) + { + + msCount += 10; + + if (_plugin->state == IvpPluginState_registered) + { + RoadSafetyMessage rsm_1; + RoadSafetyMessage &rsm = rsm_1; + + this_thread::sleep_for(chrono::milliseconds(100)); + + msCount = 0; + } + } + + PLOG(logINFO) << "Plugin terminating gracefully."; + return EXIT_SUCCESS; +} + +} /* namespace RsmPlugin */ + +int main(int argc, char *argv[]) +{ + return run_plugin("RsmPlugin", argc, argv); +} diff --git a/src/v2i-hub/RsmPlugin/src/RsmPluginWorker.cpp b/src/v2i-hub/RsmPlugin/src/RsmPluginWorker.cpp new file mode 100644 index 000000000..ceb8fac60 --- /dev/null +++ b/src/v2i-hub/RsmPlugin/src/RsmPluginWorker.cpp @@ -0,0 +1,15 @@ +//========================================================================== +// Name : RsmPlugin.cpp +// Author : FHWA Saxton Transportation Operations Laboratory +// Version : +// Copyright : Copyright (c) 2024 FHWA Saxton Transportation Operations Laboratory. All rights reserved. +// Description : RSM Plugin +//========================================================================== + +#include "include/RsmPluginWorker.hpp" + +using namespace std; + +namespace RsmPlugin { + +}; diff --git a/src/v2i-hub/RsmPlugin/src/include/RsmPlugin.hpp b/src/v2i-hub/RsmPlugin/src/include/RsmPlugin.hpp new file mode 100644 index 000000000..555545249 --- /dev/null +++ b/src/v2i-hub/RsmPlugin/src/include/RsmPlugin.hpp @@ -0,0 +1,87 @@ +//========================================================================== +// Name : RsmPlugin.cpp +// Author : FHWA Saxton Transportation Operations Laboratory +// Version : +// Copyright : Copyright (c) 2024 FHWA Saxton Transportation Operations Laboratory. All rights reserved. +// Description : RSM Plugin +//========================================================================== +#pragma once +#include + +#include "PluginClient.h" +#include "PluginDataMonitor.h" + +#include +#include +#include +#include +#include + +#include +#include +#include "RsmPluginWorker.hpp" + + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#include +#endif +#include +#include +#include +#include + + +using namespace std; +using namespace tmx; +using namespace tmx::messages; +using namespace tmx::utils; +using namespace OpenAPI; + +namespace RsmPlugin +{ + +/** + * This plugin is an example to demonstrate the capabilities of a TMX plugin. + */ +class RsmPlugin: public PluginClient +{ +public: + RsmPlugin(std::string); + virtual ~RsmPlugin(); + int Main(); + +protected: + void UpdateConfigSettings(); + + // Virtual method overrides. + void OnConfigChanged(const char *key, const char *value); + void OnStateChange(IvpPluginState state); + + void HandleRoadSafetyMessage(RsmMessage &msg, routeable_message &routeableMsg); + void BroadcastRsm(char *rsmJson); + + int StartWebService(); + void RsmRequestHandler(QHttpEngine::Socket *socket); + void writeResponse(int responseCode , QHttpEngine::Socket *socket); + +private: + tmx::utils::UdpClient *_signSimClient = NULL; + J2735MessageFactory factory; + + uint16_t webport; + string webip; + +}; +mutex _cfgLock; + +} diff --git a/src/v2i-hub/RsmPlugin/src/include/RsmPluginWorker.hpp b/src/v2i-hub/RsmPlugin/src/include/RsmPluginWorker.hpp new file mode 100644 index 000000000..ca15f2d9e --- /dev/null +++ b/src/v2i-hub/RsmPlugin/src/include/RsmPluginWorker.hpp @@ -0,0 +1,43 @@ +//========================================================================== +// Name : RsmPlugin.cpp +// Author : FHWA Saxton Transportation Operations Laboratory +// Version : +// Copyright : Copyright (c) 2024 FHWA Saxton Transportation Operations Laboratory. All rights reserved. +// Description : RSM Plugin +//========================================================================== +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PluginClient.h" +#include "PluginDataMonitor.h" + +using namespace std; +using namespace tmx; +using namespace tmx::messages; +using namespace tmx::utils; + +namespace RsmPlugin { + + class RsmPluginWorker { + public: + // struct RSM { + // RoadSafetyMessage rsm; + // RSM(int anInt, double aDouble) : rsm.TemporaryID_t(anInt), number(aDouble) { } + + // }; + + }; + + + +};