当前位置: 首页 > news >正文

基于CAPL的DDS子消息解析- Data

1往期回顾

通过《DDS—RTPS一致性测试案例分析》一文,我们了解到 Data 子消息在 Data Distribution Service(DDS) 通信中扮演着至关重要的角色。它不仅负责 DDS 实体的 Simple Participant Discovery Protocol (SPDP) 发现流程,还参与了 Simple Endpoint Discovery Protocol (SEDP) 中的 Topic 和 QoS 确认流程。此外,Data 子消息还承担着发布 Topic 信息、声明存活性以及通知对端自身下线等功能,堪称 DDS 协议中“最忙碌”的子消息。为了更深入地理解 Data 子消息的工作原理,并能够通过 CAPL 仿真和解析该子消息,我们需要揭开其神秘的面纱,详细分析其结构和功能。这不仅有助于我们更好地掌握 DDS 协议的通信机制,还能为 CAPL 脚本开发提供清晰的思路和实用的方法。接下来,我们将从 Data 子消息的结构、字段作用以及实际应用场景入手,逐步解析其核心功能,并通过 CAPL 仿真实例展示如何解析和生成 Data 子消息,为开发高效的 DDS 通信脚本奠定基础。

2 Data详解

Data子消息是由The Real-time Publish-Subscribe Protocol(RTPS) DDS Interoperability Wire Protocol Specification定义的。RTPS 是 DDS 协议的核心实现,负责定义数据分发的具体通信机制。根据 RTPS 协议,Data 子消息的报文结构如下所示:

图1 Data定义

2.1 DATA

表示SubmessageId,为固定值,是0x15。RTPS中定义的消息类型为SubmessageFlag,占1字节,因此在CAPL中可以用byte类型表示,如“byte  submessageId;”。

2.2 Flags

消息类型为SubmessageFlag ,占1字节, Flags只使用了高5位。由低到高分别简写为N、K、D、Q和E,其中N = 1表示serializedPayload采用非标准格式,否则采用DDS规定的标准格式。标准格式指的是PL_CDR_BE、PL_CDR_LE等序列化格式,RTPS V2.5的10章节有介绍,而这些序列化细则主要是在DDS-XTypes协议中定义;Q = 1表示消息中包含inlineQos参数,否则不包含。当Q = 0时,解析Data时要跳过inlineQos参数;E = 1表示为数据格式为Little_Endian,否则为Big_Endian;K和D是组合使用的,如下:

•D=0和K=0表示没有serializedPayload。

•D=1和K=0表示serializedPayload包含序列化的数据。

•D=0和K=1表示serializedPayload包含序列化的Key。

•D=1和K=1在这个版本的协议中是一个无效的组合。

Flags在CAPL中可以用byte类型表示,如“byte  flags;”,但是实际操作中会频繁的用到D、K、E等标志位,所以可以采用struct的数据类型,如下所示:

  struct DataFlags_t {

     byte value;  //原始Flags值

     byte E;

     byte Q;

     byte D;

     byte K;

     byte N;

};

这样在脚本中就可以直观地使用标志位,如Data.flags.E、Data.flags.Q等。

2.3 OctetsToNextHeaderextraFlagsOctetsToInlineQos

OctetsToNextHeader字段表示当前子消息的长度,其值为到下一个子消息头起始位置的字节数。RTPS中定义的消息类型为unsigned short,占2字节,在CAPL中可以用word类型表示,如“word submessageLength;”。

extraFlags未使用,该版本应该将参数设置为零。RTPS中定义的消息类型为unsigned short,占2字节,在CAPL中可以用word类型表示,如“word extraFlags;”。

OctetsToInlineQos表示紧跟该字段的第一个字节开始直到inlineQos的第一个字节的字节数。如果inlineQos不存在,则表示到serializedPayload的第一个字节的字节数。RTPS中定义的消息类型为unsigned short,占2 字节,在CAPL中可以用word类型表示,如“word inlineQosFlag”。

2.4 readerIdwriterId

readerId表示消息接收实体Id,writerId表示消息发送实体Id。应该重点是关注这两个参数,进而判断该消息的实际用途(详见《DDS—RTPS一致性测试案例分析》)。RTPS中定义的数据类型为EntityId_t,其定义如下所示:

typedef octet OctetArray3[3];

struct EntityId_t {

OctetArray3 entityKey;

octet entityKind;

};

其中entityKey表示Entity类型,比如是Participant、Reader、Writer、Reader Group、Writer Group,以及该Entity是built-in Entity、user-defined Entity 或者vendor-specific Entity。

entityKind表示Entity是built-in Entity、user-defined Entity 或者vendor-specific Entity,比如entityKind = 0x02表示User-defined 的Writer (with Key), entityKind = 0x07表示User-defined Entity的Reader(with Key),具体规则详见RTPS V2.5协议的9.3.1.2。

例如,协议规定ENTITYID_SPDP_BUILTIN_PARTICIPANT_ANNOUNCER = {{00,01,00},c2},所以当Data的writerId = {{00,01,00},c2}时,我们就可以知道这是一个SPDP消息。

虽然EntityId_t是一个结构体,但是RTPS协议对EntityId都整体定义好的,所以在CAPL可以简化为dword类型,占4字节,如“dword writerId;”,因此writerId = {{00,01,00},c2}可以等效表示为writerId = 0x000100c2。

2.5 writerSN

表示 Writer 发送 Data 消息的相对顺序。每次发送新的 Data 消息时,writerSN 会递增,从而为每条消息分配一个连续的序号。例如,第一条发送的 Data 消息的 writerSN = 1,之后每发送一条新消息,writerSN 增加 1。如果某条消息丢失,Reader 可以通过 writerSN 检测到丢失的消息,并请求重传缺失writerSN的Data消息,这也是DDS消息的可靠性不依赖于传输协议的机制。

需要注意的是,writerSN 的更新是由每个 Entity 独立管理的,而不是全局统一的。每个 Entity 维护自己的 writerSN 序列。此外,只有 Data 消息的内容发生实质更新时,writerSN 才会递增。例如:

  1. SPDP 消息:由built-in Entity的 SPDP BuiltinParticipantWriter 发送。如果在程序执行周期内,DomainId、单播 IP 或广播 IP 等配置没有发生变化,SPDP 报文内容不会改变,因此 writerSN 并不会增加。
  2. Topic消息:由User-defined Entity的 Writer发送。每次发布Topic 时,其内容通常会发生变化,因此 writerSN 会递增。

RTPS中定义的消息类型为SequenceNumber_t,数据结构如下:

struct SequenceNumber_t {

 long high;

 unsigned long low;

};

其中high表示有符号的高4字节,low表示无符号的低4字节,所以是一个有符号数。因此,在CAPL中也用struct类型表示,如下所示:

  struct SequenceNumber_t  {

     long  high;

     dword low;

  };

2.6 inlineQos

仅当Q = 1才会有效,包含可能影响消息解释的QoS。消息格式定义如下所示:

struct Parameter {

ParameterId_t parameterId;

short length;

octet value[length]; // Pseudo-IDL: array of non-const length

};

parameterId标识独一无二的参数,由协议规定,length表示value的长度。具体规则详见RTPS V2.5协议的9.6.4,这只介绍一个常用的inlineQos来帮助大家熟悉报文分析方法。例如,inlineQos的原始报文为71 00 04 00 00 00 00 03,采用Little_Endian。

这段报文解析为parameterId = 0x0071,length = 0x0004,value[4] = {0,0,0,3}。根据RTPS协议,0x71表示PID_STATUS_INFO,该消息的value表示一个32位的标志,只用到了高3位,由低到高分别是F、U、D,其中:

D = 1表示 Writer 处理了子消息中 Key 对应的数据对象实例,该实例的生命周期结束了。

U = 1表示 Writer 注销了子消息中 Key 对应的数据对象实例,不再管理该实例。

F = 1表示Writer为子消息中 Key 对应的数据对象实例写入了一个样本,但样本未通过 Reader 内容过滤器,也就是没有提交给Reader。

所以,上面的inlineQoS表示DataWriter已经注销了。

在CAPL中可以用struct类型表示,由于CAPL不支持动态创建数组,因此需要预定义较大的数组来保存数据,如下所示:

  struct ParameterList_t{

       word parameterId;

       word length;

       byte value[512]; // value[length]

  };

2.7 serializedPayload

仅当D = 1或者K = 1才显示,如果D = 1则表示为序列化的数据,如果K = 1则表示为序列化后的Key值,标识数据对象的实例,数据结构定义如下:

typedef octet RepresentationIdentifier[2];

typedef octet RepresentationOptions[2];

struct SerializedPayloadHeader {

 RepresentationIdentifier representation_identifier;

 RepresentationOptions representation_options;

};       //SerializedPayload

representation_identifier标识所使用的数据表示方式,如果协议规定PL_CDR_BE = {0x00, 0x02},PL_CDR_LE = {0x00, 0x03},详情请看RTPS V2.5的10.3 -10.6章节。representation_options默认为全0,应该忽略。所以如果脚本需要解析payload需要关注representation_identifier的参数。

在CAPL中可以用struct类型表示,其中buffer表示序列化的payload,并且要额外的记录数据长度,如下所示:

  struct serializedData_t  {

     word kind;

     word option;

     byte buffer[DATA_SERIALIZED_DATA_LENGTH]; 

     dword len;

  };

3 CAPL解析

通过上文的介绍,我们已经知道了Data子消息的构成,因此解析Data子消息前首先要基于CAPL建立其对应的数据结构,如下所示:

图2 Data_t定义

解析时,首先将RTPS消息分割成报头和子消息,根据SubmessageId判断子消息类型,然后进行反序列化完成数据解析,当这个子消息解析完成后,将继续接下一个,直到所有子消息被解析完成,整个流程如下所示:

图3 子消息解析流程和示例代码

解析过程中,需要对“特殊”Data进行解析,比如SPDP报文的解析核心是对serializedPayload解析,由于篇幅有限只为大家展现下某车企的SPDP报文解析结果,如下所示:

图4 SPDP报文解析结果

文中提到的 CAPL 脚本不仅能够解析 Data 子消息,覆盖了 DDS 协议中的全部子消息类型,并支持对报文格式、协议一致性以及 QoS 策略等方面的全面测试。

4结语

通过本文,我们详细介绍了 Data 子消息的结构、字段作用以及实际应用场景,并为大家提供了通过 CAPL 脚本解析报文的思路和方法,希望能够为您的开发工作提供实用参考。由于水平有限,对协议难免有所偏差,如果您对本文内容感兴趣或者有其他疑问,对 CAPL 脚本的实现细节或 DDS 协议测试有进一步需求,欢迎将需求发送至邮箱market@dotrustech.com,我们将竭诚为您提供支持。

http://www.xdnf.cn/news/733051.html

相关文章:

  • golang 基于redis实现集群中的主实例选举
  • Nginx网站服务:从入门到LNMP架构实战
  • 生动形象理解CNN
  • 文件雕刻——一种碎片文件的恢复方法
  • 为什么建立 TCP 连接时,初始序列号不固定?
  • 日志技术-LogBack、Logback快速入门、Logback配置文件、Logback日志级别
  • Kubernetes 入门:安装 kubectl 并掌握基础命令
  • RK3568 OH5.1 编译运行程序hellworld
  • (22)大文件流式处理
  • 五星级酒店技能比赛主持稿串词
  • framework之慕课大巴
  • PCL 渲染显示
  • 电子电路:初步认识4013D触发器
  • 【深度剖析】义齿定制行业数字化转型模式创新研究(上篇3:数字化转型动机分析)
  • 实验设计与分析(第6版,Montgomery)第5章析因设计引导5.7节思考题5.13 R语言解题
  • 人工智能编程学习心得:从零基础到独立开发的蜕变之路
  • 副本(Replica)在Elasticsearch中扮演什么角色?
  • 算力租赁革命:弹性模式如何重构数字时代的创新门槛​
  • MATLAB项目实战:阻尼振动与数据拟合项目
  • 大模型长对话中上下文无法承载全部历史,如何压缩或提取重点
  • 2025Mybatis最新教程(二)
  • 什么是知识蒸馏?如何做模型蒸馏?结合案例说明
  • 电子电路:深入了解4013D触发器的机制和原理
  • 加强LLM防御以实现企业部署
  • spring切面
  • 栈与队列:数据结构的有序律动
  • JS入门——三种输入方式
  • docker不用dockerfile
  • GSR 手环能耗数据实测:STM32 与 SD NAND 的功耗优化成果
  • 信息安全管理与评估2025山东卷