6 import sys, os, string, re, pwd
8 from time import time, sleep
9 from popen2 import popen2
10 from LATClient import LATClient
11 from VendingMachine import VendingMachine
12 from ConfigParser import ConfigParser
13 from HorizScroll import HorizScroll
15 GREETING = 'UCC SNACKS'
22 class DispenseDatabase:
23 def __init__(self, vending_machine, host, name, user, password):
24 self.vending_machine = vending_machine
25 self.db = pg.DB(dbname = name, host = host, user = user, passwd = password)
26 self.db.query('LISTEN vend_requests')
28 def process_requests(self):
30 query = 'SELECT request_id, request_slot FROM vend_requests WHERE request_handled = false'
32 outstanding = self.db.query(query).getresult()
33 except (pg.error,), db_err:
34 sys.stderr.write('Failed to query database: %s\n'%(db_err.strip()))
36 for (id, slot) in outstanding:
37 (worked, code, string) = self.vending_machine.vend(slot)
38 print (worked, code, string)
40 query = 'SELECT vend_success(%s)'%id
41 self.db.query(query).getresult()
43 query = 'SELECT vend_failed(%s)'%id
44 self.db.query(query).getresult()
46 def handle_events(self):
47 notifier = self.db.getnotify()
48 while notifier is not None:
49 self.process_requests()
50 notify = self.db.getnotify()
52 def scroll_options(username, mk, welcome = False):
54 msg = [(center('WELCOME'), False, 0.8),
55 (center(username), False, 0.8)]
58 choices = ' '*10+'CHOICES: '
59 coke_machine = file('/home/other/coke/coke_contents')
60 cokes = coke_machine.readlines()
63 (slot_num, price, slot_name) = c.split(' ', 2)
64 if slot_name == 'dead': continue
65 choices += '%s8-%s (%sc) '%(slot_num, slot_name, price)
67 choices += 'OR A SNACK. '
68 choices += '99 TO READ AGAIN.'
69 msg.append((choices, False, None))
74 info = pwd.getpwuid(uid)
77 if info.pw_dir == None: return False
78 pinfile = os.path.join(info.pw_dir, '.pin')
91 if not re.search('^'+'[0-9]'*PIN_LENGTH+'$', pinstr):
95 def has_good_pin(uid):
96 return get_pin(uid) != None
98 def verify_user_pin(uid, pin):
99 if get_pin(uid) == pin:
100 info = pwd.getpwuid(uid)
105 def door_open_mode(vending_machine):
106 print "Entering open door mode"
107 v.display("DOOR OPEN")
113 if params == 1: # door closed
114 v.display("BYE BYE!")
120 return ' '*((LEN-len(str))/2)+str
123 def __init__(self, vendie):
124 # Each element of scrolling_message should be a 3-tuple of
125 # ('message', True/False if it is to be repeated, time to display)
126 self.scrolling_message = []
128 self.next_update = None
130 def set_message(self, string):
131 self.scrolling_message = [(string, False, None)]
132 self.update_display(True)
134 def set_messages(self, strings):
135 self.scrolling_message = strings
136 self.update_display(True)
138 def update_display(self, forced = False):
139 if not forced and self.next_update != None and time() < self.next_update:
141 if len(self.scrolling_message) > 0:
142 if len(self.scrolling_message[0][0]) > 10:
143 (m, r, t) = self.scrolling_message[0]
145 exp = HorizScroll(m).expand(padding = 10)
152 del self.scrolling_message[0]
153 self.scrolling_message = a + self.scrolling_message
154 newmsg = self.scrolling_message[0]
155 if newmsg[2] != None:
156 self.next_update = time() + newmsg[2]
158 self.next_update = None
159 self.v.display(self.scrolling_message[0][0])
160 if self.scrolling_message[0][1]:
161 self.scrolling_message.append(self.scrolling_message[0])
162 del self.scrolling_message[0]
165 return len(self.scrolling_message) == 0
167 if __name__ == '__main__':
169 cp.read('/etc/dispense/servers.conf')
170 DBServer = cp.get('Database', 'Server')
171 DBName = cp.get('Database', 'Name')
172 DBUser = cp.get('VendingMachine', 'DBUser')
173 DBPassword = cp.get('VendingMachine', 'DBPassword')
175 ServiceName = cp.get('VendingMachine', 'ServiceName')
176 ServicePassword = cp.get('VendingMachine', 'Password')
177 # Open vending machine via LAT
179 latclient = LATClient(service = ServiceName, password = ServicePassword)
180 (rfh, wfh) = latclient.get_fh()
182 #(rfh, wfh) = popen2('../../virtualvend/vvend.py')
184 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
185 sock.connect(('localhost', 5150))
186 rfh = sock.makefile('r')
187 wfh = sock.makefile('w')
188 v = VendingMachine(rfh, wfh)
189 print 'PING is', v.ping()
191 if USE_DB: db = DispenseDatabase(v, DBServer, DBName, DBUser, DBPassword)
196 mk = MessageKeeper(v)
197 mk.set_message(GREETING)
198 logout_timeout = None
199 last_timeout_refresh = None
202 if USE_DB: db.handle_events()
204 if logout_timeout != None:
205 time_left = logout_timeout - time()
206 if time_left < 10 and (last_timeout_refresh is None or last_timeout_refresh > time_left):
207 mk.set_message('LOGOUT: '+str(int(time_left)))
208 last_timeout_refresh = int(time_left)
210 if logout_timeout != None and logout_timeout - time() <= 0:
211 logout_timeout = None
215 mk.set_message(GREETING)
217 if logout_timeout and not mk.done(): logout_timeout = None
218 if len(cur_pin) == PIN_LENGTH and mk.done() and logout_timeout == None:
220 logout_timeout = time() + 10
226 e = v.next_event(0.1)
236 mk.set_message(GREETING)
237 elif event == SWITCH:
238 # don't care right now.
242 # complicated key handling here:
243 if len(cur_user) < 5:
246 mk.set_message(GREETING)
248 cur_user += chr(key + ord('0'))
249 mk.set_message('UID: '+cur_user)
250 if len(cur_user) == 5:
252 if not has_good_pin(uid):
254 [(center('INVALID'), False, 0.7),
255 (center('PIN'), False, 0.7),
256 (center('SETUP'), False, 1.0),
257 (GREETING, False, None)])
262 mk.set_message('PIN: ')
264 elif len(cur_pin) < PIN_LENGTH:
268 mk.set_message(GREETING)
271 mk.set_message('PIN: ')
273 cur_pin += chr(key + ord('0'))
274 mk.set_message('PIN: '+'X'*len(cur_pin))
275 if len(cur_pin) == PIN_LENGTH:
276 username = verify_user_pin(int(cur_user), int(cur_pin))
280 scroll_options(username, mk, True)
285 [(center('BAD PIN'), False, 1.0),
286 (center('SORRY'), False, 0.5),
287 (GREETING, False, None)])
291 elif len(cur_selection) == 0:
298 mk.set_message(GREETING)
300 cur_selection += chr(key + ord('0'))
301 mk.set_message('SELECT: '+cur_selection)
302 elif len(cur_selection) == 1:
305 scroll_options(username, mk)
308 cur_selection += chr(key + ord('0'))
309 #make_selection(cur_selection)
310 # XXX this should move somewhere else:
311 if cur_selection == '55':
312 v.display('GOT DOOR?')
313 os.system('su - "%s" -c "dispense door"'%username)
314 elif cur_selection == '99':
315 scroll_options(username, mk)
318 elif cur_selection[1] == '8':
319 v.display('GOT COKE?')
320 os.system('su - "%s" -c "dispense %s"'%(username, cur_selection[0]))
322 v.display('HERES A '+cur_selection)
323 v.vend(cur_selection)
325 v.display('THANK YOU')
328 logout_timeout = time() + 10