/// @file /// @brief QEP/C++ platform-independent public interface. /// @ingroup qep /// @cond ///*************************************************************************** /// Last updated for version 6.8.0 /// Last updated on 2020-01-27 /// /// Q u a n t u m L e a P s /// ------------------------ /// Modern Embedded Software /// /// Copyright (C) 2005-2020 Quantum Leaps. All rights reserved. /// /// This program is open source software: you can redistribute it and/or /// modify it under the terms of the GNU General Public License as published /// by the Free Software Foundation, either version 3 of the License, or /// (at your option) any later version. /// /// Alternatively, this program may be distributed and modified under the /// terms of Quantum Leaps commercial licenses, which expressly supersede /// the GNU General Public License and are specifically designed for /// licensees interested in retaining the proprietary status of their code. /// /// This program is distributed in the hope that it will be useful, /// but WITHOUT ANY WARRANTY; without even the implied warranty of /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the /// GNU General Public License for more details. /// /// You should have received a copy of the GNU General Public License /// along with this program. If not, see . /// /// Contact information: /// /// ///*************************************************************************** /// @endcond #ifndef QEP_HPP #define QEP_HPP //**************************************************************************** //! The current QP version as a decimal constant XXYZ, where XX is a 2-digit // major version number, Y is a 1-digit minor version number, and Z is // a 1-digit release number. #define QP_VERSION 680U //! The current QP version number string of the form XX.Y.Z, where XX is // a 2-digit major version number, Y is a 1-digit minor version number, // and Z is a 1-digit release number. #define QP_VERSION_STR "6.8.0" //! Encrypted current QP release (6.8.0) and date (2020-03-31) #define QP_RELEASE 0x8897E7A7U //**************************************************************************** #ifndef Q_SIGNAL_SIZE //! The size (in bytes) of the signal of an event. Valid values: //! 1U, 2U, or 4U; default 2U /// @description /// This macro can be defined in the QEP port file (qep_port.hpp) to /// configure the QP::QSignal type. When the macro is not defined, the /// default of 2 bytes is applied. #define Q_SIGNAL_SIZE 2U #endif //**************************************************************************** // Aliases for basic numerical types; MISRA-C++ 2008 rule 3-9-2(req). //! alias for characters and character strings. /// @description /// This alias specifies character type for exclusive use in character /// strings. Use of this type, rather than plain 'char', is in compliance /// with the MISRA-C 2004 Rules 6.1(req), 6.3(adv). using char_t = char; //! alias for line numbers in assertions and return from QF::run() using int_t = int; //! alias for enumerations used for event signals using enum_t = int; //! alias for 32-bit IEEE 754 floating point numbers /// @note /// QP does not use floating-point types anywhere in the internal /// implementation, except in QS software tracing, where utilities for /// output of floating-point numbers are provided for application-level /// trace records. /// using float32_t = float; //! alias for 64-bit IEEE 754 floating point numbers /// @note /// QP does not use floating-point types anywhere in the internal /// implementation, except in QS software tracing, where utilities for /// output of floating-point numbers are provided for application-level /// trace records. /// using float64_t = double; //! Perform downcast of an event onto a subclass of QEvt @p class_ /// @description /// This macro encapsulates the downcast of QEvt pointers, which violates /// MISRA-C 2004 rule 11.4(advisory). This macro helps to localize this /// deviation. /// #define Q_EVT_CAST(class_) (static_cast(e)) //! Perform cast from unsigned integer @p uint_ to pointer of type @p type_. /// @description /// This macro encapsulates the cast to (type_ *), which QP ports or /// application might use to access embedded hardware registers. /// Such uses can trigger PC-Lint "Note 923: cast from int to pointer" /// and this macro helps to encapsulate this deviation. /// #define Q_UINT2PTR_CAST(type_, uint_) (reinterpret_cast(uint_)) //! Initializer of static constant QEvt instances /// @description /// This macro encapsulates the ugly casting of enumerated signals /// to QSignal and constants for QEvt.poolID and QEvt.refCtr_. /// #define QEVT_INITIALIZER(sig_) { static_cast(sig_), 0U, 0U } //**************************************************************************** //! namespace associated with the QP/C++ framework /// @ingroup qep qf qs qv qk qxk namespace QP { //! the current QP version number string based on QP_VERSION_STR constexpr char_t const versionStr[]{QP_VERSION_STR}; #if (Q_SIGNAL_SIZE == 1U) using QSignal = std::uint8_t; #elif (Q_SIGNAL_SIZE == 2U) //! QSignal represents the signal of an event. /// @description /// The relationship between an event and a signal is as follows. A signal /// in UML is the specification of an asynchronous stimulus that triggers /// reactions, and as such is an essential part of an event. (The signal /// conveys the type of the occurrence--what happened?) However, an event /// can also contain additional quantitative information about the /// occurrence in form of event parameters. using QSignal = std::uint16_t; #elif (Q_SIGNAL_SIZE == 4U) using QSignal = std::uint32_t; #else #error "Q_SIGNAL_SIZE defined incorrectly, expected 1U, 2U, or 4U" #endif #ifdef Q_EVT_CTOR // Provide the constructor for the QEvt class? //************************************************************************ class QEvt { public: //! public constructor (dynamic event) QEvt(QSignal const s) noexcept : sig(s) // poolId_/refCtr_ intentionally uninitialized {} enum StaticEvt : std::uint8_t { STATIC_EVT }; //! public constructor (static event) QEvt(QSignal const s, StaticEvt /*dummy*/) noexcept : sig(s), poolId_(0U), refCtr_(0U)) {} #ifdef Q_EVT_VIRTUAL //! virtual destructor virtual ~QEvt() noexcept {} #endif // Q_EVT_VIRTUAL public: QSignal sig; //!< signal of the event instance private: std::uint8_t poolId_; //!< pool ID (0 for static event) std::uint8_t volatile refCtr_; //!< reference counter friend class QF; friend class QActive; friend class QMActive; friend class QTimeEvt; friend class QEQueue; friend class QTicker; friend class QXThread; friend std::uint8_t QF_EVT_POOL_ID_ (QEvt const * const e); friend std::uint8_t QF_EVT_REF_CTR_ (QEvt const * const e); friend void QF_EVT_REF_CTR_INC_(QEvt const * const e); friend void QF_EVT_REF_CTR_DEC_(QEvt const * const e); }; #else // QEvt is a POD (Plain Old Datatype) //************************************************************************ //! QEvt base class. /// @description /// QEvt represents events without parameters and serves as the /// base class for derivation of events with parameters. /// /// @usage /// The following example illustrates how to add an event parameter by /// inheriting from the QEvt class. /// @include qep_qevt.cpp struct QEvt { QSignal sig; //!< signal of the event instance std::uint8_t poolId_; //!< pool ID (0 for static event) std::uint8_t volatile refCtr_; //!< reference counter }; #endif // Q_EVT_CTOR // forward declarations... struct QMState; struct QMTranActTable; class QXThread; //! Type returned from state-handler functions using QState = std::uint_fast8_t; //! Pointer to state-handler function using QStateHandler = QState (*)(void * const me, QEvt const * const e); //! Pointer to an action-handler function using QActionHandler = QState (*)(void * const me); //! Pointer to a thread-handler function using QXThreadHandler = void (*)(QXThread * const me); //! Attribute of for the QHsm class (Hierarchical State Machine). /// @description /// This union represents possible values stored in the 'state' and 'temp' /// attributes of the QHsm and QMsm classes. union QHsmAttr { QStateHandler fun; //!< pointer to a state handler function QActionHandler act; //!< pointer to an action-handler function QXThreadHandler thr; //!< pointer to an thread-handler function QMState const *obj; //!< pointer to QMState object QMTranActTable const *tatbl; //!< transition-action table }; //**************************************************************************** //! Hierarchical State Machine base class /// /// @description /// QHsm represents a Hierarchical State Machine (HSM) with full support for /// hierarchical nesting of states, entry/exit actions, and initial /// transitions in any composite state. QHsm inherits QMsm without adding /// new attributes, so it takes the same amount of RAM as QMsm. /// @n /// QHsm is also the base class for the QMsm state machine, which provides /// a superior efficiency, but requries the use of the QM modeling tool to /// generate code. /// /// @note /// QHsm is not intended to be instantiated directly, but rather serves as /// the base class for derivation of state machines in the application code. /// /// @usage /// The following example illustrates how to derive a state machine class /// from QHsm. /// @include qep_qhsm.cpp /// class QHsm { QHsmAttr m_state; //!< current active state (state-variable) QHsmAttr m_temp; //!< temporary: transition chain, target state, etc. public: //! virtual destructor virtual ~QHsm() noexcept; //! executes the top-most initial transition in QP::QHsm virtual void init(void const * const e); //! overloaded init(void) virtual void init(void) { this->init(nullptr); } //! Dispatches an event to QHsm virtual void dispatch(QEvt const * const e); //! Tests if a given state is part of the current active state //! configuration bool isIn(QStateHandler const s) noexcept; //! Obtain the current state (state handler function) //! @note used in the QM code generation QStateHandler state(void) const noexcept { return m_state.fun; } //! Obtain the current active child state of a given parent //! @note used in the QM code generation QStateHandler childState(QStateHandler const parent) noexcept; //! the top-state. static QState top(void * const me, QEvt const * const e) noexcept; protected: //! Protected constructor of QHsm. QHsm(QStateHandler const initial) noexcept; public: // facilities for the QHsm implementation strategy... //! event passed to the superstate to handle static constexpr QState Q_RET_SUPER {0}; //! event passed to submachine superstate static constexpr QState Q_RET_SUPER_SUB {1}; //! event unhandled due to a guard evaluating to 'false' static constexpr QState Q_RET_UNHANDLED {2}; //! event handled (internal transition) static constexpr QState Q_RET_HANDLED {3}; //! event silently ignored (bubbled up to top) static constexpr QState Q_RET_IGNORED {4}; //! state entry action executed static constexpr QState Q_RET_ENTRY {5}; //! state exit action executed static constexpr QState Q_RET_EXIT {6}; //! return value without any effect static constexpr QState Q_RET_NULL {7}; //! regular transition taken static constexpr QState Q_RET_TRAN {8}; //! initial transition taken static constexpr QState Q_RET_TRAN_INIT {9}; //! entry-point transition into a submachine static constexpr QState Q_RET_TRAN_EP {10}; //! transition to history of a given state static constexpr QState Q_RET_TRAN_HIST {11}; //! exit-point transition out of a submachine static constexpr QState Q_RET_TRAN_XP {12}; protected: //! Helper function to specify a state transition QState tran(QStateHandler const target) noexcept { m_temp.fun = target; return Q_RET_TRAN; } //! Helper function to specify a transition to history QState tran_hist(QStateHandler const hist) noexcept { m_temp.fun = hist; return Q_RET_TRAN_HIST; } //! Helper function to specify the superstate of a given state QState super(QStateHandler const superstate) noexcept { m_temp.fun = superstate; return Q_RET_SUPER; } enum ReservedHsmSignals : QSignal { Q_ENTRY_SIG = 1, //!< signal for entry actions Q_EXIT_SIG, //!< signal for exit actions Q_INIT_SIG //!< signal for nested initial transitions }; // protected facilities for the QMsm implementation strategy... //! Helper function to specify a regular state transition //! in a QM state-handler QState qm_tran(void const * const tatbl) noexcept { m_temp.tatbl = static_cast(tatbl); return Q_RET_TRAN; } //! Helper function to specifiy a transition to history //! in a QM state-handler QState qm_tran_hist(QMState const * const hist, void const * const tatbl) noexcept { m_state.obj = hist; m_temp.tatbl = static_cast(tatbl); return Q_RET_TRAN_HIST; } //! Helper function to specify an initial state transition //! in a QM state-handler QState qm_tran_init(void const * const tatbl) noexcept { m_temp.tatbl = static_cast(tatbl); return Q_RET_TRAN_INIT; } //! Helper function to specify a transition to an entry point //! to a submachine state in a QM state-handler QState qm_tran_ep(void const * const tatbl) noexcept { m_temp.tatbl = static_cast(tatbl); return Q_RET_TRAN_EP; } //! Helper function to specify a transition to an exit point //! from a submachine state in a QM state-handler QState qm_tran_xp(QActionHandler const xp, void const *const tatbl) noexcept { m_state.act = xp; m_temp.tatbl = static_cast(tatbl); return Q_RET_TRAN_XP; } #ifdef Q_SPY //! Helper function to specify a state entry in a QM state-handler QState qm_entry(QMState const * const s) noexcept { m_temp.obj = s; return Q_RET_ENTRY; } //! Helper function to specify a state exit in a QM state-handler QState qm_exit(QMState const * const s) noexcept { m_temp.obj = s; return Q_RET_EXIT; } #else //! Helper function to specify a state entry in a QM state-handler QState qm_entry(QMState const * const s) noexcept { (void)s; return Q_RET_ENTRY; } //! Helper function to specify a state exit in a QM state-handler QState qm_exit(QMState const * const s) noexcept { (void)s; return Q_RET_EXIT; } #endif //! Helper function to specify a submachine exit in a QM state-handler QState qm_sm_exit(QMState const * const s) noexcept { m_temp.obj = s; return Q_RET_EXIT; } //! Helper function to call in a QM state-handler when it passes //! the event to the host submachine state to handle an event. QState qm_super_sub(QMState const * const s) noexcept { m_temp.obj = s; return Q_RET_SUPER_SUB; } private: //!< maximum nesting depth of states in HSM static constexpr std::int_fast8_t MAX_NEST_DEPTH_{6}; //! internal helper function to take a transition in QP::QHsm std::int_fast8_t hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_]); friend class QMsm; friend class QActive; friend class QMActive; friend class QF; friend class QS; friend class QXK; friend class QXThread; friend class QXMutex; friend class QXSemaphore; #ifdef Q_UTEST friend class QActiveDummy; #endif // Q_UTEST }; //**************************************************************************** //! QM State Machine implementation strategy /// @description /// QMsm (QM State Machine) provides a more efficient state machine /// implementation strategy than QHsm, but requires the use of the QM /// modeling tool, but are the fastest and need the least run-time /// support (the smallest event-processor taking up the least code space). /// /// @note /// QMsm is not intended to be instantiated directly, but rather serves as /// the base class for derivation of state machines in the application code. /// /// @usage /// The following example illustrates how to derive a state machine class /// from QMsm. Please note that the QMsm member 'super' is defined as the /// _first_ member of the derived struct. /// @include qep_qmsm.cpp /// class QMsm : public QHsm { public: //! Performs the second step of SM initialization by triggering /// the top-most initial transition. void init(void const * const e) override; void init(void) override { this->init(nullptr); } //! Dispatches an event to a HSM void dispatch(QEvt const * const e) override; //! Tests if a given state is part of the active state configuration bool isInState(QMState const * const st) const noexcept; //! Return the current active state object (read only) QMState const *stateObj(void) const noexcept { return m_state.obj; } //! Obtain the current active child state of a given parent (read only) QMState const *childStateObj(QMState const * const parent) const noexcept; protected: //! Protected constructor QMsm(QStateHandler const initial) noexcept; private: //! disallow the inhertited isIn() function in QP::QMsm and subclasses //! @sa QP::QMsm::isInState() bool isIn(QStateHandler const s) noexcept = delete; //! disallow the inhertited state() function in QP::QMsm and subclasses //! @sa QP::QMsm::stateObj() QStateHandler state(void) const noexcept = delete; //! disallow the inhertited childState() function in QP::QMsm and subclasses //! @sa QP::QMsm::childStateObj() QStateHandler childState(QStateHandler const parent) noexcept = delete; //! disallow the inhertited top() function in QP::QMsm and subclasses //! @sa QP::QMsm::msm_top_s static QState top(void * const me, QEvt const * const e) noexcept = delete; //! Internal helper function to execute a transition-action table QState execTatbl_(QMTranActTable const * const tatbl); //! Internal helper function to exit current state to transition source void exitToTranSource_(QMState const *s, QMState const * const ts); //! Internal helper function to enter state history QState enterHistory_(QMState const * const hist); //! maximum depth of implemented entry levels for transitions to history static constexpr std::int_fast8_t MAX_ENTRY_DEPTH_ {4}; //! the top state object for the QMsm static QMState const msm_top_s; friend class QMActive; }; //! State object for the QP::QMsm class (QM State Machine). /// @description /// This class groups together the attributes of a QP::QMsm state, such as /// the parent state (state nesting), the associated state handler function /// and the exit action handler function. These attributes are used inside /// the QP::QMsm::dispatch() and QP::QMsm::init() functions. /// /// @attention /// The QP::QMState class is only intended for the QM code generator and /// should not be used in hand-crafted code. struct QMState { QMState const *superstate; //!< superstate of this state QStateHandler const stateHandler; //!< state handler function QActionHandler const entryAction; //!< entry action handler function QActionHandler const exitAction; //!< exit action handler function QActionHandler const initAction; //!< init action handler function }; //! Transition-Action Table for the QP::QMsm State Machine. struct QMTranActTable { QMState const *target; QActionHandler const act[1]; }; //**************************************************************************** //! Provides miscellaneous QEP services. class QEP { public: //! get the current QEP version number string of the form "X.Y.Z" static char_t const *getVersion(void) noexcept { return versionStr; } }; //! Offset or the user signals constexpr enum_t Q_USER_SIG {4}; } // namespace QP //**************************************************************************** // Macros for coding QHsm-style state machines... //! Macro to generate a declaration of a state-handler, state-caller and //! a state-object for a given state in a subclass of QP::QHsm. #define Q_STATE_DECL(state_) \ QP::QState state_ ## _h(QP::QEvt const * const e); \ static QP::QState state_(void * const me, QP::QEvt const * const e) //! Macro to generate a definition of a state-handler for a given state //! in a subclass of QP::QHsm. #define Q_STATE_DEF(subclass_, state_) \ QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) {\ return static_cast(me)->state_ ## _h(e); } \ QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) //! Macro to specify that the event was handled #define Q_HANDLED() (Q_RET_HANDLED) //! Macro to specify that the event was NOT handled //! due to a guard condition evaluating to 'false' #define Q_UNHANDLED() (Q_RET_UNHANDLED) //! Macro to perform casting to QStateHandler. /// @description /// This macro encapsulates the cast of a specific state handler function /// pointer to QStateHandler, which violates MISRA-C 2004 rule 11.4(advisory). /// This macro helps to localize this deviation. #define Q_STATE_CAST(handler_) \ (reinterpret_cast(handler_)) //! Macro to perform casting to QActionHandler. /// @description /// This macro encapsulates the cast of a specific action handler function /// pointer to QActionHandler, which violates MISRA-C2004 rule 11.4(advisory). /// This macro helps to localize this deviation. #define Q_ACTION_CAST(act_) (reinterpret_cast(act_)) //! Macro to provide strictly-typed zero-action to terminate action lists //! in the transition-action-tables #define Q_ACTION_NULL (nullptr) //**************************************************************************** // Macros for coding QMsm-style state machines... //! Macro to generate a declaration of a state-handler, state-caller and //! a state-object for a given state in a subclass of QP::QMsm. #define QM_STATE_DECL(state_) \ QP::QState state_ ## _h(QP::QEvt const * const e); \ static QP::QState state_(void * const me, QP::QEvt const * const e); \ static QP::QMState const state_ ## _s //! Macro to generate a declaration of a state-handler, state-caller and //! a state-object for a given *submachine* state in a subclass of QP::QMsm. #define QM_SM_STATE_DECL(subm_, state_) \ QP::QState state_ ## _h(QP::QEvt const * const e); \ static QP::QState state_(void * const me, QP::QEvt const * const e); \ static SM_ ## subm_ const state_ ## _s //! Macro to generate a declaration of an action-handler and action-caller //! in a subclass of QP::QMsm. #define QM_ACTION_DECL(action_) \ QP::QState action_ ## _h(void); \ static QP::QState action_(void * const me) //! Macro to generate a definition of a state-caller and state-handler //! for a given state in a subclass of QP::QMsm. #define QM_STATE_DEF(subclass_, state_) \ QP::QState subclass_::state_(void * const me, QP::QEvt const * const e) {\ return static_cast(me)->state_ ## _h(e); } \ QP::QState subclass_::state_ ## _h(QP::QEvt const * const e) //! Macro to generate a definition of an action-caller and action-handler //! in a subclass of QP::QMsm. #define QM_ACTION_DEF(subclass_, action_) \ QP::QState subclass_::action_(void * const me) { \ return static_cast(me)->action_ ## _h(); } \ QP::QState subclass_::action_ ## _h(void) //! Macro for a QM action-handler when it handles the event. #define QM_HANDLED() (Q_RET_HANDLED) //! Macro for a QM action-handler when it does not handle the event //! due to a guard condition evaluating to false. #define QM_UNHANDLED() (Q_RET_UNHANDLED) //! Macro for a QM action-handler when it passes the event to the superstate #define QM_SUPER() (Q_RET_SUPER) //! Macro to provide strictly-typed zero-state to use for submachines. //! Applicable to suclasses of QP::QMsm. #define QM_STATE_NULL (nullptr) #endif // QEP_HPP