001.SML状态机
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状态机相关推荐
- boost::sml 状态机
boost::sml 最近接触了一些状态机的东西,发现这个确实蛮方便的,已经在boost库里写好了,记录一下. 举个例子先 #include <iostream> #include < ...
- FPGA之道(67)代码中的约束信息(四)状态机的相关约束
文章目录 前言 状态机的相关约束 fsm_extract fsm_style fsm_encoding enum_encoding safe_implementation safe_recovery_ ...
- FPGA之道(55)状态机的HDL模板
文章目录 前言 状态机的HDL模板 状态集合的HDL定义 概念层级的定义 实现层级的定义 if or case? 各自特点分析 状态选择 次态及输出选择 状态的HDL描述方式 次态和次中间变量的描述方 ...
- FPGA中状态机实现需要注意的地方
写状态机时需要注意的地方: 1.状态机的状态编码大概有三种:二进制编码,格雷码,独热码. 其中二进制编码就是000.001.010.011.100这类的逐渐加一 格雷码就是相邻两个 ...
- Verilog --状态机编码方式
状态机所包含的N种状态通常需要用某种编码方式表示,即状态编码,或状态分配. 选择合适的编码方案,将有助于电路的面积和资源的利用. 状态编码最常见的三种类型是:顺序二进制编码.格雷码和独热码.约翰逊编码 ...
- Unity 3.Adventure Game tutorial(事件系统、动画状态机、库存、条件、反应、交互、游戏状态)
双语机翻视频: https://www.bilibili.com/video/av34383045/ 在官网asset Store下载了完整工程,和pdf asset Store:https://as ...
- mooremealy状态机区分(附例子代码)三段式描述方式
在状态机部分,moore和mealy也算是老生常谈了吧. 什么是状态? 说白了就是通过时钟信号不断改变当前的状态,可能是根据输入的数据,也可能是自身发生改变(比如一些计时器),所以少不了触发器,虽然我 ...
- verilog中一文搞懂有限状态机(FSM)Mealy和Moore状态机(及一段式,二段式,三段式)
三段式 1.什么是有限状态机 2.Mealy 状态机 2.Moore FSM 3.Mealy 和 Moore的区别 4.Encoding 风格 设计原则 5. 一段式状态机 6. 二段式状态机 控制c ...
- 状态机——简单自动售卖机的实现
状态机也称为同步有限状态机,分为more型状态机(最后的输出只与当前状态有关,而与输入无关)和mealy型状态机(最后的输出不仅与当前状态有关,还与输入有),适合用来表示事件发生有先后顺序的情况.这里 ...
最新文章
- python怎么用matplotlib生成图表_Python让图表奔跑起来,Matplotlib的神奇用处
- opencv中的push_back()函数
- Linux内核网络数据包发送(一)
- Map的Value值转换为List集合
- 相似图片搜索的原理(转)
- tensorflow中的log中数字的含义
- patator mysql 字典_利用patator进行子域名爆破
- 初学者的React全家桶完整实例
- Windows域环境下的网络问题
- 12 自定义标签/JSTL标签库/web国际化/java web之设计模式和案例
- Go语言学习路线图 初阶+中阶+高阶
- VBS整人代码 很多 测试把我给整安逸了
- 无人驾驶环境感知 | 01 车道线检测网络LanNet原理及实现
- 群晖服务器名修改,闻上云刷黑群晖后免拆机修改序列号和mac地址
- JetBrains提示我“No suitable licenses associated with account balabala”
- Android面试题Java基础篇
- selenium:class属性内带有空格的定位坑
- php后台登录,简单的PHP数据后台实现用户登录,php后台用户登录
- GT21L16S2Y点阵数据的读取显示
- CPU的指令集(指令系统)
热门文章
- springboot整合 neo4j (OGM+JPA方式操作图数据库)
- new Date将字符串转化成日期格式 兼容IE,ie8如何通过new Date将字符串转化成日期格式,js中如何进行字符串替换, replace() 方法详解
- 冬日舞会服务器维护中,冬日舞会
- android图片压缩工具类
- 【读书笔记】十年涨薪30倍:财务职场透视.html.pdf
- http://blog.sina.com.cn/s/blog_7f5ad8c20101cnna.html#commonComment
- 网络爬虫爬取某网数据并制作词云全过程【内附可执行代码注释完整】
- python 获取巨量星图数据
- python 3.10上安装pyqt5
- rk3399 usbwifi Miracast调试