量子框架(1)

简介:

量子框架简称QP,是一种状态机框架,实现了有限状态机FSM和层次状态机HSM,目前官方仅有C和C++语言的实现。对于PC端和web端的开发,这个框架有种英雄无用武之地的感觉,但在嵌入式领域这个框架彻底颠覆了我的认识。

提到这个框架,不能不提框架作者Miro Samek的书《Pratical UML Statecharts In C/C++》。这本书既可以看作是状态机方面的著作,也能看作QP的教程,强烈推荐对状态机感兴趣的人读之。

闲话不说,直接用一个简单例子来说说这个框架的使用。当然,这个框架在使用之前是需要根据具体平台和系统做一些简单的移植工作的,这部分会在之后描述。

**************************************

任务:基于状态机控制一个LED灯闪烁

(1)采用基于数值变量的状态机

复制代码
#define LED_SHORT_DELAY        1000

typedef enum
{
    LedState_Off = 0,
    LedState_On
} LedState;

typedef enum
{
    LedSignal_TurnOff = 0,
    LedSignal_TurnOn
} LedSignal;

LedState led_state = LedState_Off;
LedSignal led_sig;

void Led_On(void)
{
    printf("Led is ON.\r\n");
}

void Led_Off(void)
{
    printf("Led is OFF.\r\n");
}

void Led_Control(LedSignal sig)
{
    switch (led_state)
    {
    case LedState_Off:
        if (LedSignal_TurnOn == sig)
        {
            led_state = LedState_On;
            Led_On();
        }
        break;

    case LedState_On:
        if (LedSignal_TurnOff == sig)
        {
            led_state = LedState_Off;
            Led_Off();
        }
        break;
    }
}

void Led_Blink(void)
{
    int delay = LED_SHORT_DELAY;
    
    led_sig = LedSignal_TurnOff;
    while (1)
    {
        int delay = LED_SHORT_DELAY;
        
        led_sig = !led_sig;
        Led_Control(led_sig);
        while(delay--);
    }
}
复制代码

状态机的实现集中在Led_Control这个函数中。它包含了状态led_state、外部信号(事件)sig这两个状态机基本元素。

(2)基于QP的状态机实现

定义状态

static QState Led_StateInitial(QFsm* fsm, QEvt* e);
static QState Led_StateOn(QFsm* fsm, QEvt* e);
static QState Led_StateOff(QFsm* fsm, QEvt* e);

可以看出QP下的状态是函数指针?对,没错,QP下的状态是通过函数指针来表示的。在这里,引入了第一个问题,采用什么形式保存当前状态好?(采用流水账的形式探讨问题)

a. 变量形式

比如(1)中的led_state变量用于保存当前状态,然后通过switch-case分支对各种外部signal进行判断分别处理。这种方式简单易懂,实现起来也是非常easy。但如果系统复杂一些,n个状态变量,每个状态变量分别对应 a_i(i = 1, …, n) 种状态。在最坏情况下,n个状态相互嵌套,你的switch-case将有a_n*a_(n-1)*…*a_1层。在工业现场,随便复杂一点的应用都会有十几层的switch-case和if-else吧。不要说让别人维护你的代码,编这种程序的你也会叫苦不迭。

b. 状态表(state table)

较为流行的是采用二维表,以状态集为行,signal事件为列。如(1)中的Led_Control状态机可表示为:

  LedSignal_TurnOff LedSignal_TurnOn
LedState_Off \ Led_On(); // action
LedState_On; // next state
LedState_On Led_Off(); // action
LedState_Off; // next state

\

 

采用状态表的方式使得状态机执行效率得到很大提高(O(1)),避免了分支判断。其缺点也在上表中显而易见,对于一些状态,某些signal是没有意义的,但仍需在状态表中列出。C语言自身不支持hash,构造出来的状态表通常是一个稀疏矩阵,浪费有限的存储空间。当后期由于需求变更需要调整状态表时,很容易造成遗漏或错误。如果想实现层次状态机,可以想像状态表这种实现方式既繁琐且易出错。当然,对于简单的应用,状态表仍是很好的一个选择。

c. object-oriented状态设计模式

这种方式充分利用面向对象设计思想,设计一个抽象类定义所有signal的处理接口,然后由不同state去继承该类,并实现自身关心的事件处理接口以覆盖基类的相应接口(多态)。同样以Led_Control为例,设计一个抽象类(语法不规范)

复制代码
class Led;
class LedState
{
public:
    virtual void OnLedSignal_TurnOn(Led* contex) {};
    virtual void OnLedSignal_TurnOff(Led* contex) {};
}

class LedOnState : public  LedState
{
public:
    void OnLedSignal_TurnOff(Led* contex) { Led_Off(); contex->Tran(&contex->stateOff) }
}

class LedOffState: public LedState
{
public:
    void OnLedSignal_TurnOn(Led* contex) { Led_On(); contex->Tran(&contex->stateOn) }
}

class Led:
{
private:
    LedState* m_state;
    static LedOnState stateOn;
    static LedOffState stateOff;
    void Tran(LedState* state)    { m_state = state; }
    
public:
    void OnLedSignal_TurnOn(void)    { m_state->OnLedSignal_TurnOn(this) }    // 响应'开'信号
    void OnLedSignal_TurnOff(void)    { m_state->OnLedSignal_TurnOff(this) }    // 响应'关'信号
    void Init(void)    { Tran(stateOff) };    // 初始状态
    
    friend class LedOnState;
    friend class LedOffState;
}
复制代码

LedState是抽象基类,包含了所有外部signal(LedSignal_TurnOff、LedSignal_TurnOn)的默认处理接口。LedOnState继承于LedState,实现了它自身关心的LedSignal_TurnOff信号处理方法。LedOffState同样继承于LedState,实现了LedSignal_TurnOn信号处理方法。

Led这个类是状态机,包含了两个状态,分别是LedOnState和LedOffState的两个实例。这个状态机有一个重要的成员变量m_state(LedState*类型)用于保存当前状态,而Tran成员函数用于切换状态。当这个状态机响应外部signal时,直接调用当前状态相应的信号处理方法即可。例如,Led状态机Init()后其状态为m_state = &stateOff(LedOffState*),当收到LedSignal_TurnOff信号时,调用自身响应函数Led:OnLedSignal_TurnOff()时,有m_state->OnLedSignal_TurnOff() <—> LedOffState::OnLedSignal_TurnOff(Led*)。而LedOffState类的OnLedSignal_TurnOff继承于LedState,函数为空,因此什么也不做。当收到LedSignal_TurnOn信号时,类似的可以知道其相当于调用了LedOffState::OnLedSignal_TurnOn(Led*)。而该函数执行两个动作:Led_TurnOn();同时调用Tran将状态机的当前状态m_state切换至&stateOn(LedOnState*)。

从上面这个Led例子可看出,基于OO思想的状态机极大程度依赖于像C++这种语言的面向对象特性。好处有很多:

  • 各自的状态可以独立处理各自感兴趣的外部信号;
  • 状态迁移十分高效,只需改变指针所指内容;
  • 信号分发性能也非常可观,可保证O(1)复杂度;

尽管有如上优点,但在实际应用时,需要枚举全部外部信号的处理方法且最大化状态基类的接口数量,这个工作量其实不小,由Led代码也可看出一些端倪。

d. QEP有限状态机

顾名思义,将上述3种思想取长补短再加上QP作者个人原创思想提出的一种状态机。在QP框架中,非层次有限状态机定义为QFsm。虽说在QFsm的C语言实现中,不过是用struct封装了其成员变量和函数,没有类的真正概念,但理解起来也就是类,所以就以QFsm类称谓之。

image

QFsm含有一个成员变量State保存当前状态,这一点与方式c一致。只是这里的QFsm状态机中的状态都是函数指针,具备QState QStateHandler(void* me, QEvent* e)形式。也就是说,当有外部事件e: QEvent产生时,QFsm的处理很简单,即调用函数state(me, e)。依然以本文开始时的LED示例来说,它有二个状态:

static QState Led_StateOn(QFsm* fsm, QEvt* e);
static QState Led_StateOff(QFsm* fsm, QEvt* e);

还有一个Led_StateInitial是每个QFsm状态机必须具有的一个初始化状态,它只负责将状态迁移到默认状态。如LED默认为关状态,那么有:

static QState Led_StateInitial(QFsm* fsm, QEvt* e)
{
    Q_TRAN(&Led_StateOff);
}

再看Led的两个状态下对外部事件的处理

复制代码
static QState Led_StateOn(QFsm* fsm, QEvt* e)
{
    switch (e->sig)
    {
    case Q_ENTER_SIG:    // 进入该状态时执行动作
        return Q_HANDLED;
    
    case Q_EXIT_SIG:    // 退出该状态时执行动作
        return Q_HANDLED;
        
    case LedSignal_TurnOff:    // '关'信号
        Led_Off();
       return Q_TRAN(&Led_StateOff);
    }
    return Q_IGNORED();
}

static QState Led_StateOff(QFsm* fsm, QEvt* e)
{
    switch (e->sig)
    {
    case Q_ENTER_SIG:    // 进入该状态时执行动作
        return Q_HANDLED;
    
    case Q_EXIT_SIG:    // 退出该状态时执行动作
        return Q_HANDLED;
        
    case LedSignal_TurnOn:    // '开'信号
        Led_On();
       return Q_TRAN(&Led_StateOn);
    }
    return Q_IGNORED();
}
复制代码

是否能感觉出,有了QP框架的支持,状态机的使用显得清晰明了。同时QP下的状态都具有Enter和Exit动作,这与UML规范下的状态机完全一致。由于这系列日志只关注应用,不会对UML规范和QP状态机有何不同做对比,感兴趣的同道请仔细研读QP的那本教程。

虽然QP在Led这个例子上面的应用及其简单,但也算麻雀虽小,五脏俱全。引出了一组不认识的宏:

Q_ENTER_SIG
Q_EXIT_SIG
Q_HANDLED
Q_IGNORED
Q_TRAN

不光如此,还有QFsm状态之间的切换是怎么实现的,有心的同道肯定会想到怎么你没有讲QFsm的dispatch和Init是怎么回事,是不是状态的切换跟dispatch(分发)有关。这些内容将在这个系列的后续日志中逐一记录。

如果QP仅仅做到QFsm这个程度,根本不值得我如此推崇,当涉及到层次状态机,活动对象这样的概念时,QP框架才真正的让人感觉到了什么是强大。调了众人的胃口,实属不该。按捺不住的同道请点击下面的链接进入QP官网,立刻下载考察其应用价值。那本教程我也给出百度网盘外链以作参考(毕竟是盗版,可不敢太张扬)。


原文发布时间为:2017年01月17日
本文作者:robert_cai
本文来源:博客园,如需转载请联系原作者。

目录
相关文章
|
3月前
|
安全 量子技术 数据安全/隐私保护
量子通信技术的原理与进展
【8月更文挑战第1天】量子通信技术以其独特的优势和巨大的潜力在科技领域掀起了一场革命性的变革。随着研究的深入和技术的成熟,量子通信技术将在未来发挥更加重要的作用,为信息安全、量子计算、量子传感等领域提供强有力的支持。我们有理由相信,在不久的将来,量子通信将以其卓越的性能和广泛的应用前景,为我们带来更加安全、高效、便捷的通信体验。
|
6月前
|
机器学习/深度学习 人工智能 算法
探索量子计算的基本原理
【5月更文挑战第24天】量子计算利用量子力学原理,以量子比特(qubit)为基本单元,突破传统计算限制。核心原理包括量子叠加(允许量子比特同时处于多种状态)和量子纠缠(量子比特间的状态关联)。其应用前景广阔,涉及密码学、材料科学、人工智能和优化问题等领域。尽管仍处发展阶段,但量子计算有望引领未来计算技术的革命性突破。
|
算法 TensorFlow 定位技术
量子程序设计基础 | 量子计算简史
量子革命和量子计算发展简史。
155 0
量子程序设计基础 | 量子计算简史
|
存储 算法 量子技术
量子程序设计基础 | 从经典计算到量子计算
本篇介绍量子计算的重要性。
185 0
|
存储 机器学习/深度学习 算法
量子计算进阶:量子计算机的组建和量子计算原理(包含相关论文推荐60篇)上
量子计算进阶:量子计算机的组建和量子计算原理(包含相关论文推荐60篇)
233 0
|
机器学习/深度学习 人工智能 算法
量子计算进阶:量子计算机的组建和量子计算原理(包含相关论文推荐60篇)下
量子计算进阶:量子计算机的组建和量子计算原理(包含相关论文推荐60篇)
106 0
|
存储 并行计算 算法
|
机器学习/深度学习 算法 量子技术
IonQ公布量子计算机发展蓝图 :3年实现量子机器学习,5年实现广义量子优势
捕获离子量子计算机为何物? 算力难以度量,物理量子位并不代表一切 指日可待,算法量子位助力突破 发动机器学习早期量子优势,IonQ五年规划信心满满 继“量子霸权”之后,“广义量子优势”横空出世
339 0
IonQ公布量子计算机发展蓝图 :3年实现量子机器学习,5年实现广义量子优势
|
量子技术
带你读《量子编程基础》之一:量子编程研究简史
本书讨论了如何扩展当前计算机的新程序设计方法和技术,以利用量子计算机的独特能力。相比于现有计算机系统,量子计算机在处理速度上具有显著优势。世界各地的政府和企业都投入了大量资金,希望建造实用的量子计算机。本书结合作者在量子计算领域多年的研究经验,并辅以大量的例子和插图,介绍了量子编程语言及其所需的重要工具和技术,对于学者、研究人员和开发人员来说都是非常宝贵的参考资料。
|
量子技术 数据安全/隐私保护