From b8fdae9d16312e8f0cc88dc241105f74f372d5d5 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Mon, 21 Oct 2024 01:21:53 +0300 Subject: [PATCH] ... --- CMakeLists.txt | 5 +- net/adc_device_netmsg.h | 129 ++++++++++++++++++++-- net/adc_device_netserver.h | 215 +++++++++++++++++++++++++++++++------ net/adc_netserver.h | 103 +++++++++++++++++- 4 files changed, 411 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ed81d5..1a1ab12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,11 +21,12 @@ set(ADC_DEVICE_HEADERS device/adc_device_attribute.h device/adc_device_command.h device/adc_device.h + device/adc_device_concepts.h ) set(ADC_NETWORK_HEADERS - net/adc_netmsg.h + # net/adc_netmsg.h # net/adc_netmessage.h net/adc_netproto.h net/adc_netservice.h @@ -33,6 +34,7 @@ set(ADC_NETWORK_HEADERS net/adc_netserver.h net/adc_net_concepts.h net/adc_device_netmsg.h + net/adc_device_netserver.h ) @@ -74,6 +76,7 @@ if (SPDLOG_LIBRARY) endif() + option(USE_UWEBSOCKET "Use of uWebsocket library for websocket-related staff" ON) if (USE_UWEBSOCKET) diff --git a/net/adc_device_netmsg.h b/net/adc_device_netmsg.h index 9c9f372..cf2b64b 100644 --- a/net/adc_device_netmsg.h +++ b/net/adc_device_netmsg.h @@ -174,18 +174,26 @@ public: } } - template + template requires traits::adc_output_char_range - AdcKeyValueMessage& setKeyValue(KeyT&& key, ValuePartTs&&... values) + AdcKeyValueMessage& setKey(const KeyT& key) { if (std::ranges::size(_byteSequence)) { _byteSequence = ByteSeqT(); } - std::ranges::copy(utils::AdcDefaultValueConverter::template serialize( - std::forward(key)), + std::ranges::copy(utils::AdcDefaultValueConverter::template serialize(key), std::back_inserter(_byteSequence)); + return *this; + } + + template + requires traits::adc_output_char_range + AdcKeyValueMessage& setKeyValue(const KeyT& key, ValuePartTs&&... values) + { + setKey(key); + if constexpr (sizeof...(ValuePartTs)) { std::ranges::copy(keyValueDelimiter, std::back_inserter(_byteSequence)); setValueHelper(std::forward(values)...); @@ -194,6 +202,36 @@ public: return *this; } + + template + requires traits::adc_output_char_range + AdcKeyValueMessage& setKeyValue(const KeyT& key, const ValuePartRangeT& values) + { + setKey(key); + + for (auto const& el : values) { + std::ranges::copy(keyValueDelimiter, std::back_inserter(_byteSequence)); + setValueHelper(el); + } + + return *this; + } + + + template + requires traits::adc_output_char_range + AdcKeyValueMessage& setKeyValue(const KeyT& key, const ValuePartTupleT& values) + { + setKey(key); + + if constexpr (std::tuple_size_v) { + std::apply([this](Ts&&... args) { setValueHelper(std::forward(args)...); }, values); + } + + return *this; + } + + template requires traits::adc_output_char_range AdcKeyValueMessage& setValue(ValuePartTs&&... values) @@ -201,9 +239,32 @@ public: std::vector kw; std::ranges::copy(key>(), std::back_inserter(kw)); - return setValue(kw, std::forward(values)...); + return setKeyValue(kw, std::forward(values)...); } + + template + requires traits::adc_output_char_range && + (!traits::adc_input_char_range>) + AdcKeyValueMessage& setValue(const ValuePartRangeT& values) + { + std::vector kw; + std::ranges::copy(key>(), std::back_inserter(kw)); + + return setKeyValue(kw, values); + } + + template + requires traits::adc_output_char_range + AdcKeyValueMessage& setValue(const ValuePartTupleT& values) + { + std::vector kw; + std::ranges::copy(key>(), std::back_inserter(kw)); + + return setKeyValue(kw, values); + } + + protected: ByteSeqT& _byteSequence; @@ -374,11 +435,40 @@ public: void ack() { - base_t::setKeyValue(ACK_KEY); + base_t::setKey(ACK_KEY); keyHash(); } + + template + void ack(const ParT& param, const ParTs&... params) + { + base_t::setKeyValue(ACK_KEY, param, params...); + + keyHash(); + } + + + template + requires(!traits::adc_input_char_range>) + void ack(const ParT& param) + { + base_t::setKeyValue(ACK_KEY, param); + + keyHash(); + } + + + template + void ack(const ParT& param) + { + base_t::setKeyValue(ACK_KEY, param); + + keyHash(); + } + + template void set(AttrNameT&& attr_name, ValueT&& value, ValueTs&&... values) { @@ -429,7 +519,32 @@ public: void names() { - base_t::setKeyValue(NAMES_KEY); + base_t::setKey(NAMES_KEY); + + keyHash(); + } + + template + void names(const DevNameT& dev_name, const DevNameTs&... dev_names) + { + base_t::setKeyValue(NAMES_KEY, dev_name, dev_names...); + + keyHash(); + } + + template + requires(!traits::adc_input_char_range>) + void names(const R& dev_names) + { + base_t::setKeyValue(NAMES_KEY, dev_names); + + keyHash(); + } + + template + void names(const T& dev_names) + { + base_t::setKeyValue(NAMES_KEY, dev_names); keyHash(); } diff --git a/net/adc_device_netserver.h b/net/adc_device_netserver.h index e0cb6a7..8836ce5 100644 --- a/net/adc_device_netserver.h +++ b/net/adc_device_netserver.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "adc_device_netmsg.h" #include "adc_netserver.h" @@ -8,6 +10,100 @@ namespace adc { +enum class AdcDeviceNetServerSessionError : int { + ERROR_OK, + ERROR_NULL_DEVICE, + ERROR_UNKNOWN_PROTO_KWD, + ERROR_UNKNOWN_DEVICE_ID, + ERROR_NO_DEVICE_ID, + ERROR_NO_PROTO_KWDNAME, + ERROR_NO_PROTO_ATTRNAME, + ERROR_NO_PROTO_ATTRVALUE, + ERROR_NO_PROTO_CMDNAME, + ERROR_UNKNOWN_ERROR +}; + +} + + +// place here to allow clang compilation +namespace std +{ +template <> +class is_error_code_enum : public true_type +{ +}; + + +} // namespace std + +namespace adc +{ + + +struct AdcDeviceNetServerSessionErrorCategory : std::error_category { + AdcDeviceNetServerSessionErrorCategory() : std::error_category() {} + + const char* name() const noexcept + { + return "ADC_DEVICE_NESERVER_SESSION"; + } + + std::string message(int ec) const + { + AdcDeviceNetServerSessionError err = static_cast(ec); + + switch (err) { + case AdcDeviceNetServerSessionError::ERROR_OK: + return "OK"; + case AdcDeviceNetServerSessionError::ERROR_NULL_DEVICE: + return "device was not bound"; + case AdcDeviceNetServerSessionError::ERROR_UNKNOWN_PROTO_KWD: + return "invalid formatted message: unrecognized message keyword"; + case AdcDeviceNetServerSessionError::ERROR_UNKNOWN_DEVICE_ID: + return "invalid device identificator"; + case AdcDeviceNetServerSessionError::ERROR_NO_DEVICE_ID: + return "invalid formatted message: device identificator is omitted"; + case AdcDeviceNetServerSessionError::ERROR_NO_PROTO_KWDNAME: + return "invalid formatted message: keyword name is omitted"; + case AdcDeviceNetServerSessionError::ERROR_NO_PROTO_CMDNAME: + return "invalid formatted message: command name is omitted"; + case AdcDeviceNetServerSessionError::ERROR_NO_PROTO_ATTRNAME: + return "invalid formatted message: attribute name is omitted"; + case AdcDeviceNetServerSessionError::ERROR_NO_PROTO_ATTRVALUE: + return "invalid formatted message: attribute value is omitted"; + case AdcDeviceNetServerSessionError::ERROR_UNKNOWN_ERROR: + return "catch unhandled exception"; + default: + return "UNKNOWN"; + } + } + + static const AdcDeviceNetServerSessionErrorCategory& get() + { + static const AdcDeviceNetServerSessionErrorCategory constInst; + return constInst; + } +}; + +} // namespace adc + +namespace std +{ + +inline std::error_code make_error_code(adc::AdcDeviceNetServerSessionError ec) +{ + return std::error_code(static_cast(ec), adc::AdcDeviceNetServerSessionErrorCategory::get()); +} + +} // namespace std + + + +namespace adc +{ + + class AdcDeviceNetServer : public AdcGenericNetServer { @@ -16,14 +112,22 @@ protected: class DeviceWrapper { - std::function _get_id = []() -> serialized_t { throw std::system_error(); }; - std::function)> _get_attr = [](auto) -> serialized_t { - throw std::system_error(); + public: + using char_range_t = std::span; + + private: + serialized_t _id; + + // std::function _get_id = []() -> serialized_t { throw std::system_error(); }; + std::function _get_attr = [](auto) -> serialized_t { + throw std::system_error(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_NULL_DEVICE)); }; - std::function, std::span)> _set_attr = [](auto, auto) { - throw std::system_error(); + std::function _set_attr = [](auto, auto) { + throw std::system_error(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_NULL_DEVICE)); + }; + std::function _exec_cmd = [](auto) { + throw std::system_error(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_NULL_DEVICE)); }; - std::function)> _exec_cmd = [](auto) { throw std::system_error(); }; public: DeviceWrapper() { @@ -31,21 +135,22 @@ protected: }; template , + // typename IdSerialT = traits::adc_char_identity, typename AttrIdDeserialT = traits::adc_char_identity, typename CmdIdDeserialT = traits::adc_char_identity> DeviceWrapper(DeviceT* dev_ptr, - IdSerialT&& id_ser_func = {}, // serializer of device ID + const serialized_t& id, AttrIdDeserialT&& attr_id_deser_func = {}, CmdIdDeserialT&& cmd_id_deser_func = {}) + : _id(id) { - _get_id = [dev_ptr, wrapper = traits::adc_pf_wrapper(std::forward(id_ser_func))]() { - auto id = std::get<0>(wrapper)(dev_ptr->ident()); - return serialized_t{id.begin(), id.end()}; - }; + // _get_id = [dev_ptr, wrapper = traits::adc_pf_wrapper(std::forward(id_ser_func))]() { + // auto id = std::get<0>(wrapper)(dev_ptr->ident()); + // return serialized_t{id.begin(), id.end()}; + // }; _get_attr = [dev_ptr, wrapper = traits::adc_pf_wrapper(std::forward(attr_id_deser_func))]( - std::span attr_name) mutable { + const char_range_t& attr_name) mutable { auto attr_id = std::get<0>(wrapper)(attr_name); auto val = (*dev_ptr)[attr_id].serialize(); using val_t = std::remove_cvref_t; @@ -59,13 +164,13 @@ protected: }; _set_attr = [dev_ptr, wrapper = traits::adc_pf_wrapper(std::forward(attr_id_deser_func))]( - std::span attr_name, std::span val) mutable { + const char_range_t& attr_name, const char_range_t& val) mutable { auto attr_id = std::get<0>(wrapper)(attr_name); (*dev_ptr)[attr_id].deserialize(val); }; _exec_cmd = [dev_ptr, wrapper = traits::adc_pf_wrapper(cmd_id_deser_func)]( - std::span cmd_name) mutable { + const char_range_t& cmd_name) mutable { auto cmd_id = std::get<0>(wrapper)(cmd_name); (*dev_ptr)(cmd_id); }; @@ -74,20 +179,21 @@ protected: serialized_t ident() const { - return _get_id(); + // return _get_id(); + return _id; } - serialized_t getAttr(std::span attr_name) + serialized_t getAttr(const char_range_t& attr_name) { return _get_attr(attr_name); } - void setAttr(std::span attr_name, std::span val) + void setAttr(const char_range_t& attr_name, const char_range_t& val) { _set_attr(attr_name, val); } - void exec(std::span cmd_name) + void exec(const char_range_t& cmd_name) { _exec_cmd(cmd_name); } @@ -138,7 +244,7 @@ public: AdcDeviceProtoMessage dev_msg(*msg_sptr); - handleMessage(dev_msg); + processMessage(dev_msg); _netService.asyncSend( *msg_sptr, @@ -169,17 +275,31 @@ public: std::chrono::duration _recvTimeout = std::chrono::seconds(3600); std::chrono::duration _sendTimeout = std::chrono::seconds(5); - void handleMessage(auto& msg) + void processMessage(auto& msg) { + typedef std::vector attr_vec_t; + + attr_vec_t attrs; + + auto get_elem = [&attrs](size_t idx) -> DeviceWrapper::char_range_t { + if (idx < attrs.size()) { + auto& el = attrs[idx]; + return DeviceWrapper::char_range_t{el.begin(), el.end()}; + } else { + return DeviceWrapper::char_range_t{}; + } + }; + try { if (msg.isACK()) { msg.ack(); } else if (msg.isDEVICE()) { - auto dev_name = msg.attrs(1, 1); - if (dev_name.size()) { + attrs = msg.template attrs(0, 1); + if (attrs.size()) { + auto dev_name = get_elem(0); bool found = false; for (auto& [ptr, dev_wr] : _serverPtr->_devices) { - if (dev_wr.ident() == dev_name[0]) { + if (dev_wr.ident() == dev_name) { _bindDevice = dev_wr; found = true; break; @@ -188,19 +308,50 @@ public: if (found) { msg.ack(); } else { - msg.err(std::error_code{}); + msg.err(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_UNKNOWN_DEVICE_ID)); } } else { - msg.err(std::error_code{}); + msg.err(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_NO_DEVICE_ID)); } } else if (msg.isNAMES()) { + std::vector names; + for (auto& [ptr, dev_wr] : _serverPtr->_devices) { + names.emplace_back(dev_wr.ident()); + } + msg.ack(names); } else if (msg.isHELLO()) { - } else if (msg.isGET()) { + } else if (msg.isGET()) { // get attribute value + attrs = msg.template attrs(0, 1); + if (attrs.size()) { + auto val = _bindDevice.getAttr(get_elem(0)); + msg.ack(get_elem(0), {val.begin(), val.end()}); + } else { // no attr name! + msg.err(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_NO_PROTO_ATTRNAME)); + } } else if (msg.isSET()) { + attrs = msg.template attrs(0); + if (attrs.size() >= 2) { + auto val = msg.template joinAttrs(1); + _bindDevice.setAttr(get_elem(0), val); + msg.ack(get_elem(0), serialized_t{val.begin(), val.end()}); + } else { // no attr name or its value! + if (attrs.size() == 1) { + msg.err(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_NO_PROTO_ATTRVALUE)); + } else { + msg.err(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_NO_PROTO_ATTRNAME)); + } + } } else if (msg.isCMD()) { - msg.ack(); + attrs = msg.template attrs(0, 1); + if (attrs.size()) { + auto cmd_name = get_elem(0); + _bindDevice.exec(cmd_name); + msg.ack(cmd_name); + } else { // no cmd name! + msg.err(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_NO_PROTO_CMDNAME)); + } } else { - msg.err(std::error_code{}); // !!!!!!!!!!!! + msg.err(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_UNKNOWN_PROTO_KWD)); } } catch (std::system_error ex) { msg.err(ex.code()); @@ -221,9 +372,11 @@ public: CmdIdDeserialT&& cmd_id_deser_func = {}) // deserializer of command ID { auto id = std::forward(id_ser_func)(dev_ptr->ident()); - _devices.try_emplace(dev_ptr, dev_ptr, std::forward(id_ser_func), - std::forward(attr_id_deser_func), + _devices.try_emplace(dev_ptr, dev_ptr, id, std::forward(attr_id_deser_func), std::forward(cmd_id_deser_func)); + // _devices.try_emplace(dev_ptr, dev_ptr, std::forward(id_ser_func), + // std::forward(attr_id_deser_func), + // std::forward(cmd_id_deser_func)); } template diff --git a/net/adc_netserver.h b/net/adc_netserver.h index d9de1d5..6d481da 100644 --- a/net/adc_netserver.h +++ b/net/adc_netserver.h @@ -97,6 +97,39 @@ public: virtual ~AdcNetSessionManager() = default; protected: + AdcNetSessionManager() = default; + + AdcNetSessionManager(const AdcNetSessionManager&) = delete; + AdcNetSessionManager(AdcNetSessionManager&& other) + { + if (this == &other) { + return; + } + + for (auto& func : _moveCtorFunc) { + func(this); + } + + _stopSessionFunc = std::move(other._stopSessionFunc); + _moveCtorFunc = std::move(other._moveCtorFunc); + } + + AdcNetSessionManager& operator=(const AdcNetSessionManager&) = delete; + AdcNetSessionManager& operator=(AdcNetSessionManager&& other) + { + if (this != &other) { + for (auto& func : _moveCtorFunc) { + func(this); + } + + _stopSessionFunc = std::move(other._stopSessionFunc); + _moveCtorFunc = std::move(other._moveCtorFunc); + } + + return *this; + }; + + template constexpr static bool anySessionPredicate(const typename SessionT::netsession_ident_t&) { @@ -107,6 +140,7 @@ protected: template static std::unordered_map>> _serverSessions; std::vector> _stopSessionFunc; + std::vector> _moveCtorFunc; template void startSession(std::shared_ptr& sess_ptr) @@ -115,16 +149,24 @@ protected: if (res.second) { sess_ptr.start(); - _stopSessionFunc.emplace_back([res]() { + _stopSessionFunc.emplace_back([res, this]() { if (!res.first.expired()) { // session is still existing auto sess = res.first.lock(); sess->stop(); + _serverSessions[this].erase(res.first); return true; } else { return false; } }); } + + // define move-function only once per SessionT! + if (_serverSessions[this].size() == 1) { + _moveCtorFunc.emplace_back([this](const AdcNetSessionManager* new_instance) { + _serverSessions[new_instance] = std::move(_serverSessions[this]); + }); + } } @@ -134,20 +176,31 @@ protected: { size_t N = 0; + std::set> remove_wptr; + for (auto& wptr : _serverSessions[this]) { if (std::shared_ptr sptr = wptr.lock()) { if constexpr (std::same_as)>) { sptr->stop(); + remove_wptr.emplace(wptr); ++N; } else { if (std::forward(comp_func)(sptr->ident())) { sptr->stop(); + remove_wptr.emplace(wptr); ++N; } } + } else { // remove already stopped sessions?!! + remove_wptr.emplace(wptr); } } + for (auto& wptr : remove_wptr) { + _serverSessions[this].erase(wptr); + } + + return N; } @@ -161,6 +214,7 @@ protected: } _stopSessionFunc.clear(); + _moveCtorFunc.clear(); // there are nothing to move after stopping of all sessions return N; } @@ -172,7 +226,6 @@ protected: class AdcGenericNetServer : public AdcPosixGenericDaemon, public AdcNetSessionManager { -protected: public: typedef std::string server_ident_t; @@ -186,8 +239,46 @@ public: } } + + AdcGenericNetServer(const AdcGenericNetServer&) = delete; + AdcGenericNetServer(AdcGenericNetServer&& other) + : AdcPosixGenericDaemon(std::move(other)), AdcNetSessionManager(std::move(other)) + { + if (this == &other) { + return; + } + + _serverIdent = std::move(other._serverIdent); + _stopListenFunc = std::move(other._stopListenFunc); + + for (auto& func : other._moveCtorFunc) { + func(this); + } + + _moveCtorFunc = std::move(other._moveCtorFunc); + } + virtual ~AdcGenericNetServer() = default; + AdcGenericNetServer& operator=(const AdcGenericNetServer&) = delete; + AdcGenericNetServer& operator=(AdcGenericNetServer&& other) + { + if (this != &other) { + AdcPosixGenericDaemon::operator=(std::move(other)); + AdcNetSessionManager::operator=(std::move(other)); + + _serverIdent = std::move(other._serverIdent); + _stopListenFunc = std::move(other._stopListenFunc); + + for (auto& func : other._moveCtorFunc) { + func(this); + } + + _moveCtorFunc = std::move(other._moveCtorFunc); + } + + return *this; + } virtual server_ident_t ident() const { @@ -214,6 +305,13 @@ public: doAccept(acceptor, id, sess_ctx); } + + // only once per SessionT + if (_isListening[this].size() == 1) { + _moveCtorFunc = [this](const AdcGenericNetServer* new_instance) { + _isListening[new_instance] = std::move(_isListening[this]); + }; + } }; @@ -247,6 +345,7 @@ protected: _isListening{}; std::vector> _stopListenFunc; + std::vector> _moveCtorFunc; server_ident_t _serverIdent;