diff --git a/CMLibStorage.cmake b/CMLibStorage.cmake index 7e7142d..36796ba 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 b4285f2..9a84157 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() @@ -47,13 +47,16 @@ 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(nlohmann_json 3.2.0 REQUIRED) +FIND_PACKAGE(cxxopts 3.1.1 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) FIND_PACKAGE(fleet-protocol-interface 2.0.0 REQUIRED) 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) FILE(GLOB_RECURSE source_files "source/*") ADD_LIBRARY(module-gateway-lib STATIC "${source_files}") @@ -71,6 +74,8 @@ 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 + async-function-execution-shared::async-function-execution-shared ${CMAKE_DL_LIBS} ) diff --git a/README.md b/README.md index c56b1e2..7290425 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,17 @@ 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 -- [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 +- [cxxopts](https://github.com/jarro2783/cxxopts) >= v3.1.1 +- [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 e004024..ea369ef 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -3,13 +3,15 @@ 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(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.2.0) +BA_PACKAGE_LIBRARY(aeron v1.48.6) +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 dfa47cd..e72fd82 100644 --- a/include/bringauto/external_client/ErrorAggregator.hpp +++ b/include/bringauto/external_client/ErrorAggregator.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -25,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); + int init_error_aggregator(const std::shared_ptr &library); /** * @short Clean up. @@ -111,7 +111,7 @@ class ErrorAggregator { modules::Buffer lastStatus {}; }; - std::shared_ptr module_ {}; + 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 a6c5e8e..235bf26 100644 --- a/include/bringauto/modules/Buffer.hpp +++ b/include/bringauto/modules/Buffer.hpp @@ -18,7 +18,8 @@ namespace bringauto::modules { */ struct Buffer final { - friend class ModuleManagerLibraryHandler; + friend class ModuleManagerLibraryHandlerLocal; + friend class ModuleManagerLibraryHandlerAsync; Buffer() = default; Buffer(const Buffer& buff) = default; diff --git a/include/bringauto/modules/IModuleManagerLibraryHandler.hpp b/include/bringauto/modules/IModuleManagerLibraryHandler.hpp new file mode 100644 index 0000000..c125406 --- /dev/null +++ b/include/bringauto/modules/IModuleManagerLibraryHandler.hpp @@ -0,0 +1,78 @@ +#pragma once + +#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() = 0; + + virtual int isDeviceTypeSupported(unsigned int device_type) = 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. + * + * @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) = 0; + + virtual int commandDataValid(const Buffer &command, unsigned int device_type) = 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 new file mode 100644 index 0000000..558da78 --- /dev/null +++ b/include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp @@ -0,0 +1,123 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include + + + +namespace bringauto::modules { + +/** + * @brief Class used to load and handle library created by module maintainer + */ +class ModuleManagerLibraryHandlerAsync : public IModuleManagerLibraryHandler { +public: + explicit ModuleManagerLibraryHandlerAsync(const std::filesystem::path &moduleBinaryPath, const int moduleNumber); + + ~ModuleManagerLibraryHandlerAsync() override; + + /** + * @brief Load library created by a module maintainer + * + * @param path path to the library + */ + void loadLibrary(const std::filesystem::path &path) 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) 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 generateCommand(Buffer &generated_command, const Buffer &new_status, + const Buffer ¤t_status, const Buffer ¤t_command, + 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 aggregateStatus(Buffer &aggregated_status, 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. + * + * @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) 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) override; + + int statusDataValid(const Buffer &status, unsigned int device_type) override; + + int commandDataValid(const Buffer &command, unsigned int device_type) override; + + /** + * @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) override; + +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_ {}; + + /// Path to the module binary + std::filesystem::path moduleBinaryPath_ {}; + /// Process of the module binary + boost::process::child moduleBinaryProcess_ {}; + + /// TODO find a way to not need this + 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_ {}; + + 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/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 fbae895..bd897d5 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() 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) 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) override; - int commandDataValid(const Buffer &command, unsigned int device_type) const; + int commandDataValid(const Buffer &command, unsigned int device_type) 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: @@ -111,4 +106,4 @@ class ModuleManagerLibraryHandler { std::function deallocate_ {}; }; -} +} \ No newline at end of file diff --git a/include/bringauto/modules/StatusAggregator.hpp b/include/bringauto/modules/StatusAggregator.hpp index 03c84e9..33580bf 100644 --- a/include/bringauto/modules/StatusAggregator.hpp +++ b/include/bringauto/modules/StatusAggregator.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -20,9 +20,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 }, + module_ { + libraryHandler } {}; StatusAggregator() = default; @@ -186,7 +186,7 @@ class StatusAggregator { std::shared_ptr context_ {}; - const std::shared_ptr module_ {}; + 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 306eb9c..3aa6238 100644 --- a/include/bringauto/settings/Constants.hpp +++ b/include/bringauto/settings/Constants.hpp @@ -88,35 +88,45 @@ constexpr unsigned int max_external_queue_size { 500 }; /** * @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 */ @@ -142,6 +152,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" }; @@ -161,6 +172,8 @@ 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"}; + inline static constexpr std::string_view SEPARATOR { ":::" }; }; } diff --git a/include/bringauto/settings/Settings.hpp b/include/bringauto/settings/Settings.hpp index aad2db8..b105dda 100644 --- a/include/bringauto/settings/Settings.hpp +++ b/include/bringauto/settings/Settings.hpp @@ -31,7 +31,12 @@ struct Settings { /** * @brief paths to shared module libraries */ - std::unordered_map modulePaths {}; + std::unordered_map modulePaths {}; + + /** + * @brief path to module binary + */ + 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 5f37de3..d6b3a0d 100644 --- a/include/bringauto/structures/ModuleLibrary.hpp +++ b/include/bringauto/structures/ModuleLibrary.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -13,6 +13,7 @@ namespace bringauto::structures { * @brief Library with library handlers and status aggregators */ struct ModuleLibrary { + ModuleLibrary() = default; ~ModuleLibrary(); @@ -21,7 +22,15 @@ struct ModuleLibrary { * * @param libPaths paths to the libraries */ - void loadLibraries(const std::unordered_map &libPaths); + 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::filesystem::path &moduleBinaryPath); /** * @brief Initialize status aggregators with context @@ -30,7 +39,7 @@ struct ModuleLibrary { */ void initStatusAggregators(std::shared_ptr &context); /// Map of module handlers, key is module id - std::unordered_map> moduleLibraryHandlers {}; + 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 00a3883..0753644 100644 --- a/main.cpp +++ b/main.cpp @@ -67,9 +67,15 @@ int main(int argc, char **argv) { std::cerr << "[ERROR] Error occurred during reading configuration: " << e.what() << std::endl; return 1; } + bas::ModuleLibrary moduleLibrary {}; + try { - moduleLibrary.loadLibraries(context->settings->modulePaths); + 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/resources/config/README.md b/resources/config/README.md index 8451e27..2889f14 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 3fc59eb..0402e35 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 3bb8d46..2a5bc02 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 c28bea5..68cbcb4 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 b0f650f..4f83b6c 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) { module_ = library; return OK; } diff --git a/source/bringauto/external_client/connection/ExternalConnection.cpp b/source/bringauto/external_client/connection/ExternalConnection.cpp index e7f58c2..4051818 100644 --- a/source/bringauto/external_client/connection/ExternalConnection.cpp +++ b/source/bringauto/external_client/connection/ExternalConnection.cpp @@ -416,7 +416,16 @@ 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", + moduleNumber); + 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/modules/ModuleHandler.cpp b/source/bringauto/modules/ModuleHandler.cpp index 5f1bd81..5059cdb 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 new file mode 100644 index 0000000..ffea062 --- /dev/null +++ b/source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp @@ -0,0 +1,235 @@ +#include + +#include + +#include + + + +namespace bringauto::modules { + +ModuleManagerLibraryHandlerAsync::ModuleManagerLibraryHandlerAsync(const std::filesystem::path &moduleBinaryPath, const int moduleNumber) : + moduleBinaryPath_ { moduleBinaryPath } { + aeronClient.connect(moduleNumber); + deallocate_ = [this](struct buffer *buffer) { + this->deallocate(buffer); + }; +} + +ModuleManagerLibraryHandlerAsync::~ModuleManagerLibraryHandlerAsync() { + if (moduleBinaryProcess_.valid()) { + ::kill(moduleBinaryProcess_.id(), SIGTERM); + moduleBinaryProcess_.wait(); + } +} + +void ModuleManagerLibraryHandlerAsync::loadLibrary(const std::filesystem::path &path) { + moduleBinaryProcess_ = boost::process::child { moduleBinaryPath_.string(), "-m", path.string() }; + if (!moduleBinaryProcess_.valid()) { + 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() { + std::lock_guard lock { getModuleNumberMutex_ }; + 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(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_ }; + 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(); + } + if (new_status.isAllocated()) { + new_status_raw_buffer = new_status.getStructBuffer(); + } + + 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, + const Buffer &new_status, + const Buffer ¤t_status, + const Buffer ¤t_command, unsigned int device_type) { + std::lock_guard lock { generateCommandMutex_ }; + 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(); + } + 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(fleet_protocol::cxx::generateCommandAsync, + new_status_raw_buffer, + current_status_raw_buffer, + current_command_raw_buffer, + device_type); + + if (!ret.has_value()) { + return NOT_OK; + } + + if (ret.value().returnCode == OK) { + generated_command = constructBufferByTakeOwnership(ret.value().buffer); + } else { + generated_command = constructBuffer(); + } + return ret.value().returnCode; +} + +int ModuleManagerLibraryHandlerAsync::aggregateStatus(Buffer &aggregated_status, + const Buffer ¤t_status, + const Buffer &new_status, unsigned int device_type) { + std::lock_guard lock { aggregateStatusMutex_ }; + 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(); + } + if (new_status.isAllocated()) { + new_status_raw_buffer = new_status.getStructBuffer(); + } + + 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; + } + 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.value().buffer); + aggregated_status = current_status; + } + return ret.value().returnCode; +} + +int ModuleManagerLibraryHandlerAsync::aggregateError(Buffer &error_message, + const Buffer ¤t_error_message, + const Buffer &status, unsigned int device_type) { + std::lock_guard lock { aggregateErrorMutex_ }; + 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(); + } + if (status.isAllocated()) { + status_raw_buffer = status.getStructBuffer(); + } + + auto ret = aeronClient.callFunc(fleet_protocol::cxx::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.value().returnCode; +} + +int ModuleManagerLibraryHandlerAsync::generateFirstCommand(Buffer &default_command, unsigned int device_type) { + std::lock_guard lock { generateFirstCommandMutex_ }; + auto ret = aeronClient.callFunc(fleet_protocol::cxx::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.value().returnCode; +} + +int ModuleManagerLibraryHandlerAsync::statusDataValid(const Buffer &status, unsigned int device_type) { + std::lock_guard lock { statusDataValidMutex_ }; + fleet_protocol::cxx::ConvertibleBuffer status_raw_buffer; + if (status.isAllocated()) { + status_raw_buffer = status.getStructBuffer(); + } + + 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_ }; + fleet_protocol::cxx::ConvertibleBuffer command_raw_buffer; + if (command.isAllocated()) { + command_raw_buffer = command.getStructBuffer(); + } + + 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 { + 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 { + delete[] static_cast(buffer->data); + buffer->data = nullptr; + buffer->size_in_bytes = 0; +} + +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/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 b075a5e..193dc5c 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() { 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) { 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) { 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) { 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,11 +212,11 @@ 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" }; } return { buffer, deallocate_ }; } -} +} \ No newline at end of file diff --git a/source/bringauto/settings/SettingsParser.cpp b/source/bringauto/settings/SettingsParser.cpp index a5885cb..ae2e332 100644 --- a/source/bringauto/settings/SettingsParser.cpp +++ b/source/bringauto/settings/SettingsParser.cpp @@ -5,6 +5,7 @@ #include #include +#include @@ -37,8 +38,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 +54,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()); @@ -98,6 +96,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; @@ -107,6 +109,18 @@ bool SettingsParser::areSettingsCorrect() const { isCorrect = false; } + 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; + } + return isMissing; + }); + }); + return isCorrect; } @@ -151,14 +165,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)]); } + 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)]) { @@ -178,7 +194,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 c6d61c8..b8fa6b3 100644 --- a/source/bringauto/structures/ModuleLibrary.cpp +++ b/source/bringauto/structures/ModuleLibrary.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include @@ -11,12 +13,26 @@ ModuleLibrary::~ModuleLibrary() { [](auto &pair) { pair.second->destroy_status_aggregator(); }); } -void ModuleLibrary::loadLibraries(const std::unordered_map &libPaths) { +void ModuleLibrary::loadLibraries(const std::unordered_map &libPaths) { + std::shared_ptr handler; for(auto const &[key, path]: libPaths) { - auto handler = std::make_shared(); + 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, 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); + } +} + +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, 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()); 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/CMakeLists.txt b/test/CMakeLists.txt index 54f4550..dbf05a6 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 2c176f0..64214d0 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 0fb241e..ea7e09f 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 a5a6922..848208e 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 859f836..97514f5 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(); @@ -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 12d2dc4..258b3fc 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/SettingsParserTests.cpp b/test/source/SettingsParserTests.cpp index 42af8b0..cd25d55 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 diff --git a/test/source/StatusAggregatorTests.cpp b/test/source/StatusAggregatorTests.cpp index 81ea7c9..813218c 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();