瀏覽代碼

rename FSM -> ClassicFSM, introduce new FSM code

David Rose 22 年之前
父節點
當前提交
13437d3ff5

+ 368 - 0
direct/src/fsm/ClassicFSM.py

@@ -0,0 +1,368 @@
+"""Finite State Machine module: contains the ClassicFSM class.
+
+This module and class exist only for backward compatibility with
+existing code.  New code should use the FSM module instead.
+
+"""
+
+from DirectObject import *
+import types
+
+class ClassicFSM(DirectObject):
+    """ClassicFSM class: Finite State Machine class.
+
+    This module and class exist only for backward compatibility with
+    existing code.  New code should use the FSM class instead.
+    """
+
+    # create ClassicFSM DirectNotify category
+    notify = directNotify.newCategory("ClassicFSM")
+
+    # special methods
+
+    # these are flags that tell the ClassicFSM 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)
+
+        ClassicFSM constructor: takes name, list of states, initial state and
+        final state as:
+
+        fsm = ClassicFSM.ClassicFSM('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
+
+    # I know this isn't how __repr__ is supposed to be used, but it
+    # is nice and convenient.
+    def __repr__(self):
+        return self.__str__()
+
+    def __str__(self):
+        """__str__(self)
+        Print out something useful about the fsm
+        """
+        currentState = self.getCurrentState()
+        if currentState:
+            str = ("ClassicFSM " + self.getName() + ' in state "' +
+                   currentState.getName() + '"')
+        else:
+            str = ("ClassicFSM " + self.getName() + ' 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 "ClassicFSM: 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
+        ClassicFSM.notify.warning("[%s] : getStateNamed: %s, no such state" %
+                           (self.__name, str(stateName)))
+
+
+    # basic ClassicFSM functionality
+
+    def __exitCurrent(self, argList):
+        """__exitCurrent(self)
+        Exit the current state"""
+        assert(self.__internalStateInFlux)
+        if ClassicFSM.notify.getDebug():
+            ClassicFSM.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 ClassicFSM.notify.getDebug():
+                ClassicFSM.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
+            ClassicFSM.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
+            ClassicFSM.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:
+            ClassicFSM.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 == ClassicFSM.ALLOW:
+            transitionAllowed = 1
+            if not transitionDefined:
+                # the transition is not defined, but we're going to do it
+                # anyway. print a warning.
+                ClassicFSM.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 ClassicFSM.notify.getDebug():
+                    ClassicFSM.notify.debug("[%s]: already in final state: %s" %
+                                     (self.__name, aStateName))
+                return 1
+            else:
+                # Force a transition to allow for cleanup
+                if ClassicFSM.notify.getDebug():
+                    ClassicFSM.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 ClassicFSM.notify.getDebug():
+                ClassicFSM.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 == ClassicFSM.ERROR:
+                ClassicFSM.notify.error(msg)
+            elif self.onUndefTransition == ClassicFSM.DISALLOW_VERBOSE:
+                ClassicFSM.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
+        ClassicFSM 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
+            ClassicFSM.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:
+            ClassicFSM.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:
+            ClassicFSM.notify.debug("[%s]: condition_request: %s, transition doesnt exist" %
+                             (self.__name, aStateName))
+            return 0
+
+    def view(self):
+        import FSMInspector
+        FSMInspector.FSMInspector(self)
+
+
+
+
+
+
+
+
+
+

+ 244 - 346
direct/src/fsm/FSM.py

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

+ 4 - 4
direct/src/fsm/FourState.py

@@ -2,14 +2,14 @@
 
 import DirectNotifyGlobal
 #import DistributedObject
-import FSM
+import ClassicFSM
 import State
 import Task
 
 
 class FourState:
     """
-    Generic four state FSM base class.
+    Generic four state ClassicFSM base class.
     
     This is a mix-in class that expects that your derived class
     is a DistributedObject.
@@ -34,7 +34,7 @@ class FourState:
                      +------+
     
     There is a fifth off state, but that is an implementation
-    detail (and that's why it's not called a five state FSM).
+    detail (and that's why it's not called a five state ClassicFSM).
     
     I found that this pattern repeated in several things I was
     working on, so this base class was created.
@@ -120,7 +120,7 @@ class FourState:
                            [names[1]]),
             }
         self.stateIndex = 0
-        self.fsm = FSM.FSM('FourState',
+        self.fsm = ClassicFSM.ClassicFSM('FourState',
                            self.states.values(),
                            # Initial State
                            names[0],

+ 5 - 5
direct/src/fsm/FourStateAI.py

@@ -2,14 +2,14 @@
 
 import DirectNotifyGlobal
 #import DistributedObjectAI
-import FSM
+import ClassicFSM
 import State
 import Task
 
 
 class FourStateAI:
     """
-    Generic four state FSM base class.
+    Generic four state ClassicFSM base class.
     
     This is a mix-in class that expects that your derived class
     is a DistributedObjectAI.
@@ -34,7 +34,7 @@ class FourStateAI:
                      +------+
     
     There is a fifth off state, but that is an implementation
-    detail (and that's why it's not called a five state FSM).
+    detail (and that's why it's not called a five state ClassicFSM).
     
     I found that this pattern repeated in several things I was
     working on, so this base class was created.
@@ -58,7 +58,7 @@ class FourStateAI:
         durations is a list of durations in seconds or None values.
             The list of duration values should be the same length
             as the list of state names and the lists correspond.
-            For each state, after n seconds, the FSM will move to 
+            For each state, after n seconds, the ClassicFSM will move to 
             the next state.  That does not happen for any duration
             values of None.
         
@@ -125,7 +125,7 @@ class FourStateAI:
                            self.exitState4,
                            [names[1]]),
             }
-        self.fsm = FSM.FSM('FourState',
+        self.fsm = ClassicFSM.ClassicFSM('FourState',
                            self.states.values(),
                            # Initial State
                            names[0],

+ 193 - 0
direct/src/fsm/SampleFSM.py

@@ -0,0 +1,193 @@
+import FSM
+from PandaModules import *
+import Task
+import string
+
+
+class ClassicStyle(FSM.FSM):
+
+    def __init__(self, name):
+        FSM.FSM.__init__(self, name)
+
+        self.defaultTransitions = {
+            'Red' : ['Green'],
+            'Yellow' : ['Red'],
+            'Green' : ['Yellow'],
+            }
+
+    def enterRed(self, oldState, newState):
+        print "enterRed(self, '%s', '%s')" % (oldState, newState)
+
+    def exitRed(self, oldState, newState):
+        print "exitRed(self, '%s', '%s')" % (oldState, newState)
+
+    def enterYellow(self, oldState, newState):
+        print "enterYellow(self, '%s', '%s')" % (oldState, newState)
+
+    def exitYellow(self, oldState, newState):
+        print "exitYellow(self, '%s', '%s')" % (oldState, newState)
+
+    def enterGreen(self, oldState, newState):
+        print "enterGreen(self, '%s', '%s')" % (oldState, newState)
+
+    def exitGreen(self, oldState, newState):
+        print "exitGreen(self, '%s', '%s')" % (oldState, newState)
+
+
+class NewStyle(FSM.FSM):
+
+    def enterRed(self, oldState, newState):
+        print "enterRed(self, '%s', '%s')" % (oldState, newState)
+
+    def filterRed(self, request, args):
+        print "filterRed(self, '%s', %s)" % (request, args)
+        if request == 'advance':
+            return 'Green'
+        return self.defaultFilter(request, args)
+
+    def exitRed(self, oldState, newState):
+        print "exitRed(self, '%s', '%s')" % (oldState, newState)
+
+    def enterYellow(self, oldState, newState):
+        print "enterYellow(self, '%s', '%s')" % (oldState, newState)
+
+    def filterYellow(self, request, args):
+        print "filterYellow(self, '%s', %s)" % (request, args)
+        if request == 'advance':
+            return 'Red'
+        return self.defaultFilter(request, args)
+
+    def exitYellow(self, oldState, newState):
+        print "exitYellow(self, '%s', '%s')" % (oldState, newState)
+
+    def enterGreen(self, oldState, newState):
+        print "enterGreen(self, '%s', '%s')" % (oldState, newState)
+
+    def filterGreen(self, request, args):
+        print "filterGreen(self, '%s', %s)" % (request, args)
+        if request == 'advance':
+            return 'Yellow'
+        return self.defaultFilter(request, args)
+
+    def exitGreen(self, oldState, newState):
+        print "exitGreen(self, '%s', '%s')" % (oldState, newState)
+
+
+class ToonEyes(FSM.FSM):
+    def __init__(self):
+        FSM.FSM.__init__(self, 'eyes')
+
+        self.__unblinkName = "unblink"
+
+        # Eyes are initially open.
+        self.request('Open')
+
+    def defaultFilter(self, request, args):
+        # The default filter accepts any direct state request (these
+        # start with a capital letter).
+        if request[0] in string.uppercase:
+            return request
+
+        # Unexpected command requests are quietly ignored.
+        return None
+
+    def enterOpen(self, oldState, newState):
+        print "swap in eyes open model"
+
+    def filterOpen(self, request, args):
+        if request == 'blink':
+            taskMgr.remove(self.__unblinkName)
+            taskMgr.doMethodLater(0.125, self.__unblink, self.__unblinkName)
+            return 'Closed'
+        return self.defaultFilter(request, args)
+
+    def __unblink(self, task):
+        self.request('unblink')
+        return Task.done
+
+    def enterClosed(self, oldState, newState):
+        print "swap in eyes closed model"
+
+    def filterClosed(self, request, args):
+        if request == 'unblink':
+            return 'Open'
+        return self.defaultFilter(request, args)
+
+    def enterSurprised(self, oldState, newState):
+        print "swap in eyes surprised model"
+
+    def enterOff(self, oldState, newState):
+        taskMgr.remove(self.__unblinkName)
+
+
+####
+#### Example of using ClassicStyle:
+##
+## >>> import SampleFSM
+## >>> foo = SampleFSM.ClassicStyle('foo')
+## >>> foo.request('Red')
+## enterRed(self, 'Off', 'Red')
+## 'Red'
+## >>> foo.request('Yellow')
+## Traceback (most recent call last):
+##   File "<stdin>", line 1, in ?
+##   File "/home/drose/player/direct/src/fsm/FSM.py", line 168, in request
+##     result = func(request, args)
+##   File "/home/drose/player/direct/src/fsm/FSM.py", line 210, in defaultFilter
+##     self.notify.error("%s rejecting request %s from state %s." % (self.name, request, self.state))
+##   File "/home/drose/player/direct/src/directnotify/Notifier.py", line 99, in error
+##     raise exception(errorString)
+## StandardError: foo rejecting request Yellow from state Red.
+## >>> foo.request('Green')
+## exitRed(self, 'Red', 'Green')
+## enterGreen(self, 'Red', 'Green')
+## 'Green'
+## >>> 
+
+####
+#### Example of using NewStyle:
+##
+## >>> import SampleFSM
+## >>> foo = SampleFSM.NewStyle('foo')
+## >>> foo.request('Red')
+## enterRed(self, 'Off', 'Red')
+## 'Red'
+## >>> foo.request('advance')
+## filterRed(self, 'advance', ())
+## exitRed(self, 'Red', 'Green')
+## enterGreen(self, 'Red', 'Green')
+## 'Green'
+## >>> foo.request('advance')
+## filterGreen(self, 'advance', ())
+## exitGreen(self, 'Green', 'Yellow')
+## enterYellow(self, 'Green', 'Yellow')
+## 'Yellow'
+## >>> foo.request('advance')
+## filterYellow(self, 'advance', ())
+## exitYellow(self, 'Yellow', 'Red')
+## enterRed(self, 'Yellow', 'Red')
+## 'Red'
+## >>> foo.request('advance')
+## filterRed(self, 'advance', ())
+## exitRed(self, 'Red', 'Green')
+## enterGreen(self, 'Red', 'Green')
+## 'Green'
+## >>> 
+
+####
+#### Example of using ToonEyes:
+##
+## >>> from ShowBaseGlobal import *
+## >>> import SampleFSM
+## >>> eyes = SampleFSM.ToonEyes()
+## swap in eyes open model
+## >>> eyes.request('blink')
+## swap in eyes closed model
+## 'Closed'
+## >>> run()
+## swap in eyes open model
+## >>> eyes.request('Surprised')
+## swap in eyes surprised model
+## 'Surprised'
+## >>> eyes.request('blink')
+## >>> 

+ 11 - 11
direct/src/fsm/State.py

@@ -5,7 +5,7 @@ from DirectObject import *
 import types
 
 # This gets set by a dconfig variable in ShowBase.py
-# We cannot put a dconfig in here because FSM is not
+# We cannot put a dconfig in here because ClassicFSM is not
 # dependent on Panda
 FsmRedefine = 0
 
@@ -193,20 +193,20 @@ class State(DirectObject):
         return(self.__FSMList)
 
     def setChildren(self, FSMList):
-        """setChildren(self, FSM[])
+        """setChildren(self, ClassicFSM[])
         Set the children to given list of FSMs"""
         self.__FSMList = FSMList
 
-    def addChild(self, FSM):
-        """addChild(self, FSM)
-        Add the given FSM to list of child FSMs"""
-        self.__FSMList.append(FSM)
+    def addChild(self, ClassicFSM):
+        """addChild(self, ClassicFSM)
+        Add the given ClassicFSM to list of child FSMs"""
+        self.__FSMList.append(ClassicFSM)
 
-    def removeChild(self, FSM):
-        """removeChild(self, FSM)
-        Remove the given FSM from list of child FSMs"""
-        if FSM in self.__FSMList:
-            self.__FSMList.remove(FSM)
+    def removeChild(self, ClassicFSM):
+        """removeChild(self, ClassicFSM)
+        Remove the given ClassicFSM from list of child FSMs"""
+        if ClassicFSM in self.__FSMList:
+            self.__FSMList.remove(ClassicFSM)
 
     def hasChildren(self):
         """hasChildren(self)

+ 1 - 1
direct/src/fsm/StateData.py

@@ -8,7 +8,7 @@ import DirectNotifyGlobal
 class StateData(DirectObject):
     """
     A StateData is a base class for a single state within a Finite
-    State Machine (FSM).
+    State Machine (ClassicFSM).
     """
 
     notify = DirectNotifyGlobal.directNotify.newCategory('StateData')

+ 1 - 1
direct/src/level/CutScene.py

@@ -12,7 +12,7 @@ from ClockDelta import *
 
 import ToontownGlobals
 import DirectNotifyGlobal
-import FSM
+import ClassicFSM
 #import DistributedInteractiveEntity
 import DelayDelete
 import Localizer

+ 2 - 2
direct/src/level/DistributedInteractiveEntity.py

@@ -5,7 +5,7 @@ from ShowBaseGlobal import *
 from ClockDelta import *
 
 import DirectNotifyGlobal
-import FSM
+import ClassicFSM
 import DistributedEntity
 
 class DistributedInteractiveEntity(DistributedEntity.DistributedEntity):
@@ -22,7 +22,7 @@ class DistributedInteractiveEntity(DistributedEntity.DistributedEntity):
         DistributedEntity.DistributedEntity.__init__(self, cr)
         assert(self.debugPrint("DistributedInteractiveEntity()"))
 
-        self.fsm = FSM.FSM('DistributedInteractiveEntity',
+        self.fsm = ClassicFSM.ClassicFSM('DistributedInteractiveEntity',
                            [State.State('off',
                                         self.enterOff,
                                         self.exitOff,

+ 2 - 2
direct/src/level/DistributedInteractiveEntityAI.py

@@ -7,7 +7,7 @@ from AIBaseGlobal import *
 from ClockDelta import *
 
 import DirectNotifyGlobal
-import FSM
+import ClassicFSM
 import DistributedEntityAI
 import State
 
@@ -30,7 +30,7 @@ class DistributedInteractiveEntityAI(DistributedEntityAI.DistributedEntityAI):
         assert(self.debugPrint(
                 "DistributedInteractiveEntityAI(entId=%s)"
                 %(entId)))
-        self.fsm = FSM.FSM('DistributedInteractiveEntityAI',
+        self.fsm = ClassicFSM.ClassicFSM('DistributedInteractiveEntityAI',
                            [State.State('off',
                                         self.enterOff,
                                         self.exitOff,

+ 1 - 1
direct/src/showbase/ShowBase.py

@@ -21,7 +21,7 @@ import math
 import sys
 import Loader
 import time
-import FSM
+import ClassicFSM
 import State
 import DirectObject
 import SfxPlayer

+ 16 - 16
direct/src/tkpanels/FSMInspector.py

@@ -10,7 +10,7 @@ DELTA = (5.0 / 360.) * 2.0 * math.pi
 
 class FSMInspector(AppShell):
     # Override class variables
-    appname = 'FSM Inspector'
+    appname = 'ClassicFSM Inspector'
     frameWidth  = 400
     frameHeight = 450
     usecommandarea = 0
@@ -44,23 +44,23 @@ class FSMInspector(AppShell):
         interior = self.interior()
         menuBar = self.menuBar
 
-        # FSM Menu
-        menuBar.addmenu('FSM', 'FSM Operations')
-        menuBar.addmenuitem('FSM', 'command',
+        # ClassicFSM Menu
+        menuBar.addmenu('ClassicFSM', 'ClassicFSM Operations')
+        menuBar.addmenuitem('ClassicFSM', 'command',
                                   'Input grid spacing',
                                   label = 'Grid spacing...',
                                   command = self.popupGridDialog)
         # Create the checkbutton variable
         self._fGridSnap = IntVar()
         self._fGridSnap.set(1)
-        menuBar.addmenuitem('FSM', 'checkbutton',
+        menuBar.addmenuitem('ClassicFSM', 'checkbutton',
                                   'Enable/disable grid',
                                   label = 'Snap to grid',
                                   variable = self._fGridSnap,
                                   command = self.toggleGridSnap)
-        menuBar.addmenuitem('FSM', 'command',
-                                  'Print out FSM layout',
-                                  label = 'Print FSM layout',
+        menuBar.addmenuitem('ClassicFSM', 'command',
+                                  'Print out ClassicFSM layout',
+                                  label = 'Print ClassicFSM layout',
                                   command = self.printLayout)
         
         # States Menu
@@ -238,7 +238,7 @@ class FSMInspector(AppShell):
             self.stateInspectorDict[key].setGridSize(size)
 
     def popupGridDialog(self):
-        spacing = askstring('FSM Grid Spacing', 'Grid Spacing:')
+        spacing = askstring('ClassicFSM Grid Spacing', 'Grid Spacing:')
         if spacing:
             self.setGridSize(spacing)
             self._gridSize = spacing
@@ -253,7 +253,7 @@ class FSMInspector(AppShell):
         dict = self.stateInspectorDict
         keys = dict.keys()
         keys.sort
-        print "FSM.FSM('%s', [" % self.name
+        print "ClassicFSM.ClassicFSM('%s', [" % self.name
         for key in keys[:-1]:
             si = dict[key]
             center = si.center()
@@ -282,7 +282,7 @@ class FSMInspector(AppShell):
             self.balloon.configure(state = 'none')
             
     def onDestroy(self, event):
-        """ Called on FSM Panel shutdown """
+        """ Called on ClassicFSM Panel shutdown """
         self.fsm.inspecting = 0
         for si in self.stateInspectorDict.values():
             self.ignore(self.name + '_' + si.getName() + '_entered')
@@ -454,7 +454,7 @@ want-tk #t
 
 from ShowBaseGlobal import *
 
-import FSM
+import ClassicFSM
 import State
 
 def enterState():
@@ -463,7 +463,7 @@ def enterState():
 def exitState():
     print 'exitState'
 
-fsm = FSM.FSM('stopLight',
+fsm = ClassicFSM.ClassicFSM('stopLight',
           [ State.State('red', enterState, exitState, ['green']),
             State.State('yellow', enterState, exitState, ['red']),
             State.State('green', enterState, exitState, ['yellow']) ],
@@ -476,7 +476,7 @@ inspector = FSMInspector.FSMInspector(fsm, title = fsm.getName())
 
 # Note, the inspectorPos argument is optional, the inspector will
 # automagically position states on startup
-fsm = FSM.FSM('stopLight', [
+fsm = ClassicFSM.ClassicFSM('stopLight', [
     State.State('yellow',
                 enterState,
                 exitState,
@@ -510,8 +510,8 @@ Features:
    -  Middle mouse button will grab the canvas and slide things around
         if your state machine is bigger than the viewing area
    -  There are some self explanatory menu options up at the top, the most
-        useful being: "print FSM layout" which will print out python code
-        which will create an FSM augmented with layout information for the
+        useful being: "print ClassicFSM layout" which will print out python code
+        which will create an ClassicFSM augmented with layout information for the
         viewer so everything shows up in the same place the next time you
         inspect the state machine
 

+ 3 - 3
direct/src/tkpanels/Inspector.py

@@ -408,7 +408,7 @@ class InspectorWindow:
         part = self.topInspector().partNumber(partNumber)
         print part
         from PandaModules import NodePath
-        import FSM
+        import ClassicFSM
         popupMenu = None
         if isinstance(part, NodePath):
             popupMenu = self.createPopupMenu(
@@ -416,11 +416,11 @@ class InspectorWindow:
                 [('Explore', NodePath.explore),
                  ('Place', NodePath.place),
                  ('Set Color', NodePath.rgbPanel)])
-        elif isinstance(part, FSM.FSM):
+        elif isinstance(part, ClassicFSM.ClassicFSM):
             import FSMInspector
             popupMenu = self.createPopupMenu(
                 part,
-                [('Inspect FSM', FSMInspector.FSMInspector)])
+                [('Inspect ClassicFSM', FSMInspector.FSMInspector)])
         print popupMenu
         if popupMenu:
             popupMenu.post(event.widget.winfo_pointerx(),