981 lines
37 KiB
C++
981 lines
37 KiB
C++
#pragma once
|
|
|
|
|
|
|
|
/****************************************************************************************
|
|
* *
|
|
* MOUNT CONTROL COMPONENTS LIBRARY *
|
|
* *
|
|
* *
|
|
* IMPLEMENTATION OF SERIALIZER CLASSES *
|
|
* *
|
|
****************************************************************************************/
|
|
|
|
|
|
|
|
#include <atomic>
|
|
#include <concepts>
|
|
|
|
#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<mcc::impl::MccSerializerErrorCode> : 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<MccSerializerErrorCode>(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<int>(ec), MccSerializerCategory::get());
|
|
}
|
|
|
|
|
|
|
|
template <mcc_error_c RetT>
|
|
struct mcc_serializer_interface_t {
|
|
virtual ~mcc_serializer_interface_t() = default;
|
|
|
|
typedef RetT error_t;
|
|
|
|
template <std::derived_from<mcc_serializer_interface_t> SelfT,
|
|
traits::mcc_output_char_range R,
|
|
typename ValueT,
|
|
typename FmtT>
|
|
requires(std::same_as<std::format_string<ValueT>, FmtT> || std::same_as<std::string_view, FmtT>)
|
|
RetT operator()(this SelfT&& self, R& output, ValueT const& value, FmtT fmt)
|
|
{
|
|
return std::forward<SelfT>(self)(output, value, std::move(fmt));
|
|
}
|
|
|
|
protected:
|
|
mcc_serializer_interface_t() = default;
|
|
};
|
|
|
|
template <typename T>
|
|
concept mcc_serializer_c = std::derived_from<T, mcc_serializer_interface_t<typename T::error_t>>;
|
|
|
|
|
|
namespace details
|
|
{
|
|
|
|
struct MccSerializerBase : mcc_serializer_interface_t<impl::MccError> {
|
|
using typename mcc_serializer_interface_t<impl::MccError>::error_t;
|
|
|
|
virtual ~MccSerializerBase() = default;
|
|
|
|
// set delimiter for elements of the range-type serialized value
|
|
template <traits::mcc_input_char_range R>
|
|
void setSeqDelimiter(R const& delim)
|
|
{
|
|
if constexpr (std::is_array_v<std::decay_t<R>>) {
|
|
setSeqDelimiter(std::string_view{delim});
|
|
} else {
|
|
_seqDelimiter.clear();
|
|
std::ranges::copy(delim, std::back_inserter(_seqDelimiter));
|
|
}
|
|
}
|
|
|
|
template <traits::mcc_view_or_output_char_range R>
|
|
R getSeqDelimiter() const
|
|
{
|
|
if constexpr (std::same_as<R, decltype(_seqDelimiter)>) {
|
|
return _seqDelimiter;
|
|
} else if constexpr (std::ranges::view<R>) {
|
|
return R{_seqDelimiter.begin(), _seqDelimiter.end()};
|
|
} else {
|
|
R r;
|
|
std::ranges::copy(_seqDelimiter, std::back_inserter(r));
|
|
|
|
return r;
|
|
}
|
|
}
|
|
|
|
std::string getSeqDelimiter() const
|
|
{
|
|
return getSeqDelimiter<std::string>();
|
|
}
|
|
|
|
|
|
template <traits::mcc_input_char_range R>
|
|
void setElemDelimiter(R const& delim)
|
|
{
|
|
if constexpr (std::is_array_v<std::decay_t<R>>) {
|
|
setElemDelimiter(std::string_view{delim});
|
|
} else {
|
|
_elementDelimiter.clear();
|
|
std::ranges::copy(delim, std::back_inserter(_seqDelimiter));
|
|
}
|
|
}
|
|
|
|
template <traits::mcc_view_or_output_char_range R>
|
|
R getElemDelimiter() const
|
|
{
|
|
if constexpr (std::same_as<R, decltype(_seqDelimiter)>) {
|
|
return _elementDelimiter;
|
|
} else if constexpr (std::ranges::view<R>) {
|
|
return R{_elementDelimiter.begin(), _elementDelimiter.end()};
|
|
} else {
|
|
R r;
|
|
std::ranges::copy(_elementDelimiter, std::back_inserter(r));
|
|
|
|
return r;
|
|
}
|
|
}
|
|
|
|
std::string getElemDelimiter() const
|
|
{
|
|
return getElemDelimiter<std::string>();
|
|
}
|
|
|
|
protected:
|
|
MccSerializerBase() = default;
|
|
|
|
// delimiter for sequence of serializing values
|
|
std::string _seqDelimiter{MCC_DEFAULT_SEQ_DELIMITER};
|
|
|
|
// delimiter for aggregative (multi-element) serializing value
|
|
std::string _elementDelimiter{MCC_DEFAULT_ELEM_DELIMITER};
|
|
|
|
template <typename VT, typename R>
|
|
requires(std::ranges::input_range<R> && std::same_as<VT, std::ranges::range_value_t<R>>)
|
|
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 <typename VT>
|
|
class MccSerializer : public details::MccSerializerBase
|
|
// class MccSerializer : public mcc_serializer_interface_t<impl::MccError>
|
|
{
|
|
public:
|
|
// default delimiter
|
|
static constexpr std::string_view defaultRangeDelimiter{","};
|
|
|
|
// typedef impl::MccError error_t;
|
|
|
|
MccSerializer() = default;
|
|
|
|
~MccSerializer() = default;
|
|
|
|
template <typename FmtT = std::nullptr_t>
|
|
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
|
|
{
|
|
if constexpr (std::formattable<VT, char>) {
|
|
if constexpr (std::is_null_pointer_v<FmtT>) {
|
|
std::format_to(std::back_inserter(output), "{}", value);
|
|
} else if constexpr (traits::mcc_input_char_range<FmtT>) {
|
|
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<std::format_string<VT>, 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<VT, std::string>) {
|
|
auto err = MccSerializer<std::string>{}(output, static_cast<std::string>(value), std::move(fmt));
|
|
if (err) {
|
|
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
|
|
}
|
|
|
|
} else if constexpr (std::ranges::range<VT>) {
|
|
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
|
|
|
|
// special range (character sequence)
|
|
if constexpr (std::same_as<value_t, char>) {
|
|
std::string str;
|
|
std::ranges::copy(value, std::back_inserter(str));
|
|
|
|
auto err = MccSerializer<std::string>{}(output, str, std::move(fmt));
|
|
if (err) {
|
|
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
|
|
}
|
|
|
|
} else {
|
|
MccSerializer<value_t> sr;
|
|
|
|
return serializingRange<value_t>(sr, value, output, std::move(fmt));
|
|
}
|
|
} else {
|
|
static_assert(false, "UNSUPPORTED TYPE!!!");
|
|
}
|
|
|
|
return MccSerializerErrorCode::ERROR_OK;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/* SPECIALIZATION FOR THE SOME CONCEPTS */
|
|
|
|
|
|
template <typename VT>
|
|
requires(traits::mcc_systime_c<VT> ||
|
|
(std::ranges::input_range<VT> && traits::mcc_systime_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>))
|
|
class MccSerializer<VT> : public details::MccSerializerBase
|
|
{
|
|
public:
|
|
virtual ~MccSerializer() = default;
|
|
|
|
template <typename FmtT = std::nullptr_t>
|
|
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
|
|
{
|
|
if constexpr (std::ranges::input_range<VT>) {
|
|
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
|
|
MccSerializer<value_t> sr;
|
|
|
|
return serializingRange<value_t>(sr, value, output, std::move(fmt));
|
|
} else { // scalar
|
|
if constexpr (std::is_null_pointer_v<FmtT>) {
|
|
std::format_to(std::back_inserter(output), "{:%FT%T}", value);
|
|
} else if constexpr (traits::mcc_input_char_range<FmtT>) {
|
|
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<std::format_string<VT>, 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 <typename VT>
|
|
requires(!std::is_arithmetic_v<VT> &&
|
|
(mcc_angle_c<VT> ||
|
|
(std::ranges::input_range<VT> && mcc_angle_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>)))
|
|
class MccSerializer<VT> : public virtual details::MccSerializerBase
|
|
// class MccSerializer<VT> : public mcc_serializer_interface_t<impl::MccError>
|
|
{
|
|
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 <typename FmtT = std::nullptr_t>
|
|
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
|
|
{
|
|
if constexpr (std::ranges::input_range<VT>) {
|
|
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
|
|
MccSerializer<value_t> sr;
|
|
sr.setCoordFormat(_coordFormat);
|
|
sr.setCoordPrec(_coordPrec);
|
|
|
|
return serializingRange<value_t>(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<double>{}(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<std::string>{}(output, sgm, std::move(fmt));
|
|
if (err) {
|
|
return mcc_deduced_err(err, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
|
|
}
|
|
}
|
|
|
|
return MccSerializerErrorCode::ERROR_OK;
|
|
}
|
|
|
|
protected:
|
|
// std::atomic<SerializedCoordFormat> _coordFormat{MccSerializer::CFMT_DEGREES};
|
|
std::atomic<SerializedCoordFormat> _coordFormat{MccSerializer::CFMT_SGM_DEGS};
|
|
std::atomic<SexagesimalCoordPrec> _coordPrec{SexagesimalCoordPrec{.hour_prec = 2, .deg_prec = 1}};
|
|
};
|
|
|
|
|
|
|
|
template <typename VT>
|
|
requires(mcc_coord_epoch_c<VT> ||
|
|
(std::ranges::input_range<VT> && mcc_coord_epoch_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>))
|
|
class MccSerializer<VT> : 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 <typename FmtT = std::nullptr_t>
|
|
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
|
|
{
|
|
if constexpr (std::ranges::input_range<VT>) {
|
|
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
|
|
MccSerializer<value_t> sr;
|
|
sr.setTimePointFormat(_tpFormat);
|
|
|
|
return serializingRange<value_t>(sr, value, output, std::move(fmt));
|
|
} else {
|
|
switch (_tpFormat) {
|
|
case TP_FORMAT_DATE: {
|
|
auto utc = value.UTC();
|
|
auto err = MccSerializer<std::remove_cvref_t<decltype(utc)>>{}(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<double>{}(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<std::remove_cvref_t<decltype(jepoch)>>{}(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<TimePointFormat> _tpFormat{TP_FORMAT_DATE};
|
|
};
|
|
|
|
|
|
|
|
template <typename VT>
|
|
requires(mcc_coord_pair_c<VT> ||
|
|
(std::ranges::input_range<VT> && mcc_coord_pair_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>))
|
|
class MccSerializer<VT> : public std::conditional_t<std::ranges::input_range<VT>,
|
|
MccSerializer<std::vector<MccCelestialCoordEpoch>>,
|
|
MccSerializer<MccCelestialCoordEpoch>>,
|
|
public std::conditional_t<std::ranges::input_range<VT>,
|
|
MccSerializer<std::vector<MccAngle>>,
|
|
MccSerializer<MccAngle>>
|
|
// class MccSerializer<VT> : public mcc_serializer_interface_t<MccError>
|
|
{
|
|
protected:
|
|
typedef std::conditional_t<std::ranges::input_range<VT>,
|
|
MccSerializer<std::vector<MccCelestialCoordEpoch>>,
|
|
MccSerializer<MccCelestialCoordEpoch>>
|
|
base_ser_epoch_t;
|
|
|
|
typedef std::
|
|
conditional_t<std::ranges::input_range<VT>, MccSerializer<std::vector<MccAngle>>, MccSerializer<MccAngle>>
|
|
base_ser_angle_t;
|
|
|
|
public:
|
|
using typename details::MccSerializerBase::error_t;
|
|
|
|
template <typename FmtT = std::nullptr_t>
|
|
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
|
|
{
|
|
if constexpr (std::ranges::input_range<VT>) {
|
|
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
|
|
MccSerializer<value_t> sr;
|
|
|
|
sr.setTimePointFormat(this->_tpFormat);
|
|
sr.setCoordFormat(this->_coordFormat);
|
|
sr.setCoordPrec(this->_coordPrec);
|
|
|
|
return serializingRange<value_t>(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<decltype(ep), MccCelestialCoordEpoch>) {
|
|
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<std::string_view>{}(output, MccCoordPairKindToStr(VT::pairKind));
|
|
if (err_s) {
|
|
return mcc_deduced_err(err_s, MccSerializerErrorCode::ERROR_UNDERLYING_SERIALIZER);
|
|
}
|
|
}
|
|
|
|
return MccSerializerErrorCode::ERROR_OK;
|
|
}
|
|
};
|
|
|
|
|
|
template <typename VT>
|
|
requires(mcc_skypoint_c<VT> ||
|
|
(std::ranges::input_range<VT> && mcc_skypoint_c<std::remove_cv_t<std::ranges::range_value_t<VT>>>))
|
|
class MccSerializer<VT> : public std::conditional_t<std::ranges::input_range<VT>,
|
|
MccSerializer<std::vector<MccCelestialCoordEpoch>>,
|
|
MccSerializer<MccCelestialCoordEpoch>>,
|
|
public std::conditional_t<std::ranges::input_range<VT>,
|
|
MccSerializer<std::vector<MccAngle>>,
|
|
MccSerializer<MccAngle>>
|
|
{
|
|
protected:
|
|
typedef std::conditional_t<std::ranges::input_range<VT>,
|
|
MccSerializer<std::vector<MccCelestialCoordEpoch>>,
|
|
MccSerializer<MccCelestialCoordEpoch>>
|
|
base_ser_epoch_t;
|
|
|
|
typedef std::
|
|
conditional_t<std::ranges::input_range<VT>, MccSerializer<std::vector<MccAngle>>, MccSerializer<MccAngle>>
|
|
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 <typename FmtT = std::nullptr_t>
|
|
error_t operator()(traits::mcc_output_char_range auto& output, VT const& value, FmtT fmt = nullptr)
|
|
{
|
|
if constexpr (std::ranges::input_range<VT>) {
|
|
using value_t = std::remove_cv_t<std::ranges::range_value_t<VT>>;
|
|
MccSerializer<value_t> sr;
|
|
|
|
sr.setTimePointFormat(this->_tpFormat);
|
|
sr.setCoordFormat(this->_coordFormat);
|
|
sr.setCoordPrec(this->_coordPrec);
|
|
|
|
return serializingRange<value_t>(sr, value, output, std::move(fmt));
|
|
} else { // scalar
|
|
|
|
auto serialize_pair_func = [&output, this]<typename PT>(PT const& pt) -> error_t {
|
|
MccSerializer<PT> 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<decltype(ep), MccCelestialCoordEpoch>) {
|
|
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<SkyPointFormat> _skptFormat{SKYPOINT_FORMAT_FULL};
|
|
};
|
|
|
|
|
|
|
|
static_assert(mcc_serializer_c<MccSerializer<float>>, "!!!");
|
|
|
|
static_assert(mcc_serializer_c<MccSerializer<impl::MccAngle>>, "!!!");
|
|
|
|
static_assert(std::atomic<MccSerializer<impl::MccAngle>::SerializedCoordFormat>::is_always_lock_free, "!!!");
|
|
static_assert(std::atomic<MccSerializer<impl::MccAngle>::SexagesimalCoordPrec>::is_always_lock_free, "!!!");
|
|
|
|
static_assert(std::formattable<impl::MccAngle, char>, "!!!");
|
|
|
|
void f()
|
|
{
|
|
MccSerializer<impl::MccAngle> s;
|
|
|
|
std::string str;
|
|
s(str, impl::MccAngleALT{1.1});
|
|
}
|
|
|
|
} // namespace mcc::impl
|