...
This commit is contained in:
parent
8b16ac79b8
commit
27dccfe7c0
@ -485,18 +485,24 @@ protected:
|
|||||||
inline static auto deserializer = []<typename VT>(std::string_view str, VT& value) {
|
inline static auto deserializer = []<typename VT>(std::string_view str, VT& value) {
|
||||||
std::error_code ec{};
|
std::error_code ec{};
|
||||||
|
|
||||||
|
mcc::utils::MccSimpleDeserializer deser;
|
||||||
|
deser.setRangeDelim(base_t::VALUE_ARRAY_DELIM);
|
||||||
|
|
||||||
if constexpr (std::is_arithmetic_v<VT> || mcc::traits::mcc_output_char_range<VT> || std::ranges::range<VT> ||
|
if constexpr (std::is_arithmetic_v<VT> || mcc::traits::mcc_output_char_range<VT> || std::ranges::range<VT> ||
|
||||||
mcc::traits::mcc_time_duration_c<VT>) {
|
mcc::traits::mcc_time_duration_c<VT>) {
|
||||||
ec = base_t::defaultDeserializeFunc(str, value);
|
// ec = base_t::defaultDeserializeFunc(str, value);
|
||||||
|
ec = deser(str, value);
|
||||||
} else if constexpr (std::same_as<VT, mcc::MccAngle>) { // assume here all angles are in degrees
|
} else if constexpr (std::same_as<VT, mcc::MccAngle>) { // assume here all angles are in degrees
|
||||||
double vd;
|
double vd;
|
||||||
ec = base_t::defaultDeserializeFunc(str, vd);
|
// ec = base_t::defaultDeserializeFunc(str, vd);
|
||||||
|
ec = deser(str, value);
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
value = mcc::MccAngle(vd, mcc::MccDegreeTag{});
|
value = mcc::MccAngle(vd, mcc::MccDegreeTag{});
|
||||||
}
|
}
|
||||||
} else if constexpr (std::same_as<VT, mcc::MccDefaultPCMType>) {
|
} else if constexpr (std::same_as<VT, mcc::MccDefaultPCMType>) {
|
||||||
std::string vstr;
|
std::string vstr;
|
||||||
ec = base_t::defaultDeserializeFunc(str, vstr);
|
// ec = base_t::defaultDeserializeFunc(str, vstr);
|
||||||
|
ec = deser(str, value);
|
||||||
|
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
auto s = mcc::utils::trimSpaces(vstr);
|
auto s = mcc::utils::trimSpaces(vstr);
|
||||||
|
|||||||
@ -38,7 +38,8 @@ namespace mcc::network
|
|||||||
|
|
||||||
static constexpr std::string_view MCC_COMMPROTO_STOP_SEQ = "\n";
|
static constexpr std::string_view MCC_COMMPROTO_STOP_SEQ = "\n";
|
||||||
static constexpr std::string_view MCC_COMMPROTO_KEYPARAM_DELIM_SEQ = " ";
|
static constexpr std::string_view MCC_COMMPROTO_KEYPARAM_DELIM_SEQ = " ";
|
||||||
static constexpr std::string_view MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ = " ";
|
static constexpr std::string_view MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ = ";";
|
||||||
|
static constexpr std::string_view MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ = ",";
|
||||||
|
|
||||||
|
|
||||||
/* server special keywords */
|
/* server special keywords */
|
||||||
@ -112,7 +113,7 @@ static constexpr std::string_view mcc_pairkind2str(MccCoordPairKind kind)
|
|||||||
|
|
||||||
/* keywords */
|
/* keywords */
|
||||||
|
|
||||||
// NOTE: THE COORDINATES AND TIME-RELATED QUANTITIES CAN BE EXPRESSED IN THE TWO FORMAT:
|
// NOTE: THE COORDINATES AND TIME-RELATED QUANTITIES CAN BE EXPRESSED IN THE TWO FORMATS:
|
||||||
// 1) fixed-point real number, e.g. 123.43987537359 or -0.09775
|
// 1) fixed-point real number, e.g. 123.43987537359 or -0.09775
|
||||||
// 2) sexagesimal number, e.g. 10:43:43.12 or -123:54:12.435
|
// 2) sexagesimal number, e.g. 10:43:43.12 or -123:54:12.435
|
||||||
//
|
//
|
||||||
@ -122,7 +123,7 @@ static constexpr std::string_view mcc_pairkind2str(MccCoordPairKind kind)
|
|||||||
// IN FORMAT 'HOURS:MINUTES:SECONDS', WHILE DEC/ALT/AZ/ZD COORDINATES MUST
|
// IN FORMAT 'HOURS:MINUTES:SECONDS', WHILE DEC/ALT/AZ/ZD COORDINATES MUST
|
||||||
// BE EXPRESSED AS '+/-DEGREES:ARCMINUTES:ARCSECONDS'
|
// BE EXPRESSED AS '+/-DEGREES:ARCMINUTES:ARCSECONDS'
|
||||||
//
|
//
|
||||||
// USER-ENTERED (FROM NETWORK CLIENTS) COORDINATE PAIR MAY BE PROVIDED IN A MIXED FORM, I.E.,
|
// USER-ENTERED (FROM NETWORK CLIENTS) COORDINATE PAIR CAN BE PROVIDED IN A MIXED FORM, I.E.,
|
||||||
// 12.34436658678 10:32:11.432 or
|
// 12.34436658678 10:32:11.432 or
|
||||||
// 10:32:11.432 12.34436658678
|
// 10:32:11.432 12.34436658678
|
||||||
//
|
//
|
||||||
@ -182,7 +183,7 @@ static constexpr std::string_view MCC_COMMPROTO_KEYWORD_TARGET_STR = "TARGET";
|
|||||||
// "MOUNT coord-kind", if 'coord-kind' is omitted then coordinates are according to mount type,
|
// "MOUNT coord-kind", if 'coord-kind' is omitted then coordinates are according to mount type,
|
||||||
// i.e., HADEC for equathorial-type mount and AZZD for alt-azimuthal one
|
// i.e., HADEC for equathorial-type mount and AZZD for alt-azimuthal one
|
||||||
// e.g.:
|
// e.g.:
|
||||||
// "MOUNT RADEC\n" (get mount current apparent RA and DEC mount coordinates)
|
// "MOUNT RADEC\n" (get current apparent RA and DEC mount coordinates)
|
||||||
//
|
//
|
||||||
// server must return "ACK MOUNT X-coord Y-coord XY-kind" or "ERROR INVPAR"
|
// server must return "ACK MOUNT X-coord Y-coord XY-kind" or "ERROR INVPAR"
|
||||||
// e.g.
|
// e.g.
|
||||||
@ -434,4 +435,160 @@ bool mcc_netmsg_get_cpoint(mcc_netmsg_parse_result_c auto const& parse_res,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept mcc_netmsg_valid_keys_c = requires(T t) {
|
||||||
|
// std::array of valid message keywords
|
||||||
|
[]<size_t N>(std::array<std::string_view, N>) {
|
||||||
|
// to ensure T::NETMSG_VALID_KEYWORDS can be used as compile-time constant
|
||||||
|
static constexpr auto v0 = T::NETMSG_VALID_KEYWORDS[0];
|
||||||
|
return v0;
|
||||||
|
}(T::NETMSG_VALID_KEYWORDS);
|
||||||
|
|
||||||
|
// std::array of valid message keywords hashes
|
||||||
|
[]<size_t N>(std::array<size_t, N>) {
|
||||||
|
// to ensure T::NETMSG_VALID_KEYWORD_HASHES can be used as compile-time constant
|
||||||
|
static constexpr auto v0 = T::NETMSG_VALID_KEYWORD_HASHES[0];
|
||||||
|
return v0;
|
||||||
|
}(T::NETMSG_VALID_KEYWORD_HASHES);
|
||||||
|
|
||||||
|
requires T::NETMSG_VALID_KEYWORDS.size() == T::NETMSG_VALID_KEYWORD_HASHES.size();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct MccNetMessageValidKeywords {
|
||||||
|
static constexpr std::array NETMSG_VALID_KEYWORDS = {
|
||||||
|
MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR, MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR,
|
||||||
|
MCC_COMMPROTO_KEYWORD_COORDFMT_STR, MCC_COMMPROTO_KEYWORD_COORDPREC_STR,
|
||||||
|
MCC_COMMPROTO_KEYWORD_TARGET_STR, MCC_COMMPROTO_KEYWORD_MOUNT_STR,
|
||||||
|
MCC_COMMPROTO_KEYWORD_TELEMETRY_STR, MCC_COMMPROTO_KEYWORD_INIT_STR,
|
||||||
|
MCC_COMMPROTO_KEYWORD_STOP_STR, MCC_COMMPROTO_KEYWORD_SLEW_STR,
|
||||||
|
MCC_COMMPROTO_KEYWORD_MOVE_STR, MCC_COMMPROTO_KEYWORD_TRACK_STR,
|
||||||
|
MCC_COMMPROTO_KEYWORD_STATUS_STR};
|
||||||
|
|
||||||
|
|
||||||
|
// hashes of valid keywords
|
||||||
|
static constexpr std::array NETMSG_VALID_KEYWORD_HASHES = []<size_t... Is>(std::index_sequence<Is...>) {
|
||||||
|
return std::array{mcc::utils::FNV1aHash(NETMSG_VALID_KEYWORDS[Is])...};
|
||||||
|
}(std::make_index_sequence<NETMSG_VALID_KEYWORDS.size()>());
|
||||||
|
|
||||||
|
static std::expected<size_t, std::error_code> isKeywordValid(std::string_view key)
|
||||||
|
{
|
||||||
|
const auto hash = utils::FNV1aHash(key);
|
||||||
|
|
||||||
|
for (auto const& h : NETMSG_VALID_KEYWORD_HASHES) {
|
||||||
|
if (h == hash) {
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::unexpected(std::make_error_code(std::errc::invalid_argument));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(mcc_netmsg_valid_keys_c<MccNetMessageValidKeywords>, "");
|
||||||
|
|
||||||
|
|
||||||
|
template <mcc_netmsg_valid_keys_c BASE_T, traits::mcc_output_char_range BYTEREPR_T>
|
||||||
|
class MccNetMessage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef BASE_T valid_keys_t;
|
||||||
|
typedef BYTEREPR_T byte_repr_t;
|
||||||
|
|
||||||
|
template <traits::mcc_input_char_range KT, typename... PTs>
|
||||||
|
MccNetMessage(KT&& key, PTs&&... params)
|
||||||
|
{
|
||||||
|
construct(std::forward<KT>(key), std::forward<PTs>(params)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_input_char_range KT>
|
||||||
|
bool withKey(const KT& key)
|
||||||
|
{
|
||||||
|
return utils::FNV1aHash(key) == _keywordHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename DeserFuncT>
|
||||||
|
std::expected<T, std::error_code> params(DeserFuncT&& deser_func)
|
||||||
|
{
|
||||||
|
T val;
|
||||||
|
|
||||||
|
auto ec = deser_func(_params, val);
|
||||||
|
if (ec) {
|
||||||
|
return std::unexpect(ec);
|
||||||
|
} else {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::expected<T, std::error_code> params()
|
||||||
|
{
|
||||||
|
return params<T>(utils::MccSimpleDeserializer{}.setRangeDelim(MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_input_char_range KT, typename... PTs>
|
||||||
|
std::error_code construct(KT&& key, PTs&&... params)
|
||||||
|
{
|
||||||
|
if (!std::ranges::size(key)) {
|
||||||
|
return std::make_error_code(std::errc::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto r = valid_keys_t::isKeywordValid(key);
|
||||||
|
if (!r) {
|
||||||
|
return r.error();
|
||||||
|
}
|
||||||
|
|
||||||
|
_keywordHash = r.value();
|
||||||
|
|
||||||
|
_msgBuffer = BYTEREPR_T{};
|
||||||
|
|
||||||
|
std::ranges::copy(key, std::back_inserter(_msgBuffer));
|
||||||
|
_keyword = {_msgBuffer.begin(), _msgBuffer.end()};
|
||||||
|
|
||||||
|
_params = std::string_view{};
|
||||||
|
|
||||||
|
if constexpr (sizeof...(PTs)) {
|
||||||
|
std::ranges::copy(MCC_COMMPROTO_KEYPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer));
|
||||||
|
|
||||||
|
[this]<typename T, typename... Ts>(this auto&& self, const T& par, const Ts&... pars) {
|
||||||
|
if constexpr (std::is_arithmetic_v<T>) {
|
||||||
|
std::ranges::copy(std::to_string(par), std::back_inserter(_msgBuffer));
|
||||||
|
} else if constexpr (std::convertible_to<T, std::string>) {
|
||||||
|
std::ranges::copy(static_cast<std::string>(par), std::back_inserter(_msgBuffer));
|
||||||
|
} else if constexpr (std::constructible_from<std::string, T>) {
|
||||||
|
std::ranges::copy(std::string(par), std::back_inserter(_msgBuffer));
|
||||||
|
} else if constexpr (traits::mcc_char_range<T>) {
|
||||||
|
std::ranges::copy(std::string(par.begin(), par.end()), std::back_inserter(_msgBuffer));
|
||||||
|
} else if constexpr (std::same_as<T, MccCoordPairKind>) {
|
||||||
|
std::ranges::copy(mcc_pairkind2str(par), std::back_inserter(_msgBuffer));
|
||||||
|
} else if constexpr (traits::mcc_time_duration_c<T>) {
|
||||||
|
std::forward<decltype(self)>(self)(par.count());
|
||||||
|
} else {
|
||||||
|
static_assert(false, "UNSUPPORTED TYPE!!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (sizeof...(Ts)) {
|
||||||
|
std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(_msgBuffer));
|
||||||
|
|
||||||
|
std::forward<decltype(self)>(self)(pars...);
|
||||||
|
}
|
||||||
|
}(std::forward<PTs>(params)...);
|
||||||
|
|
||||||
|
_params = {_msgBuffer.begin() + _keyword.size() + MCC_COMMPROTO_KEYPARAM_DELIM_SEQ.size(),
|
||||||
|
_msgBuffer.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
size_t _keywordHash{};
|
||||||
|
std::string_view _keyword{};
|
||||||
|
std::string_view _params{};
|
||||||
|
|
||||||
|
BYTEREPR_T _msgBuffer{};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace mcc::network
|
} // namespace mcc::network
|
||||||
|
|||||||
@ -401,6 +401,94 @@ static constexpr size_t FNV1aHash(std::forward_iterator auto begin, std::sentine
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MccSimpleDeserializer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr std::string RANGE_DELIM_SEQ = ",";
|
||||||
|
|
||||||
|
template <traits::mcc_input_char_range R>
|
||||||
|
MccSimpleDeserializer& setRangeDelim(R&& r)
|
||||||
|
{
|
||||||
|
if (std::ranges::size(std::forward<R>(r))) {
|
||||||
|
_rangeDelim.clear();
|
||||||
|
std::ranges::copy(std::forward<R>(r), std::back_inserter(_rangeDelim));
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_output_char_range R>
|
||||||
|
R getRangeDelim() const
|
||||||
|
{
|
||||||
|
R r;
|
||||||
|
|
||||||
|
std::ranges::copy(_rangeDelim, std::back_inserter(r));
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getRangeDelim() const
|
||||||
|
{
|
||||||
|
return getRangeDelim<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_input_char_range IR, typename VT>
|
||||||
|
std::error_code operator()(IR&& bytes, VT& value)
|
||||||
|
{
|
||||||
|
std::error_code ret{};
|
||||||
|
|
||||||
|
if constexpr (std::is_arithmetic_v<VT>) {
|
||||||
|
auto v = mcc::utils::numFromStr<VT>(trimSpaces(bytes));
|
||||||
|
if (!v.has_value()) {
|
||||||
|
return std::make_error_code(std::errc::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = v.value();
|
||||||
|
} else if constexpr (mcc::traits::mcc_output_char_range<VT>) {
|
||||||
|
VT r;
|
||||||
|
std::ranges::copy(bytes, std::back_inserter(r));
|
||||||
|
value = r;
|
||||||
|
} else if constexpr (std::ranges::range<VT>) {
|
||||||
|
using el_t = std::ranges::range_value_t<VT>;
|
||||||
|
|
||||||
|
if constexpr (std::is_reference_v<el_t> || std::is_const_v<el_t>) { // no reference or constants allowed
|
||||||
|
return std::make_error_code(std::errc::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
VT r;
|
||||||
|
el_t elem;
|
||||||
|
|
||||||
|
auto els = std::views::split(bytes, _rangeDelim);
|
||||||
|
|
||||||
|
for (auto const& el : els) {
|
||||||
|
ret = (*this)(std::string_view(el), elem);
|
||||||
|
if (!ret) {
|
||||||
|
std::back_inserter(r) = elem;
|
||||||
|
} else {
|
||||||
|
return std::make_error_code(std::errc::invalid_argument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = r;
|
||||||
|
} else if constexpr (mcc::traits::mcc_time_duration_c<VT>) {
|
||||||
|
typename VT::rep vd;
|
||||||
|
|
||||||
|
ret = (*this)(trimSpaces(bytes), vd);
|
||||||
|
if (!ret) {
|
||||||
|
value = VT{vd};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = std::make_error_code(std::errc::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string _rangeDelim{RANGE_DELIM_SEQ};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* key-value pair holder */
|
/* key-value pair holder */
|
||||||
|
|
||||||
// to follow std::variant requirements (not references, not array, not void)
|
// to follow std::variant requirements (not references, not array, not void)
|
||||||
@ -521,7 +609,8 @@ public:
|
|||||||
template <std::ranges::contiguous_range R, std::ranges::input_range RecDelimT = std::string_view>
|
template <std::ranges::contiguous_range R, std::ranges::input_range RecDelimT = std::string_view>
|
||||||
std::error_code fromCharRange(const R& buffer, RecDelimT rec_delim = std::string_view("\n"))
|
std::error_code fromCharRange(const R& buffer, RecDelimT rec_delim = std::string_view("\n"))
|
||||||
{
|
{
|
||||||
return fromCharRange(buffer, KeyValueHolder::defaultDeserializeFunc, std::move(rec_delim));
|
// return fromCharRange(buffer, KeyValueHolder::defaultDeserializeFunc, std::move(rec_delim));
|
||||||
|
return fromCharRange(buffer, MccSimpleDeserializer{}.setRangeDelim(VALUE_ARRAY_DELIM), std::move(rec_delim));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::ranges::contiguous_range R,
|
template <std::ranges::contiguous_range R,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user