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
15 from Idler import TrainIdler,GrayIdler
17 GREETING = 'UCC SNACKS'
24 class DispenseDatabase:
25 def __init__(self, vending_machine, host, name, user, password):
26 self.vending_machine = vending_machine
27 self.db = pg.DB(dbname = name, host = host, user = user, passwd = password)
28 self.db.query('LISTEN vend_requests')
30 def process_requests(self):
32 query = 'SELECT request_id, request_slot FROM vend_requests WHERE request_handled = false'
34 outstanding = self.db.query(query).getresult()
35 except (pg.error,), db_err:
36 sys.stderr.write('Failed to query database: %s\n'%(db_err.strip()))
38 for (id, slot) in outstanding:
39 (worked, code, string) = self.vending_machine.vend(slot)
40 print (worked, code, string)
42 query = 'SELECT vend_success(%s)'%id
43 self.db.query(query).getresult()
45 query = 'SELECT vend_failed(%s)'%id
46 self.db.query(query).getresult()
48 def handle_events(self):
49 notifier = self.db.getnotify()
50 while notifier is not None:
51 self.process_requests()
52 notify = self.db.getnotify()
54 def scroll_options(username, mk, welcome = False):
56 msg = [(center('WELCOME'), False, 0.8),
57 (center(username), False, 0.8)]
60 choices = ' '*10+'CHOICES: '
62 coke_machine = file('/home/other/coke/coke_contents')
63 cokes = coke_machine.readlines()
70 (slot_num, price, slot_name) = c.split(' ', 2)
71 if slot_name == 'dead': continue
72 choices += '%s8-%s (%sc) '%(slot_num, slot_name, price)
74 choices += 'OR A SNACK. '
75 choices += '99 TO READ AGAIN. '
77 msg.append((choices, False, None))
82 info = pwd.getpwuid(uid)
85 if info.pw_dir == None: return False
86 pinfile = os.path.join(info.pw_dir, '.pin')
99 if not re.search('^'+'[0-9]'*PIN_LENGTH+'$', pinstr):
103 def has_good_pin(uid):
104 return get_pin(uid) != None
106 def verify_user_pin(uid, pin):
107 if get_pin(uid) == pin:
108 info = pwd.getpwuid(uid)
113 def door_open_mode(vending_machine):
114 print "Entering open door mode"
115 v.display("-FEED ME-")
121 if params == 1: # door closed
122 v.display("-YUM YUM!-")
128 messages = [' WASSUP! ', 'PINK FISH ', ' SECRETS ', ' ESKIMO ', ' FORTUNES ', 'MORE MONEY']
129 choice = int(random()*len(messages))
130 msg = messages[choice]
131 left = range(len(msg))
132 for i in range(len(msg)):
133 if msg[i] == ' ': left.remove(i)
137 for i in range(0, len(msg)):
143 s += chr(int(random()*26)+ord('A'))
152 return ' '*((LEN-len(str))/2)+str
155 def __init__(self, vendie):
156 # Each element of scrolling_message should be a 3-tuple of
157 # ('message', True/False if it is to be repeated, time to display)
158 self.scrolling_message = []
160 self.next_update = None
162 def set_message(self, string):
163 self.scrolling_message = [(string, False, None)]
164 self.update_display(True)
166 def set_messages(self, strings):
167 self.scrolling_message = strings
168 self.update_display(True)
170 def update_display(self, forced = False):
171 if not forced and self.next_update != None and time() < self.next_update:
173 if len(self.scrolling_message) > 0:
174 if len(self.scrolling_message[0][0]) > 10:
175 (m, r, t) = self.scrolling_message[0]
177 exp = HorizScroll(m).expand(padding = 0, wraparound = True)
184 del self.scrolling_message[0]
185 self.scrolling_message = a + self.scrolling_message
186 newmsg = self.scrolling_message[0]
187 if newmsg[2] != None:
188 self.next_update = time() + newmsg[2]
190 self.next_update = None
191 self.v.display(self.scrolling_message[0][0])
192 if self.scrolling_message[0][1]:
193 self.scrolling_message.append(self.scrolling_message[0])
194 del self.scrolling_message[0]
197 return len(self.scrolling_message) == 0
199 if __name__ == '__main__':
201 cp.read('/etc/dispense/servers.conf')
202 DBServer = cp.get('Database', 'Server')
203 DBName = cp.get('Database', 'Name')
204 DBUser = cp.get('VendingMachine', 'DBUser')
205 DBPassword = cp.get('VendingMachine', 'DBPassword')
207 ServiceName = cp.get('VendingMachine', 'ServiceName')
208 ServicePassword = cp.get('VendingMachine', 'Password')
209 # Open vending machine via LAT
211 latclient = LATClient(service = ServiceName, password = ServicePassword)
212 (rfh, wfh) = latclient.get_fh()
214 #(rfh, wfh) = popen2('../../virtualvend/vvend.py')
216 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
217 sock.connect(('localhost', 5150))
218 rfh = sock.makefile('r')
219 wfh = sock.makefile('w')
220 v = VendingMachine(rfh, wfh)
221 print 'PING is', v.ping()
223 if USE_DB: db = DispenseDatabase(v, DBServer, DBName, DBUser, DBPassword)
228 mk = MessageKeeper(v)
229 mk.set_message(GREETING)
230 time_to_autologout = None
231 #idler = TrainIdler(v)
232 #idler = GrayIdler(v)
233 idler = GrayIdler(v,one="*",zero="-")
235 last_timeout_refresh = None
238 if USE_DB: db.handle_events()
240 if time_to_autologout != None:
241 time_left = time_to_autologout - time()
242 if time_left < 6 and (last_timeout_refresh is None or last_timeout_refresh > time_left):
243 mk.set_message('LOGOUT: '+str(int(time_left)))
244 last_timeout_refresh = int(time_left)
247 if time_to_autologout != None and time_to_autologout - time() <= 0:
248 time_to_autologout = None
252 mk.set_message(GREETING)
254 if time_to_autologout and not mk.done(): time_to_autologout = None
255 if cur_user == '' and time_to_autologout: time_to_autologout = None
256 if len(cur_pin) == PIN_LENGTH and mk.done() and time_to_autologout == None:
258 time_to_autologout = time() + 15
260 if time_to_idle == None and cur_user == '': time_to_idle = time() + 60
261 if time_to_idle != None and cur_user != '': time_to_idle = None
262 if time_to_idle is not None and time() > time_to_idle: idler.next()
268 e = v.next_event(0.1)
279 mk.set_message(GREETING)
280 elif event == SWITCH:
281 # don't care right now.
285 # complicated key handling here:
286 if len(cur_user) < 5:
289 mk.set_message(GREETING)
291 cur_user += chr(key + ord('0'))
292 mk.set_message('UID: '+cur_user)
293 if len(cur_user) == 5:
295 if not has_good_pin(uid):
297 #[(center('INVALID'), False, 0.7),
298 #(center('PIN'), False, 0.7),
299 #(center('SETUP'), False, 1.0),
300 #(GREETING, False, None)])
302 [(' '*10+'INVALID PIN SETUP'+' '*10, False, 3),
303 (GREETING, False, None)])
308 mk.set_message('PIN: ')
310 elif len(cur_pin) < PIN_LENGTH:
314 mk.set_message(GREETING)
317 mk.set_message('PIN: ')
319 cur_pin += chr(key + ord('0'))
320 mk.set_message('PIN: '+'X'*len(cur_pin))
321 if len(cur_pin) == PIN_LENGTH:
322 username = verify_user_pin(int(cur_user), int(cur_pin))
326 scroll_options(username, mk, True)
331 [(center('BAD PIN'), False, 1.0),
332 (center('SORRY'), False, 0.5),
333 (GREETING, False, None)])
337 elif len(cur_selection) == 0:
343 [(center('BYE!'), False, 1.5),
344 (GREETING, False, None)])
346 cur_selection += chr(key + ord('0'))
347 mk.set_message('SELECT: '+cur_selection)
348 time_to_autologout = None
349 elif len(cur_selection) == 1:
352 time_to_autologout = None
353 scroll_options(username, mk)
356 cur_selection += chr(key + ord('0'))
357 #make_selection(cur_selection)
358 # XXX this should move somewhere else:
359 if cur_selection == '55':
360 mk.set_message('OPENSESAME')
361 ret = os.system('su - "%s" -c "dispense door"'%username)
363 mk.set_message(center('DOOR OPEN'))
365 mk.set_message(center('BAD DOOR'))
367 elif cur_selection == '91':
369 elif cur_selection == '99':
370 scroll_options(username, mk)
373 elif cur_selection[1] == '8':
374 v.display('GOT COKE?')
375 os.system('su - "%s" -c "dispense %s"'%(username, cur_selection[0]))
377 v.display('HERES A '+cur_selection)
378 v.vend(cur_selection)
380 v.display('THANK YOU')
383 time_to_autologout = time() + 8