diff --git a/stateMachine.py b/stateMachine.py index 5d6f972..34c929f 100644 --- a/stateMachine.py +++ b/stateMachine.py @@ -1,9 +1,8 @@ import RPi.GPIO as GPIO -import time, logging, enum +import time, logging, enum, weakref from threading import Lock from functools import partial from collections import namedtuple -from enum import Enum, auto from auxilary import CountdownTimer, resetUSBDevice from config import stateFile @@ -25,37 +24,36 @@ class SIGNALS(enum.Enum): TRIGGER = enum.auto() class State: - def __init__(self, stateMachine, name, entryCallbacks=[], exitCallbacks=[], sound=None): - self.stateMachine = stateMachine + def __init__(self, sfx, name, entryCallbacks=[], exitCallbacks=[], sound=None): self.name = name self.entryCallbacks = entryCallbacks self.exitCallbacks = exitCallbacks + self._transTbl = {} - sfx = stateMachine.soundLib.soundEffects - - self._sound = sfx[name] if not sound and name in sfx else sound + #~ self._sound = sfx[name] if not sound and name in sfx else sound def entry(self): logger.info('entering ' + self.name) - if self._sound: - self._sound.play() + #~ if self._sound: + #~ self._sound.play() for c in self.entryCallbacks: c() def exit(self): logger.info('exiting ' + self.name) - if self._sound: - self._sound.stop() + #~ if self._sound: + #~ self._sound.stop() for c in self.exitCallbacks: c() def next(self, signal): if signal in SIGNALS: - s = (self, signal) - t = self.stateMachine.transitionTable - return self if s not in t else t[s] + return self if signal not in self._transTbl else self._transTbl[signal] else: raise Exception('Illegal signal') + + def addTransition(self, signal, state): + self._transTbl[signal] = weakref.ref(state) def __str__(self): return self.name @@ -115,29 +113,31 @@ class StateMachine: stateObjs = [ State( - self, + sfx, name = 'disarmed', entryCallbacks = [partial(self.LED.setBlink, False)] ), State( - self, + sfx, name = 'disarmedCountdown', - entryCallbacks = [blinkingLED, partial(startTimer, 30, sfx['disarmedCountdown'])], + #~ entryCallbacks = [blinkingLED, partial(startTimer, 30, sfx['disarmedCountdown'])], + entryCallbacks = [blinkingLED, partial(startTimer, 30)], exitCallbacks = [stopTimer] ), State( - self, + sfx, name = 'armed', entryCallbacks = [blinkingLED] ), State( - self, + sfx, name = 'armedCountdown', - entryCallbacks = [blinkingLED, partial(startTimer, 30, sfx['armedCountdown'])], + #~ entryCallbacks = [blinkingLED, partial(startTimer, 30, sfx['armedCountdown'])], + entryCallbacks = [blinkingLED, partial(startTimer, 30)], exitCallbacks = [stopTimer] ), State( - self, + sfx, name = 'triggered', entryCallbacks = [blinkingLED, intruderAlert] ) @@ -146,28 +146,28 @@ class StateMachine: for s in stateObjs: s.entryCallbacks.append(self.keypadListener.resetBuffer) - self.states = namedtuple('States', [s.name for s in stateObjs])(*stateObjs) + self.states = s = namedtuple('States', [s.name for s in stateObjs])(*stateObjs) - self.currentState = getattr(self.states, stateFile['state']) + s.disarmed.addTransition( SIGNALS.ARM, s.disarmedCountdown) + s.disarmed.addTransition( SIGNALS.INSTANT_ARM, s.armed) - self.transitionTable = { - (self.states.disarmed, SIGNALS.ARM): self.states.disarmedCountdown, - (self.states.disarmed, SIGNALS.INSTANT_ARM): self.states.armed, - - (self.states.disarmedCountdown, SIGNALS.DISARM): self.states.disarmed, - (self.states.disarmedCountdown, SIGNALS.TIMOUT): self.states.armed, - (self.states.disarmedCountdown, SIGNALS.INSTANT_ARM): self.states.armed, - - (self.states.armed, SIGNALS.DISARM): self.states.disarmed, - (self.states.armed, SIGNALS.TRIGGER): self.states.armedCountdown, - - (self.states.armedCountdown, SIGNALS.DISARM): self.states.disarmed, - (self.states.armedCountdown, SIGNALS.ARM): self.states.armed, - (self.states.armedCountdown, SIGNALS.TIMOUT): self.states.triggered, - - (self.states.triggered, SIGNALS.DISARM): self.states.disarmed, - (self.states.triggered, SIGNALS.ARM): self.states.armed, - } + s.disarmedCountdown.addTransition( SIGNALS.DISARM, s.disarmed) + s.disarmedCountdown.addTransition( SIGNALS.TIMOUT, s.armed) + s.disarmedCountdown.addTransition( SIGNALS.INSTANT_ARM, s.armed) + + s.armed.addTransition( SIGNALS.DISARM, s.disarmed) + s.armed.addTransition( SIGNALS.TRIGGER, s.armedCountdown) + + s.armedCountdown.addTransition( SIGNALS.DISARM, s.disarmed) + s.armedCountdown.addTransition( SIGNALS.TIMOUT, s.triggered) + s.armedCountdown.addTransition( SIGNALS.ARM, s.armed) + s.armedCountdown.addTransition( SIGNALS.INSTANT_ARM, s.armed) + + s.triggered.addTransition( SIGNALS.DISARM, s.disarmed) + s.triggered.addTransition( SIGNALS.ARM, s.armed) + s.triggered.addTransition( SIGNALS.INSTANT_ARM, s.armed) + + self.currentState = getattr(self.states, stateFile['state']) def start(self): resetUSBDevice('1-1', logger) @@ -204,7 +204,7 @@ class StateMachine: startWebInterface(self) self.currentState.entry() - + def selectState(self, signal): with self._lock: nextState = self.currentState.next(signal) @@ -214,7 +214,7 @@ class StateMachine: self.currentState.entry() stateFile['state'] = self.currentState.name - + def __del__(self): for i in ['LED', 'camera', 'fileDump', 'soundLib', 'secretListener', 'keypadListener']: try: