Package ixxat ::
Package j1939api ::
Module J1939api
|
|
1
2
3
4
5
6
7
8
9
10
11 """
12 Python Wrapper for the IXXAT J1939 API V1.1
13 ===========================================
14
15 This Python wrapper enables the use of the J1939 API with a more Python like
16 interface class.
17 """
18
19
20
21 import struct
22 from J1939_wrap import *
23 from ctypes import *
24
25
26
27
28 PGN_ALL = 0xFFFFFFFF
29
30
31 MSGTYPE_DATA = 0
32 MSGTYPE_RAW = 1
33 MSGTYPE_REQ = 2
34 MSGTYPE_ERR = 3
35 MSGTYPE_MASK = 7
36
37 MSGTYPE_GLOBAL = 8
38
39
40 PARAMTYPE_INVALID = 0
41 PARAMTYPE_INTEGER = 1
42 PARAMTYPE_FLOAT = 2
43 PARAMTYPE_ASCII = 3
44 PARAMTYPE_BITFIELD = 4
45 PARAMTYPE_BINARY = 5
46 PARAMTYPE_BLOB = 6
47
48
49
50
51
53 """
54 API functions return the product version number of the J1939 API.
55 @param err: J1939 API Exception
56 @return: description string
57 """
58 hr, string = J1939Api_GetVersion()
59 if 0 != hr:
60 return "get product version failed"
61 return string
62
64 """
65 API functions return a 32 bit unsigned value to indicate success or
66 failure of the function. This is represented as an exception in python.
67 The function GetExceptionString() returns a string describing a
68 particular exception value
69 @param err: J1939 API Exception
70 @return: description string
71 """
72 hr, string = J1939Api_GetExceptionString(err)
73 if 0 != hr:
74 return "unknown error"
75 return string
76
78 """
79 During the administration of an API instance, errors can occur
80 independently from the calling of particular functions. These errors
81 are then registered in the reception queue and delivered to the API
82 as error messages. The error code part of the error message indicates
83 the type of error and the location of the error occurance. The
84 GetErrorString() function returns a string describing the error code.
85 @param err_code: 16 bit error code from stack
86 @return: description string
87 """
88 hr, string = J1939Api_GetErrorString(int(err_code))
89 if 0 != hr:
90 raise J1939Error, hr
91 return string
92
94 """
95 Retrieves a list of available IXXAT CAN boards for use
96 @return: list of tuples of (board name, serial number)
97 """
98 count, buf = J1939Api_GetBoardList()
99 brd_list = []
100 for i in range(0, count):
101 name, serial = struct.unpack_from("=40s8s", buf, i * 48)
102 name = name.split('\0')[0]
103 brd_list.append((name, serial))
104 return brd_list
105
107 """
108 Function for converting python strings to a C string reference for
109 passing as a paramater to the Send() function
110 @param s_param: python string
111 @return: 32-bit C string address
112 """
113 return cast(s_param, c_void_p).value
114
116 """
117 Function for converting C string references received via the Receive()
118 function to python strings
119 @param ref_param: 32-bit C string address
120 @return: python string
121 """
122 return cast(ref_param, c_char_p).value
123
124
125
127 """
128 Exception class for J1939 API specific errors
129 """
130 pass
131
132
134 """
135 Definition of a J1939 message "struct" for transmission and reception.
136 """
138 """
139 Optional initialization attributes:
140 @param pgn: Parameter Group Number of the message (e.g. 65213)
141 @param prio: message priority (default: 6)
142 @param remote: remote device address/handle (default: 0 (global))
143 @param mtype: message type (default: MSGTYPE_DATA)
144 @param lParam: list of parameters according to the 'pgn'
145 @return: instance of the class J1939Message
146 """
147 self.pgn = pgn
148 self.prio = prio
149 self.remote = remote
150 self.mtype = mtype
151 self.lParam = lParam
152
153
155 """
156 Python Wrapper for the J1939 API.
157 Provides a native python interface to the IXXAT J1939 protocol stack.
158 """
159 - def __init__(self, deviceAddress, lDeviceName, boardIndex, canLine,
160 sXmlFile):
161 """
162 Initializes an instance of J1939 API class.
163 The initialization includes the importation of the XML module file
164 specified by the sXmlFile parameter for the mapping and
165 demapping of the J1939 messages.
166 The initialization also includes the initialization of the J1939
167 Stack software, which initializes and starts the CAN interface, starts
168 J1939 address claiming and drives the reception and transmission of
169 messages. The start-up of the CAN interface will require some time.
170 The active state of the instance can be checked using the function
171 L{getStackStatus()}.
172 @param deviceAddress: J1939 device address (e.g. 249 for the
173 Off Board Diagnostic-Service Tool #1)
174 @param lDeviceName: J1939 device name as list of 9 values:
175 Arbitrary Address Capable, Industry Group,
176 Vehicle System Instance, Vehicle System, Function,
177 Function Instance, ECU Instance, Manufacturer Code,
178 Identity Number)
179 (e. g. [0, 0, 0, 0, 129, 0, 0, 400, 123456]
180 @param boardIndex: Index corresponding to the selected VCIV3
181 hardware (see L{GetBoardList()}
182 @param canLine: Can line of the selected hardware
183 @param sXmlFile: file path for the XML file containing J1939
184 configuration data
185
186 """
187 self.api = J1939Api()
188 name = struct.pack("=BBBBBBBHL",
189 lDeviceName[0],
190 lDeviceName[1],
191 lDeviceName[2],
192 lDeviceName[3],
193 lDeviceName[4],
194 lDeviceName[5],
195 lDeviceName[6],
196 lDeviceName[7],
197 lDeviceName[8])
198 res = self.api.Initialize(chr(deviceAddress), name, chr(boardIndex),
199 chr(canLine), sXmlFile)
200 if res != 0:
201 raise J1939Error, ('__init__', res)
202
204 """
205 Gets the status value of the J1939 Stack. Before the status is active,
206 transmissions over the stack will fail.
207 @return: Current stack status
208 TRUE = active (address claiming finished)
209 FALSE = inactive (address claiming not finished)
210 """
211 return self.api.GetStackStatus() == 1
212
213 - def send(self, timeout, msg):
214 """
215 Encodes and sends a J1939 message by copying the message into the stack
216 internal transmission queue.
217 Raw encoding can be performed by setting the message type byte of msg.
218 If the raw message type is set, the input parameters will be evaluated
219 as bytes and written directly to the J1939 message data field,
220 otherwise, they will be evaluated as parameters to be converted into
221 their appropriate raw data representation by the API using the
222 information stored in the XML configuration file.
223 Mapped messages return an error on the attempted transmission with an
224 incorrect number of parameters.
225 In the case of a full transmission queue, the Send() function will
226 reattempt transmission until the timeout period has elapsed.
227 @param timeout: Receive timeout in milliseconds (-1 for infinite)
228 @param msg: J1939 message in the form of the L{J1939Message} class
229 @return: zero if sending was successful, 1 if a timeout occurred and
230 False if the parameter msg was invalid.
231 """
232 if isinstance(msg, J1939Message):
233 count = len(msg.lParam)
234 if msg.mtype == MSGTYPE_RAW:
235 s = struct.pack("=LBHBH%dB" % count, msg.pgn, msg.prio,
236 msg.remote, msg.mtype, count,
237 *(val for val in msg.lParam))
238 else:
239 s = struct.pack("=LBHBH%dL" % count, msg.pgn, msg.prio,
240 msg.remote, msg.mtype, count,
241 *(val for val in msg.lParam))
242 res = self.api.Send(timeout, s)
243 if res < 0:
244 raise J1939Error, ('send (PGN=%u)' % msg.pgn, res)
245 return res
246 return False
247
249 """
250 Decodes and returns a J1939 message or error message from the internal
251 receive queue.
252 The message type is stored in the least significant bits of the message
253 status byte. Data PGNs will be decoded.
254 Decoding is performed by the Codec unit using the information contained
255 in the XML module file. If the received PGN is not included in the XML
256 configuration file, the message will be decoded as raw, which copies
257 the data from the data field byte for byte to the byte value list, and
258 the raw message type in the message type byte will be set. The Receive()
259 function will wait on an empty queue until the timeout period has
260 elapsed.
261 @param timeout: Receive timeout in milliseconds (-1 for infinite)
262 @return: J1939 message in the form of the L{J1939Message} class or
263 None if a timeout occurred (nothing received).
264 """
265 res, buf = self.api.Receive(timeout)
266 if res < 0:
267 raise J1939Error, ('receive', res)
268 if res == 1:
269 return None
270 count = struct.unpack_from("=H", buf, 8)[0]
271 msg = J1939Message(*struct.unpack_from("=LBHB", buf))
272 if msg.mtype & MSGTYPE_MASK == MSGTYPE_RAW:
273 msg.lParam = struct.unpack_from("B" * count, buf, 10)
274 else:
275 msg.lParam = struct.unpack_from("L" * count, buf, 10)
276 return msg
277
279 """
280 Gets the current reception queue status, by representing the current
281 number of messages in the receive queue. The queue has a depth of
282 1024 messages.
283 @return: Current number of messages waiting in the receive queue
284 """
285 res = self.api.GetRxQueueStatus()
286 if res < 0:
287 raise J1939Error, ('getRxQueueStatus', res)
288 return res
289
291 """
292 Registers a data PGN for reception with the stack. PGNs not found in
293 XML configuration file will be decoded on reception as raw.
294 The reception of an unregistered message directed to the API (not
295 global) will cause an error message to be sent back to originating
296 node. The reception of a registered data message will cause the message
297 to be entered into the reception queue for retrieval by the user
298 application.
299 @param pgn: PGN to be registered
300 """
301 res = self.api.RegisterDataPgn(pgn)
302 if res < 0:
303 raise J1939Error, ('registerDataPgn', res)
304
306 """
307 Registers a request PGN for reception with the stack.
308 The reception of an unregistered request message directed to the API
309 (not global) will cause an error message to be sent back to originating
310 node. The reception of a registered request message will cause the
311 message to be entered into the reception queue as a request message
312 for retrieval and handling by the user application.
313 @param pgn: PGN to be registered of the message to be requested
314 """
315 res = self.api.RegisterReqPgn(pgn)
316 if res < 0:
317 raise J1939Error, ('registerReqPgn', res)
318
320 """
321 Registers a data Rx PGN for timeout supervision. The function uses the
322 RegisterDataPgn() functionality to register the PGN for reception by
323 the stack and in addition, registers it with a cyclical test array
324 within the API. On the timeout of an actively registered cyclic message,
325 the J1939API_USR_ERR_CYC_TIMEOUT error message is entered into the
326 reception queue. Cyclical PGNs are deactivated for testing on timeout,
327 to prevent jamming of the reception queue with timeout messages.
328 Cyclical PGNs are activated by the reception of a corresponding message
329 following the initial registration or a previous cycle timeout.
330
331 B{Remarks}: The minimum resolution of the cycle time is 5 milliseconds.
332 Cycle times will be rounded up to the next multiple of the timer
333 resolution. The registration of cyclical PGNs also performs the
334 registration of data PGNs.
335
336 @param pgn: PGN to be registered
337 @param timeout: Required reception cycle time in milliseconds. See
338 Remarks for more information regarding the cycle time.
339 """
340 res = self.api.RegisterCycPgn(pgn, timeout)
341 if res < 0:
342 raise J1939Error, ('registerCycPgn', res)
343
345 """
346 Removes all registered PGNs from the data, request and cyclic
347 registration list.
348 """
349 res = self.api.ClearPgnList()
350 if res != 0:
351 raise J1939Error, ('clearPgnList', res)
352
354 """
355 Retrieves a list of SPNs within a given PGN.
356 Note: The number of possible SPN structures returned is limited to 256.
357 @param pgn: PGN from which to retrieve the SPN list
358 @return: SPN list
359 """
360 if isinstance(msg, J1939Message):
361 res, buf = self.api.ReadSpnList(int(msg.pgn), len(msg.lParam))
362 if res == 0:
363 return list(struct.unpack("=%dL" % (len(buf) / 4), buf))
364 else:
365 return []
366 return False
367
369 """
370 Retrieves the attributes corresponding to a specific SPN. The
371 applicable value can be determined by taking the returned value from
372 the J1939 message, multiplying it by its resolution and adding the
373 offset. The type of variable represented by the attribute entries
374 "res", "off", "min" and "max" depend on the parameter type.
375 @param spn: SPN from which to retrieve the attributes
376 @return: SPN parameter attribute dictionary with the following keys:
377 - "name" : string representing name of the suspect parameter
378 - "res" : resolution of the value
379 - "off" : offset of the value
380 - "min" : minimum allowable value
381 - "max" : maximum allowable value
382 - "type" : type of value (possible are 'unknown', 'integer',
383 'float', 'ASCII', 'BitField', 'binary' and 'BLOB')
384 - "unit" : string representing the units of the value
385 """
386 res, buf = self.api.ReadSpnAttr(int(spn))
387 if res != 0:
388 raise J1939Error, ('readSpnAttr', res)
389 name, res, off, v_min, v_max, _type, unit = \
390 struct.unpack("=60s4db30s", buf)
391 return {
392 "name": name.split('\0')[0],
393 "res": res,
394 "off": off,
395 "min": v_min,
396 "max": v_max,
397 "type": ['unknown', 'integer', 'float', 'ASCII', 'BitField',
398 'binary', 'BLOB'][_type],
399 "unit": unit.split('\0')[0]
400 }
401
403 """
404 Flushes the reception queue by calling receive until empty
405 """
406 msg = True
407 while msg != None:
408 msg = self.receive(0)
409
410
411
412 if __name__ == '__main__':
413 import time
414 print "J1939 Interface Test"
415 print "Boards:", GetBoardList()
416 try:
417 api = J1939(129, [0, 0, 0, 0, 0, 0, 0, 400, 123456], 0, 0, ".\\J1939api.xml")
418 while api.getStackStatus() == False:
419 time.sleep(0.1)
420 api.registerDataPgn(65001)
421 api.registerReqPgn(652002)
422 api.registerCycPgn(65003, 100)
423 api.clearPgnList()
424 assert type(api.readSpnList(45312)) == list
425 msg = J1939Message(45312, 6, 0x33, 0, [1, 2, 3, 4, StrToRef("StrTest")])
426 assert api.send(0, msg) == 0
427 assert api.receive(0) == None
428 assert api.getRxQueueStatus() == 0
429 api.flushRxQueue()
430 assert GetExceptionString(0) == "success"
431 assert GetErrorString(0) == "Error code is unrecognized."
432 print "ready!"
433 except J1939Error, (func, err):
434 print "%s in function '%s'" % (GetExceptionString(err), func)
435 del api
436