google/hierarchical-state-machine.dart

Consider providing some example code

Opened this issue · 4 comments

This library looks really interesting but it isn't immediately clear how to use it correctly. Perhaps by adding a couple of simple examples that highlight some of the usage patterns it could help make it more approachable.

I was thinking something along the lines of the following (but obviously using the correct API)

enum TrafficLightState {
  green,
  yellow,
  red,
}

enum TrafficLightEvent {
  timerExpired,
}

class TrafficLightStateMachine {
  final Machine<TrafficLightState, TrafficLightEvent> _stateMachine;

  TrafficLightStateMachine()
      : _stateMachine = Machine<TrafficLightState, TrafficLightEvent>(
          initialState: TrafficLightState.green,
          states: {
            TrafficLightState.green: State(
              onEnter: () => print('Green light'),
              transitions: {
                TrafficLightEvent.timerExpired: TrafficLightState.yellow,
              },
            ),
            TrafficLightState.yellow: State(
              onEnter: () => print('Yellow light'),
              transitions: {
                TrafficLightEvent.timerExpired: TrafficLightState.red,
              },
            ),
            TrafficLightState.red: State(
              onEnter: () => print('Red light'),
              transitions: {
                TrafficLightEvent.timerExpired: TrafficLightState.green,
              },
            ),
          },
        );

  void start() {
    while (true) {
      _stateMachine.transition(TrafficLightEvent.timerExpired);
      sleep(Duration(seconds: 1));
    }
  }
}

void main() {
  final trafficLight = TrafficLightStateMachine();
  trafficLight.start();
}

Ah, I thought I had the keyboard state machine in the tests section of code. That's not a bad example (though we'd have to restart the timer on every enter) and could be expanded with say a pedestrian wanting to cross.

Hi, I am also trying to implement a hierarchical state machine and I thought this repo looked promising. It would be nice to declare as per the example above, but that doesn't seem to be possible. There are no docs or examples, its 2 years old so would you suggest I look elsewhere, or is this still a viable option?

So here is my effort so far, but i cannot see a way to add a state to a machine, or machines to a state?
The only state in the machine is the root.
How do I add the states to the machine?
How do I make nested machines?

import 'package:hierarchical_state_machine/hierarchical_state_machine.dart';

enum TrafficLightState {
  red,
  amber,
  green,
}

enum TrafficLightEvent {
  stop,
  readyToGo,
  readyToStop,
  go
}
enum TrafficLightTransition {
  red2amber,
  amber2green,
  green2amber,
  amber2red
}

  void init() {
    //states
    var red = State(TrafficLightState.red);
    var amber = State(TrafficLightState.amber);
    var green = State(TrafficLightState.green);

    //events
    var stop = EventHandler(
      target: red,
      guard: null,
      action: (event, data) {
        print('stop action, light is red');
      },
      isLocalTransition: true
    );
    var readyToGo = EventHandler(
      target: amber,
      guard: null,
      action: (event, data) {
        print('readyTogo action, light is amber');
      },
      isLocalTransition: true
    );
    var readyToStop = EventHandler(
      target: amber,
      guard: null,
      action: (event, data) {
        print('readyToStop action, light is amber');
      },
      isLocalTransition: true
    );
    var go = EventHandler(
      target: green,
      guard: null,
      action: (event, data) {
        print('go action, light is green');
      },
      isLocalTransition: true
    );

    red.addHandler(readyToGo);
    amber.addHandler(stop);
    amber.addHandler(go);
    green.addHandler(readyToStop);

    //machine
    TrafficLightState root = TrafficLightState.red;

    final Machine<TrafficLightState, TrafficLightEvent> trafficlight = 
      Machine<TrafficLightState, TrafficLightEvent>(
        name: 'traffic_light',
        rootId: root,
      );
    

    //transitions

    // runs twice
    var loop = 2;
    while (loop > 0) {
      trafficlight.start();
      trafficlight.handle(TrafficLightEvent.stop);
      trafficlight.handle(TrafficLightEvent.readyToGo);
      trafficlight.handle(TrafficLightEvent.go);
      trafficlight.handle(TrafficLightEvent.readyToStop);
      trafficlight.handle(TrafficLightEvent.stop);
      loop--;
    }
  }

void main() {
  init();
}

@macasas maybe this can help for now (documentation can always be improved):

 final root = hsm.root;

final red = root.newChild(TrafficLightState.red);
final yellow = root.newChild(TrafficLightState.yellow); 
final green = root.newChild(TrafficLightState.green); 

as a bonus; its easier to add event handlers without allocating:

red.addHandler(
  target: amber,
  action: (event, data) {
    print('readyTogo action, light is amber');
  },
);

I use this library for personal projects, so it's not dead, it's just "stable".

Thanks, I'm having a play :-)