#pragma once #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 { class AdcDeviceNetServer : public AdcGenericNetServer { protected: typedef std::vector serialized_t; class DeviceWrapper { 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 _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() { // null device }; 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_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))]( 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; if constexpr (std::same_as || std::convertible_to) { return val; } else { // !!!!!!!! TODO: val_t must be a char range return serialized_t{val.begin(), val.end()}; } }; _set_attr = [dev_ptr, wrapper = traits::adc_pf_wrapper(std::forward(attr_id_deser_func))]( 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)]( const char_range_t& 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 char_range_t& attr_name) { return _get_attr(attr_name); } void setAttr(const char_range_t& attr_name, const char_range_t& val) { _set_attr(attr_name, val); } void exec(const char_range_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 std::string netsession_ident_t; typedef NetServiceT netservice_t; typedef AdcDeviceNetServer* netsession_ctx_t; typedef std::vector message_t; template Session(R&& id, netservice_t srv, AdcDeviceNetServer* srv_ptr) : _ident(), _netService(std::move(srv)), _serverPtr(srv_ptr), _bindDevice(_serverPtr->_devices.size() ? _serverPtr->_devices[0] : AdcDeviceNetServer::nullDevice) { if constexpr (std::is_array_v>) { _ident = id; } else { _ident = netsession_ident_t(id.begin(), id.end()); } } netsession_ident_t ident() const { return _ident; } void start() { _netService.asyncReceive( [this](std::error_code ec, message_t msg) { if (ec) { stop(); } else { auto msg_sptr = std::make_shared(std::move(msg)); AdcDeviceProtoMessage dev_msg(*msg_sptr); processMessage(dev_msg); _netService.asyncSend( *msg_sptr, [msg_sptr, this](std::error_code ec) { if (ec) { stop(); } else { start(); } }, _sendTimeout); } }, _recvTimeout); } void stop() { _netService.close(); } protected: netsession_ident_t _ident; netservice_t _netService; AdcDeviceNetServer* _serverPtr; AdcDeviceNetServer::DeviceWrapper& _bindDevice; std::chrono::duration _recvTimeout = std::chrono::seconds(3600); std::chrono::duration _sendTimeout = std::chrono::seconds(5); 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()) { 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(); } 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(names); } else if (msg.isHELLO()) { } 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), serialized_t{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()) { 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::make_error_code(AdcDeviceNetServerSessionError::ERROR_UNKNOWN_PROTO_KWD)); } } catch (std::system_error ex) { msg.err(ex.code()); } } }; using AdcGenericNetServer::AdcGenericNetServer; 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()); _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)); return *this; } template AdcDeviceNetServer& delDevice(DeviceT* dev_ptr) { _devices.erase(dev_ptr); return *this; } }; } // namespace adc