diff --git a/mcc/mcc_netserver_proto.h b/mcc/mcc_netserver_proto.h index b1c1510..40c9f06 100644 --- a/mcc/mcc_netserver_proto.h +++ b/mcc/mcc_netserver_proto.h @@ -1,8 +1,10 @@ #pragma once +#include #include -#include #include "mcc_angle.h" +#include "mcc_generics.h" +#include "mcc_utils.h" namespace mcc::network { @@ -31,25 +33,50 @@ static constexpr std::string_view MCC_COMMPROTO_SERVER_ERROR_INVPAR_STR = "INVPA /* predefined parameters */ -static constexpr std::string_view MCC_COMMPROTO_COORD_KIND_RADEC_ICRS = "RADEC_ICRS"; -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"; -static constexpr std::string_view MCC_COMMPROTO_COORD_KIND_AZALT = "AZALT"; -static constexpr std::string_view MCC_COMMPROTO_COORD_KIND_XY = "XY"; +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) +// 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; +// } + +static constexpr MccCoordPairKind mcc_str2pairkind(traits::mcc_char_range auto const& 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; + const auto hash = utils::FNV1aHash(spair); + + return hash == utils::FNV1aHash(MCC_COMMPROTO_COORD_KIND_RADEC_ICRS) ? MccCoordPairKind::COORDS_KIND_RADEC_ICRS + : hash == utils::FNV1aHash(MCC_COMMPROTO_COORD_KIND_RADEC) ? MccCoordPairKind::COORDS_KIND_RADEC_APP + : hash == utils::FNV1aHash(MCC_COMMPROTO_COORD_KIND_HADEC) ? MccCoordPairKind::COORDS_KIND_HADEC_APP + : hash == utils::FNV1aHash(MCC_COMMPROTO_COORD_KIND_AZZD) ? MccCoordPairKind::COORDS_KIND_AZZD + : hash == utils::FNV1aHash(MCC_COMMPROTO_COORD_KIND_AZALT) ? MccCoordPairKind::COORDS_KIND_AZALT + : hash == 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 FORMAT: @@ -61,7 +88,13 @@ static constexpr MccCoordPairKind mcc_str2pairkind(std::string_view spair) // 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 MAY 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" @@ -93,7 +126,7 @@ static constexpr std::string_view MCC_COMMPROTO_KEYWORD_COORDPREC_STR = "COORDPR // set/get target coordinates -// "TRAGET X-coord Y-coord XY-kind\n", if 'XY-kind' is omitted then one should assume RADEC_ICRS +// "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" @@ -126,92 +159,197 @@ static constexpr std::string_view MCC_COMMPROTO_KEYWORD_TARGET_STR = "TARGET"; static constexpr std::string_view MCC_COMMPROTO_KEYWORD_MOUNT_STR = "MOUNT"; -static constexpr std::string_view MCC_COMMPROTO_KEYWORD_TELEMTRY_STR = "TELEMETRY"; +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"; -static const std::unordered_set MCC_COMMPROTO_VALID_KEYS = { +// 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"; + +// 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_TELEMTRY_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}; -auto MCC_COMMPROTO_KEYWORD_SERVER_ACK_HASH = - std::unordered_set::hasher{}(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR); -auto MCC_COMMPROTO_KEYWORD_SERVER_ERROR_HASH = - std::unordered_set::hasher{}(MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR); -template -std::string_view mcc_parse_netmsg(IR&& netmsg, OT& parse_res, bool from_server = false) +// hashes 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()); + + +static constexpr size_t MCC_COMMPROTO_KEYWORD_SERVER_ACK_HASH = + mcc::utils::FNV1aHash(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR); + +static constexpr size_t MCC_COMMPROTO_KEYWORD_SERVER_ERROR_HASH = + mcc::utils::FNV1aHash(MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR); + +static constexpr size_t MCC_COMMPROTO_KEYWORD_TARGET_HASH = mcc::utils::FNV1aHash(MCC_COMMPROTO_KEYWORD_TARGET_STR); + +static constexpr size_t MCC_COMMPROTO_KEYWORD_MOUNT_HASH = mcc::utils::FNV1aHash(MCC_COMMPROTO_KEYWORD_MOUNT_STR); + + +template RT = std::vector> +struct mcc_netmsg_parse_result_t { + size_t keyword_hash; + T keyword; + RT params; +}; + +template +concept mcc_netmsg_parse_result_c = requires(T t) { + requires std::same_as; + requires traits::mcc_char_range; + requires std::ranges::output_range; +}; + +// the function returns hash of message keyword +// template +template +bool mcc_parse_netmsg(const IR& netmsg, ResT& parse_res, bool from_server = false) { - if (std::ranges::size(std::forward(netmsg)) == 0) { - return ""; + if (std::ranges::size(netmsg) == 0) { + return false; }; - std::string_view ret; - - auto found = std::ranges::search(std::forward(netmsg), MCC_COMMPROTO_KEYPARAM_DELIM_SEQ); - - auto it = MCC_COMMPROTO_VALID_KEYS.find({netmsg.begin(), found.begin()}); - if (it == MCC_COMMPROTO_VALID_KEYS.end()) { - return ""; + auto found = std::ranges::search(netmsg, MCC_COMMPROTO_KEYPARAM_DELIM_SEQ); + if (std::distance(netmsg.begin(), found.begin()) == 0) { + return false; } - ret = *it; + const size_t hash = mcc::utils::FNV1aHash(netmsg.begin(), found.begin()); + auto it = std::ranges::find(MCC_COMMPROTO_VALID_KEYS_HASH, hash); + if (it == MCC_COMMPROTO_VALID_KEYS_HASH.end()) { + return false; + } - if (from_server) { // only ACK and ERROR - auto ok = std::ranges::search(netmsg.begin(), found.begin(), MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR.begin(), - MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR.end()); - if (ok) { - ret = MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR; - } - - ok = std::ranges::search(netmsg.begin(), found.begin(), MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR.begin(), - MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR.end()); - if (ok) { - ret = MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR; - } else { - return ""; + if (from_server) { // only ACK or ERROR + auto ok = hash == MCC_COMMPROTO_VALID_KEYS_HASH[0] || hash == MCC_COMMPROTO_VALID_KEYS_HASH[1]; + if (!ok) { + return false; } } - // if (found.empty()) { // no parameters? - // if (from_server) { - // auto ok = std::ranges::search(netmsg.begin(), found.begin(), - // MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR.begin(), - // MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR.end()); - // if (ok) { - // ret = MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR; - // } + parse_res.keyword_hash = hash; + parse_res.keyword = {netmsg.begin(), found.begin()}; - // ok = std::ranges::search(netmsg.begin(), found.begin(), MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR.begin(), - // MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR.end()); - // if (ok) { - // ret = MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR; - // } else { - // return ""; - // } - // } else { - // if (!MCC_COMMPROTO_VALID_KEYS.contains({netmsg.begin(), found.begin()})) { - // return ""; - // } - // } - // } else { - // if (std::distance(netmsg.begin(), found.begin()) == 0) { // an empty keyword - // return ""; - // } - // } - - - auto pars = std::forward(netmsg) | std::views::drop(std::distance(netmsg.begin(), found.end())) | + auto pars = netmsg | std::views::drop(std::distance(netmsg.begin(), found.end())) | std::views::split(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ); - OT res; - for (auto const& el : pars) { + decltype(parse_res.params) res; + + for (auto const& el : pars) { // parameters std::back_inserter(res) = {el.begin(), el.end()}; } - parse_res = std::move(res); + parse_res.params = std::move(res); - return ret; + return true; } +template +bool mcc_netmsg_construct(traits::mcc_output_char_range auto& msg, + traits::mcc_input_char_range auto const& keyword, + PTs... params) +{ + const size_t hash = utils::FNV1aHash(keyword); + if (!std::ranges::contains(MCC_COMMPROTO_VALID_KEYS_HASH, hash)) { + return false; + } + + msg = {keyword.begin(), keyword.end()}; + + if constexpr (sizeof...(PTs)) { + std::ranges::copy(MCC_COMMPROTO_KEYPARAM_DELIM_SEQ, std::back_inserter(msg)); + + [&msg](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(msg)); + } else if constexpr (std::convertible_to) { + std::ranges::copy(static_cast(par), std::back_inserter(msg)); + } else if constexpr (std::constructible_from) { + std::ranges::copy(std::string(par), std::back_inserter(msg)); + } else if constexpr (traits::mcc_char_range) { + std::ranges::copy(std::string(par.begin(), par.end()), std::back_inserter(msg)); + } else if constexpr (std::same_as) { + std::ranges::copy(mcc_pairkind2str(par), std::back_inserter(msg)); + } else { + static_assert(false, "UNSUPPORTED TYPE!!!"); + } + + if constexpr (sizeof...(Ts)) { + std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(msg)); + + std::forward(self)(pars...); + } + }(params...); + } + + return true; +} + +bool mcc_netmsg_get_cpoint(mcc_netmsg_parse_result_c auto const& parse_res, + size_t from_idx, + mcc_celestial_point_c auto& cpoint, + MccCoordPairKind default_kind) + requires std::ranges::contiguous_range +{ + if (std::ranges::size(parse_res.params) < (from_idx + 2)) { + return false; + } + + MccCoordPairKind kind = default_kind; + + if (std::ranges::size(parse_res.params) > (from_idx + 2)) { + kind = mcc_str2pairkind(parse_res.params[from_idx + 2]); + if (kind == MccCoordPairKind::COORDS_KIND_GENERIC) { + return false; + } + } + + std::optional ang1, ang2; + + switch (kind) { + case mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS: + case mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP: + case mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP: + ang1 = utils::parsAngleString(parse_res.params[from_idx], true); + break; + default: + ang1 = utils::parsAngleString(parse_res.params[from_idx]); + } + if (!ang1) { + return false; + } + + ang2 = utils::parsAngleString(parse_res.params[from_idx + 1]); + if (!ang2) { + return false; + } + + cpoint.pair_kind = kind; + cpoint.X = MccAngle(ang1.value(), mcc::MccDegreeTag{}); // to radians + cpoint.Y = MccAngle(ang2.value(), mcc::MccDegreeTag{}); // to radians + + return true; +} + + } // namespace mcc::network diff --git a/mcc/mcc_traits.h b/mcc/mcc_traits.h index b267838..e28763c 100644 --- a/mcc/mcc_traits.h +++ b/mcc/mcc_traits.h @@ -23,6 +23,11 @@ template concept mcc_char_view = std::ranges::view && std::same_as, char>; +// range of char/const char +template +concept mcc_char_range = std::ranges::range && std::same_as>, CharT>; + + // input range of char/const char template concept mcc_input_char_range = @@ -39,6 +44,9 @@ template concept mcc_view_or_output_char_range = mcc_char_view || mcc_output_char_range; +template +concept mcc_range_of_char_range = std::ranges::range && mcc_char_range>; + template concept mcc_range_of_input_char_range = std::ranges::range && traits::mcc_input_char_range>; diff --git a/mcc/mcc_utils.h b/mcc/mcc_utils.h index ecf9b7c..57b48b9 100644 --- a/mcc/mcc_utils.h +++ b/mcc/mcc_utils.h @@ -355,4 +355,48 @@ std::pair parseAnglePair(R&& str, bool hms1 = false, bool hms2 = } +template +static constexpr size_t FNV1aHash(const R& r) +{ + static_assert(sizeof(size_t) == 8 || sizeof(size_t) == 4, "ONLY FOR 32 or 64-bit size_t!!!"); + + size_t hash = 0, prime = 0; + if constexpr (sizeof(size_t) == 8) { // 64-bit + prime = 1099511628211UL; + hash = 14695981039346656037UL; + } else if constexpr (sizeof(size_t) == 4) { // 32-bit + prime = 16777619; + hash = 2166136261; + } + + for (const char& ch : r) { + hash ^= ch; + hash *= prime; + } + + return hash; +} + +static constexpr size_t FNV1aHash(std::forward_iterator auto begin, std::sentinel_for auto end) + requires std::same_as>, char> +{ + static_assert(sizeof(size_t) == 8 || sizeof(size_t) == 4, "ONLY FOR 32 or 64-bit size_t!!!"); + + size_t hash = 0, prime = 0; + if constexpr (sizeof(size_t) == 8) { // 64-bit + prime = 1099511628211UL; + hash = 14695981039346656037UL; + } else if constexpr (sizeof(size_t) == 4) { // 32-bit + prime = 16777619; + hash = 2166136261; + } + + for (auto it = begin; it != end; ++it) { + hash ^= *it; + hash *= prime; + } + + return hash; +} + } // namespace mcc::utils diff --git a/mcc/tests/ccte_test.cpp b/mcc/tests/ccte_test.cpp index b6d97fb..492998d 100644 --- a/mcc/tests/ccte_test.cpp +++ b/mcc/tests/ccte_test.cpp @@ -160,14 +160,42 @@ int main() std::cout << "\n\n"; std::string sm{"ACK TARGET 12:23:45.13 -09.23423525 RADEC"}; - std::vector sv; + // std::vector sv; + std::vector sv; + mcc::network::mcc_netmsg_parse_result_t<> p_res; - auto mr = mcc::network::mcc_parse_netmsg(sm, sv); + auto mr = mcc::network::mcc_parse_netmsg(sm, p_res); - std::cout << "MSG RET = <" << mr << ">\n"; - for (auto const& pr : sv) { + std::cout << "MSG: <" << sm << ">\n"; + std::cout << "\t" << p_res.keyword_hash << "\n"; + std::cout << "\t[" << p_res.keyword << "]\n"; + for (auto const& pr : p_res.params) { std::cout << "\t[" << pr << "]\n"; } + std::cout << "GET CPOINT RET: " + << mcc::network::mcc_netmsg_get_cpoint(p_res, 1, cp, mcc::MccCoordPairKind::COORDS_KIND_AZZD) << "\n"; + std::cout << "CPOINT.X = " << cp.X << ", CPOINT.Y = " << cp.Y << " (" + << mcc::network::mcc_pairkind2str(cp.pair_kind) << ")\n"; + + + sm = "ERROR "; + + mr = mcc::network::mcc_parse_netmsg(sm, p_res); + + std::cout << "MSG: <" << sm << ">\n"; + sm[0] = 'e'; + std::cout << "\t" << p_res.keyword_hash << "\n"; + std::cout << "\t[" << p_res.keyword << "]\n"; + for (auto const& pr : p_res.params) { + std::cout << "\t[" << pr << "]\n"; + } + + + mr = mcc::network::mcc_netmsg_construct(sm, mcc::network::MCC_COMMPROTO_KEYWORD_TARGET_STR, 12.3456789, + "34:21:56.132", mcc::MccCoordPairKind::COORDS_KIND_AZZD); + + std::cout << "\nNETMSG: [" << sm << "] (" << mr << ")\n"; + return 0; }