5 个版本

0.2.3 2023年4月12日
0.2.2 2021年9月30日
0.2.1 2021年9月30日
0.2.0 2021年9月28日
0.1.0 2021年9月27日

#795算法

每月 45 次下载

GPL-3.0 许可证

19KB
275

脏状态机

Crates.io Docs.rs Build Clippy Audit

dirty-fsm 是有限状态机的“快速简单”实现。大部分概念来自我编写的代码,作为 io.github.frc5024.lib5k.libkontrol 的一部分。

示例

在以下示例中,我创建了一个状态机,代表爪子和按钮。当按钮被按下时,爪子将在打开和关闭之间切换。

我们首先设置一个功能标志并加载库。

// This feature is required to use the new `#[default]` macro on enum variants
#![feature(derive_default_enum)]

use dirty_fsm::*;
use thiserror::Error;

接下来,我们定义机器的状态。

/// The possible states of the claw
#[derive(Debug, Default, PartialEq, Eq, Clone, Hash)]
enum ClawState {
    /// The claw is closed
    #[default]
    ClawClosed,

    /// The claw is open
    ClawOpen
}

除了状态之外,我们还需要定义某种错误类型(尽管如果根本不需要,我们可以简单地使用 ())。

/// Defines errors that can occur while running actions
#[derive(Debug, Error)]
enum ClawError {
    /// An example error
    #[error("Example error")]
    ExampleError,
}

接下来,我们定义在第一个状态(ClawClosed)中实际运行的代码。这是一个普通的 Rust 结构体,实现了 Action 特性。

Action 包含一些在动作的生命周期中在不同点调用的简单函数

  • on_register: 当动作通过 StateMachine::add_action 注册到状态机时调用一次
  • on_first_run: 在这个状态开始或切换到之前,在第一次调用 execute 之前调用一次。这应该像初始化函数一样处理。通常用于在 execute 中执行操作之前保存有关环境的信息。
  • execute: 在动作的生命周期中多次调用。这是动作代码应该放置的地方。它应该被视为 while true 循环的主体,因为它将反复运行,直到返回一个指示动作完成的 ActionFlag
  • on_finish: 当这个动作完成后,在最后一次调用 execute 之后调用一次。
/// Action that actually handles the claw being closed
#[derive(Debug)]
struct ClawClosedAction;

impl Action<ClawState, ClawError, bool> for ClawClosedAction {
    fn on_register(&mut self) -> Result<(), ClawError> {
        println!("ClawClosedAction has been registered with the state machine");
        Ok(())
    }

    fn on_first_run(&mut self, context: &bool) -> Result<(), ClawError> {
        println!("Button has been pressed, claw is closing");
        Ok(())
    }

    fn execute(
        &mut self,
        delta: &chrono::Duration,
        context: &bool,
    ) -> Result<crate::action::ActionFlag<ClawState>, ClawError> {
        println!("Claw code is running now");

        // If the button is pressed, switch to the next claw state
        if context {
            Ok(ActionFlag::SwitchState(ClawState::ClawOpen))
        } else {
            Ok(ActionFlag::Continue)
        }
    }

    fn on_finish(&mut self, interrupted: bool) -> Result<(), ClawError> {
        println!("ClawClosedAction is done executing");
        Ok(())
    }
}

由于我们有两个状态,因此需要为另一个状态再次执行此操作。

/// Action that actually handles the claw being opened
#[derive(Debug)]
struct ClawOpenedAction;

impl Action<ClawState, ClawError, bool> for ClawOpenedAction {
    fn on_register(&mut self) -> Result<(), ()> {
        println!("ClawOpenedAction has been registered with the state machine");
        Ok(())
    }

    fn on_first_run(&mut self, context: &bool) -> Result<(), ClawError> {
        println!("Button has been pressed, claw is opening");
        Ok(())
    }

    fn execute(
        &mut self,
        delta: &chrono::Duration,
        context: &bool,
    ) -> Result<crate::action::ActionFlag<ClawState>, ClawError> {
        println!("Claw code is running now");

        // If the button is pressed, throw an error as an example
        if *context {
            Err(ClawError::ExampleError)
        } else {
            Ok(ActionFlag::Continue)
        }
    }

    fn on_finish(&mut self, interrupted: bool) -> Result<(), ClawError> {
        println!("ClawOpenedAction is done executing");
        Ok(())
    }
}

最后,启动和运行状态机的代码

fn main() {
    // Create the state machine
    let mut claw_machine = StateMachine::new();
    claw_machine.add_action(ClawState::ClawClosed, ClawClosedAction {}).unwrap();
    claw_machine.add_action(ClawState::ClawOpen, ClawOpenedAction {}).unwrap();

    // State. This example assumes some outside "force" is changing this value
    let mut button_pressed = false;

    // Run the state machine
    loop {
        claw_machine.run(&mut button_pressed).unwrap();
    }
}

依赖项

~1.5MB
~20K SLoC