Package ais :: Module aisxmlbinmsg2py
[hide private]
[frames] | no frames]

Source Code for Module ais.aisxmlbinmsg2py

   1  #!/usr/bin/env python 
   2   
   3  __version__ = '$Revision: 4791 $'.split()[1] 
   4  __date__ = '$Date: 2006-09-24 14:01:41 -0400 (Sun, 24 Sep 2006) $'.split()[1] 
   5  __author__ = 'Kurt Schwehr' 
   6   
   7  __doc__=''' 
   8   
   9  Tools to generate python code to serialize/deserialize messages 
  10  between python and ais binary.  Trying to be as inline as possible, so 
  11  no XML on the fly like in ais-py.   
  12   
  13  serialize: python to ais binary 
  14  deserialize: ais binary to python 
  15   
  16  The generated code uses translators.py, binary.py, and aisstring.py 
  17  which should be packaged with the resulting files. 
  18   
  19  @requires: U{lxml<http://codespeak.net/lxml/>} 
  20  @requires: U{epydoc<http://epydoc.sourceforge.net/>} >= 3.0alpha3 
  21  @requires: U{BitVector<http://cheeseshop.python.org/pypi/BitVector>} >= 1.2 
  22   
  23  @author: U{'''+__author__+'''<http://schwehr.org/>} 
  24  @version: ''' + __version__ +''' 
  25  @copyright: 2006 
  26  @var __date__: Date of last svn commit 
  27  @undocumented: __version__ __author__ __doc__ parser 
  28  @since: 2006-Sep-24 
  29  @status: under development 
  30  @organization: U{CCOM<http://ccom.unh.edu/>} 
  31  @license: GPL v2 
  32   
  33  @todo: add a link to generated doc string to bring up the html for the pretty version 
  34  @todo: write a separate validation script that distinguishes standard messages and bin messages 
  35  @todo: make sure binary is only used in AIS ITU messages and not within the binary messages! 
  36  @todo: arrays 
  37  @bug: NOT complete 
  38  ''' 
  39   
  40  import sys, os 
  41  from decimal import Decimal 
  42  from lxml import etree  
  43   
44 -def suggestType(name,curType,printout=True):
45 ''' 46 Try to suggest a type name if one did not work. 47 48 @param printout: if true, write a suggestion to stdout. 49 50 >>> suggestType('myFieldName','unsigned int') 51 Recommend switching "unsigned int" to "uint" for field "myFieldName" 52 'uint' 53 54 >>> suggestType('JohnWarfon','yoyodyne') 55 Sorry! No recommendation available for bad type "yoyodyne" for field "JohnWarfon" 56 ''' 57 newType = None 58 if curType.lower()=='unsigned int': 59 newType = 'uint' 60 elif curType.lower()=='unsigned decimal': 61 newType = 'udecimal' 62 63 if printout: 64 if None != newType: 65 print 'Recommend switching "'+curType+'" to "'+newType+'" for field "'+name+'"' 66 else: 67 print 'Sorry! No recommendation available for bad type "'+curType+'" for field "'+name+'"' 68 return newType
69 70
71 -def hasSubtag(et,subtag):
72 ''' 73 @return: true if the tag a sub tag with name subtag 74 ''' 75 if 0<len(et.xpath(subtag)): return True 76 return False
77 78 79 #def writeBeginning(o,aisBinMsgET):
80 -def writeBeginning(o):
81 ''' 82 Write the doc string header for the message file 83 84 @param o: Open output file to write code to. 85 @param msgET: element tree for the ais message definition. 86 Must be pre-expanded with the expandais.py command. 87 ''' 88 import datetime 89 d = datetime.datetime.utcnow() 90 dateStr = str(d.year)+'-'+("%02d" %d.month)+'-'+("%02d"%d.day) 91 92 # FIX: what to do for __version__, @since, etc? 93 # Need to pass in info about the source file, etc. 94 95 # Minor trickery to get svn to ignore the keywords in the next few lines 96 o.write('''#!/usr/bin/env python 97 98 __version__ = '$Revision: 4791 $'.split()[1] 99 __date__ = '$Da'''+'''te: '''+dateStr+''' $'.split()[1] 100 __author__ = 'xmlbinmsg' 101 102 __doc__=\'\'\' 103 104 Autogenerated python functions to serialize/deserialize binary messages. 105 106 Generated by: '''+__file__+''' 107 108 Need to then wrap these functions with the outer AIS packet and then 109 convert the whole binary blob to a NMEA string. Those functions are 110 not currently provided in this file. 111 112 serialize: python to ais binary 113 deserialize: ais binary to python 114 115 The generated code uses translators.py, binary.py, and aisstring.py 116 which should be packaged with the resulting files. 117 118 ''') 119 120 o.write(''' 121 @requires: U{epydoc<http://epydoc.sourceforge.net/>} > 3.0alpha3 122 @requires: U{BitVector<http://cheeseshop.python.org/pypi/BitVector>} 123 124 @author: \'\'\'+__author__+\'\'\' 125 @version: \'\'\' + __version__ +\'\'\' 126 @var __date__: Date of last svn commit 127 @undocumented: __version__ __author__ __doc__ parser 128 @status: under development 129 @license: Generated code has no license 130 \'\'\' 131 132 import sys 133 from decimal import Decimal 134 from BitVector import BitVector 135 136 import binary, aisstring 137 138 # FIX: check to see if these will be needed 139 TrueBV = BitVector(bitstring="1") 140 "Why always rebuild the True bit? This should speed things up a bunch" 141 FalseBV = BitVector(bitstring="0") 142 "Why always rebuild the False bit? This should speed things up a bunch" 143 144 145 ''') 146 return
147
148 -def generatePython(infile,outfile, prefixName=False):
149 ''' 150 @param infile: xml ais binary message definition file 151 @param outfile: where to dump the python code 152 ''' 153 154 aisMsgsET = etree.parse(infile).getroot() 155 156 o = file(outfile,'w') 157 os.chmod(outfile,0755) 158 159 writeBeginning(o) 160 161 for msgET in aisMsgsET: 162 if msgET.tag != 'message': continue 163 print msgET.tag, msgET.attrib['name'] 164 165 if len(msgET.xpath('include-struct')) > 0: 166 sys.exit("ERROR: cannot handle xml that still has include-struct tags.\n Please use expandais.py.") 167 buildEncode(o,msgET,prefixName=prefixName) 168 buildDecode(o,msgET,prefixName=prefixName) 169 buildDecodeParts(o,msgET,prefixName=prefixName) # functions that only decode one field 170 buildPrint(o,msgET,prefixName=prefixName) 171 buildLUT(o,msgET,prefixName=prefixName) 172 173 o.write('\n\n######################################################################\n') 174 o.write('# UNIT TESTING\n') 175 o.write('######################################################################\n') 176 o.write('import unittest\n') 177 178 for msgET in aisMsgsET: 179 if msgET.tag != 'message': continue 180 print 'Building unit tests for message ...', msgET.attrib['name'] 181 182 buildUnitTest(o,msgET, prefixName=prefixName) 183 184 for msgET in aisMsgsET: 185 if msgET.tag != 'message': continue 186 buildMain(o,msgET, prefixName=prefixName) 187 188 return
189 190 ###################################################################### 191 # SIMPLE PRINT 192 ###################################################################### 193
194 -def getMaxFieldNameLen(msgET):
195 '''Get the maximum string length of any field name''' 196 maxStrLen=0 197 for field in msgET.xpath('field'): 198 fieldLen = len(field.attrib['name']) 199 if fieldLen>maxStrLen: maxStrLen = fieldLen 200 return maxStrLen
201
202 -def padStrRight(aStr,strlen):
203 '''Pad a string out to the length requested with spaces out to the right''' 204 return aStr + ' '*(strlen-len(aStr))
205
206 -def buildPrint(o,msgET, verbose=False, prefixName=False):
207 ''' 208 Write a simple in order print for the resulting dictionary. 209 210 @param o: open file where resulting code will be written 211 @param msgET: Element Tree starting at a message node 212 213 @todo: for lookuptable/entry values, make it also print the decoded value. 214 @todo: use a different name for message and field 215 ''' 216 assert(msgET.tag=='message') 217 name = msgET.attrib['name'] 218 msgname = name 219 220 print 'Generating print for',name # FIX: verbose? 221 funcName = 'printFields' 222 if prefixName: funcName = name+'PrintFields' 223 224 o.write('def '+funcName+'(params, out=sys.stdout, format=\'std\'):\n') 225 226 ######################################## 227 # doc string 228 o.write("\t'''Print a "+name+" message to stdout.\n\n") 229 o.write('\tFields in params:\n') 230 for field in msgET.xpath('field'): 231 desc = field[0].text.replace('\n',' ') # get ride of new lines 232 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc) # give the description 233 if len(field.xpath("required")) == 1: 234 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")') 235 236 o.write('\n') 237 o.write('\t@param params: Dictionary of field names/values. \n') 238 o.write('\t@param out: File like object to write to\n') 239 240 o.write("\t@rtype: stdout\n") 241 o.write("\t@return: text to out\n") 242 o.write("\t'''\n\n") 243 244 ######################################## 245 # Actually build the code 246 247 o.write('\tif \'std\'==format:\n') 248 o.write('\t\tout.write("'+name+':\\n")\n') 249 250 if verbose: print 'number of fields = ', len(msgET.xpath('field')) 251 252 # +1 for the ':' 253 maxFieldLen = 1 + getMaxFieldNameLen(msgET) 254 255 for field in msgET.xpath('field'): 256 name = field.attrib['name'] 257 type = field.attrib['type'] 258 numbits = int(field.attrib['numberofbits']) 259 required = None; 260 if hasSubtag(field,'required'): 261 required = field.xpath('required')[0].text 262 #print 'required set for',name,'to',required 263 unavailable=None; 264 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 265 arraylen=1 266 if 'arraylength' in field.attrib: 267 arraylen=int(field.attrib['arraylength']) 268 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 269 else: 270 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')' 271 272 if 1==arraylen or type=='aisstr6': 273 o.write('\t\tif \''+name+'\' in params: out.write("\t'+padStrRight(name+':',maxFieldLen)+' "+str(params[\''+name+'\'])+"\\n")\n') 274 # FIX: elif aisstr6 strip @@@ and then print 275 else: 276 sys.exit ('FIX: handle arrays in the buildPrint func') 277 278 #################### 279 ####### HTML format 280 #################### 281 o.write('\telif \'html\'==format:\n') 282 o.write('\t\tout.write("<h3>'+msgname+'<h3>\\n")\n') 283 o.write('\t\tout.write("<table border=\\"1\\">\\n")\n') 284 #o.write('\t\tout.write("<tr bgcolor=\\"#9acd32\\">\\n")\n') 285 o.write('\t\tout.write("<tr bgcolor=\\"orange\\">\\n")\n') 286 o.write('\t\tout.write("<th align=\\"left\\">Field Name</th>\\n")\n') 287 o.write('\t\tout.write("<th align=\\"left\\">Type</th>\\n")\n') 288 o.write('\t\tout.write("<th align=\\"left\\">Value</th>\\n")\n') 289 o.write('\t\tout.write("<th align=\\"left\\">Value in Lookup Table</th>\\n")\n') 290 291 292 for field in msgET.xpath('field'): 293 o.write('\t\tout.write("\\n")\n') 294 o.write('\t\tout.write("<tr>\\n")\n') 295 name = field.attrib['name'] 296 type = field.attrib['type'] 297 o.write('\t\tout.write("<td>'+name+'</td>\\n")\n') 298 o.write('\t\tout.write("<td>'+type+'</td>\\n")\n') 299 300 numbits = int(field.attrib['numberofbits']) 301 required = None; 302 if hasSubtag(field,'required'): 303 required = field.xpath('required')[0].text 304 unavailable=None; 305 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 306 arraylen=1 307 if 'arraylength' in field.attrib: arraylen=int(field.attrib['arraylength']) 308 309 if 1==arraylen or type=='aisstr6': 310 o.write('\t\tif \''+name+'\' in params:\n\t\t\tout.write("\t<td>"+str(params[\''+name+'\'])+"</td>\\n")\n') 311 if not hasSubtag(field,'lookuptable'): 312 # Just duplicate the value through 313 o.write('\t\t\tout.write("\t<td>"+str(params[\''+name+'\'])+"</td>\\n")\n') 314 else: 315 lutName = name+'DecodeLut' 316 if prefixName: lutName = msgname+name.capitalize()+'DecodeLut' 317 318 o.write('\t\t\tif str(params[\''+name+'\']) in '+lutName+':\n') 319 o.write('\t\t\t\tout.write("<td>"+'+lutName+'[str(params[\''+name+'\'])]+"</td>")\n') 320 o.write('\t\t\telse:\n') 321 o.write('\t\t\t\tout.write("<td><i>Missing LUT entry</i></td>")\n') 322 else: sys.exit ('FIX: handle arrays in the buildPrint func') 323 o.write('\t\tout.write("</tr>\\n")\n') 324 325 o.write('\t\tout.write("</table>\\n")\n') 326 327 o.write('\telse: \n') 328 o.write('\t\tprint "ERROR: unknown format:",format\n') 329 o.write('\t\tassert False\n') 330 331 o.write('\n\treturn # Nothing to return\n\n')
332 333 ###################################################################### 334 # Build Lookup tables for enumerated int or uint fields 335 ###################################################################### 336 337
338 -def buildLUT(o,msgET, verbose=False, prefixName=False):
339 ''' 340 Write lookup tables for enumerated types (uint or int, maybe bool too). 341 342 @todo: FIX: what to do about multiple entries with the same text? Need to ban that kind of thing 343 @todo: Make doc strings for each LUT. 344 345 @param o: open file where resulting code will be written 346 @param msgET: Element Tree starting at a message node 347 ''' 348 assert(msgET.tag=='message') 349 msgname = msgET.attrib['name'] 350 351 print 'Generating lookup tables for',msgname # FIX: verbose? 352 353 for field in msgET.xpath('field'): 354 name = field.attrib['name'] 355 if not hasSubtag(field,'lookuptable'): continue 356 lut = field.xpath('lookuptable')[0] 357 358 lutName = name 359 if prefixName: lutName = msgname+name.capitalize() 360 361 o.write(lutName+'EncodeLut = {\n') 362 for entry in lut.xpath('entry'): 363 o.write('\t\''+entry.text+'\':\''+entry.attrib['key']+'\',\n') 364 o.write('\t} #'+lutName+'EncodeLut\n') 365 o.write('\n') 366 367 # FIX: make doc string for LUT here 368 369 o.write(lutName+'DecodeLut = {\n') 370 for entry in lut.xpath('entry'): 371 o.write('\t\''+entry.attrib['key']+'\':\''+entry.text+'\',\n') 372 o.write('\t} # '+lutName+'EncodeLut\n') 373 o.write('\n')
374 375 # FIX: make doc string for LUT here 376 377 378 379 380 ###################################################################### 381 # ENCODERS 382 ######################################################################
383 -def encodeBool(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
384 ''' 385 Build the encoder for boolean variables 386 @type o: file like obj 387 @param o: where write the code 388 @type name: str 389 @param name: field name 390 @type type: str 391 @param type: bool, etc. 392 @type numbits: int = 1 393 @param numbits: How many bits per unit datum (must be 1 for bools) 394 @type required: bool or None 395 @param required: If not None, then the value must be set to this. 396 @type arraylen: int >= 1 397 @param arraylen: many bools will there be? FIX: handle variable 398 @type unavailable: bool or None 399 @param unavailable: the default value to use if none given (if not None) 400 @return: None 401 ''' 402 403 if verbose: print 'bool encode',name,': unvail=',unavailable 404 405 assert type.lower()=='bool' 406 assert numbits==1 407 if arraylen != 1: assert False # FIX... handle arrays 408 if verbose: o.write('\t### FIELD: '+name+' (type=bool)\n') 409 if None != required: 410 assert type(required)==bool 411 if required: o.write('\t\tbvList.append(TrueBV)\n') 412 else: o.write('\t\tbvList.append(FalseBV)\n') 413 if verbose: o.write('\n') 414 return 415 416 if None==unavailable: 417 o.write('\tif params["'+name+'"]: bvList.append(TrueBV)\n') 418 o.write('\telse: bvList.append(FalseBV)\n') 419 else: # Have a default value that can be filled in 420 assert type(unavailable)==bool 421 o.write("\tif '"+name+"' in params:\n") 422 o.write('\t\tif params["'+name+'"]: bvList.append(TrueBV)\n') 423 o.write('\t\telse: bvList.append(FalseBV)\n') 424 o.write('\telse:\n') 425 if unavailable: o.write('\t\tbvList.append(TrueBV)\n') 426 else: o.write('\t\tbvList.append(FalseBV)\n') 427 if verbose: o.write('\n')
428
429 -def encodeUInt(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
430 ''' 431 Build the encoder for unsigned integer variables 432 433 @type o: file like obj 434 @param o: where write the code 435 @type name: str 436 @param name: field name 437 @type type: str 438 @param type: uint, bool, etc. 439 @type numbits: int >= 1 440 @param numbits: How many bits per unit datum (must be 1..32) 441 @type required: bool or None 442 @param required: If not None, then the value must be set to this. 443 @type arraylen: int >= 1 444 @param arraylen: many unsigned ints will there be? FIX: handle variable 445 @type unavailable: bool or None 446 @param unavailable: the default value to use if none given (if not None) 447 @return: None 448 ''' 449 if verbose: print ' encodeUInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 450 451 assert type=='uint' 452 assert numbits>=1 and numbits<=32 453 if arraylen != 1: assert False # FIX... handle arrays 454 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 455 456 if None != required: 457 if verbose: print ' required:',required 458 required=int(required) 459 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(required)+'),'+str(numbits)+'))\n') 460 if verbose: o.write('\n') 461 return 462 463 if None==unavailable: 464 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal=params[\''+name+'\']),'+str(numbits)+'))\n') 465 else: # Have a default value that can be filled in 466 #assert type(unavailable)== 467 int(unavailable) # Make sure unavailable is a number object 468 o.write("\tif '"+name+"' in params:\n") 469 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=params[\''+name+'\']'+'),'+str(numbits)+'))\n') 470 o.write('\telse:\n') 471 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(unavailable)+'),'+str(numbits)+'))\n') 472 473 if verbose: o.write('\n')
474 475
476 -def encodeFloat(o,name,fieldType,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
477 ''' 478 Build the encoder for IEEE float variables 479 480 @type o: file like obj 481 @param o: where write the code 482 @type name: str 483 @param name: field name 484 @type fieldType: str 485 @param fieldType: uint, bool, etc. 486 @type numbits: int >= 1 487 @param numbits: How many bits per unit datum (must be 1..32) 488 @type required: bool or None 489 @param required: If not None, then the value must be set to this. 490 @type arraylen: int >= 1 491 @param arraylen: many unsigned ints will there be? FIX: handle variable 492 @type unavailable: bool or None 493 @param unavailable: the default value to use if none given (if not None) 494 @return: None 495 ''' 496 if verbose: print ' encodeUInt:',name,fieldType,numbits,'Req:',required,'alen:',arraylen,unavailable 497 498 assert numbits==32 # Force by the IEEE spec 499 if arraylen != 1: assert False # FIX... handle arrays 500 if verbose: o.write('\t### FIELD: '+name+' (type='+fieldType+')\n') 501 502 if None != required: 503 if verbose: print ' required:',required 504 required=int(required) 505 o.write('\tbvList.append(binary.float2bitvec('+str(required)+'))\n') 506 if verbose: o.write('\n') 507 return 508 509 if None==unavailable: 510 o.write('\tbvList.append(binary.float2bitvec(params[\''+name+'\']))\n') 511 else: # Have a default value that can be filled in 512 int(unavailable) # Make sure unavailable is a number object 513 o.write("\tif '"+name+"' in params:\n") 514 o.write('\t\tbvList.append(binary.float2bitvec(params[\''+name+'\']'+'))\n') 515 o.write('\telse:\n') 516 o.write('\t\tbvList.append(binary.float2bitvec('+str(unavailable)+'))\n') 517 518 if verbose: o.write('\n')
519 520
521 -def encodeAisstr6(o,name,fieldType,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
522 ''' 523 Build the encoder for aisstr6 variables. Generally are arrays. 524 @bug: do we need to optionally check for a valid string? 525 526 @type o: file like obj 527 @param o: where write the code 528 @type name: str 529 @param name: field name 530 @type fieldType: str 531 @param fieldType: uint, bool, etc. 532 @type numbits: int >= 1 533 @param numbits: How many bits per unit datum (must be 1..32) 534 @type required: bool or None 535 @param required: If not None, then the value must be set to this. 536 @type arraylen: int >= 1 537 @param arraylen: many unsigned ints will there be? FIX: handle variable 538 @type unavailable: bool or None 539 @param unavailable: the default value to use if none given (if not None) 540 @return: None 541 ''' 542 if verbose: print ' encodeUInt:',name,fieldType,numbits,'Req:',required,'alen:',arraylen,unavailable 543 totLen = str(numbits*arraylen) 544 assert numbits==6 # Each character must be 6 bits 545 if verbose: o.write('\t### FIELD: '+name+' (type='+fieldType+')\n') 546 547 if None != required: 548 if verbose: print ' required:',required 549 required=int(required) 550 o.write('\tbvList.append(aisstring.encode(\''+str(required)+'\','+totLen+'))\n') 551 if verbose: o.write('\n') 552 return 553 554 if None==unavailable: 555 o.write('\tbvList.append(aisstring.encode(params[\''+name+'\'],'+totLen+'))\n') 556 else: # Have a default value that can be filled in 557 o.write("\tif '"+name+"' in params:\n") 558 o.write('\t\tbvList.append(aisstring.encode(params[\''+name+'\'],'+totLen+'))\n') 559 o.write('\telse:\n') 560 o.write('\t\tbvList.append(aisstring.encode(\''+str(unavailable)+'\','+totLen+'))\n') 561 562 if verbose: o.write('\n')
563 564
565 -def encodeInt(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
566 ''' 567 Build the encoder for signed integer variables 568 569 @type o: file like obj 570 @param o: where write the code 571 @type name: str 572 @param name: field name 573 @type type: str 574 @param type: uint, bool, etc. 575 @type numbits: int >= 1 576 @param numbits: How many bits per unit datum (must be 1..32) 577 @type required: bool or None 578 @param required: If not None, then the value must be set to this. 579 @type arraylen: int >= 1 580 @param arraylen: many signed ints will there be? FIX: handle variable 581 @type unavailable: number or None 582 @param unavailable: the default value to use if none given (if not None) 583 @return: None 584 ''' 585 if verbose: print ' encodeInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 586 587 assert numbits>=1 and numbits<=32 588 if arraylen != 1: assert False # FIX... handle arrays 589 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 590 591 if None != required: 592 if verbose: print ' required:',required 593 required=int(required) 594 o.write('\tbvList.append(binary.bvFromSignedInt('+str(required)+','+str(numbits)+'))\n') 595 if verbose: o.write('\n') 596 return 597 598 599 if None==unavailable: 600 o.write('\tbvList.append(binary.bvFromSignedInt(params[\''+name+'\'],'+str(numbits)+'))\n') 601 else: # Have a default value that can be filled in 602 #assert type(unavailable)== 603 int(unavailable) # Make sure unavailable is a number object 604 o.write("\tif '"+name+"' in params:\n") 605 o.write('\t\tbvList.append(binary.bvFromSignedInt(params[\''+name+'\']'+','+str(numbits)+'))\n') 606 o.write('\telse:\n') 607 o.write('\t\tbvList.append(binary.bvFromSignedInt('+str(unavailable)+','+str(numbits)+'))\n') 608 609 if verbose: o.write('\n')
610 611 612 613 # FIX: Ummm... why am I passing the type? I guess it makes the one print statement easier
614 -def encodeDecimal(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
615 ''' 616 Build the encoder for signed decimal variables 617 618 @type o: file like obj 619 @param o: where write the code 620 @type name: str 621 @param name: field name 622 @type type: str 623 @param type: decimal 624 @type numbits: int >= 1 625 @param numbits: How many bits per unit datum (must be 1..32) 626 @type required: bool or None 627 @param required: If not None, then the value must be set to this. 628 @type arraylen: int >= 1 629 @param arraylen: many decimals will there be? FIX: handle variable 630 @type unavailable: Decimal or None 631 @param unavailable: the default value to use if none given (if not None) 632 @return: None 633 ''' 634 if verbose: print ' encodeDecimal:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 635 636 assert numbits>=1 and numbits<=32 637 if arraylen != 1: assert False # FIX... handle arrays 638 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 639 640 # FIX: optimize to not emit the scaling when it is not needed... or tell the user to switch to an int! 641 if None == scale: 642 print 'WARNING: if you are not scaling, then you probably want to use an int instead!' 643 print 'Beware canadians bearing travel videos' 644 scale='1' 645 646 if None != required: 647 if verbose: print ' required:',required 648 required=int(required) 649 o.write('\tbvList.append(binary.bvFromSignedInt('+str(int(Decimal(required)*Decimal(scale)))+','+str(numbits)+'))\n') 650 if verbose: o.write('\n') 651 return 652 653 # FIX: can I get rid of the Decimal around params? 654 if None==unavailable: 655 o.write('\tbvList.append(binary.bvFromSignedInt(int(Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')),'+str(numbits)+'))\n') 656 else: # Have a default value that can be filled in 657 o.write("\tif '"+name+"' in params:\n") 658 o.write('\t\tbvList.append(binary.bvFromSignedInt(int(Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')),'+str(numbits)+'))\n') 659 o.write('\telse:\n') 660 o.write('\t\tbvList.append(binary.bvFromSignedInt('+str(int(Decimal(unavailable)*Decimal(scale)))+','+str(numbits)+'))\n') 661 662 if verbose: o.write('\n')
663 664 665
666 -def encodeUDecimal(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
667 ''' 668 Build the encoder for signed decimal variables 669 670 @type o: file like obj 671 @param o: where write the code 672 @type name: str 673 @param name: field name 674 @type type: str 675 @param type: decimal 676 @type numbits: int >= 1 677 @param numbits: How many bits per unit datum (must be 1..32) 678 @type required: bool or None 679 @param required: If not None, then the value must be set to this. 680 @type arraylen: int >= 1 681 @param arraylen: many decimals will there be? FIX: handle variable 682 @type unavailable: Decimal or None 683 @param unavailable: the default value to use if none given (if not None) 684 @return: None 685 ''' 686 if verbose: print ' encodeDecimal:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 687 assert type=='udecimal' 688 assert numbits>=1 and numbits<=32 689 if arraylen != 1: assert False # FIX... handle arrays 690 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 691 692 # FIX: optimize to not emit the scaling when it is not needed... or tell the user to switch to an int! 693 if None == scale: 694 print 'WARNING: if you are not scaling, then you probably want to use an int instead!' 695 print 'Beware canadians bearing travel videos' 696 scale='1' 697 698 if None != required: 699 if verbose: print ' required:',required 700 required=int(required) 701 assert(0<=required) 702 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(int(Decimal(required)*Decimal(scale)))+'),'+str(numbits)+'))\n') 703 if verbose: o.write('\n') 704 return 705 706 # FIX: can I get rid of the Decimal around params? 707 if None==unavailable: 708 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')))),'+str(numbits)+'))\n') 709 else: # Have a default value that can be filled in 710 o.write("\tif '"+name+"' in params:\n") 711 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')))),'+str(numbits)+'))\n') 712 o.write('\telse:\n') 713 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int('+str(int(Decimal(unavailable)*Decimal(scale)))+')),'+str(numbits)+'))\n') 714 715 if verbose: o.write('\n')
716 717 718
719 -def encodeBinary(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
720 ''' 721 Build the encoder for binary variables. This is just a pass through. 722 This is used for the ais binary message wrapper (e.g. msg 8). Do 723 not use it within binary messages. 724 725 @type o: file like obj 726 @param o: where write the code 727 @type name: str 728 @param name: field name 729 @type type: str 730 @param type: binary 731 @type numbits: int >= 1 732 @param numbits: How many bits per unit datum (must be 1..1024 or so) 733 @type required: bool or None 734 @param required: If not None, then the value must be set to this. 735 @type arraylen: int >= 1 736 @param arraylen: many decimals will there be? FIX: handle variable 737 @type unavailable: Decimal or None 738 @param unavailable: the default value to use if none given (if not None) 739 @return: None 740 ''' 741 if verbose: print ' encode'+name+':',type,numbits,'Req:',required,'alen:',arraylen,unavailable 742 assert type=='binary' 743 assert (numbits>=1 and numbits<=1024) or numbits==-1 744 assert (None == required) # don't allow this 745 assert (None == unavailable) # don't allow this 746 747 if arraylen != 1: assert False # Do not handle arrays. Arrays of bits is just not necessary. 748 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 749 750 # FIX: can I get rid of the Decimal around params? 751 o.write('\tbvList.append(params[\''+name+'\'])\n') # Just pass it through 752 753 if verbose: o.write('\n')
754 755 756 ###################################################################### 757 # DECODERS 758 ###################################################################### 759
760 -def decodeBool(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 761 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
762 ''' 763 Build the decoder for boolean variables 764 765 @type o: file like obj 766 @param o: where write the code 767 @type name: str 768 @param name: field name 769 @type type: str 770 @param type: uint, bool, etc. 771 @type startindex: int 772 @param startindex: bit that begins the bool(s) 773 @type numbits: int = 1 774 @param numbits: How many bits per unit datum (must be 1 for bools) 775 @type required: bool or None 776 @param required: If not None, then the value must be set to this. 777 @type arraylen: int >= 1 778 @param arraylen: many bools will there be? FIX: handle variable 779 @type unavailable: bool or None 780 @param unavailable: the default value to use if none given (if not None) 781 @type bv: str 782 @param bv: BitVector containing the incoming data 783 @type dataDict: str 784 @param dataDict: dictionary in which to place the results 785 @type decodeOnly: bool 786 @param decodeOnly: Set to true to only get the code for decoding 787 @rtype: int 788 @return: index one past the end of where this read 789 ''' 790 assert(type=='bool') 791 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 792 #int(startindex); int(numbits) # Make sure it is a number 793 assert numbits==1 794 assert arraylen == 1 # FIX... handle arrays 795 796 if None != required: 797 assert type(required)==bool 798 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 799 if required: o.write('True\n') 800 else: o.write('False\n') 801 if not decodeOnly: o.write('\n') 802 return int(startindex)+int(numbits) 803 804 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 805 o.write('bool(int('+bv+'['+str(startindex)+':'+str(startindex+int(numbits)*int(arraylen))+']))') 806 if not decodeOnly: o.write('\n') 807 808 return int(startindex)+int(numbits)
809 810
811 -def decodeUInt(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 812 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
813 ''' 814 Build the decoder for unsigned integer variables 815 816 @type o: file like obj 817 @param o: where write the code 818 @type name: str 819 @param name: field name 820 @type type: str 821 @param type: uint, etc. 822 @type startindex: int 823 @param startindex: bit that begins the uint(s) 824 @type numbits: int >= 1 825 @param numbits: How many bits per unit datum 826 @type required: int or None 827 @param required: If not None, then the value must be set to this. 828 @type arraylen: int >= 1 829 @param arraylen: many ints will there be? FIX: handle variable 830 @type unavailable: int or None 831 @param unavailable: the default value to use if none given (if not None) 832 @type bv: str 833 @param bv: BitVector containing the incoming data 834 @type dataDict: str 835 @param dataDict: dictionary in which to place the results 836 @type decodeOnly: bool 837 @param decodeOnly: Set to true to only get the code for decoding 838 @rtype: int 839 @return: index one past the end of where this read 840 ''' 841 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 842 if None==arraylen: arraylen=1 843 assert arraylen == 1 # FIX... handle arrays 844 assert numbits>=1 845 if not decodeOnly: verbose=False 846 847 if None != required: 848 int(required) # Make sure required is a number 849 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 850 o.write(str(required)) 851 if not decodeOnly: o.write('\n') 852 return startindex+numbits 853 854 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 855 o.write('int('+bv+'['+str(startindex)+':'+str(startindex+int(numbits)*int(arraylen))+'])') 856 if not decodeOnly: o.write('\n') 857 if verbose: o.write('\n') 858 859 return startindex+numbits
860 861
862 -def decodeInt(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 863 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
864 ''' 865 Build the decoder for unsigned integer variables 866 867 @type o: file like obj 868 @param o: where write the code 869 @type name: str 870 @param name: field name 871 @type type: str 872 @param type: int 873 @type startindex: int 874 @param startindex: bit that begins the int(s) 875 @type numbits: int >= 1 876 @param numbits: How many bits per unit datum 877 @type required: int or None 878 @param required: If not None, then the value must be set to this. 879 @type arraylen: int >= 1 880 @param arraylen: many ints will there be? FIX: handle variable 881 @type unavailable: int or None 882 @param unavailable: the default value to use if none given (if not None) 883 @type bv: str 884 @param bv: BitVector containing the incoming data 885 @type dataDict: str 886 @param dataDict: dictionary in which to place the results 887 @type decodeOnly: bool 888 @param decodeOnly: Set to true to only get the code for decoding 889 @rtype: int 890 @return: index one past the end of where this read 891 ''' 892 assert type=='int' 893 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 894 if None==arraylen: arraylen=1 895 end = startindex+int(numbits)*int(arraylen) 896 assert arraylen == 1 # FIX... handle arrays 897 assert numbits>=1 898 899 if None != required: 900 int(required) # Make sure required is a number 901 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 902 o.write(str(required)) 903 if not decodeOnly: o.write('\n') 904 return end 905 906 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 907 o.write('binary.signedIntFromBV('+bv+'['+str(startindex)+':'+str(end)+'])') 908 if not decodeOnly: o.write('\n') 909 if verbose: o.write('\n') 910 911 return end
912
913 -def decodeFloat(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 914 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
915 ''' 916 Build the decoder for IEEE float variables 917 918 @type o: file like obj 919 @param o: where write the code 920 @type name: str 921 @param name: field name 922 @type type: str 923 @param type: int 924 @type startindex: int 925 @param startindex: bit that begins the int(s) 926 @type numbits: int >= 1 927 @param numbits: How many bits per unit datum 928 @type required: float or None 929 @param required: If not None, then the value must be set to this. 930 @type arraylen: int >= 1 931 @param arraylen: many ints will there be? FIX: handle variable 932 @type unavailable: float or None 933 @param unavailable: the default value to use if none given (if not None) 934 @type bv: str 935 @param bv: BitVector containing the incoming data 936 @type dataDict: str 937 @param dataDict: dictionary in which to place the results 938 @type decodeOnly: bool 939 @param decodeOnly: Set to true to only get the code for decoding 940 @rtype: int 941 @return: index one past the end of where this read 942 ''' 943 assert type=='float' 944 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 945 if None==arraylen: arraylen=1 946 end = startindex+int(numbits)*int(arraylen) 947 assert arraylen == 1 # FIX... handle arrays 948 assert numbits>=1 949 950 if None != required: 951 float(required) # Make sure required is a number 952 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 953 o.write(str(required)) 954 if not decodeOnly: o.write('\n') 955 if verbose: o.write('\n') 956 return end 957 958 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 959 o.write('binary.bitvec2float('+bv+'['+str(startindex)+':'+str(end)+'])') 960 if not decodeOnly: o.write('\n') 961 962 return end
963 964
965 -def decodeAisstr6(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 966 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
967 ''' 968 Build the decoder for aisstr6 variables. Generally arrays. 969 @bug: FIX: validate strings?? 970 @type o: file like obj 971 @param o: where write the code 972 @type name: str 973 @param name: field name 974 @type type: str 975 @param type: 'aisstr6' 976 @type startindex: int 977 @param startindex: bit that begins the int(s) 978 @type numbits: int >= 1 979 @param numbits: How many bits per unit datum 980 @type required: restricted str or None 981 @param required: If not None, then the value must be set to this. 982 @type arraylen: int >= 1 983 @param arraylen: many ints will there be? FIX: handle variable 984 @type unavailable: restricted str or None 985 @param unavailable: the default value to use if none given (if not None) 986 @type bv: str 987 @param bv: BitVector containing the incoming data 988 @type dataDict: str 989 @param dataDict: dictionary in which to place the results 990 @type decodeOnly: bool 991 @param decodeOnly: Set to true to only get the code for decoding 992 @rtype: int 993 @return: index one past the end of where this read 994 ''' 995 assert type=='aisstr6' 996 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 997 if None==arraylen: arraylen=1 998 end = startindex+int(numbits)*int(arraylen) 999 assert arraylen >= 1 # FIX... handle arrays 1000 assert numbits>=1 1001 1002 if None != required: 1003 float(required) # Make sure required is a number 1004 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1005 o.write(required) 1006 if not decodeOnly: o.write('\n') 1007 return end 1008 1009 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1010 o.write('aisstring.decode('+bv+'['+str(startindex)+':'+str(end)+'])') 1011 if not decodeOnly: o.write('\n') 1012 1013 return end
1014 1015
1016 -def decodeDecimal(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1017 bv='bv',dataDict='r',verbose=False,scale=None, decodeOnly=False):
1018 ''' 1019 Build the decoder for signed decimal variables 1020 1021 @type o: file like obj 1022 @param o: where write the code 1023 @type name: str 1024 @param name: field name 1025 @type type: str 1026 @param type: 'decimal' 1027 @type startindex: int 1028 @param startindex: bit that begins the int(s) 1029 @type numbits: int >= 1 1030 @param numbits: How many bits per unit datum 1031 @type required: Decimal or None 1032 @param required: If not None, then the value must be set to this. 1033 @type arraylen: int >= 1 1034 @param arraylen: many ints will there be? FIX: handle variable 1035 @type unavailable: Decimal or None 1036 @param unavailable: the default value to use if none given (if not None) 1037 @type bv: str 1038 @param bv: BitVector containing the incoming data 1039 @type dataDict: str 1040 @param dataDict: dictionary in which to place the results 1041 @type decodeOnly: bool 1042 @param decodeOnly: Set to true to only get the code for decoding 1043 @rtype: int 1044 @return: index one past the end of where this read 1045 ''' 1046 assert type=='decimal' 1047 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1048 if None==arraylen: arraylen=1 1049 end = startindex+int(numbits)*int(arraylen) 1050 assert arraylen == 1 # FIX... handle arrays 1051 assert numbits>=1 and numbits <= 32 1052 1053 if None == scale: scale='1' # Warning about this was in the encode section 1054 1055 if None != required: 1056 Decimal(required) # Make sure required is a number 1057 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1058 o.write(str(Decimal(required))+'/Decimal(\''+scale+'\')') 1059 if not decodeOnly: o.write('\n') 1060 return end 1061 1062 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1063 o.write('Decimal(binary.signedIntFromBV('+bv+'['+str(startindex)+':'+str(end)+']))/Decimal(\''+scale+'\')') 1064 if not decodeOnly: o.write('\n') 1065 1066 return end
1067 1068 1069 1070
1071 -def decodeUDecimal(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1072 bv='bv',dataDict='r',verbose=False,scale=None, decodeOnly=False):
1073 ''' 1074 Build the decoder for unsigned decimal variables 1075 1076 @type o: file like obj 1077 @param o: where write the code 1078 @type name: str 1079 @param name: field name 1080 @type type: str 1081 @param type: 'udecimal' 1082 @type startindex: int 1083 @param startindex: bit that begins the int(s) 1084 @type numbits: int >= 1 1085 @param numbits: How many bits per unit datum 1086 @type required: Decimal or None 1087 @param required: If not None, then the value must be set to this. 1088 @type arraylen: int >= 1 1089 @param arraylen: many ints will there be? FIX: handle variable 1090 @type unavailable: Decimal or None 1091 @param unavailable: the default value to use if none given (if not None) 1092 @type bv: str 1093 @param bv: BitVector containing the incoming data 1094 @type dataDict: str 1095 @param dataDict: dictionary in which to place the results 1096 @type decodeOnly: bool 1097 @param decodeOnly: Set to true to only get the code for decoding 1098 @rtype: int 1099 @return: index one past the end of where this read 1100 ''' 1101 assert type=='udecimal' 1102 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1103 if None==arraylen: arraylen=1 1104 end = startindex+int(numbits)*int(arraylen) 1105 assert arraylen == 1 # FIX... handle arrays 1106 assert numbits>=1 and numbits <= 32 1107 1108 if None == scale: scale='1' # Warning about this was in the encode section 1109 1110 if None != required: 1111 assert (Decimal(required)>=0.) # Make sure required is a number and not negative 1112 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1113 o.write(str(Decimal(required))+'/Decimal(\''+scale+'\')') 1114 if not decodeOnly: o.write('\n') 1115 return end 1116 1117 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1118 o.write('Decimal(int('+bv+'['+str(startindex)+':'+str(end)+']))/Decimal(\''+scale+'\')') 1119 if not decodeOnly: o.write('\n') 1120 1121 return end
1122 1123
1124 -def decodeBinary(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1125 bv='bv',dataDict='r',verbose=False,scale=None, decodeOnly=False):
1126 ''' 1127 Build the decoder for unsigned decimal variables 1128 1129 @type o: file like obj 1130 @param o: where write the code 1131 @type name: str 1132 @param name: field name 1133 @type type: str 1134 @param type: 'udecimal' 1135 @type startindex: int 1136 @param startindex: bit that begins the int(s) 1137 @type numbits: int >= 1 1138 @param numbits: How many bits per unit datum. If -1, then read to the end of the message 1139 @type required: Decimal or None 1140 @param required: If not None, then the value must be set to this. 1141 @type arraylen: int >= 1 1142 @param arraylen: many ints will there be? FIX: handle variable 1143 @type unavailable: Decimal or None 1144 @param unavailable: the default value to use if none given (if not None) 1145 @type bv: str 1146 @param bv: BitVector containing the incoming data 1147 @type dataDict: str 1148 @param dataDict: dictionary in which to place the results 1149 @type decodeOnly: bool 1150 @param decodeOnly: Set to true to only get the code for decoding 1151 @rtype: int 1152 @return: index one past the end of where this read 1153 ''' 1154 assert type=='binary' 1155 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1156 if None==arraylen: arraylen=1 1157 end = startindex+int(numbits)*int(arraylen) 1158 assert arraylen == 1 # FIX... handle arrays 1159 assert (numbits>=1 and numbits <= 1024) or -1==numbits # What is good max? 1160 # FIX: assert not required and not an array an not unavailable 1161 1162 1163 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1164 o.write(bv+'['+str(startindex)+':') 1165 if int(numbits) != -1: o.write(str(end)) # -1 means go to the end of the message 1166 o.write(']') 1167 if not decodeOnly: o.write('\n') 1168 1169 return end
1170 1171 1172 1173 ###################################################################### 1174 # THE REST 1175 ###################################################################### 1176 1177 1178
1179 -def buildTestParamFunc(o,msgET, verbose=False, prefixName=False):
1180 '''Scrape the testvalues to make a basic param 1181 1182 @bug: FIX: make this create a dictionary that sits in the overall namespace and spit out deep copies? 1183 ''' 1184 name = msgET.attrib['name'] 1185 1186 if prefixName: o.write('def '+name+'TestParams():\n') 1187 else: o.write('def testParams():\n') 1188 o.write("\t'''Return a params file base on the testvalue tags.\n\t@rtype: dict\n\t@return: params based on testvalue tags\n\t'''\n") 1189 o.write('\tparams = {}\n') 1190 for field in msgET.xpath('field'): 1191 name = field.attrib['name'] 1192 type = field.attrib['type'] 1193 if verbose: print 'buildTestParamFunc ...',name,type 1194 val = None 1195 if hasSubtag(field,'testvalue') and hasSubtag(field,'required'): 1196 print 'ERROR: can not have both test value and required tags in the same field' 1197 assert(False) 1198 if hasSubtag(field,'testvalue'): 1199 val = field.xpath('testvalue')[0].text 1200 else: 1201 if not hasSubtag(field,'required'): 1202 sys.exit("ERROR: missing required or testvalue for field: "+name) 1203 val = field.xpath('required')[0].text 1204 if verbose: print 'buildTestParamFunc for field '+name+' ...',type,val 1205 o.write('\tparams[\''+name+'\'] = ') 1206 if type=='bool': 1207 if val=='1' or val.lower=='true': val = 'True' 1208 else: val = 'False' 1209 o.write(val) 1210 elif type in ('uint','int','float'): 1211 o.write(val) 1212 elif type in ('decimal','udecimal'): 1213 o.write('Decimal(\''+val+'\')') 1214 1215 elif type in ('aisstr6'): 1216 o.write('\''+val+'\'') 1217 elif type in ('binary'): 1218 o.write('BitVector(bitstring=\''+val+'\')') 1219 else: 1220 print 'ERROR: type not handled ...',type,' (found in the ',name,' field). Time to buy more coffee' 1221 suggestType(name,type) 1222 assert(False) 1223 1224 o.write('\n') 1225 1226 1227 o.write('\n\treturn params\n\n')
1228
1229 -def buildUnitTest(o,msgET, verbose=False, prefixName=False):
1230 ''' 1231 Write the unittests for a message 1232 1233 @param o: open file where resulting code will be written 1234 @param msgET: Element Tree starting at a message node 1235 ''' 1236 assert(msgET.tag=='message') 1237 name = msgET.attrib['name'] 1238 1239 buildTestParamFunc(o,msgET, prefixName=prefixName) 1240 1241 o.write('class Test'+name+'(unittest.TestCase):\n') 1242 o.write("\t'''Use testvalue tag text from each type to build test case the "+name+" message'''\n") 1243 o.write('\tdef testEncodeDecode(self):\n\n') 1244 if prefixName: 1245 o.write('\t\tparams = '+name+'TestParams()\n') 1246 o.write('\t\tbits = '+name+'Encode(params)\n') 1247 o.write('\t\tr = '+name+'Decode(bits)\n\n') 1248 else: 1249 o.write('\t\tparams = testParams()\n') 1250 o.write('\t\tbits = encode(params)\n') 1251 o.write('\t\tr = decode(bits)\n\n') 1252 1253 o.write('\t\t# Check that each parameter came through ok.\n') 1254 for field in msgET.xpath('field'): 1255 name = field.attrib['name'] 1256 type = field.attrib['type'] 1257 if type in ('bool','uint','int','aisstr6','binary'): 1258 o.write('\t\tself.failUnlessEqual(r[\''+name+'\'],params[\''+name+'\'])\n') 1259 else: 1260 # float, decimal, udecimal 1261 # FIX: look up the decimal places if decimal 1262 places = '3' 1263 if hasSubtag(field,'decimalplaces'): places = field.xpath('decimalplaces')[0].text 1264 o.write('\t\tself.failUnlessAlmostEqual(r[\''+name+'\'],params[\''+name+'\'],'+places+')\n')
1265 1266 1267
1268 -def buildEncode(o,msgET, verbose=False, prefixName=False):
1269 ''' 1270 Write the encoder/decoder for a message 1271 1272 http://jaynes.colorado.edu/PythonIdioms.html 1273 1274 @param o: open file where resulting code will be written 1275 @param msgET: Element Tree starting at a message node 1276 ''' 1277 assert(msgET.tag=='message') 1278 name = msgET.attrib['name'] 1279 1280 print 'Generating encoder for',name # FIX: verbose? 1281 funcName = 'encode' 1282 if prefixName: funcName = name+'Encode' 1283 o.write('def '+funcName+'(params, validate=False):\n') 1284 1285 ######################################## 1286 # doc string 1287 o.write("\t'''Create a "+name+" binary message payload to pack into an AIS Msg "+name+".\n\n") 1288 o.write('\tFields in params:\n') 1289 for field in msgET.xpath('field'): 1290 desc = field[0].text.replace('\n',' ') # get ride of new lines 1291 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc) # give the description 1292 if len(field.xpath("required")) == 1: 1293 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")') 1294 1295 o.write('\n') 1296 o.write('\t@param params: Dictionary of field names/values. Throws a ValueError exception if required is missing\n') 1297 o.write('\t@param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.\n') 1298 1299 o.write("\t@rtype: BitVector\n") 1300 o.write("\t@return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8\n") 1301 o.write("\t@note: The returned bits may not be 6 bit aligned. It is up to you to pad out the bits.\n") 1302 o.write("\t'''\n\n") 1303 1304 ######################################## 1305 # Actually build the code 1306 1307 o.write('\tbvList = []\n') 1308 1309 if verbose: print 'number of fields = ', len(msgET.xpath('field')) 1310 1311 dynamicArrays = False # Set to true when positioning must be calculated 1312 1313 for field in msgET.xpath('field'): 1314 name = field.attrib['name'] 1315 type = field.attrib['type'] 1316 numbits = int(field.attrib['numberofbits']) 1317 required = None; 1318 if hasSubtag(field,'required'): 1319 required = field.xpath('required')[0].text 1320 #print 'required set for',name,'to',required 1321 unavailable=None; 1322 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 1323 arraylen=1 1324 if 'arraylength' in field.attrib: 1325 arraylen=int(field.attrib['arraylength']) 1326 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 1327 else: 1328 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')' 1329 1330 if type=='bool' : encodeBool (o,name,type,numbits,required,arraylen,unavailable) 1331 elif type=='uint' : encodeUInt (o,name,type,numbits,required,arraylen,unavailable) 1332 elif type=='int' : encodeInt (o,name,type,numbits,required,arraylen,unavailable) 1333 elif type=='float' : encodeFloat (o,name,type,numbits,required,arraylen,unavailable) 1334 elif type=='aisstr6': encodeAisstr6(o,name,type,numbits,required,arraylen,unavailable) 1335 elif type=='decimal': 1336 scale = None 1337 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1338 encodeDecimal(o,name,type,numbits,required,arraylen,unavailable,scale=scale) 1339 elif type=='udecimal': 1340 scale = None 1341 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1342 encodeUDecimal(o,name,type,numbits,required,arraylen,unavailable,scale=scale) 1343 elif type=='binary': encodeBinary(o,name,type,numbits,required,arraylen,unavailable) 1344 else: 1345 print 'WARNING: In buildEncode - Unhandled field type for',name,'...',type 1346 suggestType (name,type) 1347 assert False 1348 1349 # o.write('\n\tbv=binary.joinBV(bvList)\n') 1350 # o.write('\n\tbvLen=len(bv)\n') 1351 # o.write('\n\tif bvLen%6!=0:\n') 1352 # o.write('\n\t bv = bv + BitVector(size=bvLen%6) # \n') 1353 # o.write('\n\treturn bv\n\n') 1354 o.write('\n\treturn binary.joinBV(bvList)\n\n')
1355 1356 1357 ###################################################################### 1358 # DECODER ONE PART AT A TIME - if only using a small part, save some cycles! 1359 1360 1361 #, msgDict=None):
1362 -def buildDecodeParts(o,msgET, verbose=False, prefixName=False):
1363 1364 ''' 1365 Write the decoder for a message 1366 1367 @param o: open file where resulting code will be written 1368 @type msgET: elementtree 1369 @param prefixName: if True, put the name of the message on the functions. 1370 @param msgET: Element Tree starting at a message node 1371 @return: None 1372 1373 @todo: FIX: doc strings for each decode! 1374 @todo: FIX: check for a dac,fid, or efid. If exists, then this is an AIS Msg 8 payload 1375 @todo: May want to take a dictionary of already decoded fields to speed things that need prior info 1376 for things like variable length arrays 1377 ''' 1378 1379 assert(msgET.tag=='message') 1380 name = msgET.attrib['name'] 1381 1382 print 'Generating partial decode functions for',name 1383 1384 baseName = name+'Decode' 1385 if not prefixName: baseName = 'decode' 1386 1387 startindex = 0 # Where we are in the bitvector... FIX: what about variable length jobs? 1388 1389 for field in msgET.xpath('field'): 1390 name = field.attrib['name'] 1391 type = field.attrib['type'] 1392 1393 o.write('def '+baseName+name+'(bv, validate=False):\n') 1394 # Follow the same convention of decoding into a dict so that code is the same 1395 #o.write('\tr={};') 1396 o.write('\treturn ') 1397 1398 numbits = int(field.attrib['numberofbits']) 1399 required = None; 1400 if hasSubtag(field,'required'): 1401 required = field.xpath('required')[0].text 1402 unavailable=None; 1403 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 1404 arraylen=1 1405 if 'arraylength' in field.attrib: 1406 arraylen=int(field.attrib['arraylength']) 1407 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 1408 1409 assert None!=startindex 1410 if verbose: print 'startindex',startindex 1411 if type=='bool' : startindex = decodeBool (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1412 elif type=='uint' : startindex = decodeUInt (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1413 elif type=='int' : startindex = decodeInt (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1414 elif type=='float' : startindex = decodeFloat (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1415 elif type=='aisstr6': startindex = decodeAisstr6(o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1416 elif type=='binary' : startindex = decodeBinary (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1417 1418 elif type=='decimal': 1419 scale = None 1420 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1421 startindex = decodeDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale,decodeOnly=True) 1422 elif type=='udecimal': 1423 scale = None 1424 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1425 startindex = decodeUDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale,decodeOnly=True) 1426 1427 else: 1428 print 'WARNING: In buildDecode - Unhandled field type for',name,'...',type 1429 suggestType (name,type) 1430 assert False 1431 1432 #o.write('\treturn(r[\''+name+'\'])\n\n') 1433 o.write('\n\n')
1434 1435 1436 ###################################################################### 1437 # DECODER RING 1438
1439 -def buildDecode(o,msgET, verbose=False, prefixName=False):
1440 ''' 1441 Write the decoder for a message 1442 1443 @param o: open file where resulting code will be written 1444 @type msgET: elementtree 1445 @param msgET: Element Tree starting at a message node 1446 @return: None 1447 1448 @todo: FIX: check for a dac,fid, or efid. If exists, then this is an AIS Msg 8 payload 1449 ''' 1450 1451 assert(msgET.tag=='message') 1452 name = msgET.attrib['name'] 1453 1454 print 'Generating decoder for',name 1455 funcName = 'decode' 1456 if prefixName: funcName = name+'Decode' 1457 o.write('def '+funcName+'(bv, validate=False):\n') 1458 1459 ######################################## 1460 # doc string 1461 o.write("\t'''Unpack a "+name+" message \n\n") 1462 o.write('\tFields in params:\n') 1463 for field in msgET.xpath('field'): 1464 desc = field[0].text.replace('\n',' ') # get ride of new lines 1465 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc) # give the description 1466 if len(field.xpath("required")) == 1: 1467 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")') 1468 1469 o.write('\n') 1470 o.write('\t@type bv: BitVector\n') 1471 o.write('\t@param bv: Bits defining a message\n') 1472 o.write('\t@param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.\n') 1473 1474 o.write("\t@rtype: dict\n") 1475 o.write("\t@return: params\n") 1476 o.write("\t'''\n\n") 1477 1478 1479 o.write('\t#Would be nice to check the bit count here..\n') 1480 o.write('\t#if validate:\n') 1481 o.write('\t#\tassert (len(bv)==FIX: SOME NUMBER)\n') 1482 1483 1484 ######################################## 1485 # Actually build the code 1486 1487 o.write('\tr = {}\n') 1488 1489 1490 if verbose: print 'number of fields = ', len(msgET.xpath('field')) 1491 1492 dynamicArrays = False # Set to true when positioning must be calculated 1493 1494 startindex = 0 # Where we are in the bitvector... FIX: what about variable length jobs? 1495 1496 for field in msgET.xpath('field'): 1497 name = field.attrib['name'] 1498 type = field.attrib['type'] 1499 numbits = int(field.attrib['numberofbits']) 1500 required = None; 1501 if hasSubtag(field,'required'): 1502 required = field.xpath('required')[0].text 1503 #print 'required set for',name,'to',required 1504 unavailable=None; 1505 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 1506 arraylen=1 1507 if 'arraylength' in field.attrib: 1508 arraylen=int(field.attrib['arraylength']) 1509 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 1510 else: 1511 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')' 1512 1513 assert None!=startindex 1514 if verbose: print 'startindex',startindex 1515 if type=='bool' : startindex = decodeBool (o,name,type,startindex,numbits,required,arraylen,unavailable) 1516 elif type=='uint' : startindex = decodeUInt (o,name,type,startindex,numbits,required,arraylen,unavailable) 1517 elif type=='int' : startindex = decodeInt (o,name,type,startindex,numbits,required,arraylen,unavailable) 1518 elif type=='float' : startindex = decodeFloat (o,name,type,startindex,numbits,required,arraylen,unavailable) 1519 elif type=='aisstr6': startindex = decodeAisstr6(o,name,type,startindex,numbits,required,arraylen,unavailable) 1520 elif type=='binary' : startindex = decodeBinary (o,name,type,startindex,numbits,required,arraylen,unavailable) 1521 1522 elif type=='decimal': 1523 scale = None 1524 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1525 #print 'pre call...' 1526 #print ' required: "'+str(required)+'" scale: "'+str(scale)+'" unavail:', unavailable 1527 startindex = decodeDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale) 1528 elif type=='udecimal': 1529 scale = None 1530 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1531 #print 'pre call...' 1532 #print ' required: "'+str(required)+'" scale: "'+str(scale)+'" unavail:', unavailable 1533 startindex = decodeUDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale) 1534 1535 else: 1536 print 'WARNING: In buildDecode - Unhandled field type for',name,'...',type 1537 suggestType (name,type) 1538 assert False 1539 1540 1541 if None==startindex: print 'FIX: here. drat. treat me right' 1542 assert None!=startindex 1543 1544 1545 o.write('\treturn r\n\n')
1546 1547 1548 1549 #def getPythonType(aType): 1550 # ''' 1551 # Translate a 1552 # @rtype: str 1553 # @return: name of the python type 1554 # ''' 1555 1556 aisType2pythonType={ 1557 'bool':'Bool', 1558 'uint':'int', 1559 'int':'int', 1560 'udecimal':'Decimal', 1561 'decimal':'Decimal', 1562 'aisstr6':'str', 1563 'binary':'str', 1564 'float':'Float', 1565 } 1566 1567 import copy 1568 aisType2optParseType=copy.deepcopy(aisType2pythonType) 1569 aisType2optParseType['bool']='int' # FIX: make these appear as a flag variable 1570 aisType2optParseType['aisstr6']='string' 1571 aisType2optParseType['aisstr6']='string' 1572 aisType2optParseType['binary']='string' 1573 aisType2optParseType['udecimal']='string' # FIX: Is this a good choice? 1574 aisType2optParseType['decimal']='string' 1575 aisType2optParseType['float']='float' 1576 1577
1578 -def buildOptParse(o,msgET, prefixName=False):
1579 '''Create a function that adds the options to a parse object''' 1580 1581 assert None != msgET 1582 assert msgET.tag=='message' 1583 msgName = msgET.attrib['name'] 1584 1585 prefix='' 1586 if prefixName: prefix=msgName 1587 1588 funcName = 'addMsgOptions' 1589 if prefixName: funcName = msgName + 'AddMsgOptions' 1590 o.write('\ndef '+funcName+'(parser):') 1591 1592 # FIX: write out a doc string 1593 1594 o.write(''' 1595 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true', 1596 help='decode a "'''+msgName+'''" AIS message') 1597 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true', 1598 help='encode a "'''+msgName+'''" AIS message') 1599 ''') 1600 1601 #print 'here...', msgName, prefixName 1602 for field in msgET.xpath('field'): 1603 name = field.attrib['name'] 1604 fieldType = field.attrib['type'] 1605 if hasSubtag(field,'required'): 1606 print 'skipping required field ...',name,fieldType 1607 continue 1608 #print 'there',name,fieldType 1609 o.write('\tparser.add_option(\'--') 1610 if prefixName: o.write(msgName+'-') 1611 o.write(name+'-field\', dest=\''+name+'Field\'') 1612 if hasSubtag(field,'unavailable'): 1613 val = field.xpath('unavailable')[0].text 1614 o.write(',default=') 1615 if fieldType in ('uint','int','float'): 1616 o.write(val) 1617 elif fieldType in ('decimal','udecimal'): 1618 o.write('Decimal(\''+val+'\')') 1619 elif fieldType in ('aisstr6','bitvector'): 1620 o.write('\''+val+'\'') 1621 o.write(',metavar=\''+fieldType+'\',type=\''+aisType2optParseType[fieldType]+'\'') 1622 o.write('\n\t\t,help=\'Field parameter value [default: %default]\')\n')
1623 1624 1625
1626 -def buildMain(o, msgET, prefixName=False):
1627 assert None != msgET 1628 assert msgET.tag=='message' 1629 msgName = msgET.attrib['name'] 1630 1631 prefix='' 1632 if prefixName: prefix=msgName 1633 1634 buildOptParse(o, msgET, prefixName) 1635 1636 1637 o.write(''' 1638 ############################################################ 1639 if __name__=='__main__': 1640 1641 from optparse import OptionParser 1642 parser = OptionParser(usage="%prog [options]", 1643 version="%prog "+__version__) 1644 1645 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true', 1646 help='run the documentation tests') 1647 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true', 1648 help='run the unit tests') 1649 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 1650 help='Make the test output verbose') 1651 1652 # FIX: remove nmea from binary messages. No way to build the whole packet? 1653 # FIX: or build the surrounding msg 8 for a broadcast? 1654 typeChoices = ('binary','nmeapayload','nmea') # FIX: what about a USCG type message? 1655 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType' 1656 ,default='nmeapayload' 1657 ,help='What kind of string to expect ('+', '.join(typeChoices)+') [default: %default]') 1658 1659 outputChoices = ('std','html','xml') 1660 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType' 1661 ,default='std' 1662 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]') 1663 1664 parser.add_option('-o','--output',dest='outputFileName',default=None, 1665 help='Name of the python file to write [default: stdout]') 1666 1667 ''') 1668 1669 1670 o.write('''\taddMsgOptions(parser)\n''') 1671 1672 o.write(''' 1673 (options,args) = parser.parse_args() 1674 success=True 1675 1676 if options.doctest: 1677 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 1678 sys.argv= [sys.argv[0]] 1679 if options.verbose: sys.argv.append('-v') 1680 import doctest 1681 numfail,numtests=doctest.testmod() 1682 if numfail==0: print 'ok' 1683 else: 1684 print 'FAILED' 1685 success=False 1686 1687 if not success: sys.exit('Something Failed') 1688 del success # Hide success from epydoc 1689 1690 if options.unittest: 1691 sys.argv = [sys.argv[0]] 1692 if options.verbose: sys.argv.append('-v') 1693 unittest.main() 1694 1695 outfile = sys.stdout 1696 if None!=options.outputFileName: 1697 outfile = file(options.outputFileName,'w') 1698 1699 ''') 1700 1701 1702 1703 ############################## 1704 # encode 1705 ############################## 1706 # FIX: make everything tabs. Grr. 1707 o.write('\n\tif options.doEncode:\n') 1708 o.write('\t\t# First make sure all non required options are specified\n') 1709 for field in msgET.xpath('field'): 1710 name = field.attrib['name'] 1711 fieldType = field.attrib['type'] 1712 varName = prefix+name+'Field' 1713 if not hasSubtag(field,'required'): 1714 o.write('\t\tif None==options.'+varName+': parser.error("missing value for '+varName+'")\n') 1715 1716 # Build dict 1717 o.write('\t\tmsgDict={\n') 1718 for field in msgET.xpath('field'): 1719 name = field.attrib['name'] 1720 varName = prefix+name+'Field' 1721 if hasSubtag(field,'required'): 1722 o.write('\t\t\t\''+name+'\': \''+field.xpath('required')[0].text+'\',\n') 1723 else: 1724 o.write('\t\t\t\''+name+'\': options.'+varName+',\n') 1725 o.write('\t\t}\n') 1726 1727 encodeFunction = 'encode' 1728 if prefixName: encodeFunction = msgName+'Encode' 1729 o.write(''' 1730 bits = '''+encodeFunction+'''(msgDict) 1731 if 'binary'==options.ioType: print str(bits) 1732 elif 'nmeapayload'==options.ioType: 1733 # FIX: figure out if this might be necessary at compile time 1734 print "bitLen",len(bits) 1735 bitLen=len(bits) 1736 if bitLen%6!=0: 1737 bits = bits + BitVector(size=(6 - (bitLen%6))) # Pad out to multiple of 6 1738 print "result:",binary.bitvectoais6(bits)[0] 1739 1740 1741 # FIX: Do not emit this option for the binary message payloads. Does not make sense. 1742 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability") 1743 else: sys.exit('ERROR: unknown ioType. Help!') 1744 ''') 1745 1746 1747 ############################## 1748 # decode all 1749 ############################## 1750 decodeFunction = 'decode' 1751 printFields='printFields' 1752 if prefixName: 1753 decodeFunction = msgName+'Decode' 1754 printFields = msgName+'PrintFields' 1755 o.write(''' 1756 if options.doDecode: 1757 for msg in args: 1758 bv = None 1759 if 'binary' == options.ioType: bv = BitVector(bitstring=msg) 1760 elif 'nmeapayload'== options.ioType: bv = binary.ais6tobitvec(msg) 1761 elif 'nmea' == options.ioType: bv = binary.ais6tobitvec(msg.split(',')[5]) 1762 else: sys.exit('ERROR: unknown ioType. Help!') 1763 1764 '''+printFields+'''('''+decodeFunction+'''(bv),out=outfile,format=options.outputType) 1765 ''')
1766 1767 1768 1769 1770 ###################################################################### 1771 if __name__=='__main__': 1772 from optparse import OptionParser 1773 parser = OptionParser(usage="%prog [options]", 1774 version="%prog "+__version__) 1775 1776 parser.add_option('-o','--output',dest='outputFileName',default=None, 1777 help='Name of the python file to write') 1778 # help='Name of the python file to write [default: stdout]') 1779 1780 parser.add_option('-i','-x','--xml-definition',dest='xmlFileName',default=None, 1781 help='XML definition file for the msg to use') 1782 # help='XML definition file for the msg to use [default: stdin]') 1783 1784 # parser.add_option('-m','--message',dest='message',default=None, 1785 # help='Which message to write code for [default: all]') 1786 1787 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true', 1788 help='run the documentation tests') 1789 1790 parser.add_option('-p','--prefix',dest='prefix',default=False,action='store_true', 1791 help='put the field name in front of all function names.' 1792 +' Allows multiple messages in one file') 1793 1794 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 1795 help='run the tests run in verbose mode') 1796 1797 (options,args) = parser.parse_args() 1798 1799 success=True 1800 1801 if options.doctest: 1802 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 1803 argvOrig = sys.argv 1804 sys.argv= [sys.argv[0]] 1805 if options.verbose: sys.argv.append('-v') 1806 import doctest 1807 numfail,numtests=doctest.testmod() 1808 if numfail==0: print 'ok' 1809 else: 1810 print 'FAILED' 1811 success=False 1812 sys.argv = argvOrig # Restore the original args 1813 del argvOrig # hide from epydoc 1814 sys.exit() # FIX: Will this exit success? 1815 1816 #infile=sys.stdin 1817 #if options.xmlFileName: infile = file(options.xmlFileName,'r') 1818 1819 #outfile=sys.stdout 1820 #if options.outputFile: outfile = file(options.xmlDefinition,'w') 1821 1822 # FIX: down the road, it would be good to allow either filenames of std{in/out} 1823 1824 if None==options.xmlFileName: 1825 sys.exit('ERROR: must specify an xml definition file.') 1826 if None==options.outputFileName: 1827 sys.exit('ERROR: must specify an python file to write to.') 1828 generatePython(options.xmlFileName,options.outputFileName,prefixName=options.prefix) 1829 1830 print '\nrecommend running pychecker like this:' 1831 print ' pychecker -q',options.outputFileName 1832