stateMachine状态机框架

有限状态机-stateMachine(一) 有限状态机-stateMachine 阅读详情

1 说明

关于状态机的背景知识,在上一篇文章什么是状态机?一篇文章就够了,已经有做介绍,还没有看的小伙伴可以先点击进去看一下,如果实在没有时间看,也不影响本篇文章的阅读,本篇文章主要讲一个状态机c语言实现框架,和基于该框架的一些实例。

2 状态机框架

stateMachine状态机框架 是github上的一个开源项目,采用的是MIT License,用c语言编写,核心代码不过150行,很值得参考和学习。

传送门: github.com/misje/state…

说明文档: misje.github.io/stateMachin…

2.1 框架特点

stateMachine状态机框架 具有功能丰富、代码精练的特点(核心代码不到150行),其支持状态守护、转移守护、事件能携带数据、进入动作(entry action)、退出动作(exit action)、转移动作(transition action) 、状态数据自定义和 父状态/子状态及其嵌套。

如下为stateMachine状态机框架说明图。每一个状态都可以定义自己的entryAction()和exitAction();每一个transition都可以定义自己的guard()和action();不同的状态可以有共同的父状态;也可以不定义guard(),将transition变为无条件转移类型。

2.2 框架分析

2.2.1 数据结构

stateMachine状态机框架,有四个重要的结构体
 

struct event

struct transition

struct state

struct stateMachine

已经都对其做了中文注释,其定义分别如下:

struct event
{
    /*事件类型,这个自定义,一般定义成枚举类型*/
    int type;
    /*事件所携带的数据,类型自己定义,一般定义为结构体,便于扩展*/
    void *data;
};

struct transition
{
    /*触发转移的事件,其值应该为struct event结构体中type类型*/
    int eventType;
    /*事件event必须满足condition才会发生转移,它会作为guard函数的参数向下传递*/
    void *condition;
    /*该函数称为守护函数,自己定义,一般会将condition和事件event所携带的data
    **进行比较,输出一个bool类型的值,决定是否发生转移*/
    bool ( *guard )( void *condition, struct event *event );
    /*当转移发生了,一些需要做的事情,可以放在这里去执行,其传入参数是
    当前状态的数据和将要到达状态的数据以及事件,此函数也可以为空。*/
    void ( *action )( void *currentStateData, struct event *event, void*newStateData );
    /*将要转移过去的状态*/
    struct state *nextState;
};

struct state
{
    /*当前定义状态的父状态,如果当前定义状态有父状态,这个指针要是非空*/
    struct state *parentState;
    /*如果当前定义的状态有子状态,此指针应该指向作为入口点的子状态*/
    struct state *entryState;
    /*关于此状态转移的数组,其定义了此状态的转移流程*/
    struct transition *transitions;
    /*此状态的转移个数,它为上面数组的个数大小*/
    size_t numTransitions;
    /*状态所携带的数据,用作entryAction() 、exitAction()和
    transition action()的参数*/
    void *data;
    /*进入该状态时会被回调的函数,自己定义,也可以为空*/
    void ( *entryAction )( void *stateData, struct event *event );
    /*退出该状态时会被回调的函数,自己定义,也可以为空*/
    void ( *exitAction )( void *stateData, struct event *event );
};

struct stateMachine
{
    /*指针所指向的当前状态*/
    struct state *currentState;
    /*指向上一状态的指针,方便跟踪记录历史状态*/
    struct state *previousState;
    /*指向系统中发生异常的状态*/
    struct state *errorState;
};

2.2.2 函数接口

stateMachine状态机框架所有接口有如下五个

stateM_init(),

stateM_handleEvent(),

stateM_currentState(),

stateM_previousState(),

stateM_stopped(),

其中最重要的接口是stateM_handleEvent(),它处理了状态机最核心的流程。接口已经如下罗列了出来,加上了中文注释,现在重点分析stateM_handleEvent()的内部流程。

 
/*状态机初始化接口,需要传入初始状态和异常状态,通过指针返回状态机*/
void stateM_init( struct stateMachine *stateMachine,
      struct state *initialState, struct state *errorState );

/*将事件传入进状态机,因为事件会源源不断的来,此接口会被不断调用*/
int stateM_handleEvent( struct stateMachine *stateMachine,
      struct event *event );

/*获取系统当前状态,因为在状态机中有记录很好拿*/
struct state *stateM_currentState( struct stateMachine *stateMachine );

/*获取系统上一个状态,因为在状态机中有记录很好拿*/
struct state *stateM_previousState( struct stateMachine *stateMachine );

/*停止状态机*/
bool stateM_stopped( struct stateMachine *stateMachine );

先来看一下stateM_handleEvent()接口的返回值类型,它的返回值是一个枚举类型,总共定义了6种。枚举的含义已经在如下做了注解。stateM_handleEvent()的调用流程大概可以描述如下:

  1. 判断入参fsm和event是否为空,为空时直接返回stateM_errArg;
  2. 判断当前状态是否为空,如果为空,将当前状态置为errorState,返回stateM_errorStateReached;
  3. 判断当前状态的转移数组numTransitions大小是否为零,如果为零,返回stateM_noStateChange;
  4. 获取当前状态的transition,如果当前状态没有transition,则获取父状态的transition,如果往上遍历父状态transition都没有,直到父状态为空,返回stateM_noStateChange;
  5. 判断步骤(4.)拿到的transition,是否有nextState,如果为空,将当前状态置为errorState,返回stateM_errorStateReached;
  6. 拿到步骤(5.)中的nextState,向下遍历nextState的entryState,直到nextState没有entryState,然后将此刻的nextState,作为将要转移过去的状态;
  7. 如果当前状态和将要转移过去的状态不相等,则回调exitAction()函数;
  8. 回调transition的action()函数;
  9. 如果当前状态和将要转移过去的状态不相等,则回调entryAction()函数;
  10. 如果当前状态和将要转移过去的状态相等,返回stateM_stateLoopSelf;
  11. 如果当前状态等于errorState,则返回stateM_errorStateReached;
  12. 如果将要转移状态的转移数组numTransitions大小为零,则返回stateM_finalStateReached;
  13. 如果上面都没有遇到异常,则流程执行完成,返回stateM_stateChanged。
enum stateM_handleEventRetVals
{ 
    /*参数传递错误*/
    stateM_errArg = -2,
    /*到达一个异常状态时,会返回该值;当前状态为空,
    **transition中的nextState为空也会返回该值*/
    stateM_errorStateReached,
    /*当前状态改变了,并且进入的是非最终状态,会返回该值*/
    stateM_stateChanged,
    /*从当前状态又回到了当前状态会返回该值*/
    stateM_stateLoopSelf,
    /*所传递事件没有导致当前状态改变*/
    stateM_noStateChange,
    /*到达了最终状态(异常状态除外)*/
    stateM_finalStateReached,
};

int stateM_handleEvent( struct stateMachine *fsm,
      struct event *event )
{
   if ( !fsm || !event )
      return stateM_errArg;

   if ( !fsm->currentState )
   {
      goToErrorState( fsm, event );
      return stateM_errorStateReached;
   }

   if ( !fsm->currentState->numTransitions )
      return stateM_noStateChange;

   struct state *nextState = fsm->currentState;
   do {
      struct transition *transition = getTransition( fsm, nextState, event );

      /* If there were no transitions for the given event for the current
       * state, check if there are any transitions for any of the parent
       * states (if any): */
      if ( !transition )
      {
         nextState = nextState->parentState;
         continue;
      }

      /* A transition must have a next state defined. If the user has not
       * defined the next state, go to error state: */
      if ( !transition->nextState )
      {
         goToErrorState( fsm, event );
         return stateM_errorStateReached;
      }

      nextState = transition->nextState;

      /* If the new state is a parent state, enter its entry state (if it has
       * one). Step down through the whole family tree until a state without
       * an entry state is found: */
      while ( nextState->entryState )
         nextState = nextState->entryState;

      /* Run exit action only if the current state is left (only if it does
       * not return to itself): */
      if ( nextState != fsm->currentState && fsm->currentState->exitAction )
         fsm->currentState->exitAction( fsm->currentState->data, event );

      /* Run transition action (if any): */
      if ( transition->action )
         transition->action( fsm->currentState->data, event, nextState->
               data );

      /* Call the new state's entry action if it has any (only if state does
       * not return to itself): */
      if ( nextState != fsm->currentState && nextState->entryAction )
         nextState->entryAction( nextState->data, event );

      fsm->previousState = fsm->currentState;
      fsm->currentState = nextState;
      
      /* If the state returned to itself: */
      if ( fsm->currentState == fsm->previousState )
         return stateM_stateLoopSelf;

      if ( fsm->currentState == fsm->errorState )
         return stateM_errorStateReached;

      /* If the new state is a final state, notify user that the state
       * machine has stopped: */
      if ( !fsm->currentState->numTransitions )
         return stateM_finalStateReached;

      return stateM_stateChanged;
   } while ( nextState );

   return stateM_noStateChange;
}

现在讨论一下getTransition()函数接口。它在stateM_handleEvent()的调用流程中,获取当前状态的transition时被调用到。从该函数的调用过程可以很清楚的看出来,通过传入的event中的type来和当前状态的transitions数组进行遍历比对,直到发现那个匹配的transition,如果传入的event和匹配到的transition,满足守护函数guard(),则返回transitions,否则返回NULL

static struct transition *getTransition( struct stateMachine *fsm,
      struct state *state, struct event *const event )
{
   size_t i;

   for ( i = 0; i < state->numTransitions; ++i )
   {
      struct transition *t = &state->transitions[ i ];

      /* A transition for the given event has been found: */
      if ( t->eventType == event->type )
      {
         if ( !t->guard )
            return t;
         /* If transition is guarded, ensure that the condition is held: */
         else if ( t->guard( t->condition, event ) )
            return t;
      }
   }

   /* No transitions found for given event for given state: */
   return NULL;
}

至此,stateMachine状态机框架的核心调用流程介绍完毕,下一章节介绍一下对应的应用实例。

3 应用实例

本次实例用实际开发过程中的一个案例进行说明。为了讲述清楚状态机逻辑,在细节上做了简化处理,但丝毫不影响对过程的理解。打一通电话可以被分为如下几个过程:拨打→ 振铃 → 接通 → (呼叫保持) → 挂断 → 回到拨打界面,打括号的过程也可以没有。依据这几个过程,可以将一通电话的流程划分为如下几个状态:

  • calling(拨打中)
  • alerting(振铃中)
  • called(接通中)
  • hold(呼叫保持中)
  • disconnect(挂断中)
  • idle(拨打界面)

为了方便处理异常,这里有两种思路,

一种是定义异常状态,只要流程中出现了不期望的事情,如传参异常、transition的nextStatus为空,当前状态为空,都可以直接进入异常状态,触发异常的处理;

第二种是流程都正常,但是传入的EVENT未能满足condition,无法触发状态转移,为了方便统一集中处理这种情况,可以定义一个父状态,在父状态中统一处理。因此,再增加两个状态:

  • group(前面六种状态的父状态)
  • error_state(异常状态)

状态的转移条件做如下定义,下图将转移流程图做了详细描绘,可以对照着看。

typedef enum 
{
    CONDITION_CALLING_OUT,    /*呼出*/
    CONDITION_CALLING_IN,     /*呼入*/
    CONDITION_ALERTING_OUT,   /*对方振铃*/
    CONDITION_ALERTING_IN,    /*本机振铃*/
    CONDITION_CALLED_OUT,     /*对方接通*/
    CONDITION_CALLED_IN,      /*本机接通*/
    CONDITION_HOLD_OUT,       /*对方呼叫保持*/
    CONDITION_HOLD_IN,        /*本机呼叫保持*/
    CONDITION_RETRIEVE_OUT,   /*对方恢复通话*/
    CONDITION_RETRIEVE_IN,    /*本机恢复保持*/
    CONDITION_DISCONNECT_OUT, /*对方挂断*/
    CONDITION_DISCONNECT_IN,  /*本机挂断*/
    CONDITION_CALL_GO_TO_IDLE,/*返回到拨号界面*/
    CONDITION_CALL_MAX,
} CONDITION_TYPES_E;

本次实验的代码如下:

#include "stateMachine.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef enum 
{
   EVENT_CALL_TYPE,
   EVENT_CALL_MAX,
} EVENT_TYPES_E;

typedef enum 
{
   CONDITION_CALLING_OUT,
   CONDITION_CALLING_IN,
   CONDITION_ALERTING_OUT,
   CONDITION_ALERTING_IN,
   CONDITION_CALLED_OUT,
   CONDITION_CALLED_IN,
   CONDITION_HOLD_OUT,
   CONDITION_HOLD_IN,
   CONDITION_RETRIEVE_OUT,
   CONDITION_RETRIEVE_IN,
   CONDITION_DISCONNECT_OUT,
   CONDITION_DISCONNECT_IN,
   CONDITION_CALL_GO_TO_IDLE,
   CONDITION_CALL_MAX,
} CONDITION_TYPES_E;

typedef struct
{
    CONDITION_TYPES_E condition;
} CONDITION_T;

typedef struct 
{
   CONDITION_TYPES_E condition;
   const char *expected_state;
} EVENT_PAY_LOAD_T;

static void entryAction( void *stateData, struct event *event );
static void exitAction( void *stateData, struct event *event );
static void transAction( void *oldStateData, struct event *event,
      void *newStateData );
static bool guard( void *condition, struct event *event );
static void errorEventInput( void *oldStateData, struct event *event,
      void *newStateData );

static struct state group_state, calling, alerting, called, hold, disconnect, idle, error_state;

static struct state
group_state =
{
   .data="Group",
   .entryAction = &entryAction,
   .exitAction = &exitAction,
   .transitions = (struct transition[]) 
   {
      {EVENT_CALL_TYPE, NULL, NULL, &errorEventInput, &idle},
   },
   .numTransitions = 1,
   .parentState = NULL,
   .entryState = &idle,
},

idle = 
{
    .data = "Idle",
    .entryAction = &entryAction,
    .exitAction = &exitAction,
    .transitions = (struct transition[]) 
    {
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_CALLING_OUT}, &guard, &transAction, &calling},
        {EVENT_CALL_TYPE,  &(CONDITION_T){CONDITION_CALLING_IN}, &guard, &transAction, &calling},
    },
    .numTransitions = 2,
    .parentState = &group_state,
    .entryState = NULL,
},

calling = 
{
    .data = "Calling",
    .entryAction = &entryAction,
    .exitAction = &exitAction,
    .transitions = (struct transition[]) 
    {
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_ALERTING_OUT}, &guard, &transAction, &alerting},
        {EVENT_CALL_TYPE,  &(CONDITION_T){CONDITION_ALERTING_IN}, &guard, &transAction, &alerting},
        {EVENT_CALL_TYPE,  &(CONDITION_T){CONDITION_DISCONNECT_OUT}, &guard, &transAction, &disconnect},
        {EVENT_CALL_TYPE,  &(CONDITION_T){CONDITION_DISCONNECT_IN}, &guard, &transAction, &disconnect},
    },
    .numTransitions = 4,
    .parentState = &group_state,
    .entryState = NULL,
},

alerting = 
{
    .data = "Alerting",
    .entryAction = &entryAction,
    .exitAction = &exitAction,
    .transitions = (struct transition[]) 
    {
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_CALLED_OUT}, &guard, &transAction, &called},
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_CALLED_IN}, &guard, &transAction, &called},
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_DISCONNECT_OUT}, &guard, &transAction, &disconnect},
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_DISCONNECT_IN}, &guard, &transAction, &disconnect},
    },
    .numTransitions = 4,
    .parentState = &group_state,
    .entryState = NULL,
},

called =
{
    .data = "Called",
    .entryAction = &entryAction,
    .exitAction = &exitAction,
    .transitions = (struct transition[]) 
    {
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_HOLD_OUT}, &guard, &transAction, &hold},
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_HOLD_IN}, &guard, &transAction, &hold},
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_DISCONNECT_OUT}, &guard, &transAction, &disconnect},
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_DISCONNECT_IN}, &guard, &transAction, &disconnect},
    },
    .numTransitions = 4,
    .parentState = &group_state,
    .entryState = NULL,
},

hold = 
{
    .data = "Hold",
    .entryAction = &entryAction,
    .exitAction = &exitAction,
    .transitions = (struct transition[]) 
    {
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_RETRIEVE_OUT}, &guard, &transAction, &called},
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_RETRIEVE_IN}, &guard, &transAction, &called},
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_DISCONNECT_OUT}, &guard, &transAction, &disconnect},
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_DISCONNECT_IN}, &guard, &transAction, &disconnect},
    },
    .numTransitions = 4,
    .parentState = &group_state,
    .entryState = NULL,
},

disconnect = 
{
    .data = "Disconnect",
    .entryAction = &entryAction,
    .exitAction = &exitAction,
    .transitions = (struct transition[])
    {
        {EVENT_CALL_TYPE, &(CONDITION_T){CONDITION_CALL_GO_TO_IDLE}, &guard, &transAction, &idle},
    },
    .numTransitions = 1,
    .parentState = &group_state,
    .entryState = NULL,
},

error_state =
{
   .data = "ERROR",
   .entryAction = &entryAction,
   .parentState = &group_state,
   .entryState = NULL,
};

int main()
{
   struct stateMachine fsm;
   stateM_init( &fsm, &idle, &error_state);

   struct event events[] = {
      /* Create transitions, with the single character as triggering event
       * data, and the expected new state name as the following string. '*' is
       * used when the unconditional transition will be followed. */
      { EVENT_CALL_TYPE, &(EVENT_PAY_LOAD_T){ CONDITION_CALLING_OUT, "Calling"} },
      { EVENT_CALL_TYPE, &(EVENT_PAY_LOAD_T){ CONDITION_ALERTING_OUT, "Alerting" } },
      { EVENT_CALL_TYPE, &(EVENT_PAY_LOAD_T){ CONDITION_CALLED_OUT, "Called" } },
      { EVENT_CALL_TYPE, &(EVENT_PAY_LOAD_T){ CONDITION_HOLD_OUT, "Hold" } },
      { EVENT_CALL_TYPE, &(EVENT_PAY_LOAD_T){ CONDITION_RETRIEVE_OUT, "Called" } },
      /*{ EVENT_CALL_TYPE, &(EVENT_PAY_LOAD_T){ 17, "Called" } }, 异常测试时,请打开此注释*/
      { EVENT_CALL_TYPE, &(EVENT_PAY_LOAD_T){ CONDITION_DISCONNECT_OUT, "Disconnect" } },
      { EVENT_CALL_TYPE, &(EVENT_PAY_LOAD_T){ CONDITION_CALL_GO_TO_IDLE, "Idle" } },
   };
   int res;
   size_t i;

   /* Hand all but the last event to the state machine: */
   for ( i = 0; i < sizeof( events ) / sizeof( events[ 0 ] ); ++i )
   {
      res = stateM_handleEvent( &fsm, &events[ i ] );
      if ( res == stateM_stateLoopSelf )
      {
         /* Prevent segmentation faults (due to the following comparison)
          * (loops will not be tested in the first transition): */
         if ( i == 0 )
         {
            fputs( "Internal error. This should not happen.\n", stderr );
            exit( 4 );
         }

         /* Ensure that the reported state loop is indeed a state loop (check
          * that the expected state is the same as the previous expected
          * state): */
         if ( !strcmp( ( (EVENT_PAY_LOAD_T *)events[ i ].data
                     )->expected_state, ((EVENT_PAY_LOAD_T *)events[ i - 1 ]
                        .data )->expected_state ) )
            puts( "State changed back to itself" );
         else
         {
            fputs( "State unexpectedly changed back to itself", stderr );
            exit( 5 );
         }
      }
      /* Apart from an occasional state loop, all other events handed to the
       * state machine should result in 'stateM_stateChanged': */
      else if ( res != stateM_stateChanged )
      {
         fprintf( stderr, "Unexpected return value from stateM_handleEvent:"
               " %d\n", res );
         exit( 2 );
      }
   }

   return res;
}

static void entryAction( void *stateData, struct event *event )
{
   printf( "Entering %s\n", (char *)stateData );
}

static void exitAction( void *stateData, struct event *event )
{
   printf( "Exiting %s\n", (char *)stateData );
}

static void transAction( void *oldStateData, struct event *event,
      void *newStateData )
{
   EVENT_PAY_LOAD_T *eventData = (EVENT_PAY_LOAD_T *)event->data;

   printf( "Event %s\n", eventData->expected_state );

   if ( strcmp( ( (const char *)newStateData ), eventData->expected_state ) )
   {
      fprintf( stderr, "Unexpected state transition (to %s)\n",
            (const char *)newStateData );
      exit( 1 );
   }
}

static void errorEventInput( void *oldStateData, struct event *event,
      void *newStateData )
{
   EVENT_PAY_LOAD_T *eventData = (EVENT_PAY_LOAD_T *)event->data;
   printf( "Error Event condition=%d currentState=%s nextState=%s\n", 
   eventData->condition,(char *)oldStateData,(char *)newStateData);
   return;
}

static bool guard( void *condition, struct event *event )
{
   EVENT_PAY_LOAD_T *eventData = (EVENT_PAY_LOAD_T *)event->data;
   return ((CONDITION_T *)condition)->condition == eventData->condition;
}

下面第一张图片,是正常的测试流程,事件流程为:

  1. CONDITION_CALLING_OUT
  2. CONDITION_ALERTING_OUT
  3. CONDITION_CALLED_OUT
  4. CONDITION_HOLD_OUT
  5. CONDITION_RETRIEVE_OUT
  6. CONDITION_DISCONNECT_OUT
  7. CONDITION_CALL_GO_TO_IDLE

从图片log中,可以看到exitAction(),transitions action(),entryAction()随着事件,交错执行,符合状态转移过程。

下面第二张图片,事件流程为:

  1. CONDITION_CALLING_OUT
  2. CONDITION_ALERTING_OUT
  3. CONDITION_CALLED_OUT
  4. CONDITION_HOLD_OUT
  5. CONDITION_RETRIEVE_OUT
  6. 17
  7. CONDITION_DISCONNECT_OUT
  8. CONDITION_CALL_GO_TO_IDLE

唯一和上面正常的不同是在第六点增加了事件流程17,由于当前状态没有处理此事件的transition,此事件被转移到了父状态的transition来处理。在父状态的transitions action()中,打出了如下的异常log:Error Event condition=10 currentState=Idle nextState=Idle。

4 注意事项

  • 可以利用父状态和异常状态来处理异常行为;
  • 在父状态的子状态中,只有一个子状态可以充当entryState。如本例中group有很多子状态,但只有idle充当其entryState;
  • 如果将要转移的状态有entryState,将要转移的状态会变为entryState本身,并递归的往下进行;
  • 每个状态和transition对应的exitAction(),transitions action(),entryAction()回调函数可以自住定义,都可以不一样。本例为了简化,回调函数都复用了。
一个轻量实用的Java状态机框架--Cola-StateMachine 详解Java状态机框架--Cola-StateMachine 阅读详情

相关推荐

Spring Boot使用Spring StateMachine框架实现状态机

一 点睛 Spring StateMachine框架可能对于大部分使用Spring的开发者来说还比较生僻,它的主要功能是帮助开发者简化状态机的开发过程,让状态机结构更加层次化。它的第三个Release版本1.2.0,其中增加了对Spring Boot的自动化配置。 我们通过一个简单的示例来对Spring StateMachine有一个初步的认识。 假设我们需要实现一个订单的相关流程,其中包括...

实践求真知 8138

开源项目 `stateMachine` 使用教程

开源项目 stateMachine 使用教程 项目介绍 stateMachine 是一个用于实现状态机逻辑的开源项目。状态机是一种数学模型,用于描述系统可能处于的不同状态以及状态之间的转换。该项目提供了一个灵活的框架,使得开发者能够轻松地定义和操作状态机,适用于各种复杂的逻辑控制场景。 项目快速启动 安装 首先,你需要克隆项目仓库到本地: git clone https://github.com/...

gitblog_00752的博客 378

使用Spring StateMachine框架实现状态机

终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化[外链图片转存中…(img-jZ75Q29m-1710750244924)]由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)[外链图片转存中…(img-E0jyU4Ex-1710750244924)]终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化。

m0_57541068的博客 858

StateMachine状态机初识

StateMachine状态机的介绍中,以下的这篇博客讲得已经很全面了。 http://www.cnblogs.com/bastard/archive/2012/06/05/2536258.html为了更加方便的理解,个人简单的画了一张时序图:具体的使用:要实现自己的StateMachine并让它跑起来,需要做以下几个工作: 一 . 实现自己的State子类。 StateMachine已经帮我

sjz4860402的专栏 3820

JAVA:Spring StateMachine 状态机框架的技术指南

JAVA:Spring StateMachine 状态机框架的技术指南

木头 936

【深大计算机网络】实验三 可靠传输机制 实验报告 附代码

【内附代码】【深大计算机网络】实验三 可靠传输机制 实验报告 附代码

m0_74268508的博客 1654

玩转 Spring 状态机:实现订单状态流转

作者:京东云开发者-京东科技孙扬威链接:https://my.oschina.net/u/4090830/blog/10322627说起 Spring 状态机,大家很容易联想到这个状态机和设计模式中状态模式的区别是啥呢?没错,Spring 状态机就是状态模式的一种实现,在介绍 Spring 状态机之前,让我们来看看设计模式中的状态模式。1. 状态模式状态模式的定义如下:状态模式(State Pa...

程序猿DD 743

第4章 本地一键部署(新手首选)

本文介绍了OpenClaw的本地部署方法,特别适合新手用户。部署过程仅需执行一条命令,通过自动脚本完成安装和配置向导。安装完成后,用户可通过终端TUI或Web控制面板验证部署是否成功。文章还提供了常见问题的排查方法,包括端口占用、权限不足、APIKey无效和安装卡顿等情况。整个部署流程简单直观,无需手动修改配置文件,适合快速搭建使用。

eastyuxiao的专栏 194

StateMachine 的简单使用

StateMachine 的简单使用步骤 源码的frameworks/base/core/java/com/android/internal/util里面把StateMachine.java 、State.java 、IState.java复制到project目录中 自定义StateMachine类继承StateMachine 自定义状态State继承State:重写enter、processMs

菜鸟实验室 6264

PackML工业标准解析:统一机器状态管理与数据交换的实践指南

在工业自动化与智能制造领域,实现设备间的互联互通与高效协同是核心挑战。这背后依赖于一套标准化的机器行为与数据交换规范,其原理在于通过定义统一的状态模型与接口协议,消除不同厂商设备间的“语言”隔阂。这项技术的核心价值在于大幅降低系统集成复杂度、提升运维效率与生产透明度,是构建数字化工厂的基石。其典型应用场景包括包装机械、离散制造产线等需要多设备协同的领域。本文聚焦的PackML(包装机械语言)标准,正是这一理念的杰出实践,它通过一套严谨的**状态机模型**和**数据标签(PackTags)** 体系,为机器提

weixin_30070339的博客 401

VS2022下OSG+OSGEARTH开发环境一键配置指南(Win10 x64+CMake自动化构建)

本文详细介绍了在VS2022环境下配置OSG和OSGEARTH开发环境的完整指南,包括软件下载、CMake自动化构建、编译实战及环境验证。通过一键配置脚本和常见问题解决方案,帮助开发者快速搭建3D开发环境,提升开发效率。

weixin_28828243的博客 405

StateMachine — 状态机框架

是一个零外部依赖的通用状态机基础框架。它解决了工作流/执行单元在不同场景下需要不同状态转换模式的问题,提供四种正交的状态机模式,覆盖从简单的条件转换到复杂的异步事件驱动的全场景。描述一个执行单元(工作流、任务、操作等)从创建到完成的完整生命周期状态。fill:#333;important;important;fill:none;fill:none;ry:5px;ry:5px;ry:0;ry:5px;rx:0;ry:0;rx:0;ry:0;fill:#333;开始执行取消暂停恢复完成失败超时。

qq_45424503的博客 1261

Android蓝牙开发—经典蓝牙和BLE(低功耗)蓝牙的区别

      最近在做蓝牙开发,刚接触时傻傻的分不清经典蓝牙和低功耗蓝牙的区别,一直用开发低功耗蓝牙的方法去连接经典蓝牙设备,最后当然是一直连接不上了。在此记录下经典蓝牙和低功耗蓝牙的区别和联系。 Android中的蓝牙         说到Android中的蓝牙,大家听到的可能有蓝牙1.0、蓝牙2.0、蓝牙3.0、蓝牙4.0之类的以数字结尾的蓝牙版本号,而实际上,在最新的标准中,已经不再使用数...

zqf_888的博客 4万+

Spring StateMachine框架实现状态机

Spring StateMachine框架可能对于大部分使用Spring的开发者来说还比较生僻,该框架目前差不多也才刚满一岁多。它的主要功能是帮助开发者简化状态机的开发过程,让状态机结构更加层次化。前几天刚刚发布了它的第三个Release版本1.2.0,其中增加了对Spring Boot的自动化配置,既然一直在写Spring Boot的教程,所以干脆就将该内容也纳入进来吧,希望对有需求的小伙伴有一

qq_38724991的博客 492

运维颠覆!一文解锁AI赋能运维实战秘籍,效率飙升!

将AI应用于运维工作是一场极具价值的变革,从故障预测与诊断到自动化任务执行,再到资源管理优化,AI都展现出巨大潜力。尽管在实践过程中会面临数据质量、模型复杂性等挑战,但通过合理的技术选型和解决方案,我们能够充分挖掘AI的优势,实现运维工作的高效、智能转型。持续探索和创新AI在运维领域的应用,将为企业的数字化发展提供坚实保障。在科技飞速发展的当下,运维领域正经历着深刻变革,AI的融入成为提升运维效率与质量的关键。本文将深入实战,带你领略AI如何在运维各环节大显身手,让你的运维工作开启智能高效新模式。

探索云原生与智能化驱动下的安全运维新范式。关注DevSecOps、可观测性、AIOps等前沿领域,与您共赴技术前沿。 401

当AI学会写“自传”:OpenClaw 的 SOUL.md 如何把配置文件变成一颗会变形的心

SOUL.md。

步子哥的博客 1309

springboot干货——(十九)Spring StateMachine框架实现状态机

简介: Spring StateMachine框架的主要功能是帮助开发者简化状态机的开发过程,让状态机结构更加层次化。 1.老规矩,先上项目结构图 2.新建项目springboot-statemachine,pom.xml如下 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://

东天里的冬天 2万+

Ceres&OpenCV 单目相机标定(论文复现)

Ceres&OpenCV 单目相机标定(论文复现)

dayuhaitang1的博客 370

springboot【28】状态机的实现-Spring StateMachine框架

Spring StateMachine框架的第三个Release版本1.2.0增加了对Spring Boot的自动化配置。 快速入门          假设我们需要实现一个订单的相关流程,其中包括订单创建、订单支付、订单收货三个动作。整个实现过程: 创建一个Spring Boot的基础工程,并在pom.xml中加入spring-statemachine-core的依赖,具体如

Java Learning 2672

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Spring状态机StateMachine框架---企业开发实战(含代码)

Spring状态机StateMachine框架—企业开发实战(含代码) ​ 最近,在工作中上级派了一个任务给我,叫我写下状态机。一开始我听了是直接懵逼的,我都不知道什么是状态机。后来听了业务需求后才慢慢弄懂,这个状态机的作用是修改订单状态,为了让业务代码能够高效复用。这又引出了第二问题了,要怎样实现状态机,是写一堆if-else判断吗?一开始我的想法就是这样,后来上网查了状态机,发现有个StateMachine框架,然后就去看了官方文档https://docs.spring.io/spring-s

gorylee的博客 4234

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

spring 状态机_使用Spring StateMachine框架实现状态机

Spring StateMachine框架可能对于大部分使用Spring的开发者来说还比较生僻,该框架目前差不多也才刚满一岁多。它的主要功能是帮助开发者简化状态机的开发过程,让状态机结构更加层次化。前几天刚刚发布了它的第三个Release版本1.2.0,其中增加了对Spring Boot的自动化配置,既然一直在写Spring Boot的教程,所以干脆就将该内容也纳入进来吧,希望对有需求的小伙伴有一...

weixin_39797758的博客 618

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

上一篇: MCU嵌入式初始化架构
下一篇: 3σ准则(3 Sigma Rule)
Let's Chat Coding
博客等级 码龄3年 1020粉丝 92原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值