我们需要在 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 ++,我认为Boost Statechart库可能是最佳选择。但是,它确实不帮助C.让我们专注于C用例。
答案 0 :(得分:123)
我更喜欢对大多数状态机使用表驱动方法:
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;
};
表驱动方法更易于维护和扩展,更易于映射到状态图。
答案 1 :(得分:25)
你可能已经看到了我提到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的其他技术中,转换的结构隐藏在控制结构中(而如果,切换......)并由变量值(简称为state
变量)控制,它可能是复杂的将漂亮的图表与错综复杂的代码联系起来的任务。
我从伟大的“计算机语言”杂志上发表的一篇文章中学到了这种技巧,不幸的是,该文章已不再发表。
答案 2 :(得分:13)
我也使用了表格方法。但是,有开销。为什么要存储第二个指针列表?没有()的C函数是一个const指针。所以你可以这样做:
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
}
}
当然,根据您的恐惧因素(即安全性与速度),您可能需要检查有效指针。对于大于三个状态的状态机,上面的方法应该比等效的开关或表方法更少的指令。你甚至可以宏观化:
#define RUN_STATE(state_ptr_) ((state_ptr_)->function(state_ptr_))
另外,我从OP的例子中感觉到,在考虑/设计状态机时应该进行简化。我不认为过渡状态应该用于逻辑。每个状态函数都应该能够执行其给定的角色,而无需明确了解过去的状态。基本上你设计的是如何从你所处的状态转换到另一个状态。
最后,不要基于“功能”边界开始设计状态机,为此使用子功能。相反,根据何时需要等待某些事情发生才能继续进行划分状态。这将有助于在获得结果之前最小化运行状态机的次数。在编写I / O函数或中断处理程序时,这很重要。
另外,经典开关声明的一些优点和缺点:
优点:
缺点:
请注意pro和con这两个属性。我认为转换允许在州之间进行过多共享的机会,并且各州之间的相互依赖性变得难以管理。但是对于少数州,它可能是最易读和可维护的。
答案 3 :(得分:9)
对于一个简单的状态机,只需为您的状态使用switch语句和枚举类型。根据您的输入在switch语句中进行转换。在实际程序中,您显然会更改“if(输入)”以检查转换点。希望这会有所帮助。
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;
}
...
}
答案 4 :(得分:8)
还有logic grid随着状态机变得更大而更易于维护
答案 5 :(得分:5)
在Martin Fowler's UML Distilled中,他在第10章国家机器图(强调我的)中陈述(没有双关语):
状态图可以通过三种主要方式实现:嵌套开关,状态模式,以及 状态表。
让我们使用移动电话显示状态的简化示例:
Fowler举了一个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;
}
}
这是我使用GoF State模式的示例的实现:
从福勒那里获取灵感,这是我的例子:
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
嵌套开关将所有逻辑保留在一个位置,但是当存在大量状态和转换时,代码很难读取。与其他方法相比,它可能更安全,更容易验证(无多态或解释)。
状态模式实现可能会将逻辑分散到几个单独的类中,这可能会将其理解为一个整体问题。另一方面,小班很容易理解。如果通过添加或删除转换来更改行为,则设计会特别脆弱,因为它们是层次结构中的方法,并且代码可能会有很多更改。如果你遵循小接口的设计原则,你会发现这种模式并不是那么好。但是,如果状态机稳定,那么就不需要这样的改变。
状态表方法需要为内容编写某种解释器(如果您使用您正在使用的语言进行反射,这可能会更容易),这可能需要做很多工作。正如Fowler指出的那样,如果您的表与您的代码分开,您可以修改软件的行为而无需重新编译。然而,这有一些安全隐患;该软件的行为基于外部文件的内容。
还有一种流畅的界面(也称为内部领域特定语言)方法,这可能由具有first-class functions的语言促成。 Stateless library存在,该博客显示了一个带代码的简单示例。讨论了Java implementation (pre Java8)。我也被展示了Python example on GitHub。
答案 6 :(得分:4)
我在edx.org课程嵌入式系统 - 形成世界UTAustinX - UT.6.02x,第10章,由Jonathan Valvano和Ramesh Yerraballi发现了一个非常光滑的C实现Moore FSM ....
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];
}
}
答案 7 :(得分:4)
对于简单的情况,你可以使用你的开关样式方法。我发现过去运作良好的是处理转换:
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库一无所知,但这种方法很简单,不需要任何外部依赖,并且易于实现。
答案 8 :(得分:4)
switch()是一种在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;
}
答案 9 :(得分:2)
This article是一个很好的模式(尽管它是C ++,而不是特定的C)。
如果你可以把手放在书上“Head First Design Patterns”,那么解释和例子就非常清楚了。
答案 10 :(得分:2)
您可能需要查看 libero FSM生成器软件。从状态描述语言和/或(windows)状态图编辑器,您可以为C,C ++,Java和许多其他人生成代码......还有很好的文档和图表。 来自iMatix
的来源和二进制文件答案 11 :(得分:2)
我最喜欢的模式之一是州设计模式。对同一组输入做出回应或表现不同。
使用状态机的switch / 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;
}
对于大多数国家机器,尤其是有限状态机,每个州都知道它的下一个状态应该是什么,以及过渡到下一个状态的标准。对于松散状态设计,情况可能并非如此,因此可以选择为转换状态公开API。如果你想要更多的抽象,每个状态处理程序可以分成它自己的文件,这相当于GoF书中的具体状态处理程序。如果您的设计很简单,只有几个状态,那么stateCtxt.c和statehandlers.c可以合并为一个文件以简化。
答案 12 :(得分:1)
您可以在c中使用简约的UML状态机框架。 https://github.com/kiishor/UML-State-Machine-in-C
它同时支持有限状态机和分层状态机。它只有3个API,2个结构和1个枚举。
状态机由state_machine_t
结构表示。它是一种可以继承以创建状态机的抽象结构。
//! Abstract state machine structure
struct state_machine_t
{
uint32_t Event; //!< Pending Event for state machine
const state_t* State; //!< State of state machine.
};
状态由指向框架中state_t
结构的指针表示。
如果为有限状态机配置了框架,则state_t
包含
typedef struct finite_state_t state_t;
// finite state structure
typedef struct finite_state_t{
state_handler Handler; //!< State handler function (function pointer)
state_handler Entry; //!< Entry action for state (function pointer)
state_handler Exit; //!< Exit action for state (function pointer)
}finite_state_t;
该框架提供了一个API dispatch_event
来将事件分派给状态机,并提供了两个API来进行状态遍历。
state_machine_result_t dispatch_event(state_machine_t* const pState_Machine[], uint32_t quantity);
state_machine_result_t switch_state(state_machine_t* const, const state_t*);
state_machine_result_t traverse_state(state_machine_t* const, const state_t*);
有关如何实现分层状态机的更多详细信息,请参阅GitHub存储库。
代码示例
https://github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/simple_state_machine/readme.md
https://github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/simple_state_machine_enhanced/readme.md
答案 13 :(得分:1)
对于支持__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__
答案 14 :(得分:1)
有一本名为Practical Statecharts in C/C++的书。 但是,对于我们需要的东西,方式太重了。
答案 15 :(得分:1)
根据我的经验,使用'switch'语句是处理多种可能状态的标准方法。虽然我很惊讶你将过渡值传递给每个州的处理。我认为状态机的重点是每个州都执行了一次动作。然后,下一个动作/输入确定要转换到哪个新状态。所以我希望每个状态处理函数能够立即执行为进入状态而修复的任何内容,然后决定是否需要转换到另一个状态。
答案 16 :(得分:0)
您的问题类似于“是否存在典型的数据库实施模式”? 答案取决于你想要达到的目标?如果要实现更大的确定性状态机,可以使用模型和状态机生成器。 可以在www.StateSoft.org - SM Gallery上查看示例。 Janusz Dobrowolski
答案 17 :(得分:0)
在C ++中,考虑State pattern。
答案 18 :(得分:0)
在C中,对于简单的机器,我通常最终会使用这样的东西。
事件驱动的FSM由与动作(FsmAction)相关联的状态对象(FsmState)和由当前状态和事件定义的转换(FsmEdge)描述。
它还提供存储和传递FSM和用户数据,分离FSM绑定和用户绑定信息,并允许相同FSM的多个实例(即使用相同的描述但传递不同的用户数据)。
事件由整数类型(FsmEvent)表示。实现保留负值以允许特殊的公共事件(Reset,None,Any,Empty,End)。非负面事件是用户定义的。
为简单起见,转换列在数组中,并按数组顺序尝试匹配,主要提供转换优先级。它们具有可选的保护功能。下一个状态既可以直接在转换列表中指示,也可以通过跳转功能指示,这样可以提供更大的灵活性,从而实现动态FSM行为。
在转换描述中,A NULL当前状态将匹配任何状态,并且通配符事件(Any)将匹配任何事件。无论如何,触发转换的事件的实际值将被传递给跳转和保护功能。
对于复杂的FSM,简单的边缘阵列解决方案可能变得效率太低。在这种情况下,可以使用边数组和事件条目转换为转换矩阵或状态邻接列表来实现正确的跳转函数。
状态操作意味着使用可重入函数来实现,该函数区分状态进入(Enter),状态退出(Leave)和状态(State)操作。通过这种方式,可以使用静态函数变量来封装和保存本地状态信息。
通常,状态进入和退出操作将无意义地执行,并且不返回任何新事件(无)。如果没有,则新事件被捕获并立即返回。这将有效地防止在退出当前状态时发生转换。
FSM步骤函数(fsmStep)将执行FSM的单个步骤,使用新事件触发转换,或者不执行任何事件(无)来执行当前状态的状态内操作。 step函数返回一个新的发出事件,可以处理或重新馈送到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;
}
答案 19 :(得分:-1)
Boost有状态图库。 http://www.boost.org/doc/libs/1_36_0/libs/statechart/doc/index.html
但是,我不能说它的使用。自己没用过(还)