1,创建事件和状态

状态机由有限数量的状态和转换组成,这些状态和转换通过事件触发

事件只是一种唯一的类型,它将由状态机处理。

struct my_event { ... };

您还可以创建事件实例来简化转换表表示法

auto event = sml::event<my_event>;

如果您碰巧有一个Clang/GCC编译器,您可以动态创建一个Event

using namespace sml;
auto event  = "event"_e;

注:这种创建的方式不会存储任何的数据信息

当机器进入/离开State时,State可以执行进入/退出行为,并表示状态机流的当前位置。

auto idle = sml::state<class idle>;

如果你碰巧有一个Clang/GCC编译器,你可以动态地创建一个State。

using namespace sml;
auto state  = "idle"_s;

注:SML状态不能有数据,因为数据直接被注入到守卫/动作中

状态机可能是状态本身

sml::state<state_machine> composite;

SML支持终止状态,终止状态将停止要处理的事件。它由X定义。回到本来状态的本身

"state"_s = X;

状态也可以打印

assert(string("idle") == "idle"_s.c_str());

2,创建守卫和动作

守卫和动作是可调用的对象,将由状态机执行,以验证转换后的操作是否应该发生。

注:守卫必须返回一个bool的值

auto guard1 = [] {return true;
};auto guard2 = [](int, double) { // guard with dependenciesreturn true;
};auto guard3 = [](int, auto event, double) { // guard with an event and dependenciesreturn true;
};struct guard4 {bool operator()() const noexcept {return true;}
};

动作不需要返回值

auto action1 = [] { };
auto action2 = [](int, double) { }; // action with dependencies
auto action3 = [](int, auto event, double) { }; // action with an event and dependencies
struct action4 {void operator()() noexcept { }
};

3,创建一个转换表

当我们有了状态和事件,我们最终可以创建一个转换表来表示我们的转换。

SML使用类似于euml的DSL是为了尽可能接近UML设计。

后缀表示法:

Expression Description
state + event [ guard ] 警戒时事件e的内部过渡
src_state / [] {} = dst_state 带有操作的匿名转换
src_state / [] {} = src_state 自转换 (调用 on_exit/on_entry)
src_state + event = dst_state 事件e的外部过渡,没有警戒或动作
src_state + event [ guard ] / action = dst_state 在事件e上使用保护和动作从src_state转换到dst_state
src_state + event [ guard && (![]{return true;} && guard2) ] / (action, action2, []{}) = dst_state 在事件e上使用保护和动作从src_state转换到dst_state

过度流:

src_state + event [ guard ] / action = dst_state^||1. src_state + on_exit2. dst_state + on_entry

为了创建一个转换表,提供了make_transition_table。

using namespace sml; // Postfix Notationmake_transition_table(*"src_state"_s + event<my_event> [ guard ] / action = "dst_state"_s
, "dst_state"_s + "other_event"_e = X
);

4,设置初始化状态

初始状态告诉状态机从哪里开始。它可以通过在State前加上*来设置

using namespace sml;
make_transition_table(*"src_state"_s + event<my_event> [ guard ] / action = "dst_state"_s,"dst_state"_s + event<game_over> = X
);

可以有多个初始态。所有初始状态将以伪并行的方式执行。这样的状态称为正交区域

using namespace sml;
make_transition_table(*"region_1"_s   + event<my_event1> [ guard ] / action = "dst_state1"_s,"dst_state1"_s + event<game_over> = X,*"region_2"_s   + event<my_event2> [ guard ] / action = "dst_state2"_s,"dst_state2"_s + event<game_over> = X
);

5,创建一个状态机

状态机是保存当前状态和处理事件的转换表的抽象。要创建状态机,我们必须添加一个转换表

class example {
public:auto opeartor()() {using namespace sml;return make_transition_table(*"src_state"_s + event<my_event> [ guard ] / action = "dst_state"_s,"dst_state"_s + event<game_over> = X);}
};

配置好转换表后,我们可以创建一个状态机

状态机构造函数为动作和守护提供必需的依赖项

                           /---- event (injected from process_event)|
auto guard = [](double d, auto event) { return true; }|\--------\|
auto action = [](int i){}   ||         ||         |\-\   /---/|   |
sml::sm<example> s{42, 87.0};sml::sm<example> s{87.0, 42}; // order in which parameters have to passed is not specificied

传递和维护大量的依赖关系可能是乏味的,并且需要大量的样板代码。

为了避免这种情况,可以使用依赖注入库来自动化这个过程。例如,我们可以使用ext Boost.DI。

auto injector = di::make_injector(di::bind<>.to(42), di::bind<interface>.to<implementation>()
);auto sm = injector.create<sm<example>>();
sm.process_event(e1{});

6,处理事件

状态机是一种简单的creature。它的主要目的是处理事件。要做到这一点,可以使用process_event方法。

sml::sm<example> sm;sm.process_event(my_event{}); // handled
sm.process_event(int{}); // not handled -> unexpected_event<int>

也可能在转换表上触发流程事件,也就是一个事件触发后,接着在动作的位置触发事件流

using namespace sml;
return make_transition_table(*"s1"_s + event<my_event> / process_event(other_event{}) = "s2"_s,"s2"_s + event<other_event> = X
);

8,解决错误

当状态机不能处理给定事件时,将触发一个unexpected_event。

make_transition_table(*"src_state"_s + event<my_event> [ guard ] / action = "dst_state"_s
, "src_state"_s + unexpected_event<some_event> = X
);

任何意外事件也可以通过使用unexpected_event<_>来处理。

make_transition_table(*"src_state"_s + event<my_event> [ guard ] / action = "dst_state"_s
, "src_state"_s + unexpected_event<some_event> / [] { std::cout << "unexpected 'some_event' << '\n'; "}
, "src_state"_s + unexpected_event<_> = X // any event
);

sm.process_event (some_event {});// "意外的'some_event' sm.process_event(int{});/ /terminate assert(sm.is (X));

Usually, it's handy to create additional `Orthogonal region` to cover this scenario,
This way State causing unexpected event does not matter.```cpp
make_transition_table(*"idle"_s + event<my_event> [ guard ] / action = "s1"_s
, "s1"_s + event<other_event> [ guard ] / action = "s2"_s
, "s2"_s + event<yet_another_event> [ guard ] / action = X
// terminate (=X) the Machine or reset to another state
,*"error_handler"_s + unexpected_event<some_event> = X
);

我们总是可以通过。来检查状态机是否处于终止状态。

assert(sm.is(sml::X)); // doesn't matter how many regions there are

当异常被启用(project不是用-fno-exceptions编译)时,可以使用exception<name>语法捕获它们。

</name>异常处理程序将按照它们被定义的顺序进行处理,Exception <>可以用来捕获任何东西(相当于catch(…))。

请注意,当转换表中没有定义异常处理程序时,状态机将不会处理异常。

make_transition_table(*"idle"_s + event<event> / [] { throw std::runtime_error{"error"}; }
,*"error_handler"_s + exception<std::runtime_error> = X
, "error_handler"_s + exception<std::logic_error> = X
, "error_handler"_s + exception<> / [] { cleanup...; } = X // any exception
);

9,测试它

有时,验证状态机是否处于特定状态是很有用的,例如,我们是否处于终止状态。我们可以使用is或visit_current_states功能来使用SML。

sml::sm<example> sm;
sm.process_event(my_event{});
assert(sm.is(X)); // is(X, s1, ...) when you have orthogonal regions//orsm.visit_current_states([](auto state) { std::cout << state.c_str() << std::endl; });

在此基础上,SML提供测试工具来整体检查状态机。在test::sm中可以使用Set_current_states方法来将状态机设置为请求状态。

sml::sm<example, sml::testing> sm{fake_data...};
sm.set_current_states("s3"_s); // set_current_states("s3"_s, "s1"_s, ...) for orthogonal regions
sm.process_event(event{});
assert(sm.is(X));

番外篇:

Initial Pseudostate

Terminate Pseudostate

External transition

Anonymous transition

001.SML状态机相关推荐

  1. boost::sml 状态机

    boost::sml 最近接触了一些状态机的东西,发现这个确实蛮方便的,已经在boost库里写好了,记录一下. 举个例子先 #include <iostream> #include < ...

  2. FPGA之道(67)代码中的约束信息(四)状态机的相关约束

    文章目录 前言 状态机的相关约束 fsm_extract fsm_style fsm_encoding enum_encoding safe_implementation safe_recovery_ ...

  3. FPGA之道(55)状态机的HDL模板

    文章目录 前言 状态机的HDL模板 状态集合的HDL定义 概念层级的定义 实现层级的定义 if or case? 各自特点分析 状态选择 次态及输出选择 状态的HDL描述方式 次态和次中间变量的描述方 ...

  4. FPGA中状态机实现需要注意的地方

    写状态机时需要注意的地方: 1.状态机的状态编码大概有三种:二进制编码,格雷码,独热码.      其中二进制编码就是000.001.010.011.100这类的逐渐加一      格雷码就是相邻两个 ...

  5. Verilog --状态机编码方式

    状态机所包含的N种状态通常需要用某种编码方式表示,即状态编码,或状态分配. 选择合适的编码方案,将有助于电路的面积和资源的利用. 状态编码最常见的三种类型是:顺序二进制编码.格雷码和独热码.约翰逊编码 ...

  6. Unity 3.Adventure Game tutorial(事件系统、动画状态机、库存、条件、反应、交互、游戏状态)

    双语机翻视频: https://www.bilibili.com/video/av34383045/ 在官网asset Store下载了完整工程,和pdf asset Store:https://as ...

  7. mooremealy状态机区分(附例子代码)三段式描述方式

    在状态机部分,moore和mealy也算是老生常谈了吧. 什么是状态? 说白了就是通过时钟信号不断改变当前的状态,可能是根据输入的数据,也可能是自身发生改变(比如一些计时器),所以少不了触发器,虽然我 ...

  8. verilog中一文搞懂有限状态机(FSM)Mealy和Moore状态机(及一段式,二段式,三段式)

    三段式 1.什么是有限状态机 2.Mealy 状态机 2.Moore FSM 3.Mealy 和 Moore的区别 4.Encoding 风格 设计原则 5. 一段式状态机 6. 二段式状态机 控制c ...

  9. 状态机——简单自动售卖机的实现

    状态机也称为同步有限状态机,分为more型状态机(最后的输出只与当前状态有关,而与输入无关)和mealy型状态机(最后的输出不仅与当前状态有关,还与输入有),适合用来表示事件发生有先后顺序的情况.这里 ...

最新文章

  1. python怎么用matplotlib生成图表_Python让图表奔跑起来,Matplotlib的神奇用处
  2. opencv中的push_back()函数
  3. Linux内核网络数据包发送(一)
  4. Map的Value值转换为List集合
  5. 相似图片搜索的原理(转)
  6. tensorflow中的log中数字的含义
  7. patator mysql 字典_利用patator进行子域名爆破
  8. 初学者的React全家桶完整实例
  9. Windows域环境下的网络问题
  10. 12 自定义标签/JSTL标签库/web国际化/java web之设计模式和案例
  11. Go语言学习路线图 初阶+中阶+高阶
  12. VBS整人代码 很多 测试把我给整安逸了
  13. 无人驾驶环境感知 | 01 车道线检测网络LanNet原理及实现
  14. 群晖服务器名修改,闻上云刷黑群晖后免拆机修改序列号和mac地址
  15. JetBrains提示我“No suitable licenses associated with account balabala”
  16. Android面试题Java基础篇
  17. selenium:class属性内带有空格的定位坑
  18. php后台登录,简单的PHP数据后台实现用户登录,php后台用户登录
  19. GT21L16S2Y点阵数据的读取显示
  20. CPU的指令集(指令系统)

热门文章

  1. springboot整合 neo4j (OGM+JPA方式操作图数据库)
  2. new Date将字符串转化成日期格式 兼容IE,ie8如何通过new Date将字符串转化成日期格式,js中如何进行字符串替换, replace() 方法详解
  3. 冬日舞会服务器维护中,冬日舞会
  4. android图片压缩工具类
  5. 【读书笔记】十年涨薪30倍:财务职场透视.html.pdf
  6. http://blog.sina.com.cn/s/blog_7f5ad8c20101cnna.html#commonComment
  7. 网络爬虫爬取某网数据并制作词云全过程【内附可执行代码注释完整】
  8. python 获取巨量星图数据
  9. python 3.10上安装pyqt5
  10. rk3399 usbwifi Miracast调试