【FSM有限状态机】
状态机
为什么要使用状态机?
使用状态机实现代码是一种非常方便的设计技术,可用于解决复杂的工程问题。状态机将设计分解为一系列步骤,或者在状态机术语中称为状态。每个状态执行一些狭义的任务。另一方面,事件是刺激,它导致状态机在状态之间移动或转换。
举一个简单的例子,我将在本文中使用它,假设我们正在设计电机控制软件。我们想要启动和停止电机,以及改变电机的速度。很简单。要向客户端软件公开的电机控制事件如下:
设置速度 - 将电机设置为以特定速度运行。
停止 - 停止电机。
这些事件提供了以任何所需速度启动电机的能力,这也意味着改变已经移动的电机的速度。或者我们可以完全停止电机。对于电机控制模块,这两个事件或功能被视为外部事件。然而,对于使用我们代码的客户端来说,这些只是普通函数。
这些事件不是状态机状态。处理这两个事件所需的步骤不同。在这种情况下,状态为:
空闲 — 电机不旋转,而是处于静止状态。
不执行任何操作。
启动 — 从完全停止状态启动电机。
打开电机电源。
设置电机速度。
更改速度 — 调整已经移动的电机的速度。
更改电机速度。
停止 — 停止移动的电机。
关闭电机电源。
进入空闲状态。
可以看出,将电机控制分解为离散状态,而不是使用一个单一函数,我们可以更轻松地管理如何操作电机的规则。
每个状态机都有“当前状态”的概念。这是状态机当前所处的状态。在任何给定时刻,状态机只能处于单一状态。特定状态机实例的每个实例都可以在定义时设置初始状态。但是,该初始状态不会在对象创建期间执行。只有发送到状态机的事件才会导致状态函数执行。
为了以图形方式说明状态和事件,我们使用状态图。下图 1 显示了电机控制模块的状态转换。方框表示状态,连接箭头表示事件转换。列出事件名称的箭头是外部事件,而未修饰的线则被视为内部事件。(我将在本文后面介绍内部事件和外部事件之间的区别。)。
状态机设计
状态机设计
内部和外部事件
正如我之前提到的,事件是导致状态机在状态之间转换的刺激。例如,按下按钮可能是一个事件。事件可以分为两类:外部和内部。外部事件在最基本的层面上是对状态机模块的函数调用。这些函数是公共的,从外部或状态机对象外部的代码调用。系统内的任何线程或任务都可以生成外部事件。如果外部事件函数调用导致状态转换发生,则状态将在调用者的控制线程内同步执行。另一方面,内部事件是由状态机本身在状态执行期间自行生成的。
典型的场景包括生成外部事件,这又归结为对模块公共接口的函数调用。根据生成的事件和状态机的当前状态,执行查找以确定是否需要转换。如果需要,状态机将转换到新状态并执行该状态的代码。在状态函数结束时,将执行检查以确定是否生成了内部事件。如果生成了内部事件,则执行另一个转换,新状态将有机会执行。此过程持续到状态机不再生成内部事件,此时原始外部事件函数调用返回。外部事件和所有内部事件(如果有)都在调用者的控制线程内执行。
一旦外部事件启动状态机执行,如果使用锁,则在外部事件和所有内部事件完成执行之前,它不能被另一个外部事件中断。此运行至完成模型为状态转换提供了多线程安全环境。状态机引擎中可以使用信号量或互斥锁来阻止可能试图同时访问同一状态机实例的其他线程。有关锁的去向,请参阅源代码函数 _SM_ExternalEvent() 注释。
事件数据
当生成事件时,它可以选择性地附加事件数据,以供状态函数在执行期间使用。事件数据是指向任何内置或用户定义数据类型的单个 const 或非常量指针。
一旦状态完成执行,事件数据将被视为已用完,必须删除。因此,发送到状态机的任何事件数据都必须通过 SM_XAlloc() 动态创建。状态机引擎使用 SM_XFree() 自动释放分配的事件数据。
状态转换
当生成外部事件时,将执行查找以确定状态转换操作过程。事件有三种可能的结果:新状态、事件被忽略或无法发生。新状态会导致转换到允许执行的新状态。转换到现有状态也是可能的,这意味着重新执行当前状态。对于被忽略的事件,不会执行任何状态。但是,事件数据(如果有)将被删除。最后一种可能性(无法发生)是为在状态机的当前状态下事件无效的情况保留的。如果发生这种情况,软件就会出现故障。
在此实现中,不需要内部事件来执行验证转换查找。状态转换被认为是有效的。您可以检查有效的内部和外部事件转换,但实际上,这只会占用更多存储空间并产生很少的好处。验证转换的真正需求在于异步外部事件,在这些事件中,客户端可能会导致事件在不适当的时间发生。一旦状态机开始执行,就不能中断。它处于私有实现的控制之下,因此不需要进行转换检查。这使设计人员可以自由地通过内部事件更改状态,而无需更新转换表。
StateMachine 模块
状态机源代码包含在 StateMachine.c 和 StateMachine.h 文件中。以下代码显示了部分标头。StateMachine 标头包含各种预处理器多行宏,以简化状态机的实现。
enum { EVENT_IGNORED = 0xFE, CANNOT_HAPPEN = 0xFF };
typedef void NoEventData;
// State machine constant data
typedef struct
{
const CHAR* name;
const BYTE maxStates;
const struct SM_StateStruct* stateMap;
const struct SM_StateStructEx* stateMapEx;
} SM_StateMachineConst;
// State machine instance data
typedef struct
{
const CHAR* name;
void* pInstance;
BYTE newState;
BYTE currentState;
BOOL eventGenerated;
void* pEventData;
} SM_StateMachine;
// Generic state function signatures
typedef void (*SM_StateFunc)(SM_StateMachine* self, void* pEventData);
typedef BOOL (*SM_GuardFunc)(SM_StateMachine* self, void* pEventData);
typedef void (*SM_EntryFunc)(SM_StateMachine* self, void* pEventData);
typedef void (*SM_ExitFunc)(SM_StateMachine* self);
typedef struct SM_StateStruct
{
SM_StateFunc pStateFunc;
} SM_StateStruct;
typedef struct SM_StateStructEx
{
SM_StateFunc pStateFunc;
SM_GuardFunc pGuardFunc;
SM_EntryFunc pEntryFunc;
SM_ExitFunc pExitFunc;
} SM_StateStructEx;
// Public functions
#define SM_Event(_smName_, _eventFunc_, _eventData_) \
_eventFunc_(&_smName_##Obj, _eventData_)
// Protected functions
#define SM_InternalEvent(_newState_, _eventData_) \
_SM_InternalEvent(self, _newState_, _eventData_)
#define SM_GetInstance(_instance_) \
(_instance_*)(self->pInstance);
// Private functions
void _SM_ExternalEvent(SM_StateMachine* self, const SM_StateMachineConst* selfConst, BYTE newState, void* pEventData);
void _SM_InternalEvent(SM_StateMachine* self, BYTE newState, void* pEventData);
void _SM_StateEngine(SM_StateMachine* self, const SM_StateMachineConst* selfConst);
void _SM_StateEngineEx(SM_StateMachine* self, const SM_StateMachineConst* selfConst);
#define SM_DECLARE(_smName_) \
extern SM_StateMachine _smName_##Obj;
#define SM_DEFINE(_smName_, _instance_) \
SM_StateMachine _smName_##Obj = { #_smName_, _instance_, \
0, 0, 0, 0 };
#define EVENT_DECLARE(_eventFunc_, _eventData_) \
void _eventFunc_(SM_StateMachine* self, _eventData_* pEventData);
#define EVENT_DEFINE(_eventFunc_, _eventData_) \
void _eventFunc_(SM_StateMachine* self, _eventData_* pEventData)
#define STATE_DECLARE(_stateFunc_, _eventData_) \
static void ST_##_stateFunc_(SM_StateMachine* self, _eventData_* pEventData);
#define STATE_DEFINE(_stateFunc_, _eventData_) \
static void ST_##_stateFunc_(SM_StateMachine* self, _eventData_* pEventData)
SM_Event() 宏用于生成外部事件,而 SM_InternalEvent() 在状态函数执行期间生成内部事件。SM_GetInstance() 获取指向当前状态机对象的指针。
SM_DECLARE 和 SM_DEFINE 用于创建状态机实例。EVENT_DECLARE 和 EVENT_DEFINE 创建外部事件函数。最后,STATE_DECLARE 和 STATE_DEFINE 创建状态函数。
enum
{
//有限状态
};
typedef
{
int SM_Type;
int (*SM_func)(void *arugs);//不指定具体参数类型,使用时具体确认
}SM_Machine;
vSECC_Charge_StateMachine vSECC_PLC_FSM[2][Charging_State_MAX];
//Init状态
Init_Machine_State()()//注册状态和状态处理函数
void InitBMS_1_vSECC_Machine(tagGunMng_Def *pGunMng)
{
UINT8 u8GunNo = pGunMng->u8GunNo;
if(u8GunNo >= 2)
{
u8GunNo = 0;
}
//#define INIT_STATE_MACHINE(pObj, State, StatePro) ((pObj)->State = (State), (pObj)->fproc = (StatePro))
INIT_STATE_MACHINE(&vSECC_PLC_FSM[u8GunNo][0], Charging_State_Standby, vSECC_StandbyState); /* Standy*/
INIT_STATE_MACHINE(&vSECC_PLC_FSM[u8GunNo][1], Charging_State_CableCheck, vSECC_CableCheckState); /* CableCheck*/
INIT_STATE_MACHINE(&vSECC_PLC_FSM[u8GunNo][2], Charging_State_PreCharge, vSECC_PreChgState); /* PreCharge*/
INIT_STATE_MACHINE(&vSECC_PLC_FSM[u8GunNo][3], Charging_State_Charge, vSECC_ChargingState); /* Charge*/
INIT_STATE_MACHINE(&vSECC_PLC_FSM[u8GunNo][4], Charging_State_PostCharge, vSECC_PostChgState); /*Charge Ending*/
INIT_STATE_MACHINE(&vSECC_PLC_FSM[u8GunNo][5], Charging_State_Failed, vSECC_ChgFailedState); /*Charge Failed or Error*/
INIT_STATE_MACHINE(&vSECC_PLC_FSM[u8GunNo][6], Charging_State_Idle, vSECC_IdleState); /* Idle */
pGunMng->PBmsCtrl->i8_CMD_Phase = Charging_State_Idle;
pGunMng->PBmsCtrl->i8_Last_CMD_Phase = Charging_State_Idle;
}
//轮询调度处理
iTempPhase = vSECC_PLC_FSM[u8GunNo][pGunMng->PBmsCtrl->i8_CMD_Phase].fproc(pGunMng);
pGunMng->PBmsCtrl->i8_CMD_Phase = iTempPhase;
pGunMng->PBmsCtrl->i8_Last_CMD_Phase = pGunMng->PBmsCtrl->i8_CMD_Phase;
电机应用实例
电机实现了我们假设的电机控制状态机,客户端可以以特定速度启动电机并停止电机。电机标头接口如下所示:
client–客户端,生产事假触发数据;server—服务端,状态机服务任务,消费事件数据、生产数据。
#include "StateMachine.h"
// Motor object structure
typedef struct
{
//电机对象类
INT currentSpeed;
} Motor;
// Event data structure
typedef struct
{
//电机对象数据类
INT speed;
} MotorData;
// State machine event functions
EVENT_DECLARE(MTR_SetSpeed, MotorData)
EVENT_DECLARE(MTR_Halt, NoEventData)
// State enumeration order must match the order of state
// method entries in the state map
enum States
{
ST_IDLE,
ST_STOP,
ST_START,
ST_CHANGE_SPEED,
ST_MAX_STATES
};
// State machine state functions
STATE_DECLARE(Idle, NoEventData)
STATE_DECLARE(Stop, NoEventData)
STATE_DECLARE(Start, MotorData)
STATE_DECLARE(ChangeSpeed, MotorData)
// State map to define state function order
BEGIN_STATE_MAP(Motor)
STATE_MAP_ENTRY(ST_Idle)
STATE_MAP_ENTRY(ST_Stop)
STATE_MAP_ENTRY(ST_Start)
STATE_MAP_ENTRY(ST_ChangeSpeed)
END_STATE_MAP(Motor)
// Set motor speed external event
EVENT_DEFINE(MTR_SetSpeed, MotorData)
{
// Given the SetSpeed event, transition to a new state based upon
// the current state of the state machine
BEGIN_TRANSITION_MAP // - Current State -
TRANSITION_MAP_ENTRY(ST_START) // ST_Idle
TRANSITION_MAP_ENTRY(CANNOT_HAPPEN) // ST_Stop
TRANSITION_MAP_ENTRY(ST_CHANGE_SPEED) // ST_Start
TRANSITION_MAP_ENTRY(ST_CHANGE_SPEED) // ST_ChangeSpeed
END_TRANSITION_MAP(Motor, pEventData)
}
// Halt motor external event
EVENT_DEFINE(MTR_Halt, NoEventData)
{
// Given the Halt event, transition to a new state based upon
// the current state of the state machine
BEGIN_TRANSITION_MAP // - Current State -
TRANSITION_MAP_ENTRY(EVENT_IGNORED) // ST_Idle
TRANSITION_MAP_ENTRY(CANNOT_HAPPEN) // ST_Stop
TRANSITION_MAP_ENTRY(ST_STOP) // ST_Start
TRANSITION_MAP_ENTRY(ST_STOP) // ST_ChangeSpeed
END_TRANSITION_MAP(Motor, pEventData)
}
Link
https://github.com/endurodave/C_StateMachine?tab=readme-ov-file#state-machine-design
更多推荐
所有评论(0)