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