open_toontown_panda3d/direct/src/fsm/FSM.py

242 lines
8.3 KiB
Python

"""Finite State Machine module: contains the FSM class"""
from DirectObject import *
import types
class FSM(DirectObject):
"""FSM class: Finite State Machine class"""
# create FSM DirectNotify category
notify = directNotify.newCategory("FSM")
# special methods
def __init__(self, name, states=[], initialStateName=None,
finalStateName=None):
"""__init__(self, string, State[], string, string)
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')
"""
self.setName(name)
self.setStates(states)
self.setInitialState(initialStateName)
self.setFinalState(finalStateName)
# 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
return None
# 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 = ("FSM " + self.getName() + ' in state "' +
currentState.getName() + '"')
else:
str = ("FSM " + self.getName() + ' not in any state')
return str
def enterInitialState(self, argList=[]):
self.__enter(self.__initialState, argList)
return None
# 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"""
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"""
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')
aState.enter(argList)
else:
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"""
self.__exitCurrent(exitArgList)
self.__enter(aState, enterArgList)
def request(self, aStateName, enterArgList=[], exitArgList=[]):
"""request(self, string)
Attempt transition from currentState to given one.
Return true is transition exists to given state,
false otherwise.
"""
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))
if (aStateName in self.__currentState.getTransitions()):
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:
if FSM.notify.getWarning():
FSM.notify.warning("[%s]: no transition exists from %s to %s" %
(self.__name,
self.__currentState.getName(),
aStateName))
return 0