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: '
60 coke_machine = file('/home/other/coke/coke_contents')
61 cokes = coke_machine.readlines()
68 (slot_num, price, slot_name) = c.split(' ', 2)
69 if slot_name == 'dead': continue
70 choices += '%s8-%s (%sc) '%(slot_num, slot_name, price)
72 choices += 'OR A SNACK. '
73 choices += '99 TO READ AGAIN. '
75 msg.append((choices, False, None))
80 info = pwd.getpwuid(uid)
83 if info.pw_dir == None: return False
84 pinfile = os.path.join(info.pw_dir, '.pin')
97 if not re.search('^'+'[0-9]'*PIN_LENGTH+'$', pinstr):
101 def has_good_pin(uid):
102 return get_pin(uid) != None
104 def verify_user_pin(uid, pin):
105 if get_pin(uid) == pin:
106 info = pwd.getpwuid(uid)
111 def door_open_mode(vending_machine):
112 print "Entering open door mode"
113 v.display("DOOR OPEN")
119 if params == 1: # door closed
120 v.display("BYE BYE!")
126 return ' '*((LEN-len(str))/2)+str
129 def __init__(self, vendie):
130 # Each element of scrolling_message should be a 3-tuple of
131 # ('message', True/False if it is to be repeated, time to display)
132 self.scrolling_message = []
134 self.next_update = None
136 def set_message(self, string):
137 self.scrolling_message = [(string, False, None)]
138 self.update_display(True)
140 def set_messages(self, strings):
141 self.scrolling_message = strings
142 self.update_display(True)
144 def update_display(self, forced = False):
145 if not forced and self.next_update != None and time() < self.next_update:
147 if len(self.scrolling_message) > 0:
148 if len(self.scrolling_message[0][0]) > 10:
149 (m, r, t) = self.scrolling_message[0]
151 exp = HorizScroll(m).expand(padding = 0, wraparound = False)
158 del self.scrolling_message[0]
159 self.scrolling_message = a + self.scrolling_message
160 newmsg = self.scrolling_message[0]
161 if newmsg[2] != None:
162 self.next_update = time() + newmsg[2]
164 self.next_update = None
165 self.v.display(self.scrolling_message[0][0])
166 if self.scrolling_message[0][1]:
167 self.scrolling_message.append(self.scrolling_message[0])
168 del self.scrolling_message[0]
171 return len(self.scrolling_message) == 0
173 if __name__ == '__main__':
175 cp.read('/etc/dispense/servers.conf')
176 DBServer = cp.get('Database', 'Server')
177 DBName = cp.get('Database', 'Name')
178 DBUser = cp.get('VendingMachine', 'DBUser')
179 DBPassword = cp.get('VendingMachine', 'DBPassword')
181 ServiceName = cp.get('VendingMachine', 'ServiceName')
182 ServicePassword = cp.get('VendingMachine', 'Password')
183 # Open vending machine via LAT
185 latclient = LATClient(service = ServiceName, password = ServicePassword)
186 (rfh, wfh) = latclient.get_fh()
188 #(rfh, wfh) = popen2('../../virtualvend/vvend.py')
190 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
191 sock.connect(('localhost', 5150))
192 rfh = sock.makefile('r')
193 wfh = sock.makefile('w')
194 v = VendingMachine(rfh, wfh)
195 print 'PING is', v.ping()
197 if USE_DB: db = DispenseDatabase(v, DBServer, DBName, DBUser, DBPassword)
202 mk = MessageKeeper(v)
203 mk.set_message(GREETING)
204 logout_timeout = None
205 last_timeout_refresh = None
208 if USE_DB: db.handle_events()
210 if logout_timeout != None:
211 time_left = logout_timeout - time()
212 if time_left < 5 and (last_timeout_refresh is None or last_timeout_refresh > time_left):
213 mk.set_message('LOGOUT: '+str(int(time_left)))
214 last_timeout_refresh = int(time_left)
217 if logout_timeout != None and logout_timeout - time() <= 0:
218 logout_timeout = None
222 mk.set_message(GREETING)
224 if logout_timeout and not mk.done(): logout_timeout = None
225 if len(cur_pin) == PIN_LENGTH and mk.done() and logout_timeout == None:
227 logout_timeout = time() + 15
233 e = v.next_event(0.1)
243 mk.set_message(GREETING)
244 elif event == SWITCH:
245 # don't care right now.
249 # complicated key handling here:
250 if len(cur_user) < 5:
253 mk.set_message(GREETING)
255 cur_user += chr(key + ord('0'))
256 mk.set_message('UID: '+cur_user)
257 if len(cur_user) == 5:
259 if not has_good_pin(uid):
261 [(center('INVALID'), False, 0.7),
262 (center('PIN'), False, 0.7),
263 (center('SETUP'), False, 1.0),
264 (GREETING, False, None)])
269 mk.set_message('PIN: ')
271 elif len(cur_pin) < PIN_LENGTH:
275 mk.set_message(GREETING)
278 mk.set_message('PIN: ')
280 cur_pin += chr(key + ord('0'))
281 mk.set_message('PIN: '+'X'*len(cur_pin))
282 if len(cur_pin) == PIN_LENGTH:
283 username = verify_user_pin(int(cur_user), int(cur_pin))
287 scroll_options(username, mk, True)
292 [(center('BAD PIN'), False, 1.0),
293 (center('SORRY'), False, 0.5),
294 (GREETING, False, None)])
298 elif len(cur_selection) == 0:
305 mk.set_message(GREETING)
307 cur_selection += chr(key + ord('0'))
308 mk.set_message('SELECT: '+cur_selection)
309 elif len(cur_selection) == 1:
312 logout_timeout = None
313 scroll_options(username, mk)
316 cur_selection += chr(key + ord('0'))
317 #make_selection(cur_selection)
318 # XXX this should move somewhere else:
319 if cur_selection == '55':
320 v.display('GOT DOOR?')
321 os.system('su - "%s" -c "dispense door"'%username)
322 elif cur_selection == '99':
323 scroll_options(username, mk)
326 elif cur_selection[1] == '8':
327 v.display('GOT COKE?')
328 os.system('su - "%s" -c "dispense %s"'%(username, cur_selection[0]))
330 v.display('HERES A '+cur_selection)
331 v.vend(cur_selection)
333 v.display('THANK YOU')
336 logout_timeout = time() + 10