Major surgery mostly complete
authorMark Tearle <[email protected]>
Sat, 9 Apr 2005 14:17:30 +0000 (14:17 +0000)
committerMark Tearle <[email protected]>
Sat, 9 Apr 2005 14:17:30 +0000 (14:17 +0000)
Main loop that was work of the devil replaced with a state table
Code now broken out into smaller chunks ... that should be extensible.

New lights in UCC burn ... UCC hackers not used to bright light!

Still needs a bit more TLC before being put into production.

sql-edition/servers/VendServer.py

index c1ef276..2adc4e1 100755 (executable)
@@ -44,6 +44,14 @@ SWITCH = 2
 KEY = 3
 TICK = 4
 
+
+STATE_IDLE = 1
+STATE_DOOR_OPENING = 2
+STATE_DOOR_CLOSING = 3
+STATE_GETTING_UID = 4
+STATE_GETTING_PIN = 5
+STATE_GET_SELECTION = 6
+
 class DispenseDatabaseException(Exception): pass
 
 class DispenseDatabase:
@@ -141,21 +149,6 @@ def verify_user_pin(uid, pin):
                logging.info('refused pin for uid %d'%(uid))
                return None
 
-def door_open_mode(v):
-       logging.warning("Entering open door mode")
-       v.display("-FEED  ME-")
-       while True:
-               e = v.next_event()
-               if e == None: break
-               (event, params) = e
-               if event == TICK: break
-
-               if event == DOOR:
-                       if params == 1: # door closed
-                               logging.warning('Leaving open door mode')
-                               v.display("-YUM YUM!-")
-                               sleep(1)
-                               return
 
 def cookie(v):
        seed(time())
@@ -214,12 +207,12 @@ def setup_idlers(v):
                StringIdler(v),
                TrainIdler(v),
                ]
-    disabled = [
+       disabled = [
                ]
        idler = choose_idler()
 
 def choose_idler():
-       global idler
+       global idlers, idler
        iiindex = 0
 
        if idler:
@@ -243,21 +236,22 @@ def idle_step():
 
 class VendState:
        def __init__(self,v):
+               self.state_table = {}
+               self.state = STATE_IDLE
+               self.counter = 0
+
                self.mk = MessageKeeper(v)
                self.cur_user = ''
                self.cur_pin = ''
+               self.username = ''
                self.cur_selection = ''
                self.time_to_autologout = None
+
                self.time_to_idle = None
+
                self.last_timeout_refresh = None
 
 
-def handle_door_event(event, params, v, vstatus):
-       if params == 0:
-               door_open_mode(v);
-               vstatus.cur_user = ''
-               vstatus.cur_pin = ''
-               vstatus.mk.set_message(GREETING)
 
 def handle_tick_event(event, params, v, vstatus):
        # don't care right now.
@@ -267,40 +261,146 @@ def handle_switch_event(event, params, v, vstatus):
        # don't care right now.
        pass
 
-def handle_key_event(event, params, v, vstatus):
+
+def do_nothing(state, event, params, v, vstatus):
+       print "doing nothing (s,e,p)", state, " ", event, " ", params
+       pass
+
+def handle_getting_uid_idle(state, event, params, v, vstatus):
+       # don't care right now.
+       pass
+
+def handle_getting_pin_idle(state, event, params, v, vstatus):
+       # don't care right now.
+       pass
+
+def handle_get_selection_idle(state, event, params, v, vstatus):
+       # don't care right now.
+       ###
+       ### State logging out ..
+       if vstatus.time_to_autologout != None:
+               time_left = vstatus.time_to_autologout - time()
+               if time_left < 6 and (vstatus.last_timeout_refresh is None or vstatus.last_timeout_refresh > time_left):
+                       vstatus.mk.set_message('LOGOUT: '+str(int(time_left)))
+                       vstatus.last_timeout_refresh = int(time_left)
+                       vstatus.cur_selection = ''
+
+       if vstatus.time_to_autologout != None and vstatus.time_to_autologout - time() <= 0:
+               vstatus.time_to_autologout = None
+               vstatus.cur_user = ''
+               vstatus.cur_pin = ''
+               vstatus.cur_selection = ''
+                       
+               idle_in(vstatus,2)
+               vstatus.state = STATE_IDLE
+
+               vstatus.mk.set_message(GREETING)
+
+       ### State fully logged out ... reset variables
+       if vstatus.time_to_autologout and not vstatus.mk.done(): 
+               vstatus.time_to_autologout = None
+       if vstatus.cur_user == '' and vstatus.time_to_autologout: 
+               vstatus.time_to_autologout = None
+       
+       ### State logged in
+       if len(vstatus.cur_pin) == PIN_LENGTH and vstatus.mk.done() and vstatus.time_to_autologout == None:
+               # start autologout
+               vstatus.time_to_autologout = time() + 15
+               vstatus.last_timeout_refresh = None
+
+       ### State logged out ... after normal logout??
+       # perhaps when logged in?
+       if vstatus.time_to_idle is not None and vstatus.cur_user != '': 
+               vstatus.time_to_idle = None
+
+
+       ## FIXME - this may need to be elsewhere.....
+       # need to check
+       vstatus.mk.update_display()
+
+
+
+def handle_get_selection_key(state, event, params, v, vstatus):
        key = params
-       # complicated key handling here:
-       if len(vstatus.cur_user) < 5:
+       if len(vstatus.cur_selection) == 0:
                if key == 11:
+                       vstatus.cur_pin = ''
                        vstatus.cur_user = ''
-                       vstatus.mk.set_message(GREETING)
+                       vstatus.cur_selection = ''
+                       
+                       idle_in(vstatus,2)
+                       vstatus.state = STATE_IDLE
+
+                       vstatus.mk.set_messages(
+                               [(center('BYE!'), False, 1.5),
+                                (GREETING, False, None)])
                        return
-               vstatus.cur_user += chr(key + ord('0'))
-               vstatus.mk.set_message('UID: '+vstatus.cur_user)
-               if len(vstatus.cur_user) == 5:
-                       uid = int(vstatus.cur_user)
-                       if not has_good_pin(uid):
-                               logging.info('user '+vstatus.cur_user+' has a bad PIN')
-                               #mk.set_messages(
-                                       #[(center('INVALID'), False, 0.7),
-                                        #(center('PIN'), False, 0.7),
-                                        #(center('SETUP'), False, 1.0),
-                                        #(GREETING, False, None)])
-                               vstatus.mk.set_messages(
-                                       [(' '*10+'INVALID PIN SETUP'+' '*10, False, 3),
-                                        (GREETING, False, None)])
-                               vstatus.cur_user = ''
-                               vstatus.cur_pin = ''
-                               return
-                       vstatus.cur_pin = ''
-                       vstatus.mk.set_message('PIN: ')
-                       logging.info('need pin for user %s'%vstatus.cur_user)
+               vstatus.cur_selection += chr(key + ord('0'))
+               vstatus.mk.set_message('SELECT: '+vstatus.cur_selection)
+               vstatus.time_to_autologout = None
+       elif len(vstatus.cur_selection) == 1:
+               if key == 11:
+                       vstatus.cur_selection = ''
+                       vstatus.time_to_autologout = None
+                       scroll_options(vstatus.username, vstatus.mk)
                        return
-       elif len(vstatus.cur_pin) < PIN_LENGTH:
+               else:
+                       vstatus.cur_selection += chr(key + ord('0'))
+                       make_selection(v,vstatus)
+                       vstatus.cur_selection = ''
+                       vstatus.time_to_autologout = time() + 8
+                       vstatus.last_timeout_refresh = None
+
+def make_selection(v, vstatus):
+       # should use sudo here
+       if vstatus.cur_selection == '55':
+               vstatus.mk.set_message('OPENSESAME')
+               logging.info('dispensing a door for %s'%vstatus.username)
+               if geteuid() == 0:
+                       ret = os.system('su - "%s" -c "dispense door"'%vstatus.username)
+               else:
+                       ret = os.system('dispense door')
+               if ret == 0:
+                       logging.info('door opened')
+                       vstatus.mk.set_message(center('DOOR OPEN'))
+               else:
+                       logging.warning('user %s tried to dispense a bad door'%vstatus.username)
+                       vstatus.mk.set_message(center('BAD DOOR'))
+               sleep(1)
+       elif vstatus.cur_selection == '91':
+               cookie(v)
+       elif vstatus.cur_selection == '99':
+               scroll_options(vstatus.username, vstatus.mk)
+               vstatus.cur_selection = ''
+               return
+       elif vstatus.cur_selection[1] == '8':
+               v.display('GOT COKE?')
+               if ((os.system('su - "%s" -c "dispense %s"'%(vstatus.username, vstatus.cur_selection[0])) >> 8) != 0):
+                       v.display('SEEMS NOT')
+               else:
+                       v.display('GOT COKE!')
+       else:
+               v.display(vstatus.cur_selection+' - $1.00')
+               if ((os.system('su - "%s" -c "dispense snack"'%(vstatus.username)) >> 8) == 0):
+                       v.vend(vstatus.cur_selection)
+                       v.display('THANK YOU')
+               else:
+                       v.display('NO MONEY?')
+       sleep(1)
+
+
+def handle_getting_pin_key(state, event, params, v, vstatus):
+       #print "handle_getting_pin_key (s,e,p)", state, " ", event, " ", params
+       key = params
+       if len(vstatus.cur_pin) < PIN_LENGTH:
                if key == 11:
                        if vstatus.cur_pin == '':
                                vstatus.cur_user = ''
                                vstatus.mk.set_message(GREETING)
+                       
+                               idle_in(vstatus,5)
+                               vstatus.state = STATE_IDLE
+
                                return
                        vstatus.cur_pin = ''
                        vstatus.mk.set_message('PIN: ')
@@ -308,11 +408,12 @@ def handle_key_event(event, params, v, vstatus):
                vstatus.cur_pin += chr(key + ord('0'))
                vstatus.mk.set_message('PIN: '+'X'*len(vstatus.cur_pin))
                if len(vstatus.cur_pin) == PIN_LENGTH:
-                       username = verify_user_pin(int(vstatus.cur_user), int(vstatus.cur_pin))
-                       if username:
+                       vstatus.username = verify_user_pin(int(vstatus.cur_user), int(vstatus.cur_pin))
+                       if vstatus.username:
                                v.beep(0, False)
                                vstatus.cur_selection = ''
-                               scroll_options(username, vstatus.mk, True)
+                               vstatus.state = STATE_GET_SELECTION
+                               scroll_options(vstatus.username, vstatus.mk, True)
                                return
                        else:
                                v.beep(40, False)
@@ -322,71 +423,154 @@ def handle_key_event(event, params, v, vstatus):
                                         (GREETING, False, None)])
                                vstatus.cur_user = ''
                                vstatus.cur_pin = ''
+                       
+                               idle_in(vstatus,5)
+                               vstatus.state = STATE_IDLE
+
                                return
-       elif len(vstatus.cur_selection) == 0:
+
+
+def handle_getting_uid_key(state, event, params, v, vstatus):
+       #print "handle_getting_uid_key (s,e,p)", state, " ", event, " ", params
+       key = params
+       # complicated key handling here:
+       if len(vstatus.cur_user) < 5:
                if key == 11:
-                       vstatus.cur_pin = ''
                        vstatus.cur_user = ''
-                       vstatus.cur_selection = ''
+                       vstatus.mk.set_message(GREETING)
+
+                       idle_in(vstatus,5)
+                       vstatus.state = STATE_IDLE
+                       return
+
+               vstatus.cur_user += chr(key + ord('0'))
+               vstatus.mk.set_message('UID: '+vstatus.cur_user)
+
+       if len(vstatus.cur_user) == 5:
+               uid = int(vstatus.cur_user)
+               if not has_good_pin(uid):
+                       logging.info('user '+vstatus.cur_user+' has a bad PIN')
                        vstatus.mk.set_messages(
-                               [(center('BYE!'), False, 1.5),
+                               [(' '*10+'INVALID PIN SETUP'+' '*10, False, 3),
                                 (GREETING, False, None)])
+                       vstatus.cur_user = ''
+                       vstatus.cur_pin = ''
+                       
+                       idle_in(vstatus,5)
+                       vstatus.state = STATE_IDLE
+
                        return
-               vstatus.cur_selection += chr(key + ord('0'))
-               vstatus.mk.set_message('SELECT: '+vstatus.cur_selection)
-               vstatus.time_to_autologout = None
-       elif len(vstatus.cur_selection) == 1:
-               if key == 11:
-                       vstatus.cur_selection = ''
-                       vstatus.time_to_autologout = None
-                       scroll_options(username, vstatus.mk)
-                       return
-               else:
-                       vstatus.cur_selection += chr(key + ord('0'))
-                       #make_selection(cur_selection)
-                       # XXX this should move somewhere else:
-                       if vstatus.cur_selection == '55':
-                               vstatus.mk.set_message('OPENSESAME')
-                               logging.info('dispensing a door for %s'%username)
-                               if geteuid() == 0:
-                                       ret = os.system('su - "%s" -c "dispense door"'%username)
-                               else:
-                                       ret = os.system('dispense door')
-                               if ret == 0:
-                                       logging.info('door opened')
-                                       vstatus.mk.set_message(center('DOOR OPEN'))
-                               else:
-                                       logging.warning('user %s tried to dispense a bad door'%username)
-                                       vstatus.mk.set_message(center('BAD DOOR'))
-                               sleep(1)
-                       elif vstatus.cur_selection == '91':
-                               cookie(v)
-                       elif vstatus.cur_selection == '99':
-                               scroll_options(username, vstatus.mk)
-                               vstatus.cur_selection = ''
-                               return
-                       elif vstatus.cur_selection[1] == '8':
-                               v.display('GOT COKE?')
-                               if ((os.system('su - "%s" -c "dispense %s"'%(username, vstatus.cur_selection[0])) >> 8) != 0):
-                                       v.display('SEEMS NOT')
-                               else:
-                                       v.display('GOT COKE!')
-                       else:
-                               v.display(vstatus.cur_selection+' - $1.00')
-                               if ((os.system('su - "%s" -c "dispense snack"'%(username)) >> 8) == 0):
-                                       v.vend(vstatus.cur_selection)
-                                       v.display('THANK YOU')
-                               else:
-                                       v.display('NO MONEY?')
-                       sleep(1)
-                       vstatus.cur_selection = ''
-                       vstatus.time_to_autologout = time() + 8
-                       vstatus.last_timeout_refresh = None
 
 
+               vstatus.cur_pin = ''
+               vstatus.mk.set_message('PIN: ')
+               logging.info('need pin for user %s'%vstatus.cur_user)
+               vstatus.state = STATE_GETTING_PIN
+               return
+
+
+def handle_idle_key(state, event, params, v, vstatus):
+       #print "handle_idle_key (s,e,p)", state, " ", event, " ", params
+
+       key = params
+
+       if key == 11:
+               vstatus.cur_user = ''
+               vstatus.mk.set_message(GREETING)
+               idle_in(vstatus,5)
+               choose_idler()
+               return
+       
+       vstatus.state = STATE_GETTING_UID
+       run_handler(event, key, v, vstatus)
+
+
+def handle_idle_tick(state, event, params, v, vstatus):
+       ### State logged out ... initiate idler in 5  (first start?)
+       if vstatus.time_to_idle == None and vstatus.cur_user == '':
+               vstatus.time_to_idle = time() + 5
+               choose_idler()
+
+       ### State idling
+
+       if vstatus.time_to_idle is not None and time() > vstatus.time_to_idle: 
+               idle_step()
+
+       if vstatus.time_to_idle is not None and time() > vstatus.time_to_idle + 30:
+               vstatus.time_to_idle = time()
+               choose_idler()
+       
+       ###
+
+       vstatus.mk.update_display()
+
+
+def handle_door_idle(state, event, params, v, vstatus):
+       # don't care right now.
+       pass
+
+def handle_door_event(state, event, params, v, vstatus):
+       vstatus.time_to_idle = None
+
+       if params == 1:  #door open
+               vstatus.state = STATE_DOOR_OPENING
+               logging.warning("Entering open door mode")
+               v.display("-FEED  ME-")
+               #door_open_mode(v);
+               vstatus.cur_user = ''
+               vstatus.cur_pin = ''
+       elif params == 0:  #door closed
+               vstatus.state = STATE_DOOR_CLOSING
+               idle_in(vstatus, 5)
+
+               logging.warning('Leaving open door mode')
+               v.display("-YUM YUM!-")
+
+def idle_in(vstatus,seconds):
+       vstatus.time_to_idle = time() + seconds
+
+def return_to_idle(state,event,params,v,vstatus):
+       if vstatus.time_to_idle is not None and time() > vstatus.time_to_idle: 
+               vstatus.mk.set_message(GREETING)
+               vstatus.state = STATE_IDLE
+               return
+       if not vstatus.time_to_idle:
+               vstatus.mk.set_message(GREETING)
+               vstatus.state = STATE_IDLE
+               return
+
+def create_state_table(vstatus):
+       vstatus.state_table[(STATE_IDLE,TICK,1)] = handle_idle_tick
+       vstatus.state_table[(STATE_IDLE,KEY,1)] = handle_idle_key
+       vstatus.state_table[(STATE_IDLE,DOOR,1)] = handle_door_event
+
+       vstatus.state_table[(STATE_DOOR_OPENING,TICK,1)] = handle_door_idle
+       vstatus.state_table[(STATE_DOOR_OPENING,DOOR,1)] = handle_door_event
+       vstatus.state_table[(STATE_DOOR_OPENING,KEY,1)] = do_nothing
+
+       vstatus.state_table[(STATE_DOOR_CLOSING,TICK,1)] = return_to_idle
+       vstatus.state_table[(STATE_DOOR_CLOSING,DOOR,1)] = handle_door_event
+       vstatus.state_table[(STATE_DOOR_CLOSING,KEY,1)] = do_nothing
+
+       vstatus.state_table[(STATE_GETTING_UID,TICK,1)] = handle_getting_uid_idle
+       vstatus.state_table[(STATE_GETTING_UID,DOOR,1)] = do_nothing
+       vstatus.state_table[(STATE_GETTING_UID,KEY,1)] = handle_getting_uid_key
+
+       vstatus.state_table[(STATE_GETTING_PIN,TICK,1)] = handle_getting_pin_idle
+       vstatus.state_table[(STATE_GETTING_PIN,DOOR,1)] = do_nothing
+       vstatus.state_table[(STATE_GETTING_PIN,KEY,1)] = handle_getting_pin_key
+
+       vstatus.state_table[(STATE_GET_SELECTION,TICK,1)] = handle_get_selection_idle
+       vstatus.state_table[(STATE_GET_SELECTION,DOOR,1)] = do_nothing
+       vstatus.state_table[(STATE_GET_SELECTION,KEY,1)] = handle_get_selection_key
+
+def get_state_table_handler(vstatus, state, event, counter):
+       return vstatus.state_table[(state,event,counter)]
+
 def run_forever(rfh, wfh, options, cf):
        v = VendingMachine(rfh, wfh)
        vstatus = VendState(v)
+       create_state_table(vstatus)
 
        logging.debug('PING is ' + str(v.ping()))
 
@@ -394,9 +578,12 @@ def run_forever(rfh, wfh, options, cf):
 
        vstatus.mk.set_message(GREETING)
        setup_idlers(v)
+       choose_idler()
+       vstatus.mk.set_message("Booted")
 
 
-       # This main loop is hideous and the work of the devil - mtearle
+       # This main loop was hideous and the work of the devil.
+       # This has now been fixed (mostly) - mtearle
        #
        #
        # notes for later surgery
@@ -404,7 +591,7 @@ def run_forever(rfh, wfh, options, cf):
        #        V
        #   d[      ] = (method)
        #
-       #  return state
+       # ( return state - not currently implemented )
 
        while True:
                if USE_DB:
@@ -413,63 +600,20 @@ def run_forever(rfh, wfh, options, cf):
                        except DispenseDatabaseException, e:
                                logging.error('Database error: '+str(e))
 
-               if vstatus.time_to_autologout != None:
-                       time_left = vstatus.time_to_autologout - time()
-                       if time_left < 6 and (vstatus.last_timeout_refresh is None or vstatus.last_timeout_refresh > time_left):
-                               vstatus.mk.set_message('LOGOUT: '+str(int(time_left)))
-                               vstatus.last_timeout_refresh = int(time_left)
-                               vstatus.cur_selection = ''
-
-               if vstatus.time_to_autologout != None and vstatus.time_to_autologout - time() <= 0:
-                       vstatus.time_to_autologout = None
-                       vstatus.cur_user = ''
-                       vstatus.cur_pin = ''
-                       vstatus.cur_selection = ''
-                       vstatus.mk.set_message(GREETING)
-
-               if vstatus.time_to_autologout and not vstatus.mk.done(): 
-                       vstatus.time_to_autologout = None
-               if vstatus.cur_user == '' and vstatus.time_to_autologout: 
-                       vstatus.time_to_autologout = None
-               if len(vstatus.cur_pin) == PIN_LENGTH and vstatus.mk.done() and vstatus.time_to_autologout == None:
-                       # start autologout
-                       vstatus.time_to_autologout = time() + 15
-                       vstatus.last_timeout_refresh = None
-
-               if vstatus.time_to_idle == None and vstatus.cur_user == '':
-                       vstatus.time_to_idle = time() + 5
-                       choose_idler()
-               if vstatus.time_to_idle is not None and vstatus.cur_user != '': 
-                       vstatus.time_to_idle = None
-
-               if vstatus.time_to_idle is not None and time() > vstatus.time_to_idle: 
-                       idle_step()
-
-               if vstatus.time_to_idle is not None and time() > vstatus.time_to_idle + 30:
-                       vstatus.time_to_idle = time()
-                       choose_idler()
-
-               vstatus.mk.update_display()
 
                e = v.next_event(0)
                (event, params) = e
 
-               if event == TICK:
-                       e = v.next_event(0.05)
-                       (event, params) = e
+               vstatus.counter=1
+               run_handler(event, params, v, vstatus)
 
-                       if event == TICK:
-                               handle_tick_event(event, params, v, vstatus)
-                               continue
-               vstatus.time_to_idle = None
-               logging.debug('Got event: ' + repr(e))
-
-               if event == DOOR:
-                       handle_door_event(event, params, v, vstatus)
-               elif event == SWITCH:
-                       handle_switch_event(event, params, v, vstatus)
-               elif event == KEY:
-                       handle_key_event(event, params, v, vstatus)
+#              logging.debug('Got event: ' + repr(e))
+
+
+def run_handler(event, params, v, vstatus):
+       handler = get_state_table_handler(vstatus,vstatus.state,event,vstatus.counter)
+       if handler:
+               handler(vstatus.state, event, params, v, vstatus)
 
 def connect_to_vend(options, cf):
 

UCC git Repository :: git.ucc.asn.au