youkebing 发表于 2016-5-1 22:24:08

开源最简状态机框架

我们写状态机时,其实还是很烦的,我觉得PT在这个方面是最直观的,但在一些特定领域并不合适。QP也是大家比较推崇的,但是感觉有点大(至少我觉得),刚好逛github碰到一个叫SM的库,据说是模仿QP的,看起来感觉不错,就改写了一点,我在windows下写了个demo,我觉得在arm下也应该是一样的,大家不妨一起探讨,研究!

youkebing 发表于 2016-5-1 22:27:03

原作者地址
https://github.com/leijian001/sm
源码

负西弱 发表于 2016-5-1 22:44:45

Mark,有讲解就更好了

youkebing 发表于 2016-5-1 22:49:42

负西弱 发表于 2016-5-1 22:44
Mark,有讲解就更好了

我改写的那个非常简单,就几行代码(ssm.c),觉得有问题,可以直接提问。我们一起探讨。

maimaige 发表于 2016-5-1 22:58:16

mark 一下 状态机

youkebing 发表于 2016-5-1 23:05:52

maimaige 发表于 2016-5-1 22:58
mark 一下 状态机

光mark不行啊,如果能一起探讨就最好了{:lol:} 压缩包里面包含一个demo和可执行文件,可以非常容易测试。我过几天再写一个简单的arm测试例子。

maimaige 发表于 2016-5-1 23:12:16

youkebing 发表于 2016-5-1 23:05
光mark不行啊,如果能一起探讨就最好了 压缩包里面包含一个demo和可执行文件,可以非常容易测试。 ...

好啊,我还想找人指导一下,消息处理,协议处理的,框架呢,一般是串口方面的控制协议

youkebing 发表于 2016-5-1 23:19:41

maimaige 发表于 2016-5-1 23:12
好啊,我还想找人指导一下,消息处理,协议处理的,框架呢,一般是串口方面的控制协议 ...

指导谈不上,大家起探讨

yikuang 发表于 2016-5-2 00:29:26

先收下,谢谢!

yuntian 发表于 2016-5-2 10:44:16

晚上下来研究下

kenson 发表于 2016-5-2 10:48:11

先了解一下再问

youkebing 发表于 2016-5-2 14:55:31

大家可以先想一些场景,我来按照这个场景写一些简单demo,当然要比较简单,有一定的代表性。或者用其他模式写起来不太顺手的。

what007 发表于 2016-5-2 15:17:59

收藏了,谢谢。

fchen2 发表于 2016-5-2 17:20:52

定时器超时消息如何处理?

youkebing 发表于 2016-5-2 19:06:22

fchen2 发表于 2016-5-2 17:20
定时器超时消息如何处理?

定时器超时不再这个框架内,
最简单的方式是实现一个定时节拍,而后设定自己要延时的节拍数,在那个节拍处理器中不停的减,为0时,给框架发一个消息。

bj232 发表于 2016-5-3 11:07:02

先收下,谢谢!

笑笑我笑了 发表于 2016-5-3 11:10:05

用switch-case写最简单,但是难维护啊。

youkebing 发表于 2016-5-3 12:13:30

笑笑我笑了 发表于 2016-5-3 11:10
用switch-case写最简单,但是难维护啊。

你看看我这个,是不是还可以?

笑笑我笑了 发表于 2016-5-3 12:21:05

youkebing 发表于 2016-5-3 12:13
你看看我这个,是不是还可以?

够简洁,但是状态不能嵌套啊。

youkebing 发表于 2016-5-3 12:41:43

笑笑我笑了 发表于 2016-5-3 12:21
够简洁,但是状态不能嵌套啊。

呵呵,太复杂可能就失去意义了,一般我也不喜欢过于庞大的框架

sgweilong 发表于 2016-5-3 12:51:25

下载了,研究研究
写好程序不容易

笑笑我笑了 发表于 2016-5-3 12:52:22

youkebing 发表于 2016-5-3 12:41
呵呵,太复杂可能就失去意义了,一般我也不喜欢过于庞大的框架

框架复杂呢,应用可能会简单,但是框架简单呢,应用会复杂。
怎么取舍,看你自己啦。

youkebing 发表于 2016-5-3 12:59:19

笑笑我笑了 发表于 2016-5-3 12:52
框架复杂呢,应用可能会简单,但是框架简单呢,应用会复杂。
怎么取舍,看你自己啦。
...

每个人看法不一样,所以也要有取舍,如果更复杂的话,其实直接上os,但是许多时候,我们不需要那么大。那么这个时候,可能一个简单的框架更合适

EngKing 发表于 2016-5-3 13:01:03

思路都差不多

youkebing 发表于 2016-5-3 17:03:22

EngKing 发表于 2016-5-3 13:01
思路都差不多

请发表高见,有没有类似的也发表出来,大家一起交流啊

我是一个大白菜 发表于 2016-5-3 21:20:33

谢谢分享,mark状态机

huangqi412 发表于 2016-5-4 09:59:58

给楼主顶下

RAMILE 发表于 2016-5-4 10:11:15

看不动,求LZ写一个定时炸弹demo,洗衣机也行

xuxi2009 发表于 2016-5-4 10:17:25

很简练的一个

wt3333 发表于 2016-5-4 11:35:55

最近正在研究状态机谢谢分享

youkebing 发表于 2016-5-4 12:09:39

RAMILE 发表于 2016-5-4 10:11
看不动,求LZ写一个定时炸弹demo,洗衣机也行

不知道你的定时是啥样的?你写个定时的简单需求呢?

笑笑我笑了 发表于 2016-5-4 12:34:46

youkebing 发表于 2016-5-4 12:09
不知道你的定时是啥样的?你写个定时的简单需求呢?

void TIMER_IRQ(void)
{
        me->tick--;
}

STATE1
{
        switch(evt)
        {
                case xx:
                        if(me->tick == xxx)
                                xxxx
                        break;

                default:
                        break;
        }
}

youkebing 发表于 2016-5-4 12:49:06

笑笑我笑了 发表于 2016-5-4 12:34
void TIMER_IRQ(void)
{
        me->tick--;


你这个代码好难懂,似乎就一个状态,一个状态还叫状态机吗?麻烦些个详细的需求呢?

262619890 发表于 2016-5-4 13:54:41

youkebing 发表于 2016-5-4 12:49
你这个代码好难懂,似乎就一个状态,一个状态还叫状态机吗?麻烦些个详细的需求呢? ...

事件+时间片轮转

308594151 发表于 2016-5-4 15:36:27

mark一下

showgu 发表于 2016-5-4 18:45:40

之前用过不少pt,感觉挺好,也没出过什么问题,不知道这个怎么样,下载看看,谢谢楼主。

wlmwwx 发表于 2016-5-4 20:20:21

谢谢,收藏了,以后研究一下。

cyberkyg 发表于 2016-5-5 09:22:47

mark            .

watcherone 发表于 2016-5-6 10:14:06

看了一下,里面的超状态什么意思啊。没弄懂

klxx68 发表于 2016-5-6 11:07:58

下下来,学习一下

youkebing 发表于 2016-5-6 11:27:46

watcherone 发表于 2016-5-6 10:14
看了一下,里面的超状态什么意思啊。没弄懂

你说的什么抄状态?我不知道你说的是哪一块

watcherone 发表于 2016-5-6 12:13:20

HSM中的SM_RET_SUPER,/*!< event passed to superstate to handle */

youkebing 发表于 2016-5-6 12:16:46

因为这是参照QP的,所以有些定义是没有用的,简化的,忽略掉就可以,看好原理就可以了

watcherone 发表于 2016-5-6 12:19:13

自己新手,基础不够。就结合QP-nano源码对这个框架做了一个详细的注释。希望大家有用,可能有点错误。但是整体还是正确的

youkebing 发表于 2016-5-6 12:22:29

watcherone 发表于 2016-5-6 12:19
自己新手,基础不够。就结合QP-nano源码对这个框架做了一个详细的注释。希望大家有用,可能有点错误。但是 ...

呵呵,大家一起学习,我没有全部看,我只找了一点最简单的看了下

richie550 发表于 2016-5-6 12:31:53

下了,慢慢看{:handshake:}

wswh2o 发表于 2016-5-6 15:57:28

标签:状态机

自由飞儿 发表于 2016-5-11 10:27:36

收藏备用

sxgtc 发表于 2016-5-13 15:33:57

还有此等好东西,收藏了~

ywlzh 发表于 2016-5-14 11:11:10

楼主,我很好奇,你那个文件夹里的.exe是怎么生成的,这个可执行文件太给力了。我好像意识到了它背后的力量

youkebing 发表于 2016-5-14 11:14:48

ywlzh 发表于 2016-5-14 11:11
楼主,我很好奇,你那个文件夹里的.exe是怎么生成的,这个可执行文件太给力了。我好像意识到了它背后的力量 ...

一点也不好奇啊,那个是gcc编译的

BigWolf 发表于 2016-5-14 13:28:12

收藏学习。               

guolun 发表于 2016-5-14 14:05:29

支持状态机的嵌套吗?

zhenghe 发表于 2016-5-14 14:13:27

来个更简单的,只给接口,怎么用,自己琢磨吧,很简单。
/*******************************************************************************
*Copyright(C)2016 by Dreistein<mcu_shilei@hotmail.com>                     *
*                                                                            *
*This program is free software; you can redistribute it and/or modify it   *
*under the terms of the GNU Lesser General Public License as published   *
*by the Free Software Foundation; either version 3 of the License, or      *
*(at your option) any later version.                                       *
*                                                                            *
*This program is distributed in the hope that it will be useful, but       *
*WITHOUT ANY WARRANTY; without even the implied warranty of                *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU          *
*General Public License for more details.                                  *
*                                                                            *
*You should have received a copy of the GNU Lesser General Public License*
*along with this program; if not, see http://www.gnu.org/licenses/.      *
*******************************************************************************/

//! \note do not move this pre-processor statement to other places
#define __EVENT_FSM_C__

/*============================ INCLUDES ======================================*/
#include ".\app_cfg.h"

/*============================ MACROS ========================================*/
/*============================ MACROFIED FUNCTIONS ===========================*/
/*============================ TYPES =========================================*/
typedef uint8_t (fn_state_t)(void *pArg);
typedef fn_state_t *event_fsm_stack_t;

typedef struct {
    event_fsm_stack_t *pStack;
    uint8_t   chStackSize;
    uint8_t   chSP;
    uint8_t   chCurrentSP;
} event_fsm_tcb_t;

/*============================ PROTOTYPES ====================================*/
/*============================ LOCAL VARIABLES ===============================*/
/*============================ GLOBAL VARIABLES ==============================*/
/*============================ IMPLEMENTATION ================================*/
bool event_fsm_init(event_fsm_tcb_t *ptTCB,
                  event_fsm_stack_t *pStack,
                  uint8_t chStackSize,
                  fn_state_t *pInitState)
{
    ptTCB->pStack       = pStack;
    ptTCB->chStackSize= chStackSize;
    ptTCB->chSP         = 0;
    ptTCB->chCurrentSP= 0;
    ptTCB->pStack    = pInitState;

    return true;
}

bool event_fsm_transfer_to(event_fsm_tcb_t *ptTCB, fn_state_t *pState)
{
    ptTCB->chSP = ptTCB->chCurrentSP;
    ptTCB->pStack = pState;
   
    return true;
}

bool event_fsm_transfer_to_end(event_fsm_tcb_t *ptTCB)
{
    ptTCB->chSP = ptTCB->chCurrentSP;
    ptTCB->pStack = NULL;

    if (ptTCB->chSP) {
      ptTCB->chSP--;
    } else {
      //! event fsm run complete.
    }
   
    return true;
}

bool event_fsm_to_current_Level(event_fsm_tcb_t *ptTCB)
{
    ptTCB->chSP = ptTCB->chCurrentSP;
    return true;
}

bool event_fsm_transfer_to_uper(event_fsm_tcb_t *ptTCB, fn_state_t *pState)
{
    if (ptTCB->chStackSize == (ptTCB->chSP + 1)) {
      return false;
    }

    ptTCB->chSP++;
    ptTCB->pStack = pState;
   
    return true;
}

//! internal use only.
bool event_fsm_current_level_decrease(event_fsm_tcb_t *ptTCB)
{
    if (ptTCB->chCurrentSP) {
      ptTCB->chCurrentSP--;
      return true;
    } else {
      return false;
    }
}

bool event_fsm_transfer_to_lower(event_fsm_tcb_t *ptTCB, fn_state_t *pState)
{
    if (!event_fsm_current_level_decrease(ptTCB)) {
      return false;
    }

    return event_fsm_transfer_to(ptTCB, pState);
}

fn_state_t *event_fsm_get_current_state(event_fsm_tcb_t *ptTCB)
{
    return ptTCB->pStack;
}

fn_state_t *event_fsm_get_state(event_fsm_tcb_t *ptTCB)
{
    ptTCB->chCurrentSP = ptTCB->chSP;
    return ptTCB->pStack;
}


/* EOF */

用在DTU上,AT指令的处理。

youkebing 发表于 2016-5-14 14:50:40

zhenghe 发表于 2016-5-14 14:13
来个更简单的,只给接口,怎么用,自己琢磨吧,很简单。

用在DTU上,AT指令的处理。 ...

似乎没有我那个简单{:titter:}

youkebing 发表于 2016-5-14 14:54:28

guolun 发表于 2016-5-14 14:05
支持状态机的嵌套吗?

这个是很简单的,不支持嵌套,当然,你可以修改,

zhenghe 发表于 2016-5-14 15:12:53

youkebing 发表于 2016-5-14 14:50
似乎没有我那个简单

你只做了fsm,我只做了hsm,人家都做了好吧。原理都那样,不同实现罢了。

youkebing 发表于 2016-5-14 15:29:31

zhenghe 发表于 2016-5-14 15:12
你只做了fsm,我只做了hsm,人家都做了好吧。原理都那样,不同实现罢了。 ...

好吧,我错了

lushanlq 发表于 2016-5-14 18:47:27

fsmhsm是有限状态机吗?能否详细说明一下?

chunyu 发表于 2016-5-14 20:17:00

好东西 下载下来学习

creep 发表于 2016-5-16 19:11:58

感谢分享,下载学习!

tianyime 发表于 2016-5-17 08:47:12

现在我都是用switch写。。

youkebing 发表于 2016-5-17 10:55:50

tianyime 发表于 2016-5-17 08:47
现在我都是用switch写。。

这个由很多方法,我这个仅仅是一个例子,switch当然是可以得。

308594151 发表于 2016-5-17 14:58:55

mark一下

altim_li 发表于 2016-5-17 15:40:50

学习一下,不错。

chencc8 发表于 2016-5-25 09:35:50

有事件框架么,还是说只是状态机框架。只是状态机的话将QP里的qfsm弄出来就是了。

leiyitan 发表于 2016-10-19 06:57:32

看来除了switch,我需要花点时间学习一下了

HalenYU 发表于 2016-10-24 20:29:45

支持创新

end2000 发表于 2016-10-24 20:57:57

学习。简单的状态机。

a7458969 发表于 2016-11-2 16:38:16

赶脚是一坨shit,这样封装和重新写有什么区别。

bigwei 发表于 2017-1-5 17:27:05

谢谢分享 下来看看

成就与价值 发表于 2017-1-5 19:23:31

这个可以有,搞来研究学习下

xxzzhy 发表于 2017-1-5 23:05:07

还可以,学习下下

xf331785508 发表于 2017-1-6 13:21:01

压缩包不利于直观看代码探讨,我先把你的代码贴出来。再探讨。

//ssm.c
#include "ssm.h"

ssm_event_t ssm_reserved_event[] = {
    { SSM_EMPTY_SIG, 0 },
    { SSM_ENTRY_SIG, 0 },
    { SSM_EXIT_SIG,0 },
    { SSM_INIT_SIG,0 },
    { SSM_USER_SIG,0 },
};

static ssm_ret_t null_state_handler(ssm_t *fsm, ssm_event_t const *e) {
    return SSM_EMPTY_SIG;
}

void fsm_ctor(ssm_t *me, ssm_state_handler_t init) {
    me->state = null_state_handler;
    me->temp= init;
    fsm_dispatch(me, &ssm_reserved_event);
}

void fsm_dispatch(ssm_t *me, ssm_event_t *e) {
    ssm_ret_t ret;

    ret = (me->temp)(me, e);
    if (SSM_RET_TRAN == ret) {
      SSM_EXIT(me, me->state);
      SSM_ENTRY(me, me->temp);
      me->state = me->temp;
    }
}

//ssm.h

#ifndef __SM_H__
#define __SM_H__

#ifdef __cplusplus
extern "C" {
#endif


typedef int ssm_sig_t;

/**
* @bref 状态机事件
*/
typedef struct ssm_event_s {
    ssm_sig_t sig;
    void *event;
} ssm_event_t;

/**
* @bref 状态处理函数返回值, 指示事件被怎么处理了
*/
typedef unsigned char ssm_ret_t;

//struct ssm_fsm_s;
typedef struct ssm_s ssm_t;
typedef ssm_ret_t (*ssm_state_handler_t)(ssm_t *fsm, ssm_event_t const *e);

/**
* @bref 状态机
*/
struct ssm_s {
    ssm_state_handler_t state;
    ssm_state_handler_t temp;
};

/**
* @bref 状态机返回值
*
*/
enum {
    SSM_RET_HANDLED,
    SSM_RET_IGNORE,
    SSM_RET_UNHANDLED,

    SSM_RET_TRAN,
    SSM_RET_SUPER,
};
#define SSM_RET_CAST(x)( (ssm_ret_t)(x) )

#define SSM_HANDLED()      SSM_RET_CAST(SSM_RET_HANDLED)
#define SSM_IGNORE()         SSM_RET_CAST(SSM_RET_IGNORE)
#define SSM_UNHANDLED()      SSM_RET_CAST(SSM_RET_UNHANDLED)

#define SSM_TRAN(me, target) \
            ((me)->temp = (target), SSM_RET_CAST(SSM_RET_TRAN))
#define SSM_SUPER(me, super) \
            ((me)->temp = (super), SSM_RET_CAST(SSM_RET_SUPER))

#define SSM_SIG(e) (e->sig)

enum ssm_reserved_sig {
    SSM_EMPTY_SIG = -5,
    SSM_ENTRY_SIG = -4,
    SSM_EXIT_SIG= -3,
    SSM_INIT_SIG= -2,
    SSM_USER_SIG= -1,
};
extern ssm_event_t ssm_reserved_event[];
#define SSM_TRIG(me, state, sig)   ((state)(me, &ssm_reserved_event))
#define SSM_ENTRY(me, state)         SSM_TRIG(me, state, SSM_ENTRY_SIG)
#define SSM_EXIT(me, state)      SSM_TRIG(me, state, SSM_EXIT_SIG)

void fsm_ctor(ssm_t *me, ssm_state_handler_t init);
void fsm_dispatch(ssm_t *me, ssm_event_t *e);

#ifdef __cplusplus
}
#endif

#endif


//main.c

#include "ssm.h"
#include <windows.h>
#include <stdio.h>

#define SSM_TIMEOUT_SIG0
static ssm_ret_t off_state_handler(ssm_t *fsm, ssm_event_t const *e);
static ssm_ret_t on_state_handler(ssm_t *fsm, ssm_event_t const *e);

static ssm_ret_t off_state_handler(ssm_t *fsm, ssm_event_t const *e) {
    ssm_ret_t status;

    switch (SSM_SIG(e)) {
      case SSM_ENTRY_SIG: {
            printf("close led\r\n");
            status = SSM_HANDLED();
            break;
      }
      case SSM_TIMEOUT_SIG: {
            printf("off time ev\r\n");
            status = SSM_TRAN(fsm, &on_state_handler);
            break;
      }
      default: {
            status = SSM_HANDLED();
            break;
      }
    }
    return status;
}
static ssm_ret_t on_state_handler(ssm_t *fsm, ssm_event_t const *e) {
    ssm_ret_t status;

    switch (SSM_SIG(e)) {
      case SSM_ENTRY_SIG: {
            printf("open led\r\n");
            status = SSM_HANDLED();
            break;
      }
      case SSM_TIMEOUT_SIG: {
            printf("on time ev\r\n");
            status = SSM_TRAN(fsm, &off_state_handler);
            break;
      }
      default: {
            status = SSM_HANDLED();
            break;
      }
    }
    return status;
}
int main(int argc, char **argv) {
    ssm_event_t ev = {SSM_TIMEOUT_SIG, 0};
    ssm_t me;

    fsm_ctor(&me, off_state_handler);

    while (1) {
      Sleep(1000);
      fsm_dispatch(&me, &ev);
    }
    return 0;
}

xf331785508 发表于 2017-1-6 13:28:58

代码看了一下,指针利用非常轻熟。
1.但这样的状态机缺少异步事件处理,还有就是单一状态下事件的时间可能远远大于TIMEOUT的值,造成其它事件直接死等。
2.适合于处理简单事务,单一状态处理函数中可做的事务越简单,耗时越小,整体状态循环就快。
3.代码的偶合性太高了,如果事件状态超过10个,20个,50个的时候,状态迁移就需要表来维护了,代码的后期维护难度将直线上升,非常困难。

我简单列一下我的观点:
1.将所要实现的功能分解成任务,各任务下再划分出不同状态,状态表要全局的;
2.状各任务配备一份状态控制表,每一状态完成后检查是否需要跳转任务状态;
3.设定定时器,处理异步事务,中断事务。

youkebing 发表于 2017-1-6 22:21:37

谢谢你的建议

jorry 发表于 2017-4-10 22:07:42

学习一下!谢谢

end2000 发表于 2017-5-14 19:45:19

这个最简状态机框架可以这样用:
- 定义一个FSM,再定义这个FSM的一个通用消息变量Evt,把FSM+Evt封装起来,这样就得到一个AO,然后程序里可以有多个AO,它们互相通过发送消息进行协作。
- 每个AO再增加一个定时器消息变量EvtTimer,可以实现延时动作。

由于消息变量Evt可以看成是长度为1的队列,所以AO可能丢失消息。但如果把Evt扩展为长度大于1的队列,就又回到QPN的完整设计。

所以,简单的项目可以用这个框架,实际工程里建议直接用QPN框架,它基本功能都齐了。

ericw2012 发表于 2017-5-30 15:00:35

学习学习。。。。

gaowh 发表于 2018-6-23 17:03:37

很不错的东西,状态机在嵌入式简直是无敌的存在。

guolun 发表于 2018-6-23 21:44:46

这个状态机也做过产品,就是事件的生成和派送繁琐。用QPN较省事。

propor123 发表于 2018-6-23 21:46:49

非常不错。

pchf005 发表于 2018-11-24 10:53:02

好好学习天天向上
页: [1]
查看完整版本: 开源最简状态机框架