import threading import queue 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 class AbstractState(object): """ An abstract state, needed to define all possible events. """ 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 class BaseState(AbstractState): """ The basic state that every other state inherits from. It defines default behaviour for some events (overriden if necessary). """ 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) print('Accepting call in AcceptingState') 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.publish_event = None self.publish_state = None self.publish_transition = None 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 if self.publish_event is not None: self.publish_event(self.state, evname, evargs, evkwargs) 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 = None self.state = newstate(self) if self.publish_transition is not None: self.publish_transition(oldstate, self.state, evname, evargs, evkwargs) 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 def stop(self, hard=False): if hard: self.__running = False self.__evqueue.put((None, None, None))