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