3 个版本 (有破坏性)
0.7.0 | 2024年4月9日 |
---|---|
0.6.0 | 2023年2月28日 |
0.5.1 | 2022年10月20日 |
0.5.0 |
|
#278 in 算法
每月下载量 65
22KB
296 行
edfsm - 事件驱动有限状态机
事件驱动有限状态机是一种用于控制和监控应用的有用形式化方法。关键概念包括
- 状态机维护的 状态 代表其外部环境和历史。
- 事件 代表环境的变化,是状态机的输入之一。事件的处理可能会导致状态的更新。
- 命令 是可能引起状态机执行效果的输入。命令也可能生成事件,从而更新状态。
- 效果 是影响环境的一些操作。
命令产生的效果(如果有的话),将取决于命令和先验状态。这模拟了 Mealy 机的 主动 行为。效果也可能在事件处理之后产生,它将取决于后验状态。这模拟了 Moore 机的 被动 行为。
为什么选择 edfsm?
edfsm 及其特定的领域特定语言(DSL)可以帮助您确定在声明状态的情况下处理命令和事件所需的功能,并对其声明进行强类型检查。简而言之,edfsm 通过利用编译器来断言您对状态机转换的声明,旨在提高您状态机的代码质量。
DSL
提供了一种属性宏,它提供了一种领域特定语言(DSL)映射,直接从有限状态机描述到代码。DSL 的目标是以一种与设计紧密匹配的方式传达有限状态机。使用该宏,编译也确保开发人员处理正确的状态、命令和事件类型。
以下是一个示例,假设已声明状态、命令、事件和效果处理程序
struct MyFsm;
#[impl_fsm]
impl Fsm for MyFsm {
type S = State;
type C = Command;
type E = Event;
type SE = EffectHandlers;
state!(Running / entry);
command!(Idle => Start => Started => Running);
command!(Running => Stop => Stopped => Idle);
ignore_command!(Idle => Stop);
ignore_command!(Running => Start);
}
state!
宏声明与状态相关的属性。目前,可以声明入口处理程序。在我们的例子中,该宏将确保为 MyFsm
调用 on_entry_running
方法。然后,开发人员需要实现这些方法,例如。
fn on_entry_running(_old_s: &Running, _se: &mut EffectHandlers) {
// Do something
}
command!
宏声明给定命令时应执行的操作,使用以下形式
<from-state> => <given-command> [=> <yields-event> [=> <to-state>]]
在声明状态时,也可以使用通配符,例如使用
_
代替<from-state>
和<to-state>
。
在我们的示例中,对于第一步声明,将调用多个开发者必须提供的函数,例如。
fn for_idle_start(_s: &Idle, _c: Start, _se: &mut EffectHandlers) -> Option<Started> {
// Perform some effect here if required. Effects are performed via the EffectHandler
Some(Started)
}
fn on_idle_started(_s: &Idle, _e: &Started) -> Option<Running> {
Some(Running)
}
注意,也可以使用
event!
宏来为事件声明步骤(未显示)。然后形式变为<from-state> => <given-event> [=> <to-state> [ / action]]
(
/ action
可用于声明将执行副作用)
ignore_command!
宏描述了在给定情况下应忽略的状态和命令
<from-state> => <given-command>
注意,如果没有提供
ignore_command!
声明,则不会强制执行状态和命令的穷举匹配。
还有一个
ignore_event!
宏可以忽略事件,其中事件提供输入。
给定可变状态和命令后,状态机将前进。可以同时发射一个可选事件以及可能的状态转换,例如。
let mut s = State::Idle(Idle);
let c = Command::Start(Start);
// Now step the state machine with the state and command,
// and, an (undeclared) effect handler.
let (e, t) = MyFsm::step(&mut s, Input::Command(c), &mut se);
状态也可以通过重新播放事件来重新构成。如果没有过渡到全新的状态,则现有状态可能仍然已被更新。以下是将事件应用于状态的示例,如果需要则更新状态,并有一个可选的r
表示发生了某种类型的更改,或者None
否则。
let r = MyFsm::on_event(&mut s, &e);
在状态本身代表具有其字段的更细粒度状态,并且我们希望直接更新它们的情况下,修改状态可能非常有用。例如,给定我们之前的表示
command!(Running => Stop => Stopped => Idle);
...
command!(Running => Stop => Stopped);
即如果我们删除目标状态,则关联的函数将能够修改状态,并且不能返回任何转换,因为它们是相互排斥的操作。以下是根据上述command!
的示例签名。
fn on_idle_started(s: &mut Idle, e: &Started) {
// `s` can now be mutated given some `e`.
}
请参阅event_driven/tests文件夹中的完整示例,包括在没有目标状态的情况下(即在发射事件但不转换时)修改传入状态的能力。
no_std
该库能够支持no_std
,并设计用于与嵌入式目标的高效使用。
贡献政策
我们欢迎从其原始作者那里通过GitHub拉取请求进行贡献。请随任何拉取请求一起声明,贡献是您自己的作品,并且您将工作许可给项目,根据项目的开源许可进行许可。无论您是否明确声明,通过拉取请求、电子邮件或其他方式提交任何受版权保护的材料,您同意根据项目的开源许可许可材料,并保证您有权这样做。
许可
此代码是开源软件,根据Apache-2.0许可许可。
© 版权所有 Titan Class P/L,2022-2024
依赖关系
~1.5MB
~37K SLoC