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





