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 ']
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 < 5 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)
334 mk.set_message(GREETING)
335 elif event == SWITCH:
336 # don't care right now.
340 # complicated key handling here:
341 if len(cur_user) < 5:
344 mk.set_message(GREETING)
346 cur_user += chr(key + ord('0'))
347 mk.set_message('UID: '+cur_user)
348 if len(cur_user) == 5:
350 if not has_good_pin(uid):
352 [(center('INVALID'), False, 0.7),
353 (center('PIN'), False, 0.7),
354 (center('SETUP'), False, 1.0),
355 (GREETING, False, None)])
360 mk.set_message('PIN: ')
362 elif len(cur_pin) < PIN_LENGTH:
366 mk.set_message(GREETING)
369 mk.set_message('PIN: ')
371 cur_pin += chr(key + ord('0'))
372 mk.set_message('PIN: '+'X'*len(cur_pin))
373 if len(cur_pin) == PIN_LENGTH:
374 username = verify_user_pin(int(cur_user), int(cur_pin))
378 scroll_options(username, mk, True)
383 [(center('BAD PIN'), False, 1.0),
384 (center('SORRY'), False, 0.5),
385 (GREETING, False, None)])
389 elif len(cur_selection) == 0:
396 mk.set_message(GREETING)
398 cur_selection += chr(key + ord('0'))
399 mk.set_message('SELECT: '+cur_selection)
400 time_to_autologout = None
401 elif len(cur_selection) == 1:
404 time_to_autologout = None
405 scroll_options(username, mk)
408 cur_selection += chr(key + ord('0'))
409 #make_selection(cur_selection)
410 # XXX this should move somewhere else:
411 if cur_selection == '55':
412 v.display('GOT DOOR?')
413 os.system('su - "%s" -c "dispense door"'%username)
414 elif cur_selection == '91':
416 elif cur_selection == '99':
417 scroll_options(username, mk)
420 elif cur_selection[1] == '8':
421 v.display('GOT COKE?')
422 os.system('su - "%s" -c "dispense %s"'%(username, cur_selection[0]))
424 v.display('HERES A '+cur_selection)
425 v.vend(cur_selection)
427 v.display('THANK YOU')
430 time_to_autologout = time() + 10