#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, 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 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"; 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; } }; 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) : LoggerT(std::make_from_tuple(std::move(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))), _mountStatus(new std::atomic{}), _lastMountError(new std::atomic{MccGenericMountErrorCode::ERROR_OK}) { *_mountStatus = mount_status_t::IDLE; } MccGenericMount(MccGenericMount&&) = default; MccGenericMount(const MccGenericMount&) = delete; MccGenericMount& operator=(MccGenericMount&&) = default; MccGenericMount& operator=(const MccGenericMount&) = delete; virtual ~MccGenericMount() { stopMount(); if (_slewingFuture.valid()) { _slewingFuture.wait_for(std::chrono::seconds(3)); } }; error_t stopMount() { logInfo("Stop any movements ..."); *_lastMountError = MccGenericMountErrorCode::ERROR_OK; this->stopTracking(); this->stopSlewing(); auto hw_err = this->hardwareStop(); if (hw_err) { *_mountStatus = mount_status_t::ERROR; *_lastMountError = mcc_deduce_error_code(hw_err, MccGenericMountErrorCode::ERROR_HW_STOP); } else { logInfo("Stop command was sent"); *_mountStatus = mount_status_t::STOPPED; } return *_lastMountError; } error_t initMount() { logInfo("Start generic mount initialization ..."); *_lastMountError = MccGenericMountErrorCode::ERROR_OK; *_mountStatus = mount_status_t::INITIALIZATION; auto hw_err = this->hardwareInit(); if (hw_err) { *_mountStatus = mount_status_t::ERROR; *_lastMountError = mcc_deduce_error_code(hw_err, MccGenericMountErrorCode::ERROR_HW_STOP); } else { logInfo("Generic mount initialization was performed"); *_mountStatus = mount_status_t::IDLE; } return *_lastMountError; } // 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) { *_lastMountError = MccGenericMountErrorCode::ERROR_OK; // save current target MccTelemetryData tdata; auto sl_params = this->getSlewingParams(); auto t_err = this->telemetryData(&tdata); if (t_err) { return *_lastMountError = mcc_deduce_error_code(t_err, MccGenericMountErrorCode::ERROR_GET_TELEMETRY); } MccCelestialPoint curr_target{.pair_kind = tdata.target.pair_kind, .time_point = tdata.target.time_point}; if (curr_target.pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) { curr_target.X = tdata.target.RA_ICRS; curr_target.Y = tdata.target.DEC_ICRS; } else if (curr_target.pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_APP) { curr_target.X = tdata.target.RA_APP; curr_target.Y = tdata.target.DEC_APP; } else if (curr_target.pair_kind == MccCoordPairKind::COORDS_KIND_HADEC_APP) { curr_target.X = tdata.target.HA; curr_target.Y = tdata.target.DEC_APP; } else if (curr_target.pair_kind == MccCoordPairKind::COORDS_KIND_AZZD) { curr_target.X = tdata.target.AZ; curr_target.Y = tdata.target.ZD; } else if (curr_target.pair_kind == MccCoordPairKind::COORDS_KIND_AZALT) { curr_target.X = tdata.target.AZ; curr_target.Y = tdata.target.ALT; } else if (curr_target.pair_kind == MccCoordPairKind::COORDS_KIND_XY) { curr_target.X = tdata.target.X; curr_target.Y = tdata.target.Y; } else { // it should not be! logError( std::format("Unsupported coordinate pair kind ({}) was read from telemetry data! Is mount " "initialized?! Cannot start slewing!", (int)curr_target.pair_kind)); return *_lastMountError = MccGenericMountErrorCode::ERROR_UNSUPPORTED_TARGET_COORDPAIR; } // set new target coordinates and check it _enteredTargetCoordiniates.time_point = std::chrono::system_clock::now(); auto err = TelemetryT::setPointingTarget(_enteredTargetCoordiniates); if (err) { *_mountStatus = mount_status_t::ERROR; return mcc_deduce_error_code(err, MccGenericMountErrorCode::ERROR_SET_TARGET); } t_err = this->waitForTelemetryData(&tdata, sl_params.telemetryTimeout); if (t_err) { return mcc_deduce_error_code(t_err, MccGenericMountErrorCode::ERROR_GET_TELEMETRY); } bool in_zone; std::vector in_zone_vec; auto pz_err = this->inPZone(tdata.target, &in_zone, &in_zone_vec); if (pz_err) { return *_lastMountError = mcc_deduce_error_code(pz_err, MccGenericMountErrorCode::ERROR_PZONE_COMP); } if (in_zone) { size_t i = 0; for (; i < in_zone_vec.size(); ++i) { if (in_zone_vec[i]) { break; } } logError("target point is in prohibited zone (zone index: {})! Entered target coordinates:", i); logError(std::format( " RA-APP, DEC-APP, HA, LST: {}, {}, {}, {}", mcc::MccAngle{tdata.target.RA_APP}.sexagesimal(true), mcc::MccAngle{tdata.target.DEC_APP}.sexagesimal(), mcc::MccAngle{tdata.target.HA}.sexagesimal(true), mcc::MccAngle{tdata.LST}.sexagesimal(true))); logError(std::format(" AZ, ZD, ALT: {}, {}, {}", mcc::MccAngle{tdata.target.AZ}.sexagesimal(), mcc::MccAngle{tdata.target.ZD}.sexagesimal(), mcc::MccAngle{tdata.target.ALT}.sexagesimal())); logError(std::format(" hardware X, Y: {}, {}", mcc::MccAngle{tdata.target.X}.sexagesimal(), mcc::MccAngle{tdata.target.Y}.sexagesimal())); return *_lastMountError = mcc_deduce_error_code(pz_err, MccGenericMountErrorCode::ERROR_TARGET_IN_ZONE); } // start slewing (asunchronous operation) _slewingFuture = std::async(std::launch::async, [slew_and_stop, this]() { *_mountStatus = mount_status_t::SLEWING; auto err = SlewModelT::slewToTarget(slew_and_stop); if (err) { *_lastMountError = mcc_deduce_error_code(err, MccGenericMountErrorCode::ERROR_MOUNT_SLEW); *_mountStatus = mount_status_t::ERROR; } else { if (slew_and_stop) { *_mountStatus = mount_status_t::IDLE; } else { *_lastMountError = trackTarget(); } } } ); return MccGenericMountErrorCode::ERROR_OK; } 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; TrackModelT::stopTracking(); return MccGenericMountErrorCode::ERROR_OK; } MccGenericMount::mount_status_t mountStatus() const { return *_mountStatus; } // returns last mount-action error code error_t mountLastError() const { return *_lastMountError; } protected: MccCelestialPoint _enteredTargetCoordiniates{}; std::unique_ptr> _mountStatus; std::future _slewingFuture{}; std::unique_ptr> _lastMountError; }; /* 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)) { if constexpr (mcc_generic_log_mount_c) { this->logDebug(std::format("Create MccGenericFsmMount class instance ({})", this->getThreadId())); this->logDebug("MccGenericFsmMount finite-state machine states:"); for (auto& el : stateIDs()) { this->logDebug(std::format(" {}", el)); } this->logDebug("MccGenericFsmMount finite-state machine events:"); for (auto& el : eventIDs()) { this->logDebug(std::format(" {}", el)); } } } // 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)) // { // } MccGenericFsmMount(MccGenericFsmMount&&) = default; MccGenericFsmMount(const MccGenericFsmMount&) = delete; MccGenericFsmMount& operator=(MccGenericFsmMount&&) = default; MccGenericFsmMount& operator=(const MccGenericFsmMount&) = delete; virtual ~MccGenericFsmMount() { if constexpr (mcc_generic_log_mount_c) { this->logDebug(std::format("Delete MccGenericFsmMount class instance ({})", this->getThreadId())); } } // 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