2 个不稳定版本

0.2.0 2023年10月16日
0.1.0 2023年5月13日

#14#concurrently

MIT 协议

98KB
2K SLoC

通用状态机管理器

设计特性

  • 使状态机成为可重用的模块化单元
  • 使用其他状态机的状态机知道它们可以使用什么,但正在使用的状态机不知道使用它们的状态机
  • 无任务/线程
  • 同时处理多个状态机
  • 触发其他状态机,等待它们完成,然后继续
  • 创建/删除计时器
  • 向状态机组外发送通知
  • 向自身发送事件
  • 从状态机组外接受事件
  • 鼓励将状态机实现以 (<State>, <Event>) 格式表示

灵感来源

概述

StateMachine 首先注册到 StateMachineManager,我将简单地称之为 Manager。每次对 Manager::cycle() 的调用都处理单个事件。单个事件对应于单个状态机的运行。 Manager 访问 Controller 的内容并对其进行操作。单个 ControllerManager 注册的所有状态机之间共享。

有两种类型的事件 UserEventSystemEventUserEvent 传递给 StateMachine::cycle(),而 SystemEvent 则不传递。 StateMachine::cycle() 接受一个 &mut Controller 和一个 UserEventStateMachine 使用 Controller 中的函数来向事件队列添加/删除事件;除了与计时器相关的函数之外,所有函数都这样做。 SystemEvent 被管理器消耗并用于修改 Controller 的内部或向状态机组外发送数据或通知。

基于节点的状态机管理器(开发中)

目标

  • 将状态机输入处理与给定 状态的当前枚举 解耦
  • 状态指示所有输入都进入同一个汇点(管理员的signal_queue);这允许电梯和过渡被统一处理,从而避免了通过Box<dyn Signal>带来的类型不透明。

实际上,设计应该将最多三个消息流连接到特定的状态机。

I/O

  • 一个Input(处理外部和内部事件)
Signal {
    id: StateId<K>,
    input: I,
}

两个输出

  • Signal输出(事件意味着要作为其他状态机的输入进行处理)
  • Notification输出(事件意味着要由任何不是由给定signal_queue提供输入的状态机处理)

新的StateMachineManager拥有

  • 状态存储层(使用NodeStorage
  • 输入事件流
  • 状态机处理器

StateMachineManager负责

  • Signal路由到适当的状态机
  • ProcessorContext注入到状态机中:这一动作允许状态机并发循环

https://github.com/knox-networks/core/blob/67f7dc6ac57f5c6650d82ce0019e65a31278ae93/common/src/state_machine/node_state_machine.rs#L65-L74

NodeStore负责

  • 插入和更新各种状态层次结构
  • 操作通过在Arc<Mutex<_>>容器中保留所有节点树来并发执行。

这允许NodeStore存储

  • 通过增加Arc计数并插入每个子节点的新条目来创建指向同一树的多个索引(通过新的DashMap键插入)
  • 允许每个状态树具有独立的内部可变性,解耦无关状态与资源竞争

https://github.com/knox-networks/core/blob/67f7dc6ac57f5c6650d82ce0019e65a31278ae93/common/src/state_machine/storage.rs#L10-L16

基于节点的TimeoutManager(开发中)

考虑

  • 每个StateId<K>只有一个活动计时器,状态机不需要跟踪发送到通知的Operation::Set(Instant::now())。因此,所有计时器都应可按StateId<K>索引。
  • 同一StateId<K>的新Operation::Set应覆盖旧计时器。
  • 超时应发出与相关状态机相关的Signal

方法

依赖项

~10–18MB
~208K SLoC