1

I am using PyTransitions to generate a simple FSM, configuring it using a yml file. An example could be something like this:

initial: A
states:
  - A
  - B
  - C
transitions:
  - {trigger: "AtoC", source: "A", dest: "C"}
  - {trigger: "CtoB", source: "C", dest: "B"}

My question is, using the yml file, how do I write in state outputs? For example, at state A turn on LED 1, state B turn on LED2, state C turn on LED1 and 2. I can't find any documentation for it in the PyTransitions page.

Mtk59
  • 121
  • 2

1 Answers1

1

My question is, using the yml file, how do I write in state outputs?

There is no dedicated output slot for states but you can use transitions to trigger certain actions when a state is entered or left. Actions can be done in callbacks. Those actions need to be implemented in the model. If you want to do most things via configurations you can customize Machine and the used State class. For instance, you could create a custom LEDState and assign a value array called led_states to it where each value represents a LED state. The array is applied to the LEDs in the after_state_change callback of the Model that is called update_leds:

from transitions import Machine, State


class LEDState(State):

    ALL_OFF = [False] * 2  # number of LEDs

    def __init__(self, name, on_enter=None, on_exit=None,
                 ignore_invalid_triggers=None, led_states=None):
        # call the base class constructor without 'led_states'...
        super().__init__(name, on_enter, on_exit, ignore_invalid_triggers)
        # ... and assign its value to a custom property
        # when 'led_states' is not passed, we assign the default 
        # setting 'ALL_OFF'
        self.led_states = led_states if led_states is not None else self.ALL_OFF

# create a custom machine that uses 'LEDState'
# 'LEDMachine' will pass all parameters in the configuration
# dictionary to the constructor of 'LEDState'.
class LEDMachine(Machine):
   
    state_cls = LEDState


class LEDModel:

    def __init__(self, config):
        self.machine = LEDMachine(model=self, **config, after_state_change='update_leds')

    def update_leds(self):
        print(f"---New State {self.state}---")
        for idx, led in enumerate(self.machine.get_state(self.state).led_states):
            print(f"Set LED {idx} {'ON' if led else 'OFF'}.")

# using a dictionary here instead of YAML
# but the process is the same
config = {
    'name': 'LEDModel',
    'initial': 'Off',
    'states': [
        {'name': 'Off'},
        {'name': 'A', 'led_states': [True, False]},
        {'name': 'B', 'led_states': [False, True]},
        {'name': 'C', 'led_states': [True, True]}
    ]
}

model = LEDModel(config)
model.to_A()
# ---New State A---
# Set LED 0 ON.
# Set LED 1 OFF.
model.to_C()
# ---New State C---
# Set LED 0 ON.
# Set LED 1 ON.

This is just one way how this could be done. You could also just pass an array with indexes representing all LEDs that should be ON. We'd switch off all LEDs when exiting a state with before_state_change



ALL_OFF = []

#  ...

self.machine = LEDMachine(model=self, **config, before_state_change='reset_leds', after_state_change='set_lets')

# ...

 def reset_leds(self):
    for led_idx in range(num_leds):
        print(f"Set {led_idx} 'OFF'.")

 def set_lets(self):
    for led_idx in self.machine.get_state(self.state).led_states:
        print(f"Set LED {led_idx} 'ON'.")
# ...

    {'name': 'A', 'led_states': [0]},
    {'name': 'B', 'led_states': [1]},
    {'name': 'C', 'led_states': [0, 1]}

aleneum
  • 2,083
  • 12
  • 29