# vim:ts=4
USE_DB = 0
+USE_MIFARE = 1
import ConfigParser
import sys, os, string, re, pwd, signal, math, syslog
from traceback import format_tb
if USE_DB: import pg
from time import time, sleep, mktime, localtime
-from popen2 import popen2
+from subprocess import Popen, PIPE
from LATClient import LATClient, LATClientException
from SerialClient import SerialClient, SerialClientException
from VendingMachine import VendingMachine, VendingException
from HorizScroll import HorizScroll
from random import random, seed
from Idler import GreetingIdler,TrainIdler,GrayIdler,StringIdler,ClockIdler,FortuneIdler,FileIdler,PipeIdler
-from SnackConfig import get_snacks, get_snack
+from SnackConfig import get_snack#, get_snacks
import socket
from posix import geteuid
+from LDAPConnector import get_uid, set_card_id
CREDITS="""
This vending machine software brought to you by:
SWITCH = 2
KEY = 3
TICK = 4
+MIFARE = 5
(
def scroll_options(username, mk, welcome = False):
if welcome:
+ # Balance checking
+ acct, unused = Popen(['dispense', 'acct', username], close_fds=True, stdout=PIPE).communicate()
+ # this is fucking appalling
+ balance = acct[acct.find("$")+1:acct.find("(")].strip()
+
msg = [(center('WELCOME'), False, TEXT_SPEED),
- (center(username), False, TEXT_SPEED)]
+ (center(username), False, TEXT_SPEED),
+ (center(balance), False, TEXT_SPEED),]
else:
msg = []
choices = ' '*10+'CHOICES: '
- try:
- coke_machine = file('/home/other/coke/coke_contents')
- cokes = coke_machine.readlines()
- coke_machine.close()
- except:
- cokes = []
- pass
+
+ # Get coke contents
+ cokes = []
+ for i in range(0, 7):
+ args = ('dispense', 'iteminfo', 'coke:%i' % i)
+ info, unused = Popen(args, close_fds=True, stdout=PIPE).communicate()
+ m = re.match("\s*[a-z]+:\d+\s+(\d+)\.(\d\d)\s+([^\n]+)", info)
+ cents = int(m.group(1))*100 + int(m.group(2))
+ cokes.append('%i %i %s' % (i, cents, m.group(3)));
+
for c in cokes:
c = c.strip()
(slot_num, price, slot_name) = c.split(' ', 2)
def has_good_pin(uid):
return get_pin(uid) != None
-def verify_user_pin(uid, pin):
- if get_pin(uid) == pin:
+def verify_user_pin(uid, pin, skip_pin_check=False):
+ if skip_pin_check or get_pin(uid) == pin:
info = pwd.getpwuid(uid)
- logging.info('accepted pin for uid %d (%s)'%(uid,info.pw_name))
+ if skip_pin_check:
+ logging.info('accepted mifare for uid %d (%s)'%(uid,info.pw_name))
+ else:
+ logging.info('accepted pin for uid %d (%s)'%(uid,info.pw_name))
return info.pw_name
else:
logging.info('refused pin for uid %d'%(uid))
vstatus.mk.set_message('OPENSESAME')
logging.info('dispensing a door for %s'%vstatus.username)
if geteuid() == 0:
- ret = os.system('su - "%s" -c "dispense door"'%vstatus.username)
+ #ret = os.system('su - "%s" -c "dispense door"'%vstatus.username)
+ ret = os.system('dispense -u "%s" door'%vstatus.username)
else:
ret = os.system('dispense door')
if ret == 0:
return
elif vstatus.cur_selection[1] == '8':
v.display('GOT DRINK?')
- if ((os.system('su - "%s" -c "dispense %s"'%(vstatus.username, vstatus.cur_selection[0])) >> 8) != 0):
+ if ((os.system('dispense -u "%s" coke:%s'%(vstatus.username, vstatus.cur_selection[0])) >> 8) != 0):
v.display('SEEMS NOT')
else:
v.display('GOT DRINK!')
price, shortname, name = get_snack( '--' )
dollarprice = "$%.2f" % ( price / 100.0 )
v.display(vstatus.cur_selection+' - %s'%dollarprice)
- exitcode = os.system('su - "%s" -c "dispense give oday %d"'%(vstatus.username, price)) >> 8
+ exitcode = os.system('dispense -u "%s" give \>snacksales %d "%s"'%(vstatus.username, price, name)) >> 8
+# exitcode = os.system('dispense -u "%s" give \>sales\:snack %d "%s"'%(vstatus.username, price, name)) >> 8
+# exitcode = os.system('dispense -u "%s" snack:%s'%(vstatus.username, vstatus.cur_selection)) >> 8
if (exitcode == 0):
# magic dispense syslog service
syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "vended %s (slot %s) for %s" % (name, vstatus.cur_selection, vstatus.username))
v.vend(vstatus.cur_selection)
v.display('THANK YOU')
+ elif (exitcode == 5): # RV_BALANCE
+ v.display('NO MONEY?')
+ elif (exitcode == 4): # RV_ARGUMENTS (zero give causes arguments)
+ v.display('EMPTY SLOT')
+ elif (exitcode == 1): # RV_BADITEM (Dead slot)
+ v.display('EMPTY SLOT')
else:
syslog.syslog(syslog.LOG_INFO | syslog.LOG_LOCAL4, "failed vending %s (slot %s) for %s (code %d)" % (name, vstatus.cur_selection, vstatus.username, exitcode))
- v.display('NO MONEY?')
+ v.display('UNK ERROR')
sleep(1)
def price_check(v, vstatus):
if vstatus.cur_selection[1] == '8':
- v.display(center('SEE COKE'))
+ args = ('dispense', 'iteminfo', 'coke:' + vstatus.cur_selection[0])
+ info, unused = Popen(args, close_fds=True, stdout=PIPE).communicate()
+ dollarprice = re.match("\s*[a-z]+:\d+\s+(\d+\.\d\d)\s+([^\n]+)", info).group(1)
else:
# first see if it's a named slot
try:
except:
price, shortname, name = get_snack( '--' )
dollarprice = "$%.2f" % ( price / 100.0 )
- v.display(vstatus.cur_selection+' - %s'%dollarprice)
+ v.display(vstatus.cur_selection+' - %s'%dollarprice)
def handle_getting_pin_key(state, event, params, v, vstatus):
else:
vstatus.mk.set_message('UID: '+vstatus.cur_user)
-
- # Easter egg for nikita's birthday -- DGB
- if len(vstatus.cur_user) == 8:
- if vstatus.cur_user != "07051980":
- vstatus.mk.set_messages(
- [(' '*9+'ONE MORE TRY NiKiTa'+' '*10, False, 3)])
- vstatus.cur_user = ''
- reset_idler(v, vstatus, 3)
- return
-
- # Do stuff here
- vstatus.mk.set_messages(
- [(center(' GUILD MAILBOX NUMBER 64 '), False, 20),
- (center(' GUILD MAILBOX NUMBER 64 '), False, 20),
- (center(' GUILD MAILBOX NUMBER 64 '), False, 20),
- (center(' GUILD MAILBOX NUMBER 64 '), False, 20)])
-
- # Reset
- vstatus.cur_user = ''
- vstatus.cur_pin = ''
- #reset_idler(v, vstatus, 10)
- reset_idler(v, vstatus, 2)
- return
- # End easter egg part 1
if len(vstatus.cur_user) == 5:
uid = int(vstatus.cur_user)
- # Easter egg for nikita's birthday -- DGB
- if vstatus.cur_user == '07051':
- if key == 11:
- vstatus.cur_user = ''
- reset_idler(v, vstatus)
- return
-# vstatus.cur_user += chr(key + ord('0'))
- logging.info(' == 5 dob: '+vstatus.cur_user)
- vstatus.mk.set_message('>'+vstatus.cur_user)
- return
- # end easter egg part 2
-
if uid == 0:
logging.info('user '+vstatus.cur_user+' has a bad PIN')
pfalken="""
logging.warning('Leaving open door mode')
v.display("-YUM YUM!-")
+def handle_mifare_event(state, event, params, v, vstatus):
+ card_id = params
+ # Translate card_id into uid.
+ if card_id == None:
+ return
+
+ try:
+ vstatus.cur_user = get_uid(card_id)
+ logging.info('Mapped card id to uid %s'%vstatus.cur_user)
+ vstatus.username = verify_user_pin(int(vstatus.cur_user), None, True)
+ except ValueError:
+ vstatus.username = None
+ if vstatus.username:
+ v.beep(0, False)
+ vstatus.cur_selection = ''
+ vstatus.change_state(STATE_GET_SELECTION)
+ scroll_options(vstatus.username, vstatus.mk, True)
+ return
+ else:
+ v.beep(40, False)
+ vstatus.mk.set_messages(
+ [(center('BAD CARD'), False, 1.0),
+ (center('SORRY'), False, 0.5)])
+ vstatus.cur_user = ''
+ vstatus.cur_pin = ''
+
+ reset_idler(v, vstatus, 2)
+ return
+
+def handle_mifare_add_user_event(state, event, params, v, vstatus):
+ card_id = params
+
+ # Translate card_id into uid.
+ if card_id == None:
+ return
+
+ try:
+ if get_uid(card_id) != None:
+ vstatus.mk.set_messages(
+ [(center('ALREADY'), False, 0.5),
+ (center('ENROLLED'), False, 0.5)])
+
+ # scroll_options(vstatus.username, vstatus.mk)
+ return
+ except ValueError:
+ pass
+
+ logging.info('Enrolling card %s to uid %s (%s)'%(card_id, vstatus.cur_user, vstatus.username))
+ set_card_id(vstatus.cur_user, card_id)
+ vstatus.mk.set_messages(
+ [(center('CARD'), False, 0.5),
+ (center('ENROLLED'), False, 0.5)])
+
+ # scroll_options(vstatus.username, vstatus.mk)
+
def return_to_idle(state,event,params,v,vstatus):
reset_idler(v, vstatus)
vstatus.state_table[(STATE_IDLE,TICK,1)] = handle_idle_tick
vstatus.state_table[(STATE_IDLE,KEY,1)] = handle_idle_key
vstatus.state_table[(STATE_IDLE,DOOR,1)] = handle_door_event
+ vstatus.state_table[(STATE_IDLE,MIFARE,1)] = handle_mifare_event
vstatus.state_table[(STATE_DOOR_OPENING,TICK,1)] = handle_door_idle
vstatus.state_table[(STATE_DOOR_OPENING,DOOR,1)] = handle_door_event
vstatus.state_table[(STATE_DOOR_OPENING,KEY,1)] = do_nothing
+ vstatus.state_table[(STATE_DOOR_OPENING,MIFARE,1)] = do_nothing
vstatus.state_table[(STATE_DOOR_CLOSING,TICK,1)] = return_to_idle
vstatus.state_table[(STATE_DOOR_CLOSING,DOOR,1)] = handle_door_event
vstatus.state_table[(STATE_DOOR_CLOSING,KEY,1)] = do_nothing
+ vstatus.state_table[(STATE_DOOR_CLOSING,MIFARE,1)] = do_nothing
vstatus.state_table[(STATE_GETTING_UID,TICK,1)] = handle_getting_uid_idle
vstatus.state_table[(STATE_GETTING_UID,DOOR,1)] = do_nothing
vstatus.state_table[(STATE_GETTING_UID,KEY,1)] = handle_getting_uid_key
+ vstatus.state_table[(STATE_GETTING_UID,MIFARE,1)] = handle_mifare_event
vstatus.state_table[(STATE_GETTING_PIN,TICK,1)] = handle_getting_pin_idle
vstatus.state_table[(STATE_GETTING_PIN,DOOR,1)] = do_nothing
vstatus.state_table[(STATE_GETTING_PIN,KEY,1)] = handle_getting_pin_key
+ vstatus.state_table[(STATE_GETTING_PIN,MIFARE,1)] = handle_mifare_event
vstatus.state_table[(STATE_GET_SELECTION,TICK,1)] = handle_get_selection_idle
vstatus.state_table[(STATE_GET_SELECTION,DOOR,1)] = do_nothing
vstatus.state_table[(STATE_GET_SELECTION,KEY,1)] = handle_get_selection_key
+ vstatus.state_table[(STATE_GET_SELECTION,MIFARE,1)] = handle_mifare_add_user_event
vstatus.state_table[(STATE_GRANDFATHER_CLOCK,TICK,1)] = handle_idle_grandfather_tick
vstatus.state_table[(STATE_GRANDFATHER_CLOCK,TICK,2)] = handle_grandfather_tick
vstatus.state_table[(STATE_GRANDFATHER_CLOCK,DOOR,2)] = do_nothing
vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,1)] = do_nothing
vstatus.state_table[(STATE_GRANDFATHER_CLOCK,KEY,2)] = do_nothing
+ vstatus.state_table[(STATE_GRANDFATHER_CLOCK,MIFARE,1)] = handle_mifare_event
def get_state_table_handler(vstatus, state, event, counter):
return vstatus.state_table[(state,event,counter)]
return idle_update
def run_forever(rfh, wfh, options, cf):
- v = VendingMachine(rfh, wfh)
+ v = VendingMachine(rfh, wfh, USE_MIFARE)
vstatus = VendState(v)
create_state_table(vstatus)
except DispenseDatabaseException, e:
logging.error('Database error: '+str(e))
-
timeout = time_to_next_update(vstatus)
e = v.next_event(timeout)
(event, params) = e
sock.connect((options.host, options.port))
rfh = sock.makefile('r')
wfh = sock.makefile('w')
+ global USE_MIFARE
+ USE_MIFARE = 0
return rfh, wfh
from optparse import OptionParser
op = OptionParser(usage="%prog [OPTION]...")
- op.add_option('-f', '--config-file', default='/etc/dispense/servers.conf', metavar='FILE', dest='config_file', help='use the specified config file instead of /etc/dispense/servers.conf')
+ op.add_option('-f', '--config-file', default='/etc/dispense2/servers.conf', metavar='FILE', dest='config_file', help='use the specified config file instead of /etc/dispense/servers.conf')
op.add_option('--serial', action='store_true', default=True, dest='use_serial', help='use the serial port')
op.add_option('--lat', action='store_true', default=False, dest='use_lat', help='use LAT')
op.add_option('--virtualvend', action='store_false', default=True, dest='use_serial', help='use the virtual vending server instead of LAT')
sleep(5)
continue
+# run_forever(rfh, wfh, options, config_opts)
+
try:
run_forever(rfh, wfh, options, config_opts)
except VendingException: