From cbe106fe95c81180436145df3fb05d7f6fbb26f5 Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Sat, 11 Oct 2025 23:02:43 +0300 Subject: [PATCH] ... --- asibfm700/CMakeLists.txt | 2 +- mcc/CMakeLists.txt | 41 ++--- mcc/mcc_netserver_proto.h | 308 ++++++++++++++++---------------------- mcc/tests/netmsg_test.cpp | 20 ++- 4 files changed, 165 insertions(+), 206 deletions(-) diff --git a/asibfm700/CMakeLists.txt b/asibfm700/CMakeLists.txt index dd5ac07..d141da0 100644 --- a/asibfm700/CMakeLists.txt +++ b/asibfm700/CMakeLists.txt @@ -40,7 +40,7 @@ set(ASIBFM700_LIB asibfm700mount) add_library(${ASIBFM700_LIB} STATIC ${ASIBFM700_LIB_SRC} asibfm700_mount.h asibfm700_mount.cpp asibfm700_configfile.h) -target_include_directories(${ASIBFM700_LIB} PRIVATE mcc spdlog) +target_include_directories(${ASIBFM700_LIB} PRIVATE mcc spdlog ${ERFA_INCLUDE_DIR}) target_link_libraries(${ASIBFM700_LIB} PRIVATE mcc spdlog) option(WITH_TESTS "Build tests" ON) diff --git a/mcc/CMakeLists.txt b/mcc/CMakeLists.txt index de0b998..d040889 100644 --- a/mcc/CMakeLists.txt +++ b/mcc/CMakeLists.txt @@ -16,26 +16,29 @@ include(ExternalProject) set(SPDLOG_USE_STD_FORMAT ON CACHE INTERNAL "Use of C++20 std::format") set(SPDLOG_FMT_EXTERNAL OFF CACHE INTERNAL "Turn off external fmt library") -FetchContent_Declare(spdlog -# ExternalProject_Add(spdlog - # SOURCE_DIR ${CMAKE_BINARY_DIR}/spdlog_lib - # BINARY_DIR ${CMAKE_BINARY_DIR}/spdlog_lib/build - GIT_REPOSITORY "https://github.com/gabime/spdlog.git" - GIT_TAG "v1.15.1" - GIT_SHALLOW TRUE - GIT_SUBMODULES "" - GIT_PROGRESS TRUE - CMAKE_ARGS "-DSPDLOG_USE_STD_FORMAT=ON -DSPDLOG_FMT_EXTERNAL=OFF" - # CONFIGURE_COMMAND "" - # BUILD_COMMAND "" - # INSTALL_COMMAND "" - # UPDATE_COMMAND "" - # SOURCE_SUBDIR cmake # turn off building - OVERRIDE_FIND_PACKAGE -) find_package(spdlog CONFIG) +if (NOT ${spdlog_FOUND}) + FetchContent_Declare(spdlog + # ExternalProject_Add(spdlog + # SOURCE_DIR ${CMAKE_BINARY_DIR}/spdlog_lib + # BINARY_DIR ${CMAKE_BINARY_DIR}/spdlog_lib/build + GIT_REPOSITORY "https://github.com/gabime/spdlog.git" + GIT_TAG "v1.15.1" + GIT_SHALLOW TRUE + GIT_SUBMODULES "" + GIT_PROGRESS TRUE + CMAKE_ARGS "-DSPDLOG_USE_STD_FORMAT=ON -DSPDLOG_FMT_EXTERNAL=OFF" + # CONFIGURE_COMMAND "" + # BUILD_COMMAND "" + # INSTALL_COMMAND "" + # UPDATE_COMMAND "" + # SOURCE_SUBDIR cmake # turn off building + OVERRIDE_FIND_PACKAGE + ) + find_package(spdlog CONFIG) +endif() - +message(STATUS "-----: " ${spdlog_FOUND}) # ******* ERFA LIBRARY ******* @@ -77,6 +80,8 @@ list(APPEND MCC_LIBRARY_SRC mcc_spdlog.h) set(MCC_LIBRARY mcc) add_library(${MCC_LIBRARY} INTERFACE ${MCC_LIBRARY_SRC}) target_compile_features(${MCC_LIBRARY} INTERFACE cxx_std_23) +target_compile_definitions(${MCC_LIBRARY} INTERFACE SPDLOG_USE_STD_FORMAT=1 SPDLOG_FMT_EXTERNAL=0) +target_link_libraries(${MCC_LIBRARY} INTERFACE spdlog) target_include_directories(${MCC_LIBRARY} INTERFACE ${ERFA_INCLUDE_DIR} ${BSPLINES_INCLUDE_DIR}) target_include_directories(${MCC_LIBRARY} INTERFACE $ diff --git a/mcc/mcc_netserver_proto.h b/mcc/mcc_netserver_proto.h index 3130bc9..40f5c7f 100644 --- a/mcc/mcc_netserver_proto.h +++ b/mcc/mcc_netserver_proto.h @@ -492,8 +492,20 @@ static_assert(mcc_netmsg_valid_keys_c, ""); template class MccNetMessage { - static inline utils::MccSimpleDeserializer defaultDeserilizer{MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ}; +public: + /* helper types to format serialized celestial coordinates */ + /* it can be used as inputs in "construct" method or corresponded constructor */ + // format of output (serialized) coordinates + enum MccNetMessageCoordFormat { CFMT_DEGREES, CFMT_SGM }; + + // precision of sexagesimal coordinates (number of decimal places in seconds/arcseconds) + struct MccNetMessageCoordPrec { + uint8_t hour_prec = 2; + uint8_t deg_prec = 1; + }; + +protected: class DefaultDeserializer : protected utils::MccSimpleDeserializer { protected: @@ -512,13 +524,13 @@ class MccNetMessage return ec; } - if (vs.size() < 2) { + if (vs.size() < 2) { // al least a pair of coordinates must be given return std::make_error_code(std::errc::invalid_argument); } MccCelestialPoint pt{.pair_kind = MccCoordPairKind::COORDS_KIND_RADEC_ICRS}; - if (vs.size() > 2) { + if (vs.size() > 2) { // pair of coordinates and the pair kind pt.pair_kind = MccCoordStrToPairKind(vs[2]); if (pt.pair_kind == MccCoordPairKind::COORDS_KIND_GENERIC) { return std::make_error_code(std::errc::invalid_argument); @@ -532,17 +544,25 @@ class MccNetMessage std::chrono::hours(11) + std::chrono::minutes(58) + std::chrono::milliseconds(55816); mcc_tp2tp(tp, pt.time_point); } else { - if (vs.size() > 3) { - std::chrono::sys_time tp; + if (vs.size() > 3) { // coordinates epoch is given + // std::chrono::sys_time tp; - std::istringstream iss(std::string{utils::trimSpaces(vs[3])}); + // std::istringstream iss(std::string{utils::trimSpaces(vs[3])}); - std::chrono::from_stream(iss, "%FT%T", tp); - if (iss.fail()) { + // std::chrono::from_stream(iss, "%FT%T", tp); + // if (iss.fail()) { + // return std::make_error_code(std::errc::invalid_argument); + // } + + // mcc_tp2tp(tp, pt.time_point); + + MccCelestialCoordEpoch cep; + // bool ok = cep.fromCharRange(utils::trimSpaces(vs[3])); + bool ok = cep.fromCharRange(vs[3]); + if (!ok) { return std::make_error_code(std::errc::invalid_argument); } - - mcc_tp2tp(tp, pt.time_point); + mcc_tp2tp(cep.UTC(), pt.time_point); } else { // no time point - use NOW mcc_tp2tp(std::chrono::system_clock::now(), pt.time_point); } @@ -574,7 +594,7 @@ class MccNetMessage pt.X = MccAngle(ang1.value(), mcc::MccDegreeTag{}); pt.Y = MccAngle(ang2.value(), mcc::MccDegreeTag{}); - mcc_copy_celestial_point(pt, value); + mcc_copy_celestial_point(pt, &value); } else { return base_t::operator()(std::forward(bytes), value); } @@ -583,48 +603,88 @@ class MccNetMessage } }; -public: - // helper method to convert celestial point to char range - // (actually, to range of strings) - template - static R celestialPointToString(mcc_celestial_point_c auto const& pt, - bool degrees = false, - std::pair prec = {2, 1}) + + class DefaultSerializer { - R res; + MccNetMessageCoordFormat _currentCoordFormat = MccNetMessage::CFMT_SGM; + MccNetMessageCoordPrec _currentCoordPrec{2, 1}; - if (degrees) { - std::format_to(std::back_inserter(res), "{}{}{}", MccAngle(pt.X).degrees(), - MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, MccAngle(pt.Y).degrees()); - } else { - switch (pt.pair_kind) { - case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: - case MccCoordPairKind::COORDS_KIND_RADEC_APP: - case MccCoordPairKind::COORDS_KIND_HADEC_APP: - std::format_to(std::back_inserter(res), "{}{}", MccAngle(pt.X).sexagesimal(true, prec.first), - MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ); - break; - default: - std::format_to(std::back_inserter(res), "{}{}", MccAngle(pt.X).sexagesimal(false, prec.second), - MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ); - } - - std::format_to(std::back_inserter(res), "{}", MccAngle(pt.Y).sexagesimal(false, prec.second)); + public: + void setCoordFormat(MccNetMessageCoordFormat fmt) + { + _currentCoordFormat = fmt; } - std::format_to(std::back_inserter(res), "{0:}{1:}{2:}{3:%F}T{3:%T}", MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, - MccCoordPairKindToStr(pt.pair_kind), MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, pt.time_point); + void setCoordPrec(MccNetMessageCoordPrec prec) + { + _currentCoordPrec = prec; + } - return res; - } + template + void operator()(const T& value, OR& bytes) + { + if constexpr (std::is_arithmetic_v) { + std::format_to(std::back_inserter(bytes), "{}", value); + } else if constexpr (std::convertible_to) { + std::ranges::copy(static_cast(value), std::back_inserter(bytes)); + } else if constexpr (std::constructible_from) { + std::ranges::copy(std::string(value), std::back_inserter(bytes)); + } else if constexpr (traits::mcc_char_range) { + std::ranges::copy(std::string(value.begin(), value.end()), std::back_inserter(bytes)); + } else if constexpr (std::same_as) { + std::ranges::copy(mcc_pairkind2str(value), std::back_inserter(bytes)); + } else if constexpr (traits::mcc_time_duration_c) { + (*this)(value.count(), bytes); + } else if constexpr (mcc_celestial_point_c) { + if (_currentCoordFormat == MccNetMessage::CFMT_DEGREES) { + std::format_to(std::back_inserter(bytes), "{}{}{}", MccAngle(value.X).degrees(), + MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, MccAngle(value.Y).degrees()); + } else { + switch (value.pair_kind) { + case MccCoordPairKind::COORDS_KIND_RADEC_ICRS: + case MccCoordPairKind::COORDS_KIND_RADEC_APP: + case MccCoordPairKind::COORDS_KIND_HADEC_APP: + std::format_to(std::back_inserter(bytes), "{}{}", + MccAngle(value.X).sexagesimal(true, _currentCoordPrec.hour_prec), + MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ); + break; + default: + std::format_to(std::back_inserter(bytes), "{}{}", + MccAngle(value.X).sexagesimal(false, _currentCoordPrec.deg_prec), + MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ); + } - static std::string celestialPointToString(mcc_celestial_point_c auto const& pt, - bool degrees = false, - std::pair prec = {2, 1}) - { - return celestialPointToString(pt, degrees, prec); - } + std::format_to(std::back_inserter(bytes), "{}", + MccAngle(value.Y).sexagesimal(false, _currentCoordPrec.deg_prec)); + } + std::format_to(std::back_inserter(bytes), "{0:}{1:}{2:}{3:%F}T{3:%T}", + MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, MccCoordPairKindToStr(value.pair_kind), + MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, value.time_point); + } else if constexpr (std::ranges::range) { + auto sz = std::ranges::size(value); + if (sz == 0) { + return; + } + + (*this)(*value.begin(), bytes); // the first element + + if (sz > 1) { + for (auto const& el : value | std::views::drop(1)) { + std::ranges::copy(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, std::back_inserter(bytes)); + (*this)(el, bytes); + } + } + } else if constexpr (std::formattable) { + std::format_to(std::back_inserter(bytes), "{}", value); + } else { + static_assert(false, "UNSUPPORTED TYPE!!!"); + } + } + }; + + +public: typedef BASE_T valid_keys_t; typedef BYTEREPR_T byte_repr_t; @@ -768,81 +828,10 @@ public: template std::expected paramValue(size_t idx) { - return paramValue(idx, defaultDeserilizer); + return paramValue(idx, _defaultDeserilizer); } - // template - // std::expected paramCelestialPoint( - // size_t from_idx, - // MccCoordPairKind default_kind = MccCoordPairKind::COORDS_KIND_RADEC_ICRS) - // { - // if (_params.size() < (from_idx + 2)) { - // return std::unexpected(std::make_error_code(std::errc::message_size)); - // } - - // MccCelestialPoint pt{.pair_kind = default_kind}; - - // if (_params.size() > (from_idx + 2)) { - // pt.pair_kind = MccCoordStrToPairKind(_params[from_idx + 2]); - // if (pt.pair_kind == MccCoordPairKind::COORDS_KIND_GENERIC) { - // return std::unexpected(std::make_error_code(std::errc::invalid_argument)); - // } - // } - - // if (pt.pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { - // // J2000.0: 11:58:55.816 1 January 2000 UTC - // auto tp = std::chrono::sys_days(std::chrono::year_month_day(std::chrono::January / std::chrono::day(1) / - // std::chrono::year(2000))) + - // std::chrono::hours(11) + std::chrono::minutes(58) + std::chrono::milliseconds(55816); - // mcc_tp2tp(tp, pt.time_point); - // } else { - // if (_params.size() > (from_idx + 3)) { - // std::chrono::sys_time tp; - - // std::istringstream iss(std::string{utils::trimSpaces(_params[from_idx + 3])}); - - // std::chrono::from_stream(iss, "%FT%T", tp); - // if (iss.fail()) { - // return std::unexpected(std::make_error_code(std::errc::invalid_argument)); - // } - - // mcc_tp2tp(tp, pt.time_point); - // } else { // no time point - use NOW - // mcc_tp2tp(std::chrono::system_clock::now(), pt.time_point); - // } - // } - - - // std::optional ang1, ang2; - - // switch (pt.pair_kind) { - // // if sexagesimal then hours:minutes:seconds form - // case mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS: - // case mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP: - // case mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP: - // ang1 = utils::parsAngleString(_params[from_idx], true); - // break; - // default: - // // if sexagesimal then degrees:arcminutes:arcseconds form - // ang1 = utils::parsAngleString(_params[from_idx]); - // } - // if (!ang1) { - // return std::unexpected(std::make_error_code(std::errc::invalid_argument)); - // } - - // ang2 = utils::parsAngleString(_params[from_idx + 1]); - // if (!ang2) { - // return std::unexpected(std::make_error_code(std::errc::invalid_argument)); - // } - - - // pt.X = MccAngle(ang1.value(), mcc::MccDegreeTag{}); - // pt.Y = MccAngle(ang2.value(), mcc::MccDegreeTag{}); - - // return pt; - // } - template R byteRepr() const { @@ -965,74 +954,35 @@ protected: BYTEREPR_T _msgBuffer{}; + inline static DefaultDeserializer _defaultDeserilizer{}; + + DefaultSerializer _defaultSerializer{}; + template void convertFunc(std::vector& idx, const T& par, const Ts&... pars) { - idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end())); - - // if constexpr (std::is_arithmetic_v) { - // std::format_to(std::back_inserter(_msgBuffer), "{}", par); - // } else if constexpr (std::convertible_to) { - // std::ranges::copy(static_cast(par), std::back_inserter(_msgBuffer)); - // } else if constexpr (std::constructible_from) { - // std::ranges::copy(std::string(par), std::back_inserter(_msgBuffer)); - // } else if constexpr (traits::mcc_char_range) { - // std::ranges::copy(std::string(par.begin(), par.end()), std::back_inserter(_msgBuffer)); - // } else if constexpr (std::same_as) { - // std::ranges::copy(mcc_pairkind2str(par), std::back_inserter(_msgBuffer)); - // } else if constexpr (traits::mcc_time_duration_c) { - // idx.resize(idx.size() - 1); - // convertFunc(idx, par.count()); - // idx.resize(idx.size() - 1); - // } else if constexpr (std::formattable) { - // std::format_to(std::back_inserter(_msgBuffer), "{}", par); - // } else { - // static_assert(false, "UNSUPPORTED TYPE!!!"); - // } - - // use the lambda here to enable recursive calling with no special handling of 'idx' - [](this auto const& self, auto& buff, auto const& p) { - using p_t = std::decay_t; - - if constexpr (std::is_arithmetic_v) { - std::format_to(std::back_inserter(buff), "{}", p); - } else if constexpr (std::convertible_to) { - std::ranges::copy(static_cast(p), std::back_inserter(buff)); - } else if constexpr (std::constructible_from) { - std::ranges::copy(std::string(p), std::back_inserter(buff)); - } else if constexpr (traits::mcc_char_range) { - std::ranges::copy(std::string(p.begin(), p.end()), std::back_inserter(buff)); - } else if constexpr (std::same_as) { - std::ranges::copy(mcc_pairkind2str(p), std::back_inserter(buff)); - } else if constexpr (traits::mcc_time_duration_c) { - self(buff, p.count()); - } else if constexpr (std::ranges::range) { - auto sz = std::ranges::size(p); - if (sz == 0) { - return; - } - - self(buff, *p.begin()); // the first element - - if (sz > 1) { - for (auto const& el : p | std::views::drop(1)) { - std::ranges::copy(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ, std::back_inserter(buff)); - self(buff, el); - } - } - } else if constexpr (std::formattable) { - std::format_to(std::back_inserter(buff), "{}", p); - } else { - static_assert(false, "UNSUPPORTED TYPE!!!"); + if constexpr (std::same_as) { + _defaultSerializer.setCoordFormat(par); + if constexpr (sizeof...(Ts)) { + convertFunc(idx, pars...); } - }(_msgBuffer, par); + } else if constexpr (std::same_as) { + _defaultSerializer.setCoordPrec(par); + if constexpr (sizeof...(Ts)) { + convertFunc(idx, pars...); + } + } else { + idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end())); - idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end())); + _defaultSerializer(par, _msgBuffer); - if constexpr (sizeof...(Ts)) { - std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer)); + idx.emplace_back(std::distance(_msgBuffer.begin(), _msgBuffer.end())); - convertFunc(idx, pars...); + if constexpr (sizeof...(Ts)) { + std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer)); + + convertFunc(idx, pars...); + } } }; }; diff --git a/mcc/tests/netmsg_test.cpp b/mcc/tests/netmsg_test.cpp index e62df6b..b859ba2 100644 --- a/mcc/tests/netmsg_test.cpp +++ b/mcc/tests/netmsg_test.cpp @@ -49,15 +49,19 @@ int main() } std::cout << ">\n"; - std::cout << msg2_t::celestialPointToString(mcc::MccCelestialPoint{}) << "\n"; - std::cout << msg2_t::celestialPointToString( - mcc::MccCelestialPoint{.pair_kind = mcc::MccCoordPairKind::COORDS_KIND_AZALT, - .time_point = std::chrono::system_clock::now(), - .X = 1.3284237648726384768273, - .Y = std::numbers::pi / 2.234}, - true) - << "\n"; + mcc::MccCelestialPoint cpt{.pair_kind = mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP, + .time_point = std::chrono::system_clock::now(), + .X = mcc::MccAngle("10:23:56.12331", mcc::mcc_hms), + .Y = mcc::MccAngle(67.9827148715, mcc::mcc_degrees)}; + // msg2.construct("ACK", "MOUNT", msg2_t::CFMT_DEGREES, msg2_t::MccNetMessageCoordPrec{2, 3}, cpt); + msg2.construct("ACK", "MOUNT", msg2_t::MccNetMessageCoordPrec{2, 3}, cpt); + std::cout << "MSG2: " << msg2.byteRepr() << "\n"; + auto cpt1 = msg2.paramValue(1); + if (cpt1) { + std::cout << "cpt.time_point = " << cpt1.value().time_point << "\n"; + std::cout << "cpt.X = " << cpt1.value().X << "\n"; + } std::cout << "\n\n\n";