用C語言實現有限狀態自動機FSM


摘要:狀態機模式是一種行為模式,在《設計模式》這本書中對其有詳細的描述,通過多態實現不同狀態的調轉行為的確是一種很好的方法,只可惜在嵌入式環境下,有時只能寫純C代碼,並且還需要考慮代碼的重入和多任務請求跳轉等情形,因此實現起來着實需要一番考慮。本文主要為你實現一個簡單的有限狀態機,沒有考慮代碼的重入和多任務跳轉,為以后復雜的狀態機實現,打下基礎。

 本文來源:用C語言實現有限狀態自動機FSM


一、狀態機實現的要素


首先,分析一下一個普通的狀態機究竟要實現哪些內容。

狀態機存儲從開始時刻到現在的變化,並根據當前輸入,決定下一個狀態。這意味着,狀態機要存儲狀態、獲得輸入(我們把它叫做跳轉條件)、做出響應。

image

如上圖所示,{s1, s2, s3}均為狀態,箭頭c1/a1表示在s1狀態、輸入為c1時,跳轉到s2,並進行a1操作。

最下方為一組輸入,狀態機應做出如下反應:

當前狀態 輸入 下一個狀態 動作
s1 c1 s2 a1
s2 c2 s3 a2
s3 c1 s2 a3
s2 c2 s3 a2
s3 c1 s2 a3
s2 c1 s_trap a_trap
s_trap c1 s_trap a_trap

 

當某個狀態遇到不能識別的輸入時,就默認進入陷阱狀態,在陷阱狀態中,不論遇到怎樣的輸入都不能跳出。

為了表達上面這個自動機,我們定義它們的狀態和輸入類型:

1
2
3
4
5
6
7
8
9
10
11
12
typedef  int s tate;
typedef  int c ondition;
 
#define STATES 4
#define STATE1 0
#define STATE2 1
#define STATE3 2
#define STATETRAP 3
 
#define CONDITIONS 2
#define CONDITION1 0
#define CONDITION2 1

 總結一下,我們需要定義的有狀態、輸入、行為(動作+下一個狀態),其中,行為的個數是“狀態數*輸入數量”(其中有一些是重復的);其中動作一般來說可以用一個函數指針來實現。


二、具體設計


       在嵌入式環境中,由於存儲空間比較小,因此把它們全部定義成宏。此外,為了降低執行時間的不確定性,我們使用O(1)的跳轉表來模擬狀態的跳轉。

首先定義跳轉類型:

1
2
3
4
5
6
7
typedef  void  (*actiontype)(state mystate, condition condition);
 
typedef  struct
{
    s tate next;
    a ctiontype action;
} trasition, * ptrasition;

 

然后按照上圖中的跳轉關系,把三個跳轉加一個陷阱跳轉先定義出來:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// (s1, c1, s2, a1)
trasition t1 = {
     STATE2,
     action1
};
 
// (s2, c2, s3, a2)
trasition t2 = {
     STATE3,
     action2
};
 
// (s3, c1, s2, a3)
trasition t3 = {
     STATE2,
     action3
};
 
// (s, c, trap, a1)
trasition tt = {
     STATETRAP,
     actiontrap
};

 

其中的動作,由用戶自己完成,在這里僅定義一條輸出語句。

1
2
3
4
void  action1(State state, Condition condition)
{
     printf ( "Action 1 triggered.\n" );
}
1
最后定義跳轉表:
1
2
3
4
5
6
7
pt rasition transition_table[STATES][CONDITIONS] = {
/*      c1,  c2*/
/* s1 */ &t1, &tt,
/* s2 */ &tt, &t2,
/* s3 */ &t3, &tt,
/* st */ &tt, &tt,
};

 

即可表達上文中的跳轉關系。

最后定義狀態機,如果不考慮多任務請求,那么狀態機僅需要存儲當前狀態便行了。例如:

1
2
3
4
5
6
7
8
9
10
11
12
typedef  struct
{
     State current;
} StateMachine, * pStateMachine;
 
State step(pStateMachine machine, Condition condition)
{
     pTrasition t = transition_table[machine->current][condition];
     (*(t->action))(machine->current, condition);
     machine->current = t->next;
     return  machine->current;
}

總結:我們現在設計實現好了一個狀態機,然后要給這個狀態機特定的輸入,看看狀態機的運轉情況,以上面圖中的那個狀態機為例,我們輸入的序列是0和1分別代表c1和C2,然后狀態s1,s2分別對應0,1.用程序實現這個內容如下

三、程序實現


程序清單:小型狀態機的實現
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
typedef int state;
typedef int condition;

#define STATENUM 4
#define STATE1 0
#define STATE2 1
#define STATE3 2
#define STATETRAP 3

#define CONDITIONS 2
#define CONDITION1 0
#define CONDITION2 1

typedef void (* actiontype)(state mystate,condition mycondition);
typedef struct{
  state next;
  actiontype action;
}trasition, *ptrasition;

void action1(state mystate,condition myconditon);
void action2(state mystate,condition myconditon);
void action3(state mystate,condition myconditon);
void actiontrap(state mystate,condition myconditon);
trasition t1={
  STATE2,action1
};
trasition t2={
  STATE3,action2
};
trasition t3={
  STATE2,action3
};
trasition tt={
  STATETRAP,actiontrap
};

void action1(state mystate,condition myconditon){
  printf("action1 one triggered\n");
}
void action2(state mystate,condition myconditon){
  printf("action2 one triggered\n");
}
void action3(state mystate,condition myconditon){
  printf("action3 one triggered\n");
}
void actiontrap(state mystate,condition myconditon){
  printf("actiontrap one triggered\n");
}

ptrasition transition_table[STATENUM][CONDITIONS] = {
/*      c1,  c2*/
/* s1 */&t1, &tt,
/* s2 */&tt, &t2,
/* s3 */&t3, &tt,
/* st */&tt, &tt,
};
typedef struct
{
    state current;
} StateMachine, * pStateMachine;
 
state step(pStateMachine machine, condition mycondition)
{
    ptrasition t = transition_table[machine->current][mycondition];
    (*(t->action))(machine->current, mycondition);
    machine->current = t->next;
    printf("the current state is %d\n",t->next );
    return machine->current;
}
int main(int argc, char *argv[])
{
  StateMachine mymachine;
  mymachine.current=STATE1;
  int mycon;
  char ch;
  while(1){
    scanf("%d",&mycon); 
    step(&mymachine,mycon);
  }
  return 0;
}

程序輸入與輸出結果示例:

四、外部參考

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM