import time import threading import Queue as queue from phoneinterface import PhoneInterface, PhoneEvent from tuitest import FeApPinConfiguration, FeApUserInterface 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_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() """ 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_gabelschalter_up(self): return None def on_gabelschalter_down(self): return None def on_incoming_call(self): self._controller.get_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.get_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.get_feap().set_schauzeichen(False) return IdleState class IdleState(BaseState): def on_incoming_call(self): print('incomfing call') 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.get_feap().set_wecker(True) def __on_leave(self): self._controller.get_feap().set_wecker(False) def on_gabelschalter_up(self): self.__on_leave() return AcceptingState def on_call_ended(self): self.__on_leave() return IdleState class AcceptingState(BaseState): def __init__(self, controller): super(AcceptingState, self).__init__(controller) self._controller.get_phone().accept_call() def on_call_accepted(self): return CallRunningState class CallTerminatingState(BaseState): def __init__(self, controller): super(CallTerminatingState, self).__init__(controller) self._controller.get_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.get_phone().play_busy_tone() def __on_leave(self): self._controller.get_phone().stop_playing() def on_timeout(self): return ForgottenState def on_gabelschalter_down(self): self.__on_leave() return IdleState class CallRunningState(BaseState): def on_gabelschalter_down(self): return CallTerminatingState def on_call_ended(self): return BusyBeepingState class WecktState(BaseState): 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.get_phone().play_dial_tone() self.__dial_tone = True self.__number = '' def __on_leave(self): if self.__dial_tone: self._controller.get_phone().stop_playing() self._controller.abort_timeout() def on_gabelschalter_down(self): self.__on_leave() return IdleState def on_nummernschalter_active(self): self._controller.abort_timeout() if self.__dial_tone: self._controller.get_phone().stop_playing() def on_nummernschalter_input(self, num): print('nummernschalter: %d' % (num)) if self.__dial_tone: self._controller.get_phone().stop_playing() self.__number += str(num) self._controller.abort_timeout() self._controller.set_timeout(5000) def on_timeout(self): self.__on_leave() print 'Dialing number:', self.__number self._controller.get_phone().call(self.__number) return ConnectingState class StateMachineController(object): def __init__(self, phone, feap): self.__state = InitState(self) self.__phone = phone self.__feap = feap 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 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 def get_phone(self): return phone def get_feap(self): return feap 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.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__': phone = PhoneInterface(None, '.linphonerc') pinconf = FeApPinConfiguration() feap = FeApUserInterface(pinconf) c = StateMachineController(phone, feap) 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()