You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

371 lines
8.8 KiB

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_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.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 leave(self):
self._controller.get_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.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 leave(self):
self._controller.get_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 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 leave(self):
if self.__dial_tone:
self._controller.get_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.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):
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
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
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.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__':
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()