From 8e14ed2f7ccdb5b84993338575b358104bbeabf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikul=C3=A1=C5=A1=20=C5=A0=C5=A5astn=C3=BD?= Date: Mon, 4 Aug 2025 11:44:14 +0200 Subject: [PATCH 01/55] Change systemd file Add user and change workingdirectory, so it does work by default with Package to Image Placer. Ensure that config path is absolute. --- resources/systemd/module-gateway.service.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/systemd/module-gateway.service.in b/resources/systemd/module-gateway.service.in index 35df8cf0..68e55858 100644 --- a/resources/systemd/module-gateway.service.in +++ b/resources/systemd/module-gateway.service.in @@ -4,10 +4,11 @@ After=network.target nss-lookup.target [Service] Type=simple +User=root Restart=always RestartSec=20 -WorkingDirectory=@CMAKE_INSTALL_SYSCONFDIR@ -ExecStart=@CMAKE_INSTALL_PREFIX@/bin/module-gateway-app --config-path=@CMAKE_INSTALL_SYSCONFDIR@/bringauto/module-gateway/config.json +WorkingDirectory=@CMAKE_INSTALL_PREFIX@ +ExecStart=@CMAKE_INSTALL_PREFIX@/bin/module-gateway-app --config-path=/@CMAKE_INSTALL_SYSCONFDIR@/bringauto/module-gateway/config.json [Install] WantedBy=multi-user.target From 26ec6fbc19cd8055ebb646b7e6bd22ed94d3bc9b Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Fri, 18 Jul 2025 15:40:31 +0200 Subject: [PATCH 02/55] performance/networking optimizations --- CMakeLists.txt | 6 +- README.md | 9 ++ include/bringauto/common_utils/EnumUtils.hpp | 36 ++++++-- .../bringauto/common_utils/ProtobufUtils.hpp | 16 ++-- .../external_client/ErrorAggregator.hpp | 7 +- .../external_client/ExternalClient.hpp | 12 +-- .../connection/ExternalConnection.hpp | 13 ++- .../communication/DummyCommunication.hpp | 28 +++++++ .../communication/ICommunicationChannel.hpp | 5 +- .../communication/MqttCommunication.hpp | 1 - .../connection/messages/NotAckedStatus.hpp | 7 +- .../messages/SentMessagesHandler.hpp | 3 +- .../internal_server/InternalServer.hpp | 36 ++++---- include/bringauto/modules/Buffer.hpp | 4 +- include/bringauto/modules/ModuleHandler.hpp | 33 ++++---- .../modules/ModuleManagerLibraryHandler.hpp | 28 +++---- .../bringauto/modules/StatusAggregator.hpp | 18 ++-- include/bringauto/settings/Constants.hpp | 1 + include/bringauto/settings/LoggerId.hpp | 27 +++++- include/bringauto/settings/LoggerWrapper.hpp | 82 +++++++++++++++++++ include/bringauto/settings/Settings.hpp | 4 +- include/bringauto/settings/SettingsParser.hpp | 14 ++-- include/bringauto/structures/AtomicQueue.hpp | 2 +- include/bringauto/structures/Connection.hpp | 13 ++- .../structures/DeviceIdentification.hpp | 2 +- .../structures/ExternalConnectionSettings.hpp | 9 +- .../structures/InternalClientMessage.hpp | 1 - .../structures/ModuleHandlerMessage.hpp | 1 - .../bringauto/structures/ModuleLibrary.hpp | 7 +- .../structures/ReconnectQueueItem.hpp | 1 - .../StatusAggregatorDeviceState.hpp | 3 +- include/bringauto/structures/ThreadTimer.hpp | 14 ++-- main.cpp | 9 +- source/bringauto/common_utils/EnumUtils.cpp | 38 +-------- .../bringauto/common_utils/ProtobufUtils.cpp | 40 ++++----- .../external_client/ErrorAggregator.cpp | 5 +- .../external_client/ExternalClient.cpp | 36 ++++---- .../connection/ExternalConnection.cpp | 62 +++++++------- .../communication/DummyCommunication.cpp | 42 ++++++++++ .../communication/MqttCommunication.cpp | 2 +- .../connection/messages/NotAckedStatus.cpp | 4 +- .../messages/SentMessagesHandler.cpp | 6 +- .../internal_server/InternalServer.cpp | 80 +++++++++--------- source/bringauto/modules/ModuleHandler.cpp | 44 +++++----- .../modules/ModuleManagerLibraryHandler.cpp | 47 +++++------ source/bringauto/modules/StatusAggregator.cpp | 32 ++++---- source/bringauto/settings/SettingsParser.cpp | 21 +++-- source/bringauto/structures/ModuleLibrary.cpp | 6 +- .../StatusAggregatorDeviceState.cpp | 6 +- test/include/InternalServerTests.hpp | 2 +- test/include/testing_utils/ConfigMock.hpp | 6 +- test/source/EnumUtilsTests.cpp | 4 +- test/source/StatusAggregatorTests.cpp | 1 + 53 files changed, 556 insertions(+), 380 deletions(-) create mode 100644 include/bringauto/external_client/connection/communication/DummyCommunication.hpp create mode 100644 include/bringauto/settings/LoggerWrapper.hpp create mode 100644 source/bringauto/external_client/connection/communication/DummyCommunication.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cfa7803..ae0f62c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,8 +8,12 @@ FIND_PACKAGE(CMLIB SET(BRINGAUTO_MODULE_GATEWAY_VERSION 1.3.4) +OPTION(MINIMUM_LOGGER_VERBOSITY "Minimum logger verbosity level" 0) + CMDEF_COMPILE_DEFINITIONS( - ALL "MODULE_GATEWAY_VERSION=\"${BRINGAUTO_MODULE_GATEWAY_VERSION}\"" + ALL + "MODULE_GATEWAY_VERSION=\"${BRINGAUTO_MODULE_GATEWAY_VERSION}\"" + "MINIMUM_LOGGER_VERBOSITY=${MINIMUM_LOGGER_VERBOSITY}" ) SET(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMDEF_LIBRARY_INSTALL_DIR}") SET(CMAKE_CXX_STANDARD 20) diff --git a/README.md b/README.md index 3610eb2d..7787b9ba 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,15 @@ make * BRINGAUTO_SYSTEM_DEP=ON/OFF - DEFAULT: OFF +* MINIMUM_LOGGER_VERBOSITY=0/1/2/3/4 + - DEFAULT: 0 + - sets the minimum logger verbosity on compile level to improve performance + - 0: DEBUG + - 1: INFO + - 2: WARNING + - 3: ERROR + - 4: CRITICAL + * CURRENTLY UNUSED * BRINGAUTO_SAMPLES=ON/OFF diff --git a/include/bringauto/common_utils/EnumUtils.hpp b/include/bringauto/common_utils/EnumUtils.hpp index f0f1fc1d..cb6b0bc5 100644 --- a/include/bringauto/common_utils/EnumUtils.hpp +++ b/include/bringauto/common_utils/EnumUtils.hpp @@ -2,6 +2,7 @@ #include #include +#include @@ -24,10 +25,20 @@ class EnumUtils { /** * @brief Converts protocol type to string * - * @param toEnum structures::ProtocolType - * @return std::string + * @param toString structures::ProtocolType + * @return std::string_view */ - static std::string protocolTypeToString(structures::ProtocolType toString); + static constexpr std::string_view protocolTypeToString(structures::ProtocolType toString) { + switch(toString) { + case structures::ProtocolType::MQTT: + return settings::Constants::MQTT; + case structures::ProtocolType::DUMMY: + return settings::Constants::DUMMY; + case structures::ProtocolType::INVALID: + default: + return ""; + } + }; /** * @brief Converts string to logger verbosity @@ -41,9 +52,24 @@ class EnumUtils { * @brief Converts logger verbosity to string * * @param verbosity logging::LoggerVerbosity - * @return std::string + * @return std::string_view */ - static std::string loggerVerbosityToString(logging::LoggerVerbosity verbosity); + static constexpr std::string_view loggerVerbosityToString(logging::LoggerVerbosity verbosity) { + switch(verbosity) { + case logging::LoggerVerbosity::Debug: + return settings::Constants::LOG_LEVEL_DEBUG; + case logging::LoggerVerbosity::Info: + return settings::Constants::LOG_LEVEL_INFO; + case logging::LoggerVerbosity::Warning: + return settings::Constants::LOG_LEVEL_WARNING; + case logging::LoggerVerbosity::Error: + return settings::Constants::LOG_LEVEL_ERROR; + case logging::LoggerVerbosity::Critical: + return settings::Constants::LOG_LEVEL_CRITICAL; + default: + return settings::Constants::LOG_LEVEL_INVALID; + } + }; }; diff --git a/include/bringauto/common_utils/ProtobufUtils.hpp b/include/bringauto/common_utils/ProtobufUtils.hpp index 5c709b5d..c124689a 100644 --- a/include/bringauto/common_utils/ProtobufUtils.hpp +++ b/include/bringauto/common_utils/ProtobufUtils.hpp @@ -5,8 +5,6 @@ #include #include -#include -#include @@ -37,7 +35,7 @@ class ProtobufUtils { const InternalProtocol::DeviceConnectResponse_ResponseType &resType); /** - * @brief Create a Internal Server Command message + * @brief Create an Internal Server Command message * * @param device Protobuf message Device * @param command command data @@ -47,7 +45,7 @@ class ProtobufUtils { const modules::Buffer &command); /** - * @brief Create a Internal Client Status message + * @brief Create an Internal Client Status message * * @param device Protobuf message Device * @param status status data @@ -67,7 +65,7 @@ class ProtobufUtils { const modules::Buffer &status); /** - * @brief Create a External Client Connect message + * @brief Create an External Client Connect message * * @param sessionId session identification * @param company name of the company @@ -81,7 +79,7 @@ class ProtobufUtils { const std::vector &devices); /** - * @brief Create a External Client Status message + * @brief Create an External Client Status message * * @param sessionId session identification * @param deviceState state of the device @@ -97,7 +95,7 @@ class ProtobufUtils { const modules::Buffer &errorMessage = modules::Buffer {}); /** - * @brief Create a External Client Command Response object + * @brief Create an External Client Command Response object * * @param sessionId session identification * @param type command response message type @@ -114,7 +112,7 @@ class ProtobufUtils { * @param status status to be copied * @param buffer buffer to copy to */ - static void copyStatusToBuffer(const InternalProtocol::DeviceStatus &status, modules::Buffer &buffer); + static void copyStatusToBuffer(const InternalProtocol::DeviceStatus &status, const modules::Buffer &buffer); /** * @brief Copy command data from DeviceCommand to a Buffer @@ -122,7 +120,7 @@ class ProtobufUtils { * @param command command to be copied * @param buffer buffer to copy to */ - static void copyCommandToBuffer(const InternalProtocol::DeviceCommand &command, modules::Buffer &buffer); + static void copyCommandToBuffer(const InternalProtocol::DeviceCommand &command, const modules::Buffer &buffer); }; } diff --git a/include/bringauto/external_client/ErrorAggregator.hpp b/include/bringauto/external_client/ErrorAggregator.hpp index e913d325..dfa47cd9 100644 --- a/include/bringauto/external_client/ErrorAggregator.hpp +++ b/include/bringauto/external_client/ErrorAggregator.hpp @@ -1,13 +1,8 @@ #pragma once #include - -#include -#include - #include -#include #include #include @@ -108,7 +103,7 @@ class ErrorAggregator { * * @see fleet-protocol/lib/common_headers/include/device_management.h */ - int is_device_type_supported(unsigned int device_type); + int is_device_type_supported(unsigned int device_type) const; private: struct DeviceState { diff --git a/include/bringauto/external_client/ExternalClient.hpp b/include/bringauto/external_client/ExternalClient.hpp index 0879964d..fb827d7a 100644 --- a/include/bringauto/external_client/ExternalClient.hpp +++ b/include/bringauto/external_client/ExternalClient.hpp @@ -20,9 +20,9 @@ namespace bringauto::external_client { class ExternalClient { public: - ExternalClient(std::shared_ptr &context, + ExternalClient(const std::shared_ptr &context, structures::ModuleLibrary &moduleLibrary, - std::shared_ptr> &toExternalQueue); + const std::shared_ptr> &toExternalQueue); /** * @brief Initialize connections, error aggregators @@ -54,7 +54,7 @@ class ExternalClient { void handleAggregatedMessages(); /** - * @brief Handle commands messages from from an external server + * @brief Handle commands messages from an external server */ void handleCommands(); @@ -68,10 +68,10 @@ class ExternalClient { /** * @brief Send aggregated status message to the external server * - * @param deviceStatus aggregated status message ready to send + * @param internalMessage aggregated status message ready to send * @return reconnect expected if true, reconnect not expected if false */ - bool sendStatus(const structures::InternalClientMessage &deviceStatus); + bool sendStatus(const structures::InternalClientMessage &internalMessage); bool insideConnectSequence_ { false }; @@ -79,7 +79,7 @@ class ExternalClient { * @brief Map of external connections, key is number from settings * - map is needed because of the possibility of multiple modules connected to one external server */ - std::map> externalConnectionMap_ {}; + std::unordered_map> externalConnectionMap_ {}; /// List of external connections, each device can have its own connection or multiple devices can share one connection std::list externalConnectionsList_ {}; /// Queue for messages from module handler to external client to be sent to external server diff --git a/include/bringauto/external_client/connection/ExternalConnection.hpp b/include/bringauto/external_client/connection/ExternalConnection.hpp index 0b19a4df..66c108b6 100644 --- a/include/bringauto/external_client/connection/ExternalConnection.hpp +++ b/include/bringauto/external_client/connection/ExternalConnection.hpp @@ -11,8 +11,6 @@ #include #include -#include - #include #include #include @@ -72,9 +70,10 @@ class ExternalConnection { * @brief Force aggregation on all devices in all modules that the connection services. * Is used before the connect sequence to assure that every device has an available status to be sent * + * @param connectedDevices * @return number of devices */ - std::vector forceAggregationOnAllDevices(std::vector connectedDevices); + std::vector forceAggregationOnAllDevices(const std::vector &connectedDevices); /** * @brief Fill error aggregator with not acknowledged status messages @@ -104,11 +103,11 @@ class ExternalConnection { * @brief Check if module type is supported * * @param moduleNum module type number - * @return true if moudle type is supported otherwise false + * @return true if module type is supported otherwise false */ - bool isModuleSupported(int moduleNum); + bool isModuleSupported(int moduleNum) const; - std::vector getAllConnectedDevices(); + std::vector getAllConnectedDevices() const; private: @@ -172,7 +171,7 @@ class ExternalConnection { /// Class handling sent messages - timers, not acknowledged statuses etc. std::unique_ptr sentMessagesHandler_ {}; /// @brief Map of error aggregators, key is module number - std::map errorAggregators_ {}; + std::unordered_map errorAggregators_ {}; /// Queue of commands received from external server, commands are processed by aggregator std::shared_ptr > commandQueue_ {}; diff --git a/include/bringauto/external_client/connection/communication/DummyCommunication.hpp b/include/bringauto/external_client/connection/communication/DummyCommunication.hpp new file mode 100644 index 00000000..07d3cd21 --- /dev/null +++ b/include/bringauto/external_client/connection/communication/DummyCommunication.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + + + +namespace bringauto::external_client::connection::communication { + +class DummyCommunication: public ICommunicationChannel { +public: + explicit DummyCommunication(const structures::ExternalConnectionSettings &settings); + + ~DummyCommunication() override; + + void initializeConnection() override; + + bool sendMessage(ExternalProtocol::ExternalClient *message) override; + + std::shared_ptr receiveMessage() override; + + void closeConnection() override; + +protected: + /// Flag to indicate if the fake connection is established + bool isConnected_ { false }; +}; + +} diff --git a/include/bringauto/external_client/connection/communication/ICommunicationChannel.hpp b/include/bringauto/external_client/connection/communication/ICommunicationChannel.hpp index 1d952c05..1c54ca4c 100644 --- a/include/bringauto/external_client/connection/communication/ICommunicationChannel.hpp +++ b/include/bringauto/external_client/connection/communication/ICommunicationChannel.hpp @@ -1,11 +1,10 @@ #pragma once #include +#include #include -#include - namespace bringauto::external_client::connection::communication { @@ -16,7 +15,7 @@ namespace bringauto::external_client::connection::communication { */ class ICommunicationChannel { public: - explicit ICommunicationChannel(const structures::ExternalConnectionSettings &settings): settings_ { settings } {}; + explicit ICommunicationChannel(structures::ExternalConnectionSettings settings): settings_ {std::move( settings )} {}; virtual ~ICommunicationChannel() = default; diff --git a/include/bringauto/external_client/connection/communication/MqttCommunication.hpp b/include/bringauto/external_client/connection/communication/MqttCommunication.hpp index 5b4ecca4..31c76650 100644 --- a/include/bringauto/external_client/connection/communication/MqttCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/MqttCommunication.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include diff --git a/include/bringauto/external_client/connection/messages/NotAckedStatus.hpp b/include/bringauto/external_client/connection/messages/NotAckedStatus.hpp index b3fef09f..f2706878 100644 --- a/include/bringauto/external_client/connection/messages/NotAckedStatus.hpp +++ b/include/bringauto/external_client/connection/messages/NotAckedStatus.hpp @@ -2,7 +2,6 @@ #include #include - #include @@ -14,8 +13,8 @@ namespace bringauto::external_client::connection::messages { */ class NotAckedStatus { public: - NotAckedStatus(const ExternalProtocol::Status &status, boost::asio::io_context &timerContext, - std::atomic &responseHandled, std::mutex &responseHandledMutex): status_ { status }, + NotAckedStatus(ExternalProtocol::Status status, boost::asio::io_context &timerContext, + std::atomic &responseHandled, std::mutex &responseHandledMutex): status_ {std::move( status )}, timer_ { timerContext }, responseHandled_ { responseHandled }, @@ -54,7 +53,7 @@ class NotAckedStatus { * * @param endConnectionFunc function which is called when status does not get response */ - void timeoutHandler(const std::function &endConnectionFunc); + void timeoutHandler(const std::function &endConnectionFunc) const; /// Status message that was not acknowledged yet ExternalProtocol::Status status_ {}; /// Timer for checking if status got response diff --git a/include/bringauto/external_client/connection/messages/SentMessagesHandler.hpp b/include/bringauto/external_client/connection/messages/SentMessagesHandler.hpp index 7ac7cdc0..cf12b59d 100644 --- a/include/bringauto/external_client/connection/messages/SentMessagesHandler.hpp +++ b/include/bringauto/external_client/connection/messages/SentMessagesHandler.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include @@ -91,7 +90,7 @@ class SentMessagesHandler { /** * @brief returns message counter of status_response - * @param status + * @param statusResponse * @return counter */ [[nodiscard]] static u_int32_t getStatusResponseCounter(const ExternalProtocol::StatusResponse &statusResponse); diff --git a/include/bringauto/internal_server/InternalServer.hpp b/include/bringauto/internal_server/InternalServer.hpp index d0d299b5..08b1aeaf 100644 --- a/include/bringauto/internal_server/InternalServer.hpp +++ b/include/bringauto/internal_server/InternalServer.hpp @@ -15,19 +15,19 @@ namespace bringauto::internal_server { /** - * Server implements internal protocol. Serves as link between Module handler and Internal client.. + * Server implements internal protocol. Serves as link between Module handler and Internal client. * It accepts connections from multiple Internal clients. * Receives messages on these connections. The message needs to begin with 4 bytes header. - * Header's format is 32 bit unsigned int with little endian endianity. + * Header's format is 32 bit unsigned int with little endian endianness. * Header represents size of the remaining part of the message. - * Messaged is send thru queue to ModuleHandler, and when answer is given resends it to Internal client. + * Messaged is send through queue to ModuleHandler, and when answer is given resends it to Internal client. */ class InternalServer { public: /** * @brief Constructs Internal Server. - * @param settings shares context with Internal Server + * @param context shares context with Internal Server * @param fromInternalQueue queue for sending data from Server to Module Handler * @param toInternalQueue queue for sending data from Module Handler to Server */ @@ -64,7 +64,7 @@ class InternalServer { /** * @brief Asynchronously receives data. - * @param connection connection that data are being sent thru + * @param connection connection that data are being sent through */ void addAsyncReceive(const std::shared_ptr &connection); @@ -79,7 +79,7 @@ class InternalServer { /** * @brief Processes buffer data and once message is complete calls handleMessage(...) - * @param connection conneection with context holding received and processed data + * @param connection connection with context holding received and processed data * @param bytesTransferred size of received data * @param bufferOffset offset of buffer where data starts * @return true if data and whole message is correct in context to fleet protocol @@ -97,16 +97,16 @@ class InternalServer { bool handleMessage(const std::shared_ptr &connection); /** - * @brief Checks if status is valid, if it is sends message to Module Handler. + * @brief Checks if status is valid. If it is, the message is sent to Module Handler. * @param connection connection with information about validity * @param client message to be checked and sent * @return true if status is valid. */ bool handleStatus(const std::shared_ptr &connection, - const InternalProtocol::InternalClient &client); + const InternalProtocol::InternalClient &client) const; /** - * @brief Checks for existance of this device and possibly its priority and calls matching method. + * @brief Checks for existence of this device and possibly its priority and calls matching method. * @param connection connection holding data * @param client message to be checked * @return true if connect is valid. @@ -132,7 +132,7 @@ class InternalServer { /** * @brief Sends response to InternalClient, that device is already connected and with higher priority. - * @param connection connection response will be sent thru + * @param connection connection response will be sent through * @param connect message containing data for response message * @param deviceId unique device identification */ @@ -142,7 +142,7 @@ class InternalServer { /** * @brief Sends response to InternalClient, that device is already connected and with same priority. - * @param connection connection response will be sent thru + * @param connection connection response will be sent through * @param connect message containing data for response message * @param deviceId unique device identification */ @@ -153,18 +153,18 @@ class InternalServer { /** * Ends all operations of previous connection using same device, * closes its socket, then replaces it in map with new connection. - * Afterwards sends new connection message to Module Handler. - * @param connection new connection to replace the old one + * Afterward sends new connection message to Module Handler. + * @param newConnection new connection to replace the old one * @param connect message to be sent * @param deviceId unique device identification */ - void changeConnection(const std::shared_ptr &connection, + void changeConnection(const std::shared_ptr &newConnection, const InternalProtocol::InternalClient &connect, const structures::DeviceIdentification &deviceId); /** * @brief Writes messages to Internal client. - * @param connection connection message will be sent thru + * @param connection connection message will be sent through * @param message message to be sent * @return true if writes are successful */ @@ -178,15 +178,15 @@ class InternalServer { void removeConnFromMap(const std::shared_ptr &connection); /** - * Periodicly checks for new messages received from module handler thru queue. + * Periodically checks for new messages received from module handler through queue. * If message is received calls validatesResponse(...). * Runs until stop() is called. */ void listenToQueue(); /** - * Validates if message belongs to any active connection, if it does resends the message to InternalClient, - * then notifies waiting connection. + * Validates if message belongs to any active connection. If it does, the message is resent to InternalClient, + * then the waiting connection is notified. * @param message message to be validated */ void validateResponse(const InternalProtocol::InternalServer &message); diff --git a/include/bringauto/modules/Buffer.hpp b/include/bringauto/modules/Buffer.hpp index cd0edfd4..a6c5e8ea 100644 --- a/include/bringauto/modules/Buffer.hpp +++ b/include/bringauto/modules/Buffer.hpp @@ -1,10 +1,8 @@ #pragma once #include -#include #include -#include #include @@ -94,7 +92,7 @@ struct Buffer final { } /** - * Underlyig data type used to hold information by shared_ptr. + * Underlying data type used to hold information by shared_ptr. * Data type in ::buffer struct is a type void*. It is not viable * to use void* in C++ --> use 1-byte data type. */ diff --git a/include/bringauto/modules/ModuleHandler.hpp b/include/bringauto/modules/ModuleHandler.hpp index ea70aeca..1ae711d3 100644 --- a/include/bringauto/modules/ModuleHandler.hpp +++ b/include/bringauto/modules/ModuleHandler.hpp @@ -3,13 +3,10 @@ #include #include #include -#include #include #include #include -#include - #include @@ -19,11 +16,11 @@ namespace bringauto::modules { class ModuleHandler { public: ModuleHandler( - std::shared_ptr &context, + const std::shared_ptr &context, structures::ModuleLibrary &moduleLibrary, - std::shared_ptr > &fromInternalQueue, - std::shared_ptr > &toInternalQueue, - std::shared_ptr > &toExternalQueue) + const std::shared_ptr > &fromInternalQueue, + const std::shared_ptr > &toInternalQueue, + const std::shared_ptr > &toExternalQueue) : context_ { context }, moduleLibrary_ { moduleLibrary }, fromInternalQueue_ { fromInternalQueue }, toInternalQueue_ { toInternalQueue }, toExternalQueue_ { toExternalQueue } {} @@ -34,13 +31,13 @@ class ModuleHandler { * Initializes all modules and handles incoming messages from Internal Server through queues * */ - void run(); + void run() const; /** * @brief Stop Module handler and clean all initialized modules * */ - void destroy(); + void destroy() const; private: @@ -48,20 +45,20 @@ class ModuleHandler { * @brief Process all incoming messages from internal server * */ - void handleMessages(); + void handleMessages() const; /** * @brief Check if there are any timeouted messages * */ - void checkTimeoutedMessages(); + void checkTimeoutedMessages() const; /** * @brief Process disconnect device * - * @param device device identification + * @param deviceId device identification */ - void handleDisconnect(const structures::DeviceIdentification& deviceId); + void handleDisconnect(const structures::DeviceIdentification& deviceId) const; /** * @brief Send aggregated status to external server @@ -70,14 +67,14 @@ class ModuleHandler { * @param device protobuf device * @param disconnected true if device is disconnected otherwise false */ - void sendAggregatedStatus(const structures::DeviceIdentification &deviceId, const InternalProtocol::Device &device, bool disconnected); + void sendAggregatedStatus(const structures::DeviceIdentification &deviceId, const InternalProtocol::Device &device, bool disconnected) const; /** * @brief Process connect message * * @param connect Connect message */ - void handleConnect(const InternalProtocol::DeviceConnect &connect); + void handleConnect(const InternalProtocol::DeviceConnect &connect) const; /** * @brief Send connect response message to internal client @@ -85,19 +82,19 @@ class ModuleHandler { * @param device internal client * @param response_type type of the response */ - void sendConnectResponse(const InternalProtocol::Device &device, InternalProtocol::DeviceConnectResponse_ResponseType response_type); + void sendConnectResponse(const InternalProtocol::Device &device, InternalProtocol::DeviceConnectResponse_ResponseType response_type) const; /** * @brief Process status message * * @param status Status message */ - void handleStatus(const InternalProtocol::DeviceStatus &status); + void handleStatus(const InternalProtocol::DeviceStatus &status) const; /** * @brief Throws an error if external queue size is too big */ - void checkExternalQueueSize(); + void checkExternalQueueSize() const; std::shared_ptr context_ {}; diff --git a/include/bringauto/modules/ModuleManagerLibraryHandler.hpp b/include/bringauto/modules/ModuleManagerLibraryHandler.hpp index cf937027..fbae8958 100644 --- a/include/bringauto/modules/ModuleManagerLibraryHandler.hpp +++ b/include/bringauto/modules/ModuleManagerLibraryHandler.hpp @@ -3,8 +3,6 @@ #include #include -#include -#include #include #include @@ -29,19 +27,19 @@ class ModuleManagerLibraryHandler { */ void loadLibrary(const std::filesystem::path &path); - int getModuleNumber(); + int getModuleNumber() const; - int isDeviceTypeSupported(unsigned int device_type); + int isDeviceTypeSupported(unsigned int device_type) const; - int sendStatusCondition(const Buffer current_status, const Buffer new_status, unsigned int device_type); + int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) const; /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. * * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h */ - int generateCommand(Buffer &generated_command, const Buffer new_status, - const Buffer current_status, const Buffer current_command, + int generateCommand(Buffer &generated_command, const Buffer &new_status, + const Buffer ¤t_status, const Buffer ¤t_command, unsigned int device_type); /** @@ -49,15 +47,15 @@ class ModuleManagerLibraryHandler { * * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h */ - int aggregateStatus(Buffer &aggregated_status, const Buffer current_status, - const Buffer new_status, unsigned int device_type); + int aggregateStatus(Buffer &aggregated_status, const Buffer ¤t_status, + const Buffer &new_status, unsigned int device_type); /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. * * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h */ - int aggregateError(Buffer &error_message, const Buffer current_error_message, const Buffer status, + int aggregateError(Buffer &error_message, const Buffer ¤t_error_message, const Buffer &status, unsigned int device_type); /** @@ -67,9 +65,9 @@ class ModuleManagerLibraryHandler { */ int generateFirstCommand(Buffer &default_command, unsigned int device_type); - int statusDataValid(const Buffer status, unsigned int device_type); + int statusDataValid(const Buffer &status, unsigned int device_type) const; - int commandDataValid(const Buffer command, unsigned int device_type); + int commandDataValid(const Buffer &command, unsigned int device_type) const; /** * @brief Constructs a buffer with the given size @@ -81,11 +79,11 @@ class ModuleManagerLibraryHandler { private: - int allocate(struct buffer *buffer_pointer, size_t size_in_bytes); + int allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const; - void deallocate(struct buffer *buffer); + void deallocate(struct buffer *buffer) const; - void *checkFunction(const char *functionName); + void *checkFunction(const char *functionName) const; /** * @brief Constructs a buffer with the same raw c buffer as provided diff --git a/include/bringauto/modules/StatusAggregator.hpp b/include/bringauto/modules/StatusAggregator.hpp index 05d034b0..03c84e90 100644 --- a/include/bringauto/modules/StatusAggregator.hpp +++ b/include/bringauto/modules/StatusAggregator.hpp @@ -4,13 +4,7 @@ #include #include -#include -#include - -#include #include -#include -#include #include @@ -151,10 +145,10 @@ class StatusAggregator { /** * @brief Get the device timeout count * - * @param key device unique key, obtained from getId function in ProtobufUtils + * @param device device unique key, obtained from getId function in ProtobufUtils * @return number of timeouts */ - int getDeviceTimeoutCount(const structures::DeviceIdentification& ); + int getDeviceTimeoutCount(const structures::DeviceIdentification& device); private: @@ -166,9 +160,9 @@ class StatusAggregator { * @param device_type device type * @return struct buffer with aggregated status message */ - Buffer aggregateStatus(structures::StatusAggregatorDeviceState &deviceState, + Buffer aggregateStatus(const structures::StatusAggregatorDeviceState &deviceState, const Buffer &status, - const unsigned int &device_type); + const unsigned int &device_type) const; /** * @brief Aggregate and set status message @@ -178,7 +172,7 @@ class StatusAggregator { * @param device_type device type */ void aggregateSetStatus(structures::StatusAggregatorDeviceState &deviceState, const Buffer &status, - const unsigned int &device_type); + const unsigned int &device_type) const; /** * @brief Aggregate, set and send status message @@ -188,7 +182,7 @@ class StatusAggregator { * @param device_type device type */ void aggregateSetSendStatus(structures::StatusAggregatorDeviceState &deviceState, const Buffer &status, - const unsigned int &device_type); + const unsigned int &device_type) const; std::shared_ptr context_ {}; diff --git a/include/bringauto/settings/Constants.hpp b/include/bringauto/settings/Constants.hpp index 730ec38d..306eb9c5 100644 --- a/include/bringauto/settings/Constants.hpp +++ b/include/bringauto/settings/Constants.hpp @@ -153,6 +153,7 @@ class Constants { inline static constexpr std::string_view PROTOCOL_TYPE { "protocol-type" }; inline static constexpr std::string_view MQTT { "MQTT" }; + inline static constexpr std::string_view DUMMY { "DUMMY" }; inline static constexpr std::string_view MQTT_SETTINGS { "mqtt-settings" }; inline static constexpr std::string_view SSL { "ssl" }; inline static constexpr std::string_view CA_FILE { "ca-file" }; diff --git a/include/bringauto/settings/LoggerId.hpp b/include/bringauto/settings/LoggerId.hpp index 062fa601..56594f43 100644 --- a/include/bringauto/settings/LoggerId.hpp +++ b/include/bringauto/settings/LoggerId.hpp @@ -1,10 +1,31 @@ #pragma once -#include +#include + +#ifndef MINIMUM_LOGGER_VERBOSITY +#define MINUMUM_LOGGER_VERBOSITY 0 // DEBUG +#endif + +#if MINIMUM_LOGGER_VERBOSITY == 0 // DEBUG +#define LOGGER_VERBOSITY bringauto::logging::LoggerVerbosity::Debug +#elif MINIMUM_LOGGER_VERBOSITY == 1 // INFO +#define LOGGER_VERBOSITY bringauto::logging::LoggerVerbosity::Info +#elif MINIMUM_LOGGER_VERBOSITY == 2 // WARNING +#define LOGGER_VERBOSITY bringauto::logging::LoggerVerbosity::Warning +#elif MINIMUM_LOGGER_VERBOSITY == 3 // ERROR +#define LOGGER_VERBOSITY bringauto::logging::LoggerVerbosity::Error +#elif MINIMUM_LOGGER_VERBOSITY == 4 // CRITICAL +#define LOGGER_VERBOSITY bringauto::logging::LoggerVerbosity::Critical +#else +#error "Invalid MINIMUM_LOGGER_VERBOSITY defined. Use DEBUG(0), INFO(1), WARNING(2), ERROR(3), or CRITICAL(4)." +#endif + + namespace bringauto::settings { -constexpr bringauto::logging::LoggerId logId = {.id = "ModuleGateway"}; -using Logger = bringauto::logging::Logger; +constexpr logging::LoggerId logId = {.id = "ModuleGateway"}; +using BaseLogger = logging::Logger; +using Logger = LoggerWrapper; } diff --git a/include/bringauto/settings/LoggerWrapper.hpp b/include/bringauto/settings/LoggerWrapper.hpp new file mode 100644 index 00000000..1d1e690c --- /dev/null +++ b/include/bringauto/settings/LoggerWrapper.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include + + + +namespace bringauto::settings { +/** + * @brief Custom Logger wrapper, which optimises out logging calls bellow the set verbosity level at compile time. + * + */ +template +class LoggerWrapper { +public: + // Forward addSink to the base logger + template + static void addSink(Args &&...args) { + BaseLogger::template addSink(std::forward(args)...); + } + + static void init(const logging::LoggerSettings &settings) { + if (settings.verbosity != verbosity) { + throw std::runtime_error("LoggerWrapper verbosity is different than the provided settings verbosity."); + } + BaseLogger::init(settings); + } + + static void destroy() { + BaseLogger::destroy(); + } + + // wrappers for common log levels + template + static void logDebug(LogArgs &&...args) { + if constexpr (verbosity > logging::LoggerVerbosity::Debug) { + return; // Skip logging if verbosity is lower than Debug + } + BaseLogger::log(logging::LoggerVerbosity::Debug, std::forward(args)...); + } + + template + static void logInfo(LogArgs &&...args) { + if constexpr (verbosity > logging::LoggerVerbosity::Info) { + return; // Skip logging if verbosity is lower than Info + } + BaseLogger::log(logging::LoggerVerbosity::Info, std::forward(args)...); + } + + template + static void logWarning(LogArgs &&...args) { + if constexpr (verbosity > logging::LoggerVerbosity::Warning) { + return; // Skip logging if verbosity is lower than Warning + } + BaseLogger::log(logging::LoggerVerbosity::Warning, std::forward(args)...); + } + + template + static void logError(LogArgs &&...args) { + if constexpr (verbosity > logging::LoggerVerbosity::Error) { + return; // Skip logging if verbosity is lower than Error + } + BaseLogger::log(logging::LoggerVerbosity::Error, std::forward(args)...); + } + + template + static void logCritical(LogArgs &&...args) { + if constexpr (verbosity > logging::LoggerVerbosity::Critical) { + return; // Skip logging if verbosity is lower than Critical + } + BaseLogger::log(logging::LoggerVerbosity::Critical, std::forward(args)...); + } + + template + static void log(logging::LoggerVerbosity dynVerbosity, LogArgs &&...args) { + if (verbosity > dynVerbosity) { + return; // Skip logging if verbosity is lower than Critical + } + BaseLogger::log(dynVerbosity, std::forward(args)...); + } +}; + +} diff --git a/include/bringauto/settings/Settings.hpp b/include/bringauto/settings/Settings.hpp index cf687f78..aad2db84 100644 --- a/include/bringauto/settings/Settings.hpp +++ b/include/bringauto/settings/Settings.hpp @@ -3,10 +3,8 @@ #include #include -#include #include #include -#include @@ -33,7 +31,7 @@ struct Settings { /** * @brief paths to shared module libraries */ - std::map modulePaths {}; + std::unordered_map modulePaths {}; /** * @brief Setting of external connection endpoints and protocols diff --git a/include/bringauto/settings/SettingsParser.hpp b/include/bringauto/settings/SettingsParser.hpp index 910c261a..2a82f3f0 100644 --- a/include/bringauto/settings/SettingsParser.hpp +++ b/include/bringauto/settings/SettingsParser.hpp @@ -36,7 +36,7 @@ class SettingsParser { * @brief Serializes settings to json * @return string with json representation of settings */ - std::string serializeToJson(); + std::string serializeToJson() const; private: cxxopts::ParseResult cmdArguments_ {}; @@ -44,18 +44,18 @@ class SettingsParser { void parseCmdArguments(int argc, char **argv); - bool areCmdArgumentsCorrect(); + bool areCmdArgumentsCorrect() const; - bool areSettingsCorrect(); + bool areSettingsCorrect() const; void fillSettings(); - void fillLoggingSettings(const nlohmann::json &file); + void fillLoggingSettings(const nlohmann::json &file) const; - void fillInternalServerSettings(const nlohmann::json &file); + void fillInternalServerSettings(const nlohmann::json &file) const; - void fillModulePathsSettings(const nlohmann::json &file); + void fillModulePathsSettings(const nlohmann::json &file) const; - void fillExternalConnectionSettings(const nlohmann::json &file); + void fillExternalConnectionSettings(const nlohmann::json &file) const; }; } diff --git a/include/bringauto/structures/AtomicQueue.hpp b/include/bringauto/structures/AtomicQueue.hpp index 40e58232..ec9149ed 100644 --- a/include/bringauto/structures/AtomicQueue.hpp +++ b/include/bringauto/structures/AtomicQueue.hpp @@ -30,7 +30,7 @@ class AtomicQueue { } /** - * @brief Waits for timeout or till being notifed that queue is not empty. + * @brief Waits for timeout or till being notified that queue is not empty. * @param timeout length of timeout * @return true if the queue is empty */ diff --git a/include/bringauto/structures/Connection.hpp b/include/bringauto/structures/Connection.hpp index 01dbe790..07e7013e 100644 --- a/include/bringauto/structures/Connection.hpp +++ b/include/bringauto/structures/Connection.hpp @@ -5,7 +5,6 @@ #include #include -#include @@ -21,6 +20,18 @@ struct Connection { */ explicit Connection(boost::asio::io_context &io_context_): socket(io_context_) {} + /** + * @brief Get the remote endpoint address as a string. + * @return remote endpoint address as a string + */ + [[nodiscard]] + std::string getRemoteEndpointAddress() const { + if (!socket.is_open()) { + return "(N/A, socket is not open)"; + } + return socket.remote_endpoint().address().to_string(); + } + /** * @brief socket endpoint in communication between server and client */ diff --git a/include/bringauto/structures/DeviceIdentification.hpp b/include/bringauto/structures/DeviceIdentification.hpp index d8308ceb..619244ff 100644 --- a/include/bringauto/structures/DeviceIdentification.hpp +++ b/include/bringauto/structures/DeviceIdentification.hpp @@ -59,7 +59,7 @@ class DeviceIdentification { /** * @brief get priority value - * @return priorirt value + * @return priority value */ [[nodiscard]] uint32_t getPriority() const; diff --git a/include/bringauto/structures/ExternalConnectionSettings.hpp b/include/bringauto/structures/ExternalConnectionSettings.hpp index b1fd0a15..bdc462e8 100644 --- a/include/bringauto/structures/ExternalConnectionSettings.hpp +++ b/include/bringauto/structures/ExternalConnectionSettings.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include @@ -13,14 +13,15 @@ namespace bringauto::structures { */ enum class ProtocolType { INVALID = -1, - MQTT + MQTT, + DUMMY }; struct ExternalConnectionSettings { /// Communication protocol ProtocolType protocolType {}; - /// Map of protocol specific settings, taken from config, pair of key and value - std::map protocolSettings {}; + /// Map of protocol specific settings, taken from config, a pair of key and value + std::unordered_map protocolSettings {}; /// Ip address of the external server std::string serverIp {}; /// Port of the external server diff --git a/include/bringauto/structures/InternalClientMessage.hpp b/include/bringauto/structures/InternalClientMessage.hpp index 20f4ca38..06442535 100644 --- a/include/bringauto/structures/InternalClientMessage.hpp +++ b/include/bringauto/structures/InternalClientMessage.hpp @@ -2,7 +2,6 @@ #include -#include #include diff --git a/include/bringauto/structures/ModuleHandlerMessage.hpp b/include/bringauto/structures/ModuleHandlerMessage.hpp index 87f30ab7..4d9c0c73 100644 --- a/include/bringauto/structures/ModuleHandlerMessage.hpp +++ b/include/bringauto/structures/ModuleHandlerMessage.hpp @@ -2,7 +2,6 @@ #include -#include #include diff --git a/include/bringauto/structures/ModuleLibrary.hpp b/include/bringauto/structures/ModuleLibrary.hpp index 79761e2d..5f37de37 100644 --- a/include/bringauto/structures/ModuleLibrary.hpp +++ b/include/bringauto/structures/ModuleLibrary.hpp @@ -3,7 +3,6 @@ #include #include -#include #include @@ -22,7 +21,7 @@ struct ModuleLibrary { * * @param libPaths paths to the libraries */ - void loadLibraries(const std::map &libPaths); + void loadLibraries(const std::unordered_map &libPaths); /** * @brief Initialize status aggregators with context @@ -31,9 +30,9 @@ struct ModuleLibrary { */ void initStatusAggregators(std::shared_ptr &context); /// Map of module handlers, key is module id - std::map> moduleLibraryHandlers {}; + std::unordered_map> moduleLibraryHandlers {}; /// Map of status aggregators, key is module id - std::map> statusAggregators {}; + std::unordered_map> statusAggregators {}; }; } diff --git a/include/bringauto/structures/ReconnectQueueItem.hpp b/include/bringauto/structures/ReconnectQueueItem.hpp index fb87c71a..0687b5b8 100644 --- a/include/bringauto/structures/ReconnectQueueItem.hpp +++ b/include/bringauto/structures/ReconnectQueueItem.hpp @@ -14,7 +14,6 @@ namespace bringauto::structures { * @brief Item in reconnect queue in external client */ struct ReconnectQueueItem { -public: explicit ReconnectQueueItem( const std::reference_wrapper &connection, bool reconnect): connection_ { connection }, reconnect { reconnect } {} diff --git a/include/bringauto/structures/StatusAggregatorDeviceState.hpp b/include/bringauto/structures/StatusAggregatorDeviceState.hpp index 97a198bc..13c80d2d 100644 --- a/include/bringauto/structures/StatusAggregatorDeviceState.hpp +++ b/include/bringauto/structures/StatusAggregatorDeviceState.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -73,7 +72,7 @@ class StatusAggregatorDeviceState { /** * @brief Add a command to the queue of received commands from the external server. - * Commands are being retreived by the getCommand method, which also removes them from the queue. + * Commands are being retrieved by the getCommand method, which also removes them from the queue. * If the queue is full, the oldest command will be removed. * * @param commandBuffer command Buffer diff --git a/include/bringauto/structures/ThreadTimer.hpp b/include/bringauto/structures/ThreadTimer.hpp index b9e340b7..671ff259 100644 --- a/include/bringauto/structures/ThreadTimer.hpp +++ b/include/bringauto/structures/ThreadTimer.hpp @@ -4,11 +4,8 @@ #include #include -#include #include -#include #include -#include #include #include @@ -27,13 +24,12 @@ class ThreadTimer final { /** * @brief Constructor * - * @param context Global context - * @param func Function which should be executed - * @param interval Interval of time in which function will be executed - * (in seconds) + * @param context Global context + * @param function Function which should be executed + * @param deviceId DeviceIdentification */ - explicit ThreadTimer(std::shared_ptr &context, - std::function &function, + explicit ThreadTimer(const std::shared_ptr &context, + const std::function &function, const DeviceIdentification& deviceId): timer_ { context->ioContext }, fun_ { function }, deviceId_ { deviceId } {} diff --git a/main.cpp b/main.cpp index a607e9f1..7af68694 100644 --- a/main.cpp +++ b/main.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -36,8 +37,7 @@ void initLogger(const bringauto::structures::LoggingSettings &settings) { bringauto::settings::Logger::addSink(paramFileSink); } - bringauto::logging::LoggerSettings params { "ModuleGateway", - bringauto::logging::LoggerVerbosity::Debug }; + const bringauto::logging::LoggerSettings params { "ModuleGateway", LOGGER_VERBOSITY }; bringauto::settings::Logger::init(params); } @@ -46,6 +46,11 @@ int main(int argc, char **argv) { namespace bas = bringauto::structures; namespace baset = bringauto::settings; auto context = std::make_shared(); + + // Disable synchronization with stdio to improve performance + std::ios_base::sync_with_stdio(false); + std::cin.tie(nullptr); + try { baset::SettingsParser settingsParser; if(!settingsParser.parseSettings(argc, argv)) { diff --git a/source/bringauto/common_utils/EnumUtils.cpp b/source/bringauto/common_utils/EnumUtils.cpp index 614e7eca..d7ab700a 100644 --- a/source/bringauto/common_utils/EnumUtils.cpp +++ b/source/bringauto/common_utils/EnumUtils.cpp @@ -1,5 +1,4 @@ #include -#include #include #include @@ -11,19 +10,12 @@ structures::ProtocolType EnumUtils::stringToProtocolType(std::string toEnum) { std::transform(toEnum.begin(), toEnum.end(), toEnum.begin(), ::toupper); if(toEnum == settings::Constants::MQTT) { return structures::ProtocolType::MQTT; - } + } else if(toEnum == settings::Constants::DUMMY) { + return structures::ProtocolType::DUMMY; + } return structures::ProtocolType::INVALID; } -std::string EnumUtils::protocolTypeToString(structures::ProtocolType toString) { - std::string result {}; - if(toString == structures::ProtocolType::MQTT) { - result = settings::Constants::MQTT; - } - std::transform(result.begin(), result.end(), result.begin(), ::tolower); - return result; -} - logging::LoggerVerbosity EnumUtils::stringToLoggerVerbosity(std::string toEnum) { std::transform(toEnum.begin(), toEnum.end(), toEnum.begin(), ::toupper); if(toEnum == settings::Constants::LOG_LEVEL_DEBUG) { @@ -41,28 +33,4 @@ logging::LoggerVerbosity EnumUtils::stringToLoggerVerbosity(std::string toEnum) return logging::LoggerVerbosity::Debug; } -std::string EnumUtils::loggerVerbosityToString(logging::LoggerVerbosity verbosity) { - std::string result {}; - switch(verbosity) { - case logging::LoggerVerbosity::Debug: - result = settings::Constants::LOG_LEVEL_DEBUG; - break; - case logging::LoggerVerbosity::Info: - result = settings::Constants::LOG_LEVEL_INFO; - break; - case logging::LoggerVerbosity::Warning: - result = settings::Constants::LOG_LEVEL_WARNING; - break; - case logging::LoggerVerbosity::Error: - result = settings::Constants::LOG_LEVEL_ERROR; - break; - case logging::LoggerVerbosity::Critical: - result = settings::Constants::LOG_LEVEL_CRITICAL; - break; - default: - result = settings::Constants::LOG_LEVEL_INVALID; - break; - } - return result; -} } diff --git a/source/bringauto/common_utils/ProtobufUtils.cpp b/source/bringauto/common_utils/ProtobufUtils.cpp index 3e1fd762..553ea11d 100644 --- a/source/bringauto/common_utils/ProtobufUtils.cpp +++ b/source/bringauto/common_utils/ProtobufUtils.cpp @@ -1,9 +1,5 @@ #include -#include - -#include - namespace bringauto::common_utils { @@ -12,10 +8,9 @@ InternalProtocol::InternalServer ProtobufUtils::createInternalServerConnectResponseMessage(const InternalProtocol::Device &device, const InternalProtocol::DeviceConnectResponse_ResponseType &resType) { InternalProtocol::InternalServer message {}; - auto response = message.mutable_deviceconnectresponse(); + const auto response = message.mutable_deviceconnectresponse(); response->set_responsetype(resType); - auto device_ = response->mutable_device(); - device_->CopyFrom(device); + *response->mutable_device() = std::move(device); return message; } @@ -23,12 +18,11 @@ InternalProtocol::InternalServer ProtobufUtils::createInternalServerCommandMessage(const InternalProtocol::Device &device, const modules::Buffer &command) { InternalProtocol::InternalServer message {}; - auto deviceCommand = message.mutable_devicecommand(); + const auto deviceCommand = message.mutable_devicecommand(); if (command.isAllocated()) { deviceCommand->set_commanddata(command.getStructBuffer().data, command.getStructBuffer().size_in_bytes); } - auto device_ = deviceCommand->mutable_device(); - device_->CopyFrom(device); + *deviceCommand->mutable_device() = std::move(device); return message; } @@ -36,12 +30,11 @@ InternalProtocol::InternalClient ProtobufUtils::createInternalClientStatusMessage(const InternalProtocol::Device &device, const modules::Buffer &status) { InternalProtocol::InternalClient message {}; - auto deviceStatus = message.mutable_devicestatus(); + const auto deviceStatus = message.mutable_devicestatus(); if (status.isAllocated()) { deviceStatus->set_statusdata(status.getStructBuffer().data, status.getStructBuffer().size_in_bytes); } - auto device_ = deviceStatus->mutable_device(); - device_->CopyFrom(device); + *deviceStatus->mutable_device() = std::move(device); return message; } @@ -60,14 +53,15 @@ ExternalProtocol::ExternalClient ProtobufUtils::createExternalClientConnect(cons const std::string &vehicleName, const std::vector &devices) { ExternalProtocol::ExternalClient externalMessage {}; - auto connectMessage = externalMessage.mutable_connect(); + const auto connectMessage = externalMessage.mutable_connect(); connectMessage->set_sessionid(sessionId); connectMessage->set_company(company); connectMessage->set_vehiclename(vehicleName); + connectMessage->mutable_devices()->Reserve(devices.size()); for(const auto &tmpDevice: devices) { - auto devicePtr = connectMessage->add_devices(); + const auto devicePtr = connectMessage->add_devices(); devicePtr->CopyFrom(tmpDevice.convertToIPDevice()); } @@ -81,7 +75,7 @@ ExternalProtocol::ExternalClient ProtobufUtils::createExternalClientStatus(const const modules::Buffer &errorMessage) { ExternalProtocol::ExternalClient externalMessage {}; ExternalProtocol::Status *status = externalMessage.mutable_status(); - status->mutable_devicestatus()->CopyFrom(deviceStatus); + *status->mutable_devicestatus() = std::move(deviceStatus); status->set_sessionid(sessionId); status->set_devicestate(deviceState); status->set_messagecounter(messageCounter); @@ -102,24 +96,26 @@ ExternalProtocol::ExternalClient ProtobufUtils::createExternalClientCommandRespo return externalMessage; } -void ProtobufUtils::copyStatusToBuffer(const InternalProtocol::DeviceStatus &status, modules::Buffer &buffer) { +void ProtobufUtils::copyStatusToBuffer(const InternalProtocol::DeviceStatus &status, const modules::Buffer &buffer) { if (!buffer.isAllocated()) { throw BufferNotAllocated { "Buffer is not allocated" }; } - if (status.statusdata().size() > buffer.getStructBuffer().size_in_bytes) { + const auto statusSize = status.statusdata().size(); + if (statusSize > buffer.getStructBuffer().size_in_bytes) { throw BufferTooSmall { "Buffer does not have enough space allocated for status" }; } - std::memcpy(buffer.getStructBuffer().data, status.statusdata().c_str(), status.statusdata().size()); + std::memcpy(buffer.getStructBuffer().data, status.statusdata().c_str(), statusSize); } -void ProtobufUtils::copyCommandToBuffer(const InternalProtocol::DeviceCommand &command, modules::Buffer &buffer) { +void ProtobufUtils::copyCommandToBuffer(const InternalProtocol::DeviceCommand &command, const modules::Buffer &buffer) { if (!buffer.isAllocated()) { throw BufferNotAllocated { "Buffer is not allocated" }; } - if (command.commanddata().size() > buffer.getStructBuffer().size_in_bytes) { + const auto commandSize = command.commanddata().size(); + if (commandSize > buffer.getStructBuffer().size_in_bytes) { throw BufferTooSmall { "Buffer does not have enough space allocated for command" }; } - std::memcpy(buffer.getStructBuffer().data, command.commanddata().c_str(), command.commanddata().size()); + std::memcpy(buffer.getStructBuffer().data, command.commanddata().c_str(), commandSize); } } diff --git a/source/bringauto/external_client/ErrorAggregator.cpp b/source/bringauto/external_client/ErrorAggregator.cpp index 02afaf99..b0f650fe 100644 --- a/source/bringauto/external_client/ErrorAggregator.cpp +++ b/source/bringauto/external_client/ErrorAggregator.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -58,7 +57,7 @@ int ErrorAggregator::get_error(modules::Buffer &error, const structures::DeviceI return DEVICE_NOT_REGISTERED; } - auto ¤tError = devices_[device].errorMessage; + const auto ¤tError = devices_[device].errorMessage; if(!currentError.isAllocated()) { return NO_MESSAGE_AVAILABLE; @@ -79,7 +78,7 @@ int ErrorAggregator::get_module_number() const { return module_->getModuleNumber(); } -int ErrorAggregator::is_device_type_supported(unsigned int device_type) { +int ErrorAggregator::is_device_type_supported(unsigned int device_type) const { return module_->isDeviceTypeSupported(device_type); } diff --git a/source/bringauto/external_client/ExternalClient.cpp b/source/bringauto/external_client/ExternalClient.cpp index b7e2b617..65c964f4 100644 --- a/source/bringauto/external_client/ExternalClient.cpp +++ b/source/bringauto/external_client/ExternalClient.cpp @@ -3,9 +3,11 @@ #include #include #include - +#include #include +#include + #include @@ -14,9 +16,9 @@ namespace bringauto::external_client { namespace ip = InternalProtocol; -ExternalClient::ExternalClient(std::shared_ptr &context, +ExternalClient::ExternalClient(const std::shared_ptr &context, structures::ModuleLibrary &moduleLibrary, - std::shared_ptr> &toExternalQueue): + const std::shared_ptr> &toExternalQueue): toExternalQueue_ { toExternalQueue }, context_ { context }, moduleLibrary_ { moduleLibrary }, @@ -44,7 +46,8 @@ void ExternalClient::handleCommand(const InternalProtocol::DeviceCommand &device const auto &device = deviceCommand.device(); const auto &moduleNumber = device.module(); auto &statusAggregators = moduleLibrary_.statusAggregators; - if(not statusAggregators.contains(moduleNumber)) { + const auto it = statusAggregators.find(moduleNumber); + if (it == statusAggregators.end()) { settings::Logger::logWarning("Module with module number {} does no exists", static_cast(moduleNumber)); return; } @@ -54,13 +57,13 @@ void ExternalClient::handleCommand(const InternalProtocol::DeviceCommand &device settings::Logger::logWarning("Received empty command for device: {} {}", device.devicerole(), device.devicename()); return; } - auto &moduleLibraryHandler = moduleLibrary_.moduleLibraryHandlers.at(moduleNumber); - auto commandBuffer = moduleLibraryHandler->constructBuffer(commandData.size()); + const auto &moduleLibraryHandler = moduleLibrary_.moduleLibraryHandlers.at(moduleNumber); + const auto commandBuffer = moduleLibraryHandler->constructBuffer(commandData.size()); common_utils::ProtobufUtils::copyCommandToBuffer(deviceCommand, commandBuffer); - auto deviceId = structures::DeviceIdentification(device); - int ret = statusAggregators.at(moduleNumber)->update_command(commandBuffer, deviceId); + const auto deviceId = structures::DeviceIdentification(device); + int ret = it->second->update_command(commandBuffer, deviceId); if (ret == OK) { settings::Logger::logInfo("Command for device {} was added to queue", device.devicename()); } @@ -84,16 +87,21 @@ void ExternalClient::run() { } void ExternalClient::initConnections() { - for(auto const &connection: context_->settings->externalConnectionSettingsList) { - externalConnectionsList_.emplace_back(context_, moduleLibrary_, connection, fromExternalQueue_, + for(auto const &connectionSettings: context_->settings->externalConnectionSettingsList) { + externalConnectionsList_.emplace_back(context_, moduleLibrary_, connectionSettings, fromExternalQueue_, reconnectQueue_); auto &newConnection = externalConnectionsList_.back(); std::shared_ptr communicationChannel; - switch(connection.protocolType) { + switch(connectionSettings.protocolType) { case structures::ProtocolType::MQTT: communicationChannel = std::make_shared( - connection, context_->settings->company, context_->settings->vehicleName + connectionSettings, context_->settings->company, context_->settings->vehicleName + ); + break; + case structures::ProtocolType::DUMMY: + communicationChannel = std::make_shared( + connectionSettings ); break; case structures::ProtocolType::INVALID: @@ -103,7 +111,7 @@ void ExternalClient::initConnections() { } newConnection.init(communicationChannel); - for(auto const &moduleNumber: connection.modules) { + for(auto const &moduleNumber: connectionSettings.modules) { externalConnectionMap_.emplace(moduleNumber, newConnection); } } @@ -139,7 +147,7 @@ void ExternalClient::handleAggregatedMessages() { bool ExternalClient::sendStatus(const structures::InternalClientMessage &internalMessage) { auto &deviceStatus = internalMessage.getMessage().devicestatus(); const auto &moduleNumber = deviceStatus.device().module(); - auto it = externalConnectionMap_.find(moduleNumber); + const auto it = externalConnectionMap_.find(moduleNumber); if(it == externalConnectionMap_.end()) { settings::Logger::logError("Module number {} not found in the map\n", static_cast(moduleNumber)); return true; diff --git a/source/bringauto/external_client/connection/ExternalConnection.cpp b/source/bringauto/external_client/connection/ExternalConnection.cpp index dc6f08c9..e7f58c2a 100644 --- a/source/bringauto/external_client/connection/ExternalConnection.cpp +++ b/source/bringauto/external_client/connection/ExternalConnection.cpp @@ -1,15 +1,16 @@ #include #include #include - #include +#include + #include namespace bringauto::external_client::connection { -using log = bringauto::settings::Logger; +using log = settings::Logger; ExternalConnection::ExternalConnection(const std::shared_ptr &context, structures::ModuleLibrary &moduleLibrary, @@ -41,23 +42,24 @@ void ExternalConnection::sendStatus(const InternalProtocol::DeviceStatus &status const auto &device = status.device(); const auto &deviceModule = device.module(); - if(!errorAggregators_.contains(deviceModule)){ + const auto it = errorAggregators_.find(deviceModule); + if(it == errorAggregators_.end()) { log::logError( "Status with module number ({}) was passed to external connection, that doesn't support this module", static_cast(device.module())); return; } - auto &errorAggregator = errorAggregators_.at(deviceModule); - auto deviceId = structures::DeviceIdentification(device); - auto moduleLibraryHandler = moduleLibrary_.moduleLibraryHandlers.at(deviceModule); + auto &errorAggregator = it->second; + const auto deviceId = structures::DeviceIdentification(device); + const auto moduleLibraryHandler = moduleLibrary_.moduleLibraryHandlers.at(deviceModule); modules::Buffer lastStatus {}; - auto isRegistered = errorAggregator.get_last_status(lastStatus, deviceId); + const auto isRegistered = errorAggregator.get_last_status(lastStatus, deviceId); if (isRegistered == DEVICE_NOT_REGISTERED){ deviceState = ExternalProtocol::Status_DeviceState_CONNECTING; const auto &statusData = status.statusdata(); - auto statusBuffer = moduleLibraryHandler->constructBuffer(statusData.size()); + const auto statusBuffer = moduleLibraryHandler->constructBuffer(statusData.size()); if (!statusBuffer.isEmpty()) { common_utils::ProtobufUtils::copyStatusToBuffer(status, statusBuffer); } @@ -186,7 +188,7 @@ int ExternalConnection::statusMessageHandle(const std::vectorsecond; if(sentMessagesHandler_->isDeviceConnected(deviceId)) { responseType = ExternalProtocol::CommandResponse_Type_OK; commandQueue_->pushAndNotify(commandMessage.devicecommand()); @@ -378,28 +381,25 @@ void ExternalConnection::fillErrorAggregatorWithNotAckedStatuses() { void ExternalConnection::fillErrorAggregator(const InternalProtocol::DeviceStatus &deviceStatus) { int moduleNum = deviceStatus.device().module(); - if(not errorAggregators_.contains(moduleNum)){ + const auto it = errorAggregators_.find(moduleNum); + if(it == errorAggregators_.end()) { log::logError("Module with module number {} does no exists", moduleNum); return; } fillErrorAggregatorWithNotAckedStatuses(); - if(errorAggregators_.find(moduleNum) != errorAggregators_.end()) { - const auto &statusData = deviceStatus.statusdata(); - auto statusBuffer = moduleLibrary_.moduleLibraryHandlers.at(moduleNum)->constructBuffer( - statusData.size()); - if (!statusBuffer.isEmpty()) { - common_utils::ProtobufUtils::copyStatusToBuffer(deviceStatus, statusBuffer); - } - - auto deviceId = structures::DeviceIdentification(deviceStatus.device()); - auto &errorAggregator = errorAggregators_.at(moduleNum); - errorAggregator.add_status_to_error_aggregator(statusBuffer, deviceId); - } else { - log::logError("Device status with unsupported module was passed to fillErrorAggregator()"); + const auto &statusData = deviceStatus.statusdata(); + const auto statusBuffer = moduleLibrary_.moduleLibraryHandlers.at(moduleNum)->constructBuffer( + statusData.size()); + if (!statusBuffer.isEmpty()) { + common_utils::ProtobufUtils::copyStatusToBuffer(deviceStatus, statusBuffer); } + + const auto deviceId = structures::DeviceIdentification(deviceStatus.device()); + auto &errorAggregator = it->second; + errorAggregator.add_status_to_error_aggregator(statusBuffer, deviceId); } -std::vector ExternalConnection::forceAggregationOnAllDevices(std::vector connectedDevices) { +std::vector ExternalConnection::forceAggregationOnAllDevices(const std::vector &connectedDevices) { std::vector forcedDevices {}; for(const auto &device: connectedDevices) { modules::Buffer last_status {}; @@ -412,11 +412,11 @@ std::vector ExternalConnection::forceAggregati return forcedDevices; } -std::vector ExternalConnection::getAllConnectedDevices() { +std::vector ExternalConnection::getAllConnectedDevices() const { std::vector devices {}; for(const auto &moduleNumber: settings_.modules) { std::list unique_devices {}; - int ret = moduleLibrary_.statusAggregators.at(moduleNumber)->get_unique_devices(unique_devices); + const int ret = moduleLibrary_.statusAggregators.at(moduleNumber)->get_unique_devices(unique_devices); if(ret <= 0) { log::logWarning("Module {} does not have any connected devices", moduleNumber); continue; @@ -436,8 +436,8 @@ void ExternalConnection::setNotInitialized() { state_.exchange(ConnectionState::NOT_INITIALIZED); } -bool ExternalConnection::isModuleSupported(int moduleNum) { - return errorAggregators_.find(moduleNum) != errorAggregators_.end(); +bool ExternalConnection::isModuleSupported(int moduleNum) const { + return errorAggregators_.contains(moduleNum); } diff --git a/source/bringauto/external_client/connection/communication/DummyCommunication.cpp b/source/bringauto/external_client/connection/communication/DummyCommunication.cpp new file mode 100644 index 00000000..0a770fee --- /dev/null +++ b/source/bringauto/external_client/connection/communication/DummyCommunication.cpp @@ -0,0 +1,42 @@ +#include +#include + + + +namespace bringauto::external_client::connection::communication { + +DummyCommunication::DummyCommunication(const structures::ExternalConnectionSettings &settings) : ICommunicationChannel(settings) { + settings::Logger::logDebug("Creating DummyCommunication"); +} + +DummyCommunication::~DummyCommunication() { + closeConnection(); +} + +void DummyCommunication::initializeConnection() { + settings::Logger::logDebug("Initializing DummyCommunication connection"); + isConnected_ = true; +} + +bool DummyCommunication::sendMessage(ExternalProtocol::ExternalClient *message) { + if (!isConnected_) { + settings::Logger::logDebug("Failed sending message, DummyCommunication is not connected"); + return false; + } + + settings::Logger::logDebug("Sending message in DummyCommunication"); + return true; +} + +std::shared_ptr DummyCommunication::receiveMessage() { + settings::Logger::logDebug("Receiving message in DummyCommunication"); + return nullptr; +} + +void DummyCommunication::closeConnection() { + settings::Logger::logDebug("Closing DummyCommunication connection"); + isConnected_ = false; +} + + +} diff --git a/source/bringauto/external_client/connection/communication/MqttCommunication.cpp b/source/bringauto/external_client/connection/communication/MqttCommunication.cpp index a9e380d1..969da2ca 100644 --- a/source/bringauto/external_client/connection/communication/MqttCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/MqttCommunication.cpp @@ -64,7 +64,7 @@ void MqttCommunication::initializeConnection() { if (client_ != nullptr && client_->is_connected()) { return; } - else if (client_ != nullptr) { + if (client_ != nullptr) { closeConnection(); } connect(); diff --git a/source/bringauto/external_client/connection/messages/NotAckedStatus.cpp b/source/bringauto/external_client/connection/messages/NotAckedStatus.cpp index 354297b8..1f0fb137 100644 --- a/source/bringauto/external_client/connection/messages/NotAckedStatus.cpp +++ b/source/bringauto/external_client/connection/messages/NotAckedStatus.cpp @@ -5,8 +5,6 @@ #include #include -#include - namespace bringauto::external_client::connection::messages { @@ -25,7 +23,7 @@ void NotAckedStatus::cancelTimer() { timer_.cancel(); } -void NotAckedStatus::timeoutHandler(const std::function &endConnectionFunc) { +void NotAckedStatus::timeoutHandler(const std::function &endConnectionFunc) const { std::string loggingStr("Status response Timeout (" + std::to_string(status_.messagecounter()) + "):"); std::unique_lock lock(responseHandledMutex_); if(responseHandled_.load()) { diff --git a/source/bringauto/external_client/connection/messages/SentMessagesHandler.cpp b/source/bringauto/external_client/connection/messages/SentMessagesHandler.cpp index c50ef638..9bbb4fec 100644 --- a/source/bringauto/external_client/connection/messages/SentMessagesHandler.cpp +++ b/source/bringauto/external_client/connection/messages/SentMessagesHandler.cpp @@ -23,7 +23,7 @@ void SentMessagesHandler::addNotAckedStatus(const ExternalProtocol::Status &stat int SentMessagesHandler::acknowledgeStatus(const ExternalProtocol::StatusResponse &statusResponse) { std::scoped_lock lock {ackMutex_}; - auto responseCounter = getStatusResponseCounter(statusResponse); + const auto responseCounter = getStatusResponseCounter(statusResponse); for(auto i = 0u; i < notAckedStatuses_.size(); ++i) { if(getStatusCounter(notAckedStatuses_[i]->getStatus()) == responseCounter) { notAckedStatuses_[i]->cancelTimer(); @@ -55,7 +55,7 @@ void SentMessagesHandler::addDeviceAsConnected(const structures::DeviceIdentific } void SentMessagesHandler::deleteConnectedDevice(const structures::DeviceIdentification &device) { - auto it = std::find(connectedDevices_.begin(), connectedDevices_.end(), device); + const auto it = std::find(connectedDevices_.begin(), connectedDevices_.end(), device); if(it != connectedDevices_.end()) { connectedDevices_.erase(it); } else { @@ -74,7 +74,7 @@ bool SentMessagesHandler::isAnyDeviceConnected() const { } void SentMessagesHandler::clearAllTimers() { - for(auto ¬AckedStatus: notAckedStatuses_) { + for(const auto ¬AckedStatus: notAckedStatuses_) { notAckedStatus->cancelTimer(); } responseHandled_ = false; diff --git a/source/bringauto/internal_server/InternalServer.cpp b/source/bringauto/internal_server/InternalServer.cpp index dd1aa924..6f9a93ed 100644 --- a/source/bringauto/internal_server/InternalServer.cpp +++ b/source/bringauto/internal_server/InternalServer.cpp @@ -7,14 +7,14 @@ namespace bringauto::internal_server { -using log = bringauto::settings::Logger; +using log = settings::Logger; void InternalServer::run() { log::logInfo("Internal server started, constants used: fleet_protocol_timeout_length: {}, queue_timeout_length: {}", settings::fleet_protocol_timeout_length.count(), settings::queue_timeout_length.count()); - boost::asio::ip::tcp::endpoint endpoint { boost::asio::ip::tcp::v4(), context_->settings->port }; + const boost::asio::ip::tcp::endpoint endpoint { boost::asio::ip::tcp::v4(), context_->settings->port }; acceptor_.open(endpoint.protocol()); acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); acceptor_.bind(endpoint); @@ -33,11 +33,15 @@ void InternalServer::addAsyncAccept() { log::logError("Error in addAsyncAccept(): {}", error.message()); return; } - boost::asio::socket_base::keep_alive option(true); - connection->socket.set_option(option); + + const boost::asio::socket_base::keep_alive keepAliveOption(true); + connection->socket.set_option(keepAliveOption); + const boost::asio::ip::tcp::no_delay noDelayOption(true); + connection->socket.set_option(noDelayOption); + log::logInfo("Accepted connection with Internal Client, " "connection's ip address is {}", - connection->socket.remote_endpoint().address().to_string()); + connection->getRemoteEndpointAddress()); addAsyncReceive(connection); addAsyncAccept(); }); @@ -74,14 +78,14 @@ void InternalServer::asyncReceiveHandler( } else { log::logWarning( "Internal Client with ip address {} has been disconnected. Reason: {}", - connection->socket.remote_endpoint().address().to_string(), error.message()); + connection->getRemoteEndpointAddress(), error.message()); } std::lock_guard lock(serverMutex_); removeConnFromMap(connection); return; } - bool result = processBufferData(connection, bytesTransferred); + const bool result = processBufferData(connection, bytesTransferred); if(result) { addAsyncReceive(connection); } else { @@ -98,19 +102,19 @@ bool InternalServer::processBufferData( "Error in processBufferData(...): bufferOffset: {} is greater than bytesTransferred: {}, " "Invalid bufferOffset: {} received from Internal Client, " "connection's ip address is {}", bufferOffset, bytesTransferred, - connection->socket.remote_endpoint().address().to_string()); + connection->getRemoteEndpointAddress()); return false; } auto &completeMessageSize = connection->connContext.completeMessageSize; auto &completeMessage = connection->connContext.completeMessage; auto &buffer = connection->connContext.buffer; - const uint8_t headerSize = settings::header; + constexpr uint8_t headerSize = settings::header; if(bytesTransferred < headerSize && completeMessageSize == 0) { log::logError( "Error in processBufferData(...): Incomplete header received from Internal Client, " - "connection's ip address is {}", connection->socket.remote_endpoint().address().to_string()); + "connection's ip address is {}", connection->getRemoteEndpointAddress()); return false; } @@ -121,7 +125,7 @@ bool InternalServer::processBufferData( uint32_t size { 0 }; - std::copy(dataBegin, dataBegin + headerSize, (uint8_t *)&size); + std::copy_n(dataBegin, headerSize, reinterpret_cast(&size)); completeMessageSize = size; dataBegin = dataBegin + headerSize; @@ -129,13 +133,13 @@ bool InternalServer::processBufferData( if(bytesLeft < headerSize) { log::logError( "Error in processBufferData(...): Incomplete header received from Internal Client, " - "connection's ip address is {}", connection->socket.remote_endpoint().address().to_string()); + "connection's ip address is {}", connection->getRemoteEndpointAddress()); return false; } bytesLeft -= headerSize; } const auto messageBytesLeft = std::min(completeMessageSize - completeMessage.size(), bytesLeft); - auto dataEnd = dataBegin + messageBytesLeft; + const auto dataEnd = dataBegin + messageBytesLeft; std::copy(dataBegin, dataEnd, std::back_inserter(completeMessage)); @@ -150,7 +154,7 @@ bool InternalServer::processBufferData( if(bytesLeft < messageBytesLeft) { log::logError("Error in processBufferData(...): messageBytesLeft: {} is greater than bytesLeft: {}, " "connection's ip address is {}", messageBytesLeft, bytesLeft, - connection->socket.remote_endpoint().address().to_string()); + connection->getRemoteEndpointAddress()); return false; } bytesLeft -= messageBytesLeft; @@ -158,14 +162,14 @@ bool InternalServer::processBufferData( if(bytesTransferred < bytesLeft) { log::logError("Error in processBufferData(...): bytesLeft: {} is greater than bytesTransferred: {}, " "connection's ip address is {}", bytesLeft, bytesTransferred, - connection->socket.remote_endpoint().address().to_string()); + connection->getRemoteEndpointAddress()); return false; } if(bytesLeft && !processBufferData(connection, bytesLeft, bytesTransferred - bytesLeft)) { log::logError("Error in processBufferData(...): " "Received extra invalid bytes of data: {} from Internal Client, " "connection's ip address is {}", bytesLeft, - connection->socket.remote_endpoint().address().to_string()); + connection->getRemoteEndpointAddress()); return false; } return true; @@ -178,7 +182,7 @@ bool InternalServer::handleMessage(const std::shared_ptr connection->connContext.completeMessage.size())) { log::logError( "Error in handleMessage(...): message received from Internal Client cannot be parsed, " - "connection's ip address is {}", connection->socket.remote_endpoint().address().to_string()); + "connection's ip address is {}", connection->getRemoteEndpointAddress()); return false; } if(client.has_devicestatus()) { @@ -192,7 +196,7 @@ bool InternalServer::handleMessage(const std::shared_ptr } else { log::logError( "Error in handleMessage(...): message received from Internal Client cannot be parsed, " - "connection's ip address is {}", connection->socket.remote_endpoint().address().to_string()); + "connection's ip address is {}", connection->getRemoteEndpointAddress()); return false; } std::unique_lock lk(connection->connectionMutex); @@ -204,19 +208,19 @@ bool InternalServer::handleMessage(const std::shared_ptr log::logError("Error in handleMessage(...): " "Module Handler did not respond to a message in time, " "connection's ip address is {}", - connection->socket.remote_endpoint().address().to_string()); + connection->getRemoteEndpointAddress()); return false; } return !context_->ioContext.stopped(); } bool InternalServer::handleStatus(const std::shared_ptr &connection, - const InternalProtocol::InternalClient &client) { + const InternalProtocol::InternalClient &client) const { if(!connection->ready) { log::logError("Error in handleStatus(...): " "received status from Internal Client without being connected, " "connection's ip address is {}", - connection->socket.remote_endpoint().address().to_string()); + connection->getRemoteEndpointAddress()); return false; } std::lock_guard lk(connection->connectionMutex); @@ -232,12 +236,12 @@ bool InternalServer::handleConnection(const std::shared_ptrsocket.remote_endpoint().address().to_string()); + connection->getRemoteEndpointAddress()); return false; } - structures::DeviceIdentification deviceId { client.deviceconnect().device() }; - auto existingConnection = findConnection(deviceId); + const structures::DeviceIdentification deviceId { client.deviceconnect().device() }; + const auto existingConnection = findConnection(deviceId); if(not existingConnection) { connectNewDevice(connection, client, deviceId); @@ -258,7 +262,7 @@ bool InternalServer::handleConnection(const std::shared_ptr &connection, const InternalProtocol::InternalClient &connect, const structures::DeviceIdentification &deviceId) { - auto message = common_utils::ProtobufUtils::createInternalServerConnectResponseMessage( + const auto message = common_utils::ProtobufUtils::createInternalServerConnectResponseMessage( connect.deviceconnect().device(), InternalProtocol::DeviceConnectResponse_ResponseType_HIGHER_PRIORITY_ALREADY_CONNECTED); log::logInfo( @@ -294,7 +298,7 @@ void InternalServer::respondWithHigherPriorityConnected(const std::shared_ptr &connection, const InternalProtocol::InternalClient &connect, const structures::DeviceIdentification &deviceId) { - auto message = common_utils::ProtobufUtils::createInternalServerConnectResponseMessage( + const auto message = common_utils::ProtobufUtils::createInternalServerConnectResponseMessage( connect.deviceconnect().device(), InternalProtocol::DeviceConnectResponse_ResponseType_ALREADY_CONNECTED); log::logInfo( @@ -309,7 +313,7 @@ void InternalServer::respondWithAlreadyConnected(const std::shared_ptr &newConnection, const InternalProtocol::InternalClient &connect, const structures::DeviceIdentification &deviceId) { - auto oldConnection = findConnection(deviceId); + const auto oldConnection = findConnection(deviceId); if(oldConnection) { removeConnFromMap(oldConnection); } @@ -335,22 +339,22 @@ bool InternalServer::sendResponse(const std::shared_ptr log::logError("Error in sendResponse(...): " "Cannot write message header to Internal Client, " "connection's ip address is {}", - connection->socket.remote_endpoint().address().to_string()); + connection->getRemoteEndpointAddress()); return false; } try { log::logDebug("Sending response to Internal Client, " "connection's ip address is {}", - connection->socket.remote_endpoint().address().to_string()); + connection->getRemoteEndpointAddress()); const auto dataWSize = connection->socket.write_some(boost::asio::buffer(data)); if(dataWSize != header) { log::logError("Error in sendResponse(...): " "Cannot write data to Internal Client, " "connection's ip address is {}", - connection->socket.remote_endpoint().address().to_string()); + connection->getRemoteEndpointAddress()); return false; } - } catch(const boost::exception &e) { + } catch(const boost::exception &) { log::logError("Error in sendResponse(...): " "Cannot write data to Internal Client"); return false; @@ -381,7 +385,7 @@ void InternalServer::validateResponse(const InternalProtocol::InternalServer &me deviceId = message.deviceconnectresponse().device(); } std::lock_guard lock(serverMutex_); - auto connection = findConnection(deviceId); + const auto connection = findConnection(deviceId); if(connection && connection->deviceId->getPriority() == deviceId.getPriority()) { sendResponse(connection, message); @@ -400,10 +404,10 @@ void InternalServer::removeConnFromMap(const std::shared_ptrdeviceId == nullptr) { return; } - auto it = std::find_if(connectedDevices_.begin(), connectedDevices_.end(), - [&connection](const std::shared_ptr &toCompare) { - return connection->deviceId->isSame(toCompare->deviceId); - }); + const auto it = std::find_if(connectedDevices_.begin(), connectedDevices_.end(), + [&connection](const std::shared_ptr &toCompare) { + return connection->deviceId->isSame(toCompare->deviceId); + }); if(it != connectedDevices_.end() && (*it)->deviceId->getPriority() == connection->deviceId->getPriority()) { connectedDevices_.erase(it); @@ -420,7 +424,7 @@ void InternalServer::removeConnFromMap(const std::shared_ptr InternalServer::findConnection(const structures::DeviceIdentification &deviceId) { std::shared_ptr connectionFound {}; - auto it = std::find_if(connectedDevices_.begin(), connectedDevices_.end(), + const auto it = std::find_if(connectedDevices_.begin(), connectedDevices_.end(), [&deviceId](const auto& toCompare) { return deviceId.isSame(toCompare->deviceId); }); diff --git a/source/bringauto/modules/ModuleHandler.cpp b/source/bringauto/modules/ModuleHandler.cpp index 0b23e2e5..5f1bd819 100644 --- a/source/bringauto/modules/ModuleHandler.cpp +++ b/source/bringauto/modules/ModuleHandler.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include @@ -11,7 +11,7 @@ namespace bringauto::modules { namespace ip = InternalProtocol; -void ModuleHandler::destroy() { +void ModuleHandler::destroy() const { while(not fromInternalQueue_->empty()) { auto &message = fromInternalQueue_->front(); if(message.disconnected()) { @@ -22,13 +22,13 @@ void ModuleHandler::destroy() { settings::Logger::logInfo("Module handler stopped"); } -void ModuleHandler::run() { +void ModuleHandler::run() const { settings::Logger::logInfo("Module handler started, constants used: queue_timeout_length: {}, status_aggregation_timeout: {}", settings::queue_timeout_length.count(), settings::status_aggregation_timeout.count()); handleMessages(); } -void ModuleHandler::handleMessages() { +void ModuleHandler::handleMessages() const { while(not context_->ioContext.stopped()) { if(fromInternalQueue_->waitForValueWithTimeout(settings::queue_timeout_length)) { checkTimeoutedMessages(); @@ -48,12 +48,12 @@ void ModuleHandler::handleMessages() { } } -void ModuleHandler::checkTimeoutedMessages(){ +void ModuleHandler::checkTimeoutedMessages() const { for (const auto& [key, statusAggregator] : moduleLibrary_.statusAggregators) { auto moduleLibraryHandler = moduleLibrary_.moduleLibraryHandlers.at(key); if(statusAggregator->getTimeoutedMessageReady()){ std::list unique_devices {}; - int ret = statusAggregator->get_unique_devices(unique_devices); + const int ret = statusAggregator->get_unique_devices(unique_devices); if (ret == NOT_OK) { settings::Logger::logError("Could not get unique devices in checkTimeoutedMessages"); return; @@ -62,7 +62,7 @@ void ModuleHandler::checkTimeoutedMessages(){ for (auto &device: unique_devices) { while(true) { Buffer aggregatedStatusBuffer {}; - int remainingMessages = statusAggregator->get_aggregated_status(aggregatedStatusBuffer, device); + const int remainingMessages = statusAggregator->get_aggregated_status(aggregatedStatusBuffer, device); if(remainingMessages < 0) { break; } @@ -85,10 +85,10 @@ void ModuleHandler::checkTimeoutedMessages(){ } } -void ModuleHandler::handleDisconnect(const structures::DeviceIdentification& deviceId) { +void ModuleHandler::handleDisconnect(const structures::DeviceIdentification& deviceId) const { const auto &moduleNumber = deviceId.getModule(); const std::string& deviceName { deviceId.getDeviceName() }; - auto &statusAggregators = moduleLibrary_.statusAggregators; + const auto &statusAggregators = moduleLibrary_.statusAggregators; if(not statusAggregators.contains(moduleNumber)) { settings::Logger::logWarning("Module number: {} is not supported", moduleNumber); @@ -106,8 +106,8 @@ void ModuleHandler::handleDisconnect(const structures::DeviceIdentification& dev settings::Logger::logWarning("Force aggregation failed on device: {} with error code: {}", deviceName, ret); return; } - auto device = structures::DeviceIdentification(deviceId); - auto internalProtocolDevice = device.convertToIPDevice(); + const auto device = structures::DeviceIdentification(deviceId); + const auto internalProtocolDevice = device.convertToIPDevice(); sendAggregatedStatus(deviceId, internalProtocolDevice, true); statusAggregator->remove_device(deviceId); @@ -116,7 +116,7 @@ void ModuleHandler::handleDisconnect(const structures::DeviceIdentification& dev } void ModuleHandler::sendAggregatedStatus(const structures::DeviceIdentification &deviceId, const ip::Device &device, - bool disconnected) { + bool disconnected) const { const auto &statusAggregator = moduleLibrary_.statusAggregators.at(deviceId.getModule()); Buffer aggregatedStatusBuffer {}; statusAggregator->get_aggregated_status(aggregatedStatusBuffer, deviceId); @@ -127,11 +127,11 @@ void ModuleHandler::sendAggregatedStatus(const structures::DeviceIdentification checkExternalQueueSize(); } -void ModuleHandler::handleConnect(const ip::DeviceConnect &connect) { +void ModuleHandler::handleConnect(const ip::DeviceConnect &connect) const { const auto &device = connect.device(); const auto &moduleNumber = device.module(); const auto &deviceName = device.devicename(); - auto &statusAggregators = moduleLibrary_.statusAggregators; + const auto &statusAggregators = moduleLibrary_.statusAggregators; settings::Logger::logInfo("Module handler received Connect message from device: {}", deviceName); if(not statusAggregators.contains(moduleNumber)) { @@ -147,7 +147,7 @@ void ModuleHandler::handleConnect(const ip::DeviceConnect &connect) { return; } - auto deviceId = structures::DeviceIdentification(device); + const auto deviceId = structures::DeviceIdentification(device); if(statusAggregator->is_device_valid(deviceId) == OK) { settings::Logger::logInfo("Device {} is replaced by device with higher priority", deviceName); } @@ -156,13 +156,13 @@ void ModuleHandler::handleConnect(const ip::DeviceConnect &connect) { } void -ModuleHandler::sendConnectResponse(const ip::Device &device, ip::DeviceConnectResponse_ResponseType response_type) { - auto response = common_utils::ProtobufUtils::createInternalServerConnectResponseMessage(device, response_type); +ModuleHandler::sendConnectResponse(const ip::Device &device, ip::DeviceConnectResponse_ResponseType response_type) const { + const auto response = common_utils::ProtobufUtils::createInternalServerConnectResponseMessage(device, response_type); toInternalQueue_->pushAndNotify(structures::ModuleHandlerMessage(false,response)); settings::Logger::logInfo("New device {} is trying to connect, sending response {}", device.devicename(), static_cast(response_type)); } -void ModuleHandler::handleStatus(const ip::DeviceStatus &status) { +void ModuleHandler::handleStatus(const ip::DeviceStatus &status) const { const auto &device = status.device(); const auto &moduleNumber = device.module(); const auto &deviceName = device.devicename(); @@ -173,11 +173,11 @@ void ModuleHandler::handleStatus(const ip::DeviceStatus &status) { settings::Logger::logWarning("Module number: {} is not supported", static_cast(moduleNumber)); return; } - auto statusAggregator = statusAggregators[moduleNumber]; + const auto statusAggregator = statusAggregators[moduleNumber]; const auto moduleHandler = moduleLibrary_.moduleLibraryHandlers.at(moduleNumber); const auto &statusData = status.statusdata(); - auto statusBuffer = moduleHandler->constructBuffer(statusData.size()); + const auto statusBuffer = moduleHandler->constructBuffer(statusData.size()); if (!statusBuffer.isEmpty()) { common_utils::ProtobufUtils::copyStatusToBuffer(status, statusBuffer); } @@ -198,7 +198,7 @@ void ModuleHandler::handleStatus(const ip::DeviceStatus &status) { Buffer commandBuffer {}; int getCommandRc = statusAggregator->get_command(statusBuffer, deviceId, commandBuffer); if(getCommandRc == OK) { - auto deviceCommandMessage = common_utils::ProtobufUtils::createInternalServerCommandMessage(device, + const auto deviceCommandMessage = common_utils::ProtobufUtils::createInternalServerCommandMessage(device, commandBuffer); toInternalQueue_->pushAndNotify(structures::ModuleHandlerMessage(false, deviceCommandMessage)); settings::Logger::logDebug("Module handler successfully retrieved command and sent it to device: {}", deviceName); @@ -213,7 +213,7 @@ void ModuleHandler::handleStatus(const ip::DeviceStatus &status) { } } -void ModuleHandler::checkExternalQueueSize() { +void ModuleHandler::checkExternalQueueSize() const { if(toExternalQueue_->size() > settings::max_external_queue_size) { settings::Logger::logError("External queue size is too big, external client is not handling messages"); //temporarily disabled to verify if the bug related to deadlocks is fixed diff --git a/source/bringauto/modules/ModuleManagerLibraryHandler.cpp b/source/bringauto/modules/ModuleManagerLibraryHandler.cpp index 8296e556..b075a5e7 100644 --- a/source/bringauto/modules/ModuleManagerLibraryHandler.cpp +++ b/source/bringauto/modules/ModuleManagerLibraryHandler.cpp @@ -1,7 +1,8 @@ #include - #include +#include + #include #include @@ -17,7 +18,7 @@ struct FunctionTypeDeducer> { using fncptr = R (*)(Args...); }; -using log = bringauto::settings::Logger; +using log = settings::Logger; ModuleManagerLibraryHandler::~ModuleManagerLibraryHandler() { if(module_ != nullptr) { @@ -56,25 +57,25 @@ void ModuleManagerLibraryHandler::loadLibrary(const std::filesystem::path &path) log::logDebug("Library " + path.string() + " was successfully loaded"); } -void *ModuleManagerLibraryHandler::checkFunction(const char *functionName) { - auto function = dlsym(module_, functionName); +void *ModuleManagerLibraryHandler::checkFunction(const char *functionName) const { + const auto function = dlsym(module_, functionName); if(not function) { throw std::runtime_error {"Function " + std::string(functionName) + " is not included in library"}; } return function; } -int ModuleManagerLibraryHandler::getModuleNumber() { +int ModuleManagerLibraryHandler::getModuleNumber() const { return getModuleNumber_(); } -int ModuleManagerLibraryHandler::isDeviceTypeSupported(unsigned int device_type) { +int ModuleManagerLibraryHandler::isDeviceTypeSupported(unsigned int device_type) const { return isDeviceTypeSupported_(device_type); } -int ModuleManagerLibraryHandler::sendStatusCondition(const Buffer current_status, - const Buffer new_status, - unsigned int device_type) { +int ModuleManagerLibraryHandler::sendStatusCondition(const Buffer ¤t_status, + const Buffer &new_status, + unsigned int device_type) const { struct ::buffer current_status_raw_buffer {}; struct ::buffer new_status_raw_buffer {}; @@ -89,9 +90,9 @@ int ModuleManagerLibraryHandler::sendStatusCondition(const Buffer current_status } int ModuleManagerLibraryHandler::generateCommand(Buffer &generated_command, - const Buffer new_status, - const Buffer current_status, - const Buffer current_command, unsigned int device_type) { + const Buffer &new_status, + const Buffer ¤t_status, + const Buffer ¤t_command, unsigned int device_type) { struct ::buffer raw_buffer {}; struct ::buffer new_status_raw_buffer {}; struct ::buffer current_status_raw_buffer {}; @@ -118,8 +119,8 @@ int ModuleManagerLibraryHandler::generateCommand(Buffer &generated_command, } int ModuleManagerLibraryHandler::aggregateStatus(Buffer &aggregated_status, - const Buffer current_status, - const Buffer new_status, unsigned int device_type) { + const Buffer ¤t_status, + const Buffer &new_status, unsigned int device_type) { struct ::buffer raw_buffer {}; struct ::buffer current_status_raw_buffer {}; struct ::buffer new_status_raw_buffer {}; @@ -131,7 +132,7 @@ int ModuleManagerLibraryHandler::aggregateStatus(Buffer &aggregated_status, new_status_raw_buffer = new_status.getStructBuffer(); } - int ret = aggregateStatus_(&raw_buffer, current_status_raw_buffer, new_status_raw_buffer, device_type); + const int ret = aggregateStatus_(&raw_buffer, current_status_raw_buffer, new_status_raw_buffer, device_type); if (ret == OK) { aggregated_status = constructBufferByTakeOwnership(raw_buffer); } else { @@ -141,8 +142,8 @@ int ModuleManagerLibraryHandler::aggregateStatus(Buffer &aggregated_status, } int ModuleManagerLibraryHandler::aggregateError(Buffer &error_message, - const Buffer current_error_message, - const Buffer status, unsigned int device_type) { + const Buffer ¤t_error_message, + const Buffer &status, unsigned int device_type) { struct ::buffer raw_buffer {}; struct ::buffer current_error_raw_buffer {}; @@ -155,7 +156,7 @@ int ModuleManagerLibraryHandler::aggregateError(Buffer &error_message, status_raw_buffer = status.getStructBuffer(); } - int ret = aggregateError_(&raw_buffer, current_error_raw_buffer, status_raw_buffer, device_type); + const int ret = aggregateError_(&raw_buffer, current_error_raw_buffer, status_raw_buffer, device_type); if (ret == OK) { error_message = constructBufferByTakeOwnership(raw_buffer); } else { @@ -166,7 +167,7 @@ int ModuleManagerLibraryHandler::aggregateError(Buffer &error_message, int ModuleManagerLibraryHandler::generateFirstCommand(Buffer &default_command, unsigned int device_type) { struct ::buffer raw_buffer {}; - int ret = generateFirstCommand_(&raw_buffer, device_type); + const int ret = generateFirstCommand_(&raw_buffer, device_type); if (ret == OK) { default_command = constructBufferByTakeOwnership(raw_buffer); } else { @@ -175,7 +176,7 @@ int ModuleManagerLibraryHandler::generateFirstCommand(Buffer &default_command, u return ret; } -int ModuleManagerLibraryHandler::statusDataValid(const Buffer status, unsigned int device_type) { +int ModuleManagerLibraryHandler::statusDataValid(const Buffer &status, unsigned int device_type) const { struct ::buffer raw_buffer {}; if (status.isAllocated()) { raw_buffer = status.getStructBuffer(); @@ -183,7 +184,7 @@ int ModuleManagerLibraryHandler::statusDataValid(const Buffer status, unsigned i return statusDataValid_(raw_buffer, device_type); } -int ModuleManagerLibraryHandler::commandDataValid(const Buffer command, unsigned int device_type) { +int ModuleManagerLibraryHandler::commandDataValid(const Buffer &command, unsigned int device_type) const { struct ::buffer raw_buffer {}; if (command.isAllocated()) { raw_buffer = command.getStructBuffer(); @@ -191,11 +192,11 @@ int ModuleManagerLibraryHandler::commandDataValid(const Buffer command, unsigned return commandDataValid_(raw_buffer, device_type); } -int ModuleManagerLibraryHandler::allocate(struct buffer *buffer_pointer, size_t size_in_bytes){ +int ModuleManagerLibraryHandler::allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const { return allocate_(buffer_pointer, size_in_bytes); } -void ModuleManagerLibraryHandler::deallocate(struct buffer *buffer){ +void ModuleManagerLibraryHandler::deallocate(struct buffer *buffer) const { deallocate_(buffer); } diff --git a/source/bringauto/modules/StatusAggregator.cpp b/source/bringauto/modules/StatusAggregator.cpp index daa95bca..ee7c11df 100644 --- a/source/bringauto/modules/StatusAggregator.cpp +++ b/source/bringauto/modules/StatusAggregator.cpp @@ -2,19 +2,21 @@ #include #include +#include + namespace bringauto::modules { -using log = bringauto::settings::Logger; +using log = settings::Logger; -int StatusAggregator::clear_device(const structures::DeviceIdentification &key) { - if(is_device_valid(key) == NOT_OK) { +int StatusAggregator::clear_device(const structures::DeviceIdentification &device) { + if(is_device_valid(device) == NOT_OK) { return DEVICE_NOT_REGISTERED; } - if (not devices.contains(key)) { + if (not devices.contains(device)) { return NOT_OK; } - auto &deviceState = devices.at(key); + auto &deviceState = devices.at(device); auto &aggregatedMessages = deviceState.aggregatedMessages(); while(not aggregatedMessages.empty()) { aggregatedMessages.pop(); @@ -23,8 +25,8 @@ int StatusAggregator::clear_device(const structures::DeviceIdentification &key) } Buffer -StatusAggregator::aggregateStatus(structures::StatusAggregatorDeviceState &deviceState, const Buffer &status, - const unsigned int &device_type) { +StatusAggregator::aggregateStatus(const structures::StatusAggregatorDeviceState &deviceState, const Buffer &status, + const unsigned int &device_type) const { auto &currStatus = deviceState.getStatus(); Buffer aggregatedStatusBuff {}; if (module_->aggregateStatus(aggregatedStatusBuff, currStatus, status, device_type) != OK) { @@ -34,14 +36,14 @@ StatusAggregator::aggregateStatus(structures::StatusAggregatorDeviceState &devic } void StatusAggregator::aggregateSetStatus(structures::StatusAggregatorDeviceState &deviceState, const Buffer &status, - const unsigned int &device_type) { + const unsigned int &device_type) const { const auto aggregatedStatusBuff = aggregateStatus(deviceState, status, device_type); deviceState.setStatus(aggregatedStatusBuff); } void StatusAggregator::aggregateSetSendStatus(structures::StatusAggregatorDeviceState &deviceState, const Buffer &status, - const unsigned int &device_type) { + const unsigned int &device_type) const { const auto aggregatedStatusBuff = aggregateStatus(deviceState, status, device_type); deviceState.setStatusAndResetTimer(aggregatedStatusBuff); @@ -92,14 +94,14 @@ int StatusAggregator::add_status_to_aggregator(const Buffer& status, return COMMAND_INVALID; } - std::function timeouted_force_aggregation = [device, this]( + const std::function timeouted_force_aggregation = [device, this]( const structures::DeviceIdentification& deviceId) { timeoutedMessageReady_.store(true); deviceTimeouts_[device]++; return force_aggregation_on_device(deviceId); }; - devices.insert( - { device, structures::StatusAggregatorDeviceState(context_, timeouted_force_aggregation, device, commandBuffer, status) }); + devices.emplace( + device, structures::StatusAggregatorDeviceState(context_, timeouted_force_aggregation, device, commandBuffer, status)); force_aggregation_on_device(device); return 1; @@ -107,7 +109,7 @@ int StatusAggregator::add_status_to_aggregator(const Buffer& status, auto &deviceState = devices.at(device); auto &currStatus = deviceState.getStatus(); - auto &aggregatedMessages = deviceState.aggregatedMessages(); + const auto &aggregatedMessages = deviceState.aggregatedMessages(); if(module_->sendStatusCondition(currStatus, status, device_type) == OK) { aggregateSetSendStatus(deviceState, status, device_type); } else { @@ -222,8 +224,8 @@ bool StatusAggregator::getTimeoutedMessageReady() const { return timeoutedMessageReady_.load(); } -int StatusAggregator::getDeviceTimeoutCount(const structures::DeviceIdentification &key) { - return deviceTimeouts_[key]; +int StatusAggregator::getDeviceTimeoutCount(const structures::DeviceIdentification &device) { + return deviceTimeouts_[device]; } } diff --git a/source/bringauto/settings/SettingsParser.cpp b/source/bringauto/settings/SettingsParser.cpp index aa3df832..a5885cb2 100644 --- a/source/bringauto/settings/SettingsParser.cpp +++ b/source/bringauto/settings/SettingsParser.cpp @@ -48,7 +48,7 @@ void SettingsParser::parseCmdArguments(int argc, char **argv) { } } -bool SettingsParser::areCmdArgumentsCorrect() { +bool SettingsParser::areCmdArgumentsCorrect() const { bool isCorrect = true; std::vector requiredParams { std::string(Constants::CONFIG_PATH) @@ -87,7 +87,7 @@ bool SettingsParser::areCmdArgumentsCorrect() { return isCorrect; } -bool SettingsParser::areSettingsCorrect() { +bool SettingsParser::areSettingsCorrect() const { bool isCorrect = true; if(settings_->loggingSettings.file.use && !std::filesystem::exists(settings_->loggingSettings.file.path)) { @@ -127,7 +127,7 @@ void SettingsParser::fillSettings() { fillExternalConnectionSettings(file); } -void SettingsParser::fillLoggingSettings(const nlohmann::json &file) { +void SettingsParser::fillLoggingSettings(const nlohmann::json &file) const { const auto &consoleLogging = file[std::string(Constants::LOGGING)][std::string(Constants::LOGGING_CONSOLE)]; const auto &fileLogging = file[std::string(Constants::LOGGING)][std::string(Constants::LOGGING_FILE)]; @@ -141,7 +141,7 @@ void SettingsParser::fillLoggingSettings(const nlohmann::json &file) { settings_->loggingSettings.file.path = std::filesystem::path(fileLogging[std::string(Constants::LOG_PATH)]); } -void SettingsParser::fillInternalServerSettings(const nlohmann::json &file) { +void SettingsParser::fillInternalServerSettings(const nlohmann::json &file) const { if(cmdArguments_.count(std::string(Constants::PORT))) { settings_->port = cmdArguments_[std::string(Constants::PORT)].as(); } else { @@ -149,13 +149,13 @@ void SettingsParser::fillInternalServerSettings(const nlohmann::json &file) { } } -void SettingsParser::fillModulePathsSettings(const nlohmann::json &file) { +void SettingsParser::fillModulePathsSettings(const nlohmann::json &file) const { for(auto &[key, val]: file[std::string(Constants::MODULE_PATHS)].items()) { settings_->modulePaths[stoi(key)] = val; } } -void SettingsParser::fillExternalConnectionSettings(const nlohmann::json &file) { +void SettingsParser::fillExternalConnectionSettings(const nlohmann::json &file) const { settings_->vehicleName = file[std::string(Constants::EXTERNAL_CONNECTION)][std::string( Constants::VEHICLE_NAME)]; settings_->company = file[std::string(Constants::EXTERNAL_CONNECTION)][std::string(Constants::COMPANY)]; @@ -170,6 +170,8 @@ void SettingsParser::fillExternalConnectionSettings(const nlohmann::json &file) case structures::ProtocolType::MQTT: settingsName = std::string(Constants::MQTT_SETTINGS); break; + case structures::ProtocolType::DUMMY: + break; case structures::ProtocolType::INVALID: default: std::cerr << "Invalid protocol type: " << endpoint[std::string(Constants::PROTOCOL_TYPE)] << std::endl; @@ -180,7 +182,7 @@ void SettingsParser::fillExternalConnectionSettings(const nlohmann::json &file) externalConnectionSettings.port = endpoint[std::string(Constants::PORT)]; externalConnectionSettings.modules = endpoint[std::string(Constants::MODULES)].get>(); - if(endpoint.find(settingsName) != endpoint.end()) { + if(!settingsName.empty() && endpoint.find(settingsName) != endpoint.end()) { for(auto &[key, val]: endpoint[settingsName].items()) { externalConnectionSettings.protocolSettings[key] = to_string(val); } @@ -190,7 +192,7 @@ void SettingsParser::fillExternalConnectionSettings(const nlohmann::json &file) } } -std::string SettingsParser::serializeToJson() { +std::string SettingsParser::serializeToJson() const { nlohmann::json settingsAsJson {}; settingsAsJson[std::string(Constants::LOGGING)][std::string(Constants::LOGGING_CONSOLE)] @@ -223,6 +225,9 @@ std::string SettingsParser::serializeToJson() { case structures::ProtocolType::MQTT: settingsName = std::string(Constants::MQTT_SETTINGS); break; + case structures::ProtocolType::DUMMY: + settingsName = std::string(Constants::DUMMY); + break; case structures::ProtocolType::INVALID: settingsName = "INVALID"; break; diff --git a/source/bringauto/structures/ModuleLibrary.cpp b/source/bringauto/structures/ModuleLibrary.cpp index b5d69428..c6d61c8b 100644 --- a/source/bringauto/structures/ModuleLibrary.cpp +++ b/source/bringauto/structures/ModuleLibrary.cpp @@ -2,8 +2,6 @@ #include -#include - namespace bringauto::structures { @@ -13,7 +11,7 @@ ModuleLibrary::~ModuleLibrary() { [](auto &pair) { pair.second->destroy_status_aggregator(); }); } -void ModuleLibrary::loadLibraries(const std::map &libPaths) { +void ModuleLibrary::loadLibraries(const std::unordered_map &libPaths) { for(auto const &[key, path]: libPaths) { auto handler = std::make_shared(); handler->loadLibrary(path); @@ -30,7 +28,7 @@ void ModuleLibrary::initStatusAggregators(std::shared_ptr &contex auto statusAggregator = std::make_shared(context, libraryHandler); statusAggregator->init_status_aggregator(); auto moduleNumber = statusAggregator->get_module_number(); - if(statusAggregators.find(moduleNumber) != statusAggregators.end()) { + if(statusAggregators.contains(moduleNumber)) { settings::Logger::logWarning("Module with number: {} is already initialized, so skipping this module", moduleNumber); continue; diff --git a/source/bringauto/structures/StatusAggregatorDeviceState.cpp b/source/bringauto/structures/StatusAggregatorDeviceState.cpp index 92f6067a..6da352f9 100644 --- a/source/bringauto/structures/StatusAggregatorDeviceState.cpp +++ b/source/bringauto/structures/StatusAggregatorDeviceState.cpp @@ -1,6 +1,6 @@ #include -#include +#include @@ -45,8 +45,8 @@ std::queue &StatusAggregatorDeviceState::aggregatedMessa return aggregatedMessages_; } -int StatusAggregatorDeviceState::addExternalCommand(const modules::Buffer &command) { - externalCommandQueue_.push(command); +int StatusAggregatorDeviceState::addExternalCommand(const modules::Buffer &commandBuffer) { + externalCommandQueue_.push(commandBuffer); if (externalCommandQueue_.size() > settings::max_external_commands) { externalCommandQueue_.pop(); return NOT_OK; diff --git a/test/include/InternalServerTests.hpp b/test/include/InternalServerTests.hpp index 2d888b56..bea8e15a 100644 --- a/test/include/InternalServerTests.hpp +++ b/test/include/InternalServerTests.hpp @@ -24,7 +24,7 @@ class InternalServerTests: public ::testing::Test { bringauto::settings::Logger::addSink(); bringauto::logging::LoggerSettings params { "InternalServerTests", - bringauto::logging::LoggerVerbosity::Debug + LOGGER_VERBOSITY }; bringauto::settings::Logger::init(params); } diff --git a/test/include/testing_utils/ConfigMock.hpp b/test/include/testing_utils/ConfigMock.hpp index c4e0309b..859f836a 100644 --- a/test/include/testing_utils/ConfigMock.hpp +++ b/test/include/testing_utils/ConfigMock.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include @@ -27,7 +27,7 @@ class ConfigMock { int port { 1636 }; } internal_server_settings; - std::map module_paths { {1, "/path/to/lib1.so"}, {2, "/path/to/lib2.so"}, {3, "/path/to/lib3.so"} }; + std::unordered_map module_paths { {1, "/path/to/lib1.so"}, {2, "/path/to/lib2.so"}, {3, "/path/to/lib3.so"} }; std::string modulePathsToString() const { std::string result = ""; for (auto [key, value] : module_paths) { @@ -48,7 +48,7 @@ class ConfigMock { std::string server_ip { "localhost" }; int port { 1883 }; - std::map mqtt_settings { + std::unordered_map mqtt_settings { { "ssl", "\"false\"" }, { "ca-file", "\"ca.pem\"" }, { "client-cert", "\"client.pem\"" }, diff --git a/test/source/EnumUtilsTests.cpp b/test/source/EnumUtilsTests.cpp index f56ee9fb..5194b0f3 100644 --- a/test/source/EnumUtilsTests.cpp +++ b/test/source/EnumUtilsTests.cpp @@ -14,9 +14,11 @@ TEST(EnumUtilsTests, EnumUtils){ namespace balog = bringauto::logging; EXPECT_EQ(bacu::EnumUtils::stringToProtocolType(std::string(baset::Constants::MQTT)), bas::ProtocolType::MQTT); + EXPECT_EQ(bacu::EnumUtils::stringToProtocolType(std::string(baset::Constants::DUMMY)), bas::ProtocolType::DUMMY); EXPECT_EQ(bacu::EnumUtils::stringToProtocolType("INVALID"), bas::ProtocolType::INVALID); - EXPECT_EQ(bacu::EnumUtils::protocolTypeToString(bas::ProtocolType::MQTT), "mqtt"); + EXPECT_EQ(bacu::EnumUtils::protocolTypeToString(bas::ProtocolType::MQTT), "MQTT"); + EXPECT_EQ(bacu::EnumUtils::protocolTypeToString(bas::ProtocolType::DUMMY), "DUMMY"); EXPECT_EQ(bacu::EnumUtils::protocolTypeToString(bas::ProtocolType::INVALID), ""); EXPECT_EQ(bacu::EnumUtils::stringToLoggerVerbosity(std::string(baset::Constants::LOG_LEVEL_DEBUG)), balog::LoggerVerbosity::Debug); diff --git a/test/source/StatusAggregatorTests.cpp b/test/source/StatusAggregatorTests.cpp index 9d92866a..81ea7c92 100644 --- a/test/source/StatusAggregatorTests.cpp +++ b/test/source/StatusAggregatorTests.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace modules = bringauto::modules; From ad5640953cc3fdcb840917d9d2aa64764cf054bf Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Fri, 18 Jul 2025 16:04:04 +0200 Subject: [PATCH 03/55] static analysis fixes --- .../connection/communication/DummyCommunication.hpp | 2 +- include/bringauto/settings/LoggerId.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/DummyCommunication.hpp b/include/bringauto/external_client/connection/communication/DummyCommunication.hpp index 07d3cd21..3c489077 100644 --- a/include/bringauto/external_client/connection/communication/DummyCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/DummyCommunication.hpp @@ -20,7 +20,7 @@ class DummyCommunication: public ICommunicationChannel { void closeConnection() override; -protected: +private: /// Flag to indicate if the fake connection is established bool isConnected_ { false }; }; diff --git a/include/bringauto/settings/LoggerId.hpp b/include/bringauto/settings/LoggerId.hpp index 56594f43..dfb37573 100644 --- a/include/bringauto/settings/LoggerId.hpp +++ b/include/bringauto/settings/LoggerId.hpp @@ -3,7 +3,7 @@ #include #ifndef MINIMUM_LOGGER_VERBOSITY -#define MINUMUM_LOGGER_VERBOSITY 0 // DEBUG +#define MINIMUM_LOGGER_VERBOSITY 0 // DEBUG #endif #if MINIMUM_LOGGER_VERBOSITY == 0 // DEBUG From bdf7b216f9a9c1205358d559a8250cf8f9824718 Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Mon, 21 Jul 2025 15:09:11 +0200 Subject: [PATCH 04/55] update versions --- CMakeLists.txt | 2 +- Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae0f62c3..ade42cf7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ FIND_PACKAGE(CMLIB REQUIRED ) -SET(BRINGAUTO_MODULE_GATEWAY_VERSION 1.3.4) +SET(BRINGAUTO_MODULE_GATEWAY_VERSION 1.3.5) OPTION(MINIMUM_LOGGER_VERBOSITY "Minimum logger verbosity level" 0) diff --git a/Dockerfile b/Dockerfile index c2c9b0d1..d6cb4db2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ RUN cmake .. -DCMAKE_BUILD_TYPE=Release -DBRINGAUTO_GET_PACKAGES_ONLY=ON FROM bringauto/cpp-build-environment:latest AS mission_module_builder -ARG MISSION_MODULE_VERSION=v1.2.12 +ARG MISSION_MODULE_VERSION=v1.2.13 # Install mission module dependencies WORKDIR /home/bringauto/modules/ From 74db3ead7a02024a0d01833189bcc8811ca8cf82 Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Tue, 22 Jul 2025 09:52:19 +0200 Subject: [PATCH 05/55] dont move const references; remade logger id macros to a constexpr function --- CMakeLists.txt | 4 +- README.md | 2 +- include/bringauto/settings/LoggerId.hpp | 43 +++++++++++-------- include/bringauto/settings/LoggerWrapper.hpp | 1 + include/bringauto/structures/Connection.hpp | 2 +- main.cpp | 5 ++- .../bringauto/common_utils/ProtobufUtils.cpp | 11 +++-- .../internal_server/InternalServer.cpp | 32 +++++++------- 8 files changed, 58 insertions(+), 42 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ade42cf7..5f473c43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,12 +8,12 @@ FIND_PACKAGE(CMLIB SET(BRINGAUTO_MODULE_GATEWAY_VERSION 1.3.5) -OPTION(MINIMUM_LOGGER_VERBOSITY "Minimum logger verbosity level" 0) +OPTION(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY "Minimum logger verbosity level" 0) CMDEF_COMPILE_DEFINITIONS( ALL "MODULE_GATEWAY_VERSION=\"${BRINGAUTO_MODULE_GATEWAY_VERSION}\"" - "MINIMUM_LOGGER_VERBOSITY=${MINIMUM_LOGGER_VERBOSITY}" + "MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY=${MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY}" ) SET(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMDEF_LIBRARY_INSTALL_DIR}") SET(CMAKE_CXX_STANDARD 20) diff --git a/README.md b/README.md index 7787b9ba..f3da2f89 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ make * BRINGAUTO_SYSTEM_DEP=ON/OFF - DEFAULT: OFF -* MINIMUM_LOGGER_VERBOSITY=0/1/2/3/4 +* MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY=0/1/2/3/4 - DEFAULT: 0 - sets the minimum logger verbosity on compile level to improve performance - 0: DEBUG diff --git a/include/bringauto/settings/LoggerId.hpp b/include/bringauto/settings/LoggerId.hpp index dfb37573..fdae9d56 100644 --- a/include/bringauto/settings/LoggerId.hpp +++ b/include/bringauto/settings/LoggerId.hpp @@ -2,30 +2,39 @@ #include -#ifndef MINIMUM_LOGGER_VERBOSITY -#define MINIMUM_LOGGER_VERBOSITY 0 // DEBUG -#endif - -#if MINIMUM_LOGGER_VERBOSITY == 0 // DEBUG -#define LOGGER_VERBOSITY bringauto::logging::LoggerVerbosity::Debug -#elif MINIMUM_LOGGER_VERBOSITY == 1 // INFO -#define LOGGER_VERBOSITY bringauto::logging::LoggerVerbosity::Info -#elif MINIMUM_LOGGER_VERBOSITY == 2 // WARNING -#define LOGGER_VERBOSITY bringauto::logging::LoggerVerbosity::Warning -#elif MINIMUM_LOGGER_VERBOSITY == 3 // ERROR -#define LOGGER_VERBOSITY bringauto::logging::LoggerVerbosity::Error -#elif MINIMUM_LOGGER_VERBOSITY == 4 // CRITICAL -#define LOGGER_VERBOSITY bringauto::logging::LoggerVerbosity::Critical -#else -#error "Invalid MINIMUM_LOGGER_VERBOSITY defined. Use DEBUG(0), INFO(1), WARNING(2), ERROR(3), or CRITICAL(4)." +#ifndef MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY +#define MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY 0 // DEBUG #endif namespace bringauto::settings { +static_assert(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY >= 0 && MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY <= 4, + "Invalid MINIMUM_LOGGER_VERBOSITY defined. Use DEBUG(0), INFO(1), WARNING(2), ERROR(3), or CRITICAL(4)."); + +/** + * @brief Converts the minimum logger verbosity defined in CMake to LoggerVerbosity enum. + * @param verbosityNumber The minimum logger verbosity defined in CMake. + */ +constexpr logging::LoggerVerbosity toLoggerVerbosity(uint8_t verbosityNumber) { + switch (verbosityNumber) { + case 1: + return logging::LoggerVerbosity::Info; + case 2: + return logging::LoggerVerbosity::Warning; + case 3: + return logging::LoggerVerbosity::Error; + case 4: + return logging::LoggerVerbosity::Critical; + case 0: + default: + return logging::LoggerVerbosity::Debug; + } +} + constexpr logging::LoggerId logId = {.id = "ModuleGateway"}; using BaseLogger = logging::Logger; -using Logger = LoggerWrapper; +using Logger = LoggerWrapper; } diff --git a/include/bringauto/settings/LoggerWrapper.hpp b/include/bringauto/settings/LoggerWrapper.hpp index 1d1e690c..38b39d38 100644 --- a/include/bringauto/settings/LoggerWrapper.hpp +++ b/include/bringauto/settings/LoggerWrapper.hpp @@ -5,6 +5,7 @@ namespace bringauto::settings { + /** * @brief Custom Logger wrapper, which optimises out logging calls bellow the set verbosity level at compile time. * diff --git a/include/bringauto/structures/Connection.hpp b/include/bringauto/structures/Connection.hpp index 07e7013e..a1fe1aec 100644 --- a/include/bringauto/structures/Connection.hpp +++ b/include/bringauto/structures/Connection.hpp @@ -25,7 +25,7 @@ struct Connection { * @return remote endpoint address as a string */ [[nodiscard]] - std::string getRemoteEndpointAddress() const { + std::string remoteEndpointAddress() const { if (!socket.is_open()) { return "(N/A, socket is not open)"; } diff --git a/main.cpp b/main.cpp index 7af68694..00a3883c 100644 --- a/main.cpp +++ b/main.cpp @@ -37,7 +37,10 @@ void initLogger(const bringauto::structures::LoggingSettings &settings) { bringauto::settings::Logger::addSink(paramFileSink); } - const bringauto::logging::LoggerSettings params { "ModuleGateway", LOGGER_VERBOSITY }; + const bringauto::logging::LoggerSettings params { + "ModuleGateway", + bringauto::settings::toLoggerVerbosity(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY) + }; bringauto::settings::Logger::init(params); } diff --git a/source/bringauto/common_utils/ProtobufUtils.cpp b/source/bringauto/common_utils/ProtobufUtils.cpp index 553ea11d..a68a1493 100644 --- a/source/bringauto/common_utils/ProtobufUtils.cpp +++ b/source/bringauto/common_utils/ProtobufUtils.cpp @@ -10,7 +10,8 @@ ProtobufUtils::createInternalServerConnectResponseMessage(const InternalProtocol InternalProtocol::InternalServer message {}; const auto response = message.mutable_deviceconnectresponse(); response->set_responsetype(resType); - *response->mutable_device() = std::move(device); + const auto device_ = response->mutable_device(); + device_->CopyFrom(device); return message; } @@ -22,7 +23,8 @@ ProtobufUtils::createInternalServerCommandMessage(const InternalProtocol::Device if (command.isAllocated()) { deviceCommand->set_commanddata(command.getStructBuffer().data, command.getStructBuffer().size_in_bytes); } - *deviceCommand->mutable_device() = std::move(device); + const auto device_ = deviceCommand->mutable_device(); + device_->CopyFrom(device); return message; } @@ -34,7 +36,8 @@ ProtobufUtils::createInternalClientStatusMessage(const InternalProtocol::Device if (status.isAllocated()) { deviceStatus->set_statusdata(status.getStructBuffer().data, status.getStructBuffer().size_in_bytes); } - *deviceStatus->mutable_device() = std::move(device); + const auto device_ = deviceStatus->mutable_device(); + device_->CopyFrom(device); return message; } @@ -75,7 +78,7 @@ ExternalProtocol::ExternalClient ProtobufUtils::createExternalClientStatus(const const modules::Buffer &errorMessage) { ExternalProtocol::ExternalClient externalMessage {}; ExternalProtocol::Status *status = externalMessage.mutable_status(); - *status->mutable_devicestatus() = std::move(deviceStatus); + status->mutable_devicestatus()->CopyFrom(deviceStatus); status->set_sessionid(sessionId); status->set_devicestate(deviceState); status->set_messagecounter(messageCounter); diff --git a/source/bringauto/internal_server/InternalServer.cpp b/source/bringauto/internal_server/InternalServer.cpp index 6f9a93ed..54282f32 100644 --- a/source/bringauto/internal_server/InternalServer.cpp +++ b/source/bringauto/internal_server/InternalServer.cpp @@ -41,7 +41,7 @@ void InternalServer::addAsyncAccept() { log::logInfo("Accepted connection with Internal Client, " "connection's ip address is {}", - connection->getRemoteEndpointAddress()); + connection->remoteEndpointAddress()); addAsyncReceive(connection); addAsyncAccept(); }); @@ -78,7 +78,7 @@ void InternalServer::asyncReceiveHandler( } else { log::logWarning( "Internal Client with ip address {} has been disconnected. Reason: {}", - connection->getRemoteEndpointAddress(), error.message()); + connection->remoteEndpointAddress(), error.message()); } std::lock_guard lock(serverMutex_); removeConnFromMap(connection); @@ -102,7 +102,7 @@ bool InternalServer::processBufferData( "Error in processBufferData(...): bufferOffset: {} is greater than bytesTransferred: {}, " "Invalid bufferOffset: {} received from Internal Client, " "connection's ip address is {}", bufferOffset, bytesTransferred, - connection->getRemoteEndpointAddress()); + connection->remoteEndpointAddress()); return false; } @@ -114,7 +114,7 @@ bool InternalServer::processBufferData( if(bytesTransferred < headerSize && completeMessageSize == 0) { log::logError( "Error in processBufferData(...): Incomplete header received from Internal Client, " - "connection's ip address is {}", connection->getRemoteEndpointAddress()); + "connection's ip address is {}", connection->remoteEndpointAddress()); return false; } @@ -133,7 +133,7 @@ bool InternalServer::processBufferData( if(bytesLeft < headerSize) { log::logError( "Error in processBufferData(...): Incomplete header received from Internal Client, " - "connection's ip address is {}", connection->getRemoteEndpointAddress()); + "connection's ip address is {}", connection->remoteEndpointAddress()); return false; } bytesLeft -= headerSize; @@ -154,7 +154,7 @@ bool InternalServer::processBufferData( if(bytesLeft < messageBytesLeft) { log::logError("Error in processBufferData(...): messageBytesLeft: {} is greater than bytesLeft: {}, " "connection's ip address is {}", messageBytesLeft, bytesLeft, - connection->getRemoteEndpointAddress()); + connection->remoteEndpointAddress()); return false; } bytesLeft -= messageBytesLeft; @@ -162,14 +162,14 @@ bool InternalServer::processBufferData( if(bytesTransferred < bytesLeft) { log::logError("Error in processBufferData(...): bytesLeft: {} is greater than bytesTransferred: {}, " "connection's ip address is {}", bytesLeft, bytesTransferred, - connection->getRemoteEndpointAddress()); + connection->remoteEndpointAddress()); return false; } if(bytesLeft && !processBufferData(connection, bytesLeft, bytesTransferred - bytesLeft)) { log::logError("Error in processBufferData(...): " "Received extra invalid bytes of data: {} from Internal Client, " "connection's ip address is {}", bytesLeft, - connection->getRemoteEndpointAddress()); + connection->remoteEndpointAddress()); return false; } return true; @@ -182,7 +182,7 @@ bool InternalServer::handleMessage(const std::shared_ptr connection->connContext.completeMessage.size())) { log::logError( "Error in handleMessage(...): message received from Internal Client cannot be parsed, " - "connection's ip address is {}", connection->getRemoteEndpointAddress()); + "connection's ip address is {}", connection->remoteEndpointAddress()); return false; } if(client.has_devicestatus()) { @@ -196,7 +196,7 @@ bool InternalServer::handleMessage(const std::shared_ptr } else { log::logError( "Error in handleMessage(...): message received from Internal Client cannot be parsed, " - "connection's ip address is {}", connection->getRemoteEndpointAddress()); + "connection's ip address is {}", connection->remoteEndpointAddress()); return false; } std::unique_lock lk(connection->connectionMutex); @@ -208,7 +208,7 @@ bool InternalServer::handleMessage(const std::shared_ptr log::logError("Error in handleMessage(...): " "Module Handler did not respond to a message in time, " "connection's ip address is {}", - connection->getRemoteEndpointAddress()); + connection->remoteEndpointAddress()); return false; } return !context_->ioContext.stopped(); @@ -220,7 +220,7 @@ bool InternalServer::handleStatus(const std::shared_ptr log::logError("Error in handleStatus(...): " "received status from Internal Client without being connected, " "connection's ip address is {}", - connection->getRemoteEndpointAddress()); + connection->remoteEndpointAddress()); return false; } std::lock_guard lk(connection->connectionMutex); @@ -236,7 +236,7 @@ bool InternalServer::handleConnection(const std::shared_ptrgetRemoteEndpointAddress()); + connection->remoteEndpointAddress()); return false; } @@ -339,19 +339,19 @@ bool InternalServer::sendResponse(const std::shared_ptr log::logError("Error in sendResponse(...): " "Cannot write message header to Internal Client, " "connection's ip address is {}", - connection->getRemoteEndpointAddress()); + connection->remoteEndpointAddress()); return false; } try { log::logDebug("Sending response to Internal Client, " "connection's ip address is {}", - connection->getRemoteEndpointAddress()); + connection->remoteEndpointAddress()); const auto dataWSize = connection->socket.write_some(boost::asio::buffer(data)); if(dataWSize != header) { log::logError("Error in sendResponse(...): " "Cannot write data to Internal Client, " "connection's ip address is {}", - connection->getRemoteEndpointAddress()); + connection->remoteEndpointAddress()); return false; } } catch(const boost::exception &) { From 81b3e800a594a3874fa222c38ad2f509be4ed8c1 Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Tue, 22 Jul 2025 11:34:21 +0200 Subject: [PATCH 06/55] fix cmake; add dummy communication documentation --- CMakeLists.txt | 4 +- README.md | 9 +--- .../communication/DummyCommunication.hpp | 6 +++ include/bringauto/settings/LoggerId.hpp | 45 ++++++++++++------- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f473c43..b4285f2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,12 +8,12 @@ FIND_PACKAGE(CMLIB SET(BRINGAUTO_MODULE_GATEWAY_VERSION 1.3.5) -OPTION(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY "Minimum logger verbosity level" 0) +SET(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY "DEBUG" CACHE STRING "Minimum logger verbosity level for module-gateway") CMDEF_COMPILE_DEFINITIONS( ALL "MODULE_GATEWAY_VERSION=\"${BRINGAUTO_MODULE_GATEWAY_VERSION}\"" - "MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY=${MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY}" + "MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY=\"${MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY}\"" ) SET(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMDEF_LIBRARY_INSTALL_DIR}") SET(CMAKE_CXX_STANDARD 20) diff --git a/README.md b/README.md index f3da2f89..c56b1e2f 100644 --- a/README.md +++ b/README.md @@ -77,14 +77,9 @@ make * BRINGAUTO_SYSTEM_DEP=ON/OFF - DEFAULT: OFF -* MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY=0/1/2/3/4 - - DEFAULT: 0 +* MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY=DEBUG/INFO/WARNING/ERROR/CRITICAL + - DEFAULT: DEBUG - sets the minimum logger verbosity on compile level to improve performance - - 0: DEBUG - - 1: INFO - - 2: WARNING - - 3: ERROR - - 4: CRITICAL * CURRENTLY UNUSED diff --git a/include/bringauto/external_client/connection/communication/DummyCommunication.hpp b/include/bringauto/external_client/connection/communication/DummyCommunication.hpp index 3c489077..83bf3923 100644 --- a/include/bringauto/external_client/connection/communication/DummyCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/DummyCommunication.hpp @@ -6,6 +6,12 @@ namespace bringauto::external_client::connection::communication { +/** + * @brief Dummy communication channel for testing purposes. + * Does not establish any real connection, just simulates it. + * receiveMessage always returns nullptr. + * Initialization and closing connection only changes the debug logs of sendMessage. + */ class DummyCommunication: public ICommunicationChannel { public: explicit DummyCommunication(const structures::ExternalConnectionSettings &settings); diff --git a/include/bringauto/settings/LoggerId.hpp b/include/bringauto/settings/LoggerId.hpp index fdae9d56..30384618 100644 --- a/include/bringauto/settings/LoggerId.hpp +++ b/include/bringauto/settings/LoggerId.hpp @@ -3,34 +3,45 @@ #include #ifndef MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY -#define MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY 0 // DEBUG +#define MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY "DEBUG" #endif namespace bringauto::settings { -static_assert(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY >= 0 && MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY <= 4, - "Invalid MINIMUM_LOGGER_VERBOSITY defined. Use DEBUG(0), INFO(1), WARNING(2), ERROR(3), or CRITICAL(4)."); +/** + * @brief Compile time string comparison + * @param str1 First string to compare + * @param str2 Second string to compare + * @return true if both strings are equal, false otherwise + */ +constexpr bool stringsEqual(char const *str1, char const *str2) { + return std::string_view(str1) == str2; +} + +static_assert(stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "DEBUG") || + stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "INFO") || + stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "WARNING") || + stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "ERROR") || + stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "CRITICAL"), + "Invalid MINIMUM_LOGGER_VERBOSITY defined. Use DEBUG, INFO, WARNING, ERROR, or CRITICAL."); /** * @brief Converts the minimum logger verbosity defined in CMake to LoggerVerbosity enum. - * @param verbosityNumber The minimum logger verbosity defined in CMake. + * @param verbosityString The minimum logger verbosity defined in CMake. */ -constexpr logging::LoggerVerbosity toLoggerVerbosity(uint8_t verbosityNumber) { - switch (verbosityNumber) { - case 1: - return logging::LoggerVerbosity::Info; - case 2: - return logging::LoggerVerbosity::Warning; - case 3: - return logging::LoggerVerbosity::Error; - case 4: - return logging::LoggerVerbosity::Critical; - case 0: - default: - return logging::LoggerVerbosity::Debug; +constexpr logging::LoggerVerbosity toLoggerVerbosity(std::string_view verbosityString) { + if (verbosityString == "INFO") { + return logging::LoggerVerbosity::Info; + } else if (verbosityString == "WARNING") { + return logging::LoggerVerbosity::Warning; + } else if (verbosityString == "ERROR") { + return logging::LoggerVerbosity::Error; + } else if (verbosityString == "CRITICAL") { + return logging::LoggerVerbosity::Critical; } + return logging::LoggerVerbosity::Debug; } constexpr logging::LoggerId logId = {.id = "ModuleGateway"}; From 31746e504f803d054d7fae447400c5af15a15751 Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Tue, 22 Jul 2025 12:01:37 +0200 Subject: [PATCH 07/55] fix test init --- include/bringauto/settings/LoggerId.hpp | 2 +- test/include/InternalServerTests.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bringauto/settings/LoggerId.hpp b/include/bringauto/settings/LoggerId.hpp index 30384618..3c268c8b 100644 --- a/include/bringauto/settings/LoggerId.hpp +++ b/include/bringauto/settings/LoggerId.hpp @@ -25,7 +25,7 @@ static_assert(stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "DEBUG") || stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "WARNING") || stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "ERROR") || stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "CRITICAL"), - "Invalid MINIMUM_LOGGER_VERBOSITY defined. Use DEBUG, INFO, WARNING, ERROR, or CRITICAL."); + "Invalid MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY defined. Use DEBUG, INFO, WARNING, ERROR, or CRITICAL."); /** * @brief Converts the minimum logger verbosity defined in CMake to LoggerVerbosity enum. diff --git a/test/include/InternalServerTests.hpp b/test/include/InternalServerTests.hpp index bea8e15a..6054d359 100644 --- a/test/include/InternalServerTests.hpp +++ b/test/include/InternalServerTests.hpp @@ -24,7 +24,7 @@ class InternalServerTests: public ::testing::Test { bringauto::settings::Logger::addSink(); bringauto::logging::LoggerSettings params { "InternalServerTests", - LOGGER_VERBOSITY + bringauto::settings::toLoggerVerbosity(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY), }; bringauto::settings::Logger::init(params); } From 7e9d3ad34c173935b54af40eb235538f78d8ccec Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Tue, 22 Jul 2025 16:25:22 +0200 Subject: [PATCH 08/55] fix some unit tests --- include/bringauto/settings/LoggerId.hpp | 19 +++++++++----- test/include/ErrorAggregatorTests.hpp | 2 +- test/include/ExternalConnectionTests.hpp | 2 +- test/include/StatusAggregatorTests.hpp | 2 +- test/source/InternalServerTests.cpp | 32 ++++++++++++++++------- test/source/testing_utils/TestHandler.cpp | 4 +-- 6 files changed, 40 insertions(+), 21 deletions(-) diff --git a/include/bringauto/settings/LoggerId.hpp b/include/bringauto/settings/LoggerId.hpp index 3c268c8b..0823ae50 100644 --- a/include/bringauto/settings/LoggerId.hpp +++ b/include/bringauto/settings/LoggerId.hpp @@ -8,7 +8,7 @@ -namespace bringauto::settings { +namespace { /** * @brief Compile time string comparison @@ -27,25 +27,30 @@ static_assert(stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "DEBUG") || stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "CRITICAL"), "Invalid MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY defined. Use DEBUG, INFO, WARNING, ERROR, or CRITICAL."); +} + +namespace bringauto::settings { + /** * @brief Converts the minimum logger verbosity defined in CMake to LoggerVerbosity enum. * @param verbosityString The minimum logger verbosity defined in CMake. */ -constexpr logging::LoggerVerbosity toLoggerVerbosity(std::string_view verbosityString) { +constexpr bringauto::logging::LoggerVerbosity toLoggerVerbosity(std::string_view verbosityString) { if (verbosityString == "INFO") { - return logging::LoggerVerbosity::Info; + return bringauto::logging::LoggerVerbosity::Info; } else if (verbosityString == "WARNING") { - return logging::LoggerVerbosity::Warning; + return bringauto::logging::LoggerVerbosity::Warning; } else if (verbosityString == "ERROR") { - return logging::LoggerVerbosity::Error; + return bringauto::logging::LoggerVerbosity::Error; } else if (verbosityString == "CRITICAL") { - return logging::LoggerVerbosity::Critical; + return bringauto::logging::LoggerVerbosity::Critical; } - return logging::LoggerVerbosity::Debug; + return bringauto::logging::LoggerVerbosity::Debug; } constexpr logging::LoggerId logId = {.id = "ModuleGateway"}; using BaseLogger = logging::Logger; using Logger = LoggerWrapper; + } diff --git a/test/include/ErrorAggregatorTests.hpp b/test/include/ErrorAggregatorTests.hpp index 37bc1402..2c176f08 100644 --- a/test/include/ErrorAggregatorTests.hpp +++ b/test/include/ErrorAggregatorTests.hpp @@ -23,7 +23,7 @@ class ErrorAggregatorTests: public ::testing::Test { bringauto::settings::Logger::addSink(); bringauto::logging::LoggerSettings settings { "ErrorAggregatorTests", - bringauto::logging::LoggerVerbosity::Critical + bringauto::settings::toLoggerVerbosity(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY) }; bringauto::settings::Logger::init(settings); } diff --git a/test/include/ExternalConnectionTests.hpp b/test/include/ExternalConnectionTests.hpp index 15dd09e8..0fb241e9 100644 --- a/test/include/ExternalConnectionTests.hpp +++ b/test/include/ExternalConnectionTests.hpp @@ -80,7 +80,7 @@ class ExternalConnectionTests: public ::testing::Test { bringauto::settings::Logger::addSink(); bringauto::logging::LoggerSettings settings { "ExternalConnectionTests", - bringauto::logging::LoggerVerbosity::Error + bringauto::settings::toLoggerVerbosity(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY) }; bringauto::settings::Logger::init(settings); }; diff --git a/test/include/StatusAggregatorTests.hpp b/test/include/StatusAggregatorTests.hpp index 88cb1bb3..a5a6922c 100644 --- a/test/include/StatusAggregatorTests.hpp +++ b/test/include/StatusAggregatorTests.hpp @@ -23,7 +23,7 @@ class StatusAggregatorTests: public ::testing::Test { bringauto::settings::Logger::addSink(); bringauto::logging::LoggerSettings settings { "StatusAggregatorTests", - bringauto::logging::LoggerVerbosity::Critical + bringauto::settings::toLoggerVerbosity(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY) }; bringauto::settings::Logger::init(settings); } diff --git a/test/source/InternalServerTests.cpp b/test/source/InternalServerTests.cpp index 44b745d1..a12bf943 100644 --- a/test/source/InternalServerTests.cpp +++ b/test/source/InternalServerTests.cpp @@ -31,6 +31,7 @@ TEST_F(InternalServerTests, OneClient) { testing_utils::TestHandler testedData(devices, data); testedData.runTestsParallelConnections(); } + /** * @brief Tests Connection of 2 clients, and communication between clients-server-handler */ @@ -50,6 +51,7 @@ TEST_F(InternalServerTests, TwoClients) { testing_utils::TestHandler testedData(devices, data); testedData.runTestsParallelConnections(); } + /** * @brief Tests Connection of 5 clients, and communication between clients-server-handler */ @@ -69,6 +71,7 @@ TEST_F(InternalServerTests, FiveClients) { testing_utils::TestHandler testedData(devices, data); testedData.runTestsParallelConnections(); } + /** * @brief Tests Connection of 50 clients, and communication between clients-server-handler */ @@ -88,6 +91,7 @@ TEST_F(InternalServerTests, FiftyClients) { testing_utils::TestHandler testedData(devices, data); testedData.runTestsParallelConnections(); } + ///TESTS FOR RESPONSES TO DIFFERENT PRIORITES /** * @brief tests if server responds to each client with correct response and running communication is not broken @@ -115,6 +119,7 @@ TEST_F(InternalServerTests, SameRolePriority000) { testing_utils::TestHandler testedData(devices, responseType, data); testedData.runTestsSerialConnections(); } + /** * @brief tests if server responds to each client with correct response and running communication is not broken */ @@ -142,6 +147,7 @@ TEST_F(InternalServerTests, SameRolePriority001) { testing_utils::TestHandler testedData(devices, responseType, data); testedData.runTestsSerialConnections(); } + /** * @brief tests if server responds to each client with correct response, lower priority connection is disconnected correctly and no communication is broken */ @@ -168,6 +174,7 @@ TEST_F(InternalServerTests, SameRolePriority110) { testing_utils::TestHandler testedData(devices, responseType, data); testedData.runTestsSerialConnections(); } + /** * @brief tests if server responds to each client with correct response and running communication is not broken */ @@ -194,6 +201,7 @@ TEST_F(InternalServerTests, SameRolePriority121) { testing_utils::TestHandler testedData(devices, responseType, data); testedData.runTestsSerialConnections(); } + /** * @brief tests if server responds to each client with correct response, lower priority connection is disconnected correctly and no communication is broken */ @@ -220,6 +228,7 @@ TEST_F(InternalServerTests, SameRolePriority101) { testing_utils::TestHandler testedData(devices, responseType, data); testedData.runTestsSerialConnections(); } + /** * @brief tests if server responds to each client with correct response and running communication is not broken */ @@ -246,6 +255,7 @@ TEST_F(InternalServerTests, SameRolePriority122) { testing_utils::TestHandler testedData(devices, responseType, data); testedData.runTestsSerialConnections(); } + /** * @brief tests if server responds to each client with correct response, lower priority connection is disconnected correctly and no communication is broken */ @@ -272,6 +282,7 @@ TEST_F(InternalServerTests, SameRolePriority120) { testing_utils::TestHandler testedData(devices, responseType, data); testedData.runTestsSerialConnections(); } + /** * @brief tests if server responds to each client with correct response, lower priority connection is disconnected correctly and no communication is broken */ @@ -298,6 +309,7 @@ TEST_F(InternalServerTests, SameRolePriority210) { testing_utils::TestHandler testedData(devices, responseType, data); testedData.runTestsSerialConnections(); } + /** * @brief tests if server responds to each client with correct response, lower priority connection is disconnected correctly and no communication is broken */ @@ -324,6 +336,7 @@ TEST_F(InternalServerTests, SameRolePriority211) { testing_utils::TestHandler testedData(devices, responseType, data); testedData.runTestsSerialConnections(); } + /** * @brief tests if connection is rejected when we send less than 4 bytes of a message and other communication is not broken */ @@ -350,6 +363,7 @@ TEST_F(InternalServerTests, RejectMessageSmallerThan4Bytes) { auto connectStr = testedData.getConnect(0).SerializeAsString(); testedData.runTestsWithWrongMessage(1, connectStr.size(), "", false, true); } + /** * @brief tests if connection is rejected when we send 4 bytes of the message (as the header) defining size of the rest of message as 0 and other communication is not broken */ @@ -375,10 +389,9 @@ TEST_F(InternalServerTests, RejectMesseageComposedOfOnlyHeaderWithNumber0) { testing_utils::TestHandler testedData(devices, responseType, data); testedData.runTestsWithWrongMessage(1, 0, "", true); } + /** - * this is timeout from client side - * test is not working and the feature is not implemented - * should test for if connection is rejected when we send only 4 bytes of the message (as the header) defining message size larger than 0 and other communication is not broken + * this test will only make sense when processBufferData returns false if bytesTransferred is 0 and header value is not 0 (not intended as of v1.3.5) */ TEST_F(InternalServerTests, RejectMessageComposedOfOnlyHeader) { GTEST_SKIP(); @@ -404,6 +417,7 @@ TEST_F(InternalServerTests, RejectMessageComposedOfOnlyHeader) { auto connectStr = testedData.getConnect(0).SerializeAsString(); testedData.runTestsWithWrongMessage(0, connectStr.size(), "", true); } + /** * @brief tests if connection is rejected when we send message that is not matching InternalProtocol::InternalClient message and other communication is not broken */ @@ -430,14 +444,13 @@ TEST_F(InternalServerTests, RejectMessageWithGarbageDataMatchingHeaderSize) { auto connectStr = testedData.getConnect(0).SerializeAsString(); testedData.runTestsWithWrongMessage(1, connectStr.size() + 5, connectStr + "12345", true); } + /** * this is timeout from client side * test is not working and the feature is not implemented * should tests for if connection is rejected when we send less data than header defines as size of the message and other communication is not broken - */ TEST_F(InternalServerTests, RejectMessageWithLessDataThanHeaderSays) { - GTEST_SKIP(); std::vector responseType { InternalProtocol::DeviceConnectResponse_ResponseType_OK, InternalProtocol::DeviceConnectResponse_ResponseType_OK, @@ -488,6 +501,7 @@ TEST_F(InternalServerTests, RejectMessageWhereStatusIsSentBeforeConnection) { auto connectStr = testedData.getConnect(1).SerializeAsString(); testedData.runTestsWithWrongMessage(1, connectStr.size(), connectStr, true); } + /** * @brief tests for disconnection when client receives connect message on connection that is already connected */ @@ -515,11 +529,11 @@ TEST_F(InternalServerTests, RejectMessageWhereConnectionIsSentAfterAlreadyBeingC auto connectStr = testedData.getConnect(0).SerializeAsString(); testedData.runTestsWithWrongMessage(0, connectStr.size(), connectStr, false); } + /** - * @brief tests if server correctly timeouts then disconnects if it does not receive response to connect from module hanlder in itme + * @brief tests if server correctly disconnects if it does not receive response to connect from module hanlder in itme */ TEST_F(InternalServerTests, TestForBehaviorWhereModuleHandlerDoesntRespondToConnect) { - GTEST_SKIP(); std::vector responseType { InternalProtocol::DeviceConnectResponse_ResponseType_OK, InternalProtocol::DeviceConnectResponse_ResponseType_OK, @@ -541,11 +555,11 @@ TEST_F(InternalServerTests, TestForBehaviorWhereModuleHandlerDoesntRespondToConn testing_utils::TestHandler testedData(devices, responseType, data); testedData.runTestsWithModuleHandlerTimeout(true, 2); } + /** - * @brief tests if server correctly timeouts then disconnects if it does not receive command to status from module hanlder in time + * @brief tests if server correctly disconnects if it does not receive command to status from module hanlder in time */ TEST_F(InternalServerTests, TestForBehaviorWhereModuleHandlerDoesntRespondToStatus) { - GTEST_SKIP(); std::vector responseType { InternalProtocol::DeviceConnectResponse_ResponseType_OK, InternalProtocol::DeviceConnectResponse_ResponseType_OK, diff --git a/test/source/testing_utils/TestHandler.cpp b/test/source/testing_utils/TestHandler.cpp index 2ea29b7a..1f7188a9 100644 --- a/test/source/testing_utils/TestHandler.cpp +++ b/test/source/testing_utils/TestHandler.cpp @@ -302,7 +302,7 @@ void TestHandler::runConnects(size_t numberOfErrors) { if(i < numberOfErrors) { clients[i].connectSocket(); clients[i].sendMessage(connects[i]); - clients[i].insteadOfMessageExpectTimeoutThenError(); + clients[i].insteadOfMessageExpectError(); clients[i].disconnectSocket(); } else { clients[i].connectSocket(); @@ -332,7 +332,7 @@ void TestHandler::runStatuses(size_t numberOfErrors) { for(size_t i = 0; i < clients.size()*(numberOfMessages - 1); ++i) { if(i < numberOfErrors) { clients[i].sendMessage(statuses[i]); - clients[i].insteadOfMessageExpectTimeoutThenError(); + clients[i].insteadOfMessageExpectError(); clients[i].disconnectSocket(); } else if(clients[i%clients.size()].isOpen()) { clients[i%clients.size()].sendMessage(statuses[i%clients.size()]); From 43ed3c714f99d7972cdec0c9c4c563dfa4a8b203 Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Wed, 23 Jul 2025 08:56:28 +0200 Subject: [PATCH 09/55] skip not needed unit test --- test/source/InternalServerTests.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/source/InternalServerTests.cpp b/test/source/InternalServerTests.cpp index a12bf943..0de845e3 100644 --- a/test/source/InternalServerTests.cpp +++ b/test/source/InternalServerTests.cpp @@ -446,11 +446,10 @@ TEST_F(InternalServerTests, RejectMessageWithGarbageDataMatchingHeaderSize) { } /** - * this is timeout from client side - * test is not working and the feature is not implemented - * should tests for if connection is rejected when we send less data than header defines as size of the message and other communication is not broken + * this test will only make sense if processBufferData returns false if bytesTransferred is less than header size and header value is not 0 (not intended as of v1.3.5) */ TEST_F(InternalServerTests, RejectMessageWithLessDataThanHeaderSays) { + GTEST_SKIP(); std::vector responseType { InternalProtocol::DeviceConnectResponse_ResponseType_OK, InternalProtocol::DeviceConnectResponse_ResponseType_OK, From 115f620e1ad4561f12ca67103e9d551d151ea78c Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Wed, 23 Jul 2025 09:25:00 +0200 Subject: [PATCH 10/55] made stringsEqual inaccessible in other files --- include/bringauto/settings/LoggerId.hpp | 21 --------------------- source/bringauto/settings/LoggerId.cpp | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 21 deletions(-) create mode 100644 source/bringauto/settings/LoggerId.cpp diff --git a/include/bringauto/settings/LoggerId.hpp b/include/bringauto/settings/LoggerId.hpp index 0823ae50..5ba0c1bd 100644 --- a/include/bringauto/settings/LoggerId.hpp +++ b/include/bringauto/settings/LoggerId.hpp @@ -8,27 +8,6 @@ -namespace { - -/** - * @brief Compile time string comparison - * @param str1 First string to compare - * @param str2 Second string to compare - * @return true if both strings are equal, false otherwise - */ -constexpr bool stringsEqual(char const *str1, char const *str2) { - return std::string_view(str1) == str2; -} - -static_assert(stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "DEBUG") || - stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "INFO") || - stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "WARNING") || - stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "ERROR") || - stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "CRITICAL"), - "Invalid MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY defined. Use DEBUG, INFO, WARNING, ERROR, or CRITICAL."); - -} - namespace bringauto::settings { /** diff --git a/source/bringauto/settings/LoggerId.cpp b/source/bringauto/settings/LoggerId.cpp new file mode 100644 index 00000000..41480047 --- /dev/null +++ b/source/bringauto/settings/LoggerId.cpp @@ -0,0 +1,22 @@ +#include + +namespace { + +/** + * @brief Compile time string comparison + * @param str1 First string to compare + * @param str2 Second string to compare + * @return true if both strings are equal, false otherwise + */ +constexpr bool stringsEqual(char const *str1, char const *str2) { + return std::string_view(str1) == str2; +} + +static_assert(stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "DEBUG") || + stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "INFO") || + stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "WARNING") || + stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "ERROR") || + stringsEqual(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY, "CRITICAL"), + "Invalid MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY defined. Use DEBUG, INFO, WARNING, ERROR, or CRITICAL."); + +} \ No newline at end of file From e6c80a51c1b450422a77e8ba98337f1a9f052154 Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Wed, 30 Jul 2025 16:23:40 +0200 Subject: [PATCH 11/55] added aeron classes for initial testing --- CMakeLists.txt | 4 + .../aeron_communication/AeronClient.hpp | 51 ++++++++ .../aeron_communication/AeronDriver.hpp | 51 ++++++++ .../modules/ModuleManagerLibraryHandler.hpp | 4 + include/bringauto/settings/Constants.hpp | 11 ++ .../bringauto/structures/ModuleLibrary.hpp | 4 + main.cpp | 9 ++ .../aeron_communication/AeronClient.cpp | 115 ++++++++++++++++++ .../aeron_communication/AeronDriver.cpp | 56 +++++++++ .../modules/ModuleManagerLibraryHandler.cpp | 29 +++++ source/bringauto/structures/ModuleLibrary.cpp | 6 +- 11 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 include/bringauto/aeron_communication/AeronClient.hpp create mode 100644 include/bringauto/aeron_communication/AeronDriver.hpp create mode 100644 source/bringauto/aeron_communication/AeronClient.cpp create mode 100644 source/bringauto/aeron_communication/AeronDriver.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b4285f2e..164ed20c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ FIND_PACKAGE(eclipse-paho-mqtt-c REQUIRED) FIND_PACKAGE(libbringauto_logger 2.0.0 REQUIRED) FIND_PACKAGE(fleet-protocol-interface 2.0.0 REQUIRED) FIND_PACKAGE(ZLIB 1.2.11 REQUIRED) +FIND_PACKAGE(aeron REQUIRED) # Requires local installation FILE(GLOB_RECURSE source_files "source/*") ADD_LIBRARY(module-gateway-lib STATIC "${source_files}") @@ -71,6 +72,9 @@ TARGET_LINK_LIBRARIES(module-gateway-lib PUBLIC eclipse-paho-mqtt-c::paho-mqtt3as PahoMqttCpp::paho-mqttpp3 ZLIB::ZLIB + aeron::aeron + aeron::aeron_client + aeron::aeron_driver ${CMAKE_DL_LIBS} ) diff --git a/include/bringauto/aeron_communication/AeronClient.hpp b/include/bringauto/aeron_communication/AeronClient.hpp new file mode 100644 index 00000000..1906edba --- /dev/null +++ b/include/bringauto/aeron_communication/AeronClient.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include + + + +namespace bringauto::aeron_communication { + +class AeronClient { + +public: + enum ModuleFunctions { + ALLOCATE = 0, + AGGREGATE_ERROR = 1, + AGGREGATE_STATUS = 2, + COMMAND_DATA_VALID = 3, + DEALLOCATE = 4, + GENERATE_COMMAND = 5, + GENERATE_FIRST_COMMAND = 6, + SEND_STATUS_CONDITION = 7, + STATUS_DATA_VALID = 8 + }; + + AeronClient(); + + ~AeronClient() = default; + + void addModule(uint16_t moduleId); + + void callModuleFunction(ModuleFunctions function ,const std::string &message) const; + +private: + bool waitForAeronResponse(); + aeron::fragment_handler_t handleMessage(); + std::string moduleFunctionToCode(ModuleFunctions function) const; + + aeron::Context aeronContext_ {}; + std::shared_ptr aeron_ {nullptr}; + std::shared_ptr aeronPublication_ {nullptr}; + std::shared_ptr aeronSubscription_ {nullptr}; + std::shared_ptr aeronFragmentAssembler_ {nullptr}; + std::shared_ptr aeronHandler_ {nullptr}; + std::shared_ptr aeronIdleStrategy_ {nullptr}; + std::atomic aeronPolling_ {false}; + std::string aeronMessage_ {}; + +}; + +} diff --git a/include/bringauto/aeron_communication/AeronDriver.hpp b/include/bringauto/aeron_communication/AeronDriver.hpp new file mode 100644 index 00000000..210548a1 --- /dev/null +++ b/include/bringauto/aeron_communication/AeronDriver.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include + + + +namespace bringauto::aeron_communication { + +/** + * This class is meant for aeron testing. It provides a simple interface to start the Aeron media driver. + */ +class AeronDriver { + +public: + /** + * @brief Constructs Aeron Driver. + * Initializes the driver with default settings. + */ + AeronDriver(); + + ~AeronDriver() = default; + + /** + * @brief Starts the Aeron Driver. + * This method will block until the driver is stopped or an error occurs. + */ + void run(); + + /** + * @brief Checks if the Aeron Driver is running. + * @return true if the driver is running, false otherwise. + */ + bool isRunning() const; + + /** + * @brief Stops the Aeron Driver. + * This method will stop the driver and release all resources. + */ + void stop(); + +private: + /// Context for the Aeron driver. Driver settings can be adjusted in this struct. + aeron_driver_context_t *driverContext_; + /// Pointer to the Aeron driver instance. + aeron_driver_t *driver_ {nullptr}; + /// Variable used in aeron macros that returns error codes. + volatile int exitStatus_ {AERON_NULL_VALUE}; + +}; + +} diff --git a/include/bringauto/modules/ModuleManagerLibraryHandler.hpp b/include/bringauto/modules/ModuleManagerLibraryHandler.hpp index fbae8958..795570ce 100644 --- a/include/bringauto/modules/ModuleManagerLibraryHandler.hpp +++ b/include/bringauto/modules/ModuleManagerLibraryHandler.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -17,6 +18,8 @@ namespace bringauto::modules { class ModuleManagerLibraryHandler { public: ModuleManagerLibraryHandler() = default; + ModuleManagerLibraryHandler(std::shared_ptr &aeronClient): + aeronClient_(aeronClient) {}; ~ModuleManagerLibraryHandler(); @@ -109,6 +112,7 @@ class ModuleManagerLibraryHandler { std::function generateCommand_ {}; std::function allocate_ {}; std::function deallocate_ {}; + std::shared_ptr aeronClient_ {nullptr}; }; } diff --git a/include/bringauto/settings/Constants.hpp b/include/bringauto/settings/Constants.hpp index 306eb9c5..9d22d163 100644 --- a/include/bringauto/settings/Constants.hpp +++ b/include/bringauto/settings/Constants.hpp @@ -86,6 +86,16 @@ constexpr unsigned int max_external_commands { 3 }; */ constexpr unsigned int max_external_queue_size { 500 }; +/** + * @brief base stream id for Aeron communication from Module Gateway to module binary + */ +constexpr unsigned int aeron_to_module_stream_id_base { 10000 }; + +/** + * @brief base stream id for Aeron communication from module binary to Module Gateway + */ +constexpr unsigned int aeron_to_gateway_stream_id_base { 20000 }; + /** * @brief Constants for Mqtt communication */ @@ -161,6 +171,7 @@ class Constants { inline static constexpr std::string_view CLIENT_KEY { "client-key" }; inline static constexpr std::string_view MODULES { "modules" }; + inline static constexpr std::string_view AERON_CONNECTION { "aeron:ipc"}; }; } diff --git a/include/bringauto/structures/ModuleLibrary.hpp b/include/bringauto/structures/ModuleLibrary.hpp index 5f37de37..88655b50 100644 --- a/include/bringauto/structures/ModuleLibrary.hpp +++ b/include/bringauto/structures/ModuleLibrary.hpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -13,6 +14,7 @@ namespace bringauto::structures { * @brief Library with library handlers and status aggregators */ struct ModuleLibrary { + ModuleLibrary(); ~ModuleLibrary(); @@ -33,6 +35,8 @@ struct ModuleLibrary { std::unordered_map> moduleLibraryHandlers {}; /// Map of status aggregators, key is module id std::unordered_map> statusAggregators {}; + /// Aeron client used for communication with modules + std::shared_ptr aeronClient {nullptr}; }; } diff --git a/main.cpp b/main.cpp index 00a3883c..4c8a32aa 100644 --- a/main.cpp +++ b/main.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -67,7 +68,13 @@ int main(int argc, char **argv) { std::cerr << "[ERROR] Error occurred during reading configuration: " << e.what() << std::endl; return 1; } + + bringauto::aeron_communication::AeronDriver aeronDriver {}; + std::jthread aeronDriverThread([&aeronDriver]() { aeronDriver.run(); }); + baset::Logger::logInfo("Aeron Driver starting..."); + std::this_thread::sleep_for(std::chrono::seconds(2)); //TODO Not sure how much time is needed. bas::ModuleLibrary moduleLibrary {}; + try { moduleLibrary.loadLibraries(context->settings->modulePaths); moduleLibrary.initStatusAggregators(context); @@ -99,10 +106,12 @@ int main(int argc, char **argv) { context->ioContext.stop(); } + aeronDriver.stop(); contextThread2.join(); contextThread1.join(); externalClientThread.join(); moduleHandlerThread.join(); + aeronDriverThread.join(); internalServer.destroy(); moduleHandler.destroy(); diff --git a/source/bringauto/aeron_communication/AeronClient.cpp b/source/bringauto/aeron_communication/AeronClient.cpp new file mode 100644 index 00000000..402f9d2b --- /dev/null +++ b/source/bringauto/aeron_communication/AeronClient.cpp @@ -0,0 +1,115 @@ +#include +#include +#include + + + +namespace bringauto::aeron_communication { + +using log = settings::Logger; + +AeronClient::AeronClient() { + aeronContext_.newPublicationHandler( + [](const std::string &channel, std::int32_t streamId, std::int32_t sessionId, std::int64_t correlationId) { + log::logDebug("AeronClient: New publication on channel: " + channel + + " with correlationId: " + std::to_string(correlationId) + + ", streamId: " + std::to_string(streamId) + + ", sessionId: " + std::to_string(sessionId) + ); + } + ); + + aeronContext_.newSubscriptionHandler( + [](const std::string &channel, std::int32_t streamId, std::int64_t correlationId) { + log::logDebug("AeronClient: New subscription on channel: " + channel + + " with correlationId: " + std::to_string(correlationId) + + ", streamId: " + std::to_string(streamId) + ); + } + ); + + aeronContext_.availableImageHandler( + [](aeron::Image &image) { + log::logDebug("AeronClient: Available image on correlationId: " + std::to_string(image.correlationId()) + + ", sessionId: " + std::to_string(image.sessionId()) + + ", position: " + std::to_string(image.position()) + + ", sourceIdentity: " + image.sourceIdentity() + ); + } + ); + + aeronContext_.unavailableImageHandler( + [](aeron::Image &image) { + log::logDebug("AeronClient: Unavailable image on correlationId: " + std::to_string(image.correlationId()) + + ", sessionId: " + std::to_string(image.sessionId()) + + ", position: " + std::to_string(image.position()) + + ", sourceIdentity: " + image.sourceIdentity() + ); + } + ); + + aeron_ = aeron::Aeron::connect(aeronContext_); + aeronFragmentAssembler_ = std::make_shared(handleMessage()); + aeronHandler_ = std::make_shared(aeronFragmentAssembler_->handler()); + aeronIdleStrategy_ = std::make_shared(); +} + + +void AeronClient::addModule(uint16_t moduleId) { + std::int64_t id = aeron_->addPublication( + std::string(settings::Constants::AERON_CONNECTION), + settings::aeron_to_module_stream_id_base + moduleId + ); + aeronPublication_ = aeron_->findPublication(id); + while (!aeronPublication_) { + std::this_thread::yield(); + aeronPublication_ = aeron_->findPublication(id); + } + + id = aeron_->addSubscription( + std::string(settings::Constants::AERON_CONNECTION), + settings::aeron_to_gateway_stream_id_base + moduleId + ); + aeronSubscription_ = aeron_->findSubscription(id); + while (!aeronSubscription_) { + std::this_thread::yield(); + aeronSubscription_ = aeron_->findSubscription(id); + } +} + + +void AeronClient::callModuleFunction(ModuleFunctions function, const std::string &message) const { + std::string fullMessage = std::string(moduleFunctionToCode(function)) + ":" + message; + + std::array buff; + aeron::concurrent::AtomicBuffer srcBuffer(&buff[0], buff.size()); + char charMessage[256]; + const int messageLen = ::snprintf(charMessage, sizeof(charMessage), "%s", fullMessage.c_str()); + srcBuffer.putBytes(0, reinterpret_cast(charMessage), messageLen); + aeronPublication_->offer(srcBuffer, 0, messageLen); +} + + +bool AeronClient::waitForAeronResponse() { + aeronPolling_ = true; + while (aeronPolling_) { + const int fragmentsRead = aeronSubscription_->poll(*aeronHandler_, 10); + aeronIdleStrategy_->idle(fragmentsRead); + } + return true; +} + + +aeron::fragment_handler_t AeronClient::handleMessage() { + return [&](const aeron::AtomicBuffer &buffer, aeron::util::index_t offset, aeron::util::index_t length, const aeron::Header &header) { + std::string message(reinterpret_cast(buffer.buffer()) + offset, static_cast(length)); + aeronMessage_ = message; + aeronPolling_ = false; + }; +} + +std::string AeronClient::moduleFunctionToCode(ModuleFunctions function) const { + return std::to_string(static_cast(function)); +} + +} diff --git a/source/bringauto/aeron_communication/AeronDriver.cpp b/source/bringauto/aeron_communication/AeronDriver.cpp new file mode 100644 index 00000000..f6a4b130 --- /dev/null +++ b/source/bringauto/aeron_communication/AeronDriver.cpp @@ -0,0 +1,56 @@ +#include + +#include + + + +static bringauto::aeron_communication::AeronDriver* globalDriverInstance = nullptr; + +void terminationHook(void *state) { + if (globalDriverInstance == nullptr) { + globalDriverInstance->stop(); + } +} + +void signalHandler(int signal) { + if (globalDriverInstance != nullptr) { + globalDriverInstance->stop(); + } +} + + +namespace bringauto::aeron_communication { + +AeronDriver::AeronDriver() { + globalDriverInstance = this; + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); + aeron_driver_context_init(&driverContext_); + aeron_driver_context_set_driver_termination_hook(driverContext_, terminationHook, NULL); + driverContext_->agent_on_start_func_delegate = driverContext_->agent_on_start_func; + driverContext_->agent_on_start_state_delegate = driverContext_->agent_on_start_state; + aeron_driver_context_set_agent_on_start_function(driverContext_, aeron_set_thread_affinity_on_start, driverContext_); + aeron_driver_init(&driver_, driverContext_); +} + + +void AeronDriver::run() { + aeron_driver_start(driver_, true); + while (isRunning()) { + aeron_driver_main_idle_strategy(driver_, aeron_driver_main_do_work(driver_)); + } +} + + +bool AeronDriver::isRunning() const { + int result; + AERON_GET_ACQUIRE(result, exitStatus_); + return result == AERON_NULL_VALUE; +} + + +void AeronDriver::stop() { + AERON_SET_RELEASE(exitStatus_, EXIT_SUCCESS); +} + +} diff --git a/source/bringauto/modules/ModuleManagerLibraryHandler.cpp b/source/bringauto/modules/ModuleManagerLibraryHandler.cpp index b075a5e7..ebc1d3a3 100644 --- a/source/bringauto/modules/ModuleManagerLibraryHandler.cpp +++ b/source/bringauto/modules/ModuleManagerLibraryHandler.cpp @@ -54,6 +54,11 @@ void ModuleManagerLibraryHandler::loadLibrary(const std::filesystem::path &path) "allocate")); deallocate_ = reinterpret_cast::fncptr>(checkFunction( "deallocate")); + + if (aeronClient_ != nullptr) { + aeronClient_->addModule(getModuleNumber_()); + } + log::logDebug("Library " + path.string() + " was successfully loaded"); } @@ -115,6 +120,9 @@ int ModuleManagerLibraryHandler::generateCommand(Buffer &generated_command, } else { generated_command = constructBuffer(); } + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::GENERATE_COMMAND, "TODO"); + } return ret; } @@ -138,6 +146,9 @@ int ModuleManagerLibraryHandler::aggregateStatus(Buffer &aggregated_status, } else { aggregated_status = current_status; } + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::AGGREGATE_STATUS, "TODO"); + } return ret; } @@ -162,6 +173,9 @@ int ModuleManagerLibraryHandler::aggregateError(Buffer &error_message, } else { error_message = constructBuffer(); } + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::AGGREGATE_ERROR, "TODO"); + } return ret; } @@ -173,6 +187,9 @@ int ModuleManagerLibraryHandler::generateFirstCommand(Buffer &default_command, u } else { default_command = constructBuffer(); } + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::GENERATE_FIRST_COMMAND, "TODO"); + } return ret; } @@ -181,6 +198,9 @@ int ModuleManagerLibraryHandler::statusDataValid(const Buffer &status, unsigned if (status.isAllocated()) { raw_buffer = status.getStructBuffer(); } + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::STATUS_DATA_VALID, "TODO"); + } return statusDataValid_(raw_buffer, device_type); } @@ -189,14 +209,23 @@ int ModuleManagerLibraryHandler::commandDataValid(const Buffer &command, unsigne if (command.isAllocated()) { raw_buffer = command.getStructBuffer(); } + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::COMMAND_DATA_VALID, "TODO"); + } return commandDataValid_(raw_buffer, device_type); } int ModuleManagerLibraryHandler::allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const { + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::ALLOCATE, "TODO"); + } return allocate_(buffer_pointer, size_in_bytes); } void ModuleManagerLibraryHandler::deallocate(struct buffer *buffer) const { + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::DEALLOCATE, "TODO"); + } deallocate_(buffer); } diff --git a/source/bringauto/structures/ModuleLibrary.cpp b/source/bringauto/structures/ModuleLibrary.cpp index c6d61c8b..20f6dfd9 100644 --- a/source/bringauto/structures/ModuleLibrary.cpp +++ b/source/bringauto/structures/ModuleLibrary.cpp @@ -6,6 +6,10 @@ namespace bringauto::structures { +ModuleLibrary::ModuleLibrary() { + aeronClient = std::make_shared(); +} + ModuleLibrary::~ModuleLibrary() { std::for_each(statusAggregators.cbegin(), statusAggregators.cend(), [](auto &pair) { pair.second->destroy_status_aggregator(); }); @@ -13,7 +17,7 @@ ModuleLibrary::~ModuleLibrary() { void ModuleLibrary::loadLibraries(const std::unordered_map &libPaths) { for(auto const &[key, path]: libPaths) { - auto handler = std::make_shared(); + auto handler = std::make_shared(aeronClient); handler->loadLibrary(path); if(handler->getModuleNumber() != key) { settings::Logger::logError("Module number from shared library {} does not match the module number from config. Config: {}, binary: {}.", path, key, handler->getModuleNumber()); From 4f52450c4c4962443998e2d5c31a2632183b9bfd Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Thu, 31 Jul 2025 10:52:55 +0200 Subject: [PATCH 12/55] check returned messages from aeron client --- .../aeron_communication/AeronClient.hpp | 4 ++- .../aeron_communication/AeronClient.cpp | 8 ++++- .../modules/ModuleManagerLibraryHandler.cpp | 30 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/include/bringauto/aeron_communication/AeronClient.hpp b/include/bringauto/aeron_communication/AeronClient.hpp index 1906edba..00fff6de 100644 --- a/include/bringauto/aeron_communication/AeronClient.hpp +++ b/include/bringauto/aeron_communication/AeronClient.hpp @@ -29,7 +29,9 @@ class AeronClient { void addModule(uint16_t moduleId); - void callModuleFunction(ModuleFunctions function ,const std::string &message) const; + void callModuleFunction(ModuleFunctions function ,const std::string &message); + + std::string_view getMessage() const; private: bool waitForAeronResponse(); diff --git a/source/bringauto/aeron_communication/AeronClient.cpp b/source/bringauto/aeron_communication/AeronClient.cpp index 402f9d2b..dec69dff 100644 --- a/source/bringauto/aeron_communication/AeronClient.cpp +++ b/source/bringauto/aeron_communication/AeronClient.cpp @@ -78,7 +78,7 @@ void AeronClient::addModule(uint16_t moduleId) { } -void AeronClient::callModuleFunction(ModuleFunctions function, const std::string &message) const { +void AeronClient::callModuleFunction(ModuleFunctions function, const std::string &message) { std::string fullMessage = std::string(moduleFunctionToCode(function)) + ":" + message; std::array buff; @@ -87,6 +87,12 @@ void AeronClient::callModuleFunction(ModuleFunctions function, const std::string const int messageLen = ::snprintf(charMessage, sizeof(charMessage), "%s", fullMessage.c_str()); srcBuffer.putBytes(0, reinterpret_cast(charMessage), messageLen); aeronPublication_->offer(srcBuffer, 0, messageLen); + waitForAeronResponse(); +} + + +std::string_view AeronClient::getMessage() const { + return aeronMessage_; } diff --git a/source/bringauto/modules/ModuleManagerLibraryHandler.cpp b/source/bringauto/modules/ModuleManagerLibraryHandler.cpp index ebc1d3a3..7a027c3b 100644 --- a/source/bringauto/modules/ModuleManagerLibraryHandler.cpp +++ b/source/bringauto/modules/ModuleManagerLibraryHandler.cpp @@ -91,6 +91,12 @@ int ModuleManagerLibraryHandler::sendStatusCondition(const Buffer ¤t_statu new_status_raw_buffer = new_status.getStructBuffer(); } + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::SEND_STATUS_CONDITION, "TODO"); + if (aeronClient_->getMessage() != "sendStatusCondition") { + throw std::runtime_error("AeronClient did not receive the expected message"); + } + } return sendStatusCondition_(current_status_raw_buffer, new_status_raw_buffer, device_type); } @@ -122,6 +128,9 @@ int ModuleManagerLibraryHandler::generateCommand(Buffer &generated_command, } if (aeronClient_ != nullptr) { aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::GENERATE_COMMAND, "TODO"); + if (aeronClient_->getMessage() != "generateCommand") { + throw std::runtime_error("AeronClient did not receive the expected message"); + } } return ret; } @@ -148,6 +157,9 @@ int ModuleManagerLibraryHandler::aggregateStatus(Buffer &aggregated_status, } if (aeronClient_ != nullptr) { aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::AGGREGATE_STATUS, "TODO"); + if (aeronClient_->getMessage() != "aggregateStatus") { + throw std::runtime_error("AeronClient did not receive the expected message"); + } } return ret; } @@ -175,6 +187,9 @@ int ModuleManagerLibraryHandler::aggregateError(Buffer &error_message, } if (aeronClient_ != nullptr) { aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::AGGREGATE_ERROR, "TODO"); + if (aeronClient_->getMessage() != "aggregateError") { + throw std::runtime_error("AeronClient did not receive the expected message"); + } } return ret; } @@ -189,6 +204,9 @@ int ModuleManagerLibraryHandler::generateFirstCommand(Buffer &default_command, u } if (aeronClient_ != nullptr) { aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::GENERATE_FIRST_COMMAND, "TODO"); + if (aeronClient_->getMessage() != "generateFirstCommand") { + throw std::runtime_error("AeronClient did not receive the expected message"); + } } return ret; } @@ -200,6 +218,9 @@ int ModuleManagerLibraryHandler::statusDataValid(const Buffer &status, unsigned } if (aeronClient_ != nullptr) { aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::STATUS_DATA_VALID, "TODO"); + if (aeronClient_->getMessage() != "statusDataValid") { + throw std::runtime_error("AeronClient did not receive the expected message"); + } } return statusDataValid_(raw_buffer, device_type); } @@ -211,6 +232,9 @@ int ModuleManagerLibraryHandler::commandDataValid(const Buffer &command, unsigne } if (aeronClient_ != nullptr) { aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::COMMAND_DATA_VALID, "TODO"); + if (aeronClient_->getMessage() != "commandDataValid") { + throw std::runtime_error("AeronClient did not receive the expected message"); + } } return commandDataValid_(raw_buffer, device_type); } @@ -218,6 +242,9 @@ int ModuleManagerLibraryHandler::commandDataValid(const Buffer &command, unsigne int ModuleManagerLibraryHandler::allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const { if (aeronClient_ != nullptr) { aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::ALLOCATE, "TODO"); + if (aeronClient_->getMessage() != "allocate") { + throw std::runtime_error("AeronClient did not receive the expected message"); + } } return allocate_(buffer_pointer, size_in_bytes); } @@ -225,6 +252,9 @@ int ModuleManagerLibraryHandler::allocate(struct buffer *buffer_pointer, size_t void ModuleManagerLibraryHandler::deallocate(struct buffer *buffer) const { if (aeronClient_ != nullptr) { aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::DEALLOCATE, "TODO"); + if (aeronClient_->getMessage() != "deallocate") { + throw std::runtime_error("AeronClient did not receive the expected message"); + } } deallocate_(buffer); } From 4ce9cec2902e086f83eaff6734548563630fd90d Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Fri, 1 Aug 2025 16:29:57 +0200 Subject: [PATCH 13/55] functional aeron module communication --- CMakeLists.txt | 4 + cmake/Dependencies.cmake | 4 +- .../modules/ModuleManagerLibraryHandler.hpp | 18 +++ include/bringauto/settings/Constants.hpp | 1 + .../modules/ModuleManagerLibraryHandler.cpp | 144 +++++++++++------- .../modules/memory_management/CMakeLists.txt | 10 ++ .../source/memory_management.cpp | 21 +++ 7 files changed, 146 insertions(+), 56 deletions(-) create mode 100644 source/bringauto/modules/memory_management/CMakeLists.txt create mode 100644 source/bringauto/modules/memory_management/source/memory_management.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 164ed20c..70c22859 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,11 +54,13 @@ FIND_PACKAGE(eclipse-paho-mqtt-c REQUIRED) FIND_PACKAGE(libbringauto_logger 2.0.0 REQUIRED) FIND_PACKAGE(fleet-protocol-interface 2.0.0 REQUIRED) FIND_PACKAGE(ZLIB 1.2.11 REQUIRED) +FIND_PACKAGE(fleet-protocol-cxx-helpers-static 1.1.1 REQUIRED) FIND_PACKAGE(aeron REQUIRED) # Requires local installation FILE(GLOB_RECURSE source_files "source/*") ADD_LIBRARY(module-gateway-lib STATIC "${source_files}") TARGET_INCLUDE_DIRECTORIES(module-gateway-lib PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) +ADD_SUBDIRECTORY(source/bringauto/modules/memory_management) TARGET_LINK_LIBRARIES(module-gateway-lib PUBLIC Boost::headers protobuf::libprotobuf @@ -72,9 +74,11 @@ TARGET_LINK_LIBRARIES(module-gateway-lib PUBLIC eclipse-paho-mqtt-c::paho-mqtt3as PahoMqttCpp::paho-mqttpp3 ZLIB::ZLIB + fleet-protocol-cxx-helpers-static::fleet-protocol-cxx-helpers-static aeron::aeron aeron::aeron_client aeron::aeron_driver + memory_management ${CMAKE_DL_LIBS} ) diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index e004024d..ba66f513 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -5,11 +5,11 @@ BA_PACKAGE_LIBRARY(fleet-protocol-interface v2.0.0 NO_DEBUG ON) BA_PACKAGE_LIBRARY(nlohmann-json v3.10.5 NO_DEBUG ON) BA_PACKAGE_LIBRARY(cxxopts v3.0.5 NO_DEBUG ON) BA_PACKAGE_LIBRARY(boost v1.86.0) -BA_PACKAGE_LIBRARY(ba-logger v2.0.0 -) +BA_PACKAGE_LIBRARY(ba-logger v2.0.0) BA_PACKAGE_LIBRARY(pahomqttc v1.3.9) BA_PACKAGE_LIBRARY(pahomqttcpp v1.3.2) BA_PACKAGE_LIBRARY(zlib v1.2.11 OUTPUT_PATH_VAR ZLIB_DIR) +BA_PACKAGE_LIBRARY(fleet-protocol-cpp v1.1.1) IF (BRINGAUTO_TESTS) BA_PACKAGE_LIBRARY(gtest v1.12.1) diff --git a/include/bringauto/modules/ModuleManagerLibraryHandler.hpp b/include/bringauto/modules/ModuleManagerLibraryHandler.hpp index 795570ce..3346eddb 100644 --- a/include/bringauto/modules/ModuleManagerLibraryHandler.hpp +++ b/include/bringauto/modules/ModuleManagerLibraryHandler.hpp @@ -96,6 +96,24 @@ class ModuleManagerLibraryHandler { */ Buffer constructBufferByTakeOwnership(struct ::buffer& buffer); + /** + * @brief Constructs a message for AeronClient to send to the module + * + * @param buffers vector of raw c buffers to be included in the message + * @param deviceType type of the device for which the message is constructed + * @return a string message to be sent + */ + std::string constructAeronMessage(const std::vector &buffers, int deviceType) const; + + /** + * @brief Parses the response from AeronClient + * + * @param raw_buffer raw buffer to be filled with the response data + * @param response response string from AeronClient + * @return status code of the response + */ + int parseAeronResponse(struct ::buffer &raw_buffer, std::string_view response) const; + void *module_ {}; std::function getModuleNumber_ {}; diff --git a/include/bringauto/settings/Constants.hpp b/include/bringauto/settings/Constants.hpp index 9d22d163..5d4e9540 100644 --- a/include/bringauto/settings/Constants.hpp +++ b/include/bringauto/settings/Constants.hpp @@ -172,6 +172,7 @@ class Constants { inline static constexpr std::string_view MODULES { "modules" }; inline static constexpr std::string_view AERON_CONNECTION { "aeron:ipc"}; + inline static constexpr std::string_view SEPARATOR { ":::" }; }; } diff --git a/source/bringauto/modules/ModuleManagerLibraryHandler.cpp b/source/bringauto/modules/ModuleManagerLibraryHandler.cpp index 7a027c3b..801b04b0 100644 --- a/source/bringauto/modules/ModuleManagerLibraryHandler.cpp +++ b/source/bringauto/modules/ModuleManagerLibraryHandler.cpp @@ -1,6 +1,8 @@ #include #include +#include +#include #include #include @@ -92,10 +94,11 @@ int ModuleManagerLibraryHandler::sendStatusCondition(const Buffer ¤t_statu } if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::SEND_STATUS_CONDITION, "TODO"); - if (aeronClient_->getMessage() != "sendStatusCondition") { - throw std::runtime_error("AeronClient did not receive the expected message"); - } + aeronClient_->callModuleFunction( + aeron_communication::AeronClient::ModuleFunctions::SEND_STATUS_CONDITION, + constructAeronMessage({¤t_status_raw_buffer, &new_status_raw_buffer}, device_type) + ); + return std::stoi(std::string(aeronClient_->getMessage())); } return sendStatusCondition_(current_status_raw_buffer, new_status_raw_buffer, device_type); } @@ -119,19 +122,22 @@ int ModuleManagerLibraryHandler::generateCommand(Buffer &generated_command, current_command_raw_buffer = current_command.getStructBuffer(); } - int ret = generateCommand_(&raw_buffer, new_status_raw_buffer, - current_status_raw_buffer, current_command_raw_buffer, device_type); + int ret; + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction( + aeron_communication::AeronClient::ModuleFunctions::GENERATE_COMMAND, + constructAeronMessage({&new_status_raw_buffer, ¤t_status_raw_buffer, ¤t_command_raw_buffer}, device_type) + ); + ret = parseAeronResponse(raw_buffer, aeronClient_->getMessage()); + } else { + ret = generateCommand_(&raw_buffer, new_status_raw_buffer, + current_status_raw_buffer, current_command_raw_buffer, device_type); + } if (ret == OK) { generated_command = constructBufferByTakeOwnership(raw_buffer); } else { generated_command = constructBuffer(); } - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::GENERATE_COMMAND, "TODO"); - if (aeronClient_->getMessage() != "generateCommand") { - throw std::runtime_error("AeronClient did not receive the expected message"); - } - } return ret; } @@ -149,18 +155,21 @@ int ModuleManagerLibraryHandler::aggregateStatus(Buffer &aggregated_status, new_status_raw_buffer = new_status.getStructBuffer(); } - const int ret = aggregateStatus_(&raw_buffer, current_status_raw_buffer, new_status_raw_buffer, device_type); + int ret; + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction( + aeron_communication::AeronClient::ModuleFunctions::AGGREGATE_STATUS, + constructAeronMessage({¤t_status_raw_buffer, &new_status_raw_buffer}, device_type) + ); + ret = parseAeronResponse(raw_buffer, aeronClient_->getMessage()); + } else { + ret = aggregateStatus_(&raw_buffer, current_status_raw_buffer, new_status_raw_buffer, device_type); + } if (ret == OK) { aggregated_status = constructBufferByTakeOwnership(raw_buffer); } else { aggregated_status = current_status; } - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::AGGREGATE_STATUS, "TODO"); - if (aeronClient_->getMessage() != "aggregateStatus") { - throw std::runtime_error("AeronClient did not receive the expected message"); - } - } return ret; } @@ -178,36 +187,41 @@ int ModuleManagerLibraryHandler::aggregateError(Buffer &error_message, if (status.isAllocated()) { status_raw_buffer = status.getStructBuffer(); } - - const int ret = aggregateError_(&raw_buffer, current_error_raw_buffer, status_raw_buffer, device_type); + int ret; + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction( + aeron_communication::AeronClient::ModuleFunctions::AGGREGATE_ERROR, + constructAeronMessage({¤t_error_raw_buffer, &status_raw_buffer}, device_type) + ); + ret = parseAeronResponse(raw_buffer, aeronClient_->getMessage()); + } else { + ret = aggregateError_(&raw_buffer, current_error_raw_buffer, status_raw_buffer, device_type); + } if (ret == OK) { error_message = constructBufferByTakeOwnership(raw_buffer); } else { error_message = constructBuffer(); } - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::AGGREGATE_ERROR, "TODO"); - if (aeronClient_->getMessage() != "aggregateError") { - throw std::runtime_error("AeronClient did not receive the expected message"); - } - } return ret; } int ModuleManagerLibraryHandler::generateFirstCommand(Buffer &default_command, unsigned int device_type) { struct ::buffer raw_buffer {}; - const int ret = generateFirstCommand_(&raw_buffer, device_type); + int ret; + if (aeronClient_ != nullptr) { + aeronClient_->callModuleFunction( + aeron_communication::AeronClient::ModuleFunctions::GENERATE_FIRST_COMMAND, + constructAeronMessage({}, device_type) + ); + ret = parseAeronResponse(raw_buffer, aeronClient_->getMessage()); + } else { + ret = generateFirstCommand_(&raw_buffer, device_type); + } if (ret == OK) { default_command = constructBufferByTakeOwnership(raw_buffer); } else { default_command = constructBuffer(); } - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::GENERATE_FIRST_COMMAND, "TODO"); - if (aeronClient_->getMessage() != "generateFirstCommand") { - throw std::runtime_error("AeronClient did not receive the expected message"); - } - } return ret; } @@ -217,10 +231,11 @@ int ModuleManagerLibraryHandler::statusDataValid(const Buffer &status, unsigned raw_buffer = status.getStructBuffer(); } if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::STATUS_DATA_VALID, "TODO"); - if (aeronClient_->getMessage() != "statusDataValid") { - throw std::runtime_error("AeronClient did not receive the expected message"); - } + aeronClient_->callModuleFunction( + aeron_communication::AeronClient::ModuleFunctions::STATUS_DATA_VALID, + constructAeronMessage({&raw_buffer}, device_type) + ); + return std::stoi(std::string(aeronClient_->getMessage())); } return statusDataValid_(raw_buffer, device_type); } @@ -231,31 +246,32 @@ int ModuleManagerLibraryHandler::commandDataValid(const Buffer &command, unsigne raw_buffer = command.getStructBuffer(); } if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::COMMAND_DATA_VALID, "TODO"); - if (aeronClient_->getMessage() != "commandDataValid") { - throw std::runtime_error("AeronClient did not receive the expected message"); - } + aeronClient_->callModuleFunction( + aeron_communication::AeronClient::ModuleFunctions::COMMAND_DATA_VALID, + constructAeronMessage({&raw_buffer}, device_type) + ); + return std::stoi(std::string(aeronClient_->getMessage())); } return commandDataValid_(raw_buffer, device_type); } int ModuleManagerLibraryHandler::allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const { - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::ALLOCATE, "TODO"); - if (aeronClient_->getMessage() != "allocate") { - throw std::runtime_error("AeronClient did not receive the expected message"); - } - } + // if (aeronClient_ != nullptr) { + // aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::ALLOCATE, "TODO"); + // if (aeronClient_->getMessage() != "allocate") { + // throw std::runtime_error("AeronClient did not receive the expected message"); + // } + // } return allocate_(buffer_pointer, size_in_bytes); } void ModuleManagerLibraryHandler::deallocate(struct buffer *buffer) const { - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::DEALLOCATE, "TODO"); - if (aeronClient_->getMessage() != "deallocate") { - throw std::runtime_error("AeronClient did not receive the expected message"); - } - } + // if (aeronClient_ != nullptr) { + // aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::DEALLOCATE, "TODO"); + // if (aeronClient_->getMessage() != "deallocate") { + // throw std::runtime_error("AeronClient did not receive the expected message"); + // } + // } deallocate_(buffer); } @@ -278,4 +294,24 @@ Buffer ModuleManagerLibraryHandler::constructBufferByTakeOwnership(struct ::buff return { buffer, deallocate_ }; } +std::string ModuleManagerLibraryHandler::constructAeronMessage(const std::vector &buffers, int deviceType) const { + std::string message; + for (const auto &buff : buffers) { + message += std::string(static_cast(buff->data), buff->size_in_bytes) + std::string(settings::Constants::SEPARATOR); + } + return message + std::to_string(deviceType); +} + +int ModuleManagerLibraryHandler::parseAeronResponse(struct ::buffer &raw_buffer, std::string_view response) const { + size_t sepPos = response.find(settings::Constants::SEPARATOR); + if (sepPos == std::string::npos) { + throw std::runtime_error("Invalid response format: " + std::string(response)); + } + bringauto::fleet_protocol::cxx::StringAsBuffer::createBufferAndCopyData( + &raw_buffer, + response.substr(sepPos + settings::Constants::SEPARATOR.size()) + ); + return std::stoi(std::string(response.substr(0, sepPos))); +} + } diff --git a/source/bringauto/modules/memory_management/CMakeLists.txt b/source/bringauto/modules/memory_management/CMakeLists.txt new file mode 100644 index 00000000..e2e72b13 --- /dev/null +++ b/source/bringauto/modules/memory_management/CMakeLists.txt @@ -0,0 +1,10 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.25 FATAL_ERROR) +PROJECT(ModuleGateway) + +SET(CMAKE_CXX_STANDARD 20) + +FIND_PACKAGE(fleet-protocol-interface 2.0.0 REQUIRED) + +FILE(GLOB_RECURSE memory_management_cpp_files "./source/*") +ADD_LIBRARY(memory_management STATIC ${memory_management_cpp_files}) +TARGET_LINK_LIBRARIES(memory_management PUBLIC fleet-protocol-interface::common-headers-interface) \ No newline at end of file diff --git a/source/bringauto/modules/memory_management/source/memory_management.cpp b/source/bringauto/modules/memory_management/source/memory_management.cpp new file mode 100644 index 00000000..834c9f5f --- /dev/null +++ b/source/bringauto/modules/memory_management/source/memory_management.cpp @@ -0,0 +1,21 @@ +#include + +#include +#include + + +int allocate(struct buffer *buffer_pointer, size_t size_in_bytes){ + try{ + buffer_pointer->data = new char[size_in_bytes](); + } catch(std::bad_alloc&){ + return NOT_OK; + } + buffer_pointer->size_in_bytes = size_in_bytes; + return OK; +} + +void deallocate(struct buffer *buffer_pointer){ + delete[] static_cast(buffer_pointer->data); + buffer_pointer->data = nullptr; + buffer_pointer->size_in_bytes = 0; +} \ No newline at end of file From 1b0bc738da7d2208476bde8448f140fc88e1ecf1 Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Mon, 4 Aug 2025 10:46:57 +0200 Subject: [PATCH 14/55] added docstrings for AeronClient --- .../aeron_communication/AeronClient.hpp | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/include/bringauto/aeron_communication/AeronClient.hpp b/include/bringauto/aeron_communication/AeronClient.hpp index 00fff6de..a8c4feb5 100644 --- a/include/bringauto/aeron_communication/AeronClient.hpp +++ b/include/bringauto/aeron_communication/AeronClient.hpp @@ -8,6 +8,10 @@ namespace bringauto::aeron_communication { +/** + * This class is responsible for managing Aeron communication. + * It is used for calling module functions on a separate module binary using Aeron. + */ class AeronClient { public: @@ -23,14 +27,35 @@ class AeronClient { STATUS_DATA_VALID = 8 }; + /** + * @brief Constructs an AeronClient instance. + * Initializes the Aeron context and sets up handlers for publications and subscriptions. + */ AeronClient(); ~AeronClient() = default; + /** + * @brief Adds a module to the Aeron client with the given module ID. + * Creates a publication and subscription for the module. + * Only supports one module for now. + * @param moduleId The ID of the module to be added. + */ void addModule(uint16_t moduleId); + /** + * @brief Calls a module function with the specified message. + * Constructs a full message by combining the function code and the provided message, + * then sends it via Aeron publication. + * @param function The module function to call. + * @param message The message to send to the module. + */ void callModuleFunction(ModuleFunctions function ,const std::string &message); + /** + * @brief Retrieves the last received message from Aeron. + * @return A string view of the last message received. + */ std::string_view getMessage() const; private: @@ -45,7 +70,9 @@ class AeronClient { std::shared_ptr aeronFragmentAssembler_ {nullptr}; std::shared_ptr aeronHandler_ {nullptr}; std::shared_ptr aeronIdleStrategy_ {nullptr}; + /// Indicates if the Aeron client is currently polling for messages std::atomic aeronPolling_ {false}; + /// Last message received from Aeron std::string aeronMessage_ {}; }; From c90fe99de00541cb13697fa8ddc2bd294ec5f6bc Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Mon, 22 Sep 2025 10:09:44 +0200 Subject: [PATCH 15/55] async function execution usage --- CMLibStorage.cmake | 4 + CMakeLists.txt | 12 +- README.md | 2 +- cmake/Dependencies.cmake | 2 +- .../aeron_communication/AeronClient.hpp | 80 ------- .../aeron_communication/AeronDriver.hpp | 51 ----- .../external_client/ErrorAggregator.hpp | 5 +- include/bringauto/modules/Buffer.hpp | 1 + .../modules/ModuleManagerLibraryHandler.hpp | 24 +- .../ModuleManagerLibraryHandlerAsync.hpp | 101 +++++++++ .../bringauto/modules/StatusAggregator.hpp | 9 +- include/bringauto/settings/Constants.hpp | 114 ++++++++++ include/bringauto/settings/Settings.hpp | 5 + .../bringauto/structures/ModuleLibrary.hpp | 10 +- main.cpp | 17 +- .../aeron_communication/AeronClient.cpp | 121 ---------- .../aeron_communication/AeronDriver.cpp | 56 ----- .../external_client/ErrorAggregator.cpp | 2 +- .../modules/ModuleManagerLibraryHandler.cpp | 109 +-------- .../ModuleManagerLibraryHandlerAsync.cpp | 207 ++++++++++++++++++ .../source/memory_management.cpp | 2 +- source/bringauto/settings/SettingsParser.cpp | 5 + source/bringauto/structures/ModuleLibrary.cpp | 11 +- 23 files changed, 480 insertions(+), 470 deletions(-) delete mode 100644 include/bringauto/aeron_communication/AeronClient.hpp delete mode 100644 include/bringauto/aeron_communication/AeronDriver.hpp create mode 100644 include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp delete mode 100644 source/bringauto/aeron_communication/AeronClient.cpp delete mode 100644 source/bringauto/aeron_communication/AeronDriver.cpp create mode 100644 source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp diff --git a/CMLibStorage.cmake b/CMLibStorage.cmake index 7e7142da..36796bad 100644 --- a/CMLibStorage.cmake +++ b/CMLibStorage.cmake @@ -1,3 +1,7 @@ +FIND_PACKAGE(CMLIB REQUIRED COMPONENTS CMCONF) + +CMCONF_INIT_SYSTEM(FLEET_PROTOCOL) + SET(STORAGE_LIST DEP) SET(STORAGE_LIST_DEP "https://github.com/bacpack-system/package-tracker.git") diff --git a/CMakeLists.txt b/CMakeLists.txt index 70c22859..9ca001a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ SET(Protobuf_USE_STATIC_LIBS ON) FIND_PACKAGE(Boost 1.74 REQUIRED CONFIG) FIND_PACKAGE(Protobuf 3.21.12 REQUIRED) -FIND_PACKAGE(cxxopts 3.0.0 REQUIRED) +FIND_PACKAGE(cxxopts 3.1.1 REQUIRED) FIND_PACKAGE(nlohmann_json 3.2.0 REQUIRED) FIND_PACKAGE(PahoMqttCpp REQUIRED) FIND_PACKAGE(eclipse-paho-mqtt-c REQUIRED) @@ -55,7 +55,9 @@ FIND_PACKAGE(libbringauto_logger 2.0.0 REQUIRED) FIND_PACKAGE(fleet-protocol-interface 2.0.0 REQUIRED) FIND_PACKAGE(ZLIB 1.2.11 REQUIRED) FIND_PACKAGE(fleet-protocol-cxx-helpers-static 1.1.1 REQUIRED) -FIND_PACKAGE(aeron REQUIRED) # Requires local installation + +#temp +ADD_SUBDIRECTORY(${CMAKE_CURRENT_LIST_DIR}/aeron-interface) FILE(GLOB_RECURSE source_files "source/*") ADD_LIBRARY(module-gateway-lib STATIC "${source_files}") @@ -75,9 +77,7 @@ TARGET_LINK_LIBRARIES(module-gateway-lib PUBLIC PahoMqttCpp::paho-mqttpp3 ZLIB::ZLIB fleet-protocol-cxx-helpers-static::fleet-protocol-cxx-helpers-static - aeron::aeron - aeron::aeron_client - aeron::aeron_driver + async-function-execution-static memory_management ${CMAKE_DL_LIBS} ) @@ -102,7 +102,7 @@ TARGET_LINK_LIBRARIES(module-gateway-app PUBLIC module-gateway-lib) IF(BRINGAUTO_TESTS) ENABLE_TESTING() - INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt) + #INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt) INCLUDE(CTest) ENDIF(BRINGAUTO_TESTS) diff --git a/README.md b/README.md index c56b1e2f..be8a1b18 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ connection is broken and as soon as the connection is up, then error aggregated - [cmlib](https://github.com/cmakelib/cmakelib) - [protobuf](https://github.com/protocolbuffers/protobuf/tree/main/src) >= v3.21.12 -- [cxxopts](https://github.com/jarro2783/cxxopts) >= v3.0.0 +- [cxxopts](https://github.com/jarro2783/cxxopts) >= v3.1.1 - [boost](https://github.com/boostorg/boost) >= v1.74.0 - [nlohmann-json](https://github.com/nlohmann/json) >= v3.2.0 - [ba-logger](https://github.com/bringauto/ba-logger) >= v1.2.0 diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index ba66f513..072f74e6 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -3,7 +3,7 @@ SET(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH FALSE) BA_PACKAGE_LIBRARY(protobuf v4.21.12) BA_PACKAGE_LIBRARY(fleet-protocol-interface v2.0.0 NO_DEBUG ON) BA_PACKAGE_LIBRARY(nlohmann-json v3.10.5 NO_DEBUG ON) -BA_PACKAGE_LIBRARY(cxxopts v3.0.5 NO_DEBUG ON) +BA_PACKAGE_LIBRARY(cxxopts v3.1.1 NO_DEBUG ON) BA_PACKAGE_LIBRARY(boost v1.86.0) BA_PACKAGE_LIBRARY(ba-logger v2.0.0) BA_PACKAGE_LIBRARY(pahomqttc v1.3.9) diff --git a/include/bringauto/aeron_communication/AeronClient.hpp b/include/bringauto/aeron_communication/AeronClient.hpp deleted file mode 100644 index a8c4feb5..00000000 --- a/include/bringauto/aeron_communication/AeronClient.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include -#include -#include - - - -namespace bringauto::aeron_communication { - -/** - * This class is responsible for managing Aeron communication. - * It is used for calling module functions on a separate module binary using Aeron. - */ -class AeronClient { - -public: - enum ModuleFunctions { - ALLOCATE = 0, - AGGREGATE_ERROR = 1, - AGGREGATE_STATUS = 2, - COMMAND_DATA_VALID = 3, - DEALLOCATE = 4, - GENERATE_COMMAND = 5, - GENERATE_FIRST_COMMAND = 6, - SEND_STATUS_CONDITION = 7, - STATUS_DATA_VALID = 8 - }; - - /** - * @brief Constructs an AeronClient instance. - * Initializes the Aeron context and sets up handlers for publications and subscriptions. - */ - AeronClient(); - - ~AeronClient() = default; - - /** - * @brief Adds a module to the Aeron client with the given module ID. - * Creates a publication and subscription for the module. - * Only supports one module for now. - * @param moduleId The ID of the module to be added. - */ - void addModule(uint16_t moduleId); - - /** - * @brief Calls a module function with the specified message. - * Constructs a full message by combining the function code and the provided message, - * then sends it via Aeron publication. - * @param function The module function to call. - * @param message The message to send to the module. - */ - void callModuleFunction(ModuleFunctions function ,const std::string &message); - - /** - * @brief Retrieves the last received message from Aeron. - * @return A string view of the last message received. - */ - std::string_view getMessage() const; - -private: - bool waitForAeronResponse(); - aeron::fragment_handler_t handleMessage(); - std::string moduleFunctionToCode(ModuleFunctions function) const; - - aeron::Context aeronContext_ {}; - std::shared_ptr aeron_ {nullptr}; - std::shared_ptr aeronPublication_ {nullptr}; - std::shared_ptr aeronSubscription_ {nullptr}; - std::shared_ptr aeronFragmentAssembler_ {nullptr}; - std::shared_ptr aeronHandler_ {nullptr}; - std::shared_ptr aeronIdleStrategy_ {nullptr}; - /// Indicates if the Aeron client is currently polling for messages - std::atomic aeronPolling_ {false}; - /// Last message received from Aeron - std::string aeronMessage_ {}; - -}; - -} diff --git a/include/bringauto/aeron_communication/AeronDriver.hpp b/include/bringauto/aeron_communication/AeronDriver.hpp deleted file mode 100644 index 210548a1..00000000 --- a/include/bringauto/aeron_communication/AeronDriver.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include - - - -namespace bringauto::aeron_communication { - -/** - * This class is meant for aeron testing. It provides a simple interface to start the Aeron media driver. - */ -class AeronDriver { - -public: - /** - * @brief Constructs Aeron Driver. - * Initializes the driver with default settings. - */ - AeronDriver(); - - ~AeronDriver() = default; - - /** - * @brief Starts the Aeron Driver. - * This method will block until the driver is stopped or an error occurs. - */ - void run(); - - /** - * @brief Checks if the Aeron Driver is running. - * @return true if the driver is running, false otherwise. - */ - bool isRunning() const; - - /** - * @brief Stops the Aeron Driver. - * This method will stop the driver and release all resources. - */ - void stop(); - -private: - /// Context for the Aeron driver. Driver settings can be adjusted in this struct. - aeron_driver_context_t *driverContext_; - /// Pointer to the Aeron driver instance. - aeron_driver_t *driver_ {nullptr}; - /// Variable used in aeron macros that returns error codes. - volatile int exitStatus_ {AERON_NULL_VALUE}; - -}; - -} diff --git a/include/bringauto/external_client/ErrorAggregator.hpp b/include/bringauto/external_client/ErrorAggregator.hpp index dfa47cd9..e6724f49 100644 --- a/include/bringauto/external_client/ErrorAggregator.hpp +++ b/include/bringauto/external_client/ErrorAggregator.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -25,7 +26,7 @@ class ErrorAggregator { * @return OK if initialization was successful * @return NOT_OK if an error occurred */ - int init_error_aggregator(const std::shared_ptr &library); + int init_error_aggregator(const std::shared_ptr &library); //TODO select type from config /** * @short Clean up. @@ -111,7 +112,7 @@ class ErrorAggregator { modules::Buffer lastStatus {}; }; - std::shared_ptr module_ {}; + std::shared_ptr module_ {}; //TODO select type from config /** * @brief Map of devices states, key is device identification converted to string diff --git a/include/bringauto/modules/Buffer.hpp b/include/bringauto/modules/Buffer.hpp index a6c5e8ea..a616e9b2 100644 --- a/include/bringauto/modules/Buffer.hpp +++ b/include/bringauto/modules/Buffer.hpp @@ -19,6 +19,7 @@ namespace bringauto::modules { struct Buffer final { friend class ModuleManagerLibraryHandler; + friend class ModuleManagerLibraryHandlerAsync; Buffer() = default; Buffer(const Buffer& buff) = default; diff --git a/include/bringauto/modules/ModuleManagerLibraryHandler.hpp b/include/bringauto/modules/ModuleManagerLibraryHandler.hpp index 3346eddb..8d12252e 100644 --- a/include/bringauto/modules/ModuleManagerLibraryHandler.hpp +++ b/include/bringauto/modules/ModuleManagerLibraryHandler.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include @@ -18,8 +17,6 @@ namespace bringauto::modules { class ModuleManagerLibraryHandler { public: ModuleManagerLibraryHandler() = default; - ModuleManagerLibraryHandler(std::shared_ptr &aeronClient): - aeronClient_(aeronClient) {}; ~ModuleManagerLibraryHandler(); @@ -96,24 +93,6 @@ class ModuleManagerLibraryHandler { */ Buffer constructBufferByTakeOwnership(struct ::buffer& buffer); - /** - * @brief Constructs a message for AeronClient to send to the module - * - * @param buffers vector of raw c buffers to be included in the message - * @param deviceType type of the device for which the message is constructed - * @return a string message to be sent - */ - std::string constructAeronMessage(const std::vector &buffers, int deviceType) const; - - /** - * @brief Parses the response from AeronClient - * - * @param raw_buffer raw buffer to be filled with the response data - * @param response response string from AeronClient - * @return status code of the response - */ - int parseAeronResponse(struct ::buffer &raw_buffer, std::string_view response) const; - void *module_ {}; std::function getModuleNumber_ {}; @@ -130,7 +109,6 @@ class ModuleManagerLibraryHandler { std::function generateCommand_ {}; std::function allocate_ {}; std::function deallocate_ {}; - std::shared_ptr aeronClient_ {nullptr}; }; -} +} \ No newline at end of file diff --git a/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp b/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp new file mode 100644 index 00000000..92acf296 --- /dev/null +++ b/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include + +#include + +#include +#include + + + +namespace bringauto::modules { + +/** + * @brief Class used to load and handle library created by module maintainer + */ +class ModuleManagerLibraryHandlerAsync { +public: + ModuleManagerLibraryHandlerAsync(); + + ~ModuleManagerLibraryHandlerAsync(); + + /** + * @brief Load library created by a module maintainer + * + * @param path path to the library + * @param moduleBinaryPath path to the module binary + */ + void loadLibrary(const std::filesystem::path &path, const std::string &moduleBinaryPath); + + int getModuleNumber() const; + + int isDeviceTypeSupported(unsigned int device_type) const; + + int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) const; + + /** + * @short After executing the respective module function, an error might be thrown when allocating the buffer. + * + * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h + */ + int generateCommand(Buffer &generated_command, const Buffer &new_status, + const Buffer ¤t_status, const Buffer ¤t_command, + unsigned int device_type); + + /** + * @short After executing the respective module function, an error might be thrown when allocating the buffer. + * + * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h + */ + int aggregateStatus(Buffer &aggregated_status, const Buffer ¤t_status, + const Buffer &new_status, unsigned int device_type); + + /** + * @short After executing the respective module function, an error might be thrown when allocating the buffer. + * + * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h + */ + int aggregateError(Buffer &error_message, const Buffer ¤t_error_message, const Buffer &status, + unsigned int device_type); + + /** + * @short After executing the respective module function, an error might be thrown when allocating the buffer. + * + * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h + */ + int generateFirstCommand(Buffer &default_command, unsigned int device_type); + + int statusDataValid(const Buffer &status, unsigned int device_type) const; + + int commandDataValid(const Buffer &command, unsigned int device_type) const; + + /** + * @brief Constructs a buffer with the given size + * + * @param size size of the buffer + * @return a new Buffer object + */ + Buffer constructBuffer(std::size_t size = 0); + +private: + + int allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const; + + void deallocate(struct buffer *buffer) const; + + /** + * @brief Constructs a buffer with the same raw c buffer as provided + * + * @param buffer c buffer to be used + * @return a new Buffer object + */ + Buffer constructBufferByTakeOwnership(struct ::buffer& buffer); + + std::function deallocate_ {}; + + /// Process id of the module binary + pid_t moduleBinaryPid_ {}; +}; + +} diff --git a/include/bringauto/modules/StatusAggregator.hpp b/include/bringauto/modules/StatusAggregator.hpp index 03c84e90..4cf426fd 100644 --- a/include/bringauto/modules/StatusAggregator.hpp +++ b/include/bringauto/modules/StatusAggregator.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -20,9 +21,9 @@ class StatusAggregator { public: explicit StatusAggregator(const std::shared_ptr &context, - const std::shared_ptr &libraryHandler): context_ { context }, - module_ { - libraryHandler } {}; + const std::shared_ptr &libraryHandler): context_ { context }, //TODO select type from config + module_ { + libraryHandler } {}; StatusAggregator() = default; @@ -186,7 +187,7 @@ class StatusAggregator { std::shared_ptr context_ {}; - const std::shared_ptr module_ {}; + const std::shared_ptr module_ {}; //TODO select type from config /** * @brief Map of devices states, key is device identification diff --git a/include/bringauto/settings/Constants.hpp b/include/bringauto/settings/Constants.hpp index 5d4e9540..34dd5fb7 100644 --- a/include/bringauto/settings/Constants.hpp +++ b/include/bringauto/settings/Constants.hpp @@ -1,5 +1,11 @@ #pragma once +#include + +#include +#include +#include + #include #include @@ -152,6 +158,7 @@ class Constants { inline static constexpr std::string_view PORT { "port" }; inline static constexpr std::string_view MODULE_PATHS { "module-paths" }; + inline static constexpr std::string_view MODULE_BINARY_PATH { "module-binary-path" }; inline static constexpr std::string_view INTERNAL_SERVER_SETTINGS { "internal-server-settings" }; @@ -175,4 +182,111 @@ class Constants { inline static constexpr std::string_view SEPARATOR { ":::" }; }; +struct ConvertibleBufferReturn final { + int returnCode { 0 }; //TODO temporary, need to serialize return code aswell + struct ::buffer buffer {}; + ConvertibleBufferReturn() = default; + ConvertibleBufferReturn(int code, struct ::buffer buff) : returnCode(code), buffer(buff) {} + ~ConvertibleBufferReturn() { + buffer.data = nullptr; + } + + std::span serialize() const { + return std::span {reinterpret_cast(buffer.data), buffer.size_in_bytes}; + } + void deserialize(std::span bytes) { + if(buffer.data != nullptr) { + delete[] buffer.data; + } + buffer.size_in_bytes = bytes.size(); + buffer.data = new uint8_t[buffer.size_in_bytes]; + std::memcpy(buffer.data, bytes.data(), buffer.size_in_bytes); + } +}; + +struct ConvertibleBuffer final { + struct ::buffer buffer {}; + ConvertibleBuffer() = default; + ConvertibleBuffer(struct ::buffer buff) : buffer(buff) {} + + std::span serialize() const { + return std::span {reinterpret_cast(buffer.data), buffer.size_in_bytes}; + } + void deserialize(std::span bytes) { + buffer.data = const_cast(bytes.data()); + buffer.size_in_bytes = bytes.size(); + } +}; + +class FunctionIds { +public: + inline static const async_function_execution::FunctionDefinition getModuleNumber { + async_function_execution::FunctionId { 0 }, + async_function_execution::Return { int {} }, + async_function_execution::Arguments {} + }; + + inline static const async_function_execution::FunctionDefinition isDeviceTypeSupported { + async_function_execution::FunctionId { 1 }, + async_function_execution::Return { int {} }, + async_function_execution::Arguments { uint32_t {} } + }; + + inline static const async_function_execution::FunctionDefinition sendStatusCondition { + async_function_execution::FunctionId { 2 }, + async_function_execution::Return { int {} }, + async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } + }; + + inline static const async_function_execution::FunctionDefinition generateCommand { + async_function_execution::FunctionId { 3 }, + async_function_execution::Return { ConvertibleBufferReturn {} }, + async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } + }; + + inline static const async_function_execution::FunctionDefinition aggregateStatus { + async_function_execution::FunctionId { 4 }, + async_function_execution::Return { ConvertibleBufferReturn {} }, + async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } + }; + + inline static const async_function_execution::FunctionDefinition aggregateError { + async_function_execution::FunctionId { 5 }, + async_function_execution::Return { ConvertibleBufferReturn {} }, + async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } + }; + + inline static const async_function_execution::FunctionDefinition generateFirstCommand { + async_function_execution::FunctionId { 6 }, + async_function_execution::Return { ConvertibleBufferReturn {} }, + async_function_execution::Arguments { uint32_t {} } + }; + + inline static const async_function_execution::FunctionDefinition statusDataValid { + async_function_execution::FunctionId { 7 }, + async_function_execution::Return { int {} }, + async_function_execution::Arguments { ConvertibleBuffer {}, uint32_t {} } + }; + + inline static const async_function_execution::FunctionDefinition commandDataValid { + async_function_execution::FunctionId { 8 }, + async_function_execution::Return { int {} }, + async_function_execution::Arguments { ConvertibleBuffer {}, uint32_t {} } + }; + + inline static const async_function_execution::FunctionList functionList { + std::tuple { + getModuleNumber, + isDeviceTypeSupported, + sendStatusCondition, + generateCommand, + aggregateStatus, + aggregateError, + generateFirstCommand, + statusDataValid, + commandDataValid + } + }; +}; + } diff --git a/include/bringauto/settings/Settings.hpp b/include/bringauto/settings/Settings.hpp index aad2db84..10758209 100644 --- a/include/bringauto/settings/Settings.hpp +++ b/include/bringauto/settings/Settings.hpp @@ -33,6 +33,11 @@ struct Settings { */ std::unordered_map modulePaths {}; + /** + * @brief path to module binary + */ + std::string moduleBinaryPath {}; + /** * @brief Setting of external connection endpoints and protocols */ diff --git a/include/bringauto/structures/ModuleLibrary.hpp b/include/bringauto/structures/ModuleLibrary.hpp index 88655b50..124e84c0 100644 --- a/include/bringauto/structures/ModuleLibrary.hpp +++ b/include/bringauto/structures/ModuleLibrary.hpp @@ -1,8 +1,8 @@ #pragma once #include +#include #include -#include #include @@ -14,7 +14,7 @@ namespace bringauto::structures { * @brief Library with library handlers and status aggregators */ struct ModuleLibrary { - ModuleLibrary(); + ModuleLibrary() = default; ~ModuleLibrary(); @@ -23,7 +23,7 @@ struct ModuleLibrary { * * @param libPaths paths to the libraries */ - void loadLibraries(const std::unordered_map &libPaths); + void loadLibraries(const std::unordered_map &libPaths, const std::string &moduleBinaryPath); /** * @brief Initialize status aggregators with context @@ -32,11 +32,9 @@ struct ModuleLibrary { */ void initStatusAggregators(std::shared_ptr &context); /// Map of module handlers, key is module id - std::unordered_map> moduleLibraryHandlers {}; + std::unordered_map> moduleLibraryHandlers {}; //TODO select type from config /// Map of status aggregators, key is module id std::unordered_map> statusAggregators {}; - /// Aeron client used for communication with modules - std::shared_ptr aeronClient {nullptr}; }; } diff --git a/main.cpp b/main.cpp index 4c8a32aa..a2204717 100644 --- a/main.cpp +++ b/main.cpp @@ -10,9 +10,9 @@ #include #include #include -#include #include +#include #include #include #include @@ -69,14 +69,15 @@ int main(int argc, char **argv) { return 1; } - bringauto::aeron_communication::AeronDriver aeronDriver {}; - std::jthread aeronDriverThread([&aeronDriver]() { aeronDriver.run(); }); - baset::Logger::logInfo("Aeron Driver starting..."); - std::this_thread::sleep_for(std::chrono::seconds(2)); //TODO Not sure how much time is needed. + // bringauto::aeron_interface::AeronDriver aeronDriver {}; + // std::jthread aeronDriverThread([&aeronDriver]() { aeronDriver.run(); }); + // baset::Logger::logInfo("Aeron Driver starting..."); + // std::this_thread::sleep_for(std::chrono::seconds(3)); //TODO Not sure how much time is needed. + bas::ModuleLibrary moduleLibrary {}; try { - moduleLibrary.loadLibraries(context->settings->modulePaths); + moduleLibrary.loadLibraries(context->settings->modulePaths, context->settings->moduleBinaryPath); moduleLibrary.initStatusAggregators(context); } catch(std::exception &e) { std::cerr << "[ERROR] Error occurred during module initialization: " << e.what() << std::endl; @@ -106,12 +107,12 @@ int main(int argc, char **argv) { context->ioContext.stop(); } - aeronDriver.stop(); + // aeronDriver.stop(); contextThread2.join(); contextThread1.join(); externalClientThread.join(); moduleHandlerThread.join(); - aeronDriverThread.join(); + // aeronDriverThread.join(); internalServer.destroy(); moduleHandler.destroy(); diff --git a/source/bringauto/aeron_communication/AeronClient.cpp b/source/bringauto/aeron_communication/AeronClient.cpp deleted file mode 100644 index dec69dff..00000000 --- a/source/bringauto/aeron_communication/AeronClient.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include -#include -#include - - - -namespace bringauto::aeron_communication { - -using log = settings::Logger; - -AeronClient::AeronClient() { - aeronContext_.newPublicationHandler( - [](const std::string &channel, std::int32_t streamId, std::int32_t sessionId, std::int64_t correlationId) { - log::logDebug("AeronClient: New publication on channel: " + channel + - " with correlationId: " + std::to_string(correlationId) + - ", streamId: " + std::to_string(streamId) + - ", sessionId: " + std::to_string(sessionId) - ); - } - ); - - aeronContext_.newSubscriptionHandler( - [](const std::string &channel, std::int32_t streamId, std::int64_t correlationId) { - log::logDebug("AeronClient: New subscription on channel: " + channel + - " with correlationId: " + std::to_string(correlationId) + - ", streamId: " + std::to_string(streamId) - ); - } - ); - - aeronContext_.availableImageHandler( - [](aeron::Image &image) { - log::logDebug("AeronClient: Available image on correlationId: " + std::to_string(image.correlationId()) + - ", sessionId: " + std::to_string(image.sessionId()) + - ", position: " + std::to_string(image.position()) + - ", sourceIdentity: " + image.sourceIdentity() - ); - } - ); - - aeronContext_.unavailableImageHandler( - [](aeron::Image &image) { - log::logDebug("AeronClient: Unavailable image on correlationId: " + std::to_string(image.correlationId()) + - ", sessionId: " + std::to_string(image.sessionId()) + - ", position: " + std::to_string(image.position()) + - ", sourceIdentity: " + image.sourceIdentity() - ); - } - ); - - aeron_ = aeron::Aeron::connect(aeronContext_); - aeronFragmentAssembler_ = std::make_shared(handleMessage()); - aeronHandler_ = std::make_shared(aeronFragmentAssembler_->handler()); - aeronIdleStrategy_ = std::make_shared(); -} - - -void AeronClient::addModule(uint16_t moduleId) { - std::int64_t id = aeron_->addPublication( - std::string(settings::Constants::AERON_CONNECTION), - settings::aeron_to_module_stream_id_base + moduleId - ); - aeronPublication_ = aeron_->findPublication(id); - while (!aeronPublication_) { - std::this_thread::yield(); - aeronPublication_ = aeron_->findPublication(id); - } - - id = aeron_->addSubscription( - std::string(settings::Constants::AERON_CONNECTION), - settings::aeron_to_gateway_stream_id_base + moduleId - ); - aeronSubscription_ = aeron_->findSubscription(id); - while (!aeronSubscription_) { - std::this_thread::yield(); - aeronSubscription_ = aeron_->findSubscription(id); - } -} - - -void AeronClient::callModuleFunction(ModuleFunctions function, const std::string &message) { - std::string fullMessage = std::string(moduleFunctionToCode(function)) + ":" + message; - - std::array buff; - aeron::concurrent::AtomicBuffer srcBuffer(&buff[0], buff.size()); - char charMessage[256]; - const int messageLen = ::snprintf(charMessage, sizeof(charMessage), "%s", fullMessage.c_str()); - srcBuffer.putBytes(0, reinterpret_cast(charMessage), messageLen); - aeronPublication_->offer(srcBuffer, 0, messageLen); - waitForAeronResponse(); -} - - -std::string_view AeronClient::getMessage() const { - return aeronMessage_; -} - - -bool AeronClient::waitForAeronResponse() { - aeronPolling_ = true; - while (aeronPolling_) { - const int fragmentsRead = aeronSubscription_->poll(*aeronHandler_, 10); - aeronIdleStrategy_->idle(fragmentsRead); - } - return true; -} - - -aeron::fragment_handler_t AeronClient::handleMessage() { - return [&](const aeron::AtomicBuffer &buffer, aeron::util::index_t offset, aeron::util::index_t length, const aeron::Header &header) { - std::string message(reinterpret_cast(buffer.buffer()) + offset, static_cast(length)); - aeronMessage_ = message; - aeronPolling_ = false; - }; -} - -std::string AeronClient::moduleFunctionToCode(ModuleFunctions function) const { - return std::to_string(static_cast(function)); -} - -} diff --git a/source/bringauto/aeron_communication/AeronDriver.cpp b/source/bringauto/aeron_communication/AeronDriver.cpp deleted file mode 100644 index f6a4b130..00000000 --- a/source/bringauto/aeron_communication/AeronDriver.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include - -#include - - - -static bringauto::aeron_communication::AeronDriver* globalDriverInstance = nullptr; - -void terminationHook(void *state) { - if (globalDriverInstance == nullptr) { - globalDriverInstance->stop(); - } -} - -void signalHandler(int signal) { - if (globalDriverInstance != nullptr) { - globalDriverInstance->stop(); - } -} - - -namespace bringauto::aeron_communication { - -AeronDriver::AeronDriver() { - globalDriverInstance = this; - signal(SIGINT, signalHandler); - signal(SIGTERM, signalHandler); - aeron_driver_context_init(&driverContext_); - aeron_driver_context_set_driver_termination_hook(driverContext_, terminationHook, NULL); - driverContext_->agent_on_start_func_delegate = driverContext_->agent_on_start_func; - driverContext_->agent_on_start_state_delegate = driverContext_->agent_on_start_state; - aeron_driver_context_set_agent_on_start_function(driverContext_, aeron_set_thread_affinity_on_start, driverContext_); - aeron_driver_init(&driver_, driverContext_); -} - - -void AeronDriver::run() { - aeron_driver_start(driver_, true); - while (isRunning()) { - aeron_driver_main_idle_strategy(driver_, aeron_driver_main_do_work(driver_)); - } -} - - -bool AeronDriver::isRunning() const { - int result; - AERON_GET_ACQUIRE(result, exitStatus_); - return result == AERON_NULL_VALUE; -} - - -void AeronDriver::stop() { - AERON_SET_RELEASE(exitStatus_, EXIT_SUCCESS); -} - -} diff --git a/source/bringauto/external_client/ErrorAggregator.cpp b/source/bringauto/external_client/ErrorAggregator.cpp index b0f650fe..89890616 100644 --- a/source/bringauto/external_client/ErrorAggregator.cpp +++ b/source/bringauto/external_client/ErrorAggregator.cpp @@ -8,7 +8,7 @@ namespace bringauto::external_client { -int ErrorAggregator::init_error_aggregator(const std::shared_ptr &library) { +int ErrorAggregator::init_error_aggregator(const std::shared_ptr &library) { //TODO select type from config module_ = library; return OK; } diff --git a/source/bringauto/modules/ModuleManagerLibraryHandler.cpp b/source/bringauto/modules/ModuleManagerLibraryHandler.cpp index 801b04b0..6d21bbaf 100644 --- a/source/bringauto/modules/ModuleManagerLibraryHandler.cpp +++ b/source/bringauto/modules/ModuleManagerLibraryHandler.cpp @@ -1,8 +1,6 @@ #include #include -#include -#include #include #include @@ -56,11 +54,6 @@ void ModuleManagerLibraryHandler::loadLibrary(const std::filesystem::path &path) "allocate")); deallocate_ = reinterpret_cast::fncptr>(checkFunction( "deallocate")); - - if (aeronClient_ != nullptr) { - aeronClient_->addModule(getModuleNumber_()); - } - log::logDebug("Library " + path.string() + " was successfully loaded"); } @@ -93,13 +86,6 @@ int ModuleManagerLibraryHandler::sendStatusCondition(const Buffer ¤t_statu new_status_raw_buffer = new_status.getStructBuffer(); } - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction( - aeron_communication::AeronClient::ModuleFunctions::SEND_STATUS_CONDITION, - constructAeronMessage({¤t_status_raw_buffer, &new_status_raw_buffer}, device_type) - ); - return std::stoi(std::string(aeronClient_->getMessage())); - } return sendStatusCondition_(current_status_raw_buffer, new_status_raw_buffer, device_type); } @@ -122,17 +108,8 @@ int ModuleManagerLibraryHandler::generateCommand(Buffer &generated_command, current_command_raw_buffer = current_command.getStructBuffer(); } - int ret; - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction( - aeron_communication::AeronClient::ModuleFunctions::GENERATE_COMMAND, - constructAeronMessage({&new_status_raw_buffer, ¤t_status_raw_buffer, ¤t_command_raw_buffer}, device_type) - ); - ret = parseAeronResponse(raw_buffer, aeronClient_->getMessage()); - } else { - ret = generateCommand_(&raw_buffer, new_status_raw_buffer, - current_status_raw_buffer, current_command_raw_buffer, device_type); - } + int ret = generateCommand_(&raw_buffer, new_status_raw_buffer, + current_status_raw_buffer, current_command_raw_buffer, device_type); if (ret == OK) { generated_command = constructBufferByTakeOwnership(raw_buffer); } else { @@ -155,16 +132,7 @@ int ModuleManagerLibraryHandler::aggregateStatus(Buffer &aggregated_status, new_status_raw_buffer = new_status.getStructBuffer(); } - int ret; - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction( - aeron_communication::AeronClient::ModuleFunctions::AGGREGATE_STATUS, - constructAeronMessage({¤t_status_raw_buffer, &new_status_raw_buffer}, device_type) - ); - ret = parseAeronResponse(raw_buffer, aeronClient_->getMessage()); - } else { - ret = aggregateStatus_(&raw_buffer, current_status_raw_buffer, new_status_raw_buffer, device_type); - } + const int ret = aggregateStatus_(&raw_buffer, current_status_raw_buffer, new_status_raw_buffer, device_type); if (ret == OK) { aggregated_status = constructBufferByTakeOwnership(raw_buffer); } else { @@ -187,16 +155,8 @@ int ModuleManagerLibraryHandler::aggregateError(Buffer &error_message, if (status.isAllocated()) { status_raw_buffer = status.getStructBuffer(); } - int ret; - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction( - aeron_communication::AeronClient::ModuleFunctions::AGGREGATE_ERROR, - constructAeronMessage({¤t_error_raw_buffer, &status_raw_buffer}, device_type) - ); - ret = parseAeronResponse(raw_buffer, aeronClient_->getMessage()); - } else { - ret = aggregateError_(&raw_buffer, current_error_raw_buffer, status_raw_buffer, device_type); - } + + const int ret = aggregateError_(&raw_buffer, current_error_raw_buffer, status_raw_buffer, device_type); if (ret == OK) { error_message = constructBufferByTakeOwnership(raw_buffer); } else { @@ -207,16 +167,7 @@ int ModuleManagerLibraryHandler::aggregateError(Buffer &error_message, int ModuleManagerLibraryHandler::generateFirstCommand(Buffer &default_command, unsigned int device_type) { struct ::buffer raw_buffer {}; - int ret; - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction( - aeron_communication::AeronClient::ModuleFunctions::GENERATE_FIRST_COMMAND, - constructAeronMessage({}, device_type) - ); - ret = parseAeronResponse(raw_buffer, aeronClient_->getMessage()); - } else { - ret = generateFirstCommand_(&raw_buffer, device_type); - } + const int ret = generateFirstCommand_(&raw_buffer, device_type); if (ret == OK) { default_command = constructBufferByTakeOwnership(raw_buffer); } else { @@ -230,13 +181,6 @@ int ModuleManagerLibraryHandler::statusDataValid(const Buffer &status, unsigned if (status.isAllocated()) { raw_buffer = status.getStructBuffer(); } - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction( - aeron_communication::AeronClient::ModuleFunctions::STATUS_DATA_VALID, - constructAeronMessage({&raw_buffer}, device_type) - ); - return std::stoi(std::string(aeronClient_->getMessage())); - } return statusDataValid_(raw_buffer, device_type); } @@ -245,33 +189,14 @@ int ModuleManagerLibraryHandler::commandDataValid(const Buffer &command, unsigne if (command.isAllocated()) { raw_buffer = command.getStructBuffer(); } - if (aeronClient_ != nullptr) { - aeronClient_->callModuleFunction( - aeron_communication::AeronClient::ModuleFunctions::COMMAND_DATA_VALID, - constructAeronMessage({&raw_buffer}, device_type) - ); - return std::stoi(std::string(aeronClient_->getMessage())); - } return commandDataValid_(raw_buffer, device_type); } int ModuleManagerLibraryHandler::allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const { - // if (aeronClient_ != nullptr) { - // aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::ALLOCATE, "TODO"); - // if (aeronClient_->getMessage() != "allocate") { - // throw std::runtime_error("AeronClient did not receive the expected message"); - // } - // } return allocate_(buffer_pointer, size_in_bytes); } void ModuleManagerLibraryHandler::deallocate(struct buffer *buffer) const { - // if (aeronClient_ != nullptr) { - // aeronClient_->callModuleFunction(aeron_communication::AeronClient::ModuleFunctions::DEALLOCATE, "TODO"); - // if (aeronClient_->getMessage() != "deallocate") { - // throw std::runtime_error("AeronClient did not receive the expected message"); - // } - // } deallocate_(buffer); } @@ -294,24 +219,4 @@ Buffer ModuleManagerLibraryHandler::constructBufferByTakeOwnership(struct ::buff return { buffer, deallocate_ }; } -std::string ModuleManagerLibraryHandler::constructAeronMessage(const std::vector &buffers, int deviceType) const { - std::string message; - for (const auto &buff : buffers) { - message += std::string(static_cast(buff->data), buff->size_in_bytes) + std::string(settings::Constants::SEPARATOR); - } - return message + std::to_string(deviceType); -} - -int ModuleManagerLibraryHandler::parseAeronResponse(struct ::buffer &raw_buffer, std::string_view response) const { - size_t sepPos = response.find(settings::Constants::SEPARATOR); - if (sepPos == std::string::npos) { - throw std::runtime_error("Invalid response format: " + std::string(response)); - } - bringauto::fleet_protocol::cxx::StringAsBuffer::createBufferAndCopyData( - &raw_buffer, - response.substr(sepPos + settings::Constants::SEPARATOR.size()) - ); - return std::stoi(std::string(response.substr(0, sepPos))); -} - -} +} \ No newline at end of file diff --git a/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp new file mode 100644 index 00000000..fbc930c6 --- /dev/null +++ b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp @@ -0,0 +1,207 @@ +#include +#include + +#include + +#include + + + +namespace bringauto::modules { + +async_function_execution::AsyncFunctionExecutor aeronClient { + async_function_execution::Config { + .isProducer = true, + .defaultTimeout = std::chrono::seconds(1) + }, + settings::FunctionIds::functionList +}; + +ModuleManagerLibraryHandlerAsync::ModuleManagerLibraryHandlerAsync() { + aeronClient.connect(); + deallocate_ = [this](struct buffer *buffer) { + this->deallocate(buffer); + }; +} + +ModuleManagerLibraryHandlerAsync::~ModuleManagerLibraryHandlerAsync() { + if (moduleBinaryPid_ != 0) { + ::kill(moduleBinaryPid_, SIGTERM); + } +} + +void ModuleManagerLibraryHandlerAsync::loadLibrary(const std::filesystem::path &path, const std::string &moduleBinaryPath) { + pid_t pid = ::fork(); + if (pid < 0) { + throw std::runtime_error { "Fork failed when trying to start module binary " + moduleBinaryPath }; + } + if (pid > 0) { + moduleBinaryPid_ = pid; + return; + } + char *args[4]; + args[0] = const_cast(moduleBinaryPath.c_str()); + args[1] = const_cast("-m"); + args[2] = const_cast(path.c_str()); + args[3] = nullptr; + if (::execv(moduleBinaryPath.c_str(), args) < 0) { + throw std::runtime_error { "Exec failed when trying to start module binary " + moduleBinaryPath }; + } +} + +int ModuleManagerLibraryHandlerAsync::getModuleNumber() const { + return aeronClient.callFunc(settings::FunctionIds::getModuleNumber); +} + +int ModuleManagerLibraryHandlerAsync::isDeviceTypeSupported(unsigned int device_type) const { + return aeronClient.callFunc(settings::FunctionIds::isDeviceTypeSupported, device_type); +} + +int ModuleManagerLibraryHandlerAsync::sendStatusCondition(const Buffer ¤t_status, + const Buffer &new_status, + unsigned int device_type) const { + settings::ConvertibleBuffer current_status_raw_buffer; + settings::ConvertibleBuffer new_status_raw_buffer; + + if (current_status.isAllocated()) { + current_status_raw_buffer = current_status.getStructBuffer(); + } + if (new_status.isAllocated()) { + new_status_raw_buffer = new_status.getStructBuffer(); + } + + return aeronClient.callFunc(settings::FunctionIds::sendStatusCondition, current_status_raw_buffer, new_status_raw_buffer, device_type); +} + +int ModuleManagerLibraryHandlerAsync::generateCommand(Buffer &generated_command, + const Buffer &new_status, + const Buffer ¤t_status, + const Buffer ¤t_command, unsigned int device_type) { + settings::ConvertibleBuffer new_status_raw_buffer; + settings::ConvertibleBuffer current_status_raw_buffer; + settings::ConvertibleBuffer current_command_raw_buffer; + + if (new_status.isAllocated()) { + new_status_raw_buffer = new_status.getStructBuffer(); + } + if (current_status.isAllocated()) { + current_status_raw_buffer = current_status.getStructBuffer(); + } + if (current_command.isAllocated()) { + current_command_raw_buffer = current_command.getStructBuffer(); + } + + auto ret = aeronClient.callFunc(settings::FunctionIds::generateCommand, + new_status_raw_buffer, + current_status_raw_buffer, + current_command_raw_buffer, + device_type); + + if (ret.returnCode == OK) { + generated_command = constructBufferByTakeOwnership(ret.buffer); + } else { + generated_command = constructBuffer(); + } + return ret.returnCode; +} + +int ModuleManagerLibraryHandlerAsync::aggregateStatus(Buffer &aggregated_status, + const Buffer ¤t_status, + const Buffer &new_status, unsigned int device_type) { + settings::ConvertibleBuffer current_status_raw_buffer; + settings::ConvertibleBuffer new_status_raw_buffer; + + if (current_status.isAllocated()) { + current_status_raw_buffer = current_status.getStructBuffer(); + } + if (new_status.isAllocated()) { + new_status_raw_buffer = new_status.getStructBuffer(); + } + + auto ret = aeronClient.callFunc(settings::FunctionIds::aggregateStatus, current_status_raw_buffer, new_status_raw_buffer, device_type); + if (ret.returnCode == OK) { + aggregated_status = constructBufferByTakeOwnership(ret.buffer); + } else { + aggregated_status = current_status; + } + return ret.returnCode; +} + +int ModuleManagerLibraryHandlerAsync::aggregateError(Buffer &error_message, + const Buffer ¤t_error_message, + const Buffer &status, unsigned int device_type) { + settings::ConvertibleBuffer current_error_raw_buffer; + settings::ConvertibleBuffer status_raw_buffer; + + if (current_error_message.isAllocated()) { + current_error_raw_buffer = current_error_message.getStructBuffer(); + } + if (status.isAllocated()) { + status_raw_buffer = status.getStructBuffer(); + } + + auto ret = aeronClient.callFunc(settings::FunctionIds::aggregateError, current_error_raw_buffer, status_raw_buffer, device_type); + if (ret.returnCode == OK) { + error_message = constructBufferByTakeOwnership(ret.buffer); + } else { + error_message = constructBuffer(); + } + return ret.returnCode; +} + +int ModuleManagerLibraryHandlerAsync::generateFirstCommand(Buffer &default_command, unsigned int device_type) { + auto ret = aeronClient.callFunc(settings::FunctionIds::generateFirstCommand, device_type); + if (ret.returnCode == OK) { + default_command = constructBufferByTakeOwnership(ret.buffer); + } else { + default_command = constructBuffer(); + } + return ret.returnCode; +} + +int ModuleManagerLibraryHandlerAsync::statusDataValid(const Buffer &status, unsigned int device_type) const { + settings::ConvertibleBuffer status_raw_buffer; + if (status.isAllocated()) { + status_raw_buffer = status.getStructBuffer(); + } + + return aeronClient.callFunc(settings::FunctionIds::statusDataValid, status_raw_buffer, device_type); +} + +int ModuleManagerLibraryHandlerAsync::commandDataValid(const Buffer &command, unsigned int device_type) const { + settings::ConvertibleBuffer command_raw_buffer; + if (command.isAllocated()) { + command_raw_buffer = command.getStructBuffer(); + } + + return aeronClient.callFunc(settings::FunctionIds::commandDataValid, command_raw_buffer, device_type); +} + +int ModuleManagerLibraryHandlerAsync::allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const { + return ::allocate(buffer_pointer, size_in_bytes); +} + +void ModuleManagerLibraryHandlerAsync::deallocate(struct buffer *buffer) const { + ::deallocate(buffer); +} + +Buffer ModuleManagerLibraryHandlerAsync::constructBuffer(std::size_t size) { + if (size == 0) { + return Buffer {}; + } + struct ::buffer buff {}; + buff.size_in_bytes = size; + if(allocate(&buff, size) != OK) { + throw std::bad_alloc {}; + } + return { buff, deallocate_ }; +} + +Buffer ModuleManagerLibraryHandlerAsync::constructBufferByTakeOwnership(struct ::buffer &buffer) { + if (buffer.data == nullptr) { + throw Buffer::BufferNotAllocated { "Buffer not allocated - cannot take ownership" }; + } + return { buffer, deallocate_ }; +} + +} diff --git a/source/bringauto/modules/memory_management/source/memory_management.cpp b/source/bringauto/modules/memory_management/source/memory_management.cpp index 834c9f5f..bb270e82 100644 --- a/source/bringauto/modules/memory_management/source/memory_management.cpp +++ b/source/bringauto/modules/memory_management/source/memory_management.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include int allocate(struct buffer *buffer_pointer, size_t size_in_bytes){ diff --git a/source/bringauto/settings/SettingsParser.cpp b/source/bringauto/settings/SettingsParser.cpp index a5885cb2..2ec7a6b7 100644 --- a/source/bringauto/settings/SettingsParser.cpp +++ b/source/bringauto/settings/SettingsParser.cpp @@ -98,6 +98,10 @@ bool SettingsParser::areSettingsCorrect() const { std::cerr << "No shared module library provided." << std::endl; isCorrect = false; } + if(!settings_->moduleBinaryPath.empty() && !std::filesystem::exists(settings_->moduleBinaryPath)) { + std::cerr << "Given module binary path (" << settings_->moduleBinaryPath << ") does not exist." << std::endl; + isCorrect = false; + } if(!std::regex_match(settings_->company, std::regex("^[a-z0-9_]+$"))) { std::cerr << "Company name (" << settings_->company << ") is not valid." << std::endl; isCorrect = false; @@ -153,6 +157,7 @@ void SettingsParser::fillModulePathsSettings(const nlohmann::json &file) const { for(auto &[key, val]: file[std::string(Constants::MODULE_PATHS)].items()) { settings_->modulePaths[stoi(key)] = val; } + settings_->moduleBinaryPath = file[std::string(Constants::MODULE_BINARY_PATH)]; } void SettingsParser::fillExternalConnectionSettings(const nlohmann::json &file) const { diff --git a/source/bringauto/structures/ModuleLibrary.cpp b/source/bringauto/structures/ModuleLibrary.cpp index 20f6dfd9..3b0a23d8 100644 --- a/source/bringauto/structures/ModuleLibrary.cpp +++ b/source/bringauto/structures/ModuleLibrary.cpp @@ -6,19 +6,16 @@ namespace bringauto::structures { -ModuleLibrary::ModuleLibrary() { - aeronClient = std::make_shared(); -} - ModuleLibrary::~ModuleLibrary() { std::for_each(statusAggregators.cbegin(), statusAggregators.cend(), [](auto &pair) { pair.second->destroy_status_aggregator(); }); } -void ModuleLibrary::loadLibraries(const std::unordered_map &libPaths) { +void ModuleLibrary::loadLibraries(const std::unordered_map &libPaths, const std::string &moduleBinaryPath) { for(auto const &[key, path]: libPaths) { - auto handler = std::make_shared(aeronClient); - handler->loadLibrary(path); + auto handler = std::make_shared(); //TODO select type from config + handler->loadLibrary(path, moduleBinaryPath); + std::this_thread::sleep_for(std::chrono::seconds(3)); // TODO Not sure how much time is needed. if(handler->getModuleNumber() != key) { settings::Logger::logError("Module number from shared library {} does not match the module number from config. Config: {}, binary: {}.", path, key, handler->getModuleNumber()); throw std::runtime_error {"Module numbers from config are not corresponding to binaries. Unable to continue. Fix configuration file."}; From 65fb0ff00e726b80a5ca5a5cf895b5b39481d772 Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Wed, 24 Sep 2025 15:46:42 +0200 Subject: [PATCH 16/55] async client as a package --- CMakeLists.txt | 7 +++---- cmake/Dependencies.cmake | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ca001a0..dc71956e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,9 +55,8 @@ FIND_PACKAGE(libbringauto_logger 2.0.0 REQUIRED) FIND_PACKAGE(fleet-protocol-interface 2.0.0 REQUIRED) FIND_PACKAGE(ZLIB 1.2.11 REQUIRED) FIND_PACKAGE(fleet-protocol-cxx-helpers-static 1.1.1 REQUIRED) - -#temp -ADD_SUBDIRECTORY(${CMAKE_CURRENT_LIST_DIR}/aeron-interface) +FIND_PACKAGE(aeron 1.48.6 REQUIRED) +FIND_PACKAGE(async-function-execution-shared 0.1.0 REQUIRED) FILE(GLOB_RECURSE source_files "source/*") ADD_LIBRARY(module-gateway-lib STATIC "${source_files}") @@ -77,7 +76,7 @@ TARGET_LINK_LIBRARIES(module-gateway-lib PUBLIC PahoMqttCpp::paho-mqttpp3 ZLIB::ZLIB fleet-protocol-cxx-helpers-static::fleet-protocol-cxx-helpers-static - async-function-execution-static + async-function-execution-shared::async-function-execution-shared memory_management ${CMAKE_DL_LIBS} ) diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 072f74e6..eaa8a4ac 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -10,6 +10,8 @@ BA_PACKAGE_LIBRARY(pahomqttc v1.3.9) BA_PACKAGE_LIBRARY(pahomqttcpp v1.3.2) BA_PACKAGE_LIBRARY(zlib v1.2.11 OUTPUT_PATH_VAR ZLIB_DIR) BA_PACKAGE_LIBRARY(fleet-protocol-cpp v1.1.1) +BA_PACKAGE_LIBRARY(aeron v1.48.6) +BA_PACKAGE_LIBRARY(async-function-execution v0.1.0) IF (BRINGAUTO_TESTS) BA_PACKAGE_LIBRARY(gtest v1.12.1) From af19874a1b0d3c44124cbf0b8c38fa3120a2c777 Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Fri, 17 Oct 2025 14:17:10 +0200 Subject: [PATCH 17/55] refactor of module library handler --- CMLibStorage.cmake | 2 +- CMakeLists.txt | 10 +- README.md | 15 +- cmake/Dependencies.cmake | 2 +- .../external_client/ErrorAggregator.hpp | 7 +- include/bringauto/modules/Buffer.hpp | 2 +- .../modules/IModuleManagerLibraryHandler.hpp | 81 ++++++++++ .../ModuleManagerLibraryHandlerAsync.hpp | 138 +++++++++++++++--- ...p => ModuleManagerLibraryHandlerLocal.hpp} | 35 ++--- .../bringauto/modules/StatusAggregator.hpp | 11 +- include/bringauto/settings/Constants.hpp | 107 -------------- .../bringauto/structures/ModuleLibrary.hpp | 8 +- main.cpp | 8 - resources/config/README.md | 2 + resources/config/default.json | 1 + resources/config/example.json | 1 + resources/config/for_docker.json | 1 + .../external_client/ErrorAggregator.cpp | 2 +- source/bringauto/modules/ModuleHandler.cpp | 2 +- .../ModuleManagerLibraryHandlerAsync.cpp | 88 +++++------ ...p => ModuleManagerLibraryHandlerLocal.cpp} | 52 +++---- .../modules/memory_management/CMakeLists.txt | 2 +- source/bringauto/settings/SettingsParser.cpp | 18 +-- source/bringauto/structures/ModuleLibrary.cpp | 12 +- test/CMakeLists.txt | 2 +- test/include/ErrorAggregatorTests.hpp | 4 +- test/include/ExternalConnectionTests.hpp | 1 - test/include/StatusAggregatorTests.hpp | 4 +- test/include/testing_utils/ConfigMock.hpp | 1 + test/source/ErrorAggregatorTests.cpp | 8 +- test/source/StatusAggregatorTests.cpp | 8 +- 31 files changed, 357 insertions(+), 278 deletions(-) create mode 100644 include/bringauto/modules/IModuleManagerLibraryHandler.hpp rename include/bringauto/modules/{ModuleManagerLibraryHandler.hpp => ModuleManagerLibraryHandlerLocal.hpp} (81%) rename source/bringauto/modules/{ModuleManagerLibraryHandler.cpp => ModuleManagerLibraryHandlerLocal.cpp} (74%) diff --git a/CMLibStorage.cmake b/CMLibStorage.cmake index 36796bad..c9e0b597 100644 --- a/CMLibStorage.cmake +++ b/CMLibStorage.cmake @@ -1,6 +1,6 @@ FIND_PACKAGE(CMLIB REQUIRED COMPONENTS CMCONF) -CMCONF_INIT_SYSTEM(FLEET_PROTOCOL) +CMCONF_INIT_SYSTEM(EXAMPLE) SET(STORAGE_LIST DEP) diff --git a/CMakeLists.txt b/CMakeLists.txt index dc71956e..77469904 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ FIND_PACKAGE(CMLIB REQUIRED ) -SET(BRINGAUTO_MODULE_GATEWAY_VERSION 1.3.5) +SET(BRINGAUTO_MODULE_GATEWAY_VERSION 1.4.0) SET(MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY "DEBUG" CACHE STRING "Minimum logger verbosity level for module-gateway") @@ -16,7 +16,7 @@ CMDEF_COMPILE_DEFINITIONS( "MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY=\"${MODULE_GATEWAY_MINIMUM_LOGGER_VERBOSITY}\"" ) SET(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMDEF_LIBRARY_INSTALL_DIR}") -SET(CMAKE_CXX_STANDARD 20) +SET(CMAKE_CXX_STANDARD 23) INCLUDE(CheckPIESupported) CHECK_PIE_SUPPORTED() @@ -48,7 +48,7 @@ SET(Protobuf_USE_STATIC_LIBS ON) FIND_PACKAGE(Boost 1.74 REQUIRED CONFIG) FIND_PACKAGE(Protobuf 3.21.12 REQUIRED) FIND_PACKAGE(cxxopts 3.1.1 REQUIRED) -FIND_PACKAGE(nlohmann_json 3.2.0 REQUIRED) +FIND_PACKAGE(nlohmann_json 3.10.5 REQUIRED) FIND_PACKAGE(PahoMqttCpp REQUIRED) FIND_PACKAGE(eclipse-paho-mqtt-c REQUIRED) FIND_PACKAGE(libbringauto_logger 2.0.0 REQUIRED) @@ -56,7 +56,7 @@ FIND_PACKAGE(fleet-protocol-interface 2.0.0 REQUIRED) FIND_PACKAGE(ZLIB 1.2.11 REQUIRED) FIND_PACKAGE(fleet-protocol-cxx-helpers-static 1.1.1 REQUIRED) FIND_PACKAGE(aeron 1.48.6 REQUIRED) -FIND_PACKAGE(async-function-execution-shared 0.1.0 REQUIRED) +FIND_PACKAGE(async-function-execution-shared 1.0.0 REQUIRED) FILE(GLOB_RECURSE source_files "source/*") ADD_LIBRARY(module-gateway-lib STATIC "${source_files}") @@ -101,7 +101,7 @@ TARGET_LINK_LIBRARIES(module-gateway-app PUBLIC module-gateway-lib) IF(BRINGAUTO_TESTS) ENABLE_TESTING() - #INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt) + INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt) INCLUDE(CTest) ENDIF(BRINGAUTO_TESTS) diff --git a/README.md b/README.md index be8a1b18..72904256 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,16 @@ connection is broken and as soon as the connection is up, then error aggregated - [protobuf](https://github.com/protocolbuffers/protobuf/tree/main/src) >= v3.21.12 - [cxxopts](https://github.com/jarro2783/cxxopts) >= v3.1.1 -- [boost](https://github.com/boostorg/boost) >= v1.74.0 -- [nlohmann-json](https://github.com/nlohmann/json) >= v3.2.0 -- [ba-logger](https://github.com/bringauto/ba-logger) >= v1.2.0 +- [boost](https://github.com/boostorg/boost) >= v1.86.0 +- [nlohmann-json](https://github.com/nlohmann/json) >= v3.10.5/ +- [pahomqtt](https://github.com/eclipse-paho/paho.mqtt.c) >= v1.3.9 +- [pahomqttcpp](https://github.com/eclipse-paho/paho.mqtt.cpp) >= v1.3.2 +- [zlib](https://github.com/madler/zlib) >= v1.2.11 +- [ba-logger](https://github.com/bringauto/ba-logger) >= v2.0.0 +- [fleet-protocol-interface](https://github.com/bringauto/fleet-protocol) >= v2.0.0 +- [fleet-protocol-cpp](https://github.com/bringauto/fleet-protocol-cpp) >= v1.1.1 +- [aeron](https://github.com/aeron-io/aeron) >= v1.48.6 +- [async-function-execution](https://github.com/bringauto/async-function-execution) >= 0.1.0 - g++ >= 10 or other compiler with c++20 support ## Build @@ -50,7 +57,7 @@ make ### Arguments -* required arguments: +* Required arguments: * `-c | --config-path=`path to json configuration file ([Configs Readme](./configs/README.md)) * All arguments: * `-h | --help` print help diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index eaa8a4ac..e57bd000 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -11,7 +11,7 @@ BA_PACKAGE_LIBRARY(pahomqttcpp v1.3.2) BA_PACKAGE_LIBRARY(zlib v1.2.11 OUTPUT_PATH_VAR ZLIB_DIR) BA_PACKAGE_LIBRARY(fleet-protocol-cpp v1.1.1) BA_PACKAGE_LIBRARY(aeron v1.48.6) -BA_PACKAGE_LIBRARY(async-function-execution v0.1.0) +BA_PACKAGE_LIBRARY(async-function-execution v1.0.0) IF (BRINGAUTO_TESTS) BA_PACKAGE_LIBRARY(gtest v1.12.1) diff --git a/include/bringauto/external_client/ErrorAggregator.hpp b/include/bringauto/external_client/ErrorAggregator.hpp index e6724f49..e72fd82c 100644 --- a/include/bringauto/external_client/ErrorAggregator.hpp +++ b/include/bringauto/external_client/ErrorAggregator.hpp @@ -1,7 +1,6 @@ #pragma once -#include -#include +#include #include #include @@ -26,7 +25,7 @@ class ErrorAggregator { * @return OK if initialization was successful * @return NOT_OK if an error occurred */ - int init_error_aggregator(const std::shared_ptr &library); //TODO select type from config + int init_error_aggregator(const std::shared_ptr &library); /** * @short Clean up. @@ -112,7 +111,7 @@ class ErrorAggregator { modules::Buffer lastStatus {}; }; - std::shared_ptr module_ {}; //TODO select type from config + std::shared_ptr module_ {}; /** * @brief Map of devices states, key is device identification converted to string diff --git a/include/bringauto/modules/Buffer.hpp b/include/bringauto/modules/Buffer.hpp index a616e9b2..235bf26e 100644 --- a/include/bringauto/modules/Buffer.hpp +++ b/include/bringauto/modules/Buffer.hpp @@ -18,7 +18,7 @@ namespace bringauto::modules { */ struct Buffer final { - friend class ModuleManagerLibraryHandler; + friend class ModuleManagerLibraryHandlerLocal; friend class ModuleManagerLibraryHandlerAsync; Buffer() = default; diff --git a/include/bringauto/modules/IModuleManagerLibraryHandler.hpp b/include/bringauto/modules/IModuleManagerLibraryHandler.hpp new file mode 100644 index 00000000..75d51ec5 --- /dev/null +++ b/include/bringauto/modules/IModuleManagerLibraryHandler.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include + +#include +#include + + + +namespace bringauto::modules { + +/** + * @brief Class used to load and handle library created by module maintainer + */ +class IModuleManagerLibraryHandler { +public: + explicit IModuleManagerLibraryHandler() = default; + + virtual ~IModuleManagerLibraryHandler() = default; + + /** + * @brief Load library created by a module maintainer + * + * @param path path to the library + */ + virtual void loadLibrary(const std::filesystem::path &path) = 0; + + virtual int getModuleNumber() const = 0; + + virtual int isDeviceTypeSupported(unsigned int device_type) = 0; + + virtual int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) const = 0; + + /** + * @short After executing the respective module function, an error might be thrown when allocating the buffer. + * + * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h + */ + virtual int generateCommand(Buffer &generated_command, const Buffer &new_status, + const Buffer ¤t_status, const Buffer ¤t_command, + unsigned int device_type) = 0; + + /** + * @short After executing the respective module function, an error might be thrown when allocating the buffer. + * + * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h + */ + virtual int aggregateStatus(Buffer &aggregated_status, const Buffer ¤t_status, + const Buffer &new_status, unsigned int device_type) = 0; + + /** + * @short After executing the respective module function, an error might be thrown when allocating the buffer. + * + * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h + */ + virtual int aggregateError(Buffer &error_message, const Buffer ¤t_error_message, const Buffer &status, + unsigned int device_type) = 0; + + /** + * @short After executing the respective module function, an error might be thrown when allocating the buffer. + * + * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h + */ + virtual int generateFirstCommand(Buffer &default_command, unsigned int device_type) = 0; + + virtual int statusDataValid(const Buffer &status, unsigned int device_type) const = 0; + + virtual int commandDataValid(const Buffer &command, unsigned int device_type) const = 0; + + /** + * @brief Constructs a buffer with the given size + * + * @param size size of the buffer + * @return a new Buffer object + */ + virtual Buffer constructBuffer(std::size_t size = 0) = 0; +}; + +} diff --git a/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp b/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp index 92acf296..d1a0be1b 100644 --- a/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp +++ b/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp @@ -1,38 +1,130 @@ #pragma once -#include +#include -#include +#include +#include + +#include -#include -#include namespace bringauto::modules { +struct ConvertibleBufferReturn final { + int returnCode {}; + struct ::buffer buffer {}; + ConvertibleBufferReturn() = default; + ConvertibleBufferReturn(int code, struct ::buffer buff) : returnCode(code), buffer(buff) {} + + std::span serialize() const { + size_t total_size = sizeof(int) + buffer.size_in_bytes; + uint8_t* data = new uint8_t[total_size]; + std::memcpy(data, &returnCode, sizeof(int)); + std::memcpy(data + sizeof(int), buffer.data, buffer.size_in_bytes); + return {data, total_size}; + } + void deserialize(std::span bytes) { + auto size = bytes.size(); + if (size < sizeof(int)) return; + std::memcpy(&returnCode, bytes.data(), sizeof(int)); + size -= sizeof(int); + allocate(&buffer, size); + std::memcpy(buffer.data, bytes.data() + sizeof(int), size); + buffer.size_in_bytes = size; + } +}; + +struct ConvertibleBuffer final { + struct ::buffer buffer {}; + ConvertibleBuffer() = default; + ConvertibleBuffer(struct ::buffer buff) : buffer(buff) {} + + std::span serialize() const { + return std::span {reinterpret_cast(buffer.data), buffer.size_in_bytes}; + } + void deserialize(std::span bytes) { + buffer.data = const_cast(bytes.data()); + buffer.size_in_bytes = bytes.size(); + } +}; + +inline static const async_function_execution::FunctionDefinition getModuleNumberAsync { + async_function_execution::FunctionId { 0 }, + async_function_execution::Return { int {} }, + async_function_execution::Arguments {} +}; + +inline static const async_function_execution::FunctionDefinition isDeviceTypeSupportedAsync { + async_function_execution::FunctionId { 1 }, + async_function_execution::Return { int {} }, + async_function_execution::Arguments { uint32_t {} } +}; + +inline static const async_function_execution::FunctionDefinition sendStatusConditionAsync { + async_function_execution::FunctionId { 2 }, + async_function_execution::Return { int {} }, + async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } +}; + +inline static const async_function_execution::FunctionDefinition generateCommandAsync { + async_function_execution::FunctionId { 3 }, + async_function_execution::Return { ConvertibleBufferReturn {} }, + async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } +}; + +inline static const async_function_execution::FunctionDefinition aggregateStatusAsync { + async_function_execution::FunctionId { 4 }, + async_function_execution::Return { ConvertibleBufferReturn {} }, + async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } +}; + +inline static const async_function_execution::FunctionDefinition aggregateErrorAsync { + async_function_execution::FunctionId { 5 }, + async_function_execution::Return { ConvertibleBufferReturn {} }, + async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } +}; + +inline static const async_function_execution::FunctionDefinition generateFirstCommandAsync { + async_function_execution::FunctionId { 6 }, + async_function_execution::Return { ConvertibleBufferReturn {} }, + async_function_execution::Arguments { uint32_t {} } +}; + +inline static const async_function_execution::FunctionDefinition statusDataValidAsync { + async_function_execution::FunctionId { 7 }, + async_function_execution::Return { int {} }, + async_function_execution::Arguments { ConvertibleBuffer {}, uint32_t {} } +}; + +inline static const async_function_execution::FunctionDefinition commandDataValidAsync { + async_function_execution::FunctionId { 8 }, + async_function_execution::Return { int {} }, + async_function_execution::Arguments { ConvertibleBuffer {}, uint32_t {} } +}; + /** * @brief Class used to load and handle library created by module maintainer */ -class ModuleManagerLibraryHandlerAsync { +class ModuleManagerLibraryHandlerAsync : public IModuleManagerLibraryHandler { public: - ModuleManagerLibraryHandlerAsync(); + explicit ModuleManagerLibraryHandlerAsync(const std::string &moduleBinaryPath); - ~ModuleManagerLibraryHandlerAsync(); + ~ModuleManagerLibraryHandlerAsync() override; /** * @brief Load library created by a module maintainer * * @param path path to the library - * @param moduleBinaryPath path to the module binary */ - void loadLibrary(const std::filesystem::path &path, const std::string &moduleBinaryPath); + void loadLibrary(const std::filesystem::path &path) override; - int getModuleNumber() const; + int getModuleNumber() const override; - int isDeviceTypeSupported(unsigned int device_type) const; + int isDeviceTypeSupported(unsigned int device_type) override; - int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) const; + int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) const override; /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. @@ -41,7 +133,7 @@ class ModuleManagerLibraryHandlerAsync { */ int generateCommand(Buffer &generated_command, const Buffer &new_status, const Buffer ¤t_status, const Buffer ¤t_command, - unsigned int device_type); + unsigned int device_type) override; /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. @@ -49,7 +141,7 @@ class ModuleManagerLibraryHandlerAsync { * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h */ int aggregateStatus(Buffer &aggregated_status, const Buffer ¤t_status, - const Buffer &new_status, unsigned int device_type); + const Buffer &new_status, unsigned int device_type) override; /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. @@ -57,18 +149,18 @@ class ModuleManagerLibraryHandlerAsync { * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h */ int aggregateError(Buffer &error_message, const Buffer ¤t_error_message, const Buffer &status, - unsigned int device_type); + unsigned int device_type) override; /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. * * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h */ - int generateFirstCommand(Buffer &default_command, unsigned int device_type); + int generateFirstCommand(Buffer &default_command, unsigned int device_type) override; - int statusDataValid(const Buffer &status, unsigned int device_type) const; + int statusDataValid(const Buffer &status, unsigned int device_type) const override; - int commandDataValid(const Buffer &command, unsigned int device_type) const; + int commandDataValid(const Buffer &command, unsigned int device_type) const override; /** * @brief Constructs a buffer with the given size @@ -76,7 +168,7 @@ class ModuleManagerLibraryHandlerAsync { * @param size size of the buffer * @return a new Buffer object */ - Buffer constructBuffer(std::size_t size = 0); + Buffer constructBuffer(std::size_t size = 0) override; private: @@ -94,8 +186,12 @@ class ModuleManagerLibraryHandlerAsync { std::function deallocate_ {}; - /// Process id of the module binary - pid_t moduleBinaryPid_ {}; + /// Path to the module binary + std::string moduleBinaryPath_ {}; + /// Process of the module binary + boost::process::child moduleBinaryProcess_ {}; + /// TODO find a way to not need this + std::mutex tmpMutex_ {}; }; } diff --git a/include/bringauto/modules/ModuleManagerLibraryHandler.hpp b/include/bringauto/modules/ModuleManagerLibraryHandlerLocal.hpp similarity index 81% rename from include/bringauto/modules/ModuleManagerLibraryHandler.hpp rename to include/bringauto/modules/ModuleManagerLibraryHandlerLocal.hpp index 8d12252e..a1fd601d 100644 --- a/include/bringauto/modules/ModuleManagerLibraryHandler.hpp +++ b/include/bringauto/modules/ModuleManagerLibraryHandlerLocal.hpp @@ -1,11 +1,6 @@ #pragma once -#include - -#include - -#include -#include +#include @@ -14,24 +9,24 @@ namespace bringauto::modules { /** * @brief Class used to load and handle library created by module maintainer */ -class ModuleManagerLibraryHandler { +class ModuleManagerLibraryHandlerLocal : public IModuleManagerLibraryHandler { public: - ModuleManagerLibraryHandler() = default; + explicit ModuleManagerLibraryHandlerLocal() = default; - ~ModuleManagerLibraryHandler(); + ~ModuleManagerLibraryHandlerLocal() override; /** * @brief Load library created by a module maintainer * * @param path path to the library */ - void loadLibrary(const std::filesystem::path &path); + void loadLibrary(const std::filesystem::path &path) override; - int getModuleNumber() const; + int getModuleNumber() const override; - int isDeviceTypeSupported(unsigned int device_type) const; + int isDeviceTypeSupported(unsigned int device_type) override; - int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) const; + int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) const override; /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. @@ -40,7 +35,7 @@ class ModuleManagerLibraryHandler { */ int generateCommand(Buffer &generated_command, const Buffer &new_status, const Buffer ¤t_status, const Buffer ¤t_command, - unsigned int device_type); + unsigned int device_type) override; /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. @@ -48,7 +43,7 @@ class ModuleManagerLibraryHandler { * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h */ int aggregateStatus(Buffer &aggregated_status, const Buffer ¤t_status, - const Buffer &new_status, unsigned int device_type); + const Buffer &new_status, unsigned int device_type) override; /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. @@ -56,18 +51,18 @@ class ModuleManagerLibraryHandler { * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h */ int aggregateError(Buffer &error_message, const Buffer ¤t_error_message, const Buffer &status, - unsigned int device_type); + unsigned int device_type) override; /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. * * @see fleet-protocol/lib/module_maintainer/module_gateway/include/module_manager.h */ - int generateFirstCommand(Buffer &default_command, unsigned int device_type); + int generateFirstCommand(Buffer &default_command, unsigned int device_type) override; - int statusDataValid(const Buffer &status, unsigned int device_type) const; + int statusDataValid(const Buffer &status, unsigned int device_type) const override; - int commandDataValid(const Buffer &command, unsigned int device_type) const; + int commandDataValid(const Buffer &command, unsigned int device_type) const override; /** * @brief Constructs a buffer with the given size @@ -75,7 +70,7 @@ class ModuleManagerLibraryHandler { * @param size size of the buffer * @return a new Buffer object */ - Buffer constructBuffer(std::size_t size = 0); + Buffer constructBuffer(std::size_t size = 0) override; private: diff --git a/include/bringauto/modules/StatusAggregator.hpp b/include/bringauto/modules/StatusAggregator.hpp index 4cf426fd..33580bf2 100644 --- a/include/bringauto/modules/StatusAggregator.hpp +++ b/include/bringauto/modules/StatusAggregator.hpp @@ -1,7 +1,6 @@ #pragma once -#include -#include +#include #include #include @@ -21,9 +20,9 @@ class StatusAggregator { public: explicit StatusAggregator(const std::shared_ptr &context, - const std::shared_ptr &libraryHandler): context_ { context }, //TODO select type from config - module_ { - libraryHandler } {}; + const std::shared_ptr &libraryHandler): context_ { context }, + module_ { + libraryHandler } {}; StatusAggregator() = default; @@ -187,7 +186,7 @@ class StatusAggregator { std::shared_ptr context_ {}; - const std::shared_ptr module_ {}; //TODO select type from config + const std::shared_ptr module_ {}; /** * @brief Map of devices states, key is device identification diff --git a/include/bringauto/settings/Constants.hpp b/include/bringauto/settings/Constants.hpp index 34dd5fb7..28a0470d 100644 --- a/include/bringauto/settings/Constants.hpp +++ b/include/bringauto/settings/Constants.hpp @@ -182,111 +182,4 @@ class Constants { inline static constexpr std::string_view SEPARATOR { ":::" }; }; -struct ConvertibleBufferReturn final { - int returnCode { 0 }; //TODO temporary, need to serialize return code aswell - struct ::buffer buffer {}; - ConvertibleBufferReturn() = default; - ConvertibleBufferReturn(int code, struct ::buffer buff) : returnCode(code), buffer(buff) {} - ~ConvertibleBufferReturn() { - buffer.data = nullptr; - } - - std::span serialize() const { - return std::span {reinterpret_cast(buffer.data), buffer.size_in_bytes}; - } - void deserialize(std::span bytes) { - if(buffer.data != nullptr) { - delete[] buffer.data; - } - buffer.size_in_bytes = bytes.size(); - buffer.data = new uint8_t[buffer.size_in_bytes]; - std::memcpy(buffer.data, bytes.data(), buffer.size_in_bytes); - } -}; - -struct ConvertibleBuffer final { - struct ::buffer buffer {}; - ConvertibleBuffer() = default; - ConvertibleBuffer(struct ::buffer buff) : buffer(buff) {} - - std::span serialize() const { - return std::span {reinterpret_cast(buffer.data), buffer.size_in_bytes}; - } - void deserialize(std::span bytes) { - buffer.data = const_cast(bytes.data()); - buffer.size_in_bytes = bytes.size(); - } -}; - -class FunctionIds { -public: - inline static const async_function_execution::FunctionDefinition getModuleNumber { - async_function_execution::FunctionId { 0 }, - async_function_execution::Return { int {} }, - async_function_execution::Arguments {} - }; - - inline static const async_function_execution::FunctionDefinition isDeviceTypeSupported { - async_function_execution::FunctionId { 1 }, - async_function_execution::Return { int {} }, - async_function_execution::Arguments { uint32_t {} } - }; - - inline static const async_function_execution::FunctionDefinition sendStatusCondition { - async_function_execution::FunctionId { 2 }, - async_function_execution::Return { int {} }, - async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } - }; - - inline static const async_function_execution::FunctionDefinition generateCommand { - async_function_execution::FunctionId { 3 }, - async_function_execution::Return { ConvertibleBufferReturn {} }, - async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } - }; - - inline static const async_function_execution::FunctionDefinition aggregateStatus { - async_function_execution::FunctionId { 4 }, - async_function_execution::Return { ConvertibleBufferReturn {} }, - async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } - }; - - inline static const async_function_execution::FunctionDefinition aggregateError { - async_function_execution::FunctionId { 5 }, - async_function_execution::Return { ConvertibleBufferReturn {} }, - async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } - }; - - inline static const async_function_execution::FunctionDefinition generateFirstCommand { - async_function_execution::FunctionId { 6 }, - async_function_execution::Return { ConvertibleBufferReturn {} }, - async_function_execution::Arguments { uint32_t {} } - }; - - inline static const async_function_execution::FunctionDefinition statusDataValid { - async_function_execution::FunctionId { 7 }, - async_function_execution::Return { int {} }, - async_function_execution::Arguments { ConvertibleBuffer {}, uint32_t {} } - }; - - inline static const async_function_execution::FunctionDefinition commandDataValid { - async_function_execution::FunctionId { 8 }, - async_function_execution::Return { int {} }, - async_function_execution::Arguments { ConvertibleBuffer {}, uint32_t {} } - }; - - inline static const async_function_execution::FunctionList functionList { - std::tuple { - getModuleNumber, - isDeviceTypeSupported, - sendStatusCondition, - generateCommand, - aggregateStatus, - aggregateError, - generateFirstCommand, - statusDataValid, - commandDataValid - } - }; -}; - } diff --git a/include/bringauto/structures/ModuleLibrary.hpp b/include/bringauto/structures/ModuleLibrary.hpp index 124e84c0..7cb13601 100644 --- a/include/bringauto/structures/ModuleLibrary.hpp +++ b/include/bringauto/structures/ModuleLibrary.hpp @@ -1,7 +1,6 @@ #pragma once -#include -#include +#include #include #include @@ -22,8 +21,9 @@ struct ModuleLibrary { * @brief Load libraries from paths * * @param libPaths paths to the libraries + * @param moduleBinaryPath path to module binary for async function execution over shared memory */ - void loadLibraries(const std::unordered_map &libPaths, const std::string &moduleBinaryPath); + void loadLibraries(const std::unordered_map &libPaths, const std::string &moduleBinaryPath = ""); /** * @brief Initialize status aggregators with context @@ -32,7 +32,7 @@ struct ModuleLibrary { */ void initStatusAggregators(std::shared_ptr &context); /// Map of module handlers, key is module id - std::unordered_map> moduleLibraryHandlers {}; //TODO select type from config + std::unordered_map> moduleLibraryHandlers {}; /// Map of status aggregators, key is module id std::unordered_map> statusAggregators {}; }; diff --git a/main.cpp b/main.cpp index a2204717..46e5fe44 100644 --- a/main.cpp +++ b/main.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -69,11 +68,6 @@ int main(int argc, char **argv) { return 1; } - // bringauto::aeron_interface::AeronDriver aeronDriver {}; - // std::jthread aeronDriverThread([&aeronDriver]() { aeronDriver.run(); }); - // baset::Logger::logInfo("Aeron Driver starting..."); - // std::this_thread::sleep_for(std::chrono::seconds(3)); //TODO Not sure how much time is needed. - bas::ModuleLibrary moduleLibrary {}; try { @@ -107,12 +101,10 @@ int main(int argc, char **argv) { context->ioContext.stop(); } - // aeronDriver.stop(); contextThread2.join(); contextThread1.join(); externalClientThread.join(); moduleHandlerThread.join(); - // aeronDriverThread.join(); internalServer.destroy(); moduleHandler.destroy(); diff --git a/resources/config/README.md b/resources/config/README.md index 8451e279..2889f140 100644 --- a/resources/config/README.md +++ b/resources/config/README.md @@ -22,6 +22,8 @@ Note: at least one logging sink needs to be used ### module-paths: * key : number that corresponds to the module being loaded * value : path to the module shared library file +### module-binary-path: + - path to the module binary for async function execution over shared memory. If none is provided, the module will be loaded as a shared library ### external-connection: * company : company name used as identification in external connection (string) * vehicle-name : vehicle name used as identification in external connection (string) diff --git a/resources/config/default.json b/resources/config/default.json index 3fc59eb0..0402e352 100644 --- a/resources/config/default.json +++ b/resources/config/default.json @@ -14,6 +14,7 @@ "port": 8888 }, "module-paths": { }, + "module-binary-path": "", "external-connection" : { "company": "", "vehicle-name": "", diff --git a/resources/config/example.json b/resources/config/example.json index 3bb8d46f..2a5bc024 100644 --- a/resources/config/example.json +++ b/resources/config/example.json @@ -20,6 +20,7 @@ "1000": "./libmission-module-gateway-shared.so" }, + "module-binary-path": "", "external-connection" : { "company" : "bringauto", "vehicle-name" : "virtual_vehicle", diff --git a/resources/config/for_docker.json b/resources/config/for_docker.json index c28bea57..68cbcb4d 100644 --- a/resources/config/for_docker.json +++ b/resources/config/for_docker.json @@ -18,6 +18,7 @@ "2": "/home/bringauto/modules/io_module/lib/libio-module-gateway-shared.so", "3": "/home/bringauto/modules/transparent_module/lib/libtransparent-module-gateway-shared.so" }, + "module-binary-path": "", "external-connection" : { "company" : "bringauto", "vehicle-name" : "virtual_vehicle", diff --git a/source/bringauto/external_client/ErrorAggregator.cpp b/source/bringauto/external_client/ErrorAggregator.cpp index 89890616..4f83b6c0 100644 --- a/source/bringauto/external_client/ErrorAggregator.cpp +++ b/source/bringauto/external_client/ErrorAggregator.cpp @@ -8,7 +8,7 @@ namespace bringauto::external_client { -int ErrorAggregator::init_error_aggregator(const std::shared_ptr &library) { //TODO select type from config +int ErrorAggregator::init_error_aggregator(const std::shared_ptr &library) { module_ = library; return OK; } diff --git a/source/bringauto/modules/ModuleHandler.cpp b/source/bringauto/modules/ModuleHandler.cpp index 5f1bd819..5059cdb5 100644 --- a/source/bringauto/modules/ModuleHandler.cpp +++ b/source/bringauto/modules/ModuleHandler.cpp @@ -194,7 +194,7 @@ void ModuleHandler::handleStatus(const ip::DeviceStatus &status) const { settings::Logger::logWarning("Add status to aggregator failed with return code: {}", addStatusToAggregatorRc); return; } - + Buffer commandBuffer {}; int getCommandRc = statusAggregator->get_command(statusBuffer, deviceId, commandBuffer); if(getCommandRc == OK) { diff --git a/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp index fbc930c6..00d3a5f6 100644 --- a/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp +++ b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp @@ -14,10 +14,21 @@ async_function_execution::AsyncFunctionExecutor aeronClient { .isProducer = true, .defaultTimeout = std::chrono::seconds(1) }, - settings::FunctionIds::functionList + async_function_execution::FunctionList { + getModuleNumberAsync, + isDeviceTypeSupportedAsync, + sendStatusConditionAsync, + generateCommandAsync, + aggregateStatusAsync, + aggregateErrorAsync, + generateFirstCommandAsync, + statusDataValidAsync, + commandDataValidAsync + } }; -ModuleManagerLibraryHandlerAsync::ModuleManagerLibraryHandlerAsync() { +ModuleManagerLibraryHandlerAsync::ModuleManagerLibraryHandlerAsync(const std::string &moduleBinaryPath) : + moduleBinaryPath_ { moduleBinaryPath } { aeronClient.connect(); deallocate_ = [this](struct buffer *buffer) { this->deallocate(buffer); @@ -25,43 +36,34 @@ ModuleManagerLibraryHandlerAsync::ModuleManagerLibraryHandlerAsync() { } ModuleManagerLibraryHandlerAsync::~ModuleManagerLibraryHandlerAsync() { - if (moduleBinaryPid_ != 0) { - ::kill(moduleBinaryPid_, SIGTERM); + if (moduleBinaryProcess_.valid()) { + ::kill(moduleBinaryProcess_.id(), SIGTERM); + moduleBinaryProcess_.wait(); } } -void ModuleManagerLibraryHandlerAsync::loadLibrary(const std::filesystem::path &path, const std::string &moduleBinaryPath) { - pid_t pid = ::fork(); - if (pid < 0) { - throw std::runtime_error { "Fork failed when trying to start module binary " + moduleBinaryPath }; - } - if (pid > 0) { - moduleBinaryPid_ = pid; - return; - } - char *args[4]; - args[0] = const_cast(moduleBinaryPath.c_str()); - args[1] = const_cast("-m"); - args[2] = const_cast(path.c_str()); - args[3] = nullptr; - if (::execv(moduleBinaryPath.c_str(), args) < 0) { - throw std::runtime_error { "Exec failed when trying to start module binary " + moduleBinaryPath }; +void ModuleManagerLibraryHandlerAsync::loadLibrary(const std::filesystem::path &path) { + moduleBinaryProcess_ = boost::process::child { moduleBinaryPath_, "-m", path.string() }; + if (!moduleBinaryProcess_.valid()) { + throw std::runtime_error { "Failed to start module binary " + moduleBinaryPath_ }; } + std::this_thread::sleep_for(std::chrono::seconds(1)); // TODO Not sure how much time is needed. } int ModuleManagerLibraryHandlerAsync::getModuleNumber() const { - return aeronClient.callFunc(settings::FunctionIds::getModuleNumber); + return aeronClient.callFunc(getModuleNumberAsync).value(); } -int ModuleManagerLibraryHandlerAsync::isDeviceTypeSupported(unsigned int device_type) const { - return aeronClient.callFunc(settings::FunctionIds::isDeviceTypeSupported, device_type); +int ModuleManagerLibraryHandlerAsync::isDeviceTypeSupported(unsigned int device_type) { + std::lock_guard lock { tmpMutex_ }; + return aeronClient.callFunc(isDeviceTypeSupportedAsync, device_type).value(); } int ModuleManagerLibraryHandlerAsync::sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) const { - settings::ConvertibleBuffer current_status_raw_buffer; - settings::ConvertibleBuffer new_status_raw_buffer; + ConvertibleBuffer current_status_raw_buffer; + ConvertibleBuffer new_status_raw_buffer; if (current_status.isAllocated()) { current_status_raw_buffer = current_status.getStructBuffer(); @@ -70,16 +72,16 @@ int ModuleManagerLibraryHandlerAsync::sendStatusCondition(const Buffer ¤t_ new_status_raw_buffer = new_status.getStructBuffer(); } - return aeronClient.callFunc(settings::FunctionIds::sendStatusCondition, current_status_raw_buffer, new_status_raw_buffer, device_type); + return aeronClient.callFunc(sendStatusConditionAsync, current_status_raw_buffer, new_status_raw_buffer, device_type).value(); } int ModuleManagerLibraryHandlerAsync::generateCommand(Buffer &generated_command, const Buffer &new_status, const Buffer ¤t_status, const Buffer ¤t_command, unsigned int device_type) { - settings::ConvertibleBuffer new_status_raw_buffer; - settings::ConvertibleBuffer current_status_raw_buffer; - settings::ConvertibleBuffer current_command_raw_buffer; + ConvertibleBuffer new_status_raw_buffer; + ConvertibleBuffer current_status_raw_buffer; + ConvertibleBuffer current_command_raw_buffer; if (new_status.isAllocated()) { new_status_raw_buffer = new_status.getStructBuffer(); @@ -91,11 +93,11 @@ int ModuleManagerLibraryHandlerAsync::generateCommand(Buffer &generated_command, current_command_raw_buffer = current_command.getStructBuffer(); } - auto ret = aeronClient.callFunc(settings::FunctionIds::generateCommand, + auto ret = aeronClient.callFunc(generateCommandAsync, new_status_raw_buffer, current_status_raw_buffer, current_command_raw_buffer, - device_type); + device_type).value(); if (ret.returnCode == OK) { generated_command = constructBufferByTakeOwnership(ret.buffer); @@ -108,8 +110,8 @@ int ModuleManagerLibraryHandlerAsync::generateCommand(Buffer &generated_command, int ModuleManagerLibraryHandlerAsync::aggregateStatus(Buffer &aggregated_status, const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) { - settings::ConvertibleBuffer current_status_raw_buffer; - settings::ConvertibleBuffer new_status_raw_buffer; + ConvertibleBuffer current_status_raw_buffer; + ConvertibleBuffer new_status_raw_buffer; if (current_status.isAllocated()) { current_status_raw_buffer = current_status.getStructBuffer(); @@ -118,10 +120,12 @@ int ModuleManagerLibraryHandlerAsync::aggregateStatus(Buffer &aggregated_status, new_status_raw_buffer = new_status.getStructBuffer(); } - auto ret = aeronClient.callFunc(settings::FunctionIds::aggregateStatus, current_status_raw_buffer, new_status_raw_buffer, device_type); + auto ret = aeronClient.callFunc(aggregateStatusAsync, current_status_raw_buffer, new_status_raw_buffer, device_type).value(); if (ret.returnCode == OK) { aggregated_status = constructBufferByTakeOwnership(ret.buffer); } else { + // Needed to properly free the allocated buffer memory + auto invalid_buffer = constructBufferByTakeOwnership(ret.buffer); aggregated_status = current_status; } return ret.returnCode; @@ -130,8 +134,8 @@ int ModuleManagerLibraryHandlerAsync::aggregateStatus(Buffer &aggregated_status, int ModuleManagerLibraryHandlerAsync::aggregateError(Buffer &error_message, const Buffer ¤t_error_message, const Buffer &status, unsigned int device_type) { - settings::ConvertibleBuffer current_error_raw_buffer; - settings::ConvertibleBuffer status_raw_buffer; + ConvertibleBuffer current_error_raw_buffer; + ConvertibleBuffer status_raw_buffer; if (current_error_message.isAllocated()) { current_error_raw_buffer = current_error_message.getStructBuffer(); @@ -140,7 +144,7 @@ int ModuleManagerLibraryHandlerAsync::aggregateError(Buffer &error_message, status_raw_buffer = status.getStructBuffer(); } - auto ret = aeronClient.callFunc(settings::FunctionIds::aggregateError, current_error_raw_buffer, status_raw_buffer, device_type); + auto ret = aeronClient.callFunc(aggregateErrorAsync, current_error_raw_buffer, status_raw_buffer, device_type).value(); if (ret.returnCode == OK) { error_message = constructBufferByTakeOwnership(ret.buffer); } else { @@ -150,7 +154,7 @@ int ModuleManagerLibraryHandlerAsync::aggregateError(Buffer &error_message, } int ModuleManagerLibraryHandlerAsync::generateFirstCommand(Buffer &default_command, unsigned int device_type) { - auto ret = aeronClient.callFunc(settings::FunctionIds::generateFirstCommand, device_type); + auto ret = aeronClient.callFunc(generateFirstCommandAsync, device_type).value(); if (ret.returnCode == OK) { default_command = constructBufferByTakeOwnership(ret.buffer); } else { @@ -160,21 +164,21 @@ int ModuleManagerLibraryHandlerAsync::generateFirstCommand(Buffer &default_comma } int ModuleManagerLibraryHandlerAsync::statusDataValid(const Buffer &status, unsigned int device_type) const { - settings::ConvertibleBuffer status_raw_buffer; + ConvertibleBuffer status_raw_buffer; if (status.isAllocated()) { status_raw_buffer = status.getStructBuffer(); } - return aeronClient.callFunc(settings::FunctionIds::statusDataValid, status_raw_buffer, device_type); + return aeronClient.callFunc(statusDataValidAsync, status_raw_buffer, device_type).value(); } int ModuleManagerLibraryHandlerAsync::commandDataValid(const Buffer &command, unsigned int device_type) const { - settings::ConvertibleBuffer command_raw_buffer; + ConvertibleBuffer command_raw_buffer; if (command.isAllocated()) { command_raw_buffer = command.getStructBuffer(); } - return aeronClient.callFunc(settings::FunctionIds::commandDataValid, command_raw_buffer, device_type); + return aeronClient.callFunc(commandDataValidAsync, command_raw_buffer, device_type).value(); } int ModuleManagerLibraryHandlerAsync::allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const { diff --git a/source/bringauto/modules/ModuleManagerLibraryHandler.cpp b/source/bringauto/modules/ModuleManagerLibraryHandlerLocal.cpp similarity index 74% rename from source/bringauto/modules/ModuleManagerLibraryHandler.cpp rename to source/bringauto/modules/ModuleManagerLibraryHandlerLocal.cpp index 6d21bbaf..cadd716c 100644 --- a/source/bringauto/modules/ModuleManagerLibraryHandler.cpp +++ b/source/bringauto/modules/ModuleManagerLibraryHandlerLocal.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -20,14 +20,14 @@ struct FunctionTypeDeducer> { using log = settings::Logger; -ModuleManagerLibraryHandler::~ModuleManagerLibraryHandler() { +ModuleManagerLibraryHandlerLocal::~ModuleManagerLibraryHandlerLocal() { if(module_ != nullptr) { dlclose(module_); module_ = nullptr; } } -void ModuleManagerLibraryHandler::loadLibrary(const std::filesystem::path &path) { +void ModuleManagerLibraryHandlerLocal::loadLibrary(const std::filesystem::path &path) { module_ = dlmopen(LM_ID_NEWLM, path.c_str(), RTLD_LAZY); if(module_ == nullptr) { throw std::runtime_error {"Unable to load library " + path.string() + dlerror()}; @@ -57,7 +57,7 @@ void ModuleManagerLibraryHandler::loadLibrary(const std::filesystem::path &path) log::logDebug("Library " + path.string() + " was successfully loaded"); } -void *ModuleManagerLibraryHandler::checkFunction(const char *functionName) const { +void *ModuleManagerLibraryHandlerLocal::checkFunction(const char *functionName) const { const auto function = dlsym(module_, functionName); if(not function) { throw std::runtime_error {"Function " + std::string(functionName) + " is not included in library"}; @@ -65,17 +65,17 @@ void *ModuleManagerLibraryHandler::checkFunction(const char *functionName) const return function; } -int ModuleManagerLibraryHandler::getModuleNumber() const { +int ModuleManagerLibraryHandlerLocal::getModuleNumber() const { return getModuleNumber_(); } -int ModuleManagerLibraryHandler::isDeviceTypeSupported(unsigned int device_type) const { +int ModuleManagerLibraryHandlerLocal::isDeviceTypeSupported(unsigned int device_type) { return isDeviceTypeSupported_(device_type); } -int ModuleManagerLibraryHandler::sendStatusCondition(const Buffer ¤t_status, - const Buffer &new_status, - unsigned int device_type) const { +int ModuleManagerLibraryHandlerLocal::sendStatusCondition(const Buffer ¤t_status, + const Buffer &new_status, + unsigned int device_type) const { struct ::buffer current_status_raw_buffer {}; struct ::buffer new_status_raw_buffer {}; @@ -89,10 +89,10 @@ int ModuleManagerLibraryHandler::sendStatusCondition(const Buffer ¤t_statu return sendStatusCondition_(current_status_raw_buffer, new_status_raw_buffer, device_type); } -int ModuleManagerLibraryHandler::generateCommand(Buffer &generated_command, - const Buffer &new_status, - const Buffer ¤t_status, - const Buffer ¤t_command, unsigned int device_type) { +int ModuleManagerLibraryHandlerLocal::generateCommand(Buffer &generated_command, + const Buffer &new_status, + const Buffer ¤t_status, + const Buffer ¤t_command, unsigned int device_type) { struct ::buffer raw_buffer {}; struct ::buffer new_status_raw_buffer {}; struct ::buffer current_status_raw_buffer {}; @@ -118,9 +118,9 @@ int ModuleManagerLibraryHandler::generateCommand(Buffer &generated_command, return ret; } -int ModuleManagerLibraryHandler::aggregateStatus(Buffer &aggregated_status, - const Buffer ¤t_status, - const Buffer &new_status, unsigned int device_type) { +int ModuleManagerLibraryHandlerLocal::aggregateStatus(Buffer &aggregated_status, + const Buffer ¤t_status, + const Buffer &new_status, unsigned int device_type) { struct ::buffer raw_buffer {}; struct ::buffer current_status_raw_buffer {}; struct ::buffer new_status_raw_buffer {}; @@ -141,9 +141,9 @@ int ModuleManagerLibraryHandler::aggregateStatus(Buffer &aggregated_status, return ret; } -int ModuleManagerLibraryHandler::aggregateError(Buffer &error_message, - const Buffer ¤t_error_message, - const Buffer &status, unsigned int device_type) { +int ModuleManagerLibraryHandlerLocal::aggregateError(Buffer &error_message, + const Buffer ¤t_error_message, + const Buffer &status, unsigned int device_type) { struct ::buffer raw_buffer {}; struct ::buffer current_error_raw_buffer {}; @@ -165,7 +165,7 @@ int ModuleManagerLibraryHandler::aggregateError(Buffer &error_message, return ret; } -int ModuleManagerLibraryHandler::generateFirstCommand(Buffer &default_command, unsigned int device_type) { +int ModuleManagerLibraryHandlerLocal::generateFirstCommand(Buffer &default_command, unsigned int device_type) { struct ::buffer raw_buffer {}; const int ret = generateFirstCommand_(&raw_buffer, device_type); if (ret == OK) { @@ -176,7 +176,7 @@ int ModuleManagerLibraryHandler::generateFirstCommand(Buffer &default_command, u return ret; } -int ModuleManagerLibraryHandler::statusDataValid(const Buffer &status, unsigned int device_type) const { +int ModuleManagerLibraryHandlerLocal::statusDataValid(const Buffer &status, unsigned int device_type) const { struct ::buffer raw_buffer {}; if (status.isAllocated()) { raw_buffer = status.getStructBuffer(); @@ -184,7 +184,7 @@ int ModuleManagerLibraryHandler::statusDataValid(const Buffer &status, unsigned return statusDataValid_(raw_buffer, device_type); } -int ModuleManagerLibraryHandler::commandDataValid(const Buffer &command, unsigned int device_type) const { +int ModuleManagerLibraryHandlerLocal::commandDataValid(const Buffer &command, unsigned int device_type) const { struct ::buffer raw_buffer {}; if (command.isAllocated()) { raw_buffer = command.getStructBuffer(); @@ -192,15 +192,15 @@ int ModuleManagerLibraryHandler::commandDataValid(const Buffer &command, unsigne return commandDataValid_(raw_buffer, device_type); } -int ModuleManagerLibraryHandler::allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const { +int ModuleManagerLibraryHandlerLocal::allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const { return allocate_(buffer_pointer, size_in_bytes); } -void ModuleManagerLibraryHandler::deallocate(struct buffer *buffer) const { +void ModuleManagerLibraryHandlerLocal::deallocate(struct buffer *buffer) const { deallocate_(buffer); } -Buffer ModuleManagerLibraryHandler::constructBuffer(std::size_t size) { +Buffer ModuleManagerLibraryHandlerLocal::constructBuffer(std::size_t size) { if (size == 0) { return Buffer {}; } @@ -212,7 +212,7 @@ Buffer ModuleManagerLibraryHandler::constructBuffer(std::size_t size) { return { buff, deallocate_ }; } -Buffer ModuleManagerLibraryHandler::constructBufferByTakeOwnership(struct ::buffer &buffer) { +Buffer ModuleManagerLibraryHandlerLocal::constructBufferByTakeOwnership(struct ::buffer &buffer) { if (buffer.data == nullptr) { throw Buffer::BufferNotAllocated { "Buffer not allocated - cannot take ownership" }; } diff --git a/source/bringauto/modules/memory_management/CMakeLists.txt b/source/bringauto/modules/memory_management/CMakeLists.txt index e2e72b13..f47babe6 100644 --- a/source/bringauto/modules/memory_management/CMakeLists.txt +++ b/source/bringauto/modules/memory_management/CMakeLists.txt @@ -1,7 +1,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.25 FATAL_ERROR) PROJECT(ModuleGateway) -SET(CMAKE_CXX_STANDARD 20) +SET(CMAKE_CXX_STANDARD 23) FIND_PACKAGE(fleet-protocol-interface 2.0.0 REQUIRED) diff --git a/source/bringauto/settings/SettingsParser.cpp b/source/bringauto/settings/SettingsParser.cpp index 2ec7a6b7..9a09d46e 100644 --- a/source/bringauto/settings/SettingsParser.cpp +++ b/source/bringauto/settings/SettingsParser.cpp @@ -37,8 +37,6 @@ void SettingsParser::parseCmdArguments(int argc, char **argv) { cxxopts::value()); options.add_options("Internal Server")(std::string(Constants::PORT), "Port on which Server listens", cxxopts::value()); - options.add_options("Module Handler")(std::string(Constants::MODULE_PATHS), "Paths to shared module libraries", - cxxopts::value>()); options.allow_unrecognised_options(); cmdArguments_ = options.parse(argc, argv); @@ -55,8 +53,7 @@ bool SettingsParser::areCmdArgumentsCorrect() const { }; std::vector allParameters = { std::string(Constants::CONFIG_PATH), - std::string(Constants::PORT), - std::string(Constants::MODULE_PATHS) + std::string(Constants::PORT) }; allParameters.insert(allParameters.end(), requiredParams.begin(), requiredParams.end()); @@ -155,15 +152,16 @@ void SettingsParser::fillInternalServerSettings(const nlohmann::json &file) cons void SettingsParser::fillModulePathsSettings(const nlohmann::json &file) const { for(auto &[key, val]: file[std::string(Constants::MODULE_PATHS)].items()) { - settings_->modulePaths[stoi(key)] = val; + val.get_to(settings_->modulePaths[stoi(key)]); } - settings_->moduleBinaryPath = file[std::string(Constants::MODULE_BINARY_PATH)]; + file.at(std::string(Constants::MODULE_BINARY_PATH)).get_to(settings_->moduleBinaryPath); } void SettingsParser::fillExternalConnectionSettings(const nlohmann::json &file) const { - settings_->vehicleName = file[std::string(Constants::EXTERNAL_CONNECTION)][std::string( - Constants::VEHICLE_NAME)]; - settings_->company = file[std::string(Constants::EXTERNAL_CONNECTION)][std::string(Constants::COMPANY)]; + file.at(std::string(Constants::EXTERNAL_CONNECTION)).at(std::string(Constants::VEHICLE_NAME)).get_to( + settings_->vehicleName); + file.at(std::string(Constants::EXTERNAL_CONNECTION)).at(std::string(Constants::COMPANY)).get_to( + settings_->company); for(const auto &endpoint: file[std::string(Constants::EXTERNAL_CONNECTION)][std::string( Constants::EXTERNAL_ENDPOINTS)]) { @@ -183,7 +181,7 @@ void SettingsParser::fillExternalConnectionSettings(const nlohmann::json &file) continue; } - externalConnectionSettings.serverIp = endpoint[std::string(Constants::SERVER_IP)]; + endpoint.at(std::string(Constants::SERVER_IP)).get_to(externalConnectionSettings.serverIp); externalConnectionSettings.port = endpoint[std::string(Constants::PORT)]; externalConnectionSettings.modules = endpoint[std::string(Constants::MODULES)].get>(); diff --git a/source/bringauto/structures/ModuleLibrary.cpp b/source/bringauto/structures/ModuleLibrary.cpp index 3b0a23d8..28bb84b6 100644 --- a/source/bringauto/structures/ModuleLibrary.cpp +++ b/source/bringauto/structures/ModuleLibrary.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include @@ -12,10 +14,14 @@ ModuleLibrary::~ModuleLibrary() { } void ModuleLibrary::loadLibraries(const std::unordered_map &libPaths, const std::string &moduleBinaryPath) { + std::shared_ptr handler; for(auto const &[key, path]: libPaths) { - auto handler = std::make_shared(); //TODO select type from config - handler->loadLibrary(path, moduleBinaryPath); - std::this_thread::sleep_for(std::chrono::seconds(3)); // TODO Not sure how much time is needed. + if (moduleBinaryPath.empty()) { + handler = std::make_shared(); + } else { + handler = std::make_shared(moduleBinaryPath); + } + handler->loadLibrary(path); if(handler->getModuleNumber() != key) { settings::Logger::logError("Module number from shared library {} does not match the module number from config. Config: {}, binary: {}.", path, key, handler->getModuleNumber()); throw std::runtime_error {"Module numbers from config are not corresponding to binaries. Unable to continue. Fix configuration file."}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 54f4550a..dbf05a6f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,7 +1,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.25) PROJECT(ModuleGateway) -SET(CMAKE_CXX_STANDARD 20) +SET(CMAKE_CXX_STANDARD 23) ADD_SUBDIRECTORY("${CMAKE_CURRENT_LIST_DIR}/lib/example-module") diff --git a/test/include/ErrorAggregatorTests.hpp b/test/include/ErrorAggregatorTests.hpp index 2c176f08..64214d01 100644 --- a/test/include/ErrorAggregatorTests.hpp +++ b/test/include/ErrorAggregatorTests.hpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -31,7 +31,7 @@ class ErrorAggregatorTests: public ::testing::Test { bringauto::modules::Buffer init_status_buffer(); bringauto::external_client::ErrorAggregator errorAggregator_ {}; - std::shared_ptr libHandler_ {}; + std::shared_ptr libHandler_ {}; #ifdef DEBUG static constexpr const char* PATH_TO_MODULE { "./test/lib/example-module/libexample-module-gateway-sharedd.so" }; #else diff --git a/test/include/ExternalConnectionTests.hpp b/test/include/ExternalConnectionTests.hpp index 0fb241e9..ea7e09fc 100644 --- a/test/include/ExternalConnectionTests.hpp +++ b/test/include/ExternalConnectionTests.hpp @@ -2,7 +2,6 @@ #include #include -#include #include #include diff --git a/test/include/StatusAggregatorTests.hpp b/test/include/StatusAggregatorTests.hpp index a5a6922c..848208e1 100644 --- a/test/include/StatusAggregatorTests.hpp +++ b/test/include/StatusAggregatorTests.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include @@ -43,7 +43,7 @@ class StatusAggregatorTests: public ::testing::Test { std::unique_ptr statusAggregator_ {}; - std::shared_ptr libHandler_ {}; + std::shared_ptr libHandler_ {}; #ifdef DEBUG static constexpr const char* PATH_TO_MODULE { "./test/lib/example-module/libexample-module-gateway-sharedd.so" }; diff --git a/test/include/testing_utils/ConfigMock.hpp b/test/include/testing_utils/ConfigMock.hpp index 859f836a..c30fda3b 100644 --- a/test/include/testing_utils/ConfigMock.hpp +++ b/test/include/testing_utils/ConfigMock.hpp @@ -105,6 +105,7 @@ class ConfigMock { "\"module-paths\": {{\n" "{}\n" "}},\n" + "\"module-binary-path\": \"\",\n" "\"external-connection\": {{\n" "\"company\": \"{}\",\n" "\"vehicle-name\": \"{}\",\n" diff --git a/test/source/ErrorAggregatorTests.cpp b/test/source/ErrorAggregatorTests.cpp index 12d2dc4c..258b3fc9 100644 --- a/test/source/ErrorAggregatorTests.cpp +++ b/test/source/ErrorAggregatorTests.cpp @@ -1,5 +1,7 @@ #include #include +#include + #include @@ -15,7 +17,7 @@ bam::Buffer ErrorAggregatorTests::init_status_buffer() { } void ErrorAggregatorTests::SetUp(){ - libHandler_ = std::make_shared(); + libHandler_ = std::make_shared(); libHandler_->loadLibrary(PATH_TO_MODULE); errorAggregator_.init_error_aggregator(libHandler_); } @@ -26,14 +28,14 @@ void ErrorAggregatorTests::TearDown(){ TEST_F(ErrorAggregatorTests, init_error_aggregator_ok) { external_client::ErrorAggregator errorAggregatorTest {}; - const auto libHandler = std::make_shared(); + const auto libHandler = std::make_shared(); const int ret = errorAggregatorTest.init_error_aggregator(libHandler); EXPECT_EQ(ret, OK); } TEST_F(ErrorAggregatorTests, destroy_error_aggregator_ok) { external_client::ErrorAggregator errorAggregatorTest {}; - const auto libHandler = std::make_shared(); + const auto libHandler = std::make_shared(); errorAggregatorTest.init_error_aggregator(libHandler); const int ret = errorAggregatorTest.destroy_error_aggregator(); EXPECT_EQ(ret, OK); diff --git a/test/source/StatusAggregatorTests.cpp b/test/source/StatusAggregatorTests.cpp index 81ea7c92..813218c3 100644 --- a/test/source/StatusAggregatorTests.cpp +++ b/test/source/StatusAggregatorTests.cpp @@ -1,5 +1,7 @@ #include #include +#include + #include @@ -41,7 +43,7 @@ void StatusAggregatorTests::remove_device_from_status_aggregator(){ void StatusAggregatorTests::SetUp(){ context_ = std::make_shared(); - libHandler_ = std::make_shared(); + libHandler_ = std::make_shared(); libHandler_->loadLibrary(PATH_TO_MODULE); statusAggregator_ = std::make_unique(context_, libHandler_); statusAggregator_->init_status_aggregator(); @@ -59,7 +61,7 @@ TEST_F(StatusAggregatorTests, init_status_aggregator_ok) { } TEST_F(StatusAggregatorTests, init_status_aggregator_bad_path) { - auto libHandler = std::make_shared(); + auto libHandler = std::make_shared(); EXPECT_THROW(libHandler->loadLibrary(WRONG_PATH_TO_MODULE), std::runtime_error); } @@ -94,7 +96,7 @@ TEST_F(StatusAggregatorTests, add_status_to_aggregator_status_register_device){ } TEST_F(StatusAggregatorTests, add_status_to_aggregator_without_aggregation){ - auto libHandler = std::make_shared(); + auto libHandler = std::make_shared(); libHandler->loadLibrary(PATH_TO_MODULE); add_status_to_aggregator(); auto size = std::string(BUTTON_PRESSED).size(); From e48c5efe00a1156096f9baa1179b9802be4b1417 Mon Sep 17 00:00:00 2001 From: Jan Kubalek Date: Fri, 17 Oct 2025 20:45:08 +0200 Subject: [PATCH 18/55] Set correct system name --- CMLibStorage.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMLibStorage.cmake b/CMLibStorage.cmake index c9e0b597..36796bad 100644 --- a/CMLibStorage.cmake +++ b/CMLibStorage.cmake @@ -1,6 +1,6 @@ FIND_PACKAGE(CMLIB REQUIRED COMPONENTS CMCONF) -CMCONF_INIT_SYSTEM(EXAMPLE) +CMCONF_INIT_SYSTEM(FLEET_PROTOCOL) SET(STORAGE_LIST DEP) From 049f9e3e683139112554962a79f5e61e8d6140bb Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Mon, 3 Nov 2025 09:10:00 +0100 Subject: [PATCH 19/55] added mutexes for async client; use filesystem::path for file paths --- CMLibStorage.cmake | 2 +- .../modules/IModuleManagerLibraryHandler.hpp | 8 +++---- .../ModuleManagerLibraryHandlerAsync.hpp | 23 ++++++++++++------ .../ModuleManagerLibraryHandlerLocal.hpp | 8 +++---- include/bringauto/settings/Settings.hpp | 4 ++-- .../bringauto/structures/ModuleLibrary.hpp | 9 ++++++- main.cpp | 6 ++++- .../ModuleManagerLibraryHandlerAsync.cpp | 24 ++++++++++++------- .../ModuleManagerLibraryHandlerLocal.cpp | 8 +++---- source/bringauto/structures/ModuleLibrary.cpp | 21 +++++++++++----- test/include/testing_utils/ConfigMock.hpp | 4 ++-- 11 files changed, 77 insertions(+), 40 deletions(-) diff --git a/CMLibStorage.cmake b/CMLibStorage.cmake index 36796bad..c9e0b597 100644 --- a/CMLibStorage.cmake +++ b/CMLibStorage.cmake @@ -1,6 +1,6 @@ FIND_PACKAGE(CMLIB REQUIRED COMPONENTS CMCONF) -CMCONF_INIT_SYSTEM(FLEET_PROTOCOL) +CMCONF_INIT_SYSTEM(EXAMPLE) SET(STORAGE_LIST DEP) diff --git a/include/bringauto/modules/IModuleManagerLibraryHandler.hpp b/include/bringauto/modules/IModuleManagerLibraryHandler.hpp index 75d51ec5..49bf44be 100644 --- a/include/bringauto/modules/IModuleManagerLibraryHandler.hpp +++ b/include/bringauto/modules/IModuleManagerLibraryHandler.hpp @@ -27,11 +27,11 @@ class IModuleManagerLibraryHandler { */ virtual void loadLibrary(const std::filesystem::path &path) = 0; - virtual int getModuleNumber() const = 0; + virtual int getModuleNumber() = 0; virtual int isDeviceTypeSupported(unsigned int device_type) = 0; - virtual int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) const = 0; + virtual int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) = 0; /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. @@ -65,9 +65,9 @@ class IModuleManagerLibraryHandler { */ virtual int generateFirstCommand(Buffer &default_command, unsigned int device_type) = 0; - virtual int statusDataValid(const Buffer &status, unsigned int device_type) const = 0; + virtual int statusDataValid(const Buffer &status, unsigned int device_type) = 0; - virtual int commandDataValid(const Buffer &command, unsigned int device_type) const = 0; + virtual int commandDataValid(const Buffer &command, unsigned int device_type) = 0; /** * @brief Constructs a buffer with the given size diff --git a/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp b/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp index d1a0be1b..2300e066 100644 --- a/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp +++ b/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp @@ -109,7 +109,7 @@ inline static const async_function_execution::FunctionDefinition commandDataVali */ class ModuleManagerLibraryHandlerAsync : public IModuleManagerLibraryHandler { public: - explicit ModuleManagerLibraryHandlerAsync(const std::string &moduleBinaryPath); + explicit ModuleManagerLibraryHandlerAsync(const std::filesystem::path &moduleBinaryPath); ~ModuleManagerLibraryHandlerAsync() override; @@ -120,11 +120,11 @@ class ModuleManagerLibraryHandlerAsync : public IModuleManagerLibraryHandler { */ void loadLibrary(const std::filesystem::path &path) override; - int getModuleNumber() const override; + int getModuleNumber() override; int isDeviceTypeSupported(unsigned int device_type) override; - int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) const override; + int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) override; /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. @@ -158,9 +158,9 @@ class ModuleManagerLibraryHandlerAsync : public IModuleManagerLibraryHandler { */ int generateFirstCommand(Buffer &default_command, unsigned int device_type) override; - int statusDataValid(const Buffer &status, unsigned int device_type) const override; + int statusDataValid(const Buffer &status, unsigned int device_type) override; - int commandDataValid(const Buffer &command, unsigned int device_type) const override; + int commandDataValid(const Buffer &command, unsigned int device_type) override; /** * @brief Constructs a buffer with the given size @@ -187,11 +187,20 @@ class ModuleManagerLibraryHandlerAsync : public IModuleManagerLibraryHandler { std::function deallocate_ {}; /// Path to the module binary - std::string moduleBinaryPath_ {}; + std::filesystem::path moduleBinaryPath_ {}; /// Process of the module binary boost::process::child moduleBinaryProcess_ {}; + /// TODO find a way to not need this - std::mutex tmpMutex_ {}; + std::mutex getModuleNumberMutex_ {}; + std::mutex isDeviceTypeSupportedMutex_ {}; + std::mutex sendStatusConditionMutex_ {}; + std::mutex generateCommandMutex_ {}; + std::mutex aggregateStatusMutex_ {}; + std::mutex aggregateErrorMutex_ {}; + std::mutex generateFirstCommandMutex_ {}; + std::mutex statusDataValidMutex_ {}; + std::mutex commandDataValidMutex_ {}; }; } diff --git a/include/bringauto/modules/ModuleManagerLibraryHandlerLocal.hpp b/include/bringauto/modules/ModuleManagerLibraryHandlerLocal.hpp index a1fd601d..bd897d51 100644 --- a/include/bringauto/modules/ModuleManagerLibraryHandlerLocal.hpp +++ b/include/bringauto/modules/ModuleManagerLibraryHandlerLocal.hpp @@ -22,11 +22,11 @@ class ModuleManagerLibraryHandlerLocal : public IModuleManagerLibraryHandler { */ void loadLibrary(const std::filesystem::path &path) override; - int getModuleNumber() const override; + int getModuleNumber() override; int isDeviceTypeSupported(unsigned int device_type) override; - int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) const override; + int sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) override; /** * @short After executing the respective module function, an error might be thrown when allocating the buffer. @@ -60,9 +60,9 @@ class ModuleManagerLibraryHandlerLocal : public IModuleManagerLibraryHandler { */ int generateFirstCommand(Buffer &default_command, unsigned int device_type) override; - int statusDataValid(const Buffer &status, unsigned int device_type) const override; + int statusDataValid(const Buffer &status, unsigned int device_type) override; - int commandDataValid(const Buffer &command, unsigned int device_type) const override; + int commandDataValid(const Buffer &command, unsigned int device_type) override; /** * @brief Constructs a buffer with the given size diff --git a/include/bringauto/settings/Settings.hpp b/include/bringauto/settings/Settings.hpp index 10758209..b105dda6 100644 --- a/include/bringauto/settings/Settings.hpp +++ b/include/bringauto/settings/Settings.hpp @@ -31,12 +31,12 @@ struct Settings { /** * @brief paths to shared module libraries */ - std::unordered_map modulePaths {}; + std::unordered_map modulePaths {}; /** * @brief path to module binary */ - std::string moduleBinaryPath {}; + std::filesystem::path moduleBinaryPath {}; /** * @brief Setting of external connection endpoints and protocols diff --git a/include/bringauto/structures/ModuleLibrary.hpp b/include/bringauto/structures/ModuleLibrary.hpp index 7cb13601..d6b3a0db 100644 --- a/include/bringauto/structures/ModuleLibrary.hpp +++ b/include/bringauto/structures/ModuleLibrary.hpp @@ -17,13 +17,20 @@ struct ModuleLibrary { ~ModuleLibrary(); + /** + * @brief Load libraries from paths + * + * @param libPaths paths to the libraries + */ + void loadLibraries(const std::unordered_map &libPaths); + /** * @brief Load libraries from paths * * @param libPaths paths to the libraries * @param moduleBinaryPath path to module binary for async function execution over shared memory */ - void loadLibraries(const std::unordered_map &libPaths, const std::string &moduleBinaryPath = ""); + void loadLibraries(const std::unordered_map &libPaths, const std::filesystem::path &moduleBinaryPath); /** * @brief Initialize status aggregators with context diff --git a/main.cpp b/main.cpp index 46e5fe44..07536448 100644 --- a/main.cpp +++ b/main.cpp @@ -71,7 +71,11 @@ int main(int argc, char **argv) { bas::ModuleLibrary moduleLibrary {}; try { - moduleLibrary.loadLibraries(context->settings->modulePaths, context->settings->moduleBinaryPath); + if(context->settings->moduleBinaryPath.empty()) { + moduleLibrary.loadLibraries(context->settings->modulePaths); + } else { + moduleLibrary.loadLibraries(context->settings->modulePaths, context->settings->moduleBinaryPath); + } moduleLibrary.initStatusAggregators(context); } catch(std::exception &e) { std::cerr << "[ERROR] Error occurred during module initialization: " << e.what() << std::endl; diff --git a/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp index 00d3a5f6..bb44536c 100644 --- a/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp +++ b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp @@ -27,7 +27,7 @@ async_function_execution::AsyncFunctionExecutor aeronClient { } }; -ModuleManagerLibraryHandlerAsync::ModuleManagerLibraryHandlerAsync(const std::string &moduleBinaryPath) : +ModuleManagerLibraryHandlerAsync::ModuleManagerLibraryHandlerAsync(const std::filesystem::path &moduleBinaryPath) : moduleBinaryPath_ { moduleBinaryPath } { aeronClient.connect(); deallocate_ = [this](struct buffer *buffer) { @@ -43,25 +43,27 @@ ModuleManagerLibraryHandlerAsync::~ModuleManagerLibraryHandlerAsync() { } void ModuleManagerLibraryHandlerAsync::loadLibrary(const std::filesystem::path &path) { - moduleBinaryProcess_ = boost::process::child { moduleBinaryPath_, "-m", path.string() }; + moduleBinaryProcess_ = boost::process::child { moduleBinaryPath_.string(), "-m", path.string() }; if (!moduleBinaryProcess_.valid()) { - throw std::runtime_error { "Failed to start module binary " + moduleBinaryPath_ }; + throw std::runtime_error { "Failed to start module binary " + moduleBinaryPath_.string() }; } std::this_thread::sleep_for(std::chrono::seconds(1)); // TODO Not sure how much time is needed. } -int ModuleManagerLibraryHandlerAsync::getModuleNumber() const { +int ModuleManagerLibraryHandlerAsync::getModuleNumber() { + std::lock_guard lock { getModuleNumberMutex_ }; return aeronClient.callFunc(getModuleNumberAsync).value(); } int ModuleManagerLibraryHandlerAsync::isDeviceTypeSupported(unsigned int device_type) { - std::lock_guard lock { tmpMutex_ }; + std::lock_guard lock { isDeviceTypeSupportedMutex_ }; return aeronClient.callFunc(isDeviceTypeSupportedAsync, device_type).value(); } int ModuleManagerLibraryHandlerAsync::sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, - unsigned int device_type) const { + unsigned int device_type) { + std::lock_guard lock { isDeviceTypeSupportedMutex_ }; ConvertibleBuffer current_status_raw_buffer; ConvertibleBuffer new_status_raw_buffer; @@ -79,6 +81,7 @@ int ModuleManagerLibraryHandlerAsync::generateCommand(Buffer &generated_command, const Buffer &new_status, const Buffer ¤t_status, const Buffer ¤t_command, unsigned int device_type) { + std::lock_guard lock { generateCommandMutex_ }; ConvertibleBuffer new_status_raw_buffer; ConvertibleBuffer current_status_raw_buffer; ConvertibleBuffer current_command_raw_buffer; @@ -110,6 +113,7 @@ int ModuleManagerLibraryHandlerAsync::generateCommand(Buffer &generated_command, int ModuleManagerLibraryHandlerAsync::aggregateStatus(Buffer &aggregated_status, const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) { + std::lock_guard lock { aggregateStatusMutex_ }; ConvertibleBuffer current_status_raw_buffer; ConvertibleBuffer new_status_raw_buffer; @@ -134,6 +138,7 @@ int ModuleManagerLibraryHandlerAsync::aggregateStatus(Buffer &aggregated_status, int ModuleManagerLibraryHandlerAsync::aggregateError(Buffer &error_message, const Buffer ¤t_error_message, const Buffer &status, unsigned int device_type) { + std::lock_guard lock { aggregateErrorMutex_ }; ConvertibleBuffer current_error_raw_buffer; ConvertibleBuffer status_raw_buffer; @@ -154,6 +159,7 @@ int ModuleManagerLibraryHandlerAsync::aggregateError(Buffer &error_message, } int ModuleManagerLibraryHandlerAsync::generateFirstCommand(Buffer &default_command, unsigned int device_type) { + std::lock_guard lock { generateFirstCommandMutex_ }; auto ret = aeronClient.callFunc(generateFirstCommandAsync, device_type).value(); if (ret.returnCode == OK) { default_command = constructBufferByTakeOwnership(ret.buffer); @@ -163,7 +169,8 @@ int ModuleManagerLibraryHandlerAsync::generateFirstCommand(Buffer &default_comma return ret.returnCode; } -int ModuleManagerLibraryHandlerAsync::statusDataValid(const Buffer &status, unsigned int device_type) const { +int ModuleManagerLibraryHandlerAsync::statusDataValid(const Buffer &status, unsigned int device_type) { + std::lock_guard lock { statusDataValidMutex_ }; ConvertibleBuffer status_raw_buffer; if (status.isAllocated()) { status_raw_buffer = status.getStructBuffer(); @@ -172,7 +179,8 @@ int ModuleManagerLibraryHandlerAsync::statusDataValid(const Buffer &status, unsi return aeronClient.callFunc(statusDataValidAsync, status_raw_buffer, device_type).value(); } -int ModuleManagerLibraryHandlerAsync::commandDataValid(const Buffer &command, unsigned int device_type) const { +int ModuleManagerLibraryHandlerAsync::commandDataValid(const Buffer &command, unsigned int device_type) { + std::lock_guard lock { commandDataValidMutex_ }; ConvertibleBuffer command_raw_buffer; if (command.isAllocated()) { command_raw_buffer = command.getStructBuffer(); diff --git a/source/bringauto/modules/ModuleManagerLibraryHandlerLocal.cpp b/source/bringauto/modules/ModuleManagerLibraryHandlerLocal.cpp index cadd716c..193dc5cd 100644 --- a/source/bringauto/modules/ModuleManagerLibraryHandlerLocal.cpp +++ b/source/bringauto/modules/ModuleManagerLibraryHandlerLocal.cpp @@ -65,7 +65,7 @@ void *ModuleManagerLibraryHandlerLocal::checkFunction(const char *functionName) return function; } -int ModuleManagerLibraryHandlerLocal::getModuleNumber() const { +int ModuleManagerLibraryHandlerLocal::getModuleNumber() { return getModuleNumber_(); } @@ -75,7 +75,7 @@ int ModuleManagerLibraryHandlerLocal::isDeviceTypeSupported(unsigned int device_ int ModuleManagerLibraryHandlerLocal::sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, - unsigned int device_type) const { + unsigned int device_type) { struct ::buffer current_status_raw_buffer {}; struct ::buffer new_status_raw_buffer {}; @@ -176,7 +176,7 @@ int ModuleManagerLibraryHandlerLocal::generateFirstCommand(Buffer &default_comma return ret; } -int ModuleManagerLibraryHandlerLocal::statusDataValid(const Buffer &status, unsigned int device_type) const { +int ModuleManagerLibraryHandlerLocal::statusDataValid(const Buffer &status, unsigned int device_type) { struct ::buffer raw_buffer {}; if (status.isAllocated()) { raw_buffer = status.getStructBuffer(); @@ -184,7 +184,7 @@ int ModuleManagerLibraryHandlerLocal::statusDataValid(const Buffer &status, unsi return statusDataValid_(raw_buffer, device_type); } -int ModuleManagerLibraryHandlerLocal::commandDataValid(const Buffer &command, unsigned int device_type) const { +int ModuleManagerLibraryHandlerLocal::commandDataValid(const Buffer &command, unsigned int device_type) { struct ::buffer raw_buffer {}; if (command.isAllocated()) { raw_buffer = command.getStructBuffer(); diff --git a/source/bringauto/structures/ModuleLibrary.cpp b/source/bringauto/structures/ModuleLibrary.cpp index 28bb84b6..3dd1e227 100644 --- a/source/bringauto/structures/ModuleLibrary.cpp +++ b/source/bringauto/structures/ModuleLibrary.cpp @@ -13,17 +13,26 @@ ModuleLibrary::~ModuleLibrary() { [](auto &pair) { pair.second->destroy_status_aggregator(); }); } -void ModuleLibrary::loadLibraries(const std::unordered_map &libPaths, const std::string &moduleBinaryPath) { +void ModuleLibrary::loadLibraries(const std::unordered_map &libPaths) { std::shared_ptr handler; for(auto const &[key, path]: libPaths) { - if (moduleBinaryPath.empty()) { - handler = std::make_shared(); - } else { - handler = std::make_shared(moduleBinaryPath); + handler = std::make_shared(); + handler->loadLibrary(path); + if(handler->getModuleNumber() != key) { + settings::Logger::logError("Module number from shared library {} does not match the module number from config. Config: {}, binary: {}.", path.string(), key, handler->getModuleNumber()); + throw std::runtime_error {"Module numbers from config are not corresponding to binaries. Unable to continue. Fix configuration file."}; } + moduleLibraryHandlers.emplace(key, handler); + } +} + +void ModuleLibrary::loadLibraries(const std::unordered_map &libPaths, const std::filesystem::path &moduleBinaryPath) { + std::shared_ptr handler; + for(auto const &[key, path]: libPaths) { + handler = std::make_shared(moduleBinaryPath); handler->loadLibrary(path); if(handler->getModuleNumber() != key) { - settings::Logger::logError("Module number from shared library {} does not match the module number from config. Config: {}, binary: {}.", path, key, handler->getModuleNumber()); + settings::Logger::logError("Module number from shared library {} does not match the module number from config. Config: {}, binary: {}.", path.string(), key, handler->getModuleNumber()); throw std::runtime_error {"Module numbers from config are not corresponding to binaries. Unable to continue. Fix configuration file."}; } moduleLibraryHandlers.emplace(key, handler); diff --git a/test/include/testing_utils/ConfigMock.hpp b/test/include/testing_utils/ConfigMock.hpp index c30fda3b..97514f5a 100644 --- a/test/include/testing_utils/ConfigMock.hpp +++ b/test/include/testing_utils/ConfigMock.hpp @@ -27,11 +27,11 @@ class ConfigMock { int port { 1636 }; } internal_server_settings; - std::unordered_map module_paths { {1, "/path/to/lib1.so"}, {2, "/path/to/lib2.so"}, {3, "/path/to/lib3.so"} }; + std::unordered_map module_paths { {1, "/path/to/lib1.so"}, {2, "/path/to/lib2.so"}, {3, "/path/to/lib3.so"} }; std::string modulePathsToString() const { std::string result = ""; for (auto [key, value] : module_paths) { - result += std::format("\"{}\": \"{}\",\n", key, value); + result += std::format("\"{}\": \"{}\",\n", key, value.string()); } if (!result.empty()) { result.pop_back(); From 9a37b77676ac511b41503a7634ad2524ab90f96c Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Mon, 3 Nov 2025 14:08:27 +0100 Subject: [PATCH 20/55] removed memory management; added module based channel offset --- CMLibStorage.cmake | 2 +- CMakeLists.txt | 2 -- .../modules/IModuleManagerLibraryHandler.hpp | 3 -- .../ModuleManagerLibraryHandlerAsync.hpp | 5 +-- include/bringauto/settings/Constants.hpp | 36 ++++++++----------- .../ModuleManagerLibraryHandlerAsync.cpp | 18 +++++++--- .../modules/memory_management/CMakeLists.txt | 10 ------ .../source/memory_management.cpp | 21 ----------- source/bringauto/structures/ModuleLibrary.cpp | 2 +- 9 files changed, 33 insertions(+), 66 deletions(-) delete mode 100644 source/bringauto/modules/memory_management/CMakeLists.txt delete mode 100644 source/bringauto/modules/memory_management/source/memory_management.cpp diff --git a/CMLibStorage.cmake b/CMLibStorage.cmake index c9e0b597..36796bad 100644 --- a/CMLibStorage.cmake +++ b/CMLibStorage.cmake @@ -1,6 +1,6 @@ FIND_PACKAGE(CMLIB REQUIRED COMPONENTS CMCONF) -CMCONF_INIT_SYSTEM(EXAMPLE) +CMCONF_INIT_SYSTEM(FLEET_PROTOCOL) SET(STORAGE_LIST DEP) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77469904..ac6eb24e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,6 @@ FIND_PACKAGE(async-function-execution-shared 1.0.0 REQUIRED) FILE(GLOB_RECURSE source_files "source/*") ADD_LIBRARY(module-gateway-lib STATIC "${source_files}") TARGET_INCLUDE_DIRECTORIES(module-gateway-lib PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) -ADD_SUBDIRECTORY(source/bringauto/modules/memory_management) TARGET_LINK_LIBRARIES(module-gateway-lib PUBLIC Boost::headers protobuf::libprotobuf @@ -77,7 +76,6 @@ TARGET_LINK_LIBRARIES(module-gateway-lib PUBLIC ZLIB::ZLIB fleet-protocol-cxx-helpers-static::fleet-protocol-cxx-helpers-static async-function-execution-shared::async-function-execution-shared - memory_management ${CMAKE_DL_LIBS} ) diff --git a/include/bringauto/modules/IModuleManagerLibraryHandler.hpp b/include/bringauto/modules/IModuleManagerLibraryHandler.hpp index 49bf44be..c1254063 100644 --- a/include/bringauto/modules/IModuleManagerLibraryHandler.hpp +++ b/include/bringauto/modules/IModuleManagerLibraryHandler.hpp @@ -2,9 +2,6 @@ #include -#include - -#include #include diff --git a/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp b/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp index 2300e066..73fe9827 100644 --- a/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp +++ b/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp @@ -30,7 +30,8 @@ struct ConvertibleBufferReturn final { if (size < sizeof(int)) return; std::memcpy(&returnCode, bytes.data(), sizeof(int)); size -= sizeof(int); - allocate(&buffer, size); + buffer.data = new uint8_t[size]; + buffer.size_in_bytes = size; std::memcpy(buffer.data, bytes.data() + sizeof(int), size); buffer.size_in_bytes = size; } @@ -109,7 +110,7 @@ inline static const async_function_execution::FunctionDefinition commandDataVali */ class ModuleManagerLibraryHandlerAsync : public IModuleManagerLibraryHandler { public: - explicit ModuleManagerLibraryHandlerAsync(const std::filesystem::path &moduleBinaryPath); + explicit ModuleManagerLibraryHandlerAsync(const std::filesystem::path &moduleBinaryPath, const int moduleNumber); ~ModuleManagerLibraryHandlerAsync() override; diff --git a/include/bringauto/settings/Constants.hpp b/include/bringauto/settings/Constants.hpp index 28a0470d..3aa62380 100644 --- a/include/bringauto/settings/Constants.hpp +++ b/include/bringauto/settings/Constants.hpp @@ -1,11 +1,5 @@ #pragma once -#include - -#include -#include -#include - #include #include @@ -92,47 +86,47 @@ constexpr unsigned int max_external_commands { 3 }; */ constexpr unsigned int max_external_queue_size { 500 }; -/** - * @brief base stream id for Aeron communication from Module Gateway to module binary - */ -constexpr unsigned int aeron_to_module_stream_id_base { 10000 }; - -/** - * @brief base stream id for Aeron communication from module binary to Module Gateway - */ -constexpr unsigned int aeron_to_gateway_stream_id_base { 20000 }; - /** * @brief Constants for Mqtt communication -*/ + */ struct MqttConstants { /** * @brief keep alive interval in seconds; * value reasoning: keepalive is half of the default timeout in Fleet protocol * The value is chosen based on empiric measurement. - */ + */ static constexpr std::chrono::seconds keepalive { status_response_timeout / 2U }; /** * @brief automatic reconnection of mqtt client option - */ + */ static constexpr bool automatic_reconnect { true }; /** * @brief max time that the mqtt client will wait for a connection before failing; * value reasoning: TCP timeout for retransmission when TCP packet is dropped is 200ms, * this value is multiple of three of this value - */ + */ static constexpr std::chrono::milliseconds connect_timeout { 600 }; /** * @brief max messages that can be in the process of transmission simultaneously; * value reasoning: How many MQTT inflight messages can be open at one time. * The value is chosen as a recommendation from a MQTT community. - */ + */ static constexpr size_t max_inflight { 20 }; }; +/** + * @brief Constants for Aeron client communication + */ +struct AeronClientConstants { + /** + * @brief default timeout for Aeron client function calls + */ + static constexpr std::chrono::milliseconds aeron_client_default_timeout { 1000 }; +}; + /** * @brief Constant string views */ diff --git a/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp index bb44536c..61d1531c 100644 --- a/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp +++ b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp @@ -12,7 +12,7 @@ namespace bringauto::modules { async_function_execution::AsyncFunctionExecutor aeronClient { async_function_execution::Config { .isProducer = true, - .defaultTimeout = std::chrono::seconds(1) + .defaultTimeout = settings::AeronClientConstants::aeron_client_default_timeout, }, async_function_execution::FunctionList { getModuleNumberAsync, @@ -27,9 +27,9 @@ async_function_execution::AsyncFunctionExecutor aeronClient { } }; -ModuleManagerLibraryHandlerAsync::ModuleManagerLibraryHandlerAsync(const std::filesystem::path &moduleBinaryPath) : +ModuleManagerLibraryHandlerAsync::ModuleManagerLibraryHandlerAsync(const std::filesystem::path &moduleBinaryPath, const int moduleNumber) : moduleBinaryPath_ { moduleBinaryPath } { - aeronClient.connect(); + aeronClient.connect(moduleNumber); deallocate_ = [this](struct buffer *buffer) { this->deallocate(buffer); }; @@ -190,11 +190,19 @@ int ModuleManagerLibraryHandlerAsync::commandDataValid(const Buffer &command, un } int ModuleManagerLibraryHandlerAsync::allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const { - return ::allocate(buffer_pointer, size_in_bytes); + try{ + buffer_pointer->data = new char[size_in_bytes](); + } catch(std::bad_alloc&){ + return NOT_OK; + } + buffer_pointer->size_in_bytes = size_in_bytes; + return OK; } void ModuleManagerLibraryHandlerAsync::deallocate(struct buffer *buffer) const { - ::deallocate(buffer); + delete[] static_cast(buffer->data); + buffer->data = nullptr; + buffer->size_in_bytes = 0; } Buffer ModuleManagerLibraryHandlerAsync::constructBuffer(std::size_t size) { diff --git a/source/bringauto/modules/memory_management/CMakeLists.txt b/source/bringauto/modules/memory_management/CMakeLists.txt deleted file mode 100644 index f47babe6..00000000 --- a/source/bringauto/modules/memory_management/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.25 FATAL_ERROR) -PROJECT(ModuleGateway) - -SET(CMAKE_CXX_STANDARD 23) - -FIND_PACKAGE(fleet-protocol-interface 2.0.0 REQUIRED) - -FILE(GLOB_RECURSE memory_management_cpp_files "./source/*") -ADD_LIBRARY(memory_management STATIC ${memory_management_cpp_files}) -TARGET_LINK_LIBRARIES(memory_management PUBLIC fleet-protocol-interface::common-headers-interface) \ No newline at end of file diff --git a/source/bringauto/modules/memory_management/source/memory_management.cpp b/source/bringauto/modules/memory_management/source/memory_management.cpp deleted file mode 100644 index bb270e82..00000000 --- a/source/bringauto/modules/memory_management/source/memory_management.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include - -#include -#include - - -int allocate(struct buffer *buffer_pointer, size_t size_in_bytes){ - try{ - buffer_pointer->data = new char[size_in_bytes](); - } catch(std::bad_alloc&){ - return NOT_OK; - } - buffer_pointer->size_in_bytes = size_in_bytes; - return OK; -} - -void deallocate(struct buffer *buffer_pointer){ - delete[] static_cast(buffer_pointer->data); - buffer_pointer->data = nullptr; - buffer_pointer->size_in_bytes = 0; -} \ No newline at end of file diff --git a/source/bringauto/structures/ModuleLibrary.cpp b/source/bringauto/structures/ModuleLibrary.cpp index 3dd1e227..b8fa6b31 100644 --- a/source/bringauto/structures/ModuleLibrary.cpp +++ b/source/bringauto/structures/ModuleLibrary.cpp @@ -29,7 +29,7 @@ void ModuleLibrary::loadLibraries(const std::unordered_map &libPaths, const std::filesystem::path &moduleBinaryPath) { std::shared_ptr handler; for(auto const &[key, path]: libPaths) { - handler = std::make_shared(moduleBinaryPath); + handler = std::make_shared(moduleBinaryPath, key); handler->loadLibrary(path); if(handler->getModuleNumber() != key) { settings::Logger::logError("Module number from shared library {} does not match the module number from config. Config: {}, binary: {}.", path.string(), key, handler->getModuleNumber()); From fe33fa3f4d49bf808f8d064c69a4a74ffc1df294 Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Tue, 4 Nov 2025 12:52:16 +0100 Subject: [PATCH 21/55] added error handling to async module --- .../ModuleManagerLibraryHandlerAsync.cpp | 57 ++++++++++++------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp index 61d1531c..f588c5ce 100644 --- a/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp +++ b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp @@ -52,12 +52,12 @@ void ModuleManagerLibraryHandlerAsync::loadLibrary(const std::filesystem::path & int ModuleManagerLibraryHandlerAsync::getModuleNumber() { std::lock_guard lock { getModuleNumberMutex_ }; - return aeronClient.callFunc(getModuleNumberAsync).value(); + return aeronClient.callFunc(getModuleNumberAsync).value_or(NOT_OK); } int ModuleManagerLibraryHandlerAsync::isDeviceTypeSupported(unsigned int device_type) { std::lock_guard lock { isDeviceTypeSupportedMutex_ }; - return aeronClient.callFunc(isDeviceTypeSupportedAsync, device_type).value(); + return aeronClient.callFunc(isDeviceTypeSupportedAsync, device_type).value_or(NOT_OK); } int ModuleManagerLibraryHandlerAsync::sendStatusCondition(const Buffer ¤t_status, @@ -74,7 +74,7 @@ int ModuleManagerLibraryHandlerAsync::sendStatusCondition(const Buffer ¤t_ new_status_raw_buffer = new_status.getStructBuffer(); } - return aeronClient.callFunc(sendStatusConditionAsync, current_status_raw_buffer, new_status_raw_buffer, device_type).value(); + return aeronClient.callFunc(sendStatusConditionAsync, current_status_raw_buffer, new_status_raw_buffer, device_type).value_or(NOT_OK); } int ModuleManagerLibraryHandlerAsync::generateCommand(Buffer &generated_command, @@ -100,14 +100,18 @@ int ModuleManagerLibraryHandlerAsync::generateCommand(Buffer &generated_command, new_status_raw_buffer, current_status_raw_buffer, current_command_raw_buffer, - device_type).value(); + device_type); - if (ret.returnCode == OK) { - generated_command = constructBufferByTakeOwnership(ret.buffer); + if (!ret.has_value()) { + return NOT_OK; + } + + if (ret.value().returnCode == OK) { + generated_command = constructBufferByTakeOwnership(ret.value().buffer); } else { generated_command = constructBuffer(); } - return ret.returnCode; + return ret.value().returnCode; } int ModuleManagerLibraryHandlerAsync::aggregateStatus(Buffer &aggregated_status, @@ -124,15 +128,18 @@ int ModuleManagerLibraryHandlerAsync::aggregateStatus(Buffer &aggregated_status, new_status_raw_buffer = new_status.getStructBuffer(); } - auto ret = aeronClient.callFunc(aggregateStatusAsync, current_status_raw_buffer, new_status_raw_buffer, device_type).value(); - if (ret.returnCode == OK) { - aggregated_status = constructBufferByTakeOwnership(ret.buffer); + auto ret = aeronClient.callFunc(aggregateStatusAsync, current_status_raw_buffer, new_status_raw_buffer, device_type); + if (!ret.has_value()) { + return NOT_OK; + } + if (ret.value().returnCode == OK) { + aggregated_status = constructBufferByTakeOwnership(ret.value().buffer); } else { // Needed to properly free the allocated buffer memory - auto invalid_buffer = constructBufferByTakeOwnership(ret.buffer); + auto invalid_buffer = constructBufferByTakeOwnership(ret.value().buffer); aggregated_status = current_status; } - return ret.returnCode; + return ret.value().returnCode; } int ModuleManagerLibraryHandlerAsync::aggregateError(Buffer &error_message, @@ -149,24 +156,30 @@ int ModuleManagerLibraryHandlerAsync::aggregateError(Buffer &error_message, status_raw_buffer = status.getStructBuffer(); } - auto ret = aeronClient.callFunc(aggregateErrorAsync, current_error_raw_buffer, status_raw_buffer, device_type).value(); - if (ret.returnCode == OK) { - error_message = constructBufferByTakeOwnership(ret.buffer); + auto ret = aeronClient.callFunc(aggregateErrorAsync, current_error_raw_buffer, status_raw_buffer, device_type); + if (!ret.has_value()) { + return NOT_OK; + } + if (ret.value().returnCode == OK) { + error_message = constructBufferByTakeOwnership(ret.value().buffer); } else { error_message = constructBuffer(); } - return ret.returnCode; + return ret.value().returnCode; } int ModuleManagerLibraryHandlerAsync::generateFirstCommand(Buffer &default_command, unsigned int device_type) { std::lock_guard lock { generateFirstCommandMutex_ }; - auto ret = aeronClient.callFunc(generateFirstCommandAsync, device_type).value(); - if (ret.returnCode == OK) { - default_command = constructBufferByTakeOwnership(ret.buffer); + auto ret = aeronClient.callFunc(generateFirstCommandAsync, device_type); + if (!ret.has_value()) { + return NOT_OK; + } + if (ret.value().returnCode == OK) { + default_command = constructBufferByTakeOwnership(ret.value().buffer); } else { default_command = constructBuffer(); } - return ret.returnCode; + return ret.value().returnCode; } int ModuleManagerLibraryHandlerAsync::statusDataValid(const Buffer &status, unsigned int device_type) { @@ -176,7 +189,7 @@ int ModuleManagerLibraryHandlerAsync::statusDataValid(const Buffer &status, unsi status_raw_buffer = status.getStructBuffer(); } - return aeronClient.callFunc(statusDataValidAsync, status_raw_buffer, device_type).value(); + return aeronClient.callFunc(statusDataValidAsync, status_raw_buffer, device_type).value_or(NOT_OK); } int ModuleManagerLibraryHandlerAsync::commandDataValid(const Buffer &command, unsigned int device_type) { @@ -186,7 +199,7 @@ int ModuleManagerLibraryHandlerAsync::commandDataValid(const Buffer &command, un command_raw_buffer = command.getStructBuffer(); } - return aeronClient.callFunc(commandDataValidAsync, command_raw_buffer, device_type).value(); + return aeronClient.callFunc(commandDataValidAsync, command_raw_buffer, device_type).value_or(NOT_OK); } int ModuleManagerLibraryHandlerAsync::allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const { From 659de1777e48502f28ddcbdfb0f16d4d68b13cd9 Mon Sep 17 00:00:00 2001 From: MarioIvancik Date: Fri, 21 Nov 2025 16:07:06 +0100 Subject: [PATCH 22/55] use function definitions from fleet protocol cpp --- CMakeLists.txt | 2 +- cmake/Dependencies.cmake | 2 +- .../ModuleManagerLibraryHandlerAsync.hpp | 104 ++---------------- .../ModuleManagerLibraryHandlerAsync.cpp | 73 ++++++------ 4 files changed, 46 insertions(+), 135 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ac6eb24e..9a84157b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,7 @@ FIND_PACKAGE(eclipse-paho-mqtt-c REQUIRED) FIND_PACKAGE(libbringauto_logger 2.0.0 REQUIRED) FIND_PACKAGE(fleet-protocol-interface 2.0.0 REQUIRED) FIND_PACKAGE(ZLIB 1.2.11 REQUIRED) -FIND_PACKAGE(fleet-protocol-cxx-helpers-static 1.1.1 REQUIRED) +FIND_PACKAGE(fleet-protocol-cxx-helpers-static 1.2.0 REQUIRED) FIND_PACKAGE(aeron 1.48.6 REQUIRED) FIND_PACKAGE(async-function-execution-shared 1.0.0 REQUIRED) diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index e57bd000..ea369eff 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -9,7 +9,7 @@ BA_PACKAGE_LIBRARY(ba-logger v2.0.0) BA_PACKAGE_LIBRARY(pahomqttc v1.3.9) BA_PACKAGE_LIBRARY(pahomqttcpp v1.3.2) BA_PACKAGE_LIBRARY(zlib v1.2.11 OUTPUT_PATH_VAR ZLIB_DIR) -BA_PACKAGE_LIBRARY(fleet-protocol-cpp v1.1.1) +BA_PACKAGE_LIBRARY(fleet-protocol-cpp v1.2.0) BA_PACKAGE_LIBRARY(aeron v1.48.6) BA_PACKAGE_LIBRARY(async-function-execution v1.0.0) diff --git a/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp b/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp index 73fe9827..558da78c 100644 --- a/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp +++ b/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp @@ -1,110 +1,18 @@ #pragma once #include +#include #include +#include #include #include - namespace bringauto::modules { -struct ConvertibleBufferReturn final { - int returnCode {}; - struct ::buffer buffer {}; - ConvertibleBufferReturn() = default; - ConvertibleBufferReturn(int code, struct ::buffer buff) : returnCode(code), buffer(buff) {} - - std::span serialize() const { - size_t total_size = sizeof(int) + buffer.size_in_bytes; - uint8_t* data = new uint8_t[total_size]; - std::memcpy(data, &returnCode, sizeof(int)); - std::memcpy(data + sizeof(int), buffer.data, buffer.size_in_bytes); - return {data, total_size}; - } - void deserialize(std::span bytes) { - auto size = bytes.size(); - if (size < sizeof(int)) return; - std::memcpy(&returnCode, bytes.data(), sizeof(int)); - size -= sizeof(int); - buffer.data = new uint8_t[size]; - buffer.size_in_bytes = size; - std::memcpy(buffer.data, bytes.data() + sizeof(int), size); - buffer.size_in_bytes = size; - } -}; - -struct ConvertibleBuffer final { - struct ::buffer buffer {}; - ConvertibleBuffer() = default; - ConvertibleBuffer(struct ::buffer buff) : buffer(buff) {} - - std::span serialize() const { - return std::span {reinterpret_cast(buffer.data), buffer.size_in_bytes}; - } - void deserialize(std::span bytes) { - buffer.data = const_cast(bytes.data()); - buffer.size_in_bytes = bytes.size(); - } -}; - -inline static const async_function_execution::FunctionDefinition getModuleNumberAsync { - async_function_execution::FunctionId { 0 }, - async_function_execution::Return { int {} }, - async_function_execution::Arguments {} -}; - -inline static const async_function_execution::FunctionDefinition isDeviceTypeSupportedAsync { - async_function_execution::FunctionId { 1 }, - async_function_execution::Return { int {} }, - async_function_execution::Arguments { uint32_t {} } -}; - -inline static const async_function_execution::FunctionDefinition sendStatusConditionAsync { - async_function_execution::FunctionId { 2 }, - async_function_execution::Return { int {} }, - async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } -}; - -inline static const async_function_execution::FunctionDefinition generateCommandAsync { - async_function_execution::FunctionId { 3 }, - async_function_execution::Return { ConvertibleBufferReturn {} }, - async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } -}; - -inline static const async_function_execution::FunctionDefinition aggregateStatusAsync { - async_function_execution::FunctionId { 4 }, - async_function_execution::Return { ConvertibleBufferReturn {} }, - async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } -}; - -inline static const async_function_execution::FunctionDefinition aggregateErrorAsync { - async_function_execution::FunctionId { 5 }, - async_function_execution::Return { ConvertibleBufferReturn {} }, - async_function_execution::Arguments { ConvertibleBuffer {}, ConvertibleBuffer {}, uint32_t {} } -}; - -inline static const async_function_execution::FunctionDefinition generateFirstCommandAsync { - async_function_execution::FunctionId { 6 }, - async_function_execution::Return { ConvertibleBufferReturn {} }, - async_function_execution::Arguments { uint32_t {} } -}; - -inline static const async_function_execution::FunctionDefinition statusDataValidAsync { - async_function_execution::FunctionId { 7 }, - async_function_execution::Return { int {} }, - async_function_execution::Arguments { ConvertibleBuffer {}, uint32_t {} } -}; - -inline static const async_function_execution::FunctionDefinition commandDataValidAsync { - async_function_execution::FunctionId { 8 }, - async_function_execution::Return { int {} }, - async_function_execution::Arguments { ConvertibleBuffer {}, uint32_t {} } -}; - /** * @brief Class used to load and handle library created by module maintainer */ @@ -202,6 +110,14 @@ class ModuleManagerLibraryHandlerAsync : public IModuleManagerLibraryHandler { std::mutex generateFirstCommandMutex_ {}; std::mutex statusDataValidMutex_ {}; std::mutex commandDataValidMutex_ {}; + + fleet_protocol::cxx::ModuleFunctionExecutor aeronClient { + async_function_execution::Config { + .isProducer = true, + .defaultTimeout = settings::AeronClientConstants::aeron_client_default_timeout, + }, + fleet_protocol::cxx::moduleFunctionList + }; }; } diff --git a/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp index f588c5ce..ffea062c 100644 --- a/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp +++ b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp @@ -1,5 +1,4 @@ #include -#include #include @@ -9,24 +8,6 @@ namespace bringauto::modules { -async_function_execution::AsyncFunctionExecutor aeronClient { - async_function_execution::Config { - .isProducer = true, - .defaultTimeout = settings::AeronClientConstants::aeron_client_default_timeout, - }, - async_function_execution::FunctionList { - getModuleNumberAsync, - isDeviceTypeSupportedAsync, - sendStatusConditionAsync, - generateCommandAsync, - aggregateStatusAsync, - aggregateErrorAsync, - generateFirstCommandAsync, - statusDataValidAsync, - commandDataValidAsync - } -}; - ModuleManagerLibraryHandlerAsync::ModuleManagerLibraryHandlerAsync(const std::filesystem::path &moduleBinaryPath, const int moduleNumber) : moduleBinaryPath_ { moduleBinaryPath } { aeronClient.connect(moduleNumber); @@ -52,20 +33,21 @@ void ModuleManagerLibraryHandlerAsync::loadLibrary(const std::filesystem::path & int ModuleManagerLibraryHandlerAsync::getModuleNumber() { std::lock_guard lock { getModuleNumberMutex_ }; - return aeronClient.callFunc(getModuleNumberAsync).value_or(NOT_OK); + return aeronClient.callFunc(fleet_protocol::cxx::getModuleNumberAsync).value_or(NOT_OK); } int ModuleManagerLibraryHandlerAsync::isDeviceTypeSupported(unsigned int device_type) { std::lock_guard lock { isDeviceTypeSupportedMutex_ }; - return aeronClient.callFunc(isDeviceTypeSupportedAsync, device_type).value_or(NOT_OK); + return aeronClient.callFunc(fleet_protocol::cxx::isDeviceTypeSupportedAsync, + device_type).value_or(NOT_OK); } int ModuleManagerLibraryHandlerAsync::sendStatusCondition(const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) { std::lock_guard lock { isDeviceTypeSupportedMutex_ }; - ConvertibleBuffer current_status_raw_buffer; - ConvertibleBuffer new_status_raw_buffer; + fleet_protocol::cxx::ConvertibleBuffer current_status_raw_buffer; + fleet_protocol::cxx::ConvertibleBuffer new_status_raw_buffer; if (current_status.isAllocated()) { current_status_raw_buffer = current_status.getStructBuffer(); @@ -74,7 +56,10 @@ int ModuleManagerLibraryHandlerAsync::sendStatusCondition(const Buffer ¤t_ new_status_raw_buffer = new_status.getStructBuffer(); } - return aeronClient.callFunc(sendStatusConditionAsync, current_status_raw_buffer, new_status_raw_buffer, device_type).value_or(NOT_OK); + return aeronClient.callFunc(fleet_protocol::cxx::sendStatusConditionAsync, + current_status_raw_buffer, + new_status_raw_buffer, + device_type).value_or(NOT_OK); } int ModuleManagerLibraryHandlerAsync::generateCommand(Buffer &generated_command, @@ -82,9 +67,9 @@ int ModuleManagerLibraryHandlerAsync::generateCommand(Buffer &generated_command, const Buffer ¤t_status, const Buffer ¤t_command, unsigned int device_type) { std::lock_guard lock { generateCommandMutex_ }; - ConvertibleBuffer new_status_raw_buffer; - ConvertibleBuffer current_status_raw_buffer; - ConvertibleBuffer current_command_raw_buffer; + fleet_protocol::cxx::ConvertibleBuffer new_status_raw_buffer; + fleet_protocol::cxx::ConvertibleBuffer current_status_raw_buffer; + fleet_protocol::cxx::ConvertibleBuffer current_command_raw_buffer; if (new_status.isAllocated()) { new_status_raw_buffer = new_status.getStructBuffer(); @@ -96,7 +81,7 @@ int ModuleManagerLibraryHandlerAsync::generateCommand(Buffer &generated_command, current_command_raw_buffer = current_command.getStructBuffer(); } - auto ret = aeronClient.callFunc(generateCommandAsync, + auto ret = aeronClient.callFunc(fleet_protocol::cxx::generateCommandAsync, new_status_raw_buffer, current_status_raw_buffer, current_command_raw_buffer, @@ -118,8 +103,8 @@ int ModuleManagerLibraryHandlerAsync::aggregateStatus(Buffer &aggregated_status, const Buffer ¤t_status, const Buffer &new_status, unsigned int device_type) { std::lock_guard lock { aggregateStatusMutex_ }; - ConvertibleBuffer current_status_raw_buffer; - ConvertibleBuffer new_status_raw_buffer; + fleet_protocol::cxx::ConvertibleBuffer current_status_raw_buffer; + fleet_protocol::cxx::ConvertibleBuffer new_status_raw_buffer; if (current_status.isAllocated()) { current_status_raw_buffer = current_status.getStructBuffer(); @@ -128,7 +113,10 @@ int ModuleManagerLibraryHandlerAsync::aggregateStatus(Buffer &aggregated_status, new_status_raw_buffer = new_status.getStructBuffer(); } - auto ret = aeronClient.callFunc(aggregateStatusAsync, current_status_raw_buffer, new_status_raw_buffer, device_type); + auto ret = aeronClient.callFunc(fleet_protocol::cxx::aggregateStatusAsync, + current_status_raw_buffer, + new_status_raw_buffer, + device_type); if (!ret.has_value()) { return NOT_OK; } @@ -146,8 +134,8 @@ int ModuleManagerLibraryHandlerAsync::aggregateError(Buffer &error_message, const Buffer ¤t_error_message, const Buffer &status, unsigned int device_type) { std::lock_guard lock { aggregateErrorMutex_ }; - ConvertibleBuffer current_error_raw_buffer; - ConvertibleBuffer status_raw_buffer; + fleet_protocol::cxx::ConvertibleBuffer current_error_raw_buffer; + fleet_protocol::cxx::ConvertibleBuffer status_raw_buffer; if (current_error_message.isAllocated()) { current_error_raw_buffer = current_error_message.getStructBuffer(); @@ -156,7 +144,10 @@ int ModuleManagerLibraryHandlerAsync::aggregateError(Buffer &error_message, status_raw_buffer = status.getStructBuffer(); } - auto ret = aeronClient.callFunc(aggregateErrorAsync, current_error_raw_buffer, status_raw_buffer, device_type); + auto ret = aeronClient.callFunc(fleet_protocol::cxx::aggregateErrorAsync, + current_error_raw_buffer, + status_raw_buffer, + device_type); if (!ret.has_value()) { return NOT_OK; } @@ -170,7 +161,7 @@ int ModuleManagerLibraryHandlerAsync::aggregateError(Buffer &error_message, int ModuleManagerLibraryHandlerAsync::generateFirstCommand(Buffer &default_command, unsigned int device_type) { std::lock_guard lock { generateFirstCommandMutex_ }; - auto ret = aeronClient.callFunc(generateFirstCommandAsync, device_type); + auto ret = aeronClient.callFunc(fleet_protocol::cxx::generateFirstCommandAsync, device_type); if (!ret.has_value()) { return NOT_OK; } @@ -184,22 +175,26 @@ int ModuleManagerLibraryHandlerAsync::generateFirstCommand(Buffer &default_comma int ModuleManagerLibraryHandlerAsync::statusDataValid(const Buffer &status, unsigned int device_type) { std::lock_guard lock { statusDataValidMutex_ }; - ConvertibleBuffer status_raw_buffer; + fleet_protocol::cxx::ConvertibleBuffer status_raw_buffer; if (status.isAllocated()) { status_raw_buffer = status.getStructBuffer(); } - return aeronClient.callFunc(statusDataValidAsync, status_raw_buffer, device_type).value_or(NOT_OK); + return aeronClient.callFunc(fleet_protocol::cxx::statusDataValidAsync, + status_raw_buffer, + device_type).value_or(NOT_OK); } int ModuleManagerLibraryHandlerAsync::commandDataValid(const Buffer &command, unsigned int device_type) { std::lock_guard lock { commandDataValidMutex_ }; - ConvertibleBuffer command_raw_buffer; + fleet_protocol::cxx::ConvertibleBuffer command_raw_buffer; if (command.isAllocated()) { command_raw_buffer = command.getStructBuffer(); } - return aeronClient.callFunc(commandDataValidAsync, command_raw_buffer, device_type).value_or(NOT_OK); + return aeronClient.callFunc(fleet_protocol::cxx::commandDataValidAsync, + command_raw_buffer, + device_type).value_or(NOT_OK); } int ModuleManagerLibraryHandlerAsync::allocate(struct buffer *buffer_pointer, size_t size_in_bytes) const { From 46c7c2479c6a957b12624b051e53e68f40dcbc69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladislav=20R=C3=BDgl?= Date: Wed, 3 Dec 2025 14:36:18 +0100 Subject: [PATCH 23/55] Check if module defined in external-connection endpoint exists in module-paths --- .../connection/ExternalConnection.cpp | 10 +++++++++- source/bringauto/settings/SettingsParser.cpp | 14 ++++++++++++++ test/source/SettingsParserTests.cpp | 18 ++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/source/bringauto/external_client/connection/ExternalConnection.cpp b/source/bringauto/external_client/connection/ExternalConnection.cpp index e7f58c2a..df3606ee 100644 --- a/source/bringauto/external_client/connection/ExternalConnection.cpp +++ b/source/bringauto/external_client/connection/ExternalConnection.cpp @@ -416,7 +416,15 @@ std::vector ExternalConnection::getAllConnecte std::vector devices {}; for(const auto &moduleNumber: settings_.modules) { std::list unique_devices {}; - const int ret = moduleLibrary_.statusAggregators.at(moduleNumber)->get_unique_devices(unique_devices); + auto statusAggregatorItr = moduleLibrary_.statusAggregators.find(moduleNumber); + + if (statusAggregatorItr == moduleLibrary_.statusAggregators.end()) + { + log::logWarning("Module {} is defined in external-connection endpoint but is not specified in module-paths"); + continue; + } + + const int ret = (*statusAggregatorItr).second->get_unique_devices(unique_devices); if(ret <= 0) { log::logWarning("Module {} does not have any connected devices", moduleNumber); continue; diff --git a/source/bringauto/settings/SettingsParser.cpp b/source/bringauto/settings/SettingsParser.cpp index 9a09d46e..ae84d3b3 100644 --- a/source/bringauto/settings/SettingsParser.cpp +++ b/source/bringauto/settings/SettingsParser.cpp @@ -5,6 +5,7 @@ #include #include +#include @@ -108,6 +109,19 @@ bool SettingsParser::areSettingsCorrect() const { isCorrect = false; } + std::ranges::any_of(settings_->externalConnectionSettingsList, [&](auto& externalConnectionSettings){ + return std::ranges::any_of(externalConnectionSettings.modules, [&](auto const& externalModuleId) { + bool isMissing = !settings_->modulePaths.contains(externalModuleId); + if (isMissing) + { + std::cerr << "Module " << externalModuleId << + " is defined in external-connection endpoint modules but is not specified in module-paths" << std::endl; + isCorrect = false; + } + return isMissing; + }); + }); + return isCorrect; } diff --git a/test/source/SettingsParserTests.cpp b/test/source/SettingsParserTests.cpp index 42af8b08..cd25d550 100644 --- a/test/source/SettingsParserTests.cpp +++ b/test/source/SettingsParserTests.cpp @@ -185,3 +185,21 @@ TEST_F(SettingsParserTests, InvalidProtocol){ EXPECT_TRUE(result); EXPECT_TRUE(settingsParser.getSettings()->externalConnectionSettingsList.empty()); } + +/** + * @brief Test if modules specified in endpoint missing in module-paths are handled correctly + */ +TEST_F(SettingsParserTests, MissingModules){ + testing_utils::ConfigMock::Config config {}; + config.module_paths = { {1, "/path/to/lib1.so"}, {2, "/path/to/lib2.so"}, {3, "/path/to/lib3.so"} }; + config.external_connection.endpoint.modules = { 1, 2, 3, 4}; + + bool failed = false; + try { + parseConfig(config); + }catch (std::invalid_argument &e){ + EXPECT_STREQ(e.what(), "Arguments are not correct."); + failed = true; + } + EXPECT_TRUE(failed); +} \ No newline at end of file From 933b21701f05fe9db8e9e3ff724be1d2cb6e7c4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladislav=20R=C3=BDgl?= Date: Fri, 5 Dec 2025 10:36:42 +0100 Subject: [PATCH 24/55] dont ignore nodiscard value in std::ranged::any_of --- source/bringauto/settings/SettingsParser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/bringauto/settings/SettingsParser.cpp b/source/bringauto/settings/SettingsParser.cpp index ae84d3b3..ae2e3321 100644 --- a/source/bringauto/settings/SettingsParser.cpp +++ b/source/bringauto/settings/SettingsParser.cpp @@ -109,14 +109,13 @@ bool SettingsParser::areSettingsCorrect() const { isCorrect = false; } - std::ranges::any_of(settings_->externalConnectionSettingsList, [&](auto& externalConnectionSettings){ + isCorrect &= !std::ranges::any_of(settings_->externalConnectionSettingsList, [&](auto& externalConnectionSettings){ return std::ranges::any_of(externalConnectionSettings.modules, [&](auto const& externalModuleId) { bool isMissing = !settings_->modulePaths.contains(externalModuleId); if (isMissing) { std::cerr << "Module " << externalModuleId << " is defined in external-connection endpoint modules but is not specified in module-paths" << std::endl; - isCorrect = false; } return isMissing; }); From 3787807661c1e113716c8a58fc4772626d3feb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladislav=20R=C3=BDgl?= Date: Fri, 5 Dec 2025 12:35:00 +0100 Subject: [PATCH 25/55] Added missing param in logWarning --- .../external_client/connection/ExternalConnection.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/bringauto/external_client/connection/ExternalConnection.cpp b/source/bringauto/external_client/connection/ExternalConnection.cpp index df3606ee..4051818f 100644 --- a/source/bringauto/external_client/connection/ExternalConnection.cpp +++ b/source/bringauto/external_client/connection/ExternalConnection.cpp @@ -420,7 +420,8 @@ std::vector ExternalConnection::getAllConnecte if (statusAggregatorItr == moduleLibrary_.statusAggregators.end()) { - log::logWarning("Module {} is defined in external-connection endpoint but is not specified in module-paths"); + log::logWarning("Module {} is defined in external-connection endpoint but is not specified in module-paths", + moduleNumber); continue; } From 4a82a9846120ce811f8c8a916b771af4fda5ca33 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Thu, 18 Dec 2025 11:08:23 +0000 Subject: [PATCH 26/55] Introduce QuicCommunication: Add basic connection of QUIC protocol support to ExternalClient --- CMakeLists.txt | 2 + include/bringauto/common_utils/EnumUtils.hpp | 2 + .../communication/QuicCommunication.hpp | 94 ++++++++++ include/bringauto/settings/Constants.hpp | 2 + .../structures/ExternalConnectionSettings.hpp | 1 + source/bringauto/common_utils/EnumUtils.cpp | 2 + .../external_client/ExternalClient.cpp | 6 + .../communication/QuicCommunication.cpp | 170 ++++++++++++++++++ source/bringauto/settings/SettingsParser.cpp | 18 +- 9 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 include/bringauto/external_client/connection/communication/QuicCommunication.hpp create mode 100644 source/bringauto/external_client/connection/communication/QuicCommunication.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a84157b..4ff6e19b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,7 @@ FIND_PACKAGE(ZLIB 1.2.11 REQUIRED) FIND_PACKAGE(fleet-protocol-cxx-helpers-static 1.2.0 REQUIRED) FIND_PACKAGE(aeron 1.48.6 REQUIRED) FIND_PACKAGE(async-function-execution-shared 1.0.0 REQUIRED) +FIND_PACKAGE(msquic CONFIG REQUIRED) FILE(GLOB_RECURSE source_files "source/*") ADD_LIBRARY(module-gateway-lib STATIC "${source_files}") @@ -76,6 +77,7 @@ TARGET_LINK_LIBRARIES(module-gateway-lib PUBLIC ZLIB::ZLIB fleet-protocol-cxx-helpers-static::fleet-protocol-cxx-helpers-static async-function-execution-shared::async-function-execution-shared + msquic ${CMAKE_DL_LIBS} ) diff --git a/include/bringauto/common_utils/EnumUtils.hpp b/include/bringauto/common_utils/EnumUtils.hpp index cb6b0bc5..6618c364 100644 --- a/include/bringauto/common_utils/EnumUtils.hpp +++ b/include/bringauto/common_utils/EnumUtils.hpp @@ -32,6 +32,8 @@ class EnumUtils { switch(toString) { case structures::ProtocolType::MQTT: return settings::Constants::MQTT; + case structures::ProtocolType::QUIC: + return settings::Constants::QUIC; case structures::ProtocolType::DUMMY: return settings::Constants::DUMMY; case structures::ProtocolType::INVALID: diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp new file mode 100644 index 00000000..7a49c345 --- /dev/null +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -0,0 +1,94 @@ +#pragma once + +#include + +#include + +namespace bringauto::external_client::connection::communication { + +class QuicCommunication: public ICommunicationChannel { +public: + explicit QuicCommunication(const structures::ExternalConnectionSettings &settings, const std::string &company, + const std::string &vehicleName); + + ~QuicCommunication() override; + + /// void setProperties(const std::string &company, const std::string &vehicleName); + + void initializeConnection() override; + + bool sendMessage(ExternalProtocol::ExternalClient *message) override; + + std::shared_ptr receiveMessage() override; + + void closeConnection() override; + +private: + const QUIC_API_TABLE* quic_ { nullptr }; + HQUIC registration_ { nullptr }; + HQUIC config_ { nullptr }; + HQUIC connection_ { nullptr }; + std::string alpn_; + QUIC_BUFFER alpnBuffer_ {}; + + QUIC_CERTIFICATE_FILE certificate_ {}; + QUIC_CREDENTIAL_CONFIG credential_ {}; + + + + std::string host_; + uint16_t port_ { 0 }; + std::string certFile_; + std::string keyFile_; + std::string caFile_; + + static QUIC_STATUS QUIC_API connectionCallback(HQUIC connection, void *context, QUIC_CONNECTION_EVENT *event); + + /// void connect(); + + // /** + // * @brief Create a client id from company name and vehicle name + // * + // * @param company name of the company + // * @param vehicleName name of the vehicle + // * @return std::string + // */ + // static std::string createClientId(const std::string &company, const std::string &vehicleName); + + // /** + // * @brief Create a publish topic from company name and vehicle name + // * + // * @param company name of the company + // * @param vehicleName name of the vehicle + // * @return std::string + // */ + // static std::string createPublishTopic(const std::string &company, const std::string &vehicleName); + + // /** + // * @brief Create a subscribe topic from company name and vehicle name + // * + // * @param company name of the company + // * @param vehicleName name of the vehicle + // * @return std::string + // */ + // static std::string createSubscribeTopic(const std::string &company, const std::string &vehicleName); + + // /// MQTT client handling the connection + // std::unique_ptr client_ { nullptr }; + // /// Unique ID of the client, changes with every connection + // std::string clientId_ {}; + // /// Topic to publish messages to, sender is external client, receiver is external server + // std::string publishTopic_ {}; + // /// Topic to subscribe to, sender is external server, receiver is external client + // std::string subscribeTopic_ {}; + // /// MQTT library connection options + // mqtt::connect_options connopts_ {}; + // /// Address of the MQTT server + // std::string serverAddress_ {}; + // /// MQTT QOS level. Level 1 assures that message is delivered at least once + // constexpr static int8_t qos { 1 }; + // /// Mutex to prevent deadlocks when receiving messages + // std::mutex receiveMessageMutex_ {}; +}; + +} diff --git a/include/bringauto/settings/Constants.hpp b/include/bringauto/settings/Constants.hpp index 3aa62380..2f9b1483 100644 --- a/include/bringauto/settings/Constants.hpp +++ b/include/bringauto/settings/Constants.hpp @@ -164,8 +164,10 @@ class Constants { inline static constexpr std::string_view PROTOCOL_TYPE { "protocol-type" }; inline static constexpr std::string_view MQTT { "MQTT" }; + inline static constexpr std::string_view QUIC { "QUIC" }; inline static constexpr std::string_view DUMMY { "DUMMY" }; inline static constexpr std::string_view MQTT_SETTINGS { "mqtt-settings" }; + inline static constexpr std::string_view QUIC_SETTINGS { "quic-settings" }; inline static constexpr std::string_view SSL { "ssl" }; inline static constexpr std::string_view CA_FILE { "ca-file" }; inline static constexpr std::string_view CLIENT_CERT { "client-cert" }; diff --git a/include/bringauto/structures/ExternalConnectionSettings.hpp b/include/bringauto/structures/ExternalConnectionSettings.hpp index bdc462e8..5aee40bb 100644 --- a/include/bringauto/structures/ExternalConnectionSettings.hpp +++ b/include/bringauto/structures/ExternalConnectionSettings.hpp @@ -14,6 +14,7 @@ namespace bringauto::structures { enum class ProtocolType { INVALID = -1, MQTT, + QUIC, DUMMY }; diff --git a/source/bringauto/common_utils/EnumUtils.cpp b/source/bringauto/common_utils/EnumUtils.cpp index d7ab700a..ae8976d0 100644 --- a/source/bringauto/common_utils/EnumUtils.cpp +++ b/source/bringauto/common_utils/EnumUtils.cpp @@ -10,6 +10,8 @@ structures::ProtocolType EnumUtils::stringToProtocolType(std::string toEnum) { std::transform(toEnum.begin(), toEnum.end(), toEnum.begin(), ::toupper); if(toEnum == settings::Constants::MQTT) { return structures::ProtocolType::MQTT; + } else if(toEnum == settings::Constants::QUIC) { + return structures::ProtocolType::QUIC; } else if(toEnum == settings::Constants::DUMMY) { return structures::ProtocolType::DUMMY; } diff --git a/source/bringauto/external_client/ExternalClient.cpp b/source/bringauto/external_client/ExternalClient.cpp index 65c964f4..5835491d 100644 --- a/source/bringauto/external_client/ExternalClient.cpp +++ b/source/bringauto/external_client/ExternalClient.cpp @@ -10,6 +10,7 @@ #include +#include "bringauto/external_client/connection/communication/QuicCommunication.hpp" namespace bringauto::external_client { @@ -99,6 +100,11 @@ void ExternalClient::initConnections() { connectionSettings, context_->settings->company, context_->settings->vehicleName ); break; + case structures::ProtocolType::QUIC: + communicationChannel = std::make_shared( + connectionSettings, context_->settings->company, context_->settings->vehicleName + ); + break; case structures::ProtocolType::DUMMY: communicationChannel = std::make_shared( connectionSettings diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp new file mode 100644 index 00000000..ea0445e1 --- /dev/null +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -0,0 +1,170 @@ +#include +#include + +#include + +#include + + +namespace bringauto::external_client::connection::communication { + + QuicCommunication::QuicCommunication(const structures::ExternalConnectionSettings &settings, const std::string &company, + const std::string &vehicleName) : ICommunicationChannel(settings), + certFile_(settings.protocolSettings.at(std::string(settings::Constants::CLIENT_CERT))), + keyFile_(settings.protocolSettings.at(std::string(settings::Constants::CLIENT_KEY))), + caFile_(settings.protocolSettings.at(std::string(settings::Constants::CA_FILE))) + { + std::cout << "QuicCommunication for " << vehicleName << "/" << company << std::endl; + + // TODO: Implement + + alpn_ = "sample"; + alpnBuffer_.Buffer = reinterpret_cast(alpn_.data()); + alpnBuffer_.Length = static_cast(alpn_.size()); + + QUIC_STATUS status = MsQuicOpen2(&quic_); + if (QUIC_FAILED(status)) { + throw std::runtime_error("Failed to open QUIC"); + } + + QUIC_REGISTRATION_CONFIG config {}; + config.AppName = const_cast("module-gateway-quic-client"); + config.ExecutionProfile = QUIC_EXECUTION_PROFILE_LOW_LATENCY; + + status = quic_->RegistrationOpen(&config, ®istration_); + if (QUIC_FAILED(status)) { + throw std::runtime_error("Failed to open QUIC registration"); + } + + status = quic_->ConfigurationOpen( + registration_, + &alpnBuffer_, + 1, + nullptr, + 0, + nullptr, + &config_ + ); + + if (QUIC_FAILED(status)) { + throw std::runtime_error("Failed to open QUIC configuration"); + } + + certificate_.CertificateFile = certFile_.c_str(); + certificate_.PrivateKeyFile = keyFile_.c_str(); + + credential_.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE; + credential_.Flags = QUIC_CREDENTIAL_FLAG_CLIENT | QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE; + credential_.CertificateFile = &certificate_; + credential_.CaCertificateFile = caFile_.c_str(); + + status = quic_->ConfigurationLoadCredential(config_, &credential_); + if (QUIC_FAILED(status)) { + throw std::runtime_error("Failed to load QUIC credential"); + } + + status = quic_->ConnectionOpen(registration_, connectionCallback, this, &connection_); + if (QUIC_FAILED(status)) { + throw std::runtime_error("Failed to open QUIC connection"); + } + + status = quic_->ConnectionStart( + connection_, + config_, + QUIC_ADDRESS_FAMILY_INET, + settings.serverIp.c_str(), + settings.port + ); + + if (QUIC_FAILED(status)) { + throw std::runtime_error("Failed to start QUIC connection"); + } + + // loadMsQuic(); + // initRegistration("quic_client"); + // initConfiguration(); + } + + QuicCommunication::~QuicCommunication() { + // TODO: Implement + + // stop() + } + + void QuicCommunication::initializeConnection() { + // TODO: Implement + + // initConnection(); + // isRunning_ = true; + + } + + bool QuicCommunication::sendMessage(ExternalProtocol::ExternalClient *message) { + (void) message; + return true; + + // TODO: Implement + + // send() + } + + std::shared_ptr QuicCommunication::receiveMessage() { + return nullptr; + + // TODO: Implement + + // Budeme potřebovat? + } + + void QuicCommunication::closeConnection() { + // TODO: Implement + + // stop() + } + + QUIC_STATUS QUIC_API QuicCommunication::connectionCallback(HQUIC connection, + void* context, + QUIC_CONNECTION_EVENT* event) { + auto* self = static_cast(context); + + switch (event->Type) { + case QUIC_CONNECTION_EVENT_CONNECTED: { + + std::cout << "[quic] Connected\n"; + + // self->connectionState_ = ConnectionState::Connected; + // self->tryFlushQueue(); + break; + } + + case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE: { + std::cout << "[quic] Connection shutdown complete\n"; + + self->quic_->ConnectionClose(connection); + self->connection_ = nullptr; + // self->connectionState_ = ConnectionState::Disconnected; + break; + } + + // case QUIC_CONNECTION_EVENT_RESUMPTION_TICKET_RECEIVED: { + // if (self->zeroRttMode_ == ZeroRttMode::Enabled) { + // self->resumptionTicket_.assign( + // event->RESUMPTION_TICKET_RECEIVED.ResumptionTicket, + // event->RESUMPTION_TICKET_RECEIVED.ResumptionTicket + + // event->RESUMPTION_TICKET_RECEIVED.ResumptionTicketLength + // ); + // + // std::cout << "[quic] Resumption ticket received!\n"; + // } + // break; + // } + + default: { + std::cout << "[quic] Connection event type: 0x" << std::hex << event->Type << std::dec << "\n"; + break; + } + } + + return QUIC_STATUS_SUCCESS; + } +} diff --git a/source/bringauto/settings/SettingsParser.cpp b/source/bringauto/settings/SettingsParser.cpp index ae2e3321..d149b113 100644 --- a/source/bringauto/settings/SettingsParser.cpp +++ b/source/bringauto/settings/SettingsParser.cpp @@ -186,6 +186,9 @@ void SettingsParser::fillExternalConnectionSettings(const nlohmann::json &file) case structures::ProtocolType::MQTT: settingsName = std::string(Constants::MQTT_SETTINGS); break; + case structures::ProtocolType::QUIC: + settingsName = std::string(Constants::QUIC_SETTINGS); + break; case structures::ProtocolType::DUMMY: break; case structures::ProtocolType::INVALID: @@ -200,7 +203,13 @@ void SettingsParser::fillExternalConnectionSettings(const nlohmann::json &file) if(!settingsName.empty() && endpoint.find(settingsName) != endpoint.end()) { for(auto &[key, val]: endpoint[settingsName].items()) { + if (val.is_string()) { + externalConnectionSettings.protocolSettings[key] = val.get(); + std::cout << externalConnectionSettings.protocolSettings[key] << std::endl; + continue; + } externalConnectionSettings.protocolSettings[key] = to_string(val); + std::cout << externalConnectionSettings.protocolSettings[key] << std::endl; } } @@ -241,6 +250,9 @@ std::string SettingsParser::serializeToJson() const { case structures::ProtocolType::MQTT: settingsName = std::string(Constants::MQTT_SETTINGS); break; + case structures::ProtocolType::QUIC: + settingsName = std::string(Constants::QUIC_SETTINGS); + break; case structures::ProtocolType::DUMMY: settingsName = std::string(Constants::DUMMY); break; @@ -249,7 +261,11 @@ std::string SettingsParser::serializeToJson() const { break; } for(const auto &[key, val]: endpoint.protocolSettings) { - endpointAsJson[settingsName][key] = nlohmann::json::parse(val); + if (nlohmann::json::accept(val)) { + endpointAsJson[settingsName][key] = nlohmann::json::parse(val); + continue; + } + endpointAsJson[settingsName][key] = val; } endpoints.push_back(endpointAsJson); } From 47822331f6b4f06bbf646b56fd825be7c04a610b Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Thu, 18 Dec 2025 13:27:09 +0000 Subject: [PATCH 27/55] Refactor QuicCommunication: Modularize QUIC initialization, improve logging, and implement connection management. --- .../communication/QuicCommunication.hpp | 15 +- .../communication/QuicCommunication.cpp | 189 ++++++++++-------- 2 files changed, 115 insertions(+), 89 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index 7a49c345..45080f5e 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -31,17 +31,18 @@ class QuicCommunication: public ICommunicationChannel { std::string alpn_; QUIC_BUFFER alpnBuffer_ {}; - QUIC_CERTIFICATE_FILE certificate_ {}; - QUIC_CREDENTIAL_CONFIG credential_ {}; - - - - std::string host_; - uint16_t port_ { 0 }; std::string certFile_; std::string keyFile_; std::string caFile_; + /// ---------- Connection ---------- + void loadMsQuic(); + void initRegistration(const char *appName); + void initConfiguration(); + void configurationOpen(const QUIC_SETTINGS *settings); + void configurationLoadCredential(const QUIC_CREDENTIAL_CONFIG *credential) const; + void stop(); + static QUIC_STATUS QUIC_API connectionCallback(HQUIC connection, void *context, QUIC_CONNECTION_EVENT *event); /// void connect(); diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index ea0445e1..510c811d 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -5,6 +5,8 @@ #include +#include "bringauto/settings/LoggerId.hpp" + namespace bringauto::external_client::connection::communication { @@ -14,89 +16,38 @@ namespace bringauto::external_client::connection::communication { keyFile_(settings.protocolSettings.at(std::string(settings::Constants::CLIENT_KEY))), caFile_(settings.protocolSettings.at(std::string(settings::Constants::CA_FILE))) { - std::cout << "QuicCommunication for " << vehicleName << "/" << company << std::endl; - - // TODO: Implement - alpn_ = "sample"; alpnBuffer_.Buffer = reinterpret_cast(alpn_.data()); alpnBuffer_.Length = static_cast(alpn_.size()); - QUIC_STATUS status = MsQuicOpen2(&quic_); - if (QUIC_FAILED(status)) { - throw std::runtime_error("Failed to open QUIC"); - } - - QUIC_REGISTRATION_CONFIG config {}; - config.AppName = const_cast("module-gateway-quic-client"); - config.ExecutionProfile = QUIC_EXECUTION_PROFILE_LOW_LATENCY; + settings::Logger::logInfo("Initialize QUIC connection to {}:{} for {}/{}", settings.serverIp, settings.port, company, vehicleName); - status = quic_->RegistrationOpen(&config, ®istration_); - if (QUIC_FAILED(status)) { - throw std::runtime_error("Failed to open QUIC registration"); - } - - status = quic_->ConfigurationOpen( - registration_, - &alpnBuffer_, - 1, - nullptr, - 0, - nullptr, - &config_ - ); - - if (QUIC_FAILED(status)) { - throw std::runtime_error("Failed to open QUIC configuration"); - } - - certificate_.CertificateFile = certFile_.c_str(); - certificate_.PrivateKeyFile = keyFile_.c_str(); - - credential_.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE; - credential_.Flags = QUIC_CREDENTIAL_FLAG_CLIENT | QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE; - credential_.CertificateFile = &certificate_; - credential_.CaCertificateFile = caFile_.c_str(); + loadMsQuic(); + initRegistration("module-gateway-quic-client"); + initConfiguration(); + } - status = quic_->ConfigurationLoadCredential(config_, &credential_); - if (QUIC_FAILED(status)) { - throw std::runtime_error("Failed to load QUIC credential"); - } + QuicCommunication::~QuicCommunication() { + stop(); + } - status = quic_->ConnectionOpen(registration_, connectionCallback, this, &connection_); + void QuicCommunication::initializeConnection() { + QUIC_STATUS status = quic_->ConnectionOpen(registration_, connectionCallback, this, &connection_); if (QUIC_FAILED(status)) { - throw std::runtime_error("Failed to open QUIC connection"); + settings::Logger::logError("[quic] Failed to open QUIC connection; QUIC_STATUS => {:x}", status); } status = quic_->ConnectionStart( connection_, config_, QUIC_ADDRESS_FAMILY_INET, - settings.serverIp.c_str(), - settings.port + settings_.serverIp.c_str(), + settings_.port ); if (QUIC_FAILED(status)) { - throw std::runtime_error("Failed to start QUIC connection"); + settings::Logger::logError("[quic] Failed to start QUIC connection; QUIC_STATUS => {:x}", status); } - - // loadMsQuic(); - // initRegistration("quic_client"); - // initConfiguration(); - } - - QuicCommunication::~QuicCommunication() { - // TODO: Implement - - // stop() - } - - void QuicCommunication::initializeConnection() { - // TODO: Implement - - // initConnection(); - // isRunning_ = true; - } bool QuicCommunication::sendMessage(ExternalProtocol::ExternalClient *message) { @@ -122,6 +73,94 @@ namespace bringauto::external_client::connection::communication { // stop() } + + /// ---------- Connection ---------- + void QuicCommunication::loadMsQuic() { + QUIC_STATUS status = MsQuicOpen2(&quic_); + if (QUIC_FAILED(status)) { + settings::Logger::logError("[quic] Failed to open QUIC; QUIC_STATUS => {:x}", status); + } + } + + void QuicCommunication::initRegistration(const char* appName) { + QUIC_REGISTRATION_CONFIG config {}; + config.AppName = const_cast(appName); + config.ExecutionProfile = QUIC_EXECUTION_PROFILE_LOW_LATENCY; + + QUIC_STATUS status = quic_->RegistrationOpen(&config, ®istration_); + if (QUIC_FAILED(status)) { + settings::Logger::logError("[quic] Failed to open QUIC registration; QUIC_STATUS => {:x}", status); + } + } + + void QuicCommunication::initConfiguration() { + configurationOpen(nullptr); + + QUIC_CERTIFICATE_FILE certificate {}; + certificate.CertificateFile = certFile_.c_str(); + certificate.PrivateKeyFile = keyFile_.c_str(); + + QUIC_CREDENTIAL_CONFIG credential {}; + credential.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE; + credential.Flags = QUIC_CREDENTIAL_FLAG_CLIENT | QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE; + credential.CertificateFile = &certificate; + credential.CaCertificateFile = caFile_.c_str(); + + configurationLoadCredential(&credential); + } + + void QuicCommunication::configurationOpen(const QUIC_SETTINGS* settings) { + const uint32_t settingsSize = settings ? sizeof(*settings) : 0; + + QUIC_STATUS status = quic_->ConfigurationOpen( + registration_, + &alpnBuffer_, + 1, + settings, + settingsSize, + nullptr, + &config_ + ); + + if (QUIC_FAILED(status)) { + settings::Logger::logError("[quic] Failed to open QUIC configuration; QUIC_STATUS => {:x}", status); + } + } + + void QuicCommunication::configurationLoadCredential(const QUIC_CREDENTIAL_CONFIG* credential) const { + const QUIC_STATUS status = quic_->ConfigurationLoadCredential(config_, credential); + if (QUIC_FAILED(status)) { + settings::Logger::logError("[quic] Failed to load QUIC credential; QUIC_STATUS => {:x}", status); + } + } + + void QuicCommunication::stop() { + // if (!isRunning_.exchange(false)) { + // return; + // } + + if (connection_) { + quic_->ConnectionShutdown(connection_, QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0); + quic_->ConnectionClose(connection_); + } + if (config_) { + quic_->ConfigurationClose(config_); + } + if (registration_) { + quic_->RegistrationClose(registration_); + } + if (quic_) { + MsQuicClose(quic_); + } + + settings::Logger::logInfo("[quic] Connection stopped"); + + connection_ = nullptr; + config_ = nullptr; + registration_ = nullptr; + quic_ = nullptr; + } + QUIC_STATUS QUIC_API QuicCommunication::connectionCallback(HQUIC connection, void* context, QUIC_CONNECTION_EVENT* event) { @@ -129,8 +168,7 @@ namespace bringauto::external_client::connection::communication { switch (event->Type) { case QUIC_CONNECTION_EVENT_CONNECTED: { - - std::cout << "[quic] Connected\n"; + settings::Logger::logInfo("[quic] Connected to server"); // self->connectionState_ = ConnectionState::Connected; // self->tryFlushQueue(); @@ -138,7 +176,7 @@ namespace bringauto::external_client::connection::communication { } case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE: { - std::cout << "[quic] Connection shutdown complete\n"; + settings::Logger::logInfo("[quic] Connection shutdown complete"); self->quic_->ConnectionClose(connection); self->connection_ = nullptr; @@ -146,21 +184,8 @@ namespace bringauto::external_client::connection::communication { break; } - // case QUIC_CONNECTION_EVENT_RESUMPTION_TICKET_RECEIVED: { - // if (self->zeroRttMode_ == ZeroRttMode::Enabled) { - // self->resumptionTicket_.assign( - // event->RESUMPTION_TICKET_RECEIVED.ResumptionTicket, - // event->RESUMPTION_TICKET_RECEIVED.ResumptionTicket + - // event->RESUMPTION_TICKET_RECEIVED.ResumptionTicketLength - // ); - // - // std::cout << "[quic] Resumption ticket received!\n"; - // } - // break; - // } - default: { - std::cout << "[quic] Connection event type: 0x" << std::hex << event->Type << std::dec << "\n"; + settings::Logger::logInfo("[quic] Unexpected connection event 0x{:x}", static_cast(event->Type)); break; } } From bfb354bf774823c2af7dd3917e2de7279a2ec6a8 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Mon, 22 Dec 2025 11:41:57 +0000 Subject: [PATCH 28/55] Enhance QuicCommunication: Add stream callback + message sending, implement connection state management, improve logging, and refine cleanup processes. --- .../communication/QuicCommunication.hpp | 11 + .../communication/QuicCommunication.cpp | 192 +++++++++++++++--- 2 files changed, 174 insertions(+), 29 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index 45080f5e..55e756ca 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -24,6 +24,8 @@ class QuicCommunication: public ICommunicationChannel { void closeConnection() override; private: + enum class ConnectionState : uint8_t { DISCONNECTED, CONNECTING, CONNECTED, CLOSING }; + const QUIC_API_TABLE* quic_ { nullptr }; HQUIC registration_ { nullptr }; HQUIC config_ { nullptr }; @@ -35,6 +37,8 @@ class QuicCommunication: public ICommunicationChannel { std::string keyFile_; std::string caFile_; + std::atomic connectionState_ { ConnectionState::DISCONNECTED }; + /// ---------- Connection ---------- void loadMsQuic(); void initRegistration(const char *appName); @@ -43,7 +47,14 @@ class QuicCommunication: public ICommunicationChannel { void configurationLoadCredential(const QUIC_CREDENTIAL_CONFIG *credential) const; void stop(); + /// ---------- Closing client ---------- + void closeMsQuic(); + void closeConfiguration(); + void closeRegistration(); + + /// ---------- Callbacks ---------- static QUIC_STATUS QUIC_API connectionCallback(HQUIC connection, void *context, QUIC_CONNECTION_EVENT *event); + static unsigned int streamCallback(HQUIC stream, void *context, QUIC_STREAM_EVENT *event); /// void connect(); diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 510c811d..9d2b50db 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -20,7 +20,7 @@ namespace bringauto::external_client::connection::communication { alpnBuffer_.Buffer = reinterpret_cast(alpn_.data()); alpnBuffer_.Length = static_cast(alpn_.size()); - settings::Logger::logInfo("Initialize QUIC connection to {}:{} for {}/{}", settings.serverIp, settings.port, company, vehicleName); + settings::Logger::logInfo("[quic] Initialize QUIC connection to {}:{} for {}/{}", settings.serverIp, settings.port, company, vehicleName); loadMsQuic(); initRegistration("module-gateway-quic-client"); @@ -32,6 +32,14 @@ namespace bringauto::external_client::connection::communication { } void QuicCommunication::initializeConnection() { + settings::Logger::logInfo("[quic] Connecting to server when {}", toString(connectionState_)); + + + ConnectionState expected = ConnectionState::DISCONNECTED; + if (! connectionState_.compare_exchange_strong(expected, ConnectionState::CONNECTING, std::memory_order_acq_rel)) { + return; + } + QUIC_STATUS status = quic_->ConnectionOpen(registration_, connectionCallback, this, &connection_); if (QUIC_FAILED(status)) { settings::Logger::logError("[quic] Failed to open QUIC connection; QUIC_STATUS => {:x}", status); @@ -48,15 +56,48 @@ namespace bringauto::external_client::connection::communication { if (QUIC_FAILED(status)) { settings::Logger::logError("[quic] Failed to start QUIC connection; QUIC_STATUS => {:x}", status); } + + connectionState_ = ConnectionState::CONNECTING; } bool QuicCommunication::sendMessage(ExternalProtocol::ExternalClient *message) { - (void) message; - return true; + HQUIC stream { nullptr }; + if (QUIC_FAILED(quic_->StreamOpen(connection_, QUIC_STREAM_OPEN_FLAG_NONE, streamCallback, this, &stream))) { + settings::Logger::logError("[quic] StreamOpen failed"); + return false; + } - // TODO: Implement + const auto size = message->ByteSizeLong(); + const auto buffer = std::make_unique(size); + + if (!message->SerializeToArray(buffer.get(), static_cast(size))) { + settings::Logger::logError("[quic] Message serialization failed"); + return false; + } + + auto* buf = new QUIC_BUFFER{}; + buf->Length = static_cast(size); + buf->Buffer = new uint8_t[buf->Length]; + + std::memcpy(buf->Buffer, buffer.get(), buf->Length); + + const QUIC_STATUS status = quic_->StreamSend( + stream, + buf, + 1, + QUIC_SEND_FLAG_START | QUIC_SEND_FLAG_FIN, + buf + ); - // send() + if (QUIC_FAILED(status)) { + delete[] buf->Buffer; + delete buf; + + settings::Logger::logError("[quic] Failed to send QUIC stream; QUIC_STATUS => {:x}", status); + return false; + } + + return true; } std::shared_ptr QuicCommunication::receiveMessage() { @@ -67,14 +108,8 @@ namespace bringauto::external_client::connection::communication { // Budeme potřebovat? } - void QuicCommunication::closeConnection() { - // TODO: Implement - - // stop() - } - - /// ---------- Connection ---------- + void QuicCommunication::loadMsQuic() { QUIC_STATUS status = MsQuicOpen2(&quic_); if (QUIC_FAILED(status)) { @@ -134,43 +169,61 @@ namespace bringauto::external_client::connection::communication { } } - void QuicCommunication::stop() { - // if (!isRunning_.exchange(false)) { - // return; - // } + /// ---------- Closing client ---------- - if (connection_) { - quic_->ConnectionShutdown(connection_, QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0); - quic_->ConnectionClose(connection_); + void QuicCommunication::closeConnection() { + if (! connection_) { + return; } + + quic_->ConnectionShutdown(connection_, QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0); + + // Waiting for QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE then continue in connectionCallback + } + + void QuicCommunication::closeConfiguration() { if (config_) { quic_->ConfigurationClose(config_); } + + config_ = nullptr; + } + + void QuicCommunication::closeRegistration() { if (registration_) { quic_->RegistrationClose(registration_); } + + registration_ = nullptr; + } + + void QuicCommunication::closeMsQuic() { if (quic_) { MsQuicClose(quic_); } - settings::Logger::logInfo("[quic] Connection stopped"); - - connection_ = nullptr; - config_ = nullptr; - registration_ = nullptr; quic_ = nullptr; } - QUIC_STATUS QUIC_API QuicCommunication::connectionCallback(HQUIC connection, - void* context, - QUIC_CONNECTION_EVENT* event) { + void QuicCommunication::stop() { + closeConnection(); + closeConfiguration(); + closeRegistration(); + closeMsQuic(); + + settings::Logger::logInfo("[quic] Connection stopped"); + } + + /// ---------- Callbacks ---------- + + QUIC_STATUS QUIC_API QuicCommunication::connectionCallback(HQUIC connection, void* context, QUIC_CONNECTION_EVENT* event) { auto* self = static_cast(context); switch (event->Type) { case QUIC_CONNECTION_EVENT_CONNECTED: { settings::Logger::logInfo("[quic] Connected to server"); - // self->connectionState_ = ConnectionState::Connected; + self->connectionState_ = ConnectionState::CONNECTED; // self->tryFlushQueue(); break; } @@ -179,11 +232,17 @@ namespace bringauto::external_client::connection::communication { settings::Logger::logInfo("[quic] Connection shutdown complete"); self->quic_->ConnectionClose(connection); + self->connection_ = nullptr; - // self->connectionState_ = ConnectionState::Disconnected; + self->connectionState_ = ConnectionState::DISCONNECTED; break; } + case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER: + settings::Logger::logWarning("[quic] Connection shutdown initiated"); + self->connectionState_ = ConnectionState::CLOSING; + break; + default: { settings::Logger::logInfo("[quic] Unexpected connection event 0x{:x}", static_cast(event->Type)); break; @@ -192,4 +251,79 @@ namespace bringauto::external_client::connection::communication { return QUIC_STATUS_SUCCESS; } + + QUIC_STATUS QUIC_API QuicCommunication::streamCallback(HQUIC stream, void* context, QUIC_STREAM_EVENT* event) { + auto* self = static_cast(context); + + switch (event->Type) { + case QUIC_STREAM_EVENT_RECEIVE: { + settings::Logger::logInfo("[quic] Received {:d} bytes", event->RECEIVE.TotalBufferLength); + + //quicHandle(event->RECEIVE.Buffers, event->RECEIVE.BufferCount); + self->quic_->StreamReceiveComplete(stream, event->RECEIVE.TotalBufferLength); + break; + } + + case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: { + settings::Logger::logInfo("[quic] Peer stream send shutdown"); + self->quic_->StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0); + break; + } + + case QUIC_STREAM_EVENT_START_COMPLETE: { + settings::Logger::logInfo("[quic] Stream start completed"); + break; + } + + case QUIC_STREAM_EVENT_SEND_COMPLETE: { + /** + * This event is raised when MsQuic has finished processing + * a single StreamSend request. + * + * Meaning: + * - MsQuic no longer needs the application-provided buffer: + * - the data has been acknowledged (ACKed) by the peer + * at the QUIC transport level and will not be retransmitted + * - OR the send was canceled (Canceled == TRUE), e.g. due to + * stream or connection shutdown + * + * Reliability semantics: + * - the ACK is strictly a QUIC transport-level acknowledgment + * - it does NOT mean the peer application has read or processed + * the data + * + * Practical consequence: + * - this is the only correct place to safely free the memory + * passed to StreamSend (via ClientContext) + */ + if (event->SEND_COMPLETE.Canceled) { + settings::Logger::logError("[quic] Stream send canceled"); + } else { + settings::Logger::logInfo("[quic] Stream send completed"); + } + + const auto* buf = + static_cast(event->SEND_COMPLETE.ClientContext); + + if (buf) { + delete[] buf->Buffer; + delete buf; + } + break; + } + + case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: { + settings::Logger::logInfo("[quic] Stream shutdown complete"); + self->quic_->StreamClose(stream); + break; + } + + default: { + settings::Logger::logInfo("[quic] Unexpected stream event 0x{:x}", static_cast(event->Type)); + break; + } + } + + return QUIC_STATUS_SUCCESS; + } } From 1ab7c3a49a1a7a183a76e1d9531a03ef6e796dde Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Mon, 22 Dec 2025 11:56:32 +0000 Subject: [PATCH 29/55] Add `toString` method for `ConnectionState` in QuicCommunication for improved state debugging --- .../connection/communication/QuicCommunication.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index 55e756ca..0354e432 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -101,6 +101,16 @@ class QuicCommunication: public ICommunicationChannel { // constexpr static int8_t qos { 1 }; // /// Mutex to prevent deadlocks when receiving messages // std::mutex receiveMessageMutex_ {}; + + static const char* toString(ConnectionState state) { + switch (state) { + case ConnectionState::DISCONNECTED: return "Disconnected"; + case ConnectionState::CONNECTING: return "Connecting"; + case ConnectionState::CONNECTED: return "Connected"; + case ConnectionState::CLOSING: return "Closing"; + default: return "Unknown"; + } + } }; } From b9e2a2a8788ce4f0c9e22394ba713fdc050f23a7 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Sat, 3 Jan 2026 12:13:06 +0000 Subject: [PATCH 30/55] Implement message queuing and threading in QuicCommunication for bidirectional communication --- .../communication/QuicCommunication.hpp | 23 +- .../communication/QuicCommunication.cpp | 249 ++++++++++++++---- 2 files changed, 217 insertions(+), 55 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index 0354e432..72b9c1f0 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -1,8 +1,11 @@ #pragma once +#include #include #include +#include +#include namespace bringauto::external_client::connection::communication { @@ -13,8 +16,6 @@ class QuicCommunication: public ICommunicationChannel { ~QuicCommunication() override; - /// void setProperties(const std::string &company, const std::string &vehicleName); - void initializeConnection() override; bool sendMessage(ExternalProtocol::ExternalClient *message) override; @@ -39,6 +40,18 @@ class QuicCommunication: public ICommunicationChannel { std::atomic connectionState_ { ConnectionState::DISCONNECTED }; + // inbound (server → client) + std::queue> inboundQueue_; + std::mutex inboundMutex_; + std::condition_variable inboundCv_; + + // outbound (client → server) + std::queue> outboundQueue_; + std::mutex outboundMutex_; + std::condition_variable outboundCv_; + + std::jthread senderThread_; + /// ---------- Connection ---------- void loadMsQuic(); void initRegistration(const char *appName); @@ -47,6 +60,10 @@ class QuicCommunication: public ICommunicationChannel { void configurationLoadCredential(const QUIC_CREDENTIAL_CONFIG *credential) const; void stop(); + void onMessageDecoded(std::shared_ptr msg); + + bool sendViaQuicStream(const std::shared_ptr &message); + /// ---------- Closing client ---------- void closeMsQuic(); void closeConfiguration(); @@ -56,6 +73,8 @@ class QuicCommunication: public ICommunicationChannel { static QUIC_STATUS QUIC_API connectionCallback(HQUIC connection, void *context, QUIC_CONNECTION_EVENT *event); static unsigned int streamCallback(HQUIC stream, void *context, QUIC_STREAM_EVENT *event); + void senderLoop(); + /// void connect(); // /** diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 9d2b50db..20af7893 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "bringauto/settings/LoggerId.hpp" @@ -60,52 +61,37 @@ namespace bringauto::external_client::connection::communication { connectionState_ = ConnectionState::CONNECTING; } - bool QuicCommunication::sendMessage(ExternalProtocol::ExternalClient *message) { - HQUIC stream { nullptr }; - if (QUIC_FAILED(quic_->StreamOpen(connection_, QUIC_STREAM_OPEN_FLAG_NONE, streamCallback, this, &stream))) { - settings::Logger::logError("[quic] StreamOpen failed"); - return false; - } + bool QuicCommunication::sendMessage(ExternalProtocol::ExternalClient* message) { + settings::Logger::logInfo("[quic] Enqueueing message"); + auto copy = std::make_shared(*message); - const auto size = message->ByteSizeLong(); - const auto buffer = std::make_unique(size); - - if (!message->SerializeToArray(buffer.get(), static_cast(size))) { - settings::Logger::logError("[quic] Message serialization failed"); - return false; + { + std::lock_guard lock(outboundMutex_); + outboundQueue_.push(std::move(copy)); } - - auto* buf = new QUIC_BUFFER{}; - buf->Length = static_cast(size); - buf->Buffer = new uint8_t[buf->Length]; - - std::memcpy(buf->Buffer, buffer.get(), buf->Length); - - const QUIC_STATUS status = quic_->StreamSend( - stream, - buf, - 1, - QUIC_SEND_FLAG_START | QUIC_SEND_FLAG_FIN, - buf - ); - - if (QUIC_FAILED(status)) { - delete[] buf->Buffer; - delete buf; - - settings::Logger::logError("[quic] Failed to send QUIC stream; QUIC_STATUS => {:x}", status); - return false; - } - + settings::Logger::logInfo("[quic] Notifying sender thread"); + outboundCv_.notify_one(); return true; } std::shared_ptr QuicCommunication::receiveMessage() { - return nullptr; + std::unique_lock lock(inboundMutex_); + + if (!inboundCv_.wait_for( + lock, + settings::receive_message_timeout, + [this] { return !inboundQueue_.empty() || connectionState_.load() != ConnectionState::CONNECTED; } + )) { + return nullptr; + } - // TODO: Implement + if (connectionState_.load() != ConnectionState::CONNECTED || inboundQueue_.empty()) { + return nullptr; + } - // Budeme potřebovat? + auto msg = inboundQueue_.front(); + inboundQueue_.pop(); + return msg; } /// ---------- Connection ---------- @@ -211,9 +197,72 @@ namespace bringauto::external_client::connection::communication { closeRegistration(); closeMsQuic(); + inboundCv_.notify_all(); + outboundCv_.notify_all(); + settings::Logger::logInfo("[quic] Connection stopped"); } + + /// ---------- Outgoings ---------- + void QuicCommunication::onMessageDecoded( + std::shared_ptr msg + ) { + { + std::lock_guard lock(inboundMutex_); + settings::Logger::logInfo("[quic] Moving message to inboundQueue"); + inboundQueue_.push(std::move(msg)); + } + settings::Logger::logInfo("[quic] Notifying receiver thread"); + inboundCv_.notify_one(); + } + + bool QuicCommunication::sendViaQuicStream(const std::shared_ptr& message) { + settings::Logger::logInfo("[quic] Sending message"); + + HQUIC stream { nullptr }; + if (QUIC_FAILED(quic_->StreamOpen(connection_, QUIC_STREAM_OPEN_FLAG_NONE, streamCallback, this, &stream))) { + settings::Logger::logError("[quic] StreamOpen failed"); + return false; + } + + const auto size = message->ByteSizeLong(); + const auto buffer = std::make_unique(size); + + if (!message->SerializeToArray(buffer.get(), static_cast(size))) { + settings::Logger::logError("[quic] Message serialization failed"); + return false; + } + + auto* buf = new QUIC_BUFFER{}; + buf->Length = static_cast(size); + buf->Buffer = new uint8_t[buf->Length]; + + std::memcpy(buf->Buffer, buffer.get(), buf->Length); + + const QUIC_STATUS status = quic_->StreamSend( + stream, + buf, + 1, + /** + * START => Simulates quic_->StreamStart before send + * FIN => Simulates quic_->StreamShutdown after send + */ + QUIC_SEND_FLAG_START | QUIC_SEND_FLAG_FIN, + buf + ); + + if (QUIC_FAILED(status)) { + delete[] buf->Buffer; + delete buf; + + settings::Logger::logError("[quic] Failed to send QUIC stream; QUIC_STATUS => {:x}", status); + return false; + } + + return true; + } + /// ---------- Callbacks ---------- QUIC_STATUS QUIC_API QuicCommunication::connectionCallback(HQUIC connection, void* context, QUIC_CONNECTION_EVENT* event) { @@ -223,25 +272,34 @@ namespace bringauto::external_client::connection::communication { case QUIC_CONNECTION_EVENT_CONNECTED: { settings::Logger::logInfo("[quic] Connected to server"); - self->connectionState_ = ConnectionState::CONNECTED; - // self->tryFlushQueue(); + auto expected = ConnectionState::CONNECTING; + if (self->connectionState_.compare_exchange_strong(expected, ConnectionState::CONNECTED)) { + self->senderThread_ = std::jthread(&QuicCommunication::senderLoop, self); + self->outboundCv_.notify_all(); + } break; } case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE: { settings::Logger::logInfo("[quic] Connection shutdown complete"); - self->quic_->ConnectionClose(connection); + self->connectionState_ = ConnectionState::DISCONNECTED; + self->outboundCv_.notify_all(); + if (self->senderThread_.joinable()) { + self->senderThread_.request_stop(); + } + + self->quic_->ConnectionClose(connection); self->connection_ = nullptr; - self->connectionState_ = ConnectionState::DISCONNECTED; break; } - case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER: + case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER: { settings::Logger::logWarning("[quic] Connection shutdown initiated"); self->connectionState_ = ConnectionState::CLOSING; break; + } default: { settings::Logger::logInfo("[quic] Unexpected connection event 0x{:x}", static_cast(event->Type)); @@ -256,20 +314,80 @@ namespace bringauto::external_client::connection::communication { auto* self = static_cast(context); switch (event->Type) { - case QUIC_STREAM_EVENT_RECEIVE: { - settings::Logger::logInfo("[quic] Received {:d} bytes", event->RECEIVE.TotalBufferLength); - - //quicHandle(event->RECEIVE.Buffers, event->RECEIVE.BufferCount); - self->quic_->StreamReceiveComplete(stream, event->RECEIVE.TotalBufferLength); - break; - } - - case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: { + case QUIC_STREAM_EVENT_RECEIVE: { + settings::Logger::logInfo( + "[quic] Received {:d} bytes in {:d} buffers", + event->RECEIVE.TotalBufferLength, + event->RECEIVE.BufferCount + ); + + uint64_t streamId = 0; + uint32_t streamIdLen = sizeof(streamId); + + self->quic_->GetParam( + stream, + QUIC_PARAM_STREAM_ID, + &streamIdLen, + &streamId + ); + + settings::Logger::logInfo( + "[quic] [stream {}] Event RECEIVE", + streamId + ); + + std::vector data; + data.reserve(event->RECEIVE.TotalBufferLength); + + for (uint32_t i = 0; i < event->RECEIVE.BufferCount; ++i) { + const auto& b = event->RECEIVE.Buffers[i]; + data.insert(data.end(), b.Buffer, b.Buffer + b.Length); + } + + auto msg = std::make_shared(); + if (!msg->ParseFromArray(data.data(), static_cast(data.size()))) { + settings::Logger::logError("[quic] Failed to parse ExternalServer message"); + } else { + self->onMessageDecoded(std::move(msg)); + } + + self->quic_->StreamReceiveComplete(stream, event->RECEIVE.TotalBufferLength); + + if (event->RECEIVE.Flags & QUIC_RECEIVE_FLAG_FIN) { + settings::Logger::logInfo( + "[quic] [stream {}] Peer FIN received, shutting down receive", + streamId + ); + + QUIC_STATUS status = self->quic_->StreamShutdown( + stream, + QUIC_STREAM_SHUTDOWN_FLAG_ABORT_RECEIVE, + 0 + ); + + settings::Logger::logInfo( + "[quic] [stream {}] StreamShutdown(RECEIVE) -> 0x{:x}", + streamId, + status + ); + } + + break; + } + + /// Server send FIN + case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: { settings::Logger::logInfo("[quic] Peer stream send shutdown"); - self->quic_->StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0); + //self->quic_->StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0); break; } + /// My FIN was sended to server + case QUIC_STREAM_EVENT_SEND_SHUTDOWN_COMPLETE: { + settings::Logger::logInfo("[quic] Stream send shutdown complete"); + break; + } + case QUIC_STREAM_EVENT_START_COMPLETE: { settings::Logger::logInfo("[quic] Stream start completed"); break; @@ -312,6 +430,7 @@ namespace bringauto::external_client::connection::communication { break; } + /// Stream is closed from both sides case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: { settings::Logger::logInfo("[quic] Stream shutdown complete"); self->quic_->StreamClose(stream); @@ -326,4 +445,28 @@ namespace bringauto::external_client::connection::communication { return QUIC_STATUS_SUCCESS; } + + void QuicCommunication::senderLoop() { + settings::Logger::logInfo("[quic] Sender thread loop started"); + while (connectionState_.load() == ConnectionState::CONNECTED) { + std::unique_lock lock(outboundMutex_); + + settings::Logger::logInfo("[quic] Sender thread loop waiting for outbound queue"); + outboundCv_.wait(lock, [this] { + return !outboundQueue_.empty() || + connectionState_.load() != ConnectionState::CONNECTED; + }); + + if (connectionState_.load() != ConnectionState::CONNECTED) { + break; + } + + settings::Logger::logInfo("[quic] Sender thread loop sending outbound queue"); + auto msg = outboundQueue_.front(); + outboundQueue_.pop(); + lock.unlock(); + + sendViaQuicStream(msg); + } + } } From bad6532f90986664c9926f5cea757c241f336488 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Mon, 5 Jan 2026 06:45:51 +0000 Subject: [PATCH 31/55] Refactor `QuicCommunication`: Improve comments and documentation, remove outdated code sections, and organize code structure for clarity. --- .../communication/QuicCommunication.hpp | 276 ++++++++++++++---- .../communication/QuicCommunication.cpp | 10 +- 2 files changed, 224 insertions(+), 62 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index 72b9c1f0..1bb0fc98 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -16,111 +16,277 @@ class QuicCommunication: public ICommunicationChannel { ~QuicCommunication() override; + /** + * @brief Initializes a QUIC connection to the server. + * + * Attempts to establish a new QUIC connection. + * It first atomically verifies that the current connection state is + * DISCONNECTED and transitions it to CONNECTING in order to prevent + * concurrent connection attempts. + * + * After the state transition, it opens a QUIC connection handle and + * starts the connection using the configured server address, port, + * and QUIC configuration. + * + * Any failures during the connection open or start process are logged. + */ void initializeConnection() override; + /** + * @brief Enqueues an outgoing message to be sent over the QUIC connection. + * + * Creates a shared copy of the provided ExternalClient message + * and pushes it into the outbound message queue in a thread-safe manner. + * After enqueuing, it notifies the sender thread via a condition variable + * that a new message is available for sending. + * + * @param message Pointer to the message that should be sent. + * @return true Always returns true to indicate the message was successfully enqueued. + */ bool sendMessage(ExternalProtocol::ExternalClient *message) override; + /** + * @brief Receives an incoming message from the QUIC connection. + * + * Waits for an incoming message to appear in the inbound + * queue or for the connection state to change from CONNECTED. + * The wait is bounded by a configurable timeout. + * + * If the wait times out, the connection is no longer in the CONNECTED + * state, or no message is available, the function returns nullptr. + * Otherwise, it retrieves and removes the next message from the inbound + * queue and returns it. + * + * @return A shared pointer to the received ExternalServer message, + * or nullptr if no message is available or the connection is not active. + */ std::shared_ptr receiveMessage() override; + /** + * @brief Initiates a graceful shutdown of the QUIC connection. + * + * Requests an orderly shutdown of the active QUIC connection. + * If no connection is currently established, the function returns immediately. + * + * The shutdown is performed asynchronously. Completion is signaled via the + * QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE event, which is handled in + * the connectionCallback. + */ void closeConnection() override; private: + /// Represents the current state of the QUIC connection lifecycle enum class ConnectionState : uint8_t { DISCONNECTED, CONNECTING, CONNECTED, CLOSING }; + /// Pointer to the MsQuic API function table const QUIC_API_TABLE* quic_ { nullptr }; + /// QUIC registration handle associated with the application HQUIC registration_ { nullptr }; + /// QUIC configuration handle (ALPN, credentials, transport settings) HQUIC config_ { nullptr }; + /// Active QUIC connection handle HQUIC connection_ { nullptr }; + /// Application-Layer Protocol Negotiation (ALPN) string std::string alpn_; + /// QUIC buffer wrapping the ALPN string QUIC_BUFFER alpnBuffer_ {}; + /// Path to the client certificate file std::string certFile_; + /// Path to the client private key file std::string keyFile_; + /// Path to the CA certificate file std::string caFile_; + /// Atomic state of the connection used for synchronization across threads std::atomic connectionState_ { ConnectionState::DISCONNECTED }; - // inbound (server → client) + /// @name Inbound (peer → this) + /// @{ + /// Queue of incoming messages received from the peer std::queue> inboundQueue_; + /// Mutex protecting access to the inbound message queue std::mutex inboundMutex_; + /// Condition variable for signaling inbound message availability std::condition_variable inboundCv_; + /// @} - // outbound (client → server) + /// @name Outbound (this → peer) + /// @{ + /// Queue of outgoing messages to be sent to the peer std::queue> outboundQueue_; + /// Mutex protecting access to the outbound message queue std::mutex outboundMutex_; + /// Condition variable for signaling outbound message availability std::condition_variable outboundCv_; - + /// Dedicated sender thread responsible for transmitting outbound messages std::jthread senderThread_; + /// @} - /// ---------- Connection ---------- + /** + * @brief Loads and initializes the MsQuic API. + * + * Initializes the MsQuic library and retrieves the + * QUIC API function table. The resulting table is stored for later + * use when creating registrations, configurations, and connections. + * + * If the initialization fails, an error is logged. + */ void loadMsQuic(); + + /** + * @brief Initializes a QUIC registration. + * + * Creates a QUIC registration with the specified application name and + * a low-latency execution profile. The registration is required for + * creating QUIC configurations and connections. + * + * If registration creation fails, an error is logged. + * + * @param appName Application name used to identify the QUIC registration. + */ void initRegistration(const char *appName); + + /** + * @brief Initializes the QUIC configuration and loads client credentials. + * + * Opens a QUIC configuration using the configured ALPN and default QUIC + * transport settings. Client TLS credentials are then set up using a + * certificate file, private key file, and CA certificate file. + * + * If configuration creation or credential loading fails, an error is logged. + */ void initConfiguration(); + + /** + * @brief Opens a QUIC configuration. + * + * Creates a QUIC configuration associated with the current registration, + * configured ALPN, and optional transport settings. + * + * If settings are not provided, default QUIC settings are used. + * On failure, an error is logged. + * + * @param settings Optional QUIC transport settings. + */ void configurationOpen(const QUIC_SETTINGS *settings); + + /** + * @brief Loads TLS credentials into the QUIC configuration. + * + * Loads client-side TLS credentials into the active QUIC configuration. + * The credentials define the certificate, private key, and CA certificate + * used for secure communication. + * + * If credential loading fails, an error is logged. + * + * @param credential Pointer to the QUIC credential configuration. + */ void configurationLoadCredential(const QUIC_CREDENTIAL_CONFIG *credential) const; - void stop(); + /** + * @brief Handles a successfully decoded incoming message. + * + * Pushes the decoded ExternalServer message into the inbound queue + * in a thread-safe manner and notifies the receiver thread that a + * new message is available. + * + * @param msg Decoded message received from the peer. + */ void onMessageDecoded(std::shared_ptr msg); + /** + * @brief Sends a message to the peer using a QUIC stream. + * + * Opens a new QUIC stream on the active connection and serializes the + * provided ExternalClient message into a byte buffer. + * The message is sent using a single StreamSend call with START and FIN + * flags, effectively opening, sending, and closing the stream. + * + * The allocated send buffer is released asynchronously in the + * QUIC_STREAM_EVENT_SEND_COMPLETE callback. + * + * @param message Message to be sent to the peer. + * @return true if the send operation was successfully initiated, false otherwise. + */ bool sendViaQuicStream(const std::shared_ptr &message); - /// ---------- Closing client ---------- - void closeMsQuic(); + /** + * @brief Closes the active QUIC configuration. + */ void closeConfiguration(); + + /** + * @brief Closes the QUIC registration. + */ void closeRegistration(); - /// ---------- Callbacks ---------- + /** + * @brief Closes the MsQuic API and releases associated resources. + */ + void closeMsQuic(); + + /** + * @brief Stops the QUIC communication and releases all resources. + * + * Initiates connection shutdown and closes the QUIC configuration, + * registration, and MsQuic API in the correct order. + * All waiting sender and receiver threads are unblocked by notifying + * the associated condition variables. + */ + void stop(); + + /** + * @brief Handles QUIC connection-level events. + * + * Processes connection lifecycle events reported by MsQuic, including + * successful connection establishment, peer-initiated shutdown, and + * shutdown completion. + * + * All QUIC_CONNECTION_EVENT cases are documented at + * https://microsoft.github.io/msquic/msquicdocs/docs/api/QUIC_CONNECTION_EVENT.html + * + * @param connection QUIC connection handle. + * @param context User-defined context pointer (QuicCommunication instance). + * @param event Connection event information provided by MsQuic. + * @return QUIC_STATUS_SUCCESS to indicate successful event handling. + */ static QUIC_STATUS QUIC_API connectionCallback(HQUIC connection, void *context, QUIC_CONNECTION_EVENT *event); - static unsigned int streamCallback(HQUIC stream, void *context, QUIC_STREAM_EVENT *event); - void senderLoop(); + /** + * @brief Handles QUIC stream-level events. + * + * Processes stream events reported by MsQuic, including data reception, + * send completion, stream startup, and shutdown notifications. + * + * All QUIC_STREAM_EVENT cases are documented at + * https://microsoft.github.io/msquic/msquicdocs/docs/api/QUIC_STREAM_EVENT.html + * + * @param stream QUIC stream handle associated with the event. + * @param context User-defined context pointer (QuicCommunication instance). + * @param event Stream event information provided by MsQuic. + * @return QUIC_STATUS_SUCCESS to indicate successful event handling. + */ + static QUIC_STATUS QUIC_API streamCallback(HQUIC stream, void *context, QUIC_STREAM_EVENT *event); - /// void connect(); - - // /** - // * @brief Create a client id from company name and vehicle name - // * - // * @param company name of the company - // * @param vehicleName name of the vehicle - // * @return std::string - // */ - // static std::string createClientId(const std::string &company, const std::string &vehicleName); - - // /** - // * @brief Create a publish topic from company name and vehicle name - // * - // * @param company name of the company - // * @param vehicleName name of the vehicle - // * @return std::string - // */ - // static std::string createPublishTopic(const std::string &company, const std::string &vehicleName); - - // /** - // * @brief Create a subscribe topic from company name and vehicle name - // * - // * @param company name of the company - // * @param vehicleName name of the vehicle - // * @return std::string - // */ - // static std::string createSubscribeTopic(const std::string &company, const std::string &vehicleName); - - // /// MQTT client handling the connection - // std::unique_ptr client_ { nullptr }; - // /// Unique ID of the client, changes with every connection - // std::string clientId_ {}; - // /// Topic to publish messages to, sender is external client, receiver is external server - // std::string publishTopic_ {}; - // /// Topic to subscribe to, sender is external server, receiver is external client - // std::string subscribeTopic_ {}; - // /// MQTT library connection options - // mqtt::connect_options connopts_ {}; - // /// Address of the MQTT server - // std::string serverAddress_ {}; - // /// MQTT QOS level. Level 1 assures that message is delivered at least once - // constexpr static int8_t qos { 1 }; - // /// Mutex to prevent deadlocks when receiving messages - // std::mutex receiveMessageMutex_ {}; + /** + * @brief Sender thread main loop for outbound messages. + * + * Waits for messages to appear in the outbound queue while the + * connection remains in the CONNECTED state. + * When a message becomes available, it is dequeued and sent + * over a newly created QUIC stream. + * + * The loop exits when the connection state changes from CONNECTED. + */ + void senderLoop(); + /** + * @brief Converts a ConnectionState value to a human-readable string. + * + * @param state Connection state to convert. + * @return Null-terminated string describing the connection state. + */ static const char* toString(ConnectionState state) { switch (state) { case ConnectionState::DISCONNECTED: return "Disconnected"; diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 20af7893..ce798187 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -94,8 +94,6 @@ namespace bringauto::external_client::connection::communication { return msg; } - /// ---------- Connection ---------- - void QuicCommunication::loadMsQuic() { QUIC_STATUS status = MsQuicOpen2(&quic_); if (QUIC_FAILED(status)) { @@ -155,8 +153,6 @@ namespace bringauto::external_client::connection::communication { } } - /// ---------- Closing client ---------- - void QuicCommunication::closeConnection() { if (! connection_) { return; @@ -164,7 +160,7 @@ namespace bringauto::external_client::connection::communication { quic_->ConnectionShutdown(connection_, QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0); - // Waiting for QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE then continue in connectionCallback + /// Asynchronously waiting for QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE event, then continue in connectionCallback } void QuicCommunication::closeConfiguration() { @@ -203,8 +199,6 @@ namespace bringauto::external_client::connection::communication { settings::Logger::logInfo("[quic] Connection stopped"); } - - /// ---------- Outgoings ---------- void QuicCommunication::onMessageDecoded( std::shared_ptr msg ) { @@ -321,6 +315,7 @@ namespace bringauto::external_client::connection::communication { event->RECEIVE.BufferCount ); + /// -------- START - Just for debugging -------- uint64_t streamId = 0; uint32_t streamIdLen = sizeof(streamId); @@ -335,6 +330,7 @@ namespace bringauto::external_client::connection::communication { "[quic] [stream {}] Event RECEIVE", streamId ); + /// -------- END - Just for debugging -------- std::vector data; data.reserve(event->RECEIVE.TotalBufferLength); From 1df1ff9bd7c74ac3edc74c29445c5276f9fb1b81 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Mon, 5 Jan 2026 09:54:36 +0000 Subject: [PATCH 32/55] Refactor `QuicCommunication`: Extract `getStreamId` method, improve logging for stream events, and clean up debug code. --- .../communication/QuicCommunication.hpp | 15 +++++++++++++++ .../communication/QuicCommunication.cpp | 17 +++++------------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index 1bb0fc98..f63c9b54 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -296,6 +296,21 @@ class QuicCommunication: public ICommunicationChannel { default: return "Unknown"; } } + + std::optional getStreamId(HQUIC stream) const { + uint64_t streamId = 0; + uint32_t streamIdLen = sizeof(streamId); + + if (QUIC_FAILED(quic_->GetParam( + stream, + QUIC_PARAM_STREAM_ID, + &streamIdLen, + &streamId))) { + return std::nullopt; + } + + return streamId; + } }; } diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index ce798187..ce0703f3 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -316,19 +316,10 @@ namespace bringauto::external_client::connection::communication { ); /// -------- START - Just for debugging -------- - uint64_t streamId = 0; - uint32_t streamIdLen = sizeof(streamId); - - self->quic_->GetParam( - stream, - QUIC_PARAM_STREAM_ID, - &streamIdLen, - &streamId - ); - + auto streamId = self->getStreamId(stream); settings::Logger::logInfo( "[quic] [stream {}] Event RECEIVE", - streamId + streamId ? *streamId : 0 ); /// -------- END - Just for debugging -------- @@ -347,6 +338,7 @@ namespace bringauto::external_client::connection::communication { self->onMessageDecoded(std::move(msg)); } + settings::Logger::logInfo("[quic] Approving stream receive completed"); self->quic_->StreamReceiveComplete(stream, event->RECEIVE.TotalBufferLength); if (event->RECEIVE.Flags & QUIC_RECEIVE_FLAG_FIN) { @@ -380,7 +372,8 @@ namespace bringauto::external_client::connection::communication { /// My FIN was sended to server case QUIC_STREAM_EVENT_SEND_SHUTDOWN_COMPLETE: { - settings::Logger::logInfo("[quic] Stream send shutdown complete"); + auto streamId = self->getStreamId(stream); + settings::Logger::logInfo("[quic] [stream {}] Stream send shutdown complete", streamId ? *streamId : 0); break; } From cf7a2a622e6b218df53fe03346d13e0c52f4b277 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Mon, 5 Jan 2026 12:38:58 +0000 Subject: [PATCH 33/55] Refactor `QuicCommunication`: Simplify `StreamSend` logic, improve error handling with `StreamShutdown`, and clean up unused callbacks and logging. --- .../communication/QuicCommunication.cpp | 32 ++----------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index ce0703f3..7ca6b4e2 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -234,10 +234,7 @@ namespace bringauto::external_client::connection::communication { std::memcpy(buf->Buffer, buffer.get(), buf->Length); - const QUIC_STATUS status = quic_->StreamSend( - stream, - buf, - 1, + const QUIC_STATUS status = quic_->StreamSend(stream, buf, 1, /** * START => Simulates quic_->StreamStart before send * FIN => Simulates quic_->StreamShutdown after send @@ -251,14 +248,13 @@ namespace bringauto::external_client::connection::communication { delete buf; settings::Logger::logError("[quic] Failed to send QUIC stream; QUIC_STATUS => {:x}", status); + quic_->StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0); return false; } return true; } - /// ---------- Callbacks ---------- - QUIC_STATUS QUIC_API QuicCommunication::connectionCallback(HQUIC connection, void* context, QUIC_CONNECTION_EVENT* event) { auto* self = static_cast(context); @@ -338,35 +334,12 @@ namespace bringauto::external_client::connection::communication { self->onMessageDecoded(std::move(msg)); } - settings::Logger::logInfo("[quic] Approving stream receive completed"); - self->quic_->StreamReceiveComplete(stream, event->RECEIVE.TotalBufferLength); - - if (event->RECEIVE.Flags & QUIC_RECEIVE_FLAG_FIN) { - settings::Logger::logInfo( - "[quic] [stream {}] Peer FIN received, shutting down receive", - streamId - ); - - QUIC_STATUS status = self->quic_->StreamShutdown( - stream, - QUIC_STREAM_SHUTDOWN_FLAG_ABORT_RECEIVE, - 0 - ); - - settings::Logger::logInfo( - "[quic] [stream {}] StreamShutdown(RECEIVE) -> 0x{:x}", - streamId, - status - ); - } - break; } /// Server send FIN case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: { settings::Logger::logInfo("[quic] Peer stream send shutdown"); - //self->quic_->StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0); break; } @@ -416,6 +389,7 @@ namespace bringauto::external_client::connection::communication { delete[] buf->Buffer; delete buf; } + break; } From b176f3f79c015b831de53b8e0d34e361bc2b4a21 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Mon, 5 Jan 2026 12:56:20 +0000 Subject: [PATCH 34/55] Refactor `QuicCommunication`: Add detailed event handling comments, improve stream and connection logging, and clean up unnecessary debug code. --- .../communication/QuicCommunication.hpp | 11 ++++++- .../communication/QuicCommunication.cpp | 30 +++++++++++++------ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index f63c9b54..059c9560 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -297,6 +297,15 @@ class QuicCommunication: public ICommunicationChannel { } } + /** + * @brief Retrieves the QUIC stream identifier for the given stream handle. + * + * Queries MsQuic for the stream ID associated with the provided HQUIC stream. + * If the parameter query fails, an empty optional is returned. + * + * @param stream Valid QUIC stream handle. + * @return Stream identifier on success, or std::nullopt if the query fails. + */ std::optional getStreamId(HQUIC stream) const { uint64_t streamId = 0; uint32_t streamIdLen = sizeof(streamId); @@ -307,7 +316,7 @@ class QuicCommunication: public ICommunicationChannel { &streamIdLen, &streamId))) { return std::nullopt; - } + } return streamId; } diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 7ca6b4e2..84e58178 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -259,17 +259,22 @@ namespace bringauto::external_client::connection::communication { auto* self = static_cast(context); switch (event->Type) { + /// Fired when the QUIC handshake is complete and the connection is ready + /// for stream creation and data transfer. case QUIC_CONNECTION_EVENT_CONNECTED: { settings::Logger::logInfo("[quic] Connected to server"); auto expected = ConnectionState::CONNECTING; if (self->connectionState_.compare_exchange_strong(expected, ConnectionState::CONNECTED)) { + /// Start sender thread only after connection is fully established self->senderThread_ = std::jthread(&QuicCommunication::senderLoop, self); self->outboundCv_.notify_all(); } break; } + /// Final notification that the connection has been fully shut down. + /// This is the last event delivered for the connection handle. case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE: { settings::Logger::logInfo("[quic] Connection shutdown complete"); @@ -285,6 +290,8 @@ namespace bringauto::external_client::connection::communication { break; } + /// Peer or transport initiated connection shutdown (error or graceful close). + /// Further sends may fail after this event. case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER: { settings::Logger::logWarning("[quic] Connection shutdown initiated"); self->connectionState_ = ConnectionState::CLOSING; @@ -292,7 +299,7 @@ namespace bringauto::external_client::connection::communication { } default: { - settings::Logger::logInfo("[quic] Unexpected connection event 0x{:x}", static_cast(event->Type)); + settings::Logger::logInfo("[quic] Unhandled connection event 0x{:x}", static_cast(event->Type)); break; } } @@ -302,8 +309,10 @@ namespace bringauto::external_client::connection::communication { QUIC_STATUS QUIC_API QuicCommunication::streamCallback(HQUIC stream, void* context, QUIC_STREAM_EVENT* event) { auto* self = static_cast(context); + auto streamId = self->getStreamId(stream); switch (event->Type) { + /// Raised when the peer sends stream data and MsQuic delivers received bytes to the application. case QUIC_STREAM_EVENT_RECEIVE: { settings::Logger::logInfo( "[quic] Received {:d} bytes in {:d} buffers", @@ -311,13 +320,10 @@ namespace bringauto::external_client::connection::communication { event->RECEIVE.BufferCount ); - /// -------- START - Just for debugging -------- - auto streamId = self->getStreamId(stream); settings::Logger::logInfo( "[quic] [stream {}] Event RECEIVE", streamId ? *streamId : 0 ); - /// -------- END - Just for debugging -------- std::vector data; data.reserve(event->RECEIVE.TotalBufferLength); @@ -337,24 +343,29 @@ namespace bringauto::external_client::connection::communication { break; } - /// Server send FIN + /// Raised when the peer has finished sending on this stream + /// (peer's FIN has been fully received and processed). case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: { settings::Logger::logInfo("[quic] Peer stream send shutdown"); break; } - /// My FIN was sended to server + /// Raised when the local send direction is fully shut down + /// and the peer has acknowledged the FIN. case QUIC_STREAM_EVENT_SEND_SHUTDOWN_COMPLETE: { - auto streamId = self->getStreamId(stream); settings::Logger::logInfo("[quic] [stream {}] Stream send shutdown complete", streamId ? *streamId : 0); break; } + /// Raised after StreamStart completes successfully + /// and the stream becomes active with a valid stream ID. case QUIC_STREAM_EVENT_START_COMPLETE: { settings::Logger::logInfo("[quic] Stream start completed"); break; } + /// Raised when a single StreamSend operation completes + /// (data was accepted, acknowledged, or the send was canceled). case QUIC_STREAM_EVENT_SEND_COMPLETE: { /** * This event is raised when MsQuic has finished processing @@ -393,7 +404,8 @@ namespace bringauto::external_client::connection::communication { break; } - /// Stream is closed from both sides + /// Raised when both send and receive directions are closed + /// and the stream lifecycle is fully complete. case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: { settings::Logger::logInfo("[quic] Stream shutdown complete"); self->quic_->StreamClose(stream); @@ -401,7 +413,7 @@ namespace bringauto::external_client::connection::communication { } default: { - settings::Logger::logInfo("[quic] Unexpected stream event 0x{:x}", static_cast(event->Type)); + settings::Logger::logInfo("[quic] Unhandled stream event 0x{:x}", static_cast(event->Type)); break; } } From a6de815b032f7a5fa5b2b0b3caf9de95de1be187 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Tue, 6 Jan 2026 08:03:08 +0000 Subject: [PATCH 35/55] Refactor `QuicCommunication`: Extract `getProtocolSettingsString` method, improve handling of JSON-encoded protocol settings, and refine `SettingsParser` logic for cleaner value parsing. --- .../communication/QuicCommunication.hpp | 61 +++++++++++-------- .../communication/QuicCommunication.cpp | 51 ++++++++++++---- source/bringauto/settings/SettingsParser.cpp | 12 +--- 3 files changed, 77 insertions(+), 47 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index 059c9560..9de810c9 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -1,12 +1,16 @@ #pragma once -#include #include #include +#include + +#include #include #include + + namespace bringauto::external_client::connection::communication { class QuicCommunication: public ICommunicationChannel { @@ -281,6 +285,37 @@ class QuicCommunication: public ICommunicationChannel { */ void senderLoop(); + /** + * @brief Retrieves the QUIC stream identifier for the given stream handle. + * + * Queries MsQuic for the stream ID associated with the provided HQUIC stream. + * If the parameter query fails, an empty optional is returned. + * + * @param stream Valid QUIC stream handle. + * @return Stream identifier on success, or std::nullopt if the query fails. + */ + std::optional getStreamId(HQUIC stream); + + /** + * @brief Retrieves a protocol setting value as a plain string. + * + * Extracts a value from ExternalConnectionSettings::protocolSettings and + * transparently handles values stored as JSON-encoded strings. + * + * Allows uniform access to protocol settings regardless of whether + * they were stored as plain strings or JSON-serialized values. + * + * @param settings External connection settings containing protocolSettings. + * @param key Key identifying the protocol setting. + * @return Plain string value suitable for direct use (e.g. file paths). + * + * @throws std::out_of_range if the key is not present in protocolSettings. + */ + static std::string getProtocolSettingsString( + const structures::ExternalConnectionSettings& settings, + std::string_view key + ); + /** * @brief Converts a ConnectionState value to a human-readable string. * @@ -296,30 +331,6 @@ class QuicCommunication: public ICommunicationChannel { default: return "Unknown"; } } - - /** - * @brief Retrieves the QUIC stream identifier for the given stream handle. - * - * Queries MsQuic for the stream ID associated with the provided HQUIC stream. - * If the parameter query fails, an empty optional is returned. - * - * @param stream Valid QUIC stream handle. - * @return Stream identifier on success, or std::nullopt if the query fails. - */ - std::optional getStreamId(HQUIC stream) const { - uint64_t streamId = 0; - uint32_t streamIdLen = sizeof(streamId); - - if (QUIC_FAILED(quic_->GetParam( - stream, - QUIC_PARAM_STREAM_ID, - &streamIdLen, - &streamId))) { - return std::nullopt; - } - - return streamId; - } }; } diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 84e58178..16d0242b 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -1,21 +1,20 @@ #include #include +#include #include #include -#include -#include "bringauto/settings/LoggerId.hpp" namespace bringauto::external_client::connection::communication { QuicCommunication::QuicCommunication(const structures::ExternalConnectionSettings &settings, const std::string &company, const std::string &vehicleName) : ICommunicationChannel(settings), - certFile_(settings.protocolSettings.at(std::string(settings::Constants::CLIENT_CERT))), - keyFile_(settings.protocolSettings.at(std::string(settings::Constants::CLIENT_KEY))), - caFile_(settings.protocolSettings.at(std::string(settings::Constants::CA_FILE))) + certFile_(getProtocolSettingsString(settings, settings::Constants::CLIENT_CERT)), + keyFile_(getProtocolSettingsString(settings, settings::Constants::CLIENT_KEY)), + caFile_(getProtocolSettingsString(settings, settings::Constants::CA_FILE)) { alpn_ = "sample"; alpnBuffer_.Buffer = reinterpret_cast(alpn_.data()); @@ -133,12 +132,12 @@ namespace bringauto::external_client::connection::communication { QUIC_STATUS status = quic_->ConfigurationOpen( registration_, - &alpnBuffer_, - 1, - settings, - settingsSize, - nullptr, - &config_ + &alpnBuffer_, + 1, + settings, + settingsSize, + nullptr, + &config_ ); if (QUIC_FAILED(status)) { @@ -444,4 +443,34 @@ namespace bringauto::external_client::connection::communication { sendViaQuicStream(msg); } } + + std::optional QuicCommunication::getStreamId(HQUIC stream) { + uint64_t streamId = 0; + uint32_t streamIdLen = sizeof(streamId); + + if (QUIC_FAILED(quic_->GetParam( + stream, + QUIC_PARAM_STREAM_ID, + &streamIdLen, + &streamId))) { + return std::nullopt; + } + + return streamId; + } + + std::string QuicCommunication::getProtocolSettingsString( + const structures::ExternalConnectionSettings& settings, + std::string_view key + ) { + const auto& raw = settings.protocolSettings.at(std::string(key)); + + if (nlohmann::json::accept(raw)) { + auto j = nlohmann::json::parse(raw); + if (j.is_string()) { + return j.get(); + } + } + return raw; + } } diff --git a/source/bringauto/settings/SettingsParser.cpp b/source/bringauto/settings/SettingsParser.cpp index d149b113..ce60789a 100644 --- a/source/bringauto/settings/SettingsParser.cpp +++ b/source/bringauto/settings/SettingsParser.cpp @@ -203,13 +203,7 @@ void SettingsParser::fillExternalConnectionSettings(const nlohmann::json &file) if(!settingsName.empty() && endpoint.find(settingsName) != endpoint.end()) { for(auto &[key, val]: endpoint[settingsName].items()) { - if (val.is_string()) { - externalConnectionSettings.protocolSettings[key] = val.get(); - std::cout << externalConnectionSettings.protocolSettings[key] << std::endl; - continue; - } externalConnectionSettings.protocolSettings[key] = to_string(val); - std::cout << externalConnectionSettings.protocolSettings[key] << std::endl; } } @@ -261,11 +255,7 @@ std::string SettingsParser::serializeToJson() const { break; } for(const auto &[key, val]: endpoint.protocolSettings) { - if (nlohmann::json::accept(val)) { - endpointAsJson[settingsName][key] = nlohmann::json::parse(val); - continue; - } - endpointAsJson[settingsName][key] = val; + endpointAsJson[settingsName][key] = nlohmann::json::parse(val); } endpoints.push_back(endpointAsJson); } From f9809989ea62ad6a7f0460313f57c9d9510e6864 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Tue, 6 Jan 2026 08:22:00 +0000 Subject: [PATCH 36/55] Refactor `QuicCommunication`: Replace `logInfo` with `logDebug`, refine logging messages for clarity, and remove unused includes. --- .../communication/QuicCommunication.cpp | 51 ++++++++----------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 16d0242b..861328ec 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -2,10 +2,6 @@ #include #include -#include - -#include - namespace bringauto::external_client::connection::communication { @@ -32,7 +28,7 @@ namespace bringauto::external_client::connection::communication { } void QuicCommunication::initializeConnection() { - settings::Logger::logInfo("[quic] Connecting to server when {}", toString(connectionState_)); + settings::Logger::logDebug("[quic] Connecting to server when {}", toString(connectionState_)); ConnectionState expected = ConnectionState::DISCONNECTED; @@ -61,14 +57,13 @@ namespace bringauto::external_client::connection::communication { } bool QuicCommunication::sendMessage(ExternalProtocol::ExternalClient* message) { - settings::Logger::logInfo("[quic] Enqueueing message"); auto copy = std::make_shared(*message); { std::lock_guard lock(outboundMutex_); outboundQueue_.push(std::move(copy)); } - settings::Logger::logInfo("[quic] Notifying sender thread"); + settings::Logger::logDebug("[quic] Notifying sender thread about enqueued message"); outboundCv_.notify_one(); return true; } @@ -203,16 +198,13 @@ namespace bringauto::external_client::connection::communication { ) { { std::lock_guard lock(inboundMutex_); - settings::Logger::logInfo("[quic] Moving message to inboundQueue"); inboundQueue_.push(std::move(msg)); } - settings::Logger::logInfo("[quic] Notifying receiver thread"); + settings::Logger::logDebug("[quic] Notifying receiver thread about dequeued message"); inboundCv_.notify_one(); } bool QuicCommunication::sendViaQuicStream(const std::shared_ptr& message) { - settings::Logger::logInfo("[quic] Sending message"); - HQUIC stream { nullptr }; if (QUIC_FAILED(quic_->StreamOpen(connection_, QUIC_STREAM_OPEN_FLAG_NONE, streamCallback, this, &stream))) { settings::Logger::logError("[quic] StreamOpen failed"); @@ -242,6 +234,9 @@ namespace bringauto::external_client::connection::communication { buf ); + auto streamId = getStreamId(stream); + settings::Logger::logDebug("[quic] [stream {}] Message sent", streamId ? *streamId : 0); + if (QUIC_FAILED(status)) { delete[] buf->Buffer; delete buf; @@ -292,13 +287,13 @@ namespace bringauto::external_client::connection::communication { /// Peer or transport initiated connection shutdown (error or graceful close). /// Further sends may fail after this event. case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER: { - settings::Logger::logWarning("[quic] Connection shutdown initiated"); + settings::Logger::logWarning("[quic] Connection shutdown initiated by peer"); self->connectionState_ = ConnectionState::CLOSING; break; } default: { - settings::Logger::logInfo("[quic] Unhandled connection event 0x{:x}", static_cast(event->Type)); + settings::Logger::logDebug("[quic] Unhandled connection event 0x{:x}", static_cast(event->Type)); break; } } @@ -313,17 +308,13 @@ namespace bringauto::external_client::connection::communication { switch (event->Type) { /// Raised when the peer sends stream data and MsQuic delivers received bytes to the application. case QUIC_STREAM_EVENT_RECEIVE: { - settings::Logger::logInfo( - "[quic] Received {:d} bytes in {:d} buffers", + settings::Logger::logDebug( + "[quic] [stream {}] Received {:d} bytes in {:d} buffers", + streamId ? *streamId : 0, event->RECEIVE.TotalBufferLength, event->RECEIVE.BufferCount ); - settings::Logger::logInfo( - "[quic] [stream {}] Event RECEIVE", - streamId ? *streamId : 0 - ); - std::vector data; data.reserve(event->RECEIVE.TotalBufferLength); @@ -345,21 +336,21 @@ namespace bringauto::external_client::connection::communication { /// Raised when the peer has finished sending on this stream /// (peer's FIN has been fully received and processed). case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: { - settings::Logger::logInfo("[quic] Peer stream send shutdown"); + settings::Logger::logDebug("[quic] Peer stream send shutdown"); break; } /// Raised when the local send direction is fully shut down /// and the peer has acknowledged the FIN. case QUIC_STREAM_EVENT_SEND_SHUTDOWN_COMPLETE: { - settings::Logger::logInfo("[quic] [stream {}] Stream send shutdown complete", streamId ? *streamId : 0); + settings::Logger::logDebug("[quic] [stream {}] Stream send shutdown complete", streamId ? *streamId : 0); break; } /// Raised after StreamStart completes successfully /// and the stream becomes active with a valid stream ID. case QUIC_STREAM_EVENT_START_COMPLETE: { - settings::Logger::logInfo("[quic] Stream start completed"); + settings::Logger::logDebug("[quic] Stream start completed"); break; } @@ -387,9 +378,9 @@ namespace bringauto::external_client::connection::communication { * passed to StreamSend (via ClientContext) */ if (event->SEND_COMPLETE.Canceled) { - settings::Logger::logError("[quic] Stream send canceled"); + settings::Logger::logWarning("[quic] Stream send canceled"); } else { - settings::Logger::logInfo("[quic] Stream send completed"); + settings::Logger::logDebug("[quic] Stream send completed"); } const auto* buf = @@ -406,13 +397,13 @@ namespace bringauto::external_client::connection::communication { /// Raised when both send and receive directions are closed /// and the stream lifecycle is fully complete. case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: { - settings::Logger::logInfo("[quic] Stream shutdown complete"); + settings::Logger::logDebug("[quic] Stream shutdown complete"); self->quic_->StreamClose(stream); break; } default: { - settings::Logger::logInfo("[quic] Unhandled stream event 0x{:x}", static_cast(event->Type)); + settings::Logger::logDebug("[quic] Unhandled stream event 0x{:x}", static_cast(event->Type)); break; } } @@ -421,11 +412,11 @@ namespace bringauto::external_client::connection::communication { } void QuicCommunication::senderLoop() { - settings::Logger::logInfo("[quic] Sender thread loop started"); + settings::Logger::logDebug("[quic] Sender thread loop started"); while (connectionState_.load() == ConnectionState::CONNECTED) { std::unique_lock lock(outboundMutex_); - settings::Logger::logInfo("[quic] Sender thread loop waiting for outbound queue"); + settings::Logger::logDebug("[quic] Sender thread loop waiting for outbound queue"); outboundCv_.wait(lock, [this] { return !outboundQueue_.empty() || connectionState_.load() != ConnectionState::CONNECTED; @@ -435,7 +426,7 @@ namespace bringauto::external_client::connection::communication { break; } - settings::Logger::logInfo("[quic] Sender thread loop sending outbound queue"); + settings::Logger::logDebug("[quic] Sender thread loop sending outbound queue"); auto msg = outboundQueue_.front(); outboundQueue_.pop(); lock.unlock(); From 4b8e10bf95d77fbef66dcd926a273e6687b8a1b8 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Tue, 6 Jan 2026 09:53:00 +0000 Subject: [PATCH 37/55] Refactor `QuicCommunication`: Replace error logs with exceptions for better error handling, improve sender loop logic, and refine logging for message and connection events. --- .../communication/QuicCommunication.hpp | 12 ++-- .../communication/QuicCommunication.cpp | 58 +++++++++++-------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index 9de810c9..c0f961a5 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -211,9 +211,8 @@ class QuicCommunication: public ICommunicationChannel { * QUIC_STREAM_EVENT_SEND_COMPLETE callback. * * @param message Message to be sent to the peer. - * @return true if the send operation was successfully initiated, false otherwise. */ - bool sendViaQuicStream(const std::shared_ptr &message); + void sendViaQuicStream(const std::shared_ptr &message); /** * @brief Closes the active QUIC configuration. @@ -276,12 +275,11 @@ class QuicCommunication: public ICommunicationChannel { /** * @brief Sender thread main loop for outbound messages. * - * Waits for messages to appear in the outbound queue while the - * connection remains in the CONNECTED state. - * When a message becomes available, it is dequeued and sent - * over a newly created QUIC stream. + * Waits for outbound messages while the connection is in the CONNECTED state. + * Messages are dequeued and sent over individual QUIC streams. * - * The loop exits when the connection state changes from CONNECTED. + * If sending fails, the message is re-enqueued for a later retry. + * The loop terminates when the connection leaves the CONNECTED state. */ void senderLoop(); diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 861328ec..4e2f8173 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -16,11 +16,15 @@ namespace bringauto::external_client::connection::communication { alpnBuffer_.Buffer = reinterpret_cast(alpn_.data()); alpnBuffer_.Length = static_cast(alpn_.size()); - settings::Logger::logInfo("[quic] Initialize QUIC connection to {}:{} for {}/{}", settings.serverIp, settings.port, company, vehicleName); - - loadMsQuic(); - initRegistration("module-gateway-quic-client"); - initConfiguration(); + settings::Logger::logInfo("[quic] Initialize QUIC communication to {}:{} for {}/{}", settings.serverIp, settings.port, company, vehicleName); + + try { + loadMsQuic(); + initRegistration("module-gateway-quic-client"); + initConfiguration(); + } catch (const std::exception& e) { + settings::Logger::logError("[quic] Failed to initialize QUIC communication; {}", e.what()); + } } QuicCommunication::~QuicCommunication() { @@ -33,12 +37,12 @@ namespace bringauto::external_client::connection::communication { ConnectionState expected = ConnectionState::DISCONNECTED; if (! connectionState_.compare_exchange_strong(expected, ConnectionState::CONNECTING, std::memory_order_acq_rel)) { - return; + throw std::logic_error("Connection already in progress or established"); } QUIC_STATUS status = quic_->ConnectionOpen(registration_, connectionCallback, this, &connection_); if (QUIC_FAILED(status)) { - settings::Logger::logError("[quic] Failed to open QUIC connection; QUIC_STATUS => {:x}", status); + throw std::runtime_error(std::format("ConnectionOpen failed (status=0x{:x})", status)); } status = quic_->ConnectionStart( @@ -50,13 +54,15 @@ namespace bringauto::external_client::connection::communication { ); if (QUIC_FAILED(status)) { - settings::Logger::logError("[quic] Failed to start QUIC connection; QUIC_STATUS => {:x}", status); + throw std::runtime_error(std::format("ConnectionOpen failed (status=0x{:x})", status)); } connectionState_ = ConnectionState::CONNECTING; } bool QuicCommunication::sendMessage(ExternalProtocol::ExternalClient* message) { + settings::Logger::logDebug("[quic] Sending message when {}", toString(connectionState_)); + auto copy = std::make_shared(*message); { @@ -91,7 +97,7 @@ namespace bringauto::external_client::connection::communication { void QuicCommunication::loadMsQuic() { QUIC_STATUS status = MsQuicOpen2(&quic_); if (QUIC_FAILED(status)) { - settings::Logger::logError("[quic] Failed to open QUIC; QUIC_STATUS => {:x}", status); + throw std::runtime_error(std::format("[quic] Failed to open QUIC; QUIC_STATUS => {:x}", status)); } } @@ -102,7 +108,7 @@ namespace bringauto::external_client::connection::communication { QUIC_STATUS status = quic_->RegistrationOpen(&config, ®istration_); if (QUIC_FAILED(status)) { - settings::Logger::logError("[quic] Failed to open QUIC registration; QUIC_STATUS => {:x}", status); + throw std::runtime_error(std::format("[quic] Failed to open QUIC registration; QUIC_STATUS => {:x}", status)); } } @@ -136,14 +142,14 @@ namespace bringauto::external_client::connection::communication { ); if (QUIC_FAILED(status)) { - settings::Logger::logError("[quic] Failed to open QUIC configuration; QUIC_STATUS => {:x}", status); + throw std::runtime_error(std::format("[quic] Failed to open QUIC configuration; QUIC_STATUS => {:x}", status)); } } void QuicCommunication::configurationLoadCredential(const QUIC_CREDENTIAL_CONFIG* credential) const { const QUIC_STATUS status = quic_->ConfigurationLoadCredential(config_, credential); if (QUIC_FAILED(status)) { - settings::Logger::logError("[quic] Failed to load QUIC credential; QUIC_STATUS => {:x}", status); + throw std::runtime_error(std::format("[quic] Failed to load QUIC credential; QUIC_STATUS => {:x}", status)); } } @@ -204,19 +210,17 @@ namespace bringauto::external_client::connection::communication { inboundCv_.notify_one(); } - bool QuicCommunication::sendViaQuicStream(const std::shared_ptr& message) { + void QuicCommunication::sendViaQuicStream(const std::shared_ptr& message) { HQUIC stream { nullptr }; if (QUIC_FAILED(quic_->StreamOpen(connection_, QUIC_STREAM_OPEN_FLAG_NONE, streamCallback, this, &stream))) { - settings::Logger::logError("[quic] StreamOpen failed"); - return false; + throw std::runtime_error("[quic] StreamOpen failed"); } const auto size = message->ByteSizeLong(); const auto buffer = std::make_unique(size); if (!message->SerializeToArray(buffer.get(), static_cast(size))) { - settings::Logger::logError("[quic] Message serialization failed"); - return false; + throw std::runtime_error("[quic] Message serialization failed"); } auto* buf = new QUIC_BUFFER{}; @@ -234,19 +238,16 @@ namespace bringauto::external_client::connection::communication { buf ); - auto streamId = getStreamId(stream); - settings::Logger::logDebug("[quic] [stream {}] Message sent", streamId ? *streamId : 0); - if (QUIC_FAILED(status)) { delete[] buf->Buffer; delete buf; - settings::Logger::logError("[quic] Failed to send QUIC stream; QUIC_STATUS => {:x}", status); quic_->StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0); - return false; + throw std::runtime_error(std::format("[quic] Failed to send QUIC stream; QUIC_STATUS => {:x}", status)); } - return true; + auto streamId = getStreamId(stream); + settings::Logger::logDebug("[quic] [stream {}] Message sent", streamId ? *streamId : 0); } QUIC_STATUS QUIC_API QuicCommunication::connectionCallback(HQUIC connection, void* context, QUIC_CONNECTION_EVENT* event) { @@ -413,7 +414,10 @@ namespace bringauto::external_client::connection::communication { void QuicCommunication::senderLoop() { settings::Logger::logDebug("[quic] Sender thread loop started"); + while (connectionState_.load() == ConnectionState::CONNECTED) { + std::shared_ptr msg; + std::unique_lock lock(outboundMutex_); settings::Logger::logDebug("[quic] Sender thread loop waiting for outbound queue"); @@ -427,11 +431,15 @@ namespace bringauto::external_client::connection::communication { } settings::Logger::logDebug("[quic] Sender thread loop sending outbound queue"); - auto msg = outboundQueue_.front(); + msg = outboundQueue_.front(); outboundQueue_.pop(); lock.unlock(); - sendViaQuicStream(msg); + try { + sendViaQuicStream(msg); + } catch (const std::exception& e) { + settings::Logger::logError(std::format("[quic] Message send failed; {}", e.what())); + } } } From 465b408e21714a8a5d610752cf6804ab6376e73a Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Tue, 6 Jan 2026 11:25:03 +0000 Subject: [PATCH 38/55] Refactor `QuicCommunication`: Add connection state check before sending messages. --- .../connection/communication/QuicCommunication.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 4e2f8173..850255c4 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -61,12 +61,14 @@ namespace bringauto::external_client::connection::communication { } bool QuicCommunication::sendMessage(ExternalProtocol::ExternalClient* message) { - settings::Logger::logDebug("[quic] Sending message when {}", toString(connectionState_)); - - auto copy = std::make_shared(*message); + if (connectionState_.load() == ConnectionState::DISCONNECTED) { + settings::Logger::logWarning("[quic] Connection not established, cannot send message"); + return false; + } - { - std::lock_guard lock(outboundMutex_); + { + auto copy = std::make_shared(*message); + std::lock_guard lock(outboundMutex_); outboundQueue_.push(std::move(copy)); } settings::Logger::logDebug("[quic] Notifying sender thread about enqueued message"); From e904d4efae84d7f55fa62db842c371751f226e57 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Wed, 7 Jan 2026 08:17:11 +0000 Subject: [PATCH 39/55] Refactor `QuicCommunication`: Replace QuicCommunication::ConnectionState with external_client::connection::ConnectionState --- include/bringauto/common_utils/EnumUtils.hpp | 18 + .../connection/ConnectionState.hpp | 6 +- .../communication/QuicCommunication.hpp | 618 +++++++++--------- .../connection/ExternalConnection.cpp | 3 + .../communication/QuicCommunication.cpp | 311 ++++----- 5 files changed, 487 insertions(+), 469 deletions(-) diff --git a/include/bringauto/common_utils/EnumUtils.hpp b/include/bringauto/common_utils/EnumUtils.hpp index 6618c364..991ba500 100644 --- a/include/bringauto/common_utils/EnumUtils.hpp +++ b/include/bringauto/common_utils/EnumUtils.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -73,6 +74,23 @@ class EnumUtils { } }; + /** + * @brief Converts connection state to string + * + * @param toString structures::ProtocolType + * @return std::string_view + */ + static constexpr std::string_view connectionStateToString(external_client::connection::ConnectionState toString) { + switch(toString) { + case external_client::connection::ConnectionState::NOT_INITIALIZED: return "Not Initialized"; + case external_client::connection::ConnectionState::NOT_CONNECTED: return "Not Connected"; + case external_client::connection::ConnectionState::CONNECTING: return "Connecting"; + case external_client::connection::ConnectionState::CONNECTED: return "Connected"; + case external_client::connection::ConnectionState::CLOSING: return "Closing"; + default: return "Unknown"; + } + }; + }; } diff --git a/include/bringauto/external_client/connection/ConnectionState.hpp b/include/bringauto/external_client/connection/ConnectionState.hpp index 91ec13f1..375ca30d 100644 --- a/include/bringauto/external_client/connection/ConnectionState.hpp +++ b/include/bringauto/external_client/connection/ConnectionState.hpp @@ -18,6 +18,10 @@ enum class ConnectionState { /** * CONNECTED - Client is connected to the External server */ - CONNECTED + CONNECTED, + /** + * CLOSING - Client closing connection to the External server + */ + CLOSING }; } diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index c0f961a5..e1a986d9 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -12,323 +13,302 @@ namespace bringauto::external_client::connection::communication { - -class QuicCommunication: public ICommunicationChannel { -public: - explicit QuicCommunication(const structures::ExternalConnectionSettings &settings, const std::string &company, - const std::string &vehicleName); - - ~QuicCommunication() override; - - /** - * @brief Initializes a QUIC connection to the server. - * - * Attempts to establish a new QUIC connection. - * It first atomically verifies that the current connection state is - * DISCONNECTED and transitions it to CONNECTING in order to prevent - * concurrent connection attempts. - * - * After the state transition, it opens a QUIC connection handle and - * starts the connection using the configured server address, port, - * and QUIC configuration. - * - * Any failures during the connection open or start process are logged. - */ - void initializeConnection() override; - - /** - * @brief Enqueues an outgoing message to be sent over the QUIC connection. - * - * Creates a shared copy of the provided ExternalClient message - * and pushes it into the outbound message queue in a thread-safe manner. - * After enqueuing, it notifies the sender thread via a condition variable - * that a new message is available for sending. - * - * @param message Pointer to the message that should be sent. - * @return true Always returns true to indicate the message was successfully enqueued. - */ - bool sendMessage(ExternalProtocol::ExternalClient *message) override; - - /** - * @brief Receives an incoming message from the QUIC connection. - * - * Waits for an incoming message to appear in the inbound - * queue or for the connection state to change from CONNECTED. - * The wait is bounded by a configurable timeout. - * - * If the wait times out, the connection is no longer in the CONNECTED - * state, or no message is available, the function returns nullptr. - * Otherwise, it retrieves and removes the next message from the inbound - * queue and returns it. - * - * @return A shared pointer to the received ExternalServer message, - * or nullptr if no message is available or the connection is not active. - */ - std::shared_ptr receiveMessage() override; - - /** - * @brief Initiates a graceful shutdown of the QUIC connection. - * - * Requests an orderly shutdown of the active QUIC connection. - * If no connection is currently established, the function returns immediately. - * - * The shutdown is performed asynchronously. Completion is signaled via the - * QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE event, which is handled in - * the connectionCallback. - */ - void closeConnection() override; - -private: - /// Represents the current state of the QUIC connection lifecycle - enum class ConnectionState : uint8_t { DISCONNECTED, CONNECTING, CONNECTED, CLOSING }; - - /// Pointer to the MsQuic API function table - const QUIC_API_TABLE* quic_ { nullptr }; - /// QUIC registration handle associated with the application - HQUIC registration_ { nullptr }; - /// QUIC configuration handle (ALPN, credentials, transport settings) - HQUIC config_ { nullptr }; - /// Active QUIC connection handle - HQUIC connection_ { nullptr }; - /// Application-Layer Protocol Negotiation (ALPN) string - std::string alpn_; - /// QUIC buffer wrapping the ALPN string - QUIC_BUFFER alpnBuffer_ {}; - - /// Path to the client certificate file - std::string certFile_; - /// Path to the client private key file - std::string keyFile_; - /// Path to the CA certificate file - std::string caFile_; - - /// Atomic state of the connection used for synchronization across threads - std::atomic connectionState_ { ConnectionState::DISCONNECTED }; - - /// @name Inbound (peer → this) - /// @{ - /// Queue of incoming messages received from the peer - std::queue> inboundQueue_; - /// Mutex protecting access to the inbound message queue - std::mutex inboundMutex_; - /// Condition variable for signaling inbound message availability - std::condition_variable inboundCv_; - /// @} - - /// @name Outbound (this → peer) - /// @{ - /// Queue of outgoing messages to be sent to the peer - std::queue> outboundQueue_; - /// Mutex protecting access to the outbound message queue - std::mutex outboundMutex_; - /// Condition variable for signaling outbound message availability - std::condition_variable outboundCv_; - /// Dedicated sender thread responsible for transmitting outbound messages - std::jthread senderThread_; - /// @} - - /** - * @brief Loads and initializes the MsQuic API. - * - * Initializes the MsQuic library and retrieves the - * QUIC API function table. The resulting table is stored for later - * use when creating registrations, configurations, and connections. - * - * If the initialization fails, an error is logged. - */ - void loadMsQuic(); - - /** - * @brief Initializes a QUIC registration. - * - * Creates a QUIC registration with the specified application name and - * a low-latency execution profile. The registration is required for - * creating QUIC configurations and connections. - * - * If registration creation fails, an error is logged. - * - * @param appName Application name used to identify the QUIC registration. - */ - void initRegistration(const char *appName); - - /** - * @brief Initializes the QUIC configuration and loads client credentials. - * - * Opens a QUIC configuration using the configured ALPN and default QUIC - * transport settings. Client TLS credentials are then set up using a - * certificate file, private key file, and CA certificate file. - * - * If configuration creation or credential loading fails, an error is logged. - */ - void initConfiguration(); - - /** - * @brief Opens a QUIC configuration. - * - * Creates a QUIC configuration associated with the current registration, - * configured ALPN, and optional transport settings. - * - * If settings are not provided, default QUIC settings are used. - * On failure, an error is logged. - * - * @param settings Optional QUIC transport settings. - */ - void configurationOpen(const QUIC_SETTINGS *settings); - - /** - * @brief Loads TLS credentials into the QUIC configuration. - * - * Loads client-side TLS credentials into the active QUIC configuration. - * The credentials define the certificate, private key, and CA certificate - * used for secure communication. - * - * If credential loading fails, an error is logged. - * - * @param credential Pointer to the QUIC credential configuration. - */ - void configurationLoadCredential(const QUIC_CREDENTIAL_CONFIG *credential) const; - - /** - * @brief Handles a successfully decoded incoming message. - * - * Pushes the decoded ExternalServer message into the inbound queue - * in a thread-safe manner and notifies the receiver thread that a - * new message is available. - * - * @param msg Decoded message received from the peer. - */ - void onMessageDecoded(std::shared_ptr msg); - - /** - * @brief Sends a message to the peer using a QUIC stream. - * - * Opens a new QUIC stream on the active connection and serializes the - * provided ExternalClient message into a byte buffer. - * The message is sent using a single StreamSend call with START and FIN - * flags, effectively opening, sending, and closing the stream. - * - * The allocated send buffer is released asynchronously in the - * QUIC_STREAM_EVENT_SEND_COMPLETE callback. - * - * @param message Message to be sent to the peer. - */ - void sendViaQuicStream(const std::shared_ptr &message); - - /** - * @brief Closes the active QUIC configuration. - */ - void closeConfiguration(); - - /** - * @brief Closes the QUIC registration. - */ - void closeRegistration(); - - /** - * @brief Closes the MsQuic API and releases associated resources. - */ - void closeMsQuic(); - - /** - * @brief Stops the QUIC communication and releases all resources. - * - * Initiates connection shutdown and closes the QUIC configuration, - * registration, and MsQuic API in the correct order. - * All waiting sender and receiver threads are unblocked by notifying - * the associated condition variables. - */ - void stop(); - - /** - * @brief Handles QUIC connection-level events. - * - * Processes connection lifecycle events reported by MsQuic, including - * successful connection establishment, peer-initiated shutdown, and - * shutdown completion. - * - * All QUIC_CONNECTION_EVENT cases are documented at - * https://microsoft.github.io/msquic/msquicdocs/docs/api/QUIC_CONNECTION_EVENT.html - * - * @param connection QUIC connection handle. - * @param context User-defined context pointer (QuicCommunication instance). - * @param event Connection event information provided by MsQuic. - * @return QUIC_STATUS_SUCCESS to indicate successful event handling. - */ - static QUIC_STATUS QUIC_API connectionCallback(HQUIC connection, void *context, QUIC_CONNECTION_EVENT *event); - - /** - * @brief Handles QUIC stream-level events. - * - * Processes stream events reported by MsQuic, including data reception, - * send completion, stream startup, and shutdown notifications. - * - * All QUIC_STREAM_EVENT cases are documented at - * https://microsoft.github.io/msquic/msquicdocs/docs/api/QUIC_STREAM_EVENT.html - * - * @param stream QUIC stream handle associated with the event. - * @param context User-defined context pointer (QuicCommunication instance). - * @param event Stream event information provided by MsQuic. - * @return QUIC_STATUS_SUCCESS to indicate successful event handling. - */ - static QUIC_STATUS QUIC_API streamCallback(HQUIC stream, void *context, QUIC_STREAM_EVENT *event); - - /** - * @brief Sender thread main loop for outbound messages. - * - * Waits for outbound messages while the connection is in the CONNECTED state. - * Messages are dequeued and sent over individual QUIC streams. - * - * If sending fails, the message is re-enqueued for a later retry. - * The loop terminates when the connection leaves the CONNECTED state. - */ - void senderLoop(); - - /** - * @brief Retrieves the QUIC stream identifier for the given stream handle. - * - * Queries MsQuic for the stream ID associated with the provided HQUIC stream. - * If the parameter query fails, an empty optional is returned. - * - * @param stream Valid QUIC stream handle. - * @return Stream identifier on success, or std::nullopt if the query fails. - */ - std::optional getStreamId(HQUIC stream); - - /** - * @brief Retrieves a protocol setting value as a plain string. - * - * Extracts a value from ExternalConnectionSettings::protocolSettings and - * transparently handles values stored as JSON-encoded strings. - * - * Allows uniform access to protocol settings regardless of whether - * they were stored as plain strings or JSON-serialized values. - * - * @param settings External connection settings containing protocolSettings. - * @param key Key identifying the protocol setting. - * @return Plain string value suitable for direct use (e.g. file paths). - * - * @throws std::out_of_range if the key is not present in protocolSettings. - */ - static std::string getProtocolSettingsString( - const structures::ExternalConnectionSettings& settings, - std::string_view key - ); - - /** - * @brief Converts a ConnectionState value to a human-readable string. - * - * @param state Connection state to convert. - * @return Null-terminated string describing the connection state. - */ - static const char* toString(ConnectionState state) { - switch (state) { - case ConnectionState::DISCONNECTED: return "Disconnected"; - case ConnectionState::CONNECTING: return "Connecting"; - case ConnectionState::CONNECTED: return "Connected"; - case ConnectionState::CLOSING: return "Closing"; - default: return "Unknown"; - } - } -}; - + class QuicCommunication : public ICommunicationChannel { + public: + explicit QuicCommunication(const structures::ExternalConnectionSettings &settings, const std::string &company, + const std::string &vehicleName); + + ~QuicCommunication() override; + + /** + * @brief Initializes a QUIC connection to the server. + * + * Attempts to establish a new QUIC connection. + * It first atomically verifies that the current connection state is + * NOT_CONNECTED and transitions it to CONNECTING in order to prevent + * concurrent connection attempts. + * + * After the state transition, it opens a QUIC connection handle and + * starts the connection using the configured server address, port, + * and QUIC configuration. + * + * Any failures during the connection open or start process are logged. + */ + void initializeConnection() override; + + /** + * @brief Enqueues an outgoing message to be sent over the QUIC connection. + * + * Creates a shared copy of the provided ExternalClient message + * and pushes it into the outbound message queue in a thread-safe manner. + * After enqueuing, it notifies the sender thread via a condition variable + * that a new message is available for sending. + * + * @param message Pointer to the message that should be sent. + * @return true Always returns true to indicate the message was successfully enqueued. + */ + bool sendMessage(ExternalProtocol::ExternalClient *message) override; + + /** + * @brief Receives an incoming message from the QUIC connection. + * + * Waits for an incoming message to appear in the inbound + * queue or for the connection state to change from CONNECTED. + * The wait is bounded by a configurable timeout. + * + * If the wait times out, the connection is no longer in the CONNECTED + * state, or no message is available, the function returns nullptr. + * Otherwise, it retrieves and removes the next message from the inbound + * queue and returns it. + * + * @return A shared pointer to the received ExternalServer message, + * or nullptr if no message is available or the connection is not active. + */ + std::shared_ptr receiveMessage() override; + + /** + * @brief Initiates a graceful shutdown of the QUIC connection. + * + * Requests an orderly shutdown of the active QUIC connection. + * If no connection is currently established, the function returns immediately. + * + * The shutdown is performed asynchronously. Completion is signaled via the + * QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE event, which is handled in + * the connectionCallback. + */ + void closeConnection() override; + + private: + /// Pointer to the MsQuic API function table + const QUIC_API_TABLE *quic_{nullptr}; + /// QUIC registration handle associated with the application + HQUIC registration_{nullptr}; + /// QUIC configuration handle (ALPN, credentials, transport settings) + HQUIC config_{nullptr}; + /// Active QUIC connection handle + HQUIC connection_{nullptr}; + /// Application-Layer Protocol Negotiation (ALPN) string + std::string alpn_; + /// QUIC buffer wrapping the ALPN string + QUIC_BUFFER alpnBuffer_{}; + + /// Path to the client certificate file + std::string certFile_; + /// Path to the client private key file + std::string keyFile_; + /// Path to the CA certificate file + std::string caFile_; + + /// Atomic state of the connection used for synchronization across threads + std::atomic connectionState_{ConnectionState::NOT_CONNECTED}; + + /// @name Inbound (peer → this) + /// @{ + /// Queue of incoming messages received from the peer + std::queue > inboundQueue_; + /// Mutex protecting access to the inbound message queue + std::mutex inboundMutex_; + /// Condition variable for signaling inbound message availability + std::condition_variable inboundCv_; + /// @} + + /// @name Outbound (this → peer) + /// @{ + /// Queue of outgoing messages to be sent to the peer + std::queue > outboundQueue_; + /// Mutex protecting access to the outbound message queue + std::mutex outboundMutex_; + /// Condition variable for signaling outbound message availability + std::condition_variable outboundCv_; + /// Dedicated sender thread responsible for transmitting outbound messages + std::jthread senderThread_; + /// @} + + /** + * @brief Loads and initializes the MsQuic API. + * + * Initializes the MsQuic library and retrieves the + * QUIC API function table. The resulting table is stored for later + * use when creating registrations, configurations, and connections. + * + * If the initialization fails, an error is logged. + */ + void loadMsQuic(); + + /** + * @brief Initializes a QUIC registration. + * + * Creates a QUIC registration with the specified application name and + * a low-latency execution profile. The registration is required for + * creating QUIC configurations and connections. + * + * If registration creation fails, an error is logged. + * + * @param appName Application name used to identify the QUIC registration. + */ + void initRegistration(const char *appName); + + /** + * @brief Initializes the QUIC configuration and loads client credentials. + * + * Opens a QUIC configuration using the configured ALPN and default QUIC + * transport settings. Client TLS credentials are then set up using a + * certificate file, private key file, and CA certificate file. + * + * If configuration creation or credential loading fails, an error is logged. + */ + void initConfiguration(); + + /** + * @brief Opens a QUIC configuration. + * + * Creates a QUIC configuration associated with the current registration, + * configured ALPN, and optional transport settings. + * + * If settings are not provided, default QUIC settings are used. + * On failure, an error is logged. + * + * @param settings Optional QUIC transport settings. + */ + void configurationOpen(const QUIC_SETTINGS *settings); + + /** + * @brief Loads TLS credentials into the QUIC configuration. + * + * Loads client-side TLS credentials into the active QUIC configuration. + * The credentials define the certificate, private key, and CA certificate + * used for secure communication. + * + * If credential loading fails, an error is logged. + * + * @param credential Pointer to the QUIC credential configuration. + */ + void configurationLoadCredential(const QUIC_CREDENTIAL_CONFIG *credential) const; + + /** + * @brief Handles a successfully decoded incoming message. + * + * Pushes the decoded ExternalServer message into the inbound queue + * in a thread-safe manner and notifies the receiver thread that a + * new message is available. + * + * @param msg Decoded message received from the peer. + */ + void onMessageDecoded(std::shared_ptr msg); + + /** + * @brief Sends a message to the peer using a QUIC stream. + * + * Opens a new QUIC stream on the active connection and serializes the + * provided ExternalClient message into a byte buffer. + * The message is sent using a single StreamSend call with START and FIN + * flags, effectively opening, sending, and closing the stream. + * + * The allocated send buffer is released asynchronously in the + * QUIC_STREAM_EVENT_SEND_COMPLETE callback. + * + * @param message Message to be sent to the peer. + */ + void sendViaQuicStream(const std::shared_ptr &message); + + /** + * @brief Closes the active QUIC configuration. + */ + void closeConfiguration(); + + /** + * @brief Closes the QUIC registration. + */ + void closeRegistration(); + + /** + * @brief Closes the MsQuic API and releases associated resources. + */ + void closeMsQuic(); + + /** + * @brief Stops the QUIC communication and releases all resources. + * + * Initiates connection shutdown and closes the QUIC configuration, + * registration, and MsQuic API in the correct order. + * All waiting sender and receiver threads are unblocked by notifying + * the associated condition variables. + */ + void stop(); + + /** + * @brief Handles QUIC connection-level events. + * + * Processes connection lifecycle events reported by MsQuic, including + * successful connection establishment, peer-initiated shutdown, and + * shutdown completion. + * + * All QUIC_CONNECTION_EVENT cases are documented at + * https://microsoft.github.io/msquic/msquicdocs/docs/api/QUIC_CONNECTION_EVENT.html + * + * @param connection QUIC connection handle. + * @param context User-defined context pointer (QuicCommunication instance). + * @param event Connection event information provided by MsQuic. + * @return QUIC_STATUS_SUCCESS to indicate successful event handling. + */ + static QUIC_STATUS QUIC_API connectionCallback(HQUIC connection, void *context, QUIC_CONNECTION_EVENT *event); + + /** + * @brief Handles QUIC stream-level events. + * + * Processes stream events reported by MsQuic, including data reception, + * send completion, stream startup, and shutdown notifications. + * + * All QUIC_STREAM_EVENT cases are documented at + * https://microsoft.github.io/msquic/msquicdocs/docs/api/QUIC_STREAM_EVENT.html + * + * @param stream QUIC stream handle associated with the event. + * @param context User-defined context pointer (QuicCommunication instance). + * @param event Stream event information provided by MsQuic. + * @return QUIC_STATUS_SUCCESS to indicate successful event handling. + */ + static QUIC_STATUS QUIC_API streamCallback(HQUIC stream, void *context, QUIC_STREAM_EVENT *event); + + /** + * @brief Sender thread main loop for outbound messages. + * + * Waits for outbound messages while the connection is in the CONNECTED state. + * Messages are dequeued and sent over individual QUIC streams. + * + * If sending fails, the message is re-enqueued for a later retry. + * The loop terminates when the connection leaves the CONNECTED state. + */ + void senderLoop(); + + /** + * @brief Retrieves the QUIC stream identifier for the given stream handle. + * + * Queries MsQuic for the stream ID associated with the provided HQUIC stream. + * If the parameter query fails, an empty optional is returned. + * + * @param stream Valid QUIC stream handle. + * @return Stream identifier on success, or std::nullopt if the query fails. + */ + std::optional getStreamId(HQUIC stream); + + /** + * @brief Retrieves a protocol setting value as a plain string. + * + * Extracts a value from ExternalConnectionSettings::protocolSettings and + * transparently handles values stored as JSON-encoded strings. + * + * Allows uniform access to protocol settings regardless of whether + * they were stored as plain strings or JSON-serialized values. + * + * @param settings External connection settings containing protocolSettings. + * @param key Key identifying the protocol setting. + * @return Plain string value suitable for direct use (e.g. file paths). + * + * @throws std::out_of_range if the key is not present in protocolSettings. + */ + static std::string getProtocolSettingsString( + const structures::ExternalConnectionSettings &settings, + std::string_view key + ); + }; } diff --git a/source/bringauto/external_client/connection/ExternalConnection.cpp b/source/bringauto/external_client/connection/ExternalConnection.cpp index 4051818f..802e275c 100644 --- a/source/bringauto/external_client/connection/ExternalConnection.cpp +++ b/source/bringauto/external_client/connection/ExternalConnection.cpp @@ -155,6 +155,7 @@ int ExternalConnection::connectMessageHandle(const std::vectorreceiveMessage(); + return OK; if(connectResponseMsg == nullptr) { log::logError("Communication client couldn't receive any message"); return NO_MESSAGE_AVAILABLE; @@ -202,6 +203,7 @@ int ExternalConnection::statusMessageHandle(const std::vectorreceiveMessage(); + return OK; if(statusResponseMsg == nullptr) { log::logError("Communication client couldn't receive any message"); return NO_MESSAGE_AVAILABLE; @@ -227,6 +229,7 @@ int ExternalConnection::commandMessageHandle(const std::vectorreceiveMessage(); + return OK; if(commandMsg == nullptr) { log::logError("Communication client couldn't receive any message"); return NO_MESSAGE_AVAILABLE; diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 850255c4..d75e3bd1 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -2,27 +2,34 @@ #include #include +#include namespace bringauto::external_client::connection::communication { - - QuicCommunication::QuicCommunication(const structures::ExternalConnectionSettings &settings, const std::string &company, - const std::string &vehicleName) : ICommunicationChannel(settings), - certFile_(getProtocolSettingsString(settings, settings::Constants::CLIENT_CERT)), - keyFile_(getProtocolSettingsString(settings, settings::Constants::CLIENT_KEY)), - caFile_(getProtocolSettingsString(settings, settings::Constants::CA_FILE)) - { + QuicCommunication::QuicCommunication(const structures::ExternalConnectionSettings &settings, + const std::string &company, + const std::string &vehicleName) : ICommunicationChannel(settings), + certFile_(getProtocolSettingsString( + settings, + settings::Constants::CLIENT_CERT)), + keyFile_(getProtocolSettingsString( + settings, + settings::Constants::CLIENT_KEY)), + caFile_(getProtocolSettingsString( + settings, + settings::Constants::CA_FILE)) { alpn_ = "sample"; - alpnBuffer_.Buffer = reinterpret_cast(alpn_.data()); + alpnBuffer_.Buffer = reinterpret_cast(alpn_.data()); alpnBuffer_.Length = static_cast(alpn_.size()); - settings::Logger::logInfo("[quic] Initialize QUIC communication to {}:{} for {}/{}", settings.serverIp, settings.port, company, vehicleName); + settings::Logger::logInfo("[quic] Initialize QUIC communication to {}:{} for {}/{}", settings.serverIp, + settings.port, company, vehicleName); try { loadMsQuic(); initRegistration("module-gateway-quic-client"); initConfiguration(); - } catch (const std::exception& e) { + } catch (const std::exception &e) { settings::Logger::logError("[quic] Failed to initialize QUIC communication; {}", e.what()); } } @@ -32,11 +39,12 @@ namespace bringauto::external_client::connection::communication { } void QuicCommunication::initializeConnection() { - settings::Logger::logDebug("[quic] Connecting to server when {}", toString(connectionState_)); - + settings::Logger::logDebug("[quic] Connecting to server when {}", + common_utils::EnumUtils::connectionStateToString(connectionState_)); - ConnectionState expected = ConnectionState::DISCONNECTED; - if (! connectionState_.compare_exchange_strong(expected, ConnectionState::CONNECTING, std::memory_order_acq_rel)) { + ConnectionState expected = ConnectionState::NOT_CONNECTED; + if (!connectionState_. + compare_exchange_strong(expected, ConnectionState::CONNECTING, std::memory_order_acq_rel)) { throw std::logic_error("Connection already in progress or established"); } @@ -60,15 +68,15 @@ namespace bringauto::external_client::connection::communication { connectionState_ = ConnectionState::CONNECTING; } - bool QuicCommunication::sendMessage(ExternalProtocol::ExternalClient* message) { - if (connectionState_.load() == ConnectionState::DISCONNECTED) { - settings::Logger::logWarning("[quic] Connection not established, cannot send message"); - return false; - } + bool QuicCommunication::sendMessage(ExternalProtocol::ExternalClient *message) { + if (connectionState_.load() == ConnectionState::NOT_CONNECTED) { + settings::Logger::logWarning("[quic] Connection not established, cannot send message"); + return false; + } - { - auto copy = std::make_shared(*message); - std::lock_guard lock(outboundMutex_); + { + auto copy = std::make_shared(*message); + std::lock_guard lock(outboundMutex_); outboundQueue_.push(std::move(copy)); } settings::Logger::logDebug("[quic] Notifying sender thread about enqueued message"); @@ -80,12 +88,12 @@ namespace bringauto::external_client::connection::communication { std::unique_lock lock(inboundMutex_); if (!inboundCv_.wait_for( - lock, - settings::receive_message_timeout, - [this] { return !inboundQueue_.empty() || connectionState_.load() != ConnectionState::CONNECTED; } - )) { - return nullptr; - } + lock, + settings::receive_message_timeout, + [this] { return !inboundQueue_.empty() || connectionState_.load() != ConnectionState::CONNECTED; } + )) { + return nullptr; + } if (connectionState_.load() != ConnectionState::CONNECTED || inboundQueue_.empty()) { return nullptr; @@ -103,25 +111,26 @@ namespace bringauto::external_client::connection::communication { } } - void QuicCommunication::initRegistration(const char* appName) { - QUIC_REGISTRATION_CONFIG config {}; - config.AppName = const_cast(appName); + void QuicCommunication::initRegistration(const char *appName) { + QUIC_REGISTRATION_CONFIG config{}; + config.AppName = const_cast(appName); config.ExecutionProfile = QUIC_EXECUTION_PROFILE_LOW_LATENCY; QUIC_STATUS status = quic_->RegistrationOpen(&config, ®istration_); if (QUIC_FAILED(status)) { - throw std::runtime_error(std::format("[quic] Failed to open QUIC registration; QUIC_STATUS => {:x}", status)); + throw std::runtime_error( + std::format("[quic] Failed to open QUIC registration; QUIC_STATUS => {:x}", status)); } } void QuicCommunication::initConfiguration() { configurationOpen(nullptr); - QUIC_CERTIFICATE_FILE certificate {}; + QUIC_CERTIFICATE_FILE certificate{}; certificate.CertificateFile = certFile_.c_str(); certificate.PrivateKeyFile = keyFile_.c_str(); - QUIC_CREDENTIAL_CONFIG credential {}; + QUIC_CREDENTIAL_CONFIG credential{}; credential.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE; credential.Flags = QUIC_CREDENTIAL_FLAG_CLIENT | QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE; credential.CertificateFile = &certificate; @@ -130,7 +139,7 @@ namespace bringauto::external_client::connection::communication { configurationLoadCredential(&credential); } - void QuicCommunication::configurationOpen(const QUIC_SETTINGS* settings) { + void QuicCommunication::configurationOpen(const QUIC_SETTINGS *settings) { const uint32_t settingsSize = settings ? sizeof(*settings) : 0; QUIC_STATUS status = quic_->ConfigurationOpen( @@ -144,11 +153,12 @@ namespace bringauto::external_client::connection::communication { ); if (QUIC_FAILED(status)) { - throw std::runtime_error(std::format("[quic] Failed to open QUIC configuration; QUIC_STATUS => {:x}", status)); + throw std::runtime_error(std::format("[quic] Failed to open QUIC configuration; QUIC_STATUS => {:x}", + status)); } } - void QuicCommunication::configurationLoadCredential(const QUIC_CREDENTIAL_CONFIG* credential) const { + void QuicCommunication::configurationLoadCredential(const QUIC_CREDENTIAL_CONFIG *credential) const { const QUIC_STATUS status = quic_->ConfigurationLoadCredential(config_, credential); if (QUIC_FAILED(status)) { throw std::runtime_error(std::format("[quic] Failed to load QUIC credential; QUIC_STATUS => {:x}", status)); @@ -156,7 +166,7 @@ namespace bringauto::external_client::connection::communication { } void QuicCommunication::closeConnection() { - if (! connection_) { + if (!connection_) { return; } @@ -212,8 +222,8 @@ namespace bringauto::external_client::connection::communication { inboundCv_.notify_one(); } - void QuicCommunication::sendViaQuicStream(const std::shared_ptr& message) { - HQUIC stream { nullptr }; + void QuicCommunication::sendViaQuicStream(const std::shared_ptr &message) { + HQUIC stream{nullptr}; if (QUIC_FAILED(quic_->StreamOpen(connection_, QUIC_STREAM_OPEN_FLAG_NONE, streamCallback, this, &stream))) { throw std::runtime_error("[quic] StreamOpen failed"); } @@ -225,19 +235,19 @@ namespace bringauto::external_client::connection::communication { throw std::runtime_error("[quic] Message serialization failed"); } - auto* buf = new QUIC_BUFFER{}; + auto *buf = new QUIC_BUFFER{}; buf->Length = static_cast(size); buf->Buffer = new uint8_t[buf->Length]; std::memcpy(buf->Buffer, buffer.get(), buf->Length); const QUIC_STATUS status = quic_->StreamSend(stream, buf, 1, - /** - * START => Simulates quic_->StreamStart before send - * FIN => Simulates quic_->StreamShutdown after send - */ - QUIC_SEND_FLAG_START | QUIC_SEND_FLAG_FIN, - buf + /** + * START => Simulates quic_->StreamStart before send + * FIN => Simulates quic_->StreamShutdown after send + */ + QUIC_SEND_FLAG_START | QUIC_SEND_FLAG_FIN, + buf ); if (QUIC_FAILED(status)) { @@ -252,8 +262,9 @@ namespace bringauto::external_client::connection::communication { settings::Logger::logDebug("[quic] [stream {}] Message sent", streamId ? *streamId : 0); } - QUIC_STATUS QUIC_API QuicCommunication::connectionCallback(HQUIC connection, void* context, QUIC_CONNECTION_EVENT* event) { - auto* self = static_cast(context); + QUIC_STATUS QUIC_API QuicCommunication::connectionCallback(HQUIC connection, void *context, + QUIC_CONNECTION_EVENT *event) { + auto *self = static_cast(context); switch (event->Type) { /// Fired when the QUIC handshake is complete and the connection is ready @@ -275,7 +286,7 @@ namespace bringauto::external_client::connection::communication { case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE: { settings::Logger::logInfo("[quic] Connection shutdown complete"); - self->connectionState_ = ConnectionState::DISCONNECTED; + self->connectionState_ = ConnectionState::NOT_CONNECTED; self->outboundCv_.notify_all(); if (self->senderThread_.joinable()) { @@ -296,7 +307,8 @@ namespace bringauto::external_client::connection::communication { } default: { - settings::Logger::logDebug("[quic] Unhandled connection event 0x{:x}", static_cast(event->Type)); + settings::Logger::logDebug("[quic] Unhandled connection event 0x{:x}", + static_cast(event->Type)); break; } } @@ -304,114 +316,115 @@ namespace bringauto::external_client::connection::communication { return QUIC_STATUS_SUCCESS; } - QUIC_STATUS QUIC_API QuicCommunication::streamCallback(HQUIC stream, void* context, QUIC_STREAM_EVENT* event) { - auto* self = static_cast(context); + QUIC_STATUS QUIC_API QuicCommunication::streamCallback(HQUIC stream, void *context, QUIC_STREAM_EVENT *event) { + auto *self = static_cast(context); auto streamId = self->getStreamId(stream); - switch (event->Type) { - /// Raised when the peer sends stream data and MsQuic delivers received bytes to the application. - case QUIC_STREAM_EVENT_RECEIVE: { - settings::Logger::logDebug( + switch (event->Type) { + /// Raised when the peer sends stream data and MsQuic delivers received bytes to the application. + case QUIC_STREAM_EVENT_RECEIVE: { + settings::Logger::logDebug( "[quic] [stream {}] Received {:d} bytes in {:d} buffers", streamId ? *streamId : 0, event->RECEIVE.TotalBufferLength, event->RECEIVE.BufferCount ); - std::vector data; - data.reserve(event->RECEIVE.TotalBufferLength); + std::vector data; + data.reserve(event->RECEIVE.TotalBufferLength); - for (uint32_t i = 0; i < event->RECEIVE.BufferCount; ++i) { - const auto& b = event->RECEIVE.Buffers[i]; - data.insert(data.end(), b.Buffer, b.Buffer + b.Length); - } + for (uint32_t i = 0; i < event->RECEIVE.BufferCount; ++i) { + const auto &b = event->RECEIVE.Buffers[i]; + data.insert(data.end(), b.Buffer, b.Buffer + b.Length); + } - auto msg = std::make_shared(); - if (!msg->ParseFromArray(data.data(), static_cast(data.size()))) { - settings::Logger::logError("[quic] Failed to parse ExternalServer message"); + auto msg = std::make_shared(); + if (!msg->ParseFromArray(data.data(), static_cast(data.size()))) { + settings::Logger::logError("[quic] Failed to parse ExternalServer message"); } else { self->onMessageDecoded(std::move(msg)); } - break; - } - - /// Raised when the peer has finished sending on this stream - /// (peer's FIN has been fully received and processed). - case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: { - settings::Logger::logDebug("[quic] Peer stream send shutdown"); - break; - } - - /// Raised when the local send direction is fully shut down - /// and the peer has acknowledged the FIN. - case QUIC_STREAM_EVENT_SEND_SHUTDOWN_COMPLETE: { - settings::Logger::logDebug("[quic] [stream {}] Stream send shutdown complete", streamId ? *streamId : 0); - break; - } - - /// Raised after StreamStart completes successfully - /// and the stream becomes active with a valid stream ID. - case QUIC_STREAM_EVENT_START_COMPLETE: { - settings::Logger::logDebug("[quic] Stream start completed"); - break; - } - - /// Raised when a single StreamSend operation completes - /// (data was accepted, acknowledged, or the send was canceled). - case QUIC_STREAM_EVENT_SEND_COMPLETE: { - /** - * This event is raised when MsQuic has finished processing - * a single StreamSend request. - * - * Meaning: - * - MsQuic no longer needs the application-provided buffer: - * - the data has been acknowledged (ACKed) by the peer - * at the QUIC transport level and will not be retransmitted - * - OR the send was canceled (Canceled == TRUE), e.g. due to - * stream or connection shutdown - * - * Reliability semantics: - * - the ACK is strictly a QUIC transport-level acknowledgment - * - it does NOT mean the peer application has read or processed - * the data - * - * Practical consequence: - * - this is the only correct place to safely free the memory - * passed to StreamSend (via ClientContext) - */ - if (event->SEND_COMPLETE.Canceled) { - settings::Logger::logWarning("[quic] Stream send canceled"); - } else { - settings::Logger::logDebug("[quic] Stream send completed"); - } - - const auto* buf = - static_cast(event->SEND_COMPLETE.ClientContext); - - if (buf) { - delete[] buf->Buffer; - delete buf; - } - - break; - } - - /// Raised when both send and receive directions are closed - /// and the stream lifecycle is fully complete. - case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: { - settings::Logger::logDebug("[quic] Stream shutdown complete"); - self->quic_->StreamClose(stream); - break; - } - - default: { - settings::Logger::logDebug("[quic] Unhandled stream event 0x{:x}", static_cast(event->Type)); - break; - } - } - - return QUIC_STATUS_SUCCESS; + break; + } + + /// Raised when the peer has finished sending on this stream + /// (peer's FIN has been fully received and processed). + case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: { + settings::Logger::logDebug("[quic] Peer stream send shutdown"); + break; + } + + /// Raised when the local send direction is fully shut down + /// and the peer has acknowledged the FIN. + case QUIC_STREAM_EVENT_SEND_SHUTDOWN_COMPLETE: { + settings::Logger::logDebug("[quic] [stream {}] Stream send shutdown complete", + streamId ? *streamId : 0); + break; + } + + /// Raised after StreamStart completes successfully + /// and the stream becomes active with a valid stream ID. + case QUIC_STREAM_EVENT_START_COMPLETE: { + settings::Logger::logDebug("[quic] Stream start completed"); + break; + } + + /// Raised when a single StreamSend operation completes + /// (data was accepted, acknowledged, or the send was canceled). + case QUIC_STREAM_EVENT_SEND_COMPLETE: { + /** + * This event is raised when MsQuic has finished processing + * a single StreamSend request. + * + * Meaning: + * - MsQuic no longer needs the application-provided buffer: + * - the data has been acknowledged (ACKed) by the peer + * at the QUIC transport level and will not be retransmitted + * - OR the send was canceled (Canceled == TRUE), e.g. due to + * stream or connection shutdown + * + * Reliability semantics: + * - the ACK is strictly a QUIC transport-level acknowledgment + * - it does NOT mean the peer application has read or processed + * the data + * + * Practical consequence: + * - this is the only correct place to safely free the memory + * passed to StreamSend (via ClientContext) + */ + if (event->SEND_COMPLETE.Canceled) { + settings::Logger::logWarning("[quic] Stream send canceled"); + } else { + settings::Logger::logDebug("[quic] Stream send completed"); + } + + const auto *buf = + static_cast(event->SEND_COMPLETE.ClientContext); + + if (buf) { + delete[] buf->Buffer; + delete buf; + } + + break; + } + + /// Raised when both send and receive directions are closed + /// and the stream lifecycle is fully complete. + case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: { + settings::Logger::logDebug("[quic] Stream shutdown complete"); + self->quic_->StreamClose(stream); + break; + } + + default: { + settings::Logger::logDebug("[quic] Unhandled stream event 0x{:x}", static_cast(event->Type)); + break; + } + } + + return QUIC_STATUS_SUCCESS; } void QuicCommunication::senderLoop() { @@ -425,7 +438,7 @@ namespace bringauto::external_client::connection::communication { settings::Logger::logDebug("[quic] Sender thread loop waiting for outbound queue"); outboundCv_.wait(lock, [this] { return !outboundQueue_.empty() || - connectionState_.load() != ConnectionState::CONNECTED; + connectionState_.load() != ConnectionState::CONNECTED; }); if (connectionState_.load() != ConnectionState::CONNECTED) { @@ -439,7 +452,7 @@ namespace bringauto::external_client::connection::communication { try { sendViaQuicStream(msg); - } catch (const std::exception& e) { + } catch (const std::exception &e) { settings::Logger::logError(std::format("[quic] Message send failed; {}", e.what())); } } @@ -461,10 +474,10 @@ namespace bringauto::external_client::connection::communication { } std::string QuicCommunication::getProtocolSettingsString( - const structures::ExternalConnectionSettings& settings, + const structures::ExternalConnectionSettings &settings, std::string_view key ) { - const auto& raw = settings.protocolSettings.at(std::string(key)); + const auto &raw = settings.protocolSettings.at(std::string(key)); if (nlohmann::json::accept(raw)) { auto j = nlohmann::json::parse(raw); From cd4eb2a901404f7e28f8f5b92cb27d4465a5ea5b Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Wed, 7 Jan 2026 08:24:11 +0000 Subject: [PATCH 40/55] Refactor `QuicCommunication`: Replace hardcoded ALPN value with `settings::Constants::ALPN` for improved configurability. --- include/bringauto/settings/Constants.hpp | 1 + .../connection/communication/QuicCommunication.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/bringauto/settings/Constants.hpp b/include/bringauto/settings/Constants.hpp index 2f9b1483..fdd358ea 100644 --- a/include/bringauto/settings/Constants.hpp +++ b/include/bringauto/settings/Constants.hpp @@ -172,6 +172,7 @@ class Constants { inline static constexpr std::string_view CA_FILE { "ca-file" }; inline static constexpr std::string_view CLIENT_CERT { "client-cert" }; inline static constexpr std::string_view CLIENT_KEY { "client-key" }; + inline static constexpr std::string_view ALPN { "alpn" }; inline static constexpr std::string_view MODULES { "modules" }; inline static constexpr std::string_view AERON_CONNECTION { "aeron:ipc"}; diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index d75e3bd1..749b628f 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -9,6 +9,9 @@ namespace bringauto::external_client::connection::communication { QuicCommunication::QuicCommunication(const structures::ExternalConnectionSettings &settings, const std::string &company, const std::string &vehicleName) : ICommunicationChannel(settings), + alpn_(getProtocolSettingsString( + settings, + settings::Constants::ALPN)), certFile_(getProtocolSettingsString( settings, settings::Constants::CLIENT_CERT)), @@ -18,7 +21,6 @@ namespace bringauto::external_client::connection::communication { caFile_(getProtocolSettingsString( settings, settings::Constants::CA_FILE)) { - alpn_ = "sample"; alpnBuffer_.Buffer = reinterpret_cast(alpn_.data()); alpnBuffer_.Length = static_cast(alpn_.size()); From b3bd07d98c526cda5df4405517140b6a0b1805cb Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Wed, 7 Jan 2026 09:02:40 +0000 Subject: [PATCH 41/55] Add `quic_example.json` configuration file for QUIC module settings --- resources/config/quic_example.json | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 resources/config/quic_example.json diff --git a/resources/config/quic_example.json b/resources/config/quic_example.json new file mode 100644 index 00000000..0edbbc18 --- /dev/null +++ b/resources/config/quic_example.json @@ -0,0 +1,35 @@ +{ + "logging": { + "console": { "level": "DEBUG", "use": true }, + "file": { "level": "DEBUG", "use": false, "path": "./log" } + }, + + "internal-server-settings": { "port": 1636 }, + + "module-paths": { + "1": "/home/bringauto/modules/mission_module/lib/libmission-module-gateway-shared.so", + "2": "/home/bringauto/modules/io_module/lib/libio-module-gateway-shared.so", + "3": "/home/bringauto/modules/transparent_module/lib/libtransparent-module-gateway-shared.so" + }, + + "module-binary-path": "", + + "external-connection" : { + "company" : "bringauto", + "vehicle-name" : "virtual_vehicle", + "endpoints" : [ + { + "protocol-type" : "QUIC", + "server-ip": "127.0.0.1", + "port": 6121, + "quic-settings": { + "ca-file" : "build/certs/ca.pem", + "client-cert" : "build/certs/client.pem", + "client-key" : "build/certs/client-key.pem", + "alpn" : "sample" + }, + "modules": [1,2,3] + } + ] + } +} From daed68a175643d401d2e569dff6a468419559bc7 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Wed, 7 Jan 2026 09:03:47 +0000 Subject: [PATCH 42/55] Refactor `ExternalConnection`: Remove redundant `return OK` statements after `receiveMessage` calls. --- .../external_client/connection/ExternalConnection.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/source/bringauto/external_client/connection/ExternalConnection.cpp b/source/bringauto/external_client/connection/ExternalConnection.cpp index 802e275c..4051818f 100644 --- a/source/bringauto/external_client/connection/ExternalConnection.cpp +++ b/source/bringauto/external_client/connection/ExternalConnection.cpp @@ -155,7 +155,6 @@ int ExternalConnection::connectMessageHandle(const std::vectorreceiveMessage(); - return OK; if(connectResponseMsg == nullptr) { log::logError("Communication client couldn't receive any message"); return NO_MESSAGE_AVAILABLE; @@ -203,7 +202,6 @@ int ExternalConnection::statusMessageHandle(const std::vectorreceiveMessage(); - return OK; if(statusResponseMsg == nullptr) { log::logError("Communication client couldn't receive any message"); return NO_MESSAGE_AVAILABLE; @@ -229,7 +227,6 @@ int ExternalConnection::commandMessageHandle(const std::vectorreceiveMessage(); - return OK; if(commandMsg == nullptr) { log::logError("Communication client couldn't receive any message"); return NO_MESSAGE_AVAILABLE; From 55947f6864e8b7f028f9c8702f9e80389bdb17a4 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Wed, 7 Jan 2026 09:05:31 +0000 Subject: [PATCH 43/55] Clean up includes in `ExternalClient` and `QuicCommunication` for consistent include order and readability. --- source/bringauto/external_client/ExternalClient.cpp | 2 +- .../connection/communication/QuicCommunication.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/source/bringauto/external_client/ExternalClient.cpp b/source/bringauto/external_client/ExternalClient.cpp index 5835491d..c21b1457 100644 --- a/source/bringauto/external_client/ExternalClient.cpp +++ b/source/bringauto/external_client/ExternalClient.cpp @@ -4,13 +4,13 @@ #include #include #include +#include #include #include #include -#include "bringauto/external_client/connection/communication/QuicCommunication.hpp" namespace bringauto::external_client { diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 749b628f..a9196c15 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -1,9 +1,8 @@ +#include #include #include #include -#include - namespace bringauto::external_client::connection::communication { QuicCommunication::QuicCommunication(const structures::ExternalConnectionSettings &settings, From dceba15c8763e18fbcee32518943dfe23e4212ce Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Wed, 7 Jan 2026 10:42:46 +0000 Subject: [PATCH 44/55] Refactor `QuicCommunication`: Add `SendBuffer` abstraction for improved memory handling, update QUIC stream send logic, refine logging with stream IDs, and switch `initRegistration` to use `std::string`. --- .../communication/QuicCommunication.hpp | 10 ++- .../communication/QuicCommunication.cpp | 74 +++++++++++-------- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index e1a986d9..8d7aca48 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -11,7 +11,6 @@ #include - namespace bringauto::external_client::connection::communication { class QuicCommunication : public ICommunicationChannel { public: @@ -124,6 +123,13 @@ namespace bringauto::external_client::connection::communication { std::jthread senderThread_; /// @} + struct SendBuffer { + QUIC_BUFFER buffer{}; + std::unique_ptr storage; + + explicit SendBuffer(size_t size); + }; + /** * @brief Loads and initializes the MsQuic API. * @@ -146,7 +152,7 @@ namespace bringauto::external_client::connection::communication { * * @param appName Application name used to identify the QUIC registration. */ - void initRegistration(const char *appName); + void initRegistration(std::string appName); /** * @brief Initializes the QUIC configuration and loads client credentials. diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index a9196c15..9d213916 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -112,9 +112,9 @@ namespace bringauto::external_client::connection::communication { } } - void QuicCommunication::initRegistration(const char *appName) { + void QuicCommunication::initRegistration(std::string appName) { QUIC_REGISTRATION_CONFIG config{}; - config.AppName = const_cast(appName); + config.AppName = appName.c_str(); config.ExecutionProfile = QUIC_EXECUTION_PROFILE_LOW_LATENCY; QUIC_STATUS status = quic_->RegistrationOpen(&config, ®istration_); @@ -223,37 +223,50 @@ namespace bringauto::external_client::connection::communication { inboundCv_.notify_one(); } + QuicCommunication::SendBuffer::SendBuffer(size_t size) : storage(std::make_unique(size)) { + buffer.Length = static_cast(size); + buffer.Buffer = storage.get(); + } + void QuicCommunication::sendViaQuicStream(const std::shared_ptr &message) { HQUIC stream{nullptr}; if (QUIC_FAILED(quic_->StreamOpen(connection_, QUIC_STREAM_OPEN_FLAG_NONE, streamCallback, this, &stream))) { throw std::runtime_error("[quic] StreamOpen failed"); } - const auto size = message->ByteSizeLong(); - const auto buffer = std::make_unique(size); + const size_t size = message->ByteSizeLong(); + auto sendBuffer = std::make_unique(size); - if (!message->SerializeToArray(buffer.get(), static_cast(size))) { + if (!message->SerializeToArray(sendBuffer->storage.get(), static_cast(size))) { throw std::runtime_error("[quic] Message serialization failed"); } - auto *buf = new QUIC_BUFFER{}; - buf->Length = static_cast(size); - buf->Buffer = new uint8_t[buf->Length]; + settings::Logger::logDebug( + "[quic][debug] SendBuffer ptr={}, QUIC_BUFFER ptr={}, data ptr={}, size={}", + static_cast(sendBuffer.get()), + static_cast(&sendBuffer->buffer), + static_cast(sendBuffer->buffer.Buffer), + sendBuffer->buffer.Length + ); - std::memcpy(buf->Buffer, buffer.get(), buf->Length); + const SendBuffer *raw = sendBuffer.get(); + const QUIC_BUFFER *quicBuf = &raw->buffer; + SendBuffer *quicBufContext = sendBuffer.release(); - const QUIC_STATUS status = quic_->StreamSend(stream, buf, 1, - /** - * START => Simulates quic_->StreamStart before send - * FIN => Simulates quic_->StreamShutdown after send - */ - QUIC_SEND_FLAG_START | QUIC_SEND_FLAG_FIN, - buf + const QUIC_STATUS status = quic_->StreamSend( + stream, + quicBuf, + 1, + /** + * START => Simulates quic_->StreamStart before send + * FIN => Simulates quic_->StreamShutdown after send + */ + QUIC_SEND_FLAG_START | QUIC_SEND_FLAG_FIN, + quicBufContext ); if (QUIC_FAILED(status)) { - delete[] buf->Buffer; - delete buf; + std::unique_ptr reclaim{quicBufContext}; quic_->StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0); throw std::runtime_error(std::format("[quic] Failed to send QUIC stream; QUIC_STATUS => {:x}", status)); @@ -352,7 +365,7 @@ namespace bringauto::external_client::connection::communication { /// Raised when the peer has finished sending on this stream /// (peer's FIN has been fully received and processed). case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: { - settings::Logger::logDebug("[quic] Peer stream send shutdown"); + settings::Logger::logDebug("[quic] [stream {}] Peer stream send shutdown", streamId ? *streamId : 0); break; } @@ -367,7 +380,7 @@ namespace bringauto::external_client::connection::communication { /// Raised after StreamStart completes successfully /// and the stream becomes active with a valid stream ID. case QUIC_STREAM_EVENT_START_COMPLETE: { - settings::Logger::logDebug("[quic] Stream start completed"); + settings::Logger::logDebug("[quic] [stream {}] Stream start completed", streamId ? *streamId : 0); break; } @@ -395,18 +408,16 @@ namespace bringauto::external_client::connection::communication { * passed to StreamSend (via ClientContext) */ if (event->SEND_COMPLETE.Canceled) { - settings::Logger::logWarning("[quic] Stream send canceled"); + settings::Logger::logDebug("[quic] [stream {}] Stream send canceled", + streamId ? *streamId : 0); } else { - settings::Logger::logDebug("[quic] Stream send completed"); + settings::Logger::logDebug("[quic] [stream {}] Stream send completed", + streamId ? *streamId : 0); } - const auto *buf = - static_cast(event->SEND_COMPLETE.ClientContext); - - if (buf) { - delete[] buf->Buffer; - delete buf; - } + std::unique_ptr sendBuf{ + static_cast(event->SEND_COMPLETE.ClientContext) + }; break; } @@ -414,13 +425,14 @@ namespace bringauto::external_client::connection::communication { /// Raised when both send and receive directions are closed /// and the stream lifecycle is fully complete. case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: { - settings::Logger::logDebug("[quic] Stream shutdown complete"); + settings::Logger::logDebug("[quic] [stream {}] Stream shutdown complete", streamId ? *streamId : 0); self->quic_->StreamClose(stream); break; } default: { - settings::Logger::logDebug("[quic] Unhandled stream event 0x{:x}", static_cast(event->Type)); + settings::Logger::logDebug("[quic] [stream {}] Unhandled stream event 0x{:x}", streamId ? *streamId : 0, + static_cast(event->Type)); break; } } From 7f982ba30aaa594b632fb18c910a95868168ca6a Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Wed, 7 Jan 2026 11:33:36 +0000 Subject: [PATCH 45/55] Refactor `QuicCommunication`: Replace exceptions with `settings::Logger::logError` for error handling and ensure graceful returns in failure scenarios. --- .../communication/QuicCommunication.cpp | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 9d213916..d1aad7b3 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -46,12 +46,14 @@ namespace bringauto::external_client::connection::communication { ConnectionState expected = ConnectionState::NOT_CONNECTED; if (!connectionState_. compare_exchange_strong(expected, ConnectionState::CONNECTING, std::memory_order_acq_rel)) { - throw std::logic_error("Connection already in progress or established"); + settings::Logger::logError("Connection already in progress or established"); + return; } QUIC_STATUS status = quic_->ConnectionOpen(registration_, connectionCallback, this, &connection_); if (QUIC_FAILED(status)) { - throw std::runtime_error(std::format("ConnectionOpen failed (status=0x{:x})", status)); + settings::Logger::logError("ConnectionOpen failed (status=0x{:x})", status); + return; } status = quic_->ConnectionStart( @@ -63,7 +65,8 @@ namespace bringauto::external_client::connection::communication { ); if (QUIC_FAILED(status)) { - throw std::runtime_error(std::format("ConnectionOpen failed (status=0x{:x})", status)); + settings::Logger::logError("ConnectionOpen failed (status=0x{:x})", status); + return; } connectionState_ = ConnectionState::CONNECTING; @@ -108,7 +111,8 @@ namespace bringauto::external_client::connection::communication { void QuicCommunication::loadMsQuic() { QUIC_STATUS status = MsQuicOpen2(&quic_); if (QUIC_FAILED(status)) { - throw std::runtime_error(std::format("[quic] Failed to open QUIC; QUIC_STATUS => {:x}", status)); + settings::Logger::logError("[quic] Failed to open QUIC; QUIC_STATUS => {:x}", status); + return; } } @@ -119,8 +123,8 @@ namespace bringauto::external_client::connection::communication { QUIC_STATUS status = quic_->RegistrationOpen(&config, ®istration_); if (QUIC_FAILED(status)) { - throw std::runtime_error( - std::format("[quic] Failed to open QUIC registration; QUIC_STATUS => {:x}", status)); + settings::Logger::logError("[quic] Failed to open QUIC registration; QUIC_STATUS => {:x}", status); + return; } } @@ -154,15 +158,16 @@ namespace bringauto::external_client::connection::communication { ); if (QUIC_FAILED(status)) { - throw std::runtime_error(std::format("[quic] Failed to open QUIC configuration; QUIC_STATUS => {:x}", - status)); + settings::Logger::logError("[quic] Failed to open QUIC configuration; QUIC_STATUS => {:x}", status); + return; } } void QuicCommunication::configurationLoadCredential(const QUIC_CREDENTIAL_CONFIG *credential) const { const QUIC_STATUS status = quic_->ConfigurationLoadCredential(config_, credential); if (QUIC_FAILED(status)) { - throw std::runtime_error(std::format("[quic] Failed to load QUIC credential; QUIC_STATUS => {:x}", status)); + settings::Logger::logError("[quic] Failed to load QUIC credential; QUIC_STATUS => {:x}", status); + return; } } @@ -231,14 +236,16 @@ namespace bringauto::external_client::connection::communication { void QuicCommunication::sendViaQuicStream(const std::shared_ptr &message) { HQUIC stream{nullptr}; if (QUIC_FAILED(quic_->StreamOpen(connection_, QUIC_STREAM_OPEN_FLAG_NONE, streamCallback, this, &stream))) { - throw std::runtime_error("[quic] StreamOpen failed"); + settings::Logger::logError("[quic] StreamOpen failed"); + return; } const size_t size = message->ByteSizeLong(); auto sendBuffer = std::make_unique(size); if (!message->SerializeToArray(sendBuffer->storage.get(), static_cast(size))) { - throw std::runtime_error("[quic] Message serialization failed"); + settings::Logger::logError("[quic] Message serialization failed"); + return; } settings::Logger::logDebug( @@ -269,7 +276,8 @@ namespace bringauto::external_client::connection::communication { std::unique_ptr reclaim{quicBufContext}; quic_->StreamShutdown(stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0); - throw std::runtime_error(std::format("[quic] Failed to send QUIC stream; QUIC_STATUS => {:x}", status)); + settings::Logger::logError("[quic] Failed to send QUIC stream; QUIC_STATUS => {:x}", status); + return; } auto streamId = getStreamId(stream); @@ -463,11 +471,7 @@ namespace bringauto::external_client::connection::communication { outboundQueue_.pop(); lock.unlock(); - try { - sendViaQuicStream(msg); - } catch (const std::exception &e) { - settings::Logger::logError(std::format("[quic] Message send failed; {}", e.what())); - } + sendViaQuicStream(msg); } } From 2b4706bb14947b306a21a8f3ddae90a6317cb991 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Wed, 7 Jan 2026 11:40:39 +0000 Subject: [PATCH 46/55] Refactor `QuicCommunication`: Simplify `SendBuffer` by replacing `std::unique_ptr` with `std::string` for memory management. --- .../connection/communication/QuicCommunication.hpp | 8 ++++++-- .../connection/communication/QuicCommunication.cpp | 7 +------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index 8d7aca48..812ac4ef 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -125,9 +125,13 @@ namespace bringauto::external_client::connection::communication { struct SendBuffer { QUIC_BUFFER buffer{}; - std::unique_ptr storage; + std::string storage; - explicit SendBuffer(size_t size); + explicit SendBuffer(size_t size) + : storage(size, '\0') { + buffer.Length = static_cast(storage.size()); + buffer.Buffer = reinterpret_cast(storage.data()); + } }; /** diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index d1aad7b3..0933c594 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -228,11 +228,6 @@ namespace bringauto::external_client::connection::communication { inboundCv_.notify_one(); } - QuicCommunication::SendBuffer::SendBuffer(size_t size) : storage(std::make_unique(size)) { - buffer.Length = static_cast(size); - buffer.Buffer = storage.get(); - } - void QuicCommunication::sendViaQuicStream(const std::shared_ptr &message) { HQUIC stream{nullptr}; if (QUIC_FAILED(quic_->StreamOpen(connection_, QUIC_STREAM_OPEN_FLAG_NONE, streamCallback, this, &stream))) { @@ -243,7 +238,7 @@ namespace bringauto::external_client::connection::communication { const size_t size = message->ByteSizeLong(); auto sendBuffer = std::make_unique(size); - if (!message->SerializeToArray(sendBuffer->storage.get(), static_cast(size))) { + if (!message->SerializeToArray(sendBuffer->storage.data(), static_cast(size))) { settings::Logger::logError("[quic] Message serialization failed"); return; } From 1b6e8b700216db0fb02d6521b5ffc53a56ddc7f1 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Wed, 7 Jan 2026 11:42:31 +0000 Subject: [PATCH 47/55] Refactor `QuicCommunication`: Reorder initialization steps to ensure proper setup before logging. --- .../connection/communication/QuicCommunication.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 0933c594..77679e3f 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -23,16 +23,12 @@ namespace bringauto::external_client::connection::communication { alpnBuffer_.Buffer = reinterpret_cast(alpn_.data()); alpnBuffer_.Length = static_cast(alpn_.size()); + loadMsQuic(); + initRegistration("module-gateway-quic-client"); + initConfiguration(); + settings::Logger::logInfo("[quic] Initialize QUIC communication to {}:{} for {}/{}", settings.serverIp, settings.port, company, vehicleName); - - try { - loadMsQuic(); - initRegistration("module-gateway-quic-client"); - initConfiguration(); - } catch (const std::exception &e) { - settings::Logger::logError("[quic] Failed to initialize QUIC communication; {}", e.what()); - } } QuicCommunication::~QuicCommunication() { From 8fa97ba9e8687248cf851347b4110b7182a408e8 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Wed, 7 Jan 2026 12:05:39 +0000 Subject: [PATCH 48/55] Refactor `QuicCommunication`: Replace `std::lock_guard` with `std::scoped_lock` and simplify enum string conversion using `using enum`. --- include/bringauto/common_utils/EnumUtils.hpp | 24 ++++++++++--------- .../communication/QuicCommunication.cpp | 8 +++---- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/include/bringauto/common_utils/EnumUtils.hpp b/include/bringauto/common_utils/EnumUtils.hpp index 991ba500..b3b2773f 100644 --- a/include/bringauto/common_utils/EnumUtils.hpp +++ b/include/bringauto/common_utils/EnumUtils.hpp @@ -77,20 +77,22 @@ class EnumUtils { /** * @brief Converts connection state to string * - * @param toString structures::ProtocolType + * @param state external_client::connection::ConnectionState * @return std::string_view */ - static constexpr std::string_view connectionStateToString(external_client::connection::ConnectionState toString) { - switch(toString) { - case external_client::connection::ConnectionState::NOT_INITIALIZED: return "Not Initialized"; - case external_client::connection::ConnectionState::NOT_CONNECTED: return "Not Connected"; - case external_client::connection::ConnectionState::CONNECTING: return "Connecting"; - case external_client::connection::ConnectionState::CONNECTED: return "Connected"; - case external_client::connection::ConnectionState::CLOSING: return "Closing"; + static constexpr std::string_view connectionStateToString( + external_client::connection::ConnectionState state + ) { + using enum external_client::connection::ConnectionState; + + switch (state) { + case NOT_INITIALIZED: return "Not Initialized"; + case NOT_CONNECTED: return "Not Connected"; + case CONNECTING: return "Connecting"; + case CONNECTED: return "Connected"; + case CLOSING: return "Closing"; default: return "Unknown"; } + } }; - -}; - } diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 77679e3f..f41d8a79 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -76,7 +76,7 @@ namespace bringauto::external_client::connection::communication { { auto copy = std::make_shared(*message); - std::lock_guard lock(outboundMutex_); + std::scoped_lock lock(outboundMutex_); outboundQueue_.push(std::move(copy)); } settings::Logger::logDebug("[quic] Notifying sender thread about enqueued message"); @@ -85,7 +85,7 @@ namespace bringauto::external_client::connection::communication { } std::shared_ptr QuicCommunication::receiveMessage() { - std::unique_lock lock(inboundMutex_); + std::unique_lock lock(inboundMutex_); if (!inboundCv_.wait_for( lock, @@ -217,7 +217,7 @@ namespace bringauto::external_client::connection::communication { std::shared_ptr msg ) { { - std::lock_guard lock(inboundMutex_); + std::scoped_lock lock(inboundMutex_); inboundQueue_.push(std::move(msg)); } settings::Logger::logDebug("[quic] Notifying receiver thread about dequeued message"); @@ -445,7 +445,7 @@ namespace bringauto::external_client::connection::communication { while (connectionState_.load() == ConnectionState::CONNECTED) { std::shared_ptr msg; - std::unique_lock lock(outboundMutex_); + std::unique_lock lock(outboundMutex_); settings::Logger::logDebug("[quic] Sender thread loop waiting for outbound queue"); outboundCv_.wait(lock, [this] { From 4794e8556e8db4ad5b3e4151c8b28e54ffe9527e Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Thu, 15 Jan 2026 09:26:47 +0000 Subject: [PATCH 49/55] Refactor `QuicCommunication`: Add early exit for empty stream receive event. --- .../communication/QuicCommunication.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index f41d8a79..de57364c 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -239,14 +239,6 @@ namespace bringauto::external_client::connection::communication { return; } - settings::Logger::logDebug( - "[quic][debug] SendBuffer ptr={}, QUIC_BUFFER ptr={}, data ptr={}, size={}", - static_cast(sendBuffer.get()), - static_cast(&sendBuffer->buffer), - static_cast(sendBuffer->buffer.Buffer), - sendBuffer->buffer.Length - ); - const SendBuffer *raw = sendBuffer.get(); const QUIC_BUFFER *quicBuf = &raw->buffer; SendBuffer *quicBufContext = sendBuffer.release(); @@ -336,6 +328,14 @@ namespace bringauto::external_client::connection::communication { switch (event->Type) { /// Raised when the peer sends stream data and MsQuic delivers received bytes to the application. case QUIC_STREAM_EVENT_RECEIVE: { + if (event->RECEIVE.BufferCount == 0) { + settings::Logger::logDebug( + "[quic] [stream {}] End of stream received", + streamId ? *streamId : 0 + ); + break; + } + settings::Logger::logDebug( "[quic] [stream {}] Received {:d} bytes in {:d} buffers", streamId ? *streamId : 0, From 311876ed99cc00df593d2491abb866ecea610fee Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Mon, 19 Jan 2026 06:03:26 +0000 Subject: [PATCH 50/55] Refactor `QuicCommunication`: Replace dynamic app name parameter in `initRegistration` with a constant for simplification and improved consistency. --- .../connection/communication/QuicCommunication.hpp | 4 +--- .../connection/communication/QuicCommunication.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index 812ac4ef..9824bfda 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -153,10 +153,8 @@ namespace bringauto::external_client::connection::communication { * creating QUIC configurations and connections. * * If registration creation fails, an error is logged. - * - * @param appName Application name used to identify the QUIC registration. */ - void initRegistration(std::string appName); + void initRegistration(); /** * @brief Initializes the QUIC configuration and loads client credentials. diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index de57364c..4858df82 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -24,7 +24,7 @@ namespace bringauto::external_client::connection::communication { alpnBuffer_.Length = static_cast(alpn_.size()); loadMsQuic(); - initRegistration("module-gateway-quic-client"); + initRegistration(); initConfiguration(); settings::Logger::logInfo("[quic] Initialize QUIC communication to {}:{} for {}/{}", settings.serverIp, @@ -112,9 +112,11 @@ namespace bringauto::external_client::connection::communication { } } - void QuicCommunication::initRegistration(std::string appName) { + constexpr auto quicRegistrationAppName = "module-gateway-quic-client"; + + void QuicCommunication::initRegistration() { QUIC_REGISTRATION_CONFIG config{}; - config.AppName = appName.c_str(); + config.AppName = quicRegistrationAppName; config.ExecutionProfile = QUIC_EXECUTION_PROFILE_LOW_LATENCY; QUIC_STATUS status = quic_->RegistrationOpen(&config, ®istration_); From 5ea491bdefc98f593b993a28b76c67e3f8a2e2f1 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Mon, 19 Jan 2026 06:27:49 +0000 Subject: [PATCH 51/55] Refactor `QuicCommunication`: Replace `std::shared_ptr` with `std::unique_ptr` in outbound queue, update `sendViaQuicStream` to use references for improved memory management and clarity. --- .../communication/QuicCommunication.hpp | 4 ++-- .../communication/QuicCommunication.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index 9824bfda..a7ef704f 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -114,7 +114,7 @@ namespace bringauto::external_client::connection::communication { /// @name Outbound (this → peer) /// @{ /// Queue of outgoing messages to be sent to the peer - std::queue > outboundQueue_; + std::queue > outboundQueue_; /// Mutex protecting access to the outbound message queue std::mutex outboundMutex_; /// Condition variable for signaling outbound message availability @@ -217,7 +217,7 @@ namespace bringauto::external_client::connection::communication { * * @param message Message to be sent to the peer. */ - void sendViaQuicStream(const std::shared_ptr &message); + void sendViaQuicStream(const ExternalProtocol::ExternalClient& message); /** * @brief Closes the active QUIC configuration. diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index 4858df82..caec057b 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -75,8 +75,8 @@ namespace bringauto::external_client::connection::communication { } { - auto copy = std::make_shared(*message); - std::scoped_lock lock(outboundMutex_); + auto copy = std::make_unique(*message); + std::lock_guard lock(outboundMutex_); outboundQueue_.push(std::move(copy)); } settings::Logger::logDebug("[quic] Notifying sender thread about enqueued message"); @@ -226,17 +226,17 @@ namespace bringauto::external_client::connection::communication { inboundCv_.notify_one(); } - void QuicCommunication::sendViaQuicStream(const std::shared_ptr &message) { + void QuicCommunication::sendViaQuicStream(const ExternalProtocol::ExternalClient& message) { HQUIC stream{nullptr}; if (QUIC_FAILED(quic_->StreamOpen(connection_, QUIC_STREAM_OPEN_FLAG_NONE, streamCallback, this, &stream))) { settings::Logger::logError("[quic] StreamOpen failed"); return; } - const size_t size = message->ByteSizeLong(); + const size_t size = message.ByteSizeLong(); auto sendBuffer = std::make_unique(size); - if (!message->SerializeToArray(sendBuffer->storage.data(), static_cast(size))) { + if (!message.SerializeToArray(sendBuffer->storage.data(), static_cast(size))) { settings::Logger::logError("[quic] Message serialization failed"); return; } @@ -445,7 +445,7 @@ namespace bringauto::external_client::connection::communication { settings::Logger::logDebug("[quic] Sender thread loop started"); while (connectionState_.load() == ConnectionState::CONNECTED) { - std::shared_ptr msg; + std::unique_ptr msg; std::unique_lock lock(outboundMutex_); @@ -460,11 +460,11 @@ namespace bringauto::external_client::connection::communication { } settings::Logger::logDebug("[quic] Sender thread loop sending outbound queue"); - msg = outboundQueue_.front(); + msg = std::move(outboundQueue_.front()); outboundQueue_.pop(); lock.unlock(); - sendViaQuicStream(msg); + sendViaQuicStream(*msg); } } From fa075eae8779f2ffa5ec1831a2143c5f3a773783 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Mon, 19 Jan 2026 06:36:24 +0000 Subject: [PATCH 52/55] Update `README.md`: Add QUIC configuration details alongside MQTT, expand protocol support, and provide examples for both. --- resources/config/README.md | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/resources/config/README.md b/resources/config/README.md index 2889f140..a67581a1 100644 --- a/resources/config/README.md +++ b/resources/config/README.md @@ -28,18 +28,27 @@ Note: at least one logging sink needs to be used * company : company name used as identification in external connection (string) * vehicle-name : vehicle name used as identification in external connection (string) * endpoints : array of objects listing possible ways to connect to external server - - protocol-type : string (only mqtt is supported) + - protocol-type : string (only mqtt and quic are supported) - server-ip : ip of the external connection (string) - port : port of the external connection (int) - modules : array of integers that represent module numbers to be used on this connection - - mqtt-settings : **only for mqtt** - - ssl : if connection requires ssl, bool - - ca-file : public trusted certificate file name (string) - - client-cert : public certificate chain file name (string) - - client-key : private key file name (string) -## Example +#### mqtt-settings (only for MQTT) +* ssl : if connection requires ssl, bool +* ca-file : public trusted certificate file name (string) +* client-cert : public certificate chain file name (string) +* client-key : private key file name (string) -[Example](./example.json) +#### quic-settings (only for QUIC) +* ca-file : path to the trusted CA certificate file (string) +* client-cert : path to the client certificate file (string) +* client-key : path to the client private key file (string) +* alpn : Application-Layer Protocol Negotiation identifier (string), must match the ALPN configured on the server + +Note: QUIC uses TLS 1.3 internally. All certificate files must be provided in a format supported by MsQuic/OpenSSL. +## Examples + +[MQTT Example](./example.json) +[QUIC Example](./quic_example.json) From 8a3391ffd58d3bbd7e22dfd174372fc40ba2d81c Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Mon, 19 Jan 2026 06:44:41 +0000 Subject: [PATCH 53/55] Refactor `QuicCommunication`: Add comments and documentation for `SendBuffer` to clarify purpose, usage, and construction. --- .../communication/QuicCommunication.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index a7ef704f..1cc10fe3 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -123,10 +123,28 @@ namespace bringauto::external_client::connection::communication { std::jthread senderThread_; /// @} + /** + * @brief Owns memory for a single MsQuic StreamSend operation. + * + * SendBuffer wraps a QUIC_BUFFER together with its backing storage. + * The memory must remain valid until MsQuic signals + * QUIC_STREAM_EVENT_SEND_COMPLETE, at which point it can be safely freed. + * + * Instances of this struct are typically allocated on the heap and passed + * to MsQuic via the StreamSend ClientContext pointer. + */ struct SendBuffer { QUIC_BUFFER buffer{}; std::string storage; + /** + * @brief Constructs a SendBuffer with zero-initialized storage. + * + * Allocates storage of the given size, fills it with zero bytes, + * and initializes the QUIC_BUFFER to point to this storage. + * + * @param size Number of bytes to allocate for the send buffer. + */ explicit SendBuffer(size_t size) : storage(size, '\0') { buffer.Length = static_cast(storage.size()); From ec8bf052235741ae3779f69b5c94c3855413380f Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Mon, 19 Jan 2026 07:07:08 +0000 Subject: [PATCH 54/55] Refactor `EnumUtils`: Replace hardcoded connection state strings with `settings::Constants` for improved maintainability. --- include/bringauto/common_utils/EnumUtils.hpp | 18 ++++++++++++------ include/bringauto/settings/Constants.hpp | 7 +++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/bringauto/common_utils/EnumUtils.hpp b/include/bringauto/common_utils/EnumUtils.hpp index b3b2773f..56994d61 100644 --- a/include/bringauto/common_utils/EnumUtils.hpp +++ b/include/bringauto/common_utils/EnumUtils.hpp @@ -86,12 +86,18 @@ class EnumUtils { using enum external_client::connection::ConnectionState; switch (state) { - case NOT_INITIALIZED: return "Not Initialized"; - case NOT_CONNECTED: return "Not Connected"; - case CONNECTING: return "Connecting"; - case CONNECTED: return "Connected"; - case CLOSING: return "Closing"; - default: return "Unknown"; + case NOT_INITIALIZED: + return settings::Constants::LOG_CONNECTION_STATE_NOT_INITIALIZED; + case NOT_CONNECTED: + return settings::Constants::LOG_CONNECTION_STATE_NOT_CONNECTED; + case CONNECTING: + return settings::Constants::LOG_CONNECTION_STATE_CONNECTING; + case CONNECTED: + return settings::Constants::LOG_CONNECTION_STATE_CONNECTED; + case CLOSING: + return settings::Constants::LOG_CONNECTION_STATE_CLOSING; + default: + return settings::Constants::LOG_UNKNOWN; } } }; diff --git a/include/bringauto/settings/Constants.hpp b/include/bringauto/settings/Constants.hpp index fdd358ea..e9628b56 100644 --- a/include/bringauto/settings/Constants.hpp +++ b/include/bringauto/settings/Constants.hpp @@ -140,6 +140,7 @@ class Constants { inline static constexpr std::string_view LOG_LEVEL { "level" }; inline static constexpr std::string_view LOG_USE { "use" }; inline static constexpr std::string_view LOG_PATH { "path" }; + inline static constexpr std::string_view LOG_UNKNOWN { "unknown" }; inline static constexpr std::string_view LOG_LEVEL_DEBUG { "DEBUG" }; inline static constexpr std::string_view LOG_LEVEL_INFO { "INFO" }; @@ -148,6 +149,12 @@ class Constants { inline static constexpr std::string_view LOG_LEVEL_CRITICAL { "CRITICAL" }; inline static constexpr std::string_view LOG_LEVEL_INVALID { "INVALID" }; + inline static constexpr std::string_view LOG_CONNECTION_STATE_NOT_INITIALIZED { "not initialized" }; + inline static constexpr std::string_view LOG_CONNECTION_STATE_NOT_CONNECTED { "not connected" }; + inline static constexpr std::string_view LOG_CONNECTION_STATE_CONNECTING { "connecting" }; + inline static constexpr std::string_view LOG_CONNECTION_STATE_CONNECTED { "connected" }; + inline static constexpr std::string_view LOG_CONNECTION_STATE_CLOSING { "closing" }; + inline static constexpr std::string_view HELP { "help" }; inline static constexpr std::string_view PORT { "port" }; From bf7ee48fdd911305bc5ceadc1e7020e3c2867d73 Mon Sep 17 00:00:00 2001 From: Daniel Prudky Date: Mon, 19 Jan 2026 08:25:22 +0000 Subject: [PATCH 55/55] Add `QuicSettingsParser` to parse QUIC settings and integrate with `QuicCommunication`. --- .../communication/QuicCommunication.hpp | 4 +- .../bringauto/settings/QuicSettingsParser.hpp | 21 ++++++ resources/config/quic_example.json | 3 +- .../communication/QuicCommunication.cpp | 10 +-- .../bringauto/settings/QuicSettingsParser.cpp | 64 +++++++++++++++++++ 5 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 include/bringauto/settings/QuicSettingsParser.hpp create mode 100644 source/bringauto/settings/QuicSettingsParser.cpp diff --git a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp index 1cc10fe3..aa286835 100644 --- a/include/bringauto/external_client/connection/communication/QuicCommunication.hpp +++ b/include/bringauto/external_client/connection/communication/QuicCommunication.hpp @@ -193,10 +193,8 @@ namespace bringauto::external_client::connection::communication { * * If settings are not provided, default QUIC settings are used. * On failure, an error is logged. - * - * @param settings Optional QUIC transport settings. */ - void configurationOpen(const QUIC_SETTINGS *settings); + void configurationOpen(); /** * @brief Loads TLS credentials into the QUIC configuration. diff --git a/include/bringauto/settings/QuicSettingsParser.hpp b/include/bringauto/settings/QuicSettingsParser.hpp new file mode 100644 index 00000000..b1df8005 --- /dev/null +++ b/include/bringauto/settings/QuicSettingsParser.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include + +#include + + +namespace bringauto::settings { + class QuicSettingsParser { + public: + static QUIC_SETTINGS parse(const structures::ExternalConnectionSettings &settings); + + private: + static std::optional getOptionalUint( + const structures::ExternalConnectionSettings &settings, + std::string_view key + ); + }; +} diff --git a/resources/config/quic_example.json b/resources/config/quic_example.json index 0edbbc18..640b3648 100644 --- a/resources/config/quic_example.json +++ b/resources/config/quic_example.json @@ -26,7 +26,8 @@ "ca-file" : "build/certs/ca.pem", "client-cert" : "build/certs/client.pem", "client-key" : "build/certs/client-key.pem", - "alpn" : "sample" + "alpn" : "sample", + "DisconnectTimeoutMs" : 800 }, "modules": [1,2,3] } diff --git a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp index caec057b..f356681a 100644 --- a/source/bringauto/external_client/connection/communication/QuicCommunication.cpp +++ b/source/bringauto/external_client/connection/communication/QuicCommunication.cpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace bringauto::external_client::connection::communication { @@ -127,7 +128,7 @@ namespace bringauto::external_client::connection::communication { } void QuicCommunication::initConfiguration() { - configurationOpen(nullptr); + configurationOpen(); QUIC_CERTIFICATE_FILE certificate{}; certificate.CertificateFile = certFile_.c_str(); @@ -142,14 +143,15 @@ namespace bringauto::external_client::connection::communication { configurationLoadCredential(&credential); } - void QuicCommunication::configurationOpen(const QUIC_SETTINGS *settings) { - const uint32_t settingsSize = settings ? sizeof(*settings) : 0; + void QuicCommunication::configurationOpen() { + const QUIC_SETTINGS settings = settings::QuicSettingsParser::parse(settings_); + const uint32_t settingsSize = sizeof(QUIC_SETTINGS); QUIC_STATUS status = quic_->ConfigurationOpen( registration_, &alpnBuffer_, 1, - settings, + &settings, settingsSize, nullptr, &config_ diff --git a/source/bringauto/settings/QuicSettingsParser.cpp b/source/bringauto/settings/QuicSettingsParser.cpp new file mode 100644 index 00000000..4c3dab74 --- /dev/null +++ b/source/bringauto/settings/QuicSettingsParser.cpp @@ -0,0 +1,64 @@ +#include +#include +#include + +#include + + +namespace bringauto::settings { + QUIC_SETTINGS QuicSettingsParser::parse( + const structures::ExternalConnectionSettings &settings + ) { + QUIC_SETTINGS quic{}; + + if (auto value = getOptionalUint(settings, "IdleTimeoutMs")) { + settings::Logger::logDebug("[quic] [settings] IdleTimeoutMs settings loaded"); + quic.IdleTimeoutMs = *value; + quic.IsSet.IdleTimeoutMs = TRUE; + } + + if (auto value = getOptionalUint(settings, "HandshakeIdleTimeoutMs")) { + settings::Logger::logDebug("[quic] [settings] HandshakeIdleTimeoutMs settings loaded"); + quic.HandshakeIdleTimeoutMs = *value; + quic.IsSet.HandshakeIdleTimeoutMs = TRUE; + } + + if (auto value = getOptionalUint(settings, "DisconnectTimeoutMs")) { + settings::Logger::logDebug("[quic] [settings] DisconnectTimeoutMs settings loaded"); + quic.DisconnectTimeoutMs = *value; + quic.IsSet.DisconnectTimeoutMs = TRUE; + } + + return quic; + } + + std::optional QuicSettingsParser::getOptionalUint( + const structures::ExternalConnectionSettings &settings, + std::string_view key + ) { + const auto it = settings.protocolSettings.find(std::string(key)); + if (it == settings.protocolSettings.end()) { + return std::nullopt; + } + + const auto &raw = it->second; + + if (!nlohmann::json::accept(raw)) { + settings::Logger::logWarning( + "[quic] QUIC setting '{}' is not valid JSON", key + ); + return std::nullopt; + } + + auto j = nlohmann::json::parse(raw); + + if (!j.is_number_unsigned()) { + settings::Logger::logWarning( + "[quic] QUIC setting '{}' must be an unsigned integer", key + ); + return std::nullopt; + } + + return j.get(); + } +}