pytransitions / transitions

A lightweight, object-oriented finite state machine implementation in Python with many extensions
MIT License
5.49k stars 524 forks source link

Does this state machine has priority setting? #493

Closed YIKAILucas closed 3 years ago

YIKAILucas commented 3 years ago

It's a great FSM lib, but I want to use prioritization to trans my state. I know it can be done with a priority queue, but I still wanna it's embedded

aleneum commented 3 years ago

Hello @YIKAILucas,

prioritity is decided by addition order. Transitions added first will be evaluated first.

transitions = [
    dict(trigger=go, source='A', dest='B', conditions='is_condition_1'),  # evaluated first
    dict(trigger=go, source='A', dest='C', conditions='is_condition_2'),
    dict(trigger=go, source='A', dest='D')  # this acts as an 'else' statement as it has no conditions and is evaluated last
]

Transitions are organized in lists by event (trigger name) and transition source. If by any reason you cannot decide priority during initialization, you can alter these lists during runtime. You could for instance extend Transition with a prio parameter and occassionaly sort transition lists accordingly.

from transitions import Machine
from transitions import Transition

class PriorityTransition(Transition):

    def __init__(self, *args, **kwargs):
        self.prio = kwargs.pop('prio', 0)
        super().__init__(*args, **kwargs)

class PriorityMachine(Machine):

    transition_cls = PriorityTransition

    def update_priority(self):
        for evt in self.events.values():
            for source in evt.transitions:
                evt.transitions[source] = sorted(evt.transitions[source], key=lambda x: x.prio, reverse=True)

    def check_transition(self, event_data):
        print(f"{event_data.transition} -> Prio ({event_data.transition.prio})")
        return event_data.transition == 0

transitions = [
    {'trigger': 'go', 'source': 'A', 'dest': 'B', 'conditions': 'check_transition'},
    {'trigger': 'go', 'source': 'A', 'dest': 'A', 'prio': 20, 'conditions': 'check_transition'},
    {'trigger': 'go', 'source': 'A', 'dest': 'C', 'prio': 10, 'conditions': 'check_transition'},
]

m = PriorityMachine(states=['A', 'B', 'C'], transitions=transitions, send_event=True, initial='A', auto_transitions=False)
m.update_priority()
m.go()

# >>> <PriorityTransition('A', 'A')@139951481238576> -> Prio (20)
#     <PriorityTransition('A', 'C')@139951479931856> -> Prio (10)
#     <PriorityTransition('A', 'B')@139951481242384> -> Prio (0)
YIKAILucas commented 3 years ago

Thanks a lot, It's a nice solution