#pragma once /* MOUNT CONTROL COMPONENTS LIBRARY */ /* * BASIC EVENTS AND MOUNT STATES DEFINITIONS (REFERENCE IMPLEMENTATION) * */ #include "mcc_mount.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_param_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"; // CTAD does not work for clang++ (at least till v. 20 and -std=c++23)! // so, one must explicitly define constructor here MccMountEventGuiding(MountT& mount) : base_t(mount) {} }; // 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().logWarning("Call an empty MccMountStateBase::exitImpl method!!! Event type is '{}'", EvT::ID); } template > EvT> void enterImpl(EvT& event) { event.mount().logWarning("Call an empty MccMountStateBase::enterImpl method!!! Event type is '{}'", 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 exitImpl(MccMountEventIDLE& event) { // normal exit from the state } // normal bihavior (transit to IDLE state after stopping) void enterImpl(MccMountEventStop& event) { auto mount = event.mount(); mount.stopMount(); // switch to IDLE state mount.template dispatchEvent>({mount}); } template > EvT> void enterImpl(EvT& event) { auto mount = event.mount(); mount.stopMount(); if constexpr (std::same_as>) { mount.logInfo("Stop reason: {}", event.reason()); // normal bihavior (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 exitImpl(MccMountEventIDLE& event) { // normal exit from the state } template > EvT> void enterImpl(EvT& event) { auto 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 exitImpl(EvT& event) { event.mount().logWarn( "The mount is in the error state!!! One must correct it before transit to any states! Event type is '{}'", EvT::ID); } void exitImpl(MccMountEventIDLE& event) { event.mount().logWarning("Suppose the error was corrected!"); } void exitImpl(MccMountEventInit& event) { // normal exit from the state } template > EvT> void enterImpl(EvT& 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>>; }; // 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 exitImpl(MccMountEventInit& event) { // normal exit from the state } template > EvT> void enterImpl(EvT& event) { event.mount().shutdownMount(); } }; // slew state 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 exitImpl(EvT& event) { } template > EvT> void enterImpl(EvT& event) { event.mount().slewMount(/* params here ...*/); } }; } // namespace mcc