#state-machine #state #fsm #machine #moore #mealy

generic-state-machine

A simple Rust library that allows to create generic or Moore or Mealy state machines that allows the use of custom transition functions

1 unstable release

0.1.0 Sep 24, 2023

#2535 in Algorithms

MIT license

24KB
313 lines

Coverage Status

Summary

A simple library that allows to create Moore or Mealy state machines It is designed around the StateMachine<S,E> type and allows the use of custom transition functions.

Requirements:

  • All states need to be of the same type S
  • All events (inputs) need to be of the same type E
  • Transition functions are executed over self (to have access to internal lists of states or events) and need to follow the prototype:
     fn(&mut StateMachine<S, E>, E) -> S;
    
    where input E is the trigger event and the return value is the new state.
  • Output generation is left to the user to allow the implementation to be as generic as possible. You can print or call exernal functions to produce the desired output

Implement Moore state machine:

  • Define transition functions that calculate the next state and use that to produce any outputs

Implement Mealy state machine:

  • Define transition functions that calculate the next state and use that together with the input event E to produce any outputs

A Basic Example of a Asynchronous State Machine:

           +------------+
  Event: 1 |            | +----+
           |            V |    | Event: 1
     +->[false]       [true]<--+
     |   | ^            |
     +---+ |            | Event: 0
 Event: 0  +------------+
use state_machine::state_machine::AsyncStateMachine;

async fn main() -> Result<()> {
let mut fsm = AsyncStateMachine::<bool, usize>::new(5);
    fsm.add_states(&mut vec![true, false])
      .await
      .add_transition(true, 0, |_fsm, _event| false)
      .await
      .add_transition(true, 1, |_fsm, _event| true)
      .await
      .add_transition(false, 0, |_fsm, _event| false)
      .await
      .add_transition(false, 1, |_fsm, _event| true)
      .await
      .set_state(false)
      .await;

    fsm.start().await.unwrap();

    println!("State Machine under test: {:?}", fsm);

    fsm.push_event(0).await.unwrap();
    assert_eq!(fsm.current_state().await, false);

    fsm.push_event(1).await.unwrap();
    assert_eq!(fsm.current_state().await, true);

    fsm.push_event(1).await.unwrap();
    assert_eq!(fsm.current_state().await, true);

    fsm.push_event(0).await.unwrap();
    assert_eq!(fsm.current_state().await, false);

    fsm.push_event(0).await.unwrap();
    assert_eq!(fsm.current_state().await, false);
}

A Basic Example of a State Machine (using the primitive type):

           +---->[1]----+
  Event: 1 |            | Event: 2
           |            V
          [3]          [2]
           ^            |
           |            | Event: 3
           +------------+
    use state_machine::primitives::StateMachine;

    // Define a transition function. It can as general as we want!
    fn tf(_fsm: &mut StateMachine<i32, i32>, event: i32) -> i32 {
    match event {
        1 => 1,
        2 => 2,
        3 => 3,
        _ => panic!(), // Invalid transition
        }
    }

    let mut fsm = StateMachine::<i32, i32>::new();
    fsm.add_states(&mut vec![1, 2, 3]);

    // We can even use captures as transition functions!
    fsm.add_transition(1, 2, |_fsm, _event| {
        // We are in state 1! Go to state 2! Ignore Input!
        2
    });

    // If you prefer you can chain configuration methods together
    fsm.add_transition(2, 3, tf)
      .add_transition(3, 1, tf)
      .set_state(1);

    println!("{:?}", fsm);

    assert_eq!(&1, fsm.execute(1));
    assert_eq!(&2, fsm.execute(2));
    assert_eq!(&3, fsm.execute(3));

Dependencies

~0.3–6.5MB
~37K SLoC