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,verbose=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,verbose=verbose) 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 haveLocatableMessage(msgET):
207 '''Make sure this message has both long/x and lat/y fields. 208 @rtype: bool 209 ''' 210 #if getLongitudeFieldName(msgET) and getLatitudeFieldName(msgET): return True 211 212 if 0==len(msgET.xpath('field[contains(@name, "longitude")]')): return False 213 if 0==len(msgET.xpath('field[contains(@name, "latitude")]')): return False 214 return True
215
216 -def getLongitudeFieldName(msgET):
217 ''' 218 Dig up the first field name that include longitude and return it 219 @todo: might want to allow a search for a special tag to mark this 220 ''' 221 return msgET.xpath('field[contains(@name, "longitude")]')[0].attrib['name']
222
223 -def getLatitudeFieldName(msgET):
224 ''' 225 Dig up the first field name that include longitude and return it 226 @todo: might want to allow a search for a special tag to mark this 227 ''' 228 return msgET.xpath('field[contains(@name, "latitude")]')[0].attrib['name']
229
230 -def buildPrint(o,msgET, verbose=False, prefixName=False):
231 ''' 232 Write a simple in order print for the resulting dictionary. 233 234 @param o: open file where resulting code will be written 235 @param msgET: Element Tree starting at a message node 236 237 @todo: for lookuptable/entry values, make it also print the decoded value. 238 @todo: use a different name for message and field 239 ''' 240 assert(msgET.tag=='message') 241 name = msgET.attrib['name'] 242 msgname = name 243 244 print 'Generating print for',name # FIX: verbose? 245 # +1 for the ':' 246 maxFieldLen = 1 + getMaxFieldNameLen(msgET) 247 248 249 ############################## 250 # Html builder 251 ############################## 252 253 printHtmlName = 'printHtml' 254 if prefixName: printHtmlName = name+'printHtml' 255 256 #################### 257 ####### HTML format 258 #################### 259 #o.write('\telif \'html\'==format:\n') 260 o.write('\n') 261 o.write('def '+printHtmlName+'(params, out=sys.stdout):\n') 262 o.write('\t\tout.write("<h3>'+msgname+'<h3>\\n")\n') 263 o.write('\t\tout.write("<table border=\\"1\\">\\n")\n') 264 #o.write('\t\tout.write("<tr bgcolor=\\"#9acd32\\">\\n")\n') 265 o.write('\t\tout.write("<tr bgcolor=\\"orange\\">\\n")\n') 266 o.write('\t\tout.write("<th align=\\"left\\">Field Name</th>\\n")\n') 267 o.write('\t\tout.write("<th align=\\"left\\">Type</th>\\n")\n') 268 o.write('\t\tout.write("<th align=\\"left\\">Value</th>\\n")\n') 269 o.write('\t\tout.write("<th align=\\"left\\">Value in Lookup Table</th>\\n")\n') 270 o.write('\t\tout.write("<th align=\\"left\\">Units</th>\\n")\n') 271 272 273 for field in msgET.xpath('field'): 274 o.write('\t\tout.write("\\n")\n') 275 o.write('\t\tout.write("<tr>\\n")\n') 276 name = field.attrib['name'] 277 type = field.attrib['type'] 278 o.write('\t\tout.write("<td>'+name+'</td>\\n")\n') 279 o.write('\t\tout.write("<td>'+type+'</td>\\n")\n') 280 281 numbits = int(field.attrib['numberofbits']) 282 required = None; 283 if hasSubtag(field,'required'): 284 required = field.xpath('required')[0].text 285 unavailable=None; 286 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 287 arraylen=1 288 if 'arraylength' in field.attrib: arraylen=int(field.attrib['arraylength']) 289 290 if 1==arraylen or type=='aisstr6': 291 o.write('\t\tif \''+name+'\' in params:\n\t\t\tout.write("\t<td>"+str(params[\''+name+'\'])+"</td>\\n")\n') 292 if not hasSubtag(field,'lookuptable'): 293 # Just duplicate the value through 294 o.write('\t\t\tout.write("\t<td>"+str(params[\''+name+'\'])+"</td>\\n")\n') 295 else: 296 lutName = name+'DecodeLut' 297 if prefixName: lutName = msgname+name.capitalize()+'DecodeLut' 298 299 o.write('\t\t\tif str(params[\''+name+'\']) in '+lutName+':\n') 300 o.write('\t\t\t\tout.write("<td>"+'+lutName+'[str(params[\''+name+'\'])]+"</td>")\n') 301 o.write('\t\t\telse:\n') 302 o.write('\t\t\t\tout.write("<td><i>Missing LUT entry</i></td>")\n') 303 else: sys.exit ('FIX: handle arrays in the buildPrint func') 304 305 if hasSubtag(field,'units'): 306 o.write('\t\tout.write("<td>'+field.xpath('units')[0].text+'</td>\\n")\n') 307 308 o.write('\t\tout.write("</tr>\\n")\n') 309 310 o.write('\t\tout.write("</table>\\n")\n') 311 312 ############################## 313 ####### KML GoogleEarth 314 ############################## 315 316 printKmlName = 'printKml' 317 if prefixName: printKmlName = name+'printKml' 318 if not haveLocatableMessage(msgET): printKmlName=None 319 else: 320 o.write('\n') 321 o.write('def printKml(params, out=sys.stdout):\n') 322 o.write('\t\'\'\'KML (Keyhole Markup Language) for Google Earth, but without the header/footer\'\'\'\n') 323 o.write('\tout.write("\\\t<Placemark>\\n")\n') 324 o.write('\tout.write("\\t\t<name>"+str(params[\''+msgET.attrib['titlefield']+'\'])+"</name>\\n")\n') 325 o.write('\tout.write("\\t\\t<description>\\n")\n') 326 327 o.write('\timport StringIO\n') 328 o.write('\tbuf = StringIO.StringIO()\n') 329 o.write('\tprintHtml(params,buf)\n') 330 o.write('\timport cgi\n') 331 o.write('\tout.write(cgi.escape(buf.getvalue()))\n') 332 333 o.write('\tout.write("\\t\\t</description>\\n")\n') 334 o.write('\tout.write("\\t\\t<styleUrl>#m_ylw-pushpin_copy0</styleUrl>\\n")\n') 335 o.write('\tout.write("\\t\\t<Point>\\n")\n') 336 o.write('\tout.write("\\t\\t\\t<coordinates>")\n') 337 o.write('\tout.write(str(params[\''+getLongitudeFieldName(msgET)+'\']))\n') 338 o.write('\tout.write(\',\')\n') 339 o.write('\tout.write(str(params[\''+getLatitudeFieldName(msgET)+'\']))\n') 340 o.write('\tout.write(",0</coordinates>\\n")\n') 341 o.write('\tout.write("\\t\\t</Point>\\n")\n') 342 o.write('\tout.write("\\t</Placemark>\\n")\n') 343 o.write('\n') 344 345 346 ############################## 347 ##### Main print dispatch 348 ############################## 349 350 funcName = 'printFields' 351 if prefixName: funcName = name+'PrintFields' 352 353 o.write('def '+funcName+'(params, out=sys.stdout, format=\'std\'):\n') 354 355 ######################################## 356 # doc string 357 o.write("\t'''Print a "+name+" message to stdout.\n\n") 358 o.write('\tFields in params:\n') 359 for field in msgET.xpath('field'): 360 desc = field[0].text.replace('\n',' ') # get ride of new lines 361 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc) # give the description 362 if len(field.xpath("required")) == 1: 363 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")') 364 365 o.write('\n') 366 o.write('\t@param params: Dictionary of field names/values. \n') 367 o.write('\t@param out: File like object to write to\n') 368 369 o.write("\t@rtype: stdout\n") 370 o.write("\t@return: text to out\n") 371 o.write("\t'''\n\n") 372 373 ######################################## 374 # Actually build the code 375 376 o.write('\tif \'std\'==format:\n') 377 o.write('\t\tout.write("'+name+':\\n")\n') 378 379 if verbose: print 'number of fields = ', len(msgET.xpath('field')) 380 381 382 for field in msgET.xpath('field'): 383 name = field.attrib['name'] 384 type = field.attrib['type'] 385 numbits = int(field.attrib['numberofbits']) 386 required = None; 387 if hasSubtag(field,'required'): 388 required = field.xpath('required')[0].text 389 #print 'required set for',name,'to',required 390 unavailable=None; 391 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 392 arraylen=1 393 if 'arraylength' in field.attrib: 394 arraylen=int(field.attrib['arraylength']) 395 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 396 else: 397 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')' 398 399 if 1==arraylen or type=='aisstr6': 400 o.write('\t\tif \''+name+'\' in params: out.write("\t'+padStrRight(name+':',maxFieldLen)+' "+str(params[\''+name+'\'])+"\\n")\n') 401 # FIX: elif aisstr6 strip @@@ and then print 402 else: 403 sys.exit ('FIX: handle arrays in the buildPrint func') 404 405 #################### 406 ####### Other dispatchers 407 #################### 408 o.write('\telif \'html\'==format:\n') 409 #o.write('\t\tout.write("'+printHtmlName+'(params,out)\\n")\n') 410 o.write('\t\t'+printHtmlName+'(params,out)\n') 411 412 413 if haveLocatableMessage(msgET): 414 # Has a lon/lat pair somewhere in there. 415 o.write('\telif \'kml\'==format:\n') 416 o.write('\t\t'+printKmlName+'(params,out)\n') 417 418 o.write('\telif \'kml-full\'==format:\n') 419 420 o.write('\t\tout.write(\"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>\\n")\n') 421 o.write('\t\tout.write("<kml xmlns=\\"http://earth.google.com/kml/2.1\\">\\n")\n') 422 o.write('\t\tout.write("<Document>\\n")\n') 423 o.write('\t\tout.write("\t<name>Position</name>\\n")\n') 424 425 o.write('\t\t'+printKmlName+'(params,out)\n') 426 427 o.write('\t\tout.write("</Document>\\n")\n') 428 o.write('\t\tout.write("</kml>\\n")\n') 429 430 431 432 #################### 433 ####### Make safety check 434 #################### 435 o.write('\telse: \n') 436 o.write('\t\tprint "ERROR: unknown format:",format\n') 437 o.write('\t\tassert False\n') 438 439 o.write('\n\treturn # Nothing to return\n\n')
440 441 ###################################################################### 442 # Build Lookup tables for enumerated int or uint fields 443 ###################################################################### 444 445
446 -def buildLUT(o,msgET, verbose=False, prefixName=False):
447 ''' 448 Write lookup tables for enumerated types (uint or int, maybe bool too). 449 450 @todo: FIX: what to do about multiple entries with the same text? Need to ban that kind of thing 451 @todo: Make doc strings for each LUT. 452 453 @param o: open file where resulting code will be written 454 @param msgET: Element Tree starting at a message node 455 ''' 456 assert(msgET.tag=='message') 457 msgname = msgET.attrib['name'] 458 459 print 'Generating lookup tables for',msgname # FIX: verbose? 460 461 for field in msgET.xpath('field'): 462 name = field.attrib['name'] 463 if not hasSubtag(field,'lookuptable'): continue 464 lut = field.xpath('lookuptable')[0] 465 466 lutName = name 467 if prefixName: lutName = msgname+name.capitalize() 468 469 o.write(lutName+'EncodeLut = {\n') 470 for entry in lut.xpath('entry'): 471 o.write('\t\''+entry.text+'\':\''+entry.attrib['key']+'\',\n') 472 o.write('\t} #'+lutName+'EncodeLut\n') 473 o.write('\n') 474 475 # FIX: make doc string for LUT here 476 477 o.write(lutName+'DecodeLut = {\n') 478 for entry in lut.xpath('entry'): 479 o.write('\t\''+entry.attrib['key']+'\':\''+entry.text+'\',\n') 480 o.write('\t} # '+lutName+'EncodeLut\n') 481 o.write('\n')
482 483 # FIX: make doc string for LUT here 484 485 486 487 488 ###################################################################### 489 # ENCODERS 490 ######################################################################
491 -def encodeBool(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
492 ''' 493 Build the encoder for boolean variables 494 @type o: file like obj 495 @param o: where write the code 496 @type name: str 497 @param name: field name 498 @type type: str 499 @param type: bool, etc. 500 @type numbits: int = 1 501 @param numbits: How many bits per unit datum (must be 1 for bools) 502 @type required: bool or None 503 @param required: If not None, then the value must be set to this. 504 @type arraylen: int >= 1 505 @param arraylen: many bools will there be? FIX: handle variable 506 @type unavailable: bool or None 507 @param unavailable: the default value to use if none given (if not None) 508 @return: None 509 ''' 510 511 if verbose: print 'bool encode',name,': unvail=',unavailable 512 513 assert type.lower()=='bool' 514 assert numbits==1 515 if arraylen != 1: assert False # FIX... handle arrays 516 if verbose: o.write('\t### FIELD: '+name+' (type=bool)\n') 517 if None != required: 518 assert type(required)==bool 519 if required: o.write('\t\tbvList.append(TrueBV)\n') 520 else: o.write('\t\tbvList.append(FalseBV)\n') 521 if verbose: o.write('\n') 522 return 523 524 if None==unavailable: 525 o.write('\tif params["'+name+'"]: bvList.append(TrueBV)\n') 526 o.write('\telse: bvList.append(FalseBV)\n') 527 else: # Have a default value that can be filled in 528 assert type(unavailable)==bool 529 o.write("\tif '"+name+"' in params:\n") 530 o.write('\t\tif params["'+name+'"]: bvList.append(TrueBV)\n') 531 o.write('\t\telse: bvList.append(FalseBV)\n') 532 o.write('\telse:\n') 533 if unavailable: o.write('\t\tbvList.append(TrueBV)\n') 534 else: o.write('\t\tbvList.append(FalseBV)\n') 535 if verbose: o.write('\n')
536
537 -def encodeUInt(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
538 ''' 539 Build the encoder for unsigned integer variables 540 541 @type o: file like obj 542 @param o: where write the code 543 @type name: str 544 @param name: field name 545 @type type: str 546 @param type: uint, bool, etc. 547 @type numbits: int >= 1 548 @param numbits: How many bits per unit datum (must be 1..32) 549 @type required: bool or None 550 @param required: If not None, then the value must be set to this. 551 @type arraylen: int >= 1 552 @param arraylen: many unsigned ints will there be? FIX: handle variable 553 @type unavailable: bool or None 554 @param unavailable: the default value to use if none given (if not None) 555 @return: None 556 ''' 557 if verbose: print ' encodeUInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 558 559 assert type=='uint' 560 assert numbits>=1 and numbits<=32 561 if arraylen != 1: assert False # FIX... handle arrays 562 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 563 564 if None != required: 565 if verbose: print ' required:',required 566 required=int(required) 567 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(required)+'),'+str(numbits)+'))\n') 568 if verbose: o.write('\n') 569 return 570 571 if None==unavailable: 572 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal=params[\''+name+'\']),'+str(numbits)+'))\n') 573 else: # Have a default value that can be filled in 574 #assert type(unavailable)== 575 int(unavailable) # Make sure unavailable is a number object 576 o.write("\tif '"+name+"' in params:\n") 577 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=params[\''+name+'\']'+'),'+str(numbits)+'))\n') 578 o.write('\telse:\n') 579 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(unavailable)+'),'+str(numbits)+'))\n') 580 581 if verbose: o.write('\n')
582 583
584 -def encodeFloat(o,name,fieldType,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
585 ''' 586 Build the encoder for IEEE float variables 587 588 @type o: file like obj 589 @param o: where write the code 590 @type name: str 591 @param name: field name 592 @type fieldType: str 593 @param fieldType: uint, bool, etc. 594 @type numbits: int >= 1 595 @param numbits: How many bits per unit datum (must be 1..32) 596 @type required: bool or None 597 @param required: If not None, then the value must be set to this. 598 @type arraylen: int >= 1 599 @param arraylen: many unsigned ints will there be? FIX: handle variable 600 @type unavailable: bool or None 601 @param unavailable: the default value to use if none given (if not None) 602 @return: None 603 ''' 604 if verbose: print ' encodeUInt:',name,fieldType,numbits,'Req:',required,'alen:',arraylen,unavailable 605 606 assert numbits==32 # Force by the IEEE spec 607 if arraylen != 1: assert False # FIX... handle arrays 608 if verbose: o.write('\t### FIELD: '+name+' (type='+fieldType+')\n') 609 610 if None != required: 611 if verbose: print ' required:',required 612 required=int(required) 613 o.write('\tbvList.append(binary.float2bitvec('+str(required)+'))\n') 614 if verbose: o.write('\n') 615 return 616 617 if None==unavailable: 618 o.write('\tbvList.append(binary.float2bitvec(params[\''+name+'\']))\n') 619 else: # Have a default value that can be filled in 620 int(unavailable) # Make sure unavailable is a number object 621 o.write("\tif '"+name+"' in params:\n") 622 o.write('\t\tbvList.append(binary.float2bitvec(params[\''+name+'\']'+'))\n') 623 o.write('\telse:\n') 624 o.write('\t\tbvList.append(binary.float2bitvec('+str(unavailable)+'))\n') 625 626 if verbose: o.write('\n')
627 628
629 -def encodeAisstr6(o,name,fieldType,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
630 ''' 631 Build the encoder for aisstr6 variables. Generally are arrays. 632 @bug: do we need to optionally check for a valid string? 633 634 @type o: file like obj 635 @param o: where write the code 636 @type name: str 637 @param name: field name 638 @type fieldType: str 639 @param fieldType: uint, bool, etc. 640 @type numbits: int >= 1 641 @param numbits: How many bits per unit datum (must be 1..32) 642 @type required: bool or None 643 @param required: If not None, then the value must be set to this. 644 @type arraylen: int >= 1 645 @param arraylen: many unsigned ints will there be? FIX: handle variable 646 @type unavailable: bool or None 647 @param unavailable: the default value to use if none given (if not None) 648 @return: None 649 ''' 650 if verbose: print ' encodeUInt:',name,fieldType,numbits,'Req:',required,'alen:',arraylen,unavailable 651 totLen = str(numbits*arraylen) 652 assert numbits==6 # Each character must be 6 bits 653 if verbose: o.write('\t### FIELD: '+name+' (type='+fieldType+')\n') 654 655 if None != required: 656 if verbose: print ' required:',required 657 required=int(required) 658 o.write('\tbvList.append(aisstring.encode(\''+str(required)+'\','+totLen+'))\n') 659 if verbose: o.write('\n') 660 return 661 662 if None==unavailable: 663 o.write('\tbvList.append(aisstring.encode(params[\''+name+'\'],'+totLen+'))\n') 664 else: # Have a default value that can be filled in 665 o.write("\tif '"+name+"' in params:\n") 666 o.write('\t\tbvList.append(aisstring.encode(params[\''+name+'\'],'+totLen+'))\n') 667 o.write('\telse:\n') 668 o.write('\t\tbvList.append(aisstring.encode(\''+str(unavailable)+'\','+totLen+'))\n') 669 670 if verbose: o.write('\n')
671 672
673 -def encodeInt(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
674 ''' 675 Build the encoder for signed integer variables 676 677 @type o: file like obj 678 @param o: where write the code 679 @type name: str 680 @param name: field name 681 @type type: str 682 @param type: uint, bool, etc. 683 @type numbits: int >= 1 684 @param numbits: How many bits per unit datum (must be 1..32) 685 @type required: bool or None 686 @param required: If not None, then the value must be set to this. 687 @type arraylen: int >= 1 688 @param arraylen: many signed ints will there be? FIX: handle variable 689 @type unavailable: number or None 690 @param unavailable: the default value to use if none given (if not None) 691 @return: None 692 ''' 693 if verbose: print ' encodeInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 694 695 assert numbits>=1 and numbits<=32 696 if arraylen != 1: assert False # FIX... handle arrays 697 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 698 699 if None != required: 700 if verbose: print ' required:',required 701 required=int(required) 702 o.write('\tbvList.append(binary.bvFromSignedInt('+str(required)+','+str(numbits)+'))\n') 703 if verbose: o.write('\n') 704 return 705 706 707 if None==unavailable: 708 o.write('\tbvList.append(binary.bvFromSignedInt(params[\''+name+'\'],'+str(numbits)+'))\n') 709 else: # Have a default value that can be filled in 710 #assert type(unavailable)== 711 int(unavailable) # Make sure unavailable is a number object 712 o.write("\tif '"+name+"' in params:\n") 713 o.write('\t\tbvList.append(binary.bvFromSignedInt(params[\''+name+'\']'+','+str(numbits)+'))\n') 714 o.write('\telse:\n') 715 o.write('\t\tbvList.append(binary.bvFromSignedInt('+str(unavailable)+','+str(numbits)+'))\n') 716 717 if verbose: o.write('\n')
718 719 720 721 # FIX: Ummm... why am I passing the type? I guess it makes the one print statement easier
722 -def encodeDecimal(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
723 ''' 724 Build the encoder for signed decimal variables 725 726 @type o: file like obj 727 @param o: where write the code 728 @type name: str 729 @param name: field name 730 @type type: str 731 @param type: decimal 732 @type numbits: int >= 1 733 @param numbits: How many bits per unit datum (must be 1..32) 734 @type required: bool or None 735 @param required: If not None, then the value must be set to this. 736 @type arraylen: int >= 1 737 @param arraylen: many decimals will there be? FIX: handle variable 738 @type unavailable: Decimal or None 739 @param unavailable: the default value to use if none given (if not None) 740 @return: None 741 ''' 742 if verbose: print ' encodeDecimal:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 743 744 assert numbits>=1 and numbits<=32 745 if arraylen != 1: assert False # FIX... handle arrays 746 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 747 748 # FIX: optimize to not emit the scaling when it is not needed... or tell the user to switch to an int! 749 if None == scale: 750 print 'WARNING: if you are not scaling, then you probably want to use an int instead!' 751 print 'Beware canadians bearing travel videos' 752 scale='1' 753 754 if None != required: 755 if verbose: print ' required:',required 756 required=int(required) 757 o.write('\tbvList.append(binary.bvFromSignedInt('+str(int(Decimal(required)*Decimal(scale)))+','+str(numbits)+'))\n') 758 if verbose: o.write('\n') 759 return 760 761 # FIX: can I get rid of the Decimal around params? 762 if None==unavailable: 763 o.write('\tbvList.append(binary.bvFromSignedInt(int(Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')),'+str(numbits)+'))\n') 764 else: # Have a default value that can be filled in 765 o.write("\tif '"+name+"' in params:\n") 766 o.write('\t\tbvList.append(binary.bvFromSignedInt(int(Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')),'+str(numbits)+'))\n') 767 o.write('\telse:\n') 768 o.write('\t\tbvList.append(binary.bvFromSignedInt('+str(int(Decimal(unavailable)*Decimal(scale)))+','+str(numbits)+'))\n') 769 770 if verbose: o.write('\n')
771 772 773
774 -def encodeUDecimal(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
775 ''' 776 Build the encoder for signed decimal variables 777 778 @type o: file like obj 779 @param o: where write the code 780 @type name: str 781 @param name: field name 782 @type type: str 783 @param type: decimal 784 @type numbits: int >= 1 785 @param numbits: How many bits per unit datum (must be 1..32) 786 @type required: bool or None 787 @param required: If not None, then the value must be set to this. 788 @type arraylen: int >= 1 789 @param arraylen: many decimals will there be? FIX: handle variable 790 @type unavailable: Decimal or None 791 @param unavailable: the default value to use if none given (if not None) 792 @return: None 793 ''' 794 if verbose: print ' encodeDecimal:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 795 assert type=='udecimal' 796 assert numbits>=1 and numbits<=32 797 if arraylen != 1: assert False # FIX... handle arrays 798 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 799 800 # FIX: optimize to not emit the scaling when it is not needed... or tell the user to switch to an int! 801 if None == scale: 802 print 'WARNING: if you are not scaling, then you probably want to use an int instead!' 803 print 'Beware canadians bearing travel videos' 804 scale='1' 805 806 if None != required: 807 if verbose: print ' required:',required 808 required=int(required) 809 assert(0<=required) 810 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(int(Decimal(required)*Decimal(scale)))+'),'+str(numbits)+'))\n') 811 if verbose: o.write('\n') 812 return 813 814 # FIX: can I get rid of the Decimal around params? 815 if None==unavailable: 816 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')))),'+str(numbits)+'))\n') 817 else: # Have a default value that can be filled in 818 o.write("\tif '"+name+"' in params:\n") 819 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')))),'+str(numbits)+'))\n') 820 o.write('\telse:\n') 821 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int('+str(int(Decimal(unavailable)*Decimal(scale)))+')),'+str(numbits)+'))\n') 822 823 if verbose: o.write('\n')
824 825 826
827 -def encodeBinary(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
828 ''' 829 Build the encoder for binary variables. This is just a pass through. 830 This is used for the ais binary message wrapper (e.g. msg 8). Do 831 not use it within binary messages. 832 833 @type o: file like obj 834 @param o: where write the code 835 @type name: str 836 @param name: field name 837 @type type: str 838 @param type: binary 839 @type numbits: int >= 1 840 @param numbits: How many bits per unit datum (must be 1..1024 or so) 841 @type required: bool or None 842 @param required: If not None, then the value must be set to this. 843 @type arraylen: int >= 1 844 @param arraylen: many decimals will there be? FIX: handle variable 845 @type unavailable: Decimal or None 846 @param unavailable: the default value to use if none given (if not None) 847 @return: None 848 ''' 849 if verbose: print ' encode'+name+':',type,numbits,'Req:',required,'alen:',arraylen,unavailable 850 assert type=='binary' 851 assert (numbits>=1 and numbits<=1024) or numbits==-1 852 assert (None == required) # don't allow this 853 assert (None == unavailable) # don't allow this 854 855 if arraylen != 1: assert False # Do not handle arrays. Arrays of bits is just not necessary. 856 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 857 858 # FIX: can I get rid of the Decimal around params? 859 o.write('\tbvList.append(params[\''+name+'\'])\n') # Just pass it through 860 861 if verbose: o.write('\n')
862 863 864 ###################################################################### 865 # DECODERS 866 ###################################################################### 867
868 -def decodeBool(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 869 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
870 ''' 871 Build the decoder for boolean variables 872 873 @type o: file like obj 874 @param o: where write the code 875 @type name: str 876 @param name: field name 877 @type type: str 878 @param type: uint, bool, etc. 879 @type startindex: int 880 @param startindex: bit that begins the bool(s) 881 @type numbits: int = 1 882 @param numbits: How many bits per unit datum (must be 1 for bools) 883 @type required: bool or None 884 @param required: If not None, then the value must be set to this. 885 @type arraylen: int >= 1 886 @param arraylen: many bools will there be? FIX: handle variable 887 @type unavailable: bool or None 888 @param unavailable: the default value to use if none given (if not None) 889 @type bv: str 890 @param bv: BitVector containing the incoming data 891 @type dataDict: str 892 @param dataDict: dictionary in which to place the results 893 @type decodeOnly: bool 894 @param decodeOnly: Set to true to only get the code for decoding 895 @rtype: int 896 @return: index one past the end of where this read 897 ''' 898 assert(type=='bool') 899 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 900 #int(startindex); int(numbits) # Make sure it is a number 901 assert numbits==1 902 assert arraylen == 1 # FIX... handle arrays 903 904 if None != required: 905 assert type(required)==bool 906 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 907 if required: o.write('True\n') 908 else: o.write('False\n') 909 if not decodeOnly: o.write('\n') 910 return int(startindex)+int(numbits) 911 912 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 913 o.write('bool(int('+bv+'['+str(startindex)+':'+str(startindex+int(numbits)*int(arraylen))+']))') 914 if not decodeOnly: o.write('\n') 915 916 return int(startindex)+int(numbits)
917 918
919 -def decodeUInt(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 920 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
921 ''' 922 Build the decoder for unsigned integer variables 923 924 @type o: file like obj 925 @param o: where write the code 926 @type name: str 927 @param name: field name 928 @type type: str 929 @param type: uint, etc. 930 @type startindex: int 931 @param startindex: bit that begins the uint(s) 932 @type numbits: int >= 1 933 @param numbits: How many bits per unit datum 934 @type required: int or None 935 @param required: If not None, then the value must be set to this. 936 @type arraylen: int >= 1 937 @param arraylen: many ints will there be? FIX: handle variable 938 @type unavailable: int or None 939 @param unavailable: the default value to use if none given (if not None) 940 @type bv: str 941 @param bv: BitVector containing the incoming data 942 @type dataDict: str 943 @param dataDict: dictionary in which to place the results 944 @type decodeOnly: bool 945 @param decodeOnly: Set to true to only get the code for decoding 946 @rtype: int 947 @return: index one past the end of where this read 948 ''' 949 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 950 if None==arraylen: arraylen=1 951 assert arraylen == 1 # FIX... handle arrays 952 assert numbits>=1 953 if not decodeOnly: verbose=False 954 955 if None != required: 956 int(required) # Make sure required is a number 957 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 958 o.write(str(required)) 959 if not decodeOnly: o.write('\n') 960 return startindex+numbits 961 962 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 963 o.write('int('+bv+'['+str(startindex)+':'+str(startindex+int(numbits)*int(arraylen))+'])') 964 if not decodeOnly: o.write('\n') 965 if verbose: o.write('\n') 966 967 return startindex+numbits
968 969
970 -def decodeInt(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 971 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
972 ''' 973 Build the decoder for unsigned integer variables 974 975 @type o: file like obj 976 @param o: where write the code 977 @type name: str 978 @param name: field name 979 @type type: str 980 @param type: int 981 @type startindex: int 982 @param startindex: bit that begins the int(s) 983 @type numbits: int >= 1 984 @param numbits: How many bits per unit datum 985 @type required: int or None 986 @param required: If not None, then the value must be set to this. 987 @type arraylen: int >= 1 988 @param arraylen: many ints will there be? FIX: handle variable 989 @type unavailable: int or None 990 @param unavailable: the default value to use if none given (if not None) 991 @type bv: str 992 @param bv: BitVector containing the incoming data 993 @type dataDict: str 994 @param dataDict: dictionary in which to place the results 995 @type decodeOnly: bool 996 @param decodeOnly: Set to true to only get the code for decoding 997 @rtype: int 998 @return: index one past the end of where this read 999 ''' 1000 assert type=='int' 1001 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1002 if None==arraylen: arraylen=1 1003 end = startindex+int(numbits)*int(arraylen) 1004 assert arraylen == 1 # FIX... handle arrays 1005 assert numbits>=1 1006 1007 if None != required: 1008 int(required) # Make sure required is a number 1009 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1010 o.write(str(required)) 1011 if not decodeOnly: o.write('\n') 1012 return end 1013 1014 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1015 o.write('binary.signedIntFromBV('+bv+'['+str(startindex)+':'+str(end)+'])') 1016 if not decodeOnly: o.write('\n') 1017 if verbose: o.write('\n') 1018 1019 return end
1020
1021 -def decodeFloat(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1022 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
1023 ''' 1024 Build the decoder for IEEE float variables 1025 1026 @type o: file like obj 1027 @param o: where write the code 1028 @type name: str 1029 @param name: field name 1030 @type type: str 1031 @param type: int 1032 @type startindex: int 1033 @param startindex: bit that begins the int(s) 1034 @type numbits: int >= 1 1035 @param numbits: How many bits per unit datum 1036 @type required: float or None 1037 @param required: If not None, then the value must be set to this. 1038 @type arraylen: int >= 1 1039 @param arraylen: many ints will there be? FIX: handle variable 1040 @type unavailable: float or None 1041 @param unavailable: the default value to use if none given (if not None) 1042 @type bv: str 1043 @param bv: BitVector containing the incoming data 1044 @type dataDict: str 1045 @param dataDict: dictionary in which to place the results 1046 @type decodeOnly: bool 1047 @param decodeOnly: Set to true to only get the code for decoding 1048 @rtype: int 1049 @return: index one past the end of where this read 1050 ''' 1051 assert type=='float' 1052 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1053 if None==arraylen: arraylen=1 1054 end = startindex+int(numbits)*int(arraylen) 1055 assert arraylen == 1 # FIX... handle arrays 1056 assert numbits>=1 1057 1058 if None != required: 1059 float(required) # Make sure required is a number 1060 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1061 o.write(str(required)) 1062 if not decodeOnly: o.write('\n') 1063 if verbose: o.write('\n') 1064 return end 1065 1066 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1067 o.write('binary.bitvec2float('+bv+'['+str(startindex)+':'+str(end)+'])') 1068 if not decodeOnly: o.write('\n') 1069 1070 return end
1071 1072
1073 -def decodeAisstr6(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1074 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
1075 ''' 1076 Build the decoder for aisstr6 variables. Generally arrays. 1077 @bug: FIX: validate strings?? 1078 @type o: file like obj 1079 @param o: where write the code 1080 @type name: str 1081 @param name: field name 1082 @type type: str 1083 @param type: 'aisstr6' 1084 @type startindex: int 1085 @param startindex: bit that begins the int(s) 1086 @type numbits: int >= 1 1087 @param numbits: How many bits per unit datum 1088 @type required: restricted str or None 1089 @param required: If not None, then the value must be set to this. 1090 @type arraylen: int >= 1 1091 @param arraylen: many ints will there be? FIX: handle variable 1092 @type unavailable: restricted str or None 1093 @param unavailable: the default value to use if none given (if not None) 1094 @type bv: str 1095 @param bv: BitVector containing the incoming data 1096 @type dataDict: str 1097 @param dataDict: dictionary in which to place the results 1098 @type decodeOnly: bool 1099 @param decodeOnly: Set to true to only get the code for decoding 1100 @rtype: int 1101 @return: index one past the end of where this read 1102 ''' 1103 assert type=='aisstr6' 1104 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1105 if None==arraylen: arraylen=1 1106 end = startindex+int(numbits)*int(arraylen) 1107 assert arraylen >= 1 # FIX... handle arrays 1108 assert numbits>=1 1109 1110 if None != required: 1111 float(required) # Make sure required is a number 1112 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1113 o.write(required) 1114 if not decodeOnly: o.write('\n') 1115 return end 1116 1117 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1118 o.write('aisstring.decode('+bv+'['+str(startindex)+':'+str(end)+'])') 1119 if not decodeOnly: o.write('\n') 1120 1121 return end
1122 1123
1124 -def decodeDecimal(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 signed 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: 'decimal' 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 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=='decimal' 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 <= 32 1160 1161 if None == scale: scale='1' # Warning about this was in the encode section 1162 1163 if None != required: 1164 Decimal(required) # Make sure required is a number 1165 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1166 o.write(str(Decimal(required))+'/Decimal(\''+scale+'\')') 1167 if not decodeOnly: o.write('\n') 1168 return end 1169 1170 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1171 o.write('Decimal(binary.signedIntFromBV('+bv+'['+str(startindex)+':'+str(end)+']))/Decimal(\''+scale+'\')') 1172 if not decodeOnly: o.write('\n') 1173 1174 return end
1175 1176 1177 1178
1179 -def decodeUDecimal(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1180 bv='bv',dataDict='r',verbose=False,scale=None, decodeOnly=False):
1181 ''' 1182 Build the decoder for unsigned decimal variables 1183 1184 @type o: file like obj 1185 @param o: where write the code 1186 @type name: str 1187 @param name: field name 1188 @type type: str 1189 @param type: 'udecimal' 1190 @type startindex: int 1191 @param startindex: bit that begins the int(s) 1192 @type numbits: int >= 1 1193 @param numbits: How many bits per unit datum 1194 @type required: Decimal or None 1195 @param required: If not None, then the value must be set to this. 1196 @type arraylen: int >= 1 1197 @param arraylen: many ints will there be? FIX: handle variable 1198 @type unavailable: Decimal or None 1199 @param unavailable: the default value to use if none given (if not None) 1200 @type bv: str 1201 @param bv: BitVector containing the incoming data 1202 @type dataDict: str 1203 @param dataDict: dictionary in which to place the results 1204 @type decodeOnly: bool 1205 @param decodeOnly: Set to true to only get the code for decoding 1206 @rtype: int 1207 @return: index one past the end of where this read 1208 ''' 1209 assert type=='udecimal' 1210 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1211 if None==arraylen: arraylen=1 1212 end = startindex+int(numbits)*int(arraylen) 1213 assert arraylen == 1 # FIX... handle arrays 1214 assert numbits>=1 and numbits <= 32 1215 1216 if None == scale: scale='1' # Warning about this was in the encode section 1217 1218 if None != required: 1219 assert (Decimal(required)>=0.) # Make sure required is a number and not negative 1220 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1221 o.write(str(Decimal(required))+'/Decimal(\''+scale+'\')') 1222 if not decodeOnly: o.write('\n') 1223 return end 1224 1225 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1226 o.write('Decimal(int('+bv+'['+str(startindex)+':'+str(end)+']))/Decimal(\''+scale+'\')') 1227 if not decodeOnly: o.write('\n') 1228 1229 return end
1230 1231
1232 -def decodeBinary(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1233 bv='bv',dataDict='r',verbose=False,scale=None, decodeOnly=False):
1234 ''' 1235 Build the decoder for unsigned decimal variables 1236 1237 @type o: file like obj 1238 @param o: where write the code 1239 @type name: str 1240 @param name: field name 1241 @type type: str 1242 @param type: 'udecimal' 1243 @type startindex: int 1244 @param startindex: bit that begins the int(s) 1245 @type numbits: int >= 1 1246 @param numbits: How many bits per unit datum. If -1, then read to the end of the message 1247 @type required: Decimal or None 1248 @param required: If not None, then the value must be set to this. 1249 @type arraylen: int >= 1 1250 @param arraylen: many ints will there be? FIX: handle variable 1251 @type unavailable: Decimal or None 1252 @param unavailable: the default value to use if none given (if not None) 1253 @type bv: str 1254 @param bv: BitVector containing the incoming data 1255 @type dataDict: str 1256 @param dataDict: dictionary in which to place the results 1257 @type decodeOnly: bool 1258 @param decodeOnly: Set to true to only get the code for decoding 1259 @rtype: int 1260 @return: index one past the end of where this read 1261 ''' 1262 assert type=='binary' 1263 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1264 if None==arraylen: arraylen=1 1265 end = startindex+int(numbits)*int(arraylen) 1266 assert arraylen == 1 # FIX... handle arrays 1267 assert (numbits>=1 and numbits <= 1024) or -1==numbits # What is good max? 1268 # FIX: assert not required and not an array an not unavailable 1269 1270 1271 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1272 o.write(bv+'['+str(startindex)+':') 1273 if int(numbits) != -1: o.write(str(end)) # -1 means go to the end of the message 1274 o.write(']') 1275 if not decodeOnly: o.write('\n') 1276 1277 return end
1278 1279 1280 1281 ###################################################################### 1282 # THE REST 1283 ###################################################################### 1284 1285 1286
1287 -def buildTestParamFunc(o,msgET, verbose=False, prefixName=False):
1288 '''Scrape the testvalues to make a basic param 1289 1290 @bug: FIX: make this create a dictionary that sits in the overall namespace and spit out deep copies? 1291 ''' 1292 name = msgET.attrib['name'] 1293 1294 if prefixName: o.write('def '+name+'TestParams():\n') 1295 else: o.write('def testParams():\n') 1296 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") 1297 o.write('\tparams = {}\n') 1298 for field in msgET.xpath('field'): 1299 name = field.attrib['name'] 1300 type = field.attrib['type'] 1301 if verbose: print 'buildTestParamFunc ...',name,type 1302 val = None 1303 if hasSubtag(field,'testvalue') and hasSubtag(field,'required'): 1304 print 'ERROR: can not have both test value and required tags in the same field' 1305 assert(False) 1306 if hasSubtag(field,'testvalue'): 1307 val = field.xpath('testvalue')[0].text 1308 else: 1309 if not hasSubtag(field,'required'): 1310 sys.exit("ERROR: missing required or testvalue for field: "+name) 1311 val = field.xpath('required')[0].text 1312 if verbose: print 'buildTestParamFunc for field '+name+' ...',type,val 1313 o.write('\tparams[\''+name+'\'] = ') 1314 if type=='bool': 1315 if val=='1' or val.lower=='true': val = 'True' 1316 else: val = 'False' 1317 o.write(val) 1318 elif type in ('uint','int','float'): 1319 o.write(val) 1320 elif type in ('decimal','udecimal'): 1321 o.write('Decimal(\''+val+'\')') 1322 1323 elif type in ('aisstr6'): 1324 o.write('\''+val+'\'') 1325 elif type in ('binary'): 1326 o.write('BitVector(bitstring=\''+val+'\')') 1327 else: 1328 print 'ERROR: type not handled ...',type,' (found in the ',name,' field). Time to buy more coffee' 1329 suggestType(name,type) 1330 assert(False) 1331 1332 o.write('\n') 1333 1334 1335 o.write('\n\treturn params\n\n')
1336
1337 -def buildUnitTest(o,msgET, verbose=False, prefixName=False):
1338 ''' 1339 Write the unittests for a message 1340 1341 @param o: open file where resulting code will be written 1342 @param msgET: Element Tree starting at a message node 1343 ''' 1344 assert(msgET.tag=='message') 1345 name = msgET.attrib['name'] 1346 1347 buildTestParamFunc(o,msgET, prefixName=prefixName) 1348 1349 o.write('class Test'+name+'(unittest.TestCase):\n') 1350 o.write("\t'''Use testvalue tag text from each type to build test case the "+name+" message'''\n") 1351 o.write('\tdef testEncodeDecode(self):\n\n') 1352 if prefixName: 1353 o.write('\t\tparams = '+name+'TestParams()\n') 1354 o.write('\t\tbits = '+name+'Encode(params)\n') 1355 o.write('\t\tr = '+name+'Decode(bits)\n\n') 1356 else: 1357 o.write('\t\tparams = testParams()\n') 1358 o.write('\t\tbits = encode(params)\n') 1359 o.write('\t\tr = decode(bits)\n\n') 1360 1361 o.write('\t\t# Check that each parameter came through ok.\n') 1362 for field in msgET.xpath('field'): 1363 name = field.attrib['name'] 1364 type = field.attrib['type'] 1365 if type in ('bool','uint','int','aisstr6','binary'): 1366 o.write('\t\tself.failUnlessEqual(r[\''+name+'\'],params[\''+name+'\'])\n') 1367 else: 1368 # float, decimal, udecimal 1369 # FIX: look up the decimal places if decimal 1370 places = '3' 1371 if hasSubtag(field,'decimalplaces'): places = field.xpath('decimalplaces')[0].text 1372 o.write('\t\tself.failUnlessAlmostEqual(r[\''+name+'\'],params[\''+name+'\'],'+places+')\n')
1373 1374 1375
1376 -def buildEncode(o,msgET, verbose=False, prefixName=False):
1377 ''' 1378 Write the encoder/decoder for a message 1379 1380 http://jaynes.colorado.edu/PythonIdioms.html 1381 1382 @param o: open file where resulting code will be written 1383 @param msgET: Element Tree starting at a message node 1384 ''' 1385 assert(msgET.tag=='message') 1386 name = msgET.attrib['name'] 1387 1388 print 'Generating encoder for',name # FIX: verbose? 1389 funcName = 'encode' 1390 if prefixName: funcName = name+'Encode' 1391 o.write('def '+funcName+'(params, validate=False):\n') 1392 1393 ######################################## 1394 # doc string 1395 o.write("\t'''Create a "+name+" binary message payload to pack into an AIS Msg "+name+".\n\n") 1396 o.write('\tFields in params:\n') 1397 for field in msgET.xpath('field'): 1398 if verbose: print field.tag,field.attrib['name'] 1399 desc = field[0].text.replace('\n',' ') # get ride of new lines 1400 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc) # give the description 1401 if len(field.xpath("required")) == 1: 1402 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")') 1403 1404 o.write('\n') 1405 o.write('\t@param params: Dictionary of field names/values. Throws a ValueError exception if required is missing\n') 1406 o.write('\t@param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.\n') 1407 1408 o.write("\t@rtype: BitVector\n") 1409 o.write("\t@return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8\n") 1410 o.write("\t@note: The returned bits may not be 6 bit aligned. It is up to you to pad out the bits.\n") 1411 o.write("\t'''\n\n") 1412 1413 ######################################## 1414 # Actually build the code 1415 1416 o.write('\tbvList = []\n') 1417 1418 if verbose: print 'number of fields = ', len(msgET.xpath('field')) 1419 1420 dynamicArrays = False # Set to true when positioning must be calculated 1421 1422 for field in msgET.xpath('field'): 1423 name = field.attrib['name'] 1424 type = field.attrib['type'] 1425 numbits = int(field.attrib['numberofbits']) 1426 required = None; 1427 if hasSubtag(field,'required'): 1428 required = field.xpath('required')[0].text 1429 #print 'required set for',name,'to',required 1430 unavailable=None; 1431 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 1432 arraylen=1 1433 if 'arraylength' in field.attrib: 1434 arraylen=int(field.attrib['arraylength']) 1435 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 1436 else: 1437 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')' 1438 1439 if type=='bool' : encodeBool (o,name,type,numbits,required,arraylen,unavailable) 1440 elif type=='uint' : encodeUInt (o,name,type,numbits,required,arraylen,unavailable) 1441 elif type=='int' : encodeInt (o,name,type,numbits,required,arraylen,unavailable) 1442 elif type=='float' : encodeFloat (o,name,type,numbits,required,arraylen,unavailable) 1443 elif type=='aisstr6': encodeAisstr6(o,name,type,numbits,required,arraylen,unavailable) 1444 elif type=='decimal': 1445 scale = None 1446 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1447 encodeDecimal(o,name,type,numbits,required,arraylen,unavailable,scale=scale) 1448 elif type=='udecimal': 1449 scale = None 1450 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1451 encodeUDecimal(o,name,type,numbits,required,arraylen,unavailable,scale=scale) 1452 elif type=='binary': encodeBinary(o,name,type,numbits,required,arraylen,unavailable) 1453 else: 1454 print 'WARNING: In buildEncode - Unhandled field type for',name,'...',type 1455 suggestType (name,type) 1456 assert False 1457 1458 # o.write('\n\tbv=binary.joinBV(bvList)\n') 1459 # o.write('\n\tbvLen=len(bv)\n') 1460 # o.write('\n\tif bvLen%6!=0:\n') 1461 # o.write('\n\t bv = bv + BitVector(size=bvLen%6) # \n') 1462 # o.write('\n\treturn bv\n\n') 1463 o.write('\n\treturn binary.joinBV(bvList)\n\n')
1464 1465 1466 ###################################################################### 1467 # DECODER ONE PART AT A TIME - if only using a small part, save some cycles! 1468 1469 1470 #, msgDict=None):
1471 -def buildDecodeParts(o,msgET, verbose=False, prefixName=False):
1472 1473 ''' 1474 Write the decoder for a message 1475 1476 @param o: open file where resulting code will be written 1477 @type msgET: elementtree 1478 @param prefixName: if True, put the name of the message on the functions. 1479 @param msgET: Element Tree starting at a message node 1480 @return: None 1481 1482 @todo: FIX: doc strings for each decode! 1483 @todo: FIX: check for a dac,fid, or efid. If exists, then this is an AIS Msg 8 payload 1484 @todo: May want to take a dictionary of already decoded fields to speed things that need prior info 1485 for things like variable length arrays 1486 ''' 1487 1488 assert(msgET.tag=='message') 1489 name = msgET.attrib['name'] 1490 1491 print 'Generating partial decode functions for',name 1492 1493 baseName = name+'Decode' 1494 if not prefixName: baseName = 'decode' 1495 1496 startindex = 0 # Where we are in the bitvector... FIX: what about variable length jobs? 1497 1498 for field in msgET.xpath('field'): 1499 name = field.attrib['name'] 1500 type = field.attrib['type'] 1501 1502 o.write('def '+baseName+name+'(bv, validate=False):\n') 1503 # Follow the same convention of decoding into a dict so that code is the same 1504 #o.write('\tr={};') 1505 o.write('\treturn ') 1506 1507 numbits = int(field.attrib['numberofbits']) 1508 required = None; 1509 if hasSubtag(field,'required'): 1510 required = field.xpath('required')[0].text 1511 unavailable=None; 1512 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 1513 arraylen=1 1514 if 'arraylength' in field.attrib: 1515 arraylen=int(field.attrib['arraylength']) 1516 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 1517 1518 assert None!=startindex 1519 if verbose: print 'startindex',startindex 1520 if type=='bool' : startindex = decodeBool (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1521 elif type=='uint' : startindex = decodeUInt (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1522 elif type=='int' : startindex = decodeInt (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1523 elif type=='float' : startindex = decodeFloat (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1524 elif type=='aisstr6': startindex = decodeAisstr6(o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1525 elif type=='binary' : startindex = decodeBinary (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1526 1527 elif type=='decimal': 1528 scale = None 1529 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1530 startindex = decodeDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale,decodeOnly=True) 1531 elif type=='udecimal': 1532 scale = None 1533 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1534 startindex = decodeUDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale,decodeOnly=True) 1535 1536 else: 1537 print 'WARNING: In buildDecode - Unhandled field type for',name,'...',type 1538 suggestType (name,type) 1539 assert False 1540 1541 #o.write('\treturn(r[\''+name+'\'])\n\n') 1542 o.write('\n\n')
1543 1544 1545 ###################################################################### 1546 # DECODER RING 1547
1548 -def buildDecode(o,msgET, verbose=False, prefixName=False):
1549 ''' 1550 Write the decoder for a message 1551 1552 @param o: open file where resulting code will be written 1553 @type msgET: elementtree 1554 @param msgET: Element Tree starting at a message node 1555 @return: None 1556 1557 @todo: FIX: check for a dac,fid, or efid. If exists, then this is an AIS Msg 8 payload 1558 ''' 1559 1560 assert(msgET.tag=='message') 1561 name = msgET.attrib['name'] 1562 1563 print 'Generating decoder for',name 1564 funcName = 'decode' 1565 if prefixName: funcName = name+'Decode' 1566 o.write('def '+funcName+'(bv, validate=False):\n') 1567 1568 ######################################## 1569 # doc string 1570 o.write("\t'''Unpack a "+name+" message \n\n") 1571 o.write('\tFields in params:\n') 1572 for field in msgET.xpath('field'): 1573 desc = field[0].text.replace('\n',' ') # get ride of new lines 1574 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc) # give the description 1575 if len(field.xpath("required")) == 1: 1576 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")') 1577 1578 o.write('\n') 1579 o.write('\t@type bv: BitVector\n') 1580 o.write('\t@param bv: Bits defining a message\n') 1581 o.write('\t@param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.\n') 1582 1583 o.write("\t@rtype: dict\n") 1584 o.write("\t@return: params\n") 1585 o.write("\t'''\n\n") 1586 1587 1588 o.write('\t#Would be nice to check the bit count here..\n') 1589 o.write('\t#if validate:\n') 1590 o.write('\t#\tassert (len(bv)==FIX: SOME NUMBER)\n') 1591 1592 1593 ######################################## 1594 # Actually build the code 1595 1596 o.write('\tr = {}\n') 1597 1598 1599 if verbose: print 'number of fields = ', len(msgET.xpath('field')) 1600 1601 dynamicArrays = False # Set to true when positioning must be calculated 1602 1603 startindex = 0 # Where we are in the bitvector... FIX: what about variable length jobs? 1604 1605 for field in msgET.xpath('field'): 1606 name = field.attrib['name'] 1607 type = field.attrib['type'] 1608 numbits = int(field.attrib['numberofbits']) 1609 required = None; 1610 if hasSubtag(field,'required'): 1611 required = field.xpath('required')[0].text 1612 #print 'required set for',name,'to',required 1613 unavailable=None; 1614 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 1615 arraylen=1 1616 if 'arraylength' in field.attrib: 1617 arraylen=int(field.attrib['arraylength']) 1618 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 1619 else: 1620 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')' 1621 1622 assert None!=startindex 1623 if verbose: print 'startindex',startindex 1624 if type=='bool' : startindex = decodeBool (o,name,type,startindex,numbits,required,arraylen,unavailable) 1625 elif type=='uint' : startindex = decodeUInt (o,name,type,startindex,numbits,required,arraylen,unavailable) 1626 elif type=='int' : startindex = decodeInt (o,name,type,startindex,numbits,required,arraylen,unavailable) 1627 elif type=='float' : startindex = decodeFloat (o,name,type,startindex,numbits,required,arraylen,unavailable) 1628 elif type=='aisstr6': startindex = decodeAisstr6(o,name,type,startindex,numbits,required,arraylen,unavailable) 1629 elif type=='binary' : startindex = decodeBinary (o,name,type,startindex,numbits,required,arraylen,unavailable) 1630 1631 elif type=='decimal': 1632 scale = None 1633 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1634 #print 'pre call...' 1635 #print ' required: "'+str(required)+'" scale: "'+str(scale)+'" unavail:', unavailable 1636 startindex = decodeDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale) 1637 elif type=='udecimal': 1638 scale = None 1639 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1640 #print 'pre call...' 1641 #print ' required: "'+str(required)+'" scale: "'+str(scale)+'" unavail:', unavailable 1642 startindex = decodeUDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale) 1643 1644 else: 1645 print 'WARNING: In buildDecode - Unhandled field type for',name,'...',type 1646 suggestType (name,type) 1647 assert False 1648 1649 1650 if None==startindex: print 'FIX: here. drat. treat me right' 1651 assert None!=startindex 1652 1653 1654 o.write('\treturn r\n\n')
1655 1656 1657 1658 #def getPythonType(aType): 1659 # ''' 1660 # Translate a 1661 # @rtype: str 1662 # @return: name of the python type 1663 # ''' 1664 1665 aisType2pythonType={ 1666 'bool':'Bool', 1667 'uint':'int', 1668 'int':'int', 1669 'udecimal':'Decimal', 1670 'decimal':'Decimal', 1671 'aisstr6':'str', 1672 'binary':'str', 1673 'float':'Float', 1674 } 1675 1676 import copy 1677 aisType2optParseType=copy.deepcopy(aisType2pythonType) 1678 aisType2optParseType['bool']='int' # FIX: make these appear as a flag variable 1679 aisType2optParseType['aisstr6']='string' 1680 aisType2optParseType['aisstr6']='string' 1681 aisType2optParseType['binary']='string' 1682 aisType2optParseType['udecimal']='string' # FIX: Is this a good choice? 1683 aisType2optParseType['decimal']='string' 1684 aisType2optParseType['float']='float' 1685 1686
1687 -def buildOptParse(o,msgET, prefixName=False):
1688 '''Create a function that adds the options to a parse object''' 1689 1690 assert None != msgET 1691 assert msgET.tag=='message' 1692 msgName = msgET.attrib['name'] 1693 1694 prefix='' 1695 if prefixName: prefix=msgName 1696 1697 funcName = 'addMsgOptions' 1698 if prefixName: funcName = msgName + 'AddMsgOptions' 1699 o.write('\ndef '+funcName+'(parser):') 1700 1701 # FIX: write out a doc string 1702 1703 o.write(''' 1704 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true', 1705 help='decode a "'''+msgName+'''" AIS message') 1706 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true', 1707 help='encode a "'''+msgName+'''" AIS message') 1708 ''') 1709 1710 #print 'here...', msgName, prefixName 1711 for field in msgET.xpath('field'): 1712 name = field.attrib['name'] 1713 fieldType = field.attrib['type'] 1714 if hasSubtag(field,'required'): 1715 print 'skipping required field ...',name,fieldType 1716 continue 1717 #print 'there',name,fieldType 1718 o.write('\tparser.add_option(\'--') 1719 if prefixName: o.write(msgName+'-') 1720 o.write(name+'-field\', dest=\''+name+'Field\'') 1721 if hasSubtag(field,'unavailable'): 1722 val = field.xpath('unavailable')[0].text 1723 o.write(',default=') 1724 if fieldType in ('uint','int','float'): 1725 o.write(val) 1726 elif fieldType in ('decimal','udecimal'): 1727 o.write('Decimal(\''+val+'\')') 1728 elif fieldType in ('aisstr6','bitvector'): 1729 o.write('\''+val+'\'') 1730 o.write(',metavar=\''+fieldType+'\',type=\''+aisType2optParseType[fieldType]+'\'') 1731 o.write('\n\t\t,help=\'Field parameter value [default: %default]\')\n')
1732 1733 1734
1735 -def buildMain(o, msgET, prefixName=False):
1736 assert None != msgET 1737 assert msgET.tag=='message' 1738 msgName = msgET.attrib['name'] 1739 1740 prefix='' 1741 if prefixName: prefix=msgName 1742 1743 buildOptParse(o, msgET, prefixName) 1744 1745 1746 o.write(''' 1747 ############################################################ 1748 if __name__=='__main__': 1749 1750 from optparse import OptionParser 1751 parser = OptionParser(usage="%prog [options]", 1752 version="%prog "+__version__) 1753 1754 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true', 1755 help='run the documentation tests') 1756 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true', 1757 help='run the unit tests') 1758 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 1759 help='Make the test output verbose') 1760 1761 # FIX: remove nmea from binary messages. No way to build the whole packet? 1762 # FIX: or build the surrounding msg 8 for a broadcast? 1763 typeChoices = ('binary','nmeapayload','nmea') # FIX: what about a USCG type message? 1764 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType' 1765 ,default='nmeapayload' 1766 ,help='What kind of string to expect ('+', '.join(typeChoices)+') [default: %default]') 1767 1768 1769 outputChoices = ('std','html','xml' ''') 1770 if haveLocatableMessage(msgET): o.write(', \'kml\',\'kml-full\'') 1771 o.write(''') 1772 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType' 1773 ,default='std' 1774 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]') 1775 1776 parser.add_option('-o','--output',dest='outputFileName',default=None, 1777 help='Name of the python file to write [default: stdout]') 1778 1779 ''') 1780 1781 1782 o.write('''\taddMsgOptions(parser)\n''') 1783 1784 o.write(''' 1785 (options,args) = parser.parse_args() 1786 success=True 1787 1788 if options.doctest: 1789 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 1790 sys.argv= [sys.argv[0]] 1791 if options.verbose: sys.argv.append('-v') 1792 import doctest 1793 numfail,numtests=doctest.testmod() 1794 if numfail==0: print 'ok' 1795 else: 1796 print 'FAILED' 1797 success=False 1798 1799 if not success: sys.exit('Something Failed') 1800 del success # Hide success from epydoc 1801 1802 if options.unittest: 1803 sys.argv = [sys.argv[0]] 1804 if options.verbose: sys.argv.append('-v') 1805 unittest.main() 1806 1807 outfile = sys.stdout 1808 if None!=options.outputFileName: 1809 outfile = file(options.outputFileName,'w') 1810 1811 ''') 1812 1813 1814 1815 ############################## 1816 # encode 1817 ############################## 1818 # FIX: make everything tabs. Grr. 1819 o.write('\n\tif options.doEncode:\n') 1820 o.write('\t\t# First make sure all non required options are specified\n') 1821 for field in msgET.xpath('field'): 1822 name = field.attrib['name'] 1823 fieldType = field.attrib['type'] 1824 varName = prefix+name+'Field' 1825 if not hasSubtag(field,'required'): 1826 o.write('\t\tif None==options.'+varName+': parser.error("missing value for '+varName+'")\n') 1827 1828 # Build dict 1829 o.write('\t\tmsgDict={\n') 1830 for field in msgET.xpath('field'): 1831 name = field.attrib['name'] 1832 varName = prefix+name+'Field' 1833 if hasSubtag(field,'required'): 1834 o.write('\t\t\t\''+name+'\': \''+field.xpath('required')[0].text+'\',\n') 1835 else: 1836 o.write('\t\t\t\''+name+'\': options.'+varName+',\n') 1837 o.write('\t\t}\n') 1838 1839 encodeFunction = 'encode' 1840 if prefixName: encodeFunction = msgName+'Encode' 1841 o.write(''' 1842 bits = '''+encodeFunction+'''(msgDict) 1843 if 'binary'==options.ioType: print str(bits) 1844 elif 'nmeapayload'==options.ioType: 1845 # FIX: figure out if this might be necessary at compile time 1846 print "bitLen",len(bits) 1847 bitLen=len(bits) 1848 if bitLen%6!=0: 1849 bits = bits + BitVector(size=(6 - (bitLen%6))) # Pad out to multiple of 6 1850 print "result:",binary.bitvectoais6(bits)[0] 1851 1852 1853 # FIX: Do not emit this option for the binary message payloads. Does not make sense. 1854 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability") 1855 else: sys.exit('ERROR: unknown ioType. Help!') 1856 ''') 1857 1858 1859 ############################## 1860 # decode all 1861 ############################## 1862 decodeFunction = 'decode' 1863 printFields='printFields' 1864 if prefixName: 1865 decodeFunction = msgName+'Decode' 1866 printFields = msgName+'PrintFields' 1867 o.write(''' 1868 if options.doDecode: 1869 for msg in args: 1870 bv = None 1871 if 'binary' == options.ioType: bv = BitVector(bitstring=msg) 1872 elif 'nmeapayload'== options.ioType: bv = binary.ais6tobitvec(msg) 1873 elif 'nmea' == options.ioType: bv = binary.ais6tobitvec(msg.split(',')[5]) 1874 else: sys.exit('ERROR: unknown ioType. Help!') 1875 1876 '''+printFields+'''('''+decodeFunction+'''(bv),out=outfile,format=options.outputType) 1877 ''')
1878 1879 1880 1881 1882 ###################################################################### 1883 if __name__=='__main__': 1884 from optparse import OptionParser 1885 parser = OptionParser(usage="%prog [options]", 1886 version="%prog "+__version__) 1887 1888 parser.add_option('-o','--output',dest='outputFileName',default=None, 1889 help='Name of the python file to write') 1890 # help='Name of the python file to write [default: stdout]') 1891 1892 parser.add_option('-i','-x','--xml-definition',dest='xmlFileName',default=None, 1893 help='XML definition file for the msg to use') 1894 # help='XML definition file for the msg to use [default: stdin]') 1895 1896 # parser.add_option('-m','--message',dest='message',default=None, 1897 # help='Which message to write code for [default: all]') 1898 1899 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true', 1900 help='run the documentation tests') 1901 1902 parser.add_option('-p','--prefix',dest='prefix',default=False,action='store_true', 1903 help='put the field name in front of all function names.' 1904 +' Allows multiple messages in one file') 1905 1906 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 1907 help='run the tests run in verbose mode') 1908 1909 (options,args) = parser.parse_args() 1910 1911 success=True 1912 1913 if options.doctest: 1914 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 1915 argvOrig = sys.argv 1916 sys.argv= [sys.argv[0]] 1917 if options.verbose: sys.argv.append('-v') 1918 import doctest 1919 numfail,numtests=doctest.testmod() 1920 if numfail==0: print 'ok' 1921 else: 1922 print 'FAILED' 1923 success=False 1924 sys.argv = argvOrig # Restore the original args 1925 del argvOrig # hide from epydoc 1926 sys.exit() # FIX: Will this exit success? 1927 1928 #infile=sys.stdin 1929 #if options.xmlFileName: infile = file(options.xmlFileName,'r') 1930 1931 #outfile=sys.stdout 1932 #if options.outputFile: outfile = file(options.xmlDefinition,'w') 1933 1934 # FIX: down the road, it would be good to allow either filenames of std{in/out} 1935 1936 if None==options.xmlFileName: 1937 sys.exit('ERROR: must specify an xml definition file.') 1938 if None==options.outputFileName: 1939 sys.exit('ERROR: must specify an python file to write to.') 1940 generatePython(options.xmlFileName,options.outputFileName,prefixName=options.prefix, verbose=options.verbose) 1941 1942 print '\nrecommend running pychecker like this:' 1943 print ' pychecker -q',options.outputFileName 1944