From 07cf211b3d4ea2b030f0d0d03be9c719c289d261 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Sun, 8 Feb 2026 21:50:48 +0300 Subject: [PATCH] ... --- CMakeLists.txt | 23 +- mcc_angle.h | 28 +- mcc_deser.h | 301 -------- mcc_deserializer.h | 374 ++++------ mcc_deserializer.h.old | 445 ++++++++++++ mcc_ser.h | 693 ------------------ mcc_serializer.h | 1272 ++++++++++++++-------------------- mcc_serializer.h.old | 910 ++++++++++++++++++++++++ tests/mcc_coord_test.cpp | 12 +- tests/mcc_telemetry_test.cpp | 11 + 10 files changed, 2085 insertions(+), 1984 deletions(-) delete mode 100644 mcc_deser.h create mode 100644 mcc_deserializer.h.old delete mode 100644 mcc_ser.h create mode 100644 mcc_serializer.h.old diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d70f90..d691f5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,17 +51,6 @@ endif() # ******* ERFA LIBRARY ******* -find_program(MESON_PROG NAMES meson HINTS ENV PATHS) - -if (NOT MESON_PROG) - message(FATAL "meson executable can not be found!!!") -endif() - -find_program(NINJA_PROG NAMES ninja ninja-build) - -if (NOT NINJA_PROG) - message(FATAL "ninja executable can not be found!!!") -endif() find_package(PkgConfig REQUIRED) @@ -92,6 +81,18 @@ if (NOT ERFALIB_FOUND) # set(CACHE{ERFALIB_LIBRARY_DIRS} TYPE PATH VALUE "${CMAKE_BINARY_DIR}/erfa_lib") # set(CACHE{ERFALIB_LIBRARIES} TYPE STRING VALUE "erfa;m") + find_program(MESON_PROG NAMES meson HINTS ENV PATHS) + + if (NOT MESON_PROG) + message(FATAL "meson executable can not be found!!!") + endif() + + find_program(NINJA_PROG NAMES ninja ninja-build) + + if (NOT NINJA_PROG) + message(FATAL "ninja executable can not be found!!!") + endif() + FetchContent_Declare(erfalib_project GIT_REPOSITORY "https://github.com/liberfa/erfa.git" diff --git a/mcc_angle.h b/mcc_angle.h index ac8a2f9..7309080 100644 --- a/mcc_angle.h +++ b/mcc_angle.h @@ -715,21 +715,21 @@ static constexpr MccCoordPairKind MccCoordStrToPairKind(R&& spair) } -enum class MccCoordinatePairRep : int { - MCC_COORDPAIR_REP_DEGREES, // both angles are in decimal degrees - MCC_COORDPAIR_REP_SXGM_HOURDEG, // X is in hour and Y is in degree sexagesimal representation - MCC_COORDPAIR_REP_SXGM_DEGDEG // both angles are in sexagesimal degrees -}; +// enum class MccCoordinatePairRep : int { +// MCC_COORDPAIR_REP_DEGREES, // both angles are in decimal degrees +// MCC_COORDPAIR_REP_SXGM_HOURDEG, // X is in hour and Y is in degree sexagesimal representation +// MCC_COORDPAIR_REP_SXGM_DEGDEG // both angles are in sexagesimal degrees +// }; -// default wide-acceptable sexagesimal representation -static constexpr MccCoordinatePairRep MccCoordinatePairToSxgmRep(MccCoordPairKind kind) -{ - return kind == MccCoordPairKind::COORDS_KIND_AZALT || kind == MccCoordPairKind::COORDS_KIND_AZZD || - kind == MccCoordPairKind::COORDS_KIND_XY || kind == MccCoordPairKind::COORDS_KIND_LONLAT || - kind == MccCoordPairKind::COORDS_KIND_GENERIC - ? MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_DEGDEG - : MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_HOURDEG; // RA-DEC or HA-DEC -} +// // default wide-acceptable sexagesimal representation +// static constexpr MccCoordinatePairRep MccCoordinatePairToSxgmRep(MccCoordPairKind kind) +// { +// return kind == MccCoordPairKind::COORDS_KIND_AZALT || kind == MccCoordPairKind::COORDS_KIND_AZZD || +// kind == MccCoordPairKind::COORDS_KIND_XY || kind == MccCoordPairKind::COORDS_KIND_LONLAT || +// kind == MccCoordPairKind::COORDS_KIND_GENERIC +// ? MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_DEGDEG +// : MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_HOURDEG; // RA-DEC or HA-DEC +// } } // namespace mcc::impl diff --git a/mcc_deser.h b/mcc_deser.h deleted file mode 100644 index de7ffd4..0000000 --- a/mcc_deser.h +++ /dev/null @@ -1,301 +0,0 @@ -#pragma once - -#include - -#include "mcc_epoch.h" -#include "mcc_serialization_common.h" - -namespace mcc::impl -{ - -enum class MccDeserializerErrorCode : int { - ERROR_OK, - ERROR_UNDERLYING_DESERIALIZER, - ERROR_INVALID_SERIALIZED_VALUE, - ERROR_COORD_TRANSFORM -}; - -} // namespace mcc::impl - - - -namespace std -{ - -template <> -class is_error_code_enum : public true_type -{ -}; - -} // namespace std - - -namespace mcc::impl -{ - - -// error category -struct MccDeserializerCategory : public std::error_category { - MccDeserializerCategory() : std::error_category() {} - - const char* name() const noexcept - { - return "MCC-DESERIALIZER-ERR-CATEGORY"; - } - - std::string message(int ec) const - { - MccDeserializerErrorCode err = static_cast(ec); - - switch (err) { - case MccDeserializerErrorCode::ERROR_OK: - return "OK"; - case MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER: - return "error returned by underlying deserializer"; - case MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE: - return "invalid serialized value"; - case MccDeserializerErrorCode::ERROR_COORD_TRANSFORM: - return "coordinates transformation error"; - default: - return "UNKNOWN"; - } - } - - static const MccDeserializerCategory& get() - { - static const MccDeserializerCategory constInst; - return constInst; - } -}; - - -inline std::error_code make_error_code(MccDeserializerErrorCode ec) -{ - return std::error_code(static_cast(ec), MccDeserializerCategory::get()); -} - - - -/* BASE DESERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */ - -struct MccDeserializerBase : mcc_deserializer_interface_t { - using typename mcc_deserializer_interface_t::error_t; - - virtual ~MccDeserializerBase() = default; - - -protected: - MccDeserializerBase() = default; - - // - // empty == true, if the 'input' is empty or if all elements consist of only spaces - // - static std::vector splitValueIntoElements(traits::mcc_input_char_range auto const& input, - mcc_serialization_params_c auto const& params, - bool& empty) - { - static_assert(std::ranges::contiguous_range, "NOT IMPLEMENTED FOR NON-CONTIGUIUS RANGES!!!"); - - std::vector res; - - if (std::ranges::size(input)) { - empty = true; - - std::ranges::for_each(std::views::split(input, params.elem_delim), [&res, &empty](auto const& el) { - std::back_inserter(res) = utils::trimSpaces(std::string_view{el.begin(), el.end()}); - if (empty && res.back().size()) { - empty = false; - } - }); - } else { - empty = true; - } - - return res; - } - - template R, typename... DeserParamsT> - static error_t deserializingRange(mcc_deserializer_c auto& dsr, - traits::mcc_input_char_range auto const& input, - R& r, - mcc_serialization_params_c auto const& params) - { - if (std::ranges::size(input) == 0) { // ignore an empty input, just return empty range?!! - r = R{}; - return MccDeserializerErrorCode::ERROR_OK; - } - - auto r_str = std::views::split(input, params.seq_delim); - VT val; - - auto it = r.begin(); - for (auto const& el : r_str) { - auto err = dsr(el, val, std::forward(params)...); - if (err) { - return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); - } - - if (it == r.end()) { - std::back_inserter(r) = val; - it = r.end(); - } else { - *it = val; - ++it; - } - } - - return MccDeserializerErrorCode::ERROR_OK; - } -}; - - -/* MAIN (FALLBACK) TEMPLATED IMPLEMENTATION */ - - -template -struct MccDeserializer : MccDeserializerBase { - static constexpr std::string_view deserializerName{"MCC-FALLBACK-DESERIALIZER"}; - - virtual ~MccDeserializer() = default; - - - template - error_t operator()(traits::mcc_input_char_range auto const& input, - VT& value, - ParamsT const& params = mcc_serialization_params_t{}) - { - if constexpr (std::is_arithmetic_v) { - auto v = mcc::utils::numFromStr(utils::trimSpaces(input)); - if (!v.has_value()) { - return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; - } - - value = v.value(); - } else if constexpr (mcc::traits::mcc_output_char_range) { - VT r; - if constexpr (traits::mcc_array_c) { - size_t N = - std::ranges::size(r) <= std::ranges::size(input) ? std::ranges::size(r) : std::ranges::size(input); - - for (size_t i = 0; i < N; ++i) { - r[i] = input[i]; - } - if (std::ranges::size(r) > N) { - for (size_t i = N; i < std::ranges::size(r); ++i) { - r[i] = '\0'; - } - } - } else { - std::ranges::copy(input, std::back_inserter(r)); - } - - value = r; - } else if constexpr (std::ranges::range) { - using el_t = std::ranges::range_value_t; - - static_assert(std::ranges::output_range, "INVALID RANGE TYPE!!!"); - - // no reference or constants allowed - static_assert(std::is_reference_v || std::is_const_v, "INVALID RANGE ELEMENT TYPE!!!"); - - MccDeserializer dsr; - return deserializingRange(dsr, input, value, params); - } else if constexpr (traits::mcc_time_duration_c) { - typename VT::rep vd; - - MccDeserializer dsr; - - auto err = dsr(trimSpaces(input), vd, params); - if (err) { - return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); - } - - value = VT{vd}; - } else { - static_assert(false, "UNSUPPORTED VALUE TYPE!!!"); - } - - - return MccDeserializerErrorCode::ERROR_OK; - } -}; - - -/* SPECIALIZATION FOR THE SOME CONCEPTS */ - -template -struct MccDeserializer : MccDeserializerBase { - static constexpr std::string_view deserializerName{"MCC-COORD-EPOCH-DESERIALIZER"}; - - template - error_t operator()(traits::mcc_input_char_range auto const& input, - VT& value, - ParamsT const& params = mcc_serialization_params_t{}) - { - bool ok = value.fromCharRange(input); - if (!ok) { - return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; - } - - return MccDeserializerErrorCode::ERROR_OK; - } -}; - - - -template - requires(!std::is_arithmetic_v && mcc_angle_c) -struct MccDeserializer : MccDeserializerBase { - static constexpr std::string_view deserializerName{"MCC-ANGLE-DESERIALIZER"}; - - template - error_t operator()(traits::mcc_input_char_range auto const& input, - VT& value, - ParamsT const& params = mcc_serialization_params_t{}) - { - bool hms = params.angle_format == MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS; - - auto res = utils::parsAngleString(input, hms); - if (res) { // returned angle is in degrees!!! - value = res.value() * MCC_DEGRESS_TO_RADS; - } else { - return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; - } - - return MccDeserializerErrorCode::ERROR_OK; - } -}; - - -template -struct MccDeserializer : MccDeserializerBase { - static constexpr std::string_view deserializerName{"MCC-SKYPOINT-DESERIALIZER"}; - - template - error_t operator()(traits::mcc_input_char_range auto const& input, - VT& value, - ParamsT const& params = mcc_serialization_params_t{}) - { - // valid format: XY[TIME-POINTPAIR-KIND] - // XY (assumed RADEC_ICRS and J2000.0 epoch) - - bool empty; - - auto elems = MccDeserializerBase::splitValueIntoElements(input, params, empty); - - if (empty || (elems.size() < 2) || (elems.size() == 3)) { - return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; - } - - MccCoordPairKind pair_kind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS}; - MccCelestialCoordEpoch epoch; // J2000.0 - - MccAngle x, y; - MccDeserializer dsr_ang; - - typename MccDeserializer::error_t dsr_err; - } -}; - - -} // namespace mcc::impl diff --git a/mcc_deserializer.h b/mcc_deserializer.h index b7c71c1..4f66e40 100644 --- a/mcc_deserializer.h +++ b/mcc_deserializer.h @@ -1,23 +1,10 @@ #pragma once - -/**************************************************************************************** - * * - * MOUNT CONTROL COMPONENTS LIBRARY * - * * - * * - * IMPLEMENTATION OF DESERIALIZER CLASSES * - * * - ****************************************************************************************/ - #include -#include -#include -#include "mcc_concepts.h" #include "mcc_coordinate.h" #include "mcc_epoch.h" -#include "mcc_error.h" +#include "mcc_serialization_common.h" namespace mcc::impl { @@ -89,34 +76,11 @@ inline std::error_code make_error_code(MccDeserializerErrorCode ec) } -template -struct mcc_deserializer_interface_t { - virtual ~mcc_deserializer_interface_t() = default; - typedef RetT error_t; +/* BASE DESERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */ - template SelfT, - traits::mcc_input_char_range R, - typename ValueT, - typename... DeserParamsT> - RetT operator()(this SelfT&& self, R const& input, ValueT& value, DeserParamsT&&... params) - { - return std::forward(self)(input, value); - } - -protected: - mcc_deserializer_interface_t() = default; -}; - -template -concept mcc_deserializer_c = std::derived_from>; - - -namespace details -{ - -struct MccDeserializerBase : mcc_deserializer_interface_t, utils::mcc_elem_sequence_with_delim_t { - using typename mcc_deserializer_interface_t::error_t; +struct MccDeserializerBase : mcc_deserializer_interface_t { + using typename mcc_deserializer_interface_t::error_t; virtual ~MccDeserializerBase() = default; @@ -127,14 +91,18 @@ protected: // // empty == true, if the 'input' is empty or if all elements consist of only spaces // - std::vector splitAggregateValue(traits::mcc_input_char_range auto const& input, bool& empty) const + static std::vector splitValueIntoElements(traits::mcc_input_char_range auto const& input, + mcc_serialization_params_c auto const& params, + bool& empty) { + static_assert(std::ranges::contiguous_range, "NOT IMPLEMENTED FOR NON-CONTIGUIUS RANGES!!!"); + std::vector res; if (std::ranges::size(input)) { empty = true; - std::ranges::for_each(std::views::split(input, this->_elementDelimiter), [&res, &empty](auto const& el) { + std::ranges::for_each(std::views::split(input, params.elem_delim), [&res, &empty](auto const& el) { std::back_inserter(res) = utils::trimSpaces(std::string_view{el.begin(), el.end()}); if (empty && res.back().size()) { empty = false; @@ -148,17 +116,17 @@ protected: } template R, typename... DeserParamsT> - error_t deserializingRange(mcc_deserializer_c auto& dsr, - traits::mcc_input_char_range auto const& input, - R& r, - DeserParamsT&&... params) const + static error_t deserializingRange(mcc_deserializer_c auto& dsr, + traits::mcc_input_char_range auto const& input, + R& r, + mcc_serialization_params_c auto const& params) { if (std::ranges::size(input) == 0) { // ignore an empty input, just return empty range?!! r = R{}; return MccDeserializerErrorCode::ERROR_OK; } - auto r_str = std::views::split(input, _seqDelimiter); + auto r_str = std::views::split(input, params.seq_delim); VT val; auto it = r.begin(); @@ -181,21 +149,21 @@ protected: } }; -} // namespace details +/* MAIN (FALLBACK) TEMPLATED IMPLEMENTATION */ -/* fallback template: basic deserializer */ template -class MccDeserializer : public details::MccDeserializerBase -{ -public: - using typename details::MccDeserializerBase::error_t; +struct MccDeserializer : MccDeserializerBase { + static constexpr std::string_view deserializerName{"MCC-FALLBACK-DESERIALIZER"}; virtual ~MccDeserializer() = default; - error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) + template + error_t operator()(traits::mcc_input_char_range auto const& input, + VT& value, + ParamsT const& params = mcc_serialization_params_t{}) { if constexpr (std::is_arithmetic_v) { auto v = mcc::utils::numFromStr(utils::trimSpaces(input)); @@ -232,13 +200,13 @@ public: static_assert(std::is_reference_v || std::is_const_v, "INVALID RANGE ELEMENT TYPE!!!"); MccDeserializer dsr; - return deserializingRange(dsr, input, value); + return deserializingRange(dsr, input, value, params); } else if constexpr (traits::mcc_time_duration_c) { typename VT::rep vd; MccDeserializer dsr; - auto err = dsr(trimSpaces(input), vd); + auto err = dsr(trimSpaces(input), vd, params); if (err) { return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); } @@ -254,72 +222,147 @@ public: }; - /* SPECIALIZATION FOR THE SOME CONCEPTS */ +template +struct MccDeserializer : MccDeserializerBase { + static constexpr std::string_view deserializerName{"MCC-COORD-EPOCH-DESERIALIZER"}; + + template + error_t operator()(traits::mcc_input_char_range auto const& input, + VT& value, + ParamsT const& params = mcc_serialization_params_t{}) + { + bool ok = value.fromCharRange(input); + if (!ok) { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + return MccDeserializerErrorCode::ERROR_OK; + } +}; + + template - requires(mcc_coord_epoch_c || (std::ranges::output_range> && - mcc_coord_epoch_c>>)) -class MccDeserializer : public details::MccDeserializerBase -{ -public: - using typename details::MccDeserializerBase::error_t; + requires(!std::is_arithmetic_v && mcc_angle_c) +struct MccDeserializer : MccDeserializerBase { + static constexpr std::string_view deserializerName{"MCC-ANGLE-DESERIALIZER"}; - virtual ~MccDeserializer() = default; - - - error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) + template + error_t operator()(traits::mcc_input_char_range auto const& input, + VT& value, + ParamsT const& params = mcc_serialization_params_t{}) { - if constexpr (mcc_coord_epoch_c) { // scalar - bool ok = value.fromCharRange(input); + bool hms = params.angle_format == MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS; + + auto res = utils::parsAngleString(input, hms); + if (res) { // returned angle is in degrees!!! + value = res.value() * MCC_DEGRESS_TO_RADS; + } else { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + return MccDeserializerErrorCode::ERROR_OK; + } +}; + + +template +struct MccDeserializer : MccDeserializerBase { + static constexpr std::string_view deserializerName{"MCC-SKYPOINT-DESERIALIZER"}; + + template + error_t operator()(traits::mcc_input_char_range auto const& input, + VT& value, + ParamsT const& params = mcc_serialization_params_t{}) + { + auto pars = params; + + // valid format: XY[TIME-POINTPAIR-KIND] + // XY (assumed RADEC_ICRS and J2000.0 epoch) + + bool empty; + + auto elems = MccDeserializerBase::splitValueIntoElements(input, params, empty); + + if (empty || (elems.size() < 2) || (elems.size() == 3)) { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + MccCoordPairKind pair_kind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS}; + MccCelestialCoordEpoch epoch; // J2000.0 + + MccAngle x, y; + MccDeserializer dsr_ang; + + typename MccDeserializer::error_t dsr_err; + + if (elems.size() > 3) { // full format + // deserialize pair kind string + pair_kind = MccCoordStrToPairKind(elems[3]); + if (pair_kind == MccCoordPairKind::COORDS_KIND_UNKNOWN) { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + // epoch + bool ok = epoch.fromCharRange(elems[2]); if (!ok) { return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; } - } else { // range - using value_t = std::remove_cv_t>; - MccDeserializer dsr; - - return deserializingRange(dsr, input, value); } - } -}; -template - requires(!std::is_arithmetic_v && - (mcc_angle_c || (std::ranges::output_range> && - mcc_angle_c>>))) -class MccDeserializer : public details::MccDeserializerBase -{ -public: - using typename details::MccDeserializerBase::error_t; + // deserialize X and Y + if (params.coordpair_format == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG) { + pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS; + } else { + pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS; + } - virtual ~MccDeserializer() = default; + dsr_err = dsr_ang(elems[0], x, pars); + if (dsr_err) { + return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); + } - // if hms == true and the input sequence is a sexagesimal string then it interpretates angle as hours:mins:secs - // otherwise the input sequence is interpretated as decimal or sexagesimal degrees - error_t operator()(traits::mcc_input_char_range auto const& input, VT& value, bool hms = false) - { - if constexpr (mcc_angle_c) { // scalar - auto res = utils::parsAngleString(input, hms); - if (res) { // returned angle is in degrees!!! - value = res.value() * MCC_DEGRESS_TO_RADS; - } else { + pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS; + dsr_err = dsr_ang(elems[1], y, pars); + if (dsr_err) { + return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); + } + + switch (pair_kind) { + case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: + value.from(MccSkyRADEC_ICRS{(double)x, (double)y}); + break; + case MccCoordPairKind::COORDS_KIND_RADEC_OBS: + value.from(MccSkyRADEC_OBS{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_RADEC_APP: + value.from(MccSkyRADEC_APP{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_HADEC_OBS: + value.from(MccSkyHADEC_OBS{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_HADEC_APP: + value.from(MccSkyHADEC_APP{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_AZZD: + value.from(MccSkyAZZD{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_AZALT: + value.from(MccSkyAZALT{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_XY: + value.from(MccGenXY{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_GENERIC: + value.from(MccCoordPair{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_LONLAT: + value.from(MccGeoLONLAT{(double)x, (double)y}); + break; + default: return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; - } - - } else { // range - using value_t = std::remove_cv_t>; - MccDeserializer dsr; - - if constexpr (std::invocable, decltype(input), VT&, bool>) { - // it is assumed here, the angle deserializer has the same behavior as the one implemented above - // (that is, it can interpret the third argument as a flag "false/true = only - // degrees/hour-like") - return deserializingRange(dsr, input, value, hms); - } else { - return deserializingRange(dsr, input, value); - } } return MccDeserializerErrorCode::ERROR_OK; @@ -327,119 +370,4 @@ public: }; -template - requires(mcc_skypoint_c || (std::ranges::output_range> && - mcc_skypoint_c>>)) -class MccDeserializer : public details::MccDeserializerBase -{ -public: - using typename details::MccDeserializerBase::error_t; - - virtual ~MccDeserializer() = default; - - error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) - { - if constexpr (mcc_skypoint_c) { // scalar: X[elem-delim]Y{[elem-delim]TIME-POINT[elem-delim]PAIR-KIND} - bool empty; // exactly 2 or >3 elements - auto elems = splitAggregateValue(input, empty); - - if (empty || (elems.size() < 2) || (elems.size() == 3)) { - return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; - } - - MccCoordPairKind pair_kind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS}; - MccCelestialCoordEpoch epoch; // J2000.0 - - MccAngle x, y; - MccDeserializer dsr_ang; - - typename MccDeserializer::error_t dsr_err; - - if (elems.size() >= 4) { // X, Y, TIME-POINT, PAIR-KIND - // first, get pair-kind - pair_kind = MccCoordStrToPairKind(elems[3]); - if (pair_kind == MccCoordPairKind::COORDS_KIND_UNKNOWN) { - return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; - } - - // epoch - bool ok = epoch.fromCharRange(elems[2]); - if (!ok) { - return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; - } - } - - if (elems.size() >= 2) { // if == 2: just X and Y (if so it is interpretated as RADEC_ICRS) - if (MccCoordinatePairToSxgmRep(pair_kind) == MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_HOURDEG) { - // if input X-angle is in sexagesimal from then interpretate it in hours::mins::secs format - if constexpr (std::invocable, decltype(elems[0]), MccAngle&, bool>) { - // it is assumed here, the angle deserializer has the same behavior as the one implemented above - // (that is, it can interpret the third argument as a flag "false/true = only - // degrees/hour-like") - dsr_err = dsr_ang(elems[0], x, true); - } else { - dsr_err = dsr_ang(elems[0], x); - } - } else { - dsr_err = dsr_ang(elems[0], x); - } - - if (dsr_err) { - return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); - } - - dsr_err = dsr_ang(elems[1], y); - - if (dsr_err) { - return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); - } - } - - - switch (pair_kind) { - case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: - value.from(MccSkyRADEC_ICRS{(double)x, (double)y}); - break; - case MccCoordPairKind::COORDS_KIND_RADEC_OBS: - value.from(MccSkyRADEC_OBS{(double)x, (double)y, epoch}); - break; - case MccCoordPairKind::COORDS_KIND_RADEC_APP: - value.from(MccSkyRADEC_APP{(double)x, (double)y, epoch}); - break; - case MccCoordPairKind::COORDS_KIND_HADEC_OBS: - value.from(MccSkyHADEC_OBS{(double)x, (double)y, epoch}); - break; - case MccCoordPairKind::COORDS_KIND_HADEC_APP: - value.from(MccSkyHADEC_APP{(double)x, (double)y, epoch}); - break; - case MccCoordPairKind::COORDS_KIND_AZZD: - value.from(MccSkyAZZD{(double)x, (double)y, epoch}); - break; - case MccCoordPairKind::COORDS_KIND_AZALT: - value.from(MccSkyAZALT{(double)x, (double)y, epoch}); - break; - case MccCoordPairKind::COORDS_KIND_XY: - value.from(MccGenXY{(double)x, (double)y, epoch}); - break; - case MccCoordPairKind::COORDS_KIND_GENERIC: - value.from(MccCoordPair{(double)x, (double)y, epoch}); - break; - case MccCoordPairKind::COORDS_KIND_LONLAT: - value.from(MccGeoLONLAT{(double)x, (double)y}); - break; - default: - return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; - } - - } else { // range - using value_t = std::remove_cv_t>; - MccDeserializer dsr; - - return deserializingRange(dsr, input, value); - } - - return MccDeserializerErrorCode::ERROR_OK; - } -}; - } // namespace mcc::impl diff --git a/mcc_deserializer.h.old b/mcc_deserializer.h.old new file mode 100644 index 0000000..b7c71c1 --- /dev/null +++ b/mcc_deserializer.h.old @@ -0,0 +1,445 @@ +#pragma once + + +/**************************************************************************************** + * * + * MOUNT CONTROL COMPONENTS LIBRARY * + * * + * * + * IMPLEMENTATION OF DESERIALIZER CLASSES * + * * + ****************************************************************************************/ + +#include +#include +#include + +#include "mcc_concepts.h" +#include "mcc_coordinate.h" +#include "mcc_epoch.h" +#include "mcc_error.h" + +namespace mcc::impl +{ + +enum class MccDeserializerErrorCode : int { + ERROR_OK, + ERROR_UNDERLYING_DESERIALIZER, + ERROR_INVALID_SERIALIZED_VALUE, + ERROR_COORD_TRANSFORM +}; + +} // namespace mcc::impl + + + +namespace std +{ + +template <> +class is_error_code_enum : public true_type +{ +}; + +} // namespace std + + +namespace mcc::impl +{ + + +// error category +struct MccDeserializerCategory : public std::error_category { + MccDeserializerCategory() : std::error_category() {} + + const char* name() const noexcept + { + return "MCC-DESERIALIZER-ERR-CATEGORY"; + } + + std::string message(int ec) const + { + MccDeserializerErrorCode err = static_cast(ec); + + switch (err) { + case MccDeserializerErrorCode::ERROR_OK: + return "OK"; + case MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER: + return "error returned by underlying deserializer"; + case MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE: + return "invalid serialized value"; + case MccDeserializerErrorCode::ERROR_COORD_TRANSFORM: + return "coordinates transformation error"; + default: + return "UNKNOWN"; + } + } + + static const MccDeserializerCategory& get() + { + static const MccDeserializerCategory constInst; + return constInst; + } +}; + + +inline std::error_code make_error_code(MccDeserializerErrorCode ec) +{ + return std::error_code(static_cast(ec), MccDeserializerCategory::get()); +} + + +template +struct mcc_deserializer_interface_t { + virtual ~mcc_deserializer_interface_t() = default; + + typedef RetT error_t; + + template SelfT, + traits::mcc_input_char_range R, + typename ValueT, + typename... DeserParamsT> + RetT operator()(this SelfT&& self, R const& input, ValueT& value, DeserParamsT&&... params) + { + return std::forward(self)(input, value); + } + +protected: + mcc_deserializer_interface_t() = default; +}; + +template +concept mcc_deserializer_c = std::derived_from>; + + +namespace details +{ + +struct MccDeserializerBase : mcc_deserializer_interface_t, utils::mcc_elem_sequence_with_delim_t { + using typename mcc_deserializer_interface_t::error_t; + + virtual ~MccDeserializerBase() = default; + + +protected: + MccDeserializerBase() = default; + + // + // empty == true, if the 'input' is empty or if all elements consist of only spaces + // + std::vector splitAggregateValue(traits::mcc_input_char_range auto const& input, bool& empty) const + { + std::vector res; + + if (std::ranges::size(input)) { + empty = true; + + std::ranges::for_each(std::views::split(input, this->_elementDelimiter), [&res, &empty](auto const& el) { + std::back_inserter(res) = utils::trimSpaces(std::string_view{el.begin(), el.end()}); + if (empty && res.back().size()) { + empty = false; + } + }); + } else { + empty = true; + } + + return res; + } + + template R, typename... DeserParamsT> + error_t deserializingRange(mcc_deserializer_c auto& dsr, + traits::mcc_input_char_range auto const& input, + R& r, + DeserParamsT&&... params) const + { + if (std::ranges::size(input) == 0) { // ignore an empty input, just return empty range?!! + r = R{}; + return MccDeserializerErrorCode::ERROR_OK; + } + + auto r_str = std::views::split(input, _seqDelimiter); + VT val; + + auto it = r.begin(); + for (auto const& el : r_str) { + auto err = dsr(el, val, std::forward(params)...); + if (err) { + return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); + } + + if (it == r.end()) { + std::back_inserter(r) = val; + it = r.end(); + } else { + *it = val; + ++it; + } + } + + return MccDeserializerErrorCode::ERROR_OK; + } +}; + +} // namespace details + + +/* fallback template: basic deserializer */ + +template +class MccDeserializer : public details::MccDeserializerBase +{ +public: + using typename details::MccDeserializerBase::error_t; + + virtual ~MccDeserializer() = default; + + + error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) + { + if constexpr (std::is_arithmetic_v) { + auto v = mcc::utils::numFromStr(utils::trimSpaces(input)); + if (!v.has_value()) { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + value = v.value(); + } else if constexpr (mcc::traits::mcc_output_char_range) { + VT r; + if constexpr (traits::mcc_array_c) { + size_t N = + std::ranges::size(r) <= std::ranges::size(input) ? std::ranges::size(r) : std::ranges::size(input); + + for (size_t i = 0; i < N; ++i) { + r[i] = input[i]; + } + if (std::ranges::size(r) > N) { + for (size_t i = N; i < std::ranges::size(r); ++i) { + r[i] = '\0'; + } + } + } else { + std::ranges::copy(input, std::back_inserter(r)); + } + + value = r; + } else if constexpr (std::ranges::range) { + using el_t = std::ranges::range_value_t; + + static_assert(std::ranges::output_range, "INVALID RANGE TYPE!!!"); + + // no reference or constants allowed + static_assert(std::is_reference_v || std::is_const_v, "INVALID RANGE ELEMENT TYPE!!!"); + + MccDeserializer dsr; + return deserializingRange(dsr, input, value); + } else if constexpr (traits::mcc_time_duration_c) { + typename VT::rep vd; + + MccDeserializer dsr; + + auto err = dsr(trimSpaces(input), vd); + if (err) { + return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); + } + + value = VT{vd}; + } else { + static_assert(false, "UNSUPPORTED VALUE TYPE!!!"); + } + + + return MccDeserializerErrorCode::ERROR_OK; + } +}; + + + +/* SPECIALIZATION FOR THE SOME CONCEPTS */ + + +template + requires(mcc_coord_epoch_c || (std::ranges::output_range> && + mcc_coord_epoch_c>>)) +class MccDeserializer : public details::MccDeserializerBase +{ +public: + using typename details::MccDeserializerBase::error_t; + + virtual ~MccDeserializer() = default; + + + error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) + { + if constexpr (mcc_coord_epoch_c) { // scalar + bool ok = value.fromCharRange(input); + if (!ok) { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + } else { // range + using value_t = std::remove_cv_t>; + MccDeserializer dsr; + + return deserializingRange(dsr, input, value); + } + } +}; + +template + requires(!std::is_arithmetic_v && + (mcc_angle_c || (std::ranges::output_range> && + mcc_angle_c>>))) +class MccDeserializer : public details::MccDeserializerBase +{ +public: + using typename details::MccDeserializerBase::error_t; + + virtual ~MccDeserializer() = default; + + // if hms == true and the input sequence is a sexagesimal string then it interpretates angle as hours:mins:secs + // otherwise the input sequence is interpretated as decimal or sexagesimal degrees + error_t operator()(traits::mcc_input_char_range auto const& input, VT& value, bool hms = false) + { + if constexpr (mcc_angle_c) { // scalar + auto res = utils::parsAngleString(input, hms); + if (res) { // returned angle is in degrees!!! + value = res.value() * MCC_DEGRESS_TO_RADS; + } else { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + } else { // range + using value_t = std::remove_cv_t>; + MccDeserializer dsr; + + if constexpr (std::invocable, decltype(input), VT&, bool>) { + // it is assumed here, the angle deserializer has the same behavior as the one implemented above + // (that is, it can interpret the third argument as a flag "false/true = only + // degrees/hour-like") + return deserializingRange(dsr, input, value, hms); + } else { + return deserializingRange(dsr, input, value); + } + } + + return MccDeserializerErrorCode::ERROR_OK; + } +}; + + +template + requires(mcc_skypoint_c || (std::ranges::output_range> && + mcc_skypoint_c>>)) +class MccDeserializer : public details::MccDeserializerBase +{ +public: + using typename details::MccDeserializerBase::error_t; + + virtual ~MccDeserializer() = default; + + error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) + { + if constexpr (mcc_skypoint_c) { // scalar: X[elem-delim]Y{[elem-delim]TIME-POINT[elem-delim]PAIR-KIND} + bool empty; // exactly 2 or >3 elements + auto elems = splitAggregateValue(input, empty); + + if (empty || (elems.size() < 2) || (elems.size() == 3)) { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + MccCoordPairKind pair_kind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS}; + MccCelestialCoordEpoch epoch; // J2000.0 + + MccAngle x, y; + MccDeserializer dsr_ang; + + typename MccDeserializer::error_t dsr_err; + + if (elems.size() >= 4) { // X, Y, TIME-POINT, PAIR-KIND + // first, get pair-kind + pair_kind = MccCoordStrToPairKind(elems[3]); + if (pair_kind == MccCoordPairKind::COORDS_KIND_UNKNOWN) { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + // epoch + bool ok = epoch.fromCharRange(elems[2]); + if (!ok) { + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + } + + if (elems.size() >= 2) { // if == 2: just X and Y (if so it is interpretated as RADEC_ICRS) + if (MccCoordinatePairToSxgmRep(pair_kind) == MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_HOURDEG) { + // if input X-angle is in sexagesimal from then interpretate it in hours::mins::secs format + if constexpr (std::invocable, decltype(elems[0]), MccAngle&, bool>) { + // it is assumed here, the angle deserializer has the same behavior as the one implemented above + // (that is, it can interpret the third argument as a flag "false/true = only + // degrees/hour-like") + dsr_err = dsr_ang(elems[0], x, true); + } else { + dsr_err = dsr_ang(elems[0], x); + } + } else { + dsr_err = dsr_ang(elems[0], x); + } + + if (dsr_err) { + return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); + } + + dsr_err = dsr_ang(elems[1], y); + + if (dsr_err) { + return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); + } + } + + + switch (pair_kind) { + case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: + value.from(MccSkyRADEC_ICRS{(double)x, (double)y}); + break; + case MccCoordPairKind::COORDS_KIND_RADEC_OBS: + value.from(MccSkyRADEC_OBS{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_RADEC_APP: + value.from(MccSkyRADEC_APP{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_HADEC_OBS: + value.from(MccSkyHADEC_OBS{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_HADEC_APP: + value.from(MccSkyHADEC_APP{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_AZZD: + value.from(MccSkyAZZD{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_AZALT: + value.from(MccSkyAZALT{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_XY: + value.from(MccGenXY{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_GENERIC: + value.from(MccCoordPair{(double)x, (double)y, epoch}); + break; + case MccCoordPairKind::COORDS_KIND_LONLAT: + value.from(MccGeoLONLAT{(double)x, (double)y}); + break; + default: + return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; + } + + } else { // range + using value_t = std::remove_cv_t>; + MccDeserializer dsr; + + return deserializingRange(dsr, input, value); + } + + return MccDeserializerErrorCode::ERROR_OK; + } +}; + +} // namespace mcc::impl diff --git a/mcc_ser.h b/mcc_ser.h deleted file mode 100644 index 8a1df2e..0000000 --- a/mcc_ser.h +++ /dev/null @@ -1,693 +0,0 @@ -#pragma once - -#include "mcc_coordinate.h" -#include "mcc_serialization_common.h" - -namespace mcc::impl -{ - -enum class MccSerializerErrorCode : int { - ERROR_OK, - ERROR_UNDERLYING_SERIALIZER, - ERROR_INVALID_EPOCH, - ERROR_COORD_TRANSFORM -}; - -} // namespace mcc::impl - - - -namespace std -{ - -template <> -class is_error_code_enum : public true_type -{ -}; - -} // namespace std - - -namespace mcc::impl -{ - -// error category -struct MccSerializerCategory : public std::error_category { - MccSerializerCategory() : std::error_category() {} - - const char* name() const noexcept - { - return "MCC-SERIALIZER-ERR-CATEGORY"; - } - - std::string message(int ec) const - { - MccSerializerErrorCode err = static_cast(ec); - - switch (err) { - case MccSerializerErrorCode::ERROR_OK: - return "OK"; - case MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER: - return "error returned by underlying serializer"; - case MccSerializerErrorCode::ERROR_INVALID_EPOCH: - return "invalid coordinate epoch"; - case MccSerializerErrorCode::ERROR_COORD_TRANSFORM: - return "coordinates transformation error"; - default: - return "UNKNOWN"; - } - } - - static const MccSerializerCategory& get() - { - static const MccSerializerCategory constInst; - return constInst; - } -}; - - -inline std::error_code make_error_code(MccSerializerErrorCode ec) -{ - return std::error_code(static_cast(ec), MccSerializerCategory::get()); -} - - -/* BASE SERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */ - -struct MccSerializerBase : mcc_serializer_interface_t { - virtual ~MccSerializerBase() = default; - - using typename mcc_serializer_interface_t::error_t; - -protected: - MccSerializerBase() = default; - - enum CoordType { CO_LON, CO_LAT }; - - static void addElemDelimiter(traits::mcc_output_char_range auto& output, - mcc_serialization_params_c auto const& params) - { - std::format_to(std::back_inserter(output), "{}", params.elem_delim); - } - - - static void addSeqDelimiter(traits::mcc_output_char_range auto& output, - mcc_serialization_params_c auto const& params) - { - std::format_to(std::back_inserter(output), "{}", params.seq_delim); - } - - - // set serialized angle format according to coordinates pair format and type of - // serializing mcc_coord_pair_c::pairKind - template - static void angleFormatFromCoordPairType(mcc_serialization_params_c auto& pars) - { - if (pars.coordpair_format == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_DEGREES) { - pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_DEGREES; - } else { // format to sexagesimal form according to celestial coordinate type - if constexpr (TYPE == MccSerializerBase::CO_LON) { - if (pars.coordpair_format == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGDEG) { - pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS; - } else if (pars.coordpair_format == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG) { - if constexpr (PAIRKIND == MccCoordPairKind::COORDS_KIND_AZZD || - PAIRKIND == MccCoordPairKind::COORDS_KIND_AZALT || - PAIRKIND == MccCoordPairKind::COORDS_KIND_XY || - PAIRKIND == MccCoordPairKind::COORDS_KIND_GENERIC) { // azimuth is in degrees - pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS; - } else { // RA or HA - pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS; - } - } else { - // !!!!!!!!!!!!!!!!!! - } - } else { // Y-coordinates (co-latitude one, DEC, ALT, ZD, generic X) is always in degrees for celestial - // point - pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS; - } - } - } - - template - requires(std::ranges::input_range && std::same_as>) - static error_t serializeRange(mcc_serializer_c auto& sr, - R const& r, - traits::mcc_output_char_range auto& output, - mcc_serialization_params_c auto const& params) - { - size_t i = 0, N = std::ranges::size(r); - - for (auto const& el : r) { - auto err = sr(output, el, params); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - if (++i < N) { - MccSerializerBase::addSeqDelimiter(output, params); - } - } - - return MccSerializerErrorCode::ERROR_OK; - } -}; - - -/* MAIN (FALLBACK) TEMPLATED IMPLEMENTATION */ - -template -struct MccSerializer : MccSerializerBase { - constexpr static std::string_view serializerName{"MCC-FALLBACK-SERIALIZER"}; - - template - error_t operator()(traits::mcc_output_char_range auto& output, - VT const& value, - ParamsT const& params = mcc_serialization_params_t{}) - { - if constexpr (std::formattable) { - } else if constexpr (std::convertible_to) { - auto err = MccSerializer{}(output, static_cast(value), params); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - } else if constexpr (std::ranges::range) { - using value_t = std::remove_cv_t>; - - // special range (character sequence) - if constexpr (std::same_as) { - std::string str; - std::ranges::copy(value, std::back_inserter(str)); - - auto err = MccSerializer{}(output, str, params); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - } else { - MccSerializer sr; - - return MccSerializerBase::serializeRange(sr, value, output, params); - } - } else { - static_assert(false, "UNSUPPORTED TYPE!!!"); - } - - return MccSerializerErrorCode::ERROR_OK; - } -}; - - -/* SPECIALIZATION FOR THE SOME CONCEPTS */ - -/* std::chrono::sys_time variants and its sequence */ - -template - requires traits::mcc_systime_c -struct MccSerializer : MccSerializerBase { - virtual ~MccSerializer() = default; - - constexpr static std::string_view serializerName{"MCC-SYSTIME-SERIALIZER"}; - - template - error_t operator()(traits::mcc_output_char_range auto& output, - VT const& value, - ParamsT const& params = mcc_serialization_params_t{}) - { - std::vformat_to(std::back_inserter(output), params.systime_format, std::make_format_args(value)); - - return MccSerializerErrorCode::ERROR_OK; - } -}; - - -template - requires(!std::is_arithmetic_v && mcc_angle_c) -struct MccSerializer : MccSerializerBase { - constexpr static std::string_view serializerName{"MCC-ANGLE-SERIALIZER"}; - - template - error_t operator()(traits::mcc_output_char_range auto& output, - VT const& value, - ParamsT const& params = mcc_serialization_params_t{}) - { - double v = (double)value; // radians (see mcc_angle_c concept) - std::string sgm; - - switch (params.angle_format) { - case MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_DEGREES: - v *= MCC_RADS_TO_DEGRESS; - return MccSerializer{}(output, v, params); - case MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS: - if (params.norm_sxgm) { - sgm = utils::rad2sxg(v, false, params.angle_prec.deg_prec); - } else { - sgm = utils::rad2sxg(v, false, params.angle_prec.deg_prec); - } - - break; - case MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS: - if (params.norm_sxgm) { - sgm = utils::rad2sxg(v, true, params.angle_prec.hour_prec); - } else { - sgm = utils::rad2sxg(v, true, params.angle_prec.hour_prec); - } - - break; - default: - break; - } - - auto err = MccSerializer{}(output, sgm, params); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - return MccSerializerErrorCode::ERROR_OK; - }; -}; - - -template -struct MccSerializer : MccSerializerBase { - constexpr static std::string_view serializerName{"MCC-COORD-EPOCH-SERIALIZER"}; - - template - error_t operator()(traits::mcc_output_char_range auto& output, - VT const& value, - ParamsT const& params = mcc_serialization_params_t{}) - { - double jd; - - switch (params.timepoint_format) { - case MccTimePointFormat::MCC_TIMEPOINT_FORMAT_DATE: { - auto tp = value.UTC(); - auto err = MccSerializer{}(output, tp, params); - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - case MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JEPOCH: { - auto ep = value.JEpoch(); - auto err = MccSerializer{}(output, ep, params); - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - case MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JD: - jd = value.MJD() + MCC_J2000_MJD; - break; - case MccTimePointFormat::MCC_TIMEPOINT_FORMAT_MJD: - jd = value.MJD(); - break; - } - - - auto err = MccSerializer{}(output, jd, params); - - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - return MccSerializerErrorCode::ERROR_OK; - } -}; - - -template -struct MccSerializer : MccSerializerBase { - constexpr static std::string_view serializerName{"MCC-COORD-PAIR-SERIALIZER"}; - - template - error_t operator()(traits::mcc_output_char_range auto& output, - VT const& value, - ParamsT const& params = mcc_serialization_params_t{}) - { - auto pars = params; - - pars.norm_sxgm = true; - pars.coordpair_format = MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG; - - // X-coordinate - MccSerializerBase::angleFormatFromCoordPairType(pars); - - auto err = MccSerializer{}(output, value.x(), params); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, params); - - pars.norm_sxgm = false; // do not normalize co-latitude angle - - // Y-coordinate - MccSerializerBase::angleFormatFromCoordPairType(pars); - - err = MccSerializer{}(output, value.y(), params); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, params); - - // epoch - auto ep = value.epoch(); - auto ep_err = MccSerializer{}(output, ep, params); - if (ep_err) { - return mcc_deduced_err(ep_err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - - MccSerializerBase::addElemDelimiter(output, params); - - // pair kind - return mcc_deduced_err(MccSerializer{}(output, MccCoordPairKindToStr(VT::pairKind), params), - MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - - return MccSerializerErrorCode::ERROR_OK; - } -}; - - -template -struct MccSerializer : MccSerializerBase { - constexpr static std::string_view serializerName{"MCC-SKYPOINT-SERIALIZER"}; - - template - error_t operator()(traits::mcc_output_char_range auto& output, - VT const& value, - ParamsT const& params = mcc_serialization_params_t{}) - { - auto serialize_cpair = [&](T& cp) { - auto ccte_err = value.to(cp); - if (ccte_err) { - return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - auto err = MccSerializer{}(output, cp, params); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - return MccSerializerErrorCode::ERROR_OK; - }; - - switch (value.pairKind()) { - case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: { - MccSkyRADEC_ICRS cp; - return serialize_cpair(cp); - } - case MccCoordPairKind::COORDS_KIND_RADEC_OBS: { - MccSkyRADEC_OBS cp; - return serialize_cpair(cp); - } - case MccCoordPairKind::COORDS_KIND_RADEC_APP: { - MccSkyRADEC_APP cp; - return serialize_cpair(cp); - } - case MccCoordPairKind::COORDS_KIND_HADEC_OBS: { - MccSkyHADEC_OBS cp; - return serialize_cpair(cp); - } - case MccCoordPairKind::COORDS_KIND_HADEC_APP: { - MccSkyHADEC_APP cp; - return serialize_cpair(cp); - } - case MccCoordPairKind::COORDS_KIND_AZZD: { - MccSkyAZZD cp; - return serialize_cpair(cp); - } - case MccCoordPairKind::COORDS_KIND_AZALT: { - MccSkyAZALT cp; - return serialize_cpair(cp); - } - case MccCoordPairKind::COORDS_KIND_XY: { - MccGenXY cp; - return serialize_cpair(cp); - } - case MccCoordPairKind::COORDS_KIND_GENERIC: { - MccGenXY cp; - return serialize_cpair(cp); - } - default: - // !!!!!!!!!!!! - } - } -}; - - -template -struct MccSerializer : MccSerializerBase { - constexpr static std::string_view serializerName{"MCC-TELEMETRY-DATA-SERIALIZER"}; - - template - error_t operator()(traits::mcc_output_char_range auto& output, - VT const& value, - ParamsT const& params = mcc_serialization_params_t{}) - { - // FORMAT: RA_OBS_MOUNT, DEC_OBS_MOUNT, RA_APP_MOUNT, DEC_APP_MOUNT, HA_APP_MOUNT, AZ_MOUNT, ZD_MOUNT, - // REFR_CORR_MOUNT, ENC_X, ENC_Y, PCM_X, PCM_Y, RA_APP_TAG, DEC_APP_TAG, AZ_TAG, ZD_TAG, LAST, EO, TIMEPOINT, - // STATUS - - // NOTE: One must assume that the returned RA coordinates are in format of underlying celestial coordinate - // transformation engine used in the mcc_skypoint_c class implementation. E.g. ERFA-library uses the - // CIO-based representation of RA - - auto pars_h = params; - - auto pars_d = params; - - - pars_h.norm_sxgm = true; - pars_h.coordpair_format = MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG; - pars_d.norm_sxgm = true; - pars_d.coordpair_format = MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG; - - MccSkyRADEC_OBS rd_obs; - MccSkyRADEC_APP rd_app; - MccSkyHADEC_APP hd_app; - MccSkyAZZD azzd; - - // quantities in hour representation - MccSerializerBase::angleFormatFromCoordPairType(pars_h); - - // quantities in degree representation - MccSerializerBase::angleFormatFromCoordPairType(pars_d); - - MccSerializer ang_sr; - - // RA_OBS_MOUNT, DEC_OBS_MOUNT - auto ccte_err = value.mountPos.toAtSameEpoch(rd_obs); - if (ccte_err) { - return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - auto err = ang_sr(output, rd_obs.x(), pars_h); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - err = ang_sr(output, rd_obs.y(), pars_h); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - // RA_APP_MOUNT, DEC_APP_MOUNT - ccte_err = value.mountPos.toAtSameEpoch(rd_app); - if (ccte_err) { - return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - err = ang_sr(output, rd_app.x(), pars_h); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - err = ang_sr(output, rd_app.y(), pars_d); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - // HA_APP_MOUNT - ccte_err = value.mountPos.toAtSameEpoch(hd_app); - if (ccte_err) { - return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - err = ang_sr(output, hd_app.x(), pars_h); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - - MccSerializerBase::addElemDelimiter(output, pars_h); - - // AZ_MOUNT, ZD_MOUNT - ccte_err = value.mountPos.toAtSameEpoch(azzd); - if (ccte_err) { - return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - err = ang_sr(output, azzd.x(), pars_d); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - err = ang_sr(output, azzd.y(), pars_d); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - // refraction correction - - MccAngle ang; - ccte_err = value.mountPos.refractCorrection(&ang); - if (ccte_err) { - return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - err = ang_sr(output, ang, pars_d); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - - // encoder X and Y - - err = ang_sr(output, value.hwState.XY.x(), pars_d); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - err = ang_sr(output, value.hwState.XY.y(), pars_d); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - - // PCM X and Y - - err = ang_sr(output, value.pcmCorrection.pcmX(), pars_d); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - err = ang_sr(output, value.pcmCorrection.pcmY(), pars_d); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - - // RA_APP_TAG, DEC_APP_TAG - ccte_err = value.targetPos.toAtSameEpoch(rd_app); - if (ccte_err) { - return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - err = ang_sr(output, rd_app.x(), pars_h); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - err = ang_sr(output, rd_app.y(), pars_d); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - // AZ_TAG, ZD_TAG - ccte_err = value.targetPos.toAtSameEpoch(azzd); - if (ccte_err) { - return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - err = ang_sr(output, azzd.x(), pars_d); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - err = ang_sr(output, azzd.y(), pars_d); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - - // LAST, local apparent sideral time - - ccte_err = value.mountPos.appSideralTime(&ang, true); - if (ccte_err) { - return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - err = ang_sr(output, ang, pars_h); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - - // EO, equation of origins - - ccte_err = value.mountPos.EO(&ang); - if (ccte_err) { - return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - err = ang_sr(output, ang, pars_h); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - // coordinates epoch - - auto ep_err = MccSerializer{}(output, value.mountPos.epoch(), pars_d); - if (ep_err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - MccSerializerBase::addElemDelimiter(output, pars_h); - - - // status (it must be formattable, see mcc_concepts.h) - - auto st_err = - MccSerializer{}(output, value.hwState.movementState, pars_d); - if (st_err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - - return MccSerializerErrorCode::ERROR_OK; - } -}; - - -static_assert(mcc_serializer_c>, "!!!"); - -} // namespace mcc::impl diff --git a/mcc_serializer.h b/mcc_serializer.h index dabc9c9..dad96c6 100644 --- a/mcc_serializer.h +++ b/mcc_serializer.h @@ -1,28 +1,7 @@ #pragma once - - -/**************************************************************************************** - * * - * MOUNT CONTROL COMPONENTS LIBRARY * - * * - * * - * IMPLEMENTATION OF SERIALIZER CLASSES * - * * - ****************************************************************************************/ - - - -#include -#include - -#include "mcc_concepts.h" -#include "mcc_constants.h" #include "mcc_coordinate.h" -#include "mcc_epoch.h" -#include "mcc_error.h" -#include "mcc_traits.h" -#include "mcc_utils.h" +#include "mcc_serialization_common.h" namespace mcc::impl { @@ -52,7 +31,6 @@ class is_error_code_enum : public true_type namespace mcc::impl { - // error category struct MccSerializerCategory : public std::error_category { MccSerializerCategory() : std::error_category() {} @@ -94,58 +72,78 @@ inline std::error_code make_error_code(MccSerializerErrorCode ec) } +/* BASE SERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */ -template -struct mcc_serializer_interface_t { - virtual ~mcc_serializer_interface_t() = default; - - typedef RetT error_t; - - template SelfT, - traits::mcc_output_char_range R, - typename ValueT, - typename FmtT> - requires(std::same_as, FmtT> || std::same_as) - RetT operator()(this SelfT&& self, R& output, ValueT const& value, FmtT fmt) - { - return std::forward(self)(output, value, std::move(fmt)); - } - -protected: - mcc_serializer_interface_t() = default; -}; - -template -concept mcc_serializer_c = std::derived_from>; - - -namespace details -{ - -struct MccSerializerBase : mcc_serializer_interface_t, utils::mcc_elem_sequence_with_delim_t { - using typename mcc_serializer_interface_t::error_t; - +struct MccSerializerBase : mcc_serializer_interface_t { virtual ~MccSerializerBase() = default; + using typename mcc_serializer_interface_t::error_t; + protected: MccSerializerBase() = default; + enum CoordType { CO_LON, CO_LAT }; + + static void addElemDelimiter(traits::mcc_output_char_range auto& output, + mcc_serialization_params_c auto const& params) + { + std::format_to(std::back_inserter(output), "{}", params.elem_delim); + } + + + static void addSeqDelimiter(traits::mcc_output_char_range auto& output, + mcc_serialization_params_c auto const& params) + { + std::format_to(std::back_inserter(output), "{}", params.seq_delim); + } + + + // set serialized angle format according to coordinates pair format and type of + // serializing mcc_coord_pair_c::pairKind + template + static void angleFormatFromCoordPairType(mcc_serialization_params_c auto& pars) + { + if (pars.coordpair_format == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_DEGREES) { + pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_DEGREES; + } else { // format to sexagesimal form according to celestial coordinate type + if constexpr (TYPE == MccSerializerBase::CO_LON) { + if (pars.coordpair_format == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGDEG) { + pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS; + } else if (pars.coordpair_format == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG) { + if constexpr (PAIRKIND == MccCoordPairKind::COORDS_KIND_AZZD || + PAIRKIND == MccCoordPairKind::COORDS_KIND_AZALT || + PAIRKIND == MccCoordPairKind::COORDS_KIND_XY || + PAIRKIND == MccCoordPairKind::COORDS_KIND_GENERIC) { // azimuth is in degrees + pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS; + } else { // RA or HA + pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS; + } + } else { + // !!!!!!!!!!!!!!!!!! + } + } else { // Y-coordinates (co-latitude one, DEC, ALT, ZD, generic X) is always in degrees for celestial + // point + pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS; + } + } + } + template requires(std::ranges::input_range && std::same_as>) - error_t serializingRange(mcc_serializer_c auto& sr, - R const& r, - traits::mcc_output_char_range auto& output, - auto fmt) const + static error_t serializeRange(mcc_serializer_c auto& sr, + R const& r, + traits::mcc_output_char_range auto& output, + mcc_serialization_params_c auto const& params) { size_t i = 0, N = std::ranges::size(r); for (auto const& el : r) { - auto err = sr(output, el, std::move(fmt)); + auto err = sr(output, el, params); if (err) { return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); } if (++i < N) { - std::format_to(std::back_inserter(output), "{}", _seqDelimiter); + MccSerializerBase::addSeqDelimiter(output, params); } } @@ -153,44 +151,25 @@ protected: } }; -} // namespace details +/* MAIN (FALLBACK) TEMPLATED IMPLEMENTATION */ -/* fallback template */ template -class MccSerializer : public details::MccSerializerBase -// class MccSerializer : public mcc_serializer_interface_t -{ -public: - // default delimiter - static constexpr std::string_view defaultRangeDelimiter{","}; +struct MccSerializer : MccSerializerBase { + constexpr static std::string_view serializerName{"MCC-FALLBACK-SERIALIZER"}; - // typedef impl::MccError error_t; - - MccSerializer() = default; - - ~MccSerializer() = default; - - template - error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) + template + error_t operator()(traits::mcc_output_char_range auto& output, + VT const& value, + ParamsT const& params = mcc_serialization_params_t{}) { if constexpr (std::formattable) { - if constexpr (std::is_null_pointer_v) { - std::format_to(std::back_inserter(output), "{}", value); - } else if constexpr (traits::mcc_input_char_range) { - std::string_view sfmt{fmt.begin(), fmt.end()}; - std::vformat_to(std::back_inserter(output), sfmt, std::make_format_args(value)); - } else if constexpr (std::same_as, FmtT>) { - std::format_to(std::back_inserter(output), std::move(fmt), value); - } else { - static_assert(false, "INVALID FORMAT STRING TYPE!!!"); - } + std::format_to(std::back_inserter(output), "{}", value); } else if constexpr (std::convertible_to) { - auto err = MccSerializer{}(output, static_cast(value), std::move(fmt)); + auto err = MccSerializer{}(output, static_cast(value), params); if (err) { return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); } - } else if constexpr (std::ranges::range) { using value_t = std::remove_cv_t>; @@ -199,7 +178,7 @@ public: std::string str; std::ranges::copy(value, std::back_inserter(str)); - auto err = MccSerializer{}(output, str, std::move(fmt)); + auto err = MccSerializer{}(output, str, params); if (err) { return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); } @@ -207,7 +186,7 @@ public: } else { MccSerializer sr; - return serializingRange(sr, value, output, std::move(fmt)); + return MccSerializerBase::serializeRange(sr, value, output, params); } } else { static_assert(false, "UNSUPPORTED TYPE!!!"); @@ -218,693 +197,508 @@ public: }; - /* SPECIALIZATION FOR THE SOME CONCEPTS */ +/* std::chrono::sys_time variants and its sequence */ template - requires(traits::mcc_systime_c || - (std::ranges::input_range && traits::mcc_systime_c>>)) -class MccSerializer : public details::MccSerializerBase -{ -public: + requires traits::mcc_systime_c +struct MccSerializer : MccSerializerBase { virtual ~MccSerializer() = default; - template - error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) - { - if constexpr (std::ranges::input_range) { - using value_t = std::remove_cv_t>; - MccSerializer sr; + constexpr static std::string_view serializerName{"MCC-SYSTIME-SERIALIZER"}; - return serializingRange(sr, value, output, std::move(fmt)); - } else { // scalar - if constexpr (std::is_null_pointer_v) { - std::format_to(std::back_inserter(output), "{:%FT%T}", value); - } else if constexpr (traits::mcc_input_char_range) { - std::string_view sfmt{fmt.begin(), fmt.end()}; - if (sfmt == "{}") { - sfmt = "{:%FT%T}"; - } - std::vformat_to(std::back_inserter(output), sfmt, std::make_format_args(value)); - } else if constexpr (std::same_as, FmtT>) { - std::string_view sfmt{fmt.get().begin(), fmt.get().end()}; - if (sfmt == "{}") { - std::format_to(std::back_inserter(output), "{:%FT%T}", value); + template + error_t operator()(traits::mcc_output_char_range auto& output, + VT const& value, + ParamsT const& params = mcc_serialization_params_t{}) + { + std::vformat_to(std::back_inserter(output), params.systime_format, std::make_format_args(value)); + + return MccSerializerErrorCode::ERROR_OK; + } +}; + + +template + requires(!std::is_arithmetic_v && mcc_angle_c) +struct MccSerializer : MccSerializerBase { + constexpr static std::string_view serializerName{"MCC-ANGLE-SERIALIZER"}; + + template + error_t operator()(traits::mcc_output_char_range auto& output, + VT const& value, + ParamsT const& params = mcc_serialization_params_t{}) + { + double v = (double)value; // radians (see mcc_angle_c concept) + std::string sgm; + + switch (params.angle_format) { + case MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_DEGREES: + v *= MCC_RADS_TO_DEGRESS; + return MccSerializer{}(output, v, params); + case MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS: + if (params.norm_sxgm) { + sgm = utils::rad2sxg(v, false, params.angle_prec.deg_prec); } else { - std::format_to(std::back_inserter(output), std::move(fmt), value); + sgm = utils::rad2sxg(v, false, params.angle_prec.deg_prec); } - } else { - static_assert(false, "INVALID FORMAT STRING TYPE!!!"); - } + + break; + case MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS: + if (params.norm_sxgm) { + sgm = utils::rad2sxg(v, true, params.angle_prec.hour_prec); + } else { + sgm = utils::rad2sxg(v, true, params.angle_prec.hour_prec); + } + + break; + default: + break; + } + + auto err = MccSerializer{}(output, sgm, params); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); } return MccSerializerErrorCode::ERROR_OK; - } -}; - - -template - requires(!std::is_arithmetic_v && - (mcc_angle_c || - (std::ranges::input_range && mcc_angle_c>>))) -class MccSerializer : public virtual details::MccSerializerBase -// class MccSerializer : public mcc_serializer_interface_t -{ -public: - enum SerializedCoordFormat { - CFMT_DEGREES, // floating-point representation in degrees - CFMT_SGM_HOURS, // sexagesimal representation: HH:MM:SS.SSSSSS (hours:minutes:seconds) - CFMT_SGM_DEGS // sexagesimal representation: DD:MM:SS.SSSSS (degrees:arcmins::arcsecs) }; - - struct SexagesimalCoordPrec { - uint8_t hour_prec = 2; // number of decimal places in hour seconds - uint8_t deg_prec = 1; // number of decimal places in arcseconds - }; - - // typedef impl::MccError error_t; - - virtual ~MccSerializer() = default; - - void setCoordFormat(SerializedCoordFormat format) - { - _coordFormat = format; - } - - SerializedCoordFormat getCoordFormat() const - { - return _coordFormat; - } - - - void setCoordPrec(SexagesimalCoordPrec prec) - { - _coordPrec = std::move(prec); - } - - SexagesimalCoordPrec getCoordPrec() const - { - return _coordPrec; - } - - template - error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) - { - if constexpr (std::ranges::input_range) { - using value_t = std::remove_cv_t>; - MccSerializer sr; - sr.setCoordFormat(_coordFormat); - sr.setCoordPrec(_coordPrec); - - return serializingRange(sr, value, output, std::move(fmt)); - } else { // scalar - double v = (double)value; // radians - std::string sgm; - - switch (_coordFormat) { - case MccSerializer::CFMT_DEGREES: - v *= 180.0 / std::numbers::pi; - return MccSerializer{}(output, v, std::move(fmt)); - case MccSerializer::CFMT_SGM_HOURS: - sgm = utils::rad2sxg(v, true, _coordPrec.load().hour_prec); - break; - case MccSerializer::CFMT_SGM_DEGS: - sgm = utils::rad2sxg(v, false, _coordPrec.load().deg_prec); - break; - default: - break; - } - auto err = MccSerializer{}(output, sgm, std::move(fmt)); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - } - - return MccSerializerErrorCode::ERROR_OK; - } - -protected: - // std::atomic _coordFormat{MccSerializer::CFMT_DEGREES}; - std::atomic _coordFormat{MccSerializer::CFMT_SGM_DEGS}; - std::atomic _coordPrec{SexagesimalCoordPrec{.hour_prec = 2, .deg_prec = 1}}; }; +template +struct MccSerializer : MccSerializerBase { + constexpr static std::string_view serializerName{"MCC-COORD-EPOCH-SERIALIZER"}; -template - requires(mcc_coord_epoch_c || - (std::ranges::input_range && mcc_coord_epoch_c>>)) -class MccSerializer : public virtual details::MccSerializerBase -{ -public: - enum TimePointFormat { TP_FORMAT_DATE, TP_FORMAT_MJD, TP_FORMAT_JEPOCH }; - - virtual ~MccSerializer() = default; - - void setTimePointFormat(TimePointFormat format) + template + error_t operator()(traits::mcc_output_char_range auto& output, + VT const& value, + ParamsT const& params = mcc_serialization_params_t{}) { - _tpFormat = format; - } + double jd; - TimePointFormat getTimePointFormat() const - { - return _tpFormat; - } - - - template - error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) - { - if constexpr (std::ranges::input_range) { - using value_t = std::remove_cv_t>; - MccSerializer sr; - sr.setTimePointFormat(_tpFormat); - - return serializingRange(sr, value, output, std::move(fmt)); - } else { - switch (_tpFormat) { - case TP_FORMAT_DATE: { - auto utc = value.UTC(); - auto err = MccSerializer>{}(output, utc, std::move(fmt)); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - } break; - case TP_FORMAT_MJD: { - double mjd = value.MJD(); - auto err = MccSerializer{}(output, mjd, std::move(fmt)); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - } break; - case TP_FORMAT_JEPOCH: { - auto jepoch = value.JEpoch(); - auto err = MccSerializer>{}(output, jepoch, std::move(fmt)); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - } - default: - break; - } - - return MccSerializerErrorCode::ERROR_OK; - } - } - -protected: - std::atomic _tpFormat{TP_FORMAT_DATE}; -}; - - - -template - requires(mcc_coord_pair_c || - (std::ranges::input_range && mcc_coord_pair_c>>)) -class MccSerializer : public std::conditional_t, - MccSerializer>, - MccSerializer>, - public std::conditional_t, - MccSerializer>, - MccSerializer> -// class MccSerializer : public mcc_serializer_interface_t -{ -protected: - typedef std::conditional_t, - MccSerializer>, - MccSerializer> - base_ser_epoch_t; - - typedef std:: - conditional_t, MccSerializer>, MccSerializer> - base_ser_angle_t; - -public: - using typename details::MccSerializerBase::error_t; - - template - error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) - { - if constexpr (std::ranges::input_range) { - using value_t = std::remove_cv_t>; - MccSerializer sr; - - sr.setTimePointFormat(this->_tpFormat); - sr.setCoordFormat(this->_coordFormat); - sr.setCoordPrec(this->_coordPrec); - - return serializingRange(sr, value, output, std::move(fmt)); - } else { // scalar: format X[elem-delimiter]Y[elem-delimiter]epoch[elem-delimiter]pair-kind - - // WARNING: still ignore format string!!! - - error_t err; - - // format to sexagesimal form according to celestial coordinate type (general XY-type is skipped) - if (this->_coordFormat != base_ser_angle_t::CFMT_DEGREES) { - auto prev_format = this->_coordFormat.load(); - - if constexpr (VT::pairKind == MccCoordPairKind::COORDS_KIND_AZZD || - VT::pairKind == MccCoordPairKind::COORDS_KIND_AZALT) { // azimuth is in degrees - - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS); - err = base_ser_angle_t::operator()(output, MccAngle{(double)value.x()}); - } else { // RA or HA are in hours - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); - err = base_ser_angle_t::operator()(output, MccAngle{(double)value.x()}); - } - - this->setCoordFormat(prev_format); // restore previous value - } else { // here all angles in deciamal degrees - err = base_ser_angle_t::operator()(output, MccAngle{(double)value.x()}); - } - - if (err) { - return err; - } - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - - // format to sexagesimal form according to celestial coordinate type (general XY-type is skipped) - // Y-coordinate of the celestial point is always in degrees - if (this->_coordFormat != base_ser_angle_t::CFMT_DEGREES) { - auto prev_format = this->_coordFormat.load(); - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS); - - err = base_ser_angle_t::operator()(output, MccAngle{(double)value.y()}); - - this->setCoordFormat(prev_format); // restore previous value - } else { // here all angles in deciamal degrees - err = base_ser_angle_t::operator()(output, MccAngle{(double)value.y()}); - } - - if (err) { - return err; - } - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - - auto ep = value.epoch(); - if constexpr (std::convertible_to) { - auto err_e = base_ser_epoch_t::operator()(output, MccCelestialCoordEpoch{ep}); - if (err_e) { - return err_e; - } - } else { - MccCelestialCoordEpoch ep1; - bool ok = ep1.fromMJD(ep.MJD()); - if (!ok) { - return MccSerializerErrorCode::ERROR_INVALID_EPOCH; - } - - auto err_e = (*this)(output, ep1); - if (err_e) { - return err_e; - } - } - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - - auto err_s = MccSerializer{}(output, MccCoordPairKindToStr(VT::pairKind)); - if (err_s) { - return mcc_deduced_err(err_s, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - } - - return MccSerializerErrorCode::ERROR_OK; - } -}; - - -template - requires(mcc_skypoint_c || - (std::ranges::input_range && mcc_skypoint_c>>)) -class MccSerializer : public std::conditional_t, - MccSerializer>, - MccSerializer>, - public std::conditional_t, - MccSerializer>, - MccSerializer> -{ -protected: - typedef std::conditional_t, - MccSerializer>, - MccSerializer> - base_ser_epoch_t; - - typedef std:: - conditional_t, MccSerializer>, MccSerializer> - base_ser_angle_t; - -public: - using typename details::MccSerializerBase::error_t; - - enum SkyPointFormat { - SKYPOINT_FORMAT_FULL, // return RA_ICRS, DEC_ICRS, RA_APP, DEC_APP, RA_OBS, DEC_OBS, AZ, ZD, HA_APP, - // TIME-POINT - SKYPOINT_FORMAT_OBS, // return observed coordinates: RA_OBS, DEC_OBS, AZ, ZD, HA_OBS, TIME-POINT - SKYPOINT_FORMAT_APP, // return apparent (in vacuo) coordinates: RA_APP, DEC_APP, HA_APP, TIME-POINT - SKYPOINT_FORMAT_PAIR // return current stored coordinate pair: X, Y, TIME-POINT, PAIR-KIND - }; - - virtual ~MccSerializer() = default; - - void setSkyPointFormat(SkyPointFormat format) - { - _skptFormat = format; - } - - SkyPointFormat getSkyPointFormat() const - { - return _skptFormat; - } - - template - error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) - { - if constexpr (std::ranges::input_range) { - using value_t = std::remove_cv_t>; - MccSerializer sr; - - sr.setTimePointFormat(this->_tpFormat); - sr.setCoordFormat(this->_coordFormat); - sr.setCoordPrec(this->_coordPrec); - - return serializingRange(sr, value, output, std::move(fmt)); - } else { // scalar - - auto serialize_pair_func = [&output, this](PT const& pt) -> error_t { - MccSerializer pt_sr; - - pt_sr.setTimePointFormat(this->_tpFormat); - pt_sr.setCoordFormat(this->_coordFormat); - pt_sr.setCoordPrec(this->_coordPrec); - - auto err = pt_sr(output, pt); + switch (params.timepoint_format) { + case MccTimePointFormat::MCC_TIMEPOINT_FORMAT_DATE: { + auto tp = value.UTC(); + auto err = MccSerializer{}(output, tp, params); if (err) { return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); } return MccSerializerErrorCode::ERROR_OK; }; - - // WARNING: still ignore format string!!! - - auto skpt_fmt = _skptFormat.load(); - - if (skpt_fmt == SKYPOINT_FORMAT_PAIR) { - switch (value.pairKind()) { - case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: - return serialize_pair_func(value.operator MccSkyRADEC_ICRS()); - case MccCoordPairKind::COORDS_KIND_RADEC_APP: - return serialize_pair_func(value.operator MccSkyRADEC_APP()); - case MccCoordPairKind::COORDS_KIND_RADEC_OBS: - return serialize_pair_func(value.operator MccSkyRADEC_OBS()); - case MccCoordPairKind::COORDS_KIND_HADEC_APP: - return serialize_pair_func(value.operator MccSkyHADEC_APP()); - case MccCoordPairKind::COORDS_KIND_HADEC_OBS: - return serialize_pair_func(value.operator MccSkyHADEC_OBS()); - case MccCoordPairKind::COORDS_KIND_AZZD: - return serialize_pair_func(value.operator MccSkyAZZD()); - case MccCoordPairKind::COORDS_KIND_AZALT: - return serialize_pair_func(value.operator MccSkyAZALT()); - case MccCoordPairKind::COORDS_KIND_XY: - return serialize_pair_func(value.operator MccGenXY()); - default: - return serialize_pair_func(value.operator MccGenXY()); // ???????!!!!!!!!!!! - } - } else { - MccSkyRADEC_ICRS radec_icrs; - MccSkyRADEC_OBS radec_obs; - MccSkyHADEC_OBS hadec_obs; - MccSkyRADEC_APP radec_app; - MccSkyHADEC_APP hadec_app; - MccSkyAZZD azzd; - // MccGenXY xy; - - if (value.pairKind() == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { - auto ep_now = MccCelestialCoordEpoch::now(); - radec_obs.setEpoch(ep_now); - radec_app.setEpoch(ep_now); - hadec_obs.setEpoch(ep_now); - hadec_app.setEpoch(ep_now); - azzd.setEpoch(ep_now); - } else { - auto ep = value.epoch(); - radec_obs.setEpoch(ep); - radec_app.setEpoch(ep); - hadec_obs.setEpoch(ep); - hadec_app.setEpoch(ep); - azzd.setEpoch(ep); + case MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JEPOCH: { + auto ep = value.JEpoch(); + auto err = MccSerializer{}(output, ep, params); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); } - auto prev_format = this->_coordFormat.load(); - - if (skpt_fmt == SKYPOINT_FORMAT_FULL) { // RA_ICRS, DEC_ICRS, RA_APP, DEC_APP - auto err = value.to(radec_icrs, radec_app); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - // RA_ICRS is in sexagesimal hours - if (prev_format != base_ser_angle_t::CFMT_DEGREES) { - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); - } - - auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_icrs.x()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - - - // DEC_ICRS is in sexagesimal degrees - if (prev_format != base_ser_angle_t::CFMT_DEGREES) { - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS); - } - - err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_icrs.y()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - - // RA_APP is in sexagesimal hours - if (prev_format != base_ser_angle_t::CFMT_DEGREES) { - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); - } - - err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_app.x()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - - - // DEC_APP is in sexagesimal degrees - if (prev_format != base_ser_angle_t::CFMT_DEGREES) { - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS); - } - - err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_app.y()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - } - - if (skpt_fmt == SKYPOINT_FORMAT_OBS || skpt_fmt == SKYPOINT_FORMAT_FULL) { // RA_OBS, DEC_OBS, AZ, ZD - auto err = value.to(radec_obs, azzd); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - // RA_OBS is in sexagesimal hours - if (prev_format != base_ser_angle_t::CFMT_DEGREES) { - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); - } - - auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_obs.x()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - - - // DEC_OBS, AZ and ZD are in sexagesimal degrees - if (prev_format != base_ser_angle_t::CFMT_DEGREES) { - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS); - } - - err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_obs.y()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - - - err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)azzd.x()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - - err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)azzd.y()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - } - - if (skpt_fmt == SKYPOINT_FORMAT_FULL) { // HA_APP - auto err = value.to(hadec_app); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - // HA_APP is in sexagesimal hours - if (prev_format != base_ser_angle_t::CFMT_DEGREES) { - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); - } - - auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)hadec_app.x()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - } - - if (skpt_fmt == SKYPOINT_FORMAT_OBS) { // HA_OBS - auto err = value.to(hadec_obs); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - // HA_OBS is in sexagesimal hours - if (prev_format != base_ser_angle_t::CFMT_DEGREES) { - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); - } - - auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)hadec_obs.x()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - } - - - if (skpt_fmt == SKYPOINT_FORMAT_APP) { // RA_APP, DEC_APP, HA_APP - auto err = value.to(radec_app, hadec_app); - if (err) { - return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); - } - - // RA_APP is in sexagesimal hours - if (prev_format != base_ser_angle_t::CFMT_DEGREES) { - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); - } - - auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_app.x()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - - - // DEC_APP is in sexagesimal degrees - if (prev_format != base_ser_angle_t::CFMT_DEGREES) { - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS); - } - - err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_app.y()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - - // HA_APP is in sexagesimal hours - if (prev_format != base_ser_angle_t::CFMT_DEGREES) { - this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); - } - - err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)hadec_app.x()}); - if (err_ang) { - return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); - } - - std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); - } - - this->setCoordFormat(prev_format); // restore previous value - - - // epoch of app/obs coordinates - auto ep = value.epoch(); - if constexpr (std::convertible_to) { - auto err_e = base_ser_epoch_t::operator()(output, MccCelestialCoordEpoch{ep}); - if (err_e) { - return err_e; - } - } else { - MccCelestialCoordEpoch ep1; - bool ok = ep1.fromMJD(ep.MJD()); - if (!ok) { - return MccSerializerErrorCode::ERROR_INVALID_EPOCH; - } - - auto err_e = (*this)(output, ep1); - if (err_e) { - return err_e; - } - } - } - - return MccSerializerErrorCode::ERROR_OK; + return MccSerializerErrorCode::ERROR_OK; + } break; + case MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JD: + jd = value.MJD() + MCC_J2000_MJD; + break; + case MccTimePointFormat::MCC_TIMEPOINT_FORMAT_MJD: + jd = value.MJD(); + break; } - } -protected: - std::atomic _skptFormat{SKYPOINT_FORMAT_FULL}; + + auto err = MccSerializer{}(output, jd, params); + + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + return MccSerializerErrorCode::ERROR_OK; + } }; +template +struct MccSerializer : MccSerializerBase { + constexpr static std::string_view serializerName{"MCC-COORD-PAIR-SERIALIZER"}; -static_assert(mcc_serializer_c>, "!!!"); + template + error_t operator()(traits::mcc_output_char_range auto& output, + VT const& value, + ParamsT const& params = mcc_serialization_params_t{}) + { + auto pars = params; -static_assert(mcc_serializer_c>, "!!!"); + // pars.norm_sxgm = true; + // pars.coordpair_format = MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG; -static_assert(std::atomic::SerializedCoordFormat>::is_always_lock_free, "!!!"); -static_assert(std::atomic::SexagesimalCoordPrec>::is_always_lock_free, "!!!"); + // X-coordinate + MccSerializerBase::angleFormatFromCoordPairType(pars); -static_assert(std::formattable, "!!!"); + auto err = MccSerializer{}(output, value.x(), pars); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } -void f() -{ - MccSerializer s; + MccSerializerBase::addElemDelimiter(output, params); - std::string str; - s(str, impl::MccAngleALT{1.1}); -} + pars.norm_sxgm = false; // do not normalize co-latitude angle + + // Y-coordinate + MccSerializerBase::angleFormatFromCoordPairType(pars); + + err = MccSerializer{}(output, value.y(), pars); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, params); + + // epoch + auto ep = value.epoch(); + auto ep_err = MccSerializer{}(output, ep, params); + if (ep_err) { + return mcc_deduced_err(ep_err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + + MccSerializerBase::addElemDelimiter(output, params); + + // pair kind + auto pk_err = MccSerializer{}(output, MccCoordPairKindToStr(VT::pairKind), params); + if (pk_err) { + return mcc_deduced_err(pk_err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + return MccSerializerErrorCode::ERROR_OK; + } +}; + + +template +struct MccSerializer : MccSerializerBase { + constexpr static std::string_view serializerName{"MCC-SKYPOINT-SERIALIZER"}; + + template + error_t operator()(traits::mcc_output_char_range auto& output, + VT const& value, + ParamsT const& params = mcc_serialization_params_t{}) + { + auto serialize_cpair = [&](T& cp) { + auto ccte_err = value.to(cp); + if (ccte_err) { + return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + auto err = MccSerializer{}(output, cp, params); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + return MccSerializerErrorCode::ERROR_OK; + }; + + switch (value.pairKind()) { + case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: { + MccSkyRADEC_ICRS cp; + return serialize_cpair(cp); + } + case MccCoordPairKind::COORDS_KIND_RADEC_OBS: { + MccSkyRADEC_OBS cp; + return serialize_cpair(cp); + } + case MccCoordPairKind::COORDS_KIND_RADEC_APP: { + MccSkyRADEC_APP cp; + return serialize_cpair(cp); + } + case MccCoordPairKind::COORDS_KIND_HADEC_OBS: { + MccSkyHADEC_OBS cp; + return serialize_cpair(cp); + } + case MccCoordPairKind::COORDS_KIND_HADEC_APP: { + MccSkyHADEC_APP cp; + return serialize_cpair(cp); + } + case MccCoordPairKind::COORDS_KIND_AZZD: { + MccSkyAZZD cp; + return serialize_cpair(cp); + } + case MccCoordPairKind::COORDS_KIND_AZALT: { + MccSkyAZALT cp; + return serialize_cpair(cp); + } + case MccCoordPairKind::COORDS_KIND_XY: { + MccGenXY cp; + return serialize_cpair(cp); + } + case MccCoordPairKind::COORDS_KIND_GENERIC: { + MccGenXY cp; + return serialize_cpair(cp); + } + default: + return MccSerializerErrorCode::ERROR_COORD_TRANSFORM; + } + } +}; + + +template +struct MccSerializer : MccSerializerBase { + constexpr static std::string_view serializerName{"MCC-TELEMETRY-DATA-SERIALIZER"}; + + template + error_t operator()(traits::mcc_output_char_range auto& output, + VT const& value, + ParamsT const& params = mcc_serialization_params_t{}) + { + // FORMAT: RA_OBS_MOUNT, DEC_OBS_MOUNT, RA_APP_MOUNT, DEC_APP_MOUNT, HA_APP_MOUNT, AZ_MOUNT, ZD_MOUNT, + // REFR_CORR_MOUNT, ENC_X, ENC_Y, PCM_X, PCM_Y, RA_APP_TAG, DEC_APP_TAG, AZ_TAG, ZD_TAG, LAST, EO, TIMEPOINT, + // STATUS + + // NOTE: One must assume that the returned RA coordinates are in format of underlying celestial coordinate + // transformation engine used in the mcc_skypoint_c class implementation. E.g. ERFA-library uses the + // CIO-based representation of RA + + auto pars_h = params; + + auto pars_d = params; + + + pars_h.norm_sxgm = true; + pars_h.coordpair_format = MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG; + pars_d.norm_sxgm = true; + pars_d.coordpair_format = MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG; + + MccSkyRADEC_OBS rd_obs; + MccSkyRADEC_APP rd_app; + MccSkyHADEC_APP hd_app; + MccSkyAZZD azzd; + + // quantities in hour representation + MccSerializerBase::angleFormatFromCoordPairType(pars_h); + + // quantities in degree representation + MccSerializerBase::angleFormatFromCoordPairType(pars_d); + + MccSerializer ang_sr; + + // RA_OBS_MOUNT, DEC_OBS_MOUNT + auto ccte_err = value.mountPos.toAtSameEpoch(rd_obs); + if (ccte_err) { + return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + auto err = ang_sr(output, rd_obs.x(), pars_h); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + err = ang_sr(output, rd_obs.y(), pars_h); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + // RA_APP_MOUNT, DEC_APP_MOUNT + ccte_err = value.mountPos.toAtSameEpoch(rd_app); + if (ccte_err) { + return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + err = ang_sr(output, rd_app.x(), pars_h); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + err = ang_sr(output, rd_app.y(), pars_d); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + // HA_APP_MOUNT + ccte_err = value.mountPos.toAtSameEpoch(hd_app); + if (ccte_err) { + return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + err = ang_sr(output, hd_app.x(), pars_h); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + + MccSerializerBase::addElemDelimiter(output, pars_h); + + // AZ_MOUNT, ZD_MOUNT + ccte_err = value.mountPos.toAtSameEpoch(azzd); + if (ccte_err) { + return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + err = ang_sr(output, azzd.x(), pars_d); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + err = ang_sr(output, azzd.y(), pars_d); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + // refraction correction + + MccAngle ang; + ccte_err = value.mountPos.refractCorrection(&ang); + if (ccte_err) { + return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + err = ang_sr(output, ang, pars_d); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + + // encoder X and Y + + err = ang_sr(output, value.hwState.XY.x(), pars_d); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + err = ang_sr(output, value.hwState.XY.y(), pars_d); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + + // PCM X and Y + + err = ang_sr(output, value.pcmCorrection.pcmX(), pars_d); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + err = ang_sr(output, value.pcmCorrection.pcmY(), pars_d); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + + // RA_APP_TAG, DEC_APP_TAG + ccte_err = value.targetPos.toAtSameEpoch(rd_app); + if (ccte_err) { + return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + err = ang_sr(output, rd_app.x(), pars_h); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + err = ang_sr(output, rd_app.y(), pars_d); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + // AZ_TAG, ZD_TAG + ccte_err = value.targetPos.toAtSameEpoch(azzd); + if (ccte_err) { + return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + err = ang_sr(output, azzd.x(), pars_d); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + err = ang_sr(output, azzd.y(), pars_d); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + + // LAST, local apparent sideral time + + ccte_err = value.mountPos.appSideralTime(&ang, true); + if (ccte_err) { + return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + err = ang_sr(output, ang, pars_h); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + + // EO, equation of origins + + ccte_err = value.mountPos.EO(&ang); + if (ccte_err) { + return mcc_deduced_err(ccte_err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + err = ang_sr(output, ang, pars_h); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + // coordinates epoch + + auto ep_err = MccSerializer{}(output, value.mountPos.epoch(), pars_d); + if (ep_err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + MccSerializerBase::addElemDelimiter(output, pars_h); + + + // status (it must be formattable, see mcc_concepts.h) + + auto st_err = + MccSerializer{}(output, value.hwState.movementState, pars_d); + if (st_err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + + return MccSerializerErrorCode::ERROR_OK; + } +}; + + +static_assert(mcc_serializer_c>, "!!!"); } // namespace mcc::impl diff --git a/mcc_serializer.h.old b/mcc_serializer.h.old new file mode 100644 index 0000000..dabc9c9 --- /dev/null +++ b/mcc_serializer.h.old @@ -0,0 +1,910 @@ +#pragma once + + + +/**************************************************************************************** + * * + * MOUNT CONTROL COMPONENTS LIBRARY * + * * + * * + * IMPLEMENTATION OF SERIALIZER CLASSES * + * * + ****************************************************************************************/ + + + +#include +#include + +#include "mcc_concepts.h" +#include "mcc_constants.h" +#include "mcc_coordinate.h" +#include "mcc_epoch.h" +#include "mcc_error.h" +#include "mcc_traits.h" +#include "mcc_utils.h" + +namespace mcc::impl +{ + +enum class MccSerializerErrorCode : int { + ERROR_OK, + ERROR_UNDERLYING_SERIALIZER, + ERROR_INVALID_EPOCH, + ERROR_COORD_TRANSFORM +}; + +} // namespace mcc::impl + + + +namespace std +{ + +template <> +class is_error_code_enum : public true_type +{ +}; + +} // namespace std + + +namespace mcc::impl +{ + + +// error category +struct MccSerializerCategory : public std::error_category { + MccSerializerCategory() : std::error_category() {} + + const char* name() const noexcept + { + return "MCC-SERIALIZER-ERR-CATEGORY"; + } + + std::string message(int ec) const + { + MccSerializerErrorCode err = static_cast(ec); + + switch (err) { + case MccSerializerErrorCode::ERROR_OK: + return "OK"; + case MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER: + return "error returned by underlying serializer"; + case MccSerializerErrorCode::ERROR_INVALID_EPOCH: + return "invalid coordinate epoch"; + case MccSerializerErrorCode::ERROR_COORD_TRANSFORM: + return "coordinates transformation error"; + default: + return "UNKNOWN"; + } + } + + static const MccSerializerCategory& get() + { + static const MccSerializerCategory constInst; + return constInst; + } +}; + + +inline std::error_code make_error_code(MccSerializerErrorCode ec) +{ + return std::error_code(static_cast(ec), MccSerializerCategory::get()); +} + + + +template +struct mcc_serializer_interface_t { + virtual ~mcc_serializer_interface_t() = default; + + typedef RetT error_t; + + template SelfT, + traits::mcc_output_char_range R, + typename ValueT, + typename FmtT> + requires(std::same_as, FmtT> || std::same_as) + RetT operator()(this SelfT&& self, R& output, ValueT const& value, FmtT fmt) + { + return std::forward(self)(output, value, std::move(fmt)); + } + +protected: + mcc_serializer_interface_t() = default; +}; + +template +concept mcc_serializer_c = std::derived_from>; + + +namespace details +{ + +struct MccSerializerBase : mcc_serializer_interface_t, utils::mcc_elem_sequence_with_delim_t { + using typename mcc_serializer_interface_t::error_t; + + virtual ~MccSerializerBase() = default; + +protected: + MccSerializerBase() = default; + + template + requires(std::ranges::input_range && std::same_as>) + error_t serializingRange(mcc_serializer_c auto& sr, + R const& r, + traits::mcc_output_char_range auto& output, + auto fmt) const + { + size_t i = 0, N = std::ranges::size(r); + + for (auto const& el : r) { + auto err = sr(output, el, std::move(fmt)); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + if (++i < N) { + std::format_to(std::back_inserter(output), "{}", _seqDelimiter); + } + } + + return MccSerializerErrorCode::ERROR_OK; + } +}; + +} // namespace details + + +/* fallback template */ +template +class MccSerializer : public details::MccSerializerBase +// class MccSerializer : public mcc_serializer_interface_t +{ +public: + // default delimiter + static constexpr std::string_view defaultRangeDelimiter{","}; + + // typedef impl::MccError error_t; + + MccSerializer() = default; + + ~MccSerializer() = default; + + template + error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) + { + if constexpr (std::formattable) { + if constexpr (std::is_null_pointer_v) { + std::format_to(std::back_inserter(output), "{}", value); + } else if constexpr (traits::mcc_input_char_range) { + std::string_view sfmt{fmt.begin(), fmt.end()}; + std::vformat_to(std::back_inserter(output), sfmt, std::make_format_args(value)); + } else if constexpr (std::same_as, FmtT>) { + std::format_to(std::back_inserter(output), std::move(fmt), value); + } else { + static_assert(false, "INVALID FORMAT STRING TYPE!!!"); + } + } else if constexpr (std::convertible_to) { + auto err = MccSerializer{}(output, static_cast(value), std::move(fmt)); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + } else if constexpr (std::ranges::range) { + using value_t = std::remove_cv_t>; + + // special range (character sequence) + if constexpr (std::same_as) { + std::string str; + std::ranges::copy(value, std::back_inserter(str)); + + auto err = MccSerializer{}(output, str, std::move(fmt)); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + } else { + MccSerializer sr; + + return serializingRange(sr, value, output, std::move(fmt)); + } + } else { + static_assert(false, "UNSUPPORTED TYPE!!!"); + } + + return MccSerializerErrorCode::ERROR_OK; + } +}; + + + +/* SPECIALIZATION FOR THE SOME CONCEPTS */ + + +template + requires(traits::mcc_systime_c || + (std::ranges::input_range && traits::mcc_systime_c>>)) +class MccSerializer : public details::MccSerializerBase +{ +public: + virtual ~MccSerializer() = default; + + template + error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) + { + if constexpr (std::ranges::input_range) { + using value_t = std::remove_cv_t>; + MccSerializer sr; + + return serializingRange(sr, value, output, std::move(fmt)); + } else { // scalar + if constexpr (std::is_null_pointer_v) { + std::format_to(std::back_inserter(output), "{:%FT%T}", value); + } else if constexpr (traits::mcc_input_char_range) { + std::string_view sfmt{fmt.begin(), fmt.end()}; + if (sfmt == "{}") { + sfmt = "{:%FT%T}"; + } + std::vformat_to(std::back_inserter(output), sfmt, std::make_format_args(value)); + } else if constexpr (std::same_as, FmtT>) { + std::string_view sfmt{fmt.get().begin(), fmt.get().end()}; + if (sfmt == "{}") { + std::format_to(std::back_inserter(output), "{:%FT%T}", value); + } else { + std::format_to(std::back_inserter(output), std::move(fmt), value); + } + } else { + static_assert(false, "INVALID FORMAT STRING TYPE!!!"); + } + } + + return MccSerializerErrorCode::ERROR_OK; + } +}; + + +template + requires(!std::is_arithmetic_v && + (mcc_angle_c || + (std::ranges::input_range && mcc_angle_c>>))) +class MccSerializer : public virtual details::MccSerializerBase +// class MccSerializer : public mcc_serializer_interface_t +{ +public: + enum SerializedCoordFormat { + CFMT_DEGREES, // floating-point representation in degrees + CFMT_SGM_HOURS, // sexagesimal representation: HH:MM:SS.SSSSSS (hours:minutes:seconds) + CFMT_SGM_DEGS // sexagesimal representation: DD:MM:SS.SSSSS (degrees:arcmins::arcsecs) + }; + + struct SexagesimalCoordPrec { + uint8_t hour_prec = 2; // number of decimal places in hour seconds + uint8_t deg_prec = 1; // number of decimal places in arcseconds + }; + + // typedef impl::MccError error_t; + + virtual ~MccSerializer() = default; + + void setCoordFormat(SerializedCoordFormat format) + { + _coordFormat = format; + } + + SerializedCoordFormat getCoordFormat() const + { + return _coordFormat; + } + + + void setCoordPrec(SexagesimalCoordPrec prec) + { + _coordPrec = std::move(prec); + } + + SexagesimalCoordPrec getCoordPrec() const + { + return _coordPrec; + } + + template + error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) + { + if constexpr (std::ranges::input_range) { + using value_t = std::remove_cv_t>; + MccSerializer sr; + sr.setCoordFormat(_coordFormat); + sr.setCoordPrec(_coordPrec); + + return serializingRange(sr, value, output, std::move(fmt)); + } else { // scalar + double v = (double)value; // radians + std::string sgm; + + switch (_coordFormat) { + case MccSerializer::CFMT_DEGREES: + v *= 180.0 / std::numbers::pi; + return MccSerializer{}(output, v, std::move(fmt)); + case MccSerializer::CFMT_SGM_HOURS: + sgm = utils::rad2sxg(v, true, _coordPrec.load().hour_prec); + break; + case MccSerializer::CFMT_SGM_DEGS: + sgm = utils::rad2sxg(v, false, _coordPrec.load().deg_prec); + break; + default: + break; + } + auto err = MccSerializer{}(output, sgm, std::move(fmt)); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + } + + return MccSerializerErrorCode::ERROR_OK; + } + +protected: + // std::atomic _coordFormat{MccSerializer::CFMT_DEGREES}; + std::atomic _coordFormat{MccSerializer::CFMT_SGM_DEGS}; + std::atomic _coordPrec{SexagesimalCoordPrec{.hour_prec = 2, .deg_prec = 1}}; +}; + + + +template + requires(mcc_coord_epoch_c || + (std::ranges::input_range && mcc_coord_epoch_c>>)) +class MccSerializer : public virtual details::MccSerializerBase +{ +public: + enum TimePointFormat { TP_FORMAT_DATE, TP_FORMAT_MJD, TP_FORMAT_JEPOCH }; + + virtual ~MccSerializer() = default; + + void setTimePointFormat(TimePointFormat format) + { + _tpFormat = format; + } + + TimePointFormat getTimePointFormat() const + { + return _tpFormat; + } + + + template + error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) + { + if constexpr (std::ranges::input_range) { + using value_t = std::remove_cv_t>; + MccSerializer sr; + sr.setTimePointFormat(_tpFormat); + + return serializingRange(sr, value, output, std::move(fmt)); + } else { + switch (_tpFormat) { + case TP_FORMAT_DATE: { + auto utc = value.UTC(); + auto err = MccSerializer>{}(output, utc, std::move(fmt)); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + } break; + case TP_FORMAT_MJD: { + double mjd = value.MJD(); + auto err = MccSerializer{}(output, mjd, std::move(fmt)); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + } break; + case TP_FORMAT_JEPOCH: { + auto jepoch = value.JEpoch(); + auto err = MccSerializer>{}(output, jepoch, std::move(fmt)); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + } + default: + break; + } + + return MccSerializerErrorCode::ERROR_OK; + } + } + +protected: + std::atomic _tpFormat{TP_FORMAT_DATE}; +}; + + + +template + requires(mcc_coord_pair_c || + (std::ranges::input_range && mcc_coord_pair_c>>)) +class MccSerializer : public std::conditional_t, + MccSerializer>, + MccSerializer>, + public std::conditional_t, + MccSerializer>, + MccSerializer> +// class MccSerializer : public mcc_serializer_interface_t +{ +protected: + typedef std::conditional_t, + MccSerializer>, + MccSerializer> + base_ser_epoch_t; + + typedef std:: + conditional_t, MccSerializer>, MccSerializer> + base_ser_angle_t; + +public: + using typename details::MccSerializerBase::error_t; + + template + error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) + { + if constexpr (std::ranges::input_range) { + using value_t = std::remove_cv_t>; + MccSerializer sr; + + sr.setTimePointFormat(this->_tpFormat); + sr.setCoordFormat(this->_coordFormat); + sr.setCoordPrec(this->_coordPrec); + + return serializingRange(sr, value, output, std::move(fmt)); + } else { // scalar: format X[elem-delimiter]Y[elem-delimiter]epoch[elem-delimiter]pair-kind + + // WARNING: still ignore format string!!! + + error_t err; + + // format to sexagesimal form according to celestial coordinate type (general XY-type is skipped) + if (this->_coordFormat != base_ser_angle_t::CFMT_DEGREES) { + auto prev_format = this->_coordFormat.load(); + + if constexpr (VT::pairKind == MccCoordPairKind::COORDS_KIND_AZZD || + VT::pairKind == MccCoordPairKind::COORDS_KIND_AZALT) { // azimuth is in degrees + + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS); + err = base_ser_angle_t::operator()(output, MccAngle{(double)value.x()}); + } else { // RA or HA are in hours + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); + err = base_ser_angle_t::operator()(output, MccAngle{(double)value.x()}); + } + + this->setCoordFormat(prev_format); // restore previous value + } else { // here all angles in deciamal degrees + err = base_ser_angle_t::operator()(output, MccAngle{(double)value.x()}); + } + + if (err) { + return err; + } + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + + // format to sexagesimal form according to celestial coordinate type (general XY-type is skipped) + // Y-coordinate of the celestial point is always in degrees + if (this->_coordFormat != base_ser_angle_t::CFMT_DEGREES) { + auto prev_format = this->_coordFormat.load(); + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS); + + err = base_ser_angle_t::operator()(output, MccAngle{(double)value.y()}); + + this->setCoordFormat(prev_format); // restore previous value + } else { // here all angles in deciamal degrees + err = base_ser_angle_t::operator()(output, MccAngle{(double)value.y()}); + } + + if (err) { + return err; + } + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + + auto ep = value.epoch(); + if constexpr (std::convertible_to) { + auto err_e = base_ser_epoch_t::operator()(output, MccCelestialCoordEpoch{ep}); + if (err_e) { + return err_e; + } + } else { + MccCelestialCoordEpoch ep1; + bool ok = ep1.fromMJD(ep.MJD()); + if (!ok) { + return MccSerializerErrorCode::ERROR_INVALID_EPOCH; + } + + auto err_e = (*this)(output, ep1); + if (err_e) { + return err_e; + } + } + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + + auto err_s = MccSerializer{}(output, MccCoordPairKindToStr(VT::pairKind)); + if (err_s) { + return mcc_deduced_err(err_s, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + } + + return MccSerializerErrorCode::ERROR_OK; + } +}; + + +template + requires(mcc_skypoint_c || + (std::ranges::input_range && mcc_skypoint_c>>)) +class MccSerializer : public std::conditional_t, + MccSerializer>, + MccSerializer>, + public std::conditional_t, + MccSerializer>, + MccSerializer> +{ +protected: + typedef std::conditional_t, + MccSerializer>, + MccSerializer> + base_ser_epoch_t; + + typedef std:: + conditional_t, MccSerializer>, MccSerializer> + base_ser_angle_t; + +public: + using typename details::MccSerializerBase::error_t; + + enum SkyPointFormat { + SKYPOINT_FORMAT_FULL, // return RA_ICRS, DEC_ICRS, RA_APP, DEC_APP, RA_OBS, DEC_OBS, AZ, ZD, HA_APP, + // TIME-POINT + SKYPOINT_FORMAT_OBS, // return observed coordinates: RA_OBS, DEC_OBS, AZ, ZD, HA_OBS, TIME-POINT + SKYPOINT_FORMAT_APP, // return apparent (in vacuo) coordinates: RA_APP, DEC_APP, HA_APP, TIME-POINT + SKYPOINT_FORMAT_PAIR // return current stored coordinate pair: X, Y, TIME-POINT, PAIR-KIND + }; + + virtual ~MccSerializer() = default; + + void setSkyPointFormat(SkyPointFormat format) + { + _skptFormat = format; + } + + SkyPointFormat getSkyPointFormat() const + { + return _skptFormat; + } + + template + error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr) + { + if constexpr (std::ranges::input_range) { + using value_t = std::remove_cv_t>; + MccSerializer sr; + + sr.setTimePointFormat(this->_tpFormat); + sr.setCoordFormat(this->_coordFormat); + sr.setCoordPrec(this->_coordPrec); + + return serializingRange(sr, value, output, std::move(fmt)); + } else { // scalar + + auto serialize_pair_func = [&output, this](PT const& pt) -> error_t { + MccSerializer pt_sr; + + pt_sr.setTimePointFormat(this->_tpFormat); + pt_sr.setCoordFormat(this->_coordFormat); + pt_sr.setCoordPrec(this->_coordPrec); + + auto err = pt_sr(output, pt); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + return MccSerializerErrorCode::ERROR_OK; + }; + + // WARNING: still ignore format string!!! + + auto skpt_fmt = _skptFormat.load(); + + if (skpt_fmt == SKYPOINT_FORMAT_PAIR) { + switch (value.pairKind()) { + case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: + return serialize_pair_func(value.operator MccSkyRADEC_ICRS()); + case MccCoordPairKind::COORDS_KIND_RADEC_APP: + return serialize_pair_func(value.operator MccSkyRADEC_APP()); + case MccCoordPairKind::COORDS_KIND_RADEC_OBS: + return serialize_pair_func(value.operator MccSkyRADEC_OBS()); + case MccCoordPairKind::COORDS_KIND_HADEC_APP: + return serialize_pair_func(value.operator MccSkyHADEC_APP()); + case MccCoordPairKind::COORDS_KIND_HADEC_OBS: + return serialize_pair_func(value.operator MccSkyHADEC_OBS()); + case MccCoordPairKind::COORDS_KIND_AZZD: + return serialize_pair_func(value.operator MccSkyAZZD()); + case MccCoordPairKind::COORDS_KIND_AZALT: + return serialize_pair_func(value.operator MccSkyAZALT()); + case MccCoordPairKind::COORDS_KIND_XY: + return serialize_pair_func(value.operator MccGenXY()); + default: + return serialize_pair_func(value.operator MccGenXY()); // ???????!!!!!!!!!!! + } + } else { + MccSkyRADEC_ICRS radec_icrs; + MccSkyRADEC_OBS radec_obs; + MccSkyHADEC_OBS hadec_obs; + MccSkyRADEC_APP radec_app; + MccSkyHADEC_APP hadec_app; + MccSkyAZZD azzd; + // MccGenXY xy; + + if (value.pairKind() == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { + auto ep_now = MccCelestialCoordEpoch::now(); + radec_obs.setEpoch(ep_now); + radec_app.setEpoch(ep_now); + hadec_obs.setEpoch(ep_now); + hadec_app.setEpoch(ep_now); + azzd.setEpoch(ep_now); + } else { + auto ep = value.epoch(); + radec_obs.setEpoch(ep); + radec_app.setEpoch(ep); + hadec_obs.setEpoch(ep); + hadec_app.setEpoch(ep); + azzd.setEpoch(ep); + } + + auto prev_format = this->_coordFormat.load(); + + if (skpt_fmt == SKYPOINT_FORMAT_FULL) { // RA_ICRS, DEC_ICRS, RA_APP, DEC_APP + auto err = value.to(radec_icrs, radec_app); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + // RA_ICRS is in sexagesimal hours + if (prev_format != base_ser_angle_t::CFMT_DEGREES) { + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); + } + + auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_icrs.x()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + + + // DEC_ICRS is in sexagesimal degrees + if (prev_format != base_ser_angle_t::CFMT_DEGREES) { + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS); + } + + err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_icrs.y()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + + // RA_APP is in sexagesimal hours + if (prev_format != base_ser_angle_t::CFMT_DEGREES) { + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); + } + + err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_app.x()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + + + // DEC_APP is in sexagesimal degrees + if (prev_format != base_ser_angle_t::CFMT_DEGREES) { + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS); + } + + err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_app.y()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + } + + if (skpt_fmt == SKYPOINT_FORMAT_OBS || skpt_fmt == SKYPOINT_FORMAT_FULL) { // RA_OBS, DEC_OBS, AZ, ZD + auto err = value.to(radec_obs, azzd); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + // RA_OBS is in sexagesimal hours + if (prev_format != base_ser_angle_t::CFMT_DEGREES) { + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); + } + + auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_obs.x()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + + + // DEC_OBS, AZ and ZD are in sexagesimal degrees + if (prev_format != base_ser_angle_t::CFMT_DEGREES) { + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS); + } + + err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_obs.y()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + + + err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)azzd.x()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + + err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)azzd.y()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + } + + if (skpt_fmt == SKYPOINT_FORMAT_FULL) { // HA_APP + auto err = value.to(hadec_app); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + // HA_APP is in sexagesimal hours + if (prev_format != base_ser_angle_t::CFMT_DEGREES) { + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); + } + + auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)hadec_app.x()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + } + + if (skpt_fmt == SKYPOINT_FORMAT_OBS) { // HA_OBS + auto err = value.to(hadec_obs); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + // HA_OBS is in sexagesimal hours + if (prev_format != base_ser_angle_t::CFMT_DEGREES) { + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); + } + + auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)hadec_obs.x()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + } + + + if (skpt_fmt == SKYPOINT_FORMAT_APP) { // RA_APP, DEC_APP, HA_APP + auto err = value.to(radec_app, hadec_app); + if (err) { + return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_COORD_TRANSFORM); + } + + // RA_APP is in sexagesimal hours + if (prev_format != base_ser_angle_t::CFMT_DEGREES) { + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); + } + + auto err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_app.x()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + + + // DEC_APP is in sexagesimal degrees + if (prev_format != base_ser_angle_t::CFMT_DEGREES) { + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_DEGS); + } + + err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)radec_app.y()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + + // HA_APP is in sexagesimal hours + if (prev_format != base_ser_angle_t::CFMT_DEGREES) { + this->setCoordFormat(base_ser_angle_t::CFMT_SGM_HOURS); + } + + err_ang = base_ser_angle_t::operator()(output, MccAngle{(double)hadec_app.x()}); + if (err_ang) { + return mcc_deduced_err(err_ang, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER); + } + + std::format_to(std::back_inserter(output), "{}", this->_elementDelimiter); + } + + this->setCoordFormat(prev_format); // restore previous value + + + // epoch of app/obs coordinates + auto ep = value.epoch(); + if constexpr (std::convertible_to) { + auto err_e = base_ser_epoch_t::operator()(output, MccCelestialCoordEpoch{ep}); + if (err_e) { + return err_e; + } + } else { + MccCelestialCoordEpoch ep1; + bool ok = ep1.fromMJD(ep.MJD()); + if (!ok) { + return MccSerializerErrorCode::ERROR_INVALID_EPOCH; + } + + auto err_e = (*this)(output, ep1); + if (err_e) { + return err_e; + } + } + } + + return MccSerializerErrorCode::ERROR_OK; + } + } + +protected: + std::atomic _skptFormat{SKYPOINT_FORMAT_FULL}; +}; + + + +static_assert(mcc_serializer_c>, "!!!"); + +static_assert(mcc_serializer_c>, "!!!"); + +static_assert(std::atomic::SerializedCoordFormat>::is_always_lock_free, "!!!"); +static_assert(std::atomic::SexagesimalCoordPrec>::is_always_lock_free, "!!!"); + +static_assert(std::formattable, "!!!"); + +void f() +{ + MccSerializer s; + + std::string str; + s(str, impl::MccAngleALT{1.1}); +} + +} // namespace mcc::impl diff --git a/tests/mcc_coord_test.cpp b/tests/mcc_coord_test.cpp index 2ebed01..e696d9c 100644 --- a/tests/mcc_coord_test.cpp +++ b/tests/mcc_coord_test.cpp @@ -1,7 +1,7 @@ #include // #include -#include +// #include #include #include @@ -26,10 +26,13 @@ void serialize(VT const& value) { MccSerializer ser; std::string s; + mcc_serialization_params_t pars{}; + pars.norm_sxgm = true; + pars.coordpair_format = mcc::MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG; - auto err = ser(s, value); + auto err = ser(s, value, pars); if (err) { - std::cout << "SERIALIZING ERR: " << err << "\n"; + std::cout << "SERIALIZING ERR: " << err.message() << "\n"; } else { std::cout << "SERIALIZED: " << s << "\n"; } @@ -136,6 +139,9 @@ int main() std::cout << "\tfor 'double' type: "; serialize(v); + std::cout << "\tfor 'coord pair' type: "; + serialize(icrs); + std::cout << "\tfor 'coord pair' type: "; serialize(radec_obs); diff --git a/tests/mcc_telemetry_test.cpp b/tests/mcc_telemetry_test.cpp index cb50ded..422c571 100644 --- a/tests/mcc_telemetry_test.cpp +++ b/tests/mcc_telemetry_test.cpp @@ -61,6 +61,17 @@ struct hw_t { } }; +template <> +struct std::formatter + : std::formatter, char> { + auto format(hw_t::hardware_movement_state_t e, auto& ctx) const + { + return formatter>::format( + std::underlying_type_t(e), ctx); + } +}; + + static_assert(mcc::mcc_hardware_c, "!!!!!");