嵌入式軟件架構設計-分層狀態(tài)機
掃描二維碼
隨時隨地手機看文章
前言
之前介紹的狀態(tài)機是有限狀態(tài)機(FSM),這篇介紹一下分層狀態(tài)機(HSM)的理解。
分層狀態(tài)機是一種用于描述系統(tǒng)行為和控制流的模型,它將系統(tǒng)劃分為多個層級,并將每個層級表示為一個狀態(tài)機。每個狀態(tài)機描述了一個特定的狀態(tài)集合以及狀態(tài)之間的轉移規(guī)則。每個狀態(tài)機都有自己的輸入和輸出,可以與其他狀態(tài)機進行交互,從而形成整個系統(tǒng)的行為。
分層狀態(tài)機的設計有助于降低系統(tǒng)復雜度,并且能夠更好地組織系統(tǒng)的功能和行為。在分層狀態(tài)機中,每個狀態(tài)機負責處理一個特定的子任務或功能,這使得系統(tǒng)更易于理解和維護。
在分層狀態(tài)機中,高層狀態(tài)機負責協(xié)調和控制下層狀態(tài)機的行為。高層狀態(tài)機可以通過向下層狀態(tài)機發(fā)送命令或事件來觸發(fā)狀態(tài)轉換,下層狀態(tài)機可以將其處理結果返回給上層狀態(tài)機,以便上層狀態(tài)機作出相應的決策。
區(qū)別
分層狀態(tài)機(HSM)和有限狀態(tài)機(FSM)是兩種不同的模型,雖然它們都用于描述系統(tǒng)的行為和控制流,但它們之間還是存在一些區(qū)別的。
-
層次結構不同:分層狀態(tài)機是將系統(tǒng)劃分為多個層級,并將每個層級表示為一個狀態(tài)機,每個狀態(tài)機有自己的輸入和輸出,可以與其他狀態(tài)機進行交互,形成整個系統(tǒng)的行為。而有限狀態(tài)機則是單層的結構,描述系統(tǒng)在特定條件下的狀態(tài)和狀態(tài)之間的轉移。
-
狀態(tài)數(shù)量不同:分層狀態(tài)機中的狀態(tài)數(shù)量通常比有限狀態(tài)機更多。由于分層狀態(tài)機的每個層級都有自己的狀態(tài)集合,因此整個系統(tǒng)的狀態(tài)集合是各層狀態(tài)集合的并集。而有限狀態(tài)機只有一個狀態(tài)集合,狀態(tài)數(shù)量相對較少。
-
狀態(tài)之間的轉移規(guī)則不同:在分層狀態(tài)機中,狀態(tài)之間的轉移規(guī)則可以根據(jù)不同的層級進行定義,每個狀態(tài)機有自己的轉移規(guī)則。而有限狀態(tài)機的狀態(tài)之間的轉移規(guī)則通常是全局統(tǒng)一的。
-
功能不同:分層狀態(tài)機通常用于描述復雜系統(tǒng)的行為和控制流,其設計目的是為了降低系統(tǒng)的復雜度并更好地組織系統(tǒng)的功能和行為。而有限狀態(tài)機通常用于描述簡單的控制流程或者算法。
總之,分層狀態(tài)機和有限狀態(tài)機雖然都是描述系統(tǒng)行為和控制流的模型,但是它們的層次結構、狀態(tài)數(shù)量、狀態(tài)之間的轉移規(guī)則和應用場景等方面存在較大的不同
舉例說明
假設有一個自動售貨機,它需要根據(jù)用戶選擇的按鈕來售出對應的商品。我們可以使用有限狀態(tài)機和分層狀態(tài)機分別來實現(xiàn)這個自動售貨機。
首先,我們來看一下如何使用有限狀態(tài)機來實現(xiàn)自動售貨機。假設我們有三個商品可以售賣,分別是飲料、糖果和薯片,對應的按鈕分別為A、B和C。那么,我們可以定義以下狀態(tài):
初始狀態(tài):待命狀態(tài)
狀態(tài)1:等待用戶選擇按鈕
狀態(tài)2:出貨狀態(tài)
根據(jù)這些狀態(tài),我們可以設計狀態(tài)轉移規(guī)則如下:
待命狀態(tài):如果用戶按下按鈕A、B或C,則轉移到等待用戶選擇按鈕狀態(tài)。
等待用戶選擇按鈕狀態(tài):如果用戶按下按鈕A,則轉移到出貨狀態(tài)1;如果用戶按下按鈕B,則轉移到出貨狀態(tài)2;如果用戶按下按鈕C,則轉移到出貨狀態(tài)3。
出貨狀態(tài)1:出售飲料,并轉移到待命狀態(tài)。
出貨狀態(tài)2:出售糖果,并轉移到待命狀態(tài)。
出貨狀態(tài)3:出售薯片,并轉移到待命狀態(tài)。
流程圖:
接下來,我們來看一下如何使用分層狀態(tài)機來實現(xiàn)自動售貨機。首先,我們可以將自動售貨機分解成三個層次:選擇商品層、支付層和出貨層。每個層次都有自己的狀態(tài)和狀態(tài)轉移規(guī)則,如下所示:
選擇商品層:等待用戶選擇按鈕狀態(tài)
支付層:等待用戶支付狀態(tài)
出貨層:出貨狀態(tài)
然后,我們可以進一步細化每個層次的狀態(tài)和狀態(tài)轉移規(guī)則,如下所示:
選擇商品層:
初始狀態(tài):待命狀態(tài)
狀態(tài)1:等待用戶選擇按鈕
轉移規(guī)則:如果用戶按下按鈕A、B或C,則轉移到等待用戶支付狀態(tài),并保存用戶選擇的商品。
支付層:
初始狀態(tài):待命狀態(tài)
狀態(tài)1:等待用戶支付
轉移規(guī)則:如果用戶完成支付,則轉移到出貨狀態(tài),并發(fā)送出貨指令。
出貨層:
初始狀態(tài):待命狀態(tài)
狀態(tài)1:等待出貨指令
狀態(tài)2:正在出貨
轉移規(guī)則:如果接收到出貨指令,則轉移到正在出貨狀態(tài),并執(zhí)行出貨操作;如果出貨操作完成,則轉移到待命狀態(tài)。
流程圖:
需要注意的是,有限狀態(tài)機和分層狀態(tài)機都可以用于自動售貨機的設計,選擇哪種方法取決于具體的需求和復雜度。如果系統(tǒng)相對簡單,可以使用有限狀態(tài)機。如果需要管理多個子系統(tǒng)、處理多個事件和狀態(tài),可以考慮使用分層狀態(tài)機。
最重要的是,在實際應用中,理解分層狀態(tài)機的關鍵是正確地設計狀態(tài)轉移邏輯和事件處理函數(shù)。如果狀態(tài)轉移邏輯不正確,可能會導致狀態(tài)機無法正確響應事件;如果事件處理函數(shù)不正確,可能會導致狀態(tài)機無法正確處理事件。因此,在設計分層狀態(tài)機時,需要認真考慮狀態(tài)轉移和事件處理的邏輯,并進行充分的測試和驗證。
代碼參考
有限狀態(tài)機
有限狀態(tài)機可以使用switch/case或if/else語句實現(xiàn)狀態(tài)轉移,并在每個狀態(tài)轉移時執(zhí)行相應的操作。
#include#include enum State { ST_IDLE, ST_WAIT_SELECT, ST_WAIT_PAY, ST_DISPENSE }; enum Button { BTN_NONE, BTN_A, BTN_B, BTN_C }; enum State current_state = ST_IDLE; enum Button current_button = BTN_NONE; void update_fsm(enum Button button) { switch (current_state) { case ST_IDLE: if (button != BTN_NONE) { current_button = button; current_state = ST_WAIT_SELECT; printf("商品選擇: %c\n", button); } break; case ST_WAIT_SELECT: if (button == BTN_NONE) { current_state = ST_WAIT_PAY; printf("等待支付...\n"); } break; case ST_WAIT_PAY: if (button == BTN_NONE) { current_state = ST_DISPENSE; printf("出貨中...\n"); } break; case ST_DISPENSE: if (button == BTN_NONE) { current_state = ST_IDLE; printf("出售完成\n"); } break; } } int main() { update_fsm(BTN_NONE); update_fsm(BTN_A); update_fsm(BTN_NONE); update_fsm(BTN_NONE); update_fsm(BTN_NONE); update_fsm(BTN_NONE); return 0; }
分層狀態(tài)機
而分層狀態(tài)機中由于更加復雜,單純地采用switch/case或if/else語句已經不太適合不同狀態(tài)機及其子狀態(tài)的轉移,最好的方式是采用表驅動的方式去實現(xiàn),然后在數(shù)組表中定義每個狀態(tài)機及其子狀態(tài),并且有回調函數(shù)用來處理每個狀態(tài)機及其子狀態(tài)的處理等,提高系統(tǒng)的可維護性和可擴展性。
由于實現(xiàn)較為復雜,以下代碼僅供參考,不具體實現(xiàn)
#include#include #include // 狀態(tài)機狀態(tài)枚舉 typedef enum { STATE_IDLE, STATE_WAITING_FOR_COIN, STATE_WAITING_FOR_SELECTION, STATE_DISPENSING, STATE_COUNT } state_t; // 事件枚舉 typedef enum { EVENT_INSERT_COIN, EVENT_MAKE_SELECTION, EVENT_DISPENSE, EVENT_COUNT } event_t; // 狀態(tài)轉移條件函數(shù)類型定義 typedef bool (*condition_func_t)(void); // 狀態(tài)機狀態(tài)結構體定義 typedef struct { void (*enter_func)(void); // 進入狀態(tài)函數(shù) void (*exit_func)(void); // 退出狀態(tài)函數(shù) void (*poll_func)(void); // 事件輪詢處理函數(shù) condition_func_t (*cond_func)(void); // 轉移條件函數(shù) } state_info_t; // 狀態(tài)轉移表 static const int transition_table[STATE_COUNT][EVENT_COUNT] = { // EVENT_INSERT_COIN EVENT_MAKE_SELECTION EVENT_DISPENSE { STATE_WAITING_FOR_COIN, STATE_IDLE, STATE_IDLE }, // STATE_IDLE { STATE_WAITING_FOR_COIN, STATE_WAITING_FOR_SELECTION, STATE_IDLE }, // STATE_WAITING_FOR_COIN { STATE_WAITING_FOR_COIN, STATE_WAITING_FOR_SELECTION, STATE_DISPENSING }, // STATE_WAITING_FOR_SELECTION { STATE_WAITING_FOR_COIN, STATE_WAITING_FOR_SELECTION, STATE_IDLE } // STATE_DISPENSING }; // 狀態(tài)機狀態(tài)數(shù)組 static const state_info_t state_table[STATE_COUNT] = { // 進入狀態(tài)函數(shù) 退出狀態(tài)函數(shù) 事件輪詢處理函數(shù) 轉移條件函數(shù) { NULL, NULL, NULL, NULL }, // STATE_IDLE { wait_for_coin_enter, wait_for_coin_exit, wait_for_coin_poll, wait_for_coin }, // STATE_WAITING_FOR_COIN { wait_for_select_enter, wait_for_select_exit, wait_for_select_poll, waiting_for_select }, // STATE_WAITING_FOR_SELECTION { dispensing_enter, dispensing_exit, dispensing_poll, dispensing } // STATE_DISPENSING }; // 當前狀態(tài) static state_t current_state = STATE_IDLE;





