123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- #!/usr/bin/env python
- '''
- parse a MAVLink protocol XML file and generate a CSharp implementation
- '''
- from __future__ import print_function
- from builtins import range
- import os
- import platform
- from . import mavtemplate
- t = mavtemplate.MAVTemplate()
- # todo - refactor this in to the other array
- map = {
- 'float' : 'float',
- 'double' : 'double',
- 'char' : 'byte',
- 'int8_t' : 'sbyte',
- 'uint8_t' : 'byte',
- 'uint8_t_mavlink_version' : 'B',
- 'int16_t' : 'Int16',
- 'uint16_t' : 'UInt16',
- 'int32_t' : 'Int32',
- 'uint32_t' : 'UInt32',
- 'int64_t' : 'Int64',
- 'uint64_t' : 'UInt64',
- }
- # Map of field type to bitconverter bytedecoding function, and number of bytes used for the encoding
- mapType = {
- 'float' : ('ToSingle', 4),
- 'double' : ('ToDouble', 8),
- 'int8_t' : ('ToInt8', 1),
- 'uint8_t' : ('ToUInt8', 1),
- 'char' : ('ToChar', 1),
- 'int16_t' : ('ToInt16', 2),
- 'uint16_t' : ('ToUInt16', 2),
- 'int32_t' : ('ToInt32', 4),
- 'uint32_t' : ('ToUInt32', 4),
- 'int64_t' : ('ToInt64', 8),
- 'uint64_t' : ('ToUInt64', 8),
- }
- # Map of field names to names that are C# compatible and not illegal class field names
- mapFieldName = {
- 'fixed' : '@fixed'
- }
-
- def generate_preamble(outf, msgs, args, xml):
- print("Generating preamble")
- t.write(outf, """
- /*
- MAVLink protocol implementation (auto-generated by mavgen.py)
- Generated from: ${FILELIST}
- Note: this file has been auto-generated. DO NOT EDIT
- */
- using System;
- """, {'FILELIST' : ",".join(args)})
- def generate_xmlDocSummary(outf, summaryText, tabDepth):
- indent = '\t' * tabDepth
- escapedText = summaryText.replace("\n","\n%s///" % indent)
- outf.write("\n%s/// <summary>\n" % indent)
- outf.write("%s/// %s\n" % (indent, escapedText))
- outf.write("%s/// </summary>\n" % indent)
-
-
- def generate_enums(outf, enums):
- print("Generating enums")
- outf.write("namespace MavLink\n{\n")
- for e in enums:
- #if len(e.description) > 0:
- generate_xmlDocSummary(outf, e.description, 1)
- outf.write("\tpublic enum %s : uint\n\t{\n" % e.name)
- for entry in e.entry:
- if len(entry.description) > 0:
- generate_xmlDocSummary(outf, entry.description, 2)
- outf.write("\t\t%s = %u,\n" % (entry.name, entry.value))
- outf.write("\n\t}\n\n")
- outf.write("\n}\n")
-
- def generate_classes(outf, msgs):
- print("Generating class definitions")
- outf.write("""
-
-
- namespace MavLink\n{
- public abstract class MavlinkMessage
- {
- public abstract int Serialize(byte[] bytes, ref int offset);
- }
- """)
- for m in msgs:
- if (len(m.description) >0):
- generate_xmlDocSummary(outf, m.description, 1)
- outf.write("""\tpublic class Msg_%s : MavlinkMessage
- {
- """ % m.name.lower())
-
- for f in m.fields:
- if (f.description.upper() != f.name.upper()):
- generate_xmlDocSummary(outf, f.description, 2)
- if (f.array_length):
- outf.write("\t\tpublic %s[] %s; // Array size %s\n" % (map[f.type], mapFieldName.get(f.name, f.name), f.array_length))
- else:
- outf.write("\t\tpublic %s %s;\n" % (map[f.type], mapFieldName.get(f.name, f.name)))
-
- outf.write("""
- public override int Serialize(byte[] bytes, ref int offset)
- {
- return MavLinkSerializer.Serialize_%s(this, bytes, ref offset);
- }
- """ % m.name.upper())
- outf.write("\t}\n\n")
- outf.write("}\n\n")
-
-
- def generate_Deserialization(outf, messages):
-
- # Create the deserialization funcs
- for m in messages:
- classname="Msg_%s" % m.name.lower()
- outf.write("\n\t\tinternal static MavlinkMessage Deserialize_%s(byte[] bytes, int offset)\n\t\t{\n" % (m.name))
- offset = 0
-
- outf.write("\t\t\treturn new %s\n" % classname)
- outf.write("\t\t\t{\n")
- for f in m.ordered_fields:
- if (f.array_length):
- outf.write("\t\t\t\t%s = ByteArrayUtil.%s(bytes, offset + %s, %s),\n" % (mapFieldName.get(f.name, f.name), mapType[f.type][0], offset, f.array_length))
- offset += (f.array_length * mapType[f.type][1])
- continue
-
- # mapping 'char' to byte here since there is no real equivalent in the CLR
- if (f.type == 'uint8_t' or f.type == 'char' ):
- outf.write("\t\t\t\t%s = bytes[offset + %s],\n" % (mapFieldName.get(f.name, f.name),offset))
- offset+=1
- else:
- outf.write("\t\t\t\t%s = bitconverter.%s(bytes, offset + %s),\n" % (mapFieldName.get(f.name, f.name), mapType[f.type][0] , offset))
- offset += mapType[f.type][1]
- outf.write("\t\t\t};\n")
- outf.write("\t\t}\n")
-
- def generate_Serialization(outf, messages):
-
- # Create the table of serialization delegates
- for m in messages:
- classname="Msg_%s" % m.name.lower()
- outf.write("\n\t\tinternal static int Serialize_%s(this %s msg, byte[] bytes, ref int offset)\n\t\t{\n" % (m.name, classname))
- offset=0
-
- # Now (since Mavlink 1.0) we need to deal with ordering of fields
- for f in m.ordered_fields:
-
- if (f.array_length):
- outf.write("\t\t\tByteArrayUtil.ToByteArray(msg.%s, bytes, offset + %s, %s);\n" % (f.name, offset, f.array_length))
- offset += f.array_length * mapType[f.type][1]
- continue
- if (f.type == 'uint8_t'):
- outf.write("\t\t\tbytes[offset + %s] = msg.%s;\n" % (offset,mapFieldName.get(f.name, f.name)))
- offset+=1
- elif (f.type == 'int8_t'):
- outf.write("\t\t\tbytes[offset + %s] = unchecked((byte)msg.%s);\n" % (offset,mapFieldName.get(f.name, f.name)))
- offset+=1
- elif (f.type == 'char'):
- outf.write("\t\t\tbytes[offset + %s] = msg.%s; // todo: check int8_t and char are compatible\n" % (offset,mapFieldName.get(f.name, f.name)))
- offset+=1
- else:
- outf.write("\t\t\tbitconverter.GetBytes(msg.%s, bytes, offset + %s);\n" % (mapFieldName.get(f.name, f.name),offset))
- offset += mapType[f.type][1]
-
- outf.write("\t\t\toffset += %s;\n" % offset)
- outf.write("\t\t\treturn %s;\n" % m.id)
- outf.write("\t\t}\n")
- def generate_CodecIndex(outf, messages, xml):
-
- outf.write("""
- /*
- MAVLink protocol implementation (auto-generated by mavgen.py)
- Note: this file has been auto-generated. DO NOT EDIT
- */
- using System;
- using System.Collections;
- using System.Collections.Generic;
-
- namespace MavLink
- {
- public static class MavlinkSettings
- {
- """)
- outf.write('\t\tpublic const string WireProtocolVersion = "%s";' % xml[0].wire_protocol_version)
- outf.write('\n\t\tpublic const byte ProtocolMarker = 0x%x;' % xml[0].protocol_marker)
- outf.write('\n\t\tpublic const bool CrcExtra = %s;' % str(xml[0].crc_extra).lower())
- outf.write('\n\t\tpublic const bool IsLittleEndian = %s;' % str(xml[0].little_endian).lower())
-
- outf.write("""
- }
-
- public delegate MavlinkMessage MavlinkPacketDeserializeFunc(byte[] bytes, int offset);
- //returns the message ID, offset is advanced by the number of bytes used to serialize
- public delegate int MavlinkPacketSerializeFunc(byte[] bytes, ref int offset, object mavlinkPacket);
-
- public class MavPacketInfo
- {
- public MavlinkPacketDeserializeFunc Deserializer;
- public int [] OrderMap;
- public byte CrcExtra;
- public MavPacketInfo(MavlinkPacketDeserializeFunc deserializer, byte crcExtra)
- {
- this.Deserializer = deserializer;
- this.CrcExtra = crcExtra;
- }
- }
-
- public static class MavLinkSerializer
- {
- public static void SetDataIsLittleEndian(bool isLittle)
- {
- bitconverter.SetDataIsLittleEndian(isLittle);
- }
-
- private static readonly FrameworkBitConverter bitconverter = new FrameworkBitConverter();
-
- public static Dictionary<int, MavPacketInfo> Lookup = new Dictionary<int, MavPacketInfo>
- {""")
- for m in messages:
- classname="Msg_%s" % m.name.lower()
- outf.write("\n\t\t\t{%s, new MavPacketInfo(Deserialize_%s, %s)}," % (m.id, m.name, m.crc_extra))
- outf.write("\n\t\t};\n")
-
- def generate(basename, xml):
- '''generate complete MAVLink CSharp implemenation'''
- structsfilename = basename + '.generated.cs'
-
- msgs = []
- enums = []
- filelist = []
- for x in xml:
- msgs.extend(x.message)
- enums.extend(x.enum)
- filelist.append(os.path.basename(x.filename))
- for m in msgs:
- m.order_map = [ 0 ] * len(m.fieldnames)
- for i in range(0, len(m.fieldnames)):
- m.order_map[i] = m.ordered_fieldnames.index(m.fieldnames[i])
-
- m.fields_in_order = []
- for i in range(0, len(m.fieldnames)):
- m.order_map[i] = m.ordered_fieldnames.index(m.fieldnames[i])
-
- print("Generating messages file: %s" % structsfilename)
- dir = os.path.dirname(structsfilename)
- if not os.path.exists(dir):
- os.makedirs(dir)
- outf = open(structsfilename, "w")
- generate_preamble(outf, msgs, filelist, xml[0])
-
- outf.write("""
-
- using System.Reflection;
-
- [assembly: AssemblyTitle("Mavlink Classes")]
- [assembly: AssemblyDescription("Generated Message Classes for Mavlink. See http://qgroundcontrol.org/mavlink/start")]
- [assembly: AssemblyProduct("Mavlink")]
- [assembly: AssemblyVersion("1.0.0.0")]
- [assembly: AssemblyFileVersion("1.0.0.0")]
- """)
-
- generate_enums(outf, enums)
- generate_classes(outf, msgs)
- outf.close()
-
- print("Generating the (De)Serializer classes")
- serfilename = basename + '_codec.generated.cs'
- outf = open(serfilename, "w")
- generate_CodecIndex(outf, msgs, xml)
- generate_Deserialization(outf, msgs)
- generate_Serialization(outf, msgs)
-
- outf.write("\t}\n\n")
- outf.write("}\n\n")
-
- outf.close()
-
- # Some build commands depend on the platform - eg MS .NET Windows Vs Mono on Linux
- if platform.system() == "Windows":
- winpath=os.environ['WinDir']
- cscCommand = winpath + "\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe"
-
- if not os.path.exists(cscCommand):
- print("\nError: CS compiler not found. .Net Assembly generation skipped")
- return
- else:
- print("Error:.Net Assembly generation not yet supported on non Windows platforms")
- return
- cscCommand = "csc"
- print("Compiling Assembly for .Net Framework 4.0")
-
- generatedCsFiles = [ serfilename, structsfilename]
-
- includedCsFiles = [ 'CS/common/ByteArrayUtil.cs', 'CS/common/FrameworkBitConverter.cs', 'CS/common/Mavlink.cs' ]
-
- outputLibraryPath = os.path.normpath(dir + "/mavlink.dll")
-
- compileCommand = "%s %s" % (cscCommand, "/target:library /debug /out:" + outputLibraryPath)
- compileCommand = compileCommand + " /doc:" + os.path.normpath(dir + "/mavlink.xml")
-
- for csFile in generatedCsFiles + includedCsFiles:
- compileCommand = compileCommand + " " + os.path.normpath(csFile)
-
- #print("Cmd:" + compileCommand)
- res = os.system (compileCommand)
-
- if res == 0:
- print("Generated %s OK" % outputLibraryPath)
- else:
- print("Error")
- print("Error: Compilation failed. (" + str(res) + ")")
- raise SystemError("Compilation failed. (" + str(res) + ")")
|