Browse Source

adding ability to define specialized behavior for particular transitions

Josh Wilson 15 years ago
parent
commit
4d91590a26
1 changed files with 40 additions and 13 deletions
  1. 40 13
      direct/src/fsm/FSM.py

+ 40 - 13
direct/src/fsm/FSM.py

@@ -121,6 +121,14 @@ class FSM(DirectObject):
     function, and clean it up within the corresponding exitState()
     function, and clean it up within the corresponding exitState()
     function.
     function.
 
 
+    There is a way to define specialized transition behavior between
+    two particular states.  This is done by defining a from<X>To<Y>()
+    function, where X is the old state and Y is the new state.  If this
+    is defined, it will be run in place of the exit<X> and enter<Y>
+    functions, so if you want that behavior, you'll have to call them
+    specifically.  Otherwise, you can completely replace that transition's
+    behavior.
+
     See the code in SampleFSM.py for further examples.
     See the code in SampleFSM.py for further examples.
     """
     """
 
 
@@ -180,6 +188,19 @@ class FSM(DirectObject):
         # functions will already have been called.
         # functions will already have been called.
         return 'FSM-%s-%s-stateChange' % (self._serialNum, self.name)
         return 'FSM-%s-%s-stateChange' % (self._serialNum, self.name)
 
 
+    def getCurrentFilter(self):
+        if not self.state:
+            error = "requested %s while FSM is in transition from %s to %s." % (request, self.oldState, self.newState)
+            raise AlreadyInTransition, error
+
+        filter = getattr(self, "filter" + self.state, None)
+        if not filter:
+            # If there's no matching filterState() function, call
+            # defaultFilter() instead.
+            filter = self.defaultFilter
+            
+        return filter
+
     def getCurrentOrNextState(self):
     def getCurrentOrNextState(self):
         # Returns the current state if we are in a state now, or the
         # Returns the current state if we are in a state now, or the
         # state we are transitioning into if we are currently within
         # state we are transitioning into if we are currently within
@@ -289,16 +310,8 @@ class FSM(DirectObject):
             self.notify.debug("%s.request(%s, %s" % (
             self.notify.debug("%s.request(%s, %s" % (
                 self.name, request, str(args)[1:]))
                 self.name, request, str(args)[1:]))
 
 
-            if not self.state:
-                error = "requested %s while FSM is in transition from %s to %s." % (request, self.oldState, self.newState)
-                raise AlreadyInTransition, error
-
-            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)
+            filter = self.getCurrentFilter()
+            result = filter(request, args)
             if result:
             if result:
                 if isinstance(result, types.StringTypes):
                 if isinstance(result, types.StringTypes):
                     # If the return value is a string, it's just the name
                     # If the return value is a string, it's just the name
@@ -439,8 +452,11 @@ class FSM(DirectObject):
         self.state = None
         self.state = None
 
 
         try:
         try:
-            self.__callExitFunc(self.oldState)
-            self.__callEnterFunc(self.newState, *args)
+            if not self.__callFromToFunc(self.oldState, self.newState, *args):
+                self.__callExitFunc(self.oldState)
+                self.__callEnterFunc(self.newState, *args)
+                pass
+            pass
         except:
         except:
             # If we got an exception during the enter or exit methods,
             # If we got an exception during the enter or exit methods,
             # go directly to state "InternalError" and raise up the
             # go directly to state "InternalError" and raise up the
@@ -476,6 +492,17 @@ class FSM(DirectObject):
             func = self.defaultEnter
             func = self.defaultEnter
         func(*args)
         func(*args)
 
 
+    def __callFromToFunc(self, oldState, newState, *args):
+        # Calls the appropriate fromTo function when transitioning into
+        # a new state, if it exists.
+        assert self.state == None and self.oldState == oldState and self.newState == newState
+
+        func = getattr(self, "from%sTo%s" % (oldState,newState), None)
+        if func:
+            func(*args)
+            return True
+        return False
+
     def __callExitFunc(self, name):
     def __callExitFunc(self, name):
         # Calls the appropriate exit function when leaving a
         # Calls the appropriate exit function when leaving a
         # state, if it exists.
         # state, if it exists.
@@ -501,7 +528,7 @@ class FSM(DirectObject):
             if self.state:
             if self.state:
                 str = ('%s FSM:%s in state "%s"' % (className, self.name, self.state))
                 str = ('%s FSM:%s in state "%s"' % (className, self.name, self.state))
             else:
             else:
-                str = ('%s FSM:%s not in any state' % (className, self.name))
+                str = ('%s FSM:%s in transition from \'%s\' to \'%s\'' % (className, self.name, self.oldState, self.newState))
             return str
             return str
         finally:
         finally:
             self.fsmLock.release()
             self.fsmLock.release()