411 lines
15 KiB
C++
411 lines
15 KiB
C++
#pragma once
|
|
|
|
#include <memory>
|
|
#include <system_error>
|
|
|
|
#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<adc::AdcDeviceNetServerSessionError> : 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<AdcDeviceNetServerSessionError>(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<int>(ec), adc::AdcDeviceNetServerSessionErrorCategory::get());
|
|
}
|
|
|
|
} // namespace std
|
|
|
|
|
|
|
|
namespace adc
|
|
{
|
|
|
|
|
|
template <typename IdentT = std::string>
|
|
class AdcDeviceNetServer : public AdcGenericNetServer<IdentT>
|
|
{
|
|
public:
|
|
using typename AdcGenericNetServer<IdentT>::server_ident_t;
|
|
|
|
// type for serialized data (attr/command ID, attr values etc...)
|
|
typedef std::vector<char> serialized_t;
|
|
|
|
protected:
|
|
class DeviceWrapper
|
|
{
|
|
public:
|
|
// using char_range_t = std::span<const char>;
|
|
using char_range_t = std::span<char>;
|
|
|
|
private:
|
|
serialized_t _id;
|
|
|
|
// std::function<serialized_t()> _get_id = []() -> serialized_t { throw std::system_error(); };
|
|
std::function<serialized_t(const serialized_t&)> _get_attr = [](auto) -> serialized_t {
|
|
throw std::system_error(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_NULL_DEVICE));
|
|
};
|
|
std::function<void(const serialized_t&, const serialized_t&)> _set_attr = [](auto, auto) {
|
|
throw std::system_error(std::make_error_code(AdcDeviceNetServerSessionError::ERROR_NULL_DEVICE));
|
|
};
|
|
std::function<void(const serialized_t&)> _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 <interfaces::adc_device_c DeviceT,
|
|
// typename IdSerialT = traits::adc_char_identity<typename DeviceT::ident_t>,
|
|
typename AttrIdDeserialT = traits::adc_char_identity<typename DeviceT::attr_ident_t>,
|
|
typename CmdIdDeserialT = traits::adc_char_identity<typename DeviceT::cmd_ident_t>>
|
|
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<AttrIdDeserialT>(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<AttrIdDeserialT>(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<CmdIdDeserialT>(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<void*, DeviceWrapper> _devices;
|
|
|
|
public:
|
|
template <interfaces::adc_netservice_c NetServiceT, traits::adc_hashable_c SessionIdentT = std::string>
|
|
class Session : public std::enable_shared_from_this<Session<NetServiceT, SessionIdentT>>
|
|
{
|
|
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<char> 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)
|
|
{
|
|
}
|
|
|
|
netsession_ident_t ident() const
|
|
{
|
|
return _ident;
|
|
}
|
|
|
|
void start()
|
|
{
|
|
auto self(this->shared_from_this());
|
|
|
|
_netService.asyncReceive(
|
|
[self, this](netservice_t::async_callback_err_t ec, message_t msg) {
|
|
if (ec) {
|
|
_serverPtr->errorMessage(std::string("asyncReceive operation completed with") +
|
|
netservice_t::formatError(ec));
|
|
stop();
|
|
} else {
|
|
auto msg_sptr = std::make_shared<message_t>(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->errorMessage(std::string("asyncSend operation completed with") +
|
|
netservice_t::formatError(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::milliseconds _recvTimeout = std::chrono::hours(12);
|
|
std::chrono::milliseconds _sendTimeout = std::chrono::seconds(5);
|
|
|
|
void processMessage(auto& msg)
|
|
{
|
|
typedef std::decay_t<decltype(msg)> msg_t;
|
|
|
|
typedef std::vector<serialized_t> 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<attr_vec_t>(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);
|
|
} 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<serialized_t> names;
|
|
for (auto& [ptr, dev_wr] : _serverPtr->_devices) {
|
|
names.emplace_back(dev_wr.ident());
|
|
}
|
|
msg.ack(msg_t::NAMES_KEY, names);
|
|
} else if (msg.isHELLO()) {
|
|
msg.ack(msg_t::HELLO_KEY, _serverPtr->_serverIdent);
|
|
} else if (msg.isGET()) { // get attribute value
|
|
attrs = msg.template attrs<attr_vec_t>(0, 1);
|
|
if (attrs.size()) {
|
|
auto val = _bindDevice->getAttr(get_elem(0));
|
|
msg.ack(msg_t::GET_KEY, 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<attr_vec_t>(0);
|
|
if (attrs.size() >= 2) {
|
|
auto val = msg.template joinAttrs<serialized_t>(1);
|
|
_bindDevice->setAttr(get_elem(0), val);
|
|
msg.ack(msg_t::SET_KEY, 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<attr_vec_t>(0, 1);
|
|
if (attrs.size()) {
|
|
auto cmd_name = get_elem(0);
|
|
_bindDevice->exec(cmd_name);
|
|
msg.ack(msg_t::CMD_KEY, 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<IdentT>(id), _devices() {}
|
|
|
|
virtual ~AdcDeviceNetServer() = default;
|
|
|
|
template <interfaces::adc_device_c DeviceT,
|
|
typename IdSerialT = traits::adc_char_identity<serialized_t>,
|
|
typename AttrIdDeserialT = traits::adc_char_identity<typename DeviceT::attr_ident_t>,
|
|
typename CmdIdDeserialT = traits::adc_char_identity<typename DeviceT::cmd_ident_t>>
|
|
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<IdSerialT>(id_ser_func)(dev_ptr->ident());
|
|
_devices.try_emplace(dev_ptr, dev_ptr, id, std::forward<AttrIdDeserialT>(attr_id_deser_func),
|
|
std::forward<CmdIdDeserialT>(cmd_id_deser_func));
|
|
// _devices.try_emplace(dev_ptr, dev_ptr, std::forward<IdSerialT>(id_ser_func),
|
|
// std::forward<AttrIdDeserialT>(attr_id_deser_func),
|
|
// std::forward<CmdIdDeserialT>(cmd_id_deser_func));
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <interfaces::adc_device_c DeviceT>
|
|
AdcDeviceNetServer& delDevice(DeviceT* dev_ptr)
|
|
{
|
|
_devices.erase(dev_ptr);
|
|
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
} // namespace adc
|