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  @todo: handle entry ranges in lookup tables 
  38  @todo: arraylength of -1 for messages 12 and 14 
  39  @bug: NOT complete 
  40  ''' 
  41   
  42  import sys, os 
  43  from decimal import Decimal 
  44  from lxml import etree  
  45   
46 -def suggestType(name,curType,printout=True):
47 ''' 48 Try to suggest a type name if one did not work. 49 50 @param printout: if true, write a suggestion to stdout. 51 52 >>> suggestType('myFieldName','unsigned int') 53 Recommend switching "unsigned int" to "uint" for field "myFieldName" 54 'uint' 55 56 >>> suggestType('JohnWarfon','yoyodyne') 57 Sorry! No recommendation available for bad type "yoyodyne" for field "JohnWarfon" 58 ''' 59 newType = None 60 if curType.lower()=='unsigned int': 61 newType = 'uint' 62 elif curType.lower()=='unsigned decimal': 63 newType = 'udecimal' 64 65 if printout: 66 if None != newType: 67 print 'Recommend switching "'+curType+'" to "'+newType+'" for field "'+name+'"' 68 else: 69 print 'Sorry! No recommendation available for bad type "'+curType+'" for field "'+name+'"' 70 return newType
71 72
73 -def hasSubTag(et,subtag):
74 ''' 75 @return: true if the tag a sub tag with name subtag 76 ''' 77 if 0<len(et.xpath(subtag)): return True 78 return False
79 80
81 -def writeBeginning(o):
82 ''' 83 Write the doc string header for the message file 84 85 @param o: Open output file to write code to. 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 @todo: FIX: put in a description of the message here with fields and types. 131 \'\'\' 132 133 import sys 134 from decimal import Decimal 135 from BitVector import BitVector 136 137 import binary, aisstring 138 139 # FIX: check to see if these will be needed 140 TrueBV = BitVector(bitstring="1") 141 "Why always rebuild the True bit? This should speed things up a bunch" 142 FalseBV = BitVector(bitstring="0") 143 "Why always rebuild the False bit? This should speed things up a bunch" 144 145 146 ''') 147 return
148
149 -def generatePython(infile,outfile, prefixName=False,verbose=False):
150 ''' 151 @param infile: xml ais binary message definition file 152 @param outfile: where to dump the python code 153 ''' 154 155 aisMsgsET = etree.parse(infile).getroot() 156 157 o = file(outfile,'w') 158 os.chmod(outfile,0755) 159 160 writeBeginning(o) 161 162 for msgET in aisMsgsET: 163 if msgET.tag != 'message': continue 164 print msgET.tag, msgET.attrib['name'] 165 166 if len(msgET.xpath('include-struct')) > 0: 167 sys.exit("ERROR: cannot handle xml that still has include-struct tags.\n Please use expandais.py.") 168 buildHelpers(o,msgET,prefixName=prefixName,verbose=verbose) 169 buildEncode(o,msgET,prefixName=prefixName,verbose=verbose) 170 buildDecode(o,msgET,prefixName=prefixName) 171 buildDecodeParts(o,msgET,prefixName=prefixName) # functions that only decode one field 172 buildPrint(o,msgET,prefixName=prefixName) 173 buildLUT(o,msgET,prefixName=prefixName) 174 buildSQL(o,msgET,prefixName=prefixName) 175 buildLaTeX(o,msgET,prefixName=prefixName) 176 177 o.write('\n\n######################################################################\n') 178 o.write('# UNIT TESTING\n') 179 o.write('######################################################################\n') 180 o.write('import unittest\n') 181 182 for msgET in aisMsgsET: 183 if msgET.tag != 'message': continue 184 print 'Building unit tests for message ...', msgET.attrib['name'] 185 186 buildUnitTest(o,msgET, prefixName=prefixName) 187 188 for msgET in aisMsgsET: 189 if msgET.tag != 'message': continue 190 buildMain(o,msgET, prefixName=prefixName) 191 192 return
193 194 ###################################################################### 195 # Build Helpers 196 ###################################################################### 197
198 -def buildHelpers(o,msgET, verbose=False, prefixName=False):
199 ''' 200 emit the fieldList and other things??? 201 202 @param o: open file where resulting code will be written 203 @param msgET: Element Tree starting at a message node 204 205 @todo: for lookuptable/entry values, make it also print the decoded value. 206 @todo: use a different name for message and field 207 @todo: put in comments with the python and sql data type for each field 208 ''' 209 210 if verbose: 211 msgname = msgET.attrib['name'] 212 print 'Building helpers for',msgname 213 214 if prefixName: 215 o.write(prefixName,'FieldList = (\n') 216 else: 217 o.write('fieldList = (\n') 218 219 for field in msgET.xpath('field'): 220 name = field.attrib['name'] 221 o.write('\t\''+name+'\',\n') 222 223 o.write(')\n\n') 224 225 # FIX: add some documentation to the fieldList 226 # FIX: like that it is used as the default csv list 227 228 if prefixName: 229 o.write(prefixName,'FieldListPostgres = (\n') 230 else: 231 o.write('fieldListPostgres = (\n') 232 finished=[] # postgis names 233 for field in msgET.xpath('field'): 234 name = field.attrib['name'] 235 if 'postgisName' in field.attrib: 236 name = field.attrib['postgisName'] 237 if name in finished: continue # already done, so skip 238 finished.append(name) 239 o.write('\t\''+name+'\',\t# PostGIS data type\n') 240 else: 241 o.write('\t\''+name+'\',\n') 242 243 o.write(')\n\n') 244 245 del finished 246 247 248 249 if prefixName: 250 o.write(prefixName,'ToPgFields = {\n') 251 else: 252 o.write('toPgFields = {\n') 253 for field in msgET.xpath('field'): 254 if 'postgisName' not in field.attrib: continue 255 name = field.attrib['name'] 256 pgName = field.attrib['postgisName'] 257 o.write('\t\''+name+'\':\''+pgName+'\',\n') 258 o.write('}\n') 259 o.write('''\'\'\' 260 Go to the Postgis field names from the straight field name 261 \'\'\' 262 263 ''') 264 265 266 if prefixName: 267 o.write(prefixName,'FromPgFields = {\n') 268 else: 269 o.write('fromPgFields = {\n') 270 finished=[] # postgis names 271 for field in msgET.xpath('field'): 272 if 'postgisName' not in field.attrib: continue 273 name = field.attrib['name'] 274 pgName = field.attrib['postgisName'] 275 276 if pgName in finished: continue # already done, so skip 277 finished.append(pgName) 278 o.write('\t\''+pgName+'\':(') 279 xp = 'field[@postgisName=\''+pgName+'\']' 280 parts = ['\''+part.attrib['name']+'\'' for part in msgET.xpath(xp)] 281 o.write(','.join(parts)+',),\n') 282 o.write('}\n') 283 o.write('''\'\'\' 284 Go from the Postgis field names to the straight field name 285 \'\'\' 286 287 ''') 288 289 290 if prefixName: 291 o.write(prefixName,'PgTypes = {\n') 292 else: 293 o.write('pgTypes = {\n') 294 finished=[] # postgis names 295 for field in msgET.xpath('field'): 296 if 'postgisName' not in field.attrib: continue 297 pgType = field.attrib['postgisType'] 298 pgName = field.attrib['postgisName'] 299 if pgName in finished: continue # already done, so skip 300 finished.append(pgName) 301 o.write('\t\''+pgName+'\':\''+pgType+'\',\n') 302 o.write('}\n') 303 o.write('''\'\'\' 304 Lookup table for each postgis field name to get its type. 305 \'\'\' 306 307 ''')
308 309 310 311 312 313 314 315 ###################################################################### 316 # SIMPLE PRINT 317 ###################################################################### 318
319 -def getMaxFieldNameLen(msgET):
320 '''Get the maximum string length of any field name''' 321 maxStrLen=0 322 for field in msgET.xpath('field'): 323 fieldLen = len(field.attrib['name']) 324 if fieldLen>maxStrLen: maxStrLen = fieldLen 325 return maxStrLen
326
327 -def padStrRight(aStr,strlen):
328 '''Pad a string out to the length requested with spaces out to the right''' 329 return aStr + ' '*(strlen-len(aStr))
330
331 -def haveLocatableMessage(msgET):
332 '''Make sure this message has both long/x and lat/y fields. 333 @rtype: bool 334 ''' 335 #if getLongitudeFieldName(msgET) and getLatitudeFieldName(msgET): return True 336 337 if 0==len(msgET.xpath('field[contains(@name, "longitude")]')): return False 338 if 0==len(msgET.xpath('field[contains(@name, "latitude")]')): return False 339 return True
340
341 -def getLongitudeFieldName(msgET):
342 ''' 343 Dig up the first field name that include longitude and return it 344 @todo: might want to allow a search for a special tag to mark this 345 ''' 346 return msgET.xpath('field[contains(@name, "longitude")]')[0].attrib['name']
347
348 -def getLatitudeFieldName(msgET):
349 ''' 350 Dig up the first field name that include longitude and return it 351 @todo: might want to allow a search for a special tag to mark this 352 ''' 353 return msgET.xpath('field[contains(@name, "latitude")]')[0].attrib['name']
354
355 -def buildPrint(o,msgET, verbose=False, prefixName=False):
356 ''' 357 Write a simple in order print for the resulting dictionary. 358 359 @param o: open file where resulting code will be written 360 @param msgET: Element Tree starting at a message node 361 362 @todo: for lookuptable/entry values, make it also print the decoded value. 363 @todo: use a different name for message and field 364 ''' 365 assert(msgET.tag=='message') 366 msgName = msgET.attrib['name'] 367 #msgName = msgname = name # FIX: make these all msgName 368 369 print 'Generating print for',msgName # FIX: verbose? 370 # +1 for the ':' 371 maxFieldLen = 1 + getMaxFieldNameLen(msgET) 372 373 374 ############################## 375 # Html builder 376 ############################## 377 378 printHtmlName = 'printHtml' 379 if prefixName: printHtmlName = name+'printHtml' 380 381 382 383 384 #################### 385 ####### HTML format 386 #################### 387 #o.write('\telif \'html\'==format:\n') 388 o.write('\n') 389 o.write('def '+printHtmlName+'(params, out=sys.stdout):\n') 390 o.write('\t\tout.write("<h3>'+msgName+'<h3>\\n")\n') 391 o.write('\t\tout.write("<table border=\\"1\\">\\n")\n') 392 #o.write('\t\tout.write("<tr bgcolor=\\"#9acd32\\">\\n")\n') 393 o.write('\t\tout.write("<tr bgcolor=\\"orange\\">\\n")\n') 394 o.write('\t\tout.write("<th align=\\"left\\">Field Name</th>\\n")\n') 395 o.write('\t\tout.write("<th align=\\"left\\">Type</th>\\n")\n') 396 o.write('\t\tout.write("<th align=\\"left\\">Value</th>\\n")\n') 397 o.write('\t\tout.write("<th align=\\"left\\">Value in Lookup Table</th>\\n")\n') 398 o.write('\t\tout.write("<th align=\\"left\\">Units</th>\\n")\n') 399 400 401 for field in msgET.xpath('field'): 402 o.write('\t\tout.write("\\n")\n') 403 o.write('\t\tout.write("<tr>\\n")\n') 404 fieldname = field.attrib['name'] 405 fieldtype = field.attrib['type'] 406 o.write('\t\tout.write("<td>'+fieldname+'</td>\\n")\n') 407 o.write('\t\tout.write("<td>'+fieldtype+'</td>\\n")\n') 408 409 numbits = int(field.attrib['numberofbits']) 410 required = None; 411 if hasSubTag(field,'required'): 412 required = field.xpath('required')[0].text 413 unavailable=None; 414 if hasSubTag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 415 arraylen=1 416 if 'arraylength' in field.attrib: arraylen=int(field.attrib['arraylength']) 417 418 if 1==arraylen or fieldtype=='aisstr6': 419 o.write('\t\tif \''+fieldname+'\' in params:\n\t\t\tout.write("\t<td>"+str(params[\''+fieldname+'\'])+"</td>\\n")\n') 420 if not hasSubTag(field,'lookuptable'): 421 # Just duplicate the value through 422 o.write('\t\t\tout.write("\t<td>"+str(params[\''+fieldname+'\'])+"</td>\\n")\n') 423 else: 424 lutName = fieldname+'DecodeLut' 425 if prefixName: lutName = msgname+fieldname.capitalize()+'DecodeLut' 426 427 o.write('\t\t\tif str(params[\''+fieldname+'\']) in '+lutName+':\n') 428 o.write('\t\t\t\tout.write("<td>"+'+lutName+'[str(params[\''+fieldname+'\'])]+"</td>")\n') 429 o.write('\t\t\telse:\n') 430 o.write('\t\t\t\tout.write("<td><i>Missing LUT entry</i></td>")\n') 431 else: sys.exit ('FIX: handle arrays in the buildPrint func') 432 433 if hasSubTag(field,'units'): 434 o.write('\t\tout.write("<td>'+field.xpath('units')[0].text+'</td>\\n")\n') 435 436 o.write('\t\tout.write("</tr>\\n")\n') 437 438 o.write('\t\tout.write("</table>\\n")\n') 439 o.write('\n') 440 441 ############################## 442 ####### KML GoogleEarth 443 ############################## 444 445 printKmlName = 'printKml' 446 if prefixName: printKmlName = msgName+'printKml' 447 if not haveLocatableMessage(msgET): printKmlName=None 448 else: 449 o.write('\n') 450 o.write('def printKml(params, out=sys.stdout):\n') 451 o.write('\t\'\'\'KML (Keyhole Markup Language) for Google Earth, but without the header/footer\'\'\'\n') 452 o.write('\tout.write("\\\t<Placemark>\\n")\n') 453 o.write('\tout.write("\\t\t<name>"+str(params[\''+msgET.attrib['titlefield']+'\'])+"</name>\\n")\n') 454 o.write('\tout.write("\\t\\t<description>\\n")\n') 455 456 o.write('\timport StringIO\n') 457 o.write('\tbuf = StringIO.StringIO()\n') 458 o.write('\tprintHtml(params,buf)\n') 459 o.write('\timport cgi\n') 460 o.write('\tout.write(cgi.escape(buf.getvalue()))\n') 461 462 o.write('\tout.write("\\t\\t</description>\\n")\n') 463 o.write('\tout.write("\\t\\t<styleUrl>#m_ylw-pushpin_copy0</styleUrl>\\n")\n') 464 o.write('\tout.write("\\t\\t<Point>\\n")\n') 465 o.write('\tout.write("\\t\\t\\t<coordinates>")\n') 466 o.write('\tout.write(str(params[\''+getLongitudeFieldName(msgET)+'\']))\n') 467 o.write('\tout.write(\',\')\n') 468 o.write('\tout.write(str(params[\''+getLatitudeFieldName(msgET)+'\']))\n') 469 o.write('\tout.write(",0</coordinates>\\n")\n') 470 o.write('\tout.write("\\t\\t</Point>\\n")\n') 471 o.write('\tout.write("\\t</Placemark>\\n")\n') 472 o.write('\n') 473 474 475 ############################## 476 ##### Main print dispatch 477 ############################## 478 479 funcName = 'printFields' 480 if prefixName: funcName = msgName+'PrintFields' 481 482 o.write('def '+funcName+'(params, out=sys.stdout, format=\'std\', fieldList=None, dbType=\'postgres\'):\n') 483 484 ######################################## 485 # doc string 486 o.write("\t'''Print a "+msgName+" message to stdout.\n\n") 487 o.write('\tFields in params:\n') 488 for field in msgET.xpath('field'): 489 desc = field[0].text.replace('\n',' ') # get ride of new lines 490 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc) # give the description 491 if len(field.xpath("required")) == 1: 492 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")') 493 494 o.write('\n') 495 o.write('\t@param params: Dictionary of field names/values. \n') 496 o.write('\t@param out: File like object to write to\n') 497 498 o.write("\t@rtype: stdout\n") 499 o.write("\t@return: text to out\n") 500 o.write("\t'''\n\n") 501 502 ######################################## 503 # Actually build the code 504 505 o.write('\tif \'std\'==format:\n') 506 o.write('\t\tout.write("'+msgName+':\\n")\n') 507 508 if verbose: print 'number of fields = ', len(msgET.xpath('field')) 509 510 511 for field in msgET.xpath('field'): 512 fieldname = field.attrib['name'] 513 fieldtype = field.attrib['type'] 514 numbits = int(field.attrib['numberofbits']) 515 required = None; 516 if hasSubTag(field,'required'): 517 required = field.xpath('required')[0].text 518 unavailable=None; 519 if hasSubTag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 520 arraylen=1 521 if 'arraylength' in field.attrib: 522 arraylen=int(field.attrib['arraylength']) 523 if verbose: print 'Processing field ...',fieldname,'('+fieldtype+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 524 else: 525 if verbose: print 'Processing field ...',fieldname,'(',fieldtype+' ',numbits,')' 526 527 if 1==arraylen or fieldtype=='aisstr6': 528 o.write('\t\tif \''+fieldname+'\' in params: out.write("\t'+padStrRight(fieldname+':',maxFieldLen)+' "+str(params[\''+fieldname+'\'])+"\\n")\n') 529 # FIX: elif aisstr6 strip @@@ and then print 530 else: 531 print '\n','ERROR:',fieldname,fieldtype 532 sys.exit ('ERROR: FIX: handle arrays in the buildPrint func') 533 534 #################### 535 ####### Comma separated values (csv) 536 #################### 537 o.write(''' elif 'csv'==format: 538 if None == options.fieldList: 539 options.fieldList = fieldList 540 needComma = False; 541 for field in fieldList: 542 if needComma: out.write(',') 543 needComma = True 544 if field in params: 545 out.write(str(params[field])) 546 # else: leave it empty 547 out.write("\\n") 548 ''') 549 550 #################### 551 ####### Other dispatchers 552 #################### 553 o.write('\telif \'html\'==format:\n') 554 o.write('\t\t'+printHtmlName+'(params,out)\n') 555 556 o.write(''' elif 'sql'==format: 557 sqlInsertStr(params,out,dbType=dbType) 558 ''') 559 560 if haveLocatableMessage(msgET): 561 # Has a lon/lat pair somewhere in there. 562 o.write('\telif \'kml\'==format:\n') 563 o.write('\t\t'+printKmlName+'(params,out)\n') 564 565 o.write('\telif \'kml-full\'==format:\n') 566 567 o.write('\t\tout.write(\"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>\\n")\n') 568 o.write('\t\tout.write("<kml xmlns=\\"http://earth.google.com/kml/2.1\\">\\n")\n') 569 o.write('\t\tout.write("<Document>\\n")\n') 570 # o.write('\t\tout.write("\t<name>Position</name>\\n")\n') 571 o.write('\t\tout.write("\t<name>'+msgName+'</name>\\n")\n') 572 573 o.write('\t\t'+printKmlName+'(params,out)\n') 574 575 o.write('\t\tout.write("</Document>\\n")\n') 576 o.write('\t\tout.write("</kml>\\n")\n') 577 578 579 580 #################### 581 ####### Make safety check 582 #################### 583 o.write('\telse: \n') 584 o.write('\t\tprint "ERROR: unknown format:",format\n') 585 o.write('\t\tassert False\n') 586 587 o.write('\n\treturn # Nothing to return\n\n')
588 589 590 ###################################################################### 591 # Build Lookup tables for enumerated int or uint fields 592 ###################################################################### 593 594
595 -def buildLaTeX(o,msgET, verbose=False, prefixName=False):
596 ''' 597 Write functions for LaTeX output 598 599 @param o: open file where resulting code will be written 600 @param msgET: Element Tree starting at a message node 601 @param verbose: talk lots in the process 602 @param prefixName: set to a string to have the commands prefixed by that character. 603 604 @todo: should this be a style sheet thing instead? 605 ''' 606 assert(msgET.tag=='message') 607 msgName = msgET.attrib['name'] 608 print 'Generating SQL commands for', msgName 609 610 createFuncName = 'latexDefinitionTable' 611 if prefixName: createFuncName = msgName+'LaTeXDefinitionTable' 612 613 o.write('''\n###################################################################### 614 # LATEX SUPPORT 615 ###################################################################### 616 617 ''') 618 619 # Should this really default to true for the uscg fields? 620 o.write('def '+createFuncName+'''(outfile=sys.stdout 621 ): 622 \'\'\' 623 Return the LaTeX definition table for this message type 624 @param outfile: file like object to print to. 625 @type outfile: file obj 626 @return: LaTeX table string via the outfile 627 @rtype: str 628 629 \'\'\' 630 o = outfile 631 ''') 632 633 # NOTE: using / to mean one \ in the output compiled code 634 o.write(''' 635 o.write(\'\'\' 636 /begin{table}%[htb] 637 /centering 638 /begin{tabular}{|l|c|l|} 639 /hline 640 Parameter & Number of bits & Description 641 // /hline/hline 642 '''.replace('/','\\\\')) 643 644 lines = [] 645 totalBits = 0 646 for field in msgET.xpath('field'): 647 fieldName = field.attrib['name'].replace('_','\_') # _ means subscript to latex 648 fieldType = field.attrib['type'] 649 fieldBits = field.attrib['numberofbits'] 650 if 'arraylength' in field.attrib: 651 fieldBits = str(int(fieldBits) * int(field.attrib['arraylength'])) 652 totalBits += int(fieldBits) 653 # FIX: maybe cut down to the first sentence? 654 fieldDescription = field.xpath('description')[0].text.replace('\n',' ') 655 lines.append(fieldName+' & '+fieldBits+' & '+fieldDescription) 656 657 o.write(' \\\\\\\\ \\hline \n'.join(lines)) 658 # 168 in the first slot and following slots get to use the whole 256?? FIX: is this right? 659 from math import ceil 660 numSlots = 1 + int( ceil((totalBits-168)/256.) ) 661 emptySlotBits = ( 168 + (numSlots-1)*256 ) - totalBits 662 s = '' 663 if numSlots>1: s = 's' 664 o.write(('// /hline /hline\nTotal bits & '+str(totalBits)+' & Appears to take '+str(numSlots)+' slot'+s).replace('/','\\\\')) 665 if emptySlotBits > 0: 666 o.write(' with '+str(emptySlotBits)+' pad bits to fill the last slot') 667 668 o.write( (''' // /hline 669 /end{tabular} 670 /caption{AIS message number '''+msgET.attrib['aismsgnum']+': '+msgET.xpath('description')[0].text.replace('\n',' ')+'''} 671 /label{tab:'''+msgET.attrib['name']+'''} 672 /end{table} 673 \'\'\') 674 ''').replace('/','\\\\'))
675 676 # FIX: should it return a copy of outfile? 677
678 -def buildSQL(o,msgET, verbose=False, prefixName=False):
679 ''' 680 Write SQL code 681 682 @param o: open file where resulting code will be written 683 @param msgET: Element Tree starting at a message node 684 @param verbose: talk lots in the process 685 @param prefixName: set to a string to have the commands prefixed by that character. 686 ''' 687 assert(msgET.tag=='message') 688 msgName = msgET.attrib['name'] 689 print 'Generating SQL commands for', msgName 690 691 createFuncName = 'sqlCreate' 692 if prefixName: createFuncName = msgName+'SqlCreate' 693 insertFuncName = 'sqlInsert' 694 if prefixName: insertFuncName = msgName+'SqlInsert' 695 696 o.write('''###################################################################### 697 # SQL SUPPORT 698 ###################################################################### 699 700 ''') 701 702 # Should this really default to true for the uscg fields? 703 o.write('def '+createFuncName+'''Str(outfile=sys.stdout, fields=None, extraFields=None 704 ,addCoastGuardFields=True 705 ,dbType='postgres' 706 ): 707 \'\'\' 708 Return the SQL CREATE command for this message type 709 @param outfile: file like object to print to. 710 @param fields: which fields to put in the create. Defaults to all. 711 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields 712 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format 713 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres') 714 @type addCoastGuardFields: bool 715 @return: sql create string 716 @rtype: str 717 718 @see: sqlCreate 719 \'\'\' 720 # FIX: should this sqlCreate be the same as in LaTeX (createFuncName) rather than hard coded? 721 outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType))) 722 723 ''') 724 725 o.write('def '+createFuncName+'''(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'): 726 \'\'\' 727 Return the sqlhelp object to create the table. 728 729 @param fields: which fields to put in the create. Defaults to all. 730 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields 731 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format 732 @type addCoastGuardFields: bool 733 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres') 734 @return: An object that can be used to generate a return 735 @rtype: sqlhelp.create 736 \'\'\' 737 if None == fields: fields = fieldList 738 import sqlhelp 739 ''') 740 741 o.write('\tc = sqlhelp.create(\''+msgName+'\',dbType=dbType)\n') 742 o.write('\tc.addPrimaryKey()\n'); 743 744 for field in msgET.xpath('field'): 745 fieldName = field.attrib['name'] 746 fieldType = field.attrib['type'] 747 748 postgis = False 749 if 'postgisType' in field.attrib: 750 postgis=True 751 o.write('\tif dbType != \'postgres\':\n\t') 752 o.write('\tif \''+fieldName+'\' in fields: c.add') 753 # FIX: add the ADD command here. 754 if fieldType in ('int','uint'): o.write('Int (\''+fieldName+'\')') 755 elif fieldType in ('float' ): o.write('Real(\''+fieldName+'\')') 756 elif fieldType == 'bool': o.write('Bool(\''+fieldName+'\')') 757 elif fieldType in ('decimal','udecimal'): 758 if not hasSubTag(field,'decimalplaces'): 759 print '\n ** ERROR: missing decimalplaces field for ',fieldName,'\n' 760 assert (False) 761 scaleSQL = int(field.xpath('decimalplaces')[0].text) # number of decimal places 762 numBits = int(field.attrib['numberofbits']) 763 if not hasSubTag(field,'scale'): 764 print 'ERROR: missing required <scale> for',fieldName,'of type',fieldType 765 scaleVal = float(field.xpath('scale')[0].text) 766 precision = scaleSQL + len(str(int((2**numBits)/scaleVal))) 767 #precision += 1 # FIX: do I really need this safety factor 768 o.write('Decimal(\''+fieldName+'\','+str(precision)+','+str(scaleSQL)+')') 769 elif fieldType == 'binary': 770 numBits = int(field.attrib['numberofbits']) 771 if -1 == numBits: numBits=1024 # FIX: what is a good maximum for AIS? 772 o.write('BitVarying(\''+fieldName+'\','+str(numBits)+')') 773 elif fieldType == 'aisstr6': 774 arrayLength = int(field.attrib['arraylength']) 775 o.write('VarChar(\''+fieldName+'\','+str(arrayLength)+')') 776 else: 777 print '\n\n *** Unknown type in SQL code generation for field',fieldName+':',fieldType,'\n\n' 778 assert(False) 779 o.write('\n') 780 781 o.write(''' 782 if addCoastGuardFields: 783 # c.addInt('cg_rssi') # Relative signal strength indicator 784 # c.addInt('cg_d') # dBm receive strength 785 # c.addInt('cg_T') # Receive timestamp from the AIS equipment 786 # c.addInt('cg_S') # Slot received in 787 # c.addVarChar('cg_x',10) # Idonno 788 c.addVarChar('cg_r',15) # Receiver station ID - should usually be an MMSI, but sometimes is a string 789 c.addInt('cg_sec') # UTC seconds since the epoch 790 791 c.addTimestamp('cg_timestamp') # UTC decoded cg_sec - not actually in the data stream 792 ''') 793 794 795 # 796 # Postgres with PostGIS added 797 # 798 postgisFields = msgET.xpath('field[@postgisType]') 799 if len(postgisFields)<1: 800 if verbose: print 'No postgis fields' 801 else: 802 o.write('\n\tif dbType == \'postgres\':\n') 803 finished=[] # Should be a set? 804 print 'processing postgis fields for create:' 805 for field in postgisFields: 806 #print ' ',field.attrib['name'],field.attrib['postgisName'] 807 pgName = field.attrib['postgisName'] 808 if pgName in finished: 809 print 'already handled',pgName 810 continue 811 finished.append(pgName) 812 pgType = field.attrib['postgisType'] 813 #print ' doing',pgName,pgType 814 components = msgET.xpath('field[@postgisName=\''+pgName+'\']') 815 #print ' all components' 816 #for c in components: 817 # print ' ',c.attrib['postgisName'] 818 o.write('\t\tc.addPostGIS(\''+pgName+'\',\''+pgType+'\','+str(len(components))+');\n') 819 820 o.write('\n\treturn c\n\n') 821 822 823 # FIX: function name 824 o.write('''def '''+insertFuncName+'''Str(params, outfile=sys.stdout, extraParams=None, dbType='postgres'): 825 \'\'\' 826 Return the SQL INSERT command for this message type 827 @param params: dictionary of values keyed by field name 828 @param outfile: file like object to print to. 829 @param extraParams: A sequence of tuples containing (name,sql type) for additional fields 830 @return: sql create string 831 @rtype: str 832 833 @see: sqlCreate 834 \'\'\' 835 outfile.write(str(sqlInsert(params,extraParams,dbType=dbType))) 836 837 838 ''') 839 840 # FIX: function name 841 o.write('''def '''+insertFuncName+'''(params,extraParams=None,dbType='postgres'): 842 \'\'\' 843 Give the SQL INSERT statement 844 @param params: dict keyed by field name of values 845 @param extraParams: any extra fields that you have created beyond the normal ais message fields 846 @rtype: sqlhelp.insert 847 @return: insert class instance 848 @todo: allow optional type checking of params? 849 @warning: this will take invalid keys happily and do what??? 850 \'\'\' 851 import sqlhelp 852 i = sqlhelp.insert(\'''' + msgName + '''\',dbType=dbType) 853 854 if dbType=='postgres': 855 finished = [] 856 for key in params: 857 if key in finished: 858 continue 859 860 if key not in toPgFields and key not in fromPgFields: 861 if type(params[key])==Decimal: i.add(key,float(params[key])) 862 else: i.add(key,params[key]) 863 else: 864 if key in fromPgFields: 865 val = params[key] 866 # Had better be a WKT type like POINT(-88.1 30.321) 867 i.addPostGIS(key,val) 868 finished.append(key) 869 else: 870 # Need to construct the type. 871 pgName = toPgFields[key] 872 #valStr='GeomFromText(\\''+pgTypes[pgName]+'(' 873 valStr=pgTypes[pgName]+'(' 874 vals = [] 875 for nonPgKey in fromPgFields[pgName]: 876 vals.append(str(params[nonPgKey])) 877 finished.append(nonPgKey) 878 valStr+=' '.join(vals)+')' 879 i.addPostGIS(pgName,valStr) 880 else: 881 for key in params: 882 if type(params[key])==Decimal: i.add(key,float(params[key])) 883 else: i.add(key,params[key]) 884 885 if None != extraParams: 886 for key in extraParams: 887 i.add(key,extraParams[key]) 888 889 return i 890 ''')
891 892 893 894 ###################################################################### 895 # Build Lookup tables for enumerated int or uint fields 896 ###################################################################### 897 898
899 -def buildLUT(o,msgET, verbose=False, prefixName=False):
900 ''' 901 Write lookup tables for enumerated types (uint or int, maybe bool too). 902 903 @todo: FIX: what to do about multiple entries with the same text? Need to ban that kind of thing 904 @todo: Make doc strings for each LUT. 905 906 @param o: open file where resulting code will be written 907 @param msgET: Element Tree starting at a message node 908 ''' 909 assert(msgET.tag=='message') 910 msgname = msgET.attrib['name'] 911 912 print 'Generating lookup tables for',msgname # FIX: verbose? 913 914 for field in msgET.xpath('field'): 915 name = field.attrib['name'] 916 if not hasSubTag(field,'lookuptable'): continue 917 lut = field.xpath('lookuptable')[0] 918 919 lutName = name 920 if prefixName: lutName = msgname+name.capitalize() 921 922 o.write(lutName+'EncodeLut = {\n') 923 for entry in lut.xpath('entry'): 924 o.write('\t\''+entry.text+'\':\''+entry.attrib['key']+'\',\n') 925 o.write('\t} #'+lutName+'EncodeLut\n') 926 o.write('\n') 927 928 # FIX: make doc string for LUT here 929 930 o.write(lutName+'DecodeLut = {\n') 931 for entry in lut.xpath('entry'): 932 o.write('\t\''+entry.attrib['key']+'\':\''+entry.text+'\',\n') 933 o.write('\t} # '+lutName+'EncodeLut\n') 934 o.write('\n')
935 936 # FIX: make doc string for LUT here 937 938 939 940 941 ###################################################################### 942 # ENCODERS 943 ######################################################################
944 -def encodeBool(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
945 ''' 946 Build the encoder for boolean variables 947 @type o: file like obj 948 @param o: where write the code 949 @type name: str 950 @param name: field name 951 @type type: str 952 @param type: bool, etc. 953 @type numbits: int = 1 954 @param numbits: How many bits per unit datum (must be 1 for bools) 955 @type required: bool or None 956 @param required: If not None, then the value must be set to this. 957 @type arraylen: int >= 1 958 @param arraylen: many bools will there be? FIX: handle variable 959 @type unavailable: bool or None 960 @param unavailable: the default value to use if none given (if not None) 961 @return: None 962 ''' 963 964 if verbose: print 'bool encode',name,': unvail=',unavailable 965 966 assert type.lower()=='bool' 967 assert numbits==1 968 if arraylen != 1: assert False # FIX... handle arrays 969 if verbose: o.write('\t### FIELD: '+name+' (type=bool)\n') 970 if None != required: 971 assert type(required)==bool 972 if required: o.write('\t\tbvList.append(TrueBV)\n') 973 else: o.write('\t\tbvList.append(FalseBV)\n') 974 if verbose: o.write('\n') 975 return 976 977 if None==unavailable: 978 o.write('\tif params["'+name+'"]: bvList.append(TrueBV)\n') 979 o.write('\telse: bvList.append(FalseBV)\n') 980 else: # Have a default value that can be filled in 981 assert type(unavailable)==bool 982 o.write("\tif '"+name+"' in params:\n") 983 o.write('\t\tif params["'+name+'"]: bvList.append(TrueBV)\n') 984 o.write('\t\telse: bvList.append(FalseBV)\n') 985 o.write('\telse:\n') 986 if unavailable: o.write('\t\tbvList.append(TrueBV)\n') 987 else: o.write('\t\tbvList.append(FalseBV)\n') 988 if verbose: o.write('\n')
989
990 -def encodeUInt(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
991 ''' 992 Build the encoder for unsigned integer variables 993 994 @type o: file like obj 995 @param o: where write the code 996 @type name: str 997 @param name: field name 998 @type type: str 999 @param type: uint, bool, etc. 1000 @type numbits: int >= 1 1001 @param numbits: How many bits per unit datum (must be 1..32) 1002 @type required: bool or None 1003 @param required: If not None, then the value must be set to this. 1004 @type arraylen: int >= 1 1005 @param arraylen: many unsigned ints will there be? FIX: handle variable 1006 @type unavailable: bool or None 1007 @param unavailable: the default value to use if none given (if not None) 1008 @return: None 1009 ''' 1010 if verbose: print ' encodeUInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 1011 1012 assert type=='uint' 1013 assert numbits>=1 and numbits<=32 1014 if arraylen != 1: assert False # FIX... handle arrays 1015 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 1016 1017 if None != required: 1018 if verbose: print ' required:',required 1019 required=int(required) 1020 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(required)+'),'+str(numbits)+'))\n') 1021 if verbose: o.write('\n') 1022 return 1023 1024 if None==unavailable: 1025 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal=params[\''+name+'\']),'+str(numbits)+'))\n') 1026 else: # Have a default value that can be filled in 1027 #assert type(unavailable)== 1028 int(unavailable) # Make sure unavailable is a number object 1029 o.write("\tif '"+name+"' in params:\n") 1030 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=params[\''+name+'\']'+'),'+str(numbits)+'))\n') 1031 o.write('\telse:\n') 1032 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(unavailable)+'),'+str(numbits)+'))\n') 1033 1034 if verbose: o.write('\n')
1035 1036
1037 -def encodeFloat(o,name,fieldType,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
1038 ''' 1039 Build the encoder for IEEE float variables 1040 1041 @type o: file like obj 1042 @param o: where write the code 1043 @type name: str 1044 @param name: field name 1045 @type fieldType: str 1046 @param fieldType: uint, bool, etc. 1047 @type numbits: int >= 1 1048 @param numbits: How many bits per unit datum (must be 1..32) 1049 @type required: bool or None 1050 @param required: If not None, then the value must be set to this. 1051 @type arraylen: int >= 1 1052 @param arraylen: many unsigned ints will there be? FIX: handle variable 1053 @type unavailable: bool or None 1054 @param unavailable: the default value to use if none given (if not None) 1055 @return: None 1056 ''' 1057 if verbose: print ' encodeUInt:',name,fieldType,numbits,'Req:',required,'alen:',arraylen,unavailable 1058 1059 assert numbits==32 # Force by the IEEE spec 1060 if arraylen != 1: assert False # FIX... handle arrays 1061 if verbose: o.write('\t### FIELD: '+name+' (type='+fieldType+')\n') 1062 1063 if None != required: 1064 if verbose: print ' required:',required 1065 required=int(required) 1066 o.write('\tbvList.append(binary.float2bitvec('+str(required)+'))\n') 1067 if verbose: o.write('\n') 1068 return 1069 1070 if None==unavailable: 1071 o.write('\tbvList.append(binary.float2bitvec(params[\''+name+'\']))\n') 1072 else: # Have a default value that can be filled in 1073 int(unavailable) # Make sure unavailable is a number object 1074 o.write("\tif '"+name+"' in params:\n") 1075 o.write('\t\tbvList.append(binary.float2bitvec(params[\''+name+'\']'+'))\n') 1076 o.write('\telse:\n') 1077 o.write('\t\tbvList.append(binary.float2bitvec('+str(unavailable)+'))\n') 1078 1079 if verbose: o.write('\n')
1080 1081
1082 -def encodeAisstr6(o,name,fieldType,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
1083 ''' 1084 Build the encoder for aisstr6 variables. Generally are arrays. 1085 @bug: do we need to optionally check for a valid string? 1086 1087 @type o: file like obj 1088 @param o: where write the code 1089 @type name: str 1090 @param name: field name 1091 @type fieldType: str 1092 @param fieldType: uint, bool, etc. 1093 @type numbits: int >= 1 1094 @param numbits: How many bits per unit datum (must be 1..32) 1095 @type required: bool or None 1096 @param required: If not None, then the value must be set to this. 1097 @type arraylen: int >= 1 1098 @param arraylen: many unsigned ints will there be? FIX: handle variable 1099 @type unavailable: bool or None 1100 @param unavailable: the default value to use if none given (if not None) 1101 @return: None 1102 ''' 1103 if verbose: print ' encodeUInt:',name,fieldType,numbits,'Req:',required,'alen:',arraylen,unavailable 1104 totLen = str(numbits*arraylen) 1105 assert numbits==6 # Each character must be 6 bits 1106 if verbose: o.write('\t### FIELD: '+name+' (type='+fieldType+')\n') 1107 1108 if None != required: 1109 if verbose: print ' required:',required 1110 required=int(required) 1111 o.write('\tbvList.append(aisstring.encode(\''+str(required)+'\','+totLen+'))\n') 1112 if verbose: o.write('\n') 1113 return 1114 1115 if None==unavailable: 1116 o.write('\tbvList.append(aisstring.encode(params[\''+name+'\'],'+totLen+'))\n') 1117 else: # Have a default value that can be filled in 1118 o.write("\tif '"+name+"' in params:\n") 1119 o.write('\t\tbvList.append(aisstring.encode(params[\''+name+'\'],'+totLen+'))\n') 1120 o.write('\telse:\n') 1121 o.write('\t\tbvList.append(aisstring.encode(\''+str(unavailable)+'\','+totLen+'))\n') 1122 1123 if verbose: o.write('\n')
1124 1125
1126 -def encodeInt(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
1127 ''' 1128 Build the encoder for signed integer variables 1129 1130 @type o: file like obj 1131 @param o: where write the code 1132 @type name: str 1133 @param name: field name 1134 @type type: str 1135 @param type: uint, bool, etc. 1136 @type numbits: int >= 1 1137 @param numbits: How many bits per unit datum (must be 1..32) 1138 @type required: bool or None 1139 @param required: If not None, then the value must be set to this. 1140 @type arraylen: int >= 1 1141 @param arraylen: many signed ints will there be? FIX: handle variable 1142 @type unavailable: number or None 1143 @param unavailable: the default value to use if none given (if not None) 1144 @return: None 1145 ''' 1146 if verbose: print ' encodeInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 1147 1148 assert numbits>=1 and numbits<=32 1149 if arraylen != 1: assert False # FIX... handle arrays 1150 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 1151 1152 if None != required: 1153 if verbose: print ' required:',required 1154 required=int(required) 1155 o.write('\tbvList.append(binary.bvFromSignedInt('+str(required)+','+str(numbits)+'))\n') 1156 if verbose: o.write('\n') 1157 return 1158 1159 1160 if None==unavailable: 1161 o.write('\tbvList.append(binary.bvFromSignedInt(params[\''+name+'\'],'+str(numbits)+'))\n') 1162 else: # Have a default value that can be filled in 1163 #assert type(unavailable)== 1164 int(unavailable) # Make sure unavailable is a number object 1165 o.write("\tif '"+name+"' in params:\n") 1166 o.write('\t\tbvList.append(binary.bvFromSignedInt(params[\''+name+'\']'+','+str(numbits)+'))\n') 1167 o.write('\telse:\n') 1168 o.write('\t\tbvList.append(binary.bvFromSignedInt('+str(unavailable)+','+str(numbits)+'))\n') 1169 1170 if verbose: o.write('\n')
1171 1172 1173 1174 # FIX: Ummm... why am I passing the type? I guess it makes the one print statement easier
1175 -def encodeDecimal(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
1176 ''' 1177 Build the encoder for signed 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: decimal 1185 @type numbits: int >= 1 1186 @param numbits: How many bits per unit datum (must be 1..32) 1187 @type required: bool 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 decimals 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 @return: None 1194 ''' 1195 if verbose: print ' encodeDecimal:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 1196 1197 assert numbits>=1 and numbits<=32 1198 if arraylen != 1: assert False # FIX... handle arrays 1199 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 1200 1201 # FIX: optimize to not emit the scaling when it is not needed... or tell the user to switch to an int! 1202 if None == scale: 1203 print 'WARNING: if you are not scaling, then you probably want to use an int instead!' 1204 print 'Beware canadians bearing travel videos' 1205 scale='1' 1206 1207 if None != required: 1208 if verbose: print ' required:',required 1209 required=int(required) 1210 o.write('\tbvList.append(binary.bvFromSignedInt('+str(int(Decimal(required)*Decimal(scale)))+','+str(numbits)+'))\n') 1211 if verbose: o.write('\n') 1212 return 1213 1214 # FIX: can I get rid of the Decimal around params? 1215 if None==unavailable: 1216 o.write('\tbvList.append(binary.bvFromSignedInt(int(Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')),'+str(numbits)+'))\n') 1217 else: # Have a default value that can be filled in 1218 o.write("\tif '"+name+"' in params:\n") 1219 o.write('\t\tbvList.append(binary.bvFromSignedInt(int(Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')),'+str(numbits)+'))\n') 1220 o.write('\telse:\n') 1221 o.write('\t\tbvList.append(binary.bvFromSignedInt('+str(int(Decimal(unavailable)*Decimal(scale)))+','+str(numbits)+'))\n') 1222 1223 if verbose: o.write('\n')
1224 1225 1226
1227 -def encodeUDecimal(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
1228 ''' 1229 Build the encoder for signed decimal variables 1230 1231 @type o: file like obj 1232 @param o: where write the code 1233 @type name: str 1234 @param name: field name 1235 @type type: str 1236 @param type: decimal 1237 @type numbits: int >= 1 1238 @param numbits: How many bits per unit datum (must be 1..32) 1239 @type required: bool or None 1240 @param required: If not None, then the value must be set to this. 1241 @type arraylen: int >= 1 1242 @param arraylen: many decimals will there be? FIX: handle variable 1243 @type unavailable: Decimal or None 1244 @param unavailable: the default value to use if none given (if not None) 1245 @return: None 1246 ''' 1247 if verbose: print ' encodeDecimal:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 1248 assert type=='udecimal' 1249 assert numbits>=1 and numbits<=32 1250 if arraylen != 1: assert False # FIX... handle arrays 1251 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 1252 1253 # FIX: optimize to not emit the scaling when it is not needed... or tell the user to switch to an int! 1254 if None == scale: 1255 print 'WARNING: if you are not scaling, then you probably want to use an int instead!' 1256 print 'Beware canadians bearing travel videos' 1257 scale='1' 1258 1259 if None != required: 1260 if verbose: print ' required:',required 1261 required=int(required) 1262 assert(0<=required) 1263 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(int(Decimal(required)*Decimal(scale)))+'),'+str(numbits)+'))\n') 1264 if verbose: o.write('\n') 1265 return 1266 1267 # FIX: can I get rid of the Decimal around params? 1268 if None==unavailable: 1269 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')))),'+str(numbits)+'))\n') 1270 else: # Have a default value that can be filled in 1271 o.write("\tif '"+name+"' in params:\n") 1272 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')))),'+str(numbits)+'))\n') 1273 o.write('\telse:\n') 1274 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int('+str(int(Decimal(unavailable)*Decimal(scale)))+')),'+str(numbits)+'))\n') 1275 1276 if verbose: o.write('\n')
1277 1278 1279
1280 -def encodeBinary(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
1281 ''' 1282 Build the encoder for binary variables. This is just a pass through. 1283 This is used for the ais binary message wrapper (e.g. msg 8). Do 1284 not use it within binary messages. 1285 1286 @type o: file like obj 1287 @param o: where write the code 1288 @type name: str 1289 @param name: field name 1290 @type type: str 1291 @param type: binary 1292 @type numbits: int >= 1 1293 @param numbits: How many bits per unit datum (must be 1..1024 or so) 1294 @type required: bool or None 1295 @param required: If not None, then the value must be set to this. 1296 @type arraylen: int >= 1 1297 @param arraylen: many decimals will there be? FIX: handle variable 1298 @type unavailable: Decimal or None 1299 @param unavailable: the default value to use if none given (if not None) 1300 @return: None 1301 ''' 1302 if verbose: print ' encode'+name+':',type,numbits,'Req:',required,'alen:',arraylen,unavailable 1303 assert type=='binary' 1304 assert (numbits>=1 and numbits<=1024) or numbits==-1 1305 assert (None == required) # don't allow this 1306 assert (None == unavailable) # don't allow this 1307 1308 if arraylen != 1: assert False # Do not handle arrays. Arrays of bits is just not necessary. 1309 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 1310 1311 # FIX: can I get rid of the Decimal around params? 1312 o.write('\tbvList.append(params[\''+name+'\'])\n') # Just pass it through 1313 1314 if verbose: o.write('\n')
1315 1316 1317 ###################################################################### 1318 # DECODERS 1319 ###################################################################### 1320
1321 -def decodeBool(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1322 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
1323 ''' 1324 Build the decoder for boolean variables 1325 1326 @type o: file like obj 1327 @param o: where write the code 1328 @type name: str 1329 @param name: field name 1330 @type type: str 1331 @param type: uint, bool, etc. 1332 @type startindex: int 1333 @param startindex: bit that begins the bool(s) 1334 @type numbits: int = 1 1335 @param numbits: How many bits per unit datum (must be 1 for bools) 1336 @type required: bool or None 1337 @param required: If not None, then the value must be set to this. 1338 @type arraylen: int >= 1 1339 @param arraylen: many bools will there be? FIX: handle variable 1340 @type unavailable: bool or None 1341 @param unavailable: the default value to use if none given (if not None) 1342 @type bv: str 1343 @param bv: BitVector containing the incoming data 1344 @type dataDict: str 1345 @param dataDict: dictionary in which to place the results 1346 @type decodeOnly: bool 1347 @param decodeOnly: Set to true to only get the code for decoding 1348 @rtype: int 1349 @return: index one past the end of where this read 1350 ''' 1351 assert(type=='bool') 1352 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1353 #int(startindex); int(numbits) # Make sure it is a number 1354 assert numbits==1 1355 assert arraylen == 1 # FIX... handle arrays 1356 1357 if None != required: 1358 assert type(required)==bool 1359 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1360 if required: o.write('True\n') 1361 else: o.write('False\n') 1362 if not decodeOnly: o.write('\n') 1363 return int(startindex)+int(numbits) 1364 1365 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1366 o.write('bool(int('+bv+'['+str(startindex)+':'+str(startindex+int(numbits)*int(arraylen))+']))') 1367 if not decodeOnly: o.write('\n') 1368 1369 return int(startindex)+int(numbits)
1370 1371
1372 -def decodeUInt(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1373 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
1374 ''' 1375 Build the decoder for unsigned integer variables 1376 1377 @type o: file like obj 1378 @param o: where write the code 1379 @type name: str 1380 @param name: field name 1381 @type type: str 1382 @param type: uint, etc. 1383 @type startindex: int 1384 @param startindex: bit that begins the uint(s) 1385 @type numbits: int >= 1 1386 @param numbits: How many bits per unit datum 1387 @type required: int or None 1388 @param required: If not None, then the value must be set to this. 1389 @type arraylen: int >= 1 1390 @param arraylen: many ints will there be? FIX: handle variable 1391 @type unavailable: int or None 1392 @param unavailable: the default value to use if none given (if not None) 1393 @type bv: str 1394 @param bv: BitVector containing the incoming data 1395 @type dataDict: str 1396 @param dataDict: dictionary in which to place the results 1397 @type decodeOnly: bool 1398 @param decodeOnly: Set to true to only get the code for decoding 1399 @rtype: int 1400 @return: index one past the end of where this read 1401 ''' 1402 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1403 if None==arraylen: arraylen=1 1404 assert arraylen == 1 # FIX... handle arrays 1405 assert numbits>=1 1406 if not decodeOnly: verbose=False 1407 1408 if None != required: 1409 int(required) # Make sure required is a number 1410 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1411 o.write(str(required)) 1412 if not decodeOnly: o.write('\n') 1413 return startindex+numbits 1414 1415 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1416 o.write('int('+bv+'['+str(startindex)+':'+str(startindex+int(numbits)*int(arraylen))+'])') 1417 if not decodeOnly: o.write('\n') 1418 if verbose: o.write('\n') 1419 1420 return startindex+numbits
1421 1422
1423 -def decodeInt(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1424 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
1425 ''' 1426 Build the decoder for unsigned integer variables 1427 1428 @type o: file like obj 1429 @param o: where write the code 1430 @type name: str 1431 @param name: field name 1432 @type type: str 1433 @param type: int 1434 @type startindex: int 1435 @param startindex: bit that begins the int(s) 1436 @type numbits: int >= 1 1437 @param numbits: How many bits per unit datum 1438 @type required: int or None 1439 @param required: If not None, then the value must be set to this. 1440 @type arraylen: int >= 1 1441 @param arraylen: many ints will there be? FIX: handle variable 1442 @type unavailable: int or None 1443 @param unavailable: the default value to use if none given (if not None) 1444 @type bv: str 1445 @param bv: BitVector containing the incoming data 1446 @type dataDict: str 1447 @param dataDict: dictionary in which to place the results 1448 @type decodeOnly: bool 1449 @param decodeOnly: Set to true to only get the code for decoding 1450 @rtype: int 1451 @return: index one past the end of where this read 1452 ''' 1453 assert type=='int' 1454 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1455 if None==arraylen: arraylen=1 1456 end = startindex+int(numbits)*int(arraylen) 1457 assert arraylen == 1 # FIX... handle arrays 1458 assert numbits>=1 1459 1460 if None != required: 1461 int(required) # Make sure required is a number 1462 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1463 o.write(str(required)) 1464 if not decodeOnly: o.write('\n') 1465 return end 1466 1467 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1468 o.write('binary.signedIntFromBV('+bv+'['+str(startindex)+':'+str(end)+'])') 1469 if not decodeOnly: o.write('\n') 1470 if verbose: o.write('\n') 1471 1472 return end
1473
1474 -def decodeFloat(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1475 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
1476 ''' 1477 Build the decoder for IEEE float variables 1478 1479 @type o: file like obj 1480 @param o: where write the code 1481 @type name: str 1482 @param name: field name 1483 @type type: str 1484 @param type: int 1485 @type startindex: int 1486 @param startindex: bit that begins the int(s) 1487 @type numbits: int >= 1 1488 @param numbits: How many bits per unit datum 1489 @type required: float or None 1490 @param required: If not None, then the value must be set to this. 1491 @type arraylen: int >= 1 1492 @param arraylen: many ints will there be? FIX: handle variable 1493 @type unavailable: float or None 1494 @param unavailable: the default value to use if none given (if not None) 1495 @type bv: str 1496 @param bv: BitVector containing the incoming data 1497 @type dataDict: str 1498 @param dataDict: dictionary in which to place the results 1499 @type decodeOnly: bool 1500 @param decodeOnly: Set to true to only get the code for decoding 1501 @rtype: int 1502 @return: index one past the end of where this read 1503 ''' 1504 assert type=='float' 1505 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1506 if None==arraylen: arraylen=1 1507 end = startindex+int(numbits)*int(arraylen) 1508 assert arraylen == 1 # FIX... handle arrays 1509 assert numbits>=1 1510 1511 if None != required: 1512 float(required) # Make sure required is a number 1513 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1514 o.write(str(required)) 1515 if not decodeOnly: o.write('\n') 1516 if verbose: o.write('\n') 1517 return end 1518 1519 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1520 o.write('binary.bitvec2float('+bv+'['+str(startindex)+':'+str(end)+'])') 1521 if not decodeOnly: o.write('\n') 1522 1523 return end
1524 1525
1526 -def decodeAisstr6(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1527 bv='bv',dataDict='r',verbose=False, decodeOnly=False):
1528 ''' 1529 Build the decoder for aisstr6 variables. Generally arrays. 1530 @bug: FIX: validate strings?? 1531 @type o: file like obj 1532 @param o: where write the code 1533 @type name: str 1534 @param name: field name 1535 @type type: str 1536 @param type: 'aisstr6' 1537 @type startindex: int 1538 @param startindex: bit that begins the int(s) 1539 @type numbits: int >= 1 1540 @param numbits: How many bits per unit datum 1541 @type required: restricted str or None 1542 @param required: If not None, then the value must be set to this. 1543 @type arraylen: int >= 1 1544 @param arraylen: many ints will there be? FIX: handle variable 1545 @type unavailable: restricted str or None 1546 @param unavailable: the default value to use if none given (if not None) 1547 @type bv: str 1548 @param bv: BitVector containing the incoming data 1549 @type dataDict: str 1550 @param dataDict: dictionary in which to place the results 1551 @type decodeOnly: bool 1552 @param decodeOnly: Set to true to only get the code for decoding 1553 @rtype: int 1554 @return: index one past the end of where this read 1555 ''' 1556 assert type=='aisstr6' 1557 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1558 if None==arraylen: arraylen=1 1559 end = startindex+int(numbits)*int(arraylen) 1560 assert arraylen >= 1 # FIX... handle arrays 1561 assert numbits>=1 1562 1563 if None != required: 1564 float(required) # Make sure required is a number 1565 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1566 o.write(required) 1567 if not decodeOnly: o.write('\n') 1568 return end 1569 1570 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1571 o.write('aisstring.decode('+bv+'['+str(startindex)+':'+str(end)+'])') 1572 if not decodeOnly: o.write('\n') 1573 1574 return end
1575 1576
1577 -def decodeDecimal(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1578 bv='bv',dataDict='r',verbose=False,scale=None, decodeOnly=False):
1579 ''' 1580 Build the decoder for signed decimal variables 1581 1582 @type o: file like obj 1583 @param o: where write the code 1584 @type name: str 1585 @param name: field name 1586 @type type: str 1587 @param type: 'decimal' 1588 @type startindex: int 1589 @param startindex: bit that begins the int(s) 1590 @type numbits: int >= 1 1591 @param numbits: How many bits per unit datum 1592 @type required: Decimal or None 1593 @param required: If not None, then the value must be set to this. 1594 @type arraylen: int >= 1 1595 @param arraylen: many ints will there be? FIX: handle variable 1596 @type unavailable: Decimal or None 1597 @param unavailable: the default value to use if none given (if not None) 1598 @type bv: str 1599 @param bv: BitVector containing the incoming data 1600 @type dataDict: str 1601 @param dataDict: dictionary in which to place the results 1602 @type decodeOnly: bool 1603 @param decodeOnly: Set to true to only get the code for decoding 1604 @rtype: int 1605 @return: index one past the end of where this read 1606 ''' 1607 assert type=='decimal' 1608 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1609 if None==arraylen: arraylen=1 1610 end = startindex+int(numbits)*int(arraylen) 1611 assert arraylen == 1 # FIX... handle arrays 1612 assert numbits>=1 and numbits <= 32 1613 1614 if None == scale: scale='1' # Warning about this was in the encode section 1615 1616 if None != required: 1617 Decimal(required) # Make sure required is a number 1618 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1619 o.write(str(Decimal(required))+'/Decimal(\''+scale+'\')') 1620 if not decodeOnly: o.write('\n') 1621 return end 1622 1623 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1624 o.write('Decimal(binary.signedIntFromBV('+bv+'['+str(startindex)+':'+str(end)+']))/Decimal(\''+scale+'\')') 1625 if not decodeOnly: o.write('\n') 1626 1627 return end
1628 1629 1630 1631
1632 -def decodeUDecimal(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1633 bv='bv',dataDict='r',verbose=False,scale=None, decodeOnly=False):
1634 ''' 1635 Build the decoder for unsigned decimal variables 1636 1637 @type o: file like obj 1638 @param o: where write the code 1639 @type name: str 1640 @param name: field name 1641 @type type: str 1642 @param type: 'udecimal' 1643 @type startindex: int 1644 @param startindex: bit that begins the int(s) 1645 @type numbits: int >= 1 1646 @param numbits: How many bits per unit datum 1647 @type required: Decimal or None 1648 @param required: If not None, then the value must be set to this. 1649 @type arraylen: int >= 1 1650 @param arraylen: many ints will there be? FIX: handle variable 1651 @type unavailable: Decimal or None 1652 @param unavailable: the default value to use if none given (if not None) 1653 @type bv: str 1654 @param bv: BitVector containing the incoming data 1655 @type dataDict: str 1656 @param dataDict: dictionary in which to place the results 1657 @type decodeOnly: bool 1658 @param decodeOnly: Set to true to only get the code for decoding 1659 @rtype: int 1660 @return: index one past the end of where this read 1661 ''' 1662 assert type=='udecimal' 1663 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1664 if None==arraylen: arraylen=1 1665 end = startindex+int(numbits)*int(arraylen) 1666 assert arraylen == 1 # FIX... handle arrays 1667 assert numbits>=1 and numbits <= 32 1668 1669 if None == scale: scale='1' # Warning about this was in the encode section 1670 1671 if None != required: 1672 assert (Decimal(required)>=0.) # Make sure required is a number and not negative 1673 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1674 o.write(str(Decimal(required))+'/Decimal(\''+scale+'\')') 1675 if not decodeOnly: o.write('\n') 1676 return end 1677 1678 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1679 o.write('Decimal(int('+bv+'['+str(startindex)+':'+str(end)+']))/Decimal(\''+scale+'\')') 1680 if not decodeOnly: o.write('\n') 1681 1682 return end
1683 1684
1685 -def decodeBinary(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 1686 bv='bv',dataDict='r',verbose=False,scale=None, decodeOnly=False):
1687 ''' 1688 Build the decoder for unsigned decimal variables 1689 1690 @type o: file like obj 1691 @param o: where write the code 1692 @type name: str 1693 @param name: field name 1694 @type type: str 1695 @param type: 'udecimal' 1696 @type startindex: int 1697 @param startindex: bit that begins the int(s) 1698 @type numbits: int >= 1 1699 @param numbits: How many bits per unit datum. If -1, then read to the end of the message 1700 @type required: Decimal or None 1701 @param required: If not None, then the value must be set to this. 1702 @type arraylen: int >= 1 1703 @param arraylen: many ints will there be? FIX: handle variable 1704 @type unavailable: Decimal or None 1705 @param unavailable: the default value to use if none given (if not None) 1706 @type bv: str 1707 @param bv: BitVector containing the incoming data 1708 @type dataDict: str 1709 @param dataDict: dictionary in which to place the results 1710 @type decodeOnly: bool 1711 @param decodeOnly: Set to true to only get the code for decoding 1712 @rtype: int 1713 @return: index one past the end of where this read 1714 ''' 1715 assert type=='binary' 1716 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 1717 if None==arraylen: arraylen=1 1718 end = startindex+int(numbits)*int(arraylen) 1719 assert arraylen == 1 # FIX... handle arrays 1720 assert (numbits>=1 and numbits <= 1024) or -1==numbits # What is good max? 1721 # FIX: assert not required and not an array an not unavailable 1722 1723 1724 if not decodeOnly: o.write('\t'+dataDict+'[\''+name+'\']=') 1725 o.write(bv+'['+str(startindex)+':') 1726 if int(numbits) != -1: o.write(str(end)) # -1 means go to the end of the message 1727 o.write(']') 1728 if not decodeOnly: o.write('\n') 1729 1730 return end
1731 1732 1733 1734 ###################################################################### 1735 # THE REST 1736 ###################################################################### 1737 1738 1739
1740 -def buildTestParamFunc(o,msgET, verbose=False, prefixName=False):
1741 '''Scrape the testvalues to make a basic param 1742 1743 @bug: FIX: make this create a dictionary that sits in the overall namespace and spit out deep copies? 1744 ''' 1745 name = msgET.attrib['name'] 1746 1747 if prefixName: o.write('def '+name+'TestParams():\n') 1748 else: o.write('def testParams():\n') 1749 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") 1750 o.write('\tparams = {}\n') 1751 for field in msgET.xpath('field'): 1752 name = field.attrib['name'] 1753 type = field.attrib['type'] 1754 if verbose: print 'buildTestParamFunc ...',name,type 1755 val = None 1756 if hasSubTag(field,'testvalue') and hasSubTag(field,'required'): 1757 print 'ERROR: can not have both test value and required tags in the same field' 1758 assert(False) 1759 if hasSubTag(field,'testvalue'): 1760 val = field.xpath('testvalue')[0].text 1761 else: 1762 if not hasSubTag(field,'required'): 1763 sys.exit("ERROR: missing required or testvalue for field: "+name) 1764 val = field.xpath('required')[0].text 1765 if verbose: print 'buildTestParamFunc for field '+name+' ...',type,val 1766 o.write('\tparams[\''+name+'\'] = ') 1767 if type=='bool': 1768 if val=='1' or val.lower=='true': val = 'True' 1769 else: val = 'False' 1770 o.write(val) 1771 elif type in ('uint','int','float'): 1772 o.write(val) 1773 elif type in ('decimal','udecimal'): 1774 o.write('Decimal(\''+val+'\')') 1775 1776 elif type in ('aisstr6'): 1777 o.write('\''+val+'\'') 1778 elif type in ('binary'): 1779 o.write('BitVector(bitstring=\''+val+'\')') 1780 else: 1781 print 'ERROR: type not handled ...',type,' (found in the ',name,' field). Time to buy more coffee' 1782 suggestType(name,type) 1783 assert(False) 1784 1785 o.write('\n') 1786 1787 1788 o.write('\n\treturn params\n\n')
1789
1790 -def buildUnitTest(o,msgET, verbose=False, prefixName=False):
1791 ''' 1792 Write the unittests for a message 1793 1794 @param o: open file where resulting code will be written 1795 @param msgET: Element Tree starting at a message node 1796 ''' 1797 assert(msgET.tag=='message') 1798 name = msgET.attrib['name'] 1799 1800 buildTestParamFunc(o,msgET, prefixName=prefixName) 1801 1802 o.write('class Test'+name+'(unittest.TestCase):\n') 1803 o.write("\t'''Use testvalue tag text from each type to build test case the "+name+" message'''\n") 1804 o.write('\tdef testEncodeDecode(self):\n\n') 1805 if prefixName: 1806 o.write('\t\tparams = '+name+'TestParams()\n') 1807 o.write('\t\tbits = '+name+'Encode(params)\n') 1808 o.write('\t\tr = '+name+'Decode(bits)\n\n') 1809 else: 1810 o.write('\t\tparams = testParams()\n') 1811 o.write('\t\tbits = encode(params)\n') 1812 o.write('\t\tr = decode(bits)\n\n') 1813 1814 o.write('\t\t# Check that each parameter came through ok.\n') 1815 for field in msgET.xpath('field'): 1816 name = field.attrib['name'] 1817 type = field.attrib['type'] 1818 if type in ('bool','uint','int','aisstr6','binary'): 1819 o.write('\t\tself.failUnlessEqual(r[\''+name+'\'],params[\''+name+'\'])\n') 1820 else: 1821 # float, decimal, udecimal 1822 # FIX: look up the decimal places if decimal 1823 places = '3' 1824 if hasSubTag(field,'decimalplaces'): places = field.xpath('decimalplaces')[0].text 1825 o.write('\t\tself.failUnlessAlmostEqual(r[\''+name+'\'],params[\''+name+'\'],'+places+')\n')
1826 1827 1828
1829 -def buildEncode(o,msgET, verbose=False, prefixName=False):
1830 ''' 1831 Write the encoder/decoder for a message 1832 1833 http://jaynes.colorado.edu/PythonIdioms.html 1834 1835 @param o: open file where resulting code will be written 1836 @param msgET: Element Tree starting at a message node 1837 @todo: handle ais message 20 optional. very troublesome 1838 ''' 1839 assert(msgET.tag=='message') 1840 name = msgET.attrib['name'] # fix rename this variable to avoid hiding later on by field name 1841 1842 print 'Generating encoder for',name # FIX: verbose? 1843 funcName = 'encode' 1844 if prefixName: funcName = name+'Encode' 1845 o.write('def '+funcName+'(params, validate=False):\n') 1846 1847 ######################################## 1848 # doc string 1849 o.write("\t'''Create a "+name+" binary message payload to pack into an AIS Msg "+name+".\n\n") 1850 o.write('\tFields in params:\n') 1851 for field in msgET.xpath('field'): 1852 if verbose: 1853 print field.tag,field.attrib['name'] 1854 print field.attrib 1855 desc = field[0].text.replace('\n',' ') # get ride of new lines 1856 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc) # give the description 1857 if len(field.xpath("required")) == 1: 1858 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")') 1859 1860 o.write('\n') 1861 o.write('\t@param params: Dictionary of field names/values. Throws a ValueError exception if required is missing\n') 1862 o.write('\t@param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.\n') 1863 1864 o.write("\t@rtype: BitVector\n") 1865 o.write("\t@return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8\n") 1866 o.write("\t@note: The returned bits may not be 6 bit aligned. It is up to you to pad out the bits.\n") 1867 o.write("\t'''\n\n") 1868 1869 ######################################## 1870 # Actually build the code 1871 1872 o.write('\tbvList = []\n') 1873 1874 if verbose: print 'number of fields = ', len(msgET.xpath('field')) 1875 1876 dynamicArrays = False # Set to true when positioning must be calculated 1877 1878 for field in msgET.xpath('field'): 1879 name = field.attrib['name'] 1880 type = field.attrib['type'] 1881 numbits = int(field.attrib['numberofbits']) 1882 required = None; 1883 if hasSubTag(field,'required'): 1884 required = field.xpath('required')[0].text 1885 #print 'required set for',name,'to',required 1886 unavailable=None; 1887 if hasSubTag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 1888 arraylen=1 1889 if 'arraylength' in field.attrib: 1890 arraylen=int(field.attrib['arraylength']) 1891 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 1892 else: 1893 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')' 1894 1895 if type=='bool' : encodeBool (o,name,type,numbits,required,arraylen,unavailable) 1896 elif type=='uint' : encodeUInt (o,name,type,numbits,required,arraylen,unavailable) 1897 elif type=='int' : encodeInt (o,name,type,numbits,required,arraylen,unavailable) 1898 elif type=='float' : encodeFloat (o,name,type,numbits,required,arraylen,unavailable) 1899 elif type=='aisstr6': encodeAisstr6(o,name,type,numbits,required,arraylen,unavailable) 1900 elif type=='decimal': 1901 scale = None 1902 if hasSubTag(field,'scale'): scale = field.xpath('scale')[0].text 1903 encodeDecimal(o,name,type,numbits,required,arraylen,unavailable,scale=scale) 1904 elif type=='udecimal': 1905 scale = None 1906 if hasSubTag(field,'scale'): scale = field.xpath('scale')[0].text 1907 encodeUDecimal(o,name,type,numbits,required,arraylen,unavailable,scale=scale) 1908 elif type=='binary': encodeBinary(o,name,type,numbits,required,arraylen,unavailable) 1909 else: 1910 print 'WARNING: In buildEncode - Unhandled field type for',name,'...',type 1911 suggestType (name,type) 1912 assert False 1913 1914 # o.write('\n\tbv=binary.joinBV(bvList)\n') 1915 # o.write('\n\tbvLen=len(bv)\n') 1916 # o.write('\n\tif bvLen%6!=0:\n') 1917 # o.write('\n\t bv = bv + BitVector(size=bvLen%6) # \n') 1918 # o.write('\n\treturn bv\n\n') 1919 o.write('\n\treturn binary.joinBV(bvList)\n\n')
1920 1921 1922 ###################################################################### 1923 # DECODER ONE PART AT A TIME - if only using a small part, save some cycles! 1924 1925 1926 #, msgDict=None):
1927 -def buildDecodeParts(o,msgET, verbose=False, prefixName=False):
1928 1929 ''' 1930 Write the decoder for a message 1931 1932 @param o: open file where resulting code will be written 1933 @type msgET: elementtree 1934 @param prefixName: if True, put the name of the message on the functions. 1935 @param msgET: Element Tree starting at a message node 1936 @return: None 1937 1938 @todo: FIX: doc strings for each decode! 1939 @todo: FIX: check for a dac,fid, or efid. If exists, then this is an AIS Msg 8 payload 1940 @todo: May want to take a dictionary of already decoded fields to speed things that need prior info 1941 for things like variable length arrays 1942 ''' 1943 1944 assert(msgET.tag=='message') 1945 name = msgET.attrib['name'] 1946 1947 print 'Generating partial decode functions for',name 1948 1949 baseName = name+'Decode' 1950 if not prefixName: baseName = 'decode' 1951 1952 startindex = 0 # Where we are in the bitvector... FIX: what about variable length jobs? 1953 1954 for field in msgET.xpath('field'): 1955 name = field.attrib['name'] 1956 type = field.attrib['type'] 1957 1958 o.write('def '+baseName+name+'(bv, validate=False):\n') 1959 # Follow the same convention of decoding into a dict so that code is the same 1960 #o.write('\tr={};') 1961 o.write('\treturn ') 1962 1963 numbits = int(field.attrib['numberofbits']) 1964 required = None; 1965 if hasSubTag(field,'required'): 1966 required = field.xpath('required')[0].text 1967 unavailable=None; 1968 if hasSubTag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 1969 arraylen=1 1970 if 'arraylength' in field.attrib: 1971 arraylen=int(field.attrib['arraylength']) 1972 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 1973 1974 assert None!=startindex 1975 if verbose: print 'startindex',startindex 1976 if type=='bool' : startindex = decodeBool (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1977 elif type=='uint' : startindex = decodeUInt (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1978 elif type=='int' : startindex = decodeInt (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1979 elif type=='float' : startindex = decodeFloat (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1980 elif type=='aisstr6': startindex = decodeAisstr6(o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1981 elif type=='binary' : startindex = decodeBinary (o,name,type,startindex,numbits,required,arraylen,unavailable,decodeOnly=True) 1982 1983 elif type=='decimal': 1984 scale = None 1985 if hasSubTag(field,'scale'): scale = field.xpath('scale')[0].text 1986 startindex = decodeDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale,decodeOnly=True) 1987 elif type=='udecimal': 1988 scale = None 1989 if hasSubTag(field,'scale'): scale = field.xpath('scale')[0].text 1990 startindex = decodeUDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale,decodeOnly=True) 1991 1992 else: 1993 print 'WARNING: In buildDecode - Unhandled field type for',name,'...',type 1994 suggestType (name,type) 1995 assert False 1996 1997 #o.write('\treturn(r[\''+name+'\'])\n\n') 1998 o.write('\n\n')
1999 2000 2001 ###################################################################### 2002 # DECODER RING 2003
2004 -def buildDecode(o,msgET, verbose=False, prefixName=False):
2005 ''' 2006 Write the decoder for a message 2007 2008 @param o: open file where resulting code will be written 2009 @type msgET: elementtree 2010 @param msgET: Element Tree starting at a message node 2011 @return: None 2012 2013 @todo: FIX: check for a dac,fid, or efid. If exists, then this is an AIS Msg 8 payload 2014 ''' 2015 2016 assert(msgET.tag=='message') 2017 name = msgET.attrib['name'] 2018 2019 print 'Generating decoder for',name 2020 funcName = 'decode' 2021 if prefixName: funcName = name+'Decode' 2022 o.write('def '+funcName+'(bv, validate=False):\n') 2023 2024 ######################################## 2025 # doc string 2026 o.write("\t'''Unpack a "+name+" message \n\n") 2027 o.write('\tFields in params:\n') 2028 for field in msgET.xpath('field'): 2029 desc = field[0].text.replace('\n',' ') # get ride of new lines 2030 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc) # give the description 2031 if len(field.xpath("required")) == 1: 2032 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")') 2033 2034 o.write('\n') 2035 o.write('\t@type bv: BitVector\n') 2036 o.write('\t@param bv: Bits defining a message\n') 2037 o.write('\t@param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.\n') 2038 2039 o.write("\t@rtype: dict\n") 2040 o.write("\t@return: params\n") 2041 o.write("\t'''\n\n") 2042 2043 2044 o.write('\t#Would be nice to check the bit count here..\n') 2045 o.write('\t#if validate:\n') 2046 o.write('\t#\tassert (len(bv)==FIX: SOME NUMBER)\n') 2047 2048 2049 ######################################## 2050 # Actually build the code 2051 2052 o.write('\tr = {}\n') 2053 2054 2055 if verbose: print 'number of fields = ', len(msgET.xpath('field')) 2056 2057 dynamicArrays = False # Set to true when positioning must be calculated 2058 2059 startindex = 0 # Where we are in the bitvector... FIX: what about variable length jobs? 2060 2061 for field in msgET.xpath('field'): 2062 name = field.attrib['name'] 2063 type = field.attrib['type'] 2064 numbits = int(field.attrib['numberofbits']) 2065 required = None; 2066 if hasSubTag(field,'required'): 2067 required = field.xpath('required')[0].text 2068 #print 'required set for',name,'to',required 2069 unavailable=None; 2070 if hasSubTag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 2071 arraylen=1 2072 if 'arraylength' in field.attrib: 2073 arraylen=int(field.attrib['arraylength']) 2074 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 2075 else: 2076 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')' 2077 2078 assert None!=startindex 2079 if verbose: print 'startindex',startindex 2080 2081 if hasSubTag(field,'optional'): 2082 # NoReturn means that this field is not a point that needs a check to see if we are done 2083 # FIX: need to do something more efficient 2084 if msgET.attrib['aismsgnum']!='20': 2085 sys.exit('optional is only allow on AIS msg 20. Do NOT use it on new messages!!!') 2086 text = field.xpath('optional')[0].text 2087 if None==field.xpath('optional')[0].text: # Found a NoReturn 2088 # Message 20 is a pain! 2089 pad = 8-(startindex%8) 2090 print 'not found: noreturn. pad=',pad 2091 assert(pad<=6) 2092 o.write('\tif len(bv)<='+str(startindex+pad)+': return r; # All fields below are optional\n') 2093 elif text != 'NoReturn': 2094 sys.exit ('ERROR: optional text must be NoReturn or empty') 2095 2096 if type=='bool' : startindex = decodeBool (o,name,type,startindex,numbits,required,arraylen,unavailable) 2097 elif type=='uint' : startindex = decodeUInt (o,name,type,startindex,numbits,required,arraylen,unavailable) 2098 elif type=='int' : startindex = decodeInt (o,name,type,startindex,numbits,required,arraylen,unavailable) 2099 elif type=='float' : startindex = decodeFloat (o,name,type,startindex,numbits,required,arraylen,unavailable) 2100 elif type=='aisstr6': startindex = decodeAisstr6(o,name,type,startindex,numbits,required,arraylen,unavailable) 2101 elif type=='binary' : startindex = decodeBinary (o,name,type,startindex,numbits,required,arraylen,unavailable) 2102 2103 elif type=='decimal': 2104 scale = None 2105 if hasSubTag(field,'scale'): scale = field.xpath('scale')[0].text 2106 #print 'pre call...' 2107 #print ' required: "'+str(required)+'" scale: "'+str(scale)+'" unavail:', unavailable 2108 startindex = decodeDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale) 2109 elif type=='udecimal': 2110 scale = None 2111 if hasSubTag(field,'scale'): scale = field.xpath('scale')[0].text 2112 #print 'pre call...' 2113 #print ' required: "'+str(required)+'" scale: "'+str(scale)+'" unavail:', unavailable 2114 startindex = decodeUDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale) 2115 2116 else: 2117 print 'WARNING: In buildDecode - Unhandled field type for',name,'...',type 2118 suggestType (name,type) 2119 assert False 2120 2121 2122 if None==startindex: print 'FIX: here. drat. treat me right' 2123 assert None!=startindex 2124 2125 2126 o.write('\treturn r\n\n')
2127 2128 2129 2130 #def getPythonType(aType): 2131 # ''' 2132 # Translate a 2133 # @rtype: str 2134 # @return: name of the python type 2135 # ''' 2136 2137 aisType2pythonType={ 2138 'bool':'Bool', 2139 'uint':'int', 2140 'int':'int', 2141 'udecimal':'Decimal', 2142 'decimal':'Decimal', 2143 'aisstr6':'str', 2144 'binary':'str', 2145 'float':'Float', 2146 } 2147 2148 import copy 2149 aisType2optParseType=copy.deepcopy(aisType2pythonType) 2150 aisType2optParseType['bool']='int' # FIX: make these appear as a flag variable 2151 aisType2optParseType['aisstr6']='string' 2152 aisType2optParseType['aisstr6']='string' 2153 aisType2optParseType['binary']='string' 2154 aisType2optParseType['udecimal']='string' # FIX: Is this a good choice? 2155 aisType2optParseType['decimal']='string' 2156 aisType2optParseType['float']='float' 2157 2158
2159 -def buildOptParse(o,msgET, prefixName=False):
2160 '''Create a function that adds the options to a parse object''' 2161 2162 assert None != msgET 2163 assert msgET.tag=='message' 2164 msgName = msgET.attrib['name'] 2165 2166 prefix='' 2167 if prefixName: prefix=msgName 2168 2169 funcName = 'addMsgOptions' 2170 if prefixName: funcName = msgName + 'AddMsgOptions' 2171 o.write('\ndef '+funcName+'(parser):') 2172 2173 # FIX: write out a doc string 2174 2175 o.write(''' 2176 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true', 2177 help='decode a "'''+msgName+'''" AIS message') 2178 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true', 2179 help='encode a "'''+msgName+'''" AIS message') 2180 ''') 2181 2182 #print 'here...', msgName, prefixName 2183 for field in msgET.xpath('field'): 2184 name = field.attrib['name'] 2185 fieldType = field.attrib['type'] 2186 if hasSubTag(field,'required'): 2187 print 'skipping required field ...',name,fieldType 2188 continue 2189 #print 'there',name,fieldType 2190 o.write('\tparser.add_option(\'--') 2191 if prefixName: o.write(msgName+'-') 2192 o.write(name+'-field\', dest=\''+name+'Field\'') 2193 if hasSubTag(field,'unavailable'): 2194 val = field.xpath('unavailable')[0].text 2195 o.write(',default=') 2196 if fieldType in ('uint','int','float'): 2197 o.write(val) 2198 elif fieldType in ('decimal','udecimal'): 2199 o.write('Decimal(\''+val+'\')') 2200 elif fieldType in ('aisstr6','bitvector'): 2201 o.write('\''+val+'\'') 2202 o.write(',metavar=\''+fieldType+'\',type=\''+aisType2optParseType[fieldType]+'\'') 2203 o.write('\n\t\t,help=\'Field parameter value [default: %default]\')\n')
2204 2205 2206
2207 -def buildMain(o, msgET, prefixName=False):
2208 assert None != msgET 2209 assert msgET.tag=='message' 2210 msgName = msgET.attrib['name'] 2211 2212 prefix='' 2213 if prefixName: prefix=msgName 2214 2215 buildOptParse(o, msgET, prefixName) 2216 2217 2218 o.write(''' 2219 ############################################################ 2220 if __name__=='__main__': 2221 2222 from optparse import OptionParser 2223 parser = OptionParser(usage="%prog [options]", 2224 version="%prog "+__version__) 2225 2226 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true', 2227 help='run the documentation tests') 2228 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true', 2229 help='run the unit tests') 2230 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 2231 help='Make the test output verbose') 2232 2233 # FIX: remove nmea from binary messages. No way to build the whole packet? 2234 # FIX: or build the surrounding msg 8 for a broadcast? 2235 typeChoices = ('binary','nmeapayload','nmea') # FIX: what about a USCG type message? 2236 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType' 2237 ,default='nmeapayload' 2238 ,help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]') 2239 2240 2241 outputChoices = ('std','html','csv','sql' ''') 2242 # 'xml' - FIX: need to add xml output 2243 if haveLocatableMessage(msgET): o.write(', \'kml\',\'kml-full\'') 2244 o.write(''') 2245 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType' 2246 ,default='std' 2247 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]') 2248 2249 parser.add_option('-o','--output',dest='outputFileName',default=None, 2250 help='Name of the python file to write [default: stdout]') 2251 2252 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append', 2253 choices=fieldList, 2254 help='Which fields to include in the output. Currently only for csv output [default: all]') 2255 2256 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true', 2257 help='Print the field name for csv') 2258 2259 parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true', 2260 help='Print out an sql create command for the table.') 2261 2262 parser.add_option('--latex-table',dest='latexDefinitionTable',default=False,action='store_true', 2263 help='Print a LaTeX table of the type') 2264 2265 dbChoices = ('sqlite','postgres') 2266 parser.add_option('-D','--db-type',dest='dbType',default='postgres' 2267 ,choices=dbChoices,type='choice' 2268 ,help='What kind of database ('+', '.join(dbChoices)+') [default: %default]') 2269 2270 ''') 2271 2272 o.write('''\taddMsgOptions(parser)\n''') 2273 2274 o.write(''' 2275 (options,args) = parser.parse_args() 2276 success=True 2277 2278 if options.doctest: 2279 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 2280 sys.argv= [sys.argv[0]] 2281 if options.verbose: sys.argv.append('-v') 2282 import doctest 2283 numfail,numtests=doctest.testmod() 2284 if numfail==0: print 'ok' 2285 else: 2286 print 'FAILED' 2287 success=False 2288 2289 if not success: sys.exit('Something Failed') 2290 del success # Hide success from epydoc 2291 2292 if options.unittest: 2293 sys.argv = [sys.argv[0]] 2294 if options.verbose: sys.argv.append('-v') 2295 unittest.main() 2296 2297 outfile = sys.stdout 2298 if None!=options.outputFileName: 2299 outfile = file(options.outputFileName,'w') 2300 2301 ''') 2302 2303 2304 2305 ############################## 2306 # encode 2307 ############################## 2308 # FIX: make everything tabs. Grr. 2309 o.write('\n\tif options.doEncode:\n') 2310 o.write('\t\t# First make sure all non required options are specified\n') 2311 for field in msgET.xpath('field'): 2312 name = field.attrib['name'] 2313 fieldType = field.attrib['type'] 2314 varName = prefix+name+'Field' 2315 if not hasSubTag(field,'required'): 2316 o.write('\t\tif None==options.'+varName+': parser.error("missing value for '+varName+'")\n') 2317 2318 # Build dict 2319 o.write('\t\tmsgDict={\n') 2320 for field in msgET.xpath('field'): 2321 name = field.attrib['name'] 2322 varName = prefix+name+'Field' 2323 if hasSubTag(field,'required'): 2324 o.write('\t\t\t\''+name+'\': \''+field.xpath('required')[0].text+'\',\n') 2325 else: 2326 o.write('\t\t\t\''+name+'\': options.'+varName+',\n') 2327 o.write('\t\t}\n') 2328 2329 encodeFunction = 'encode' 2330 if prefixName: encodeFunction = msgName+'Encode' 2331 o.write(''' 2332 bits = '''+encodeFunction+'''(msgDict) 2333 if 'binary'==options.ioType: print str(bits) 2334 elif 'nmeapayload'==options.ioType: 2335 # FIX: figure out if this might be necessary at compile time 2336 print "bitLen",len(bits) 2337 bitLen=len(bits) 2338 if bitLen%6!=0: 2339 bits = bits + BitVector(size=(6 - (bitLen%6))) # Pad out to multiple of 6 2340 print "result:",binary.bitvectoais6(bits)[0] 2341 2342 2343 # FIX: Do not emit this option for the binary message payloads. Does not make sense. 2344 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability") 2345 else: sys.exit('ERROR: unknown ioType. Help!') 2346 ''') 2347 2348 2349 ############################## 2350 # decode all 2351 ############################## 2352 decodeFunction = 'decode' 2353 printFields='printFields' 2354 if prefixName: 2355 decodeFunction = msgName+'Decode' 2356 printFields = msgName+'PrintFields' 2357 o.write(''' 2358 2359 if options.sqlCreate: 2360 sqlCreateStr(outfile,options.fieldList,dbType=options.dbType) 2361 2362 if options.latexDefinitionTable: 2363 latexDefinitionTable(outfile) 2364 2365 if options.printCsvfieldList: 2366 # Make a csv separated list of fields that will be displayed for csv 2367 if None == options.fieldList: options.fieldList = fieldList 2368 import StringIO 2369 buf = StringIO.StringIO() 2370 for field in options.fieldList: 2371 buf.write(field+',') 2372 result = buf.getvalue() 2373 if result[-1] == ',': print result[:-1] 2374 else: print result 2375 2376 if options.doDecode: 2377 for msg in args: 2378 bv = None 2379 2380 if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'): 2381 # Found nmea 2382 # FIX: do checksum 2383 bv = binary.ais6tobitvec(msg.split(',')[5]) 2384 else: # either binary or nmeapayload... expect mostly nmeapayloads 2385 # assumes that an all 0 and 1 string can not be a nmeapayload 2386 binaryMsg=True 2387 for c in msg: 2388 if c not in ('0','1'): 2389 binaryMsg=False 2390 break 2391 if binaryMsg: 2392 bv = BitVector(bitstring=msg) 2393 else: # nmeapayload 2394 bv = binary.ais6tobitvec(msg) 2395 2396 '''+printFields+'''('''+decodeFunction+'''(bv) 2397 ,out=outfile 2398 ,format=options.outputType 2399 ,fieldList=options.fieldList 2400 ,dbType=options.dbType 2401 ) 2402 2403 ''')
2404 2405 2406 ###################################################################### 2407 if __name__=='__main__': 2408 from optparse import OptionParser 2409 parser = OptionParser(usage="%prog [options]", 2410 version="%prog "+__version__) 2411 2412 parser.add_option('-o','--output',dest='outputFileName',default=None, 2413 help='Name of the python file to write') 2414 # help='Name of the python file to write [default: stdout]') 2415 2416 parser.add_option('-i','-x','--xml-definition',dest='xmlFileName',default=None, 2417 help='XML definition file for the msg to use') 2418 # help='XML definition file for the msg to use [default: stdin]') 2419 2420 # parser.add_option('-m','--message',dest='message',default=None, 2421 # help='Which message to write code for [default: all]') 2422 2423 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true', 2424 help='run the documentation tests') 2425 2426 parser.add_option('-p','--prefix',dest='prefix',default=False,action='store_true', 2427 help='put the field name in front of all function names.' 2428 +' Allows multiple messages in one file') 2429 2430 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 2431 help='run the tests run in verbose mode') 2432 2433 (options,args) = parser.parse_args() 2434 2435 success=True 2436 2437 if options.doctest: 2438 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 2439 argvOrig = sys.argv 2440 sys.argv= [sys.argv[0]] 2441 if options.verbose: sys.argv.append('-v') 2442 import doctest 2443 numfail,numtests=doctest.testmod() 2444 if numfail==0: print 'ok' 2445 else: 2446 print 'FAILED' 2447 success=False 2448 sys.argv = argvOrig # Restore the original args 2449 del argvOrig # hide from epydoc 2450 sys.exit() # FIX: Will this exit success? 2451 2452 #infile=sys.stdin 2453 #if options.xmlFileName: infile = file(options.xmlFileName,'r') 2454 2455 #outfile=sys.stdout 2456 #if options.outputFile: outfile = file(options.xmlDefinition,'w') 2457 2458 # FIX: down the road, it would be good to allow either filenames of std{in/out} 2459 2460 if None==options.xmlFileName: 2461 sys.exit('ERROR: must specify an xml definition file.') 2462 if None==options.outputFileName: 2463 sys.exit('ERROR: must specify an python file to write to.') 2464 generatePython(options.xmlFileName,options.outputFileName,prefixName=options.prefix, verbose=options.verbose) 2465 2466 print '\nrecommend running pychecker like this:' 2467 print ' pychecker -q',options.outputFileName 2468