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