X-Git-Url: https://git.ucc.asn.au/?p=zanchey%2Fdispense2.git;a=blobdiff_plain;f=sql-edition%2Fservers%2FVendServer.py;h=c1ef2765e3815a77fb841c062642f6b2b7bf603d;hp=992a65d34da88719dfb2ed55a47c772c21a92594;hb=662aabb9f5d3d042076acb516020251d3f86784c;hpb=cfeb950d84e7ae33007df9185348d0569361086c diff --git a/sql-edition/servers/VendServer.py b/sql-edition/servers/VendServer.py index 992a65d..c1ef276 100755 --- a/sql-edition/servers/VendServer.py +++ b/sql-edition/servers/VendServer.py @@ -4,26 +4,45 @@ USE_DB = 0 import ConfigParser -import sys, os, string, re, pwd, signal +import sys, os, string, re, pwd, signal, math import logging, logging.handlers from traceback import format_tb if USE_DB: import pg from time import time, sleep from popen2 import popen2 from LATClient import LATClient, LATClientException +from SerialClient import SerialClient, SerialClientException from VendingMachine import VendingMachine, VendingException +from MessageKeeper import MessageKeeper from HorizScroll import HorizScroll from random import random, seed -from Idler import TrainIdler,GrayIdler +from Idler import TrainIdler,GrayIdler,StringIdler,ClockIdler,FortuneIdler,FileIdler,PipeIdler import socket from posix import geteuid +CREDITS=""" +This vending machine software brought to you by: +Bernard Blackham +Mark Tearle +Nick Bannon +Cameron Patrick +and a collective of hungry alpacas. + + + +For a good time call +61 8 6488 3901 + + + +""" + GREETING = 'UCC SNACKS' PIN_LENGTH = 4 DOOR = 1 SWITCH = 2 KEY = 3 +TICK = 4 class DispenseDatabaseException(Exception): pass @@ -129,6 +148,8 @@ def door_open_mode(v): 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') @@ -164,91 +185,226 @@ def center(str): LEN = 10 return ' '*((LEN-len(str))/2)+str -class MessageKeeper: - def __init__(self, vendie): - # Each element of scrolling_message should be a 3-tuple of - # ('message', True/False if it is to be repeated, time to display) - self.scrolling_message = [] - self.v = vendie - self.next_update = None - - def set_message(self, string): - self.scrolling_message = [(string, False, None)] - self.update_display(True) - - def set_messages(self, strings): - self.scrolling_message = strings - self.update_display(True) - - def update_display(self, forced = False): - if not forced and self.next_update != None and time() < self.next_update: - return - if len(self.scrolling_message) > 0: - if len(self.scrolling_message[0][0]) > 10: - (m, r, t) = self.scrolling_message[0] - a = [] - exp = HorizScroll(m).expand(padding = 0, wraparound = True) - if t == None: - t = 0.1 - else: - t = t / len(exp) - for x in exp: - a.append((x, r, t)) - del self.scrolling_message[0] - self.scrolling_message = a + self.scrolling_message - newmsg = self.scrolling_message[0] - if newmsg[2] != None: - self.next_update = time() + newmsg[2] - else: - self.next_update = None - self.v.display(self.scrolling_message[0][0]) - if self.scrolling_message[0][1]: - self.scrolling_message.append(self.scrolling_message[0]) - del self.scrolling_message[0] - def done(self): - return len(self.scrolling_message) == 0 idlers = [] idler = None + def setup_idlers(v): global idlers, idler idlers = [ + GrayIdler(v), + StringIdler(v, text="Kill 'em all", repeat=False), + GrayIdler(v,one="*",zero="-"), + StringIdler(v, text=CREDITS), + GrayIdler(v,one="/",zero="\\"), + ClockIdler(v), + GrayIdler(v,one="X",zero="O"), + FileIdler(v, '/usr/share/common-licenses/GPL-2'), + GrayIdler(v,one="*",zero="-",reorder=1), + StringIdler(v, text=str(math.pi) + " "), + ClockIdler(v), + GrayIdler(v,one="/",zero="\\",reorder=1), + StringIdler(v, text=str(math.e) + " "), + GrayIdler(v,one="X",zero="O",reorder=1), + StringIdler(v, text=" I want some pizza - please call Pizza Hut Shenton Park on +61 8 9381 9979 - and order as Quinn - I am getting really hungry", repeat=False), + PipeIdler(v, "/usr/bin/ypcat", "passwd"), + FortuneIdler(v), + ClockIdler(v), + StringIdler(v), TrainIdler(v), - GrayIdler(v), - GrayIdler(v,one="*",zero="-"), - GrayIdler(v,one="/",zero="\\"), - GrayIdler(v,one="X",zero="O"), - GrayIdler(v,one="*",zero="-",reorder=1), - GrayIdler(v,one="/",zero="\\",reorder=1), - GrayIdler(v,one="X",zero="O",reorder=1), + ] + disabled = [ ] idler = choose_idler() def choose_idler(): global idler - idler = idlers[int(random()*len(idlers))] + iiindex = 0 + + if idler: + iiindex = idlers.index(idler) + + iilen = len(idlers) + + move = int(random()*len(idlers)) + 1 + + while move >= 0: + idler = idlers[( (iiindex + 1) % iilen)] + move = move - idler.affinity() + idler.reset() def idle_step(): global idler + if idler.finished(): + choose_idler() idler.next() +class VendState: + def __init__(self,v): + self.mk = MessageKeeper(v) + self.cur_user = '' + self.cur_pin = '' + 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. + pass + +def handle_switch_event(event, params, v, vstatus): + # don't care right now. + pass + +def handle_key_event(event, params, v, vstatus): + key = params + # complicated key handling here: + if len(vstatus.cur_user) < 5: + if key == 11: + vstatus.cur_user = '' + vstatus.mk.set_message(GREETING) + 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) + return + elif len(vstatus.cur_pin) < PIN_LENGTH: + if key == 11: + if vstatus.cur_pin == '': + vstatus.cur_user = '' + vstatus.mk.set_message(GREETING) + return + vstatus.cur_pin = '' + vstatus.mk.set_message('PIN: ') + return + 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: + v.beep(0, False) + vstatus.cur_selection = '' + scroll_options(username, vstatus.mk, True) + return + else: + v.beep(40, False) + vstatus.mk.set_messages( + [(center('BAD PIN'), False, 1.0), + (center('SORRY'), False, 0.5), + (GREETING, False, None)]) + vstatus.cur_user = '' + vstatus.cur_pin = '' + return + elif len(vstatus.cur_selection) == 0: + if key == 11: + vstatus.cur_pin = '' + vstatus.cur_user = '' + vstatus.cur_selection = '' + vstatus.mk.set_messages( + [(center('BYE!'), False, 1.5), + (GREETING, False, None)]) + 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 + + def run_forever(rfh, wfh, options, cf): v = VendingMachine(rfh, wfh) + vstatus = VendState(v) + logging.debug('PING is ' + str(v.ping())) if USE_DB: db = DispenseDatabase(v, cf.DBServer, cf.DBName, cf.DBUser, cf.DBPassword) - cur_user = '' - cur_pin = '' - cur_selection = '' - mk = MessageKeeper(v) - mk.set_message(GREETING) - time_to_autologout = None + vstatus.mk.set_message(GREETING) setup_idlers(v) - time_to_idle = None - last_timeout_refresh = None + + + # This main loop is hideous and the work of the devil - mtearle + # + # + # notes for later surgery + # (event, counter, ' ') + # V + # d[ ] = (method) + # + # return state while True: if USE_DB: @@ -257,170 +413,75 @@ def run_forever(rfh, wfh, options, cf): except DispenseDatabaseException, e: logging.error('Database error: '+str(e)) - if time_to_autologout != None: - time_left = time_to_autologout - time() - if time_left < 6 and (last_timeout_refresh is None or last_timeout_refresh > time_left): - mk.set_message('LOGOUT: '+str(int(time_left))) - last_timeout_refresh = int(time_left) - cur_selection = '' - - if time_to_autologout != None and time_to_autologout - time() <= 0: - time_to_autologout = None - cur_user = '' - cur_pin = '' - cur_selection = '' - mk.set_message(GREETING) - - if time_to_autologout and not mk.done(): time_to_autologout = None - if cur_user == '' and time_to_autologout: time_to_autologout = None - if len(cur_pin) == PIN_LENGTH and mk.done() and time_to_autologout == None: + 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 - time_to_autologout = time() + 15 + vstatus.time_to_autologout = time() + 15 + vstatus.last_timeout_refresh = None - if time_to_idle == None and cur_user == '': - time_to_idle = time() + 30 + if vstatus.time_to_idle == None and vstatus.cur_user == '': + vstatus.time_to_idle = time() + 5 choose_idler() - if time_to_idle != None and cur_user != '': time_to_idle = None - if time_to_idle is not None and time() > time_to_idle: idle_step() - if time() > time_to_idle + 300: - time_to_idle = time() + 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() - mk.update_display() + vstatus.mk.update_display() e = v.next_event(0) - if e == None: + (event, params) = e + + if event == TICK: e = v.next_event(0.05) - if e == None: + (event, params) = e + + if event == TICK: + handle_tick_event(event, params, v, vstatus) continue - time_to_idle = None - (event, params) = e + vstatus.time_to_idle = None logging.debug('Got event: ' + repr(e)) + if event == DOOR: - if params == 0: - door_open_mode(v); - cur_user = '' - cur_pin = '' - mk.set_message(GREETING) + handle_door_event(event, params, v, vstatus) elif event == SWITCH: - # don't care right now. - pass + handle_switch_event(event, params, v, vstatus) elif event == KEY: - key = params - # complicated key handling here: - if len(cur_user) < 5: - if key == 11: - cur_user = '' - mk.set_message(GREETING) - continue - cur_user += chr(key + ord('0')) - mk.set_message('UID: '+cur_user) - if len(cur_user) == 5: - uid = int(cur_user) - if not has_good_pin(uid): - logging.info('user '+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)]) - mk.set_messages( - [(' '*10+'INVALID PIN SETUP'+' '*10, False, 3), - (GREETING, False, None)]) - cur_user = '' - cur_pin = '' - continue - cur_pin = '' - mk.set_message('PIN: ') - logging.info('need pin for user %s'%cur_user) - continue - elif len(cur_pin) < PIN_LENGTH: - if key == 11: - if cur_pin == '': - cur_user = '' - mk.set_message(GREETING) - continue - cur_pin = '' - mk.set_message('PIN: ') - continue - cur_pin += chr(key + ord('0')) - mk.set_message('PIN: '+'X'*len(cur_pin)) - if len(cur_pin) == PIN_LENGTH: - username = verify_user_pin(int(cur_user), int(cur_pin)) - if username: - v.beep(0, False) - cur_selection = '' - scroll_options(username, mk, True) - continue - else: - v.beep(40, False) - mk.set_messages( - [(center('BAD PIN'), False, 1.0), - (center('SORRY'), False, 0.5), - (GREETING, False, None)]) - cur_user = '' - cur_pin = '' - continue - elif len(cur_selection) == 0: - if key == 11: - cur_pin = '' - cur_user = '' - cur_selection = '' - mk.set_messages( - [(center('BYE!'), False, 1.5), - (GREETING, False, None)]) - continue - cur_selection += chr(key + ord('0')) - mk.set_message('SELECT: '+cur_selection) - time_to_autologout = None - elif len(cur_selection) == 1: - if key == 11: - cur_selection = '' - time_to_autologout = None - scroll_options(username, mk) - continue - else: - cur_selection += chr(key + ord('0')) - #make_selection(cur_selection) - # XXX this should move somewhere else: - if cur_selection == '55': - 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') - mk.set_message(center('DOOR OPEN')) - else: - logging.warning('user %s tried to dispense a bad door'%username) - mk.set_message(center('BAD DOOR')) - sleep(1) - elif cur_selection == '91': - cookie(v) - elif cur_selection == '99': - scroll_options(username, mk) - cur_selection = '' - continue - elif cur_selection[1] == '8': - v.display('GOT COKE?') - os.system('su - "%s" -c "dispense %s"'%(username, cur_selection[0])) - else: - v.display('HERES A '+cur_selection) - v.vend(cur_selection) - sleep(0.5) - v.display('THANK YOU') - sleep(0.5) - cur_selection = '' - time_to_autologout = time() + 8 + handle_key_event(event, params, v, vstatus) def connect_to_vend(options, cf): - # Open vending machine via LAT? + if options.use_lat: logging.info('Connecting to vending machine using LAT') latclient = LATClient(service = cf.ServiceName, password = cf.ServicePassword, server_name = cf.ServerName, connect_password = cf.ConnectPassword, priv_password = cf.PrivPassword) rfh, wfh = latclient.get_fh() + elif options.use_serial: + # Open vending machine via serial. + logging.info('Connecting to vending machine using serial') + serialclient = SerialClient(port = '/dev/ttyS1', baud = 9600) + rfh,wfh = serialclient.get_fh() else: #(rfh, wfh) = popen2('../../virtualvend/vvend.py') logging.info('Connecting to virtual vending machine on %s:%d'%(options.host,options.port)) @@ -437,7 +498,9 @@ def parse_args(): op = OptionParser(usage="%prog [OPTION]...") op.add_option('-f', '--config-file', default='/etc/dispense/servers.conf', metavar='FILE', dest='config_file', help='use the specified config file instead of /etc/dispense/servers.conf') - op.add_option('--virtualvend', action='store_false', default=True, dest='use_lat', help='use the virtual vending server instead of LAT') + op.add_option('--serial', action='store_true', default=True, dest='use_serial', help='use the serial port') + op.add_option('--lat', action='store_true', default=False, dest='use_lat', help='use LAT') + op.add_option('--virtualvend', action='store_false', default=True, dest='use_serial', help='use the virtual vending server instead of LAT') op.add_option('-n', '--hostname', dest='host', default='localhost', help='the hostname to connect to for virtual vending machine mode (default: localhost)') op.add_option('-p', '--port', dest='port', default=5150, type='int', help='the port number to connect to (default: 5150)') op.add_option('-l', '--log-file', metavar='FILE', dest='log_file', default='', help='log output to the specified file') @@ -557,7 +620,7 @@ def do_vend_server(options, config_opts): while True: try: rfh, wfh = connect_to_vend(options, config_opts) - except (LATClientException, socket.error), e: + except (SerialClientException, socket.error), e: (exc_type, exc_value, exc_traceback) = sys.exc_info() del exc_traceback logging.error("Connection error: "+str(exc_type)+" "+str(e))