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.
407 lines
9.9 KiB
407 lines
9.9 KiB
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() |
|
|
|
|