This commit is contained in:
Timur A. Fatkhullin 2025-10-07 23:51:58 +03:00
parent 8b16ac79b8
commit 27dccfe7c0
3 changed files with 260 additions and 8 deletions

View File

@ -485,18 +485,24 @@ protected:
inline static auto deserializer = []<typename VT>(std::string_view str, VT& value) {
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> ||
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
double vd;
ec = base_t::defaultDeserializeFunc(str, vd);
// ec = base_t::defaultDeserializeFunc(str, vd);
ec = deser(str, value);
if (!ec) {
value = mcc::MccAngle(vd, mcc::MccDegreeTag{});
}
} else if constexpr (std::same_as<VT, mcc::MccDefaultPCMType>) {
std::string vstr;
ec = base_t::defaultDeserializeFunc(str, vstr);
// ec = base_t::defaultDeserializeFunc(str, vstr);
ec = deser(str, value);
if (!ec) {
auto s = mcc::utils::trimSpaces(vstr);

View File

@ -38,7 +38,8 @@ namespace mcc::network
static constexpr std::string_view MCC_COMMPROTO_STOP_SEQ = "\n";
static constexpr std::string_view MCC_COMMPROTO_KEYPARAM_DELIM_SEQ = " ";
static constexpr std::string_view MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ = " ";
static constexpr std::string_view MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ = ";";
static constexpr std::string_view MCC_COMMPROTO_RANGEPARAM_DELIM_SEQ = ",";
/* server special keywords */
@ -112,7 +113,7 @@ static constexpr std::string_view mcc_pairkind2str(MccCoordPairKind kind)
/* 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
// 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
// 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
// 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,
// i.e., HADEC for equathorial-type mount and AZZD for alt-azimuthal one
// e.g.:
// "MOUNT RADEC\n" (get mount current apparent RA and DEC mount coordinates)
// "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"
// 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

View File

@ -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 */
// 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>
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,