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

Source Code for Module ais.aisxmlbinmsg2py

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