当前位置:首页 > 知识普及 > 正文内容

用C语言实现状态机设计模式

admin12小时前知识普及1

用C语言实现状态机设计模式

状态机模式是一种行为模式,在《设计模式》这本书中对其有详细的描述,通过多态实现不同状态的调转行为的确是一种很好的方法,只可惜在嵌入式环境下,有时只能写纯C代码,并且还需要考虑代码的重入和多任务请求跳转等情形c语言状态机设计模式,因此实现起来着实需要一番考虑。

近日在看了一个开源系统时,看到了一个状态机的实现,也学着写了一个,与大家分享。

首先c语言状态机设计模式,分析一下一个普通的状态机究竟要实现哪些内容。

状态机存储从开始时刻到现在的变化,并根据当前输入,决定下一个状态。这意味着,状态机要存储状态、获得输入(我们把它叫做跳转条件)、做出响应。

用C语言实现状态机设计模式 第1张

如上图所示用C语言实现状态机设计模式,{s1, s2, s3}均为状态,箭头c1/a1表示在s1状态、输入为c1时,跳转到s2,并进行a1操作。

最下方为一组输入,状态机应做出如下反应:

当前状态

输入

下一个状态

动作

s1

c1

s2

a1

s2

c2

s3

a2

s3

c1

s2

a3

s2

c2

s3

a2

s3

c1

s2

a3

s2

c1

c1

当某个状态遇到不能识别的输入时,就默认进入陷阱状态,在陷阱状态中用C语言实现状态机设计模式,不论遇到怎样的输入都不能跳出。

为了表达上面这个自动机,我们定义它们的状态和输入类型:

typedef int State;
typedef int Condition;
 
#define STATES      3 + 1
#define STATE_1     0
#define STATE_2     1
#define STATE_3     2
#define STATE_TRAP  3
 
#define CONDITIONS   2
#define CONDITION_1  0
#define CONDITION_2  1

在嵌入式环境中,由于存储空间比较小,因此把它们全部定义成宏。此外,为了降低执行时间的不确定性,我们使用O(1)的跳转表来模拟状态的跳转。

首先定义跳转类型:

typedef void(*ActionType)(State state, Condition condition);
 
typedef struct
{
    State next;
    ActionType action;
} Trasition, * pTrasition;

然后按照上图中的跳转关系,把三个跳转加一个陷阱跳转先定义出来:

// (s1, c1, s2, a1)
Trasition t1 = {
    STATE_2,
    action_1
};
 
// (s2, c2, s3, a2)
Trasition t2 = {
    STATE_3,
    action_2
};
 
// (s3, c1, s2, a3)
Trasition t3 = {
    STATE_2,
    action_3
};
 
// (s, c, trap, a1)

用C语言实现状态机设计模式 第2张

Trasition tt = {     STATE_TRAP,     action_trap };

其中的动作,由用户自己完成,在这里仅定义一条输出语句。

void action_1(State state, Condition condition)
{
    printf("Action 1 triggered.
");
}

最后定义跳转表:

pTrasition transition_table[STATES][CONDITIONS] = {
/*        c1,  c2*/
/* s1 */&t1, &tt,
/* s2 */&tt, &t2,
/* s3 */&t3, &tt,
/* st */&tt, &tt,
};

即可表达上文中的跳转关系。

最后定义状态机,如果不考虑多任务请求,那么状态机仅需要存储当前状态便行了。例如:

typedef struct
{
    State current;
} StateMachine, * pStateMachine;
 
State step(pStateMachine machine, Condition condition)
{
    pTrasition t = transition_table[machine->current][condition];
    (*(t->action))(machine->current, condition);
    machine->current = t->next;
    return machine->current;
}

但是考虑到当一个跳转正在进行的时候,同时又有其他任务请求跳转,则会出现数据不一致的问题。

举个例子:task1(s1, c1/a1 –> s2) 和 task2(s2, c2/a2 –> s3) 先后执行,是可以顺利到达s3状态的c语言状态机设计模式,但若操作a1运行的时候,执行权限被task2抢占,则task2此时看到的当前状态还是s1,s1遇到c2就进入陷阱状态,而不会到达s3了,也就是说,状态的跳转发生了不确定,这是不能容忍的。

因此要重新设计状态机,增加一个“事务中”条件和一个用于存储输入的条件队列。修改后的代码如下:

#define E_OK         0
#define E_NO_DATA    1
#define E_OVERFLOW   2
 
typedef struct
{
    Condition queue[QMAX];
    int head;
    int tail;
    bool overflow;
} ConditionQueue, * pConditionQueue;
 
 
int push(ConditionQueue * queue, Condition c)
{    
    unsigned int flags;
    Irq_Save(flags);
    if ((queue->head == queue->tail + 1) || ((queue->head == 0) && (queue->tail == 0)))
    {
        queue->overflow = true;
        Irq_Restore(flags);
        return E_OVERFLOW;
    }
    else
    {
        queue->queue[queue->tail] = c;
        queue->tail = (queue->tail + 1) % QMAX;
        Irq_Restore(flags);
    }
    return E_OK;
}
 
int poll(ConditionQueue * queue, Condition * c)
{
    unsigned int flags;
    Irq_Save(flags);
    if (queue->head == queue->tail)
    {
        Irq_Restore(flags);
        return E_NO_DATA;
    }
    else
    {
        *c = queue->queue[queue->head];
        queue->overflow = false;
        queue->head = (queue->head + 1) % QMAX;
        Irq_Restore(flags);
    }
    return E_OK;
}
 
typedef struct
{
    State current;
    bool inTransaction;
    ConditionQueue queue;
} StateMachine, * pStateMachine;
 
static State __step(pStateMachine machine, Condition condition)
{
    State current = machine -> current;
    pTrasition t = transition_table[current][condition];
    (*(t->action))(current, condition);
    current = t->next;
    machine->current = current;
    return current;
}
 
State step(pStateMachine machine, Condition condition)
{
    Condition next_condition;
    int status;
    State current;
    if (machine->inTransaction)
    {
        push(&(machine->queue), condition);
        return STATE_INTRANSACTION;
    }
    else
    {
        machine->inTransaction = true;
        current = __step(machine, condition);
        status = poll(&(machine->queue), &next_condition);
        while(status == E_OK)
        {
            __step(machine, next_condition);
            status = poll(&(machine->queue), &next_condition);
        }
        machine->inTransaction = false;
        return current;
    }
}
 
void initialize(pStateMachine machine, State s)
{
    machine->current = s;
    machine->inTransaction = false;
    machine->queue.head = 0;
    machine->queue.tail = 0;
    machine->queue.overflow = false;
}

  

加入微信交流群:************ ,请猛戳这里→点击入群

扫描二维码推送至手机访问。

版权声明:本文由智汇百科网发布,如需转载请注明出处。

本文链接:https://zhihuibkw.com/post/6394.html

分享给朋友:

“用C语言实现状态机设计模式” 的相关文章

哈佛大学取消标化考试!招生官:更看重综合素质

哈佛大学取消标化考试!招生官:更看重综合素质

在当今高等教育的舞台上,哈佛大学这所举世闻名的学府做出了一项极具震撼力的决定——取消标化考试。这一举措引发了广泛的关注与热议,背后所蕴含的深意,正是招生官们对综合素质的愈发看重。标化考试曾长期在大学招生中占据着重要地位,它仿佛是一扇衡量学生学术能力的窗户,透过这扇窗户,招生官们试图窥探学生的智力水平...

葡萄酒品鉴知识:从新手到行家

葡萄酒品鉴知识:从新手到行家

葡萄酒,那是一种流淌在时光与味蕾之间的神奇液体,它承载着历史、文化与自然的馈赠。对于葡萄酒的品鉴,从新手到行家,是一段充满趣味与挑战的旅程,让我们一同踏上这美妙的品鉴之路。新手阶段,仿佛是打开葡萄酒世界大门的初始时刻。我们需要了解葡萄酒的基本分类。从颜色上,有红葡萄酒、白葡萄酒和桃红葡萄酒之分;从酿...

信用卡知识合理用卡避免陷阱

信用卡知识合理用卡避免陷阱

在当今的金融社会中,信用卡已成为人们生活中不可或缺的一部分。它为我们的消费提供了便利,让我们能够在无需携带大量现金的情况下轻松购物和支付。正如任何金融工具一样,信用卡也存在着一些陷阱,如果不加以合理使用,可能会给我们带来不必要的麻烦和损失。因此,了解信用卡知识,掌握合理用卡的方法,避免陷入信用卡陷阱...

风筝制作知识:放飞童年梦想

风筝制作知识:放飞童年梦想

风筝,那是童年天空中最绚丽的一抹色彩,承载着我们无尽的梦想与欢乐。它仿佛是一只自由的鸟儿,在蓝天与白云之间翱翔,而制作风筝的过程,就如同编织着我们童年的梦想。风筝的历史源远流长,早在中国古代,人们就开始制作风筝,用于传递信息、娱乐等多种用途。随着时间的推移,风筝的制作工艺不断发展和创新,成为了一门独...

灯谜知识感受传统民俗魅力

灯谜知识感受传统民俗魅力

灯谜,作为中华民族传统民俗文化的瑰宝之一,以其独特的形式和深厚的内涵,展现出了无尽的魅力。它不仅是一种智力游戏,更是一种传承千年的文化符号,让我们在解谜的过程中,感受到了传统民俗的博大精深。灯谜的起源可以追溯到古代的隐语和廋辞。在古代,人们为了表达自己的情感、思想或传递信息,常常使用隐语和廋辞。这些...

了解古代计时工具

了解古代计时工具

在历史的长河中,人类不断探索和发明各种计时工具,以记录时间的流逝。这些古老的计时工具不仅是技术的杰作,更是人类智慧的结晶,它们见证了时代的变迁,为我们了解古代社会提供了珍贵的线索。最早的计时工具之一是日晷。日晷利用太阳的投影来指示时间,通常由一个晷面和晷针组成。晷面通常是一个圆形或半圆形的盘面,上面...