|
|
|
import time
|
|
|
|
import threading
|
|
|
|
import Queue as queue
|
|
|
|
from phoneinterface import PhoneInterface, PhoneEvent
|
|
|
|
from tuitest import FeApPinConfiguration, FeApUserInterface
|
|
|
|
|
|
|
|
class IllegalEventError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class AbstractState(object):
|
|
|
|
def __init__(self, controller):
|
|
|
|
self._controller = controller
|
|
|
|
|
|
|
|
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_input(self, num):
|
|
|
|
raise IllegalEventError()
|
|
|
|
|
|
|
|
def on_timeout(self):
|
|
|
|
raise IllegalEventError()
|
|
|
|
|
|
|
|
class InitState(AbstractState):
|
|
|
|
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(AbstractState):
|
|
|
|
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(AbstractState):
|
|
|
|
def on_incoming_call(self):
|
|
|
|
print('incomfing call')
|
|
|
|
return SchelltState
|
|
|
|
|
|
|
|
def on_gabelschalter_up(self):
|
|
|
|
print('gabel up')
|
|
|
|
return DialingState
|
|
|
|
|
|
|
|
def on_gabelschalter_down(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def on_nummernschalter_input(self, x):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class SchelltState(AbstractState):
|
|
|
|
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(AbstractState):
|
|
|
|
def __init__(self, controller):
|
|
|
|
super(AcceptingState, self).__init__(controller)
|
|
|
|
self._controller.get_phone().accept_call()
|
|
|
|
|
|
|
|
def on_call_accepted(self):
|
|
|
|
return CallRunningState
|
|
|
|
|
|
|
|
class CallTerminatingState(AbstractState):
|
|
|
|
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(AbstractState):
|
|
|
|
def on_gabelschalter_down(self):
|
|
|
|
return IdleState
|
|
|
|
|
|
|
|
class BusyBeepingState(AbstractState):
|
|
|
|
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(AbstractState):
|
|
|
|
def on_gabelschalter_down(self):
|
|
|
|
return CallTerminatingState
|
|
|
|
|
|
|
|
def on_call_ended(self):
|
|
|
|
return BusyBeepingState
|
|
|
|
|
|
|
|
class WecktState(AbstractState):
|
|
|
|
def on_gabelschalter_down(self):
|
|
|
|
return CallTerminatingState
|
|
|
|
|
|
|
|
def on_call_ended(self):
|
|
|
|
return BusyBeepingState
|
|
|
|
|
|
|
|
def on_call_accepted(self):
|
|
|
|
return CallRunningState
|
|
|
|
|
|
|
|
class ConnectingState(AbstractState):
|
|
|
|
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
|
|
|
|
|
|
|
|
class DialingState(AbstractState):
|
|
|
|
def __init__(self, controller):
|
|
|
|
super(DialingState, self).__init__(controller)
|
|
|
|
self._controller.get_phone().play_dial_tone()
|
|
|
|
self.__number = ''
|
|
|
|
|
|
|
|
def __on_leave(self):
|
|
|
|
self._controller.get_phone().stop_playing()
|
|
|
|
self._controller.abort_timeout()
|
|
|
|
|
|
|
|
def on_gabelschalter_down(self):
|
|
|
|
self.__on_leave()
|
|
|
|
return IdleState
|
|
|
|
|
|
|
|
def on_nummernschalter_input(self, num):
|
|
|
|
print('nummernschalter: %d' % (num))
|
|
|
|
self.__number += str(num)
|
|
|
|
self._controller.abort_timeout()
|
|
|
|
self._controller.set_timeout(7000)
|
|
|
|
|
|
|
|
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, lambda: self.queue_event('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_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.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_callback(nummernschalter_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()
|
|
|
|
|