mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 10:22:45 -04:00
rename FSM -> ClassicFSM, introduce new FSM code
This commit is contained in:
parent
193984d955
commit
13437d3ff5
368
direct/src/fsm/ClassicFSM.py
Normal file
368
direct/src/fsm/ClassicFSM.py
Normal file
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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):
|
||||
"""FSM class: Finite State Machine class"""
|
||||
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.
|
||||
|
||||
# create FSM DirectNotify category
|
||||
notify = directNotify.newCategory("FSM")
|
||||
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.)
|
||||
|
||||
# special methods
|
||||
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.:
|
||||
|
||||
# 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 enterRed(self, oldState, newState):
|
||||
... do stuff ...
|
||||
|
||||
def __init__(self, name, states=[], initialStateName=None,
|
||||
finalStateName=None, onUndefTransition=DISALLOW_VERBOSE):
|
||||
"""__init__(self, string, State[], string, string, int)
|
||||
def exitRed(self, oldState, newState):
|
||||
... cleanup stuff ...
|
||||
|
||||
FSM constructor: takes name, list of states, initial state and
|
||||
final state as:
|
||||
def enterYellow(self, oldState, newState):
|
||||
... do stuff ...
|
||||
|
||||
fsm = FSM.FSM('stopLight',
|
||||
[ State.State('red', enterRed, exitRed, ['green']),
|
||||
State.State('yellow', enterYellow, exitYellow, ['red']),
|
||||
State.State('green', enterGreen, exitGreen, ['yellow']) ],
|
||||
'red',
|
||||
'red')
|
||||
def exitYellow(self, oldState, newState):
|
||||
... cleanup stuff ...
|
||||
|
||||
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
|
||||
def enterGreen(self, oldState, newState):
|
||||
... do stuff ...
|
||||
|
||||
'onUndefTransition' flag determines behavior when undefined
|
||||
transition is requested; see flag definitions above
|
||||
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".)
|
||||
|
||||
self.setName(name)
|
||||
self.setStates(states)
|
||||
self.setInitialState(initialStateName)
|
||||
self.setFinalState(finalStateName)
|
||||
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.
|
||||
|
||||
self.onUndefTransition = onUndefTransition
|
||||
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:
|
||||
|
||||
# Flag to see if we are inspecting
|
||||
self.inspecting = 0
|
||||
def filterRed(self, request, args):
|
||||
if request in ['Green']:
|
||||
return request
|
||||
return None
|
||||
|
||||
# We do not enter the initial state to separate
|
||||
# construction from activation
|
||||
self.__currentState = None
|
||||
def filterYellow(self, request, args):
|
||||
if request in ['Red']:
|
||||
return request
|
||||
return 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 filterGreen(self, request, args):
|
||||
if request in ['Yellow']:
|
||||
return request
|
||||
return None
|
||||
|
||||
# I know this isn't how __repr__ is supposed to be used, but it
|
||||
# is nice and convenient.
|
||||
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)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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],
|
||||
|
@ -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
direct/src/fsm/SampleFSM.py
Normal file
193
direct/src/fsm/SampleFSM.py
Normal file
@ -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')
|
||||
## >>>
|
@ -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)
|
||||
|
@ -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')
|
||||
|
@ -12,7 +12,7 @@ from ClockDelta import *
|
||||
|
||||
import ToontownGlobals
|
||||
import DirectNotifyGlobal
|
||||
import FSM
|
||||
import ClassicFSM
|
||||
#import DistributedInteractiveEntity
|
||||
import DelayDelete
|
||||
import Localizer
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -21,7 +21,7 @@ import math
|
||||
import sys
|
||||
import Loader
|
||||
import time
|
||||
import FSM
|
||||
import ClassicFSM
|
||||
import State
|
||||
import DirectObject
|
||||
import SfxPlayer
|
||||
|
@ -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
|
||||
|
||||
|
@ -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(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user