2 个版本
0.1.1 | 2024年3月9日 |
---|---|
0.1.0 | 2024年1月6日 |
#7 在 #state-charts
每月 44 次下载
用于 kaori-hsm
17KB
94 行
kaori-hsm
kaori_hsm 状态机库
kaori_hsm 是一个用于在 Rust 中开发分层状态机 (HSM) 的库。该库的低内存占用和执行速度是其主要关注点,因为它旨在在资源有限的系统上运行,例如微控制器。由于它是硬件无关的,因此该库可以在任何存在 Rust 编译器的系统上运行。该库的一些关键优势包括
- 不使用动态内存分配
- 快速执行,低栈和程序内存使用
- 不使用 Rust 标准库,也不使用任何其他外部 crate
什么是分层状态机?
状态机是软件实体,根据它们所处的状态以不同的方式处理事件。不同的输入事件可能导致状态机执行不同的操作,并触发到其他状态的转换。
分层状态机是可以有嵌套状态的状态机。这意味着如果一个事件在某个状态下无法处理,其父状态最终可以处理它。因此,HSMs 特别适用于设计具有复杂行为的状态机。
为了理解状态机和尤其是 HSM 的工作原理,我特别推荐由 Miro Samek 制作的视频系列,您可以在 这里 找到
如何使用该库?
要构建自己的状态机,您首先需要定义将保存其数据的结构,然后您需要在该结构上实现库的以下特质:TopState
特质以及您想定义的状态的 State<Tag>
特质变体。
以下顺序必须遵循以构建一个可操作的状态机
- 创建一个将保存状态机数据的结构的实例。
- 使用 [
InitStateMachine::from()
] 函数将此结构的实例封装到 InitStateMachine 实例中。 - 通过调用此实例上的[
InitStateMachine::init()
]方法来初始化状态机。这将初始化状态机并将其引导到其第一个状态。此方法将返回一个StateMachine
实例。此类型代表一个完全运行的状态机,仅暴露用于向其注入事件变体的[StateMachine::dispatch()
]方法。
项目中的示例
本库包含许多示例,展示了其潜在用途,并帮助您了解如何使用它。大多数示例都可以在没有特定硬件的情况下运行。您将在库的类型和函数定义中找到嵌入的小示例。这些示例主要关注展示这些类型和函数的用法。然后,您还可以在kaori_hsm/examples
目录中找到更复杂的示例。这些示例易于操作,是构建自己的状态机的良好基础。在kaori_hsm/tests
目录中的集成测试也可以作为示例,但它们非常严格,包含大量特定于测试的代码。最后,您可以在此存储库中找到一个旨在测试stm32f103c8T6微控制器上此库性能的项目。对于新用户来说,性能测试可能不容易理解,但它可能是最实用的示例。
一个介绍性的分层状态机示例
以下示例展示了使用kaori_hsm
库编写的假设状态机。此HSM模拟根据按钮状态的变化来闪烁LED。当状态机启动时,LED关闭。当按钮被按下时,LED开始闪烁。当按钮释放时,LED停止闪烁。此示例附带了测试代码。测试使用一个队列,每次HSM执行特定操作时,它都会在队列上发布特定的字符串。在初始化HSM或将事件分发给它之后,测试代码检查队列上的字符串序列是否与预期相符。
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
use kaori_hsm::*;
enum BlinkingEvent{
ButtonPressed,
ButtonReleased,
TimerTick,
}
struct BasicStateMachine{
sender: Sender<String>,
}
impl BasicStateMachine{
pub fn new(sender: Sender<String>) -> BasicStateMachine {
BasicStateMachine { sender }
}
// Post a string to the test queue
fn post_string(&self, s : &str){
self.sender.send(String::from(s)).unwrap();
}
}
impl TopState for BasicStateMachine{
type Evt = BlinkingEvent;
fn init(&mut self) -> InitResult<Self> {
self.post_string("Starting HSM");
init_transition!(BlinkingDisabled)
}
}
#[state(super_state= Top)]
impl State<BlinkingDisabled> for BasicStateMachine{
fn handle(&mut self, evt: & BlinkingEvent) -> HandleResult<Self> {
match evt{
BlinkingEvent::ButtonPressed => {
self.post_string("Button pressed");
transition!(BlinkingEnabled)
}
_ => ignored!()
}
}
}
#[state(super_state= Top)]
impl State<BlinkingEnabled> for BasicStateMachine{
fn entry(&mut self) {
self.post_string("Arm timer");
}
fn exit(&mut self) {
self.post_string("Disarm timer");
}
fn init(&mut self) -> InitResult<Self>{
init_transition!(LedOn)
}
fn handle(&mut self, evt: & BlinkingEvent) -> HandleResult<Self> {
match evt{
BlinkingEvent::ButtonReleased => {
self.post_string("Button released");
transition!(BlinkingDisabled)
}
_ => ignored!()
}
}
}
#[state(super_state= BlinkingEnabled)]
impl State<LedOn> for BasicStateMachine{
fn entry(&mut self) {
self.post_string("Led turned on");
}
fn exit(&mut self) {
self.post_string("Led turned off");
}
fn handle(&mut self, evt: & BlinkingEvent) -> HandleResult<Self> {
match evt{
BlinkingEvent::TimerTick =>{
self.post_string("Timer tick");
transition!(LedOff)
}
_ => ignored!()
}
}
}
#[state(super_state= BlinkingEnabled)]
impl State<LedOff> for BasicStateMachine{
fn handle(&mut self, evt: & BlinkingEvent) -> HandleResult<Self> {
match evt{
BlinkingEvent::TimerTick =>{
self.post_string("Timer tick");
transition!(LedOn)
}
_ => ignored!()
}
}
}
let (sender, mut receiver) = channel();
let basic_state_machine = BasicStateMachine::new(sender);
let ism = InitStateMachine::from(basic_state_machine);
// Execute the topmost initial transition of the state machine, leading to BlinkingDisabled
// state
let mut sm = ism.init();
assert_eq_sm_output(&receiver, &["Starting HSM"]);
// Event ButtonReleased is ignored in this state
sm.dispatch(&BlinkingEvent::ButtonReleased);
assert_eq_sm_output(&receiver, &[]);
sm.dispatch(&BlinkingEvent::ButtonPressed);
assert_eq_sm_output(&receiver, &["Button pressed", "Arm timer","Led turned on"]);
sm.dispatch(&BlinkingEvent::TimerTick);
assert_eq_sm_output(&receiver, &["Timer tick", "Led turned off"]);
sm.dispatch(&BlinkingEvent::TimerTick);
assert_eq_sm_output(&receiver, &["Timer tick", "Led turned on"]);
sm.dispatch(&BlinkingEvent::ButtonReleased);
assert_eq_sm_output(&receiver, &["Button released","Led turned off", "Disarm timer"]);
Cargo命令索引
当前目录必须是kaori_hsm/kaori_hsm
才能运行每个Cargo命令。
以发布模式构建lib
cargo build --release
运行文档测试
cargo test --doc
运行特定的集成测试
cargo test --test [test_name]
运行examples
目录中的特定示例
cargo run --example [example_name]
许可证
许可方式如下:
- Apache License,版本2.0(LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT许可证(LICENSE-MIT 或 http://opensource.org/licenses/MIT)
由您选择。
许可证:MIT OR Apache-2.0
依赖项
~285–740KB
~18K SLoC