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 在 嵌入式开发
每月313 次下载
11KB
为无std和嵌入式环境提供的静态状态机生成器
状态机是许多软件架构的必要组成部分,尤其是在嵌入式系统等底层系统中特别常见。它们可以将复杂系统分解成许多小的状态,并明确定义它们之间的转换。虽然它们有助于分解复杂性,但它们也必须很好地记录,以便理解。
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
。状态机可以处于两种可能的状态 - WaitForLaunch
和 Launch
。 WaitForLaunch
是初始状态,可以通过 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
);
类似于普通状态机,这将生成一个用户需要实现状态和转换行为的状态机。在可出错的状态机中,必须实现 TryState
和 TryTransition
特性。此外,错误状态必须实现 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
}
}
这封装了较小的 OnlineMachine
到 Online
状态中。
消息系统
此外,可以定义推送到状态或从状态中轮询的消息。
// 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
。每个状态可以有多个接收和返回消息。它们必须实现相应的 ReturnMessage
和 ReceiveMessage
特性。
特性
跟踪
在调试状态机时,尤其是在现场调试时,了解状态机的行为、它经历了哪些转换以及错误发生的位置非常有帮助。使用跟踪功能,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