ADC/net/adc_device_netmsg.h
2024-10-29 17:36:16 +03:00

556 lines
16 KiB
C++

#pragma once
#include <cstring>
#include "../common/adc_utils.h"
// #include "adc_netmsg.h"
namespace adc
{
/* ABSTRACT DEVICE DEFAULT APPLICATION-LEVEL NETWORK PROTOCOL DEFINITIONS */
namespace constants
{
static constexpr char ADC_DEFAULT_KEY_VALUE_DELIMITER1[] = " ";
static constexpr char ADC_DEFAULT_VALUE_DELIMITER[] = " ";
static constexpr char ADC_DEFAULT_COMPOSITE_VALUE_DELIMITER[] = ",";
} // namespace constants
template <traits::adc_char_range ByteSeqT,
const char KEY_VALUE_DELIM[] = constants::ADC_DEFAULT_KEY_VALUE_DELIMITER1,
const char VALUE_DELIM[] = constants::ADC_DEFAULT_VALUE_DELIMITER,
const char COMPOSITE_VALUE_DELIM[] = constants::ADC_DEFAULT_COMPOSITE_VALUE_DELIMITER>
class AdcKeyValueMessage
{
public:
static constexpr size_t keyValueDelimiterSize = utils::AdcCharArrSize(KEY_VALUE_DELIM);
static constexpr std::span keyValueDelimiter{KEY_VALUE_DELIM, keyValueDelimiterSize};
static constexpr size_t valueDelimiterSize = utils::AdcCharArrSize(VALUE_DELIM);
static constexpr std::span valueDelimiter{VALUE_DELIM, valueDelimiterSize};
static constexpr size_t compValueDelimiterSize = utils::AdcCharArrSize(COMPOSITE_VALUE_DELIM);
static constexpr std::span compValueDelimiter{COMPOSITE_VALUE_DELIM, compValueDelimiterSize};
static_assert(keyValueDelimiterSize && valueDelimiterSize && compValueDelimiterSize,
"KEY-VALUE, VALUE-PARTS AND COMPOSITE-VALUE DELIMITERS MUST NOT BE AN EMPTY ARRAY!!!");
AdcKeyValueMessage(ByteSeqT& byte_seq) : _byteSequence(byte_seq) {}
virtual ~AdcKeyValueMessage() = default;
bool empty() const
{
return std::ranges::size(_byteSequence) == 0;
}
template <typename T>
requires(traits::adc_char_view<T> && std::ranges::contiguous_range<ByteSeqT>) ||
traits::adc_output_char_range<T>
auto key() const
{
if constexpr (traits::adc_char_view<T> && std::ranges::contiguous_range<ByteSeqT>) {
auto bs = utils::AdcTrimSpacesView<T>(_byteSequence, utils::AdcTrimType::TRIM_LEFT);
auto found = std::ranges::search(bs, keyValueDelimiter);
if (found.empty()) { // no value
return bs;
}
return utils::AdcTrimSpacesView<T>(std::span<const char>(bs.begin(), found.begin()),
utils::AdcTrimType::TRIM_RIGHT);
} else {
T res;
auto bs = _byteSequence | std::views::drop_while([](const auto& ch) { return ch = ' '; });
auto found = std::ranges::search(bs, keyValueDelimiter);
std::ranges::copy(bs.begin(), found.begin(), std::back_inserter(res));
// if (found.empty()) { // no value
// std::ranges::copy(bs, std::back_inserter(res));
// } else {
// std::ranges::copy(bs.begin(), found.begin(), std::back_inserter(res));
// }
return res;
}
}
auto key() const
{
if constexpr (std::ranges::contiguous_range<ByteSeqT>) {
return key<std::string_view>();
} else {
return key<std::string>();
}
}
template <typename T>
requires(traits::adc_char_view<T> && std::ranges::contiguous_range<ByteSeqT>) ||
traits::adc_output_char_range<T>
auto value() const
{
if constexpr (traits::adc_char_view<T> && std::ranges::contiguous_range<ByteSeqT>) {
auto bs = utils::AdcTrimSpacesView<T>(_byteSequence, utils::AdcTrimType::TRIM_LEFT);
auto found = std::ranges::search(bs, keyValueDelimiter);
return T(found.end(), bs.end());
} else {
T res;
auto bs = _byteSequence | std::views::drop_while([](const auto& ch) { return ch == ' '; });
auto found = std::ranges::search(bs, keyValueDelimiter);
if (!found.empty()) {
std::ranges::copy(found.end(), bs.end(), std::back_inserter(res));
}
return res;
}
}
auto value() const
{
if constexpr (std::ranges::contiguous_range<ByteSeqT>) {
return value<std::string_view>();
} else {
return value<std::string>();
}
}
template <typename T>
requires(std::ranges::contiguous_range<ByteSeqT> && traits::adc_char_view<std::ranges::range_value_t<T>>) ||
traits::adc_range_of_output_char_range<T>
auto valueParts(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
auto val = value<std::ranges::range_value_t<T>>();
return utils::AdcSplitCharRange<T>(val, valueDelimiter, start, N);
}
auto valueParts(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
if constexpr (std::ranges::contiguous_range<ByteSeqT>) {
return valueParts<std::vector<std::string_view>>(start, N);
} else {
return valueParts<std::vector<std::string>>(start, N);
}
}
template <typename T>
requires traits::adc_output_char_range<T> || traits::adc_char_view<T>
auto joinValueParts(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
T res;
if constexpr (std::ranges::contiguous_range<ByteSeqT>) {
auto pp = valueParts<std::vector<std::span<const char>>>(start, N);
if (pp.size()) {
res = T{pp.front().begin(), pp.back().end()};
}
} else {
utils::AdcJoinRange(valueParts<std::vector<std::vector<const char>>>(start, N), valueDelimiter, res);
}
return res;
}
auto joinValueParts(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
if constexpr (std::ranges::contiguous_range<ByteSeqT>) {
return joinValueParts<std::string_view>(start, N);
} else {
return joinValueParts<std::string>(start, N);
}
}
template <typename KeyT>
requires traits::adc_output_char_range<ByteSeqT>
AdcKeyValueMessage& setKey(const KeyT& key)
{
if (std::ranges::size(_byteSequence)) {
_byteSequence = ByteSeqT();
}
std::ranges::copy(utils::AdcDefaultValueConverter<COMPOSITE_VALUE_DELIM>::template serialize<ByteSeqT>(key),
std::back_inserter(_byteSequence));
return *this;
}
template <typename KeyT, typename... ValuePartTs>
requires traits::adc_output_char_range<ByteSeqT>
AdcKeyValueMessage& setKeyValue(const KeyT& key, ValuePartTs&&... values)
{
setKey(key);
if constexpr (sizeof...(ValuePartTs)) {
std::ranges::copy(keyValueDelimiter, std::back_inserter(_byteSequence));
setValueHelper(std::forward<ValuePartTs>(values)...);
}
return *this;
}
template <typename KeyT, std::ranges::input_range ValuePartRangeT>
requires traits::adc_output_char_range<ByteSeqT>
AdcKeyValueMessage& setKeyValue(const KeyT& key, const ValuePartRangeT& values)
{
setKey(key);
for (auto const& el : values) {
std::ranges::copy(keyValueDelimiter, std::back_inserter(_byteSequence));
setValueHelper(el);
}
return *this;
}
template <typename KeyT, traits::adc_tuple_like ValuePartTupleT>
requires traits::adc_output_char_range<ByteSeqT>
AdcKeyValueMessage& setKeyValue(const KeyT& key, const ValuePartTupleT& values)
{
setKey(key);
if constexpr (std::tuple_size_v<ValuePartTupleT>) {
std::apply([this]<typename... Ts>(Ts&&... args) { setValueHelper(std::forward<Ts>(args)...); }, values);
}
return *this;
}
template <typename... ValuePartTs>
requires traits::adc_output_char_range<ByteSeqT>
AdcKeyValueMessage& setValue(ValuePartTs&&... values)
{
std::vector<char> kw;
std::ranges::copy(key<std::span<const char>>(), std::back_inserter(kw));
return setKeyValue(kw, std::forward<ValuePartTs>(values)...);
}
template <typename KeyT, std::ranges::input_range ValuePartRangeT>
requires traits::adc_output_char_range<ByteSeqT> &&
(!traits::adc_input_char_range<std::ranges::range_value_t<ValuePartRangeT>>)
AdcKeyValueMessage& setValue(const ValuePartRangeT& values)
{
std::vector<char> kw;
std::ranges::copy(key<std::span<const char>>(), std::back_inserter(kw));
return setKeyValue(kw, values);
}
template <typename KeyT, traits::adc_tuple_like ValuePartTupleT>
requires traits::adc_output_char_range<ByteSeqT>
AdcKeyValueMessage& setValue(const ValuePartTupleT& values)
{
std::vector<char> kw;
std::ranges::copy(key<std::span<const char>>(), std::back_inserter(kw));
return setKeyValue(kw, values);
}
protected:
ByteSeqT& _byteSequence;
template <typename ValuePartT, typename... ValuePartTs>
void setValueHelper(ValuePartT&& value, ValuePartTs&&... values)
{
std::ranges::copy(utils::AdcDefaultValueConverter<COMPOSITE_VALUE_DELIM>::template serialize<ByteSeqT>(
std::forward<ValuePartT>(value)),
std::back_inserter(_byteSequence));
if constexpr (sizeof...(ValuePartTs)) {
std::ranges::copy(valueDelimiter, std::back_inserter(_byteSequence));
setValueHelper(std::forward<ValuePartTs>(values)...);
}
}
};
template <traits::adc_char_range ByteSeqT,
const char KEY_VALUE_DELIM[] = constants::ADC_DEFAULT_KEY_VALUE_DELIMITER1,
const char VALUE_DELIM[] = constants::ADC_DEFAULT_VALUE_DELIMITER,
const char COMPOSITE_VALUE_DELIM[] = constants::ADC_DEFAULT_COMPOSITE_VALUE_DELIMITER>
class AdcDeviceProtoMessage
: protected AdcKeyValueMessage<ByteSeqT, KEY_VALUE_DELIM, VALUE_DELIM, COMPOSITE_VALUE_DELIM>
{
using base_t = AdcKeyValueMessage<ByteSeqT, KEY_VALUE_DELIM, VALUE_DELIM, COMPOSITE_VALUE_DELIM>;
public:
static constexpr std::string_view ACK_KEY{"ACK"};
static constexpr std::string_view SET_KEY{"SET"};
static constexpr std::string_view GET_KEY{"GET"};
static constexpr std::string_view CMD_KEY{"CMD"};
static constexpr std::string_view ERR_KEY{"ERR"};
static constexpr std::string_view HELLO_KEY{"HELLO"};
static constexpr std::string_view DEVICE_KEY{"DEVICE"};
static constexpr std::string_view NAMES_KEY{"NAMES"};
static constexpr std::array VALID_KEY{ACK_KEY, SET_KEY, GET_KEY, CMD_KEY,
ERR_KEY, HELLO_KEY, DEVICE_KEY, NAMES_KEY};
typedef std::array<size_t, VALID_KEY.size()> keyword_hash_array_t;
enum KEY_IDX : size_t {
ACK_KEY_IDX,
SET_KEY_IDX,
GET_KEY_IDX,
CMD_KEY_IDX,
ERR_KEY_IDX,
HELLO_KEY_IDX,
DEVICE_KEY_IDX,
NAMES_KEY_IDX
};
private: // include here to allow clang compilation
size_t _keyHash = 0;
template <size_t... I>
static constexpr auto computeKeywordHashesImpl(std::index_sequence<I...>)
{
return keyword_hash_array_t{utils::AdcFNV1aHash(VALID_KEY[I])...};
}
template <typename Indices = std::make_index_sequence<VALID_KEY.size()>>
static constexpr auto computeKeywordHashes()
{
return computeKeywordHashesImpl(Indices{});
}
static constexpr keyword_hash_array_t KEY_HASHES = computeKeywordHashes();
auto keyHash()
{
return _keyHash = utils::AdcFNV1aHash(key());
}
bool isKey(size_t idx) const
{
return _keyHash == KEY_HASHES[idx];
}
public:
AdcDeviceProtoMessage(ByteSeqT& byte_seq) : base_t(byte_seq)
{
keyHash();
}
bool isValid() const
{
return std::ranges::any_of(KEY_HASHES, [this](size_t h) { return _keyHash == h; });
}
bool isACK() const
{
return isKey(ACK_KEY_IDX);
}
bool isSET() const
{
return isKey(SET_KEY_IDX);
}
bool isGET() const
{
return isKey(GET_KEY_IDX);
}
bool isCMD() const
{
return isKey(CMD_KEY_IDX);
}
bool isERR() const
{
return isKey(ERR_KEY_IDX);
}
bool isHELLO() const
{
return isKey(HELLO_KEY_IDX);
}
bool isDEVICE() const
{
return isKey(DEVICE_KEY_IDX);
}
bool isNAMES() const
{
return isKey(NAMES_KEY_IDX);
}
template <typename T>
auto key() const
{
return base_t::template key<T>();
}
auto key() const
{
return base_t::key();
}
template <typename T>
auto attrs(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
return base_t::template valueParts<T>(start, N);
}
auto attrs(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
return base_t::valueParts(start, N);
}
template <typename T>
auto joinAttrs(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
return base_t::template joinValueParts<T>(start, N);
}
auto joinAttrs(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
return base_t::joinValueParts(start, N);
}
void ack()
{
base_t::setKey(ACK_KEY);
keyHash();
}
template <typename ParT, typename... ParTs>
void ack(const ParT& param, const ParTs&... params)
{
base_t::setKeyValue(ACK_KEY, param, params...);
keyHash();
}
template <std::ranges::range ParT>
requires(!traits::adc_input_char_range<std::ranges::range_value_t<ParT>>)
void ack(const ParT& param)
{
base_t::setKeyValue(ACK_KEY, param);
keyHash();
}
template <traits::adc_tuple_like ParT>
void ack(const ParT& param)
{
base_t::setKeyValue(ACK_KEY, param);
keyHash();
}
template <traits::adc_input_char_range AttrNameT, typename ValueT, typename... ValueTs>
void set(AttrNameT&& attr_name, ValueT&& value, ValueTs&&... values)
{
base_t::setKeyValue(SET_KEY, std::forward<AttrNameT>(attr_name), std::forward<ValueT>(value),
std::forward<ValueTs>(values)...);
keyHash();
}
template <traits::adc_input_char_range AttrNameT>
void get(AttrNameT&& attr_name)
{
base_t::setKeyValue(GET_KEY, std::forward<AttrNameT>(attr_name));
keyHash();
}
template <traits::adc_input_char_range CmdNameT>
void cmd(CmdNameT&& cmd_name)
{
base_t::setKeyValue(CMD_KEY, std::forward<CmdNameT>(cmd_name));
keyHash();
}
void err(const std::error_code& ec)
{
base_t::setKeyValue(ERR_KEY, ec.value(), ec.category().name(), ec.message());
keyHash();
}
template <traits::adc_input_char_range SenderNameT, typename... ParamTs>
void hello(SenderNameT&& name, ParamTs&&... params)
{
base_t::setKeyValue(HELLO_KEY, std::forward<SenderNameT>(name), std::forward<ParamTs>(params)...);
keyHash();
}
template <traits::adc_input_char_range DevNameT>
void device(DevNameT&& dev_name)
{
base_t::setKeyValue(DEVICE_KEY, std::forward<DevNameT>(dev_name));
keyHash();
}
void names()
{
base_t::setKey(NAMES_KEY);
keyHash();
}
template <typename DevNameT, typename... DevNameTs>
void names(const DevNameT& dev_name, const DevNameTs&... dev_names)
{
base_t::setKeyValue(NAMES_KEY, dev_name, dev_names...);
keyHash();
}
template <std::ranges::input_range R>
requires(!traits::adc_input_char_range<std::ranges::range_value_t<R>>)
void names(const R& dev_names)
{
base_t::setKeyValue(NAMES_KEY, dev_names);
keyHash();
}
template <traits::adc_tuple_like T>
void names(const T& dev_names)
{
base_t::setKeyValue(NAMES_KEY, dev_names);
keyHash();
}
};
} // namespace adc