add AdcDeviceNetClient class
This commit is contained in:
parent
f3a6aa3571
commit
ae6fbf18ca
@ -57,6 +57,7 @@ set(ADC_NETWORK_HEADERS
|
||||
net/adc_net_concepts.h
|
||||
net/adc_device_netmsg.h
|
||||
net/adc_device_netserver.h
|
||||
net/adc_device_netclient.h
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -4,6 +4,82 @@
|
||||
#include "adc_device_netmsg.h"
|
||||
#include "adc_netclient.h"
|
||||
|
||||
|
||||
namespace adc
|
||||
{
|
||||
|
||||
enum class AdcDeviceNetClientSessionError : int {
|
||||
ERROR_OK,
|
||||
ERROR_INVALID_SERVER_RESPOND,
|
||||
ERROR_UNEXPECTED_SERVER_RESPOND,
|
||||
ERROR_UNKNOWN_ERROR
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
// place here to allow clang compilation
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
class is_error_code_enum<adc::AdcDeviceNetClientSessionError> : public true_type
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace adc
|
||||
{
|
||||
|
||||
|
||||
struct AdcDeviceNetClientSessionErrorCategory : std::error_category {
|
||||
AdcDeviceNetClientSessionErrorCategory() : std::error_category() {}
|
||||
|
||||
const char* name() const noexcept
|
||||
{
|
||||
return "ADC_DEVICE_NESERVER_SESSION";
|
||||
}
|
||||
|
||||
std::string message(int ec) const
|
||||
{
|
||||
AdcDeviceNetClientSessionError err = static_cast<AdcDeviceNetClientSessionError>(ec);
|
||||
|
||||
switch (err) {
|
||||
case AdcDeviceNetClientSessionError::ERROR_OK:
|
||||
return "OK";
|
||||
case AdcDeviceNetClientSessionError::ERROR_INVALID_SERVER_RESPOND:
|
||||
return "invalid server respond message";
|
||||
case AdcDeviceNetClientSessionError::ERROR_UNEXPECTED_SERVER_RESPOND:
|
||||
return "unexpected server respond message";
|
||||
case AdcDeviceNetClientSessionError::ERROR_UNKNOWN_ERROR:
|
||||
return "catch unhandled exception";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const AdcDeviceNetClientSessionErrorCategory& get()
|
||||
{
|
||||
static const AdcDeviceNetClientSessionErrorCategory constInst;
|
||||
return constInst;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace adc
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
inline std::error_code make_error_code(adc::AdcDeviceNetClientSessionError ec)
|
||||
{
|
||||
return std::error_code(static_cast<int>(ec), adc::AdcDeviceNetClientSessionErrorCategory::get());
|
||||
}
|
||||
|
||||
} // namespace std
|
||||
|
||||
|
||||
|
||||
namespace adc
|
||||
{
|
||||
|
||||
@ -17,6 +93,8 @@ public:
|
||||
class Session : public std::enable_shared_from_this<Session<NetServiceT, SessionIdentT>>
|
||||
{
|
||||
public:
|
||||
enum ServerResponseType { RESP_INVALID, RESP_ACK, RESP_ERROR, RESP_UNEXPECTED };
|
||||
|
||||
typedef SessionIdentT netsession_ident_t;
|
||||
typedef NetServiceT netservice_t;
|
||||
|
||||
@ -67,6 +145,125 @@ public:
|
||||
}
|
||||
|
||||
|
||||
// ADC device helper methods (blocking)
|
||||
|
||||
typedef std::vector<std::string> default_server_resp_t;
|
||||
|
||||
// get names of devices
|
||||
template <traits::adc_range_of_output_char_range R>
|
||||
R deviceNames(ServerResponseType& rtype)
|
||||
{
|
||||
typename netservice_t::send_msg_t bytes;
|
||||
|
||||
AdcDeviceProtoMessage msg(bytes);
|
||||
msg.names();
|
||||
using m_t = std::remove_cvref_t<decltype(msg)>;
|
||||
|
||||
// expected respond: ACK NAMES DEV1 DEV2 ...
|
||||
// return DEV1 DEV2 ... (or error description 'code category what')
|
||||
return _getFromServer<R>(m_t::NAMES_KEY, bytes, rtype);
|
||||
}
|
||||
|
||||
default_server_resp_t deviceNames(ServerResponseType& rtype)
|
||||
{
|
||||
return deviceNames<default_server_resp_t>(rtype);
|
||||
}
|
||||
|
||||
// bind device to the client
|
||||
template <traits::adc_range_of_output_char_range R, traits::adc_input_char_range DevNameT>
|
||||
R bindDevice(DevNameT&& dev_name, ServerResponseType& rtype)
|
||||
{
|
||||
typename netservice_t::send_msg_t bytes;
|
||||
|
||||
AdcDeviceProtoMessage msg(bytes);
|
||||
msg.device(std::forward<DevNameT>(dev_name));
|
||||
using m_t = std::remove_cvref_t<decltype(msg)>;
|
||||
|
||||
// expected respond: ACK DEVICE DEV_NAME
|
||||
// return DEV_NAME ... (or error description 'code category what')
|
||||
return _getFromServer<R>(m_t::DEVICE_KEY, bytes, rtype);
|
||||
}
|
||||
|
||||
template <traits::adc_input_char_range DevNameT>
|
||||
default_server_resp_t bindDevice(DevNameT&& dev_name, ServerResponseType& rtype)
|
||||
{
|
||||
return bindDevice<default_server_resp_t>(std::forward<DevNameT>(dev_name), rtype);
|
||||
}
|
||||
|
||||
|
||||
// execute a command
|
||||
template <traits::adc_range_of_output_char_range R, traits::adc_input_char_range CmdNameT>
|
||||
R exec(CmdNameT&& cmd_name, ServerResponseType& rtype)
|
||||
{
|
||||
typename netservice_t::send_msg_t bytes;
|
||||
|
||||
AdcDeviceProtoMessage msg(bytes);
|
||||
msg.cmd(std::forward<CmdNameT>(cmd_name));
|
||||
using m_t = std::remove_cvref_t<decltype(msg)>;
|
||||
|
||||
|
||||
// expected respond: ACK CMD CMD_NAME
|
||||
// return CMD_NAME ... (or error description 'code category what')
|
||||
return _getFromServer<R>(m_t::CMD_KEY, bytes, rtype);
|
||||
}
|
||||
|
||||
template <traits::adc_input_char_range CmdNameT>
|
||||
default_server_resp_t exec(CmdNameT&& cmd_name, ServerResponseType& rtype)
|
||||
{
|
||||
return exec<default_server_resp_t>(std::forward<CmdNameT>(cmd_name), rtype);
|
||||
}
|
||||
|
||||
|
||||
// get an attribute value
|
||||
template <traits::adc_range_of_output_char_range R, traits::adc_input_char_range AttrNameT>
|
||||
R getAttr(AttrNameT&& attr_name, ServerResponseType& rtype)
|
||||
{
|
||||
typename netservice_t::send_msg_t bytes;
|
||||
|
||||
AdcDeviceProtoMessage msg(bytes);
|
||||
msg.get(std::forward<AttrNameT>(attr_name));
|
||||
using m_t = std::remove_cvref_t<decltype(msg)>;
|
||||
|
||||
// expected respond: ACK GET ATTR_NAME ATTR_VALUE
|
||||
// return ATTR_NAME ATTR_VALUE (or error description 'code category what')
|
||||
return _getFromServer<R>(m_t::GET_KEY, bytes, rtype);
|
||||
}
|
||||
|
||||
template <traits::adc_input_char_range AttrNameT>
|
||||
default_server_resp_t getAttr(AttrNameT&& attr_name, ServerResponseType& rtype)
|
||||
{
|
||||
return getAttr<default_server_resp_t>(std::forward<AttrNameT>(attr_name), rtype);
|
||||
}
|
||||
|
||||
|
||||
// set an attribute value
|
||||
template <traits::adc_range_of_output_char_range R,
|
||||
traits::adc_input_char_range AttrNameT,
|
||||
typename ValueT,
|
||||
typename... ValueTs>
|
||||
R setAttr(AttrNameT&& attr_name, ServerResponseType& rtype, ValueT&& value, ValueTs&&... values)
|
||||
{
|
||||
typename netservice_t::send_msg_t bytes;
|
||||
|
||||
AdcDeviceProtoMessage msg(bytes);
|
||||
msg.set(std::forward<AttrNameT>(attr_name), std::forward<ValueT>(value), std::forward<ValueTs>(values)...);
|
||||
using m_t = std::remove_cvref_t<decltype(msg)>;
|
||||
|
||||
// expected respond: ACK SET ATTR_NAME ATTR_VALUE
|
||||
// return ATTR_NAME ATTR_VALUE (or error description 'code category what')
|
||||
return _getFromServer<R>(m_t::SET_KEY, bytes, rtype);
|
||||
}
|
||||
|
||||
template <traits::adc_input_char_range AttrNameT, typename ValueT, typename... ValueTs>
|
||||
default_server_resp_t setAttr(AttrNameT&& attr_name,
|
||||
ServerResponseType& rtype,
|
||||
ValueT&& value,
|
||||
ValueTs&&... values)
|
||||
{
|
||||
return setAttr<default_server_resp_t>(std::forward<AttrNameT>(attr_name), rtype,
|
||||
std::forward<ValueT>(value), std::forward<ValueTs>(values)...);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
netsession_ident_t _ident;
|
||||
@ -76,29 +273,60 @@ public:
|
||||
std::chrono::milliseconds _recvTimeout = std::chrono::seconds(5);
|
||||
std::chrono::milliseconds _sendTimeout = std::chrono::seconds(5);
|
||||
|
||||
// main 'run' method
|
||||
|
||||
virtual void run() = 0;
|
||||
|
||||
// helper methods
|
||||
std::function<void(typename netservice_t::async_callback_err_t,
|
||||
typename netservice_t::async_receive_callback_t)>
|
||||
_defaultRecvCallback = [this](auto err, auto msg) {
|
||||
if (err) {
|
||||
_clientPtr->logError("An error occured while receiving server respond: {}",
|
||||
netservice_t::formattableError(err));
|
||||
} else {
|
||||
AdcDeviceProtoMessage dev_msg(msg);
|
||||
|
||||
if (!dev_msg.isValid()) {
|
||||
_clientPtr->logError("Invalid server respond");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev_msg.isACK()) {
|
||||
} else if (dev_msg.isERROR()) {
|
||||
} else {
|
||||
_clientPtr->logError("Unexpectable server respond");
|
||||
}
|
||||
template <traits::adc_range_of_output_char_range R>
|
||||
R _getFromServer(std::string_view key, const typename netservice_t::send_msg_t& bytes, ServerResponseType& type)
|
||||
{
|
||||
auto rbytes = sendRecv(bytes);
|
||||
AdcDeviceProtoMessage dev_msg(rbytes);
|
||||
|
||||
if (!dev_msg.isValid()) {
|
||||
throw std::system_error(AdcDeviceNetClientSessionError::ERROR_INVALID_SERVER_RESPOND);
|
||||
}
|
||||
|
||||
if (dev_msg.isACK(key)) {
|
||||
type = RESP_ACK;
|
||||
return dev_msg.template attrs<R>(1);
|
||||
} else if (dev_msg.isERROR()) {
|
||||
type = RESP_ERROR;
|
||||
return dev_msg.template attrs<R>();
|
||||
} else {
|
||||
throw std::system_error(AdcDeviceNetClientSessionError::ERROR_UNEXPECTED_SERVER_RESPOND);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _sendRecvCallbackWrapper(auto err,
|
||||
typename netservice_t::recv_msg_t msg,
|
||||
auto&& ack_func,
|
||||
auto&& error_func)
|
||||
{
|
||||
if (err) {
|
||||
_clientPtr->logError("An error occured while receiving server respond: {}",
|
||||
netservice_t::formattableError(err));
|
||||
} else {
|
||||
AdcDeviceProtoMessage dev_msg(msg);
|
||||
|
||||
if (!dev_msg.isValid()) {
|
||||
_clientPtr->logError("Invalid server respond");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if (dev_msg.isACK()) {
|
||||
std::forward<decltype(ack_func)>(ack_func)(dev_msg.attrs());
|
||||
} else if (dev_msg.isERROR()) {
|
||||
std::forward<decltype(error_func)>(error_func)(dev_msg.attrs());
|
||||
} else {
|
||||
_clientPtr->logError("Unexpectable server respond (msg key: {})", dev_msg.key());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <traits::adc_input_char_range SendMsgT, typename TokenT>
|
||||
auto asyncSendRecv(const SendMsgT& send_msg, TokenT&& token)
|
||||
@ -115,6 +343,14 @@ public:
|
||||
},
|
||||
_sendTimeout);
|
||||
}
|
||||
|
||||
template <traits::adc_input_char_range SendMsgT>
|
||||
auto sendRecv(const SendMsgT& send_msg)
|
||||
{
|
||||
_netService.send(send_msg, _sendTimeout);
|
||||
return _netService.receive(_recvTimeout);
|
||||
}
|
||||
|
||||
}; // end of 'Session' class declaration
|
||||
|
||||
using base_t::base_t;
|
||||
|
||||
@ -369,6 +369,17 @@ public:
|
||||
return isKey(ACK_KEY_IDX);
|
||||
}
|
||||
|
||||
// ACK KEY_NAME ...
|
||||
bool isACK(std::string_view key) const
|
||||
{
|
||||
auto ats = attrs();
|
||||
if (std::ranges::size(ats)) {
|
||||
return isKey(ACK_KEY_IDX) && (key == *ats.begin());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isSET() const
|
||||
{
|
||||
return isKey(SET_KEY_IDX);
|
||||
|
||||
@ -52,15 +52,15 @@ using adc_common_duration_t = adc_duration_common_type_t<std::chrono::nanosecond
|
||||
// a) true - asynchronous operation completed without errors
|
||||
// b) false - an error occured
|
||||
template <typename ERRT>
|
||||
concept adc_async_callback_err_t = std::convertible_to<std::remove_cvref_t<ERRT>, bool> ||
|
||||
concept adc_async_callback_err_c = std::convertible_to<std::remove_cvref_t<ERRT>, bool> ||
|
||||
requires(const std::remove_cvref_t<ERRT> err) { err.operator bool(); };
|
||||
|
||||
// concepts for asynchronous opereration callback callable
|
||||
// 1) the type must be a callable with at least 1 input argument
|
||||
// 2) the first argument type must satisfy the concept adc_async_callback_err_t
|
||||
// 2) the first argument type must satisfy the concept adc_async_callback_err_c
|
||||
template <typename T>
|
||||
concept adc_async_callback_t = traits::adc_is_callable<T> && (traits::adc_func_traits<T>::arity >= 1) &&
|
||||
adc_async_callback_err_t<traits::adc_func_arg1_t<T>>;
|
||||
concept adc_async_callback_c = traits::adc_is_callable<T> && (traits::adc_func_traits<T>::arity >= 1) &&
|
||||
adc_async_callback_err_c<traits::adc_func_arg1_t<T>>;
|
||||
|
||||
/*
|
||||
struct NetService {
|
||||
@ -107,17 +107,17 @@ concept adc_netservice_c = requires(SRVT srv, const SRVT srv_const) {
|
||||
// underlying protocol
|
||||
|
||||
// asynchronous operation error
|
||||
requires adc_async_callback_err_t<typename SRVT::async_callback_err_t>;
|
||||
requires adc_async_callback_err_c<typename SRVT::async_callback_err_t>;
|
||||
|
||||
// callback callables for asynchronous operations
|
||||
requires adc_async_callback_t<typename SRVT::async_connect_callback_t>;
|
||||
requires adc_async_callback_t<typename SRVT::async_send_callback_t>;
|
||||
requires adc_async_callback_t<typename SRVT::async_receive_callback_t>;
|
||||
requires adc_async_callback_c<typename SRVT::async_connect_callback_t>;
|
||||
requires adc_async_callback_c<typename SRVT::async_send_callback_t>;
|
||||
requires adc_async_callback_c<typename SRVT::async_receive_callback_t>;
|
||||
|
||||
|
||||
// acceptor type
|
||||
requires std::is_class_v<typename SRVT::acceptor_t>;
|
||||
requires adc_async_callback_t<typename SRVT::acceptor_t::async_accept_callback_t>;
|
||||
requires adc_async_callback_c<typename SRVT::acceptor_t::async_accept_callback_t>;
|
||||
requires requires(typename SRVT::acceptor_t acc, const typename SRVT::acceptor_t acc_const) {
|
||||
acc.asyncAccept(std::declval<typename SRVT::acceptor_t::async_accept_callback_t>(),
|
||||
std::declval<const typename SRVT::timeout_t&>());
|
||||
@ -136,9 +136,6 @@ concept adc_netservice_c = requires(SRVT srv, const SRVT srv_const) {
|
||||
|
||||
|
||||
// asynchronous (non-blocking) operations
|
||||
// srv.asyncAccept(std::declval<const typename SRVT::endpoint_t&>(), std::declval<typename
|
||||
// SRVT::async_call_ctx_t&>(),
|
||||
// std::declval<const typename SRVT::timeout_t&>());
|
||||
|
||||
srv.asyncConnect(std::declval<const typename SRVT::endpoint_t&>(),
|
||||
std::declval<typename SRVT::async_connect_callback_t>(),
|
||||
@ -152,7 +149,7 @@ concept adc_netservice_c = requires(SRVT srv, const SRVT srv_const) {
|
||||
std::declval<const typename SRVT::timeout_t&>());
|
||||
|
||||
// synchronous (blocking) operations
|
||||
// srv.accept(std::declval<const typename SRVT::endpoint_t&>(), std::declval<const typename SRVT::timeout_t&>());
|
||||
// it is assumed these methods throw an exception if error occures
|
||||
|
||||
srv.connect(std::declval<const typename SRVT::endpoint_t&>(), std::declval<const typename SRVT::timeout_t&>());
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user