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