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