層次狀態機(Hierarchical State Machine, HSM),從基本原理、結構設計、實現方法以及如何結合 Qt 進行具體實現等方面進行分析。
1. 層次狀態機的基本原理
層次狀態機是一種用于管理復雜系統行為的狀態機模型,它通過將狀態組織成層次結構來簡化設計和維護。這種結構使得復雜的邏輯可以分解為更小、更易于管理的部分。
關鍵概念:
- 狀態(State): 系統在某一時刻所處的條件或模式。
- 事件(Event): 觸發狀態轉換的信息或信號。
- 轉換(Transition): 從一個狀態到另一個狀態的遷移過程。
- 動作(Action): 在進入、離開狀態或進行轉換時執行的操作。
- 父狀態和子狀態:
- 父狀態(Superstate): 包含多個子狀態的狀態。
- 子狀態(Substate): 屬于某個父狀態的更具體的狀態。
特點:
- 嵌套結構: 狀態可以嵌套在其他狀態中,形成層次結構。
- 繼承行為: 子狀態可以繼承父狀態的行為和動作。
- 事件委托: 事件可以從子狀態傳遞到父狀態進行處理。
- 簡化設計: 將復雜的狀態邏輯分解為更小的、可管理的部分。
2. 層次狀態機的設計
在設計層次狀態機時,需要仔細規劃狀態之間的關系以及如何組織這些狀態。以下是一些設計原則和步驟:
設計步驟:
-
識別頂級狀態:
- 確定系統的基本操作模式或主要功能。
- 例如,在一個電梯系統中,頂級狀態可能包括“待命”、“運行”和“維護”。
-
定義子狀態:
- 對于每個頂級狀態,進一步分解為更具體的狀態。
- 例如,“運行”狀態可以包含“上升”、“下降”和“停止”等子狀態。
-
確定事件和轉換:
- 定義可能觸發狀態轉換的事件。
- 確定每個狀態在接收到特定事件時應執行的操作以及如何進行轉換。
- 例如,“上升”狀態在接收到“到達樓層”事件時,可以轉換到“停止”狀態。
-
實現繼承和委托:
- 設計父狀態的行為,并讓子狀態繼承這些行為。
- 當子狀態無法處理某個事件時,將該事件傳遞給其父狀態進行處理。
-
編寫動作函數:
- 實現每個狀態的進入(Entry)、離開(Exit)操作以及轉換期間的動作(Action)。
- 例如,在“上升”狀態下進入時啟動電機,在離開時停止電機。
-
定義初始狀態和歷史狀態:
- 指定每個復合狀態的初始子狀態。
- 使用歷史狀態來記住上次活動的子狀態,以便在返回該狀態時恢復到之前的狀態。
示例:
假設我們設計一個簡單的電視遙控器狀態機,包含以下狀態:
- 待命(Standby)
- 運行(Running)
- 頻道選擇(Channel Selection)
- 瀏覽模式(Browse Mode)
- 鎖定模式(Lock Mode)
- 音量控制(Volume Control)
- 頻道選擇(Channel Selection)
事件包括:
POWER
CHANNEL_UP
,CHANNEL_DOWN
VOLUME_UP
,VOLUME_DOWN
MODE_SWITCH
狀態圖示例:
+-------------------+| Standby |+--------+----------+|POWER|+----v-----+| Running |+--+-+------+| |CHANNEL_UP|VOLUME_DOWN/ | \/ v \+------------+ +------------+| Browse Mode| |Volume Ctrl|+------------+ +------------+
3. 層次狀態機的實現
在實際編程中,層次狀態機可以通過多種方式實現。以下是一個使用 C++ 和 Qt 的具體示例。
使用結構體定義狀態和事件
首先,我們定義狀態、事件及其處理函數的類型:
#include <QObject>
#include <QVector>
#include <QDebug>namespace HSMUtilityDef {typedef uint32_t HSM_EVENT;// 定義常見事件const uint32_t MAX_DEPTH = 5;const HSM_EVENT HSME_NULL = 0;const HSM_EVENT HSME_START = 1;const HSM_EVENT HSME_INIT = static_cast<HSM_EVENT>(-3);const HSM_EVENT HSME_ENTRY = static_cast<HSM_EVENT>(-2);const HSM_EVENT HSME_EXIT = static_cast<HSM_EVENT>(-1);// 自定義事件const HSM_EVENT POWER = 100;const HSM_EVENT CHANNEL_UP = 101;const HSM_EVENT CHANNEL_DOWN = 102;const HSM_EVENT VOLUME_UP = 103;const HSM_EVENT VOLUME_DOWN = 104;const HSM_EVENT MODE_SWITCH = 105;
}
定義狀態基類
創建一個抽象基類 HSMState
,包含處理事件的虛函數:
class HSMState : public QObject {Q_OBJECTpublic:explicit HSMState(HSMState *parent = nullptr) : QObject(parent), m_parent(parent) {}virtual ~HSMState() {}// 處理事件的主要接口virtual HSMUtilityDef::HSM_EVENT handleEvent(HSMUtilityDef::HSM_EVENT event, void* param = nullptr) {switch (event) {case HSMUtilityDef::HSME_ENTRY:onEntry(param);break;case HSMUtilityDef::HSME_EXIT:onExit(param);break;default:return unhandledEvent(event, param);}return HSMUtilityDef::HSME_NULL;}protected:// 進入狀態時執行的動作virtual void onEntry(void* param) {}// 離開狀態時執行的動作virtual void onExit(void* param) {}// 處理未定義事件的方法virtual HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) {qDebug() << "Unhandled event" << event;return event;}// 獲取父狀態HSMState* parent() const { return m_parent; }private:HSMState *m_parent;
};
定義具體狀態類
創建具體的派生狀態類,實現特定的邏輯:
// 待命狀態(Standby)
class StandbyState : public HSMState {Q_OBJECTpublic:explicit StandbyState(HSMState* parent = nullptr) : HSMState(parent) {}protected:void onEntry(void* param) override {qDebug() << "Entering Standby State";}void onExit(void* param) override {qDebug() << "Exiting Standby State";}HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {if (event == HSMUtilityDef::POWER) {return static_cast<HSMUtilityDef::HSM_EVENT>(HSMUtilityDef::HSME_INIT);}return HSMState::unhandledEvent(event, param);}
};// 運行狀態(Running)
class RunningState : public HSMState {Q_OBJECTpublic:explicit RunningState(HSMState* parent = nullptr) : HSMState(parent) {}protected:void onEntry(void* param) override {qDebug() << "Entering Running State";}void onExit(void* param) override {qDebug() << "Exiting Running State";}HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {if (event == HSMUtilityDef::POWER) {return static_cast<HSMUtilityDef::HSM_EVENT>(HSMUtilityDef::HSME_EXIT);}return HSMState::unhandledEvent(event, param);}
};// 頻道選擇狀態(Channel Selection)
class ChannelSelectionState : public HSMState {Q_OBJECTpublic:explicit ChannelSelectionState(HSMState* parent = nullptr) : HSMState(parent), m_currentMode(BROWSE_MODE) {}protected:void onEntry(void* param) override {qDebug() << "Entering Channel Selection State";}void onExit(void* param) override {qDebug() << "Exiting Channel Selection State";}HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {switch (event) {case HSMUtilityDef::CHANNEL_UP:channelUp();break;case HSMUtilityDef::CHANNEL_DOWN:channelDown();break;case HSMUtilityDef::MODE_SWITCH:modeSwitch();break;default:return HSMState::unhandledEvent(event, param);}return HSMUtilityDef::HSME_NULL;}private:enum Mode {BROWSE_MODE,LOCK_MODE};Mode m_currentMode;void channelUp() {if (m_currentMode == BROWSE_MODE) {qDebug() << "Channel Up in Browse Mode";} else if (m_currentMode == LOCK_MODE) {qDebug() << "Channel Up in Lock Mode";}}void channelDown() {if (m_currentMode == BROWSE_MODE) {qDebug() << "Channel Down in Browse Mode";} else if (m_currentMode == LOCK_MODE) {qDebug() << "Channel Down in Lock Mode";}}void modeSwitch() {if (m_currentMode == BROWSE_MODE) {m_currentMode = LOCK_MODE;qDebug() << "Switched to Lock Mode";} else {m_currentMode = BROWSE_MODE;qDebug() << "Switched to Browse Mode";}}
};// 音量控制狀態(Volume Control)
class VolumeControlState : public HSMState {Q_OBJECTpublic:explicit VolumeControlState(HSMState* parent = nullptr) : HSMState(parent) {}protected:void onEntry(void* param) override {qDebug() << "Entering Volume Control State";}void onExit(void* param) override {qDebug() << "Exiting Volume Control State";}HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {switch (event) {case HSMUtilityDef::VOLUME_UP:volumeUp();break;case HSMUtilityDef::VOLUME_DOWN:volumeDown();break;default:return HSMState::unhandledEvent(event, param);}return HSMUtilityDef::HSME_NULL;}private:void volumeUp() {qDebug() << "Volume Up";}void volumeDown() {qDebug() << "Volume Down";}
};
定義層次狀態機類
創建一個管理狀態轉換的主類 HSM
:
class HSM : public QObject {Q_OBJECTpublic:explicit HSM(QObject* parent = nullptr) : QObject(parent), m_currentState(nullptr) {}void start(HSMState* initialState) {if (m_currentState == nullptr) {initialize(initialState);}}void processEvent(HSMUtilityDef::HSM_EVENT event, void* param = nullptr) {if (m_currentState != nullptr) {HSMState* nextState = m_currentState;HSMUtilityDef::HSM_EVENT nextEvent = event;// 處理事件,直到返回 HSME_NULLwhile (nextEvent != HSMUtilityDef::HSME_NULL) {nextState = processSingleEvent(nextState, nextEvent, param);nextEvent = nextState->handleEvent(event, param);}}}private:HSMState* m_currentState;void initialize(HSMState* initialState) {if (initialState != nullptr) {// 初始化狀態棧QVector<HSMState*> stateStack;while (initialState != nullptr) {stateStack.append(initialState);initialState = initialState->parent();}// 從頂層狀態開始初始化for (int i = stateStack.size() - 1; i >= 0; --i) {HSMState* currentState = stateStack[i];currentState->handleEvent(HSMUtilityDef::HSME_ENTRY, nullptr);}m_currentState = stateStack.last();}}HSMState* processSingleEvent(HSMState* currentState, HSMUtilityDef::HSM_EVENT event, void* param) {switch (event) {case HSMUtilityDef::HSME_INIT:return initializeChildStates(currentState);case HSMUtilityDef::HSME_ENTRY:currentState->onEntry(param);break;case HSMUtilityDef::HSME_EXIT:currentState->onExit(param);return processSingleEvent(currentState->parent(), HSMUtilityDef::HSME_EXIT, param);}return currentState;}HSMState* initializeChildStates(HSMState* parentState) {if (parentState == nullptr) {return nullptr;}QVector<HSMState*> childStates = findInitialStates(parentState);for (HSMState* state : childStates) {processSingleEvent(state, HSMUtilityDef::HSME_ENTRY, nullptr);}return childStates.last();}QVector<HSMState*> findInitialStates(HSMState* parentState) const {// 在實際應用中,可能需要更復雜的邏輯來確定初始子狀態// 這里簡單地假設每個父狀態只有一個直接的初始子狀態QVector<HSMState*> children;QObjectList childObjects = parentState->children();for (QObject* obj : childObjects) {HSMState* state = qobject_cast<HSMState*>(obj);if (state != nullptr) {children.append(state);}}// 返回第一個子狀態作為初始狀態return children;}
};
構建和運行狀態機
在 main
函數中構建并運行層次狀態機:
#include <QCoreApplication>
#include <QDebug>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);// 創建狀態對象StandbyState* standby = new StandbyState();RunningState* running = new RunningState(standby);ChannelSelectionState* channelSel = new ChannelSelectionState(running);VolumeControlState* volumeCtrl = new VolumeControlState(running);// 構建層次結構running->setParent(standby);channelSel->setParent(running);volumeCtrl->setParent(running);// 創建狀態機并啟動HSM hsm;hsm.start(standby);// 處理事件hsm.processEvent(HSMUtilityDef::POWER); // 切換到運行模式hsm.processEvent(HSMUtilityDef::CHANNEL_UP); // 選擇頻道向上hsm.processEvent(HSMUtilityDef::MODE_SWITCH); // 切換到鎖定模式hsm.processEvent(HSMUtilityDef::VOLUME_UP); // 增加音量hsm.processEvent(HSMUtilityDef::POWER); // 關閉電視return a.exec();
}
4. 使用 Qt 的信號和槽機制增強狀態機
Qt 提供了強大的信號和槽機制,可以用來進一步簡化狀態機的設計和實現。以下是如何將 Qt 的信號和槽與層次狀態機結合使用的方法。
修改 HSMState
類以支持信號和槽
在 HSMState
中添加信號來通知狀態轉換或動作執行:
#include <QObject>
#include <QVector>
#include <QDebug>namespace HSMUtilityDef {typedef uint32_t HSM_EVENT;// 定義常見事件const uint32_t MAX_DEPTH = 5;const HSM_EVENT HSME_NULL = 0;const HSM_EVENT HSME_START = 1;const HSM_EVENT HSME_INIT = static_cast<HSM_EVENT>(-3);const HSM_EVENT HSME_ENTRY = static_cast<HSM_EVENT>(-2);const HSM_EVENT HSME_EXIT = static_cast<HSM_EVENT>(-1);// 自定義事件const HSM_EVENT POWER = 100;const HSM_EVENT CHANNEL_UP = 101;const HSM_EVENT CHANNEL_DOWN = 102;const HSM_EVENT VOLUME_UP = 103;const HSM_EVENT VOLUME_DOWN = 104;const HSM_EVENT MODE_SWITCH = 105;
}class HSMState : public QObject {Q_OBJECTpublic:explicit HSMState(HSMState *parent = nullptr) : QObject(parent), m_parent(parent) {}virtual ~HSMState() {}// 處理事件的主要接口virtual HSMUtilityDef::HSM_EVENT handleEvent(HSMUtilityDef::HSM_EVENT event, void* param = nullptr) {switch (event) {case HSMUtilityDef::HSME_ENTRY:onEntry(param);break;case HSMUtilityDef::HSME_EXIT:onExit(param);break;default:return unhandledEvent(event, param);}return HSMUtilityDef::HSME_NULL;}signals:// 信號用于通知狀態轉換或動作執行void stateEntered(HSMState* state);void stateExited(HSMState* state);protected:// 進入狀態時執行的動作virtual void onEntry(void* param) {emit stateEntered(this);}// 離開狀態時執行的動作virtual void onExit(void* param) {emit stateExited(this);}// 處理未定義事件的方法virtual HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) {qDebug() << "Unhandled event" << event;return event;}// 獲取父狀態HSMState* parent() const { return m_parent; }private:HSMState *m_parent;
};