Package ixxat :: Package j1939api :: Module J1939api
[hide private]
[frames] | no frames]

Source Code for Module ixxat.j1939api.J1939api

  1  # -*- coding: iso-8859-1 -*- 
  2  ################################################################################ 
  3  #    (C) 2009 IXXAT Automation GmbH, all rights reserved (Template V2) 
  4  ################################################################################ 
  5  #      File: J1939api.py 
  6  #    Author: Joachim Stolberg 
  7  #   Summary: see following doc-string 
  8  #  $HeadURL: $ 
  9  # $Revision: 1 $ 
 10  #     $Date: 23.03.09 13:49 $ 
 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  #--------------- Import Section -------------------------------------------- 
 20  # standard library imports 
 21  import struct 
 22  from J1939_wrap import *  # wrapper generated by swig 
 23  from ctypes import * # string conversion 
 24   
 25  #--------------- Constants ------------------------------------------------- 
 26   
 27  # Open PGN Filter 
 28  PGN_ALL            = 0xFFFFFFFF 
 29   
 30  # Message types 
 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  # Parameter types 
 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  #--------------- Variables ------------------------------------------------- 
 49   
 50  #--------------- Functions ------------------------------------------------- 
 51   
52 -def GetVersion():
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
63 -def GetExceptionString(err):
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
77 -def GetErrorString(err_code):
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
93 -def GetBoardList():
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
106 -def StrToRef(s_param):
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
115 -def RefToStr(ref_param):
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 #--------------- Classes --------------------------------------------------- 125
126 -class J1939Error(Exception):
127 """ 128 Exception class for J1939 API specific errors 129 """ 130 pass
131 132
133 -class J1939Message(object):
134 """ 135 Definition of a J1939 message "struct" for transmission and reception. 136 """
137 - def __init__ (self, pgn=0, prio=6, remote=0, mtype=MSGTYPE_DATA, lParam=[]):
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
154 -class J1939(object):
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
203 - def getStackStatus(self):
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
248 - def receive(self, timeout):
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
278 - def getRxQueueStatus(self):
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
290 - def registerDataPgn(self, pgn):
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
305 - def registerReqPgn(self, pgn):
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
319 - def registerCycPgn(self, pgn, timeout):
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
344 - def clearPgnList(self):
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
353 - def readSpnList(self, msg):
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 [] # no data available 366 return False
367
368 - def readSpnAttr(self, spn):
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) # XML file corrupt ? 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
402 - def flushRxQueue(self):
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 #--------------- Main ------------------------------------------------------ 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