#pragma once #include #include #include "mcc_angle.h" #include "mcc_generics.h" #include "mcc_utils.h" namespace mcc::network { /* * network communication message format: * [[]param1[param-param-delim][param2]...] * e.g. * "TARGET 12:23:45.56 00:32:21.978\n" */ /* message */ 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 = " "; /* 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 /* 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; // } static constexpr MccCoordPairKind mcc_str2pairkind(traits::mcc_char_range auto const& spair) { 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: // 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 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" // 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 mount 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"; // 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}; // 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(netmsg) == 0) { return false; }; auto found = std::ranges::search(netmsg, MCC_COMMPROTO_KEYPARAM_DELIM_SEQ); if (std::distance(netmsg.begin(), found.begin()) == 0) { return false; } 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 or ERROR auto ok = hash == MCC_COMMPROTO_VALID_KEYS_HASH[0] || hash == MCC_COMMPROTO_VALID_KEYS_HASH[1]; if (!ok) { return false; } } parse_res.keyword_hash = hash; parse_res.keyword = {netmsg.begin(), found.begin()}; auto pars = netmsg | std::views::drop(std::distance(netmsg.begin(), found.end())) | std::views::split(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ); decltype(parse_res.params) res; for (auto const& el : pars) { // parameters std::back_inserter(res) = {el.begin(), el.end()}; } parse_res.params = std::move(res); 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