2017-02-16 18 views
1

Я пытаюсь реализовать конечный автомат с использованием модуля transitions. Версия Python 2.7.13 и переходы - версия 0.4.4.Как управлять переходами состояний в python fsm с использованием библиотеки переходов

В проектных документах все примеры проходят через состояния, вводя вызовы функций в командной строке. Принимая фрагмент из первого примера в переходах документации, состояние batman достигается путем вызова именованных функций wake_up и work_out

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

Я хотел бы иметь государственную машину автоматически перейдет через состояние условного на модельных данных. Ниже приведен пример игрушек, который я пытаюсь сделать, но полагаюсь на использование фиктивной функции в качестве указателя для установки next_state.

Есть ли способ сделать это, не связанный с созданием функции next_state и использованием ее как указателя? Переходные документы имеют упорядоченный переход и условные переходы. Но я действительно хочу иметь условные упорядоченные переходы.

Можно ли переписать код ниже без использования указателя на функцию?

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() 

Спасибо!

+0

Что не так с указателями функций? –

+0

Вообще ничего. Библиотека переходов кажется настолько приятной, что использование указателей так, как я делал, выглядело неуклюжим по сравнению с примерами. Если бы я делал это в C, указатели на функции были бы абсолютно естественными. – Matt

ответ

1

Эта функция запрошена и раньше (см. this issue). Как вы можете видеть, есть кто-то, кто работает над этим. В ближайшем будущем он может открыть запрос на вытаскивание. Я не рассматривал его изменения, но, безусловно, это произойдет, когда это произойдет.

На данный момент вы можете позволить своей модели обрабатывать условия проверки и объединить это с упорядоченными переходами, чтобы избавиться от необходимости часто обновлять указатель функции next_state. Так как вы просто проверить индекс это может выглядеть следующим образом:

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() 

Я предположил, что вы опрашивать Типовом каждый раз, когда индекс увеличивается. Если это так, то self.sv == 6 и self.sv >= 6 делают то же самое (для sC - sD). Однако, если оператор был преднамеренным выбран вы можете изменить проверку модели условие использовать значение оператора кортеж:

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() 

В обоих случаях выход:

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 

Но опять же, я предположил, что-то, что может быть неправильным: я предположил, что изменение состояний, если условие выполнено, является достаточным. Так будут работать условия. Но, возможно, вы на самом деле собираетесь выходить и вводить состояния каждый раз, когда вы опросите свою модель. В этом случае вы можете использовать auto_transitions и получить их динамически getattr:

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() 

Для простоты я добавил функции, которые печатают сообщения каждый раз, когда состояние вводится или выхода. Это не требуется, если вы используете регистратор с transitions и регистрируете эти события.

+0

Отличный ответ! Я ценю, как вы показываете несколько подходов с использованием упорядоченных переходов. Это выглядит очень чистыми, очень ясными примерами. Спасибо – Matt

+0

Пробовал проголосовать за ваш ответ, но мой представитель слишком низок, чтобы он был общедоступным. – Matt

+0

@Matt: не стоит беспокоиться. Рад, что смог помочь. Я желаю вам всего наилучшего для вашего проекта! – aleneum

0

Я не понял, как добавить это к модулю переходов, однако я взбитыми это до автоматически с переходной зависимости от условий:

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')   

Для того, чтобы запустить государственную машину, просто запустите ' функция запуска, например:

sm.run()