為實用的軟件系統(tǒng)編寫狀態(tài)機(jī)并不是一件十分輕松的事情,特別是當(dāng)狀態(tài)機(jī)本身比較復(fù)雜的時候尤其如此,許多有過類似經(jīng)歷的程序員往往將其形容為"毫無創(chuàng)意"的過程,因為他們需要將大量的時間與精力傾注在如何管理好狀態(tài)機(jī)中的各種狀態(tài)上,而不是程序本身的運(yùn)行邏輯。
作為一種通用的軟件設(shè)計模式,各種軟件系統(tǒng)的狀態(tài)機(jī)之間肯定會或多或少地存在著一些共性,因此人們開始嘗試開發(fā)一些工具來自動生成有限狀態(tài)機(jī)的框架代碼,而在Linux下就有一個挺不錯的選擇──FSME(Finite State Machine Editor)。
可視化的FSME
FSME是一個基于Qt的有限狀態(tài)機(jī)工具,它能夠讓用戶通過圖形化的方式來對程序中所需要的狀態(tài)機(jī)進(jìn)行建模,并且還能夠自動生成用C++或者Python實現(xiàn)的狀態(tài)機(jī)框架代碼。
下面就以下圖中城門的狀態(tài)機(jī)為例,來介紹如何利用FSME來自動生成程序中所需要的狀態(tài)機(jī)代碼。
控制城門的狀態(tài)機(jī)
1狀態(tài)機(jī)建模
首先運(yùn)行fsme命令來啟動狀態(tài)機(jī)編輯器,然后單擊工具欄上的"New"按鈕來創(chuàng)建一個新的狀態(tài)機(jī)。FSME中用于構(gòu)建狀態(tài)機(jī)的基本元素一共有五種:事件(Event)、輸入(Input)、輸出(Output)、狀態(tài)(State)和轉(zhuǎn)換(Transition),在界面左邊的樹形列表中可以找到其中的四種。
- 狀態(tài)建模
在FSME界面左邊的樹形列表中選擇"States"項,然后按下鍵盤上的Insert鍵來插入一個新的狀態(tài),接著在右下方的"Name"文本框中輸入狀態(tài)的名稱,再在右上方的繪圖區(qū)域單擊該狀態(tài)所要放置的位置,一個新的狀態(tài)就創(chuàng)建好了。用同樣的辦法可以添加狀態(tài)機(jī)所需要的所有狀態(tài),如下圖所示。
狀態(tài)建模
- 事件建模
在FSME界面左邊的樹形列表中選擇"Events"項,然后按下鍵盤上的Insert鍵來添加一個新的事件,接著在右下方的"Name"文本框中輸入事件的名稱,再單擊"Apply"按鈕,一個新的事件就創(chuàng)建好了。用同樣的辦法可以添加狀態(tài)機(jī)所需要的所有事件,如下圖所示。
- 轉(zhuǎn)換建模
狀態(tài)轉(zhuǎn)換是整個建模過程中最重要的一個部分,它用來定義有限狀態(tài)機(jī)中的一個狀態(tài)是如何切換到另一個狀態(tài)的。例如,當(dāng)用來控制城門的狀態(tài)機(jī)處于Opened狀態(tài)時,如果此時有Close事件產(chǎn)生,那么狀態(tài)機(jī)的當(dāng)前狀態(tài)將切換到Closed狀態(tài),這樣一個完整的過程在狀態(tài)機(jī)模型中可以用closeDoor這樣一個轉(zhuǎn)換來進(jìn)行描述。
要在FSME中添加這樣一個轉(zhuǎn)換,首先需要在界面左邊的樹形列表中選擇"States"下的"Opened"項,然后按下鍵盤上的Insert鍵來添加一個新的轉(zhuǎn)換,接著在右下角的"Name"文本框中輸入轉(zhuǎn)換的名字"closeDoor",在"Condition"文本框中輸入"Close"表明觸發(fā)該轉(zhuǎn)換的條件是事件Close的產(chǎn)生,在"Target"下拉框中選擇"Closed"項表明該轉(zhuǎn)換發(fā)生后狀態(tài)機(jī)將被切換到Closed狀態(tài),最后再單擊"Apply"按鈕,一個新的狀態(tài)轉(zhuǎn)換關(guān)系就定義好了,如下圖所示。用同樣的辦法可以添加狀態(tài)機(jī)所需要的所有轉(zhuǎn)換。
轉(zhuǎn)換建模
2 生成狀態(tài)機(jī)框架
使用FSME不僅能夠進(jìn)行可視化的狀態(tài)機(jī)建模,更重要的是它還可以根據(jù)得到的模型自動生成用C++或者Python實現(xiàn)的狀態(tài)機(jī)框架。首先在FSME界面左邊的樹形列表中選擇"Root"項,然后在右下角的"Name"文本框中輸入狀態(tài)機(jī)的名字"DoorFSM",再從"Initial State"下拉列表中選擇狀態(tài)"Opened"作為狀態(tài)機(jī)的初始化狀態(tài),如圖6所示。
設(shè)置初始屬性
在將狀態(tài)機(jī)模型保存為door.fsm文件之后,使用下面的命令可以生成包含有狀態(tài)機(jī)定義的頭文件:
[xiaowp@linuxgam code]$ fsmc door.fsm -d -o DoorFSM.h
進(jìn)一步還可以生成包含有狀態(tài)機(jī)實現(xiàn)的框架代碼:
[xiaowp@linuxgam code]$ fsmc door.fsm -d -impl DoorFSM.h -o DoorFSM.cpp
如果想對生成的狀態(tài)機(jī)進(jìn)行驗證,只需要再手工編寫一段用于測試的代碼就可以了:
/* * TestFSM.cpp * 測試生成的狀態(tài)機(jī)框架 */ #include "DoorFSM.h" int main() { DoorFSM door; door.A(DoorFSM::Close); door.A(DoorFSM::Lock); door.A(DoorFSM::Unlock); door.A(DoorFSM::Open); }
有限狀態(tài)機(jī)是由事件來進(jìn)行驅(qū)動的,在FSME生成的狀態(tài)機(jī)框架代碼中,方法A()可以被用來向狀態(tài)機(jī)發(fā)送相應(yīng)的事件,從而提供狀態(tài)機(jī)正常運(yùn)轉(zhuǎn)所需要的"動力"。狀態(tài)機(jī)負(fù)責(zé)在其內(nèi)部維護(hù)一個事件隊列,所有到達(dá)的事件都會先被放到事件隊列中進(jìn)行等候,從而能夠保證它們將按照到達(dá)的先后順序被依次處理。在處理每一個到達(dá)的事件時,狀態(tài)機(jī)都會根據(jù)自己當(dāng)前所處的狀態(tài),檢查與該狀態(tài)對應(yīng)的轉(zhuǎn)換條件是否已經(jīng)被滿足,如果滿足的話則激活相應(yīng)的狀態(tài)轉(zhuǎn)換過程。
使用下面的命令能夠?qū)⑸傻臓顟B(tài)機(jī)框架和測試代碼編譯成一個可執(zhí)行文件:
[xiaowp@linuxgam code]$ g++ DoorFSM.cpp TestFSM.cpp -o fsm
由于之前在用fsmc命令生成狀態(tài)機(jī)代碼時使用了-d選項,生成的狀態(tài)機(jī)框架中會包含一定的調(diào)試信息,包括狀態(tài)機(jī)中每次狀態(tài)轉(zhuǎn)換時的激活事件、轉(zhuǎn)換前的狀態(tài)、所經(jīng)歷的轉(zhuǎn)換、轉(zhuǎn)換后的狀態(tài)等,如下所示:
[xiaowp@linuxgam code]$ ./fsm DoorFSM'Close' DoorFSM'Opened' DoorFSM'closeDoor' DoorFSM:new state:'Closed' DoorFSM'Lock' DoorFSM'Closed' DoorFSM'lockDoor' DoorFSM:new state:'Locked' DoorFSM'Unlock' DoorFSM'Locked' DoorFSM'unlockDoor' DoorFSM:new state:'Unlocked' DoorFSM'Open' DoorFSM'Unlocked' DoorFSM'openDoor' DoorFSM:new state:'Opened'
3定制狀態(tài)機(jī)
目前得到的狀態(tài)機(jī)已經(jīng)能夠響應(yīng)來自外部的各種事件,并適當(dāng)?shù)卣{(diào)整自己當(dāng)前所處的狀態(tài),也就是說已經(jīng)實現(xiàn)了狀態(tài)機(jī)引擎的功能,接下來要做的就是根據(jù)應(yīng)用的具體需求來進(jìn)行定制,為狀態(tài)機(jī)加入與軟件系統(tǒng)本身相關(guān)的那些處理邏輯。在FSME中,與具體應(yīng)用相關(guān)的操作稱為輸出(Output),它們實際上就是一些需要用戶給出具體實現(xiàn)的虛函數(shù),自動生成的狀態(tài)機(jī)引擎負(fù)責(zé)在進(jìn)入或者退出某個狀態(tài)時調(diào)用它們。
仍然以控制城門的那個狀態(tài)機(jī)為例,假設(shè)我們希望在進(jìn)入每個狀態(tài)時都添加一部分處理邏輯。首在FSME界面左邊的樹形列表選擇"Outputs"項,然后按下鍵盤上的Insert鍵來添加一個新的輸出,接著在右下方的"Name"文本框中輸入相應(yīng)的名稱,再單擊"Apply"按鈕,一個新的輸出就創(chuàng)建好了,如圖所示。用同樣的辦法可以添加狀態(tài)機(jī)所需要的所有輸出。
添加輸出
當(dāng)所有的輸出都定義好之后,接下來就可以為狀態(tài)機(jī)中的每個狀態(tài)綁定相應(yīng)的輸出。首先在FSME界面左側(cè)的"States"項中選擇相應(yīng)的狀態(tài),然后從右下角的"Available"列表框中選擇與該狀態(tài)對應(yīng)的輸出,再單擊"<"按鈕將其添加到"In"列表中,如圖所示。用同樣的辦法可以為狀態(tài)機(jī)中的所有狀態(tài)設(shè)置相應(yīng)的輸出,同一個狀態(tài)可以對應(yīng)有多個輸出,其中In列表中的輸出會在進(jìn)入該狀態(tài)時被調(diào)用,而Out列表中的輸出則會在退出該狀態(tài)時被調(diào)用,輸出調(diào)用的順序是與其在In或者Out列表中的順序相一致的。
圖為狀態(tài)設(shè)置輸出
由于對狀態(tài)機(jī)模型進(jìn)行了修改,我們需要再次生成狀態(tài)機(jī)的框架代碼,不過這次不需要加上-d參數(shù):
[xiaowp@linuxgam code]$ fsmc door.fsm -o DoorFSM.h [xiaowp@linuxgam code]$ fsmc door.fsm -d -impl DoorFSM.h -o DoorFSM.cpp
我們在新的狀態(tài)機(jī)模型中添加了enterOpend、enterClosed、enterLocked和enterUnlocked四個輸出,因此生成的類DoorFSM中會包含如下幾個純虛函數(shù):
virtual void enterOpened() = 0; virtual void enterLocked() = 0; virtual void enterUnlocked() = 0; virtual void enterClosed() = 0;
顯然,此時生成的狀態(tài)機(jī)框架不能夠再被直接編譯了,我們必須從類DoorFSM派生出一個子類,并提供對這幾個純虛函數(shù)的具體實現(xiàn):
/* * DoorFSMLogic.h * 狀態(tài)機(jī)控制邏輯的頭文件 */ #include "DoorFSM.h" class DoorFSMLogic : public DoorFSM { protected: virtual void enterOpened(); virtual void enterLocked(); virtual void enterUnlocked(); virtual void enterClosed(); };
正如前面所提到過的,這幾個函數(shù)實際上代表的正是應(yīng)用系統(tǒng)的處理邏輯,作為例子我們只是簡單地輸出一些提示信息:
/* * DoorFSMLogic.cpp * 狀態(tài)機(jī)控制邏輯的實現(xiàn)文件 */ #include "DoorFSMLogic.h" #include
同樣,為了對生成的狀態(tài)機(jī)進(jìn)行驗證,我們還需要手工編寫一段測試代碼:
/* * TestFSM.cpp * 測試狀態(tài)機(jī)邏輯 */ #include "DoorFSMLogic.h" int main() { DoorFSMLogic door; door.A(DoorFSM::Close); door.A(DoorFSM::Lock); door.A(DoorFSM::Unlock); door.A(DoorFSM::Open); }
使用下面的命令能夠?qū)⑸傻臓顟B(tài)機(jī)框架和測試代碼編譯成一個可執(zhí)行文件:
[xiaowp@linuxgam code]$ g++ DoorFSM.cpp DoorFSMLogic.cpp TestLogic.cpp -o logic
運(yùn)行結(jié)果如下所示:
[xiaowp@linuxgam code]$ ./logic Enter Closed state. Enter Locked state. Enter Unlocked state. Enter Opened state.
-
Linux
+關(guān)注
關(guān)注
87文章
11207瀏覽量
208716 -
狀態(tài)機(jī)
+關(guān)注
關(guān)注
2文章
491瀏覽量
27456 -
軟件系統(tǒng)
+關(guān)注
關(guān)注
0文章
61瀏覽量
9478
原文標(biāo)題:一款狀態(tài)機(jī)自動生成工具
文章出處:【微信號:strongerHuang,微信公眾號:strongerHuang】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論