#state-machine #static #no-std

no-std sfsm

为无std和嵌入式环境提供的静态状态机生成器

13个不稳定版本 (3个破坏性更新)

0.4.3 2022年5月15日
0.4.2 2021年10月21日
0.4.0 2021年9月16日
0.3.0 2021年8月22日
0.1.1 2021年4月15日

#295嵌入式开发

Download history 4/week @ 2024-03-14 2/week @ 2024-03-21 7/week @ 2024-03-28 3/week @ 2024-04-04 8/week @ 2024-05-23 11/week @ 2024-05-30

每月313 次下载

MIT 协议

11KB

为无std和嵌入式环境提供的静态状态机生成器

crates.io crates.io crates.io

状态机是许多软件架构的必要组成部分,尤其是在嵌入式系统等底层系统中特别常见。它们可以将复杂系统分解成许多小的状态,并明确定义它们之间的转换。虽然它们有助于分解复杂性,但它们也必须很好地记录,以便理解。

Rust因其枚举的设计方式非常适合实现状态机。不幸的是,这仍然伴随着大量的样板代码。

Sfsm旨在让用户实现简单、高效且易于审查的状态机,这些状态机适用于嵌入式系统。因此,主要目标是

主要目标是

  • 无std兼容性
  • 自我文档化
  • 易于使用
  • 低成本

Sfsm试图通过提供sfsm-proc中的状态机生成器以及在sfsm-proc中提供转换和状态特性来实现这些目标。通过这种方式,用户可以指定整个状态机,只需要几行易于审查的代码。从这些定义中,可以生成整个状态机,而不依赖于动态机制,从而允许其完全静态。剩下的只是实现满足转换和状态特性的必要状态和转换。

使用方法

普通状态机

可以使用以下宏调用定义状态机。

 // Only relevant parts included.

 add_state_machine!(
     Rocket,                          // Name of the state machine. Accepts a visibility modifier.
     WaitForLaunch,                   // The initial state the state machine will start in
     [WaitForLaunch, Launch],         // All possible states
     [
         WaitForLaunch => Launch,     // All transitions
     ]
 );

这将生成一个名为 Rocket 的状态机,其初始状态为 WaitForLaunch。状态机可以处于两种可能的状态 - WaitForLaunchLaunchWaitForLaunch 是初始状态,可以通过 WaitForLaunch => Launch 转换定义转换为 Launch。状态机可以有任意多的状态和转换,但所有这些都必须实现 State 和相应的 Transition 特性。

错误处理状态机

使用 add_fallible_state_machine 宏,可以生成具有内在错误处理的状态机。一旦发生指定的错误,状态机将立即跳转到错误状态,在那里可以处理错误。

 // Only relevant parts included.

 add_fallible_state_machine!(
    Rocket,                                      // Name of the state machine. Accepts a visibility modifier.
    WaitForLaunch,                               // The initial state the state machine will start in
    [WaitForLaunch, Launch, HandleMalfunction],  // All possible states
    [
        WaitForLaunch => Launch,                 // All possible Transitions
        HandleMalfunction => WaitForLaunch
    ],
    RocketMalfunction,                           // The error type
    HandleMalfunction                            // The error state
 );

类似于普通状态机,这将生成一个用户需要实现状态和转换行为的状态机。在可出错的状态机中,必须实现 TryStateTryTransition 特性。此外,错误状态必须实现 TryErrorState 特性以定义如何处理错误。

分层状态机

在复杂环境中,通常将较小的内部状态机封装在较大的外部状态机中,以将复杂性分解成更易管理的部分。以下代码演示了如何构建嵌套状态机。

 // Only relevant parts included.
 
 // Defines the Forward Observer top-level state machine.
 add_state_machine!(
     ForwardObserver,
     Offline,
     [Offline, Online],
     [
         Offline => Online,
         Online => Offline,
     ]
 );
 
 // Defines the Online inner state machine.
 add_state_machine!(
     OnlineMachine,
     Standby,
     [Standby, Requesting, Observing, Reporting],
     [
         Standby => Requesting,
         Requesting => Observing,
         Observing => Reporting,
         Reporting => Standby,
     ]
 );
 
 struct Online {
     state_machine: OnlineMachine,
 }
 
 impl State for Online {
     /// Executes the sub-state machine on each step.
     fn execute(&mut self) {
         self.state_machine.step().unwrap();
     }
 }
 
 impl From<Offline> for Online {
     /// Constructs, and starts, the [`Online`] state machine on a transition from Offline
     fn from(_: Offline) -> Self {
         let mut online = Self {
             state_machine: OnlineMachine::new(),
         };
         online.state_machine.start(Standby {}).unwrap();
 
         online
     }
 }

这封装了较小的 OnlineMachineOnline 状态中。

消息系统

此外,可以定义推送到状态或从状态中轮询的消息。

 // Only relevant parts included.

 add_messages!(
     Rocket,
     [
         StartLaunch -> WaitForLaunch,      // Command the WaitForLaunch state to liftoff
         Status <- Launch,                  // Poll the status of the launch state
     ]
 );

这创建代码以将 StartLaunch 推送到 WaitForLaunch 状态,并从 Launch 状态轮询 Status。每个状态可以有多个接收和返回消息。它们必须实现相应的 ReturnMessageReceiveMessage 特性。

特性

跟踪

在调试状态机时,尤其是在现场调试时,了解状态机的行为、它经历了哪些转换以及错误发生的位置非常有帮助。使用跟踪功能,sfsm 状态机内置了一个机制来创建此类日志。要使用它,只需启用所需功能并添加一个记录器函数。

以下跟踪模式作为功能可用

[dependencies]
sfsm = {
    version = "*", 
    features = [
        "trace",            // Trace start, stop, transitions, entries and exits
        "trace-messages",   // Trace executes 
        "trace-steps"       // Trace message push and polls
    ]}

跟踪功能可以按需组合。要使跟踪正常工作,必须提供记录器函数,使用 #[sfsm_trace] 宏,如下面的代码片段所示,其中定义了状态机

#[sfsm_trace]
fn trace(log: &str) {
    println!("{}", log);
}

示例

完整的示例可以在这里找到,更多信息在文档中。

依赖项

~1.5MB
~36K SLoC