From afce11efa80080f43c455521faf980b79c04c515 Mon Sep 17 00:00:00 2001 From: "Aubry, Kyllian" Date: Fri, 26 Apr 2024 15:38:16 +0200 Subject: [PATCH] SKA-477 inotify limits (#12) * SKA-477: unique inotify instance per mode * SKA-477: update refactoring * SKA-477: clean and optimize manager and adapter classes * SKA-477: refactore code * SKA-477: feedbacks took into account + refacto --- CMakeLists.txt | 3 +- adapter/IOAdapter.hpp | 17 +++- adapter/IOManager.hpp | 5 +- adapter/SilKitAdapterGenericLinuxIO.cpp | 5 +- advalues/adapter/AdAdapter.cpp | 10 +-- advalues/adapter/AdAdapter.hpp | 38 ++++---- advalues/adapter/AdManager.cpp | 53 +++++------ advalues/adapter/AdManager.hpp | 24 +++-- chardev/adapter/ChardevAdapter.cpp | 89 +++---------------- chardev/adapter/ChardevAdapter.hpp | 68 +++++--------- chardev/adapter/ChardevManager.cpp | 113 ++++++++++++++++++++---- chardev/adapter/ChardevManager.hpp | 43 ++++++--- gpio/adapter/GpioAdapter.cpp | 13 +-- gpio/adapter/GpioAdapter.hpp | 66 ++++++++------ gpio/adapter/GpioManager.cpp | 30 ++++--- gpio/adapter/GpioManager.hpp | 15 ++-- gpio/adapter/GpioWrapper.hpp | 7 +- 17 files changed, 315 insertions(+), 284 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c99a05e..aec7a11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,8 +68,7 @@ else() message(FATAL_ERROR "Something went wrong : Could not find SIL Kit package.") endif() -set(compile_options - #-g -O0 # Compile options for valgrind +set(compile_options -pedantic -Wall -Wextra -Wcast-align -Wformat=2 -Wshadow -Wsign-promo -Wstrict-overflow=5 diff --git a/adapter/IOAdapter.hpp b/adapter/IOAdapter.hpp index a6612d6..eb826d6 100644 --- a/adapter/IOAdapter.hpp +++ b/adapter/IOAdapter.hpp @@ -4,10 +4,25 @@ #include +#include "silkit/SilKit.hpp" +#include "silkit/services/logging/all.hpp" +#include "silkit/services/pubsub/all.hpp" + // Interface for handling one data class IOAdapter { public: + // Sil Kit logger + SilKit::Services::Logging::ILogger* _logger; + + // Sil Kit pub/sub + SilKit::Services::PubSub::IDataPublisher* _publisher; + SilKit::Services::PubSub::IDataSubscriber* _subscriber; + + // Publisher and subscriber topics + std::string _publishTopic{}; + std::string _subscribeTopic{}; + virtual ~IOAdapter() = default; virtual void Publish() = 0; @@ -15,8 +30,6 @@ class IOAdapter virtual auto Serialize() -> std::vector = 0; // Deserialize all received values virtual void Deserialize(const std::vector& bytes) = 0; - // Handler to receive events - virtual void ReceiveEvent() = 0; // Create an associate data subscriber can be done in a specific method virtual void CreateDataSubscriber() {}; diff --git a/adapter/IOManager.hpp b/adapter/IOManager.hpp index 5cf8eab..eb58483 100644 --- a/adapter/IOManager.hpp +++ b/adapter/IOManager.hpp @@ -13,11 +13,14 @@ class IOManager { public: + // Sil Kit logger + SilKit::Services::Logging::ILogger* _logger; + virtual ~IOManager() = default; // Initialize the adapters from YAML configuration file virtual void InitAdaptersFromConfigFile(const YAML::Node& configFile, - std::vector>& ioAdapters, + std::vector>& ioAdapters, SilKit::IParticipant* participant) = 0; virtual void Stop() = 0; diff --git a/adapter/SilKitAdapterGenericLinuxIO.cpp b/adapter/SilKitAdapterGenericLinuxIO.cpp index 7209c1c..02f3640 100644 --- a/adapter/SilKitAdapterGenericLinuxIO.cpp +++ b/adapter/SilKitAdapterGenericLinuxIO.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "IOAdapter.hpp" #include "IOManager.hpp" @@ -122,7 +123,7 @@ int main(int argc, char** argv) // Initialize chip and values std::vector> ioManagers; - std::vector> ioAdapters; + std::vector> ioAdapters; if (configFile["advalues"]) { @@ -147,7 +148,7 @@ int main(int argc, char** argv) // Stop all io_contexts and threads std::for_each(ioManagers.begin(), ioManagers.end(), [](const auto& manager){ manager->Stop(); }); - + auto runningStateFuture = runningStatePromise.get_future(); auto futureStatus = runningStateFuture.wait_for(15s); if (futureStatus != std::future_status::ready) diff --git a/advalues/adapter/AdAdapter.cpp b/advalues/adapter/AdAdapter.cpp index 886efc4..b508db7 100644 --- a/advalues/adapter/AdAdapter.cpp +++ b/advalues/adapter/AdAdapter.cpp @@ -10,12 +10,12 @@ AdAdapter::AdAdapter(SilKit::IParticipant* participant, const std::string& publisherName, const std::string& subscriberName, - std::unique_ptr pubDataSpec, - std::unique_ptr subDataSpec, + PubSubSpec* pubDataSpec, + PubSubSpec* subDataSpec, const std::string& pathToCharDev, - asio::io_context* ioc, - const std::string& dataType) : - ChardevAdapter(participant, publisherName, subscriberName, std::move(pubDataSpec), std::move(subDataSpec), pathToCharDev, ioc), + const std::string& dataType, + int inotifyFd) : + ChardevAdapter(participant, publisherName, subscriberName, std::move(pubDataSpec), std::move(subDataSpec), pathToCharDev, inotifyFd), _strDataType(dataType) { static std::unordered_map map { diff --git a/advalues/adapter/AdAdapter.hpp b/advalues/adapter/AdAdapter.hpp index 49a07c9..5d00018 100644 --- a/advalues/adapter/AdAdapter.hpp +++ b/advalues/adapter/AdAdapter.hpp @@ -2,16 +2,16 @@ #pragma once -#include "../../chardev/adapter/ChardevAdapter.hpp" - #include #include #include +#include "../../chardev/adapter/ChardevAdapter.hpp" + #include "silkit/SilKit.hpp" #include "silkit/services/pubsub/all.hpp" -#include +#include "asio/posix/stream_descriptor.hpp" // Each file has a specific AdAdapter class AdAdapter : public ChardevAdapter @@ -22,41 +22,47 @@ enum EnumTypes {enum_int8_t, enum_uint8_t, enum_int16_t, enum_uint16_t, enum_int32_t, enum_uint32_t, enum_int64_t, enum_uint64_t, enum_float, enum_double}; public: + friend class AdManager; + AdAdapter() = delete; AdAdapter(SilKit::IParticipant* participant, const std::string& publisherName, const std::string& subscriberName, - std::unique_ptr pubDataSpec, - std::unique_ptr subDataSpec, + PubSubSpec* pubDataSpec, + PubSubSpec* subDataSpec, const std::string& pathToCharDev, - asio::io_context* ioc, - const std::string& dataType); + const std::string& dataType, + int inotifyFd); + +private: + EnumTypes _dataType; + std::string _strDataType; // Serialize chip values auto Serialize() -> std::vector override; // Deserialize received values void Deserialize(const std::vector& bytes) override; -private: - EnumTypes _dataType; - std::string _strDataType; - + // Converting and checking values template - inline T bufferFromChardevTo(); + inline auto bufferFromChardevTo() -> T; template inline void throwIfInvalid(const T max, const T lowest, const U value); template - inline T isValidData(const std::string& str); + inline auto isValidData(const std::string& str) -> T; void strContainsOnly(const std::string& str, const std::string& allowedChars, bool isFloatingNumber = false, bool isSigned = false); auto strWithoutNewLine(const std::string& str) -> std::string; }; -// Inline implementations +//////////////////////////// +// Inline implementations // +//////////////////////////// + template -T AdAdapter::bufferFromChardevTo() +auto AdAdapter::bufferFromChardevTo() -> T { std::string str(_bufferFromChardev.begin(), _bufferFromChardev.end()); std::stringstream val(str); @@ -75,7 +81,7 @@ void AdAdapter::throwIfInvalid(const T max, const T lowest, const U value) } template -T AdAdapter::isValidData(const std::string& str) +auto AdAdapter::isValidData(const std::string& str) -> T { static const std::string strNum{"0123456789"}; diff --git a/advalues/adapter/AdManager.cpp b/advalues/adapter/AdManager.cpp index 30ba593..8200ae2 100644 --- a/advalues/adapter/AdManager.cpp +++ b/advalues/adapter/AdManager.cpp @@ -3,14 +3,16 @@ #include "AdManager.hpp" #include +#include -#include "AdAdapter.hpp" #include "../../util/Exceptions.hpp" #include "yaml-cpp/yaml.h" #include "silkit/util/serdes/Serialization.hpp" +#include "asio/read.hpp" + using namespace adapters; using namespace SilKit::Services::PubSub; @@ -21,34 +23,17 @@ static const std::vector types {"int8_t", "uint8_t", "int16_t", "uint16_t", "int32_t", "uint32_t", "int64_t", "uint64_t", "float", "double"}; AdManager::AdManager(const YAML::Node& configFile, - std::vector>& ioAdapters, + std::vector>& ioAdapters, SilKit::Services::Logging::ILogger* logger, SilKit::IParticipant* participant) : - _logger(logger) + ChardevManager(logger) { // Initialize values from config file InitAdaptersFromConfigFile(configFile, ioAdapters, participant); } -AdManager::~AdManager() -{ - Stop(); -} - -void AdManager::Stop() -{ - if (!_ioc.stopped()) - { - _ioc.stop(); - } - if (_thread.joinable()) - { - _thread.join(); - } -} - void AdManager::InitAdaptersFromConfigFile(const YAML::Node& configFile, - std::vector>& ioAdapters, + std::vector>& ioAdapters, SilKit::IParticipant* participant) { std::vector> advaluesYAMLConfigs; @@ -77,21 +62,23 @@ void AdManager::InitAdaptersFromConfigFile(const YAML::Node& configFile, publisherName = "pub" + fileValuesYaml.fileName; } - auto newAdapter = std::make_shared(participant, - publisherName, - subscriberName, - std::move(pubDataSpec), - std::move(subDataSpec), - fileValuesYaml.path + fileValuesYaml.fileName, - &_ioc, - fileValuesYaml.dataType); - - newAdapter->Initialize(); + auto newAdapter = std::make_unique(participant, + publisherName, + subscriberName, + pubDataSpec.get(), + subDataSpec.get(), + fileValuesYaml.path + fileValuesYaml.fileName, + fileValuesYaml.dataType, + _inotifyFd); - ioAdapters.push_back(newAdapter); + _wdAdapter[newAdapter->_wd] = newAdapter.get(); + + ioAdapters.push_back(std::move(newAdapter)); } } + ReceiveEvent(); + _thread = std::thread([&]() -> void { _ioc.run(); }); @@ -178,4 +165,4 @@ void AdManager::FillAdvaluesYAML(const YAML::Node& mainNode, std::vector #include +#include + +#include "AdAdapter.hpp" #include "./../adapter/IOAdapter.hpp" #include "../../adapter/IOManager.hpp" +#include "../../chardev/adapter/ChardevManager.hpp" #include "../../util/YamlHelper.hpp" #include "silkit/SilKit.hpp" #include "silkit/services/logging/all.hpp" -#include +#include "asio/posix/stream_descriptor.hpp" // Manage all advalues adapters (initialize, events identification) -class AdManager : public IOManager +class AdManager : public ChardevManager { public: AdManager() = delete; AdManager(const YAML::Node& configFile, - std::vector>& ioAdapters, + std::vector>& ioAdapters, SilKit::Services::Logging::ILogger* logger, SilKit::IParticipant* participant); - ~AdManager(); - - void Stop() override; + ~AdManager() = default; +private: // Get chip config from YAML file and initialize all chardev adapters void InitAdaptersFromConfigFile(const YAML::Node& configFile, - std::vector>& ioAdapters, + std::vector>& ioAdapters, SilKit::IParticipant* participant) override; -private: - // Ioc to handle every character devices - asio::io_context _ioc; - std::thread _thread; - - SilKit::Services::Logging::ILogger* _logger; - // Get all informations from YAML configuration file void GetYamlConfig(const YAML::Node& doc, std::vector>& dataYAMLConfigs); void FillAdvaluesYAML(const YAML::Node& mainNode, std::vector& advaluesYAMLConfigs, const std::string& dataType); diff --git a/chardev/adapter/ChardevAdapter.cpp b/chardev/adapter/ChardevAdapter.cpp index 9d596de..14a4837 100644 --- a/chardev/adapter/ChardevAdapter.cpp +++ b/chardev/adapter/ChardevAdapter.cpp @@ -8,7 +8,7 @@ #include "silkit/util/serdes/Deserializer.hpp" #include "silkit/util/serdes/Serializer.hpp" -#include +#include "asio/read.hpp" using namespace SilKit::Services::PubSub; using namespace adapters; @@ -16,16 +16,20 @@ using namespace adapters; ChardevAdapter::ChardevAdapter(SilKit::IParticipant* participant, const std::string& publisherName, const std::string& subscriberName, - std::unique_ptr pubDataSpec, - std::unique_ptr subDataSpec, + PubSubSpec* pubDataSpec, + PubSubSpec* subDataSpec, const std::string& pathToCharDev, - asio::io_context* ioc) : + int inotifyFd) : _pathToCharDev(pathToCharDev), - _logger(participant->GetLogger()), - _isRecvValue(false), - _isCancelled(false), - _ioc(ioc) + _isRecvValue(false) { + _logger = participant->GetLogger(); + + _wd = inotify_add_watch(inotifyFd, _pathToCharDev.c_str(), IN_CLOSE_WRITE); + if (_wd == -1) { + throw InotifyError("inotify add watch error (" + std::to_string(errno) +") on: " + _pathToCharDev); + } + if (pubDataSpec) { _publishTopic = pubDataSpec->Topic(); @@ -56,33 +60,6 @@ ChardevAdapter::ChardevAdapter(SilKit::IParticipant* participant, } } -ChardevAdapter::~ChardevAdapter() -{ - int ret = close(inotifyFd); - if (ret == -1) - { - _logger->Error("Error while closing inotify file descriptor (" + std::to_string(inotifyFd) + ") for " + _pathToCharDev); - } -} - -void ChardevAdapter::Initialize() -{ - // Handle event only if there is a publisher topic - // Initialize file event handler - inotifyFd = inotify_init1( IN_NONBLOCK ); - if (inotifyFd == -1) { - throw InotifyError("inotify initialization error"); - } - - fd = std::make_unique(*_ioc, inotifyFd); - auto wd = inotify_add_watch(inotifyFd, _pathToCharDev.c_str(), IN_CLOSE_WRITE); - if (wd == -1) { - throw InotifyError("inotify add watcher error"); - } - - ReceiveEvent(); -} - void ChardevAdapter::Publish() { if (!_publishTopic.empty()) @@ -117,45 +94,3 @@ void ChardevAdapter::Deserialize(const std::vector& bytes) SilKit::Util::SerDes::Deserializer deserializer(bytes); _bufferToChardev = deserializer.Deserialize>(); } - -void ChardevAdapter::ReceiveEvent() -{ - async_read(*fd, asio::buffer(_eventBuffer, sizeof(inotify_event)), - [that = shared_from_this(), this](const std::error_code ec, const std::size_t bytes_transferred){ - if (ec) - { - if(_isCancelled && (ec == asio::error::operation_aborted)) - { - // An error code comes right after calling fd.cancel() in order to close all asynchronous reads - _isCancelled = false; - } - else - { - // If the error does not happened after fd.cancel(), handle it - _logger->Error("Unable to handle event on " + _pathToCharDev + ". " + - "Error code: " + std::to_string(ec.value()) + " (" + ec.message()+ "). " + - "Error category: " + ec.category().name()); - } - } - else - { - if (_isRecvValue) - { - _isRecvValue = false; - } - // The file has been modified and does not come from deserialization - else - { - _logger->Debug(_pathToCharDev + " has been updated"); - // Read the value only if it has to be sent - if (!_publishTopic.empty()) - { - _bufferFromChardev = Util::ReadFile(_pathToCharDev, _logger, BUF_LEN); - Publish(); - } - } - - ReceiveEvent(); - } - }); -} \ No newline at end of file diff --git a/chardev/adapter/ChardevAdapter.hpp b/chardev/adapter/ChardevAdapter.hpp index c7dd862..e7c7ec2 100644 --- a/chardev/adapter/ChardevAdapter.hpp +++ b/chardev/adapter/ChardevAdapter.hpp @@ -12,67 +12,47 @@ #include "silkit/SilKit.hpp" #include "silkit/services/pubsub/all.hpp" -#include +#include "asio/posix/stream_descriptor.hpp" // Each file has a specific ChardevAdapter -class ChardevAdapter : public IOAdapter, public std::enable_shared_from_this +class ChardevAdapter : public IOAdapter { using PubSubSpec = SilKit::Services::PubSub::PubSubSpec; -static constexpr uint32_t BUF_LEN = 4096; -static constexpr uint32_t EVENT_SIZE = sizeof(inotify_event); -public: +public: + friend class ChardevManager; + ChardevAdapter() = delete; ChardevAdapter(SilKit::IParticipant* participant, const std::string& publisherName, const std::string& subscriberName, - std::unique_ptr pubDataSpec, - std::unique_ptr subDataSpec, + PubSubSpec* pubDataSpec, + PubSubSpec* subDataSpec, const std::string& pathToCharDev, - asio::io_context* ioc); - - ~ChardevAdapter(); + int inotifyFd); + ~ChardevAdapter() = default; - // Called after the constructor in order to use shared_from_this() - void Initialize(); - - // Publish chip values - void Publish() override; - // Serialize chip values - auto Serialize() -> std::vector override; - // Deserialize received values - void Deserialize(const std::vector& bytes) override; +protected: + static constexpr uint32_t BUF_LEN = 4096; + static constexpr uint32_t EVENT_SIZE = sizeof(inotify_event); + // Access and manage the chardev std::vector _bufferToChardev = {}; std::vector _bufferFromChardev = {}; - std::string _pathToCharDev; - - // Sil Kit logger - SilKit::Services::Logging::ILogger* _logger; - - // Publisher and subscriber topics - std::string _publishTopic = {}; - std::string _subscribeTopic = {}; -private: - // Buffers to handle received/sent values - std::array _eventBuffer = {}; - - // Prevent catching an event after deserializing and updating the character devices + // Avoid to read several times the same received value bool _isRecvValue; - // Handle error code introduced by fd.cancel() - bool _isCancelled; - - // Handlers for events on the character device - int inotifyFd; - std::unique_ptr fd; - asio::io_context* _ioc; - // Sil Kit pub/sub - SilKit::Services::PubSub::IDataPublisher* _publisher; - SilKit::Services::PubSub::IDataSubscriber* _subscriber; + // Inotify watch descriptor + int _wd; + +private: + // Publish chardev values + void Publish() override; - // Receive and handle events - void ReceiveEvent() override; + // Serialize chip values + auto Serialize() -> std::vector override; + // Deserialize received values + void Deserialize(const std::vector& bytes) override; }; \ No newline at end of file diff --git a/chardev/adapter/ChardevManager.cpp b/chardev/adapter/ChardevManager.cpp index d9c6466..c77a296 100644 --- a/chardev/adapter/ChardevManager.cpp +++ b/chardev/adapter/ChardevManager.cpp @@ -2,39 +2,57 @@ #include "ChardevManager.hpp" -#include - -#include "ChardevAdapter.hpp" - #include "../../util/FileHelper.hpp" #include "../../util/Exceptions.hpp" #include "silkit/util/serdes/Serialization.hpp" +#include "asio/read.hpp" + using namespace adapters; using namespace SilKit::Services::PubSub; -static constexpr std::size_t MAX_EVENTS = 4096; - ChardevManager::ChardevManager(const YAML::Node& configFile, - std::vector>& ioAdapters, + std::vector>& ioAdapters, SilKit::Services::Logging::ILogger* logger, SilKit::IParticipant* participant) : - _logger(logger) + ChardevManager(logger) { // Initialize values from config file InitAdaptersFromConfigFile(configFile, ioAdapters, participant); } +ChardevManager::ChardevManager(SilKit::Services::Logging::ILogger* logger) : + _isCancelled(false) +{ + _logger = logger; + + _inotifyFd = inotify_init1( IN_NONBLOCK ); + if (_inotifyFd == -1) { + throw InotifyError("inotify initialization error (" + std::to_string(errno) +")"); + } + + _fd = std::make_unique(_ioc, _inotifyFd); +} + ChardevManager::~ChardevManager() { Stop(); } void ChardevManager::Stop() -{ +{ + _isCancelled = true; + + if (_fd->is_open()) + { + _logger->Debug("Cancel operation on asio stream_descriptor (with inotify fd: " + std::to_string(_inotifyFd) + ") and close it."); + _fd->cancel(); + _fd->close(); + } if (!_ioc.stopped()) { + _logger->Debug("Stop the associated asio io_context."); _ioc.stop(); } if (_thread.joinable()) @@ -43,8 +61,8 @@ void ChardevManager::Stop() } } -void ChardevManager::InitAdaptersFromConfigFile(const YAML::Node& configFile, - std::vector>& ioAdapters, +void ChardevManager::InitAdaptersFromConfigFile(const YAML::Node& configFile, + std::vector>& ioAdapters, SilKit::IParticipant* participant) { std::vector chardevYAMLConfigs; @@ -84,19 +102,22 @@ void ChardevManager::InitAdaptersFromConfigFile(const YAML::Node& configFile, publisherName = "pub" + pathToFile_; } - auto newAdapter = std::make_shared(participant, + auto newAdapter = std::make_unique(participant, publisherName, subscriberName, - std::move(pubDataSpec), - std::move(subDataSpec), - pathToFile, - &_ioc); + pubDataSpec.get(), + subDataSpec.get(), + pathToFile, + _inotifyFd); - newAdapter->Initialize(); + // _wdAdapter[newAdapter->_wd] = std::move(newAdapter); + _wdAdapter[newAdapter->_wd] = newAdapter.get(); - ioAdapters.push_back(newAdapter); + ioAdapters.push_back(std::move(newAdapter)); } + ReceiveEvent(); + _thread = std::thread([&]() -> void { _ioc.run(); }); @@ -130,4 +151,58 @@ void ChardevManager::GetYamlConfig(const YAML::Node& doc, std::vectorError("Unable to handle event. " + "Error code: " + std::to_string(ec.value()) + " (" + ec.message()+ "). " + + "Error category: " + ec.category().name()); + } + } + else + { + auto event = reinterpret_cast(_eventBuffer.data()); + + ChardevAdapter* adapterOnEvent; + + if (auto search = _wdAdapter.find(event->wd); search != _wdAdapter.end()) + { + adapterOnEvent = search->second; + + if (adapterOnEvent->_isRecvValue) + { + adapterOnEvent->_isRecvValue = false; + } + else // The file has been modified and does not come from deserialization + { + _logger->Debug(adapterOnEvent->_pathToCharDev + " has been updated"); + // Read the value only if it has to be sent + if (!adapterOnEvent->_publishTopic.empty()) + { + adapterOnEvent->_bufferFromChardev = Util::ReadFile(adapterOnEvent->_pathToCharDev, _logger, ChardevAdapter::BUF_LEN); + adapterOnEvent->Publish(); + } + } + } + else + { + _logger->Error("Event error on watch descriptor " + std::to_string(event->wd)); + } + + ReceiveEvent(); + } + }); +} diff --git a/chardev/adapter/ChardevManager.hpp b/chardev/adapter/ChardevManager.hpp index b861782..5d22098 100644 --- a/chardev/adapter/ChardevManager.hpp +++ b/chardev/adapter/ChardevManager.hpp @@ -5,43 +5,64 @@ #include #include #include +#include +#include + +#include "ChardevAdapter.hpp" #include "./../adapter/IOAdapter.hpp" #include "../../adapter/IOManager.hpp" #include "../../util/YamlHelper.hpp" +#include "../../util/FileHelper.hpp" #include "silkit/SilKit.hpp" #include "silkit/services/logging/all.hpp" #include "yaml-cpp/yaml.h" -#include +#include "asio/posix/stream_descriptor.hpp" // Manage all chardev adapters (initialize, events identification) class ChardevManager : public IOManager { public: ChardevManager() = default; + ChardevManager(SilKit::Services::Logging::ILogger* logger); ChardevManager(const YAML::Node& configFile, - std::vector>& ioAdapters, + std::vector>& ioAdapters, SilKit::Services::Logging::ILogger* logger, SilKit::IParticipant* participant); ~ChardevManager(); - // Get chip config from YAML file and initialize all chardev adapters - void InitAdaptersFromConfigFile(const YAML::Node& configFile, - std::vector>& ioAdapters, - SilKit::IParticipant* participant) override; - void Stop() override; -private: +protected: + // Inotify instance + int _inotifyFd; + + // Perform asynchronous reads on the inotify instance + std::unique_ptr _fd; + std::array _eventBuffer = {}; + + // Handle error code introduced by fd.cancel() + bool _isCancelled; + // Ioc to handle every character devices asio::io_context _ioc; std::thread _thread; - SilKit::Services::Logging::ILogger* _logger; + // Pointer to each adapter instance with its associated watch descriptor + std::unordered_map _wdAdapter; + + // Handle the events on each chardev + void ReceiveEvent(); + +private: + // Get chip config from YAML file and initialize all chardev adapters + void InitAdaptersFromConfigFile(const YAML::Node& configFile, + std::vector>& ioAdapters, + SilKit::IParticipant* participant) override; // Get all informations from YAML configuration file - void GetYamlConfig(const YAML::Node& doc, std::vector& dataYAMLConfigs); -}; \ No newline at end of file + void GetYamlConfig(const YAML::Node& doc, std::vector& dataYAMLConfigs); +}; diff --git a/gpio/adapter/GpioAdapter.cpp b/gpio/adapter/GpioAdapter.cpp index 38f50f8..7bef7ad 100644 --- a/gpio/adapter/GpioAdapter.cpp +++ b/gpio/adapter/GpioAdapter.cpp @@ -9,8 +9,8 @@ #include "silkit/util/serdes/Deserializer.hpp" #include "silkit/util/serdes/Serializer.hpp" -#include -#include +#include "asio/posix/stream_descriptor.hpp" +#include "asio/read.hpp" using namespace SilKit::Services::PubSub; using namespace GpioWrapper; @@ -18,7 +18,7 @@ using namespace GpioWrapper; GpioAdapter::GpioAdapter(SilKit::IParticipant* participant, const std::string& publisherName, const std::string& subscriberName, - std::unique_ptr pubDataSpec, + PubSubSpec* pubDataSpec, std::unique_ptr subDataSpec, Chip* gpiochip, Ioc* ioc, @@ -27,9 +27,10 @@ GpioAdapter::GpioAdapter(SilKit::IParticipant* participant, _isCancelled(false), _chip(gpiochip), _ioc(ioc), - _participant(participant), - _logger(participant->GetLogger()) + _participant(participant) { + _logger = participant->GetLogger(); + if (subDataSpec) { _subscribeTopic = subDataSpec->Topic(); @@ -151,7 +152,7 @@ void GpioAdapter::ReceiveEvent() { gpioevent_data data; asio::async_read(*(_eh->GetFd()), asio::buffer(&data, sizeof(data)), - [that = shared_from_this(), this, &data](const std::error_code ec, const long unsigned int size){ + [this, &data](const std::error_code ec, const long unsigned int size){ if (ec) { if(_isCancelled && (ec == asio::error::operation_aborted)) diff --git a/gpio/adapter/GpioAdapter.hpp b/gpio/adapter/GpioAdapter.hpp index bb282e6..3957bd4 100644 --- a/gpio/adapter/GpioAdapter.hpp +++ b/gpio/adapter/GpioAdapter.hpp @@ -15,34 +15,34 @@ #include "silkit/services/pubsub/all.hpp" // Each gpiochip line has a specific GpioAdapter -class GpioAdapter : public IOAdapter, public std::enable_shared_from_this +class GpioAdapter : public IOAdapter { using PubSubSpec = SilKit::Services::PubSub::PubSubSpec; using offset_t = uint8_t; public: + friend class GpioManager; + GpioAdapter(SilKit::IParticipant* participant, const std::string& publisherName, const std::string& subscriberName, - std::unique_ptr pubDataSpec, + PubSubSpec* pubDataSpec, std::unique_ptr subDataSpec, GpioWrapper::Chip* gpiochip, GpioWrapper::Ioc* ioc, const offset_t offset); - ~GpioAdapter() = default; - - // Called after the constructor in order to use shared_from_this() - void Initialize(); - - // Access publish method in main function - void Publish() override; - // Deserialize received value and direction - void Deserialize(const std::vector& bytes) override; - // Serialize value and direction - auto Serialize() -> std::vector override; - - // Create data subscriber after the end of the initialization - void CreateDataSubscriber() override; + ~GpioAdapter() + { + if (_eh->IsFdOpen()) + { + _eh->Cancel(); + _eh->Close(); + } + if (_lh->IsFdOpen()) + { + _lh->Close(); + } + } private: // Specific enum to gpios @@ -71,30 +71,42 @@ using offset_t = uint8_t; GpioWrapper::Chip* _chip; GpioWrapper::Ioc* _ioc; // Requests on the line - std::shared_ptr _eh; - std::shared_ptr _lh; + std::unique_ptr _eh; + std::unique_ptr _lh; - // Publisher and subscriber name and topics - std::string _publishTopic{}; - std::string _subscribeTopic{}; + // Subscriber name std::string _subscriberName{}; - // Sil Kit participant and pub/sub + // Sil Kit participant SilKit::IParticipant* _participant; - SilKit::Services::Logging::ILogger* _logger; - SilKit::Services::PubSub::IDataPublisher* _publisher; - SilKit::Services::PubSub::IDataSubscriber* _subscriber; std::unique_ptr _subDataSpec; + // Publish internal data + void Publish() override; + + // Called after the constructor in order to use shared_from_this() + void Initialize(); + + // Deserialize received value and direction + void Deserialize(const std::vector& bytes) override; + // Serialize value and direction + auto Serialize() -> std::vector override; + + // Create data subscriber after the end of the initialization + void CreateDataSubscriber() override; + // Receive and handle events - void ReceiveEvent() override; + void ReceiveEvent(); // Close the current request void CloseRequests(); inline auto to_string(const offset_t offset) const -> std::string; }; -// Inline implementations +//////////////////////////// +// Inline implementations // +//////////////////////////// + auto GpioAdapter::to_string(const offset_t offset) const -> std::string { return std::to_string(static_cast(offset)); diff --git a/gpio/adapter/GpioManager.cpp b/gpio/adapter/GpioManager.cpp index b83961f..09f0367 100644 --- a/gpio/adapter/GpioManager.cpp +++ b/gpio/adapter/GpioManager.cpp @@ -19,11 +19,12 @@ using namespace std::chrono_literals; using namespace SilKit::Services::PubSub; GpioManager::GpioManager(const YAML::Node& configFile, - std::vector>& ioAdapters, + std::vector>& ioAdapters, SilKit::Services::Logging::ILogger* logger, - SilKit::IParticipant* participant) : - _logger(logger) + SilKit::IParticipant* participant) { + _logger = logger; + InitAdaptersFromConfigFile(configFile, ioAdapters, participant); } @@ -35,30 +36,32 @@ GpioManager::~GpioManager() void GpioManager::Stop() { std::size_t i = 0; - for (const auto& chipContext : _chipContexts) + for (auto& [chip, ioc] : _chipContexts) { - if (!chipContext.second->stopped()) + if (chip->IsFdOpen()) { - chipContext.second->stop(); + chip->Close(); + } + if (!ioc->stopped()) + { + ioc->stop(); } if (threadPool[i].joinable()) { threadPool[i].join(); } - - chipContext.first->Close(); ++i; } } void GpioManager::InitAdaptersFromConfigFile(const YAML::Node& configFile, - std::vector>& ioAdapters, + std::vector>& ioAdapters, SilKit::IParticipant* participant) { // Root attribute gpiochips const auto gpiochipNodes = configFile["gpiochips"]; - _logger->Debug("Gpio chips found in the YAML configuration file."); + _logger->Debug("GPIO chips found in the YAML configuration file."); // for each gpio chip for (const auto& chipNode : gpiochipNodes) @@ -71,6 +74,7 @@ void GpioManager::InitAdaptersFromConfigFile(const YAML::Node& configFile, const auto chipName = chipPath.substr(chipPath.find_last_of('/') + 1); auto ioc = std::make_unique(); + _logger->Debug("Opening " + chipPath); auto chip = std::make_unique(*ioc, chipPath); // Manage each line specified in the YAML config file @@ -92,10 +96,10 @@ void GpioManager::InitAdaptersFromConfigFile(const YAML::Node& configFile, subscriberName = chipName + "SubLine" + gpioYAMLConfigs[j].offset; } - auto newAdapter = std::make_shared(participant, + auto newAdapter = std::make_unique(participant, publisherName, subscriberName, - std::move(pubDataSpec), + pubDataSpec.get(), std::move(subDataSpec), chip.get(), ioc.get(), @@ -103,7 +107,7 @@ void GpioManager::InitAdaptersFromConfigFile(const YAML::Node& configFile, newAdapter->Initialize(); - ioAdapters.push_back(newAdapter); + ioAdapters.push_back(std::move(newAdapter)); } _chipContexts.insert(std::make_pair(std::move(chip), std::move(ioc))); diff --git a/gpio/adapter/GpioManager.hpp b/gpio/adapter/GpioManager.hpp index 6572257..951e552 100644 --- a/gpio/adapter/GpioManager.hpp +++ b/gpio/adapter/GpioManager.hpp @@ -21,29 +21,26 @@ // Manage all gpio adapters (initialize, events identification) class GpioManager : public IOManager { - public: GpioManager() = delete; GpioManager(const YAML::Node& configFile, - std::vector>& ioAdapters, + std::vector>& ioAdapters, SilKit::Services::Logging::ILogger* logger, SilKit::IParticipant* participant); ~GpioManager(); - // Open the chip and create Adapters - void InitAdaptersFromConfigFile(const YAML::Node& configFile, - std::vector>& ioAdapters, - SilKit::IParticipant* participant) override; - void Stop() override; private: - SilKit::Services::Logging::ILogger* _logger; - // One ioc and thread per gpio chip std::unordered_map, std::unique_ptr> _chipContexts; std::vector threadPool; + // Open the chip and create Adapters + void InitAdaptersFromConfigFile(const YAML::Node& configFile, + std::vector>& ioAdapters, + SilKit::IParticipant* participant) override; + // Get all informations from YAML configuration file void GetYamlConfig(const YAML::Node& chipNode, std::vector& dataYAMLConfigs, std::string& chipPath); }; \ No newline at end of file diff --git a/gpio/adapter/GpioWrapper.hpp b/gpio/adapter/GpioWrapper.hpp index f526443..a27a4eb 100644 --- a/gpio/adapter/GpioWrapper.hpp +++ b/gpio/adapter/GpioWrapper.hpp @@ -6,8 +6,8 @@ #include #include -#include -#include +#include "asio/posix/stream_descriptor.hpp" +#include "asio/read.hpp" namespace GpioWrapper { @@ -63,7 +63,8 @@ class Chip ChipInfo GetInfo(); LineInfo GetLineInfo(offset_t); - inline void Close() { if (fd.is_open()) fd.close(); } + inline void Close() { fd.close(); } + inline bool IsFdOpen() { return fd.is_open(); } private: Fd fd;