root / pykota / trunk / contributed / itcprint / itcxfer.py @ 3199

Revision 3199, 12.6 kB (checked in by jerome, 17 years ago)

Added George Farris' work, under the terms of the GNU GPL v3.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
Line 
1#! /usr/bin/python
2#
3# itcprint.py
4# (c) 2007 George Farris <farrisg@shaw.ca>     
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19#
20
21
22# This documents and provides a demo of the protocol used to maniplulate
23# the ITC Print  2015 smart card reader, http://www.itcsystems.com/smart-card-stored.html
24# The  2015 is connected to the serial port of the PC to charge for things like
25# computer time usage, pay-for-print, cash registers sales, etc.
26#
27# The following description assumes that "Host" is the PC and Reader is the 2015
28#
29# -----------------------------------------------------------------------------------------------------
30# Poll card reader for indication of card insertion
31# -----------------------------------------------------------------------------------------------------
32#   Transmit from Host
33#     Char line      : <STX><NUL><SOH><SOH><ETX><NUL><BEL><EOT>
34#     Hex translation: 0x02 0x00 0x01 0x01 0x03 0x00 0x07 0x04
35#   Receive from Reader
36#     No card inserted
37#       Char line      : <STX><NUL><SOH>@<ETX><NUL>F<EOT>
38#       Hex translation: 0x02 0x00 0x01 0x40 0x03 0x00 0x46 0x04
39#     Card Inserted
40#       Char line      : <STX><NUL><SOH>@<ETX><NUL>F<EOT>
41#       Hex translation: 0x02 0x00 0x01 0x40 0x03 0x00 0x46 0x04
42# =====================================================================================================
43
44
45# -----------------------------------------------------------------------------------------------------
46# Request current dollar(1) value stored on card
47# -----------------------------------------------------------------------------------------------------
48#   Transmit from Host
49#     Char line      : <STX><NUL><SOH>!<ETX><NUL>'<EOT>
50#     Hex translation: 0x02 0x00 0x01 0x21 0x03 0x00 0x27 0x04
51#   Receive from Reader
52#     Char line      : <STX><NUL><SOH><NUL><NUL><NUL><NUL><NUL><NUL><DLE>h<SOH><ETX><NUL><DEL><EOT>
53#     Hex translation: 0x02 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x10 0x68 0x01 0x03 0x00 0x7F 0x04
54#                                     [  Dollar value in this case 0x10 0x68 ]
55#                                     [     0x1068 = 4200 = $4.20            ]
56#                                     [______________________________________]
57# =====================================================================================================
58
59
60# -----------------------------------------------------------------------------------------------------
61# Update Reader with new dollar value - must be less than what is stored on card
62# -----------------------------------------------------------------------------------------------------
63#   Transmit from Host
64#     Char line      : <STX><NUL><SOH>$<NUL><NUL><NUL><NUL><NUL><NUL><DLE><EOT><SOH><ETX><NUL>?<FF>
65#     Hex translation: 0x02 0x00 0x01 0x24 0x00 0x00 0x00 0x00 0x00 0x00 0x10 0x04 0x01 0x03 0x00 0x3F 0x0C
66#                                          [  Dollar value in this case 0x10 0x04 ]
67#                                          [     0x1004 = 4100 = $4.10            ]
68#                                          [______________________________________]
69#
70#   Receive from successful transaction from Reader
71#     Char line      : <STX><NUL><SOH><SYN><ETX><NUL><FS><BS>
72#     Hex translation: 0x02 0x00 0x01 0x16 0x03 0x00 0x1C 0x08
73# =====================================================================================================
74
75
76# -----------------------------------------------------------------------------------------------------
77# Eject card from Reader
78# -----------------------------------------------------------------------------------------------------
79#   Transmit from Host
80#     Char line      : <STX><NUL><SOH><SPACE><ETX><NUL>&<EOT>
81#     Hex translation: 0x02 0x00 0x01 0x20 0x03 0x00 0x26 0x04
82#   Receive from Reader
83#     Char line      : <STX><NUL><SOH><SYN><ETX><NUL><FS><BS>
84#     Hex translation: 0x02 0x00 0x01 0x16 0x03 0x00 0x1C 0x08
85# =====================================================================================================
86
87# (1) Currency can be set from the card reader keypad
88
89import sys, os, serial, string, binascii, time
90import gtk, gtk.glade, gobject, pg
91
92# Database server settings
93HOST = 'localhost'
94PORT = 5432
95DBNAME = 'pykota'
96USER = 'pykotaadmin'
97PASS = 'secret'
98
99
100
101class gui:
102        def __init__(self):
103                self.cardstate = 0  # 0 not inserted, 1 inserted
104               
105                self.gladefile = "itcprint.glade" 
106                self.xml = gtk.glade.XML(self.gladefile) 
107
108                self.window = self.xml.get_widget("MainWindow")
109                self.utext = self.xml.get_widget("UsernameEntry")
110                self.cardlabel = self.xml.get_widget("CardBalanceLabel")
111                self.printlabel = self.xml.get_widget("PrintBalanceLabel")
112                self.spinbutton = self.xml.get_widget("Spinbutton")
113               
114                self.cardlabel.set_label('<big><b>unknown</b></big>')
115                self.printlabel.set_label('<big><b>unknown</b></big>')
116               
117                self.cardbalance = ''
118                self.username = ''
119                self.addbalance = ''
120                self.pykotauid = ''
121                self.pykotabalance = 0.0
122               
123                # TODO put try  except around here
124                #connect([dbname], [host], [port], [opt], [tty], [user], [passwd])
125                try:
126                        self.sql = pg.connect(dbname=DBNAME, host=HOST, port=PORT, user=USER, passwd=PASS)
127                except:
128                        pass
129                               
130#               query = self.sql.query("""SELECT printername FROM printers WHERE printername='cc200-LaserJet' """)
131                #query = db.get(printers, "cc200-laserjet")
132#               if len(query.getresult()) > 0:
133#                       d2 = query.dictresult()
134#                       print d2 #['username']
135
136                self.sc = smartcard(self.sql)
137               
138                #If you wanted to pass an argument, you would use a tuple like this:
139        # dic = { "on button1_clicked" : (self.button1_clicked, arg1,arg2) }
140                dic = { "on_TransferButton_clicked" : self.xferbutton_clicked,
141                                "on_GetbalanceButton_clicked" : self.getcardbalance_clicked,
142                                "on_EjectButton_clicked" : self.ejectbutton_clicked,
143                                "on_quit_activate" : (gtk.main_quit),
144                                "on_UsernameEntry_changed" : self.username_changed,
145                                "on_UsernameEntry_focus_out_event" : self.username_entered,
146                                "on_ItcPrint_destroy" : (gtk.main_quit) }
147                               
148                self.xml.signal_autoconnect (dic)
149               
150                return
151
152        # I might want to do username search as you type later
153        def username_changed (self, widget):
154                print "Text is now ->", self.utext.get_text()
155
156        def username_entered (self, widget, event):
157                self.username = self.utext.get_text()
158                print "Username is ->", self.username
159                # This is where we need to look up username in wbinfo
160               
161                try:
162                        query = self.sql.query("SELECT id FROM users WHERE username='%s'" % (self.username))
163                        self.pykotauid = (query.dictresult()[0])['id']
164                        print "User ID is  ->", self.pykotauid
165                except:
166                        print "Username is invalid"
167                        result = gtk.RESPONSE_CANCEL
168                        dlg = gtk.MessageDialog(None,gtk.DIALOG_MODAL |
169                                gtk.DIALOG_DESTROY_WITH_PARENT,gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, 
170                                "Your username is invalid or does not exist.\nPlease try re-entering it", )
171                        result = dlg.run()
172                        dlg.destroy()
173                try:
174                        query = self.sql.query("SELECT balance FROM users WHERE id='%s'" % (self.pykotauid))
175                        self.pykotabalance = float((query.dictresult()[0])['balance'])
176                        self.printlabel.set_label("%s%.2f%s" % ("<big><b>$",self.pykotabalance,"</b></big>"))
177                except:
178                        print "balance sql error..."
179                try:
180                        query = self.sql.query("SELECT lifetimepaid FROM users WHERE id='%s'" % (self.pykotauid))
181                        self.pykotalifebalance = float((query.dictresult()[0])['lifetimepaid'])
182                        print "%s%.2f" % ("$", self.pykotalifebalance)
183                except:
184                        print "lifetimepaid sql error..."
185
186
187
188        def xferbutton_clicked (self, widget):
189                print "xfer button clicked...."
190                self.addbalance = self.spinbutton.get_value()
191                newbalance = self.addbalance + self.pykotabalance
192                lifetimebalance = self.pykotalifebalance + self.addbalance
193                self.sc.set_balance(newbalance, lifetimebalance, self.pykotauid)
194                self.ejectbutton_clicked(None)
195                               
196        def getcardbalance_clicked(self, widget):
197                if self.sc.checkforcardready():
198                        self.sc.waitforcardready()
199                        self.cardbalance = self.sc.get_balance()
200                        self.cardlabel.set_label("%s%.2f%s" % ("<big><b>$",self.cardbalance,"</b></big>"))
201                        self.cardstate = 1
202                        self.source_id = gobject.timeout_add(2000, self.sc.inhibit_eject)
203                        self.spinbutton.set_range(0,float(self.cardbalance))
204                       
205        def ejectbutton_clicked(self, widget):
206                # TODO put a pop dialog here
207                self.sc.eject_card()
208                self.cardlabel.set_label('<big><b>unknown</b></big>')
209                self.printlabel.set_label('<big><b>unknown</b></big>')
210                self.cardstate = 0
211                self.cardbalance = 0.0
212                self.username = ''
213                self.utext.set_text('')
214                self.addbalance = 0.0
215                self.pykotabalance = 0.0
216                self.pykotalifebalance = 0.0
217                self.spinbutton.set_range(0,0)
218               
219                # Is it possible this might not be set
220                try:
221                        gobject.source_remove(self.source_id)
222                except:
223                        pass
224               
225               
226               
227class smartcard:
228        def __init__(self, sql):
229                self.ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
230                self.scsql = sql
231               
232        # Need comms to contiune to keep card in machine.
233        # This loop keeps the card in until it stops so basically the print
234        # job can release the card after it is finished
235        def checkforcardready(self):
236                self.ser.write(binascii.a2b_hex("0200010103000704"))
237                s = self.ser.read(8)
238
239                if binascii.b2a_hex(s) == "0200014003004604":
240                        result = gtk.RESPONSE_CANCEL
241                        dlg = gtk.MessageDialog(None,gtk.DIALOG_MODAL |
242                                gtk.DIALOG_DESTROY_WITH_PARENT,gtk.MESSAGE_INFO, gtk.BUTTONS_OK_CANCEL, "Please insert your card...", )
243                        result = dlg.run()
244                        if (result==gtk.RESPONSE_OK):
245                                dlg.destroy()
246                                return 1
247                        else:
248                                dlg.destroy()
249                                return 0
250                if binascii.b2a_hex(s) == "0200016c0300721c":
251                        return 1
252                               
253        def waitforcardready(self):
254                print "  Waiting for card to be inserted..."
255                self.ser.write(binascii.a2b_hex("0200010103000704"))
256                s = self.ser.read(8)
257
258                while binascii.b2a_hex(s) == "0200014003004604":
259                        #time.sleep(2)
260                        #print binascii.b2a_hex(s)
261                        self.ser.write(binascii.a2b_hex("0200010103000704"))
262                        #print "Tx -> 0200010103000704"
263                        s = self.ser.read(8)
264                        #print "Rx -> %s" % binascii.b2a_hex(s)
265               
266                if binascii.b2a_hex(s) == "0200016c0300721c":
267                        print "  Card is inserted..."
268                        return 1
269                else:
270                        print "  Card Error..."
271                        return 0
272
273        # Get current value from card
274        def get_balance(self):
275                self.ser.write(binascii.a2b_hex("0200012103002704"))
276                s1 = self.ser.read(16)
277                print "  %s%.2f" % ("Card valued at -> $",float(string.atoi(binascii.b2a_hex(s1[3:11]), 16))/1000)
278                return float(string.atoi(binascii.b2a_hex(s1[3:11]), 16))/1000
279
280        def set_balance(self, new, life, uid):
281                #self.ser.write(binascii.a2b_hex("0200012400000000000010040103003F0C"))
282                #s2 = self.ser.read(8)
283                #print binascii.b2a_hex(s2)
284
285                try:
286                        query = self.scsql.query("UPDATE users SET balance=%s, lifetimepaid=%s WHERE id='%s'" % 
287                                (new, life, uid))
288                except:
289                        print "sql error..."
290                        result = gtk.RESPONSE_CANCEL
291                        dlg = gtk.MessageDialog(None,gtk.DIALOG_MODAL |
292                                gtk.DIALOG_DESTROY_WITH_PARENT,gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, 
293                                "An error was encountered while updating your record.\nPlease contact technical support.")
294                        result = dlg.run()
295                        dlg.destroy()
296       
297        """
298        def writeUserAccountBalance(self, user, newbalance, newlifetimepaid=None) :   
299        #Sets the new account balance and eventually new lifetime paid.
300        if newlifetimepaid is not None :
301            self.doModify("UPDATE users SET balance=%s, lifetimepaid=%s WHERE id=%s" % (self.doQuote(newbalance), self.doQuote(newlifetimepaid), self.doQuote(user.ident)))
302        else :   
303            self.doModify("UPDATE users SET balance=%s WHERE id=%s" % (self.doQuote(newbalance), self.doQuote(user.ident)))
304           
305    def writeNewPayment(self, user, amount, comment="") :
306        #Adds a new payment to the payments history.
307        self.doModify("INSERT INTO payments (userid, amount, description) VALUES (%s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(amount), self.doQuote(comment)))
308       
309        """
310       
311        def eject_card(self):
312                print "  Ejecting card ..."
313                self.ser.write(binascii.a2b_hex("0200012003002604"))
314                s2 = self.ser.read(8)
315                #print "Rx -> %s" % binascii.b2a_hex(s2)
316
317        def inhibit_eject(self):
318                self.ser.write(binascii.a2b_hex("0200010103000704"))
319                s = self.ser.read(8)
320                return True
321
322        def close_port(self):   
323                self.ser.close()
324
325
326if __name__ == "__main__":
327        hwg = gui()
328        gtk.main()
329        hwg.sql.close() 
330        print "Goodbye..."
Note: See TracBrowser for help on using the browser.