mavgen_wlua.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 '''
00003 parse a MAVLink protocol XML file and generate a Wireshark LUA dissector
00004 
00005 Copyright Holger Steinhaus 2012
00006 Released under GNU GPL version 3 or later
00007 
00008 Instructions for use: 
00009 1. python -m pymavlink.generator.mavgen --lang=wlua mymavlink.xml -o ~/.wireshark/plugins/mymavlink.lua
00010 2. convert binary stream int .pcap file format (see ../examples/mav2pcap.py)
00011 3. open the pcap file in Wireshark
00012 '''
00013 
00014 import sys, textwrap, os, re
00015 from . import mavparse, mavtemplate
00016 
00017 t = mavtemplate.MAVTemplate()
00018 
00019 
00020 def lua_type(mavlink_type):
00021     # qnd typename conversion
00022     if (mavlink_type=='char'):
00023         lua_t = 'uint8'
00024     else:
00025         lua_t = mavlink_type.replace('_t', '')
00026     return lua_t
00027 
00028 def type_size(mavlink_type):
00029     # infer size of mavlink types
00030     re_int = re.compile('^(u?)int(8|16|32|64)_t$')
00031     int_parts = re_int.findall(mavlink_type)
00032     if len(int_parts):
00033         return int(int_parts[0][1])/8
00034     elif mavlink_type == 'float':
00035         return 4
00036     elif mavlink_type == 'double':
00037         return 8
00038     elif mavlink_type == 'char':
00039         return 1
00040     else:
00041         raise Exception('unsupported MAVLink type - please fix me')
00042     
00043 
00044 def mavfmt(field):
00045     '''work out the struct format for a type'''
00046     map = {
00047         'float'    : 'f',
00048         'double'   : 'd',
00049         'char'     : 'c',
00050         'int8_t'   : 'b',
00051         'uint8_t'  : 'B',
00052         'uint8_t_mavlink_version'  : 'B',
00053         'int16_t'  : 'h',
00054         'uint16_t' : 'H',
00055         'int32_t'  : 'i',
00056         'uint32_t' : 'I',
00057         'int64_t'  : 'q',
00058         'uint64_t' : 'Q',
00059         }
00060 
00061     if field.array_length:
00062         if field.type in ['char', 'int8_t', 'uint8_t']:
00063             return str(field.array_length)+'s'
00064         return str(field.array_length)+map[field.type]
00065     return map[field.type]
00066 
00067 
00068 def generate_preamble(outf):
00069     print("Generating preamble")
00070     t.write(outf, 
00071 """
00072 -- Wireshark dissector for the MAVLink protocol (please see http://qgroundcontrol.org/mavlink/start for details) 
00073 
00074 mavlink_proto = Proto("mavlink_proto", "MAVLink protocol")
00075 f = mavlink_proto.fields
00076 
00077 payload_fns = {}
00078 
00079 """ )
00080     
00081     
00082 def generate_body_fields(outf):
00083     t.write(outf, 
00084 """
00085 f.magic = ProtoField.uint8("mavlink_proto.magic", "Magic value / version", base.HEX)
00086 f.length = ProtoField.uint8("mavlink_proto.length", "Payload length")
00087 f.sequence = ProtoField.uint8("mavlink_proto.sequence", "Packet sequence")
00088 f.sysid = ProtoField.uint8("mavlink_proto.sysid", "System id", base.HEX)
00089 f.compid = ProtoField.uint8("mavlink_proto.compid", "Component id", base.HEX)
00090 f.msgid = ProtoField.uint8("mavlink_proto.msgid", "Message id", base.HEX)
00091 f.crc = ProtoField.uint16("mavlink_proto.crc", "Message CRC", base.HEX)
00092 f.payload = ProtoField.uint8("mavlink_proto.crc", "Payload", base.DEC, messageName)
00093 f.rawheader = ProtoField.bytes("mavlink_proto.rawheader", "Unparsable header fragment")
00094 f.rawpayload = ProtoField.bytes("mavlink_proto.rawpayload", "Unparsable payload")
00095 
00096 """)
00097 
00098 
00099 def generate_msg_table(outf, msgs):
00100     t.write(outf, """
00101 messageName = {
00102 """)
00103     for msg in msgs:
00104         assert isinstance(msg, mavparse.MAVType)
00105         t.write(outf, """
00106     [${msgid}] = '${msgname}',
00107 """, {'msgid':msg.id, 'msgname':msg.name})
00108 
00109     t.write(outf, """
00110 }
00111 
00112 """)
00113         
00114 
00115 def generate_msg_fields(outf, msg):
00116     assert isinstance(msg, mavparse.MAVType)
00117     for f in msg.fields:
00118         assert isinstance(f, mavparse.MAVField)
00119         mtype = f.type
00120         ltype = lua_type(mtype)
00121         count = f.array_length if f.array_length>0 else 1
00122 
00123         # string is no array, but string of chars
00124         if mtype == 'char' and count > 1: 
00125             count = 1
00126             ltype = 'string'
00127         
00128         for i in range(0,count):
00129             if count>1: 
00130                 array_text = '[' + str(i) + ']'
00131                 index_text = '_' + str(i)
00132             else:
00133                 array_text = ''
00134                 index_text = ''
00135                 
00136             t.write(outf,
00137 """
00138 f.${fmsg}_${fname}${findex} = ProtoField.${ftype}("mavlink_proto.${fmsg}_${fname}${findex}", "${fname}${farray} (${ftype})")
00139 """, {'fmsg':msg.name, 'ftype':ltype, 'fname':f.name, 'findex':index_text, 'farray':array_text})        
00140 
00141     t.write(outf, '\n\n')
00142 
00143 def generate_field_dissector(outf, msg, field):
00144     assert isinstance(field, mavparse.MAVField)
00145     
00146     mtype = field.type
00147     size = type_size(mtype)
00148     ltype = lua_type(mtype)
00149     count = field.array_length if field.array_length>0 else 1
00150 
00151     # string is no array but string of chars
00152     if mtype == 'char': 
00153         size = count
00154         count = 1
00155     
00156     # handle arrays, but not strings
00157     
00158     for i in range(0,count):
00159         if count>1: 
00160             index_text = '_' + str(i)
00161         else:
00162             index_text = ''
00163         t.write(outf,
00164 """
00165     tree:add_le(f.${fmsg}_${fname}${findex}, buffer(offset, ${fbytes}))
00166     offset = offset + ${fbytes}
00167     
00168 """, {'fname':field.name, 'ftype':mtype, 'fmsg': msg.name, 'fbytes':size, 'findex':index_text})
00169     
00170 
00171 def generate_payload_dissector(outf, msg):
00172     assert isinstance(msg, mavparse.MAVType)
00173     t.write(outf, 
00174 """
00175 -- dissect payload of message type ${msgname}
00176 function payload_fns.payload_${msgid}(buffer, tree, msgid, offset)
00177 """, {'msgid':msg.id, 'msgname':msg.name})
00178     
00179     for f in msg.ordered_fields:
00180         generate_field_dissector(outf, msg, f)
00181 
00182 
00183     t.write(outf, 
00184 """
00185     return offset
00186 end
00187 
00188 
00189 """)
00190     
00191 
00192 def generate_packet_dis(outf):
00193     t.write(outf, 
00194 """
00195 -- dissector function
00196 function mavlink_proto.dissector(buffer,pinfo,tree)
00197     local offset = 0
00198             
00199     local subtree = tree:add (mavlink_proto, buffer(), "MAVLink Protocol ("..buffer:len()..")")
00200 
00201     -- decode protocol version first
00202     local version = buffer(offset,1):uint()
00203     local protocolString = ""
00204     
00205     if (version == 0xfe) then
00206             protocolString = "MAVLink 1.0"
00207     elseif (version == 0x55) then
00208             protocolString = "MAVLink 0.9"
00209     else
00210             protocolString = "unknown"
00211     end 
00212 
00213     -- some Wireshark decoration
00214     pinfo.cols.protocol = protocolString
00215 
00216     -- HEADER ----------------------------------------
00217     
00218     local msgid
00219     if (buffer:len() - 2 - offset > 6) then
00220         -- normal header
00221         local header = subtree:add("Header")
00222         header:add(f.magic,version)
00223         offset = offset + 1
00224         
00225         local length = buffer(offset,1)
00226         header:add(f.length, length)
00227         offset = offset + 1
00228         
00229         local sequence = buffer(offset,1)
00230         header:add(f.sequence, sequence)
00231         offset = offset + 1
00232         
00233         local sysid = buffer(offset,1)
00234         header:add(f.sysid, sysid)
00235         offset = offset + 1
00236     
00237         local compid = buffer(offset,1)
00238         header:add(f.compid, compid)
00239         offset = offset + 1
00240         
00241         pinfo.cols.src = "System: "..tostring(sysid:uint())..', Component: '..tostring(compid:uint())
00242     
00243         msgid = buffer(offset,1)
00244         header:add(f.msgid, msgid)
00245         offset = offset + 1
00246     else 
00247         -- handle truncated header
00248         local hsize = buffer:len() - 2 - offset
00249         subtree:add(f.rawheader, buffer(offset, hsize))
00250         offset = offset + hsize
00251     end
00252 
00253 
00254     -- BODY ----------------------------------------
00255     
00256     -- dynamically call the type-specific payload dissector    
00257     local msgnr = msgid:uint()
00258     local dissect_payload_fn = "payload_"..tostring(msgnr)
00259     local fn = payload_fns[dissect_payload_fn]
00260     
00261     if (fn == nil) then
00262         pinfo.cols.info:append ("Unkown message type   ")
00263         subtree:add_expert_info(PI_MALFORMED, PI_ERROR, "Unkown message type")
00264         size = buffer:len() - 2 - offset
00265         subtree:add(f.rawpayload, buffer(offset,size))
00266         offset = offset + size
00267     else
00268         local payload = subtree:add(f.payload, msgid)
00269         pinfo.cols.dst:set(messageName[msgid:uint()])
00270         pinfo.cols.info = messageName[msgid:uint()]
00271         offset = fn(buffer, payload, msgid, offset)
00272     end
00273 
00274     -- CRC ----------------------------------------
00275     local crc = buffer(offset,2)
00276     subtree:add_le(f.crc, crc)
00277     offset = offset + 2
00278 
00279 end
00280 
00281 
00282 """)
00283     
00284 
00285 
00286 def generate_epilog(outf):
00287     print("Generating epilog")
00288     t.write(outf, 
00289 """   
00290 -- bind protocol dissector to USER0 linktype
00291 
00292 wtap_encap = DissectorTable.get("wtap_encap")
00293 wtap_encap:add(wtap.USER0, mavlink_proto)
00294 
00295 -- bind protocol dissector to port 14550
00296 
00297 local udp_dissector_table = DissectorTable.get("udp.port")
00298 udp_dissector_table:add(14550, mavlink_proto)
00299 """)
00300 
00301 def generate(basename, xml):
00302     '''generate complete python implemenation'''
00303     if basename.endswith('.lua'):
00304         filename = basename
00305     else:
00306         filename = basename + '.lua'
00307 
00308     msgs = []
00309     enums = []
00310     filelist = []
00311     for x in xml:
00312         msgs.extend(x.message)
00313         enums.extend(x.enum)
00314         filelist.append(os.path.basename(x.filename))
00315 
00316     for m in msgs:
00317         if xml[0].little_endian:
00318             m.fmtstr = '<'
00319         else:
00320             m.fmtstr = '>'
00321         for f in m.ordered_fields:
00322             m.fmtstr += mavfmt(f)
00323         m.order_map = [ 0 ] * len(m.fieldnames)
00324         for i in range(0, len(m.fieldnames)):
00325             m.order_map[i] = m.ordered_fieldnames.index(m.fieldnames[i])
00326 
00327     print("Generating %s" % filename)
00328     outf = open(filename, "w")
00329     generate_preamble(outf)
00330     generate_msg_table(outf, msgs)
00331     generate_body_fields(outf)
00332     
00333     for m in msgs:
00334         generate_msg_fields(outf, m)
00335     
00336     for m in msgs:
00337         generate_payload_dissector(outf, m)
00338     
00339     generate_packet_dis(outf)
00340 #    generate_enums(outf, enums)
00341 #    generate_message_ids(outf, msgs)
00342 #    generate_classes(outf, msgs)
00343 #    generate_mavlink_class(outf, msgs, xml[0])
00344 #    generate_methods(outf, msgs)
00345     generate_epilog(outf)
00346     outf.close()
00347     print("Generated %s OK" % filename)
00348 


mavlink
Author(s): Lorenz Meier
autogenerated on Wed Sep 9 2015 18:06:17