X-Git-Url: https://git.ucc.asn.au/?p=uccvend-vendserver.git;a=blobdiff_plain;f=VendServer%2FVendServer.py;h=860c1c8456d5a7dbf24f8b4c8dd9284965ccda37;hp=d079b4edae274616e2a83329581865c0fb821f47;hb=060a73740d793c8f1643554829c90a0dd442ca79;hpb=fc54224064166688cff70891381774884d5c7729 diff --git a/VendServer/VendServer.py b/VendServer/VendServer.py index d079b4e..860c1c8 100755 --- a/VendServer/VendServer.py +++ b/VendServer/VendServer.py @@ -22,6 +22,7 @@ from SnackConfig import get_snack#, get_snacks import socket from posix import geteuid from LDAPConnector import get_uid,get_uname, set_card_id +from OpenDispense import OpenDispense as Dispense CREDITS=""" This vending machine software brought to you by: @@ -65,6 +66,43 @@ STATE_GRANDFATHER_CLOCK, TEXT_SPEED = 0.8 IDLE_SPEED = 0.05 +_pin_uid = 0 +_pin_uname = 'root' +_pin_pin = '----' + +_last_card_id = -1 + +idlers = [] +idler = None + +config_options = { + 'DBServer': ('Database', 'Server'), + 'DBName': ('Database', 'Name'), + 'DBUser': ('VendingMachine', 'DBUser'), + 'DBPassword': ('VendingMachine', 'DBPassword'), + + 'ServiceName': ('VendingMachine', 'ServiceName'), + 'ServicePassword': ('VendingMachine', 'Password'), + + 'ServerName': ('DecServer', 'Name'), + 'ConnectPassword': ('DecServer', 'ConnectPassword'), + 'PrivPassword': ('DecServer', 'PrivPassword'), + } + +class VendConfigFile: + def __init__(self, config_file, options): + try: + cp = ConfigParser.ConfigParser() + cp.read(config_file) + + for option in options: + section, name = options[option] + value = cp.get(section, name) + self.__dict__[option] = value + + except ConfigParser.Error, e: + raise SystemExit("Error reading config file "+config_file+": " + str(e)) + class DispenseDatabaseException(Exception): pass class DispenseDatabase: @@ -95,8 +133,36 @@ class DispenseDatabase: while notifier is not None: self.process_requests() notify = self.db.getnotify() +""" +This class manages the current state of the vending machine. +""" +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.last_timeout_refresh = None + + def change_state(self,newstate,newcounter=None): + if self.state != newstate: + self.state = newstate + if newcounter is not None and self.counter != newcounter: + self.counter = newcounter + +""" +Show information to the user as to what can be dispensed. +""" def scroll_options(username, mk, welcome = False): + # If the user has just logged in, show them their balance if welcome: # Balance checking acct, unused = Popen(['dispense', 'acct', username], close_fds=True, stdout=PIPE).communicate() @@ -110,7 +176,7 @@ def scroll_options(username, mk, welcome = False): msg = [] choices = ' '*10+'CHOICES: ' - # Get coke contents + # Show what is in the coke machine cokes = [] for i in range(0, 7): args = ('dispense', 'iteminfo', 'coke:%i' % i) @@ -125,27 +191,19 @@ def scroll_options(username, mk, welcome = False): if slot_name == 'dead': continue choices += '%s-(%sc)-%s8 '%(slot_name, price, slot_num) -# we don't want to print snacks for now since it'll be too large -# and there's physical bits of paper in the machine anyway - matt -# try: -# snacks = get_snacks() -# except: -# snacks = {} -# -# for slot, ( name, price ) in snacks.items(): -# choices += '%s8-%s (%sc) ' % ( slot, name, price ) - + # Show the final few options choices += '55-DOOR ' choices += 'OR ANOTHER SNACK. ' choices += '99 TO READ AGAIN. ' choices += 'CHOICE? ' msg.append((choices, False, None)) + # Send it to the display mk.set_messages(msg) -_pin_uid = 0 -_pin_uname = 'root' -_pin_pin = '----' +""" +Verify the users pin +""" def _check_pin(uid, pin): global _pin_uid global _pin_uname @@ -188,6 +246,9 @@ def _check_pin(uid, pin): logging.info("Pin incorrect for %d",uid) return pin == int(pinstr) +""" +Check if the users account has been disabled +""" def acct_is_disabled(name=None): global _pin_uname if name == None: @@ -201,9 +262,15 @@ def acct_is_disabled(name=None): return True return False +""" +Check that the user has a valid pin set +""" def has_good_pin(uid): return _check_pin(uid, None) != None +""" +Verify the users pin. +""" def verify_user_pin(uid, pin, skip_pin_check=False): if skip_pin_check or _check_pin(uid, pin) == True: info = pwd.getpwuid(uid) @@ -219,7 +286,9 @@ def verify_user_pin(uid, pin, skip_pin_check=False): logging.info('refused pin for uid %d'%(uid)) return None - +""" +In here just for fun. +""" def cookie(v): seed(time()) messages = [' WASSUP! ', 'PINK FISH ', ' SECRETS ', ' ESKIMO ', ' FORTUNES ', 'MORE MONEY'] @@ -244,15 +313,16 @@ def cookie(v): s += msg[i] v.display(s) +""" +Format text so it will appear centered on the screen. +""" def center(str): LEN = 10 return ' '*((LEN-len(str))/2)+str - - -idlers = [] -idler = None - +""" +Configure the things that will appear on screen whil the machine is idling. +""" def setup_idlers(v): global idlers, idler idlers = [ @@ -288,6 +358,9 @@ def setup_idlers(v): disabled = [ ] +""" +Go back to the default idler. +""" def reset_idler(v, vstatus, t = None): global idlers, idler idler = GreetingIdler(v, t) @@ -296,6 +369,9 @@ def reset_idler(v, vstatus, t = None): vstatus.time_to_autologout = None vstatus.change_state(STATE_IDLE, 1) +""" +Change to a random idler. +""" def choose_idler(): global idlers, idler @@ -325,7 +401,9 @@ def choose_idler(): if idler: idler.reset() - +""" +Run every step while the machine is idling. +""" def idle_step(vstatus): global idler if idler.finished(): @@ -336,38 +414,9 @@ def idle_step(vstatus): nextidle = IDLE_SPEED vstatus.time_of_next_idlestep = time()+nextidle -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.last_timeout_refresh = None - - def change_state(self,newstate,newcounter=None): - if self.state != newstate: - #print "Changing state from: ", - #print self.state, - #print " to ", - #print newstate - self.state = newstate - - if newcounter is not None and self.counter != newcounter: - #print "Changing counter from: ", - #print self.counter, - #print " to ", - #print newcounter - self.counter = newcounter - - - +""" +These next two events trigger no response in the code. +""" def handle_tick_event(event, params, v, vstatus): # don't care right now. pass @@ -376,11 +425,16 @@ def handle_switch_event(event, params, v, vstatus): # don't care right now. pass - +""" +Don't do anything for this event. +""" def do_nothing(state, event, params, v, vstatus): print "doing nothing (s,e,p)", state, " ", event, " ", params pass +""" +These next few entrie tell us to do nothing while we are idling +""" def handle_getting_uid_idle(state, event, params, v, vstatus): # don't care right now. pass @@ -389,6 +443,9 @@ def handle_getting_pin_idle(state, event, params, v, vstatus): # don't care right now. pass +""" +While logged in and waiting for user input, slowly get closer to logging out. +""" def handle_get_selection_idle(state, event, params, v, vstatus): global _last_card_id # don't care right now. @@ -425,8 +482,9 @@ def handle_get_selection_idle(state, event, params, v, vstatus): # need to check vstatus.mk.update_display() - - +""" +Triggered on user input while logged in. +""" def handle_get_selection_key(state, event, params, v, vstatus): global _last_card_id key = params @@ -462,13 +520,15 @@ def handle_get_selection_key(state, event, params, v, vstatus): vstatus.time_to_autologout = None vstatus.last_timeout_refresh = None +""" +Triggered when the user has entered the id of something they would like to purchase. +""" 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) ret = os.system('dispense -u "%s" door'%vstatus.username) else: ret = os.system('dispense door') @@ -499,8 +559,6 @@ def make_selection(v, vstatus): price, shortname, name = get_snack( '--' ) dollarprice = "$%.2f" % ( price / 100.0 ) v.display(vstatus.cur_selection+' - %s'%dollarprice) -# exitcode = os.system('dispense -u "%s" give \>snacksales %d "%s"'%(vstatus.username, price, name)) >> 8 -# exitcode = os.system('dispense -u "%s" give \>sales\:snack %d "%s"'%(vstatus.username, price, name)) >> 8 exitcode = os.system('dispense -u "%s" snack:%s'%(vstatus.username, vstatus.cur_selection)) >> 8 if (exitcode == 0): # magic dispense syslog service @@ -522,7 +580,9 @@ def make_selection(v, vstatus): v.display('UNK ERROR') sleep(1) - +""" +Find the price of an item. +""" def price_check(v, vstatus): if vstatus.cur_selection[1] == '8': args = ('dispense', 'iteminfo', 'coke:' + vstatus.cur_selection[0]) @@ -537,7 +597,9 @@ def price_check(v, vstatus): dollarprice = "$%.2f" % ( price / 100.0 ) v.display(vstatus.cur_selection+' - %s'%dollarprice) - +""" +Triggered when the user presses a button while entering their pin. +""" def handle_getting_pin_key(state, event, params, v, vstatus): #print "handle_getting_pin_key (s,e,p)", state, " ", event, " ", params key = params @@ -573,7 +635,9 @@ def handle_getting_pin_key(state, event, params, v, vstatus): return - +""" +Triggered when the user presses a button while entering their user id. +""" def handle_getting_uid_key(state, event, params, v, vstatus): #print "handle_getting_uid_key (s,e,p)", state, " ", event, " ", params key = params @@ -673,7 +737,9 @@ Wouldn't you prefer a nice game of chess? vstatus.change_state(STATE_GETTING_PIN) return - +""" +Triggered when a key is pressed and the machine is idling. +""" def handle_idle_key(state, event, params, v, vstatus): #print "handle_idle_key (s,e,p)", state, " ", event, " ", params @@ -687,7 +753,9 @@ def handle_idle_key(state, event, params, v, vstatus): vstatus.change_state(STATE_GETTING_UID) run_handler(event, key, v, vstatus) - +""" +What to do when there is nothing to do. +""" def handle_idle_tick(state, event, params, v, vstatus): ### State idling if vstatus.mk.done(): @@ -705,6 +773,9 @@ def handle_idle_tick(state, event, params, v, vstatus): run_handler(event, params, v, vstatus) sleep(0.05) +""" +Manages the beeps for the grandfather clock +""" def beep_on(when, before=0): start = int(when - before) end = int(when) @@ -809,6 +880,9 @@ def handle_grandfather_tick(state, event, params, v, vstatus): if go_idle and vstatus.mk.done(): vstatus.change_state(STATE_IDLE,1) +""" +What to do when the door is open. +""" def handle_door_idle(state, event, params, v, vstatus): def twiddle(clock,v,wise = 2): if (clock % 4 == 0): @@ -828,7 +902,9 @@ def handle_door_idle(state, event, params, v, vstatus): else: twiddle(now, v, wise=0) - +""" +What to do when the door is opened or closed. +""" def handle_door_event(state, event, params, v, vstatus): if params == 0: #door open vstatus.change_state(STATE_DOOR_OPENING) @@ -844,8 +920,9 @@ def handle_door_event(state, event, params, v, vstatus): logging.warning('Leaving open door mode') v.display("-YUM YUM!-") -_last_card_id = -1 - +""" +Triggered when a user swipes their caed, and the machine is logged out. +""" def handle_mifare_event(state, event, params, v, vstatus): global _last_card_id card_id = params @@ -892,6 +969,9 @@ def handle_mifare_event(state, event, params, v, vstatus): reset_idler(v, vstatus, 2) return +""" +Triggered when a user swipes their card and the machine is logged in. +""" def handle_mifare_add_user_event(state, event, params, v, vstatus): global _last_card_id card_id = params @@ -924,6 +1004,9 @@ def handle_mifare_add_user_event(state, event, params, v, vstatus): def return_to_idle(state,event,params,v,vstatus): reset_idler(v, vstatus) +""" +Maps what to do when the state changes. +""" 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 @@ -963,6 +1046,9 @@ def create_state_table(vstatus): vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,2)] = do_nothing vstatus.state_table[(STATE_GRANDFATHER_CLOCK,MIFARE,1)] = handle_mifare_event +""" +Get what to do on a state change. +""" def get_state_table_handler(vstatus, state, event, counter): return vstatus.state_table[(state,event,counter)] @@ -986,17 +1072,6 @@ def run_forever(rfh, wfh, options, cf): setup_idlers(v) reset_idler(v, vstatus) - # This main loop was hideous and the work of the devil. - # This has now been fixed (mostly) - mtearle - # - # - # notes for later surgery - # (event, counter, ' ') - # V - # d[ ] = (method) - # - # ( return state - not currently implemented ) - while True: if USE_DB: try: @@ -1010,14 +1085,14 @@ def run_forever(rfh, wfh, options, cf): run_handler(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) +""" +Connect to the machine. +""" def connect_to_vend(options, cf): if options.use_lat: @@ -1042,6 +1117,9 @@ def connect_to_vend(options, cf): return rfh, wfh +""" +Parse arguments from the command line +""" def parse_args(): from optparse import OptionParser @@ -1065,34 +1143,6 @@ def parse_args(): return options -config_options = { - 'DBServer': ('Database', 'Server'), - 'DBName': ('Database', 'Name'), - 'DBUser': ('VendingMachine', 'DBUser'), - 'DBPassword': ('VendingMachine', 'DBPassword'), - - 'ServiceName': ('VendingMachine', 'ServiceName'), - 'ServicePassword': ('VendingMachine', 'Password'), - - 'ServerName': ('DecServer', 'Name'), - 'ConnectPassword': ('DecServer', 'ConnectPassword'), - 'PrivPassword': ('DecServer', 'PrivPassword'), - } - -class VendConfigFile: - def __init__(self, config_file, options): - try: - cp = ConfigParser.ConfigParser() - cp.read(config_file) - - for option in options: - section, name = options[option] - value = cp.get(section, name) - self.__dict__[option] = value - - except ConfigParser.Error, e: - raise SystemExit("Error reading config file "+config_file+": " + str(e)) - def create_pid_file(name): try: pid_file = file(name, 'w')