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