• 状态机


    状态机



      理论上分为有限状态机和无限状态机,但无限状态机的状态集Q是无限的,这蕴含着转移函数有无限多的映射,因此它是一种思考理论上的模型。

      状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:

    • 现态:是指当前所处的状态。
    • 条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
    • 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
    • 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

    有限状态机FSM分为两大类

    • 若输出只和状态有关而与输入无关,则称为Moore状态机
    • 输出不仅和状态有关而且和输入有关系,则称为Mealy状态机

    作为两年以上的程序老鸟,状态机的写法理应成熟简洁。但往往在快速完成的要求下总会选用标记位和switch、if-else快速构成状态机逻辑。俗话说磨刀不误砍柴工,地基决定上层建筑,花点时间把每一个知识点框架整理一遍,将来你的摩天大楼建造将会更快更稳。以下两个是一些典型的c语言状态机,写法不一样,但是蕴含的思维是大同小异的,第二个更符合大多数场合。

    • 处理按键引发的多状态切换

    转载自https://blog.csdn.net/qq_35579835/article/details/106536745

    
    /***********************************************
    1、定义状态接口,以MP3的状态接口为例,每种状态下都可能发生
    两种按键动作。
    ************************************************/
    
    typedef struct State{
      void (* stop)();
      void (* palyOrPause)();
    }State;
    
    
    /***********************************************
    2、定义系统当前状态指针,保存系统的当前状态
    ************************************************/
    
    State * pCurrentState;
    
    
    /***********************************************
    3、定义具体状态,根据状态迁移图来实现具体功能和状态切换。
    ************************************************/
    
    void ignore();
    void startPlay();
    void stopPlay();
    void pausePlay();
    void resumePlay();
    
    //空闲状态时,stop键操作无效,play/pause会开始播放音乐
    State IDLE = {
      ignore,
      startPlay
    };
    
    //播放状态时,stop键会停止播放音乐,play/pause会暂停播放音乐
    State PLAY = {
      stopPlay,
      pausePlay
    };
    
    //暂停状态时,stop键会停止播放音乐,play/pause会恢复播放音乐
    State PAUSE = {
      stopPlay,
      resumePlay
    };
    
    void ignore()
    {
      //空函数,不进行操作
    }
    
    void startPlay()
    {
      //实现具体功能
      printf("开始播放音乐
    ");
      //进入播放状态
      pCurrentState = &PLAY;
    }
    void stopPlay()
    {
      //实现具体功能
      printf("停止播放音乐
    ");
      //进入空闲状态
      pCurrentState = &IDLE;
    }
    
    void pausePlay()
    {
      //实现具体功能
      printf("暂停播放音乐
    ");
      //进入暂停状态
      pCurrentState = &PAUSE;
    }
    
    void resumePlay()
    {
      //实现具体功能
      printf("恢复播放音乐
    ");
      //进入播放状态
      pCurrentState = &PLAY;
    }
    
    
    /***********************************************
    4、定义主程序上下文操作接口,主程序只关心当前状态,不关心状态之间
    是怎么变化的。
    ************************************************/
    
    void onStop();
    void onPlayOrPause();
    
    State context = {
      onStop,
      onPlayOrPause
    };
    
    void onStop(State *pThis)
    {
      pCurrentState->stop(pThis);
    }
    
    void onPlayOrPause(State *pThis)
    {
      pCurrentState->palyOrPause(pThis);
    }
    
    
    /***********************************************
    5、初始化系统当前状态指针,其实就是指定系统的起始状态
    ************************************************/
    
    void init()
    {
      pCurrentState = &IDLE;
    }
    
    /***********************************************
    6、主程序通过上下文操作接口来控制系统当前状态的变化
    ************************************************/
    void main()
    {
      init();
      context.palyOrPause();//播放
      context.palyOrPause();//暂停
      context.palyOrPause();//播放
      context.stop();//停止
    }
    
    
    

    • 由于某个事件触发的状态切换

    转载自https://www.jianshu.com/p/917c0fb8778b

    /*1.定义状态数据的枚举类型*/
    enum year_state
    {
        SPRING = 1,
        SUMMER,
        AUTUMN,
        WINTER
    };
    
    /*2.定义事件的枚举类型*/
    enum year_event
    {
        EVENT1 = 1,
        EVENT2,
        EVENT3,
        EVENT4,
    };
    
    /*3.定义状态表的数据类型*/
    typedef struct FsmTable_s
    {
        int event;   //事件
        int CurState;  //当前状态
        void (*eventActFun)();  //函数指针
        int NextState;  //下一个状态
    }FsmTable_t;
    
    /*4.定义处理函数及建立状态表*/
    void spring_thing()
    {
        printf("this is spring
    ");
    }
    void summer_thing()
    {
        printf("this is summer
    ");
    }
    void autumn_thing()
    {
        printf("this is autumn
    ");
    }
    void winter_thing()
    {
        printf("this is winter
    ");
    }
    
    FsmTable_t year_table[] =
    {
        //{到来的事件,当前的状态,将要要执行的函数,下一个状态}
        { EVENT1,  SPRING,    summer_thing,  SUMMER },
        { EVENT2,  SUMMER,    autumn_thing,  AUTUMN },
        { EVENT3,  AUTUMN,    winter_thing,  WINTER },
        { EVENT4,  WINTER,    spring_thing,  SPRING },
        //add your codes here
    };
    
    
    /*5.状态机类型,及状态机接口函数*/
    
    /*状态机类型*/
    typedef struct FSM_s{
        int curState;//当前状态
        FsmTable_t * pFsmTable;//状态表
        int size;//表的项数
    }FSM_t;
    
    /*状态机注册,给它一个状态表*/
    void FSM_Regist(FSM_t* pFsm, FsmTable_t* pTable)
    {
        pFsm->pFsmTable = pTable;
    }
    
    /*状态迁移*/
    void FSM_StateTransfer(FSM_t* pFsm, int state)
    {
        pFsm->curState = state;
    }
    
    /*事件处理*/
    void FSM_EventHandle(FSM_t* pFsm, int event)
    {
        FsmTable_t* pActTable = pFsm->pFsmTable;
        void (*eventActFun)() = NULL;  //函数指针初始化为空
        int NextState;
        int CurState = pFsm->curState;
        int g_max_num = pFsm->size;
        int flag = 0; //标识是否满足条件
        int i;
    
        /*获取当前动作函数*/
        for (i = 0; i<g_max_num; i++)
        {
            //当且仅当当前状态下来个指定的事件,我才执行它
            if (event == pActTable[i].event && CurState == pActTable[i].CurState)
            {
                flag = 1;
                eventActFun = pActTable[i].eventActFun;
                NextState = pActTable[i].NextState;
                break;
            }
        }
    
    
        if (flag) //如果满足条件了
        {
            /*动作执行*/
            if (eventActFun)
            {
                eventActFun();
            }
    
            //跳转到下一个状态
            FSM_StateTransfer(pFsm, NextState);
        }
        else
        {
            printf("there is no match
    ");
        }
    }
    
    

    测试程序代码为

    /*state.c*/
    #include <stdio.h>
    
    
    enum year_state{
        SPRING = 1,
        SUMMER,
        AUTUMN,
        WINTER
    };
    
    
    
    enum year_event{
        EVENT1 = 1,
        EVENT2,
        EVENT3,
        EVENT4,
    };
    
    
    typedef struct FsmTable_s{
        int event;   //事件
        int CurState;  //当前状态
        void (*eventActFun)();  //函数指针
        int NextState;  //下一个状态
    }FsmTable_t;
    
    
    
    
    void spring_thing()
    {
        printf("this is spring
    ");
    }
    void summer_thing()
    {
        printf("this is summer
    ");
    }
    void autumn_thing()
    {
        printf("this is autumn
    ");
    }
    void winter_thing()
    {
        printf("this is winter
    ");
    }
    
    
    FsmTable_t year_table[] =
    {
        //{到来的事件,当前的状态,将要要执行的函数,下一个状态}
        { EVENT1,  SPRING,    summer_thing,  SUMMER },
        { EVENT2,  SUMMER,    autumn_thing,  AUTUMN },
        { EVENT3,  AUTUMN,    winter_thing,  WINTER },
        { EVENT4,  WINTER,    spring_thing,  SPRING },
        //add your codes here
    };
    
    
    typedef struct FSM_s{
        int curState;//当前状态
        FsmTable_t * pFsmTable;//状态表
        int size;//表的项数
    }FSM_t;
    
    /*状态机注册,给它一个状态表*/
    void FSM_Regist(FSM_t* pFsm, FsmTable_t* pTable)
    {
        pFsm->pFsmTable = pTable;
    }
    
    /*状态迁移*/
    void FSM_StateTransfer(FSM_t* pFsm, int state)
    {
        pFsm->curState = state;
    }
    
    /*事件处理*/
    void FSM_EventHandle(FSM_t* pFsm, int event)
    {
        FsmTable_t* pActTable = pFsm->pFsmTable;
        void (*eventActFun)() = NULL;  //函数指针初始化为空
        int NextState;
        int CurState = pFsm->curState;
        int g_max_num = pFsm->size;
        int flag = 0; //标识是否满足条件
        int i;
    
        /*获取当前动作函数*/
        for (i = 0; i<g_max_num; i++)
        {
            //当且仅当当前状态下来个指定的事件,我才执行它
            if (event == pActTable[i].event && CurState == pActTable[i].CurState)
            {
                flag = 1;
                eventActFun = pActTable[i].eventActFun;
                NextState = pActTable[i].NextState;
                break;
            }
        }
    
    
        if (flag) //如果满足条件了
        {
            /*动作执行*/
            if (eventActFun)
            {
                eventActFun();
            }
    
            //跳转到下一个状态
            FSM_StateTransfer(pFsm, NextState);
        }
        else
        {
            printf("there is no match
    ");
        }
    }
    
    
    int main()
    {
        FSM_t year_fsm;
        FSM_Regist(&year_fsm,year_table);
        year_fsm.curState = SPRING;
        year_fsm.size = sizeof(year_table)/sizeof(FsmTable_t);
    
    
        printf("
    -------1--init spring------
    ");
        printf("state:%d
    ",year_fsm.curState);
    
        printf("
    -------2--spring->summer------
    ");
        FSM_EventHandle(&year_fsm,EVENT1);
        printf("state:%d
    ",year_fsm.curState);
    
        printf("
    -------3--summer->autumn------
    ");
        FSM_EventHandle(&year_fsm,EVENT2);
        printf("state:%d
    ",year_fsm.curState);
    
        printf("
    -------4--autumn->winter------
    ");
        FSM_EventHandle(&year_fsm,EVENT3);
        printf("state:%d
    ",year_fsm.curState);
    
        printf("
    -------5--winter->spring------
    ");
        FSM_EventHandle(&year_fsm,EVENT4);
        printf("state:%d
    ",year_fsm.curState);
    
        printf("
    -------6--receive EVENT2 not EVENT1------
    ");
        FSM_EventHandle(&year_fsm,EVENT2);
        printf("state:%d
    ",year_fsm.curState);
    
        return 0;
    }
    
    
  • 相关阅读:
    网络编程中 TCP 半开连接和TIME_WAIT 学习
    redis中的小秘密和持久化小细节
    排序
    Es官方文档整理-3.Doc Values和FieldData
    Es官方文档整理-2.分片内部原理
    Elasticsearch doc_value认识
    路边停靠 贴边停车不蹭轮胎的技巧
    mybatis 连接数据库
    putIfAbsent
    Hive与HBase区别 大墨垂杨
  • 原文地址:https://www.cnblogs.com/wcw12580/p/14173908.html
Copyright © 2020-2023  润新知