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