diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d420fe..a850773 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -217,6 +217,7 @@ set(MCC_SRC mcc_serialization_common.h mcc_deserializer.h mcc_serializer.h + mcc_generic_mount.h ) if(USE_SPDLOG) diff --git a/mcc_concepts.h b/mcc_concepts.h index 249133c..9cbc513 100644 --- a/mcc_concepts.h +++ b/mcc_concepts.h @@ -979,17 +979,17 @@ concept mcc_mount_status_c = requires { template -concept mcc_generic_mount_c = - mcc_logger_c && mcc_pzone_container_c && mcc_telemetry_c && mcc_movement_controls_c && requires(T t) { - // error type - requires mcc_error_c; +concept mcc_generic_mount_c = mcc_logger_c && mcc_pzone_container_c && mcc_telemetry_c && + mcc_movement_controls_c && requires(T t, const T t_const) { + // error type + requires mcc_error_c; - requires mcc_mount_status_c; + requires mcc_mount_status_c; - { t.initMount() } -> std::same_as; + { t.initMount() } -> std::same_as; - { t.mountStatus() } -> std::same_as; - }; + { t_const.mountStatus() } -> std::same_as; + }; } // namespace mcc diff --git a/mcc_generic_mount.h b/mcc_generic_mount.h new file mode 100644 index 0000000..a4db41e --- /dev/null +++ b/mcc_generic_mount.h @@ -0,0 +1,226 @@ +#pragma once + + +/**************************************************************************************** + * * + * MOUNT CONTROL COMPONENTS LIBRARY * + * * + * * + * IMPLEMENTATION OF A GENERIC MOUNT CLASS * + * (A SOME OF POSSIBLE GENERIC IMPLEMENTATIONS) * + * * + ****************************************************************************************/ + + + +#include +#include "mcc_error.h" + +namespace mcc::impl +{ + +enum class MccGenericMountErrorCode : int { + ERROR_OK, + ERROR_HW_INIT, + ERROR_HW_STOP, + ERROR_HW_GETSTATE, + ERROR_SET_TARGET, + ERROR_MOUNT_SLEW, + ERROR_MOUNT_TRACK, + ERROR_GET_TELEMETRY, + ERROR_UNSUPPORTED_TARGET_COORDPAIR, + ERROR_PZONE_COMP, + ERROR_TARGET_IN_ZONE +}; + +enum class MccGenericFsmMountErrorCode : int { ERROR_OK, ERROR_INVALID_OPERATION, ERROR_UNKNOWN_EVENT }; + +} // namespace mcc::impl + + +namespace std +{ + +template <> +class is_error_code_enum : public true_type +{ +}; + +} // namespace std + + + +namespace mcc::impl +{ + +// error category +struct MccGenericMountCategory : public std::error_category { + MccGenericMountCategory() : std::error_category() {} + + const char* name() const noexcept + { + return "MCC-GENERIC-MOUNT"; + } + + std::string message(int ec) const + { + MccGenericMountErrorCode err = static_cast(ec); + + switch (err) { + case MccGenericMountErrorCode::ERROR_OK: + return "OK"; + case MccGenericMountErrorCode::ERROR_HW_INIT: + return "an error occured while initializing mount"; + case MccGenericMountErrorCode::ERROR_HW_STOP: + return "an error occured while stopping mount"; + case MccGenericMountErrorCode::ERROR_HW_GETSTATE: + return "cannot get state of hardware"; + case MccGenericMountErrorCode::ERROR_SET_TARGET: + return "cannot set target coordinates"; + case MccGenericMountErrorCode::ERROR_MOUNT_SLEW: + return "slewing error"; + case MccGenericMountErrorCode::ERROR_MOUNT_TRACK: + return "tracking error"; + case MccGenericMountErrorCode::ERROR_GET_TELEMETRY: + return "cannot get telemetry data"; + case MccGenericMountErrorCode::ERROR_UNSUPPORTED_TARGET_COORDPAIR: + return "unsupported coordinate pair of target"; + case MccGenericMountErrorCode::ERROR_PZONE_COMP: + return "an error occured while computing prohibited zone"; + case MccGenericMountErrorCode::ERROR_TARGET_IN_ZONE: + return "target coordinates are in prohibitted zone"; + default: + return "UNKNOWN"; + } + } + + static const MccGenericMountCategory& get() + { + static const MccGenericMountCategory constInst; + return constInst; + } +}; + + +inline std::error_code make_error_code(MccGenericMountErrorCode ec) +{ + return std::error_code(static_cast(ec), MccGenericMountCategory::get()); +} + + + +template +class MccGenericMount : protected HARDWARE_T, + public TELEMETRY_T, + public PZONE_CONT_T, + public MOVE_CNTRL_T, + public LOGGER_T +{ +public: + using LOGGER_T::logDebug; + using LOGGER_T::logError; + using LOGGER_T::logInfo; + using LOGGER_T::logTrace; + using LOGGER_T::logWarn; + + typedef MccError error_t; + + using typename TELEMETRY_T::telemetry_data_t; + + enum class mount_status_t : int { + MOUNT_STATUS_ERROR, + MOUNT_STATUS_IDLE, + MOUNT_STATUS_UNINITIALIZED, + MOUNT_STATUS_INITIALIZATION, + MOUNT_STATUS_STOPPED, + MOUNT_STATUS_STOPPING, + MOUNT_STATUS_SLEWING, + MOUNT_STATUS_ADJUSTING, + MOUNT_STATUS_GUIDING, + MOUNT_STATUS_TRACKING + }; + + template + MccGenericMount(std::tuple hw_ctor_args, + std::tuple telemetry_ctor_args, + std::tuple pzone_cont_ctor_ars, + std::tuple move_cntrl_ctor_ars, + std::tuple logger_ctor_args) + : HARDWARE_T(std::make_from_tuple(std::move(hw_ctor_args))), + TELEMETRY_T(std::make_from_tuple(std::move(telemetry_ctor_args))), + PZONE_CONT_T(std::make_from_tuple(pzone_cont_ctor_ars)), + MOVE_CNTRL_T(std::make_from_tuple(move_cntrl_ctor_ars)), + LOGGER_T(std::make_from_tuple(logger_ctor_args)) + { + logDebug(std::format("Create MccGenericMount class instance (thread: {})", std::this_thread::get_id())); + } + + MccGenericMount(const MccGenericMount&) = delete; + MccGenericMount(MccGenericMount&&) = default; + + MccGenericMount& operator=(const MccGenericMount&) = delete; + MccGenericMount& operator=(MccGenericMount&&) = default; + + virtual ~MccGenericMount() + { + logDebug(std::format("Delete MccGenericMount class instance (thread: {})", std::this_thread::get_id())); + + auto err = MOVE_CNTRL_T::stopMount(); + if (err) { + logError(formatError(err)); + } + } + + error_t initMount() + { + logInfo(std::format("Start MccGenericMount class initialization (thread: {}) ...", std::this_thread::get_id())); + + *_lastMountError = MccGenericMountErrorCode::ERROR_OK; + + *_mountStatus = mount_status_t::MOUNT_STATUS_INITIALIZATION; + + auto hw_err = this->hardwareInit(); + if (hw_err) { + *_mountStatus = mount_status_t::MOUNT_STATUS_ERROR; + + *_lastMountError = mcc_deduce_err(hw_err, MccGenericMountErrorCode::ERROR_HW_INIT); + } else { + logInfo("Generic mount initialization was performed"); + + *_mountStatus = mount_status_t::IDLE; + } + + return *_lastMountError; + } + + mount_status_t mountStatus() const + { + return _mountStatus->load(); + } + + error_t mountLastError() const + { + return _lastMountError->load(); + } + +protected: + std::unique_ptr> _mountStatus{ + new std::atomic{mount_status_t::MOUNT_STATUS_UNINITIALIZED}}; + + std::unique_ptr> _lastMountError{new std::atomic{MccGenericMountErrorCode::ERROR_OK}}; + + std::string formatError(error_t const& err, std::string_view prefix = "") const + { + return std::format("{}{} (category: {}, code: {})", prefix, err.message(), err.value(), err.category().name()); + } +}; + +} // namespace mcc::impl