2017-02-16 7 views
1

transitionsモジュールを使用して状態マシンを実装しようとしています。 Pythonバージョン2.7.13と遷移はバージョン0.4.4です。遷移ライブラリを使用して、python fsmの状態遷移を管理する方法

プロジェクトのドキュメントでは、コマンドプロンプトに関数呼び出しを入力することで、すべての例が状態を進めます。トランジションのドキュメントの最初の例からスニペットを取ると、batmanの状態は、私は、ステートマシンが自動的にモデルデータに条件付け状態を通じて進めていたいという名前の関数wake_upwork_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 == 6self.sv >= 6は同じことをします(sCsD)。オペレータはあなたがオペレータ値のタプルを使用するためのモデル条件チェックを変更することができます選択した意図的だった場合 はしかし、:いずれの場合も

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() 
関連する問題