#pragma once /* MOUNT CONTROL COMPONENTS LIBRARY */ /* * BASIC EVENTS AND MOUNT STATES DEFINITIONS (POSSIBLE IMPLEMENTATION) * */ #include #include "mcc_mount_concepts.h" namespace mcc { /* MOUNT STATE MACHINE STATES */ // a base class for mount state machine events template class MccMountEventBase { public: typedef MountT mount_t; virtual ~MccMountEventBase() = default; mount_t& mount() const { return _mount; } protected: MccMountEventBase(mount_t& mount) : _mount(mount) {} mount_t& _mount; }; // transit to IDLE state template struct MccMountEventIDLE : public MccMountEventBase { typedef MccMountEventBase base_t; static constexpr std::string_view ID = "MCC-MOUNT-IDLE-EVENT"; // CTAD does not work for clang++ (at least till v. 20 and -std=c++23)! // so, one must explicitly define constructor here MccMountEventIDLE(MountT& mount) : base_t(mount) {} }; // transit to initialization state template struct MccMountEventInit : public MccMountEventBase { typedef MccMountEventBase base_t; static constexpr std::string_view ID = "MCC-MOUNT-INIT-EVENT"; // CTAD does not work for clang++ (at least till v. 20 and -std=c++23)! // so, one must explicitly define constructor here MccMountEventInit(MountT& mount) : base_t(mount) {} }; // transit to error state template struct MccMountEventError : public MccMountEventBase { typedef MccMountEventBase base_t; static constexpr std::string_view ID = "MCC-MOUNT-ERROR-EVENT"; using event_data_t = std::error_code; event_data_t eventData() const { return _error; } MccMountEventError(MountT& mount, const event_data_t& error) : base_t(mount), _error(error) {} protected: event_data_t _error; }; // transit to slew state template struct MccMountEventSlew : public MccMountEventBase { typedef MccMountEventBase base_t; static constexpr std::string_view ID = "MCC-MOUNT-SLEW-EVENT"; using event_data_t = typename MountT::slew_model_t::slew_point_t; event_data_t eventData() const { return _eventData; } MccMountEventSlew(MountT& mount, const event_data_t& ev_data) : base_t(mount), _eventData(ev_data) {} protected: event_data_t _eventData; }; // transit to guiding state template struct MccMountEventGuiding : public MccMountEventBase { typedef MccMountEventBase base_t; static constexpr std::string_view ID = "MCC-MOUNT-GUIDING-EVENT"; using event_data_t = typename MountT::guiding_model_t::guiding_point_t; event_data_t eventData() const { return _eventData; } MccMountEventGuiding(MountT& mount, const event_data_t& ev_data) : base_t(mount), _eventData(ev_data) {} protected: event_data_t _eventData; }; // transit to stop state template struct MccMountEventStop : public MccMountEventBase { typedef MccMountEventBase base_t; static constexpr std::string_view ID = "MCC-MOUNT-STOP-EVENT"; enum event_data_t { EVENT_STOP_CLIENT, // software stop (mount client) EVENT_STOP_BUTTON // hardware button }; event_data_t eventData() const { return _reason; } std::string_view reason() const { return _reason == EVENT_STOP_BUTTON ? "Hardware stop-button" : _reason == EVENT_STOP_CLIENT ? "Stop from client" : "UNKNOWN"; } MccMountEventStop(MountT& mount, event_data_t reason) : base_t(mount), _reason(reason) {} protected: event_data_t _reason; }; // transit to shutdown state template struct MccMountEventShutdown : public MccMountEventBase { typedef MccMountEventBase base_t; static constexpr std::string_view ID = "MCC-MOUNT-SHUTDOWN-EVENT"; // CTAD does not work for clang++ (at least till v. 20 and -std=c++23)! // so, one must explicitly define constructor here MccMountEventShutdown(MountT& mount) : base_t(mount) {} }; /* MOUNT STATE MACHINE STATES */ template struct MccMountStateBase { // template > EvT> // void exit(this auto&& self, EvT& event) // { // using self_t = std::remove_cvref_t; // std::forward(self).exitImpl(event); // event.mount().logDebug("Exit from '{}' state due to '{}' event ...", self_t::ID, EvT::ID); // } // template > EvT> // void enter(this auto&& self, EvT& event) // { // using self_t = std::remove_cvref_t; // event.mount().logDebug("Enter to '{}' state due to '{}' event ...", self_t::ID, EvT::ID); // std::forward(self).enterImpl(event); // } protected: // template > EvT> // void exitImpl(EvT& event) // { // event.mount().logWarn("Call an empty MccMountStateBase::exitImpl method!!! Event type is '{}'", EvT::ID); // } // template > EvT> // void enterImpl(EvT& event) // { // event.mount().logWarn("Call an empty MccMountStateBase::enterImpl method!!! Event type is '{}'", EvT::ID); // } // MccMountStateBase() = default; template > EvT> void exitLog(this auto&& self, EvT& event) { using self_t = std::remove_cvref_t; event.mount().logDebug(std::format("Exit from '{}' state due to '{}' event ...", self_t::ID, EvT::ID)); } template > EvT> void enterLog(this auto&& self, EvT& event) { using self_t = std::remove_cvref_t; event.mount().logDebug(std::format("Enter to '{}' state due to '{}' event ...", self_t::ID, EvT::ID)); } }; // just forward declarations template struct MccMountStateIDLE; template struct MccMountStateInit; template struct MccMountStateError; template struct MccMountStateSlew; template > struct MccMountStateStop; template struct MccMountStateGuiding; template struct MccMountStateShutdown; // stop state template struct MccMountStateStop : MccMountStateBase { static constexpr std::string_view ID = "MCC-MOUNT-STOP-STATE"; using transition_t = fsm::fsm_transition_table_t, MccMountStateIDLE>>; // protected: void exit(MccMountEventIDLE& event) { // normal exit from the state this->exitLog(event); } // normal behavior (transit to IDLE state after stopping) // void enter(MccMountEventStop& event) // { // this->enterLog(event); // auto mount = event.mount(); // mount.stopMount(); // // switch to IDLE state // mount.template dispatchEvent>({mount}); // } template > EvT> void enter(EvT& event) { this->enterLog(event); auto mount = event.mount(); // should one check current state (slewing, guiding)? mount.slewModel.stop(); mount.guidingModel.stop(); mount.hardware.stop(); if constexpr (std::same_as>) { mount.logInfo(std::format("Stop reason: {}", event.reason())); // normal behavior (transit to IDLE state after stopping) mount.template dispatchEvent>({mount}); } else { mount.logInfo("Stop reason: special state"); mount.dispatchEvent(event); } } }; // initialization state template struct MccMountStateInit : MccMountStateBase { static constexpr std::string_view ID = "MCC-MOUNT-INIT-STATE"; using transition_t = fsm::fsm_transition_table_t, MccMountStateIDLE>>; // protected: void exit(MccMountEventIDLE& event) { // normal exit from the state this->exitLog(event); } template > EvT> void enter(EvT& event) { this->enterLog(event); MountT& mount = event.mount(); mount.initMount(); // switch to IDLE state mount.template dispatchEvent>({mount}); } }; // error state template struct MccMountStateError : MccMountStateBase { static constexpr std::string_view ID = "MCC-MOUNT-ERROR-STATE"; using transition_t = fsm::fsm_transition_table_t< std::pair, MccMountStateIDLE>, // ???? after error correction? std::pair, MccMountStateInit>, std::pair, MccMountStateError>, std::pair, MccMountStateError>, std::pair, MccMountStateError>, std::pair, MccMountStateError>, std::pair, MccMountStateShutdown>>; // protected: template > EvT> void exit(EvT& event) { this->exitLog(event); event.mount().logWarn(std::format( "The mount is in the error state!!! One must correct it before transit to any states! Event type is '{}'", EvT::ID)); } void exit(MccMountEventIDLE& event) { this->exitLog(event); event.mount().logWarn("Suppose the error was corrected!"); } void exit(MccMountEventInit& event) { // normal exit from the state this->exitLog(event); } void enter(MccMountEventError& event) { this->enterLog(event); 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())); } // template > EvT> // void enter(EvT& event) // { // this->enterLog(event); // auto err = event.eventData(); // event.mount().logError("The mount is in the error state: code = {}, category = {}, message = '{}'", // err.value(), // err.category().name(), err.message()); // } }; // IDLE state template struct MccMountStateIDLE : MccMountStateBase { static constexpr std::string_view ID = "MCC-MOUNT-IDLE-STATE"; using transition_t = fsm::fsm_transition_table_t, MccMountStateInit>, std::pair, MccMountStateError>, std::pair, MccMountStateSlew>, std::pair, MccMountStateGuiding>, std::pair, MccMountStateIDLE>, std::pair, MccMountStateShutdown>>; template > EvT> void exit(EvT& event) { this->exitLog(event); } template > EvT> void enter(EvT& event) { this->enterLog(event); } }; // shutdown state template struct MccMountStateShutdown : MccMountStateBase { static constexpr std::string_view ID = "MCC-MOUNT-SHUTDOWN-STATE"; // only initialization using transition_t = fsm::fsm_transition_table_t, MccMountStateInit>>; // protected: void exit(MccMountEventInit& event) { // normal exit from the state this->exitLog(event); } template > EvT> void enter(EvT& event) { this->enterLog(event); event.mount().shutdownMount(); } }; // slew state // WARNING: It must be a friend to 'MountT' class if it inherits it base class mount_controls_t // as protected or private!!! template struct MccMountStateSlew : MccMountStateBase { static constexpr std::string_view ID = "MCC-MOUNT-SLEW-STATE"; // only initialization using transition_t = fsm::fsm_transition_table_t, MccMountStateInit>, std::pair, MccMountStateStop>, // std::pair, MccMountStateSlew>, std::pair, MccMountStateStop>, std::pair, MccMountStateStop>, std::pair, MccMountStateStop>>; // protected: template > EvT> void exit(EvT& event) { this->exitLog(event); } template > EvT> void enter(EvT& event) { this->enterLog(event); auto slew_err = event.mount().slewModel.slew(event.eventData()); if (slew_err) { if constexpr (std::same_as) { event.mount().dispatchEvent(MccMountEventError{event.mount(), slew_err}); } else { // ... } return; } // switch to IDLE state event.mount().template dispatchEvent>({event.mount()}); } }; // guiding state template struct MccMountStateGuiding : MccMountStateBase { static constexpr std::string_view ID = "MCC-MOUNT-GUIDING-STATE"; // only initialization using transition_t = fsm::fsm_transition_table_t, MccMountStateInit>, std::pair, MccMountStateStop>, // std::pair, MccMountStateSlew>, std::pair, MccMountStateStop>, std::pair, MccMountStateStop>, std::pair, MccMountStateStop>>; // protected: template > EvT> void exit(EvT& event) { this->exitLog(event); } template > EvT> void enter(EvT& event) { this->enterLog(event); auto err = event.mount().guidingModel.guiding(event.eventData()); if (err) { if constexpr (std::same_as) { event.mount().dispatchEvent(MccMountEventError{event.mount(), err}); } else { // ... } } // switch to IDLE state event.mount().template dispatchEvent>({event.mount()}); } }; } // namespace mcc