From 27dccfe7c045a2d295af3b244409a32bd63bf332 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Tue, 7 Oct 2025 23:51:58 +0300 Subject: [PATCH] ... --- asibfm700/asibfm700_configfile.h | 12 ++- mcc/mcc_netserver_proto.h | 165 ++++++++++++++++++++++++++++++- mcc/mcc_utils.h | 91 ++++++++++++++++- 3 files changed, 260 insertions(+), 8 deletions(-) diff --git a/asibfm700/asibfm700_configfile.h b/asibfm700/asibfm700_configfile.h index d112041..9125618 100644 --- a/asibfm700/asibfm700_configfile.h +++ b/asibfm700/asibfm700_configfile.h @@ -485,18 +485,24 @@ protected: inline static auto deserializer = [](std::string_view str, VT& value) { std::error_code ec{}; + mcc::utils::MccSimpleDeserializer deser; + deser.setRangeDelim(base_t::VALUE_ARRAY_DELIM); + if constexpr (std::is_arithmetic_v || mcc::traits::mcc_output_char_range || std::ranges::range || mcc::traits::mcc_time_duration_c) { - ec = base_t::defaultDeserializeFunc(str, value); + // ec = base_t::defaultDeserializeFunc(str, value); + ec = deser(str, value); } else if constexpr (std::same_as) { // assume here all angles are in degrees double vd; - ec = base_t::defaultDeserializeFunc(str, vd); + // ec = base_t::defaultDeserializeFunc(str, vd); + ec = deser(str, value); if (!ec) { value = mcc::MccAngle(vd, mcc::MccDegreeTag{}); } } else if constexpr (std::same_as) { std::string vstr; - ec = base_t::defaultDeserializeFunc(str, vstr); + // ec = base_t::defaultDeserializeFunc(str, vstr); + ec = deser(str, value); if (!ec) { auto s = mcc::utils::trimSpaces(vstr); diff --git a/mcc/mcc_netserver_proto.h b/mcc/mcc_netserver_proto.h index b1a23a4..57e1e43 100644 --- a/mcc/mcc_netserver_proto.h +++ b/mcc/mcc_netserver_proto.h @@ -38,7 +38,8 @@ namespace mcc::network static constexpr std::string_view MCC_COMMPROTO_STOP_SEQ = "\n"; static constexpr std::string_view MCC_COMMPROTO_KEYPARAM_DELIM_SEQ = " "; -static constexpr std::string_view MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ = " "; +static constexpr std::string_view MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ = ";"; +static constexpr std::string_view MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ = ","; /* server special keywords */ @@ -112,7 +113,7 @@ static constexpr std::string_view mcc_pairkind2str(MccCoordPairKind kind) /* keywords */ -// NOTE: THE COORDINATES AND TIME-RELATED QUANTITIES CAN BE EXPRESSED IN THE TWO FORMAT: +// NOTE: THE COORDINATES AND TIME-RELATED QUANTITIES CAN BE EXPRESSED IN THE TWO FORMATS: // 1) fixed-point real number, e.g. 123.43987537359 or -0.09775 // 2) sexagesimal number, e.g. 10:43:43.12 or -123:54:12.435 // @@ -122,7 +123,7 @@ static constexpr std::string_view mcc_pairkind2str(MccCoordPairKind kind) // IN FORMAT 'HOURS:MINUTES:SECONDS', WHILE DEC/ALT/AZ/ZD COORDINATES MUST // BE EXPRESSED AS '+/-DEGREES:ARCMINUTES:ARCSECONDS' // -// USER-ENTERED (FROM NETWORK CLIENTS) COORDINATE PAIR MAY BE PROVIDED IN A MIXED FORM, I.E., +// USER-ENTERED (FROM NETWORK CLIENTS) COORDINATE PAIR CAN BE PROVIDED IN A MIXED FORM, I.E., // 12.34436658678 10:32:11.432 or // 10:32:11.432 12.34436658678 // @@ -182,7 +183,7 @@ static constexpr std::string_view MCC_COMMPROTO_KEYWORD_TARGET_STR = "TARGET"; // "MOUNT coord-kind", if 'coord-kind' is omitted then coordinates are according to mount type, // i.e., HADEC for equathorial-type mount and AZZD for alt-azimuthal one // e.g.: -// "MOUNT RADEC\n" (get mount current apparent RA and DEC mount coordinates) +// "MOUNT RADEC\n" (get current apparent RA and DEC mount coordinates) // // server must return "ACK MOUNT X-coord Y-coord XY-kind" or "ERROR INVPAR" // e.g. @@ -434,4 +435,160 @@ bool mcc_netmsg_get_cpoint(mcc_netmsg_parse_result_c auto const& parse_res, } +template +concept mcc_netmsg_valid_keys_c = requires(T t) { + // std::array of valid message keywords + [](std::array) { + // to ensure T::NETMSG_VALID_KEYWORDS can be used as compile-time constant + static constexpr auto v0 = T::NETMSG_VALID_KEYWORDS[0]; + return v0; + }(T::NETMSG_VALID_KEYWORDS); + + // std::array of valid message keywords hashes + [](std::array) { + // to ensure T::NETMSG_VALID_KEYWORD_HASHES can be used as compile-time constant + static constexpr auto v0 = T::NETMSG_VALID_KEYWORD_HASHES[0]; + return v0; + }(T::NETMSG_VALID_KEYWORD_HASHES); + + requires T::NETMSG_VALID_KEYWORDS.size() == T::NETMSG_VALID_KEYWORD_HASHES.size(); +}; + + +struct MccNetMessageValidKeywords { + static constexpr std::array NETMSG_VALID_KEYWORDS = { + MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR, MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR, + MCC_COMMPROTO_KEYWORD_COORDFMT_STR, MCC_COMMPROTO_KEYWORD_COORDPREC_STR, + MCC_COMMPROTO_KEYWORD_TARGET_STR, MCC_COMMPROTO_KEYWORD_MOUNT_STR, + MCC_COMMPROTO_KEYWORD_TELEMETRY_STR, MCC_COMMPROTO_KEYWORD_INIT_STR, + MCC_COMMPROTO_KEYWORD_STOP_STR, MCC_COMMPROTO_KEYWORD_SLEW_STR, + MCC_COMMPROTO_KEYWORD_MOVE_STR, MCC_COMMPROTO_KEYWORD_TRACK_STR, + MCC_COMMPROTO_KEYWORD_STATUS_STR}; + + + // hashes of valid keywords + static constexpr std::array NETMSG_VALID_KEYWORD_HASHES = [](std::index_sequence) { + return std::array{mcc::utils::FNV1aHash(NETMSG_VALID_KEYWORDS[Is])...}; + }(std::make_index_sequence()); + + static std::expected isKeywordValid(std::string_view key) + { + const auto hash = utils::FNV1aHash(key); + + for (auto const& h : NETMSG_VALID_KEYWORD_HASHES) { + if (h == hash) { + return h; + } + } + + return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + } +}; + +static_assert(mcc_netmsg_valid_keys_c, ""); + + +template +class MccNetMessage +{ +public: + typedef BASE_T valid_keys_t; + typedef BYTEREPR_T byte_repr_t; + + template + MccNetMessage(KT&& key, PTs&&... params) + { + construct(std::forward(key), std::forward(params)...); + } + + template + bool withKey(const KT& key) + { + return utils::FNV1aHash(key) == _keywordHash; + } + + template + std::expected params(DeserFuncT&& deser_func) + { + T val; + + auto ec = deser_func(_params, val); + if (ec) { + return std::unexpect(ec); + } else { + return val; + } + } + + template + std::expected params() + { + return params(utils::MccSimpleDeserializer{}.setRangeDelim(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ)); + } + + template + std::error_code construct(KT&& key, PTs&&... params) + { + if (!std::ranges::size(key)) { + return std::make_error_code(std::errc::invalid_argument); + } + + auto r = valid_keys_t::isKeywordValid(key); + if (!r) { + return r.error(); + } + + _keywordHash = r.value(); + + _msgBuffer = BYTEREPR_T{}; + + std::ranges::copy(key, std::back_inserter(_msgBuffer)); + _keyword = {_msgBuffer.begin(), _msgBuffer.end()}; + + _params = std::string_view{}; + + if constexpr (sizeof...(PTs)) { + std::ranges::copy(MCC_COMMPROTO_KEYPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer)); + + [this](this auto&& self, const T& par, const Ts&... pars) { + if constexpr (std::is_arithmetic_v) { + std::ranges::copy(std::to_string(par), std::back_inserter(_msgBuffer)); + } else if constexpr (std::convertible_to) { + std::ranges::copy(static_cast(par), std::back_inserter(_msgBuffer)); + } else if constexpr (std::constructible_from) { + std::ranges::copy(std::string(par), std::back_inserter(_msgBuffer)); + } else if constexpr (traits::mcc_char_range) { + std::ranges::copy(std::string(par.begin(), par.end()), std::back_inserter(_msgBuffer)); + } else if constexpr (std::same_as) { + std::ranges::copy(mcc_pairkind2str(par), std::back_inserter(_msgBuffer)); + } else if constexpr (traits::mcc_time_duration_c) { + std::forward(self)(par.count()); + } else { + static_assert(false, "UNSUPPORTED TYPE!!!"); + } + + if constexpr (sizeof...(Ts)) { + std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer)); + + std::forward(self)(pars...); + } + }(std::forward(params)...); + + _params = {_msgBuffer.begin() + _keyword.size() + MCC_COMMPROTO_KEYPARAM_DELIM_SEQ.size(), + _msgBuffer.end()}; + } + + return {}; + } + +protected: + size_t _keywordHash{}; + std::string_view _keyword{}; + std::string_view _params{}; + + BYTEREPR_T _msgBuffer{}; +}; + + + } // namespace mcc::network diff --git a/mcc/mcc_utils.h b/mcc/mcc_utils.h index 08e746f..d2bdaf2 100644 --- a/mcc/mcc_utils.h +++ b/mcc/mcc_utils.h @@ -401,6 +401,94 @@ static constexpr size_t FNV1aHash(std::forward_iterator auto begin, std::sentine } +class MccSimpleDeserializer +{ +public: + static constexpr std::string RANGE_DELIM_SEQ = ","; + + template + MccSimpleDeserializer& setRangeDelim(R&& r) + { + if (std::ranges::size(std::forward(r))) { + _rangeDelim.clear(); + std::ranges::copy(std::forward(r), std::back_inserter(_rangeDelim)); + } + + return *this; + } + + template + R getRangeDelim() const + { + R r; + + std::ranges::copy(_rangeDelim, std::back_inserter(r)); + + return r; + } + + std::string getRangeDelim() const + { + return getRangeDelim(); + } + + template + std::error_code operator()(IR&& bytes, VT& value) + { + std::error_code ret{}; + + if constexpr (std::is_arithmetic_v) { + auto v = mcc::utils::numFromStr(trimSpaces(bytes)); + if (!v.has_value()) { + return std::make_error_code(std::errc::invalid_argument); + } + + value = v.value(); + } else if constexpr (mcc::traits::mcc_output_char_range) { + VT r; + std::ranges::copy(bytes, std::back_inserter(r)); + value = r; + } else if constexpr (std::ranges::range) { + using el_t = std::ranges::range_value_t; + + if constexpr (std::is_reference_v || std::is_const_v) { // no reference or constants allowed + return std::make_error_code(std::errc::invalid_argument); + } + + VT r; + el_t elem; + + auto els = std::views::split(bytes, _rangeDelim); + + for (auto const& el : els) { + ret = (*this)(std::string_view(el), elem); + if (!ret) { + std::back_inserter(r) = elem; + } else { + return std::make_error_code(std::errc::invalid_argument); + } + } + + value = r; + } else if constexpr (mcc::traits::mcc_time_duration_c) { + typename VT::rep vd; + + ret = (*this)(trimSpaces(bytes), vd); + if (!ret) { + value = VT{vd}; + } + } else { + ret = std::make_error_code(std::errc::invalid_argument); + } + + return ret; + } + +protected: + std::string _rangeDelim{RANGE_DELIM_SEQ}; +}; + + /* key-value pair holder */ // to follow std::variant requirements (not references, not array, not void) @@ -521,7 +609,8 @@ public: template std::error_code fromCharRange(const R& buffer, RecDelimT rec_delim = std::string_view("\n")) { - return fromCharRange(buffer, KeyValueHolder::defaultDeserializeFunc, std::move(rec_delim)); + // return fromCharRange(buffer, KeyValueHolder::defaultDeserializeFunc, std::move(rec_delim)); + return fromCharRange(buffer, MccSimpleDeserializer{}.setRangeDelim(VALUE_ARRAY_DELIM), std::move(rec_delim)); } template