1
2
3 __version__ = '$Revision: 4791 $'.split()[1]
4 __date__ = '$Date: 2007-03-18 $'.split()[1]
5 __author__ = 'xmlbinmsg'
6
7 __doc__='''
8
9 Autogenerated python functions to serialize/deserialize binary messages.
10
11 Generated by: ./aisxmlbinmsg2py.py
12
13 Need to then wrap these functions with the outer AIS packet and then
14 convert the whole binary blob to a NMEA string. Those functions are
15 not currently provided in this file.
16
17 serialize: python to ais binary
18 deserialize: ais binary to python
19
20 The generated code uses translators.py, binary.py, and aisstring.py
21 which should be packaged with the resulting files.
22
23
24 @requires: U{epydoc<http://epydoc.sourceforge.net/>} > 3.0alpha3
25 @requires: U{BitVector<http://cheeseshop.python.org/pypi/BitVector>}
26
27 @author: '''+__author__+'''
28 @version: ''' + __version__ +'''
29 @var __date__: Date of last svn commit
30 @undocumented: __version__ __author__ __doc__ parser
31 @status: under development
32 @license: Generated code has no license
33 @todo: FIX: put in a description of the message here with fields and types.
34 '''
35
36 import sys
37 from decimal import Decimal
38 from BitVector import BitVector
39
40 import binary, aisstring
41
42
43 TrueBV = BitVector(bitstring="1")
44 "Why always rebuild the True bit? This should speed things up a bunch"
45 FalseBV = BitVector(bitstring="0")
46 "Why always rebuild the False bit? This should speed things up a bunch"
47
48
49 fieldList = (
50 'MessageID',
51 'RepeatIndicator',
52 'UserID',
53 'Spare1',
54 'DestID',
55 'Spare2',
56 )
57
58 fieldListPostgres = (
59 'MessageID',
60 'RepeatIndicator',
61 'UserID',
62 'Spare1',
63 'DestID',
64 'Spare2',
65 )
66
67 toPgFields = {
68 }
69 '''
70 Go to the Postgis field names from the straight field name
71 '''
72
73 fromPgFields = {
74 }
75 '''
76 Go from the Postgis field names to the straight field name
77 '''
78
79 pgTypes = {
80 }
81 '''
82 Lookup table for each postgis field name to get its type.
83 '''
84
85 -def encode(params, validate=False):
86 '''Create a utcquery binary message payload to pack into an AIS Msg utcquery.
87
88 Fields in params:
89 - MessageID(uint): AIS message number. Must be 10 (field automatically set to "10")
90 - RepeatIndicator(uint): Indicated how many times a message has been repeated
91 - UserID(uint): Unique ship identification number (MMSI)
92 - Spare1(uint): Not used. Should be set to zero. (field automatically set to "0")
93 - DestID(uint): Unique ship identification number (MMSI)
94 - Spare2(uint): Not used. Should be set to zero. (field automatically set to "0")
95 @param params: Dictionary of field names/values. Throws a ValueError exception if required is missing
96 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.
97 @rtype: BitVector
98 @return: encoded binary message (for binary messages, this needs to be wrapped in a msg 8
99 @note: The returned bits may not be 6 bit aligned. It is up to you to pad out the bits.
100 '''
101
102 bvList = []
103 bvList.append(binary.setBitVectorSize(BitVector(intVal=10),6))
104 if 'RepeatIndicator' in params:
105 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['RepeatIndicator']),2))
106 else:
107 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
108 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['UserID']),30))
109 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
110 bvList.append(binary.setBitVectorSize(BitVector(intVal=params['DestID']),30))
111 bvList.append(binary.setBitVectorSize(BitVector(intVal=0),2))
112
113 return binary.joinBV(bvList)
114
115 -def decode(bv, validate=False):
116 '''Unpack a utcquery message
117
118 Fields in params:
119 - MessageID(uint): AIS message number. Must be 10 (field automatically set to "10")
120 - RepeatIndicator(uint): Indicated how many times a message has been repeated
121 - UserID(uint): Unique ship identification number (MMSI)
122 - Spare1(uint): Not used. Should be set to zero. (field automatically set to "0")
123 - DestID(uint): Unique ship identification number (MMSI)
124 - Spare2(uint): Not used. Should be set to zero. (field automatically set to "0")
125 @type bv: BitVector
126 @param bv: Bits defining a message
127 @param validate: Set to true to cause checking to occur. Runs slower. FIX: not implemented.
128 @rtype: dict
129 @return: params
130 '''
131
132
133
134
135 r = {}
136 r['MessageID']=10
137 r['RepeatIndicator']=int(bv[6:8])
138 r['UserID']=int(bv[8:38])
139 r['Spare1']=0
140 r['DestID']=int(bv[40:70])
141 r['Spare2']=0
142 return r
143
146
149
152
155
157 return int(bv[40:70])
158
161
162
164 out.write("<h3>utcquery<h3>\n")
165 out.write("<table border=\"1\">\n")
166 out.write("<tr bgcolor=\"orange\">\n")
167 out.write("<th align=\"left\">Field Name</th>\n")
168 out.write("<th align=\"left\">Type</th>\n")
169 out.write("<th align=\"left\">Value</th>\n")
170 out.write("<th align=\"left\">Value in Lookup Table</th>\n")
171 out.write("<th align=\"left\">Units</th>\n")
172 out.write("\n")
173 out.write("<tr>\n")
174 out.write("<td>MessageID</td>\n")
175 out.write("<td>uint</td>\n")
176 if 'MessageID' in params:
177 out.write(" <td>"+str(params['MessageID'])+"</td>\n")
178 out.write(" <td>"+str(params['MessageID'])+"</td>\n")
179 out.write("</tr>\n")
180 out.write("\n")
181 out.write("<tr>\n")
182 out.write("<td>RepeatIndicator</td>\n")
183 out.write("<td>uint</td>\n")
184 if 'RepeatIndicator' in params:
185 out.write(" <td>"+str(params['RepeatIndicator'])+"</td>\n")
186 if str(params['RepeatIndicator']) in RepeatIndicatorDecodeLut:
187 out.write("<td>"+RepeatIndicatorDecodeLut[str(params['RepeatIndicator'])]+"</td>")
188 else:
189 out.write("<td><i>Missing LUT entry</i></td>")
190 out.write("</tr>\n")
191 out.write("\n")
192 out.write("<tr>\n")
193 out.write("<td>UserID</td>\n")
194 out.write("<td>uint</td>\n")
195 if 'UserID' in params:
196 out.write(" <td>"+str(params['UserID'])+"</td>\n")
197 out.write(" <td>"+str(params['UserID'])+"</td>\n")
198 out.write("</tr>\n")
199 out.write("\n")
200 out.write("<tr>\n")
201 out.write("<td>Spare1</td>\n")
202 out.write("<td>uint</td>\n")
203 if 'Spare1' in params:
204 out.write(" <td>"+str(params['Spare1'])+"</td>\n")
205 out.write(" <td>"+str(params['Spare1'])+"</td>\n")
206 out.write("</tr>\n")
207 out.write("\n")
208 out.write("<tr>\n")
209 out.write("<td>DestID</td>\n")
210 out.write("<td>uint</td>\n")
211 if 'DestID' in params:
212 out.write(" <td>"+str(params['DestID'])+"</td>\n")
213 out.write(" <td>"+str(params['DestID'])+"</td>\n")
214 out.write("</tr>\n")
215 out.write("\n")
216 out.write("<tr>\n")
217 out.write("<td>Spare2</td>\n")
218 out.write("<td>uint</td>\n")
219 if 'Spare2' in params:
220 out.write(" <td>"+str(params['Spare2'])+"</td>\n")
221 out.write(" <td>"+str(params['Spare2'])+"</td>\n")
222 out.write("</tr>\n")
223 out.write("</table>\n")
224
225 -def printFields(params, out=sys.stdout, format='std', fieldList=None, dbType='postgres'):
226 '''Print a utcquery message to stdout.
227
228 Fields in params:
229 - MessageID(uint): AIS message number. Must be 10 (field automatically set to "10")
230 - RepeatIndicator(uint): Indicated how many times a message has been repeated
231 - UserID(uint): Unique ship identification number (MMSI)
232 - Spare1(uint): Not used. Should be set to zero. (field automatically set to "0")
233 - DestID(uint): Unique ship identification number (MMSI)
234 - Spare2(uint): Not used. Should be set to zero. (field automatically set to "0")
235 @param params: Dictionary of field names/values.
236 @param out: File like object to write to
237 @rtype: stdout
238 @return: text to out
239 '''
240
241 if 'std'==format:
242 out.write("utcquery:\n")
243 if 'MessageID' in params: out.write(" MessageID: "+str(params['MessageID'])+"\n")
244 if 'RepeatIndicator' in params: out.write(" RepeatIndicator: "+str(params['RepeatIndicator'])+"\n")
245 if 'UserID' in params: out.write(" UserID: "+str(params['UserID'])+"\n")
246 if 'Spare1' in params: out.write(" Spare1: "+str(params['Spare1'])+"\n")
247 if 'DestID' in params: out.write(" DestID: "+str(params['DestID'])+"\n")
248 if 'Spare2' in params: out.write(" Spare2: "+str(params['Spare2'])+"\n")
249 elif 'csv'==format:
250 if None == options.fieldList:
251 options.fieldList = fieldList
252 needComma = False;
253 for field in fieldList:
254 if needComma: out.write(',')
255 needComma = True
256 if field in params:
257 out.write(str(params[field]))
258
259 out.write("\n")
260 elif 'html'==format:
261 printHtml(params,out)
262 elif 'sql'==format:
263 sqlInsertStr(params,out,dbType=dbType)
264 else:
265 print "ERROR: unknown format:",format
266 assert False
267
268 return
269
270 RepeatIndicatorEncodeLut = {
271 'default':'0',
272 'do not repeat any more':'3',
273 }
274
275 RepeatIndicatorDecodeLut = {
276 '0':'default',
277 '3':'do not repeat any more',
278 }
279
280
281
282
283
284 -def sqlCreateStr(outfile=sys.stdout, fields=None, extraFields=None
285 ,addCoastGuardFields=True
286 ,dbType='postgres'
287 ):
288 '''
289 Return the SQL CREATE command for this message type
290 @param outfile: file like object to print to.
291 @param fields: which fields to put in the create. Defaults to all.
292 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
293 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
294 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
295 @type addCoastGuardFields: bool
296 @return: sql create string
297 @rtype: str
298
299 @see: sqlCreate
300 '''
301 outfile.write(str(sqlCreate(fields,extraFields,addCoastGuardFields,dbType=dbType)))
302
303 -def sqlCreate(fields=None, extraFields=None, addCoastGuardFields=True, dbType='postgres'):
304 '''
305 Return the sqlhelp object to create the table.
306
307 @param fields: which fields to put in the create. Defaults to all.
308 @param extraFields: A sequence of tuples containing (name,sql type) for additional fields
309 @param addCoastGuardFields: Add the extra fields that come after the NMEA check some from the USCG N-AIS format
310 @type addCoastGuardFields: bool
311 @param dbType: Which flavor of database we are using so that the create is tailored ('sqlite' or 'postgres')
312 @return: An object that can be used to generate a return
313 @rtype: sqlhelp.create
314 '''
315 if None == fields: fields = fieldList
316 import sqlhelp
317 c = sqlhelp.create('utcquery',dbType=dbType)
318 c.addPrimaryKey()
319 if 'MessageID' in fields: c.addInt ('MessageID')
320 if 'RepeatIndicator' in fields: c.addInt ('RepeatIndicator')
321 if 'UserID' in fields: c.addInt ('UserID')
322 if 'Spare1' in fields: c.addInt ('Spare1')
323 if 'DestID' in fields: c.addInt ('DestID')
324 if 'Spare2' in fields: c.addInt ('Spare2')
325
326 if addCoastGuardFields:
327
328
329
330
331
332 c.addVarChar('cg_r',15)
333 c.addInt('cg_sec')
334
335 c.addTimestamp('cg_timestamp')
336
337 return c
338
339 -def sqlInsertStr(params, outfile=sys.stdout, extraParams=None, dbType='postgres'):
340 '''
341 Return the SQL INSERT command for this message type
342 @param params: dictionary of values keyed by field name
343 @param outfile: file like object to print to.
344 @param extraParams: A sequence of tuples containing (name,sql type) for additional fields
345 @return: sql create string
346 @rtype: str
347
348 @see: sqlCreate
349 '''
350 outfile.write(str(sqlInsert(params,extraParams,dbType=dbType)))
351
352
353 -def sqlInsert(params,extraParams=None,dbType='postgres'):
354 '''
355 Give the SQL INSERT statement
356 @param params: dict keyed by field name of values
357 @param extraParams: any extra fields that you have created beyond the normal ais message fields
358 @rtype: sqlhelp.insert
359 @return: insert class instance
360 @todo: allow optional type checking of params?
361 @warning: this will take invalid keys happily and do what???
362 '''
363 import sqlhelp
364 i = sqlhelp.insert('utcquery',dbType=dbType)
365
366 if dbType=='postgres':
367 finished = []
368 for key in params:
369 if key in finished:
370 continue
371
372 if key not in toPgFields and key not in fromPgFields:
373 if type(params[key])==Decimal: i.add(key,float(params[key]))
374 else: i.add(key,params[key])
375 else:
376 if key in fromPgFields:
377 val = params[key]
378
379 i.addPostGIS(key,val)
380 finished.append(key)
381 else:
382
383 pgName = toPgFields[key]
384
385 valStr=pgTypes[pgName]+'('
386 vals = []
387 for nonPgKey in fromPgFields[pgName]:
388 vals.append(str(params[nonPgKey]))
389 finished.append(nonPgKey)
390 valStr+=' '.join(vals)+')'
391 i.addPostGIS(pgName,valStr)
392 else:
393 for key in params:
394 if type(params[key])==Decimal: i.add(key,float(params[key]))
395 else: i.add(key,params[key])
396
397 if None != extraParams:
398 for key in extraParams:
399 i.add(key,extraParams[key])
400
401 return i
402
403
404
405
406
407 import unittest
409 '''Return a params file base on the testvalue tags.
410 @rtype: dict
411 @return: params based on testvalue tags
412 '''
413 params = {}
414 params['MessageID'] = 10
415 params['RepeatIndicator'] = 1
416 params['UserID'] = 1193046
417 params['Spare1'] = 0
418 params['DestID'] = 1193046
419 params['Spare2'] = 0
420
421 return params
422
424 '''Use testvalue tag text from each type to build test case the utcquery message'''
426
427 params = testParams()
428 bits = encode(params)
429 r = decode(bits)
430
431
432 self.failUnlessEqual(r['MessageID'],params['MessageID'])
433 self.failUnlessEqual(r['RepeatIndicator'],params['RepeatIndicator'])
434 self.failUnlessEqual(r['UserID'],params['UserID'])
435 self.failUnlessEqual(r['Spare1'],params['Spare1'])
436 self.failUnlessEqual(r['DestID'],params['DestID'])
437 self.failUnlessEqual(r['Spare2'],params['Spare2'])
438
440 parser.add_option('-d','--decode',dest='doDecode',default=False,action='store_true',
441 help='decode a "utcquery" AIS message')
442 parser.add_option('-e','--encode',dest='doEncode',default=False,action='store_true',
443 help='encode a "utcquery" AIS message')
444 parser.add_option('--RepeatIndicator-field', dest='RepeatIndicatorField',default=0,metavar='uint',type='int'
445 ,help='Field parameter value [default: %default]')
446 parser.add_option('--UserID-field', dest='UserIDField',metavar='uint',type='int'
447 ,help='Field parameter value [default: %default]')
448 parser.add_option('--DestID-field', dest='DestIDField',metavar='uint',type='int'
449 ,help='Field parameter value [default: %default]')
450
451
452 if __name__=='__main__':
453
454 from optparse import OptionParser
455 parser = OptionParser(usage="%prog [options]",
456 version="%prog "+__version__)
457
458 parser.add_option('--doc-test',dest='doctest',default=False,action='store_true',
459 help='run the documentation tests')
460 parser.add_option('--unit-test',dest='unittest',default=False,action='store_true',
461 help='run the unit tests')
462 parser.add_option('-v','--verbose',dest='verbose',default=False,action='store_true',
463 help='Make the test output verbose')
464
465
466
467 typeChoices = ('binary','nmeapayload','nmea')
468 parser.add_option('-t','--type',choices=typeChoices,type='choice',dest='ioType'
469 ,default='nmeapayload'
470 ,help='What kind of string to write for encoding ('+', '.join(typeChoices)+') [default: %default]')
471
472
473 outputChoices = ('std','html','csv','sql' )
474 parser.add_option('-T','--output-type',choices=outputChoices,type='choice',dest='outputType'
475 ,default='std'
476 ,help='What kind of string to output ('+', '.join(outputChoices)+') [default: %default]')
477
478 parser.add_option('-o','--output',dest='outputFileName',default=None,
479 help='Name of the python file to write [default: stdout]')
480
481 parser.add_option('-f','--fields',dest='fieldList',default=None, action='append',
482 choices=fieldList,
483 help='Which fields to include in the output. Currently only for csv output [default: all]')
484
485 parser.add_option('-p','--print-csv-field-list',dest='printCsvfieldList',default=False,action='store_true',
486 help='Print the field name for csv')
487
488 parser.add_option('-c','--sql-create',dest='sqlCreate',default=False,action='store_true',
489 help='Print out an sql create command for the table.')
490
491 dbChoices = ('sqlite','postgres')
492 parser.add_option('-D','--db-type',dest='dbType',default='postgres'
493 ,choices=dbChoices,type='choice'
494 ,help='What kind of database ('+', '.join(dbChoices)+') [default: %default]')
495
496 addMsgOptions(parser)
497
498 (options,args) = parser.parse_args()
499 success=True
500
501 if options.doctest:
502 import os; print os.path.basename(sys.argv[0]), 'doctests ...',
503 sys.argv= [sys.argv[0]]
504 if options.verbose: sys.argv.append('-v')
505 import doctest
506 numfail,numtests=doctest.testmod()
507 if numfail==0: print 'ok'
508 else:
509 print 'FAILED'
510 success=False
511
512 if not success: sys.exit('Something Failed')
513 del success
514
515 if options.unittest:
516 sys.argv = [sys.argv[0]]
517 if options.verbose: sys.argv.append('-v')
518 unittest.main()
519
520 outfile = sys.stdout
521 if None!=options.outputFileName:
522 outfile = file(options.outputFileName,'w')
523
524
525 if options.doEncode:
526
527 if None==options.RepeatIndicatorField: parser.error("missing value for RepeatIndicatorField")
528 if None==options.UserIDField: parser.error("missing value for UserIDField")
529 if None==options.DestIDField: parser.error("missing value for DestIDField")
530 msgDict={
531 'MessageID': '10',
532 'RepeatIndicator': options.RepeatIndicatorField,
533 'UserID': options.UserIDField,
534 'Spare1': '0',
535 'DestID': options.DestIDField,
536 'Spare2': '0',
537 }
538
539 bits = encode(msgDict)
540 if 'binary'==options.ioType: print str(bits)
541 elif 'nmeapayload'==options.ioType:
542
543 print "bitLen",len(bits)
544 bitLen=len(bits)
545 if bitLen%6!=0:
546 bits = bits + BitVector(size=(6 - (bitLen%6)))
547 print "result:",binary.bitvectoais6(bits)[0]
548
549
550
551 elif 'nmea'==options.ioType: sys.exit("FIX: need to implement this capability")
552 else: sys.exit('ERROR: unknown ioType. Help!')
553
554
555 if options.sqlCreate:
556 sqlCreateStr(outfile,options.fieldList,dbType=options.dbType)
557
558 if options.printCsvfieldList:
559
560 if None == options.fieldList: options.fieldList = fieldList
561 import StringIO
562 buf = StringIO.StringIO()
563 for field in options.fieldList:
564 buf.write(field+',')
565 result = buf.getvalue()
566 if result[-1] == ',': print result[:-1]
567 else: print result
568
569 if options.doDecode:
570 for msg in args:
571 bv = None
572
573 if msg[0] in ('$','!') and msg[3:6] in ('VDM','VDO'):
574
575
576 bv = binary.ais6tobitvec(msg.split(',')[5])
577 else:
578
579 binaryMsg=True
580 for c in msg:
581 if c not in ('0','1'):
582 binaryMsg=False
583 break
584 if binaryMsg:
585 bv = BitVector(bitstring=msg)
586 else:
587 bv = binary.ais6tobitvec(msg)
588
589 printFields(decode(bv)
590 ,out=outfile
591 ,format=options.outputType
592 ,fieldList=options.fieldList
593 ,dbType=options.dbType
594 )
595