• 有限状态机的实现


    有限状态机的实现

    对于有限状态机的几点说明

    • fsm应该是一个死循环

    FSM的处理机制

    • 状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:
    • ①现态:是指当前所处的状态。
    • ②条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
    • ③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
      ④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了
    • FSM的实现方式:

    1、switch/case或者if/else
    这无意是最直观的方式,使用一堆条件判断,会编程的人都可以做到,对简单小巧的状态机来说最合适,但是毫无疑问,这样的方式比较原始,对庞大的状态机难以维护。

    2、状态表
    维护一个二维状态表,横坐标表示当前状态,纵坐标表示输入,表中一个元素存储下一个状态和对应的操作。这一招易于维护,但是运行时间和存储空间的代价较大。

    简单状态机的实现(if/else)

    int main()
    {
        int state = GET_UP;
        //lbq的一天
        while (1)
        {
            if (state == GET_UP)
            {
                GetUp(); //具体调用的函数
                state = GO_TO_SCHOOL;  //状态的转移
            }
            else if (state == GO_TO_SCHOOL)
            {
                GotoSchool();
                state = HAVE_LUNCH;
            }
            else if (state == HAVE_LUNCH)
            {
                HaveLunch();
            }
            else if (state == SLEEP)
            {
                GotoBed();
                state = GET_UP;
            }
        }
    
        return 0;
    }
    
    • 这种实现方式适合小巧的状态机,这种实现方式难以维护。

    在网上学习了一下状态表实现的状态机

    • 参考代码如下(本代码是参考链接中的代码供参考)
    #include <stdio.h>
    //#include <windows.h> //windows
    #include <unistd.h>  //linux
    
    //比如我们定义了小明一天的状态如下
    enum
    {
    	GET_UP,
    	GO_TO_SCHOOL,
    	HAVE_LUNCH,
    	DO_HOMEWORK,
    	SLEEP,
    };
    
    //我们定义的事件有以下几个
    enum
    {
    	EVENT1 = 1,
    	EVENT2,
    	EVENT3,
    };
    
    
    typedef struct FsmTable_s
    {
    	int event;   //事件
    	int CurState;  //当前状态
    	void (*eventActFun)();  //函数指针
    	int NextState;  //下一个状态
    }FsmTable_t;
    
    
    typedef struct FSM_s
    {
    	FsmTable_t* FsmTable;   //指向的状态表
    	int curState;  //FSM当前所处的状态
    
    }FSM_t;
    
    
    int g_max_num;  //状态表里含有的状态个数
    
    
    
    void GetUp()
    {
    	// do something
    	printf("xiao ming gets up!
    ");
    
    }
    
    void Go2School()
    {
    	// do something
    	printf("xiao ming goes to school!
    ");
    }
    
    void HaveLunch()
    {
    	// do something
    	printf("xiao ming has lunch!
    ");
    }
    
    void DoHomework()
    {
    	// do something
    	printf("xiao ming does homework!
    ");
    }
    
    void Go2Bed()
    {
    	// do something
    	printf("xiao ming goes to bed!
    ");
    }
    
    /*状态机注册*/
    void FSM_Regist(FSM_t* pFsm, FsmTable_t* pTable)
    {
    	pFsm->FsmTable = pTable;
    }
    
    /*状态迁移*/
    void FSM_StateTransfer(FSM_t* pFsm, int state)
    {
    	pFsm->curState = state;
    }
    
    
    /*事件处理*/
    void FSM_EventHandle(FSM_t* pFsm, int event)
    {
    	FsmTable_t* pActTable = pFsm->FsmTable;
    	void (*eventActFun)() = NULL;  //函数指针初始化为空
    	int NextState;
    	int CurState = pFsm->curState;
    	int flag = 0; //标识是否满足条件
    
    	/*获取当前动作函数*/
    	for (int 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
    	{
    		// do nothing
    	}
    }
    
    FsmTable_t XiaoMingTable[] =
    {
    	//{到来的事件,当前的状态,将要要执行的函数,下一个状态}
    	{ EVENT1,  SLEEP,           GetUp,        GET_UP },
    	{ EVENT2,  GET_UP,          Go2School,    GO_TO_SCHOOL },
    	{ EVENT3,  GO_TO_SCHOOL,    HaveLunch,    HAVE_LUNCH },
    	{ EVENT1,  HAVE_LUNCH,      DoHomework,   DO_HOMEWORK },
    	{ EVENT2,  DO_HOMEWORK,     Go2Bed,       SLEEP },
    
    	//add your codes here
    };
    
    //初始化FSM
    void InitFsm(FSM_t* pFsm)
    {
    	g_max_num = sizeof(XiaoMingTable) / sizeof(FsmTable_t);
    	pFsm->curState = SLEEP;
    	FSM_Regist(pFsm, XiaoMingTable);
    }
    
    
    //测试用的
    void test(int *event)
    {
    	if (*event == 3)
    	{
    		*event = 1;
    	}
    	else
    	{
    		(*event)++;
    	}
    	
    }
    
    
    int main()
    {
    	FSM_t fsm;
    	InitFsm(&fsm);
    	int event = EVENT1; 
    	//小明的一天,周而复始的一天又一天,进行着相同的活动
    	while (1)
    	{
    		printf("event %d is coming...
    ", event);
    		FSM_EventHandle(&fsm, event);
    		printf("fsm current state %d
    ", fsm.curState);
    		test(&event); 
    		Sleep(1);  //休眠1秒,方便观察
    	}
    
    	return 0;
    }
    
    • 这种形式的有限状态机,适合规模比较大的,后期也易于维护。

    实验成果

    • 写了一个人颓废的一天。

    总结

    • 两种状态机各有优劣,总体来说状态表FSM更好一些。
    • 在学习的过程中,由于能力有限只能做第一种,最简单无脑的状态机。

    思考

    • 昨天还在思考写这个无聊的状态机有什么用,老师上课说的地铁门的例子,我想应该是硬件实现的,也和计算机没关系呀 。为什么非要我们再用编程实现一次呢?
    • 今早起床发现老师在群里给了我们答案。(看完过后也无感),只是觉得这是一次没有时间限制的编程,不像课上的编程测试那样时间紧巴巴的,每次感觉自己都快写完的时候时间到了,纯粹是想锻炼自己编程能力才做的实践。

    参考链接(这是一个Java实现的状态机)

    参考链接

  • 相关阅读:
    Docker从12升级到17ce
    镜像清理和删除
    flask-session 在redis中存储session
    linux后台运行python程序 nohup
    flask 自动切换环境
    Linux SSH登录很慢的解决方法
    docker-compose docker启动工具,容器互联
    为什么企业需要IT资产管理
    sql 中取整,四舍五入取整,向下取整,向上取整。
    sqlalchemy 获取表结构。
  • 原文地址:https://www.cnblogs.com/hpl20155329/p/8036526.html
Copyright © 2020-2023  润新知