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
14 from random import random, seed
16 GREETING = 'UCC SNACKS'
23 class DispenseDatabase:
24 def __init__(self, vending_machine, host, name, user, password):
25 self.vending_machine = vending_machine
26 self.db = pg.DB(dbname = name, host = host, user = user, passwd = password)
27 self.db.query('LISTEN vend_requests')
29 def process_requests(self):
31 query = 'SELECT request_id, request_slot FROM vend_requests WHERE request_handled = false'
33 outstanding = self.db.query(query).getresult()
34 except (pg.error,), db_err:
35 sys.stderr.write('Failed to query database: %s\n'%(db_err.strip()))
37 for (id, slot) in outstanding:
38 (worked, code, string) = self.vending_machine.vend(slot)
39 print (worked, code, string)
41 query = 'SELECT vend_success(%s)'%id
42 self.db.query(query).getresult()
44 query = 'SELECT vend_failed(%s)'%id
45 self.db.query(query).getresult()
47 def handle_events(self):
48 notifier = self.db.getnotify()
49 while notifier is not None:
50 self.process_requests()
51 notify = self.db.getnotify()
53 def scroll_options(username, mk, welcome = False):
55 msg = [(center('WELCOME'), False, 0.8),
56 (center(username), False, 0.8)]
59 choices = ' '*10+'CHOICES: '
61 coke_machine = file('/home/other/coke/coke_contents')
62 cokes = coke_machine.readlines()
69 (slot_num, price, slot_name) = c.split(' ', 2)
70 if slot_name == 'dead': continue
71 choices += '%s8-%s (%sc) '%(slot_num, slot_name, price)
73 choices += 'OR A SNACK. '
74 choices += '99 TO READ AGAIN. '
76 msg.append((choices, False, None))
81 info = pwd.getpwuid(uid)
84 if info.pw_dir == None: return False
85 pinfile = os.path.join(info.pw_dir, '.pin')
98 if not re.search('^'+'[0-9]'*PIN_LENGTH+'$', pinstr):
102 def has_good_pin(uid):
103 return get_pin(uid) != None
105 def verify_user_pin(uid, pin):
106 if get_pin(uid) == pin:
107 info = pwd.getpwuid(uid)
112 def door_open_mode(vending_machine):
113 print "Entering open door mode"
114 v.display("-FEED ME-")
120 if params == 1: # door closed
121 v.display("-YUM YUM!-")
126 def __init__(self, v):
130 def put_shark(self, s, l):
133 elif self.s[l] == 'X':
139 # does the next stage of a dance
141 shark1 = self.idle_state % 18
143 self.put_shark('^', shark1)
145 self.put_shark('^', 18-shark1)
147 shark2 = ((self.idle_state+4) % 36)/2
149 self.put_shark('<', shark2)
151 self.put_shark('<', 18-shark2)
153 shark3 = ((self.idle_state+7) % 54)/3
155 self.put_shark('>', 9-shark3)
157 self.put_shark('>', 9-(18-shark3))
159 train1 = ((self.idle_state%(18*36)))
161 if train1 > train1_start and train1 < train1_start+(10*2):
163 ptr = i+train1-train1_start-5
164 if ptr >= 0 and ptr < 10: self.s[ptr] = '#'
166 train2 = ((self.idle_state%(18*36)))
168 if train2 > train2_start and train2 < train2_start+(10*2):
170 ptr = i+train2-train2_start-5
171 if ptr >= 0 and ptr < 10: self.s[9-ptr] = '#'
173 train3 = ((self.idle_state%(18*36)))
175 if train3 > train3_start and train3 < train3_start+(10*2):
177 ptr = i+train3-train3_start-10
178 if ptr >= 0 and ptr < 10: self.s[ptr] = '-'
180 self.v.display(string.join(self.s, ''))
182 self.idle_state %= 18*36*54
186 messages = [' WASSUP! ', 'PINK FISH ', ' SECRETS ', ' ESKIMO ', ' FORTUNES ', 'MORE MONEY']
187 choice = int(random()*len(messages))
188 msg = messages[choice]
189 left = range(len(msg))
190 for i in range(len(msg)):
191 if msg[i] == ' ': left.remove(i)
195 for i in range(0, len(msg)):
201 s += chr(int(random()*26)+ord('A'))
210 return ' '*((LEN-len(str))/2)+str
213 def __init__(self, vendie):
214 # Each element of scrolling_message should be a 3-tuple of
215 # ('message', True/False if it is to be repeated, time to display)
216 self.scrolling_message = []
218 self.next_update = None
220 def set_message(self, string):
221 self.scrolling_message = [(string, False, None)]
222 self.update_display(True)
224 def set_messages(self, strings):
225 self.scrolling_message = strings
226 self.update_display(True)
228 def update_display(self, forced = False):
229 if not forced and self.next_update != None and time() < self.next_update:
231 if len(self.scrolling_message) > 0:
232 if len(self.scrolling_message[0][0]) > 10:
233 (m, r, t) = self.scrolling_message[0]
235 exp = HorizScroll(m).expand(padding = 0, wraparound = True)
242 del self.scrolling_message[0]
243 self.scrolling_message = a + self.scrolling_message
244 newmsg = self.scrolling_message[0]
245 if newmsg[2] != None:
246 self.next_update = time() + newmsg[2]
248 self.next_update = None
249 self.v.display(self.scrolling_message[0][0])
250 if self.scrolling_message[0][1]:
251 self.scrolling_message.append(self.scrolling_message[0])
252 del self.scrolling_message[0]
255 return len(self.scrolling_message) == 0
257 if __name__ == '__main__':
259 cp.read('/etc/dispense/servers.conf')
260 DBServer = cp.get('Database', 'Server')
261 DBName = cp.get('Database', 'Name')
262 DBUser = cp.get('VendingMachine', 'DBUser')
263 DBPassword = cp.get('VendingMachine', 'DBPassword')
265 ServiceName = cp.get('VendingMachine', 'ServiceName')
266 ServicePassword = cp.get('VendingMachine', 'Password')
267 # Open vending machine via LAT
269 latclient = LATClient(service = ServiceName, password = ServicePassword)
270 (rfh, wfh) = latclient.get_fh()
272 #(rfh, wfh) = popen2('../../virtualvend/vvend.py')
274 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
275 sock.connect(('localhost', 5150))
276 rfh = sock.makefile('r')
277 wfh = sock.makefile('w')
278 v = VendingMachine(rfh, wfh)
279 print 'PING is', v.ping()
281 if USE_DB: db = DispenseDatabase(v, DBServer, DBName, DBUser, DBPassword)
286 mk = MessageKeeper(v)
287 mk.set_message(GREETING)
288 time_to_autologout = None
291 last_timeout_refresh = None
294 if USE_DB: db.handle_events()
296 if time_to_autologout != None:
297 time_left = time_to_autologout - time()
298 if time_left < 6 and (last_timeout_refresh is None or last_timeout_refresh > time_left):
299 mk.set_message('LOGOUT: '+str(int(time_left)))
300 last_timeout_refresh = int(time_left)
303 if time_to_autologout != None and time_to_autologout - time() <= 0:
304 time_to_autologout = None
308 mk.set_message(GREETING)
310 if time_to_autologout and not mk.done(): time_to_autologout = None
311 if cur_user == '' and time_to_autologout: time_to_autologout = None
312 if len(cur_pin) == PIN_LENGTH and mk.done() and time_to_autologout == None:
314 time_to_autologout = time() + 15
316 if time_to_idle == None and cur_user == '': time_to_idle = time() + 60
317 if time_to_idle != None and cur_user != '': time_to_idle = None
318 if time_to_idle is not None and time() > time_to_idle: idler.next()
324 e = v.next_event(0.1)
335 mk.set_message(GREETING)
336 elif event == SWITCH:
337 # don't care right now.
341 # complicated key handling here:
342 if len(cur_user) < 5:
345 mk.set_message(GREETING)
347 cur_user += chr(key + ord('0'))
348 mk.set_message('UID: '+cur_user)
349 if len(cur_user) == 5:
351 if not has_good_pin(uid):
353 #[(center('INVALID'), False, 0.7),
354 #(center('PIN'), False, 0.7),
355 #(center('SETUP'), False, 1.0),
356 #(GREETING, False, None)])
358 [(' '*10+'INVALID PIN SETUP'+' '*10, False, 3),
359 (GREETING, False, None)])
364 mk.set_message('PIN: ')
366 elif len(cur_pin) < PIN_LENGTH:
370 mk.set_message(GREETING)
373 mk.set_message('PIN: ')
375 cur_pin += chr(key + ord('0'))
376 mk.set_message('PIN: '+'X'*len(cur_pin))
377 if len(cur_pin) == PIN_LENGTH:
378 username = verify_user_pin(int(cur_user), int(cur_pin))
382 scroll_options(username, mk, True)
387 [(center('BAD PIN'), False, 1.0),
388 (center('SORRY'), False, 0.5),
389 (GREETING, False, None)])
393 elif len(cur_selection) == 0:
399 [(center('BYE!'), False, 1.5),
400 (GREETING, False, None)])
402 cur_selection += chr(key + ord('0'))
403 mk.set_message('SELECT: '+cur_selection)
404 time_to_autologout = None
405 elif len(cur_selection) == 1:
408 time_to_autologout = None
409 scroll_options(username, mk)
412 cur_selection += chr(key + ord('0'))
413 #make_selection(cur_selection)
414 # XXX this should move somewhere else:
415 if cur_selection == '55':
416 mk.set_message('OPENSESAME')
417 ret = os.system('su - "%s" -c "dispense door"'%username)
419 mk.set_message(center('DOOR OPEN'))
421 mk.set_message(center('BAD DOOR'))
423 elif cur_selection == '91':
425 elif cur_selection == '99':
426 scroll_options(username, mk)
429 elif cur_selection[1] == '8':
430 v.display('GOT COKE?')
431 os.system('su - "%s" -c "dispense %s"'%(username, cur_selection[0]))
433 v.display('HERES A '+cur_selection)
434 v.vend(cur_selection)
436 v.display('THANK YOU')
439 time_to_autologout = time() + 8