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