pymavlink 源码剖析(二)之生成代码

 2023-09-19 阅读 24 评论 0

摘要:文章目录1 引言2 C 代码生成3 generate_one 函数分析4 MAVTemplate5 头文件生成 相关: pymavlink 源码剖析(一)之XML文件的数据解析MAVLink 协议解析之原理篇;MAVLink 协议解析之XML定义篇; 1 引言 html代码。本文是 pymavlink 源码剖析 文章的第二篇。上一篇

文章目录

      • 1 引言
      • 2 C 代码生成
      • 3 generate_one 函数分析
      • 4 MAVTemplate
      • 5 头文件生成

相关:

  1. pymavlink 源码剖析(一)之XML文件的数据解析
  2. MAVLink 协议解析之原理篇;
  3. MAVLink 协议解析之XML定义篇;

1 引言

html代码。本文是 pymavlink 源码剖析 文章的第二篇。上一篇 pymavlink 源码剖析(一)之XML文件的数据解析 主要是分析 pymavlinkMAVLINK XML 格式的定义文件是如何解析的,这一篇则分析 pymavlink 是如何在解析获得的结果的基础上生成目标代码的。本篇主要关注于 C 的目标代码生成的实现。即

 mavgen_c.generate(opts.output, xml)

的内部实现,如果对 MAVLink 协议还不太熟悉请参考文章目录下方 “相关” 里面给出的链接。
为了方便阅读,这里把上篇的 optsxml 的内容在这里重新列出来
opts:

definitions: ['/mnt/d/github_/mavl...ommon.xml']
error_limit: 200
language: 'c'
output: '/mnt/d/github_/mavlink_cpp'
strict_units: False
validate: False
wire_protocol: '2.0'

xml:

[<pymavlink.generator.mavparse.MAVXML object at 0x7f5a30ec1fd0>]

matlab源代码。列表中的 MAVXML object 展开为:

allow_extensions: True
basename: 'common'
basename_upper: 'COMMON'
command_24bit: True
crc_extra: True
crc_struct: True
enum: [<pymavlink.generator...a30ed02b0>, <pymavlink.generator...a30ed06d8>, <pymavlink.generator...a30edf208>, <pymavlink.generator...a30edf278>, <pymavlink.generator...a30edf470>, <pymavlink.generator...a30edf898>, <pymavlink.generator...a30edfa90>, <pymavlink.generator...a30edfba8>, <pymavlink.generator...a30edfc18>, <pymavlink.generator...a30ee50b8>, <pymavlink.generator...a30ef0be0>, <pymavlink.generator...a30efb3c8>, <pymavlink.generator...a30efb630>, <pymavlink.generator...a30efba58>, ...]
filename: '/mnt/d/github_/mavlink/message_definitions/v1.0/common.xml'
include: []
largest_payload: 255
little_endian: True
message: [<pymavlink.generator...a30058dd8>, <pymavlink.generator...a3005e2e8>, <pymavlink.generator...a3005e9b0>, <pymavlink.generator...a3005eb00>, <pymavlink.generator...a3005edd8>, <pymavlink.generator...a30063208>, <pymavlink.generator...a300634a8>, <pymavlink.generator...a30063630>, <pymavlink.generator...a30063cf8>, <pymavlink.generator...a30063eb8>, <pymavlink.generator...a300671d0>, <pymavlink.generator...a30067358>, <pymavlink.generator...a300675f8>, <pymavlink.generator...a30067978>, ...]
message_crcs: {0: 50, 1: 124, 2: 137, 4: 237, 5: 217, 6: 104, 7: 119, 8: 117, 11: 89, 20: 214, 21: 159, 22: 220, 23: 168, 24: 24, ...}
message_flags: {0: 0, 1: 0, 2: 0, 4: 3, 5: 1, 6: 0, 7: 0, 8: 0, 11: 1, 20: 3, 21: 3, 22: 0, 23: 3, 24: 0, ...}
message_lengths: {0: 9, 1: 31, 2: 12, 4: 14, 5: 28, 6: 3, 7: 32, 8: 36, 11: 6, 20: 20, 21: 2, 22: 25, 23: 23, 24: 52, ...}
message_min_lengths: {0: 9, 1: 31, 2: 12, 4: 14, 5: 28, 6: 3, 7: 32, 8: 36, 11: 6, 20: 20, 21: 2, 22: 25, 23: 23, 24: 30, ...}
message_names: {0: 'HEARTBEAT', 1: 'SYS_STATUS', 2: 'SYSTEM_TIME', 4: 'PING', 5: 'CHANGE_OPERATOR_CONTROL', 6: 'CHANGE_OPERATOR_CONTROL_ACK', 7: 'AUTH_KEY', 8: 'LINK_NODE_STATUS', 11: 'SET_MODE', 20: 'PARAM_REQUEST_READ', 21: 'PARAM_REQUEST_LIST', 22: 'PARAM_VALUE', 23: 'PARAM_SET', 24: 'GPS_RAW_INT', ...}
message_target_component_ofs: {0: 0, 1: 0, 2: 0, 4: 13, 5: 0, 6: 0, 7: 0, 8: 0, 11: 0, 20: 3, 21: 1, 22: 0, 23: 5, 24: 0, ...}
message_target_system_ofs: {0: 0, 1: 0, 2: 0, 4: 12, 5: 0, 6: 0, 7: 0, 8: 0, 11: 4, 20: 2, 21: 0, 22: 0, 23: 4, 24: 0, ...}
parse_time: 'Fri Mar 27 2020'
protocol_marker: 253
sort_fields: True
version: 3
wire_protocol_version: '2.0'
__len__: 1

2 C 代码生成

pymavlink 生成 C 的原理是模板填充 — 给出代码模板然后把从 XML 中解析处的相关参数填充到其中。对应于C 代码生成的函数主要在 mavgen_c.py 这个文件里面。 在 mavgen_c.py 文件中定义了如下五个函数:

  • generate: 代码生成入口函数
  • generate_main_h: 生成主头文件,所谓主头文件即以 XML 文件名的去掉后缀来命名的文件;
  • generate_mavlink_h: 生成 mavlink.h 文件;
  • generate_message_h: 生成各个message 的头文件;
  • generate_one: 由 generate 调用,分别对每一个 XML 生成头文件;
  • generate_version_h: 生成 version.h 文件,其中包括了 MAVLink 的版本号,代码生成时间等;
  • generate_testsuite_h 生成 testsuite.h 文件,testsuite.h 为测试生成的头文件的代码。

这里首先从generate 函数看起

python怎么运行代码、代码段 1

703 def generate(basename, xml_list):
704     '''generate complete MAVLink C implemenation'''
705
706     for idx in range(len(xml_list)):
707         xml = xml_list[idx]
708         xml.xml_idx = idx
709         generate_one(basename, xml)
710     copy_fixed_headers(basename, xml_list[0])

参数:

  • basename:“common”
  • xml_list: [<pymavlink.generator.mavparse.MAVXML object at 0x7f5a30ec1fd0>]

从这段代码可以看出其只是对传入进来的 xml_list 列表调用 generate_one 进行解析,由于xml_list 只有一个元素所以循环只会执行一次。在generate_one 解析之后调用了 copy_fixed_headers 函数进行收尾。 对于generate_one 函数相对略复杂单独放在 第3节 说明,这里先分析copy_fixed_headers 函数。

编译器源码。代码段 2

519 def copy_fixed_headers(directory, xml):
520     '''copy the fixed protocol headers to the target directory'''
521     import shutil, filecmp
522     hlist = {
523         "0.9": [ 'protocol.h', 'mavlink_helpers.h', 'mavlink_types.h', 'checksum.h' ],
524         "1.0": [ 'protocol.h', 'mavlink_helpers.h', 'mavlink_types.h', 'checksum.h', 'mavlink_conversions.h' ],
525         "2.0": [ 'protocol.h', 'mavlink_helpers.h', 'mavlink_types.h', 'checksum.h', 'mavlink_conversions.h',
526                  'mavlink_get_info.h', 'mavlink_sha256.h' ]
527         }
528     basepath = os.path.dirname(os.path.realpath(__file__))
529     srcpath = os.path.join(basepath, 'C/include_v%s' % xml.wire_protocol_version)
530     print("Copying fixed headers for protocol %s to %s" % (xml.wire_protocol_version, directory))
531     for h in hlist[xml.wire_protocol_version]:
532         src = os.path.realpath(os.path.join(srcpath, h))
533         dest = os.path.realpath(os.path.join(directory, h))
534         if src == dest or (os.path.exists(dest) and filecmp.cmp(src, dest)):
535             continue
536         shutil.copy(src, dest)

参数:

  • directory: “common”
  • xml: <pymavlink.generator.mavparse.MAVXML object at 0x7f5a30ec1fd0>

可以看出,copy_fixed_headers 函数的主要功能就是依据当前的 MAVLink 版本把一些相对不要模板生成的头文件挨个复制到输出文件夹下。

3 generate_one 函数分析

generate_one 函数可以归纳为几部分:

  1. MAVXML 中的数据进行预处理
  2. 调用头文件生成函数生成头文件

第 1 部分是 generate_one 的主要功能代码所覆盖的内容,第二部分则由下面几节涵盖。

属性名称明确化,方便后面进行处理

545     directory = os.path.join(basename, xml.basename)
546
547     print("Generating C implementation in directory %s" % directory)
548     mavparse.mkdir_p(directory)
549
550     if xml.little_endian:
551         xml.mavlink_endian = "MAVLINK_LITTLE_ENDIAN"
552     else:
553         xml.mavlink_endian = "MAVLINK_BIG_ENDIAN"
554
555     if xml.crc_extra:
556         xml.crc_extra_define = "1"
557     else:
558         xml.crc_extra_define = "0"
559
560     if xml.command_24bit:
561         xml.command_24bit_define = "1"
562     else:
563         xml.command_24bit_define = "0"
564
565     if xml.sort_fields:
566         xml.aligned_fields_define = "1"
567     else:
568         xml.aligned_fields_define = "0"

·

导入的其他 XML 文件的配置,例如对于 paparazzi.xml ,由于其中包含 <incude> common.xml</incldue>, 此时xml.inlcude 的值为 common.xml.

570     # work out the included headers
571     xml.include_list = []
572     for i in xml.include:
573         base = i[:-4]
574         xml.include_list.append(mav_include(base))

mav_include 的定义如下:

538 class mav_include(object):
539     def __init__(self, base):
540         self.base = base

xml.include_list 中包含一个 mav_include 实例,该实例的base 属性为common
.

生成 message_lengths_array 字符串,该字符串描述以每个消息的message_min_lengths 拼接而成,以逗号分隔,主要是针对的是 mavlink 0.9 版本。关于message_min_lengths 的含义见 上篇的代码段10

576     # form message lengths array
577     xml.message_lengths_array = ''
578     if not xml.command_24bit:
579         for msgid in range(256):
580             mlen = xml.message_min_lengths.get(msgid, 0)
581             xml.message_lengths_array += '%u, ' % mlen
582         xml.message_lengths_array = xml.message_lengths_array[:-2]

.
生成 crc 字节
‘{0, 50, 9, 9, 0, 0, 0}, ’

584     # and message CRCs array
585     xml.message_crcs_array = ''
586     if xml.command_24bit:
587         # we sort with primary key msgid
588         for msgid in sorted(xml.message_crcs.keys()):
589             xmy.message_crcs_array += '{%u, %u, %u, %u, %u, %u, %u}, ' % (msgid,
590                                                                       xml.message_crcs[msgid],
591                                                                       xml.message_min_lengths[msgid],
592                                                                       xml.message_lengths[msgid],
593                                                                       xml.message_flags[msgid],
594                                                                       xml.message_target_system_ofs[msgid],
595                                                                       xml.message_target_component_ofs[msgid])
596     else:
597         for msgid in range(256):
598             crc = xml.message_crcs.get(msgid, 0)
599             xml.message_crcs_array += '%u, ' % crc

.
生成消息名称
MAVLINK_MESSAGE_INFO_HEARTBEAT, MAVLINK_MESSAGE_INFO_SYS_STATUS

602     # form message info array
603     xml.message_info_array = ''
604     if xml.command_24bit:
605         # we sort with primary key msgid
606         for msgid in sorted(xml.message_names.keys()):
607             name = xml.message_names[msgid]
608             xml.message_info_array += 'MAVLINK_MESSAGE_INFO_%s, ' % name
609     else:
610         for msgid in range(256):
611             name = xml.message_names.get(msgid, None)
612             if name is not None:
613                 xml.message_info_array += 'MAVLINK_MESSAGE_INFO_%s, ' % name
614             else:
615                 # Several C compilers don't accept {NULL} for
616                 # multi-dimensional arrays and structs
617                 # feed the compiler a "filled" empty message
618                 xml.message_info_array += '{"EMPTY",0,{{"","",MAVLINK_TYPE_CHAR,0,0,0}}}, '
619     xml.message_info_array = xml.message_info_array[:-2]

.
message_name_array : '{ “ACTUATOR_CONTROL_TARGET”, 140 }, ’

621     # form message name array
622     xml.message_name_array = ''
623     # sort by names
624     for msgid, name in sorted(iteritems(xml.message_names), key=lambda k_v: (k_v[1], k_v[0])):
625         xml.message_name_array += '{ "%s", %u }, ' % (name, msgid)
626     xml.message_name_array = xml.message_name_array[:-2]

.
添加一些包括打印的格式格式字符串

628     # add some extra field attributes for convenience with arrays
629     for m in xml.message:
630         m.msg_name = m.name
631         if xml.crc_extra:
632             m.crc_extra_arg = ", %s" % m.crc_extra
633         else:
634             m.crc_extra_arg = ""
635         for f in m.fields:
636             if f.print_format is None:
637                 f.c_print_format = 'NULL'
638             else:
639                 f.c_print_format = '"%s"' % f.print_format
640             if f.array_length != 0:
641                 f.array_suffix = '[%u]' % f.array_length
642                 f.array_prefix = '*'
643                 f.array_tag = '_array'
644                 f.array_arg = ', %u' % f.array_length
645                 f.array_return_arg = '%s, %u, ' % (f.name, f.array_length)
646                 f.array_const = 'const '
647                 f.decode_left = ''
648                 f.decode_right = ', %s->%s' % (m.name_lower, f.name)
649                 f.return_type = 'uint16_t'
650                 f.get_arg = ', %s *%s' % (f.type, f.name)
651                 if f.type == 'char':
652                     f.c_test_value = '"%s"' % f.test_value
653                 else:
654                     test_strings = []
655                     for v in f.test_value:
655                     for v in f.test_value:
656                         test_strings.append(str(v))
657                     f.c_test_value = '{ %s }' % ', '.join(test_strings)
658             else:
659                 f.array_suffix = ''
660                 f.array_prefix = ''
661                 f.array_tag = ''
662                 f.array_arg = ''
663                 f.array_return_arg = ''
664                 f.array_const = ''
665                 f.decode_left = "%s->%s = " % (m.name_lower, f.name)
666                 f.decode_right = ''
667                 f.get_arg = ''
668                 f.return_type = f.type
669                 if f.type == 'char':
670                     f.c_test_value = "'%s'" % f.test_value
671                 elif f.type == 'uint64_t':
672                     f.c_test_value = "%sULL" % f.test_value
673                 elif f.type == 'int64_t':
674                     f.c_test_value = "%sLL" % f.test_value
675                 else:
676                     f.c_test_value = f.test_value

.
添加一些域

678     # cope with uint8_t_mavlink_version
679     for m in xml.message:
680         m.arg_fields = []
681         m.array_fields = []
682         m.scalar_fields = []
683         for f in m.ordered_fields:
684             if f.array_length != 0:
685                 m.array_fields.append(f)
686             else:
687                 m.scalar_fields.append(f)
688         for f in m.fields:
689             if not f.omit_arg:
690                 m.arg_fields.append(f)
691                 f.putname = f.name
692             else:
693                 f.putname = f.const_value

至此所有的第 1 部分关于预处理的内容都分析完了,下面几节分析具体的头文件生成函数

4 MAVTemplate

由于MAVTemplate 的结构不够清晰,这里先阐述其原理,然后再对照代码具体描述其功能。
假设有 python 对象

class A:crc = '{0,1,2}'enum = [{'name':'good', 'length':4},{'name':'bad', 'length':3}]messages = [{'name':'status', 'entry':['header', 'body']}]a = A()

并假设有下面的模板

crc = ${crc};
${{enum:${name} = ${length};
}}

则对 a 应用模板则会生成

crc = {0, 1, 2};
good = 4;
bad = 3;

首先 ${crc} 会被 a.crc 值替换为 {0,1,2,3} 于是 crc = ${crc} 变成了 crc = {0, 1, 2}。对于

${{enum:${name} = ${length};
}}

首先会在 a 中找到 enum 属性值,然后其值中的每个元素按照 ${name} = {length};\n展开,注意到其中包括了换行符的 ascii 的 C 语言中的转义表示\n

下面开始剖析代码,MAVTemplate 中主要

  • find_end: 找到字符的匹配位置
  • find_var_end: 通过调用 find_end 找到字符串中 } 的位置;
  • find_rep_end: 通过调用 find_end 找到支付串中 }} 的位置;
  • substitute: 按照模式${}${{}} 执行替换;
  • write: 调用 substitute 进行替换。

5 头文件生成

这几个函数调用MAVTemplate 对象的方法生成具体的头文件。

  • generate_mavlink_h: 生成 mavlink.h
  • generate_version_h: 生成 version.h
  • generate_main_h: 生成和 XML 文件同名的头文件,如对于 common.xml 会生成 common.h
  • generate_message_h: 生成以 mavlink_msg_ 开头的各种消息类型的头文件
  • generate_testsuite_h: 生成 testsuite.h

下面大概概述一下各个头文件的内容:
mavlink.h: 包含了和版本相关的数据帧的配置同时 include 了 version.h 和 common.h
version.h: 包含了版本信息
common.h: 包含了针对 common.xml 中定义的消息类型的数据结构并include 了所有消息头文件
mavlink_msg_*.h: 所有消息实现的头文件
testsuite.h: 基于内存的消息测试在这里插入图片描述

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/77792.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息