qpc层次状态机源码分析--Apple的学习笔记 一,前言 二,QHsm层次状态机源码分析 三,小结

一,前言

轻量级状态机软件QM入门--Apple的学习笔记中可以看出,我已经入门工具的使用及集成调试,那么可以比喻为昨天我已经拿到了旅游路线图以及到达车站。今天高铁就出发了,我的探秘之旅开始啦~

二,QHsm层次状态机源码分析

1. 用源码来集成整个工程
一开始编译不过,后来看了编译库用的makefile,原来src中不是所有c代码都参与编译的。只有需要如下文件就编译过了(当前我选择的是win32单线程的port文件夹win32-pv),宏定义添加了_DEBUG

-- qpc
    |-- include
    |   |-- qassert.h
    |   |-- qep.h
    |   |-- qequeue.h
    |   |-- qf.h
    |   |-- qf_pkg.h
    |   |-- qk.h
    |   |-- qmpool.h
    |   |-- qpc.h
    |   |-- qs.h
    |   |-- qs_dummy.h
    |   |-- qs_pkg.h
    |   |-- qstamp.c
    |   |-- qstamp.h
    |   |-- qv.h
    |   `-- qxk.h
    |-- ports
    |   `-- win32-qv
    |       |-- qep_port.h
    |       |-- qf_port.c
    |       |-- qf_port.h
    |       |-- qs_port.h
    |       |-- qwin_gui.h
    |       `-- safe_std.h
    `-- src
        `-- qf
            |-- qep_hsm.c
            |-- qep_msm.c
            |-- qf_act.c
            |-- qf_actq.c
            |-- qf_defer.c
            |-- qf_dyn.c
            |-- qf_mem.c
            |-- qf_ps.c
            |-- qf_qact.c
            |-- qf_qeq.c
            |-- qf_qmact.c
            `-- qf_time.c

2. 有了源码调试分析代码我理解会更加方便。
可以比喻为我还雇用了一个导游,接着开始看代码,发现设计思路确实比较容易理解的。
简单来说就是一个事件处理驱动,类似于我之前看的windows GUI的设计,和SDL2或者QT框架类似。就是等待事件放入队列,然后while(1)中处理事件。
3. 特别之处
我之前自己设计稍微复杂的状态机,也就是用表驱动法,这里的状态机处理用了while来判断状态机返回值为Q_HANDLED( )则退出while。这样就可以想象为在matlab simulink中模拟运行的时候,一个个状态机会变颜色。这个while只要不返回退出状态,那么就一直可以调用每个状态进行处理。
3.1 重点分析
typedef QState (* QStateHandler )(void * const me, QEvt const * const e);
a. 每个状态处理函数有 2 个参数:状态机指针 me 和指向 QEvent的恒指针 e 。它返回QState ,携带的事件处理状态的信息会交给事件处理器。
b. 事件指针被声明为 const,以避免在状态处理函数内部修改事件。换句话说,状态处理函数被确保对事件只有只读操作。
c. 从一个层次式状态处理函数返回的 Q_HANDLED( ) 通知 QEP 事件处理器一个特定的事件已经
被处理了。
d. 状态转换完成后返回宏 Q_TRAN( ) ,它需要使用转换的目标作为参数。
e. 如果没有 case 被执行,状态处理函数将返回宏 Q_SUPER( ),它指派超状态并把它通知事件处理器。
f. 关于每个状态都有一个default返回到它的上一层次状态机。
g. 关于进入层次状态机要先进入高状态再进入低层级状态,但是我们的继承设计方法default是返回的上一级状态,所以正好相反,解决方法就是先利用default返回值来记录上一级的函数到临时数组path中,最后再反向执行path即可。所以我觉得这个default返回上级状态机,设计的非常好。

            ip = 0;
            path[0] = me->temp.fun;

            /* find superstate */
            (void)QEP_TRIG_(me->temp.fun, QEP_EMPTY_SIG_);

            while (me->temp.fun != t) {
                ++ip;
                path[ip] = me->temp.fun;
                /* find superstate */
                (void)QEP_TRIG_(me->temp.fun, QEP_EMPTY_SIG_);
            }
            me->temp.fun = path[0];

            /* entry path must not overflow */
            Q_ASSERT_ID(410, ip < QHSM_MAX_NEST_DEPTH_);

            /* retrace the entry path in reverse (correct) order... */
            do {
                QEP_ENTER_(path[ip], qs_id); /* enter path[ip] */
                --ip;
            } while (ip >= 0);

            t = path[0]; /* current state becomes the new source */

三,小结

由于我写代码不会用while1来判断逻辑退出,因为我觉得while1是危险的,万一逻辑错误就会导致一直循环无法退出,所以我在项目中不会采用类似的设计,但是我之前看littlevgl等GUI好像都是类似设计,所以若让我设计GUI引擎那么就另当别论了。
另外,今天的源码就用了轻量级的事件驱动框架(pf)框架。任务调度微内核(pk,pv,pxk)和实时跟踪调试器(ps)都没用到。虽然我不会把此状态机框架列入我的学习清单,因为我不用while来做逻辑判断,但是其它模块的代码设计和实现,后续我还是会抽空了解下,一定还有可圈可点的地方。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章