2017-02-16 2 views
1

Ich bin versucht, eine Zustandsmaschine zu implementieren, mit dem transitions Modul zu verwalten. Python Version 2.7.13 und Übergänge ist Version 0.4.4.Wie Zustandsübergänge in Python FSM mit Übergängen Bibliothek

In dem Projekt docs, die Beispiele all vorher durch die Zustände durch die Funktion der Eingabe fordert die Eingabeaufforderung. Einen Ausschnitt aus dem ersten Beispiel in den Übergängen docs, wird der Zustand der batman erreicht, indem dem Namen Aufruf von Funktionen wake_up und work_out

>>> batman.wake_up() 
>>> batman.work_out() 
>>> batman.state 
'hungry' 

Ich möchte die Zustandsmaschine automatisch vorrücken durch auf Modelldaten konditionierten Zustand. Das Spielzeugbeispiel unten ist das, was ich versuche zu tun, beruht aber auf der Verwendung einer Dummy-Funktion als Zeiger, um die next_state einzustellen.

Gibt es eine Möglichkeit, dies zu tun, ist nicht mit einer next_state Funktion erstellen und wie ein Zeiger verwendet? Die Übergangsdokumente haben einen geordneten Übergang und bedingte Übergänge. Aber was ich wirklich will, ist bedingte geordnete Übergänge.

Ist es möglich, den Code neu zu schreiben, ohne einen Funktionszeiger zu verwenden?

from transitions import Machine 

class AModel(object): 
    def __init__(self): 
     self.sv = 0 # state variable of the model 

    def on_enter_sA(self): 
     print "Entered sA" 
     self.next_state = self.to_sB 

    def on_enter_sB(self): 
     print "Entered sB" 
     if self.sv < 3: 
      self.next_state = self.to_sB 
     else: 
      self.next_state = self.to_sC 

    def on_enter_sC(self): 
     print "Entered sC" 
     if self.sv == 6: 
      self.next_state = self.to_sD 

    def on_enter_sD(self): 
     print "Entered sD" 
     self.next_state = self.to_sA 

    def next_state(self): 
     pass 

#setup model and state machine 
model = AModel() 

#init transitions model 
list_of_states = ['sA','sB','sC','sD'] 
transitions = [ 
    {'trigger':'to_sA','source':'sD','dest':'sA'}, 
    {'trigger':'to_sB','source':'sA','dest':'sB'}, 
    {'trigger':'to_sC','source':'sB','dest':'sC'}, 
    {'trigger':'to_sD','source':'sC','dest':'sD'} 
] 
machine = Machine(model=model, states=list_of_states, initial='sA', 
      transitions=transitions) 

model.next_state = model.to_sB #init next state pointer 

#begin main 
for i in range(0,8): 
    print 'iter is: ' + str(i) + " -model state is:" + model.state 
    model.sv = i #update model state variable, local state logic 
       #will determine what next_state points to 
    model.next_state() 

Vielen Dank!

+0

Was ist falsch mit Funktionszeigern? –

+0

Im Allgemeinen nichts. Die Transitions-Bibliothek scheint so nett zu sein, dass die Verwendung von Zeigern im Vergleich zu den Beispielen ein ungeschicktes Verhalten war. Wenn ich das in C machen würde, wären Funktionszeiger völlig natürlich. – Matt

Antwort

1

Diese Funktion wurde vor angefordert (siehe this issue). Wie Sie sehen können, arbeitet jemand daran. Er könnte in naher Zukunft eine Pull-Anfrage öffnen. Ich habe seine Änderungen nicht überprüft, werde es aber sicherlich tun, wenn dies geschieht.

Jetzt können Sie Ihr Modell die Bedingungsprüfungen verarbeiten lassen und diese mit geordneten Übergängen kombinieren, um den Funktionszeiger next_state häufig aktualisieren zu müssen. Da Sie nur einen Index überprüft dies könnte wie folgt aussehen:

from transitions import Machine 


class AModel(object): 
    def __init__(self): 
     self.sv = 0 # state variable of the model 
     self.conditions = { # each state 
      'sA': 0, 
      'sB': 3, 
      'sC': 6, 
      'sD': 0, 
     } 

    def poll(self): 
     if self.sv >= self.conditions[self.state]: 
      self.next_state() 


# setup model and state machine 
model = AModel() 

# init transitions model 
list_of_states = ['sA', 'sB', 'sC', 'sD'] 
machine = Machine(model=model, states=list_of_states, initial='sA', ordered_transitions=True) 

# begin main 
for i in range(0, 10): 
    print('iter is: ' + str(i) + " -model state is:" + model.state) 
    model.sv = i 
    model.poll() 

ich davon ausgegangen, dass Sie das Modell jedes Mal abfragen, der Index erhöht. Wenn dies der Fall ist self.sv == 6 und self.sv >= 6 das gleiche tun (für sC-sD). Wenn jedoch der Betreiber absichtlich gewählt wurde Sie das Modell Bedingungsprüfung ändern kann ein Operator Wert Tupel zu verwenden:

from transitions import Machine 
import operator 


class AModel(object): 
    def __init__(self): 
     self.sv = 0 # state variable of the model 
     self.conditions = { # each state 
      'sA': (operator.ne, None), 
      'sB': (operator.ge, 3), 
      'sC': (operator.eq, 6), 
      'sD': (operator.ne, None), 
     } 

    def poll(self): 
     op, value = self.conditions[self.state] 
     if op(self.sv, value): 
      self.next_state() 


# setup model and state machine 
model = AModel() 

# init transitions model 
list_of_states = ['sA', 'sB', 'sC', 'sD'] 
machine = Machine(model=model, states=list_of_states, initial='sA', ordered_transitions=True) 

# begin main 
for i in range(0, 10): 
    print('iter is: ' + str(i) + " -model state is:" + model.state) 
    model.sv = i 
    model.poll() 

In beiden Fällen ist die Ausgabe:

iter is: 0 -model state is:sA 
iter is: 1 -model state is:sB 
iter is: 2 -model state is:sB 
iter is: 3 -model state is:sB 
iter is: 4 -model state is:sC 
iter is: 5 -model state is:sC 
iter is: 6 -model state is:sC 
iter is: 7 -model state is:sD 
iter is: 8 -model state is:sA 
iter is: 9 -model state is:sB 

Aber noch einmal, ich angenommen etwas, das falsch sein könnte: Ich nahm an, dass das Ändern von Zuständen, wenn die Bedingung erfüllt ist, ausreichend ist. So würden die Bedingungen funktionieren. Aber vielleicht möchten Sie tatsächlich jedes Mal, wenn Sie Ihr Modell abfragen, den Status verlassen und den Status eingeben. In diesem Fall Sie auto_transitions und abzurufen sie dynamisch mit getattr verwenden können:

from transitions import Machine 


class AModel(object): 
    def __init__(self): 
     self.sv = 0 # state variable of the model 
     self.conditions = { # each state 
      'sA': 0, 
      'sB': 3, 
      'sC': 6, 
      'sD': 0, 
     } 

    def poll(self): 
     if self.sv >= self.conditions[self.state]: 
      self.next_state() # go to next state 
     else: 
      getattr(self, 'to_%s' % self.state)() # enter current state again 

    def on_enter(self): 
     print('entered state %s' % self.state) 

    def on_exit(self): 
     print('exited state %s' % self.state) 


# setup model and state machine 
model = AModel() 

# init transitions model 
list_of_states = ['sA', 'sB', 'sC', 'sD'] 
machine = Machine(model=model, states=list_of_states, initial='sA', 
        ordered_transitions=True, before_state_change='on_exit', 
        after_state_change='on_enter') 

# begin main 
for i in range(0, 10): 
    print('iter is: ' + str(i) + " -model state is:" + model.state) 
    model.sv = i 
    model.poll() 

Aus Gründen der Einfachheit halber habe ich Funktionen, die Nachrichten jedes Mal, wenn ein Zustand gedruckt wird betreten oder verlassen. Dies ist nicht erforderlich, wenn Sie einen Logger verwenden, da diese Ereignisse ebenfalls in der transitions protokolliert werden.

+0

Große Antwort! Ich schätze es, wie Sie mehrere Ansätze mit geordneten Übergängen zeigen. Das sieht sehr sauber aus, sehr klare Beispiele. Danke – Matt

+0

Versucht, um Ihre Antwort zu stimmen, aber mein Vertreter ist zu niedrig, um öffentlich sichtbar zu sein .. – Matt

+0

@Matt: keine Sorgen. Froh, dass ich helfen konnte. Ich wünsche Ihnen alles Gute für Ihr Projekt! – aleneum

0

Ich habe nicht herausgefunden, wie dies den Übergang Modul hinzufügen, aber peitschte ich diese automatisch auf Bedingungen Übergang auf bis zu Grunde:

class StateMachine: 
    def __init__(self, name, states, transitions, initialState): 
     self.name=name 
     self.states=set() 
     self.transitions=set() 
     self.state=None 

     for s in states: 
      _s=State(s) 
      self.states.add(_s) 
      if self.state==None: 
       self.state=_s 
      elif s==initialState: 
       self.state=_s 

     for t in transitions: 
      # self.addTransition(t) 

    def addTransition(self, transition): 
     name=transition[0] 
     for s in self.states: 
      #fromState 
      if s.name==transition[1]: 
       fromState=s 
      #toState 
      elif s.name==transition[2]: 
       toState=s 
     #condition 
     condition=getattr(self, transition[3]) 
     #action  
     if len(transition)==5: 
      action = getattr(self, transition[4])() 
     else: 
      action=self.passAction 
     t=Transition(name, fromState, toState, condition, action) 
     fromState.transitions.add(t) 

    def passAction(self): 
     print('pass!!!') 

    def run(self): 
     self.state=self.state.testTransitions() 

    def __repr__(self): 
     return self.name+':'+str(self.states) 

class State: 
    def __init__(self, name='state'): 
     self.name=name 
     self.transitions=set() 

    def __repr__(self): 
     return self.name+':'+str(self.transitions) 

    def testTransitions(self): 
     state=self 
     for t in self.transitions: 
      if t.condition() is True: 
       t.action() 
       state=t.toState 
     return state 

class Transition: 
    def __init__(self, name, fromState, toState, condition, action): 
     self.name=name 
     self.fromState=fromState 
     self.toState=toState 
     self.condition=condition 
     self.action=action 

    def __repr__(self): 
     return self.name 

class TestSM(StateMachine): 
    def __init__(self, name, states, transitions, initialState): 
     StateMachine.__init__(self, name, states, transitions, initialState) 

    def testCondition(self): 
     print('testCondition!!!') 
     return True 



states=['a', 'b', 'c'] 
transitions=[ 
    ['aTOb', 'a', 'b', 'testCondition'], 
    ['bTOc', 'b', 'c', 'testCondition'], 
    ['cTOa', 'c', 'a', 'testCondition'], 
    ] 
sm=TestSM('testSM', states, transitions, 'a') 

for s in sm.states: 
    print(s.name) 

print('fin')   

Um die Zustandsmaschine laufen zu lassen, führen Sie einfach die ' run 'Funktion, zB:

sm.run() 
Verwandte Themen