#pragma once /* MOUNT CONTROL COMPONENTS LIBRARY */ /* BASIC NETWORK PROTOCOL DEFINITIONS */ #include #include #include "mcc_angle.h" #include "mcc_defaults.h" #include "mcc_generics.h" #include "mcc_utils.h" namespace mcc::network { /* * The network protocol is the ASCII-based, case-sensitive textual protocol. * The "client-server" communication is performed through messages. * The message is a minimal unit of this communication. * The model of network communication is a simple "client-server" one, i.e., * client asks - server responds. * * network communication message format: * [[][][]...] * * where * - mandatory message keyword (one or more ASCII symbols) * * * e.g. * "TARGET 12:23:45.56 00:32:21.978\n" */ /* low-level network message format definitions */ 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_RANGEPARAM_DELIM_SEQ = ","; /* server special keywords */ static constexpr std::string_view MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR = "ACK"; // ACK static constexpr std::string_view MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR = "ERROR"; // mount operational error // pre-defined errors static constexpr std::string_view MCC_COMMPROTO_SERVER_ERROR_INVKEY_STR = "INVKEY"; // invalid keyword static constexpr std::string_view MCC_COMMPROTO_SERVER_ERROR_INVPAR_STR = "INVPAR"; // invalid parameter /* server control keywords */ static constexpr std::string_view MCC_COMMPROTO_KEYWORD_RESTART_SERVER_STR = "RESTART"; // restart server /* BELOW IS ONE OF THE PROTOCOL OPTIONS CORRESPONDING MCC_GENERIC_MOUNT_C CONCEPT */ /* predefined parameters */ static constexpr std::string_view MCC_COMMPROTO_COORD_KIND_RADEC_ICRS = "RADEC_ICRS"; // ICRS RA and DEC static constexpr std::string_view MCC_COMMPROTO_COORD_KIND_RADEC = "RADEC"; // apparent RA and DEC static constexpr std::string_view MCC_COMMPROTO_COORD_KIND_HADEC = "HADEC"; // apparent HA and DEC static constexpr std::string_view MCC_COMMPROTO_COORD_KIND_AZZD = "AZZD"; // azimuth and zenithal distance static constexpr std::string_view MCC_COMMPROTO_COORD_KIND_AZALT = "AZALT"; // azimuth and altitude static constexpr std::string_view MCC_COMMPROTO_COORD_KIND_XY = "XY"; // hardware (encoder) coordinates // static constexpr MccCoordPairKind mcc_str2pairkind(std::string_view spair) // { // return spair == MCC_COMMPROTO_COORD_KIND_RADEC_ICRS ? MccCoordPairKind::COORDS_KIND_RADEC_ICRS // : spair == MCC_COMMPROTO_COORD_KIND_RADEC ? MccCoordPairKind::COORDS_KIND_RADEC_APP // : spair == MCC_COMMPROTO_COORD_KIND_HADEC ? MccCoordPairKind::COORDS_KIND_HADEC_APP // : spair == MCC_COMMPROTO_COORD_KIND_AZZD ? MccCoordPairKind::COORDS_KIND_AZZD // : spair == MCC_COMMPROTO_COORD_KIND_AZALT ? MccCoordPairKind::COORDS_KIND_AZALT // : spair == MCC_COMMPROTO_COORD_KIND_XY ? MccCoordPairKind::COORDS_KIND_XY // : MccCoordPairKind::COORDS_KIND_GENERIC; // } template static constexpr MccCoordPairKind mcc_str2pairkind(R&& spair) { if constexpr (std::is_pointer_v>) { return mcc_str2pairkind(std::string_view{spair}); } const auto hash = mcc::utils::FNV1aHash(std::forward(spair)); return hash == mcc::utils::FNV1aHash(MCC_COMMPROTO_COORD_KIND_RADEC_ICRS) ? MccCoordPairKind::COORDS_KIND_RADEC_ICRS : hash == mcc::utils::FNV1aHash(MCC_COMMPROTO_COORD_KIND_RADEC) ? MccCoordPairKind::COORDS_KIND_RADEC_APP : hash == mcc::utils::FNV1aHash(MCC_COMMPROTO_COORD_KIND_HADEC) ? MccCoordPairKind::COORDS_KIND_HADEC_APP : hash == mcc::utils::FNV1aHash(MCC_COMMPROTO_COORD_KIND_AZZD) ? MccCoordPairKind::COORDS_KIND_AZZD : hash == mcc::utils::FNV1aHash(MCC_COMMPROTO_COORD_KIND_AZALT) ? MccCoordPairKind::COORDS_KIND_AZALT : hash == mcc::utils::FNV1aHash(MCC_COMMPROTO_COORD_KIND_XY) ? MccCoordPairKind::COORDS_KIND_XY : MccCoordPairKind::COORDS_KIND_GENERIC; } static constexpr std::string_view mcc_pairkind2str(MccCoordPairKind kind) { return kind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS ? MCC_COMMPROTO_COORD_KIND_RADEC_ICRS : kind == MccCoordPairKind::COORDS_KIND_RADEC_APP ? MCC_COMMPROTO_COORD_KIND_RADEC : kind == MccCoordPairKind::COORDS_KIND_HADEC_APP ? MCC_COMMPROTO_COORD_KIND_HADEC : kind == MccCoordPairKind::COORDS_KIND_AZZD ? MCC_COMMPROTO_COORD_KIND_AZZD : kind == MccCoordPairKind::COORDS_KIND_AZALT ? MCC_COMMPROTO_COORD_KIND_AZALT : kind == MccCoordPairKind::COORDS_KIND_XY ? MCC_COMMPROTO_COORD_KIND_XY : "UNKNOWN"; } /* keywords */ // 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 // // IN THE FIRST CASE ALL NUMBERS MUST BE INTERPRETATED AS DEGREES, // IN THE SECOND CASE NUMBERS MUST BE INTERPRETATED ACCORDING TO ITS TYPE: // ALL TIME-RELATED QUANTITIES AND RA/HA COORDINATES MUST BE EXPRESSED // 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 CAN BE PROVIDED IN A MIXED FORM, I.E., // 12.34436658678 10:32:11.432 or // 10:32:11.432 12.34436658678 // // SERVER-RESPONDED COORDINATES ARE ALWAYS IN THE SAME FORMAT, SEXAGESIMAL OR FIXED-POINT // // format of output coordinates: // "COORDFMT FMT-type\n" // e.g.: // "COORDFMT SGM\n" // "COORDFMT\n" // // server must return "ACK" or "ERROR INVPAR" in the case of 'set'-operation and // "ACK COORDFMT FMT-type" in the case of 'get'-operation // e.g.: // "COORDFMT FIX\n" -> "ACK\n" // "COORDFMT SXT\n" -> "ERROR INVPAR\n" (invalid parameter of format type) // "COORDFMT\n" -> "ACK COORDFMT FIX\n" // static constexpr std::string_view MCC_COMMPROTO_KEYWORD_COORDFMT_STR = "COORDFMT"; static constexpr std::string_view MCC_COMMPROTO_KEYWORD_COORDFMT_SEXGM_STR = "SGM"; // sexagesimal static constexpr std::string_view MCC_COMMPROTO_KEYWORD_COORDFMT_FIXED_STR = "FIX"; // fixed point // precision (number of decimal places) of returned coordinates: // "COORDPREC seconds-prec arcseconds-prec\n" // seconds-prec - precision of hour-based coordinates (RA and HA) or time-related quantities // arcseconds-prec - precision of degree-based coordinates (DEC, AZ, ZD, ALT) // precision must be given as non-negative integer number // e.g. // "COORDPREC 2,1\n" (output sexagesimal RA=12:34:56.67, DEC=32:54:21.9) // static constexpr std::string_view MCC_COMMPROTO_KEYWORD_COORDPREC_STR = "COORDPREC"; // set/get target coordinates // "TARGET X-coord Y-coord XY-kind\n", if 'XY-kind' is omitted then one should assume RADEC_ICRS // e.g.: // "TARGET 12.7683487 10:23:09.75 AZZD\n" // "TARGET HADEC\n" // "TARGET\n" // // server must return "ACK" or "ERROR INVPAR" in the case of 'set'-operation and // "ACK TARGET X-coord Y-coord XY-kind" in the case of 'get'-operation // e.g.: // "TARGET 12.7683487 10:23:09.75 AZZD\n" -> "ACK\n" // "TARGET 12.7683487 10:23:09.75 AZZE\n" -> "ERROR INVPAR\n" (invalid parameter of coordinates pair kind) // // "TARGET HADEC\n" -> "ACK TARGET 20:21:56.32 -01:32:34.2 HADEC\n" // "TARGET\n" -> "ACK TARGET 20:21:56.32 -01:32:34.2 RADEC_ICRS\n" // static constexpr std::string_view MCC_COMMPROTO_KEYWORD_TARGET_STR = "TARGET"; // get mount coordinates: // "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 current apparent RA and DEC mount coordinates) // // server must return "ACK MOUNT X-coord Y-coord XY-kind" or "ERROR INVPAR" // e.g. // "MOUNT AZALT\n" -> "ACK MOUNT 1.2332325 54.23321312 AZALT\n" // "MOUNT AZAL\n" -> "ERROR INVPAR\n" (invalid parameter of coordinates pair kind) // "MOUNT\n" -> "ACK MOUNT 1.2332325 54.23321312 AZZD\n" for alt-azimuthal mount // "MOUNT\n" -> "ACK MOUNT 1.2332325 54.23321312 HADEC\n" for equathorial mount static constexpr std::string_view MCC_COMMPROTO_KEYWORD_MOUNT_STR = "MOUNT"; static constexpr std::string_view MCC_COMMPROTO_KEYWORD_TELEMETRY_STR = "TELEMETRY"; // init mount // "INIT\n" static constexpr std::string_view MCC_COMMPROTO_KEYWORD_INIT_STR = "INIT"; // stop any movements // "STOP\n" static constexpr std::string_view MCC_COMMPROTO_KEYWORD_STOP_STR = "STOP"; // slew mount and track target: // "SLEW\n" static constexpr std::string_view MCC_COMMPROTO_KEYWORD_SLEW_STR = "SLEW"; // slew mount and stop: // "MOVE\n" static constexpr std::string_view MCC_COMMPROTO_KEYWORD_MOVE_STR = "MOVE"; // track target // "TRACK\n" static constexpr std::string_view MCC_COMMPROTO_KEYWORD_TRACK_STR = "TRACK"; // get mount status // "STATUS\n" static constexpr std::string_view MCC_COMMPROTO_KEYWORD_STATUS_STR = "STATUS"; // valid keywords static constexpr std::array MCC_COMMPROTO_VALID_KEYS = { 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 MCC_COMMPROTO_VALID_KEYS_HASH = [](std::index_sequence) { return std::array{mcc::utils::FNV1aHash(MCC_COMMPROTO_VALID_KEYS[Is])...}; }(std::make_index_sequence()); 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()); constexpr static const size_t* isKeywordValid(std::string_view key) { const auto hash = mcc::utils::FNV1aHash(key); for (auto const& h : NETMSG_VALID_KEYWORD_HASHES) { if (h == hash) { return &h; } } return nullptr; } }; static_assert(mcc_netmsg_valid_keys_c, ""); template concept mcc_netmessage_c = requires(T t) { T(); }; template class MccNetMessage { protected: class DefaultDeserializer : protected mcc::utils::MccSimpleDeserializer { protected: using base_t = mcc::utils::MccSimpleDeserializer; inline static mcc::MccCelestialPointDeserializer _cpDeserializer{MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ}; inline static mcc::MccEqtHrzCoordsDeserializer _eqhrDeserializer{MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ}; inline static mcc::MccTelemetryDataDeserializer _telemetryDeserializer{MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ}; public: DefaultDeserializer() : base_t(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ) {} template std::error_code operator()(IR&& bytes, VT& value) const { if constexpr (mcc_telemetry_data_c) { return _telemetryDeserializer(std::forward(bytes), value); } else if constexpr (mcc_eqt_hrz_coord_c) { return _eqhrDeserializer(std::forward(bytes), value); } else if constexpr (mcc_celestial_point_c) { return _cpDeserializer(std::forward(bytes), value); } else if constexpr (std::same_as) { value = MccCoordStrToPairKind(bytes); if (value == MccCoordPairKind::COORDS_KIND_UNKNOWN) { return std::make_error_code(std::errc::invalid_argument); } } else if constexpr (std::same_as) { std::string v; auto ec = (*this)(std::forward(bytes), v); if (ec) { return ec; } if (v.compare(MCC_COMMPROTO_KEYWORD_COORDFMT_SEXGM_STR) == 0) { value = MccCoordinateSerializer::SerializedCoordFormat::CFMT_SGM; } else if (v.compare(MCC_COMMPROTO_KEYWORD_COORDFMT_FIXED_STR) == 0) { value = MccCoordinateSerializer::SerializedCoordFormat::CFMT_DEGREES; } else { return std::make_error_code(std::errc::invalid_argument); } } else if constexpr (std::same_as) { std::vector v; auto ec = (*this)(std::forward(bytes), v); if (ec) { return ec; } auto hprec = v[0]; value.hour_prec = hprec > 0 ? (hprec < std::numeric_limits::max() ? hprec : std::numeric_limits::max()) : 2; if (v.size() == 1) { value.deg_prec = 1; } else { auto dprec = v[1]; value.deg_prec = dprec > 0 ? dprec < std::numeric_limits::max() ? dprec : std::numeric_limits::max() : 1; } } else { return base_t::operator()(std::forward(bytes), value); } return {}; } }; class DefaultSerializer { friend class MccNetMessage; MccCoordinateSerializer::SerializedCoordFormat _coordFmt{}; MccCoordinateSerializer::SexagesimalCoordPrec _coordPrec{}; public: template void operator()(const T& value, OR& bytes) { if constexpr (std::is_arithmetic_v) { std::format_to(std::back_inserter(bytes), "{}", value); } else if constexpr (std::convertible_to) { std::ranges::copy(static_cast(value), std::back_inserter(bytes)); } else if constexpr (std::constructible_from) { std::ranges::copy(std::string(value), std::back_inserter(bytes)); } else if constexpr (traits::mcc_char_range) { std::ranges::copy(std::string(value.begin(), value.end()), std::back_inserter(bytes)); // } else if constexpr (std::same_as) { // std::ranges::copy(mcc_pairkind2str(value), std::back_inserter(bytes)); } else if constexpr (traits::mcc_time_duration_c) { (*this)(value.count(), bytes); } else if constexpr (mcc_telemetry_data_c) { static MccTelemetryDataSerializer sr; sr.setDelimiter(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ); sr.setFormat(_coordFmt); sr.setPrecision(_coordPrec); sr(value, bytes); } else if constexpr (mcc_eqt_hrz_coord_c) { static MccEqtHrzCoordsSerializer sr; sr.setDelimiter(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ); sr.setFormat(_coordFmt); sr.setPrecision(_coordPrec); sr(value, bytes); } else if constexpr (mcc_celestial_point_c) { static MccCelestialPointSerializer sr; sr.setDelimiter(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ); sr.setFormat(_coordFmt); sr.setPrecision(_coordPrec); sr(value, bytes); } else if constexpr (std::ranges::range) { auto sz = std::ranges::size(value); if (sz == 0) { return; } (*this)(*value.begin(), bytes); // the first element if (sz > 1) { for (auto const& el : value | std::views::drop(1)) { std::ranges::copy(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, std::back_inserter(bytes)); (*this)(el, bytes); } } } else if constexpr (std::same_as) { std::format_to(std::back_inserter(bytes), "{}{}{}{}{}", value.value(), MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, value.message(), MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, value.category().name()); } else if constexpr (std::formattable) { std::format_to(std::back_inserter(bytes), "{}", value); } else { static_assert(false, "UNSUPPORTED TYPE!!!"); } } }; public: typedef BASE_T valid_keys_t; typedef BYTEREPR_T byte_repr_t; enum MccNetMessageError { ERROR_OK, ERROR_EMPTY_MESSAGE, ERROR_INVALID_KEYWORD, ERROR_EMPTY_KEYWORD }; MccNetMessage() = default; template MccNetMessage(KT&& key, PTs&&... params) requires traits::mcc_output_char_range { construct(_defaultSerializer, std::forward(key), std::forward(params)...); } template constexpr MccNetMessage(const R& msg) requires traits::mcc_input_char_range { fromCharRange(msg); } // constexpr MccNetMessage(const BYTEREPR_T& msg) // requires traits::mcc_input_char_range // { // fromCharRange(msg); // } virtual ~MccNetMessage() = default; template constexpr bool withKey(const KT& key) const { if constexpr (std::is_pointer_v>) { return withKey(std::string_view{key}); } return mcc::utils::FNV1aHash(key) == _keywordHash; } template R keyword() const { if constexpr (traits::mcc_char_view) { return R{_keyword.begin(), _keyword.end()}; } else { R r; std::ranges::copy(_keyword, std::back_inserter(r)); return r; } } std::string_view keyword() const { return _keyword; } size_t paramSize() const { return _params.size(); } template R params(size_t start_idx = 0, size_t Nelemes = std::numeric_limits::max()) const requires(traits::mcc_view_or_output_char_range || traits::mcc_range_of_char_range) { if (start_idx >= _params.size()) { return R{}; } auto stop_idx = start_idx + Nelemes - 1; if (stop_idx >= _params.size()) { stop_idx = _params.size() - 1; } if constexpr (traits::mcc_range_of_char_range) { // returm parameters as array using el_t = std::ranges::range_value_t; R r; if constexpr (traits::mcc_char_view || traits::mcc_output_char_range) { for (size_t i = start_idx; i <= stop_idx; ++i) { auto& el = _params[i]; std::back_inserter(r) = el_t{el.begin(), el.end()}; } } else { static_assert(false, "UNSUPPORTED RANGE TYPE!!!"); } return r; } else { if constexpr (traits::mcc_char_view) { // return joined parameters as a single char-range return R{_params[start_idx].begin(), _params[stop_idx].end()}; } else { R r; std::ranges::copy(std::string_view{_params[start_idx].begin(), _params[stop_idx].end()}, std::back_inserter(r)); return r; } } } std::string_view params(size_t start_idx = 0, size_t Nelemes = std::numeric_limits::max()) const { return params(start_idx, Nelemes); } template R param(size_t idx) const { if (idx >= _params.size()) { return {}; } if constexpr (traits::mcc_char_view) { return R{_params[idx].begin(), _params[idx].end()}; } else { R r; std::ranges::copy(_params[idx], std::back_inserter(r)); return r; } } std::string_view param(size_t idx) const { if (idx >= _params.size()) { return {}; } return _params[idx]; } template std::expected paramValue(size_t idx, DeserFuncT&& deser_func) const { if (idx >= _params.size()) { return std::unexpected{std::make_error_code(std::errc::argument_out_of_domain)}; } T val; auto ec = std::forward(deser_func)(_params[idx], val); if (ec) { return std::unexpected(ec); } else { return val; } } template std::expected paramValue(size_t idx) const { return paramValue(idx, _defaultDeserializer); } template R byteRepr() const { if constexpr (traits::mcc_char_view) { return R{_msgBuffer.begin(), _msgBuffer.end()}; } else { R r; std::ranges::copy(_msgBuffer, std::back_inserter(r)); return r; } } std::string_view byteRepr() const { return byteRepr(); } template std::error_code construct(KT&& key, PTs&&... params) requires traits::mcc_output_char_range { return construct(_defaultSerializer, std::forward(key), std::forward(params)...); } // // serializing function SerFuncT - a callable with the signature: // template // void ser_func(const T& val, R&& buffer) // template std::error_code construct(SerFuncT&& ser_func, KT&& key, PTs&&... params) requires(traits::mcc_output_char_range && !traits::mcc_input_char_range>) { if constexpr (std::is_pointer_v>) { return construct(std::forward(ser_func), std::string_view(key), std::forward(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 std::make_error_code(std::errc::argument_out_of_domain); } _keywordHash = *r; _msgBuffer = BYTEREPR_T{}; std::ranges::copy(std::forward(key), std::back_inserter(_msgBuffer)); // _keyword = {_msgBuffer.begin(), _msgBuffer.end()}; size_t key_idx = std::distance(_msgBuffer.begin(), _msgBuffer.end()); std::vector par_idx; _params.clear(); if constexpr (sizeof...(PTs)) { std::ranges::copy(MCC_COMMPROTO_KEYPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer)); convertFunc(std::forward(ser_func), par_idx, std::forward(params)...); for (size_t i = 0; i < par_idx.size(); i += 2) { _params.emplace_back(_msgBuffer.begin() + par_idx[i], _msgBuffer.begin() + par_idx[i + 1]); } } _keyword = std::string_view{_msgBuffer.begin(), _msgBuffer.begin() + key_idx}; return {}; } template constexpr MccNetMessageError fromCharRange(const R& r) { if constexpr (std::is_pointer_v>) { return fromCharRange(std::string_view(r)); } if (std::ranges::size(r) == 0) { return ERROR_EMPTY_MESSAGE; } std::string_view key; // auto prev_msg_buff = _msgBuffer; if constexpr (traits::mcc_output_char_range) { _msgBuffer = BYTEREPR_T{}; std::ranges::copy(r, std::back_inserter(_msgBuffer)); } else { _msgBuffer = {std::begin(r), std::end(r)}; } auto found = std::ranges::search(_msgBuffer, MCC_COMMPROTO_KEYPARAM_DELIM_SEQ); if (found.empty()) { // only keyword key = mcc::utils::trimSpaces(std::string_view{_msgBuffer.begin(), _msgBuffer.end()}); } else { key = mcc::utils::trimSpaces(std::string_view{_msgBuffer.begin(), found.begin()}); } auto kv = valid_keys_t::isKeywordValid(key); if (!kv) { // _msgBuffer = prev_msg_buff; // restore previous netmessage state return ERROR_INVALID_KEYWORD; } _keywordHash = *kv; _keyword = key; if (!found.empty()) { // params ... _params.clear(); auto pr = std::views::split(std::string_view{found.end(), _msgBuffer.end()}, MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ); for (auto const& p : pr) { _params.emplace_back(p.begin(), p.end()); } } return ERROR_OK; } protected: size_t _keywordHash{}; std::string_view _keyword{}; std::vector _params{}; BYTEREPR_T _msgBuffer{}; inline static DefaultDeserializer _defaultDeserializer{}; DefaultSerializer _defaultSerializer{}; template void convertFunc(std::vector& idx, const T& par, const Ts&... pars) { if constexpr (std::same_as) { _defaultSerializer._coordFmt = par; if constexpr (sizeof...(Ts)) { convertFunc(idx, pars...); } } else if constexpr (std::same_as) { _defaultSerializer._coordPrec = par; if constexpr (sizeof...(Ts)) { convertFunc(idx, pars...); } } else { convertFunc(_defaultSerializer, idx, par, pars...); // idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end())); // _defaultSerializer(par, _msgBuffer); // idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end())); // if constexpr (sizeof...(Ts)) { // std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer)); // convertFunc(idx, pars...); // } } }; template void convertFunc(SerFuncT&& ser_func, std::vector& idx, const T& par, const Ts&... pars) requires(!std::same_as, std::vector>) { if constexpr (std::derived_from, DefaultSerializer>) { if constexpr (std::same_as) { // _defaultSerializer._coordFmt = par; ser_func._coordFmt = par; } else if constexpr (std::same_as) { _defaultSerializer._coordPrec = par; } else { idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end())); std::forward(ser_func)(par, _msgBuffer); idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end())); if constexpr (sizeof...(Ts)) { std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer)); } } } else { idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end())); std::forward(ser_func)(par, _msgBuffer); idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end())); if constexpr (sizeof...(Ts)) { std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer)); } } if constexpr (sizeof...(Ts)) { convertFunc(std::forward(ser_func), idx, pars...); } } }; static_assert(MccNetMessage{"ACK"}.withKey("ACK")); static_assert(MccNetMessage{"ACK"}.withKey("ACK")); } // namespace mcc::network