4 import sys, os, string, re, pwd
6 from time import time, sleep
7 from popen2 import popen2
8 from LATClient import LATClient
9 from VendingMachine import VendingMachine
10 from ConfigParser import ConfigParser
11 from HorizScroll import HorizScroll
13 GREETING = 'UCC SNACKS'
20 class DispenseDatabase:
21 def __init__(self, vending_machine, host, name, user, password):
22 self.vending_machine = vending_machine
23 self.db = pg.DB(dbname = name, host = host, user = user, passwd = password)
24 self.db.query('LISTEN vend_requests')
26 def process_requests(self):
28 query = 'SELECT request_id, request_slot FROM vend_requests WHERE request_handled = false'
30 outstanding = self.db.query(query).getresult()
31 except (pg.error,), db_err:
32 sys.stderr.write('Failed to query database: %s\n'%(db_err.strip()))
34 for (id, slot) in outstanding:
35 (worked, code, string) = self.vending_machine.vend(slot)
36 print (worked, code, string)
38 query = 'SELECT vend_success(%s)'%id
39 self.db.query(query).getresult()
41 query = 'SELECT vend_failed(%s)'%id
42 self.db.query(query).getresult()
44 def handle_events(self):
45 notifier = self.db.getnotify()
46 while notifier is not None:
47 self.process_requests()
48 notify = self.db.getnotify()
50 def scroll_options(username, mk, welcome = False):
52 msg = [(center('WELCOME'), False, 0.8),
53 (center(username), False, 0.8)]
56 choices = ' '*10+'CHOICES: '
57 coke_machine = file('/home/other/coke/coke_contents')
58 cokes = coke_machine.readlines()
61 (slot_num, price, slot_name) = c.split(' ', 2)
62 if slot_name == 'dead': continue
63 choices += '%s8-%s (%sc) '%(slot_num, slot_name, price)
65 choices += 'OR A SNACK. '
66 choices += '99 TO READ AGAIN.'
67 msg.append((choices, False, None))
72 info = pwd.getpwuid(uid)
75 if info.pw_dir == None: return False
76 pinfile = os.path.join(info.pw_dir, '.pin')
89 if not re.search('^'+'[0-9]'*PIN_LENGTH+'$', pinstr):
93 def has_good_pin(uid):
94 return get_pin(uid) != None
96 def verify_user_pin(uid, pin):
97 if get_pin(uid) == pin:
98 info = pwd.getpwuid(uid)
103 def door_open_mode(vending_machine):
104 print "Entering open door mode"
105 v.display("DOOR OPEN")
111 if params == 1: # door closed
112 v.display("BYE BYE!")
118 return ' '*((LEN-len(str))/2)+str
121 def __init__(self, vendie):
122 # Each element of scrolling_message should be a 3-tuple of
123 # ('message', True/False if it is to be repeated, time to display)
124 self.scrolling_message = []
126 self.next_update = None
128 def set_message(self, string):
129 self.scrolling_message = [(string, False, None)]
130 self.update_display(True)
132 def set_messages(self, strings):
133 self.scrolling_message = strings
134 self.update_display(True)
136 def update_display(self, forced = False):
137 if not forced and self.next_update != None and time() < self.next_update:
139 if len(self.scrolling_message) > 0:
140 if len(self.scrolling_message[0][0]) > 10:
141 (m, r, t) = self.scrolling_message[0]
143 exp = HorizScroll(m).expand(padding = 10)
150 del self.scrolling_message[0]
151 self.scrolling_message = a + self.scrolling_message
152 newmsg = self.scrolling_message[0]
153 if newmsg[2] != None:
154 self.next_update = time() + newmsg[2]
156 self.next_update = None
157 self.v.display(self.scrolling_message[0][0])
158 if self.scrolling_message[0][1]:
159 self.scrolling_message.append(self.scrolling_message[0])
160 del self.scrolling_message[0]
163 return len(self.scrolling_message) == 0
165 if __name__ == '__main__':
167 cp.read('/etc/dispense/servers.conf')
168 DBServer = cp.get('Database', 'Server')
169 DBName = cp.get('Database', 'Name')
170 DBUser = cp.get('VendingMachine', 'DBUser')
171 DBPassword = cp.get('VendingMachine', 'DBPassword')
173 ServiceName = cp.get('VendingMachine', 'ServiceName')
174 ServicePassword = cp.get('VendingMachine', 'Password')
175 # Open vending machine via LAT
177 latclient = LATClient(service = ServiceName, password = ServicePassword)
178 (rfh, wfh) = latclient.get_fh()
180 #(rfh, wfh) = popen2('../../virtualvend/vvend.py')
182 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
183 sock.connect(('localhost', 5150))
184 rfh = sock.makefile('r')
185 wfh = sock.makefile('w')
186 v = VendingMachine(rfh, wfh)
187 print 'PING is', v.ping()
189 db = DispenseDatabase(v, DBServer, DBName, DBUser, DBPassword)
194 mk = MessageKeeper(v)
195 mk.set_message(GREETING)
196 logout_timeout = None
197 last_timeout_refresh = None
202 if logout_timeout != None:
203 time_left = logout_timeout - time()
204 if time_left < 10 and (last_timeout_refresh > time_left or last_timeout_refresh is None):
205 mk.set_message('LOGOUT: '+str(int(time_left)))
206 last_timeout_refresh = int(time_left)
208 if logout_timeout != None and logout_timeout - time() <= 0:
209 logout_timeout = None
213 mk.set_message(GREETING)
215 if logout_timeout and not mk.done(): logout_timeout = None
216 if cur_user and cur_pin and mk.done() and logout_timeout == None:
218 logout_timeout = time() + 10
224 e = v.next_event(0.1)
234 mk.set_message(GREETING)
235 elif event == SWITCH:
236 # don't care right now.
240 # complicated key handling here:
241 if len(cur_user) < 5:
244 mk.set_message(GREETING)
246 cur_user += chr(key + ord('0'))
247 mk.set_message('UID: '+cur_user)
248 if len(cur_user) == 5:
250 if not has_good_pin(uid):
252 [(center('INVALID'), False, 0.7),
253 (center('PIN'), False, 0.7),
254 (center('SETUP'), False, 1.0),
255 (GREETING, False, None)])
260 mk.set_message('PIN: ')
262 elif len(cur_pin) < PIN_LENGTH:
266 mk.set_message(GREETING)
269 mk.set_message('PIN: ')
271 cur_pin += chr(key + ord('0'))
272 mk.set_message('PIN: '+'X'*len(cur_pin))
273 if len(cur_pin) == PIN_LENGTH:
274 username = verify_user_pin(int(cur_user), int(cur_pin))
278 scroll_options(username, mk, True)
283 [(center('BAD PIN'), False, 1.0),
284 (center('SORRY'), False, 0.5),
285 (GREETING, False, None)])
289 elif len(cur_selection) == 0:
296 mk.set_message(GREETING)
298 cur_selection += chr(key + ord('0'))
299 mk.set_message('SELECT: '+cur_selection)
300 elif len(cur_selection) == 1:
303 scroll_options(username, mk)
306 cur_selection += chr(key + ord('0'))
307 #make_selection(cur_selection)
308 # XXX this should move somewhere else:
309 if cur_selection == '55':
310 v.display('GOT DOOR?')
311 os.system('su - "%s" -c "dispense door"'%username)
312 elif cur_selection == '99':
313 scroll_options(username, mk)
316 elif cur_selection[1] == '8':
317 v.display('GOT COKE?')
318 os.system('su - "%s" -c "dispense %s"'%(username, cur_selection[0]))
320 v.display('HERES A '+cur_selection)
321 v.vend(cur_selection)
323 v.display('THANK YOU')
326 logout_timeout = time() + 10