#状态机 #状态 #机器 #HSM #层次

async-hsm

轻量级异步分层状态机(HSM)

1 个不稳定版本

0.2.0 2020年10月16日
0.1.1 2020年10月16日
0.1.0 2020年10月16日

#2072 in 算法

MIT/Apache

19KB

异步分层状态机(HSM)

概念

"让编译器进行优化"

"每个状态都是一个异步函数"

"状态转换是异步函数的序列"

"相同复合状态使用'Composite'的一个实例共享状态"

"复合可以嵌套,形成层次状态机(HSM)"

"在复合内部,其状态可以是循环的"

"异步-await函数在复合的上下文中管理"

"退出异步函数将释放所有资源,包括其他异步-await"

"通过一系列异步-await函数执行状态转换"

"总代码量只有几行代码"

"通过异步-await组合形成并行状态(示例待完成)"

功能

  • 分层状态组合
  • 异步状态处理器
  • 性能良好
  • 并行状态(示例待完成)
  • 并行复合之间的同步点(计划作为下一个功能)

许可

本作品根据Apache 2.0或MIT双许可。如果您使用此作品,可以选择其中之一。

SPDX-License-Identifier: Apache-2.0 OR MIT

集成

将此内容放入您的Cargo.toml

## Cargo.toml file
[dependencies]
async-hsm = "^0.2"

示例

以下状态图(PlantUml语法)可以通过以下Rust代码编码


@startuml res/hierarchy
[*] --> App
state App {
[*] --> Menu
state Menu {
}

state Play {
  [*] --> Ping

  Ping --> Ping : ping
  Ping --> Pong : ping
  Pong --> Pong : pong
  Pong --> Ping : ping
}
Menu --> Play: play
Play --> Menu: menu
}
App --> [*]: terminate
@enduml

请参见这里的ping pong状态图

    use async_std::prelude::*;
    use async_std::stream;
    use async_std::task;
    use async_hsm::{Composite, Transit, Builder, BuilderPair};
    use std::rc::Rc;
    use std::cell::RefCell;

    type Score = u32;
    type EnterStateScore = u32;
    type AppComposite = Composite<AppData>;
    type PlayComposite = Composite<AppData>;


    type AppTransit<'s> = Transit<'s, AppComposite, Score, AppError>;
    type PlayTransit<'s> = Transit<'s, PlayComposite, BuilderPair<AppComposite, EnterStateScore, Score, AppError>, AppError>;

    type AppBuilder = Builder<AppComposite, EnterStateScore, Score, AppError>;
    type AppBuilderPair = BuilderPair<AppComposite, EnterStateScore, Score, AppError>;

    #[derive(Debug, Clone, PartialEq)]
    enum AppError { Failure }

    static TO_MENU: AppBuilder = || |comp, score| Box::pin(menu(comp, score));
    static TO_PLAY: AppBuilder = || |comp, score| Box::pin(play(comp, score));
    static TERMINATE: AppBuilder = || |comp, score| Box::pin(terminate(comp, score));

    #[derive(Debug, Clone, PartialEq)]
    enum IoEvent { Ping, Pong, Terminate, Menu, Play }

    #[derive(Debug, Clone)]
    struct AppData {
        event: Rc<RefCell<stream::FromIter<std::vec::IntoIter<IoEvent>>>>,
    }

    async fn pong<'s>(comp: &'s mut PlayComposite, score: Score) -> Result<PlayTransit<'s>, AppError> {
        let mut score = score + 1;
        let event = comp.data.event.clone();
        while let Some(event) = (*event).borrow_mut().next().await {
            match event {
                IoEvent::Ping => return Ok(Transit::To(Box::pin(ping(comp, score)))),
                IoEvent::Terminate => return Ok(Transit::Lift((TERMINATE, score))),
                _ => score += 1,
            }
        }
        Ok(Transit::Lift((TERMINATE, score)))
    }

    async fn ping<'s>(comp: &'s mut PlayComposite, score: Score) -> Result<PlayTransit<'s>, AppError> {
        let mut score = score + 1;
        let event = comp.data.event.clone();
        while let Some(event) = (*event).borrow_mut().next().await {
            match event {
                IoEvent::Pong => return Ok(Transit::To(Box::pin(pong(comp, score)))),
                IoEvent::Terminate => return Ok(Transit::Lift((TERMINATE, score))),
                _ => score += 1,
            }
        }
        Ok(Transit::Lift((TERMINATE, score)))
    }

    async fn terminate<'s>(_comp: &'s mut AppComposite, score: Score) -> Result<AppTransit<'s>, AppError> {
        Ok(Transit::Lift(score))
    }

    async fn play<'s>(comp: &'s mut AppComposite, score: Score) -> Result<AppTransit<'s>, AppError> {
        let event = comp.data.event.clone();
        let mut play = PlayComposite::new(AppData { event: event });
        let (builder, build_arg): AppBuilderPair = play.init(ping, score).await?;
        builder()(comp, build_arg).await
    }

    async fn menu<'s>(comp: &'s mut AppComposite, score: Score) -> Result<AppTransit<'s>, AppError> {
        let score = score;
        let event = comp.data.event.clone();
        while let Some(event) = (*event).borrow_mut().next().await {
            match event {
                IoEvent::Play => return Ok(Transit::To(Box::pin(play(comp, score)))),
                IoEvent::Terminate => return Ok(Transit::Lift(score)),
                _ => continue,
            }
        }
        Ok(Transit::Lift(score))
    }

    #[test]
    fn test_game() {
        let sequence = vec![IoEvent::Play, IoEvent::Ping, IoEvent::Pong,
                            IoEvent::Ping, IoEvent::Pong, IoEvent::Terminate];
        let event = Rc::new(RefCell::new(stream::from_iter(sequence)));
        let start_score = 0;
        let mut app = AppComposite::new(AppData { event: event });
        let result: Result<Score, AppError> = task::block_on(app.init(menu, start_score));
        assert_eq!(Ok(5), result);
    }

无运行时依赖