/// @file /// @brief QEP/C++ platform-independent public interface. /// @ingroup qep /// @cond ///*************************************************************************** /// Last updated for version 5.9.6 /// Last updated on 2017-08-01 /// /// Q u a n t u m L e a P s /// --------------------------- /// innovating embedded systems /// /// Copyright (C) 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: /// https://state-machine.com /// mailto:info@state-machine.com ///*************************************************************************** /// @endcond #ifndef qep_h #define qep_h //**************************************************************************** //! The current QP version as a decimal constant XYZ, where X is a 1-digit // major version number, Y is a 1-digit minor version number, and Z is // a 1-digit release number. #define QP_VERSION 596 //! The current QP version number string of the form X.Y.Z, where X is // a 1-digit major version number, Y is a 1-digit minor version number, // and Z is a 1-digit release number. #define QP_VERSION_STR "5.9.6" //! Tamperproof current QP release (5.9.6) and date (2017-08-04) #define QP_RELEASE 0x9A315E6BU //**************************************************************************** #ifndef Q_SIGNAL_SIZE //! The size (in bytes) of the signal of an event. Valid values: //! 1, 2, or 4; default 1 /// @description /// This macro can be defined in the QEP port file (qep_port.h) to /// configure the QP::QSignal type. When the macro is not defined, the /// default of 1 byte is chosen. #define Q_SIGNAL_SIZE 2 #endif //**************************************************************************** //! helper macro to calculate static dimension of a 1-dim array @p array_ #define Q_DIM(array_) (sizeof(array_) / sizeof((array_)[0])) //**************************************************************************** // typedefs for basic numerical types; MISRA-C++ 2008 rule 3-9-2(req). //! typedef for character strings. /// @description /// This typedef 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). typedef char char_t; //! typedef for line numbers in assertions and return from QF_run() typedef int int_t; //! typedef for enumerations used for event signals typedef int enum_t; //! typedef 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. /// typedef float float32_t; //! typedef 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. /// typedef double float64_t; //! 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_), \ static_cast(0), static_cast(0) } //**************************************************************************** //! 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 extern char_t const versionStr[6]; #if (Q_SIGNAL_SIZE == 1) typedef uint8_t QSignal; #elif (Q_SIGNAL_SIZE == 2) //! 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. typedef uint16_t QSignal; #elif (Q_SIGNAL_SIZE == 4) typedef uint32_t QSignal; #else #error "Q_SIGNAL_SIZE defined incorrectly, expected 1, 2, or 4" #endif #ifdef Q_EVT_CTOR // Provide the constructor for the QEvt class? //************************************************************************ class QEvt { public: //! public constructor (dynamic event) QEvt(QSignal const s) // poolId_/refCtr_ intentionally uninitialized : sig(s) {} enum StaticEvt { STATIC_EVT }; //! public constructor (static event) QEvt(QSignal const s, StaticEvt /*dummy*/) : sig(s), poolId_(static_cast(0)), refCtr_(static_cast(0)) {} #ifdef Q_EVT_VIRTUAL //! virtual destructor virtual ~QEvt() {} #endif // Q_EVT_VIRTUAL public: QSignal sig; //!< signal of the event instance private: uint8_t poolId_; //!< pool ID (0 for static event) 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 uint8_t QF_EVT_POOL_ID_ (QEvt const * const e); friend 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 uint8_t poolId_; //!< pool ID (0 for static event) uint8_t volatile refCtr_; //!< reference counter }; #endif // Q_EVT_CTOR //! Type returned from state-handler functions typedef uint_fast8_t QState; //! pointer to state-handler function typedef QState (*QStateHandler)(void * const me, QEvt const * const e); //! pointer to an action-handler function typedef QState (*QActionHandler)(void * const me); // forward declarations... struct QMState; struct QMTranActTable; //! 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 QMState const *obj; //!< pointer to QMState object QMTranActTable const *tatbl; //!< transition-action table }; //**************************************************************************** //! event passed to the superstate to handle QState const Q_RET_SUPER = static_cast(0); //! event passed to submachine superstate QState const Q_RET_SUPER_SUB = static_cast(1); //! event unhandled due to a guard evaluating to FALSE QState const Q_RET_UNHANDLED = static_cast(2); //! event handled (internal transition) QState const Q_RET_HANDLED = static_cast(3); //! event silently ignored (bubbled up to top) QState const Q_RET_IGNORED = static_cast(4); //! state entry action executed QState const Q_RET_ENTRY = static_cast(5); //! state exit action executed QState const Q_RET_EXIT = static_cast(6); //! return value without any effect QState const Q_RET_NULL = static_cast(7); //! regular transition taken QState const Q_RET_TRAN = static_cast(8); //! initial transition taken QState const Q_RET_TRAN_INIT = static_cast(9); //! event handled (transition to history) QState const Q_RET_TRAN_HIST = static_cast(10); //! entry-point transition into a submachine QState const Q_RET_TRAN_EP = static_cast(11); //! exit-point transition out of a submachine QState const Q_RET_TRAN_XP = static_cast(12); //**************************************************************************** //! 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(); //! Executes the top-most initial transition in QP::QHsm virtual void init(QEvt const * const e); virtual void init(void) { this->init(static_cast(0)); } //! 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); //! the top-state. static QState top(void * const me, QEvt const * const e); //! Obtain the current state (state handler function) //! @note used in the QM code generation QStateHandler state(void) const { 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); protected: //! Protected constructor of QHsm. QHsm(QStateHandler const initial); public: // facilities for coding HSMs... //! internal helper function to specify the return of a state-handler //! when it handles the event. static QState Q_HANDLED(void) { return Q_RET_HANDLED; } //! internal helper function to specify the return of a state-handler //! function when it attempts to handle the event but a guard condition //! evaluates to false and there is no other explicit way of handling //! the event. static QState Q_UNHANDLED(void) { return Q_RET_UNHANDLED; } //! internal helper function to record a state transition QState tran_(QStateHandler const target) { m_temp.fun = target; return Q_RET_TRAN; } //! internal helper function to record a transition to history QState tran_hist_(QStateHandler const hist) { m_temp.fun = hist; return Q_RET_TRAN_HIST; } //! internal helper function to record the superstate QState super_(QStateHandler const superstate) { m_temp.fun = superstate; return Q_RET_SUPER; } enum ReservedHsmSignals { 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 coding MSMs... //! internal helper function to record a regular state transition QState qm_tran_(QMTranActTable const * const tatbl) { m_temp.tatbl = tatbl; return Q_RET_TRAN; } //! Internal helper function to record a regular state transition QState qm_tran_hist_(QMState const * const hist, QMTranActTable const * const tatbl) { m_state.obj = hist; m_temp.tatbl = tatbl; return Q_RET_TRAN_HIST; } //! Internal helper function to record an initial state transition QState qm_tran_init_(QMTranActTable const * const tatbl) { m_temp.tatbl = tatbl; return Q_RET_TRAN_INIT; } //! Internal helper function to record an transition to an entry point //! to a submachine state QState qm_tran_ep_(QMTranActTable const * const tatbl) { m_temp.tatbl = tatbl; return Q_RET_TRAN_EP; } //! Internal helper function to record an transition to an exit point //! from a submachine state QState qm_tran_xp_(QActionHandler const xp, QMTranActTable const *const tatbl) { m_state.act = xp; m_temp.tatbl = tatbl; return Q_RET_TRAN_XP; } //! Internal helper function to record a state entry QState qm_entry_(QMState const * const s) { m_temp.obj = s; return Q_RET_ENTRY; } //! Internal helper function to record a state exit QState qm_exit_(QMState const * const s) { m_temp.obj = s; return Q_RET_EXIT; } //! Internal helper function to call in a QM action-handler when //! it passes the event to the host submachine state to handle an event. QState qm_super_sub_(QMState const * const s) { m_temp.obj = s; return Q_RET_SUPER_SUB; } //! Internal helper function to call in a QM action-handler when //! it handles an event. static QState QM_HANDLED(void) { return Q_RET_HANDLED; } //! Macro to call in a QM action-handler when it does not handle //! an event due to a guard condition evaluating to false. static QState QM_UNHANDLED(void) { return Q_RET_UNHANDLED; } //! Internal helper function to call in a QM action-handler when //! it passes the event to the superstate for processing. static QState QM_SUPER(void) { return Q_RET_SUPER; } private: enum { MAX_NEST_DEPTH_ = 6 //!< maximum nesting depth of states in HSM }; //! internal helper function to take a transition int_fast8_t hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_]); friend class QMsm; friend class QActive; friend class QMActive; friend class QF; friend class QXK; friend class QXThread; friend class QXMutex; friend class QXSemaphore; }; //**************************************************************************** //! 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. virtual void init(QEvt const * const e); virtual void init(void) { this->init(static_cast(0)); } //! Dispatches an event to a HSM virtual void dispatch(QEvt const * const e); //! Tests if a given state is part of the active state configuration bool isInState(QMState const *st) const; //! Return the current active state object (read only) QMState const *stateObj(void) const { return m_state.obj; } //! Obtain the current active child state of a given parent (read only) QMState const *childStateObj(QMState const * const parent) const; protected: //! Protected constructor of QMsm QMsm(QStateHandler const initial); private: //! 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 int_fast8_t const MAX_ENTRY_DEPTH_ = static_cast(4); //! the top state object for the QMsm static QMState const msm_top_s; friend class QMActive; }; //! Top-most state of QMSM is NULL QMState * const QMsm_top = static_cast(0); //! State object for the QMsm class (Meta State Machine). /// @description /// This class groups together the attributes of a 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 QMStateObj 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 Meta 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) { return versionStr; } }; //! Offset or the user signals enum_t const Q_USER_SIG = static_cast(4); } // namespace QP //**************************************************************************** //! Perform cast 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_)) //! Perform cast 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(action_) \ (reinterpret_cast(action_)) #ifdef Q_SPY //! Macro to call in a QM state entry-handler. Applicable only to QMSMs. #define QM_ENTRY(state_) (me->qm_entry_((state_))) //! Macro to call in a QM state exit-handler. Applicable only to QMSMs. #define QM_EXIT(state_) (me->qm_exit_((state_))) #else #define QM_ENTRY(dummy) (QP::Q_RET_ENTRY) #define QM_EXIT(dummy) (QP::Q_RET_EXIT) #endif //! Macro to call in a QM submachine exit-handler. Applicable only to QMSMs. #define QM_SM_EXIT(state_) (me->qm_exit_((state_))) //! Macro to call in a QM state-handler when it executes a regular //! transition. Applicable only to QMSMs. #define QM_TRAN(tatbl_) \ (me->qm_tran_(reinterpret_cast(tatbl_))) //! Macro to call in a QM state-handler when it executes an initial //! transition. Applicable only to QMSMs. #define QM_TRAN_INIT(tatbl_) \ (me->qm_tran_init_(reinterpret_cast(tatbl_))) //! Macro to call in a QM state-handler when it executes a transition //! to history. Applicable only to QMSMs. #define QM_TRAN_HIST(history_, tatbl_) \ (me->qm_tran_hist_((history_), \ reinterpret_cast(tatbl_))) //! Macro to call in a QM state-handler when it executes an initial //! transition. Applicable only to QMSMs. #define QM_TRAN_EP(tatbl_) \ (me->qm_tran_ep_(reinterpret_cast(tatbl_))) //! Macro to call in a QM state-handler when it executes a transition //! to exit point. Applicable only to QMSMs. #define QM_TRAN_XP(xp_, tatbl_) \ (me->qm_tran_xp_((xp_), \ reinterpret_cast(tatbl_))) //! Designates the superstate of a given state in an MSM. #define QM_SUPER_SUB(state_) (me->qm_super_sub_((state_))) //! Designates a target for an initial or regular transition. //! Q_TRAN() can be used both in the FSMs and HSMs. /// @usage /// @include qep_qtran.cpp #define Q_TRAN(target_) (me->tran_(Q_STATE_CAST(target_))) //! Designates a target for an initial or regular transition. //! Q_TRAN() can be used both in the FSMs and HSMs. /// @usage /// @include qep_qtran.cpp #define Q_TRAN_HIST(hist_) (me->tran_hist_((hist_))) //! Designates the superstate of a given state in an HSM. /// @usage /// @include qep_qhsm.cpp #define Q_SUPER(state_) (me->super_(Q_STATE_CAST(state_))) #endif // qep_h