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