mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
*** empty log message ***
This commit is contained in:
parent
1b3acf63c1
commit
be0a8a38ec
185
direct/src/fsm/FourState.py
Executable file
185
direct/src/fsm/FourState.py
Executable file
@ -0,0 +1,185 @@
|
||||
|
||||
|
||||
import DirectNotifyGlobal
|
||||
#import DistributedObject
|
||||
import FSM
|
||||
import State
|
||||
import Task
|
||||
|
||||
|
||||
class FourState:
|
||||
"""
|
||||
Generic four state FSM base class.
|
||||
|
||||
This is a mix-in class that expects that your derived class
|
||||
is a DistributedObject.
|
||||
|
||||
Inherit from FourStateFSM and pass in your states. Two of
|
||||
the states should be oposites of each other and the other
|
||||
two should be the transition states between the first two.
|
||||
E.g.
|
||||
|
||||
+--------+
|
||||
-->| closed | --
|
||||
| +--------+ |
|
||||
| |
|
||||
| v
|
||||
+---------+ +---------+
|
||||
| closing |<----->| opening |
|
||||
+---------+ +---------+
|
||||
^ |
|
||||
| |
|
||||
| +------+ |
|
||||
----| open |<---
|
||||
+------+
|
||||
|
||||
There is a fifth off state, but that is an implementation
|
||||
detail (and that's why it's not called a five state FSM).
|
||||
|
||||
I found that this pattern repeated in several things I was
|
||||
working on, so this base class was created.
|
||||
"""
|
||||
if __debug__:
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'FourStateFSM')
|
||||
|
||||
def __init__(self, names, durations = [0, 1, None, 1, 1]):
|
||||
"""
|
||||
names is a list of state names
|
||||
|
||||
E.g.
|
||||
['off', 'opening', 'open', 'closing', 'closed', ]
|
||||
|
||||
e.g. 2:
|
||||
['off', 'locking', 'locked', 'unlocking', 'unlocked', ]
|
||||
|
||||
e.g. 3:
|
||||
['off', 'deactivating', 'deactive', 'activating', 'activated', ]
|
||||
|
||||
More Details
|
||||
|
||||
Here is a diagram showing the where the names from the list
|
||||
are used:
|
||||
|
||||
+---------+
|
||||
| 0 (off) |----> (any other state and vice versa).
|
||||
+---------+
|
||||
|
||||
+--------+
|
||||
-->| 4 (on) |---
|
||||
| +--------+ |
|
||||
| |
|
||||
| v
|
||||
+---------+ +---------+
|
||||
| 3 (off) |<----->| 1 (off) |
|
||||
+---------+ +---------+
|
||||
^ |
|
||||
| |
|
||||
| +---------+ |
|
||||
--| 2 (off) |<--
|
||||
+---------+
|
||||
|
||||
Each states also has an associated on or off value. The only
|
||||
state that is 'on' is state 4. So, the transition states
|
||||
between off and on (states 1 and 3) are also considered
|
||||
off (and so is state 2 which is oposite of 4 and therefore
|
||||
oposite of 'on').
|
||||
"""
|
||||
self.isOn = 0 # used in debugPrint()
|
||||
assert(self.debugPrint(
|
||||
"FourState(names=%s)"
|
||||
%(names)))
|
||||
self.doLaterTask = None
|
||||
self.names = names
|
||||
self.durations = durations
|
||||
self.states = {
|
||||
0: State.State(names[0],
|
||||
self.enterState0,
|
||||
self.exitState0,
|
||||
[names[1],
|
||||
names[2],
|
||||
names[3],
|
||||
names[4]]),
|
||||
1: State.State(names[1],
|
||||
self.enterState1,
|
||||
self.exitState1,
|
||||
[names[2], names[3]]),
|
||||
2: State.State(names[2],
|
||||
self.enterState2,
|
||||
self.exitState2,
|
||||
[names[3]]),
|
||||
3: State.State(names[3],
|
||||
self.enterState3,
|
||||
self.exitState3,
|
||||
[names[4], names[1]]),
|
||||
4: State.State(names[4],
|
||||
self.enterState4,
|
||||
self.exitState4,
|
||||
[names[1]]),
|
||||
}
|
||||
self.stateIndex = 0
|
||||
self.fsm = FSM.FSM('DistributedDoorEntity',
|
||||
self.states.values(),
|
||||
# Initial State
|
||||
names[0],
|
||||
# Final State
|
||||
names[0],
|
||||
)
|
||||
self.fsm.enterInitialState()
|
||||
|
||||
def getIsOn(self):
|
||||
assert(self.debugPrint("getIsOn() returning %s"%(self.isOn,)))
|
||||
return self.isOn
|
||||
|
||||
##### state 0 #####
|
||||
|
||||
def enterState0(self):
|
||||
assert(self.debugPrint("enter0()"))
|
||||
self.stateIndex = 0
|
||||
self.isOn = 0
|
||||
|
||||
def exitState0(self):
|
||||
assert(self.debugPrint("exit0()"))
|
||||
|
||||
##### state 1 #####
|
||||
|
||||
def enterState1(self):
|
||||
self.stateIndex = 1
|
||||
self.isOn = 0
|
||||
|
||||
def exitState1(self):
|
||||
assert(self.debugPrint("exitState1()"))
|
||||
|
||||
##### state 2 #####
|
||||
|
||||
def enterState2(self):
|
||||
self.stateIndex = 2
|
||||
self.isOn = 0
|
||||
|
||||
def exitState2(self):
|
||||
assert(self.debugPrint("exitState2()"))
|
||||
|
||||
##### state 3 #####
|
||||
|
||||
def enterState3(self):
|
||||
self.stateIndex = 2
|
||||
self.isOn = 0
|
||||
|
||||
def exitState3(self):
|
||||
assert(self.debugPrint("exitState3()"))
|
||||
|
||||
##### state 4 #####
|
||||
|
||||
def enterState4(self):
|
||||
self.stateIndex = 4
|
||||
self.isOn = 1
|
||||
|
||||
def exitState4(self):
|
||||
assert(self.debugPrint("exitState4()"))
|
||||
|
||||
if __debug__:
|
||||
def debugPrint(self, message):
|
||||
"""for debugging"""
|
||||
return self.notify.debug("%d (%d) %s"%(
|
||||
id(self), self.isOn, message))
|
||||
|
228
direct/src/fsm/FourStateAI.py
Executable file
228
direct/src/fsm/FourStateAI.py
Executable file
@ -0,0 +1,228 @@
|
||||
|
||||
|
||||
import DirectNotifyGlobal
|
||||
#import DistributedObjectAI
|
||||
import FSM
|
||||
import State
|
||||
import Task
|
||||
|
||||
|
||||
class FourStateAI:
|
||||
"""
|
||||
Generic four state FSM base class.
|
||||
|
||||
This is a mix-in class that expects that your derived class
|
||||
is a DistributedObjectAI.
|
||||
|
||||
Inherit from FourStateFSM and pass in your states. Two of
|
||||
the states should be oposites of each other and the other
|
||||
two should be the transition states between the first two.
|
||||
E.g.
|
||||
|
||||
+--------+
|
||||
-->| closed | --
|
||||
| +--------+ |
|
||||
| |
|
||||
| v
|
||||
+---------+ +---------+
|
||||
| closing |<----->| opening |
|
||||
+---------+ +---------+
|
||||
^ |
|
||||
| |
|
||||
| +------+ |
|
||||
----| open |<---
|
||||
+------+
|
||||
|
||||
There is a fifth off state, but that is an implementation
|
||||
detail (and that's why it's not called a five state FSM).
|
||||
|
||||
I found that this pattern repeated in several things I was
|
||||
working on, so this base class was created.
|
||||
"""
|
||||
if __debug__:
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||
'FourStateFSM')
|
||||
|
||||
def __init__(self, names, durations = [0, 1, None, 1, 1]):
|
||||
"""
|
||||
names is a list of state names
|
||||
E.g.
|
||||
['off', 'opening', 'open', 'closing', 'closed', ]
|
||||
|
||||
e.g. 2:
|
||||
['off', 'locking', 'locked', 'unlocking', 'unlocked', ]
|
||||
|
||||
e.g. 3:
|
||||
['off', 'deactivating', 'deactive', 'activating', 'activated', ]
|
||||
|
||||
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
|
||||
the next state. That does not happen for any duration
|
||||
values of None.
|
||||
|
||||
More Details
|
||||
|
||||
Here is a diagram showing the where the names from the list
|
||||
are used:
|
||||
|
||||
+---------+
|
||||
| 0 (off) |----> (any other state and vice versa).
|
||||
+---------+
|
||||
|
||||
+--------+
|
||||
-->| 4 (on) |---
|
||||
| +--------+ |
|
||||
| |
|
||||
| v
|
||||
+---------+ +---------+
|
||||
| 3 (off) |<----->| 1 (off) |
|
||||
+---------+ +---------+
|
||||
^ |
|
||||
| |
|
||||
| +---------+ |
|
||||
--| 2 (off) |<--
|
||||
+---------+
|
||||
|
||||
Each states also has an associated on or off value. The only
|
||||
state that is 'on' is state 4. So, the transition states
|
||||
between off and on (states 1 and 3) are also considered
|
||||
off (and so is state 2 which is oposite of state 4 and therefore
|
||||
oposite of 'on').
|
||||
"""
|
||||
self.isOn = 0 # used in debugPrint()
|
||||
assert(self.debugPrint(
|
||||
"FourStateAI(names=%s, durations=%s)"
|
||||
%(names, durations)))
|
||||
self.doLaterTask = None
|
||||
self.stateIndex = 0
|
||||
assert len(names) == 5
|
||||
assert len(names) == len(durations)
|
||||
self.names = names
|
||||
self.durations = durations
|
||||
self.states = {
|
||||
0: State.State(names[0],
|
||||
self.enterState0,
|
||||
self.exitState0,
|
||||
[names[1],
|
||||
names[2],
|
||||
names[3],
|
||||
names[4]]),
|
||||
1: State.State(names[1],
|
||||
self.enterState1,
|
||||
self.exitState1,
|
||||
[names[2], names[3]]),
|
||||
2: State.State(names[2],
|
||||
self.enterState2,
|
||||
self.exitState2,
|
||||
[names[3]]),
|
||||
3: State.State(names[3],
|
||||
self.enterState3,
|
||||
self.exitState3,
|
||||
[names[4], names[1]]),
|
||||
4: State.State(names[4],
|
||||
self.enterState4,
|
||||
self.exitState4,
|
||||
[names[1]]),
|
||||
}
|
||||
self.fsm = FSM.FSM('DistributedDoorEntity',
|
||||
self.states.values(),
|
||||
# Initial State
|
||||
names[0],
|
||||
# Final State
|
||||
names[0],
|
||||
)
|
||||
self.fsm.enterInitialState()
|
||||
|
||||
def getInitialState(self):
|
||||
return self.stateIndex
|
||||
|
||||
def getIsOn(self):
|
||||
assert(self.debugPrint("getIsOn() returning %s"%(self.isOn,)))
|
||||
return self.isOn
|
||||
|
||||
##### states #####
|
||||
|
||||
def switchToNextStateTask(self, task):
|
||||
assert(self.debugPrint("switchToState1Task()"))
|
||||
self.fsm.request(self.states[self.nextStateIndex])
|
||||
return Task.done
|
||||
|
||||
def distributeStateChange(self):
|
||||
"""
|
||||
This function is intentionaly simple so that derived classes
|
||||
may easily alter the network message.
|
||||
"""
|
||||
self.sendUpdate('setState', [self.stateIndex, globalClockDelta.getRealNetworkTime()])
|
||||
|
||||
def enterStateN(self, isOn, stateIndex, nextStateIndex):
|
||||
assert(self.debugPrint("enterStateN(stateIndex=%s, nextStateIndex=%s)"%(
|
||||
stateIndex, nextStateIndex)))
|
||||
self.stateIndex = stateIndex
|
||||
self.nextStateIndex = nextStateIndex
|
||||
self.isOn = isOn
|
||||
self.distributeStateChange()
|
||||
if self.durations[stateIndex] is not None:
|
||||
self.doLaterTask=taskMgr.doMethodLater(
|
||||
self.durations[stateIndex],
|
||||
self.switchToNextStateTask,
|
||||
self.uniqueName('enterStateN-timer'))
|
||||
|
||||
def exitStateN(self):
|
||||
if self.doLaterTask:
|
||||
taskMgr.remove(self.doLaterTask)
|
||||
self.doLaterTask=None
|
||||
|
||||
##### state 0 #####
|
||||
|
||||
def enterState0(self):
|
||||
assert(self.debugPrint("enter0()"))
|
||||
self.stateIndex = 0
|
||||
self.isOn = 0
|
||||
|
||||
def exitState0(self):
|
||||
assert(self.debugPrint("exit0()"))
|
||||
|
||||
##### state 1 #####
|
||||
|
||||
def enterState1(self):
|
||||
self.enterStateN(0, 1, 2)
|
||||
|
||||
def exitState1(self):
|
||||
assert(self.debugPrint("exitState1()"))
|
||||
self.exitStateN()
|
||||
|
||||
##### state 2 #####
|
||||
|
||||
def enterState2(self):
|
||||
self.enterStateN(0, 2, 3)
|
||||
|
||||
def exitState2(self):
|
||||
assert(self.debugPrint("exitState2()"))
|
||||
self.exitStateN()
|
||||
|
||||
##### state 3 #####
|
||||
|
||||
def enterState3(self):
|
||||
self.enterStateN(0, 3, 4)
|
||||
|
||||
def exitState3(self):
|
||||
assert(self.debugPrint("exitState3()"))
|
||||
self.exitStateN()
|
||||
|
||||
##### state 4 #####
|
||||
|
||||
def enterState4(self):
|
||||
self.enterStateN(1, 4, 1)
|
||||
|
||||
def exitState4(self):
|
||||
assert(self.debugPrint("exitState4()"))
|
||||
self.exitStateN()
|
||||
|
||||
if __debug__:
|
||||
def debugPrint(self, message):
|
||||
"""for debugging"""
|
||||
return self.notify.debug("%d (%d) %s"%(
|
||||
id(self), self.isOn, message))
|
||||
|
Loading…
x
Reference in New Issue
Block a user