#pragma once #include #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 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; } size_t size() const { return std::ranges::size(_byteSequence); } template requires(traits::adc_char_view && std::ranges::contiguous_range) || traits::adc_output_char_range auto key() const { if constexpr (traits::adc_char_view && std::ranges::contiguous_range) { auto bs = utils::AdcTrimSpacesView(_byteSequence, utils::AdcTrimType::TRIM_LEFT); auto found = std::ranges::search(bs, keyValueDelimiter); if (found.empty()) { // no value return bs; } return utils::AdcTrimSpacesView(std::span(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) { return key(); } else { return key(); } } template requires(traits::adc_char_view && std::ranges::contiguous_range) || traits::adc_output_char_range auto value() const { if constexpr (traits::adc_char_view && std::ranges::contiguous_range) { auto bs = utils::AdcTrimSpacesView(_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) { return value(); } else { return value(); } } template requires(std::ranges::contiguous_range && traits::adc_char_view>) || traits::adc_range_of_output_char_range auto valueParts(size_t start = 0, size_t N = std::numeric_limits::max()) const { auto val = value>(); return utils::AdcSplitCharRange(val, valueDelimiter, start, N); } auto valueParts(size_t start = 0, size_t N = std::numeric_limits::max()) const { if constexpr (std::ranges::contiguous_range) { return valueParts>(start, N); } else { return valueParts>(start, N); } } template requires traits::adc_output_char_range || traits::adc_char_view auto joinValueParts(size_t start = 0, size_t N = std::numeric_limits::max()) const { T res; if constexpr (std::ranges::contiguous_range) { // auto pp = valueParts>>(start, N); auto pp = valueParts>>(start, N); if (pp.size()) { // res = T{pp.front().begin(), pp.back().end()}; res = T(pp.front().begin(), pp.back().end()); } } else { utils::AdcJoinRange(valueParts>>(start, N), valueDelimiter, res); } return res; } auto joinValueParts(size_t start = 0, size_t N = std::numeric_limits::max()) const { if constexpr (std::ranges::contiguous_range) { return joinValueParts(start, N); } else { return joinValueParts(start, N); } } template requires traits::adc_output_char_range AdcKeyValueMessage& setKey(const KeyT& key) { if (std::ranges::size(_byteSequence)) { _byteSequence = ByteSeqT(); } std::ranges::copy(utils::AdcDefaultValueConverter::template serialize(key), std::back_inserter(_byteSequence)); return *this; } template requires traits::adc_output_char_range AdcKeyValueMessage& setKeyValue(const KeyT& key, ValuePartTs&&... values) { setKey(key); if constexpr (sizeof...(ValuePartTs)) { std::ranges::copy(keyValueDelimiter, std::back_inserter(_byteSequence)); setValueHelper(std::forward(values)...); } return *this; } template requires traits::adc_output_char_range 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 requires traits::adc_output_char_range AdcKeyValueMessage& setKeyValue(const KeyT& key, const ValuePartTupleT& values) { setKey(key); if constexpr (std::tuple_size_v) { std::apply([this](Ts&&... args) { setValueHelper(std::forward(args)...); }, values); } return *this; } template requires traits::adc_output_char_range AdcKeyValueMessage& setValue(ValuePartTs&&... values) { std::vector kw; std::ranges::copy(key>(), std::back_inserter(kw)); return setKeyValue(kw, std::forward(values)...); } template requires traits::adc_output_char_range && (!traits::adc_input_char_range>) AdcKeyValueMessage& setValue(const ValuePartRangeT& values) { std::vector kw; std::ranges::copy(key>(), std::back_inserter(kw)); return setKeyValue(kw, values); } template requires traits::adc_output_char_range AdcKeyValueMessage& setValue(const ValuePartTupleT& values) { std::vector kw; std::ranges::copy(key>(), std::back_inserter(kw)); return setKeyValue(kw, values); } protected: ByteSeqT& _byteSequence; template void setValueHelper(ValuePartT&& value, ValuePartTs&&... values) { std::ranges::copy(utils::AdcDefaultValueConverter::template serialize( std::forward(value)), std::back_inserter(_byteSequence)); if constexpr (sizeof...(ValuePartTs)) { std::ranges::copy(valueDelimiter, std::back_inserter(_byteSequence)); setValueHelper(std::forward(values)...); } } }; template class AdcDeviceProtoMessage : protected AdcKeyValueMessage { using base_t = AdcKeyValueMessage; 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 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 static constexpr auto computeKeywordHashesImpl(std::index_sequence) { return keyword_hash_array_t{utils::AdcFNV1aHash(VALID_KEY[I])...}; } template > 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 auto key() const { return base_t::template key(); } auto key() const { return base_t::key(); } template auto attrs(size_t start = 0, size_t N = std::numeric_limits::max()) const { return base_t::template valueParts(start, N); } auto attrs(size_t start = 0, size_t N = std::numeric_limits::max()) const { return base_t::valueParts(start, N); } template auto joinAttrs(size_t start = 0, size_t N = std::numeric_limits::max()) const { return base_t::template joinValueParts(start, N); } auto joinAttrs(size_t start = 0, size_t N = std::numeric_limits::max()) const { return base_t::joinValueParts(start, N); } void ack() { base_t::setKey(ACK_KEY); keyHash(); } template void ack(const ParT& param, const ParTs&... params) { base_t::setKeyValue(ACK_KEY, param, params...); keyHash(); } template // requires(!traits::adc_input_char_range>) requires(!traits::adc_input_char_range) void ack(const ParT& param) { base_t::setKeyValue(ACK_KEY, param); keyHash(); } template void ack(const ParT& param) { base_t::setKeyValue(ACK_KEY, param); keyHash(); } template void set(AttrNameT&& attr_name, ValueT&& value, ValueTs&&... values) { base_t::setKeyValue(SET_KEY, std::forward(attr_name), std::forward(value), std::forward(values)...); keyHash(); } template void get(AttrNameT&& attr_name) { base_t::setKeyValue(GET_KEY, std::forward(attr_name)); keyHash(); } template void cmd(CmdNameT&& cmd_name) { base_t::setKeyValue(CMD_KEY, std::forward(cmd_name)); keyHash(); } void err(const std::error_code& ec) { base_t::setKeyValue(ERR_KEY, ec.value(), ec.category().name(), ec.message()); keyHash(); } template void hello(SenderNameT&& name, ParamTs&&... params) { base_t::setKeyValue(HELLO_KEY, std::forward(name), std::forward(params)...); keyHash(); } template void device(DevNameT&& dev_name) { base_t::setKeyValue(DEVICE_KEY, std::forward(dev_name)); keyHash(); } void names() { base_t::setKey(NAMES_KEY); keyHash(); } template void names(const DevNameT& dev_name, const DevNameTs&... dev_names) { base_t::setKeyValue(NAMES_KEY, dev_name, dev_names...); keyHash(); } template requires(!traits::adc_input_char_range>) void names(const R& dev_names) { base_t::setKeyValue(NAMES_KEY, dev_names); keyHash(); } template void names(const T& dev_names) { base_t::setKeyValue(NAMES_KEY, dev_names); keyHash(); } }; } // namespace adc