Compare commits

...

6 Commits

Author SHA1 Message Date
c6ee8f6c5b ... 2026-08-11 17:00:41 +03:00
4491f27c68 ... 2026-08-11 12:42:16 +03:00
60dbba7a5f /// 2026-08-07 17:22:08 +03:00
Timur A. Fatkhullin
176022d490 ... 2026-08-05 23:10:56 +03:00
13b22b0d1c ... 2026-07-29 17:52:34 +03:00
b90a59c3ed ... 2026-07-28 19:12:54 +03:00
4 changed files with 519 additions and 0 deletions

View File

@@ -45,6 +45,11 @@ if(ADCLIB_BUILD_EXAM)
target_link_libraries(device_exam PRIVATE ${PROJECT_NAME}) target_link_libraries(device_exam PRIVATE ${PROJECT_NAME})
endif() endif()
if(ADCLIB_BUILD_EXAM)
add_executable(netmsg_exam examples/netmsg_exam.cpp)
target_link_libraries(netmsg_exam PRIVATE ${PROJECT_NAME})
endif()
include(GNUInstallDirs) include(GNUInstallDirs)
install( install(
TARGETS ${PROJECT_NAME} TARGETS ${PROJECT_NAME}

31
examples/netmsg_exam.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include <print>
#include <adclib/adclib_netmsg.h>
int main()
{
adc::AdcNetworkMessage msg;
std::println("is ACK: {}", msg.isACK());
msg.createSET("ATTR1", snplib::snplib_serialization_params_t{}, 10, std::vector<char>{'V', 'A', 'L'});
std::println("KEY: <{}>", msg.key());
std::println("VALUE: <{}>", msg.value());
std::println("is SET: {}", msg.isSET());
std::println("is VALID: {}", msg.isValid());
std::string str{"SET ATTRQ 32.235 dwejh kd2jl"};
msg.fromCharRange(str);
std::println("KEY: <{}>", msg.key());
std::println("VALUE: <{}>", msg.value());
return 0;
}

View File

@@ -0,0 +1,304 @@
#pragma once
/****************************************************************************************
* ABSTRACT DEVICE COMPONENTS LIBRARY *
****************************************************************************************/
#include <concepts>
#include <format>
#include <functional>
#include <string>
#include <system_error>
#include <type_traits>
#include <utility>
#include <snipplib/concepts/snplib_traits.h>
#include <snipplib/containers/snplib_hmap.h>
#include <snipplib/serialization/snplib_serialization.h>
#include "adclib_common.h"
namespace adc
{
enum class AdcDeviceErrorCode : int {
ERROR_OK,
ERROR_NO_CMD_ID,
ERROR_NO_ATTR_ID,
ERROR_RO_ATTR,
ERROR_WO_ATTR,
ERROR_ATTR_RANGE,
ERROR_SERIALIZATION,
ERROR_DESERIALIZATION,
ERROR_UNKNOWN
};
} // namespace adc
namespace std
{
template <>
class is_error_code_enum<adc::AdcDeviceErrorCode> : public true_type
{
};
} // namespace std
namespace adc
{
// error category
struct AdcDeviceErrorCategory : std::error_category {
const char* name() const noexcept
{
return "ADCLIB-DEVICE-ERR-CATEGORY";
}
std::string message(int ec) const
{
AdcDeviceErrorCode err = static_cast<AdcDeviceErrorCode>(ec);
switch (err) {
case AdcDeviceErrorCode::ERROR_OK:
return "OK";
case AdcDeviceErrorCode::ERROR_NO_CMD_ID:
return "invalid command ID";
case AdcDeviceErrorCode::ERROR_NO_ATTR_ID:
return "invalid attribute ID";
case AdcDeviceErrorCode::ERROR_RO_ATTR:
return "read-only attribute";
case AdcDeviceErrorCode::ERROR_WO_ATTR:
return "write-only attribute";
case AdcDeviceErrorCode::ERROR_ATTR_RANGE:
return "value is out of attribute range ";
case AdcDeviceErrorCode::ERROR_SERIALIZATION:
return "serialization error";
case AdcDeviceErrorCode::ERROR_DESERIALIZATION:
return "deserialization error";
default:
return "UNKNOWN";
}
}
static const AdcDeviceErrorCategory& get()
{
static const AdcDeviceErrorCategory constInst;
return constInst;
}
};
inline std::error_code make_error_code(AdcDeviceErrorCode ec)
{
return std::error_code(static_cast<int>(ec), AdcDeviceErrorCategory::get());
}
template <typename T>
concept adclib_command_exec_t = snplib::snplib_callable_c<T> && (snplib::snplib_func_traits_t<T>::arity == 0) &&
std::same_as<adc_error_t, typename snplib::snplib_func_traits_t<T>::ret_t>;
template <typename T>
concept adclib_attr_getter_c = std::same_as<T, std::nullptr_t> || requires(T t) {
{ t() } -> adc_result_c;
};
template <typename T>
concept adclib_attr_setter_c =
std::same_as<T, std::nullptr_t> || (snplib::snplib_callable_c<T> && (snplib::snplib_func_traits_t<T>::arity >= 1) &&
std::same_as<adc_error_t, typename snplib::snplib_func_traits_t<T>::ret_t>);
// deduce value type from getter and setter
template <adclib_attr_getter_c GT, adclib_attr_setter_c ST>
using adclib_attr_value_deduced_t = std::conditional_t<
std::is_null_pointer_v<GT>,
std::conditional_t<std::is_null_pointer_v<ST>, void, std::remove_cvref_t<snplib::snplib_func_arg1_t<ST>>>,
typename std::invoke_result_t<GT>::value_type>;
template <adclib_attr_getter_c GT, adclib_attr_setter_c ST>
struct adclib_is_deduced_void_t {
static constexpr bool value = std::is_void_v<adclib_attr_value_deduced_t<GT, ST>>;
};
// conversional function signature:
// "from": adc_result_t<UT> func(const adc_result_t<VT>&)
// "to": adc_result_t<VT> func(const adc_result_t<UT>&)
template <typename T, typename VT>
concept adclib_attr_conv_from_func_c =
std::same_as<T, std::nullptr_t> ||
(std::invocable<T, const adc_result_t<VT>&> && !std::is_void_v<std::invoke_result_t<T, const adc_result_t<VT>&>>);
template <typename T, typename VT>
concept adclib_attr_conv_to_func_c = std::same_as<T, std::nullptr_t> ||
(snplib::snplib_callable_c<T> && (snplib::snplib_func_traits_t<T>::arity == 1) &&
std::same_as<adc_result_t<VT>, typename snplib::snplib_func_traits_t<T>::ret_t>);
template <typename VT, adclib_attr_conv_from_func_c<VT> CONV_FROM_T, adclib_attr_conv_to_func_c<VT> CONV_TO_T>
using adclib_attr_user_deduced_t =
std::conditional_t<std::is_null_pointer_v<CONV_FROM_T>,
std::conditional_t<std::is_null_pointer_v<CONV_TO_T>,
void,
std::remove_cvref_t<snplib::snplib_func_arg1_t<CONV_TO_T>>>,
typename snplib::snplib_func_traits_t<CONV_FROM_T>::ret_t>;
template <std::formattable<char> DEV_ID_T = std::string,
std::formattable<char> ATTR_ID_T = std::string,
std::formattable<char> CMD_ID_T = std::string>
class AdcGenericDevice
{
protected:
template <typename VT>
using attr_getter_t = std::function<adc_result_t<VT>()>;
template <typename VT>
using attr_setter_t = std::function<adc_error_t(VT const&)>;
typedef std::vector<char> serialized_t;
typedef std::function<adc_error_t(serialized_t&)> attr_serial_func_t;
typedef std::function<adc_error_t(serialized_t const&)> attr_deserial_func_t;
struct attr_t {
attr_t(AdcGenericDevice& dev, ATTR_ID_T id) : _dev(dev), _id(std::move(id)) {}
template <typename VT>
operator adc_result_t<VT>() const
{
auto getter = _dev._attrGetter.template get<attr_getter_t<VT>>(_id);
if (getter) {
if (getter.value()) {
return getter.value()();
} else {
return std::unexpected(AdcDeviceErrorCode::ERROR_WO_ATTR);
}
}
if (getter.error() == snplib::HeterogenMap<ATTR_ID_T>::ERROR_NO_ELEM) {
return std::unexpected(AdcDeviceErrorCode::ERROR_NO_ATTR_ID);
}
// fallback ...
return std::unexpected(AdcDeviceErrorCode::ERROR_UNKNOWN);
}
template <typename VT>
adc_error_t operator=(VT&& val)
{
auto setter = _dev._attrSetter.template get<attr_setter_t<std::remove_cvref_t<VT>>>(_id);
if (setter) {
if (setter.value()) {
return setter.value()(std::forward<VT>(val));
} else {
return AdcDeviceErrorCode::ERROR_WO_ATTR;
}
}
if (setter.error() == snplib::HeterogenMap<ATTR_ID_T>::ERROR_NO_ELEM) {
return AdcDeviceErrorCode::ERROR_NO_ATTR_ID;
}
// fallback ...
return AdcDeviceErrorCode::ERROR_UNKNOWN;
}
private:
AdcGenericDevice& _dev;
ATTR_ID_T _id;
};
DEV_ID_T _devId;
std::unordered_map<CMD_ID_T, std::function<adc_error_t()>> _commands{};
std::unordered_map<ATTR_ID_T, attr_t> _attrs{};
snplib::HeterogenMap<ATTR_ID_T> _attrGetter{};
snplib::HeterogenMap<ATTR_ID_T> _attrSetter{};
public:
static constexpr std::tuple<bool,
char,
short,
int,
long,
long long,
unsigned char,
unsigned short,
unsigned int,
unsigned long,
unsigned long long,
float,
double,
long double>
arithmetic_types{};
typedef DEV_ID_T device_id_t;
typedef ATTR_ID_T attr_id_t;
typedef CMD_ID_T cmd_id_t;
enum AttrAccessType { ATTR_ACCESS_RW, ATTR_ACCESS_RO, ATTR_ACCESS_WO };
AdcGenericDevice(device_id_t id) : _devId(id) {}
virtual ~AdcGenericDevice() = default;
device_id_t id() const
{
return _devId;
}
// template <snplib::snplib_callable_c ET>
// void addCommand(CMD_ID_T id, ET&& exec_func)
// {
// _commands.emplace(std::move(id), std::forward<ET>(exec_func));
// }
template <adclib_command_exec_t ET>
void addCommand(CMD_ID_T id, ET&& exec_func)
{
_commands.emplace(std::move(id), std::forward<ET>(exec_func));
}
template <typename GT, typename ST, typename... VTs>
void addAttr(ATTR_ID_T id, GT&& getter, ST&& setter, std::tuple<VTs...> = std::tuple<>{})
{
using v_t = adclib_attr_value_deduced_t<GT, ST>;
static_assert(!std::is_void_v<v_t>, "Getter and setter cannot be nullptr_t at the same time!");
if constexpr (!std::is_null_pointer_v<GT> && !std::is_null_pointer_v<ST>) {
static_assert(std::invocable<ST, v_t const&>, "Invalid setter argument type!");
}
_attrGetter.push(id, attr_getter_t<std::remove_cvref_t<GT>>(std::forward<GT>(getter)),
std::tuple<attr_getter_t<VTs>...>{});
_attrSetter.push(id, attr_setter_t<std::remove_cvref_t<ST>>(std::forward<ST>(setter)),
std::tuple<attr_setter_t<VTs>...>{});
}
template <adclib_attr_getter_c GT, adclib_attr_setter_c ST, typename... FuncTs>
requires(sizeof...(FuncTs) > 1)
void addAttr(ATTR_ID_T id, GT&& getter, ST&& setter, FuncTs&&... cnv_pairs)
{
static constexpr size_t NFUNCS = sizeof...(FuncTs);
static_assert(NFUNCS % 2 == 0, "Number of conversional functions must be an even!");
using v_t = adclib_attr_value_deduced_t<GT, ST>;
static_assert(!std::is_void_v<v_t>, "Getter and setter cannot be nullptr_t at the same time!");
}
};
} // namespace adc

View File

@@ -0,0 +1,179 @@
#pragma once
#include <snipplib/utils/snplib_keyvalue_expr.h>
namespace adc
{
/* */
static constexpr std::string_view ADCLIB_NETMSG_KEYWORD_PARAM_DELIM{" "};
static constexpr std::string_view ADCLIB_NETMSG_PARAM_PARAM_DELIM{" "};
/* network message valid keywords */
//
static constexpr std::string_view ADCLIB_NETMSG_KEYWORD_ACK{"ACK"};
// error message
static constexpr std::string_view ADCLIB_NETMSG_KEYWORD_ERR{"ERR"};
// set device attribute value
static constexpr std::string_view ADCLIB_NETMSG_KEYWORD_SET{"SET"};
// get device attribute value
static constexpr std::string_view ADCLIB_NETMSG_KEYWORD_GET{"GET"};
// execute device command
static constexpr std::string_view ADCLIB_NETMSG_KEYWORD_CMD{"CMD"};
static constexpr std::array ADCLIB_NETMSG_VALID_KEYWORDS{ADCLIB_NETMSG_KEYWORD_ACK, ADCLIB_NETMSG_KEYWORD_ERR,
ADCLIB_NETMSG_KEYWORD_SET, ADCLIB_NETMSG_KEYWORD_GET,
ADCLIB_NETMSG_KEYWORD_CMD};
template <snplib::snplib_char_range_c CharRangeT = std::vector<char>>
class AdcNetworkMessage final : protected snplib::snplib_keyvalue_expr_t<CharRangeT>
{
typedef snplib::snplib_keyvalue_expr_t<CharRangeT> base_t;
public:
using typename base_t::error_t;
using base_t::isValid;
using base_t::key;
using base_t::value;
using base_t::valueSeq;
AdcNetworkMessage() = default;
bool isACK() const
{
return std::ranges::equal(this->_key, ADCLIB_NETMSG_KEYWORD_ACK);
}
bool isERR() const
{
return std::ranges::equal(this->_key, ADCLIB_NETMSG_KEYWORD_ERR);
}
bool isCMD() const
{
return std::ranges::equal(this->_key, ADCLIB_NETMSG_KEYWORD_CMD);
}
bool isSET() const
{
return std::ranges::equal(this->_key, ADCLIB_NETMSG_KEYWORD_SET);
}
bool isGET() const
{
return std::ranges::equal(this->_key, ADCLIB_NETMSG_KEYWORD_GET);
}
error_t fromCharRange(snplib::snplib_input_char_range_c auto&& bytes)
requires(!std::same_as<CharRangeT, std::remove_cvref_t<decltype(bytes)>>)
{
auto err = fromCharRange(
std::forward<decltype(bytes)>(bytes),
{.keyval_delim = ADCLIB_NETMSG_KEYWORD_PARAM_DELIM, .valseq_delim = ADCLIB_NETMSG_PARAM_PARAM_DELIM});
if (err) {
return err;
}
if (!std::ranges::contains(ADCLIB_NETMSG_VALID_KEYWORDS, key())) {
this->_isValid = false;
// return an error here!!!
}
return {};
}
error_t fromCharRange(CharRangeT& bytes)
{
auto err = base_t::fromCharRange(bytes, {.keyval_delim = ADCLIB_NETMSG_KEYWORD_PARAM_DELIM,
.valseq_delim = ADCLIB_NETMSG_PARAM_PARAM_DELIM});
if (err) {
return err;
}
if (!std::ranges::contains(ADCLIB_NETMSG_VALID_KEYWORDS, key())) {
this->_isValid = false;
// return an error here!!!
}
return {};
}
error_t createACK()
{
return this->setKey(ADCLIB_NETMSG_KEYWORD_ACK);
}
error_t createERR(error_t err)
{
return this->setKeyValue(ADCLIB_NETMSG_KEYWORD_ERR, err.value());
}
template <snplib::snplib_serializable_c ATTR_NAME_T, typename... ArgTs>
requires(sizeof...(ArgTs) > 0)
error_t createSET(ATTR_NAME_T&& attr_name, ArgTs&&... args)
{
this->_isValid = false;
auto err = this->setKeyValue(ADCLIB_NETMSG_KEYWORD_SET, std::forward<ATTR_NAME_T>(attr_name));
if (err) {
return err;
}
err = this->appendValue(std::forward<ArgTs>(args)...);
if (err) {
return err;
}
this->_isValid = true;
return err;
}
template <snplib::snplib_serializable_c ATTR_NAME_T, typename... ArgTs>
error_t createGET(ATTR_NAME_T&& attr_name, ArgTs&&... args)
{
this->_isValid = false;
auto err = this->setKeyValue(ADCLIB_NETMSG_KEYWORD_GET, std::forward<ATTR_NAME_T>(attr_name));
if (err) {
return err;
}
this->_isValid = true;
if constexpr (sizeof...(ArgTs)) {
err = this->appendValue(std::forward<ArgTs>(args)...);
if (err) {
this->_isValid = false;
}
}
return err;
}
template <snplib::snplib_serializable_c CMD_NAME_T>
error_t createCMD(CMD_NAME_T&& cmd_name)
{
this->_isValid = false;
auto err = this->setKeyValue(ADCLIB_NETMSG_KEYWORD_CMD, std::forward<CMD_NAME_T>(cmd_name));
if (!err) {
this->_isValid = true;
}
return err;
}
};
} // namespace adc