Quantum Hierarchical State Machine (量子层级状态机)
Samek量子层级状态机
源代码(qf4net)是用C/C++写的,主要用于嵌入式系统,没有C#版本的,不利于使用。
不同于使用固定大小的数组,新的方法采用数组列表的方式,可以适用于任意复杂度,和深度的层级状态机。
完整翻译文章挺困难的,我还是比较喜欢应用的角度来解读代码,并用于实际工作中。只是一个梳理的过程,随着自己理解的程度,对代码的理解又是不一定对。
 
c – Boost StateCharts与Samek的“量子状态图”的比较
 
原子状态机AFSM介绍
FSM是有限状态自动机(Finite State Machine)的缩写
原子状态机(Atom FSM) 简称AFSM,代表基本状态机
FSM与AFSM
当我们引入FSM的时候,我们假定一个对象有很多个状态,以连接后台的Connection对象为例,可能有disconnected、connecting、reconnecting、connected状态,我们利用FSM可以很容易在多个状态间正确的流转
必须简化FSM模型,使得万物都可以用同一种FSM描述。 犹如四种基本力(或者两仪生四象、四个方向、四个季节、四冲程内燃机)一样,我们可以将FSM简化为四种状态的基本FSM——AFSM。
对于只包含3种状态或者2种状态的对象也可以统一成4种状态,比如启动同步成功,或者停止也是同步成功,仍然可以经历4种状态,只是连续变化一下即可。为何要统一成4状态?这样就可以在组合这些AFSM时做一些抽象的封装,产生级联效果。
基于量子平台的PMU软件及状态机描述
基于量子平台的软件开发方法非常适合用于嵌入式软件领域,其软件设计方法主要通过软件系统结构量子化,软件运行状态量子化和实体静态结构量子化,活动对象动态行为量子化和实体配置量子化等步骤实施.基于量子平台的PMU软件系统中不同的任务和共享资源分别封装在5类活动对象中,活动对象的实现采用量子节拍机制,活动对象相互间的通信采用异步事件交换机制
| Quantum Leaps发布免费状态机建模工具QM | 
|---|
Quantum Leaps发布了一款免费的状态机建模工具QM(QP Modeler),用于设计嵌入式实时应用并无缝地在QP(Quantum Platform,量子平台)实现。
QP是一套状态机的实现框架,支持的RTOS和处理器非常广泛,已经在消费电子、医疗、工业、无线、网络、国防、机器人,汽车等许多领域的实时嵌入式开发中使用。QP的作者Miro Samek的著作《Practical UML Statecharts in C/C++》更是学习状态机建模和实现状态机不可不看的深度教材,第一版2004年由国内出版社引进,第二版中译本也即将在国内出版。
QM是先有QP这个实现框架,再有图形建模工具,图形工具建立在代码框架的基础上。其他状态图工具如IAR VisualState(最新版本6.3.2),还有IBM Rational Rhapsody(最新版本7.5.3)走的是自上而下的路线,也支持状态图的验证和测试,生成的代码和RTOS整合。
现在使用的状态图是由David Harel在1980年代发明提出的,增加了层次、并行等元素,改进了之前常规FSM(有限状态机)复杂性很快增加的缺陷。状态图是面向对象建模中非常重要的要素。对象身上发生的行为导致对象属性值的变化,属性值的变化又导致行为的变化,这种不同行为之间精妙的互动关系,通过状态图展现出来。可以这样说,如果没有把核心的逻辑封装到关键类的状态机中,只能说这样的面向对象建模是“假面向对象”。通过状态图可以帮助开发人员确定一个类合适的责任到底是哪些,往往可以起到缩窄类和外界的接口,加强封装的作用。
资源
http://www.state-machine.com/downloads/index.php#QM
QM下载
http://www.wisdom.weizmann.ac.il/~harel/papers/Statecharts.pdf
1984年David Harel发表的关于状态图的文献:Statecharts: A visual formalism for complex systems
http://www.youtube.com/watch?v=KJ9WaUa_4m0
2010年David Harel在Federated Logic Conference (FLoC)上缅怀图灵奖获得者Amir Pnueli的演讲视频。
(UMLChina,不得转载用于商业用途)
qp-nano代码大餐
/****************************************************************************** Product: QEP-nano implemenation* Last Updated for Version: 4.1.05* Date of the Last Update: Oct 27, 2010** Q u a n t u m L e a P s* ---------------------------* innovating embedded systems** Copyright (C) 2002-2010 Quantum Leaps, LLC. All rights reserved.** This software may be distributed and modified under the terms of the GNU* General Public License version 2 (GPL) as published by the Free Software* Foundation and appearing in the file GPL.TXT included in the packaging of* this file. Please note that GPL Section 2[b] requires that all works based* on this software must also be made publicly available under the terms of* the GPL ("Copyleft").** Alternatively, this software may be distributed and modified under the* terms of Quantum Leaps commercial licenses, which expressly supersede* the GPL and are specifically designed for licensees interested in* retaining the proprietary status of their code.** Contact information:* Quantum Leaps Web site: http://www.quantum-leaps.com* e-mail: info@quantum-leaps.com*****************************************************************************/#include "qpn_port.h" /* QP-nano port */#ifndef Q_NHSMQ_DEFINE_THIS_MODULE(qepn)#endif/*** \file* \ingroup qepn qfn* QEP-nano implementation.*//** empty signal for internal use only */#define QEP_EMPTY_SIG_ 0/** maximum depth of state nesting (including the top level), must be >= 2 */#define QEP_MAX_NEST_DEPTH_ 5/*..........................................................................*//*lint -e970 -e971 */ /* ignore MISRA rules 13 and 14 in this function */char const Q_ROM * Q_ROM_VAR QP_getVersion(void) {static char const Q_ROM Q_ROM_VAR version[] = {((QP_VERSION >> 12) & 0xF) + '0','.',((QP_VERSION >> 8) & 0xF) + '0','.',((QP_VERSION >> 4) & 0xF) + '0',(QP_VERSION & 0xF) + '0','\0'};return version;}#ifndef Q_NFSM/*..........................................................................*/void QFsm_init(QFsm *me) {(void)(*me->state)(me); /* execute the top-most initial transition */Q_SIG(me) = (QSignal)Q_ENTRY_SIG;(void)(*me->state)(me); /* enter the target */}/*..........................................................................*/#ifndef QK_PREEMPTIVEvoid QFsm_dispatch(QFsm *me){#elsevoid QFsm_dispatch(QFsm *me) Q_REENTRANT{#endifQStateHandler s = me->state;if ((*s)(me) == Q_RET_TRAN) { /* transition taken? */Q_SIG(me) = (QSignal)Q_EXIT_SIG;(void)(*s)(me); /* exit the source */Q_SIG(me) = (QSignal)Q_ENTRY_SIG;(void)(*me->state)(me); /* enter the target */}}#endif /* Q_NFSM */#ifndef Q_NHSM/*..........................................................................*/QState QHsm_top(QHsm *me) {(void)me; /* supress the "unused argument" compiler warning */return Q_IGNORED(); /* the top state ignores all events */}/*..........................................................................*/void QHsm_init(QHsm *me) {QStateHandler t;/* 初始化状态机时必须转换到一个明确的状态 */Q_ALLEGE((*me->state)(me) == Q_RET_TRAN);/* initial tran. must be taken *//*从顶层状态开始,执行递归的进入操作* 此时me->state已经为初始化后的目标状态*/t = (QStateHandler)&QHsm_top; /* an HSM starts in the top state */do { /* drill into the target hierarchy... */QStateHandler path[QEP_MAX_NEST_DEPTH_];int8_t ip = (int8_t)0;/*利用QEP_EMPTY_SIG_信号,从目标状态回溯到顶状态,* QEP_EMPTY_SIG_信号除把状态转换到超状态,不做任何操作* 保存回溯的路径到ip数组中,保存为逆序* 即第一个数组元素为目标状态,上层状态其后* 顶层状态并不包括在路径中*/path[0] = me->state;Q_SIG(me) = (QSignal)QEP_EMPTY_SIG_;(void)(*me->state)(me);while (me->state != t) {++ip;path[ip] = me->state;(void)(*me->state)(me);}/* 回到目标状态 */me->state = path[0];/* 确保深度在预定内 *//* entry path must not overflow */Q_ASSERT(ip < (int8_t)QEP_MAX_NEST_DEPTH_);/* 从进入路径执行Q_ENTRY_SIG信号事件 */Q_SIG(me) = (QSignal)Q_ENTRY_SIG;do { /* retrace the entry path in reverse (correct) order... */(void)(*path[ip])(me); /* enter path[ip] */--ip;} while (ip >= (int8_t)0);/* 在目标状态上执行初始化 */t = path[0];Q_SIG(me) = (QSignal)Q_INIT_SIG;/*在目标状态上执行初始化操作* 如果目标状态的初始化导致状态转换,则重复上述过程* 直到进入一个状态在执行初始化后不再转换*/} while ((*t)(me) == Q_RET_TRAN); /* initial transition handled? */me->state = t;}/*..........................................................................*/#ifndef QK_PREEMPTIVEvoid QHsm_dispatch(QHsm *me) {#elsevoid QHsm_dispatch(QHsm *me) Q_REENTRANT {#endifQStateHandler path[QEP_MAX_NEST_DEPTH_];QStateHandler s;QStateHandler t;QState r;t = me->state; /* save the current state *//*事件处理,直到被当前状态或层次上的超状态处理* 因为在被处理前一直返回 Q_RET_SUPER* 所以此循环退出时,事件肯定已经被处理了* s 保存的是处理事件的超状态!* 当前的me->state则为在处理事件的超状态中转换的目标状态*/do { /* process the event hierarchically... */s = me->state;r = (*s)(me); /* invoke state handler s */} while (r == Q_RET_SUPER);if (r == Q_RET_TRAN) { /* transition taken? *//** 在事件处理时,发生了状态转换* 这个转换要么发生在分发事件的初始状态(入口参数me->state),* 要么就在初始状态的超状态* s就保存了这个发生转换的状态*/int8_t ip = (int8_t)(-1); /* transition entry path index */int8_t iq; /* helper transition entry path index *//* 把转换的目标状态保存到path[0] */path[0] = me->state; /* save the target of the transition *//* 把转换的起源状态保存到path[1],* 起源状态为入口参数me->state,先前存放在t中*/path[1] = t;/*如果转换发生在超状态中,则执行从起源状态到超状态的退出操作* 注意,最后的超状态是不会执行退出操作的*/while (t != s) { /* exit current state to transition source s... *//** 执行退出事件不能有转换,除非返回超状态*/Q_SIG(me) = (QSignal)Q_EXIT_SIG; /* find superstate of t */if ((*t)(me) == Q_RET_HANDLED) { /* exit action handled? *//** 退出被处理则要执行空信号,强迫返回超状态*/Q_SIG(me) = (QSignal)QEP_EMPTY_SIG_;(void)(*t)(me); /* find superstate of t */}/** me->state必定已变为超状态!*/t = me->state; /* me->state holds the superstate */}/* 现在,me->state回到了执行转换状态的超状态 *//* 提出目标状态 */t = path[0]; /* target of the transition *//*t已经为目标状态,* s为执行状态转换的那个状态,即实际执行处理被分发的事件的那个状态* 这个状态要么是分发事件的初始状态(入口参数me->state),* 要么是初始状态的超状态*/if (s == t) { /* (a) check source==target (transition to self) *//*转换的目标状态与转换操作发生的状态相同* 执行一个自转换,即退出操作*/Q_SIG(me) = (QSignal)Q_EXIT_SIG;(void)(*s)(me); /* exit the source *//* 准备进入目标状态, path[0]就是目标状态 */ip = (int8_t)0; /* enter the target */}else {/** 目标状态与转换源状态不同,* 即从处理事件的那个状态转换到了一个新状态* 此时,并不知道目标状态在状态树中的哪个位置* 麻烦即将来临!** s: 执行转换的起源状态 t: 目标状态*//* 求得目标状态的超状态 */Q_SIG(me) = (QSignal)QEP_EMPTY_SIG_;(void)(*t)(me); /* find superstate of target */t = me->state;/* t: 已变成目标状态的超状态 *//* 目标状态是否起源状态的子状态? *//* 目标状态的超状态就是起源状态么? */if (s == t) { /* (b) check source==target->super *//** 目标状态的超状态就是起源状态*//* 准备进入目标状态, path[0]就是目标状态 */ip = (int8_t)0; /* enter the target */}else {/** 目标状态的超状态不是起源状态*//* 求得起源状态的超状态 */Q_SIG(me) = (QSignal)QEP_EMPTY_SIG_;(void)(*s)(me); /* find superstate of source *//* 是否兄弟状态?*//* 即起源状态的超状态和目标状态的超状态相同么 ? *//* (c) check source->super==target->super */if (me->state == t) {/** 起源状态的超状态和目标状态的超状态相同,* 这个超状态就是LCA(最小公共超状态)*//* 退出起源状态,准备进入目标状态, path[0]就是目标状态 */Q_SIG(me) = (QSignal)Q_EXIT_SIG;(void)(*s)(me); /* exit the source */ip = (int8_t)0; /* enter the target */}else {/** 不是兄弟状态,那么是目标状态是起源状态的父亲么 ?* me->state已经是起源状态的超状态*//* (d) check source->super==target */if (me->state == path[0]) {/** 目标状态确实是父状态* 退出起源状态* 转换到父状态不需要执行进入操作* 因为源状态本身就在父状态之中* 故ip=-1,没有ip=0这句*/Q_SIG(me) = (QSignal)Q_EXIT_SIG;(void)(*s)(me); /* exit the source */}else { /* (e) check rest of source==target->super->super..* and store the entry path along the way*//** 彻底看看源状态是不是目标状态的超状态* 即目标状态是否在源状态的状态树上*//* iq指示是否找到LCA */iq = (int8_t)0; /* indicate that LCA not found *//* path[0]已经是目标状态 */ip = (int8_t)1; /* enter target and its superstate *//* path[1]存放目标状态的超状态 */path[1] = t; /* save the superstate of target *//* 至此,me->state是神马玩意了呢 ? *//* OK,它已经是起源状态的超状态了 */t = me->state; /* save source->super *//** 现在,从目标状态的超状态往上走* 目标状态的超状态在path[1]里*/Q_SIG(me) = (QSignal)QEP_EMPTY_SIG_;r = (*path[1])(me); /* find target->super->super *//* Go up! *//** 到顶或者* 从超状态链中找到起源状态*/while (r == Q_RET_SUPER) {/** 保存路径*/path[++ip] = me->state; /* store the entry path */if (me->state == s) { /* is it the source? *//** 找到了,那个找我麻烦的状态:-)* 设置标志*/iq = (int8_t)1; /* indicate that LCA found *//* entry path must not overflow */Q_ASSERT(ip < (int8_t)QEP_MAX_NEST_DEPTH_);/* 扣除目标状态 */--ip; /* do not enter the source *//** r还有用么,舍不得用break?* 结构化真他妈的那么重要?*/r = Q_RET_HANDLED; /* terminate the loop */}else { /* it is not the source, keep going up *//** Go up continue!* 对QEP_EMPTY_SIG_,* 只要有超状态就会返回Q_RET_SUPER* 否则就是到顶了*/r = (*me->state)(me); /* superstate of t */}}/** 在目标状态的超状态中找到源状态了么?* 实际上是看目标状态与源状态是否在同一条树枝路径上*/if (iq == (int8_t)0) { /* the LCA not found yet? *//** 悲剧啊,不在一条树枝上!* LCA自然是还没找到*//* entry path must not overflow */Q_ASSERT(ip < (int8_t)QEP_MAX_NEST_DEPTH_);/** 没办法,LCA肯定在源状态的某个超状态上了* 来,现在沿着源状态的超状态往上走* 去找与目标状态的共同祖先LCA* path已经记录了目标状态的超状态链*//** 毫无疑问,这次需要从源状态一路退出到LCA*/Q_SIG(me) = (QSignal)Q_EXIT_SIG;(void)(*s)(me); /* exit the source *//* 注意,俺开始退出了 *//** iq的作用变了,用于辅助跟踪状态链*//* (f) check the rest of source->super* == target->super->super...*/iq = ip;r = Q_RET_IGNORED; /* indicate LCA NOT found *//* 此时,t为起源状态的父状态,即第一层超状态 */do {/** 从目标状态超状态链的最上面开始*/s = path[iq];if (t == s) { /* is this the LCA? *//** 这么容易就找到了?真没劲啊*/r = Q_RET_HANDLED;/* indicate LCA found *//** 目标状态的超状态链不包括LCA* 注意:如果iq=0,* 即目标状态就是超源状态的父状态* ip=0-1=-1,此后不会执行父状态的进入* 因为此时源状态本身就在目标状态中!*/ip = (int8_t)(iq - 1);/*do not enter LCA*//** 结束循环的标志,我日,还是不用break!*/iq = (int8_t)(-1);/* terminate the loop */}else {/** 向目标状态的超状态链向下辗个位置*/--iq; /* try lower superstate of target */}} while (iq >= (int8_t)0);/* 源状态的父状态也不是LCA? */if (r != Q_RET_HANDLED) { /* LCA not found yet? *//* (g) check each source->super->...* for each target->super...*//** 确实不是,还得继续找,肏他妈的真累啊* 这下该怎么找啊,好晕* 这些变量都变成什么玩意儿了?* t:还是为起源状态的父状态*/r = Q_RET_IGNORED; /* keep looping */do {/** 唉,从源状态的父状态往上一层层地退吧!* 兄弟,听过“王霞”的故事么?见附录*//* exit t unhandled? */Q_SIG(me) = (QSignal)Q_EXIT_SIG;if ((*t)(me) == Q_RET_HANDLED) {Q_SIG(me) = (QSignal)QEP_EMPTY_SIG_;(void)(*t)(me); /* find super of t */}t = me->state; /* set to super of t *//** 一边执行退出一边往上走*//** path为目标状态的超状态链* 这个链中开始找LCA* 就是每得到一个源状态的超状态* 就在目标状态的超状态中找* 找到的第一个就是LCA* 不信?你可以画个图看看*/iq = ip;do {s = path[iq];if (t == s) { /* is this LCA? *//** 终于找到了,我的妈呀*//* do not enter LCA */ip = (int8_t)(iq - 1);iq = (int8_t)(-1);/*break inner */r = Q_RET_HANDLED;/*break outer */}else {--iq;}} while (iq >= (int8_t)0);} while (r != Q_RET_HANDLED);}}}}}}/** 在上述过程中,退出操作已经干了* 进入链也保存在path中了* 开始进入吧,慢点哦^_^*//* retrace the entry path in reverse (desired) order... */Q_SIG(me) = (QSignal)Q_ENTRY_SIG;for (; ip >= (int8_t)0; --ip) {(void)(*path[ip])(me); /* enter path[ip] */}/* 目标状态提取出来 */t = path[0]; /* stick the target into register *//* 更新状态机的目标状态 */me->state = t; /* update the current state *//** 最后一件事,在目标状态上执行初始化* 这个过程真他妈的应该和QHsm_init好好地复用一下代码* 这两个地方类似的递归初始化的代码没有复用相当挫* 注:初始化转换只能在引起初始化状态的状态树枝里*//* drill into the target hierarchy... */Q_SIG(me) = (QSignal)Q_INIT_SIG;while ((*t)(me) == Q_RET_TRAN) {ip = (int8_t)0;/** 初始化时如果状态转换则生成超状态链*/path[0] = me->state;Q_SIG(me) = (QSignal)QEP_EMPTY_SIG_;(void)(*me->state)(me); /* find the superstate */while (me->state != t) {++ip;path[ip] = me->state;(void)(*me->state)(me); /* find the superstate */}me->state = path[0];/* entry path must not overflow */Q_ASSERT(ip < (int8_t)QEP_MAX_NEST_DEPTH_);/* 在状态链上执行进入 */Q_SIG(me) = (QSignal)Q_ENTRY_SIG;do { /* retrace the entry path in reverse (correct) order... */(void)(*path[ip])(me); /* enter path[ip] */--ip;} while (ip >= (int8_t)0);/** 在目标状态上继续执行初始化* 最后,没有状态转换的初始化操作的状态为最后的状态* 这个最后的状态为最终的目标状态,放在t中*/t = path[0];Q_SIG(me) = (QSignal)Q_INIT_SIG;}}/* 目标状态终于确定,俺洗洗睡了 */me->state = t; /* set new state or restore the current state */}#endif /* Q_NHSM */————————————————版权声明:本文为CSDN博主「lyqdy1」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/lyqdy1/article/details/7085959
void QF_init(uint_fast8_t maxActive) {
    QActive *a;
    uint_fast8_t p;
    uint_fast8_t n;
    /** @pre the number of active objects must be in range */
    Q_REQUIRE_ID(100, ((uint_fast8_t)1 < maxActive)
                      && (maxActive <= (uint_fast8_t)33));
    QF_maxActive_ = (uint_fast8_t)maxActive - (uint_fast8_t)1;
#ifdef QF_TIMEEVT_USAGE
    for (n = (uint_fast8_t)0; n < (uint_fast8_t)QF_MAX_TICK_RATE; ++n) {
        QF_timerSetX_[n] = (uint_fast8_t)0;
    }
#endif /* QF_TIMEEVT_USAGE */
    QF_readySet_ = (uint_fast32_t)0;
#ifdef qkn_h
    QK_attr_.actPrio = (uint_fast8_t)8; /* QK-nano scheduler locked */
#ifdef QF_ISR_NEST
    QK_attr_.intNest = (uint_fast8_t)0;
#endif
#ifdef QK_SCHED_LOCK
    QK_attr_.lockPrio   = (uint_fast8_t)0;
    QK_attr_.lockHolder = (uint_fast8_t)0;
#endif
#endif /* #ifdef qkn_h */
    /* clear all registered active objects... */
    for (p = (uint_fast8_t)1; p <= QF_maxActive_; ++p) {
        a = QF_ROM_ACTIVE_GET_(p);
        /* QF_active[p] must be initialized */
        Q_ASSERT_ID(110, a != (QActive *)0);
        a->head    = (uint8_t)0;
        a->tail    = (uint8_t)0;
        a->nUsed   = (uint8_t)0;
#if (QF_TIMEEVT_CTR_SIZE != 0)
        for (n = (uint_fast8_t)0; n < (uint_fast8_t)QF_MAX_TICK_RATE; ++n) {
            a->tickCtr[n].nTicks   = (QTimeEvtCtr)0;
#ifdef QF_TIMEEVT_PERIODIC
            a->tickCtr[n].interval = (QTimeEvtCtr)0;
#endif /* def QF_TIMEEVT_PERIODIC */
        }
#endif /* (QF_TIMEEVT_CTR_SIZE != 0) */
    }
#ifdef QV_INIT /* initialization of the QV-nano kernel defined? */
    QV_INIT(); /* port-specific initialization of the QV-nano kernel */
#elif defined QK_INIT /* initialization of the QK-nano kernel defined? */
    QK_INIT(); /* port-specific initialization of the QK-nano kernel */
#endif
}
typedef struct QActive {
    QHsm super; /**< derives from the ::QHsm base class */
#if (QF_TIMEEVT_CTR_SIZE != 0)
    /*! Timer for the active object */
    QTimer tickCtr[QF_MAX_TICK_RATE];
#endif /* (QF_TIMEEVT_CTR_SIZE != 0) */
    /*! priority of the active object (1..8) */
    uint8_t prio;
    /*! offset to where next event will be inserted into the buffer */
    uint8_t volatile head;
    /*! offset of where next event will be extracted from the buffer */
    uint8_t volatile tail;
    /*! number of events currently present in the queue
    * (events in the ring buffer + 1 event in the state machine)
    */
    uint8_t volatile nUsed;
} QActive;
