allow extra args to enter function, don't pass oldState, newState but store them instead

This commit is contained in:
David Rose 2004-05-26 19:17:27 +00:00
parent 88ef2acf35
commit bbea3902db
2 changed files with 66 additions and 57 deletions

View File

@ -22,22 +22,22 @@ class FSM(DirectObject.DirectObject):
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):
def enterRed(self):
... do stuff ...
def exitRed(self, oldState, newState):
def exitRed(self):
... cleanup stuff ...
def enterYellow(self, oldState, newState):
def enterYellow(self):
... do stuff ...
def exitYellow(self, oldState, newState):
def exitYellow(self):
... cleanup stuff ...
def enterGreen(self, oldState, newState):
def enterGreen(self):
... do stuff ...
def exitGreen(self, oldState, newState):
def exitGreen(self):
... cleanup stuff ...
Both functions are supplied the previous state name and the new
@ -134,7 +134,7 @@ class FSM(DirectObject.DirectObject):
bypasses the filterState() function, and just calls
exitState() followed by enterState()."""
assert(isinstance(newState, types.StringType))
assert(isinstance(newState, types.StringTypes))
self.__setState(newState)
@ -147,13 +147,14 @@ class FSM(DirectObject.DirectObject):
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.)
state transition, otherwise it is a tuple containing the name
of the state followed by any optional args.)
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))
assert(isinstance(request, types.StringTypes))
self.notify.debug("%s.request(%s, %s" % (self.name, request, str(args)[1:]))
if not self.state:
@ -167,8 +168,13 @@ class FSM(DirectObject.DirectObject):
func = self.defaultFilter
result = func(request, args)
if result:
assert(isinstance(result, types.StringType))
self.__setState(result)
if isinstance(result, types.StringTypes):
# If the return value is a string, it's just the name
# of the state. Wrap it in a tuple for consistency.
result = (result,)
# Otherwise, assume it's a (name, *args) tuple
self.__setState(*result)
return result
@ -182,14 +188,14 @@ class FSM(DirectObject.DirectObject):
if request == 'Off':
# We can always go to the "Off" state.
return request
return (request,) + args
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
return (request,) + args
else:
# If self.defaultTransitions is not None, it is a map of
@ -200,7 +206,7 @@ class FSM(DirectObject.DirectObject):
if request in self.defaultTransitions.get(self.state, []):
# This transition is listed in the defaultTransitions map;
# accept it.
return request
return (request,) + args
# If self.defaultTransitions is not None, it is an error
# to request a direct state transition (capital letter
@ -222,26 +228,29 @@ class FSM(DirectObject.DirectObject):
return self.defaultFilter(request, args)
def __setState(self, newState):
def __setState(self, newState, *args):
# Internal function to change unconditionally to the indicated
# state.
assert(self.state)
oldState = self.state
self.oldState = self.state
self.newState = newState
self.state = None
self.__callTransitionFunc("exit" + oldState, oldState, newState)
self.__callTransitionFunc("enter" + newState, oldState, newState)
self.__callTransitionFunc("exit" + self.oldState)
self.__callTransitionFunc("enter" + self.newState, *args)
self.state = newState
del self.oldState
del self.newState
def __callTransitionFunc(self, name, oldState, newState):
def __callTransitionFunc(self, name, *args):
# 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)
func(*args)
def __repr__(self):
return self.__str__()

View File

@ -15,29 +15,29 @@ class ClassicStyle(FSM.FSM):
'Green' : ['Yellow'],
}
def enterRed(self, oldState, newState):
print "enterRed(self, '%s', '%s')" % (oldState, newState)
def enterRed(self):
print "enterRed(self, '%s', '%s')" % (self.oldState, self.newState)
def exitRed(self, oldState, newState):
print "exitRed(self, '%s', '%s')" % (oldState, newState)
def exitRed(self):
print "exitRed(self, '%s', '%s')" % (self.oldState, self.newState)
def enterYellow(self, oldState, newState):
print "enterYellow(self, '%s', '%s')" % (oldState, newState)
def enterYellow(self):
print "enterYellow(self, '%s', '%s')" % (self.oldState, self.newState)
def exitYellow(self, oldState, newState):
print "exitYellow(self, '%s', '%s')" % (oldState, newState)
def exitYellow(self):
print "exitYellow(self, '%s', '%s')" % (self.oldState, self.newState)
def enterGreen(self, oldState, newState):
print "enterGreen(self, '%s', '%s')" % (oldState, newState)
def enterGreen(self):
print "enterGreen(self, '%s', '%s')" % (self.oldState, self.newState)
def exitGreen(self, oldState, newState):
print "exitGreen(self, '%s', '%s')" % (oldState, newState)
def exitGreen(self):
print "exitGreen(self, '%s', '%s')" % (self.oldState, self.newState)
class NewStyle(FSM.FSM):
def enterRed(self, oldState, newState):
print "enterRed(self, '%s', '%s')" % (oldState, newState)
def enterRed(self):
print "enterRed(self, '%s', '%s')" % (self.oldState, self.newState)
def filterRed(self, request, args):
print "filterRed(self, '%s', %s)" % (request, args)
@ -45,11 +45,11 @@ class NewStyle(FSM.FSM):
return 'Green'
return self.defaultFilter(request, args)
def exitRed(self, oldState, newState):
print "exitRed(self, '%s', '%s')" % (oldState, newState)
def exitRed(self):
print "exitRed(self, '%s', '%s')" % (self.oldState, self.newState)
def enterYellow(self, oldState, newState):
print "enterYellow(self, '%s', '%s')" % (oldState, newState)
def enterYellow(self):
print "enterYellow(self, '%s', '%s')" % (self.oldState, self.newState)
def filterYellow(self, request, args):
print "filterYellow(self, '%s', %s)" % (request, args)
@ -57,11 +57,11 @@ class NewStyle(FSM.FSM):
return 'Red'
return self.defaultFilter(request, args)
def exitYellow(self, oldState, newState):
print "exitYellow(self, '%s', '%s')" % (oldState, newState)
def exitYellow(self):
print "exitYellow(self, '%s', '%s')" % (self.oldState, self.newState)
def enterGreen(self, oldState, newState):
print "enterGreen(self, '%s', '%s')" % (oldState, newState)
def enterGreen(self):
print "enterGreen(self, '%s', '%s')" % (self.oldState, self.newState)
def filterGreen(self, request, args):
print "filterGreen(self, '%s', %s)" % (request, args)
@ -69,8 +69,8 @@ class NewStyle(FSM.FSM):
return 'Yellow'
return self.defaultFilter(request, args)
def exitGreen(self, oldState, newState):
print "exitGreen(self, '%s', '%s')" % (oldState, newState)
def exitGreen(self):
print "exitGreen(self, '%s', '%s')" % (self.oldState, self.newState)
class ToonEyes(FSM.FSM):
@ -91,7 +91,7 @@ class ToonEyes(FSM.FSM):
# Unexpected command requests are quietly ignored.
return None
def enterOpen(self, oldState, newState):
def enterOpen(self):
print "swap in eyes open model"
def filterOpen(self, request, args):
@ -105,7 +105,7 @@ class ToonEyes(FSM.FSM):
self.request('unblink')
return Task.done
def enterClosed(self, oldState, newState):
def enterClosed(self):
print "swap in eyes closed model"
def filterClosed(self, request, args):
@ -113,10 +113,10 @@ class ToonEyes(FSM.FSM):
return 'Open'
return self.defaultFilter(request, args)
def enterSurprised(self, oldState, newState):
def enterSurprised(self):
print "swap in eyes surprised model"
def enterOff(self, oldState, newState):
def enterOff(self):
taskMgr.remove(self.__unblinkName)
@ -127,7 +127,7 @@ class ToonEyes(FSM.FSM):
## >>> foo = SampleFSM.ClassicStyle('foo')
## >>> foo.request('Red')
## enterRed(self, 'Off', 'Red')
## 'Red'
## ('Red',)
## >>> foo.request('Yellow')
## Traceback (most recent call last):
## File "<stdin>", line 1, in ?
@ -141,7 +141,7 @@ class ToonEyes(FSM.FSM):
## >>> foo.request('Green')
## exitRed(self, 'Red', 'Green')
## enterGreen(self, 'Red', 'Green')
## 'Green'
## ('Green',)
## >>>
####
@ -151,27 +151,27 @@ class ToonEyes(FSM.FSM):
## >>> foo = SampleFSM.NewStyle('foo')
## >>> foo.request('Red')
## enterRed(self, 'Off', 'Red')
## 'Red'
## ('Red',)
## >>> foo.request('advance')
## filterRed(self, 'advance', ())
## exitRed(self, 'Red', 'Green')
## enterGreen(self, 'Red', 'Green')
## 'Green'
## ('Green',)
## >>> foo.request('advance')
## filterGreen(self, 'advance', ())
## exitGreen(self, 'Green', 'Yellow')
## enterYellow(self, 'Green', 'Yellow')
## 'Yellow'
## ('Yellow',)
## >>> foo.request('advance')
## filterYellow(self, 'advance', ())
## exitYellow(self, 'Yellow', 'Red')
## enterRed(self, 'Yellow', 'Red')
## 'Red'
## ('Red',)
## >>> foo.request('advance')
## filterRed(self, 'advance', ())
## exitRed(self, 'Red', 'Green')
## enterGreen(self, 'Red', 'Green')
## 'Green'
## ('Green',)
## >>>
####
@ -183,11 +183,11 @@ class ToonEyes(FSM.FSM):
## swap in eyes open model
## >>> eyes.request('blink')
## swap in eyes closed model
## 'Closed'
## ('Closed',)
## >>> run()
## swap in eyes open model
## >>> eyes.request('Surprised')
## swap in eyes surprised model
## 'Surprised'
## ('Surprised',)
## >>> eyes.request('blink')
## >>>