Module aisxmlbinmsg2py
[hide private]
[frames] | no frames]

Source Code for Module 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>} 
  22   
  23  @author: U{'''+__author__+'''<http://xenon.stanford.edu/~schwehr/>} 
  24  @version: ''' + __version__ +''' 
  25  @copyright: 2006 
  26  @var __date__: Date of last svn commit 
  27  @undocumented: __version__ __author__ __doc__ myparser 
  28  @since: 2006-Sep-24 
  29  @status: under development 
  30  @organization: U{CCOM<http://ccom.unh.edu/>} 
  31  @license: Restricted while in development to NOAA and USCG. 
  32   
  33  @todo: add a link to generated doc string to bring up the html for the pretty version 
  34   
  35  @bug: NOT complete 
  36  ''' 
  37   
  38  import sys, os 
  39  from decimal import Decimal 
  40  from lxml import etree  
  41   
42 -def suggestType(name,curType,printout=True):
43 ''' 44 Try to suggest a type name if one did not work. 45 46 @param printout: if true, write a suggestion to stdout. 47 48 >>> suggestType('myFieldName','unsigned int') 49 Recommend switching "unsigned int" to "uint" for field "myFieldName" 50 'uint' 51 52 >>> suggestType('JohnWarfon','yoyodyne') 53 Sorry! No recommendation available for bad type "yoyodyne" for field "JohnWarfon" 54 ''' 55 newType = None 56 if curType.lower()=='unsigned int': 57 newType = 'uint' 58 elif curType.lower()=='unsigned decimal': 59 newType = 'udecimal' 60 61 if printout: 62 if None != newType: 63 print 'Recommend switching "'+curType+'" to "'+newType+'" for field "'+name+'"' 64 else: 65 print 'Sorry! No recommendation available for bad type "'+curType+'" for field "'+name+'"' 66 return newType
67 68
69 -def hasSubtag(et,subtag):
70 ''' 71 @return: true if the tag a sub tag with name subtag 72 ''' 73 if 0<len(et.xpath(subtag)): return True 74 return False
75 76 77 #def writeBeginning(o,aisBinMsgET):
78 -def writeBeginning(o):
79 ''' 80 Write the doc string header for the message file 81 82 param o: Open output file to write code to. 83 param msgET: element tree for the ais message definition. 84 Must be pre-expanded with the expandais.py command. 85 ''' 86 import datetime 87 d = datetime.datetime.utcnow() 88 dateStr = str(d.year)+'-'+("%02d" %d.month)+'-'+("%02d"%d.day) 89 90 # FIX: what to do for __version__, @since, etc? 91 # Need to pass in info about the source file, etc. 92 93 # Minor trickery to get svn to ignore the keywords in the next few lines 94 o.write('''#!/usr/bin/env python 95 96 __version__ = '$Revision: 4791 $'.split()[1] 97 __date__ = '$Da'''+'''te: '''+dateStr+''' $'.split()[1] 98 __author__ = 'xmlbinmsg' 99 100 __doc__=\'\'\' 101 102 Autogenerated python functions to serialize/deserialize binary messages. 103 104 Generated by: '''+__file__+''' 105 106 Need to then wrap these functions with the outer AIS packet and then 107 convert the whole binary blob to a NMEA string. Those functions are 108 not currently provided in this file. 109 110 serialize: python to ais binary 111 deserialize: ais binary to python 112 113 The generated code uses translators.py, binary.py, and aisstring.py 114 which should be packaged with the resulting files. 115 116 ''') 117 118 o.write(''' 119 @requires: U{epydoc<http://epydoc.sourceforge.net/>} > 3.0alpha3 120 @requires: U{BitVector<http://cheeseshop.python.org/pypi/BitVector>} 121 122 @author: \'\'\'+__author__+\'\'\' 123 @version: \'\'\' + __version__ +\'\'\' 124 @var __date__: Date of last svn commit 125 @undocumented: __version__ __author__ __doc__ myparser 126 @status: under development 127 @license: Generated code has no license 128 \'\'\' 129 130 import sys 131 from decimal import Decimal 132 from BitVector import BitVector 133 134 import binary, aisstring 135 136 TrueBV = BitVector(bitstring="1") 137 "Why always rebuild the True bit? This should speed things up a bunch" 138 FalseBV = BitVector(bitstring="0") 139 "Why always rebuild the False bit? This should speed things up a bunch" 140 141 142 ''') 143 return
144
145 -def generatePython(infile,outfile):
146 ''' 147 @param infile: xml ais binary message definition file 148 @param outfile: where to dump the python code 149 ''' 150 151 aisMsgsET = etree.parse(infile).getroot() 152 153 o = file(outfile,'w') 154 os.chmod(outfile,0755) 155 156 writeBeginning(o) 157 158 for msgET in aisMsgsET: 159 if msgET.tag != 'message': continue 160 print msgET.tag, msgET.attrib['name'] 161 162 if len(msgET.xpath('include-struct')) > 0: 163 sys.exit("ERROR: cannot handle xml that still has include-struct tags.\n Please use expandais.py.") 164 165 buildEncode(o,msgET) 166 buildDecode(o,msgET) 167 buildPrint(o,msgET) 168 169 o.write('\n\n######################################################################\n') 170 o.write('# UNIT TESTING\n') 171 o.write('######################################################################\n') 172 o.write('import unittest\n') 173 174 for msgET in aisMsgsET: 175 if msgET.tag != 'message': continue 176 print 'Building unit tests for message ...', msgET.attrib['name'] 177 178 buildUnitTest(o,msgET) 179 180 buildMain(o) 181 return
182 183 ###################################################################### 184 # SIMPLE PRINT 185 ###################################################################### 186
187 -def getMaxFieldNameLen(msgET):
188 '''Get the maximum string length of any field name''' 189 maxStrLen=0 190 for field in msgET.xpath('field'): 191 fieldLen = len(field.attrib['name']) 192 if fieldLen>maxStrLen: maxStrLen = fieldLen 193 return maxStrLen
194
195 -def padStrRight(aStr,strlen):
196 '''Pad a string out to the length requested with spaces out to the right''' 197 return aStr + ' '*(strlen-len(aStr))
198
199 -def buildPrint(o,msgET, verbose=False):
200 ''' 201 Write a simple in order print for the resulting dictionary. 202 203 param o: open file where resulting code will be written 204 param msgET: Element Tree starting at a message node 205 ''' 206 assert(msgET.tag=='message') 207 name = msgET.attrib['name'] 208 209 print 'Generating print for',name # FIX: verbose? 210 funcName = name+'Print' 211 o.write('def '+funcName+'(params, out=sys.stdout):\n') 212 213 ######################################## 214 # doc string 215 o.write("\t'''Print a "+name+" message to stdout.\n\n") 216 o.write('\tFields in params:\n') 217 for field in msgET.xpath('field'): 218 desc = field[0].text.replace('\n',' ') # get ride of new lines 219 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc) # give the description 220 if len(field.xpath("required")) == 1: 221 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")') 222 223 o.write('\n') 224 o.write('\t@param params: Dictionary of field names/values. \n') 225 o.write('\t@param out: File like object to write to\n') 226 227 o.write("\t@rtype: stdout\n") 228 o.write("\t@return: text to out\n") 229 o.write("\t'''\n\n") 230 231 ######################################## 232 # Actually build the code 233 234 o.write('\tout.write("'+name+':\\n")\n') 235 236 if verbose: print 'number of fields = ', len(msgET.xpath('field')) 237 238 # +1 for the ':' 239 maxFieldLen = 1 + getMaxFieldNameLen(msgET) 240 241 242 for field in msgET.xpath('field'): 243 name = field.attrib['name'] 244 type = field.attrib['type'] 245 numbits = int(field.attrib['numberofbits']) 246 required = None; 247 if hasSubtag(field,'required'): 248 required = field.xpath('required')[0].text 249 #print 'required set for',name,'to',required 250 unavailable=None; 251 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 252 arraylen=1 253 if 'arraylength' in field.attrib: 254 arraylen=int(field.attrib['arraylength']) 255 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 256 else: 257 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')' 258 259 if 1==arraylen: 260 o.write('\tout.write("\t'+padStrRight(name+':',maxFieldLen)+' "+str(+params[\''+name+'\'])+"\\n")\n') 261 else: 262 print 'FIX: handle arrays in the buildPrint func' 263 264 o.write('\n\treturn # Nothing to return\n\n')
265 266 ###################################################################### 267 # ENCODERS 268 ######################################################################
269 -def encodeBool(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
270 ''' 271 Build the encoder for boolean variables 272 @type o: file like obj 273 @param o: where write the code 274 @type name: str 275 @param name: field name 276 @type type: str 277 @param type: bool, etc. 278 @type numbits: int = 1 279 @param numbits: How many bits per unit datum (must be 1 for bools) 280 @type required: bool or None 281 @param required: If not None, then the value must be set to this. 282 @type arraylen: int >= 1 283 @param arraylen: many bools will there be? FIX: handle variable 284 @type unavailable: bool or None 285 @param unavailable: the default value to use if none given (if not None) 286 @return: None 287 ''' 288 289 if verbose: print 'bool encode',name,': unvail=',unavailable 290 291 assert type.lower()=='bool' 292 assert numbits==1 293 if arraylen != 1: assert False # FIX... handle arrays 294 if verbose: o.write('\t### FIELD: '+name+' (type=bool)\n') 295 if None != required: 296 assert type(required)==bool 297 if required: o.write('\t\tbvList.append(TrueBV)\n') 298 else: o.write('\t\tbvList.append(FalseBV)\n') 299 if verbose: o.write('\n') 300 return 301 302 if None==unavailable: 303 o.write('\tif params["'+name+'"]: bvList.append(TrueBV)\n') 304 o.write('\telse: bvList.append(FalseBV)\n') 305 else: # Have a default value that can be filled in 306 assert type(unavailable)==bool 307 o.write("\tif '"+name+"' in params:\n") 308 o.write('\t\tif params["'+name+'"]: bvList.append(TrueBV)\n') 309 o.write('\t\telse: bvList.append(FalseBV)\n') 310 o.write('\telse:\n') 311 if unavailable: o.write('\t\tbvList.append(TrueBV)\n') 312 else: o.write('\t\tbvList.append(FalseBV)\n') 313 if verbose: o.write('\n')
314
315 -def encodeUInt(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
316 ''' 317 Build the encoder for unsigned integer variables 318 319 @type o: file like obj 320 @param o: where write the code 321 @type name: str 322 @param name: field name 323 @type type: str 324 @param type: uint, bool, etc. 325 @type numbits: int >= 1 326 @param numbits: How many bits per unit datum (must be 1..32) 327 @type required: bool or None 328 @param required: If not None, then the value must be set to this. 329 @type arraylen: int >= 1 330 @param arraylen: many unsigned ints will there be? FIX: handle variable 331 @type unavailable: bool or None 332 @param unavailable: the default value to use if none given (if not None) 333 @return: None 334 ''' 335 if verbose: print ' encodeUInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 336 337 assert type=='uint' 338 assert numbits>=1 and numbits<=32 339 if arraylen != 1: assert False # FIX... handle arrays 340 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 341 342 if None != required: 343 if verbose: print ' required:',required 344 required=int(required) 345 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(required)+'),'+str(numbits)+'))\n') 346 if verbose: o.write('\n') 347 return 348 349 if None==unavailable: 350 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal=params[\''+name+'\']),'+str(numbits)+'))\n') 351 else: # Have a default value that can be filled in 352 #assert type(unavailable)== 353 int(unavailable) # Make sure unavailable is a number object 354 o.write("\tif '"+name+"' in params:\n") 355 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=params[\''+name+'\']'+'),'+str(numbits)+'))\n') 356 o.write('\telse:\n') 357 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(unavailable)+'),'+str(numbits)+'))\n') 358 359 if verbose: o.write('\n')
360 361
362 -def encodeFloat(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
363 ''' 364 Build the encoder for IEEE float variables 365 366 @type o: file like obj 367 @param o: where write the code 368 @type name: str 369 @param name: field name 370 @type type: str 371 @param type: uint, bool, etc. 372 @type numbits: int >= 1 373 @param numbits: How many bits per unit datum (must be 1..32) 374 @type required: bool or None 375 @param required: If not None, then the value must be set to this. 376 @type arraylen: int >= 1 377 @param arraylen: many unsigned ints will there be? FIX: handle variable 378 @type unavailable: bool or None 379 @param unavailable: the default value to use if none given (if not None) 380 @return: None 381 ''' 382 if verbose: print ' encodeUInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 383 384 assert numbits==32 # Force by the IEEE spec 385 if arraylen != 1: assert False # FIX... handle arrays 386 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 387 388 if None != required: 389 if verbose: print ' required:',required 390 required=int(required) 391 o.write('\tbvList.append(binary.float2bitvec('+str(required)+'))\n') 392 if verbose: o.write('\n') 393 return 394 395 if None==unavailable: 396 o.write('\tbvList.append(binary.float2bitvec(params[\''+name+'\']))\n') 397 else: # Have a default value that can be filled in 398 #assert type(unavailable)== 399 int(unavailable) # Make sure unavailable is a number object 400 o.write("\tif '"+name+"' in params:\n") 401 o.write('\t\tbvList.append(binary.float2bitvec(params[\''+name+'\']'+'))\n') 402 o.write('\telse:\n') 403 o.write('\t\tbvList.append(binary.float2bitvec('+str(unavailable)+'))\n') 404 405 if verbose: o.write('\n')
406 407
408 -def encodeAisstr6(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
409 ''' 410 Build the encoder for aisstr6 variables. Generally are arrays. 411 @bug: do we need to optionally check for a valid string? 412 413 @type o: file like obj 414 @param o: where write the code 415 @type name: str 416 @param name: field name 417 @type type: str 418 @param type: uint, bool, etc. 419 @type numbits: int >= 1 420 @param numbits: How many bits per unit datum (must be 1..32) 421 @type required: bool or None 422 @param required: If not None, then the value must be set to this. 423 @type arraylen: int >= 1 424 @param arraylen: many unsigned ints will there be? FIX: handle variable 425 @type unavailable: bool or None 426 @param unavailable: the default value to use if none given (if not None) 427 @return: None 428 ''' 429 if verbose: print ' encodeUInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 430 431 assert numbits==6 # Force by the IEEE spec 432 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 433 434 if None != required: 435 if verbose: print ' required:',required 436 required=int(required) 437 o.write('\tbvList.append(aisstring.encode(\''+str(required)+'\','+str(numbits*arraylen)+'))\n') 438 if verbose: o.write('\n') 439 return 440 441 if None==unavailable: 442 o.write('\tbvList.append(aisstring.encode(params[\''+name+'\'],'+str(numbits*arraylen)+'))\n') 443 else: # Have a default value that can be filled in 444 o.write("\tif '"+name+"' in params:\n") 445 o.write('\t\tbvList.append(aisstring.encode(params[\''+name+'\'],'+str(numbits*arraylen)+'))\n') 446 o.write('\telse:\n') 447 o.write('\t\tbvList.append(aisstring.encode(\''+str(unavailable)+'\','+str(numbits*arraylen)+'))\n') 448 449 if verbose: o.write('\n')
450 451
452 -def encodeInt(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
453 ''' 454 Build the encoder for signed integer variables 455 456 @type o: file like obj 457 @param o: where write the code 458 @type name: str 459 @param name: field name 460 @type type: str 461 @param type: uint, bool, etc. 462 @type numbits: int >= 1 463 @param numbits: How many bits per unit datum (must be 1..32) 464 @type required: bool or None 465 @param required: If not None, then the value must be set to this. 466 @type arraylen: int >= 1 467 @param arraylen: many signed ints will there be? FIX: handle variable 468 @type unavailable: number or None 469 @param unavailable: the default value to use if none given (if not None) 470 @return: None 471 ''' 472 if verbose: print ' encodeInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 473 474 assert numbits>=1 and numbits<=32 475 if arraylen != 1: assert False # FIX... handle arrays 476 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 477 478 if None != required: 479 if verbose: print ' required:',required 480 required=int(required) 481 o.write('\tbvList.append(binary.bvFromSignedInt('+str(required)+','+str(numbits)+'))\n') 482 if verbose: o.write('\n') 483 return 484 485 486 if None==unavailable: 487 o.write('\tbvList.append(binary.bvFromSignedInt(params[\''+name+'\'],'+str(numbits)+'))\n') 488 else: # Have a default value that can be filled in 489 #assert type(unavailable)== 490 int(unavailable) # Make sure unavailable is a number object 491 o.write("\tif '"+name+"' in params:\n") 492 o.write('\t\tbvList.append(binary.bvFromSignedInt(params[\''+name+'\']'+','+str(numbits)+'))\n') 493 o.write('\telse:\n') 494 o.write('\t\tbvList.append(binary.bvFromSignedInt('+str(unavailable)+','+str(numbits)+'))\n') 495 496 if verbose: o.write('\n')
497 498 499 500 # FIX: Ummm... why am I passing the type? I guess it makes the one print statement easier
501 -def encodeDecimal(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
502 ''' 503 Build the encoder for signed decimal variables 504 505 @type o: file like obj 506 @param o: where write the code 507 @type name: str 508 @param name: field name 509 @type type: str 510 @param type: decimal 511 @type numbits: int >= 1 512 @param numbits: How many bits per unit datum (must be 1..32) 513 @type required: bool or None 514 @param required: If not None, then the value must be set to this. 515 @type arraylen: int >= 1 516 @param arraylen: many decimals will there be? FIX: handle variable 517 @type unavailable: Decimal or None 518 @param unavailable: the default value to use if none given (if not None) 519 @return: None 520 ''' 521 if verbose: print ' encodeDecimal:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 522 523 assert numbits>=1 and numbits<=32 524 if arraylen != 1: assert False # FIX... handle arrays 525 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 526 527 # FIX: optimize to not emit the scaling when it is not needed... or tell the user to switch to an int! 528 if None == scale: 529 print 'WARNING: if you are not scaling, then you probably want to use an int instead!' 530 print 'Beware canadians bearing travel videos' 531 scale='1' 532 533 if None != required: 534 if verbose: print ' required:',required 535 required=int(required) 536 o.write('\tbvList.append(binary.bvFromSignedInt('+str(int(Decimal(required)*Decimal(scale)))+','+str(numbits)+'))\n') 537 if verbose: o.write('\n') 538 return 539 540 # FIX: can I get rid of the Decimal around params? 541 if None==unavailable: 542 o.write('\tbvList.append(binary.bvFromSignedInt(int(Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')),'+str(numbits)+'))\n') 543 else: # Have a default value that can be filled in 544 o.write("\tif '"+name+"' in params:\n") 545 o.write('\t\tbvList.append(binary.bvFromSignedInt(int(Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')),'+str(numbits)+'))\n') 546 o.write('\telse:\n') 547 o.write('\t\tbvList.append(binary.bvFromSignedInt('+str(int(Decimal(unavailable)*Decimal(scale)))+','+str(numbits)+'))\n') 548 549 if verbose: o.write('\n')
550 551 552
553 -def encodeUDecimal(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
554 ''' 555 Build the encoder for signed decimal variables 556 557 @type o: file like obj 558 @param o: where write the code 559 @type name: str 560 @param name: field name 561 @type type: str 562 @param type: decimal 563 @type numbits: int >= 1 564 @param numbits: How many bits per unit datum (must be 1..32) 565 @type required: bool or None 566 @param required: If not None, then the value must be set to this. 567 @type arraylen: int >= 1 568 @param arraylen: many decimals will there be? FIX: handle variable 569 @type unavailable: Decimal or None 570 @param unavailable: the default value to use if none given (if not None) 571 @return: None 572 ''' 573 if verbose: print ' encodeDecimal:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable 574 assert type=='udecimal' 575 assert numbits>=1 and numbits<=32 576 if arraylen != 1: assert False # FIX... handle arrays 577 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 578 579 # FIX: optimize to not emit the scaling when it is not needed... or tell the user to switch to an int! 580 if None == scale: 581 print 'WARNING: if you are not scaling, then you probably want to use an int instead!' 582 print 'Beware canadians bearing travel videos' 583 scale='1' 584 585 if None != required: 586 if verbose: print ' required:',required 587 required=int(required) 588 assert(0<=required) 589 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(int(Decimal(required)*Decimal(scale)))+'),'+str(numbits)+'))\n') 590 if verbose: o.write('\n') 591 return 592 593 # FIX: can I get rid of the Decimal around params? 594 if None==unavailable: 595 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')))),'+str(numbits)+'))\n') 596 else: # Have a default value that can be filled in 597 o.write("\tif '"+name+"' in params:\n") 598 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')))),'+str(numbits)+'))\n') 599 o.write('\telse:\n') 600 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int('+str(int(Decimal(unavailable)*Decimal(scale)))+')),'+str(numbits)+'))\n') 601 602 if verbose: o.write('\n')
603 604 605 606 ###################################################################### 607 # DECODERS 608 ###################################################################### 609
610 -def decodeBool(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 611 bv='bv',dataDict='r',verbose=False):
612 ''' 613 Build the decoder for boolean variables 614 615 @type o: file like obj 616 @param o: where write the code 617 @type name: str 618 @param name: field name 619 @type type: str 620 @param type: uint, bool, etc. 621 @type startindex: int 622 @param startindex: bit that begins the bool(s) 623 @type numbits: int = 1 624 @param numbits: How many bits per unit datum (must be 1 for bools) 625 @type required: bool or None 626 @param required: If not None, then the value must be set to this. 627 @type arraylen: int >= 1 628 @param arraylen: many bools will there be? FIX: handle variable 629 @type unavailable: bool or None 630 @param unavailable: the default value to use if none given (if not None) 631 @type bv: str 632 @param bv: BitVector containing the incoming data 633 @type dataDict: str 634 @param dataDict: dictionary in which to place the results 635 @rtype: int 636 @return: index one past the end of where this read 637 ''' 638 assert(type=='bool') 639 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 640 #int(startindex); int(numbits) # Make sure it is a number 641 assert numbits==1 642 assert arraylen == 1 # FIX... handle arrays 643 if verbose: o.write('\t### FIELD foo: '+name+' (type='+type+')\n') 644 645 if None != required: 646 assert type(required)==bool 647 if required: o.write('\t\t'+dataDict+'[\''+name+'\']=True\n') 648 else: o.write('\t\t'+dataDict+'[\''+name+'\']=False\n') 649 if verbose: o.write('\n') 650 return int(startindex)+int(numbits) 651 652 o.write('\t'+dataDict+'[\''+name+'\']=bool(int('+bv+'['+str(startindex)+':'+str(startindex+int(numbits)*int(arraylen))+']))\n') 653 if verbose: o.write('\n') 654 655 return int(startindex)+int(numbits)
656 657
658 -def decodeUInt(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 659 bv='bv',dataDict='r',verbose=False):
660 ''' 661 Build the decoder for unsigned integer variables 662 663 @type o: file like obj 664 @param o: where write the code 665 @type name: str 666 @param name: field name 667 @type type: str 668 @param type: uint, etc. 669 @type startindex: int 670 @param startindex: bit that begins the uint(s) 671 @type numbits: int >= 1 672 @param numbits: How many bits per unit datum 673 @type required: int or None 674 @param required: If not None, then the value must be set to this. 675 @type arraylen: int >= 1 676 @param arraylen: many ints will there be? FIX: handle variable 677 @type unavailable: int or None 678 @param unavailable: the default value to use if none given (if not None) 679 @type bv: str 680 @param bv: BitVector containing the incoming data 681 @type dataDict: str 682 @param dataDict: dictionary in which to place the results 683 @rtype: int 684 @return: index one past the end of where this read 685 ''' 686 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 687 if None==arraylen: arraylen=1 688 assert arraylen == 1 # FIX... handle arrays 689 assert numbits>=1 690 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 691 692 if None != required: 693 int(required) # Make sure required is a number 694 o.write('\t'+dataDict+'[\''+name+'\']='+str(required)+'\n') 695 if verbose: o.write('\n') 696 return startindex+numbits 697 698 o.write('\t'+dataDict+'[\''+name+'\']=int('+bv+'['+str(startindex)+':'+str(startindex+int(numbits)*int(arraylen))+'])\n') 699 if verbose: o.write('\n') 700 701 return startindex+numbits
702 703
704 -def decodeInt(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 705 bv='bv',dataDict='r',verbose=False):
706 ''' 707 Build the decoder for unsigned integer variables 708 709 @type o: file like obj 710 @param o: where write the code 711 @type name: str 712 @param name: field name 713 @type type: str 714 @param type: int 715 @type startindex: int 716 @param startindex: bit that begins the int(s) 717 @type numbits: int >= 1 718 @param numbits: How many bits per unit datum 719 @type required: int or None 720 @param required: If not None, then the value must be set to this. 721 @type arraylen: int >= 1 722 @param arraylen: many ints will there be? FIX: handle variable 723 @type unavailable: int or None 724 @param unavailable: the default value to use if none given (if not None) 725 @type bv: str 726 @param bv: BitVector containing the incoming data 727 @type dataDict: str 728 @param dataDict: dictionary in which to place the results 729 @rtype: int 730 @return: index one past the end of where this read 731 ''' 732 assert type=='int' 733 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 734 if None==arraylen: arraylen=1 735 end = startindex+int(numbits)*int(arraylen) 736 assert arraylen == 1 # FIX... handle arrays 737 assert numbits>=1 738 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 739 740 if None != required: 741 int(required) # Make sure required is a number 742 o.write('\t'+dataDict+'[\''+name+'\']='+str(required)+'\n') 743 if verbose: o.write('\n') 744 return end 745 746 o.write('\t'+dataDict+'[\''+name+'\']=binary.signedIntFromBV('+bv+'['+str(startindex)+':'+str(end)+'])\n') 747 if verbose: o.write('\n') 748 749 return end
750
751 -def decodeFloat(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 752 bv='bv',dataDict='r',verbose=False):
753 ''' 754 Build the decoder for IEEE float variables 755 756 @type o: file like obj 757 @param o: where write the code 758 @type name: str 759 @param name: field name 760 @type type: str 761 @param type: int 762 @type startindex: int 763 @param startindex: bit that begins the int(s) 764 @type numbits: int >= 1 765 @param numbits: How many bits per unit datum 766 @type required: float or None 767 @param required: If not None, then the value must be set to this. 768 @type arraylen: int >= 1 769 @param arraylen: many ints will there be? FIX: handle variable 770 @type unavailable: float or None 771 @param unavailable: the default value to use if none given (if not None) 772 @type bv: str 773 @param bv: BitVector containing the incoming data 774 @type dataDict: str 775 @param dataDict: dictionary in which to place the results 776 @rtype: int 777 @return: index one past the end of where this read 778 ''' 779 assert type=='float' 780 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 781 if None==arraylen: arraylen=1 782 end = startindex+int(numbits)*int(arraylen) 783 assert arraylen == 1 # FIX... handle arrays 784 assert numbits>=1 785 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 786 787 if None != required: 788 float(required) # Make sure required is a number 789 o.write('\t'+dataDict+'[\''+name+'\']='+str(required)+'\n') 790 if verbose: o.write('\n') 791 return end 792 793 o.write('\t'+dataDict+'[\''+name+'\']=binary.bitvec2float('+bv+'['+str(startindex)+':'+str(end)+'])\n') 794 if verbose: o.write('\n') 795 796 return end
797 798
799 -def decodeAisstr6(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 800 bv='bv',dataDict='r',verbose=False):
801 ''' 802 Build the decoder for aisstr6 variables. Generally arrays. 803 @bug: FIX: validate strings?? 804 @type o: file like obj 805 @param o: where write the code 806 @type name: str 807 @param name: field name 808 @type type: str 809 @param type: 'aisstr6' 810 @type startindex: int 811 @param startindex: bit that begins the int(s) 812 @type numbits: int >= 1 813 @param numbits: How many bits per unit datum 814 @type required: restricted str or None 815 @param required: If not None, then the value must be set to this. 816 @type arraylen: int >= 1 817 @param arraylen: many ints will there be? FIX: handle variable 818 @type unavailable: restricted str or None 819 @param unavailable: the default value to use if none given (if not None) 820 @type bv: str 821 @param bv: BitVector containing the incoming data 822 @type dataDict: str 823 @param dataDict: dictionary in which to place the results 824 @rtype: int 825 @return: index one past the end of where this read 826 ''' 827 assert type=='aisstr6' 828 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 829 if None==arraylen: arraylen=1 830 end = startindex+int(numbits)*int(arraylen) 831 assert arraylen >= 1 # FIX... handle arrays 832 assert numbits>=1 833 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 834 835 if None != required: 836 float(required) # Make sure required is a number 837 o.write('\t'+dataDict+'[\''+name+'\']='+required+'\n') 838 if verbose: o.write('\n') 839 return end 840 841 o.write('\t'+dataDict+'[\''+name+'\']=aisstring.decode('+bv+'['+str(startindex)+':'+str(end)+'])\n') 842 if verbose: o.write('\n') 843 844 return end
845 846
847 -def decodeDecimal(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 848 bv='bv',dataDict='r',verbose=False,scale=None):
849 ''' 850 Build the decoder for signed decimal variables 851 852 @type o: file like obj 853 @param o: where write the code 854 @type name: str 855 @param name: field name 856 @type type: str 857 @param type: 'decimal' 858 @type startindex: int 859 @param startindex: bit that begins the int(s) 860 @type numbits: int >= 1 861 @param numbits: How many bits per unit datum 862 @type required: Decimal or None 863 @param required: If not None, then the value must be set to this. 864 @type arraylen: int >= 1 865 @param arraylen: many ints will there be? FIX: handle variable 866 @type unavailable: Decimal or None 867 @param unavailable: the default value to use if none given (if not None) 868 @type bv: str 869 @param bv: BitVector containing the incoming data 870 @type dataDict: str 871 @param dataDict: dictionary in which to place the results 872 @rtype: int 873 @return: index one past the end of where this read 874 ''' 875 assert type=='decimal' 876 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 877 if None==arraylen: arraylen=1 878 end = startindex+int(numbits)*int(arraylen) 879 assert arraylen == 1 # FIX... handle arrays 880 assert numbits>=1 and numbits <= 32 881 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 882 883 if None == scale: scale='1' # Warning about this was in the encode section 884 885 if None != required: 886 Decimal(required) # Make sure required is a number 887 o.write('\t'+dataDict+'[\''+name+'\']='+str(Decimal(required))+'/Decimal(\''+scale+'\')\n') 888 if verbose: o.write('\n') 889 return end 890 891 o.write('\t'+dataDict+'[\''+name+'\']=Decimal(binary.signedIntFromBV('+bv+'['+str(startindex)+':'+str(end)+']))/Decimal(\''+scale+'\')\n') 892 if verbose: o.write('\n') 893 894 return end
895 896 897 898
899 -def decodeUDecimal(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None, 900 bv='bv',dataDict='r',verbose=False,scale=None):
901 ''' 902 Build the decoder for unsigned decimal variables 903 904 @type o: file like obj 905 @param o: where write the code 906 @type name: str 907 @param name: field name 908 @type type: str 909 @param type: 'udecimal' 910 @type startindex: int 911 @param startindex: bit that begins the int(s) 912 @type numbits: int >= 1 913 @param numbits: How many bits per unit datum 914 @type required: Decimal or None 915 @param required: If not None, then the value must be set to this. 916 @type arraylen: int >= 1 917 @param arraylen: many ints will there be? FIX: handle variable 918 @type unavailable: Decimal or None 919 @param unavailable: the default value to use if none given (if not None) 920 @type bv: str 921 @param bv: BitVector containing the incoming data 922 @type dataDict: str 923 @param dataDict: dictionary in which to place the results 924 @rtype: int 925 @return: index one past the end of where this read 926 ''' 927 assert type=='udecimal' 928 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex 929 if None==arraylen: arraylen=1 930 end = startindex+int(numbits)*int(arraylen) 931 assert arraylen == 1 # FIX... handle arrays 932 assert numbits>=1 and numbits <= 32 933 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n') 934 935 if None == scale: scale='1' # Warning about this was in the encode section 936 937 if None != required: 938 assert (Decimal(required)>=0.) # Make sure required is a number and not negative 939 o.write('\t'+dataDict+'[\''+name+'\']='+str(Decimal(required))+'/Decimal(\''+scale+'\')\n') 940 if verbose: o.write('\n') 941 return end 942 943 o.write('\t'+dataDict+'[\''+name+'\']=Decimal(int('+bv+'['+str(startindex)+':'+str(end)+']))/Decimal(\''+scale+'\')\n') 944 if verbose: o.write('\n') 945 946 return end
947 948 949 950 ###################################################################### 951 # THE REST 952 ###################################################################### 953 954 955
956 -def buildTestParamFunc(o,msgET, verbose=False):
957 '''Scrape the testvalues to make a basic param 958 959 @bug: FIX: make this create a dictionary that sits in the overall namespace and spit out deep copies? 960 ''' 961 name = msgET.attrib['name'] 962 963 o.write('def '+name+'TestParams():\n') 964 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") 965 o.write('\tparams = {}\n') 966 for field in msgET.xpath('field'): 967 name = field.attrib['name'] 968 type = field.attrib['type'] 969 if verbose: print 'buildTestParamFunc ...',name,type 970 val = None 971 if hasSubtag(field,'testvalue') and hasSubtag(field,'required'): 972 print 'ERROR: can not have both test value and required tags in the same field' 973 assert(False) 974 if hasSubtag(field,'testvalue'): 975 val = field.xpath('testvalue')[0].text 976 else: 977 if not hasSubtag(field,'required'): 978 sys.exit("ERROR: missing required or testvalue for field: "+name) 979 val = field.xpath('required')[0].text 980 if verbose: print 'buildTestParamFunc for field '+name+' ...',type,val 981 o.write('\tparams[\''+name+'\'] = ') 982 if type=='bool': 983 if val=='1' or val.lower=='true': val = 'True' 984 else: val = 'False' 985 o.write(val) 986 elif type in ('uint','int','float'): 987 o.write(val) 988 elif type in ('decimal','udecimal'): 989 o.write('Decimal(\''+val+'\')') 990 elif type in ('aisstr6'): 991 o.write('\''+val+'\'') 992 else: 993 print 'ERROR: type not handled ...',type,' (found in the ',name,' field). Time to buy more coffee' 994 suggestType(name,type) 995 assert(False) 996 997 o.write('\n') 998 999 1000 o.write('\n\treturn params\n\n')
1001
1002 -def buildUnitTest(o,msgET, verbose=False):
1003 ''' 1004 Write the unittests for a message 1005 1006 param o: open file where resulting code will be written 1007 param msgET: Element Tree starting at a message node 1008 ''' 1009 assert(msgET.tag=='message') 1010 name = msgET.attrib['name'] 1011 1012 buildTestParamFunc(o,msgET) 1013 1014 o.write('class Test'+name+'(unittest.TestCase):\n') 1015 o.write("\t'''Uses the testvalue tag text from each type to build a test case for the "+name+" message'''\n") 1016 o.write('\tdef testEncodeDecode(self):\n') 1017 # FIX: what is a good docstring to put here? 1018 o.write('\t\n') 1019 o.write('\t\tparams = '+name+'TestParams()\n') 1020 1021 #o.write('\t\tprint "params",params\n') 1022 o.write('\t\tbits = '+name+'Encode(params)\n') 1023 #o.write('\t\tprint "bits:",str(bits)\n') 1024 o.write('\t\tr = '+name+'Decode(bits)\n') 1025 #o.write('\t\tprint "decoded msg",r\n') 1026 o.write('\n') 1027 o.write('\t\t# Check that each parameter came through ok.\n') 1028 1029 1030 for field in msgET.xpath('field'): 1031 name = field.attrib['name'] 1032 type = field.attrib['type'] 1033 if type in ('bool','uint','int','aisstr6'): 1034 o.write('\t\tself.failUnlessEqual(r[\''+name+'\'],params[\''+name+'\'])\n') 1035 else: 1036 # float, decimal, udecimal 1037 # FIX: look up the decimal places if decimal 1038 places = '7' 1039 if hasSubtag(field,'decimalplaces'): places = field.xpath('decimalplaces')[0].text 1040 o.write('\t\tself.failUnlessAlmostEqual(r[\''+name+'\'],params[\''+name+'\'],'+places+')\n')
1041 1042 1043
1044 -def buildEncode(o,msgET, verbose=False):
1045 ''' 1046 Write the encoder/decoder for a message 1047 1048 http://jaynes.colorado.edu/PythonIdioms.html 1049 1050 param o: open file where resulting code will be written 1051 param msgET: Element Tree starting at a message node 1052 ''' 1053 assert(msgET.tag=='message') 1054 name = msgET.attrib['name'] 1055 1056 print 'Generating encoder for',name # FIX: verbose? 1057 funcName = name+'Encode' 1058 o.write('def '+funcName+'(params, validate=False):\n') 1059 1060 ######################################## 1061 # doc string 1062 o.write("\t'''Create a "+name+" binary message payload to pack into an AIS Msg "+name+".\n\n") 1063 o.write('\tFields in params:\n') 1064 for field in msgET.xpath('field'): 1065 desc = field[0].text.replace('\n',' ') # get ride of new lines 1066 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc) # give the description 1067 if len(field.xpath("required")) == 1: 1068 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")') 1069 1070 o.write('\n') 1071 o.write('\t@param params: Dictionary of field names/values. Throws a ValueError exception if required is missing\n') 1072 o.write('\t@param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.\n') 1073 1074 o.write("\t@rtype: BitVector\n") 1075 o.write("\t@return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8\n") 1076 o.write("\t'''\n\n") 1077 1078 ######################################## 1079 # Actually build the code 1080 1081 o.write('\tbvList = []\n') 1082 1083 if verbose: print 'number of fields = ', len(msgET.xpath('field')) 1084 1085 dynamicArrays = False # Set to true when positioning must be calculated 1086 1087 for field in msgET.xpath('field'): 1088 name = field.attrib['name'] 1089 type = field.attrib['type'] 1090 numbits = int(field.attrib['numberofbits']) 1091 required = None; 1092 if hasSubtag(field,'required'): 1093 required = field.xpath('required')[0].text 1094 #print 'required set for',name,'to',required 1095 unavailable=None; 1096 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 1097 arraylen=1 1098 if 'arraylength' in field.attrib: 1099 arraylen=int(field.attrib['arraylength']) 1100 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 1101 else: 1102 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')' 1103 1104 if type=='bool' : encodeBool (o,name,type,numbits,required,arraylen,unavailable) 1105 elif type=='uint' : encodeUInt (o,name,type,numbits,required,arraylen,unavailable) 1106 elif type=='int' : encodeInt (o,name,type,numbits,required,arraylen,unavailable) 1107 elif type=='float' : encodeFloat (o,name,type,numbits,required,arraylen,unavailable) 1108 elif type=='aisstr6': encodeAisstr6(o,name,type,numbits,required,arraylen,unavailable) 1109 elif type=='decimal': 1110 scale = None 1111 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1112 encodeDecimal(o,name,type,numbits,required,arraylen,unavailable,scale=scale) 1113 elif type=='udecimal': 1114 scale = None 1115 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1116 encodeUDecimal(o,name,type,numbits,required,arraylen,unavailable,scale=scale) 1117 else: 1118 print 'WARNING: In buildEncode - Unhandled field type for',name,'...',type 1119 suggestType (name,type) 1120 assert False 1121 1122 o.write('\n\treturn binary.joinBV(bvList)\n\n')
1123 1124 1125 ###################################################################### 1126 # DECODER RING 1127
1128 -def buildDecode(o,msgET, verbose=False):
1129 ''' 1130 Write the decoder for a message 1131 1132 param o: open file where resulting code will be written 1133 type msgET: elementtree 1134 param msgET: Element Tree starting at a message node 1135 return: None 1136 1137 @todo: FIX: check for a dac,fid, or efid. If exists, then this is an AIS Msg 8 payload 1138 ''' 1139 1140 assert(msgET.tag=='message') 1141 name = msgET.attrib['name'] 1142 1143 print 'Generating decoder for',name 1144 funcName = name+'Decode' 1145 o.write('def '+funcName+'(bv, validate=False):\n') 1146 1147 ######################################## 1148 # doc string 1149 o.write("\t'''Unpack a "+name+" message \n\n") 1150 o.write('\tFields in params:\n') 1151 for field in msgET.xpath('field'): 1152 desc = field[0].text.replace('\n',' ') # get ride of new lines 1153 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc) # give the description 1154 if len(field.xpath("required")) == 1: 1155 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")') 1156 1157 o.write('\n') 1158 o.write('\t@type bv: BitVector\n') 1159 o.write('\t@param bv: Bits defining a message\n') 1160 o.write('\t@param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.\n') 1161 1162 o.write("\t@rtype: dict\n") 1163 o.write("\t@return: params\n") 1164 o.write("\t'''\n\n") 1165 1166 1167 o.write('\t#Would be nice to check the bit count here..\n') 1168 o.write('\t#if validate:\n') 1169 o.write('\t#\tassert (len(bv)==FIX: SOME NUMBER)\n') 1170 1171 1172 ######################################## 1173 # Actually build the code 1174 1175 o.write('\tr = {}\n') 1176 1177 1178 if verbose: print 'number of fields = ', len(msgET.xpath('field')) 1179 1180 dynamicArrays = False # Set to true when positioning must be calculated 1181 1182 startindex = 0 # Where we are in the bitvector... FIX: what about variable length jobs? 1183 1184 for field in msgET.xpath('field'): 1185 name = field.attrib['name'] 1186 type = field.attrib['type'] 1187 numbits = int(field.attrib['numberofbits']) 1188 required = None; 1189 if hasSubtag(field,'required'): 1190 required = field.xpath('required')[0].text 1191 #print 'required set for',name,'to',required 1192 unavailable=None; 1193 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text 1194 arraylen=1 1195 if 'arraylength' in field.attrib: 1196 arraylen=int(field.attrib['arraylength']) 1197 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )' 1198 else: 1199 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')' 1200 1201 assert None!=startindex 1202 if verbose: print 'startindex',startindex 1203 if type=='bool' : startindex = decodeBool (o,name,type,startindex,numbits,required,arraylen,unavailable) 1204 elif type=='uint' : startindex = decodeUInt (o,name,type,startindex,numbits,required,arraylen,unavailable) 1205 elif type=='int' : startindex = decodeInt (o,name,type,startindex,numbits,required,arraylen,unavailable) 1206 elif type=='float' : startindex = decodeFloat (o,name,type,startindex,numbits,required,arraylen,unavailable) 1207 elif type=='aisstr6': startindex = decodeAisstr6(o,name,type,startindex,numbits,required,arraylen,unavailable) 1208 elif type=='decimal': 1209 scale = None 1210 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1211 #print 'pre call...' 1212 #print ' required: "'+str(required)+'" scale: "'+str(scale)+'" unavail:', unavailable 1213 startindex = decodeDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale) 1214 elif type=='udecimal': 1215 scale = None 1216 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text 1217 #print 'pre call...' 1218 #print ' required: "'+str(required)+'" scale: "'+str(scale)+'" unavail:', unavailable 1219 startindex = decodeUDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale) 1220 1221 else: 1222 print 'WARNING: In buildDecode - Unhandled field type for',name,'...',type 1223 suggestType (name,type) 1224 assert False 1225 1226 1227 if None==startindex: print 'FIX: here. drat. treat me right' 1228 assert None!=startindex 1229 1230 1231 o.write('\treturn r\n\n')
1232 1233 1234 1235
1236 -def buildMain(o):
1237 o.write(''' 1238 ############################################################ 1239 if __name__=='__main__': 1240 1241 from optparse import OptionParser 1242 myparser = OptionParser(usage="%prog [options]", 1243 version="%prog "+__version__) 1244 1245 #sys.exit('EARLY EXIT - FIX: remove') 1246 myparser.add_option('--doc-test',dest='doctest',default=False,action='store_true', 1247 help='run the documentation tests') 1248 myparser.add_option('--unit-test',dest='unittest',default=False,action='store_true', 1249 help='run the unit tests') 1250 myparser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 1251 help='Make the test output verbose') 1252 1253 (options,args) = myparser.parse_args() 1254 success=True 1255 1256 if options.doctest: 1257 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 1258 sys.argv= [sys.argv[0]] 1259 if options.verbose: sys.argv.append('-v') 1260 import doctest 1261 numfail,numtests=doctest.testmod() 1262 if numfail==0: print 'ok' 1263 else: 1264 print 'FAILED' 1265 success=False 1266 1267 if not success: 1268 sys.exit('Something Failed') 1269 1270 del success # Hide success from epydoc 1271 1272 if options.unittest: 1273 sys.argv = [sys.argv[0]] 1274 if options.verbose: sys.argv.append('-v') 1275 unittest.main() 1276 ''')
1277 1278 ###################################################################### 1279 if __name__=='__main__': 1280 from optparse import OptionParser 1281 myparser = OptionParser(usage="%prog [options]", 1282 version="%prog "+__version__) 1283 1284 myparser.add_option('-o','--output',dest='outputFileName',default=None, 1285 help='Name of the python file to write') 1286 # help='Name of the python file to write [default: stdout]') 1287 1288 myparser.add_option('-i','-x','--xml-definition',dest='xmlFileName',default=None, 1289 help='XML definition file for the msg to use') 1290 # help='XML definition file for the msg to use [default: stdin]') 1291 1292 # myparser.add_option('-m','--message',dest='message',default=None, 1293 # help='Which message to write code for [default: all]') 1294 1295 myparser.add_option('--doc-test',dest='doctest',default=False,action='store_true', 1296 help='run the documentation tests') 1297 1298 myparser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true', 1299 help='run the tests run in verbose mode') 1300 1301 (options,args) = myparser.parse_args() 1302 1303 success=True 1304 1305 if options.doctest: 1306 import os; print os.path.basename(sys.argv[0]), 'doctests ...', 1307 argvOrig = sys.argv 1308 sys.argv= [sys.argv[0]] 1309 if options.verbose: sys.argv.append('-v') 1310 import doctest 1311 numfail,numtests=doctest.testmod() 1312 if numfail==0: print 'ok' 1313 else: 1314 print 'FAILED' 1315 success=False 1316 sys.argv = argvOrig # Restore the original args 1317 del argvOrig # hide from epydoc 1318 sys.exit() # FIX: Will this exit success? 1319 1320 #infile=sys.stdin 1321 #if options.xmlFileName: infile = file(options.xmlFileName,'r') 1322 1323 #outfile=sys.stdout 1324 #if options.outputFile: outfile = file(options.xmlDefinition,'w') 1325 1326 # FIX: down the road, it would be good to allow either filenames of std{in/out} 1327 1328 if None==options.xmlFileName: 1329 sys.exit('ERROR: must specify an xml definition file.') 1330 if None==options.outputFileName: 1331 sys.exit('ERROR: must specify an python file to write to.') 1332 generatePython(options.xmlFileName,options.outputFileName) 1333 1334 print '\nrecommend running pychecker like this:' 1335 print ' pychecker -q',options.outputFileName 1336