fixes; implement the Pins
[saartuer.git] / statemachine.py
index f6b45dd1cbbd8112a6e6db89fb4f92a8a9da0b23..522657637b9731d236b853889db6f9af5a362395 100644 (file)
@@ -30,7 +30,7 @@ ABOUTOPEN_NERVLIST = [(5, lambda : play_sound("flipswitch")), (5, lambda:play_so
 
 # Timeout we wait after the switch was switched to "Closed", until we assume nobody will open the door and we just lock it
 # ALso the time we wait after the door was opend, till we assume something went wrong and start nerving
 
 # Timeout we wait after the switch was switched to "Closed", until we assume nobody will open the door and we just lock it
 # ALso the time we wait after the door was opend, till we assume something went wrong and start nerving
-LEAVE_TIMEOUT = 20
+LEAVE_TIMEOUT = 4
 
 # play_sound constants
 SOUNDS_DIRECTORY = "/opt/tuer/sounds/"
 
 # play_sound constants
 SOUNDS_DIRECTORY = "/opt/tuer/sounds/"
@@ -79,6 +79,8 @@ class StateMachine():
                def handle_wakeup_event(self):
                        if self._nerver is not None:
                                return self._nerver.nerv()
                def handle_wakeup_event(self):
                        if self._nerver is not None:
                                return self._nerver.nerv()
+               def on_leave(self):
+                       pass
                def pins(self):
                        return self.state_machine.pins
                def old_pins(self):
                def pins(self):
                        return self.state_machine.pins
                def old_pins(self):
@@ -97,29 +99,45 @@ class StateMachine():
                        else:
                                raise Exception("Unknown command number: %d" % ev)
        
                        else:
                                raise Exception("Unknown command number: %d" % ev)
        
-       class AbstractLockedState(State):
+       class AbstractNonStartState(State):
+               def handle_pins_event(self):
+                       if self.pins().door_locked != (not self.pins().space_active):
+                               self.actor().act(Actor.CMD_RED_ON)
+                       else:
+                               self.actor().act(Actor.CMD_RED_OFF)
+                       return super().handle_pins_event()
+       
+       class AbstractLockedState(AbstractNonStartState):
                '''A state with invariant "The space is locked", switching to StateAboutToOpen when the space becomes unlocked'''
                '''A state with invariant "The space is locked", switching to StateAboutToOpen when the space becomes unlocked'''
+               def __init__(self, sm, nervlist = None):
+                       super().__init__(sm, nervlist)
+                       self.actor().act(Actor.CMD_GREEN_OFF)
                def handle_pins_event(self):
                        if not self.pins().door_locked:
                def handle_pins_event(self):
                        if not self.pins().door_locked:
-                               return StateAboutToOpen(self.state_machine)
-                       return super().handle_pins_event
+                               logger.info("Door unlocked, space is about to open")
+                               return StateMachine.StateAboutToOpen(self.state_machine)
+                       return super().handle_pins_event()
        
        
-       class AbstractUnlockedState(State):
+       class AbstractUnlockedState(AbstractNonStartState):
                '''A state with invariant "The space is unlocked", switching to StateZu when the space becomes locked'''
                '''A state with invariant "The space is unlocked", switching to StateZu when the space becomes locked'''
+               def __init__(self, sm, nervlist = None):
+                       super().__init__(sm, nervlist)
+                       self.actor().act(Actor.CMD_GREEN_ON)
                def handle_pins_event(self):
                        if self.pins().door_locked:
                def handle_pins_event(self):
                        if self.pins().door_locked:
+                               logger.info("Door locked, closing space")
                                if self.pins().space_active:
                                        # FIXME the same state can be reached by first locking the door, and then activating the space. What to do then?
                                if self.pins().space_active:
                                        # FIXME the same state can be reached by first locking the door, and then activating the space. What to do then?
-                                       logger.info("StateMachine: door manually locked, but space switch is still on - going to StateZu")
+                                       logger.warning("StateMachine: door manually locked, but space switch is still on - going to StateZu")
                                        play_sound("manual_lock")
                                        play_sound("manual_lock")
-                               return StateZu(self.state_machine)
-                       return super().handle_pins_event
+                               return StateMachine.StateZu(self.state_machine)
+                       return super().handle_pins_event()
        
        class StateStart(State):
                def handle_pins_event(self):
                        pins = self.pins()
                        if not (pins.door_locked is None or pins.door_closed is None or pins.space_active is None or pins.bell_ringing is None):
        
        class StateStart(State):
                def handle_pins_event(self):
                        pins = self.pins()
                        if not (pins.door_locked is None or pins.door_closed is None or pins.space_active is None or pins.bell_ringing is None):
-                               # All sensors got a value, switch to a proper state
+                               logger.info("All sensors got a value, switching to a proper state")
                                if pins.door_locked:
                                        return StateMachine.StateZu(self.state_machine)
                                else:
                                if pins.door_locked:
                                        return StateMachine.StateZu(self.state_machine)
                                else:
@@ -144,17 +162,13 @@ class StateMachine():
                        for cb in self.callbacks:
                                if cb is not None:
                                        cb(s)
                        for cb in self.callbacks:
                                if cb is not None:
                                        cb(s)
-               def handle_pins_event(self):
-                       # overriding superclass as we need to do notification (TODO can this be done better? on_leave?)
-                       if not self.pins().door_locked:
-                               self.notify(True)
-                               return StateMachine.StateAboutToOpen(self.state_machine)
+               def on_leave(self):
+                       self.notify(not self.pins().door_locked)
                def handle_cmd_unlock_event(self,callback):
                        # TODO: 202 notification also here if possible
                        self.callbacks.append(callback)
                def could_not_open(self):
                        logger.critical("StateMachine: Couldn't open door after %d tries. Going back to StateZu." % OPEN_REPEAT_NUMBER)
                def handle_cmd_unlock_event(self,callback):
                        # TODO: 202 notification also here if possible
                        self.callbacks.append(callback)
                def could_not_open(self):
                        logger.critical("StateMachine: Couldn't open door after %d tries. Going back to StateZu." % OPEN_REPEAT_NUMBER)
-                       self.notify(False)
                        return StateMachine.StateZu(self.state_machine)
        
        class AbstractStateWhereUnlockingIsRedundant(AbstractUnlockedState):
                        return StateMachine.StateZu(self.state_machine)
        
        class AbstractStateWhereUnlockingIsRedundant(AbstractUnlockedState):
@@ -169,12 +183,14 @@ class StateMachine():
                def handle_pins_event(self):
                        pins = self.pins()
                        if pins.space_active:
                def handle_pins_event(self):
                        pins = self.pins()
                        if pins.space_active:
+                               logger.info("Space activated, opening procedure completed")
                                return StateMachine.StateAuf(self.state_machine)
                        return super().handle_pins_event()
        
        class StateAuf(AbstractStateWhereUnlockingIsRedundant):
                def __init__(self,sm):
                                return StateMachine.StateAuf(self.state_machine)
                        return super().handle_pins_event()
        
        class StateAuf(AbstractStateWhereUnlockingIsRedundant):
                def __init__(self,sm):
-                       super().__init__(sm)
+                       nervlist = [(24*60*60, lambda: logger.critical("Space is now open for 24h. Is everything all right?"))]
+                       super().__init__(sm, nervlist)
                        self.last_buzzed = None
                def handle_pins_event(self):
                        pins = self.pins()
                        self.last_buzzed = None
                def handle_pins_event(self):
                        pins = self.pins()
@@ -217,6 +233,7 @@ class StateMachine():
                        if not self.pins().door_closed:
                                return StateMachine.StateLeaving(self.state_machine)
                        if self.pins().space_active:
                        if not self.pins().door_closed:
                                return StateMachine.StateLeaving(self.state_machine)
                        if self.pins().space_active:
+                               logger.info("Space re-activated, cancelling leaving procedure")
                                return StateMachine.StateAuf(self.state_machine)
                        return super().handle_pins_event()
        
                                return StateMachine.StateAuf(self.state_machine)
                        return super().handle_pins_event()
        
@@ -226,8 +243,10 @@ class StateMachine():
                        super().__init__(sm, nervlist)
                def handle_pins_event(self):
                        if self.pins().door_closed:
                        super().__init__(sm, nervlist)
                def handle_pins_event(self):
                        if self.pins().door_closed:
+                               logger.info("The space was left, locking the door")
                                return StateMachine.StateLocking(self.state_machine)
                        if self.pins().space_active:
                                return StateMachine.StateLocking(self.state_machine)
                        if self.pins().space_active:
+                               logger.info("Space re-activated, cancelling leaving procedure")
                                return StateMachine.StateAuf(self.state_machine)
                        return super().handle_pins_event()
        
                                return StateMachine.StateAuf(self.state_machine)
                        return super().handle_pins_event()
        
@@ -249,6 +268,8 @@ class StateMachine():
                newstate = self.current_state.handle_event(cmd,arg) # returns None or an instance of the new state
                self.old_pins = self.pins
                while newstate is not None:
                newstate = self.current_state.handle_event(cmd,arg) # returns None or an instance of the new state
                self.old_pins = self.pins
                while newstate is not None:
+                       assert isinstance(newstate, StateMachine.State), "I should get a state"
+                       self.current_state.on_leave()
                        logger.debug("StateMachine: Doing state transition %s -> %s" % (self.current_state.__class__.__name__, newstate.__class__.__name__))
                        self.current_state = newstate
                        newstate = self.current_state.handle_event(StateMachine.CMD_PINS, self.pins)
                        logger.debug("StateMachine: Doing state transition %s -> %s" % (self.current_state.__class__.__name__, newstate.__class__.__name__))
                        self.current_state = newstate
                        newstate = self.current_state.handle_event(StateMachine.CMD_PINS, self.pins)