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

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

admin14小时前知识普及3

用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://www.zhihuibkw.com/post/6394.html

分享给朋友:

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

垃圾分类知识为环保出份力

垃圾分类知识为环保出份力

在当今社会,环保已经成为了我们每个人不可推卸的责任。而垃圾分类作为环保工作中的重要一环,更是对我们生活环境的改善起着至关重要的作用。垃圾分类,顾名思义,就是将垃圾按照其不同的性质、成分、利用价值以及对环境的影响等因素,进行分门别类的收集、运输和处理。看似简单的一个举动,却蕴含着深远的意义。从资源回收...

理财小白入门:基础金融知识扫盲

理财小白入门:基础金融知识扫盲

在当今社会,理财已经不再是富人的专属话题,越来越多的理财小白开始关注并尝试参与到金融市场中。对于初涉理财领域的人来说,金融知识往往如同一座神秘的迷宫,充满了各种专业术语和复杂的概念。本文将为理财小白们揭开基础金融知识的神秘面纱,帮助大家建立起基本的理财观念和知识框架。一、货币与货币时间价值货币是我们...

法律常识维护自身权益的武器

法律常识维护自身权益的武器

在生活的舞台上,我们每个人都可能遭遇各种权益被侵犯的情况,无论是在消费领域、职场之中,还是在日常生活的点滴之中。当权益受到侵害时,我们不能只是默默忍受,而应拿起法律这一强大的武器,来捍卫自己的合法权益。法律常识就如同我们手中的利剑,能在关键时刻为我们劈开迷雾,指引我们走向公正的道路。了解基本的法律概...

昆虫知识认识身边的小昆虫

昆虫知识认识身边的小昆虫

在我们的日常生活中,身边常常有一些小小的生物在默默活动,它们就是昆虫。昆虫是地球上最繁盛的动物类群,种类繁多,形态各异,几乎无处不在。通过了解昆虫知识,我们能更好地认识这些微小而神奇的生命,也能更加珍惜和保护我们的自然环境。让我们来认识一些常见的昆虫。蜜蜂是大家非常熟悉的昆虫,它们忙碌地在花丛中飞来...

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

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

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

如何正确使用防晒霜

如何正确使用防晒霜

在阳光明媚的日子里,防晒霜成为了我们肌肤的重要守护者。它能够有效阻挡紫外线的伤害,预防晒伤、晒黑以及紫外线长期照射可能引发的肌肤问题,如皱纹、色斑等。仅仅涂抹防晒霜是远远不够的,正确的使用方法才能让它发挥出最大的功效。选择合适的防晒霜是至关重要的。市面上的防晒霜种类繁多,根据防晒指数(SPF)和防护...