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