Bläddra i källkod

initial revision, includes StateVar, FunctionCall and EnterExit

Darren Ranalli 17 år sedan
förälder
incheckning
ce3927480c
1 ändrade filer med 200 tillägg och 0 borttagningar
  1. 200 0
      direct/src/fsm/StatePush.py

+ 200 - 0
direct/src/fsm/StatePush.py

@@ -0,0 +1,200 @@
+class PushesStateChanges:
+    # base class for objects that broadcast state changes to a set of subscriber objects
+    def __init__(self, value):
+        self._value = value
+        # push state changes to these objects
+        self._subscribers = set()
+
+    def destroy(self):
+        if len(self._subscribers) != 0:
+            raise '%s object still has subscribers in destroy(): %s' % (
+                self.__class__.__name__, self._subscribers)
+        del self._subscribers
+        del self._value
+
+    def getState(self):
+        return self._value
+
+    def _addSubscription(self, subscriber):
+        self._subscribers.add(subscriber)
+
+    def _removeSubscription(self, subscriber):
+        self._subscribers.remove(subscriber)
+
+    def _handlePotentialStateChange(self, value):
+        oldValue = self._value
+        self._value = value
+        if oldValue != value:
+            self._handleStateChange()
+
+    def _handleStateChange(self):
+        # push this object's state to the subscribing objects
+        for subscriber in self._subscribers:
+            subscriber._recvStatePush(self)
+
+if __debug__:
+    psc = PushesStateChanges(0)
+    assert psc.getState() == 0
+    psc.destroy()
+    del psc
+
+class ReceivesStateChanges:
+    # base class for objects that subscribe to state changes from PushesStateChanges objects
+    def __init__(self, other):
+        self._other = None
+        self._subscribeTo(other)
+
+    def destroy(self):
+        self._unsubscribe()
+        del self._other
+
+    def _subscribeTo(self, other):
+        self._unsubscribe()
+        self._other = other
+        if self._other:
+            self._other._addSubscription(self)
+
+    def _unsubscribe(self):
+        if self._other:
+            self._other._removeSubscription(self)
+            self._other = None
+
+    def _recvStatePush(self, other):
+        pass
+
+if __debug__:
+    rsc = ReceivesStateChanges(None)
+    rsc.destroy()
+    del rsc
+
+class StateVar(PushesStateChanges):
+    # coder-friendly object that allows values to be set on it and pushes those values
+    # as state changes
+    def set(self, value):
+        PushesStateChanges._handlePotentialStateChange(self, value)
+
+    def get(self):
+        return PushesStateChanges.getState(self)
+
+if __debug__:
+    sv = StateVar(0)
+    assert sv.get() == 0
+    sv.set(1)
+    assert sv.get() == 1
+    sv.destroy()
+    del sv
+
+class StateChangeNode(PushesStateChanges, ReceivesStateChanges):
+    # base class that can be used to create a state-change notification chain
+    def __init__(self, other):
+        ReceivesStateChanges.__init__(self, other)
+        PushesStateChanges.__init__(self, other.getState())
+
+    def destroy(self):
+        PushesStateChanges.destroy(self)
+        ReceivesStateChanges.destroy(self)
+
+    def _recvStatePush(self, other):
+        # got a state push, apply new state to self
+        self._handlePotentialStateChange(other._value)
+
+if __debug__:
+    sv = StateVar(0)
+    assert sv.get() == 0
+    scn = StateChangeNode(sv)
+    assert scn.getState() == 0
+    sv.set(1)
+    assert sv.get() == 1
+    assert scn.getState() == 1
+    scn2 = StateChangeNode(scn)
+    assert scn2.getState() == 1
+    sv.set(2)
+    assert scn2.getState() == 2
+    scn3 = StateChangeNode(scn)
+    assert scn3.getState() == 2
+    sv.set(3)
+    assert scn2.getState() == 3
+    assert scn3.getState() == 3
+    scn3.destroy()
+    scn2.destroy()
+    scn.destroy()
+    sv.destroy()
+    del scn3
+    del scn2
+    del scn
+    del sv
+
+class FunctionCall(StateChangeNode):
+    # calls func with new state whenever state changes
+    def __init__(self, other, func):
+        self._func = func
+        StateChangeNode.__init__(self, other)
+
+    def destroy(self):
+        StateChangeNode.destroy(self)
+        del self._func
+
+    def _handleStateChange(self):
+        self._func(self._value)
+        StateChangeNode._handleStateChange(self)
+        
+if __debug__:
+    l = []
+    def handler(value, l=l):
+        l.append(value)
+    assert l == []
+    sv = StateVar(0)
+    fc = FunctionCall(sv, handler)
+    assert l == []
+    sv.set(1)
+    assert l == [1,]
+    sv.set(2)
+    assert l == [1,2,]
+    fc.destroy()
+    sv.destroy()
+    del fc
+    del sv
+
+class EnterExit(StateChangeNode):
+    # call enterFunc when our state becomes true, exitFunc when it becomes false
+    def __init__(self, other, enterFunc, exitFunc):
+        self._enterFunc = enterFunc
+        self._exitFunc = exitFunc
+        StateChangeNode.__init__(self, other)
+
+    def destroy(self):
+        StateChangeNode.destroy(self)
+        del self._exitFunc
+        del self._enterFunc
+
+    def _handlePotentialStateChange(self, value):
+        # convert the incoming state as a bool
+        StateChangeNode._handlePotentialStateChange(self, bool(value))
+
+    def _handleStateChange(self):
+        if self._value:
+            self._enterFunc()
+        else:
+            self._exitFunc()
+        StateChangeNode._handleStateChange(self)
+
+if __debug__:
+    l = []
+    def enter(l=l):
+        l.append(1)
+    def exit(l=l):
+        l.append(0)
+    sv = StateVar(0)
+    ee = EnterExit(sv, enter, exit)
+    sv.set(0)
+    assert l == []
+    sv.set(1)
+    assert l == [1,]
+    sv.set(2)
+    assert l == [1,]
+    sv.set(0)
+    assert l == [1,0,]
+    sv.set(True)
+    assert l == [1,0,1,]
+    sv.set(False)
+    assert l == [1,0,1,0,]