Statistics
| Revision:

root / trunk / common / common.py @ 283

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
TT_MAX_RETRY = 3
46
47
MAX_PAYLOAD_LEN = 32
48
49
BAUD_RATE = 9600
50
51
# cardbox id
52
CARDBOX_ID = 2
53
54
#fails until a warning is sent
55
MAX_TOOL_FAILS = 5
56
57
bus = None
58
59
class BusException(Exception):
60
    def __str__(self):
61
        return "Bus Not Init'd"
62
63
class TimeoutException(Exception):
64
    def __init__(self, val):
65
        Exception.__init__(self)
66
        self.str = val
67
68
    def __str__(self):
69
        return "TimeoutException: " + self.str
70
71
def printMsg(msg):
72
    if len(msg) < 5:
73
        print "ERROR: trying to print message thats too small!"
74
        return
75
    if msg[0] == '^':
76
        print '^',
77
    else:
78
        print str(ord(msg[0]))
79
80
    print str(ord(msg[1])), str(ord(msg[2])),
81
    print msg[3],
82
83
    for c in msg[4:]:
84
        print str(ord(c)),
85
86
    print
87
88
89
#tn is an actual tool number
90
# ^ <src> <dest> <cmd> <plen> <payload> <crc>
91
def sendMessage(tn, cmd, msg):
92
    global bus
93
    if bus == None:
94
        raise BusException
95
    if len(msg) > MAX_PAYLOAD_LEN:
96
        print "ERROR: message too long! max size is " + str(MAX_PAYLOAD_LEN)
97
    else:
98
        body = chr(1) + chr(tn) + cmd + chr(len(msg)) + msg
99
        crc = 0
100
        for c in body:
101
            crc = crc ^ ord(c)
102
        msg = '^' + body + chr(crc)
103
        print "sending packet to tool",tn
104
        printMsg(msg)
105
        bus.write(msg)
106
107
    #time.sleep(0.05) #TODO: do I still need this?
108
109
def sendTool(t):
110
    tn = t
111
    print "sending power to tool ID",tn
112
    sendMessage(tn, TT_GRANT, "")
113
114
def sendGrant():
115
    print "seding grant to cardbox (",CARDBOX_ID,")"
116
    sendMessage(CARDBOX_ID, TT_GRANT, "")
117
118
def sendDeny():
119
    print "seding deny to cardbox (",CARDBOX_ID,")"
120
    sendMessage(CARDBOX_ID, TT_DENY, "")
121
122
def sendAck(toolNum):
123
   print "seding ACK to",toolNum
124
   sendMessage(toolNum, TT_ACK, "")
125
126
def sendNack(toolNum):
127
   print "seding NACK to",toolNum
128
   sendMessage(toolNum, TT_NACK, "")
129
130
# This function eats the rest of the packet where data is what we have so far
131
def flushPacket(data):
132
    global bus
133
    if bus == None:
134
        raise BusException
135
136
    while len(data) < 6:
137
        c = bus.read(1)
138
        #print "("+str(ord(c))+")"
139
        data += c
140
141
    plen = data[4]
142
    if plen > 0:
143
        bus.read(ord(plen))
144
145
146
#reads until a timeout
147
def readInf():
148
    c = ' '
149
    while c != '':
150
        c = bus.read(1)
151
        if c != '':
152
            print "<" + str(ord(c)) + ">"
153
154
# Returns [src, dest, cmd, data]
155
def readMessage():
156
    global bus
157
    if bus == None:
158
        raise BusException
159
160
    start = bus.read(1)
161
    if start == '^':
162
        src = bus.read(1)
163
        if src == chr(1): #reflection
164
            print "(reflection)"
165
            flushPacket('^' + src)
166
            return readMessage()
167
168
        dest = bus.read(1)
169
        cmd = bus.read(1)
170
        plen = bus.read(1)
171
        data = bus.read(ord(plen))
172
        x = bus.read(1)
173
174
        print "got packet"
175
        printMsg('^' + src + dest + cmd + plen + data + x)
176
177
        crc = ord(src) ^ ord(dest) ^ ord(cmd) ^ ord(plen)
178
        for d in data:
179
            crc ^= ord(d)
180
181
        if crc == ord(x):
182
            return [src, dest, cmd, data]
183
        else:
184
            print "xor fail. got", str(ord(x)), "should have been got",crc
185
            return None
186
    else:
187
        if start == '':
188
            #print "ERROR: timeout on start delim"
189
            pass #TODO: do I need this printout for other (non-transaction reading) cases???
190
        else:
191
            print "ERROR: did not get start delimiter: "+str(ord(start))
192
        return None
193
194
# This function keeps trying to read the message until it gets something
195
def readMessageBlocking():
196
    print "blocking on read"
197
    m = None
198
    while m == None:
199
        m = readMessage();
200
201
    return m
202
203
#returns [key, cardnum] pair
204
def readTransaction():
205
    m = readMessageBlocking()
206
    if m != None:
207
        print "got a message:"
208
        [src, dest, cmd, data] = m
209
        src = ord(src)
210
        dest = ord(dest)
211
        print "src:", src, " (should be:",CARDBOX_ID,")"
212
        print "dest:", dest
213
        print "cmd:", cmd, " (should be:",TT_KC,")"
214
        print "len(data)", len(data)
215
        print "data:", data
216
        if src == CARDBOX_ID and cmd == TT_KC and len(data) >= 2:
217
            print "looks like a transaction"
218
            card = re.search('%([0-9]*)=.*', data[1:])
219
            if card != None:
220
                print "got a card number!"
221
                sendAck(CARDBOX_ID)
222
                return [data[0], card.group(1)] #return the key and parsed out cardnum
223
224
    # send nack if things didn't look right
225
    sendNack(CARDBOX_ID)
226
    return None
227
228
229
#returns [src, dest, command] or [] on error
230
# because of reflection, this will quietly ignore packets send by the server
231
def readTool():
232
    m = readMessage()
233
    if m == None:
234
        return None
235
    [src, dest, cmd, data] = m
236
    return [src, dest, cmd]
237
238
def checkAck(t):
239
    tn = t
240
    m = readTool()
241
    if m==None:
242
        print "timeout"
243
        return False
244
    [src,dest,cmd] = m
245
    return cmd == TT_ACK
246
247
248
def initBus(filename):
249
    global bus
250
251
    bus = serial.Serial(filename, BAUD_RATE, timeout = 2)
252
    bus.flushInput()
253
    print bus
254
255
    return True