Program Listing for File hsm.hpp

Return to documentation for file (include/hsmcpp/hsm.hpp)

// Copyright (C) 2021 Igor Krechetov
// Distributed under MIT license. See file LICENSE for details

#ifndef HSMCPP_HSM_HPP
#define HSMCPP_HSM_HPP

#include <list>
#include <memory>
#include <string>

#include "HsmTypes.hpp"
#include "variant.hpp"

namespace hsmcpp {

class IHsmEventDispatcher;

class HierarchicalStateMachine {
public:
    explicit HierarchicalStateMachine(const StateID_t initialState);

    virtual ~HierarchicalStateMachine();

    void setInitialState(const StateID_t initialState);

    virtual bool initialize(const std::weak_ptr<IHsmEventDispatcher>& dispatcher);

    std::weak_ptr<IHsmEventDispatcher> dispatcher() const;

    bool isInitialized() const;

    void release();

    void registerFailedTransitionCallback(HsmTransitionFailedCallback_t onFailedTransition);

    template <class HsmHandlerClass>
    void registerFailedTransitionCallback(HsmHandlerClass* handler,
                                          HsmTransitionFailedCallbackPtr_t(HsmHandlerClass, onFailedTransition));

    void registerState(const StateID_t state,
                       HsmStateChangedCallback_t onStateChanged = nullptr,
                       HsmStateEnterCallback_t onEntering = nullptr,
                       HsmStateExitCallback_t onExiting = nullptr);

    template <class HsmHandlerClass>
    void registerState(const StateID_t state,
                       HsmHandlerClass* handler = nullptr,
                       HsmStateChangedCallbackPtr_t(HsmHandlerClass, onStateChanged) = nullptr,
                       HsmStateEnterCallbackPtr_t(HsmHandlerClass, onEntering) = nullptr,
                       HsmStateExitCallbackPtr_t(HsmHandlerClass, onExiting) = nullptr);

    void registerFinalState(const StateID_t state,
                            const EventID_t event = INVALID_HSM_EVENT_ID,
                            HsmStateChangedCallback_t onStateChanged = nullptr,
                            HsmStateEnterCallback_t onEntering = nullptr,
                            HsmStateExitCallback_t onExiting = nullptr);

    template <class HsmHandlerClass>
    void registerFinalState(const StateID_t state,
                            const EventID_t event = INVALID_HSM_EVENT_ID,
                            HsmHandlerClass* handler = nullptr,
                            HsmStateChangedCallbackPtr_t(HsmHandlerClass, onStateChanged) = nullptr,
                            HsmStateEnterCallbackPtr_t(HsmHandlerClass, onEntering) = nullptr,
                            HsmStateExitCallbackPtr_t(HsmHandlerClass, onExiting) = nullptr);

    // TODO: check structure and return FALSE?
    void registerHistory(const StateID_t parent,
                         const StateID_t historyState,
                         const HistoryType type = HistoryType::SHALLOW,
                         const StateID_t defaultTarget = INVALID_HSM_STATE_ID,
                         HsmTransitionCallback_t transitionCallback = nullptr);

    template <class HsmHandlerClass>
    void registerHistory(const StateID_t parent,
                         const StateID_t historyState,
                         const HistoryType type = HistoryType::SHALLOW,
                         const StateID_t defaultTarget = INVALID_HSM_STATE_ID,
                         HsmHandlerClass* handler = nullptr,
                         HsmTransitionCallbackPtr_t(HsmHandlerClass, transitionCallback) = nullptr);

    bool registerSubstate(const StateID_t parent, const StateID_t substate);

    bool registerSubstateEntryPoint(const StateID_t parent,
                                    const StateID_t substate,
                                    const EventID_t onEvent = INVALID_HSM_EVENT_ID,
                                    HsmTransitionConditionCallback_t conditionCallback = nullptr,
                                    const bool expectedConditionValue = true);

    template <class HsmHandlerClass>
    bool registerSubstateEntryPoint(const StateID_t parent,
                                    const StateID_t substate,
                                    const EventID_t onEvent = INVALID_HSM_EVENT_ID,
                                    HsmHandlerClass* handler = nullptr,
                                    HsmTransitionConditionCallbackPtr_t(HsmHandlerClass, conditionCallback) = nullptr,
                                    const bool expectedConditionValue = true);

    void registerTimer(const TimerID_t timerID, const EventID_t event);

    // TODO: add support for transition actions
    template <typename... Args>
    bool registerStateAction(const StateID_t state,
                             const StateActionTrigger actionTrigger,
                             const StateAction action,
                             Args&&... args);

    void registerTransition(const StateID_t fromState,
                            const StateID_t toState,
                            const EventID_t onEvent,
                            HsmTransitionCallback_t transitionCallback = nullptr,
                            HsmTransitionConditionCallback_t conditionCallback = nullptr,
                            const bool expectedConditionValue = true);

    template <class HsmHandlerClass>
    void registerTransition(const StateID_t fromState,
                            const StateID_t toState,
                            const EventID_t onEvent,
                            HsmHandlerClass* handler = nullptr,
                            HsmTransitionCallbackPtr_t(HsmHandlerClass, transitionCallback) = nullptr,
                            HsmTransitionConditionCallbackPtr_t(HsmHandlerClass, conditionCallback) = nullptr,
                            const bool expectedConditionValue = true);

    void registerSelfTransition(const StateID_t state,
                                const EventID_t onEvent,
                                const TransitionType type = TransitionType::EXTERNAL_TRANSITION,
                                HsmTransitionCallback_t transitionCallback = nullptr,
                                HsmTransitionConditionCallback_t conditionCallback = nullptr,
                                const bool expectedConditionValue = true);

    template <class HsmHandlerClass>
    void registerSelfTransition(const StateID_t state,
                                const EventID_t onEvent,
                                const TransitionType type = TransitionType::EXTERNAL_TRANSITION,
                                HsmHandlerClass* handler = nullptr,
                                HsmTransitionCallbackPtr_t(HsmHandlerClass, transitionCallback) = nullptr,
                                HsmTransitionConditionCallbackPtr_t(HsmHandlerClass, conditionCallback) = nullptr,
                                const bool expectedConditionValue = true);

    StateID_t getLastActiveState() const;

    const std::list<StateID_t>& getActiveStates() const;

    bool isStateActive(const StateID_t state) const;

    template <typename... Args>
    void transition(const EventID_t event, Args&&... args);

    template <typename... Args>
    bool transitionEx(const EventID_t event, const bool clearQueue, const bool sync, const int timeoutMs, Args&&... args);

    void transitionWithArgsArray(const EventID_t event, VariantVector_t&& args);

    bool transitionExWithArgsArray(const EventID_t event,
                                   const bool clearQueue,
                                   const bool sync,
                                   const int timeoutMs,
                                   VariantVector_t&& args);

    template <typename... Args>
    bool transitionSync(const EventID_t event, const int timeoutMs, Args&&... args);

    template <typename... Args>
    void transitionWithQueueClear(const EventID_t event, Args&&... args);

    bool transitionInterruptSafe(const EventID_t event);

    template <typename... Args>
    bool isTransitionPossible(const EventID_t event, Args&&... args);

    void startTimer(const TimerID_t timerID, const unsigned int intervalMs, const bool isSingleShot);

    void restartTimer(const TimerID_t timerID);

    void stopTimer(const TimerID_t timerID);

    bool isTimerRunning(const TimerID_t timerID);

    bool enableHsmDebugging();

    bool enableHsmDebugging(const std::string& dumpPath);

    void disableHsmDebugging();

protected:
    virtual std::string getStateName(const StateID_t state) const;

    virtual std::string getEventName(const EventID_t event) const;

private:
    template <typename... Args>
    void makeVariantList(VariantVector_t& vList, Args&&... args);

    bool registerStateActionImpl(const StateID_t state,
                                 const StateActionTrigger actionTrigger,
                                 const StateAction action,
                                 const VariantVector_t& args);
    bool isTransitionPossibleImpl(const EventID_t event, const VariantVector_t& args);

private:
    class Impl;
    std::shared_ptr<Impl> mImpl;
};

// =================================================================================================================
// Template Functions
// =================================================================================================================
template <class HsmHandlerClass>
void HierarchicalStateMachine::registerFailedTransitionCallback(HsmHandlerClass* handler,
                                                                HsmTransitionFailedCallbackPtr_t(HsmHandlerClass,
                                                                                                 onFailedTransition)) {
    registerFailedTransitionCallback(std::bind(onFailedTransition, handler, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
}

template <class HsmHandlerClass>
void HierarchicalStateMachine::registerState(const StateID_t state,
                                             HsmHandlerClass* handler,
                                             HsmStateChangedCallbackPtr_t(HsmHandlerClass, onStateChanged),
                                             HsmStateEnterCallbackPtr_t(HsmHandlerClass, onEntering),
                                             HsmStateExitCallbackPtr_t(HsmHandlerClass, onExiting)) {
    HsmStateChangedCallback_t funcStateChanged;
    HsmStateEnterCallback_t funcEntering;
    HsmStateExitCallback_t funcExiting;

    if (nullptr != handler) {
        if (nullptr != onStateChanged) {
            funcStateChanged = std::bind(onStateChanged, handler, std::placeholders::_1);
        }

        if (nullptr != onEntering) {
            funcEntering = std::bind(onEntering, handler, std::placeholders::_1);
        }

        if (nullptr != onExiting) {
            funcExiting = std::bind(onExiting, handler);
        }
    }

    registerState(state, funcStateChanged, funcEntering, funcExiting);
}

template <class HsmHandlerClass>
void HierarchicalStateMachine::registerFinalState(const StateID_t state,
                                                  const EventID_t event,
                                                  HsmHandlerClass* handler,
                                                  HsmStateChangedCallbackPtr_t(HsmHandlerClass, onStateChanged),
                                                  HsmStateEnterCallbackPtr_t(HsmHandlerClass, onEntering),
                                                  HsmStateExitCallbackPtr_t(HsmHandlerClass, onExiting)) {
    HsmStateChangedCallback_t funcStateChanged;
    HsmStateEnterCallback_t funcEntering;
    HsmStateExitCallback_t funcExiting;

    if (nullptr != handler) {
        if (nullptr != onStateChanged) {
            funcStateChanged = std::bind(onStateChanged, handler, std::placeholders::_1);
        }

        if (nullptr != onEntering) {
            funcEntering = std::bind(onEntering, handler, std::placeholders::_1);
        }

        if (nullptr != onExiting) {
            funcExiting = std::bind(onExiting, handler);
        }
    }

    registerFinalState(state, event, funcStateChanged, funcEntering, funcExiting);
}

template <class HsmHandlerClass>
void HierarchicalStateMachine::registerHistory(const StateID_t parent,
                                               const StateID_t historyState,
                                               const HistoryType type,
                                               const StateID_t defaultTarget,
                                               HsmHandlerClass* handler,
                                               HsmTransitionCallbackPtr_t(HsmHandlerClass, transitionCallback)) {
    HsmTransitionCallback_t funcTransitionCallback;

    if (nullptr != handler) {
        if (nullptr != transitionCallback) {
            funcTransitionCallback = std::bind(transitionCallback, handler, std::placeholders::_1);
        }
    }

    registerHistory(parent, historyState, type, defaultTarget, funcTransitionCallback);
}

template <class HsmHandlerClass>
bool HierarchicalStateMachine::registerSubstateEntryPoint(const StateID_t parent,
                                                          const StateID_t substate,
                                                          const EventID_t onEvent,
                                                          HsmHandlerClass* handler,
                                                          HsmTransitionConditionCallbackPtr_t(HsmHandlerClass,
                                                                                              conditionCallback),
                                                          const bool expectedConditionValue) {
    HsmTransitionConditionCallback_t condition;

    if ((nullptr != handler) && (nullptr != conditionCallback)) {
        condition = std::bind(conditionCallback, handler, std::placeholders::_1);
    }

    return registerSubstateEntryPoint(parent, substate, onEvent, condition, expectedConditionValue);
}

template <typename... Args>
bool HierarchicalStateMachine::registerStateAction(const StateID_t state,
                                                   const StateActionTrigger actionTrigger,
                                                   const StateAction action,
                                                   Args&&... args) {
    VariantVector_t eventArgs;

    makeVariantList(eventArgs, std::forward<Args>(args)...);
    return registerStateActionImpl(state, actionTrigger, action, eventArgs);
}

template <class HsmHandlerClass>
void HierarchicalStateMachine::registerTransition(const StateID_t fromState,
                                                  const StateID_t toState,
                                                  const EventID_t onEvent,
                                                  HsmHandlerClass* handler,
                                                  HsmTransitionCallbackPtr_t(HsmHandlerClass, transitionCallback),
                                                  HsmTransitionConditionCallbackPtr_t(HsmHandlerClass, conditionCallback),
                                                  const bool expectedConditionValue) {
    HsmTransitionCallback_t funcTransitionCallback;
    HsmTransitionConditionCallback_t funcConditionCallback;

    if (nullptr != handler) {
        if (nullptr != transitionCallback) {
            funcTransitionCallback = std::bind(transitionCallback, handler, std::placeholders::_1);
        }

        if (nullptr != conditionCallback) {
            funcConditionCallback = std::bind(conditionCallback, handler, std::placeholders::_1);
        }
    }

    registerTransition(fromState, toState, onEvent, funcTransitionCallback, funcConditionCallback, expectedConditionValue);
}

template <class HsmHandlerClass>
void HierarchicalStateMachine::registerSelfTransition(const StateID_t state,
                                                      const EventID_t onEvent,
                                                      const TransitionType type,
                                                      HsmHandlerClass* handler,
                                                      HsmTransitionCallbackPtr_t(HsmHandlerClass, transitionCallback),
                                                      HsmTransitionConditionCallbackPtr_t(HsmHandlerClass, conditionCallback),
                                                      const bool expectedConditionValue) {
    HsmTransitionCallback_t funcTransitionCallback;
    HsmTransitionConditionCallback_t funcConditionCallback;

    if (nullptr != handler) {
        if (nullptr != transitionCallback) {
            funcTransitionCallback = std::bind(transitionCallback, handler, std::placeholders::_1);
        }

        if (nullptr != conditionCallback) {
            funcConditionCallback = std::bind(conditionCallback, handler, std::placeholders::_1);
        }
    }

    registerSelfTransition(state, onEvent, type, funcTransitionCallback, funcConditionCallback, expectedConditionValue);
}

template <typename... Args>
void HierarchicalStateMachine::transition(const EventID_t event, Args&&... args) {
    (void)transitionEx(event, false, false, 0, std::forward<Args>(args)...);
}

template <typename... Args>
bool HierarchicalStateMachine::transitionEx(const EventID_t event,
                                            const bool clearQueue,
                                            const bool sync,
                                            const int timeoutMs,
                                            Args&&... args) {
    VariantVector_t eventArgs;

    makeVariantList(eventArgs, std::forward<Args>(args)...);
    return transitionExWithArgsArray(event, clearQueue, sync, timeoutMs, std::move(eventArgs));
}

template <typename... Args>
bool HierarchicalStateMachine::transitionSync(const EventID_t event, const int timeoutMs, Args&&... args) {
    return transitionEx(event, false, true, timeoutMs, std::forward<Args>(args)...);
}

template <typename... Args>
void HierarchicalStateMachine::transitionWithQueueClear(const EventID_t event, Args&&... args) {
    // NOTE: async transitions always return true
    (void)transitionEx(event, true, false, 0, std::forward<Args>(args)...);
}

template <typename... Args>
bool HierarchicalStateMachine::isTransitionPossible(const EventID_t event, Args&&... args) {
    VariantVector_t eventArgs;

    makeVariantList(eventArgs, std::forward<Args>(args)...);

    return isTransitionPossibleImpl(event, eventArgs);
}

template <typename... Args>
void HierarchicalStateMachine::makeVariantList(VariantVector_t& vList, Args&&... args) {
    volatile int make_variant[] = {0, (vList.emplace_back(std::forward<Args>(args)), 0)...};
    (void)make_variant;
}

}  // namespace hsmcpp

#endif  // HSMCPP_HSM_HPP