Browse Source

rename FSM -> ClassicFSM, introduce new FSM code

David Rose 22 years ago
parent
commit
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 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):
     def __repr__(self):
         return self.__str__()
         return self.__str__()
 
 
@@ -70,290 +250,8 @@ class FSM(DirectObject):
         """__str__(self)
         """__str__(self)
         Print out something useful about the fsm
         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:
         else:
-            str = ("FSM " + self.getName() + ' not in any state')
+            str = ("FSM " + self.name + ' not in any state')
         return str
         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 DirectNotifyGlobal
 #import DistributedObject
 #import DistributedObject
-import FSM
+import ClassicFSM
 import State
 import State
 import Task
 import Task
 
 
 
 
 class FourState:
 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
     This is a mix-in class that expects that your derived class
     is a DistributedObject.
     is a DistributedObject.
@@ -34,7 +34,7 @@ class FourState:
                      +------+
                      +------+
     
     
     There is a fifth off state, but that is an implementation
     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
     I found that this pattern repeated in several things I was
     working on, so this base class was created.
     working on, so this base class was created.
@@ -120,7 +120,7 @@ class FourState:
                            [names[1]]),
                            [names[1]]),
             }
             }
         self.stateIndex = 0
         self.stateIndex = 0
-        self.fsm = FSM.FSM('FourState',
+        self.fsm = ClassicFSM.ClassicFSM('FourState',
                            self.states.values(),
                            self.states.values(),
                            # Initial State
                            # Initial State
                            names[0],
                            names[0],

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

@@ -2,14 +2,14 @@
 
 
 import DirectNotifyGlobal
 import DirectNotifyGlobal
 #import DistributedObjectAI
 #import DistributedObjectAI
-import FSM
+import ClassicFSM
 import State
 import State
 import Task
 import Task
 
 
 
 
 class FourStateAI:
 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
     This is a mix-in class that expects that your derived class
     is a DistributedObjectAI.
     is a DistributedObjectAI.
@@ -34,7 +34,7 @@ class FourStateAI:
                      +------+
                      +------+
     
     
     There is a fifth off state, but that is an implementation
     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
     I found that this pattern repeated in several things I was
     working on, so this base class was created.
     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.
         durations is a list of durations in seconds or None values.
             The list of duration values should be the same length
             The list of duration values should be the same length
             as the list of state names and the lists correspond.
             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
             the next state.  That does not happen for any duration
             values of None.
             values of None.
         
         
@@ -125,7 +125,7 @@ class FourStateAI:
                            self.exitState4,
                            self.exitState4,
                            [names[1]]),
                            [names[1]]),
             }
             }
-        self.fsm = FSM.FSM('FourState',
+        self.fsm = ClassicFSM.ClassicFSM('FourState',
                            self.states.values(),
                            self.states.values(),
                            # Initial State
                            # Initial State
                            names[0],
                            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
 import types
 
 
 # This gets set by a dconfig variable in ShowBase.py
 # 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
 # dependent on Panda
 FsmRedefine = 0
 FsmRedefine = 0
 
 
@@ -193,20 +193,20 @@ class State(DirectObject):
         return(self.__FSMList)
         return(self.__FSMList)
 
 
     def setChildren(self, FSMList):
     def setChildren(self, FSMList):
-        """setChildren(self, FSM[])
+        """setChildren(self, ClassicFSM[])
         Set the children to given list of FSMs"""
         Set the children to given list of FSMs"""
         self.__FSMList = FSMList
         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):
     def hasChildren(self):
         """hasChildren(self)
         """hasChildren(self)

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

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

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

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

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

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

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

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

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

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

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

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

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

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