层次状态机-HSM代码解析

有限状态机在学习和工作中经常能够遇到,前面的文章也有使用到。但是对于层次状态机网上的学习资源却很少,导致一直不理解这个工作机制,后面偶然在GitHub看到一篇文章,深入学习后发现层次状态机太实用了,如果将其在项目上结合,肯定能够创造出一个比较好的代码框架。

一、HSM状态调度机制

HSM状态间是存在等级关系,状态间至少存在2个等级,并且除根状态外至少存在ENTER和EXTI事件,我认为HSM的状态机结构类似树状结构,HSM状态如图所示:

很明显这里存在四个等级,CAMERA为d0,其余为d1,d2,d3。 CAMERA为根状态(根状态不做任何处理),其余的六种状态,有些的父状态为根状态,有些则作为其他的父状;

1.切换状态-等级不同

例如从CAMERA_StateOnDispPlay状态(d3)切换到CAMERA_StateOnShoot(d2)状态;

首先函数调度会先找到他们共同的父状态,这里即为CAMERA_StateOn,然后依次调用CAMERA_StateOnDispPlay和CAMERA_StateOnDisp状态的EXTI事件,再调用CAMERA_StateOnShoot的ENTER事件,即可切换到目标状态,方向如图:

 2.切换状态-等级相同

例如从CAMERA_StateOnDisp状态(d2)切换到CAMERA_StateOnShoot(d2)状态;

 他们父状态是相同的,所以只需要调用当前状态的EXTI事件后,再进入目标状态的ENTER事件即可。

状态调度函数源码如下

// Func: void HSM_Tran(HSM *This, HSM_STATE *nextState, void *param, void (*method)(HSM *This, void *param))
// Desc: Transition to another HSM STATE
// This: Pointer to HSM instance
// nextState: Pointer to next HSM STATE
// param: Optional Parameter associated with HSME_ENTRY and HSME_EXIT event
// method: Optional function hook between the HSME_ENTRY and HSME_EXIT event handling
void HSM_Tran(HSM *This, HSM_STATE *nextState, void *param, void (*method)(HSM *This, void *param))
{
#if HSM_FEATURE_SAFETY_CHECK
    // [optional] Check for illegal call to HSM_Tran in HSME_ENTRY or HSME_EXIT
    if (This->hsmTran)
    {
        HSM_DEBUG("!!!!Illegal call of HSM_Tran[%s -> %s] in HSME_ENTRY or HSME_EXIT Handler!!!!",
            This->curState->name, nextState->name);
        return;
    }
    // Guard HSM_Tran() from certain recursive calls
    This->hsmTran = 1;
#endif // HSM_FEATURE_SAFETY_CHECK

    HSM_STATE *list_exit[HSM_MAX_DEPTH];
    HSM_STATE *list_entry[HSM_MAX_DEPTH];
    uint8_t cnt_exit = 0;
    uint8_t cnt_entry = 0;
    uint8_t idx;
    // This performs the state transition with calls of exit, entry and init
    // Bulk of the work handles the exit and entry event during transitions
    HSM_DEBUGC2("Tran %s[%s -> %s]", This->name, This->curState->name, nextState->name);
    // 1) Find the lowest common parent state
    HSM_STATE *src = This->curState;
    HSM_STATE *dst = nextState;
    // 1a) Equalize the levels
    while (src->level != dst->level)
    {
        if (src->level > dst->level)
        {
            // source is deeper
            list_exit[cnt_exit++] = src;
            src = src->parent;
        }
        else
        {
            // destination is deeper
            list_entry[cnt_entry++] = dst;
            dst = dst->parent;
        }
    }
    // 1b) find the common parent
    while (src != dst)
    {
        list_exit[cnt_exit++] = src;
        src = src->parent;
        list_entry[cnt_entry++] = dst;
        dst = dst->parent;
    }
    // 2) Process all the exit events
    for (idx = 0; idx < cnt_exit; idx++)
    {
        src = list_exit[idx];
        HSM_DEBUGC3("  %s[%s](EXIT)", This->name, src->name);
        src->handler(This, HSME_EXIT, param);
    }
    // 3) Call the transitional method hook
    if (method)
    {
        method(This, param);
    }
    // 4) Process all the entry events
    for (idx = 0; idx < cnt_entry; idx++)
    {
        dst = list_entry[cnt_entry - idx - 1];
        HSM_DEBUGC3("  %s[%s](ENTRY)", This->name, dst->name);
        dst->handler(This, HSME_ENTRY, param);
    }
    // 5) Now we can set the destination state
    This->curState = nextState;
#if HSM_FEATURE_SAFETY_CHECK
    This->hsmTran = 0;
#endif // HSM_FEATURE_SAFETY_CHECK
#if HSM_FEATURE_INIT
    // 6) Invoke INIT signal, NOTE: Only HSME_INIT can recursively call HSM_Tran()
    HSM_DEBUGC3("  %s[%s](INIT)", This->name, nextState->name);
    This->curState->handler(This, HSME_INIT, param);
#endif // HSM_FEATURE_INIT
}

二、HSM事件调度机制

HSM事件的调度比较简单,HSM状态事件如图所示:

 这里的状态关系和上述的一致,可以看到不同状态均会存在ENTER和EXTI事件,并且每个状态也有自己可能触发的事件类型;

1.切换事件-目标状态不存在事件

例如在CAMERA_StateOnDispPlay状态中触发LOWBATT事件;

调度函数HSM_Run首先会进入回调函数,如果没有该事件则会返回LOWBATT事件类型,HSM_Run接着进入父状态CAMERA_StateOnDisp回调函数查看是否存在LOWBATT事件,很明显没有,最后回到CAMERA_StateOn状态回调函数,触发LOWBATT事件进行关机处理。

2.切换事件-目标状态存在事件

例如在CAMERA_StateOn状态中触发LOWBATT事件,立即执行关机处理后退出HSM_Run函数

事件调度函数源码如下

void HSM_Run(HSM *This, HSM_EVENT event, void *param)
{
#if HSM_FEATURE_DEBUG_ENABLE && HSM_FEATURE_DEBUG_NESTED_CALL
    // Increment the nesting count
    gucHsmNestLevel++;
#endif // HSM_FEATURE_DEBUG_ENABLE && HSM_FEATURE_DEBUG_NESTED_CALL

    // This runs the state's event handler and forwards unhandled events to
    // the parent state
    HSM_STATE *state = This->curState;
#ifdef HSM_DEBUG_EVT2STR
    HSM_DEBUGC1("Run %s[%s](evt:%s, param:%08lx)", This->name, state->name, HSM_DEBUG_EVT2STR(event), (unsigned long)param);
#else
    HSM_DEBUGC1("Run %s[%s](evt:%lx, param:%08lx)", This->name, state->name, (unsigned long)event, (unsigned long)param);
#endif // HSM_DEBUG_EVT2STR
    while (event)
    {
        event = state->handler(This, event, param);
        state = state->parent;
        if (event)
        {
#ifdef HSM_DEBUG_EVT2STR
            HSM_DEBUGC1("  evt:%s unhandled, passing to %s[%s]", HSM_DEBUG_EVT2STR(event), This->name, state->name);
#else
            HSM_DEBUGC1("  evt:%lx unhandled, passing to %s[%s]", (unsigned long)event, This->name, state->name);
#endif // HSM_DEBUG_EVT2STR
        }
    }
#if HSM_FEATURE_DEBUG_ENABLE
    // Restore debug back to the configured debug
    This->hsmDebug = This->hsmDebugCfg;
#if HSM_FEATURE_DEBUG_NESTED_CALL
    if (gucHsmNestLevel)
    {
        // Decrement the nesting count
        gucHsmNestLevel--;
    }
#endif // HSM_FEATURE_DEBUG_NESTED_CALL
#endif // HSM_FEATURE_DEBUG_ENABLE
}

三、HSM实例和状态创建

1.HSM状态结构体

在创建HSM状态函数前,需要了解状态结构体里面的成员;

struct  HSM_STATE_T为HSM状态结构体,包含指向父状态的指针,状态的回调函数,状态的字符串名称,状态的等级;

结构体源码如下:

//----Structure declaration----
typedef uint32_t HSM_EVENT;
typedef struct HSM_STATE_T HSM_STATE;
typedef struct HSM_T HSM;
typedef HSM_EVENT (* HSM_FN)(HSM *This, HSM_EVENT event, void *param);

struct HSM_STATE_T
{
    HSM_STATE *parent;          // parent state
    HSM_FN handler;             // associated event handler for state
    const char *name;           // name of state
    uint8_t level;              // depth level of the state
};

2.HSM状态创建

使用HSM_STATE_Create函数创建HSM状态,首先会判断该结构体是否添加父状态,如果没有默认会将根状态作为该结构体节点的父状态,依次将字符串名称、回调函数、状态等级、父状态等注册到输入的结构体内;

HSM_EVENT HSM_RootHandler(HSM *This, HSM_EVENT event, void *param)
{
#ifdef HSM_DEBUG_EVT2STR
    HSM_DEBUG("\tEvent:%s dropped, No Parent handling of %s[%s] param %lx",
              HSM_DEBUG_EVT2STR(event), This->name, This->curState->name, (unsigned long)param);
#else
    HSM_DEBUG("\tEvent:%lx dropped, No Parent handling of %s[%s] param %lx",
              (unsigned long)event, This->name, This->curState->name, (unsigned long)param);
#endif // HSM_DEBUG_EVT2STR
    return HSME_NULL;
}

HSM_STATE const HSM_ROOT =
{
    .parent = ((void *)0),
    .handler = HSM_RootHandler,
    .name = ":ROOT:",
    .level = 0
};

void HSM_STATE_Create(HSM_STATE *This, const char *name, HSM_FN handler, HSM_STATE *parent)
{
    if (((void *)0) == parent)
    {
        parent = (HSM_STATE *)&HSM_ROOT;
    }
    This->name = name;
    This->handler = handler;
    This->parent = parent;
    This->level = parent->level + 1;
    if (This->level >= HSM_MAX_DEPTH)
    {
        HSM_DEBUG("Please increase HSM_MAX_DEPTH > %d", This->level);
        // assert(0, "Please increase HSM_MAX_DEPTH");
        while(1);
    }
}

 3.HSM状态获取

通过HSM_GetState函数可以获取到当前最新的状态

HSM_STATE *HSM_GetState(HSM *This)
{
    // This returns the current HSM state
    return This->curState;
}

uint8_t HSM_IsInState(HSM *This, HSM_STATE *state)
{
    HSM_STATE *curState;
    // Traverse the parents to find the matching state.
    for (curState = This->curState; curState; curState = curState->parent)
    {
        if (state == curState)
        {
            // Match found, HSM is in state or parent state
            return 1;
        }
    }
    // This HSM is not in state or parent state
    return 0;
}

4.HSM实例结构体

struct HSM_T为HSM实例结构体,该类型里面包含一个struct  HSM_STATE_T状态结构体成员指针,相当于在HSM状态结构体的基础上进行封装,在创建HSM实例后,该指针会一直记录当前的最新的HSM状态。

结构体源码如下:

struct HSM_T
{
    HSM_STATE *curState;        // Current HSM State
#if HSM_FEATURE_DEBUG_ENABLE
    const char *name;           // Name of HSM Machine
    const char *prefix;         // Prefix for debugging (e.g. grep)
    uint8_t hsmDebugCfg;        // HSM debug configuration flag
    uint8_t hsmDebug;           // HSM run-time debug flag
#endif // HSM_FEATURE_DEBUG_ENABLE
#if HSM_FEATURE_SAFETY_CHECK
    uint8_t hsmTran;            // HSM Transition Flag
#endif // HSM_FEATURE_SAFETY_CHECK
};

5.HSM实例创建

通过HSM_Create函数可以创建一个HSM实例,并且初始化当前的状态

void HSM_Create(HSM *This, const char *name, HSM_STATE *initState)
{
    // Setup debug
#if HSM_FEATURE_DEBUG_ENABLE
    This->name = name;
    This->prefix = "";
    This->hsmDebugCfg = 0;
    This->hsmDebug = 0;
#endif // HSM_FEATURE_DEBUG_ENABLE
    // Supress warning for unused variable if HSM_FEATURE_DEBUG_ENABLE is not defined
    (void)name;

    // Initialize state
    This->curState = initState;
    // Invoke ENTRY and INIT event
    HSM_DEBUGC1("  %s[%s](ENTRY)", This->name, initState->name);
    This->curState->handler(This, HSME_ENTRY, 0);
    HSM_DEBUGC1("  %s[%s](INIT)", This->name, initState->name);
    This->curState->handler(This, HSME_INIT, 0);
}

四、总结

HSM层次状态两个关键因素为状态的切换和事件的切换,前面状态和事件的调度是重中之重,必须得理解HSM类似树结构的运行方式。至此HSM层次状态机的核心代码解析完成;

早在2022年12月就应该写这篇文章,因为工作刚好上了项目比较紧急一直没有时间,也是当时没有完全理解调度机制,导致这个文章在四个月后在编写完成。本文章只是介绍HSM层次状态机的核心代码,下一篇文章会继续把GitHub文章HSM应用例子写完。

  • 9
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java状态机设计模式是一种用于实现状态机的软件设计模式。状态机是一种抽象的模型,它描述了一个对象或者系统在不同的状态之间的转换。状态机通常包含状态、转移和事件。在Java中,可以使用状态机设计模式来实现各种不同的状态机,如有限状态自动机(FSM)和层次状态机HSM)等。 在Java中,状态机设计模式通常使用状态模式和策略模式实现。状态模式用于描述状态转换的过程,而策略模式用于确定状态机的行为。在状态模式中,每个状态都是一个对象,而状态转换则是通过调用状态对象的方法来实现的。在策略模式中,状态机的行为是由一组策略对象来实现的,每个策略对象负责处理一种状态。 下面是一个简单的Java状态机设计模式的示例代码: ```java // 状态接口 interface State { void handle(); } // 具体状态 class ConcreteStateA implements State { public void handle() { System.out.println("State A"); } } class ConcreteStateB implements State { public void handle() { System.out.println("State B"); } } // 状态机 class Context { private State state; public Context(State state) { this.state = state; } public void setState(State state) { this.state = state; } public void request() { state.handle(); } } // 测试代码 public class Test { public static void main(String[] args) { State stateA = new ConcreteStateA(); State stateB = new ConcreteStateB(); Context context = new Context(stateA); context.request(); context.setState(stateB); context.request(); } } ``` 在这个例子中,我们定义了一个状态接口和两个具体状态(ConcreteStateA和ConcreteStateB)。我们还定义了一个状态机(Context),它包含一个状态对象,并且可以通过调用setState方法来更改状态。最后,我们在测试代码中创建了一个状态机对象,并且依次调用了两种不同状态的handle方法。 这就是Java状态机设计模式的基本实现方式。通过这种模式,我们可以更加灵活地管理对象或系统在不同状态之间的转换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值