#pragma once /* MOUNT CONTROL COMPONENTS LIBRARY */ /* GENERIC MOUNT REFERENCE IMPLEMENTATION */ #include "mcc_defaults.h" #include "mcc_generics.h" namespace mcc { enum class MccGenericMountErrorCode : int { ERROR_OK, ERROR_HW_INIT, ERROR_HW_STOP, ERROR_HW_GETSTATE, ERROR_SET_TARGET, ERROR_MOUNT_SLEW, ERROR_MOUNT_TRACK }; enum class MccGenericFsmMountErrorCode : int { ERROR_OK, ERROR_INVALID_OPERATION, ERROR_UNKNOWN_EVENT }; } // namespace mcc namespace std { template <> class is_error_code_enum : public true_type { }; template <> class is_error_code_enum : public true_type { }; } // namespace std namespace mcc { // 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"; default: return "UNKNOWN"; } } static const MccGenericMountCategory& get() { static const MccGenericMountCategory constInst; return constInst; } }; struct MccGenericFsmMountCategory : public std::error_category { MccGenericFsmMountCategory() : std::error_category() {} const char* name() const noexcept { return "MCC-GENERIC-FSM-MOUNT"; } std::string message(int ec) const { MccGenericFsmMountErrorCode err = static_cast(ec); switch (err) { case MccGenericFsmMountErrorCode::ERROR_OK: return "OK"; case MccGenericFsmMountErrorCode::ERROR_INVALID_OPERATION: return "requested operation is not allowed"; case MccGenericFsmMountErrorCode::ERROR_UNKNOWN_EVENT: return "unexpected finite-state-machine event"; default: return "UNKNOWN"; } } static const MccGenericFsmMountCategory& get() { static const MccGenericFsmMountCategory constInst; return constInst; } }; inline std::error_code make_error_code(MccGenericMountErrorCode ec) { return std::error_code(static_cast(ec), MccGenericMountCategory::get()); } inline std::error_code make_error_code(MccGenericFsmMountErrorCode ec) { return std::error_code(static_cast(ec), MccGenericFsmMountCategory::get()); } template class MccGenericMount : public HardwareT, public TelemetryT, public PZoneContT, public SlewModelT, public TrackModelT, public LoggerT { public: typedef std::error_code error_t; using LoggerT::logDebug; using LoggerT::logError; using LoggerT::logInfo; using LoggerT::logWarn; using typename SlewModelT::slewing_params_t; using typename TrackModelT::tracking_params_t; enum class mount_status_t : int { IDLE, INITIALIZATION, STOPPED, SLEWING, ADJUSTING, TRACKING, ERROR }; static constexpr std::string_view mountStatusStr(mount_status_t status) { return status == mount_status_t::IDLE ? "IDLE" : mount_status_t::INITIALIZATION ? "INIT" : mount_status_t::STOPPED ? "STOP" : mount_status_t::SLEWING ? "SLEW" : mount_status_t::ADJUSTING ? "ADJST" : mount_status_t::TRACKING ? "TRACK" : mount_status_t::ERROR ? "ERR" : "UNKNOWN"; } template MccGenericMount(std::tuple hw_ctor_args, std::tuple tm_ctor_args, std::tuple pzcont_ctor_args, std::tuple smodel_ctor_args, std::tuple tmodel_ctor_args, std::tuple log_ctor_args) : HardwareT(std::make_from_tuple(std::move(hw_ctor_args))), TelemetryT(std::make_from_tuple(std::move(tm_ctor_args))), PZoneContT(std::make_from_tuple(std::move(pzcont_ctor_args))), SlewModelT(std::make_from_tuple(std::move(smodel_ctor_args))), TrackModelT(std::make_from_tuple(std::move(tmodel_ctor_args))), LoggerT(std::make_from_tuple(std::move(log_ctor_args))), _mountStatus(new std::atomic{}) { *_mountStatus = mount_status_t::IDLE; } MccGenericMount(MccGenericMount&&) = default; MccGenericMount(const MccGenericMount&) = delete; MccGenericMount& operator=(MccGenericMount&&) = default; MccGenericMount& operator=(const MccGenericMount&) = delete; virtual ~MccGenericMount() { stopMount(); }; error_t stopMount() { logInfo("Stop any movements ..."); this->stopTracking(); this->stopSlewing(); auto hw_err = this->hardwareStop(); if (hw_err) { *_mountStatus = mount_status_t::ERROR; return mcc_deduce_error_code(hw_err, MccGenericMountErrorCode::ERROR_HW_STOP); } logInfo("Stop command was sent"); *_mountStatus = mount_status_t::STOPPED; return MccGenericMountErrorCode::ERROR_OK; } error_t initMount() { logInfo("Start generic mount initialization ..."); *_mountStatus = mount_status_t::INITIALIZATION; auto hw_err = this->hardwareInit(); if (hw_err) { *_mountStatus = mount_status_t::ERROR; return mcc_deduce_error_code(hw_err, MccGenericMountErrorCode::ERROR_HW_STOP); } logInfo("Generic mount initialization was performed"); *_mountStatus = mount_status_t::IDLE; return MccGenericMountErrorCode::ERROR_OK; } // re-implements TelemetryT::setPointingTarget to hold target coordinates // in some intermediate buffer error_t setPointingTarget(mcc_celestial_point_c auto pt) { mcc_copy_celestial_point(std::move(pt), &_enteredTargetCoordiniates); std::string xstr; if (_enteredTargetCoordiniates.pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS || _enteredTargetCoordiniates.pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_APP || _enteredTargetCoordiniates.pair_kind == MccCoordPairKind::COORDS_KIND_HADEC_APP) { xstr = MccAngle(_enteredTargetCoordiniates.X).sexagesimal(true); } else { xstr = MccAngle(_enteredTargetCoordiniates.X).sexagesimal(); } logInfo(std::format("Set entered target coordinates to: {} {} {}", xstr, MccAngle(_enteredTargetCoordiniates.Y).sexagesimal(), MccCoordPairKindToStr(_enteredTargetCoordiniates.pair_kind))); return MccGenericMountErrorCode::ERROR_OK; } // re-implements SlewModelT::slewToTarget to fetch input target coordinates from intermediate buffer error_t slewToTarget(bool slew_and_stop = false) { auto err = TelemetryT::setPointingTarget(_enteredTargetCoordiniates); if (err) { *_mountStatus = mount_status_t::ERROR; return mcc_deduce_error_code(err, MccGenericMountErrorCode::ERROR_SET_TARGET); } *_mountStatus = mount_status_t::SLEWING; error_t s_err = mcc_deduce_error_code(SlewModelT::slewToTarget(slew_and_stop), MccGenericMountErrorCode::ERROR_MOUNT_SLEW); if (s_err) { *_mountStatus = mount_status_t::ERROR; return s_err; } if (slew_and_stop) { *_mountStatus = mount_status_t::IDLE; } return s_err; } error_t trackTarget() { auto err = TrackModelT::trackTarget(); if (err) { *_mountStatus = mount_status_t::ERROR; return mcc_deduce_error_code(err, MccGenericMountErrorCode::ERROR_MOUNT_TRACK); } *_mountStatus = mount_status_t::TRACKING; return MccGenericMountErrorCode::ERROR_OK; } error_t stopSlewing() { *_mountStatus = mount_status_t::IDLE; SlewModelT::stopSlewing(); return MccGenericMountErrorCode::ERROR_OK; } error_t stopTracking() { // *_mountStatus = mount_status_t::IDLE; _mountStatus->store(mount_status_t::IDLE); TrackModelT::stopTracking(); return MccGenericMountErrorCode::ERROR_OK; } MccGenericMount::mount_status_t mountStatus() const { return *_mountStatus; } protected: MccCelestialPoint _enteredTargetCoordiniates{}; std::unique_ptr> _mountStatus; }; /* GENERIC FINITE-STATE-MACHINE MOUNT REFERENCE IMPLEMENTATION */ template class MccGenericFsmMount : public MOUNT_T, protected fsm::MccFiniteStateMachine { public: typedef std::error_code error_t; using fsm::MccFiniteStateMachine::currentStateID; using typename MOUNT_T::slewing_params_t; using typename MOUNT_T::tracking_params_t; protected: error_t _lastError; /* default events implementation */ struct MccGenericFsmMountBaseEvent { virtual ~MccGenericFsmMountBaseEvent() = default; MccGenericFsmMount* mount() const { return _mount; } protected: MccGenericFsmMount* _mount; MccGenericFsmMountBaseEvent(MccGenericFsmMount* mount) : _mount(mount) {} }; struct MccGenericFsmMountInitEvent : MccGenericFsmMountBaseEvent { static constexpr std::string_view ID{"GENERIC-MOUNT-INIT-EVENT"}; MccGenericFsmMountInitEvent(MccGenericFsmMount* mount) : MccGenericFsmMountBaseEvent(mount) {} }; struct MccGenericFsmMountIdleEvent : MccGenericFsmMountBaseEvent { static constexpr std::string_view ID{"GENERIC-MOUNT-IDLE-EVENT"}; MccGenericFsmMountIdleEvent(MccGenericFsmMount* mount) : MccGenericFsmMountBaseEvent(mount) {} }; struct MccGenericFsmMountStopEvent : MccGenericFsmMountBaseEvent { static constexpr std::string_view ID{"GENERIC-MOUNT-STOP-EVENT"}; MccGenericFsmMountStopEvent(MccGenericFsmMount* mount) : MccGenericFsmMountBaseEvent(mount) {} }; struct MccGenericFsmMountErrorEvent : MccGenericFsmMountBaseEvent { static constexpr std::string_view ID{"GENERIC-MOUNT-ERROR-EVENT"}; MccGenericFsmMountErrorEvent(MccGenericFsmMount* mount, const MccGenericFsmMount::error_t& err) : MccGenericFsmMountBaseEvent(mount), _err(err) { } MccGenericFsmMount::error_t eventData() const noexcept { return _err; } protected: MccGenericFsmMount::error_t _err; }; struct MccGenericFsmMountSlewEvent : MccGenericFsmMountBaseEvent { static constexpr std::string_view ID{"GENERIC-MOUNT-SLEW-EVENT"}; MccGenericFsmMountSlewEvent(MccGenericFsmMount* mount, bool slew_and_stop) : MccGenericFsmMountBaseEvent(mount), _slewAndStop(slew_and_stop) { } bool eventData() const noexcept { return _slewAndStop; } protected: bool _slewAndStop{}; }; struct MccGenericFsmMountTrackEvent : MccGenericFsmMountBaseEvent { static constexpr std::string_view ID{"GENERIC-MOUNT-TRACK-EVENT"}; MccGenericFsmMountTrackEvent(MccGenericFsmMount* mount) : MccGenericFsmMountBaseEvent(mount) {} }; /* default states implementation */ struct MccGenericFsmMountBaseState { virtual ~MccGenericFsmMountBaseState() = default; protected: MccGenericFsmMountBaseState() = default; template void exitLog(this auto&& self, EvT& event) { using self_t = std::remove_cvref_t; if constexpr (mcc_generic_log_mount_c && std::derived_from) { event.mount()->logDebug(std::format("Exit from '{}' state due to '{}' event ...", self_t::ID, EvT::ID)); } } template void enterLog(this auto&& self, EvT& event) { using self_t = std::remove_cvref_t; if constexpr (mcc_generic_log_mount_c && std::derived_from) { event.mount()->logDebug(std::format("Enter to '{}' state due to '{}' event ...", self_t::ID, EvT::ID)); } } }; struct MccGenericFsmMountErrorState; // default error-state class implementation (forward declaration) // template template struct MccGenericFsmMountInitState; template struct MccGenericFsmMountStopState; template struct MccGenericFsmMountSlewState; template struct MccGenericFsmMountTrackState; template struct MccGenericFsmMountStartState : MccGenericFsmMountBaseState // initial state after mount-class creation (uninitialized state) { static constexpr std::string_view ID{"GENERIC-MOUNT-START-STATE"}; // only initialization is allowed here using transition_t = fsm::fsm_transition_table_t< std::pair>>; void exit(fsm::traits::fsm_event_c auto& event) { this->exitLog(event); } void enter(fsm::traits::fsm_event_c auto& event) { this->enterLog(event); } }; // template template struct MccGenericFsmMountIdleState : MccGenericFsmMountBaseState // IDLE state: mount is stopped, wait for client commands { static constexpr std::string_view ID{"GENERIC-MOUNT-IDLE-STATE"}; using transition_t = fsm::fsm_transition_table_t< std::pair>, std::pair, std::pair>, std::pair>, std::pair>, std::pair>>; void exit(fsm::traits::fsm_event_c auto& event) { this->exitLog(event); } void enter(fsm::traits::fsm_event_c auto& event) { this->enterLog(event); } }; // template template struct MccGenericFsmMountInitState : MccGenericFsmMountBaseState { // static_assert(fsm::traits::fsm_state_c); static constexpr std::string_view ID{"GENERIC-MOUNT-INIT-STATE"}; using transition_t = fsm::fsm_transition_table_t< std::pair>, std::pair, std::pair>; // void exit(MccGenericFsmMountInitEvent& event) // { // if constexpr (mcc_generic_log_mount_c) { // event.mount()->logWarn("It seems a re-entering to the initializing state was asked! Ignore the // event!"); // } // } // void enter(MccGenericFsmMountInitEvent& event) // { // if constexpr (mcc_generic_log_mount_c) { // event.mount()->logWarn( // "It seems a re-entering to the initializing state was asked! Ignore the event and wait for the " // "mount " // "to initialize!"); // } // } void exit(fsm::traits::fsm_event_c auto& event) { this->exitLog(event); } void enter(fsm::traits::fsm_event_c auto& event) { this->enterLog(event); event.mount()->logDebug( std::format("Current ID: '{}', previous ID: '{}'", ID, event.mount()->previousStateID())); if (event.mount()->previousStateID() == ID) { event.mount()->logWarn( "It seems a re-entering to the initializing state was asked! Ignore the event and wait for the " "mount " "to initialize!"); return; } auto* mount_ptr = event.mount(); // call base-class initMount method! auto err = static_cast(mount_ptr)->initMount(); if (err) { mount_ptr->dispatchEvent(MccGenericFsmMountErrorEvent{mount_ptr, err}); return; } // after initialization switch to IDLE state mount_ptr->dispatchEvent(MccGenericFsmMountIdleEvent{mount_ptr}); } }; template struct MccGenericFsmMountSlewState : MccGenericFsmMountBaseState { static constexpr std::string_view ID{"GENERIC-MOUNT-SLEW-STATE"}; using transition_t = fsm::fsm_transition_table_t< std::pair>, std::pair>, std::pair, std::pair>, std::pair>; // void exit(MccGenericFsmMountSlewEvent& event) // { // if constexpr (mcc_generic_log_mount_c) { // event.mount()->logWarn( // "It seems re-entering to slewing state was asked! Do not stop the current slewing process, just " // "ignore this event!"); // } // } // void enter(MccGenericFsmMountSlewEvent& event) // { // if constexpr (mcc_generic_log_mount_c) { // event.mount()->logWarn( // "It seems re-entering to slewing state was asked! Do not start a new slewing process, just ignore // " "this event!"); // } // } void exit(fsm::traits::fsm_event_c auto& event) { this->exitLog(event); } void enter(fsm::traits::fsm_event_c auto& event) { this->enterLog(event); auto* mount_ptr = event.mount(); if (event.mount()->previousStateID() == ID) { event.mount()->logWarn( "It seems re-entering to slewing state was asked! Do not start a new slewing process, just ignore " "this event!"); return; } bool slew_and_stop = event.eventData(); auto nn = std::this_thread::get_id(); // call base-class slewToTarget method! auto err = static_cast(mount_ptr)->slewToTarget(slew_and_stop); if (err) { mount_ptr->dispatchEvent(MccGenericFsmMountErrorEvent{mount_ptr, err}); return; } if (slew_and_stop) { // after slewing switch to IDLE state mount_ptr->dispatchEvent(MccGenericFsmMountIdleEvent{mount_ptr}); } else { // after slewing switch to tracking state mount_ptr->dispatchEvent(MccGenericFsmMountTrackEvent{mount_ptr}); } } }; template struct MccGenericFsmMountTrackState : MccGenericFsmMountBaseState { static constexpr std::string_view ID{"GENERIC-MOUNT-TRACK-STATE"}; using transition_t = fsm::fsm_transition_table_t< std::pair>, std::pair>, std::pair, std::pair>; // void exit(MccGenericFsmMountTrackEvent& event) // { // if constexpr (mcc_generic_log_mount_c) { // event.mount()->logWarn( // "It seems re-entering to tracking state was asked! Do not stop the current tracking process, just // " "ignore this event!"); // } // } // void enter(MccGenericFsmMountTrackEvent& event) // { // if constexpr (mcc_generic_log_mount_c) { // event.mount()->logWarn( // "It seems re-entering to tracking state was asked! Do not start a new tracking process, just " // "ignore this event!"); // } // } void exit(fsm::traits::fsm_event_c auto& event) { this->exitLog(event); } void enter(fsm::traits::fsm_event_c auto& event) { this->enterLog(event); auto* mount_ptr = event.mount(); if (mount_ptr->previousStateID() == ID) { mount_ptr->logWarn( "It seems re-entering to tracking state was asked! Do not start a new tracking process, just " "ignore this event!"); return; } // call base-class stopMount method! auto err = static_cast(mount_ptr)->trackTarget(); if (err) { mount_ptr->dispatchEvent(MccGenericFsmMountErrorEvent{mount_ptr, err}); return; } // after stop tracking switch to IDLE state mount_ptr->dispatchEvent(MccGenericFsmMountIdleEvent{mount_ptr}); } }; template struct MccGenericFsmMountStopState : MccGenericFsmMountBaseState { static constexpr std::string_view ID{"GENERIC-MOUNT-STOP-STATE"}; using transition_t = fsm::fsm_transition_table_t< std::pair>, std::pair, std::pair>; // void exit(MccGenericFsmMountStopEvent& event) // { // if constexpr (mcc_generic_log_mount_c) { // event.mount()->logWarn("It seems a re-entering to the stop state was asked! Ignore the event!"); // } // } // void enter(MccGenericFsmMountStopEvent& event) // { // if constexpr (mcc_generic_log_mount_c) { // event.mount()->logWarn( // "It seems a re-entering to the stop state was asked! Ignore the event and wait for the mount to " // "stop!"); // } // } void exit(fsm::traits::fsm_event_c auto& event) { this->exitLog(event); } void enter(fsm::traits::fsm_event_c auto& event) { this->enterLog(event); auto* mount_ptr = event.mount(); if (mount_ptr->previousStateID() == ID) { mount_ptr->logWarn( "It seems a re-entering to the stop state was asked! Ignore the event and wait for the mount to " "stop!"); return; } // call base-class stopMount method! auto err = static_cast(mount_ptr)->stopMount(); if (err) { mount_ptr->dispatchEvent(MccGenericFsmMountErrorEvent{mount_ptr, err}); return; } // after stopping switch to IDLE state mount_ptr->dispatchEvent(MccGenericFsmMountIdleEvent{mount_ptr}); } }; struct MccGenericFsmMountErrorState : MccGenericFsmMountBaseState // default error-state class implementation // (just log error) { static constexpr std::string_view ID{"GENERIC-MOUNT-ERROR-STATE"}; using transition_t = fsm::fsm_transition_table_t< std::pair, std::pair>, std::pair>>; void exit(fsm::traits::fsm_event_c auto& event) { // just reset error event.mount()->_lastError = MccGenericFsmMountErrorCode::ERROR_OK; this->exitLog(event); if constexpr (mcc_generic_log_mount_c) { event.mount()->logWarn( std::format("The mount is in the error state!!! One must correct it before transit to any states! " "Event type is '{}'", decltype(event)::ID)); } } void enter(fsm::traits::fsm_event_c auto& event) { event.mount()->_lastError = event.eventData(); this->enterLog(event); if constexpr (mcc_generic_log_mount_c) { auto err = event.eventData(); event.mount()->logError( std::format("The mount is in the error state: code = {}, category = {}, message = '{}'", err.value(), err.category().name(), err.message())); } } }; public: template MccGenericFsmMount(StartStateT start_state, MountCtorArgTs&&... mount_ctor_args) : MOUNT_T(std::forward(mount_ctor_args)...), fsm::MccFiniteStateMachine(std::move(start_state)) { } // template // MccGenericFsmMount(MOUNT_T mount, // fsm::traits::fsm_state_c auto start_state = MccGenericFsmMountStartState{}) // : MOUNT_T(std::move(mount)), fsm::MccFiniteStateMachine(std::move(start_state)) // { // } // reimplementation of base-class methods to adapt it to FSM-behavior error_t initMount() { try { this->dispatchEvent(MccGenericFsmMountInitEvent{this}); } catch (std::system_error const& exc) { if (exc.code().category() == fsm::MccFiniteStateMachineCategory::get()) { _lastError = MccGenericFsmMountErrorCode::ERROR_INVALID_OPERATION; } else { // return exc.code(); _lastError = exc.code(); } } // return MccGenericFsmMountErrorCode::ERROR_OK; return _lastError; } auto stopMount() { try { this->dispatchEvent(MccGenericFsmMountStopEvent{this}); } catch (std::system_error const& exc) { if (exc.code().category() == fsm::MccFiniteStateMachineCategory::get()) { _lastError = MccGenericFsmMountErrorCode::ERROR_INVALID_OPERATION; } else { // return exc.code(); _lastError = exc.code(); } } // return MccGenericFsmMountErrorCode::ERROR_OK; return _lastError; } auto slewToTarget(bool slew_and_stop = false) { try { this->dispatchEvent(MccGenericFsmMountSlewEvent{this, slew_and_stop}); } catch (std::system_error const& exc) { if (exc.code().category() == fsm::MccFiniteStateMachineCategory::get()) { _lastError = MccGenericFsmMountErrorCode::ERROR_INVALID_OPERATION; } else { // return exc.code(); _lastError = exc.code(); } } // return MccGenericFsmMountErrorCode::ERROR_OK; return _lastError; } auto stopSlewing() { try { this->dispatchEvent(MccGenericFsmMountStopEvent{this}); } catch (std::system_error const& exc) { if (exc.code().category() == fsm::MccFiniteStateMachineCategory::get()) { _lastError = MccGenericFsmMountErrorCode::ERROR_INVALID_OPERATION; } else { // return exc.code(); _lastError = exc.code(); } } // return MccGenericFsmMountErrorCode::ERROR_OK; return _lastError; } auto trackTarget() { try { this->dispatchEvent(MccGenericFsmMountTrackEvent{this}); } catch (std::system_error const& exc) { if (exc.code().category() == fsm::MccFiniteStateMachineCategory::get()) { _lastError = MccGenericFsmMountErrorCode::ERROR_INVALID_OPERATION; } else { // return exc.code(); _lastError = exc.code(); } } // return MccGenericFsmMountErrorCode::ERROR_OK; return _lastError; } auto stopTracking() { try { this->dispatchEvent(MccGenericFsmMountStopEvent{this}); } catch (std::system_error const& exc) { if (exc.code().category() == fsm::MccFiniteStateMachineCategory::get()) { _lastError = MccGenericFsmMountErrorCode::ERROR_INVALID_OPERATION; } else { // return exc.code(); _lastError = exc.code(); } } // return MccGenericFsmMountErrorCode::ERROR_OK; return _lastError; } }; } // namespace mcc