有一个典型的状态机实现模式?(Is there a typical state machine im

2019-06-21 03:08发布

我们需要用C实现一个简单的状态机。
是一个标准的开关语句来最好的方法?
我们有一个当前状态(状态)和过渡的触发器。


switch(state)
{
  case STATE_1:
     state = DoState1(transition);
     break;
  case STATE_2:
     state = DoState2(transition);
     break;
}
...
DoState2(int transition)
{
   // Do State Work
   ...
   if(transition == FROM_STATE_2) {
     // New state when doing STATE 2 -> STATE 2
   }
   if(transition == FROM_STATE_1) {
    // New State when moving STATE 1 -> STATE 2
   }
   return new_state;
}

没有简单的状态机更好的办法

编辑:对于C ++,我认为加速状态图库可能是要走的路。 然而,它并不能帮助与C允许在C用例集中。

Answer 1:

我更喜欢使用的大多数状态机一台驱动方式:

typedef enum { STATE_INITIAL, STATE_FOO, STATE_BAR, NUM_STATES } state_t;
typedef struct instance_data instance_data_t;
typedef state_t state_func_t( instance_data_t *data );

state_t do_state_initial( instance_data_t *data );
state_t do_state_foo( instance_data_t *data );
state_t do_state_bar( instance_data_t *data );

state_func_t* const state_table[ NUM_STATES ] = {
    do_state_initial, do_state_foo, do_state_bar
};

state_t run_state( state_t cur_state, instance_data_t *data ) {
    return state_table[ cur_state ]( data );
};

int main( void ) {
    state_t cur_state = STATE_INITIAL;
    instance_data_t data;

    while ( 1 ) {
        cur_state = run_state( cur_state, &data );

        // do other program logic, run other state machines, etc
    }
}

这当然可以扩展到支持多个状态机等过渡动作可以被容纳,以及:

typedef void transition_func_t( instance_data_t *data );

void do_initial_to_foo( instance_data_t *data );
void do_foo_to_bar( instance_data_t *data );
void do_bar_to_initial( instance_data_t *data );
void do_bar_to_foo( instance_data_t *data );
void do_bar_to_bar( instance_data_t *data );

transition_func_t * const transition_table[ NUM_STATES ][ NUM_STATES ] = {
    { NULL,              do_initial_to_foo, NULL },
    { NULL,              NULL,              do_foo_to_bar },
    { do_bar_to_initial, do_bar_to_foo,     do_bar_to_bar }
};

state_t run_state( state_t cur_state, instance_data_t *data ) {
    state_t new_state = state_table[ cur_state ]( data );
    transition_func_t *transition =
               transition_table[ cur_state ][ new_state ];

    if ( transition ) {
        transition( data );
    }

    return new_state;
};

表驱动的方法更容易维护和扩展和简单映射到状态图。



Answer 2:

你可能已经看到我的回答,我提到FSM其它C的问题! 这是我如何做到这一点:

FSM {
  STATE(x) {
    ...
    NEXTSTATE(y);
  }

  STATE(y) {
    ...
    if (x == 0) 
      NEXTSTATE(y);
    else 
      NEXTSTATE(x);
  }
}

随着定义下面的宏

#define FSM
#define STATE(x)      s_##x :
#define NEXTSTATE(x)  goto s_##x

这可以被修改,以适应具体情况。 例如,你可能有一个文件FSMFILE要驾驶你的FSM,所以你可以结合读下一个字符到宏观的行动本身:

#define FSM
#define STATE(x)         s_##x : FSMCHR = fgetc(FSMFILE); sn_##x :
#define NEXTSTATE(x)     goto s_##x
#define NEXTSTATE_NR(x)  goto sn_##x

现在你有两个类型的转换效果:一个人去的状态,读一个新的角色,其他的都到了状态,不消耗任何输入。

您也可以像自动化EOF的处理:

#define STATE(x)  s_##x  : if ((FSMCHR = fgetc(FSMFILE) == EOF)\
                             goto sx_endfsm;\
                  sn_##x :

#define ENDFSM    sx_endfsm:

这种方法的好处是,你可以直接翻译你画一个状态图,形成工作代码,相反,你可以很容易地画出从代码的状态图。

在其它技术中用于实现FSM的转换的结构被埋藏在控制结构(而如果,开关......)和由变量值(tipically受控state变量),并且它可以是一个复杂的任务,以涉及漂亮图来一个令人费解的代码。

我知道了这种技术从物品上出现,不幸的是,不再发布伟大的“计算机语言”杂志上。



Answer 3:

我也有使用的表的方法。 然而,有开销。 为什么存储指针的第二列表? 在C A功能,而不()是一个常量指针。 所以,你可以这样做:

struct state;
typedef void (*state_func_t)( struct state* );

typedef struct state
{
  state_func_t function;

  // other stateful data

} state_t;

void do_state_initial( state_t* );
void do_state_foo( state_t* );
void do_state_bar( state_t* );

void run_state( state_t* i ) {
    i->function(i);
};

int main( void ) {
    state_t state = { do_state_initial };

    while ( 1 ) {
        run_state( state );

        // do other program logic, run other state machines, etc
    }
}

当然,这取决于你的恐惧因子(即安全VS速度)你可能要检查有效指针。 对于状态机比3点左右的状态时,上面的方法应该是比同等的开关或餐桌的方法更少的指令。 你甚至可以作为宏观IZE:

#define RUN_STATE(state_ptr_) ((state_ptr_)->function(state_ptr_))

另外,我从OP的例子觉得,有应该考虑/设计状态机时做了简化。 我不知道事情的过渡状态应该用于逻辑。 每个状态的功能应该能够不过去的状态(S)的显性知识发挥其给定的角色。 基本上你设计了如何从你到另一个状态的状态转变。

最后,不要启动基于“功能性”的界限状态机的设计,使用该子功能。 取而代之的划分依据,当你将不得不等待事情发生,然后才能继续的状态。 这将有助于减少你有你得到的结果之前运行的状态机的次数。 写I / O功能时,这是很重要的,或中断处理。

此外,经典的switch语句的一些优点和缺点:

优点:

  • 它是在语言,因此它被记录,并明确
  • 状态定义,他们被称为地方
  • 可以在一个函数调用执行多国
  • 通用于所有国家代码之前和switch语句后执行

缺点:

  • 可以在一个函数调用执行多国
  • 通用于所有国家代码之前和switch语句后执行
  • 切换的实现可能会很慢

请注意,都是正反两个属性。 我认为开关允许国家之间太多共享的机会,与各国之间的相互依赖性会变得失控。 然而,对于小数量的状态,也可能是最可读的和可维护性。



Answer 4:

对于一个简单的状态机只使用一个开关语句,并为您的状态枚举类型。 不要根据您输入的开关语句内的转换。 在实际的程序,你会明显改变,“如果(输入)”来检查您的转换点。 希望这可以帮助。

typedef enum
{
    STATE_1 = 0,
    STATE_2,
    STATE_3
} my_state_t;

my_state_t state = STATE_1;

void foo(char input)
{
    ...
    switch(state)
    {
        case STATE_1:
            if(input)
                state = STATE_2;
            break;
        case STATE_2:
            if(input)
                state = STATE_3;
            else
                state = STATE_1;
            break;
        case STATE_3:
            ...
            break;
    }
    ...
}


Answer 5:

还存在的逻辑格这是因为状态机变得更大更易于维护



Answer 6:

在Martin Fowler的UML精粹 ,他说在第10章状态机图(重点煤矿)(没有双关语意):

嵌套开关状态模式状态表 :一个状态图可以以三种主要方式来实现。

让我们使用手机的显示状态的一个简单的例子:

嵌套开关

福勒给了C#代码的例子,但我已经适应它我的例子。

public void HandleEvent(PhoneEvent anEvent) {
    switch (CurrentState) {
    case PhoneState.ScreenOff:
        switch (anEvent) {
        case PhoneEvent.PressButton:
            if (powerLow) { // guard condition
                DisplayLowPowerMessage(); // action
                // CurrentState = PhoneState.ScreenOff;
            } else {
                CurrentState = PhoneState.ScreenOn;
            }
            break;
        case PhoneEvent.PlugPower:
            CurrentState = PhoneState.ScreenCharging;
            break;
        }
        break;
    case PhoneState.ScreenOn:
        switch (anEvent) {
        case PhoneEvent.PressButton:
            CurrentState = PhoneState.ScreenOff;
            break;
        case PhoneEvent.PlugPower:
            CurrentState = PhoneState.ScreenCharging;
            break;
        }
        break;
    case PhoneState.ScreenCharging:
        switch (anEvent) {
        case PhoneEvent.UnplugPower:
            CurrentState = PhoneState.ScreenOff;
            break;
        }
        break;
    }
}

State模式

下面是我用GoF的国家图案实例的实现:

状态表

从福勒获得灵感,这里是我的例子表:

Source State    Target State    Event         Guard        Action
--------------------------------------------------------------------------------------
ScreenOff       ScreenOff       pressButton   powerLow     displayLowPowerMessage  
ScreenOff       ScreenOn        pressButton   !powerLow
ScreenOn        ScreenOff       pressButton
ScreenOff       ScreenCharging  plugPower
ScreenOn        ScreenCharging  plugPower
ScreenCharging  ScreenOff       unplugPower

对照

嵌套开关保持所有的逻辑在一个地方,但代码可能难以阅读的时候有很多的状态和转移。 这可能是更安全,更容易比其他方法(没有多态性或解释)来验证。

状态模式实现潜在的传播逻辑在几个不同的类别,这可能使理解它作为一个整体的问题。 在另一方面,小班容易理解分开。 如果您通过添加或删除的转变改变行为,因为他们是在层次结构的方法和可能有很多修改代码的设计显得尤为脆弱。 如果你住的小界面的设计原则,你会看到这种模式并没有真正做的那么好。 但是,如果状态机是稳定的,那么将不再需要这样的变化。

状态表方法需要写一些一种翻译的内容(如果你有在你使用的语言反映,这可能是更容易),这可能是很多工作要做在前面。 由于福勒指出,如果你的表是从您的代码分开,你可以修改你的软件的行为,而无需重新编译。 这有一定的安全隐患,但是, 该软件是基于外部文件的内容表现。

编辑(不是真正为C语言)

有一个流畅的接口(又称内部领域特定语言)的做法,太,这可能是由具有语言促进了一流的功能 。 在无状态库是否存在,以及博客显示了代码的简单例子。 一个Java实现(预Java8)进行了讨论。 我被示出的在GitHub Python的例子为好。



Answer 7:

对于简单的情况下,您可以在您的开关式的方法。 我发现,在过去行之有效的应对转变太:

static int current_state;    // should always hold current state -- and probably be an enum or something

void state_leave(int new_state) {
    // do processing on what it means to enter the new state
    // which might be dependent on the current state
}

void state_enter(int new_state) {
    // do processing on what is means to leave the current atate
    // might be dependent on the new state

    current_state = new_state;
}

void state_process() {
    // switch statement to handle current state
}

我不知道boost库什么,但是这种类型的方法是死的简单,不需要任何外部依赖,并且很容易实现。



Answer 8:

开关()是用C实现的状态机的强大和标准的方式,但它可以减少维护下来,如果你有大量的状态。 另一种常见的方法是使用函数指针存储的下一个状态。 这个简单的例子实现了一个置位/复位触发器:

/* Implement each state as a function with the same prototype */
void state_one(int set, int reset);
void state_two(int set, int reset);

/* Store a pointer to the next state */
void (*next_state)(int set, int reset) = state_one;

/* Users should call next_state(set, reset). This could
   also be wrapped by a real function that validated input
   and dealt with output rather than calling the function
   pointer directly. */

/* State one transitions to state one if set is true */
void state_one(int set, int reset) {
    if(set)
        next_state = state_two;
}

/* State two transitions to state one if reset is true */
void state_two(int set, int reset) {
    if(reset)
        next_state = state_one;
}


Answer 9:

我发现了edx.org当然嵌入式系统一个非常漂亮的C实现摩尔定律FSM的 - 塑造世界UTAustinX - UT.6.02x,第10章,由乔纳森Valvano和Ramesh Yerraballi ....

struct State {
  unsigned long Out;  // 6-bit pattern to output
  unsigned long Time; // delay in 10ms units 
  unsigned long Next[4]; // next state for inputs 0,1,2,3
}; 

typedef const struct State STyp;

//this example has 4 states, defining constants/symbols using #define
#define goN   0
#define waitN 1
#define goE   2
#define waitE 3


//this is the full FSM logic coded into one large array of output values, delays, 
//and next states (indexed by values of the inputs)
STyp FSM[4]={
 {0x21,3000,{goN,waitN,goN,waitN}}, 
 {0x22, 500,{goE,goE,goE,goE}},
 {0x0C,3000,{goE,goE,waitE,waitE}},
 {0x14, 500,{goN,goN,goN,goN}}};
unsigned long currentState;  // index to the current state 

//super simple controller follows
int main(void){ volatile unsigned long delay;
//embedded micro-controller configuration omitteed [...]
  currentState = goN;  
  while(1){
    LIGHTS = FSM[currentState].Out;  // set outputs lines (from FSM table)
    SysTick_Wait10ms(FSM[currentState].Time);
    currentState = FSM[currentState].Next[INPUT_SENSORS];  
  }
}


Answer 10:

你可能想看看在自由人 FSM生成软件。 从状态描述语言和/或您可以生成C,C ++,Java和许多其他代码(窗口)状态图编辑器...加上漂亮的文档和图表。 源和二进制文件iMatix



Answer 11:

本文是一个很好的用于状态模式(尽管它是C ++,没有特别的C)。

如果你可以把你的手放在书“ Head First设计模式 ”,说明和示例是很清楚的。



Answer 12:

我最喜欢的模式是国家的设计模式。 响应或行为不同,以同一个给定的输入。
一个使用开关/ case语句的状态机的一个问题是,当你创造更多的状态,开关/件变得更难/笨拙的读/维持,促进无组织的面条代码,并且越来越难以更改,恕不打破东西。 我发现使用设计模式可以帮助我整理我的数据更好,这是一个抽象的整点。 相反周围什么状态设计你的状态代码的你来的,而不是组织你的代码,这样,当你进入一个新的状态,它记录的状态。 这样,你得到有效的先前状态的记录。 我喜欢@ JoshPetit的回答,并采取了他的解决方案一步,从GoF的书直取:

stateCtxt.h:

#define STATE (void *)
typedef enum fsmSignal
{
   eEnter =0,
   eNormal,
   eExit
}FsmSignalT;

typedef struct fsm 
{
   FsmSignalT signal;
   // StateT is an enum that you can define any which way you want
   StateT currentState;
}FsmT;
extern int STATECTXT_Init(void);
/* optionally allow client context to set the target state */
extern STATECTXT_Set(StateT  stateID);
extern void STATECTXT_Handle(void *pvEvent);

stateCtxt.c:

#include "stateCtxt.h"
#include "statehandlers.h"

typedef STATE (*pfnStateT)(FsmSignalT signal, void *pvEvent);

static FsmT      fsm;
static pfnStateT UsbState ;

int STATECTXT_Init(void)
{    
    UsbState = State1;
    fsm.signal = eEnter;
    // use an enum for better maintainability
    fsm.currentState = '1';
    (*UsbState)( &fsm, pvEvent);
    return 0;
}

static void ChangeState( FsmT *pFsm, pfnStateT targetState )
{
    // Check to see if the state has changed
    if (targetState  != NULL)
    {
        // Call current state's exit event
        pFsm->signal = eExit;
        STATE dummyState = (*UsbState)( pFsm, pvEvent);

        // Update the State Machine structure
        UsbState = targetState ;

        // Call the new state's enter event
        pFsm->signal = eEnter;            
        dummyState = (*UsbState)( pFsm, pvEvent);
    }
}

void STATECTXT_Handle(void *pvEvent)
{
    pfnStateT newState;

    if (UsbState != NULL)
    {
         fsm.signal = eNormal;
         newState = (*UsbState)( &fsm, pvEvent );
         ChangeState( &fsm, newState );
    }        
}


void STATECTXT_Set(StateT  stateID)
{
     prevState = UsbState;
     switch (stateID) 
     {
         case '1':               
            ChangeState( State1 );
            break;
          case '2':
            ChangeState( State2);
            break;
          case '3':
            ChangeState( State3);
            break;
     }
}

statehandlers.h:

/* define state handlers */
extern STATE State1(void);
extern STATE State2(void);
extern STATE State3(void);

statehandlers.c:

#include "stateCtxt.h:"

/* Define behaviour to given set of inputs */
STATE State1(FsmT *fsm, void *pvEvent)
{   
    STATE nextState;
    /* do some state specific behaviours 
     * here
     */
    /* fsm->currentState currently contains the previous state
     * just before it gets updated, so you can implement behaviours 
     * which depend on previous state here
     */
    fsm->currentState = '1';
    /* Now, specify the next state
     * to transition to, or return null if you're still waiting for 
     * more stuff to process.  
     */
    switch (fsm->signal)
    {
        case eEnter:
            nextState = State2;
            break;
        case eNormal:
            nextState = null;
            break;
        case eExit:
            nextState = State2;
            break;
    }

    return nextState;
}

STATE  State3(FsmT *fsm, void *pvEvent)
{
    /* do some state specific behaviours 
     * here
     */
    fsm->currentState = '2';
    /* Now, specify the next state
     * to transition to
     */
     return State1;
}

STATE   State2(FsmT *fsm, void *pvEvent)
{   
    /* do some state specific behaviours 
     * here
     */
    fsm->currentState = '3';
    /* Now, specify the next state
     * to transition to
     */
     return State3;
}

对于大多数国家机器,ESP。 有限状态机,每个国家都知道它的下一个状态应该是什么,并为过渡到它的下一个状态的标准。 对于松散状态的设计,这可能不是这样的情况,因此,以暴露API用于转变状态的选项。 如果您需要更抽象,每个状态处理程序可以被分离成它自己的文件,这相当于在GoF的书中的具体状态处理程序。 如果你的设计是,只有少数几个州简单的,那么这两个stateCtxt.c和statehandlers.c可以组合成简单的单个文件。



Answer 13:

在使用“开关”的语句我的经验是处理多个可能状态的标准方式。 虽然我surpirsed你传递一个过渡值到每个国家的处理。 我想到了一个状态机的整个观点是,每个状态进行一个动作。 然后进行下一个动作/输入确定哪些新的状态过渡到。 所以,我本来期望每个状态处理功能立即执行任何固定的进入状态,如果转变是需要另一种状态之后再决定。



Answer 14:

有一个标题为一本书实用状态图中的C / C ++ 。 然而,这是重量级了我们所需要的。



Answer 15:

对于编译器支持__COUNTER__ ,你可以用它们的简单(但大)状态mashines。

  #define START 0      
  #define END 1000

  int run = 1;
  state = START;    
  while(run)
  {
    switch (state)
    {
        case __COUNTER__:
            //do something
            state++;
            break;
        case __COUNTER__:
            //do something
            if (input)
               state = END;
            else
               state++;
            break;
            .
            .
            .
        case __COUNTER__:
            //do something
            if (input)
               state = START;
            else
               state++;
            break;
        case __COUNTER__:
            //do something
            state++;
            break;
        case END:
            //do something
            run = 0;
            state = START;
            break;
        default:
            state++;
            break;
     } 
  } 

使用的优点__COUNTER__而不是硬编码的数字是,你可以在其他国家的中间增加状态,而不会重编每次的一切。 如果编译器支持犯规__COUNTER__ ,以有限的方式其更多钞票与预防使用__LINE__



Answer 16:

在C ++中,考虑到状态模式 。



Answer 17:

你的问题是类似“有一个典型的数据库实现模式”? 答案取决于你想要什么来实现呢? 如果你想实现一个更大的确定性状态机,你可以使用一个模型和一个状态机生成。 例如可以在www.StateSoft.org观看 - SM画廊。 雅努什Dobrowolski



Answer 18:

在C语言中,对于简单的机器这样的事情是什么,我通常最终会使用。

事件驱动FSM是由当前状态和事件定义的动作(FsmAction)相关联的状态的对象(FsmState)和过渡(FsmEdge)中所述。

它还提供了存储和传递既FSM和用户数据,以分离FSM-结合和用户绑定信息,并允许在同一FSM的多个实例(即,使用相同的描述,但通过不同的用户数据)。

事件由一个整数类型(FsmEvent)表示。 负值被实施预留允许特殊的常见事件(复位,无,任何,空,结束)。 非负性事件是用户定义的。

为简单起见,转换是在阵列中列出和匹配试图在数组顺序,基本上提供过渡的优先级。 它们具有可选的保护功能。 下一状态既可以在过渡列表或由跳转函数直接指示,这种方式提供了更多的灵活性使得能够动态FSM行为。

在过渡的描述,A NULL当前状态将匹配任何状态和通配符事件(任何)将匹配的任何事件。 无论如何,触发转换的事件的实际值将被传递到跳跃和保护功能。

对于复杂的FSM,简单的边缘阵列溶液可能变得太低效的。 在这种情况下,一个适当的跳转函数可以使用转换成转换矩阵或状态邻接列表的边缘阵列和事件条目来实现。

国家行为是为了与国家条目(回车),状态退出(离开)和状态(状态)操作之间辨别折返功能来实现。 这样的本地状态信息可以被封装和静态函数变量保存。

通常情况下,国家出入境行动将unremarkably执行并返回没有新的事件(无)。 如果不是这样,新的事件被捕获并立即返回。 这将有效地防止在情况下,它退出当前状态下发生的转变。

该FSM阶梯函数(fsmStep)将执行FSM的单个步骤,使用一个新的事件来触发的过渡,或任何情况下(无)执行当前状态的合状态动作。 阶梯函数返回一个可以被处理或重新馈送到FSM一个新的发射的事件; 或无,清空和结束在任何情况下的情况下,过渡未找到或端状态分别达到。

#ifndef FSM_H_
#define FSM_H_

#include <stdbool.h>
#include <stdint.h>

/** FSM enum type */
typedef enum
{
    // Events and return values
    fsm_User = 0, ///< User events start with this id
    fsm_Reset = -1, ///< Reset event
    fsm_None = -2, ///< No event
    fsm_Any = -3, ///< Any event, used as a wildcard
    fsm_Empty = -4, ///< No transition found for event
    fsm_End = -5, ///< Final state event generated when FSM reaches end state, or stop processing when used in transition

    // Action types
    fsm_Enter = 0, ///< Entry action
    fsm_State, ///< In-state action
    fsm_Leave ///< Exit action
} fsm_e;

typedef int FsmEvent; ///< Type for events
typedef struct FsmState FsmState; ///< Type for states
typedef struct FsmEdge FsmEdge; ///< Type for edges (transitions)

/** State action functor
    @param state Pointer to this state
    @param type Action type (Enter/State/Leave)
    @param frto Pointer to from(Enter)/to(Leave) state, NULL otherwise
    @param data User data
    @return Event id in case of a new triggered event, fsm_None otherwise
*/
typedef FsmEvent (*FsmAction)(FsmState *state, fsm_e type, FsmState *frto, void *data);

/** FSM state object */
struct FsmState
{
    FsmAction action; ///< Per-state action
    void *data; ///< Per-state data
};

/** State jump functor
    @param edge Pointer to this edge
    @param state Pointer to the actual current state
    @param event Event id that triggered the transition
    @param data User data
    @return Pointer to the next state and NULL for end state
*/
typedef FsmState *(*FsmJump)(FsmEdge *edge, FsmState *state, FsmEvent event, void *data);

/** Guard function
    @param edge Pointer to this edge
    @param state Pointer to the actual current state
    @param event Event id that triggered the transition
    @param data User data
    @return Guard result
*/
typedef bool (*FsmGuard)(FsmEdge *edge, FsmState *state, FsmEvent event, void *data);

/** FSM edge transition */
struct FsmEdge
{
    FsmState *state; ///< Matching current state pointer, or NULL to match any state
    FsmEvent event; ///< Matching event id or fsm_Any for wildcard
    void *next; ///< Next state pointer (FsmState *) or jump function (FsmJump)
    FsmGuard guard; ///< Transition guard function
    void *data; ///< Per-edge data
};

typedef struct Fsm Fsm;
struct Fsm
{
    FsmState *state; ///< Pointer to the state array
    size_t states; ///< Number of states
    void **stateData; ///< Pointer to user state data array

    FsmEdge *edge; ///< Pointer to the edge transitions array
    size_t edges; ///< Number of edges
    void **edgeData; ///< Pointer to user edge data array

    FsmEvent event; ///< Current/last event
    fsm_e type; ///< Current/last action type

    FsmState *current; ///< Pointer to the current state
    void *data; ///< Per-fsm data
};

#define fsm_INIT { 0 }

static inline FsmEvent
fsmStep(Fsm *f, FsmEvent e)
{
    FsmState *cp = f->current; // Store current state
    FsmEvent ne = fsm_None; // Next event

    // User state data
    void *us = (f->stateData && cp) ? f->stateData[cp - f->state] : NULL;

    if (!cp && e == fsm_None)
        e = fsm_Reset; // Inject reset into uninitialized FSM

    f->event = e;

    switch (e)
    {
    case fsm_Reset:
        {
            // Exit current state
            if (cp && cp->action)
            {
                f->type = fsm_Leave, ne = cp->action(cp, fsm_Leave, f->state, us);

                if (ne != fsm_None)
                    return ne; // Leave action emitted event
            }

            FsmState *ps = cp;
            cp = f->current = f->state; // First state in array is entry state

            if (!cp)
                return fsm_End; // Null state machine

            if (cp->action)
            {
                us = f->stateData ? f->stateData[0] : NULL;
                f->type = fsm_Enter, ne = cp->action(cp, fsm_Enter, ps, us);
            }
        }
        break;

    case fsm_None: // No event, run in-state action
        if (cp->action)
            f->type = fsm_State, ne = cp->action(cp, fsm_State, NULL, us);
        break;

    default: // Process user transition event
        ne = fsm_Empty; // Default return in case no transition is found

        // Search transition in listing order
        for (size_t i = 0; i < f->edges; ++i)
        {
            FsmEdge *ep = &f->edge[i];

            // Check for state match (null edge state matches any state)
            if (ep->state && ep->state != cp)
                continue; // Not a match

            // Check for stop processing filter
            if (ep->event == fsm_End)
                break;

            ne = fsm_None; // Default return for a transition

            // Check for event match
            if (ep->event == e || ep->event == fsm_Any)
            {
                // User edge data
                void *ue = f->edgeData ? f->edgeData[i] : NULL;

                // Check transition guard
                if (!ep->guard || ep->guard(ep, cp, e, ue))
                {
                    // Resolve next state pointer (NULL, new state pointer or jump function)
                    FsmState *np = (!ep->next) ? NULL
                        : ((FsmState *)(ep->next) >= f->state && (FsmState *)(ep->next) < (f->state + f->states)) ? (FsmState *)(ep->next)
                        : ((FsmJump)(ep->next))(ep, cp, e, ue);

                    if (cp->action)
                    {
                        f->type = fsm_Leave, ne = cp->action(cp, fsm_Leave, np, us);

                        if (ne != fsm_None)
                            return ne; // Leave action emitted event
                    }

                    if (!np) // Final state reached if next state is NULL
                        ne = fsm_End;
                    else if (np->action)
                    {
                        us = f->stateData ? f->stateData[np - f->state] : NULL;
                        f->type = fsm_Enter, ne = np->action(np, fsm_Enter, cp, us);
                    }

                    cp = np; // New state value

                    // Transition executed, stop
                    break;
                }
            }
        }
    }

    f->current = cp; // Commit current state

    return ne; // Last event value
}

static inline FsmEvent
fsmReset(Fsm *f)
{
    return fsmStep(f, fsm_Reset);
}

#endif // FSM_H_

下面有一个很简单的测试,以获取有关如何定义和使用FSM实现的想法。 有没有用户定义的事件中,只有FSM数据(字符串),对于每一个状态和一个共享的跳转函数相同的状态的操作:

#include <stdio.h>

#include "fsm.h"

// State action example
static FsmEvent
state_action(FsmState *s, fsm_e t, FsmState *ft, void *us)
{
    FsmEvent e = fsm_None; // State event
    const char *q = "?";

    switch (t)
    {
    case fsm_Enter:
        q = "enter";
        break;

    case fsm_Leave:
        q = "leave";
        break;

    default /* fsm_State */:
        q = "state";
    }

    printf("%s %s\n", (const char *)s->data, q);

    return e;
}

// States
FsmState fs[] =
{
    { state_action, "S0" },
    { state_action, "S1" },
    { state_action, "S2" },
};

// Transition jump example
static FsmState *
state_jump(FsmEdge *t, FsmState *s, FsmEvent e, void *ue)
{
    if (s == &fs[0])
        return &fs[1];

    if (s == &fs[2])
        return NULL; // End state

    return NULL; // Trap
}

// Transition guard example
static bool
count_attempt(FsmEdge *t, FsmState *s, FsmEvent e, void *ue)
{
    static int c = 0;
    ++c;
    bool b = c == 3;
    printf("guard is %s\n", b ? "true" : "false");
    return b;
}

// Transitions
FsmEdge fe[] =
{
    { &fs[0], fsm_Any, state_jump }, // Using jump function, no guard
    { &fs[1], fsm_Any, &fs[2], count_attempt }, // Using direct state and guard
    { &fs[2], fsm_Any, state_jump  }, // Using jump function, no guard
};

int
main(int argc, char **argv)
{
    Fsm f = { fs, 3, NULL, fe, 3, NULL, };

    fsmReset(&f);

    do 
    {
        fsmStep(&f, fsm_None);
    } while (fsmStep(&f, fsm_Any) != fsm_End);

    return 0;
}


Answer 19:

升压有状态图的库。 http://www.boost.org/doc/libs/1_36_0/libs/statechart/doc/index.html

我不能用它说话,虽然。 没有用它自己(还)



文章来源: Is there a typical state machine implementation pattern?