3 个版本 (有破坏性)

0.7.0 2024年4月9日
0.6.0 2023年2月28日
0.5.1 2022年10月20日
0.5.0 2022年10月20日

#278 in 算法

Download history 14/week @ 2024-04-29 20/week @ 2024-05-20 32/week @ 2024-06-03 71/week @ 2024-06-10 14/week @ 2024-06-17 28/week @ 2024-06-24 15/week @ 2024-07-08 22/week @ 2024-07-22 19/week @ 2024-07-29 24/week @ 2024-08-05

每月下载量 65

Apache-2.0

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