import time import threading import Queue as queue from phoneinterface import PhoneInterface, PhoneEvent from tuitest import FeApPinConfiguration, FeApUserInterface import configreader class DialConfiguration(object): def __init__(self, dial_timeout, shortcuts, blacklist): self.dial_timeout = dial_timeout self.shortcuts = shortcuts self.blacklist = blacklist class IllegalEventError(Exception): pass """ An abstract state, needed to define all possible events. """ class AbstractState(object): def on_registration_in_progress(self): raise IllegalEventError() def on_registration_successful(self): raise IllegalEventError() def on_registration_lost(self): raise IllegalEventError() def on_gabelschalter_up(self): raise IllegalEventError() def on_gabelschalter_down(self): raise IllegalEventError() def on_incoming_call(self): raise IllegalEventError() def on_call_ended(self): raise IllegalEventError() def on_call_accepted(self): raise IllegalEventError() def on_call_ringing(self): raise IllegalEventError() def on_invalid_number(self): raise IllegalEventError() def on_nummernschalter_active(self): raise IllegalEventError() def on_nummernschalter_input(self, num): raise IllegalEventError() def on_timeout(self): raise IllegalEventError() def leave(self): return None """ The basic state that every other state inherits from. It defines default behaviour for some events (overriden if necessary). """ class BaseState(AbstractState): def __init__(self, controller): self._controller = controller def on_registration_lost(self): return InitState def on_gabelschalter_up(self): return None def on_gabelschalter_down(self): return None def on_incoming_call(self): self._controller.phone.decline_call() def on_call_ended(self): # When an incoming call is declined, a call_ended event occurs, which # needs to be ignored, here. return None def on_nummernschalter_active(self): return None def on_nummernschalter_input(self, num): return None class InitState(BaseState): def __init__(self, controller): super(InitState, self).__init__(controller) self._controller.feap.set_schauzeichen(True) def on_registration_in_progress(self): print('registration in progress') return RegisteringState class RegisteringState(BaseState): def __init__(self, controller): super(RegisteringState, self).__init__(controller) def on_registration_successful(self): print('registration successful') self._controller.feap.set_schauzeichen(False) return IdleState class IdleState(BaseState): def on_incoming_call(self): print('incomfing call') caller = self._controller.phone.get_remote_number() print('From: %s' % caller) if caller in self._controller.dialconfig.blacklist: print('Caller on blacklist - declining') self._controller.phone.decline_call() return CallTerminatingState else: return SchelltState def on_gabelschalter_up(self): print('gabel up') return DialingState class SchelltState(BaseState): def __init__(self, controller): super(SchelltState, self).__init__(controller) self._controller.feap.set_wecker(True) def leave(self): self._controller.feap.set_wecker(False) def on_gabelschalter_up(self): return AcceptingState def on_call_ended(self): return IdleState class AcceptingState(BaseState): def __init__(self, controller): super(AcceptingState, self).__init__(controller) self._controller.phone.accept_call() def on_call_accepted(self): return CallRunningState class CallTerminatingState(BaseState): def __init__(self, controller): super(CallTerminatingState, self).__init__(controller) self._controller.phone.end_call() def on_call_ended(self): return IdleState def on_call_accepted(self): return None class ForgottenState(BaseState): def on_gabelschalter_down(self): return IdleState class BusyBeepingState(BaseState): def __init__(self, controller): super(BusyBeepingState, self).__init__(controller) self._controller.phone.play_busy_tone() def leave(self): self._controller.phone.stop_playing() def on_timeout(self): return ForgottenState def on_gabelschalter_down(self): return IdleState class CallRunningState(BaseState): def on_gabelschalter_down(self): return CallTerminatingState def on_call_ended(self): return BusyBeepingState class WecktState(BaseState): def __init__(self, controller): super(WecktState, self).__init__(controller) self._controller.phone.play_ringback_tone() def leave(self): self._controller.phone.stop_playing() def on_gabelschalter_down(self): return CallTerminatingState def on_call_ended(self): return BusyBeepingState def on_call_accepted(self): return CallRunningState class ConnectingState(BaseState): def on_gabelschalter_down(self): return CallTerminatingState def on_call_ringing(self): return WecktState def on_call_accepted(self): return CallRunningState def on_invalid_number(self): # TODO: play sound return BusyBeepingState def on_call_ended(self): return BusyBeepingState class DialingState(BaseState): def __init__(self, controller): super(DialingState, self).__init__(controller) self._controller.phone.play_dial_tone() self.__dial_tone = True self.__number = '' def leave(self): if self.__dial_tone: self._controller.phone.stop_playing() self._controller.abort_timeout() def on_gabelschalter_down(self): return IdleState def on_nummernschalter_active(self): self._controller.abort_timeout() if self.__dial_tone: self._controller.phone.stop_playing() def on_nummernschalter_input(self, num): print('nummernschalter: %d' % (num)) if self.__dial_tone: self._controller.phone.stop_playing() self.__number += str(num) self._controller.abort_timeout() self._controller.set_timeout(self._controller.dialconfig.dial_timeout * 1000) self._controller.phone.read_text(str(num)) def on_timeout(self): number = self.__number print 'Dialing number:', number if number in self._controller.dialconfig.shortcuts: number = self._controller.dialconfig.shortcuts[number] print 'shortcut resolved:', number self._controller.phone.call(number) return ConnectingState class StateMachineController(object): def __init__(self, phone, feap, dialconfig): self.__phone = phone self.__feap = feap self.__dialconfig = dialconfig self.__state = InitState(self) self.__timeout = None self.__running = True self.__evqueue = queue.Queue() self.__evthread = threading.Thread(target=self.__event_dispatcher) self.__evthread.start() def __event_dispatcher(self): while self.__running: (evname, evargs, evkwargs) = self.__evqueue.get() if not evname: return print('!!! event: %s' % (evname)) handler = getattr(self.__state, 'on_%s' % (evname)) try: newstate = handler(*evargs, **evkwargs) except IllegalEventError: print('illegal event occured!!!!!!!!!!!!!!!!!!!!', self.__state.__class__.__name__) if not newstate: continue self.__state.leave() self.abort_timeout() oldstate = self.__state.__class__ print('%s -> %s' % (oldstate.__name__, newstate.__name__)) self.__state = newstate(self) def queue_event(self, evname, *evargs, **evkwargs): if not hasattr(AbstractState, 'on_%s' % (evname)): raise ValueError('Illegal event name: %s' % (evname)) self.__evqueue.put((evname, evargs, evkwargs)) def set_timeout(self, timeout): self.__timeout = threading.Timer(timeout/1000, self.queue_event, args=['timeout']) self.__timeout.start() def abort_timeout(self): if self.__timeout: self.__timeout.cancel() self.__timeout = None @property def phone(self): return self.__phone @property def feap(self): return self.__feap @property def dialconfig(self): return self.__dialconfig def stop(self, hard=False): if hard: self.__running = False self.__evqueue.put((None, None, None)) c = None def gabelschalter_cb(state): global c if state == 1: c.queue_event('gabelschalter_up') else: c.queue_event('gabelschalter_down') def nummernschalter_active_cb(): global c c.queue_event('nummernschalter_active') def nummernschalter_done_cb(digit): global c c.queue_event('nummernschalter_input', digit) def phone_cb(event): if event == PhoneEvent.RegInProgress: c.queue_event('registration_in_progress') elif event == PhoneEvent.RegSuccessfull: c.queue_event('registration_successful') elif event == PhoneEvent.RegLost: c.queue_event('registration_lost') elif event == PhoneEvent.CallIncoming: c.queue_event('incoming_call') elif event == PhoneEvent.CallAccepted: c.queue_event('call_accepted') elif event == PhoneEvent.CallEnded: c.queue_event('call_ended') elif event == PhoneEvent.CallRinging: c.queue_event('call_ringing') elif event == PhoneEvent.CallBusy: c.queue_event('call_ended') elif event == PhoneEvent.CallInvalidNumber: c.queue_event('invalid_number') if __name__ == '__main__': cfg = configreader.ConfigurationReader() cfg.read('fetap.ini') phone = PhoneInterface(cfg.phoneconfig) feap = FeApUserInterface(cfg.pinconfig) c = StateMachineController(phone, feap, cfg.dialconfig) feap.add_gabelschalter_callback(gabelschalter_cb) feap.add_nummernschalter_active_callback(nummernschalter_active_cb) feap.add_nummernschalter_done_callback(nummernschalter_done_cb) phone.add_event_cb(phone_cb) phone.start() try: while True: time.sleep(1) ''' c.queue_event('gabelschalter_up') c.queue_event('nummernschalter_input', 4) c.queue_event('nummernschalter_input', 2) #c.queue_event('gabelschalter_down') #c.queue_event('call_accepted') c.queue_event('timeout') c.queue_event('call_ringing') #c.queue_event('gabelschalter_down') c.queue_event('call_accepted') c.queue_event('call_ended') c.queue_event('timeout') c.queue_event('gabelschalter_down') ''' except KeyboardInterrupt: phone.stop() feap.set_wecker(False) c.stop()