cleanups of commented code
This commit is contained in:
parent
bc300bb3de
commit
6a72ead855
@ -11,7 +11,6 @@
|
|||||||
#include <mcc_pzone_container.h>
|
#include <mcc_pzone_container.h>
|
||||||
#include <mcc_spdlog.h>
|
#include <mcc_spdlog.h>
|
||||||
|
|
||||||
#include "asibfm700_servocontroller.h"
|
|
||||||
#include "mcc_ccte_erfa.h"
|
#include "mcc_ccte_erfa.h"
|
||||||
|
|
||||||
namespace asibfm700
|
namespace asibfm700
|
||||||
@ -25,91 +24,4 @@ typedef mcc::MccPZoneContainer<mcc::MccTimeDuration> Asibfm700PZoneContainer;
|
|||||||
typedef mcc::utils::MccSpdlogLogger Asibfm700Logger;
|
typedef mcc::utils::MccSpdlogLogger Asibfm700Logger;
|
||||||
|
|
||||||
|
|
||||||
/* MOUNT CONFIGURATION CLASS */
|
|
||||||
|
|
||||||
struct Asibfm700MountConfig1 {
|
|
||||||
std::chrono::milliseconds hardwarePollingPeriod{100}; // main cycle period
|
|
||||||
|
|
||||||
// CCTE-related configuration
|
|
||||||
mcc::MccAngle siteLatitude{43.646711_degs}; // in radians
|
|
||||||
mcc::MccAngle siteLongitude{41.440732_degs}; // in radians
|
|
||||||
double siteElevation{2070.0}; // in meters
|
|
||||||
double refractWavelength{0.55}; // in mkm
|
|
||||||
|
|
||||||
std::string leapSecondFilename{""}; // an empty filename means default precompiled string
|
|
||||||
std::string bulletinAFilename{""}; // an empty filename means default precompiled string
|
|
||||||
|
|
||||||
|
|
||||||
// PCM-related configuration (no B-spline term, all coefficients are zero)
|
|
||||||
Asibfm700PCM::pcm_data_t pcmData{.type = mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY,
|
|
||||||
.siteLatitude = siteLatitude,
|
|
||||||
.geomCoefficients = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}};
|
|
||||||
|
|
||||||
// servo controller configuration
|
|
||||||
AsibFM700ServoController::hardware_config_t servoControllerConfig{};
|
|
||||||
|
|
||||||
// slew and track parameters
|
|
||||||
mcc::MccSimpleMovingModelParams movingModelParams{};
|
|
||||||
|
|
||||||
// prohibited zones parameters
|
|
||||||
mcc::MccAngle pzMinAltitude{10.0_degs}; // in radians
|
|
||||||
mcc::MccAngle pzLimitSwitchHAMin{}; // in radians
|
|
||||||
mcc::MccAngle pzLimitSwitchHAMax{}; // in radians
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* MOUNT CONFIGURATION FILE DEFAULTS */
|
|
||||||
|
|
||||||
struct Asibfm700MountConfigFileDefailts {
|
|
||||||
std::string hardwarePollingPeriod{"100"}; // main cycle period in millisecs
|
|
||||||
|
|
||||||
std::string siteLatitude{"43.646711"}; // site latitude in degrees or sexagesimal format
|
|
||||||
std::string siteLongitude{"41.440732"}; // site longitude in degrees or sexagesimal format
|
|
||||||
std::string siteElevation{"2070.0"}; // site elevation in meters
|
|
||||||
|
|
||||||
std::string refractWavelength{"0.55"}; // wavelength at which refraction is calculated (in mkm)
|
|
||||||
|
|
||||||
std::string leapSecondFilename{""}; // an empty filename means default precompiled string
|
|
||||||
std::string bulletinAFilename{""}; // an empty filename means default precompiled string
|
|
||||||
|
|
||||||
// pointing correction model
|
|
||||||
|
|
||||||
std::string pcmType{"GEOMETRY"};
|
|
||||||
std::vector<std::string> pcmGeomCoeffs{"0.0", "0.0", "0.0", "0.0", "0.0", "0.0", "0.0", "0.0", "0.0"};
|
|
||||||
std::vector<std::string> pcmBsplineDegree{"3", "3"};
|
|
||||||
// [0, 360]
|
|
||||||
std::vector<std::string> pcmBsplineXknots{"0.0", "0.6981317", "1.3962634", "2.0943951", "2.7925268",
|
|
||||||
"3.4906585", "4.1887902", "4.88692191", "5.58505361", "6.28318531"};
|
|
||||||
// [-30, 90]
|
|
||||||
std::vector<std::string> pcmBsplineYknots{"-0.52359878", "-0.29088821", "-0.05817764", "0.17453293", "0.40724349",
|
|
||||||
"0.63995406", "0.87266463", "1.10537519", "1.33808576", "1.57079633"};
|
|
||||||
|
|
||||||
std::vector<std::string> pcmBsplineXcoeffs{};
|
|
||||||
std::vector<std::string> pcmBsplineYcoeffs{};
|
|
||||||
|
|
||||||
// slewing and tracking parameters
|
|
||||||
|
|
||||||
std::string sideralRate{"15.0410686"}; // arcseconds per second
|
|
||||||
std::string telemetryTimeout{"3"}; // timeout for telemetry updating in seconds
|
|
||||||
std::string minTimeToPZone{"10"}; // minimal time in seconds to prohibited zone
|
|
||||||
// a time interval to update prohibited zones related quantities
|
|
||||||
std::string updatingPZoneInterval{"5000"}; // in millisecs
|
|
||||||
std::string slewToleranceRadius{"5.0"}; // coordinates difference in arcsecs to stop slewing
|
|
||||||
std::string adjustCoordDiff{"50.0"}; // target-mount coordinate difference in arcsecs to start adjusting of slewing
|
|
||||||
std::string adjustCycleInterval{"300"}; // minimum time in millisecs between two successive adjustments
|
|
||||||
std::string slewTimeout{"3600"}; // slew process timeout
|
|
||||||
// a time shift into future to compute target position in future (UT1-scale time duration)
|
|
||||||
std::string timeShiftToTargetPoint{"10000"}; // in millisecs
|
|
||||||
std::string trackingCycleInterval{"300"}; // minimum time in millisecs between two successive tracking corrections
|
|
||||||
|
|
||||||
// prohibited zones
|
|
||||||
std::string pzMinAltitude{"10.0"}; // minimal altitude in degrees
|
|
||||||
std::string pzLimitSwitchHAMin{""}; // HA-axis limit switch minimal value
|
|
||||||
std::string pzLimitSwitchHAMax{""}; // HA-axis limit switch maximal value
|
|
||||||
|
|
||||||
// hardware
|
|
||||||
std::string hwMaxRateHA{""}; // maximal moving rate along HA-axis (Y-axis of Sidereal servo microcontroller)
|
|
||||||
std::string hwMaxRateDEC{""}; // maximal moving rate along DEC-axis (X-axis of Sidereal servo microcontroller)
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace asibfm700
|
} // namespace asibfm700
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
#include <expected>
|
#include <expected>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include <mcc_angle.h>
|
#include <mcc_angle.h>
|
||||||
#include <mcc_moving_model_common.h>
|
#include <mcc_moving_model_common.h>
|
||||||
@ -24,282 +23,15 @@ namespace asibfm700
|
|||||||
|
|
||||||
// to follow std::variant requirements (not references, not array, not void)
|
// to follow std::variant requirements (not references, not array, not void)
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept variant_valid_type_c = requires { !std::is_array_v<T> && !std::is_void_v<T> && !std::is_reference_v<T>; };
|
concept config_record_valid_type_c = requires { !std::is_array_v<T> && !std::is_void_v<T> && !std::is_reference_v<T>; };
|
||||||
|
|
||||||
// configuration record
|
|
||||||
template <typename T>
|
|
||||||
concept config_record_c = requires(T t) {
|
|
||||||
requires std::same_as<decltype(t.key), std::string_view>; // keyword
|
|
||||||
requires variant_valid_type_c<decltype(t.value)>; // value
|
|
||||||
};
|
|
||||||
|
|
||||||
// simple minimal-requirement configuration record class
|
// simple minimal-requirement configuration record class
|
||||||
template <variant_valid_type_c T>
|
template <config_record_valid_type_c T>
|
||||||
struct simple_config_record_t {
|
struct simple_config_record_t {
|
||||||
std::string_view key;
|
std::string_view key;
|
||||||
T value;
|
T value;
|
||||||
};
|
};
|
||||||
|
|
||||||
// description of config (a std::tuple of "config_record_c"s)
|
|
||||||
template <typename T>
|
|
||||||
concept config_desc_c = requires(T t) { []<config_record_c... Ts>(std::tuple<Ts...>) {}(t); };
|
|
||||||
|
|
||||||
|
|
||||||
template <config_desc_c DESCR_T>
|
|
||||||
class ConfigHolder
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
/* helper definitions */
|
|
||||||
|
|
||||||
// deduce unique value types of the given config records
|
|
||||||
template <config_desc_c TplT>
|
|
||||||
struct deduce_val_types;
|
|
||||||
|
|
||||||
template <config_record_c RT>
|
|
||||||
struct deduce_val_types<std::tuple<RT>> {
|
|
||||||
using value_type_t = std::tuple<decltype(RT::value)>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <config_record_c RT, config_record_c... RTs>
|
|
||||||
struct deduce_val_types<std::tuple<RT, RTs...>> {
|
|
||||||
using value_type_t =
|
|
||||||
std::conditional_t<(std::same_as<RT, RTs> || ...),
|
|
||||||
typename deduce_val_types<std::tuple<RTs...>>::value_type_t,
|
|
||||||
decltype(std::tuple_cat(
|
|
||||||
std::declval<std::tuple<decltype(RT::value)>>(),
|
|
||||||
std::declval<typename deduce_val_types<std::tuple<RTs...>>::value_type_t>()))>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <config_desc_c TplT>
|
|
||||||
using deduce_val_types_t = typename deduce_val_types<TplT>::value_type_t;
|
|
||||||
|
|
||||||
// deduce std::variant type from std::tuple element types
|
|
||||||
template <mcc::traits::mcc_tuple_c TplT>
|
|
||||||
struct variant_from_tuple;
|
|
||||||
|
|
||||||
template <typename T, typename... Ts>
|
|
||||||
struct variant_from_tuple<std::tuple<T, Ts...>> {
|
|
||||||
using variant_t = std::variant<T, Ts...>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <mcc::traits::mcc_tuple_c TplT>
|
|
||||||
using variant_from_tuple_t = typename variant_from_tuple<TplT>::variant_t;
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
static constexpr char COMMENT_SYMBOL = '#';
|
|
||||||
static constexpr char KEY_VALUE_DELIM = '=';
|
|
||||||
static constexpr char VALUE_ARRAY_DELIM = ',';
|
|
||||||
|
|
||||||
|
|
||||||
// very simple de-serializer (only numbers and strings)
|
|
||||||
inline static auto defaultDeserializeFunc = [](this auto&& self, std::string_view str, auto& value) {
|
|
||||||
using value_t = std::decay_t<decltype(value)>;
|
|
||||||
|
|
||||||
if constexpr (std::is_arithmetic_v<value_t>) {
|
|
||||||
auto v = mcc::utils::numFromStr<value_t>(str);
|
|
||||||
if (!v.has_value()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = v.value();
|
|
||||||
} else if constexpr (mcc::traits::mcc_output_char_range<value_t>) {
|
|
||||||
value_t r;
|
|
||||||
std::ranges::copy(str, std::back_inserter(r));
|
|
||||||
value = r;
|
|
||||||
} else if constexpr (std::ranges::range<value_t>) {
|
|
||||||
using el_t = std::ranges::range_value_t<value_t>;
|
|
||||||
|
|
||||||
if constexpr (std::is_reference_v<el_t> || std::is_const_v<el_t>) { // no reference or constants allowed
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
value_t r;
|
|
||||||
el_t elem;
|
|
||||||
|
|
||||||
auto els = std::views::split(str, VALUE_ARRAY_DELIM);
|
|
||||||
|
|
||||||
for (auto const& el : els) {
|
|
||||||
// if (std::forward<decltype(self)>(self)(std::string_view(el), elem)) {
|
|
||||||
if (std::forward<decltype(self)>(self)(mcc::utils::trimSpaces(el), elem)) {
|
|
||||||
std::back_inserter(r) = elem;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value = r;
|
|
||||||
} else if constexpr (mcc::traits::mcc_time_duration_c<value_t>) {
|
|
||||||
typename value_t::rep vd;
|
|
||||||
|
|
||||||
bool ok = self(str, vd);
|
|
||||||
if (ok) {
|
|
||||||
value = value_t{vd};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
ConfigHolder(DESCR_T desc)
|
|
||||||
{
|
|
||||||
[desc = std::move(desc), this]<size_t... Is>(std::index_sequence<Is...>) {
|
|
||||||
((_configDB[std::get<Is>(desc).key] = std::get<Is>(desc).value), ...);
|
|
||||||
}(std::make_index_sequence<std::tuple_size_v<DESCR_T>>());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~ConfigHolder() = default;
|
|
||||||
|
|
||||||
// deser_func - de-serialization function, i.e., a conversional function
|
|
||||||
// from string-representation to some value
|
|
||||||
//
|
|
||||||
// DeserFuncT is a type of callable with signature:
|
|
||||||
// bool deser_func(std::string_view str, value_type& val)
|
|
||||||
// where
|
|
||||||
// str - input (serialized) string-representation of configuration
|
|
||||||
// item value
|
|
||||||
// 'value_type' - must take into account all possible value types
|
|
||||||
// in the input configuration description (see constructor)
|
|
||||||
//
|
|
||||||
// most suitable implementation is a generic lambda function, i.e.:
|
|
||||||
// auto deser_func(std::string_view str, auto& cnv_val)->bool {
|
|
||||||
// ...
|
|
||||||
// };
|
|
||||||
template <std::ranges::contiguous_range R, typename DeserFuncT>
|
|
||||||
std::error_code parse(const R& buffer, DeserFuncT&& deser_func)
|
|
||||||
requires std::same_as<std::remove_cvref_t<std::ranges::range_value_t<R>>, char>
|
|
||||||
{
|
|
||||||
if constexpr (std::is_array_v<std::decay_t<R>>) { // char*, const char*
|
|
||||||
return parse(std::string_view{std::forward<R>(buffer)}, std::forward<DeserFuncT>(deser_func));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto curr_buffer = std::string_view(buffer.begin(), buffer.end());
|
|
||||||
std::string_view key, value;
|
|
||||||
bool buffer_end = false;
|
|
||||||
|
|
||||||
do {
|
|
||||||
auto it = std::ranges::find(curr_buffer, '\n');
|
|
||||||
if (it == curr_buffer.end()) {
|
|
||||||
buffer_end = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto sv =
|
|
||||||
mcc::utils::trimSpaces(std::string_view(curr_buffer.begin(), it), mcc::utils::TrimType::TRIM_LEFT);
|
|
||||||
|
|
||||||
curr_buffer = {it + 1, curr_buffer.end()};
|
|
||||||
|
|
||||||
if (sv.size() && (sv[0] != COMMENT_SYMBOL)) {
|
|
||||||
it = std::ranges::find(sv, KEY_VALUE_DELIM);
|
|
||||||
if (it != sv.begin()) { // ignore an empty key
|
|
||||||
key = mcc::utils::trimSpaces(std::string_view(sv.begin(), it), mcc::utils::TrimType::TRIM_RIGHT);
|
|
||||||
auto rec_it = _configDB.find(key);
|
|
||||||
if (rec_it != _configDB.end()) { // ignore key if it is not in description
|
|
||||||
value =
|
|
||||||
mcc::utils::trimSpaces(std::string_view(it + 1, sv.end()), mcc::utils::TrimType::TRIM_BOTH);
|
|
||||||
|
|
||||||
bool ok = forIndex(rec_it->second, value, std::forward<DeserFuncT>(deser_func),
|
|
||||||
rec_it->second.index());
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
return std::make_error_code(std::errc::invalid_argument);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (!buffer_end);
|
|
||||||
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <std::ranges::contiguous_range R>
|
|
||||||
std::error_code parse(const R& buffer)
|
|
||||||
{
|
|
||||||
return parse(buffer, defaultDeserializeFunc);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::expected<T, std::error_code> value(std::string_view key)
|
|
||||||
{
|
|
||||||
auto it = _configDB.find(key);
|
|
||||||
if (it == _configDB.end()) {
|
|
||||||
return std::unexpected(std::make_error_code(std::errc::argument_out_of_domain));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::expected<T, std::error_code> res;
|
|
||||||
|
|
||||||
std::visit(
|
|
||||||
[&res](auto&& val) {
|
|
||||||
using v_t = std::decay_t<decltype(val)>;
|
|
||||||
|
|
||||||
if constexpr (std::convertible_to<v_t, T>) {
|
|
||||||
res = static_cast<T>(std::forward<decltype(val)>(val));
|
|
||||||
// } else if constexpr (std::constructible_from<T, v_t>) {
|
|
||||||
// res = T{std::forward<decltype(val)>(val)};
|
|
||||||
} else {
|
|
||||||
res = std::unexpected(std::make_error_code(std::errc::invalid_argument));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
it->second);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool update(std::string_view key, const T& value)
|
|
||||||
{
|
|
||||||
auto it = _configDB.find(key);
|
|
||||||
if (it == _configDB.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ok;
|
|
||||||
std::visit(
|
|
||||||
[&value, &ok](auto& val) {
|
|
||||||
using v_t = std::decay_t<decltype(val)>;
|
|
||||||
|
|
||||||
if constexpr (std::convertible_to<T, v_t>) {
|
|
||||||
val = static_cast<v_t>(value);
|
|
||||||
ok = true;
|
|
||||||
} else {
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
it->second);
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::unordered_map<std::string_view, variant_from_tuple_t<deduce_val_types_t<DESCR_T>>> _configDB;
|
|
||||||
|
|
||||||
template <size_t I = 0, typename FuncT, typename... Ts>
|
|
||||||
bool forIndex(std::variant<Ts...>& var, std::string_view s, FuncT&& func, size_t idx)
|
|
||||||
{
|
|
||||||
if constexpr (I < sizeof...(Ts)) {
|
|
||||||
if (I == idx) {
|
|
||||||
using v_t = std::tuple_element_t<I, std::tuple<Ts...>>;
|
|
||||||
v_t val;
|
|
||||||
bool ok = std::forward<FuncT>(func)(s, val);
|
|
||||||
if (ok) {
|
|
||||||
var = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
} else {
|
|
||||||
return forIndex<I + 1>(var, s, std::forward<FuncT>(func), idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ASTOROSIB FM700 MOUNT CONFIGURATION CLASS */
|
/* ASTOROSIB FM700 MOUNT CONFIGURATION CLASS */
|
||||||
|
|
||||||
@ -891,317 +623,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Asibfm700MountConfig2 : protected ConfigHolder<decltype(Asibfm700MountConfigDefaults)>
|
|
||||||
{
|
|
||||||
using base_t = ConfigHolder<decltype(Asibfm700MountConfigDefaults)>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using base_t::update;
|
|
||||||
using base_t::value;
|
|
||||||
|
|
||||||
Asibfm700MountConfig2() : base_t(Asibfm700MountConfigDefaults)
|
|
||||||
{
|
|
||||||
updateAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
~Asibfm700MountConfig2() = default;
|
|
||||||
|
|
||||||
std::error_code load(const std::filesystem::path& path)
|
|
||||||
{
|
|
||||||
std::string buffer;
|
|
||||||
|
|
||||||
std::error_code ec;
|
|
||||||
auto sz = std::filesystem::file_size(path, ec);
|
|
||||||
|
|
||||||
if (!ec && sz) {
|
|
||||||
std::ifstream fst(path);
|
|
||||||
|
|
||||||
try {
|
|
||||||
buffer.resize(sz);
|
|
||||||
|
|
||||||
fst.read(buffer.data(), sz);
|
|
||||||
|
|
||||||
fst.close();
|
|
||||||
|
|
||||||
ec = base_t::parse(buffer, deserializer);
|
|
||||||
if (!ec) {
|
|
||||||
updateAll();
|
|
||||||
}
|
|
||||||
} catch (std::ios_base::failure const& ex) {
|
|
||||||
ec = ex.code();
|
|
||||||
} catch (std::length_error const& ex) {
|
|
||||||
ec = std::make_error_code(std::errc::no_buffer_space);
|
|
||||||
} catch (std::bad_alloc const& ex) {
|
|
||||||
ec = std::make_error_code(std::errc::not_enough_memory);
|
|
||||||
} catch (...) {
|
|
||||||
ec = std::make_error_code(std::errc::operation_canceled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ec;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool update(std::string_view key, const T& value)
|
|
||||||
{
|
|
||||||
bool ok = base_t::update(key, value);
|
|
||||||
if (ok) {
|
|
||||||
updateAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::chrono::milliseconds hardwarePollingPeriod{};
|
|
||||||
|
|
||||||
mcc::MccAngle siteLatitude{};
|
|
||||||
mcc::MccAngle siteLongitude{};
|
|
||||||
double siteElevation{};
|
|
||||||
double refractWavelength{};
|
|
||||||
|
|
||||||
std::string leapSecondFilename{};
|
|
||||||
std::string bulletinAFilename{};
|
|
||||||
|
|
||||||
mcc::MccAngle pzMinAltitude{};
|
|
||||||
mcc::MccAngle pzLimitSwitchHAMin{};
|
|
||||||
mcc::MccAngle pzLimitSwitchHAMax{};
|
|
||||||
|
|
||||||
|
|
||||||
AsibFM700ServoController::hardware_config_t servoControllerConfig{};
|
|
||||||
mcc::MccSimpleMovingModelParams movingModelParams{};
|
|
||||||
Asibfm700PCM::pcm_data_t pcmData{};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void updateAll()
|
|
||||||
{
|
|
||||||
hardwarePollingPeriod = std::get<decltype(hardwarePollingPeriod)>(this->_configDB["hardwarePollingPeriod"]);
|
|
||||||
|
|
||||||
// CCTE
|
|
||||||
|
|
||||||
siteLatitude = std::get<mcc::MccAngle>(this->_configDB["siteLatitude"]);
|
|
||||||
siteLongitude = std::get<mcc::MccAngle>(this->_configDB["siteLongitude"]);
|
|
||||||
siteElevation = std::get<double>(this->_configDB["siteElevation"]);
|
|
||||||
refractWavelength = std::get<double>(this->_configDB["refractWavelength"]);
|
|
||||||
|
|
||||||
leapSecondFilename = std::get<std::string>(this->_configDB["leapSecondFilename"]);
|
|
||||||
bulletinAFilename = std::get<std::string>(this->_configDB["bulletinAFilename"]);
|
|
||||||
|
|
||||||
// prohibited zones
|
|
||||||
|
|
||||||
pzMinAltitude = std::get<mcc::MccAngle>(this->_configDB["pzMinAltitude"]);
|
|
||||||
pzLimitSwitchHAMin = std::get<mcc::MccAngle>(this->_configDB["pzLimitSwitchHAMin"]);
|
|
||||||
pzLimitSwitchHAMax = std::get<mcc::MccAngle>(this->_configDB["pzLimitSwitchHAMax"]);
|
|
||||||
|
|
||||||
|
|
||||||
// hardware config
|
|
||||||
|
|
||||||
servoControllerConfig.hwConfig = {};
|
|
||||||
|
|
||||||
servoControllerConfig.MountDevPath = std::get<std::string>(this->_configDB["MountDevPath"]);
|
|
||||||
servoControllerConfig.EncoderDevPath = std::get<std::string>(this->_configDB["EncoderDevPath"]);
|
|
||||||
servoControllerConfig.EncoderXDevPath = std::get<std::string>(this->_configDB["EncoderXDevPath"]);
|
|
||||||
servoControllerConfig.EncoderYDevPath = std::get<std::string>(this->_configDB["EncoderYDevPath"]);
|
|
||||||
|
|
||||||
servoControllerConfig.devConfig.MountDevPath = servoControllerConfig.MountDevPath.data();
|
|
||||||
servoControllerConfig.devConfig.EncoderDevPath = servoControllerConfig.EncoderDevPath.data();
|
|
||||||
servoControllerConfig.devConfig.EncoderXDevPath = servoControllerConfig.EncoderXDevPath.data();
|
|
||||||
servoControllerConfig.devConfig.EncoderYDevPath = servoControllerConfig.EncoderYDevPath.data();
|
|
||||||
|
|
||||||
servoControllerConfig.devConfig.RunModel = std::get<int>(this->_configDB["RunModel"]);
|
|
||||||
servoControllerConfig.devConfig.MountDevSpeed = std::get<int>(this->_configDB["MountDevSpeed"]);
|
|
||||||
servoControllerConfig.devConfig.EncoderDevSpeed = std::get<int>(this->_configDB["EncoderDevSpeed"]);
|
|
||||||
servoControllerConfig.devConfig.SepEncoder = std::get<int>(this->_configDB["SepEncoder"]);
|
|
||||||
|
|
||||||
std::chrono::duration<double> secs; // seconds as floating-point
|
|
||||||
|
|
||||||
secs = std::get<std::chrono::milliseconds>(this->_configDB["MountReqInterval"]);
|
|
||||||
servoControllerConfig.devConfig.MountReqInterval = secs.count();
|
|
||||||
|
|
||||||
secs = std::get<std::chrono::milliseconds>(this->_configDB["EncoderReqInterval"]);
|
|
||||||
servoControllerConfig.devConfig.EncoderReqInterval = secs.count();
|
|
||||||
|
|
||||||
secs = std::get<std::chrono::milliseconds>(this->_configDB["EncoderSpeedInterval"]);
|
|
||||||
servoControllerConfig.devConfig.EncoderSpeedInterval = secs.count();
|
|
||||||
|
|
||||||
std::vector<double> pid = std::get<std::vector<double>>(this->_configDB["XPIDC"]);
|
|
||||||
if (pid.size() > 2) {
|
|
||||||
servoControllerConfig.devConfig.XPIDC.P = pid[0];
|
|
||||||
servoControllerConfig.devConfig.XPIDC.I = pid[1];
|
|
||||||
servoControllerConfig.devConfig.XPIDC.D = pid[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
pid = std::get<std::vector<double>>(this->_configDB["XPIDV"]);
|
|
||||||
if (pid.size() > 2) {
|
|
||||||
servoControllerConfig.devConfig.XPIDV.P = pid[0];
|
|
||||||
servoControllerConfig.devConfig.XPIDV.I = pid[1];
|
|
||||||
servoControllerConfig.devConfig.XPIDV.D = pid[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
pid = std::get<std::vector<double>>(this->_configDB["YPIDC"]);
|
|
||||||
if (pid.size() > 2) {
|
|
||||||
servoControllerConfig.devConfig.YPIDC.P = pid[0];
|
|
||||||
servoControllerConfig.devConfig.YPIDC.I = pid[1];
|
|
||||||
servoControllerConfig.devConfig.YPIDC.D = pid[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
pid = std::get<std::vector<double>>(this->_configDB["YPIDV"]);
|
|
||||||
if (pid.size() > 2) {
|
|
||||||
servoControllerConfig.devConfig.YPIDV.P = pid[0];
|
|
||||||
servoControllerConfig.devConfig.YPIDV.I = pid[1];
|
|
||||||
servoControllerConfig.devConfig.YPIDV.D = pid[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// slew and track parameters
|
|
||||||
|
|
||||||
movingModelParams.telemetryTimeout =
|
|
||||||
std::get<decltype(movingModelParams.telemetryTimeout)>(this->_configDB["telemetryTimeout"]);
|
|
||||||
|
|
||||||
movingModelParams.minTimeToPZone =
|
|
||||||
std::get<decltype(movingModelParams.minTimeToPZone)>(this->_configDB["minTimeToPZone"]);
|
|
||||||
|
|
||||||
movingModelParams.updatingPZoneInterval =
|
|
||||||
std::get<decltype(movingModelParams.updatingPZoneInterval)>(this->_configDB["updatingPZoneInterval"]);
|
|
||||||
|
|
||||||
movingModelParams.slewToleranceRadius =
|
|
||||||
std::get<decltype(movingModelParams.slewToleranceRadius)>(this->_configDB["slewToleranceRadius"]);
|
|
||||||
|
|
||||||
movingModelParams.adjustCoordDiff =
|
|
||||||
std::get<decltype(movingModelParams.adjustCoordDiff)>(this->_configDB["adjustCoordDiff"]);
|
|
||||||
|
|
||||||
movingModelParams.adjustCycleInterval =
|
|
||||||
std::get<decltype(movingModelParams.adjustCycleInterval)>(this->_configDB["adjustCycleInterval"]);
|
|
||||||
|
|
||||||
movingModelParams.slewTimeout =
|
|
||||||
std::get<decltype(movingModelParams.slewTimeout)>(this->_configDB["slewTimeout"]);
|
|
||||||
|
|
||||||
movingModelParams.timeShiftToTargetPoint =
|
|
||||||
std::get<decltype(movingModelParams.timeShiftToTargetPoint)>(this->_configDB["timeShiftToTargetPoint"]);
|
|
||||||
|
|
||||||
movingModelParams.trackingCycleInterval =
|
|
||||||
std::get<decltype(movingModelParams.trackingCycleInterval)>(this->_configDB["trackingCycleInterval"]);
|
|
||||||
|
|
||||||
|
|
||||||
// PCM data
|
|
||||||
|
|
||||||
pcmData.type = std::get<decltype(pcmData.type)>(this->_configDB["pcmType"]);
|
|
||||||
|
|
||||||
pcmData.siteLatitude = std::get<mcc::MccAngle>(this->_configDB["siteLatitude"]);
|
|
||||||
|
|
||||||
pid = std::get<std::vector<double>>(this->_configDB["pcmGeomCoeffs"]);
|
|
||||||
if (pid.size() >= 9) { // must be 9 coefficients
|
|
||||||
pcmData.geomCoefficients = {.zeroPointX = pid[0],
|
|
||||||
.zeroPointY = pid[1],
|
|
||||||
.collimationErr = pid[2],
|
|
||||||
.nonperpendErr = pid[3],
|
|
||||||
.misalignErr1 = pid[4],
|
|
||||||
.misalignErr2 = pid[5],
|
|
||||||
.tubeFlexure = pid[6],
|
|
||||||
.forkFlexure = pid[7],
|
|
||||||
.DECaxisFlexure = pid[8]};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<size_t> dd = std::get<decltype(dd)>(this->_configDB["pcmBsplineDegree"]);
|
|
||||||
if (dd.size() >= 2) {
|
|
||||||
pcmData.bspline.bsplDegreeX = dd[0] > 0 ? dd[0] : 3;
|
|
||||||
pcmData.bspline.bsplDegreeY = dd[1] > 0 ? dd[1] : 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid = std::get<std::vector<double>>(this->_configDB["pcmBsplineXknots"]);
|
|
||||||
// pid must contains interior and border (single point for each border) knots so minimal length must be 2
|
|
||||||
if (pid.size() >= 2) {
|
|
||||||
// generate full knots array (with border knots)
|
|
||||||
size_t Nknots = pid.size() + pcmData.bspline.bsplDegreeX * 2 - 2;
|
|
||||||
pcmData.bspline.knotsX.resize(Nknots);
|
|
||||||
|
|
||||||
for (size_t i = 0; i <= pcmData.bspline.bsplDegreeX; ++i) { // border knots
|
|
||||||
pcmData.bspline.knotsX[i] = pid[0];
|
|
||||||
pcmData.bspline.knotsX[Nknots - i - 1] = pid.back();
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < (pid.size() - 2); ++i) { // interior knots
|
|
||||||
pcmData.bspline.knotsX[i + pcmData.bspline.bsplDegreeX] = pid[1 + i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pid = std::get<std::vector<double>>(this->_configDB["pcmBsplineYknots"]);
|
|
||||||
// pid must contains interior and border (single point for each border) knots so minimal length must be 2
|
|
||||||
if (pid.size() >= 2) {
|
|
||||||
// generate full knots array (with border knots)
|
|
||||||
size_t Nknots = pid.size() + pcmData.bspline.bsplDegreeY * 2 - 2;
|
|
||||||
pcmData.bspline.knotsY.resize(Nknots);
|
|
||||||
|
|
||||||
for (size_t i = 0; i <= pcmData.bspline.bsplDegreeY; ++i) { // border knots
|
|
||||||
pcmData.bspline.knotsY[i] = pid[0];
|
|
||||||
pcmData.bspline.knotsY[Nknots - i - 1] = pid.back();
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < (pid.size() - 2); ++i) { // interior knots
|
|
||||||
pcmData.bspline.knotsY[i + pcmData.bspline.bsplDegreeY] = pid[1 + i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// minimal allowed number of B-spline coefficients
|
|
||||||
size_t Ncoeffs = pcmData.type == mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY
|
|
||||||
? 0
|
|
||||||
: (pcmData.bspline.knotsX.size() - pcmData.bspline.bsplDegreeX - 1) *
|
|
||||||
(pcmData.bspline.knotsY.size() - pcmData.bspline.bsplDegreeY - 1);
|
|
||||||
|
|
||||||
pid = std::get<std::vector<double>>(this->_configDB["pcmBsplineXcoeffs"]);
|
|
||||||
|
|
||||||
if (pid.size() >= Ncoeffs) {
|
|
||||||
pcmData.bspline.coeffsX.resize(Ncoeffs);
|
|
||||||
for (size_t i = 0; i < Ncoeffs; ++i) {
|
|
||||||
pcmData.bspline.coeffsX[i] = pid[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pid = std::get<std::vector<double>>(this->_configDB["pcmBsplineYcoeffs"]);
|
|
||||||
|
|
||||||
if (pid.size() >= Ncoeffs) {
|
|
||||||
pcmData.bspline.coeffsY.resize(Ncoeffs);
|
|
||||||
for (size_t i = 0; i < Ncoeffs; ++i) {
|
|
||||||
pcmData.bspline.coeffsY[i] = pid[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static auto deserializer = [](std::string_view str, auto& value) {
|
|
||||||
using value_t = std::decay_t<decltype(value)>;
|
|
||||||
|
|
||||||
bool ok = true;
|
|
||||||
|
|
||||||
if constexpr (std::is_arithmetic_v<value_t> || mcc::traits::mcc_output_char_range<value_t> ||
|
|
||||||
std::ranges::range<value_t> || mcc::traits::mcc_time_duration_c<value_t>) {
|
|
||||||
return base_t::defaultDeserializeFunc(str, value);
|
|
||||||
} else if constexpr (std::same_as<value_t, mcc::MccAngle>) { // assume here all angles are in degrees
|
|
||||||
double vd;
|
|
||||||
ok = base_t::defaultDeserializeFunc(str, vd);
|
|
||||||
if (ok) {
|
|
||||||
value = mcc::MccAngle(vd, mcc::MccDegreeTag{});
|
|
||||||
}
|
|
||||||
} else if constexpr (std::same_as<value_t, mcc::MccDefaultPCMType>) {
|
|
||||||
std::string vstr;
|
|
||||||
ok = base_t::defaultDeserializeFunc(str, vstr);
|
|
||||||
auto s = mcc::utils::trimSpaces(vstr);
|
|
||||||
|
|
||||||
if (ok) {
|
|
||||||
if (s == mcc::MccDefaultPCMTypeString<mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY>) {
|
|
||||||
value = mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY;
|
|
||||||
} else if (s == mcc::MccDefaultPCMTypeString<mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY_BSPLINE>) {
|
|
||||||
value = mcc::MccDefaultPCMType::PCM_TYPE_GEOMETRY;
|
|
||||||
} else if (s == mcc::MccDefaultPCMTypeString<mcc::MccDefaultPCMType::PCM_TYPE_BSPLINE>) {
|
|
||||||
value = mcc::MccDefaultPCMType::PCM_TYPE_BSPLINE;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static constexpr std::string_view Asibfm700MountConfigString =
|
static constexpr std::string_view Asibfm700MountConfigString =
|
||||||
R"--(
|
R"--(
|
||||||
|
|||||||
@ -14,13 +14,13 @@ Asibfm700MountNetServer::Asibfm700MountNetServer(asio::io_context& ctx,
|
|||||||
// to avoid possible compiler optimization (one needs to catch 'mount' strictly by reference)
|
// to avoid possible compiler optimization (one needs to catch 'mount' strictly by reference)
|
||||||
auto* mount_ptr = &mount;
|
auto* mount_ptr = &mount;
|
||||||
|
|
||||||
base_t::_handleMessageFunc = [base_hndl_func = std::move(base_t::_handleMessageFunc), mount_ptr,
|
base_t::_handleMessageFunc = [mount_ptr, this](std::string_view command) {
|
||||||
this](std::string_view command) {
|
|
||||||
using mount_error_t = typename Asibfm700Mount::error_t;
|
using mount_error_t = typename Asibfm700Mount::error_t;
|
||||||
std::error_code err{};
|
std::error_code err{};
|
||||||
|
|
||||||
Asibfm700NetMessage input_msg;
|
Asibfm700NetMessage input_msg;
|
||||||
Asibfm700NetMessage<handle_message_func_result_t> output_msg;
|
using output_msg_t = Asibfm700NetMessage<handle_message_func_result_t>;
|
||||||
|
output_msg_t output_msg;
|
||||||
|
|
||||||
auto ec = parseMessage(command, input_msg);
|
auto ec = parseMessage(command, input_msg);
|
||||||
|
|
||||||
@ -41,7 +41,8 @@ Asibfm700MountNetServer::Asibfm700MountNetServer(asio::io_context& ctx,
|
|||||||
ASIBFM700_COMMPROTO_KEYWORD_METEO_STR, mount_ptr->getStateERFA().meteo);
|
ASIBFM700_COMMPROTO_KEYWORD_METEO_STR, mount_ptr->getStateERFA().meteo);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
output_msg = base_t::handleMessage<decltype(output_msg)>(input_msg, mount_ptr);
|
// basic network message processing
|
||||||
|
output_msg = base_t::handleMessage<output_msg_t>(input_msg, mount_ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,35 +21,7 @@ int main()
|
|||||||
auto desc = std::make_tuple(rec_t{"A", 1}, rec_t{"B", 2.2}, rec_t{"C", std::string("EEE")}, rec_t{"D", 3.3},
|
auto desc = std::make_tuple(rec_t{"A", 1}, rec_t{"B", 2.2}, rec_t{"C", std::string("EEE")}, rec_t{"D", 3.3},
|
||||||
rec_t{"E", std::vector<int>{1, 2, 3}});
|
rec_t{"E", std::vector<int>{1, 2, 3}});
|
||||||
|
|
||||||
asibfm700::ConfigHolder ch(desc);
|
std::error_code err;
|
||||||
|
|
||||||
// auto err = ch.parse(cfg_str, [](auto s, auto &v) {
|
|
||||||
// if constexpr (std::is_arithmetic_v<std::decay_t<decltype(v)>>) {
|
|
||||||
// v = 77;
|
|
||||||
// } else {
|
|
||||||
// v = std::string{s.begin(), s.end()};
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return true;
|
|
||||||
// });
|
|
||||||
|
|
||||||
auto err = ch.parse(cfg_str);
|
|
||||||
|
|
||||||
auto v = ch.value<float>("A");
|
|
||||||
|
|
||||||
std::cout << v.value() << "\n";
|
|
||||||
|
|
||||||
// auto v2 = ch.value<std::string>("D");
|
|
||||||
auto v2 = ch.value<std::string>("C");
|
|
||||||
std::cout << v2.value_or("<no value>") << "\n";
|
|
||||||
|
|
||||||
auto v3 = ch.value<std::vector<int>>("E");
|
|
||||||
std::cout << "[";
|
|
||||||
for (auto& el : v3.value_or({0, 0, 0})) {
|
|
||||||
std::cout << el << " ";
|
|
||||||
}
|
|
||||||
std::cout << "]\n";
|
|
||||||
|
|
||||||
|
|
||||||
std::ofstream fst("/tmp/cfg.cfg");
|
std::ofstream fst("/tmp/cfg.cfg");
|
||||||
fst << asibfm700::Asibfm700MountConfigString;
|
fst << asibfm700::Asibfm700MountConfigString;
|
||||||
@ -85,7 +57,7 @@ int main()
|
|||||||
if (ec) {
|
if (ec) {
|
||||||
std::cout << "EC = " << ec.message() << "\n";
|
std::cout << "EC = " << ec.message() << "\n";
|
||||||
} else {
|
} else {
|
||||||
v3 = kvh.getValue<std::vector<int>>("E");
|
auto v3 = kvh.getValue<std::vector<int>>("E");
|
||||||
std::cout << "[";
|
std::cout << "[";
|
||||||
for (auto& el : v3.value_or({0, 0, 0})) {
|
for (auto& el : v3.value_or({0, 0, 0})) {
|
||||||
std::cout << el << " ";
|
std::cout << el << " ";
|
||||||
|
|||||||
@ -907,148 +907,17 @@ public:
|
|||||||
mount_error_t m_err;
|
mount_error_t m_err;
|
||||||
|
|
||||||
MccNetMessage input_msg;
|
MccNetMessage input_msg;
|
||||||
MccNetMessage<handle_message_func_result_t> output_msg;
|
using output_msg_t = MccNetMessage<handle_message_func_result_t>;
|
||||||
|
output_msg_t output_msg;
|
||||||
|
|
||||||
auto ec = parseMessage(command, input_msg);
|
auto ec = parseMessage(command, input_msg);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR, ec);
|
output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR, ec);
|
||||||
} else {
|
} else {
|
||||||
output_msg = handleMessage<decltype(output_msg)>(input_msg, mount_ptr);
|
output_msg = handleMessage<output_msg_t>(input_msg, mount_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return output_msg.byteRepr();
|
return output_msg.byteRepr();
|
||||||
|
|
||||||
// std::error_code err{};
|
|
||||||
|
|
||||||
// if (auto ec = parseMessage(command, input_msg)) {
|
|
||||||
// output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR, ec);
|
|
||||||
// } else {
|
|
||||||
// if (input_msg.withKey(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR)) { // strange!
|
|
||||||
// output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR, command);
|
|
||||||
// } else if (input_msg.withKey(MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR)) { // ??!!!
|
|
||||||
// output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR, command);
|
|
||||||
// } else if (input_msg.withKey(MCC_COMMPROTO_KEYWORD_RESTART_SERVER_STR)) {
|
|
||||||
// this->restart();
|
|
||||||
// output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR, command);
|
|
||||||
// } else if (input_msg.withKey(MCC_COMMPROTO_KEYWORD_INIT_STR)) {
|
|
||||||
// m_err = mount_ptr->initMount();
|
|
||||||
// if (m_err) {
|
|
||||||
// err = mcc_deduce_error_code(m_err, MccGenericMountNetworkServerErrorCode::ERROR_MOUNT_INIT);
|
|
||||||
// }
|
|
||||||
// } else if (input_msg.withKey(MCC_COMMPROTO_KEYWORD_STOP_STR)) {
|
|
||||||
// m_err = mount_ptr->stopMount();
|
|
||||||
// if (m_err) {
|
|
||||||
// err = mcc_deduce_error_code(m_err, MccGenericMountNetworkServerErrorCode::ERROR_MOUNT_STOP);
|
|
||||||
// }
|
|
||||||
// } else if (input_msg.withKey(MCC_COMMPROTO_KEYWORD_SLEW_STR)) {
|
|
||||||
// m_err = mount_ptr->slewToTarget(false);
|
|
||||||
// if (m_err) {
|
|
||||||
// err = mcc_deduce_error_code(m_err, MccGenericMountNetworkServerErrorCode::ERROR_MOUNT_SLEW);
|
|
||||||
// }
|
|
||||||
// } else if (input_msg.withKey(MCC_COMMPROTO_KEYWORD_MOVE_STR)) {
|
|
||||||
// m_err = mount_ptr->slewToTarget(true);
|
|
||||||
// if (m_err) {
|
|
||||||
// err = mcc_deduce_error_code(m_err, MccGenericMountNetworkServerErrorCode::ERROR_MOUNT_MOVE);
|
|
||||||
// }
|
|
||||||
// } else if (input_msg.withKey(MCC_COMMPROTO_KEYWORD_TRACK_STR)) {
|
|
||||||
// m_err = mount_ptr->trackTarget();
|
|
||||||
// if (m_err) {
|
|
||||||
// err = mcc_deduce_error_code(m_err, MccGenericMountNetworkServerErrorCode::ERROR_MOUNT_TRACK);
|
|
||||||
// }
|
|
||||||
// } else if (input_msg.withKey(MCC_COMMPROTO_KEYWORD_COORDFMT_STR)) {
|
|
||||||
// auto v = input_msg.paramValue<MccCoordinateSerializer::SerializedCoordFormat>(0);
|
|
||||||
// if (v) {
|
|
||||||
// _coordFormat = v.value();
|
|
||||||
// output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR, command);
|
|
||||||
// } else {
|
|
||||||
// err = v.error();
|
|
||||||
// }
|
|
||||||
// } else if (input_msg.withKey(MCC_COMMPROTO_KEYWORD_COORDPREC_STR)) {
|
|
||||||
// auto v = input_msg.paramValue<MccCoordinateSerializer::SexagesimalCoordPrec>(0);
|
|
||||||
// if (v) {
|
|
||||||
// _coordPrec = v.value();
|
|
||||||
// output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR, command);
|
|
||||||
// } else {
|
|
||||||
// err = v.error();
|
|
||||||
// }
|
|
||||||
// } else if (input_msg.withKey(MCC_COMMPROTO_KEYWORD_TARGET_STR)) {
|
|
||||||
// // by default return ICRS coordinates
|
|
||||||
// MccCelestialPoint cp{.pair_kind = MccCoordPairKind::COORDS_KIND_RADEC_ICRS};
|
|
||||||
|
|
||||||
// auto sz = input_msg.paramSize();
|
|
||||||
// if (sz) { // set or get operation
|
|
||||||
// auto vc = input_msg.paramValue<MccCelestialPoint>(0); // is it set operation?
|
|
||||||
// if (vc) { // coordinates are given - set
|
|
||||||
// operation
|
|
||||||
// auto m_err = mount_ptr->setPointingTarget(vc.value());
|
|
||||||
// if (m_err) {
|
|
||||||
// if (m_err) {
|
|
||||||
// err = mcc_deduce_error_code(
|
|
||||||
// m_err, MccGenericMountNetworkServerErrorCode::ERROR_MOUNT_SET_TARGET);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR, command);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// auto vp = input_msg.paramValue<MccCoordPairKind>(0);
|
|
||||||
// if (vp) { // coordinate pair kind is given
|
|
||||||
// cp.pair_kind = vp.value();
|
|
||||||
// err = coordsFromTelemetryData(*mount_ptr, true, cp);
|
|
||||||
// if (!err) {
|
|
||||||
// output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR,
|
|
||||||
// MCC_COMMPROTO_KEYWORD_TARGET_STR, cp);
|
|
||||||
// }
|
|
||||||
// } else { // invalid command!!!
|
|
||||||
// err = vp.error();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else { // get operation
|
|
||||||
// err = coordsFromTelemetryData(*mount_ptr, true, cp);
|
|
||||||
// if (!err) {
|
|
||||||
// output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR,
|
|
||||||
// MCC_COMMPROTO_KEYWORD_TARGET_STR,
|
|
||||||
// _coordFormat, _coordPrec, cp);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// } else if (input_msg.withKey(MCC_COMMPROTO_KEYWORD_MOUNT_STR)) {
|
|
||||||
// // by default return ICRS coordinates
|
|
||||||
// MccCelestialPoint cp{.pair_kind = MccCoordPairKind::COORDS_KIND_RADEC_ICRS};
|
|
||||||
|
|
||||||
// if (input_msg.paramSize()) { // ccordinate pair kind is given
|
|
||||||
// auto vp = input_msg.paramValue<MccCoordPairKind>(0);
|
|
||||||
// if (vp) { // coordinate pair kind is given
|
|
||||||
// cp.pair_kind = vp.value();
|
|
||||||
// err = coordsFromTelemetryData(*mount_ptr, false, cp);
|
|
||||||
// if (!err) {
|
|
||||||
// output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR,
|
|
||||||
// MCC_COMMPROTO_KEYWORD_MOUNT_STR, cp);
|
|
||||||
// }
|
|
||||||
// } else { // invalid command!!!
|
|
||||||
// err = vp.error();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// } else {
|
|
||||||
// err = coordsFromTelemetryData(*mount_ptr, false, cp);
|
|
||||||
// if (!err) {
|
|
||||||
// output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR,
|
|
||||||
// MCC_COMMPROTO_KEYWORD_MOUNT_STR,
|
|
||||||
// _coordFormat, _coordPrec, cp);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// err = std::make_error_code(std::errc::invalid_argument);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (err) { // send error description
|
|
||||||
// output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR, err);
|
|
||||||
// }
|
|
||||||
// // else { // send ACK with copy of the input message
|
|
||||||
// // output_msg.construct(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR, command);
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return output_msg.byteRepr();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -237,204 +237,6 @@ static constexpr std::array MCC_COMMPROTO_VALID_KEYS_HASH = []<size_t... Is>(std
|
|||||||
}(std::make_index_sequence<MCC_COMMPROTO_VALID_KEYS.size()>());
|
}(std::make_index_sequence<MCC_COMMPROTO_VALID_KEYS.size()>());
|
||||||
|
|
||||||
|
|
||||||
static constexpr size_t MCC_COMMPROTO_KEYWORD_SERVER_ACK_HASH =
|
|
||||||
mcc::utils::FNV1aHash(MCC_COMMPROTO_KEYWORD_SERVER_ACK_STR);
|
|
||||||
|
|
||||||
static constexpr size_t MCC_COMMPROTO_KEYWORD_SERVER_ERROR_HASH =
|
|
||||||
mcc::utils::FNV1aHash(MCC_COMMPROTO_KEYWORD_SERVER_ERROR_STR);
|
|
||||||
|
|
||||||
static constexpr size_t MCC_COMMPROTO_KEYWORD_TARGET_HASH = mcc::utils::FNV1aHash(MCC_COMMPROTO_KEYWORD_TARGET_STR);
|
|
||||||
|
|
||||||
static constexpr size_t MCC_COMMPROTO_KEYWORD_MOUNT_HASH = mcc::utils::FNV1aHash(MCC_COMMPROTO_KEYWORD_MOUNT_STR);
|
|
||||||
|
|
||||||
|
|
||||||
template <traits::mcc_char_range T = std::string_view, std::ranges::output_range<T> RT = std::vector<T>>
|
|
||||||
struct mcc_netmsg_parse_result_t {
|
|
||||||
size_t keyword_hash;
|
|
||||||
T keyword;
|
|
||||||
RT params;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// network message parsing result class concept
|
|
||||||
template <typename T>
|
|
||||||
concept mcc_netmsg_parse_result_c = requires(T t) {
|
|
||||||
requires std::same_as<decltype(t.keyword_hash), size_t>; // hash of keyword
|
|
||||||
requires traits::mcc_char_range<decltype(t.keyword)>; // keyword char-range representation
|
|
||||||
// a range of parameters char-range representations
|
|
||||||
requires std::ranges::output_range<decltype(t.params), decltype(t.keyword)>;
|
|
||||||
};
|
|
||||||
|
|
||||||
// the function returns hash of message keyword
|
|
||||||
// if 'from_server' is true then given network message is considered as a server response, i.e.,
|
|
||||||
// valid keywords are "ACK" or "ERROR"
|
|
||||||
//
|
|
||||||
// the funtions returns false in the case of invalid message format and true otherwise
|
|
||||||
//
|
|
||||||
template <traits::mcc_input_char_range IR, mcc_netmsg_parse_result_c ResT>
|
|
||||||
bool mcc_parse_netmsg(const IR& netmsg, ResT& parse_res, bool from_server = false)
|
|
||||||
{
|
|
||||||
if (std::ranges::size(netmsg) == 0) {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto found = std::ranges::search(netmsg, MCC_COMMPROTO_KEYPARAM_DELIM_SEQ);
|
|
||||||
if (std::distance(netmsg.begin(), found.begin()) == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t hash = mcc::utils::FNV1aHash(netmsg.begin(), found.begin());
|
|
||||||
auto it = std::ranges::find(MCC_COMMPROTO_VALID_KEYS_HASH, hash);
|
|
||||||
if (it == MCC_COMMPROTO_VALID_KEYS_HASH.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (from_server) { // only ACK or ERROR
|
|
||||||
auto ok = hash == MCC_COMMPROTO_VALID_KEYS_HASH[0] || hash == MCC_COMMPROTO_VALID_KEYS_HASH[1];
|
|
||||||
if (!ok) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_res.keyword_hash = hash;
|
|
||||||
parse_res.keyword = {netmsg.begin(), found.begin()};
|
|
||||||
|
|
||||||
auto pars = netmsg | std::views::drop(std::distance(netmsg.begin(), found.end())) |
|
|
||||||
std::views::split(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ);
|
|
||||||
|
|
||||||
decltype(parse_res.params) res;
|
|
||||||
|
|
||||||
for (auto const& el : pars) { // parameters
|
|
||||||
std::back_inserter(res) = {el.begin(), el.end()};
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_res.params = std::move(res);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// construct network message
|
|
||||||
// the function returns false if input keyword is not valid one (see MCC_COMMPROTO_VALID_KEYS)!
|
|
||||||
template <typename... PTs>
|
|
||||||
bool mcc_netmsg_construct(traits::mcc_output_char_range auto& msg,
|
|
||||||
traits::mcc_input_char_range auto const& keyword,
|
|
||||||
PTs... params)
|
|
||||||
{
|
|
||||||
const size_t hash = mcc::utils::FNV1aHash(keyword);
|
|
||||||
if (!std::ranges::contains(MCC_COMMPROTO_VALID_KEYS_HASH, hash)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = {keyword.begin(), keyword.end()};
|
|
||||||
|
|
||||||
if constexpr (sizeof...(PTs)) {
|
|
||||||
std::ranges::copy(MCC_COMMPROTO_KEYPARAM_DELIM_SEQ, std::back_inserter(msg));
|
|
||||||
|
|
||||||
[&msg]<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(msg));
|
|
||||||
} else if constexpr (std::convertible_to<T, std::string>) {
|
|
||||||
std::ranges::copy(static_cast<std::string>(par), std::back_inserter(msg));
|
|
||||||
} else if constexpr (std::constructible_from<std::string, T>) {
|
|
||||||
std::ranges::copy(std::string(par), std::back_inserter(msg));
|
|
||||||
} else if constexpr (traits::mcc_char_range<T>) {
|
|
||||||
std::ranges::copy(std::string(par.begin(), par.end()), std::back_inserter(msg));
|
|
||||||
} else if constexpr (std::same_as<T, MccCoordPairKind>) {
|
|
||||||
std::ranges::copy(mcc_pairkind2str(par), std::back_inserter(msg));
|
|
||||||
} else {
|
|
||||||
static_assert(false, "UNSUPPORTED TYPE!!!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (sizeof...(Ts)) {
|
|
||||||
std::ranges::copy(MCC_COMMPROTO_PARAMPARAM_DELIM_SEQ, std::back_inserter(msg));
|
|
||||||
|
|
||||||
std::forward<decltype(self)>(self)(pars...);
|
|
||||||
}
|
|
||||||
}(params...);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// the function convert given network message parsing result class to
|
|
||||||
// celestial point coordinates according to parsed message parameters.
|
|
||||||
//
|
|
||||||
// It is assumed that the coordinates and their type are contained in the consecutive elements of the input array
|
|
||||||
// starting from the element 'from_idx' (zero-based):
|
|
||||||
//
|
|
||||||
// parse_res.params[..., X-COORD, Y-COORD, XY-KIND, ...]
|
|
||||||
//
|
|
||||||
// th last parameter 'XY-KIND' can be omitted and, in this case, 'default_kind' is assumed
|
|
||||||
//
|
|
||||||
// NOTE: IT IS ASSUMED THAT THE COORDINATES ARE REPRESENTED AS DEGREES EXPRESSED BY THE NUMBER WITH A FLOATING POINT
|
|
||||||
// OR IN SEXAGESIMAL FORM. IN THE CASE OF SEXAGESIMAL FORM THE TYPE (DEGREES OR HOURS) OF THE COORDINATE
|
|
||||||
// REPRESENTATION IS DETERMINED BY 'XY-KIND', E.G.:
|
|
||||||
// parse_res.params[..., "12:34:52.123", "23:43:56.12", "HADEC", ...]
|
|
||||||
// 'HADEC' STRING FOR 'XY-KIND' DETERMINES THE FIRST COORDINATE (HOUR ANGLE)
|
|
||||||
// AS AN ANGLE IN HOUR FORM WHILE THE SECOND ONE (DECLINATION) IN DEGREES
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// The function returns false if it can not convert coordinate string to number or the 'XY-KIND' string is invalid
|
|
||||||
//
|
|
||||||
bool mcc_netmsg_get_cpoint(mcc_netmsg_parse_result_c auto const& parse_res,
|
|
||||||
size_t from_idx,
|
|
||||||
mcc_celestial_point_c auto& cpoint,
|
|
||||||
MccCoordPairKind default_kind)
|
|
||||||
requires std::ranges::contiguous_range<decltype(parse_res.keyword)>
|
|
||||||
{
|
|
||||||
if (std::ranges::size(parse_res.params) < (from_idx + 2)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MccCoordPairKind kind = default_kind;
|
|
||||||
|
|
||||||
if (std::ranges::size(parse_res.params) > (from_idx + 2)) {
|
|
||||||
kind = mcc_str2pairkind(parse_res.params[from_idx + 2]);
|
|
||||||
if (kind == MccCoordPairKind::COORDS_KIND_GENERIC) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<double> ang1, ang2;
|
|
||||||
|
|
||||||
switch (kind) {
|
|
||||||
case mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS:
|
|
||||||
case mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP:
|
|
||||||
case mcc::MccCoordPairKind::COORDS_KIND_HADEC_APP:
|
|
||||||
ang1 = mcc::utils::parsAngleString(parse_res.params[from_idx], true);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ang1 = mcc::utils::parsAngleString(parse_res.params[from_idx]);
|
|
||||||
}
|
|
||||||
if (!ang1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ang2 = mcc::utils::parsAngleString(parse_res.params[from_idx + 1]);
|
|
||||||
if (!ang2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kind != mcc::MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
|
|
||||||
mcc_tp2tp(std::chrono::system_clock::now(), cpoint.time_point);
|
|
||||||
} else {
|
|
||||||
// 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, cpoint.time_point);
|
|
||||||
}
|
|
||||||
|
|
||||||
cpoint.pair_kind = kind;
|
|
||||||
|
|
||||||
cpoint.X = MccAngle(ang1.value(), mcc::MccDegreeTag{}); // to radians
|
|
||||||
cpoint.Y = MccAngle(ang2.value(), mcc::MccDegreeTag{}); // to radians
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept mcc_netmsg_valid_keys_c = requires(T t) {
|
concept mcc_netmsg_valid_keys_c = requires(T t) {
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
#include "../mcc_ccte_erfa.h"
|
#include "../mcc_ccte_erfa.h"
|
||||||
#include "../mcc_netserver_proto.h"
|
|
||||||
#include "../mcc_pzone.h"
|
#include "../mcc_pzone.h"
|
||||||
#include "../mcc_pzone_container.h"
|
#include "../mcc_pzone_container.h"
|
||||||
|
|
||||||
@ -157,63 +156,5 @@ int main()
|
|||||||
std::cout << a1.sexagesimal() << "\n" << a2.sexagesimal() << "\n";
|
std::cout << a1.sexagesimal() << "\n" << a2.sexagesimal() << "\n";
|
||||||
|
|
||||||
|
|
||||||
std::cout << "\n\n";
|
|
||||||
|
|
||||||
std::string sm{"ACK TARGET 12:23:45.13 -09.23423525 RADEC"};
|
|
||||||
// std::vector<std::string> sv;
|
|
||||||
std::vector<std::string_view> sv;
|
|
||||||
mcc::network::mcc_netmsg_parse_result_t<> p_res;
|
|
||||||
|
|
||||||
auto mr = mcc::network::mcc_parse_netmsg(sm, p_res);
|
|
||||||
|
|
||||||
std::cout << "MSG: <" << sm << ">\n";
|
|
||||||
std::cout << "\t" << p_res.keyword_hash << "\n";
|
|
||||||
std::cout << "\t[" << p_res.keyword << "]\n";
|
|
||||||
for (auto const& pr : p_res.params) {
|
|
||||||
std::cout << "\t[" << pr << "]\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "GET CPOINT RET: "
|
|
||||||
<< mcc::network::mcc_netmsg_get_cpoint(p_res, 1, cp, mcc::MccCoordPairKind::COORDS_KIND_AZZD) << "\n";
|
|
||||||
std::cout << "CPOINT.X = " << cp.X << ", CPOINT.Y = " << cp.Y << " ("
|
|
||||||
<< mcc::network::mcc_pairkind2str(cp.pair_kind) << ")\n";
|
|
||||||
|
|
||||||
|
|
||||||
sm = "ERROR ";
|
|
||||||
|
|
||||||
mr = mcc::network::mcc_parse_netmsg(sm, p_res);
|
|
||||||
|
|
||||||
std::cout << "MSG: <" << sm << ">\n";
|
|
||||||
sm[0] = 'e';
|
|
||||||
std::cout << "\t" << p_res.keyword_hash << "\n";
|
|
||||||
std::cout << "\t[" << p_res.keyword << "]\n";
|
|
||||||
for (auto const& pr : p_res.params) {
|
|
||||||
std::cout << "\t[" << pr << "]\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
mr = mcc::network::mcc_netmsg_construct(sm, mcc::network::MCC_COMMPROTO_KEYWORD_TARGET_STR, 12.3456789,
|
|
||||||
"34:21:56.132", mcc::MccCoordPairKind::COORDS_KIND_AZZD);
|
|
||||||
|
|
||||||
std::cout << "\nNETMSG: [" << sm << "] (" << mr << ")\n";
|
|
||||||
|
|
||||||
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::duration<double>(55.816);
|
|
||||||
+ std::chrono::hours(11) + std::chrono::minutes(58) + std::chrono::milliseconds(55816);
|
|
||||||
|
|
||||||
std::cout << tp << "\n";
|
|
||||||
// std::cout << std::chrono::system_clock::now() << "\n";
|
|
||||||
|
|
||||||
|
|
||||||
constexpr std::string_view stv{"RADEC"};
|
|
||||||
constexpr char ccv[] = "RADEC";
|
|
||||||
|
|
||||||
// const auto pk = mcc::network::mcc_str2pairkind(stv);
|
|
||||||
// const auto pk = mcc::network::mcc_str2pairkind(std::string_view{"RADEC"});
|
|
||||||
// const auto pk = mcc::network::mcc_str2pairkind("RADEC");
|
|
||||||
const auto pk = mcc::network::mcc_str2pairkind(ccv);
|
|
||||||
static_assert(pk == mcc::MccCoordPairKind::COORDS_KIND_RADEC_APP);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user