Statistics
| Revision:

root / trunk / common / common.py @ 286

History | View | Annotate | Download (6.2 KB)

1
"""
2
  This file is part of Tooltron.
3
 
4
  Tooltron is free software: you can redistribute it and/or modify
5
  it under the terms of the Lesser GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
 
9
  Tooltron is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  Lesser GNU General Public License for more details.
13
  You should have received a copy of the Lesser GNU General Public License
14
  along with Tooltron.  If not, see <http://www.gnu.org/licenses/>.
15
16
  Copyright 2009 Bradford Neuman <bneuman@andrew.cmu.edu>
17
18
"""
19
20
####
21
# This file contains common code for both manualTron and the real tooltron server
22
23
import serial
24
import sys
25
import time
26
import re
27
28
keypadTimeout = 31 #in seconds
29
30
TT_ACK     = 'a'
31
TT_NACK    = 'n'
32
TT_TIMEOUT = 't'
33
TT_KC        = 'x' # A transaction containing a key and a card number
34
TT_GRANT     = 'q' # Tool access granted
35
TT_DENY      = 'f' # Tool access denied
36
TT_PING    = 'g'
37
TT_BOOT    = 'b'
38
TT_PROGRAM_MODE = 'p'
39
TT_RESET = 'r'
40
TT_PING = 'g'
41
TT_BAD = 0
42
TT_PROGM = 'p'
43
TT_PROGD = 'd'
44
45
#depricated
46
TT_ON = 'o'
47
48
TT_MAX_RETRY = 3
49
50
MAX_PAYLOAD_LEN = 32
51
52
BAUD_RATE = 9600
53
54
# cardbox id
55
CARDBOX_ID = 2
56
57
#fails until a warning is sent
58
MAX_TOOL_FAILS = 5
59
60
bus = None
61
62
class BusException(Exception):
63
    def __str__(self):
64
        return "Bus Not Init'd"
65
66
class TimeoutException(Exception):
67
    def __init__(self, val):
68
        Exception.__init__(self)
69
        self.str = val
70
71
    def __str__(self):
72
        return "TimeoutException: " + self.str
73
74
def printMsg(msg):
75
    if len(msg) < 5:
76
        print "ERROR: trying to print message thats too small!"
77
        return
78
    if msg[0] == '^':
79
        print '^',
80
    else:
81
        print str(ord(msg[0]))
82
83
    print str(ord(msg[1])), str(ord(msg[2])),
84
    print msg[3],
85
86
    for c in msg[4:]:
87
        print str(ord(c)),
88
89
    print
90
91
92
#tn is an actual tool number
93
# ^ <src> <dest> <cmd> <plen> <payload> <crc>
94
def sendMessage(tn, cmd, msg):
95
    global bus
96
    if bus == None:
97
        raise BusException
98
    if len(msg) > MAX_PAYLOAD_LEN:
99
        print "ERROR: message too long! max size is " + str(MAX_PAYLOAD_LEN)
100
    else:
101
        body = chr(1) + chr(tn) + cmd + chr(len(msg)) + msg
102
        crc = 0
103
        for c in body:
104
            crc = crc ^ ord(c)
105
        msg = '^' + body + chr(crc)
106
        print "sending packet to tool",tn
107
        printMsg(msg)
108
        bus.write(msg)
109
110
    #time.sleep(0.05) #TODO: do I still need this?
111
112
def sendTool(t):
113
    tn = t
114
    print "sending power to tool ID",tn
115
    sendMessage(tn, TT_ON, "") #TT_GRANT, "")
116
117
def sendGrant():
118
    print "seding grant to cardbox (",CARDBOX_ID,")"
119
    sendMessage(CARDBOX_ID, TT_GRANT, "")
120
121
def sendDeny():
122
    print "seding deny to cardbox (",CARDBOX_ID,")"
123
    sendMessage(CARDBOX_ID, TT_DENY, "")
124
125
def sendAck(toolNum):
126
   print "seding ACK to",toolNum
127
   sendMessage(toolNum, TT_ACK, "")
128
129
def sendNack(toolNum):
130
   print "seding NACK to",toolNum
131
   sendMessage(toolNum, TT_NACK, "")
132
133
# This function eats the rest of the packet where data is what we have so far
134
def flushPacket(data):
135
    global bus
136
    if bus == None:
137
        raise BusException
138
139
    while len(data) < 6:
140
        c = bus.read(1)
141
        #print "("+str(ord(c))+")"
142
        data += c
143
144
    plen = data[4]
145
    if plen > 0:
146
        bus.read(ord(plen))
147
148
149
#reads until a timeout
150
def readInf():
151
    c = ' '
152
    while c != '':
153
        c = bus.read(1)
154
        if c != '':
155
            print "<" + str(ord(c)) + ">"
156
157
# Returns [src, dest, cmd, data]
158
def readMessage():
159
    global bus
160
    if bus == None:
161
        raise BusException
162
163
    start = bus.read(1)
164
    if start == '^':
165
        src = bus.read(1)
166
        if src == chr(1): #reflection
167
            print "(reflection)"
168
            flushPacket('^' + src)
169
            return readMessage()
170
171
        dest = bus.read(1)
172
        cmd = bus.read(1)
173
        plen = bus.read(1)
174
        data = bus.read(ord(plen))
175
        x = bus.read(1)
176
177
        print "got packet"
178
        printMsg('^' + src + dest + cmd + plen + data + x)
179
180
        crc = ord(src) ^ ord(dest) ^ ord(cmd) ^ ord(plen)
181
        for d in data:
182
            crc ^= ord(d)
183
184
        if crc == ord(x):
185
            return [src, dest, cmd, data]
186
        else:
187
            print "xor fail. got", str(ord(x)), "should have been got",crc
188
            return None
189
    else:
190
        if start == '':
191
            #print "ERROR: timeout on start delim"
192
            pass #TODO: do I need this printout for other (non-transaction reading) cases???
193
        else:
194
            print "ERROR: did not get start delimiter: "+str(ord(start))
195
        return None
196
197
# This function keeps trying to read the message until it gets something
198
def readMessageBlocking():
199
    print "blocking on read"
200
    m = None
201
    while m == None:
202
        m = readMessage();
203
204
    return m
205
206
#returns [key, cardnum] pair
207
def readTransaction():
208
    m = readMessageBlocking()
209
    if m != None:
210
        print "got a message:"
211
        [src, dest, cmd, data] = m
212
        src = ord(src)
213
        dest = ord(dest)
214
        print "src:", src, " (should be:",CARDBOX_ID,")"
215
        print "dest:", dest
216
        print "cmd:", cmd, " (should be:",TT_KC,")"
217
        print "len(data)", len(data)
218
        print "data:", data
219
        if src == CARDBOX_ID and cmd == TT_KC and len(data) >= 2:
220
            print "looks like a transaction"
221
            card = re.search('%([0-9]*)=.*', data[1:])
222
            if card != None:
223
                print "got a card number!"
224
                sendAck(CARDBOX_ID)
225
                return [data[0], card.group(1)] #return the key and parsed out cardnum
226
227
    # send nack if things didn't look right
228
    sendNack(CARDBOX_ID)
229
    return None
230
231
232
#returns [src, dest, command] or [] on error
233
# because of reflection, this will quietly ignore packets send by the server
234
def readTool():
235
    m = readMessage()
236
    if m == None:
237
        return None
238
    [src, dest, cmd, data] = m
239
    return [src, dest, cmd]
240
241
def checkAck(t):
242
    tn = t
243
    m = readTool()
244
    if m==None:
245
        print "timeout"
246
        return False
247
    [src,dest,cmd] = m
248
    return cmd == TT_ACK
249
250
251
def initBus(filename):
252
    global bus
253
254
    bus = serial.Serial(filename, BAUD_RATE, timeout = 1)
255
    bus.flushInput()
256
    print bus
257
258
    return True