X-Git-Url: https://git.ucc.asn.au/?p=uccvend-vendserver.git;a=blobdiff_plain;f=sql-edition%2Fservers%2FVendingMachine.py;h=25d375b0d1855bb5cb0ff2c726b2fecdd654ff02;hp=3046360fe9deb58424853d769cc581483d7a95de;hb=edd78a42d003a467dae652c9908403a26d38cf08;hpb=38b21b79dca47b875d3005a4708163d77664d223 diff --git a/sql-edition/servers/VendingMachine.py b/sql-edition/servers/VendingMachine.py index 3046360..25d375b 100644 --- a/sql-edition/servers/VendingMachine.py +++ b/sql-edition/servers/VendingMachine.py @@ -1,14 +1,27 @@ +# vim:ts=4 import re from CRC import do_crc -from binascii import unhexlify +from select import select +import socket, logging +from time import time, sleep asynchronous_responses = [ '400', '401', # door open/closed '610', # switches changed + '200', '201', '202', '203', '204', '205', '206', + '207', '208', '209', '211', # key presses ] +DOOR = 1 +SWITCH = 2 +KEY = 3 +TICK = 4 + +class VendingException(Exception): pass class VendingMachine: def __init__(self, rfh, wfh): - self.secret = 'AAAAAAAAAAAAAAAA' + self.events = [] + # Secret + self.secret = 'SN4CKZ0RZZZZZZZZ' self.rfh = rfh self.wfh = wfh self.challenge = None @@ -20,7 +33,8 @@ class VendingMachine: self.wfh.write('PING\n') code = '' while code != '000': - (code, _) = self.get_response() + code = self.get_response()[0] + self.get_switches() def await_prompt(self): self.wfh.flush() @@ -28,8 +42,12 @@ class VendingMachine: prefix = '' s = '' while True: - s = self.rfh.read(1) - if s == '': raise Exception + try: + s = self.rfh.read(1) + except socket.error: + raise VendingException('failed to read input from vending machine') + if s == '': raise VendingException('nothing read!') + if (s != '#' and s != '%') and state == 1: prefix += s if s == '\n' or s == '\r': state = 1 prefix = '' @@ -39,27 +57,51 @@ class VendingMachine: self.challenge = None return if re.search('^[0-9a-fA-F]{4}$', prefix): - self.challenge = unhexlify(prefix) + self.challenge = int(prefix, 16) return - def get_response(self): + def get_response(self, async = False): self.wfh.flush() while True: s = '' while s == '': s = self.rfh.readline() - if s == '': return None + if s == '': + raise VendingException('Input socket has closed!') s = s.strip('\r\n') code = s[0:3] text = s[4:] if code in asynchronous_responses: self.handle_event(code, text) + if async: return None else: self.await_prompt() return (code, text) + def get_switches(self): + self.wfh.write('S\n') + (code, text) = self.get_response() + if code != '600': + return (False, code, text) + self.interpret_switches(text) + return (True, code, text) + + def interpret_switches(self, text): + self.switches = (int(text[0:2], 16) << 8) | int(text[3:5], 16) + def handle_event(self, code, text): - pass + if code == '400': + self.events.append((DOOR, 1)) + elif code == '401': + self.events.append((DOOR, 0)) + elif code == '610': + # NOP this. Nothing handles this yet. + #self.events.append((SWITCH, None)) + self.interpret_switches(text) + elif code[0] == '2': + self.events.append((KEY, int(code[1:3]))) + else: + logging.warning('Unhandled event! (%s %s)\n'%(code,text)) def authed_message(self, message): if self.challenge == None: @@ -77,9 +119,9 @@ class VendingMachine: def vend(self, item): if not re.search('^[0-9][0-9]$', item): return (False, 'Invalid item requested (%s)'%item) - self.wfh.write(self.authed_message(('V%s\n'%item)+'\n')) + self.wfh.write(self.authed_message(('V%s'%item))+'\n') (code, string) = self.get_response() - return (code, string) + return (code == '100', code, string) def beep(self, duration = None, synchronous = True): msg = 'B' @@ -101,13 +143,29 @@ class VendingMachine: msg += '%02x'%duration self.wfh.write(msg+'\n') (code, string) = self.get_response() - # FIXME: workaround a bug in rom W. should be just: return (code == '500', code, string) - return (code == '500' or code == '501', code, string) + return (code == '501', code, string) def display(self, string): if len(string) > 10: string = string[0:10] + string = re.sub('(.)\.', lambda match: '.'+match.group(1), string) self.wfh.write('D'+string+'\n') (code, string) = self.get_response() return (code == '300', code, string) + def next_event(self, timeout = None): + # we don't want to buffer in the serial port, so we get all the events + # we can ASAP. + if timeout < 0: timeout = 0 + if len(self.events) > 0: timeout = 0 + while True: + (r, _, _) = select([self.rfh], [], [], timeout) + if r: + self.get_response(async = True) + timeout = 0 + else: + break + if len(self.events) == 0: return (TICK, time()) + ret = self.events[0] + del self.events[0] + return ret