3 parse a MAVLink protocol XML file and generate a C++ implementation 5 Based on C implementation and require C-library for framing. 7 Copyright Andrew Tridgell 2011 8 Copyright Vladimir Ermakov 2016 9 Released under GNU GPL version 3 or later 11 from __future__
import print_function
13 import sys, textwrap, os, time
14 from .
import mavparse, mavtemplate
30 'uint8_t_mavlink_version' :
tmax(8),
32 'uint16_t' :
tmax(16),
34 'uint32_t' :
tmax(32),
36 'uint64_t' :
tmax(64),
43 'NO_DATA':
'NO_DATA_',
46 EType = collections.namedtuple(
'EType', (
'type',
'max'))
50 '''generate main header per XML file''' 51 f = open(os.path.join(directory, xml.basename +
".hpp"), mode=
'w')
54 * @brief MAVLink comm protocol generated from ${basename}.xml 55 * @see http://mavlink.org 65 #define MAVLINK_STX ${protocol_marker} 68 #include "../message.hpp" 71 namespace ${basename} { 74 * Array of msg_entry needed for @p mavlink_parse_char() (trought @p mavlink_get_msg_entry()) 76 constexpr std::array<mavlink_msg_entry_t, ${message_entry_len}> MESSAGE_ENTRIES {{ ${message_entry_array} }}; 79 constexpr auto MAVLINK_VERSION = ${version}; 85 /** @brief ${description} */ 86 enum class ${name}${cxx_underlying_type} 88 ${{entry_flt: ${name_trim}=${value}, /* ${description} |${{param:${description}| }} */ 93 constexpr auto ${enum_end_name} = ${enum_end_value}; 97 } // namespace ${basename} 98 } // namespace mavlink 100 // MESSAGE DEFINITIONS 101 ${{message:#include "./mavlink_msg_${name_lower}.hpp" 105 ${{include_list:#include "../${base}/${base}.hpp" 113 '''generate per-message header for a XML file''' 114 f = open(os.path.join(directory,
'mavlink_msg_%s.hpp' % m.name_lower), mode=
'w')
116 // MESSAGE ${name} support class 121 namespace ${dialect_name} { 125 * @brief ${name} message 129 struct ${name} : mavlink::Message { 130 static constexpr msgid_t MSG_ID = ${id}; 131 static constexpr size_t LENGTH = ${wire_length}; 132 static constexpr size_t MIN_LENGTH = ${wire_min_length}; 133 static constexpr uint8_t CRC_EXTRA = ${crc_extra}; 134 static constexpr auto NAME = "${name}"; 137 ${{fields: ${cxx_type} ${name}; /*< ${units} ${description} */ 141 inline std::string get_name(void) const override 146 inline Info get_message_info(void) const override 148 return { MSG_ID, LENGTH, MIN_LENGTH, CRC_EXTRA }; 151 inline std::string to_yaml(void) const override 153 std::stringstream ss; 155 ss << NAME << ":" << std::endl; 156 ${{fields: ${to_yaml_code} 162 inline void serialize(mavlink::MsgMap &map) const override 164 map.reset(MSG_ID, LENGTH); 166 ${{ordered_fields: map << ${ser_name};${ser_whitespace}// offset: ${wire_offset} 170 inline void deserialize(mavlink::MsgMap &map) override 172 ${{ordered_fields: map >> ${name};${ser_whitespace}// offset: ${wire_offset} 178 } // namespace ${dialect_name} 179 } // namespace mavlink 185 '''generate gtestsuite.hpp per XML file''' 186 f = open(os.path.join(directory,
"gtestsuite.hpp"), mode=
'w')
189 * @brief MAVLink comm testsuite protocol generated from ${basename}.xml 190 * @see http://mavlink.org 195 #include <gtest/gtest.h> 196 #include "${basename}.hpp" 199 using namespace mavlink; 200 #undef MAVLINK_HELPER 205 TEST(${dialect_name}, ${name}) 207 mavlink::mavlink_message_t msg; 208 mavlink::MsgMap map1(msg); 209 mavlink::MsgMap map2(msg); 211 mavlink::${dialect_name}::msg::${name} packet_in{}; 212 ${{fields: packet_in.${name} = ${cxx_test_value}; 215 mavlink::${dialect_name}::msg::${name} packet1{}; 216 mavlink::${dialect_name}::msg::${name} packet2{}; 220 //std::cout << packet1.to_yaml() << std::endl; 222 packet1.serialize(map1); 224 mavlink::mavlink_finalize_message(&msg, 1, 1, packet1.MIN_LENGTH, packet1.LENGTH, packet1.CRC_EXTRA); 226 packet2.deserialize(map2); 228 ${{fields: EXPECT_EQ(packet1.${name}, packet2.${name}); 233 TEST(${dialect_name}_interop, ${name}) 235 mavlink_message_t msg; 238 memset(&msg, 0, sizeof(msg)); 240 mavlink_${name_lower}_t packet_c { 241 ${{ordered_fields: ${c_test_value},}} 244 mavlink::${dialect_name}::msg::${name} packet_in{}; 245 ${{fields: packet_in.${name} = ${cxx_test_value}; 248 mavlink::${dialect_name}::msg::${name} packet2{}; 250 mavlink_msg_${name_lower}_encode(1, 1, &msg, &packet_c); 252 // simulate message-handling callback 253 [&packet2](const mavlink_message_t *cmsg) { 256 packet2.deserialize(map2); 259 ${{fields: EXPECT_EQ(packet_in.${name}, packet2.${name}); 275 '''copy the fixed protocol headers to the target directory''' 276 import shutil, filecmp
278 "2.0": [
'message.hpp',
'msgmap.hpp']
280 basepath = os.path.dirname(os.path.realpath(__file__))
281 srcpath = os.path.join(basepath,
'CPP11/include_v%s' % xml.wire_protocol_version)
282 print(
"Copying fixed C++ headers for protocol %s to %s" % (xml.wire_protocol_version, directory))
283 for h
in hlist[xml.wire_protocol_version]:
284 src = os.path.realpath(os.path.join(srcpath, h))
285 dest = os.path.realpath(os.path.join(directory, h))
286 if src == dest
or (os.path.exists(dest)
and filecmp.cmp(src, dest)):
288 shutil.copy(src, dest)
297 '''remove prefix from enum entry''' 298 pl = prefix.split(
'_')
301 for i
in range(len(pl)):
307 if sl[0][0].isdigit():
311 return MACROSES.get(ret, ret)
315 '''convert unsigned char value to signed char''' 316 return struct.unpack(
'b', struct.pack(
'B', v))[0]
320 '''generate headers for one XML file''' 322 directory = os.path.join(basename, xml.basename)
324 print(
"Generating C++ implementation in directory %s" % directory)
325 mavparse.mkdir_p(directory)
327 if xml.wire_protocol_version != mavparse.PROTOCOL_2_0:
328 raise ValueError(
"C++ implementation only support --wire-protocol=2.0")
331 xml.include_list = []
332 for i
in xml.include:
338 xml.message_entry_len = len(xml.message_crcs)
339 xml.message_entry_array =
', '.join([
340 '{%u, %u, %u, %u, %u, %u}' % (
342 xml.message_crcs[msgid],
343 xml.message_min_lengths[msgid],
344 xml.message_flags[msgid],
345 xml.message_target_system_ofs[msgid],
346 xml.message_target_component_ofs[msgid])
347 for msgid
in sorted(xml.message_crcs.keys())])
350 enum_types = collections.defaultdict(list)
353 for m
in xml.message:
354 m.dialect_name = xml.basename
358 spaces = 30 - len(f.name)
359 f.ser_whitespace =
' ' * (spaces
if spaces > 1
else 1)
362 to_yaml_cast =
'+' if f.type
in [
'char',
'uint8_t',
'int8_t']
else '' 365 enum_types[f.enum].append(
EType(f.type, TYPE_MAX[f.type]))
368 if m.name ==
'TIMESYNC' and f.name ==
'ts1':
375 if f.array_length != 0:
376 f.cxx_type =
'std::array<%s, %s>' % (f.type, f.array_length)
379 if f.type ==
'int8_t':
380 f.test_value = [
fix_int8_t(v)
for v
in f.test_value]
383 f.to_yaml_code =
"""ss << " %s: \\"" << to_string(%s) << "\\"" << std::endl;""" % (f.name, f.name)
385 f.cxx_test_value =
'to_char_array("%s")' % (f.test_value)
386 f.c_test_value =
'"%s"' % f.test_value
388 f.to_yaml_code =
"""ss << " %s: [" << to_string(%s) << "]" << std::endl;""" % (f.name, f.name)
390 f.cxx_test_value =
'{{ %s }}' %
', '.join([
str(v)
for v
in f.test_value])
391 f.c_test_value =
'{ %s }' %
', '.join([
str(v)
for v
in f.test_value])
394 f.to_yaml_code =
"""ss << " %s: " << %s%s << std::endl;""" % (f.name, to_yaml_cast, f.name)
397 if f.type ==
'int8_t':
401 f.cxx_test_value =
"'%s'" % f.test_value
402 elif f.type ==
'int64_t':
403 f.cxx_test_value =
"%sLL" % f.test_value
404 elif f.type ==
'uint64_t':
405 f.cxx_test_value =
"%sULL" % f.test_value
407 f.cxx_test_value = f.test_value
409 f.c_test_value = f.cxx_test_value
414 f.ser_name =
"%s(%s)" % (f.type, f.const_value)
418 underlying_type =
None 419 if e.name
in enum_types:
420 types = enum_types[e.name]
421 types.sort(key=
lambda x: x.max)
422 underlying_type = types[-1]
430 e.entry_flt.append(f)
432 if underlying_type
and f.value > underlying_type.max:
433 raise ValueError(
"Enum %s::%s = %s > MAX(%s)" % (e.name, f.name_trim, f.value, underlying_type.max))
434 elif not underlying_type
and f.value > TYPE_MAX[
'int32_t']:
436 underlying_type =
EType(
'int64_t', TYPE_MAX[
'int64_t'])
438 e.enum_end_name = f.name
439 e.enum_end_value = f.value
441 e.cxx_underlying_type =
' : ' + underlying_type.type
if underlying_type
else '' 444 for m
in xml.message:
450 '''generate serialization MAVLink C++ implemenation''' 451 print(
"Generating C headers")
452 from .
import mavgen_c
453 mavgen_c.generate(basename, xml_list)
def generate_gtestsuite_hpp(directory, xml)
def generate_message_hpp(directory, m)
def generate(basename, xml_list)
def generate_one(basename, xml)
def enum_remove_prefix(prefix, s)
def generate_main_hpp(directory, xml)
def copy_fixed_headers(directory, xml)