|
|
@@ -1,68 +1,248 @@
|
|
|
-"""Finite State Machine module: contains the FSM class"""
|
|
|
+"""The new Finite State Machine module. This replaces the modules
|
|
|
+previously called FSM.py (now called ClassicFSM.py).
|
|
|
+"""
|
|
|
|
|
|
-from DirectObject import *
|
|
|
+import DirectObject
|
|
|
+import DirectNotifyGlobal
|
|
|
import types
|
|
|
+import string
|
|
|
+
|
|
|
+class FSM(DirectObject.DirectObject):
|
|
|
+ """A Finite State Machine. This is intended to be the base class
|
|
|
+ of any number of specific machines, which consist of a collection
|
|
|
+ of states and transitions, and rules to switch between states
|
|
|
+ according to arbitrary input data.
|
|
|
+
|
|
|
+ The states of an FSM are defined implicitly. Each state is
|
|
|
+ identified by a string, which by convention begins with a capital
|
|
|
+ letter. (Also by convention, strings passed to request that are
|
|
|
+ not state names should begin with a lowercase letter.)
|
|
|
+
|
|
|
+ To define specialized behavior when entering or exiting a
|
|
|
+ particular state, define a method named enterState() and/or
|
|
|
+ exitState(), where "State" is the name of the state, e.g.:
|
|
|
+
|
|
|
+ def enterRed(self, oldState, newState):
|
|
|
+ ... do stuff ...
|
|
|
+
|
|
|
+ def exitRed(self, oldState, newState):
|
|
|
+ ... cleanup stuff ...
|
|
|
+
|
|
|
+ def enterYellow(self, oldState, newState):
|
|
|
+ ... do stuff ...
|
|
|
+
|
|
|
+ def exitYellow(self, oldState, newState):
|
|
|
+ ... cleanup stuff ...
|
|
|
+
|
|
|
+ def enterGreen(self, oldState, newState):
|
|
|
+ ... do stuff ...
|
|
|
+
|
|
|
+ def exitGreen(self, oldState, newState):
|
|
|
+ ... cleanup stuff ...
|
|
|
+
|
|
|
+ Both functions are supplied the previous state name and the new
|
|
|
+ state name we are transitioning to. (Of course, the newState
|
|
|
+ passed to enterRed, and the oldState passed to exitRed, will
|
|
|
+ always be "Red".)
|
|
|
+
|
|
|
+ Both functions are optional. If either function is omitted, the
|
|
|
+ state is still defined, but nothing is done when transitioning
|
|
|
+ into (or out of) the state.
|
|
|
+
|
|
|
+ Additionally, you may define a filterState() function for each
|
|
|
+ state. The purpose of this function is to decide what state to
|
|
|
+ transition to next, if any, on receipt of a particular input. The
|
|
|
+ input is always a string and a tuple of optional parameters (which
|
|
|
+ is often empty), and the return value should either be None to do
|
|
|
+ nothing, or the name of the state to transition into. For
|
|
|
+ example:
|
|
|
+
|
|
|
+ def filterRed(self, request, args):
|
|
|
+ if request in ['Green']:
|
|
|
+ return request
|
|
|
+ return None
|
|
|
+
|
|
|
+ def filterYellow(self, request, args):
|
|
|
+ if request in ['Red']:
|
|
|
+ return request
|
|
|
+ return None
|
|
|
+
|
|
|
+ def filterGreen(self, request, args):
|
|
|
+ if request in ['Yellow']:
|
|
|
+ return request
|
|
|
+ return None
|
|
|
+
|
|
|
+ As above, the filterState() functions are optional. If any is
|
|
|
+ omitted, the defaultFilter() method is called instead. The
|
|
|
+ default definition of defaultFilter() always returns None, thus
|
|
|
+ disallowing all unexpected transitions. This default behavior may
|
|
|
+ be overridden in a derived class.
|
|
|
+
|
|
|
+ FSM.state may be queried at any time other than during the
|
|
|
+ handling of the enter() and exit() functions. During these
|
|
|
+ functions, FSM.state contains the value None (you are not really
|
|
|
+ in any state during the transition). At other times, FSM.state
|
|
|
+ contains the name of the current state.
|
|
|
+
|
|
|
+ Initially, the FSM is in state 'Off'. It does not call enterOff()
|
|
|
+ at construction time; it is simply in Off already by convention.
|
|
|
+ If you need to call code in enterOff() to initialize your FSM
|
|
|
+ properly, call it explicitly in the constructor. Similarly, when
|
|
|
+ cleanup() is called or the FSM is destructed, the FSM transitions
|
|
|
+ back to 'Off' by convention. (It does call enterOff() at this
|
|
|
+ point, but does not call exitOff().)
|
|
|
+
|
|
|
+ To implement nested hierarchical FSM's, simply create a nested FSM
|
|
|
+ and store it on the class within the appropriate enterState()
|
|
|
+ function, and clean it up within the corresponding exitState()
|
|
|
+ function.
|
|
|
+
|
|
|
+ See the code in SampleFSM.py for further examples.
|
|
|
+ """
|
|
|
+
|
|
|
+ notify = DirectNotifyGlobal.directNotify.newCategory("FSM")
|
|
|
+
|
|
|
+ def __init__(self, name):
|
|
|
+ self.name = name
|
|
|
+ # Initially, we are in the Off state by convention.
|
|
|
+ self.state = 'Off'
|
|
|
+
|
|
|
+ # This member lists the default transitions that are accepted
|
|
|
+ # without question by the defaultFilter. It's a map of state
|
|
|
+ # names to a list of legal target state names from that state.
|
|
|
+ # Define it only if you want to use the classic FSM model of
|
|
|
+ # defining all (or most) of your transitions up front. If
|
|
|
+ # this is set to None (the default), all named-state
|
|
|
+ # transitions (that is, those requests whose name begins with
|
|
|
+ # a capital letter) are allowed. If it is set to an empty
|
|
|
+ # map, no transitions are implicitly allowed--all transitions
|
|
|
+ # must be approved by some filter function.
|
|
|
+ self.defaultTransitions = None
|
|
|
+
|
|
|
+ def __del__(self):
|
|
|
+ self.cleanup()
|
|
|
+
|
|
|
+ def cleanup(self):
|
|
|
+ # A convenience function to force the FSM to clean itself up
|
|
|
+ # by transitioning to the "Off" state.
|
|
|
+ assert(self.state)
|
|
|
+ if self.state != 'Off':
|
|
|
+ self.__setState('Off')
|
|
|
+
|
|
|
+ def forceTransition(self, newState):
|
|
|
+ """Changes unconditionally to the indicated state. This
|
|
|
+ bypasses the filterState() function, and just calls
|
|
|
+ exitState() followed by enterState()."""
|
|
|
+
|
|
|
+ assert(isinstance(newState, types.StringType))
|
|
|
+
|
|
|
+ self.__setState(newState)
|
|
|
+
|
|
|
+ def request(self, request, *args):
|
|
|
+ """Requests a state transition (or other behavior). The request
|
|
|
+ parameter should be a string. The request, along with any
|
|
|
+ additional arguments, is passed to the current filterState()
|
|
|
+ function. If filterState() returns a string, the FSM
|
|
|
+ transitions to that state.
|
|
|
+
|
|
|
+ The return value is the same as the return value of
|
|
|
+ filterState() (that is, None if the request does not provoke a
|
|
|
+ state transition, or the name of the new state otherwise.)
|
|
|
+
|
|
|
+ If the FSM is currently in transition (i.e. in the middle of
|
|
|
+ executing an enterState or exitState function), the request is
|
|
|
+ denied and None is returned."""
|
|
|
+
|
|
|
+ assert(isinstance(request, types.StringType))
|
|
|
+ self.notify.debug("%s.request(%s, %s" % (self.name, request, str(args)[1:]))
|
|
|
+
|
|
|
+ if not self.state:
|
|
|
+ self.notify.warning("rejecting request %s while FSM is in transition." % (request))
|
|
|
+ return None
|
|
|
+
|
|
|
+ func = getattr(self, "filter" + self.state, None)
|
|
|
+ if not func:
|
|
|
+ # If there's no matching filterState() function, call
|
|
|
+ # defaultFilter() instead.
|
|
|
+ func = self.defaultFilter
|
|
|
+ result = func(request, args)
|
|
|
+ if result:
|
|
|
+ assert(isinstance(result, types.StringType))
|
|
|
+ self.__setState(result)
|
|
|
+
|
|
|
+ return result
|
|
|
+
|
|
|
+ def defaultFilter(self, request, args):
|
|
|
+ """This is the function that is called if there is no
|
|
|
+ filterState() method for a particular state name. By default,
|
|
|
+ the filter defined here in the base class always returns
|
|
|
+ None, disallowing any transition. Specialized FSM's may wish
|
|
|
+ to redefine this default filter (for instance, to always
|
|
|
+ return the request itself, thus allowing any transition.)."""
|
|
|
+
|
|
|
+ if request == 'Off':
|
|
|
+ # We can always go to the "Off" state.
|
|
|
+ return request
|
|
|
+
|
|
|
+ if self.defaultTransitions is None:
|
|
|
+ # If self.defaultTransitions is None, it means to accept
|
|
|
+ # all requests whose name begins with a capital letter.
|
|
|
+ # These are direct requests to a particular state.
|
|
|
+ if request[0] in string.uppercase:
|
|
|
+ return request
|
|
|
+
|
|
|
+ else:
|
|
|
+ # If self.defaultTransitions is not None, it is a map of
|
|
|
+ # allowed transitions from each state. That is, each key
|
|
|
+ # of the map is the current state name; for that key, the
|
|
|
+ # value is a list of allowed transitions from the
|
|
|
+ # indicated state.
|
|
|
+ if request in self.defaultTransitions.get(self.state, []):
|
|
|
+ # This transition is listed in the defaultTransitions map;
|
|
|
+ # accept it.
|
|
|
+ return request
|
|
|
+
|
|
|
+ # If self.defaultTransitions is not None, it is an error
|
|
|
+ # to request a direct state transition (capital letter
|
|
|
+ # request) not listed in defaultTransitions and not
|
|
|
+ # handled by an earlier filter.
|
|
|
+ if request[0] in string.uppercase:
|
|
|
+ self.notify.error("%s rejecting request %s from state %s." % (self.name, request, self.state))
|
|
|
+
|
|
|
+ # In either case, we quietly ignore unhandled command
|
|
|
+ # (lowercase) requests.
|
|
|
+ assert(self.notify.debug("%s ignoring request %s from state %s." % (self.name, request, self.state)))
|
|
|
+ return None
|
|
|
+
|
|
|
+ def filterOff(self, request, args):
|
|
|
+ """From the off state, we can always go directly to any other
|
|
|
+ state."""
|
|
|
+ if request[0] in string.uppercase:
|
|
|
+ return request
|
|
|
+ return self.defaultFilter(request, args)
|
|
|
+
|
|
|
|
|
|
-class FSM(DirectObject):
|
|
|
- """FSM class: Finite State Machine class"""
|
|
|
-
|
|
|
- # create FSM DirectNotify category
|
|
|
- notify = directNotify.newCategory("FSM")
|
|
|
-
|
|
|
- # special methods
|
|
|
-
|
|
|
- # these are flags that tell the FSM what to do when an
|
|
|
- # undefined transition is requested:
|
|
|
- ALLOW = 0 # print a warning, and do the transition
|
|
|
- DISALLOW = 1 # silently ignore the request (don't do the transition)
|
|
|
- DISALLOW_VERBOSE = 2 # print a warning, and don't do the transition
|
|
|
- ERROR = 3 # print an error message and raise an exception
|
|
|
-
|
|
|
- def __init__(self, name, states=[], initialStateName=None,
|
|
|
- finalStateName=None, onUndefTransition=DISALLOW_VERBOSE):
|
|
|
- """__init__(self, string, State[], string, string, int)
|
|
|
-
|
|
|
- FSM constructor: takes name, list of states, initial state and
|
|
|
- final state as:
|
|
|
-
|
|
|
- fsm = FSM.FSM('stopLight',
|
|
|
- [ State.State('red', enterRed, exitRed, ['green']),
|
|
|
- State.State('yellow', enterYellow, exitYellow, ['red']),
|
|
|
- State.State('green', enterGreen, exitGreen, ['yellow']) ],
|
|
|
- 'red',
|
|
|
- 'red')
|
|
|
-
|
|
|
- each state's last argument, a list of allowed state transitions,
|
|
|
- is optional; if left out (or explicitly specified to be
|
|
|
- State.State.Any) then any transition from the state is 'defined'
|
|
|
- and allowed
|
|
|
-
|
|
|
- 'onUndefTransition' flag determines behavior when undefined
|
|
|
- transition is requested; see flag definitions above
|
|
|
-
|
|
|
- """
|
|
|
-
|
|
|
- self.setName(name)
|
|
|
- self.setStates(states)
|
|
|
- self.setInitialState(initialStateName)
|
|
|
- self.setFinalState(finalStateName)
|
|
|
-
|
|
|
- self.onUndefTransition = onUndefTransition
|
|
|
-
|
|
|
- # Flag to see if we are inspecting
|
|
|
- self.inspecting = 0
|
|
|
-
|
|
|
- # We do not enter the initial state to separate
|
|
|
- # construction from activation
|
|
|
- self.__currentState = None
|
|
|
-
|
|
|
- # We set this while we are modifying the state. No one else
|
|
|
- # should recursively attempt to modify the state while we are
|
|
|
- # doing this.
|
|
|
- self.__internalStateInFlux = 0
|
|
|
+ def __setState(self, newState):
|
|
|
+ # Internal function to change unconditionally to the indicated
|
|
|
+ # state.
|
|
|
+ assert(self.state)
|
|
|
+
|
|
|
+ oldState = self.state
|
|
|
+ self.state = None
|
|
|
+ self.__callTransitionFunc("exit" + oldState, oldState, newState)
|
|
|
+ self.__callTransitionFunc("enter" + newState, oldState, newState)
|
|
|
+ self.state = newState
|
|
|
+
|
|
|
|
|
|
- # I know this isn't how __repr__ is supposed to be used, but it
|
|
|
- # is nice and convenient.
|
|
|
+ def __callTransitionFunc(self, name, oldState, newState):
|
|
|
+ # Calls the appropriate enter or exit function when
|
|
|
+ # transitioning between states, if it exists.
|
|
|
+ assert(self.state == None)
|
|
|
+
|
|
|
+ func = getattr(self, name, None)
|
|
|
+ if func:
|
|
|
+ func(oldState, newState)
|
|
|
+
|
|
|
def __repr__(self):
|
|
|
return self.__str__()
|
|
|
|
|
|
@@ -70,290 +250,8 @@ class FSM(DirectObject):
|
|
|
"""__str__(self)
|
|
|
Print out something useful about the fsm
|
|
|
"""
|
|
|
- currentState = self.getCurrentState()
|
|
|
- if currentState:
|
|
|
- str = ("FSM " + self.getName() + ' in state "' +
|
|
|
- currentState.getName() + '"')
|
|
|
+ if self.state:
|
|
|
+ str = ("FSM " + self.name + ' in state "' + self.state + '"')
|
|
|
else:
|
|
|
- str = ("FSM " + self.getName() + ' not in any state')
|
|
|
+ str = ("FSM " + self.name + ' not in any state')
|
|
|
return str
|
|
|
-
|
|
|
- def enterInitialState(self, argList=[]):
|
|
|
- assert(not self.__internalStateInFlux)
|
|
|
- if self.__currentState == self.__initialState:
|
|
|
- return
|
|
|
-
|
|
|
- assert(self.__currentState == None)
|
|
|
- self.__internalStateInFlux = 1
|
|
|
- self.__enter(self.__initialState, argList)
|
|
|
- assert(not self.__internalStateInFlux)
|
|
|
-
|
|
|
- # Jesse decided that simpler was better with the __str__ function
|
|
|
- def __str_not__(self):
|
|
|
- """__str__(self)"""
|
|
|
- return "FSM: name = %s \n states = %s \n initial = %s \n final = %s \n current = %s" \
|
|
|
- % (self.__name, self.__states, self.__initialState,
|
|
|
- self.__finalState, self.__currentState)
|
|
|
-
|
|
|
-
|
|
|
- # setters and getters
|
|
|
-
|
|
|
- def getName(self):
|
|
|
- """getName(self)"""
|
|
|
- return(self.__name)
|
|
|
-
|
|
|
- def setName(self, name):
|
|
|
- """setName(self, string)"""
|
|
|
- self.__name = name
|
|
|
-
|
|
|
- def getStates(self):
|
|
|
- """getStates(self)"""
|
|
|
- return(self.__states)
|
|
|
-
|
|
|
- def setStates(self, states):
|
|
|
- """setStates(self, State[])"""
|
|
|
- self.__states = states
|
|
|
-
|
|
|
- def addState(self, state):
|
|
|
- """addState(state)"""
|
|
|
- self.__states.append(state)
|
|
|
-
|
|
|
- def getInitialState(self):
|
|
|
- """getInitialState(self)"""
|
|
|
- return(self.__initialState)
|
|
|
-
|
|
|
- def setInitialState(self, initialStateName):
|
|
|
- """setInitialState(self, string)"""
|
|
|
- self.__initialState = self.getStateNamed(initialStateName)
|
|
|
-
|
|
|
- def getFinalState(self):
|
|
|
- """getFinalState(self)"""
|
|
|
- return(self.__finalState)
|
|
|
-
|
|
|
- def setFinalState(self, finalStateName):
|
|
|
- """setFinalState(self, string)"""
|
|
|
- self.__finalState = self.getStateNamed(finalStateName)
|
|
|
-
|
|
|
- def requestFinalState(self):
|
|
|
- self.request(self.__finalState.getName())
|
|
|
-
|
|
|
- def getCurrentState(self):
|
|
|
- """getCurrentState(self)"""
|
|
|
- return(self.__currentState)
|
|
|
-
|
|
|
-
|
|
|
- # lookup funcs
|
|
|
-
|
|
|
- def getStateNamed(self, stateName):
|
|
|
- """getStateNamed(self, string)
|
|
|
- Return the state with given name if found, issue warning otherwise"""
|
|
|
- for state in self.__states:
|
|
|
- if (state.getName() == stateName):
|
|
|
- return state
|
|
|
- FSM.notify.warning("[%s] : getStateNamed: %s, no such state" %
|
|
|
- (self.__name, str(stateName)))
|
|
|
-
|
|
|
-
|
|
|
- # basic FSM functionality
|
|
|
-
|
|
|
- def __exitCurrent(self, argList):
|
|
|
- """__exitCurrent(self)
|
|
|
- Exit the current state"""
|
|
|
- assert(self.__internalStateInFlux)
|
|
|
- if FSM.notify.getDebug():
|
|
|
- FSM.notify.debug("[%s]: exiting %s" % (self.__name,
|
|
|
- self.__currentState.getName()))
|
|
|
- self.__currentState.exit(argList)
|
|
|
- # Only send the state change event if we are inspecting it
|
|
|
- # If this event turns out to be generally useful, we can
|
|
|
- # turn it on all the time, but for now nobody else is using it
|
|
|
- if self.inspecting:
|
|
|
- messenger.send(self.getName() + '_' +
|
|
|
- self.__currentState.getName() + '_exited')
|
|
|
- self.__currentState = None
|
|
|
-
|
|
|
- def __enter(self, aState, argList=[]):
|
|
|
- """__enter(self, State)
|
|
|
- Enter a given state, if it exists"""
|
|
|
- assert(self.__internalStateInFlux)
|
|
|
- if (aState in self.__states):
|
|
|
- if FSM.notify.getDebug():
|
|
|
- FSM.notify.debug("[%s]: entering %s" % (self.__name,
|
|
|
- aState.getName()))
|
|
|
- self.__currentState = aState
|
|
|
- # Only send the state change event if we are inspecting it
|
|
|
- # If this event turns out to be generally useful, we can
|
|
|
- # turn it on all the time, but for now nobody else is using it
|
|
|
- if self.inspecting:
|
|
|
- messenger.send(self.getName() + '_' +
|
|
|
- aState.getName() + '_entered')
|
|
|
-
|
|
|
- # Once we begin entering the new state, we're allow to
|
|
|
- # recursively request a transition to another state.
|
|
|
- # Indicate this by marking our internal state no longer in
|
|
|
- # flux.
|
|
|
- self.__internalStateInFlux = 0
|
|
|
- aState.enter(argList)
|
|
|
- else:
|
|
|
- # notify.error is going to raise an exception; reset the
|
|
|
- # flux flag first
|
|
|
- self.__internalStateInFlux = 0
|
|
|
- FSM.notify.error("[%s]: enter: no such state" % (self.__name))
|
|
|
-
|
|
|
- def __transition(self, aState, enterArgList=[], exitArgList=[]):
|
|
|
- """__transition(self, State, enterArgList, exitArgList)
|
|
|
- Exit currentState and enter given one"""
|
|
|
- assert(not self.__internalStateInFlux)
|
|
|
- self.__internalStateInFlux = 1
|
|
|
- self.__exitCurrent(exitArgList)
|
|
|
- self.__enter(aState, enterArgList)
|
|
|
- assert(not self.__internalStateInFlux)
|
|
|
-
|
|
|
- def request(self, aStateName, enterArgList=[], exitArgList=[],
|
|
|
- force=0):
|
|
|
- """request(self, string)
|
|
|
- Attempt transition from currentState to given one.
|
|
|
- Return true is transition exists to given state,
|
|
|
- false otherwise.
|
|
|
- """
|
|
|
-
|
|
|
- # If you trigger this assertion failure, you must have
|
|
|
- # recursively requested a state transition from within the
|
|
|
- # exitState() function for the previous state. This is not
|
|
|
- # supported because we're not fully transitioned into the new
|
|
|
- # state yet.
|
|
|
- assert(not self.__internalStateInFlux)
|
|
|
-
|
|
|
- if not self.__currentState:
|
|
|
- # Make this a warning for now
|
|
|
- FSM.notify.warning("[%s]: request: never entered initial state" %
|
|
|
- (self.__name))
|
|
|
- self.__currentState = self.__initialState
|
|
|
-
|
|
|
- if isinstance(aStateName, types.StringType):
|
|
|
- aState = self.getStateNamed(aStateName)
|
|
|
- else:
|
|
|
- # Allow the caller to pass in a state in itself, not just
|
|
|
- # the name of a state.
|
|
|
- aState = aStateName
|
|
|
- aStateName = aState.getName()
|
|
|
-
|
|
|
- if aState == None:
|
|
|
- FSM.notify.error("[%s]: request: %s, no such state" %
|
|
|
- (self.__name, aStateName))
|
|
|
-
|
|
|
- # is the transition defined? if it isn't, should we allow it?
|
|
|
- transitionDefined = self.__currentState.isTransitionDefined(aStateName)
|
|
|
- transitionAllowed = transitionDefined
|
|
|
-
|
|
|
- if self.onUndefTransition == FSM.ALLOW:
|
|
|
- transitionAllowed = 1
|
|
|
- if not transitionDefined:
|
|
|
- # the transition is not defined, but we're going to do it
|
|
|
- # anyway. print a warning.
|
|
|
- FSM.notify.warning(
|
|
|
- "[%s]: performing undefined transition from %s to %s" %
|
|
|
- (self.__name,
|
|
|
- self.__currentState.getName(),
|
|
|
- aStateName))
|
|
|
-
|
|
|
- if transitionAllowed or force:
|
|
|
- self.__transition(aState,
|
|
|
- enterArgList,
|
|
|
- exitArgList)
|
|
|
- return 1
|
|
|
- # We can implicitly always transition to our final state.
|
|
|
- elif (aStateName == self.__finalState.getName()):
|
|
|
- if (self.__currentState == self.__finalState):
|
|
|
- # Do not do the transition if we are already in the final state
|
|
|
- if FSM.notify.getDebug():
|
|
|
- FSM.notify.debug("[%s]: already in final state: %s" %
|
|
|
- (self.__name, aStateName))
|
|
|
- return 1
|
|
|
- else:
|
|
|
- # Force a transition to allow for cleanup
|
|
|
- if FSM.notify.getDebug():
|
|
|
- FSM.notify.debug("[%s]: implicit transition to final state: %s" %
|
|
|
- (self.__name, aStateName))
|
|
|
- self.__transition(aState,
|
|
|
- enterArgList,
|
|
|
- exitArgList)
|
|
|
- return 1
|
|
|
- # are we already in this state?
|
|
|
- elif (aStateName == self.__currentState.getName()):
|
|
|
- if FSM.notify.getDebug():
|
|
|
- FSM.notify.debug("[%s]: already in state %s and no self transition" %
|
|
|
- (self.__name, aStateName))
|
|
|
- return 0
|
|
|
- else:
|
|
|
- msg = ("[%s]: no transition exists from %s to %s" %
|
|
|
- (self.__name,
|
|
|
- self.__currentState.getName(),
|
|
|
- aStateName))
|
|
|
- if self.onUndefTransition == FSM.ERROR:
|
|
|
- FSM.notify.error(msg)
|
|
|
- elif self.onUndefTransition == FSM.DISALLOW_VERBOSE:
|
|
|
- FSM.notify.warning(msg)
|
|
|
- return 0
|
|
|
-
|
|
|
-
|
|
|
- def forceTransition(self, aStateName, enterArgList=[], exitArgList=[]):
|
|
|
- """ force a transition -- for debugging ONLY """
|
|
|
- self.request(aStateName, enterArgList, exitArgList, force=1)
|
|
|
-
|
|
|
- def conditional_request(self, aStateName, enterArgList=[], exitArgList=[]):
|
|
|
- """request(self, string)
|
|
|
- 'if this transition is defined, do it'
|
|
|
- Attempt transition from currentState to given one, if it exists.
|
|
|
- Return true if transition exists to given state,
|
|
|
- false otherwise. It is NOT an error/warning to attempt a cond_request
|
|
|
- if the transition doesn't exist. This lets people be sloppy about
|
|
|
- FSM transitions, letting the same fn be used for different states
|
|
|
- that may not have the same out transitions.
|
|
|
- """
|
|
|
-
|
|
|
- assert(not self.__internalStateInFlux)
|
|
|
- if not self.__currentState:
|
|
|
- # Make this a warning for now
|
|
|
- FSM.notify.warning("[%s]: request: never entered initial state" %
|
|
|
- (self.__name))
|
|
|
- self.__currentState = self.__initialState
|
|
|
-
|
|
|
- if isinstance(aStateName, types.StringType):
|
|
|
- aState = self.getStateNamed(aStateName)
|
|
|
- else:
|
|
|
- # Allow the caller to pass in a state in itself, not just
|
|
|
- # the name of a state.
|
|
|
- aState = aStateName
|
|
|
- aStateName = aState.getName()
|
|
|
-
|
|
|
- if aState == None:
|
|
|
- FSM.notify.error("[%s]: request: %s, no such state" %
|
|
|
- (self.__name, aStateName))
|
|
|
-
|
|
|
- transitionDefined = (
|
|
|
- self.__currentState.isTransitionDefined(aStateName) or
|
|
|
- aStateName in [self.__currentState.getName(),
|
|
|
- self.__finalState.getName()]
|
|
|
- )
|
|
|
-
|
|
|
- if transitionDefined:
|
|
|
- return self.request(aStateName, enterArgList, exitArgList)
|
|
|
- else:
|
|
|
- FSM.notify.debug("[%s]: condition_request: %s, transition doesnt exist" %
|
|
|
- (self.__name, aStateName))
|
|
|
- return 0
|
|
|
-
|
|
|
- def view(self):
|
|
|
- import FSMInspector
|
|
|
- FSMInspector.FSMInspector(self)
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|