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>}
22
23 @author: U{'''+__author__+'''<http://xenon.stanford.edu/~schwehr/>}
24 @version: ''' + __version__ +'''
25 @copyright: 2006
26 @var __date__: Date of last svn commit
27 @undocumented: __version__ __author__ __doc__ myparser
28 @since: 2006-Sep-24
29 @status: under development
30 @organization: U{CCOM<http://ccom.unh.edu/>}
31 @license: Restricted while in development to NOAA and USCG.
32
33 @todo: add a link to generated doc string to bring up the html for the pretty version
34
35 @bug: NOT complete
36 @todo: make sure binary is only used in AIS ITU messages and not within the binary messages!
37 '''
38
39 import sys, os
40 from decimal import Decimal
41 from lxml import etree
42
44 '''
45 Try to suggest a type name if one did not work.
46
47 @param printout: if true, write a suggestion to stdout.
48
49 >>> suggestType('myFieldName','unsigned int')
50 Recommend switching "unsigned int" to "uint" for field "myFieldName"
51 'uint'
52
53 >>> suggestType('JohnWarfon','yoyodyne')
54 Sorry! No recommendation available for bad type "yoyodyne" for field "JohnWarfon"
55 '''
56 newType = None
57 if curType.lower()=='unsigned int':
58 newType = 'uint'
59 elif curType.lower()=='unsigned decimal':
60 newType = 'udecimal'
61
62 if printout:
63 if None != newType:
64 print 'Recommend switching "'+curType+'" to "'+newType+'" for field "'+name+'"'
65 else:
66 print 'Sorry! No recommendation available for bad type "'+curType+'" for field "'+name+'"'
67 return newType
68
69
71 '''
72 @return: true if the tag a sub tag with name subtag
73 '''
74 if 0<len(et.xpath(subtag)): return True
75 return False
76
77
78
80 '''
81 Write the doc string header for the message file
82
83 param o: Open output file to write code to.
84 param msgET: element tree for the ais message definition.
85 Must be pre-expanded with the expandais.py command.
86 '''
87 import datetime
88 d = datetime.datetime.utcnow()
89 dateStr = str(d.year)+'-'+("%02d" %d.month)+'-'+("%02d"%d.day)
90
91
92
93
94
95 o.write('''#!/usr/bin/env python
96
97 __version__ = '$Revision: 4791 $'.split()[1]
98 __date__ = '$Da'''+'''te: '''+dateStr+''' $'.split()[1]
99 __author__ = 'xmlbinmsg'
100
101 __doc__=\'\'\'
102
103 Autogenerated python functions to serialize/deserialize binary messages.
104
105 Generated by: '''+__file__+'''
106
107 Need to then wrap these functions with the outer AIS packet and then
108 convert the whole binary blob to a NMEA string. Those functions are
109 not currently provided in this file.
110
111 serialize: python to ais binary
112 deserialize: ais binary to python
113
114 The generated code uses translators.py, binary.py, and aisstring.py
115 which should be packaged with the resulting files.
116
117 ''')
118
119 o.write('''
120 @requires: U{epydoc<http://epydoc.sourceforge.net/>} > 3.0alpha3
121 @requires: U{BitVector<http://cheeseshop.python.org/pypi/BitVector>}
122
123 @author: \'\'\'+__author__+\'\'\'
124 @version: \'\'\' + __version__ +\'\'\'
125 @var __date__: Date of last svn commit
126 @undocumented: __version__ __author__ __doc__ myparser
127 @status: under development
128 @license: Generated code has no license
129 \'\'\'
130
131 import sys
132 from decimal import Decimal
133 from BitVector import BitVector
134
135 import binary, aisstring
136
137 TrueBV = BitVector(bitstring="1")
138 "Why always rebuild the True bit? This should speed things up a bunch"
139 FalseBV = BitVector(bitstring="0")
140 "Why always rebuild the False bit? This should speed things up a bunch"
141
142
143 ''')
144 return
145
147 '''
148 @param infile: xml ais binary message definition file
149 @param outfile: where to dump the python code
150 '''
151
152 aisMsgsET = etree.parse(infile).getroot()
153
154 o = file(outfile,'w')
155 os.chmod(outfile,0755)
156
157 writeBeginning(o)
158
159 for msgET in aisMsgsET:
160 if msgET.tag != 'message': continue
161 print msgET.tag, msgET.attrib['name']
162
163 if len(msgET.xpath('include-struct')) > 0:
164 sys.exit("ERROR: cannot handle xml that still has include-struct tags.\n Please use expandais.py.")
165
166 buildEncode(o,msgET)
167 buildDecode(o,msgET)
168 buildDecodeParts(o,msgET,prefixName=False)
169 buildPrint(o,msgET)
170
171 o.write('\n\n######################################################################\n')
172 o.write('# UNIT TESTING\n')
173 o.write('######################################################################\n')
174 o.write('import unittest\n')
175
176 for msgET in aisMsgsET:
177 if msgET.tag != 'message': continue
178 print 'Building unit tests for message ...', msgET.attrib['name']
179
180 buildUnitTest(o,msgET)
181
182 buildMain(o)
183 return
184
185
186
187
188
190 '''Get the maximum string length of any field name'''
191 maxStrLen=0
192 for field in msgET.xpath('field'):
193 fieldLen = len(field.attrib['name'])
194 if fieldLen>maxStrLen: maxStrLen = fieldLen
195 return maxStrLen
196
198 '''Pad a string out to the length requested with spaces out to the right'''
199 return aStr + ' '*(strlen-len(aStr))
200
202 '''
203 Write a simple in order print for the resulting dictionary.
204
205 param o: open file where resulting code will be written
206 param msgET: Element Tree starting at a message node
207 '''
208 assert(msgET.tag=='message')
209 name = msgET.attrib['name']
210
211 print 'Generating print for',name
212 funcName = name+'Print'
213 o.write('def '+funcName+'(params, out=sys.stdout):\n')
214
215
216
217 o.write("\t'''Print a "+name+" message to stdout.\n\n")
218 o.write('\tFields in params:\n')
219 for field in msgET.xpath('field'):
220 desc = field[0].text.replace('\n',' ')
221 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc)
222 if len(field.xpath("required")) == 1:
223 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")')
224
225 o.write('\n')
226 o.write('\t@param params: Dictionary of field names/values. \n')
227 o.write('\t@param out: File like object to write to\n')
228
229 o.write("\t@rtype: stdout\n")
230 o.write("\t@return: text to out\n")
231 o.write("\t'''\n\n")
232
233
234
235
236 o.write('\tout.write("'+name+':\\n")\n')
237
238 if verbose: print 'number of fields = ', len(msgET.xpath('field'))
239
240
241 maxFieldLen = 1 + getMaxFieldNameLen(msgET)
242
243
244 for field in msgET.xpath('field'):
245 name = field.attrib['name']
246 type = field.attrib['type']
247 numbits = int(field.attrib['numberofbits'])
248 required = None;
249 if hasSubtag(field,'required'):
250 required = field.xpath('required')[0].text
251
252 unavailable=None;
253 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text
254 arraylen=1
255 if 'arraylength' in field.attrib:
256 arraylen=int(field.attrib['arraylength'])
257 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )'
258 else:
259 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')'
260
261 if 1==arraylen or type=='aisstr6':
262 o.write('\tout.write("\t'+padStrRight(name+':',maxFieldLen)+' "+str(params[\''+name+'\'])+"\\n")\n')
263
264 else:
265 sys.exit ('FIX: handle arrays in the buildPrint func')
266
267
268
269 o.write('\n\treturn # Nothing to return\n\n')
270
271
272
273
274 -def encodeBool(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
275 '''
276 Build the encoder for boolean variables
277 @type o: file like obj
278 @param o: where write the code
279 @type name: str
280 @param name: field name
281 @type type: str
282 @param type: bool, etc.
283 @type numbits: int = 1
284 @param numbits: How many bits per unit datum (must be 1 for bools)
285 @type required: bool or None
286 @param required: If not None, then the value must be set to this.
287 @type arraylen: int >= 1
288 @param arraylen: many bools will there be? FIX: handle variable
289 @type unavailable: bool or None
290 @param unavailable: the default value to use if none given (if not None)
291 @return: None
292 '''
293
294 if verbose: print 'bool encode',name,': unvail=',unavailable
295
296 assert type.lower()=='bool'
297 assert numbits==1
298 if arraylen != 1: assert False
299 if verbose: o.write('\t### FIELD: '+name+' (type=bool)\n')
300 if None != required:
301 assert type(required)==bool
302 if required: o.write('\t\tbvList.append(TrueBV)\n')
303 else: o.write('\t\tbvList.append(FalseBV)\n')
304 if verbose: o.write('\n')
305 return
306
307 if None==unavailable:
308 o.write('\tif params["'+name+'"]: bvList.append(TrueBV)\n')
309 o.write('\telse: bvList.append(FalseBV)\n')
310 else:
311 assert type(unavailable)==bool
312 o.write("\tif '"+name+"' in params:\n")
313 o.write('\t\tif params["'+name+'"]: bvList.append(TrueBV)\n')
314 o.write('\t\telse: bvList.append(FalseBV)\n')
315 o.write('\telse:\n')
316 if unavailable: o.write('\t\tbvList.append(TrueBV)\n')
317 else: o.write('\t\tbvList.append(FalseBV)\n')
318 if verbose: o.write('\n')
319
320 -def encodeUInt(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
321 '''
322 Build the encoder for unsigned integer variables
323
324 @type o: file like obj
325 @param o: where write the code
326 @type name: str
327 @param name: field name
328 @type type: str
329 @param type: uint, bool, etc.
330 @type numbits: int >= 1
331 @param numbits: How many bits per unit datum (must be 1..32)
332 @type required: bool or None
333 @param required: If not None, then the value must be set to this.
334 @type arraylen: int >= 1
335 @param arraylen: many unsigned ints will there be? FIX: handle variable
336 @type unavailable: bool or None
337 @param unavailable: the default value to use if none given (if not None)
338 @return: None
339 '''
340 if verbose: print ' encodeUInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable
341
342 assert type=='uint'
343 assert numbits>=1 and numbits<=32
344 if arraylen != 1: assert False
345 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
346
347 if None != required:
348 if verbose: print ' required:',required
349 required=int(required)
350 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(required)+'),'+str(numbits)+'))\n')
351 if verbose: o.write('\n')
352 return
353
354 if None==unavailable:
355 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal=params[\''+name+'\']),'+str(numbits)+'))\n')
356 else:
357
358 int(unavailable)
359 o.write("\tif '"+name+"' in params:\n")
360 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=params[\''+name+'\']'+'),'+str(numbits)+'))\n')
361 o.write('\telse:\n')
362 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(unavailable)+'),'+str(numbits)+'))\n')
363
364 if verbose: o.write('\n')
365
366
367 -def encodeFloat(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
368 '''
369 Build the encoder for IEEE float variables
370
371 @type o: file like obj
372 @param o: where write the code
373 @type name: str
374 @param name: field name
375 @type type: str
376 @param type: uint, bool, etc.
377 @type numbits: int >= 1
378 @param numbits: How many bits per unit datum (must be 1..32)
379 @type required: bool or None
380 @param required: If not None, then the value must be set to this.
381 @type arraylen: int >= 1
382 @param arraylen: many unsigned ints will there be? FIX: handle variable
383 @type unavailable: bool or None
384 @param unavailable: the default value to use if none given (if not None)
385 @return: None
386 '''
387 if verbose: print ' encodeUInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable
388
389 assert numbits==32
390 if arraylen != 1: assert False
391 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
392
393 if None != required:
394 if verbose: print ' required:',required
395 required=int(required)
396 o.write('\tbvList.append(binary.float2bitvec('+str(required)+'))\n')
397 if verbose: o.write('\n')
398 return
399
400 if None==unavailable:
401 o.write('\tbvList.append(binary.float2bitvec(params[\''+name+'\']))\n')
402 else:
403
404 int(unavailable)
405 o.write("\tif '"+name+"' in params:\n")
406 o.write('\t\tbvList.append(binary.float2bitvec(params[\''+name+'\']'+'))\n')
407 o.write('\telse:\n')
408 o.write('\t\tbvList.append(binary.float2bitvec('+str(unavailable)+'))\n')
409
410 if verbose: o.write('\n')
411
412
413 -def encodeAisstr6(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
414 '''
415 Build the encoder for aisstr6 variables. Generally are arrays.
416 @bug: do we need to optionally check for a valid string?
417
418 @type o: file like obj
419 @param o: where write the code
420 @type name: str
421 @param name: field name
422 @type type: str
423 @param type: uint, bool, etc.
424 @type numbits: int >= 1
425 @param numbits: How many bits per unit datum (must be 1..32)
426 @type required: bool or None
427 @param required: If not None, then the value must be set to this.
428 @type arraylen: int >= 1
429 @param arraylen: many unsigned ints will there be? FIX: handle variable
430 @type unavailable: bool or None
431 @param unavailable: the default value to use if none given (if not None)
432 @return: None
433 '''
434 if verbose: print ' encodeUInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable
435
436 assert numbits==6
437 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
438
439 if None != required:
440 if verbose: print ' required:',required
441 required=int(required)
442 o.write('\tbvList.append(aisstring.encode(\''+str(required)+'\','+str(numbits*arraylen)+'))\n')
443 if verbose: o.write('\n')
444 return
445
446 if None==unavailable:
447 o.write('\tbvList.append(aisstring.encode(params[\''+name+'\'],'+str(numbits*arraylen)+'))\n')
448 else:
449 o.write("\tif '"+name+"' in params:\n")
450 o.write('\t\tbvList.append(aisstring.encode(params[\''+name+'\'],'+str(numbits*arraylen)+'))\n')
451 o.write('\telse:\n')
452 o.write('\t\tbvList.append(aisstring.encode(\''+str(unavailable)+'\','+str(numbits*arraylen)+'))\n')
453
454 if verbose: o.write('\n')
455
456
457 -def encodeInt(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False):
458 '''
459 Build the encoder for signed integer variables
460
461 @type o: file like obj
462 @param o: where write the code
463 @type name: str
464 @param name: field name
465 @type type: str
466 @param type: uint, bool, etc.
467 @type numbits: int >= 1
468 @param numbits: How many bits per unit datum (must be 1..32)
469 @type required: bool or None
470 @param required: If not None, then the value must be set to this.
471 @type arraylen: int >= 1
472 @param arraylen: many signed ints will there be? FIX: handle variable
473 @type unavailable: number or None
474 @param unavailable: the default value to use if none given (if not None)
475 @return: None
476 '''
477 if verbose: print ' encodeInt:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable
478
479 assert numbits>=1 and numbits<=32
480 if arraylen != 1: assert False
481 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
482
483 if None != required:
484 if verbose: print ' required:',required
485 required=int(required)
486 o.write('\tbvList.append(binary.bvFromSignedInt('+str(required)+','+str(numbits)+'))\n')
487 if verbose: o.write('\n')
488 return
489
490
491 if None==unavailable:
492 o.write('\tbvList.append(binary.bvFromSignedInt(params[\''+name+'\'],'+str(numbits)+'))\n')
493 else:
494
495 int(unavailable)
496 o.write("\tif '"+name+"' in params:\n")
497 o.write('\t\tbvList.append(binary.bvFromSignedInt(params[\''+name+'\']'+','+str(numbits)+'))\n')
498 o.write('\telse:\n')
499 o.write('\t\tbvList.append(binary.bvFromSignedInt('+str(unavailable)+','+str(numbits)+'))\n')
500
501 if verbose: o.write('\n')
502
503
504
505
506 -def encodeDecimal(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
507 '''
508 Build the encoder for signed decimal variables
509
510 @type o: file like obj
511 @param o: where write the code
512 @type name: str
513 @param name: field name
514 @type type: str
515 @param type: decimal
516 @type numbits: int >= 1
517 @param numbits: How many bits per unit datum (must be 1..32)
518 @type required: bool or None
519 @param required: If not None, then the value must be set to this.
520 @type arraylen: int >= 1
521 @param arraylen: many decimals will there be? FIX: handle variable
522 @type unavailable: Decimal or None
523 @param unavailable: the default value to use if none given (if not None)
524 @return: None
525 '''
526 if verbose: print ' encodeDecimal:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable
527
528 assert numbits>=1 and numbits<=32
529 if arraylen != 1: assert False
530 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
531
532
533 if None == scale:
534 print 'WARNING: if you are not scaling, then you probably want to use an int instead!'
535 print 'Beware canadians bearing travel videos'
536 scale='1'
537
538 if None != required:
539 if verbose: print ' required:',required
540 required=int(required)
541 o.write('\tbvList.append(binary.bvFromSignedInt('+str(int(Decimal(required)*Decimal(scale)))+','+str(numbits)+'))\n')
542 if verbose: o.write('\n')
543 return
544
545
546 if None==unavailable:
547 o.write('\tbvList.append(binary.bvFromSignedInt(int(Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')),'+str(numbits)+'))\n')
548 else:
549 o.write("\tif '"+name+"' in params:\n")
550 o.write('\t\tbvList.append(binary.bvFromSignedInt(int(Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')),'+str(numbits)+'))\n')
551 o.write('\telse:\n')
552 o.write('\t\tbvList.append(binary.bvFromSignedInt('+str(int(Decimal(unavailable)*Decimal(scale)))+','+str(numbits)+'))\n')
553
554 if verbose: o.write('\n')
555
556
557
558 -def encodeUDecimal(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
559 '''
560 Build the encoder for signed decimal variables
561
562 @type o: file like obj
563 @param o: where write the code
564 @type name: str
565 @param name: field name
566 @type type: str
567 @param type: decimal
568 @type numbits: int >= 1
569 @param numbits: How many bits per unit datum (must be 1..32)
570 @type required: bool or None
571 @param required: If not None, then the value must be set to this.
572 @type arraylen: int >= 1
573 @param arraylen: many decimals will there be? FIX: handle variable
574 @type unavailable: Decimal or None
575 @param unavailable: the default value to use if none given (if not None)
576 @return: None
577 '''
578 if verbose: print ' encodeDecimal:',name,type,numbits,'Req:',required,'alen:',arraylen,unavailable
579 assert type=='udecimal'
580 assert numbits>=1 and numbits<=32
581 if arraylen != 1: assert False
582 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
583
584
585 if None == scale:
586 print 'WARNING: if you are not scaling, then you probably want to use an int instead!'
587 print 'Beware canadians bearing travel videos'
588 scale='1'
589
590 if None != required:
591 if verbose: print ' required:',required
592 required=int(required)
593 assert(0<=required)
594 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal='+str(int(Decimal(required)*Decimal(scale)))+'),'+str(numbits)+'))\n')
595 if verbose: o.write('\n')
596 return
597
598
599 if None==unavailable:
600 o.write('\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')))),'+str(numbits)+'))\n')
601 else:
602 o.write("\tif '"+name+"' in params:\n")
603 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int((Decimal(params[\''+name+'\'])*Decimal(\''+scale+'\')))),'+str(numbits)+'))\n')
604 o.write('\telse:\n')
605 o.write('\t\tbvList.append(binary.setBitVectorSize(BitVector(intVal=int('+str(int(Decimal(unavailable)*Decimal(scale)))+')),'+str(numbits)+'))\n')
606
607 if verbose: o.write('\n')
608
609
610
611 -def encodeBinary(o,name,type,numbits,required=None,arraylen=1,unavailable=None, verbose=False, scale=None):
612 '''
613 Build the encoder for binary variables. This is just a pass through.
614 This is used for the ais binary message wrapper (e.g. msg 8). Do
615 not use it within binary messages.
616
617 @type o: file like obj
618 @param o: where write the code
619 @type name: str
620 @param name: field name
621 @type type: str
622 @param type: binary
623 @type numbits: int >= 1
624 @param numbits: How many bits per unit datum (must be 1..1024 or so)
625 @type required: bool or None
626 @param required: If not None, then the value must be set to this.
627 @type arraylen: int >= 1
628 @param arraylen: many decimals will there be? FIX: handle variable
629 @type unavailable: Decimal or None
630 @param unavailable: the default value to use if none given (if not None)
631 @return: None
632 '''
633 if verbose: print ' encode'+name+':',type,numbits,'Req:',required,'alen:',arraylen,unavailable
634 assert type=='binary'
635 assert numbits>=1 and numbits<=1024
636 assert (None == required)
637 assert (None == unavailable)
638
639 if arraylen != 1: assert False
640 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
641
642
643 o.write('\tbvList.append(params[\''+name+'\'])\n')
644
645 if verbose: o.write('\n')
646
647
648
649
650
651
652 -def decodeBool(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
653 bv='bv',dataDict='r',verbose=False):
654 '''
655 Build the decoder for boolean variables
656
657 @type o: file like obj
658 @param o: where write the code
659 @type name: str
660 @param name: field name
661 @type type: str
662 @param type: uint, bool, etc.
663 @type startindex: int
664 @param startindex: bit that begins the bool(s)
665 @type numbits: int = 1
666 @param numbits: How many bits per unit datum (must be 1 for bools)
667 @type required: bool or None
668 @param required: If not None, then the value must be set to this.
669 @type arraylen: int >= 1
670 @param arraylen: many bools will there be? FIX: handle variable
671 @type unavailable: bool or None
672 @param unavailable: the default value to use if none given (if not None)
673 @type bv: str
674 @param bv: BitVector containing the incoming data
675 @type dataDict: str
676 @param dataDict: dictionary in which to place the results
677 @rtype: int
678 @return: index one past the end of where this read
679 '''
680 assert(type=='bool')
681 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
682
683 assert numbits==1
684 assert arraylen == 1
685 if verbose: o.write('\t### FIELD foo: '+name+' (type='+type+')\n')
686
687 if None != required:
688 assert type(required)==bool
689 if required: o.write('\t\t'+dataDict+'[\''+name+'\']=True\n')
690 else: o.write('\t\t'+dataDict+'[\''+name+'\']=False\n')
691 if verbose: o.write('\n')
692 return int(startindex)+int(numbits)
693
694 o.write('\t'+dataDict+'[\''+name+'\']=bool(int('+bv+'['+str(startindex)+':'+str(startindex+int(numbits)*int(arraylen))+']))\n')
695 if verbose: o.write('\n')
696
697 return int(startindex)+int(numbits)
698
699
700 -def decodeUInt(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
701 bv='bv',dataDict='r',verbose=False):
702 '''
703 Build the decoder for unsigned integer variables
704
705 @type o: file like obj
706 @param o: where write the code
707 @type name: str
708 @param name: field name
709 @type type: str
710 @param type: uint, etc.
711 @type startindex: int
712 @param startindex: bit that begins the uint(s)
713 @type numbits: int >= 1
714 @param numbits: How many bits per unit datum
715 @type required: int or None
716 @param required: If not None, then the value must be set to this.
717 @type arraylen: int >= 1
718 @param arraylen: many ints will there be? FIX: handle variable
719 @type unavailable: int or None
720 @param unavailable: the default value to use if none given (if not None)
721 @type bv: str
722 @param bv: BitVector containing the incoming data
723 @type dataDict: str
724 @param dataDict: dictionary in which to place the results
725 @rtype: int
726 @return: index one past the end of where this read
727 '''
728 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
729 if None==arraylen: arraylen=1
730 assert arraylen == 1
731 assert numbits>=1
732 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
733
734 if None != required:
735 int(required)
736 o.write('\t'+dataDict+'[\''+name+'\']='+str(required)+'\n')
737 if verbose: o.write('\n')
738 return startindex+numbits
739
740 o.write('\t'+dataDict+'[\''+name+'\']=int('+bv+'['+str(startindex)+':'+str(startindex+int(numbits)*int(arraylen))+'])\n')
741 if verbose: o.write('\n')
742
743 return startindex+numbits
744
745
746 -def decodeInt(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
747 bv='bv',dataDict='r',verbose=False):
748 '''
749 Build the decoder for unsigned integer variables
750
751 @type o: file like obj
752 @param o: where write the code
753 @type name: str
754 @param name: field name
755 @type type: str
756 @param type: int
757 @type startindex: int
758 @param startindex: bit that begins the int(s)
759 @type numbits: int >= 1
760 @param numbits: How many bits per unit datum
761 @type required: int or None
762 @param required: If not None, then the value must be set to this.
763 @type arraylen: int >= 1
764 @param arraylen: many ints will there be? FIX: handle variable
765 @type unavailable: int or None
766 @param unavailable: the default value to use if none given (if not None)
767 @type bv: str
768 @param bv: BitVector containing the incoming data
769 @type dataDict: str
770 @param dataDict: dictionary in which to place the results
771 @rtype: int
772 @return: index one past the end of where this read
773 '''
774 assert type=='int'
775 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
776 if None==arraylen: arraylen=1
777 end = startindex+int(numbits)*int(arraylen)
778 assert arraylen == 1
779 assert numbits>=1
780 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
781
782 if None != required:
783 int(required)
784 o.write('\t'+dataDict+'[\''+name+'\']='+str(required)+'\n')
785 if verbose: o.write('\n')
786 return end
787
788 o.write('\t'+dataDict+'[\''+name+'\']=binary.signedIntFromBV('+bv+'['+str(startindex)+':'+str(end)+'])\n')
789 if verbose: o.write('\n')
790
791 return end
792
793 -def decodeFloat(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
794 bv='bv',dataDict='r',verbose=False):
795 '''
796 Build the decoder for IEEE float variables
797
798 @type o: file like obj
799 @param o: where write the code
800 @type name: str
801 @param name: field name
802 @type type: str
803 @param type: int
804 @type startindex: int
805 @param startindex: bit that begins the int(s)
806 @type numbits: int >= 1
807 @param numbits: How many bits per unit datum
808 @type required: float or None
809 @param required: If not None, then the value must be set to this.
810 @type arraylen: int >= 1
811 @param arraylen: many ints will there be? FIX: handle variable
812 @type unavailable: float or None
813 @param unavailable: the default value to use if none given (if not None)
814 @type bv: str
815 @param bv: BitVector containing the incoming data
816 @type dataDict: str
817 @param dataDict: dictionary in which to place the results
818 @rtype: int
819 @return: index one past the end of where this read
820 '''
821 assert type=='float'
822 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
823 if None==arraylen: arraylen=1
824 end = startindex+int(numbits)*int(arraylen)
825 assert arraylen == 1
826 assert numbits>=1
827 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
828
829 if None != required:
830 float(required)
831 o.write('\t'+dataDict+'[\''+name+'\']='+str(required)+'\n')
832 if verbose: o.write('\n')
833 return end
834
835 o.write('\t'+dataDict+'[\''+name+'\']=binary.bitvec2float('+bv+'['+str(startindex)+':'+str(end)+'])\n')
836 if verbose: o.write('\n')
837
838 return end
839
840
841 -def decodeAisstr6(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
842 bv='bv',dataDict='r',verbose=False):
843 '''
844 Build the decoder for aisstr6 variables. Generally arrays.
845 @bug: FIX: validate strings??
846 @type o: file like obj
847 @param o: where write the code
848 @type name: str
849 @param name: field name
850 @type type: str
851 @param type: 'aisstr6'
852 @type startindex: int
853 @param startindex: bit that begins the int(s)
854 @type numbits: int >= 1
855 @param numbits: How many bits per unit datum
856 @type required: restricted str or None
857 @param required: If not None, then the value must be set to this.
858 @type arraylen: int >= 1
859 @param arraylen: many ints will there be? FIX: handle variable
860 @type unavailable: restricted str or None
861 @param unavailable: the default value to use if none given (if not None)
862 @type bv: str
863 @param bv: BitVector containing the incoming data
864 @type dataDict: str
865 @param dataDict: dictionary in which to place the results
866 @rtype: int
867 @return: index one past the end of where this read
868 '''
869 assert type=='aisstr6'
870 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
871 if None==arraylen: arraylen=1
872 end = startindex+int(numbits)*int(arraylen)
873 assert arraylen >= 1
874 assert numbits>=1
875 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
876
877 if None != required:
878 float(required)
879 o.write('\t'+dataDict+'[\''+name+'\']='+required+'\n')
880 if verbose: o.write('\n')
881 return end
882
883 o.write('\t'+dataDict+'[\''+name+'\']=aisstring.decode('+bv+'['+str(startindex)+':'+str(end)+'])\n')
884 if verbose: o.write('\n')
885
886 return end
887
888
889 -def decodeDecimal(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
890 bv='bv',dataDict='r',verbose=False,scale=None):
891 '''
892 Build the decoder for signed decimal variables
893
894 @type o: file like obj
895 @param o: where write the code
896 @type name: str
897 @param name: field name
898 @type type: str
899 @param type: 'decimal'
900 @type startindex: int
901 @param startindex: bit that begins the int(s)
902 @type numbits: int >= 1
903 @param numbits: How many bits per unit datum
904 @type required: Decimal or None
905 @param required: If not None, then the value must be set to this.
906 @type arraylen: int >= 1
907 @param arraylen: many ints will there be? FIX: handle variable
908 @type unavailable: Decimal or None
909 @param unavailable: the default value to use if none given (if not None)
910 @type bv: str
911 @param bv: BitVector containing the incoming data
912 @type dataDict: str
913 @param dataDict: dictionary in which to place the results
914 @rtype: int
915 @return: index one past the end of where this read
916 '''
917 assert type=='decimal'
918 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
919 if None==arraylen: arraylen=1
920 end = startindex+int(numbits)*int(arraylen)
921 assert arraylen == 1
922 assert numbits>=1 and numbits <= 32
923 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
924
925 if None == scale: scale='1'
926
927 if None != required:
928 Decimal(required)
929 o.write('\t'+dataDict+'[\''+name+'\']='+str(Decimal(required))+'/Decimal(\''+scale+'\')\n')
930 if verbose: o.write('\n')
931 return end
932
933 o.write('\t'+dataDict+'[\''+name+'\']=Decimal(binary.signedIntFromBV('+bv+'['+str(startindex)+':'+str(end)+']))/Decimal(\''+scale+'\')\n')
934 if verbose: o.write('\n')
935
936 return end
937
938
939
940
941 -def decodeUDecimal(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
942 bv='bv',dataDict='r',verbose=False,scale=None):
943 '''
944 Build the decoder for unsigned decimal variables
945
946 @type o: file like obj
947 @param o: where write the code
948 @type name: str
949 @param name: field name
950 @type type: str
951 @param type: 'udecimal'
952 @type startindex: int
953 @param startindex: bit that begins the int(s)
954 @type numbits: int >= 1
955 @param numbits: How many bits per unit datum
956 @type required: Decimal or None
957 @param required: If not None, then the value must be set to this.
958 @type arraylen: int >= 1
959 @param arraylen: many ints will there be? FIX: handle variable
960 @type unavailable: Decimal or None
961 @param unavailable: the default value to use if none given (if not None)
962 @type bv: str
963 @param bv: BitVector containing the incoming data
964 @type dataDict: str
965 @param dataDict: dictionary in which to place the results
966 @rtype: int
967 @return: index one past the end of where this read
968 '''
969 assert type=='udecimal'
970 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
971 if None==arraylen: arraylen=1
972 end = startindex+int(numbits)*int(arraylen)
973 assert arraylen == 1
974 assert numbits>=1 and numbits <= 32
975 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
976
977 if None == scale: scale='1'
978
979 if None != required:
980 assert (Decimal(required)>=0.)
981 o.write('\t'+dataDict+'[\''+name+'\']='+str(Decimal(required))+'/Decimal(\''+scale+'\')\n')
982 if verbose: o.write('\n')
983 return end
984
985 o.write('\t'+dataDict+'[\''+name+'\']=Decimal(int('+bv+'['+str(startindex)+':'+str(end)+']))/Decimal(\''+scale+'\')\n')
986 if verbose: o.write('\n')
987
988 return end
989
990
991 -def decodeBinary(o,name,type,startindex,numbits,required=None,arraylen=1,unavailable=None,
992 bv='bv',dataDict='r',verbose=False,scale=None):
993 '''
994 Build the decoder for unsigned decimal variables
995
996 @type o: file like obj
997 @param o: where write the code
998 @type name: str
999 @param name: field name
1000 @type type: str
1001 @param type: 'udecimal'
1002 @type startindex: int
1003 @param startindex: bit that begins the int(s)
1004 @type numbits: int >= 1
1005 @param numbits: How many bits per unit datum
1006 @type required: Decimal or None
1007 @param required: If not None, then the value must be set to this.
1008 @type arraylen: int >= 1
1009 @param arraylen: many ints will there be? FIX: handle variable
1010 @type unavailable: Decimal or None
1011 @param unavailable: the default value to use if none given (if not None)
1012 @type bv: str
1013 @param bv: BitVector containing the incoming data
1014 @type dataDict: str
1015 @param dataDict: dictionary in which to place the results
1016 @rtype: int
1017 @return: index one past the end of where this read
1018 '''
1019 assert type=='binary'
1020 if verbose: print type,'decode',name,': unvail=',unavailable,' numbits:',numbits, ' startindex=',startindex
1021 if None==arraylen: arraylen=1
1022 end = startindex+int(numbits)*int(arraylen)
1023 assert arraylen == 1
1024 assert numbits>=1 and numbits <= 1024
1025 if verbose: o.write('\t### FIELD: '+name+' (type='+type+')\n')
1026
1027
1028
1029
1030 o.write('\t'+dataDict+'[\''+name+'\']='+bv+'['+str(startindex)+':'+str(end)+']\n')
1031 if verbose: o.write('\n')
1032
1033 return end
1034
1035
1036
1037
1038
1039
1040
1041
1042
1044 '''Scrape the testvalues to make a basic param
1045
1046 @bug: FIX: make this create a dictionary that sits in the overall namespace and spit out deep copies?
1047 '''
1048 name = msgET.attrib['name']
1049
1050 o.write('def '+name+'TestParams():\n')
1051 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")
1052 o.write('\tparams = {}\n')
1053 for field in msgET.xpath('field'):
1054 name = field.attrib['name']
1055 type = field.attrib['type']
1056 if verbose: print 'buildTestParamFunc ...',name,type
1057 val = None
1058 if hasSubtag(field,'testvalue') and hasSubtag(field,'required'):
1059 print 'ERROR: can not have both test value and required tags in the same field'
1060 assert(False)
1061 if hasSubtag(field,'testvalue'):
1062 val = field.xpath('testvalue')[0].text
1063 else:
1064 if not hasSubtag(field,'required'):
1065 sys.exit("ERROR: missing required or testvalue for field: "+name)
1066 val = field.xpath('required')[0].text
1067 if verbose: print 'buildTestParamFunc for field '+name+' ...',type,val
1068 o.write('\tparams[\''+name+'\'] = ')
1069 if type=='bool':
1070 if val=='1' or val.lower=='true': val = 'True'
1071 else: val = 'False'
1072 o.write(val)
1073 elif type in ('uint','int','float'):
1074 o.write(val)
1075 elif type in ('decimal','udecimal'):
1076 o.write('Decimal(\''+val+'\')')
1077
1078 elif type in ('aisstr6'):
1079 o.write('\''+val+'\'')
1080 elif type in ('binary'):
1081 o.write('BitVector(bitstring=\''+val+'\')')
1082 else:
1083 print 'ERROR: type not handled ...',type,' (found in the ',name,' field). Time to buy more coffee'
1084 suggestType(name,type)
1085 assert(False)
1086
1087 o.write('\n')
1088
1089
1090 o.write('\n\treturn params\n\n')
1091
1093 '''
1094 Write the unittests for a message
1095
1096 param o: open file where resulting code will be written
1097 param msgET: Element Tree starting at a message node
1098 '''
1099 assert(msgET.tag=='message')
1100 name = msgET.attrib['name']
1101
1102 buildTestParamFunc(o,msgET)
1103
1104 o.write('class Test'+name+'(unittest.TestCase):\n')
1105 o.write("\t'''Uses the testvalue tag text from each type to build a test case for the "+name+" message'''\n")
1106 o.write('\tdef testEncodeDecode(self):\n')
1107
1108 o.write('\t\n')
1109 o.write('\t\tparams = '+name+'TestParams()\n')
1110
1111
1112 o.write('\t\tbits = '+name+'Encode(params)\n')
1113
1114 o.write('\t\tr = '+name+'Decode(bits)\n')
1115
1116 o.write('\n')
1117 o.write('\t\t# Check that each parameter came through ok.\n')
1118
1119
1120 for field in msgET.xpath('field'):
1121 name = field.attrib['name']
1122 type = field.attrib['type']
1123 if type in ('bool','uint','int','aisstr6','binary'):
1124 o.write('\t\tself.failUnlessEqual(r[\''+name+'\'],params[\''+name+'\'])\n')
1125 else:
1126
1127
1128 places = '7'
1129 if hasSubtag(field,'decimalplaces'): places = field.xpath('decimalplaces')[0].text
1130 o.write('\t\tself.failUnlessAlmostEqual(r[\''+name+'\'],params[\''+name+'\'],'+places+')\n')
1131
1132
1133
1135 '''
1136 Write the encoder/decoder for a message
1137
1138 http://jaynes.colorado.edu/PythonIdioms.html
1139
1140 param o: open file where resulting code will be written
1141 param msgET: Element Tree starting at a message node
1142 '''
1143 assert(msgET.tag=='message')
1144 name = msgET.attrib['name']
1145
1146 print 'Generating encoder for',name
1147 funcName = name+'Encode'
1148 o.write('def '+funcName+'(params, validate=False):\n')
1149
1150
1151
1152 o.write("\t'''Create a "+name+" binary message payload to pack into an AIS Msg "+name+".\n\n")
1153 o.write('\tFields in params:\n')
1154 for field in msgET.xpath('field'):
1155 desc = field[0].text.replace('\n',' ')
1156 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc)
1157 if len(field.xpath("required")) == 1:
1158 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")')
1159
1160 o.write('\n')
1161 o.write('\t@param params: Dictionary of field names/values. Throws a ValueError exception if required is missing\n')
1162 o.write('\t@param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.\n')
1163
1164 o.write("\t@rtype: BitVector\n")
1165 o.write("\t@return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8\n")
1166 o.write("\t'''\n\n")
1167
1168
1169
1170
1171 o.write('\tbvList = []\n')
1172
1173 if verbose: print 'number of fields = ', len(msgET.xpath('field'))
1174
1175 dynamicArrays = False
1176
1177 for field in msgET.xpath('field'):
1178 name = field.attrib['name']
1179 type = field.attrib['type']
1180 numbits = int(field.attrib['numberofbits'])
1181 required = None;
1182 if hasSubtag(field,'required'):
1183 required = field.xpath('required')[0].text
1184
1185 unavailable=None;
1186 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text
1187 arraylen=1
1188 if 'arraylength' in field.attrib:
1189 arraylen=int(field.attrib['arraylength'])
1190 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )'
1191 else:
1192 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')'
1193
1194 if type=='bool' : encodeBool (o,name,type,numbits,required,arraylen,unavailable)
1195 elif type=='uint' : encodeUInt (o,name,type,numbits,required,arraylen,unavailable)
1196 elif type=='int' : encodeInt (o,name,type,numbits,required,arraylen,unavailable)
1197 elif type=='float' : encodeFloat (o,name,type,numbits,required,arraylen,unavailable)
1198 elif type=='aisstr6': encodeAisstr6(o,name,type,numbits,required,arraylen,unavailable)
1199 elif type=='decimal':
1200 scale = None
1201 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text
1202 encodeDecimal(o,name,type,numbits,required,arraylen,unavailable,scale=scale)
1203 elif type=='udecimal':
1204 scale = None
1205 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text
1206 encodeUDecimal(o,name,type,numbits,required,arraylen,unavailable,scale=scale)
1207 elif type=='binary': encodeBinary(o,name,type,numbits,required,arraylen,unavailable)
1208 else:
1209 print 'WARNING: In buildEncode - Unhandled field type for',name,'...',type
1210 suggestType (name,type)
1211 assert False
1212
1213 o.write('\n\treturn binary.joinBV(bvList)\n\n')
1214
1215
1216
1217
1218
1219
1220
1222
1223 '''
1224 Write the decoder for a message
1225
1226 param o: open file where resulting code will be written
1227 type msgET: elementtree
1228 param prefixName: if True, put the name of the message on the functions.
1229 param msgET: Element Tree starting at a message node
1230 return: None
1231
1232 @todo: FIX: check for a dac,fid, or efid. If exists, then this is an AIS Msg 8 payload
1233 @todo: May want to take a dictionary of already decoded fields to speed things that need prior info
1234 for things like variable length arrays
1235 '''
1236
1237 assert(msgET.tag=='message')
1238 name = msgET.attrib['name']
1239
1240 print 'Generating partial decode functions for',name
1241
1242 baseName = name+'Decode'
1243 if not prefixName:baseName = 'decode'
1244
1245
1246 startindex = 0
1247
1248 for field in msgET.xpath('field'):
1249 name = field.attrib['name']
1250 type = field.attrib['type']
1251
1252 o.write('def '+baseName+name+'(bv, validate=False):\n')
1253
1254 o.write('\tr={};')
1255
1256 numbits = int(field.attrib['numberofbits'])
1257 required = None;
1258 if hasSubtag(field,'required'):
1259 required = field.xpath('required')[0].text
1260 unavailable=None;
1261 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text
1262 arraylen=1
1263 if 'arraylength' in field.attrib:
1264 arraylen=int(field.attrib['arraylength'])
1265 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )'
1266 else:
1267 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')'
1268
1269
1270 assert None!=startindex
1271 if verbose: print 'startindex',startindex
1272 if type=='bool' : startindex = decodeBool (o,name,type,startindex,numbits,required,arraylen,unavailable)
1273 elif type=='uint' : startindex = decodeUInt (o,name,type,startindex,numbits,required,arraylen,unavailable)
1274 elif type=='int' : startindex = decodeInt (o,name,type,startindex,numbits,required,arraylen,unavailable)
1275 elif type=='float' : startindex = decodeFloat (o,name,type,startindex,numbits,required,arraylen,unavailable)
1276 elif type=='aisstr6': startindex = decodeAisstr6(o,name,type,startindex,numbits,required,arraylen,unavailable)
1277 elif type=='binary' : startindex = decodeBinary (o,name,type,startindex,numbits,required,arraylen,unavailable)
1278
1279 elif type=='decimal':
1280 scale = None
1281 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text
1282 startindex = decodeDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale)
1283 elif type=='udecimal':
1284 scale = None
1285 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text
1286 startindex = decodeUDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale)
1287
1288 else:
1289 print 'WARNING: In buildDecode - Unhandled field type for',name,'...',type
1290 suggestType (name,type)
1291 assert False
1292
1293 o.write('\treturn(r[\''+name+'\'])\n\n')
1294
1295
1296
1297
1298
1299
1301 '''
1302 Write the decoder for a message
1303
1304 param o: open file where resulting code will be written
1305 type msgET: elementtree
1306 param msgET: Element Tree starting at a message node
1307 return: None
1308
1309 @todo: FIX: check for a dac,fid, or efid. If exists, then this is an AIS Msg 8 payload
1310 '''
1311
1312 assert(msgET.tag=='message')
1313 name = msgET.attrib['name']
1314
1315 print 'Generating decoder for',name
1316 funcName = name+'Decode'
1317 o.write('def '+funcName+'(bv, validate=False):\n')
1318
1319
1320
1321 o.write("\t'''Unpack a "+name+" message \n\n")
1322 o.write('\tFields in params:\n')
1323 for field in msgET.xpath('field'):
1324 desc = field[0].text.replace('\n',' ')
1325 o.write('\t - '+field.attrib['name']+'('+field.attrib['type']+'): '+desc)
1326 if len(field.xpath("required")) == 1:
1327 o.write(' (field automatically set to "'+field.xpath("required")[0].text+'")')
1328
1329 o.write('\n')
1330 o.write('\t@type bv: BitVector\n')
1331 o.write('\t@param bv: Bits defining a message\n')
1332 o.write('\t@param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.\n')
1333
1334 o.write("\t@rtype: dict\n")
1335 o.write("\t@return: params\n")
1336 o.write("\t'''\n\n")
1337
1338
1339 o.write('\t#Would be nice to check the bit count here..\n')
1340 o.write('\t#if validate:\n')
1341 o.write('\t#\tassert (len(bv)==FIX: SOME NUMBER)\n')
1342
1343
1344
1345
1346
1347 o.write('\tr = {}\n')
1348
1349
1350 if verbose: print 'number of fields = ', len(msgET.xpath('field'))
1351
1352 dynamicArrays = False
1353
1354 startindex = 0
1355
1356 for field in msgET.xpath('field'):
1357 name = field.attrib['name']
1358 type = field.attrib['type']
1359 numbits = int(field.attrib['numberofbits'])
1360 required = None;
1361 if hasSubtag(field,'required'):
1362 required = field.xpath('required')[0].text
1363
1364 unavailable=None;
1365 if hasSubtag(field,'unavailable'): unavailable = field.xpath('unavailable')[0].text
1366 arraylen=1
1367 if 'arraylength' in field.attrib:
1368 arraylen=int(field.attrib['arraylength'])
1369 if verbose: print 'Processing field ...',name,'('+type+' ',numbits,'*',arraylen,'=',numbits*arraylen,'bits )'
1370 else:
1371 if verbose: print 'Processing field ...',name,'(',type+' ',numbits,')'
1372
1373 assert None!=startindex
1374 if verbose: print 'startindex',startindex
1375 if type=='bool' : startindex = decodeBool (o,name,type,startindex,numbits,required,arraylen,unavailable)
1376 elif type=='uint' : startindex = decodeUInt (o,name,type,startindex,numbits,required,arraylen,unavailable)
1377 elif type=='int' : startindex = decodeInt (o,name,type,startindex,numbits,required,arraylen,unavailable)
1378 elif type=='float' : startindex = decodeFloat (o,name,type,startindex,numbits,required,arraylen,unavailable)
1379 elif type=='aisstr6': startindex = decodeAisstr6(o,name,type,startindex,numbits,required,arraylen,unavailable)
1380 elif type=='binary' : startindex = decodeBinary (o,name,type,startindex,numbits,required,arraylen,unavailable)
1381
1382 elif type=='decimal':
1383 scale = None
1384 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text
1385
1386
1387 startindex = decodeDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale)
1388 elif type=='udecimal':
1389 scale = None
1390 if hasSubtag(field,'scale'): scale = field.xpath('scale')[0].text
1391
1392
1393 startindex = decodeUDecimal(o,name,type,startindex, numbits,required,arraylen,unavailable,scale=scale)
1394
1395 else:
1396 print 'WARNING: In buildDecode - Unhandled field type for',name,'...',type
1397 suggestType (name,type)
1398 assert False
1399
1400
1401 if None==startindex: print 'FIX: here. drat. treat me right'
1402 assert None!=startindex
1403
1404
1405 o.write('\treturn r\n\n')
1406
1407
1408
1409
1411 o.write('''
1412 ############################################################
1413 if __name__=='__main__':
1414
1415 from optparse import OptionParser
1416 myparser = OptionParser(usage="%prog [options]",
1417 version="%prog "+__version__)
1418
1419 #sys.exit('EARLY EXIT - FIX: remove')
1420 myparser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
1421 help='run the documentation tests')
1422 myparser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
1423 help='run the unit tests')
1424 myparser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
1425 help='Make the test output verbose')
1426
1427 (options,args) = myparser.parse_args()
1428 success=True
1429
1430 if options.doctest:
1431 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
1432 sys.argv= [sys.argv[0]]
1433 if options.verbose: sys.argv.append('-v')
1434 import doctest
1435 numfail,numtests=doctest.testmod()
1436 if numfail==0: print 'ok'
1437 else:
1438 print 'FAILED'
1439 success=False
1440
1441 if not success:
1442 sys.exit('Something Failed')
1443
1444 del success # Hide success from epydoc
1445
1446 if options.unittest:
1447 sys.argv = [sys.argv[0]]
1448 if options.verbose: sys.argv.append('-v')
1449 unittest.main()
1450 ''')
1451
1452
1453 if __name__=='__main__':
1454 from optparse import OptionParser
1455 myparser = OptionParser(usage="%prog [options]",
1456 version="%prog "+__version__)
1457
1458 myparser.add_option('-o','--output',dest='outputFileName',default=None,
1459 help='Name of the python file to write')
1460
1461
1462 myparser.add_option('-i','-x','--xml-definition',dest='xmlFileName',default=None,
1463 help='XML definition file for the msg to use')
1464
1465
1466
1467
1468
1469 myparser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
1470 help='run the documentation tests')
1471
1472 myparser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
1473 help='run the tests run in verbose mode')
1474
1475 (options,args) = myparser.parse_args()
1476
1477 success=True
1478
1479 if options.doctest:
1480 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
1481 argvOrig = sys.argv
1482 sys.argv= [sys.argv[0]]
1483 if options.verbose: sys.argv.append('-v')
1484 import doctest
1485 numfail,numtests=doctest.testmod()
1486 if numfail==0: print 'ok'
1487 else:
1488 print 'FAILED'
1489 success=False
1490 sys.argv = argvOrig
1491 del argvOrig
1492 sys.exit()
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502 if None==options.xmlFileName:
1503 sys.exit('ERROR: must specify an xml definition file.')
1504 if None==options.outputFileName:
1505 sys.exit('ERROR: must specify an python file to write to.')
1506 generatePython(options.xmlFileName,options.outputFileName)
1507
1508 print '\nrecommend running pychecker like this:'
1509 print ' pychecker -q',options.outputFileName
1510