def init_FSM(self): # Trafic light example # green -> yellow -> red -> green .. self.fsm = Machine({ "id": "lights", "initial": "green", "states": { "green": { "on": { "TIMER": "yellow" }, }, "yellow": { "on": { "TIMER": "red" } }, "red": { "on": { "TIMER": "green" } }, }, }) self.state = self.fsm.initial_state self.update_label()
def __init__( self, # { "type": "compound", "states": { ... } } config, machine: Machine, key: str, parent: Union[StateNode, Machine] = None, ): self.parent = parent self.id = (config.get("id", parent.id + "." + key) if parent else config.get("id", machine.id + "." + key)) self.entry = ([ self.get_actions(entry_action) for entry_action in config.get("entry") ] if config.get("entry") else []) self.exit = ([ self.get_actions(exit_action) for exit_action in config.get("exit") ] if config.get("exit") else []) self.key = key self.states = { k: StateNode(v, machine=machine, parent=self, key=k) for k, v in config.get("states", {}).items() } self.on = {} self.transitions = [] for k, v in config.get("on", {}).items(): transition = Transition(v, source=self, event=k) self.on[k] = transition self.transitions.append(transition) initial_key = config.get("initial") if not initial_key: self.initial = None else: self.initial = Transition(self.states.get(initial_key), source=self, event=None) self.type = config.get("type") if self.type is None: self.type = "atomic" if not self.states else "compound" if self.type == "final": self.donedata = config.get("data") if config.get("onDone"): done_event = f"done.state.{self.id}" done_transition = Transition(config.get("onDone"), source=self, event=done_event) self.on[done_event] = done_transition self.transitions.append(done_transition) machine._register(self)
def test_is_parallel_state(): machine = Machine({ "id": "test", "initial": "foo", "states": { "foo": { "type": "parallel" } } }) foo_state_node = machine._get_by_id("test.foo") assert is_parallel_state(foo_state_node) is True
def test_action(): entry_mock = Mock() exit_mock = Mock() machine = Machine( { "id": "machine", "initial": "on", "states": { "on" : { "on": {"TOGGLE": "off"}, "entry": [{"type": "entry_action"}], "exit": [{"type": "exit_action"}] }, "off": { "on": {"TOGGLE": "on"} } }, }, actions = { "entry_action": entry_mock, "exit_action": exit_mock, } ) state = machine.initial_state assert state.value is "on" for action in state.actions: action() entry_mock.assert_called_with() assert entry_mock.call_count is 1 assert exit_mock.call_count is 0 # ------------------------ state = machine.transition(state, "TOGGLE") assert state.value is "off" for action in state.actions: action() exit_mock.assert_called_with() assert entry_mock.call_count is 1 assert exit_mock.call_count is 1
def test_entry_action_inline(): mock = Mock() machine = Machine( { "id": "machine", "initial": "on", "states": { "on" : { "on": {"TOGGLE": "off"}, "entry": [ lambda: mock() ] }, "off": { "on": {"TOGGLE": "on"} } }, } ) state = machine.initial_state assert state.value is "on" for action in state.actions: action() mock.assert_called_with() assert mock.call_count is 1
def scxml_to_machine(source: str) -> Machine: tree = ET.parse(source) root = tree.getroot() result = convert(root) machine = Machine(result) return machine
class ApplicationBasic(): def __init__(self): self.init_ui() self.init_FSM() def init_FSM(self): # Trafic light example # green -> yellow -> red -> green .. self.fsm = Machine({ "id": "lights", "initial": "green", "states": { "green": { "on": { "TIMER": "yellow" }, }, "yellow": { "on": { "TIMER": "red" } }, "red": { "on": { "TIMER": "green" } }, }, }) self.state = self.fsm.initial_state self.update_label() def init_ui(self): self.fen = tkinter.Tk() self.label = tkinter.Label(self.fen, text="") self.label.pack() self.button = tkinter.Button(self.fen, text="TIMER", command=self.action) self.button.pack() def action(self): print("action") self.state = self.fsm.transition(self.state, "TIMER") self.update_label() def update_label(self): self.label["text"] = self.state.value def run(self): self.fen.mainloop()
def test_exit_action_inline(): mock = Mock() machine = Machine( { "id": "machine", "initial": "on", "states": { "on" : { "on": {"TOGGLE": "off"}, "exit": [ lambda: mock() ] }, "off": { "on": {"TOGGLE": "on"} } }, } ) state = machine.initial_state assert state.value is "on" for action in state.actions: action() assert mock.call_count is 0 state = machine.transition(state, "TOGGLE") for action in state.actions: action() assert mock.call_count is 1
import time # Trafic light example with substate # green -> yellow -> red.walk -> red.wait -> red.stop -> green .. lights = Machine( { "id": "lights", "initial": "green", "states": { "green": {"entry": [{"type": "enterGreen"}], "on": {"TIMER": "yellow"},}, "yellow": {"on": {"TIMER": "red"}}, "red": { # subFSM "initial": "walk", "states": { "walk": {"on": {"COUNTDOWN": "wait"}}, "wait": {"on": {"COUNTDOWN": "stop"}}, "stop": {"on": {"TIMEOUT": "timeout"}}, "timeout": {"type": "final"}, # type 'final' will make it to the onDone step of the superior FSM }, "onDone": "green", }, }, } ) if __name__ == "__main__": state = lights.initial_state print(state.value) time.sleep(0.5)
# https://plantuml.com/state-diagram from plantweb.render import render from xstate.machine import Machine # just a test simple_machine = Machine({ "id": "simple", "initial": "green", "states": { "green": { "on": { "JENNY_EVENT": "yellow" } }, "yellow": { "on": { "NEXT_EVENT": "red" } }, "red": { "on": { "NEXT_EVENT": "green" } } } }) def state_node_to_viz(state_node): result = "" if not state_node.parent:
lights = Machine( { "id": "lights", "initial": "green", "states": { "green": { "on": { "TIMER": "yellow" }, "entry": [{ "type": "enterGreen" }], "exit": [{ "type": "exitGreen" }] }, "yellow": { "on": { "TIMER": "red" }, "entry": [{ "type": "enterYellow" }] }, "red": { "on": { "TIMER": "green" }, "entry": [lambda: print("\tINLINE callback")] }, }, }, actions={ # action implementations "enterGreen": enterGreen, "exitGreen": exitGreen, "enterYellow": lambda: print("\tENTER_YELLOW callback") })