StateMachine.py

説明を見る。
00001 #!/usr/bin/env python
00002 # -*- coding: euc-jp -*-
00003 
00004 ##
00005 # @file StateMachine.py
00006 # @brief State machine template class
00007 # @date $Date: 2007/08/30$
00008 # @author Noriaki Ando <n-ando@aist.go.jp> and Shinji Kurihara
00009 #
00010 # Copyright (C) 2006-2008
00011 #     Task-intelligence Research Group,
00012 #     Intelligent Systems Research Institute,
00013 #     National Institute of
00014 #         Advanced Industrial Science and Technology (AIST), Japan
00015 #     All rights reserved.
00016 
00017 
00018 import threading
00019 
00020 import OpenRTM
00021 import RTC, RTC__POA
00022 
00023 
00024 ##
00025 # @if jp
00026 # @class ScopedLock
00027 # @brief ScopedLock クラス
00028 #
00029 # 排他処理用ロッククラス。
00030 #
00031 # @since 0.4.0
00032 #
00033 # @else
00034 #
00035 # @endif
00036 class ScopedLock:
00037   def __init__(self, mutex):
00038     self.mutex = mutex
00039     self.mutex.acquire()
00040 
00041   def __del__(self):
00042     self.mutex.release()
00043 
00044 
00045 
00046 ##
00047 # @if jp
00048 # @class StateHolder
00049 # @brief 状態保持用クラス
00050 # 
00051 # 状態を保持するためのホルダークラス。
00052 # 現在の状態と、1つ前の状態、遷移予定の状態を保持する。
00053 #
00054 # @param State 保持する状態の型
00055 #
00056 # @since 0.4.0
00057 #
00058 # @else
00059 #
00060 # @endif
00061 class StateHolder:
00062   def __init__(self):
00063     self.curr = None
00064     self.prev = None
00065     self.next = None
00066 
00067 
00068 ##
00069 # @if jp
00070 #
00071 # @class StateMachine
00072 #
00073 # @brief 状態マシンクラス
00074 #
00075 # StateMachine クラスは状態マシンを実現するクラスである。
00076 #
00077 # 例: ActiveObjectは状態マシンを持つアクティブオブジェクトであるとする。
00078 # 状態は3状態 INACTIVE, ACTIVE, ERROR があり、各状態でのEntryやExit動作を
00079 # 定義したいとすると、以下のように実現される。
00080 # <pre>
00081 # class ActiveObject:
00082 #   class MyState:
00083 #     INACTIVE, ACTIVE, ERROR = range(3)
00084 # 
00085 #   def __init__(self):
00086 #     m_sm = StateMachine(3)
00087 #     m_sm.setNOP(nullAction)
00088 #     m_sm.setListener(self)
00089 # 
00090 #     m_sm.setExitAction(MyState.INACTIVE, self.inactiveExit)
00091 #       : 
00092 #     m_sm.setPostDoAction(MyState.ERROR, self.errorPostDo)
00093 #     m_sm.setTransitionAction(self.transition); 
00094 # 
00095 #   def nullAction(myStates):
00096 #     pass
00097 #   def inactiveExit(myStates):
00098 #     pass
00099 #     : 
00100 #   def errorPostDo(myStates):
00101 #     pass
00102 #   def transition(myStates:
00103 #     pass
00104 # </pre>
00105 # 状態を持たせたいクラスは以下の条件を満たすように実装しなければならない。
00106 # <ol>
00107 # <li> 内部クラスで状態を定義
00108 # <li> StateMachine のコンストラクタ引数は状態の数
00109 # <li> 以下のアクション関数を(Return _function_name_(States)) の関数として設定
00110 # <ol>
00111 #  <li> 何もしない関数を必ず定義し、setNOP で与えなければならない
00112 #  <li> 各状態毎に, set(Entry|PreDo|Do|PostDo|Exit)Action でアクションを設定
00113 #  <li> 状態遷移時のアクションを setTransitionAction() で設定。
00114 # </ol>
00115 # <li> 遷移時のアクションは、与えられた現在状態、次状態、前状態を元に、
00116 #   ユーザが実装しなければならない。
00117 # <li> 状態の変更は goTo() で、状態のチェックは isIn(state) で行う。
00118 # <li> goTo()は次状態を強制的にセットする関数であり、遷移の可否は、
00119 #   ユーザが現在状態を取得し判断するロジックを実装しなければならない。
00120 # </ol>
00121 #
00122 # このクラスは、一つの状態に対して、
00123 # <ul>
00124 # <li> Entry action
00125 # <li> PreDo action
00126 # <li> Do action
00127 # <li> PostDo action
00128 # <li> Exit action
00129 # </ul>
00130 # 5つのアクションが定義することができる。
00131 # Transition action はあらゆる状態間遷移で呼び出されるアクションで、
00132 # その振る舞いはユーザが定義しなければならない。
00133 # 
00134 # このクラスは以下のようなタイミングで各アクションが実行される。
00135 #
00136 # <ul>
00137 # <li> 状態が変更され(A->B)状態が遷移する場合 <br>
00138 # (A:Exit)->|(状態更新:A->B)->(B:Entry)->(B:PreDo)->(B:Do)->(B:PostDo)
00139 #
00140 # <li> 状態が変更されず、B状態を維持する場合 (|はステップの区切りを表す)<br>
00141 # (B(n-1):PostDo)->|(B(n):PreDo)->(B(n):Do)->(B(n):PostDo)->|(B(n+1):PreDo)<br>
00142 # PreDo, Do, PostDo が繰り返し実行される。
00143 #
00144 # <li> 自己遷移する場合 <br>
00145 # (B(n-1):PostDo)->(B(n-1):Exit)->|(B(n):Entry)->(B(n):PreDo) <br>
00146 # 一旦 Exit が呼ばれた後、Entry が実行され、以降は前項と同じ動作をする。
00147 # </ul>
00148 #
00149 # @since 0.4.0
00150 #
00151 # @else
00152 #
00153 # @brief
00154 #
00155 # @endif
00156 class StateMachine:
00157   state_array = (RTC.INACTIVE_STATE,
00158            RTC.ACTIVE_STATE,
00159            RTC.ERROR_STATE,
00160            RTC.UNKNOWN_STATE)
00161 
00162 
00163 
00164   ##
00165   # @if jp
00166   # @brief コンストラクタ
00167   #
00168   # コンストラクタ
00169   #
00170   # @param self
00171   # @param num_of_state ステートマシン中の状態数
00172   #
00173   # @else
00174   # @brief Constructor
00175   # @endif
00176   def __init__(self, num_of_state):
00177     self._num = num_of_state
00178     self._entry  = {}
00179     self._predo  = {}
00180     self._do     = {}
00181     self._postdo = {}
00182     self._exit   = {}
00183 
00184     self.setNullFunc(self._entry,  None)
00185     self.setNullFunc(self._do,     None)
00186     self.setNullFunc(self._exit,   None)
00187     self.setNullFunc(self._predo,  None)
00188     self.setNullFunc(self._postdo, None)
00189     self._transit = None
00190     self._mutex = threading.RLock()
00191 
00192 
00193   ##
00194   # @if jp
00195   # @brief NOP関数を登録する
00196   #
00197   # NOP関数(何もしない関数)を登録する。
00198   #
00199   # @param self
00200   # @param call_back コールバック関数
00201   #
00202   # @else
00203   # @brief Set NOP function
00204   # @endif
00205   def setNOP(self, call_back):
00206     self.setNullFunc(self._entry,  call_back)
00207     self.setNullFunc(self._do,     call_back)
00208     self.setNullFunc(self._exit,   call_back)
00209     self.setNullFunc(self._predo,  call_back)
00210     self.setNullFunc(self._postdo, call_back)
00211     self._transit = call_back
00212 
00213 
00214   ##
00215   # @if jp
00216   # @brief Listener オブジェクトを登録する
00217   #
00218   # 各種アクション実行時に呼び出される Listener オブジェクトを登録する。
00219   #
00220   # @param self
00221   # @param listener Listener オブジェクト
00222   #
00223   # @else
00224   # @brief Set Listener Object
00225   # @endif
00226   def setListener(self, listener):
00227     self._listener = listener
00228 
00229 
00230   ##
00231   # @if jp
00232   # @brief Entry action 関数を登録する
00233   #
00234   # 各状態に入った際に実行される Entry action 用コールバック関数を登録する。
00235   #
00236   # @param self
00237   # @param state 登録対象状態
00238   # @param call_back Entry action 用コールバック関数
00239   #
00240   # @return アクション実行結果
00241   #
00242   # @else
00243   # @brief Set Entry action function
00244   # @endif
00245   def setEntryAction(self, state, call_back):
00246     if self._entry.has_key(state):
00247       self._entry[state] = call_back
00248     else:
00249       self._entry.setdefault(state, call_back)
00250     return True
00251 
00252 
00253   ##
00254   # @if jp
00255   # @brief PreDo action 関数を登録する
00256   #
00257   # 各状態内で実行される PreDo action 用コールバック関数を登録する。
00258   #
00259   # @param self
00260   # @param state 登録対象状態
00261   # @param call_back PreDo action 用コールバック関数
00262   #
00263   # @return アクション実行結果
00264   #
00265   # @else
00266   # @brief Set PreDo action function
00267   # @endif
00268   def setPreDoAction(self, state, call_back):
00269     if self._predo.has_key(state):
00270       self._predo[state] = call_back
00271     else:
00272       self._predo.setdefault(state, call_back)
00273     return True
00274 
00275 
00276   ##
00277   # @if jp
00278   # @brief Do action 関数を登録する
00279   #
00280   # 各状態内で実行される Do action 用コールバック関数を登録する。
00281   #
00282   # @param self
00283   # @param state 登録対象状態
00284   # @param call_back Do action 用コールバック関数
00285   #
00286   # @return アクション実行結果
00287   #
00288   # @else
00289   # @brief Set Do action function
00290   # @endif
00291   def setDoAction(self, state, call_back):
00292     if self._do.has_key(state):
00293       self._do[state] = call_back
00294     else:
00295       self._do.setdefault(state, call_back)
00296     return True
00297 
00298 
00299   ##
00300   # @if jp
00301   # @brief PostDo action 関数を登録する
00302   #
00303   # 各状態内で実行される PostDo action 用コールバック関数を登録する。
00304   #
00305   # @param self
00306   # @param state 登録対象状態
00307   # @param call_back PostDo action 用コールバック関数
00308   #
00309   # @return アクション実行結果
00310   #
00311   # @else
00312   # @brief Set PostDo action function
00313   # @endif
00314   def setPostDoAction(self, state, call_back):
00315     if self._postdo.has_key(state):
00316       self._postdo[state] = call_back
00317     else:
00318       self._postdo.setdefault(state, call_back)
00319     return True
00320 
00321 
00322   ##
00323   # @if jp
00324   # @brief Exit action 関数を登録する
00325   #
00326   # 各状態内で実行される Exit action 用コールバック関数を登録する。
00327   #
00328   # @param self
00329   # @param state 登録対象状態
00330   # @param call_back Exit action 用コールバック関数
00331   #
00332   # @return アクション実行結果
00333   #
00334   # @else
00335   # @brief Set Exit action function
00336   # @endif
00337   def setExitAction(self, state, call_back):
00338     if self._exit.has_key(state):
00339       self._exit[state] = call_back
00340     else:
00341       self._exit.setdefault(state, call_back)
00342     return True
00343 
00344 
00345   ##
00346   # @if jp
00347   # @brief State transition action 関数を登録する
00348   #
00349   # 状態遷移時に実行される State transition action 用コールバック関数を
00350   # 登録する。
00351   #
00352   # @param self
00353   # @param call_back State transition 用コールバック関数
00354   #
00355   # @return アクション実行結果
00356   #
00357   # @else
00358   # @brief Set state transition action function
00359   # @endif
00360   def setTransitionAction(self, call_back):
00361     self._transit = call_back
00362     return True
00363 
00364 
00365   ##
00366   # @if jp
00367   # @brief 初期状態をセットする
00368   #
00369   # ステートマシンの初期状態を設定する。
00370   #
00371   # @param self
00372   # @param states 初期状態
00373   #
00374   # @else
00375   # @brief Set Exit action function
00376   # @endif
00377   def setStartState(self, states):
00378     self._states = StateHolder()
00379     self._states.curr = states.curr
00380     self._states.prev = states.prev
00381     self._states.next = states.next
00382 
00383 
00384   ##
00385   # @if jp
00386   # @brief 状態を取得する
00387   #
00388   # 状態情報を取得する。
00389   # 現在の状態、1つ前の状態、遷移予定の状態を取得することができる。
00390   #
00391   # @param self
00392   #
00393   # @return 状態情報
00394   #
00395   # @else
00396   # @brief Get state machine's status
00397   # @endif
00398   def getStates(self):
00399     guard = ScopedLock(self._mutex)
00400     return self._states
00401 
00402 
00403   ##
00404   # @if jp
00405   # @brief 現在の状態を取得する
00406   #
00407   # 現在の状態を取得する。
00408   #
00409   # @param self
00410   #
00411   # @return 現在の状態
00412   #
00413   # @else
00414   # @brief Get current state
00415   # @endif
00416   def getState(self):
00417     guard = ScopedLock(self._mutex)
00418     return self._states.curr
00419 
00420 
00421   ##
00422   # @if jp
00423   # @brief 現在状態を確認
00424   #
00425   # 現在の状態が、引数で指定した状態と一致するか確認する。
00426   #
00427   # @param self
00428   # @param state 確認対象状態
00429   #
00430   # @return 状態確認結果
00431   #
00432   # @else
00433   # @brief Evaluate current status
00434   # @endif
00435   def isIn(self, state):
00436     guard = ScopedLock(self._mutex)
00437     if self._states.curr == state:
00438       return True
00439     else:
00440       return False
00441 
00442 
00443   ##
00444   # @if jp
00445   # @brief 状態を遷移
00446   #
00447   # 指定した状態に状態を遷移する。
00448   # 本関数は次状態を強制的にセットする関数である。
00449   # このため、遷移の可否は、ユーザが現在状態を取得し判断するロジックを
00450   # 実装しなければならない。
00451   # 遷移先が現在の状態と同じ場合には、自己遷移フラグをセットする。
00452   #
00453   # @param self
00454   # @param state 遷移先状態
00455   #
00456   # @else
00457   # @brief Change status
00458   # @endif
00459   def goTo(self, state):
00460     guard = ScopedLock(self._mutex)
00461     self._states.next = state
00462 
00463 
00464   ##
00465   # @if jp
00466   # @brief 駆動関数
00467   #
00468   # ステートマシンの駆動関数。
00469   # 実際の状態遷移および状態遷移発生時の各アクションの呼びだしを実行する。
00470   #
00471   # @param self
00472   #
00473   # @else
00474   # @brief Worker function
00475   # @endif
00476   def worker(self):
00477     states = StateHolder()
00478     self.sync(states)
00479 
00480     # If no state transition required, execute set of do-actions
00481     if states.curr == states.next:
00482       # pre-do
00483       if self._predo[states.curr]:
00484         self._predo[states.curr](states)
00485       if self.need_trans():
00486         return
00487 
00488       # do
00489       if self._do[states.curr]:
00490         self._do[states.curr](states)
00491       if self.need_trans():
00492         return
00493 
00494       # post-do
00495       if self._postdo[states.curr]:
00496         self._postdo[states.curr](states)
00497     # If state transition required, exit current state and enter next state
00498     else:
00499       if self._exit[states.curr]:
00500         self._exit[states.curr](states)
00501       self.sync(states)
00502 
00503       # If state transition still required, move to the next state
00504       if states.curr != states.next:
00505         states.curr = states.next
00506         if self._entry[states.curr]:
00507           self._entry[states.curr](states)
00508         self.update_curr(states.curr)
00509 
00510 
00511   ##
00512   # @if jp
00513   # @brief NOP関数を設定
00514   #
00515   # NOP関数(何もしない関数)を登録する。
00516   #
00517   # @param self
00518   # @param s コールバック関数設定先
00519   # @param nullfunc コールバック関数(NOP関数)
00520   #
00521   # @else
00522   # @brief Worker function
00523   # @endif
00524   def setNullFunc(self, s, nullfunc):
00525     for i in range(self._num):
00526       if s.has_key(StateMachine.state_array[i]):
00527         s[StateMachine.state_array[i]] = nullfunc
00528       else:
00529         s.setdefault(StateMachine.state_array[i], nullfunc)
00530 
00531 
00532   ##
00533   # @if jp
00534   # @brief 状態の同期処理
00535   #
00536   # @param self
00537   # @param states OpenRTM.StateHolder<RTC.LifeCycleState>
00538   #
00539   # @else
00540   # @endif
00541   def sync(self, states):
00542     guard = ScopedLock(self._mutex)
00543     states.prev = self._states.prev
00544     states.curr = self._states.curr
00545     states.next = self._states.next
00546     
00547 
00548 
00549   ##
00550   # @if jp
00551   # @brief 遷移の必要性チェック
00552   #
00553   # @param self
00554   #
00555   # @return 遷移必要性確認結果
00556   #
00557   # @else
00558   # @endif
00559   def need_trans(self):
00560     guard = ScopedLock(self._mutex)
00561     return (self._states.curr != self._states.next)
00562 
00563 
00564   ##
00565   # @if jp
00566   # @brief 現在状態の更新
00567   #
00568   # @param self
00569   # @param curr RTC.LifeCycleState
00570   #
00571   # @else
00572   # @endif
00573   def update_curr(self, curr):
00574     guard = ScopedLock(self._mutex)
00575     self._states.curr = curr

OpenRTMに対してMon Mar 17 15:11:06 2008に生成されました。  doxygen 1.5.4