#pragma once #include #include #include #include "adc_device_netmsg.h" #include "adc_netserver.h" #include "../device/adc_device_concepts.h" 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 { template > class AdcDeviceNetServer : public AdcGenericNetServer { public: using typename AdcGenericNetServer::server_ident_t; using typename AdcGenericNetServer::logger_t; // type for serialized data (attr/command ID, attr values etc...) typedef std::vector serialized_t; protected: class DeviceWrapper { public: // using char_range_t = std::span; 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 _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)); }; public: DeviceWrapper() : _id() { // null device std::string_view id{"NULL-DEVICE"}; std::ranges::copy(id, std::back_inserter(_id)); }; template , typename AttrIdDeserialT = traits::adc_char_identity, typename CmdIdDeserialT = traits::adc_char_identity> DeviceWrapper(DeviceT* dev_ptr, const serialized_t& id, AttrIdDeserialT&& attr_id_deser_func = {}, CmdIdDeserialT&& cmd_id_deser_func = {}) : _id(id) { _get_attr = [dev_ptr, wrapper = traits::adc_pf_wrapper(std::forward(attr_id_deser_func)), this](const auto& attr_name) mutable { auto attr_id = std::get<0>(wrapper)(attr_name); auto& attr = (*dev_ptr)[attr_id]; // auto& attr = dev_ptr->operator[](attr_id); auto val = attr.serialize(); return val; }; _set_attr = [dev_ptr, wrapper = traits::adc_pf_wrapper(std::forward(attr_id_deser_func))]( const auto& attr_name, const auto& 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(std::forward(cmd_id_deser_func))]( const auto& cmd_name) mutable { auto cmd_id = std::get<0>(wrapper)(cmd_name); (*dev_ptr)(cmd_id); }; } serialized_t ident() const { // return _get_id(); return _id; } serialized_t getAttr(const serialized_t& attr_name) { return _get_attr(attr_name); } void setAttr(const serialized_t& attr_name, const serialized_t& val) { _set_attr(attr_name, val); } void exec(const serialized_t& cmd_name) { _exec_cmd(cmd_name); } }; static inline DeviceWrapper nullDevice{}; std::unordered_map _devices; public: template class Session : public std::enable_shared_from_this> { public: typedef SessionIdentT netsession_ident_t; typedef NetServiceT netservice_t; // typedef AdcDeviceNetServer* netsession_ctx_t; struct netsession_ctx_t { AdcDeviceNetServer* serverPtr; std::chrono::milliseconds recvTimeout; std::chrono::milliseconds sendTimeout; }; typedef std::vector message_t; // Session(const netsession_ident_t& id, netservice_t srv, AdcDeviceNetServer* srv_ptr) Session(const netsession_ident_t& id, netservice_t srv, netsession_ctx_t ctx) : _ident(id), _netService(std::move(srv)), // _serverPtr(srv_ptr), // _bindDevice(srv_ptr->_devices.size() ? &srv_ptr->_devices.begin()->second // : &AdcDeviceNetServer::nullDevice) _serverPtr(ctx.serverPtr), _bindDevice(ctx.serverPtr->_devices.size() ? &ctx.serverPtr->_devices.begin()->second : &AdcDeviceNetServer::nullDevice), _recvTimeout(ctx.recvTimeout), _sendTimeout(ctx.sendTimeout) { _serverPtr->logInfo("Create client session with ID = {} (addr = {}, thread = {})", _ident, (void*)this, utils::AdcThisThreadId()); } virtual ~Session() { _serverPtr->logInfo("Delete client session with ID = {} (addr = {}, thread = {})", _ident, (void*)this, utils::AdcThisThreadId()); } netsession_ident_t ident() const { return _ident; } void start() { _serverPtr->logInfo("Start client session with ID = {} (addr = {}, thread = {})", _ident, (void*)this, utils::AdcThisThreadId()); do_start(); } void stop() { _serverPtr->logInfo("Stop client session with ID = {} (addr = {}, thread = {})", _ident, (void*)this, utils::AdcThisThreadId()); _netService.close(); } protected: netsession_ident_t _ident; netservice_t _netService; AdcDeviceNetServer* _serverPtr; AdcDeviceNetServer::DeviceWrapper* _bindDevice; std::chrono::milliseconds _recvTimeout = std::chrono::hours(12); std::chrono::milliseconds _sendTimeout = std::chrono::seconds(5); void do_start() { auto self(this->shared_from_this()); _netService.asyncReceive( [self, this](netservice_t::async_callback_err_t ec, message_t msg) { if (ec) { _serverPtr->logError( "asyncReceive operation completed with error: {} (session addr = {}, thread = {})", netservice_t::formattableError(ec), (void*)this, utils::AdcThisThreadId()); stop(); } else { auto msg_sptr = std::make_shared(std::move(msg)); AdcDeviceProtoMessage dev_msg(*msg_sptr); processMessage(dev_msg); _netService.asyncSend( *msg_sptr, [self, msg_sptr, this](netservice_t::async_callback_err_t ec) { if (ec) { _serverPtr->logError( "asyncSend operation completed with error: {} (session addr = {}, thread = {})", netservice_t::formattableError(ec), (void*)this, utils::AdcThisThreadId()); stop(); } else { do_start(); } }, _sendTimeout); } }, _recvTimeout); } void processMessage(auto& msg) { typedef std::decay_t msg_t; typedef std::vector attr_vec_t; attr_vec_t attrs; auto get_elem = [&attrs](size_t idx) { if (idx < attrs.size()) { return attrs[idx]; } else { return serialized_t(); } }; try { if (msg.isACK()) { msg.ack(); } else if (msg.isDEVICE()) { 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 (std::ranges::equal(dev_wr.ident(), dev_name)) { _bindDevice = &dev_wr; found = true; break; } } if (found) { // msg.ack(msg_t::DEVICE_KEY, attrs); msg.ack(constants::ADC_DEVICE_NETPROTO_KEY_DEVICE, attrs); } else { msg.err(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_UNKNOWN_DEVICE_ID)); } } else { 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(msg_t::NAMES_KEY, names); msg.ack(constants::ADC_DEVICE_NETPROTO_KEY_NAMES, names); } else if (msg.isHELLO()) { // msg.ack(msg_t::HELLO_KEY, _serverPtr->_serverIdent); msg.ack(constants::ADC_DEVICE_NETPROTO_KEY_HELLO, _serverPtr->_serverIdent); } 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(msg_t::GET_KEY, get_elem(0), val); msg.ack(constants::ADC_DEVICE_NETPROTO_KEY_GET, get_elem(0), val); } 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(msg_t::SET_KEY, get_elem(0), val); msg.ack(constants::ADC_DEVICE_NETPROTO_KEY_SET, get_elem(0), val); } 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()) { attrs = msg.template attrs(0, 1); if (attrs.size()) { auto cmd_name = get_elem(0); _bindDevice->exec(cmd_name); // msg.ack(msg_t::CMD_KEY, cmd_name); msg.ack(constants::ADC_DEVICE_NETPROTO_KEY_CMD, cmd_name); } else { // no cmd name! msg.err(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_NO_PROTO_CMDNAME)); } } else { msg.err(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_UNKNOWN_PROTO_KWD)); } } catch (std::system_error ex) { msg.err(ex.code()); } } }; using AdcGenericNetServer::AdcGenericNetServer; // AdcDeviceNetServer(const server_ident_t& id) : AdcGenericNetServer(id), _devices() {} virtual ~AdcDeviceNetServer() = default; template , typename AttrIdDeserialT = traits::adc_char_identity, typename CmdIdDeserialT = traits::adc_char_identity> AdcDeviceNetServer& addDevice(DeviceT* dev_ptr, IdSerialT&& id_ser_func = {}, // serializer of device ID AttrIdDeserialT&& attr_id_deser_func = {}, // deserializer of attribute ID CmdIdDeserialT&& cmd_id_deser_func = {}) // deserializer of command ID { auto id = std::forward(id_ser_func)(dev_ptr->ident()); if constexpr (traits::formattable) { this->logInfo("Add ADC device with ID: {} (addr = {}, thread = {})", id, (void*)dev_ptr, utils::AdcThisThreadId()); } else { std::string s; std::ranges::copy(id, std::back_inserter(s)); this->logInfo("Add ADC device with ID: {} (addr = {}, thread = {})", s, (void*)dev_ptr, utils::AdcThisThreadId()); } _devices.try_emplace(dev_ptr, dev_ptr, id, std::forward(attr_id_deser_func), std::forward(cmd_id_deser_func)); return *this; } template AdcDeviceNetServer& delDevice(DeviceT* dev_ptr) { auto it = _devices.find(dev_ptr); if (it == _devices.end()) { this->logError("Invalid ADC device pointer ({})! It seems the device was not added!", (void*)dev_ptr); return *this; } if constexpr (traits::formattable) { this->logInfo("Delete ADC device with ID: {} (addr = {}, thread = {})", it->ident(), (void*)dev_ptr, utils::AdcThisThreadId()); } else { std::string s; std::ranges::copy(it->ident(), std::back_inserter(s)); this->logInfo("Delete ADC device with ID: {} (addr = {}, thread = {})", s, (void*)dev_ptr, utils::AdcThisThreadId()); } _devices.erase(it); // _devices.erase(dev_ptr); return *this; } }; } // namespace adc