ADC/net/adc_device_netserver.h
Timur A. Fatkhullin 117d8c4a3d ...
2024-10-20 01:29:08 +03:00

237 lines
8.5 KiB
C++

#pragma once
#include "adc_device_netmsg.h"
#include "adc_netserver.h"
#include "../device/adc_device_concepts.h"
namespace adc
{
class AdcDeviceNetServer : public AdcGenericNetServer
{
protected:
typedef std::vector<char> serialized_t;
class DeviceWrapper
{
std::function<serialized_t()> _get_id = []() -> serialized_t { throw std::system_error(); };
std::function<serialized_t(std::span<const char>)> _get_attr = [](auto) -> serialized_t {
throw std::system_error();
};
std::function<void(std::span<const char>, std::span<const char>)> _set_attr = [](auto, auto) {
throw std::system_error();
};
std::function<void(std::span<const char>)> _exec_cmd = [](auto) { throw std::system_error(); };
public:
DeviceWrapper() {
// null device
};
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,
IdSerialT&& id_ser_func = {}, // serializer of device ID
AttrIdDeserialT&& attr_id_deser_func = {},
CmdIdDeserialT&& cmd_id_deser_func = {})
{
_get_id = [dev_ptr, wrapper = traits::adc_pf_wrapper(std::forward<IdSerialT>(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<AttrIdDeserialT>(attr_id_deser_func))](
std::span<const char> 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<decltype(val)>;
if constexpr (std::same_as<serialized_t, val_t> || std::convertible_to<val_t, serialized_t>) {
return val;
} else {
// !!!!!!!! TODO: val_t maust be a char range
return serialized_t{val.begin(), val.end()};
}
};
_set_attr = [dev_ptr, wrapper = traits::adc_pf_wrapper(std::forward<AttrIdDeserialT>(attr_id_deser_func))](
std::span<const char> attr_name, std::span<const char> 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<CmdIdDeserialT>(cmd_id_deser_func)](
std::span<const char> cmd_name) mutable {
auto cmd_id = std::get<0>(wrapper)(cmd_name);
(*dev_ptr)(cmd_id);
};
}
serialized_t ident() const
{
return _get_id();
}
serialized_t getAttr(std::span<const char> attr_name)
{
return _get_attr(attr_name);
}
void setAttr(std::span<const char> attr_name, std::span<const char> val)
{
_set_attr(attr_name, val);
}
void exec(std::span<const char> cmd_name)
{
_exec_cmd(cmd_name);
}
};
static DeviceWrapper nullDevice;
std::unordered_map<void*, DeviceWrapper> _devices;
public:
template <interfaces::adc_netservice_c NetServiceT>
class Session : std::enable_shared_from_this<Session<NetServiceT>>
{
public:
typedef std::string netsession_ident_t;
typedef NetServiceT netservice_t;
typedef AdcDeviceNetServer* netsession_ctx_t;
typedef std::vector<char> message_t;
template <traits::adc_input_char_range R>
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<std::remove_cvref_t<R>>) {
_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<message_t>(std::move(msg));
AdcDeviceProtoMessage dev_msg(*msg_sptr);
handleMessage(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<size_t> _recvTimeout = std::chrono::seconds(3600);
std::chrono::duration<size_t> _sendTimeout = std::chrono::seconds(5);
void handleMessage(auto& msg)
{
try {
if (msg.isACK()) {
msg.ack();
} else if (msg.isDEVICE()) {
auto dev_name = msg.attrs(1, 1);
if (dev_name.size()) {
bool found = false;
for (auto& [ptr, dev_wr] : _serverPtr->_devices) {
if (dev_wr.ident() == dev_name[0]) {
_bindDevice = dev_wr;
found = true;
break;
}
}
if (found) {
msg.ack();
} else {
msg.err(std::error_code{});
}
} else {
msg.err(std::error_code{});
}
} else if (msg.isNAMES()) {
} else if (msg.isHELLO()) {
} else if (msg.isGET()) {
} else if (msg.isSET()) {
} else if (msg.isCMD()) {
msg.ack();
} else {
msg.err(std::error_code{}); // !!!!!!!!!!!!
}
} catch (std::system_error ex) {
msg.err(ex.code());
}
}
};
using AdcGenericNetServer::AdcGenericNetServer;
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>>
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, std::forward<IdSerialT>(id_ser_func),
std::forward<AttrIdDeserialT>(attr_id_deser_func),
std::forward<CmdIdDeserialT>(cmd_id_deser_func));
}
template <interfaces::adc_device_c DeviceT>
AdcDeviceNetServer& delDevice(DeviceT* dev_ptr)
{
_devices.erase(dev_ptr);
}
};
} // namespace adc