C++使用 tinyFSM 实现有限状态机:从状态图到代码实践

0. 引言

在软件开发中,有限状态机(Finite State Machine,FSM)是一种重要的设计模式,广泛应用于系统建模、控制流管理和事件驱动程序设计。本文将介绍如何使用轻量级的 C++ 状态机库 tinyFSM 来实现一个有限状态机,从状态图描述到代码实现的全过程。

1. 状态机描述

初始状态
EvAtoB / 条件1 && 条件2
EvAtoB / 条件不满足
EvFailure
EvBtoA / 条件3
EvBtoA / 条件不满足
EvFailure
StateA
StateB
FailureState

如上图状态机,其流程可以用以下方式描述:

  • 状态 A:初始状态,可能包含一些子状态。
  • 状态 B:可通过满足特定条件从状态 A 转换而来。
  • 故障状态:当状态转换失败或条件不满足时进入的状态。

1.1 状态和转换

  • 状态 A

    • 子状态:状态 A 包含以下子状态:
      • 子状态 1
      • 子状态 2
      • 子状态 3
      • 子状态 4
      • 子状态 5
    • 转换条件
      • 当满足条件 condition1condition2 时,可以从状态 A 转换到状态 B。
      • 如果条件不满足,转换将失败,进入故障状态。
  • 状态 B

    • 转换条件
      • 当满足条件 condition3 时,可以从状态 B 转换回状态 A。
      • 如果条件不满足,转换将失败,进入故障状态。
  • 故障状态

    • 系统进入此状态后,需要处理错误或重置状态机。

1.2 枚举类

我们定义了一个枚举类 StatusFeedback,包含以下状态:

  • UNDEFINED:未定义状态。
  • STATE_A:状态 A。
  • STATE_B:状态 B。
  • FAILURE:故障状态。

2. tinyFSM 简介

tinyFSM 是一个用现代 C++ 编写的轻量级有限状态机库。它的特点包括:

  • 轻量级、易于集成,无第三方依赖。
  • 使用模板元编程,实现编译时多态,性能高效。
  • 支持事件驱动的状态转换。

3. 代码实现

下面我们使用 tinyFSM 来实现上述状态机。

3.1. 定义状态反馈枚举

引入头文件和命名空间

#include <iostream>
#include "tinyfsm.hpp"
enum class StatusFeedback
{
    UNDEFINED,
    STATE_A,
    STATE_B,
    FAILURE
};

3.2 定义事件

// 从状态 A 到状态 B 的事件
struct EvAtoB : tinyfsm::Event
{
    bool condition1;
    bool condition2;

    EvAtoB(bool cond1 = false, bool cond2 = false)
        : condition1(cond1), condition2(cond2) {}
};

// 从状态 B 到状态 A 的事件
struct EvBtoA : tinyfsm::Event
{
    bool condition3;

    EvBtoA(bool cond3 = false)
        : condition3(cond3) {}
};

// 故障事件
struct EvFailure : tinyfsm::Event { };

3.3 定义基状态类

class BaseState : public tinyfsm::Fsm<BaseState>
{
public:
    // 默认事件处理(未定义的事件)
    void react(EvAtoB const &) { }
    void react(EvBtoA const &) { }
    void react(EvFailure const &) { }

    virtual void entry() { }  // 进入状态时的动作
    virtual void exit() { }   // 离开状态时的动作

    // 获取当前状态反馈
    virtual StatusFeedback getStatus() const = 0;
};

3.4 前向声明具体状态类

class StateA;
class StateB;
class FailureState;

3.5 定义具体状态类

3.5.1 状态 A

class StateA : public BaseState
{
public:
    // 定义子状态
    enum class Substate
    {
        Substate1,
        Substate2,
        Substate3,
        Substate4,
        Substate5
    };

    Substate substate;

    void entry() override
    {
        std::cout << "进入状态 A" << std::endl;
        // 设置初始子状态
        substate = Substate::Substate1;
    }

    void exit() override
    {
        std::cout << "离开状态 A" << std::endl;
    }

    void react(EvAtoB const & event) override
    {
        std::cout << "状态 A:收到 EvAtoB" << std::endl;
        // 检查条件
        if (event.condition1 && event.condition2)
        {
            std::cout << "从状态 A 转换到状态 B" << std::endl;
            transit<StateB>();
        }
        else
        {
            std::cout << "条件不满足,进入故障状态" << std::endl;
            transit<FailureState>();
        }
    }

    void react(EvFailure const &) override
    {
        std::cout << "状态 A:收到 EvFailure,进入故障状态" << std::endl;
        transit<FailureState>();
    }

    StatusFeedback getStatus() const override
    {
        return StatusFeedback::STATE_A;
    }

    // 可添加处理子状态转换的额外方法
};

3.5.2 状态 B

class StateB : public BaseState
{
public:
    void entry() override
    {
        std::cout << "进入状态 B" << std::endl;
    }

    void exit() override
    {
        std::cout << "离开状态 B" << std::endl;
    }

    void react(EvBtoA const & event) override
    {
        std::cout << "状态 B:收到 EvBtoA" << std::endl;
        // 检查条件
        if (event.condition3)
        {
            std::cout << "从状态 B 转换到状态 A" << std::endl;
            transit<StateA>();
        }
        else
        {
            std::cout << "条件不满足,进入故障状态" << std::endl;
            transit<FailureState>();
        }
    }

    void react(EvFailure const &) override
    {
        std::cout << "状态 B:收到 EvFailure,进入故障状态" << std::endl;
        transit<FailureState>();
    }

    StatusFeedback getStatus() const override
    {
        return StatusFeedback::STATE_B;
    }
};

3.5.3 故障状态

class FailureState : public BaseState
{
public:
    void entry() override
    {
        std::cout << "进入故障状态" << std::endl;
    }

    void exit() override
    {
        std::cout << "离开故障状态" << std::endl;
    }

    StatusFeedback getStatus() const override
    {
        return StatusFeedback::FAILURE;
    }
};

4. 主函数

定义状态列表和初始状态

// 定义状态列表
using FsmList = tinyfsm::FsmList<StateA, StateB, FailureState>;

// 设置初始状态为 StateA
FSM_INITIAL_STATE(BaseState, StateA)
int main()
{
    // 启动状态机
    FsmList::start();

    // 获取当前状态
    BaseState *currentState = &BaseState::state();
    std::cout << "当前状态编号:" << static_cast<int>(currentState->getStatus()) << std::endl;

    // 尝试在条件不满足的情况下从状态 A 转换到状态 B
    EvAtoB ev_a2b(false, false);
    FsmList::dispatch(ev_a2b);

    // 在条件满足的情况下从状态 A 转换到状态 B
    EvAtoB ev_a2b_ok(true, true);
    FsmList::dispatch(ev_a2b_ok);

    // 从状态 B 转换回状态 A
    EvBtoA ev_b2a(true);
    FsmList::dispatch(ev_b2a);

    // 从状态 A 触发故障事件
    EvFailure ev_failure;
    FsmList::dispatch(ev_failure);

    return 0;
}

5. 运行结果

编译并运行上述代码,得到以下输出:

进入状态 A
当前状态编号:1
状态 A:收到 EvAtoB
条件不满足,进入故障状态
离开状态 A
进入故障状态
状态 A:收到 EvAtoB
状态 A:收到 EvAtoB
从状态 A 转换到状态 B
离开状态 A
进入状态 B
状态 B:收到 EvBtoA
从状态 B 转换到状态 A
离开状态 B
进入状态 A
状态 A:收到 EvFailure,进入故障状态
离开状态 A
进入故障状态

6. 代码解析

6.1 状态转换逻辑

  • 状态 A 到状态 B:当收到事件 EvAtoBcondition1condition2 均为 true 时,状态 A 转换到状态 B;否则,进入故障状态。
  • 状态 B 到状态 A:当收到事件 EvBtoAcondition3true 时,状态 B 转换到状态 A;否则,进入故障状态。
  • 故障状态处理:当任何状态收到 EvFailure 事件或条件不满足时,进入故障状态。

6.2 子状态管理

  • 在状态 A 中,定义了一个枚举 Substate 来表示子状态。进入状态 A 时,默认子状态为 Substate1
  • 可以根据需要在状态 A 中添加处理子状态转换的逻辑。

6.3 状态反馈

  • 使用 getStatus() 方法获取当前状态的反馈,可以用于监控和日志记录。

6.4 为什么 EvFailure 事件只影响了 状态 A 而不是 状态 B?

EvFailure ev_failure;    

FsmList::dispatch(ev_failure); 

这段代码为啥只打印如下:“状态 A:收到 EvFailure,进入故障状态” ,状态B为什么没有进入故障状态?

原因

tinyFSM 中,事件只会被派发给当前的活动状态,即状态机中的唯一一个处于活动状态的状态会对事件作出反应。对于您当前的代码来说,事件 EvFailure 只会发送给当前处于活动状态的状态,而不会广播给所有状态。

在您的程序运行时,状态 A 是当前的活动状态。所以当 EvFailure 被派发时,状态 A 对该事件作出了反应,并转换到 故障状态。因此输出:

状态 A:收到 EvFailure,进入故障状态

状态 B 没有收到 EvFailure 的原因是:状态 B 在事件派发时不是当前的活动状态,因此不会对事件作出反应。

具体流程

让我们按步骤分析一下代码的运行流程,帮助更清楚地理解:

  • 启动状态机:当程序启动时,状态机被初始化,并设置为从 状态 A 开始。

    • 输出:
      进入状态 A
      
  • 触发 EvAtoB 事件(条件不满足)

    • 当前状态:状态 A

    • 事件:EvAtoB,条件不满足(condition1 = falsecondition2 = false)。

    • 状态 A 检查事件条件,条件不满足,于是它进入 故障状态

    • 输出:

      状态 A:收到 EvAtoB
      条件不满足,进入故障状态
      离开状态 A
      进入故障状态
      
  • 触发 EvAtoB 事件(条件满足)

    • 当前状态:故障状态

    • 事件:EvAtoB,条件满足(condition1 = truecondition2 = true)。

    • 故障状态EvAtoB 不作出反应,因为它没有处理 EvAtoB 事件的逻辑。故障状态会忽略这个事件。

    • 没有输出,因为没有任何反应。

  • 从状态 A 触发 EvFailure 事件

    • 当前状态:状态 A

    • 事件:EvFailure

    • 状态 AEvFailure 事件作出反应,进入 故障状态

    • 输出:

      状态 A:收到 EvFailure,进入故障状态
      离开状态 A
      进入故障状态
      

7. 附录:完整代码

// g++ -std=c++11 -o fsm_example main.cpp
#include <iostream>
#include "tinyfsm.hpp"

// 定义状态反馈枚举
enum class StatusFeedback
{
    UNDEFINED,
    STATE_A,
    STATE_B,
    FAILURE
};

// 定义事件
// 从状态 A 到状态 B 的事件
struct EvAtoB : tinyfsm::Event
{
    bool condition1;
    bool condition2;

    EvAtoB(bool cond1 = false, bool cond2 = false)
        : condition1(cond1), condition2(cond2) {}
};

// 从状态 B 到状态 A 的事件
struct EvBtoA : tinyfsm::Event
{
    bool condition3;

    EvBtoA(bool cond3 = false)
        : condition3(cond3) {}
};

// 故障事件
struct EvFailure : tinyfsm::Event { };

// 定义基状态类
class BaseState : public tinyfsm::Fsm<BaseState>
{
public:
    // 默认事件处理(未定义的事件)
    void react(EvAtoB const &) { }
    void react(EvBtoA const &) { }
    void react(EvFailure const &) { }

    virtual void entry() { }  // 进入状态时的动作
    virtual void exit() { }   // 离开状态时的动作

    // 获取当前状态反馈
    virtual StatusFeedback getStatus() const = 0;
};

// 前向声明具体状态类
class StateA;
class StateB;
class FailureState;

// 定义具体状态类

// 状态 A
class StateA : public BaseState
{
public:
    // 定义子状态
    enum class Substate
    {
        Substate1,
        Substate2,
        Substate3,
        Substate4,
        Substate5
    };

    Substate substate;

    void entry() override
    {
        std::cout << "进入状态 A" << std::endl;
        // 设置初始子状态
        substate = Substate::Substate1;
    }

    void exit() override
    {
        std::cout << "离开状态 A" << std::endl;
    }

    void react(EvAtoB const & event) override
    {
        std::cout << "状态 A:收到 EvAtoB" << std::endl;
        // 检查条件
        if (event.condition1 && event.condition2)
        {
            std::cout << "从状态 A 转换到状态 B" << std::endl;
            transit<StateB>();
        }
        else
        {
            std::cout << "条件不满足,进入故障状态" << std::endl;
            transit<FailureState>();
        }
    }

    void react(EvFailure const &) override
    {
        std::cout << "状态 A:收到 EvFailure,进入故障状态" << std::endl;
        transit<FailureState>();
    }

    StatusFeedback getStatus() const override
    {
        return StatusFeedback::STATE_A;
    }

    // 可添加处理子状态转换的额外方法
};

// 状态 B
class StateB : public BaseState
{
public:
    void entry() override
    {
        std::cout << "进入状态 B" << std::endl;
    }

    void exit() override
    {
        std::cout << "离开状态 B" << std::endl;
    }

    void react(EvBtoA const & event) override
    {
        std::cout << "状态 B:收到 EvBtoA" << std::endl;
        // 检查条件
        if (event.condition3)
        {
            std::cout << "从状态 B 转换到状态 A" << std::endl;
            transit<StateA>();
        }
        else
        {
            std::cout << "条件不满足,进入故障状态" << std::endl;
            transit<FailureState>();
        }
    }

    void react(EvFailure const &) override
    {
        std::cout << "状态 B:收到 EvFailure,进入故障状态" << std::endl;
        transit<FailureState>();
    }

    StatusFeedback getStatus() const override
    {
        return StatusFeedback::STATE_B;
    }
};

// 故障状态
class FailureState : public BaseState
{
public:
    void entry() override
    {
        std::cout << "进入故障状态" << std::endl;
    }

    void exit() override
    {
        std::cout << "离开故障状态" << std::endl;
    }

    StatusFeedback getStatus() const override
    {
        return StatusFeedback::FAILURE;
    }
};

// 定义状态列表
using FsmList = tinyfsm::FsmList<StateA, StateB, FailureState>;

// 设置初始状态为 StateA
FSM_INITIAL_STATE(BaseState, StateA)

int main()
{
    // 启动状态机
    FsmList::start();

    // 获取当前状态
    BaseState *currentState = &BaseState::state();
    std::cout << "当前状态编号:" << static_cast<int>(currentState->getStatus()) << std::endl;

    // 尝试在条件不满足的情况下从状态 A 转换到状态 B
    EvAtoB ev_a2b(false, false);
    FsmList::dispatch(ev_a2b);

    // 在条件满足的情况下从状态 A 转换到状态 B
    EvAtoB ev_a2b_ok(true, true);
    FsmList::dispatch(ev_a2b_ok);

    // 从状态 B 转换回状态 A
    EvBtoA ev_b2a(true);
    FsmList::dispatch(ev_b2a);

    // 从状态 A 触发故障事件
    EvFailure ev_failure;
    FsmList::dispatch(ev_failure);

    return 0;
}

8. 参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘色的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值