zubr_state_machine 0.1.0

A simple asynchronous state machine library for creating Mealy machines in rust.
Documentation
  • Coverage
  • 82.35%
    14 out of 17 items documented0 out of 14 items with examples
  • Size
  • Source code size: 26.42 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 2.37 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 27s Average build duration of successful builds.
  • all releases: 27s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • MalinkyZubr/zubr_state_machine
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • MalinkyZubr

zubr_state_machine

Waltuh. State Machine Template taken from here

A simple asynchronous state machine library for creating Mealy machines in rust.

Features

  • Imitation of hardware state machine design in HDL
    • each state machine requires next state and output logic
  • Multiple concurrent inputs supported
  • Multiple concurrent outputs supported with both synchronous and asynchronous output read
  • built using tokio

Download

Github page here

Usage

  • every state machine consists of 3 main parts. Input, State, and Output
  • next state logic and output logic determine how these 3 interact
    • next state logic returns the desired next state of the state machine based on input and current state
      • it executes whenever an input is detected
    • output logic returns the desired output for the state determined by next state logic
  • you can create a state machine as shown below
let mut sm = StateMachine::<InputType, StateType, OutputType>::new(
    initial_state: StateType, 
    input_buffer_size: usize, 
    next_state_logic: fn(InputType, &StateType) -> T, 
    output_logic: fn(&StateType) -> O
);  
  • after state machine instantiation you can spawn it's input and output handles
  • input handles allow a producer to send data either synchronously (no await) or asynchronously (await)
    • the caller is responsible for error handling. This is a thinly veiled wrapper around tokio::mpsc::Sender
  • output handles allow a consumer to read the output of the state machine.
    • Output is read one of two ways
      • on demand: the handle awaits on a read() acquire on a tokio RwLock to get a copy of state machine output
      • on notification: the handle awaits a message from a tokio watch, and returns it when the output updates
    • The output handle controls the state machine lifecycle. Any call to close() will shut the state machine down if it is running as a task.
  • how to spawn these handles is shown
let mut output_handle = sm.spawn_output_handle();
let mut input_handle = sm.spawn_input_handle();
  • A simple SM operation is shown below
use std::time::Duration;
use tokio::time::sleep;
use ZubrStateMachine::*;



#[tokio::main]
async fn main() {
    let mut sm = StateMachine::<u64, u64, u64>::new(
        0u64, // initial state set to 0 
        10, // maximum input buffer size
        |input, state| input + state, // calculate state by adding the input and current state 
        |state| state * state, // output is the square of the current state
    );
    let mut output_handle = sm.spawn_output_handle();
    let input_handle_async = sm.spawn_input_handle();
    let input_handle_sync = sm.spawn_input_handle();

    let join = tokio::spawn(async move { // the state machine will be long lived and loop until .close() is called from an output handle
        sm.run().await;
    });

    let async_input_join = tokio::spawn(async move { // spawns an asynchronous task to send a piece of data
        sleep(Duration::from_millis(500)).await;
        input_handle_async.send_async(1).await.unwrap();
    });

    let asynchronous_result = output_handle.await_state_change().await.unwrap(); // wait for an update to be pushed to the output value, read that new value
    let _ = input_handle_sync.send(2);
    sleep(Duration::from_millis(50)).await;
    let synchronous_result = output_handle.try_read().unwrap(); // await RwLock acquisition and get whatever value is inside

    println!("{}", asynchronous_result); // should print 1
    println!("{}", synchronous_result); // should print 9
    
    output_handle.close();
    let _ = join.await;
    let _ = async_input_join.await;
}

Contribution

I dont anticipate many people looking at this, and its a tiny library. But please do as you please to improve it! Open issue, pull request, I will do my best to engage.

License

MIT license