8 平台独立模型(PIM)
8.1 引言
本条款定义了RTPS协议的平台独立模型(PIM)。后续条款将PIM映射到各种平台,其中最基本的一个是原生UDP数据包。
PIM以一种“虚拟机”的形式描述了协议。这个虚拟机的结构是由第8.2节中描述的类构建的,包括DataWriter和DataReader端点。这些端点使用第8.3节中描述的消息进行通信。第8.4节描述了虚拟机的行为,即端点之间发生的消息交换。它列出了互操作性的要求,并使用状态图定义了两个参考实现。第8.5节定义了用于配置虚拟机以便与其远程对等方通信所需信息的发现协议。第8.6节描述了如何为未来的需求扩展协议。最后,第8.7节描述了如何使用RTPS实现DDS的QoS和一些高级DDS功能。
引入RTPS虚拟机的唯一目的是以完整和明确的方式描述协议。这种描述不旨在以任何方式限制内部实现。合规实现的唯一标准是其外部可观察行为满足互操作性的要求。特别是,实现可以基于其他类,并且可以使用除状态机之外的编程构造来实现RTPS协议。
8.2 结构模块
此小节描述了RTPS实体的结构,这些实体是通信参与者。RTPS协议使用的主要类在图8.1中显示。
8.2.1 概述
RTPS实体是应用程序可见的DDS实体用于相互通信的协议级端点。
每个RTPS实体与一个DDS实体一一对应。历史缓存(HistoryCache)构成了DDS实体及其相应RTPS实体之间的接口。例如,对DDS DataWriter的每次写操作都会向其相应的RTPS DataWriter的历史缓存中添加一个CacheChange。随后,RTPS DataWriter将CacheChange传输到所有匹配的RTPS DataReader的历史缓存中。在接收端,DDS DataReader由RTPS DataReader通知一个新的CacheChange已经到达历史缓存,此时DDS DataReader可能选择使用DDS的read或take API来访问它。
此小节提供了RTPS虚拟机使用的主要类别的概述,以及用于描述其属性的类型。后续的小节将详细描述每个类别。
8.2.1.1 RTPS虚拟机使用的类别概述
所有RTPS实体都派生自RTPS实体类。表8.1列出了RTPS虚拟机使用的类别。
表8.1 - RTPS实体和类别概览
RTPS 实体和类别 | |
类别 | 目的 |
Entity | 所有RTPS实体的基类。代表在网络上对其他RTPS实体可见的对象。具有全局唯一标识符(GUID),可以在RTPS消息中被引用。 |
Endpoint | RTPS实体的特化,代表可以成为通信端点的对象,即可以是RTPS消息的源或目的地的对象。 |
Participant | 包含所有共享公共属性且位于单一地址空间中的RTPS实体的容器。 |
Writer | RTPS端点的特化,代表可以成为传递CacheChanges消息的源的对象。 |
Reader | RTPS端点的特化,代表可以用来接收传递CacheChanges消息的对象。 |
HistoryCache | 用于暂时存储和管理数据对象更改集的容器类。包含由写者所做更改或读者所接收更改的历史。 |
CacheChange | 表示对数据对象所做的单个更改。包括数据对象的创建、修改和删除。 |
Data | 表示与对数据对象所做更改相关联的可能数据。 |
在第8.2.1.2节中,提供了用于描述RTPS实体和类别的类型的概述。虚拟机使用的实体和类别都包含一组属性,这些属性的类型在表8.2中进行了总结。
表 8.2 - 出现在 RTPS 实体和类别中的属性类型
属性类型 | 目的 |
GUID_t | 用于持有全局唯一的RTPS实体标识符的类型。必须能够使用16字节表示。保留值:GUID_UNKNOWN。 |
GuidPrefix_t | 用于持有全局唯一的RTPS实体标识符的前缀的类型。必须能够使用12字节表示。保留值:GUIDPREFIX_UNKNOWN。 |
EntityId_t | 用于持有全局唯一的RTPS实体标识符的后缀部分的类型。必须能够使用4字节表示。保留值:ENTITYID_UNKNOWN。以及由发现模块在8.5中定义的额外预定义值。 |
SequenceNumber_t | 用于持有序列号的类型。必须能够使用64位表示。保留值:SEQUENCENUMBER_UNKNOWN。 |
Locator_t | 用于表示发送消息到RTPS端点所需的地址信息的类型。应能包含辨别器、地址和端口号。保留值:LOCATOR_INVALID, LOCATOR_KIND_INVALID等。 |
TopicKind_t | 用于区分主题是否定义了一些字段作为标识主题内数据实例的“键”的枚举。保留值:NO_KEY, WITH_KEY。 |
ChangeKind_t | 用于区分对数据对象所做更改类型的枚举。包括对数据或数据对象实例状态的更改。值包括:ALIVE, ALIVE_FILTERED, NOT_ALIVE_DISPOSED, NOT_ALIVE_UNREGISTERED。 |
ReliabilityKind_t | 用于指示通信的可靠性水平的枚举。值包括:BEST_EFFORT, RELIABLE。 |
InstanceHandle_t | 用于表示其值更改由RTPS协议传达的数据对象的身份的类型。 |
ProtocolVersion_t | 用于表示RTPS协议版本的类型。保留值:PROTOCOLVERSION, PROTOCOLVERSION_1_0等。PROTOCOLVERSION是最新版本的别名。 |
VendorId_t | 用于表示实现RTPS协议的服务供应商的类型。供应商ID的可能值由OMG分配。保留值:VENDORID_UNKNOWN。 |
8.2.1.3 RTPS实体的配置属性
RTPS实体通过一组属性进行配置。其中一些属性映射到在相应的DDS实体上设置的QoS(服务质量)策略。其他属性代表允许根据特定传输和部署情况调整协议行为的参数。还有一些额外的属性用于编码RTPS实体的状态,并不用于配置行为。
用于配置一部分RTPS实体的属性显示在图8.2中。用于配置Writer和Reader实体的属性与协议行为密切相关,将在8.4节中介绍。
本小节的其余部分将更详细地描述每个RTPS实体。
8.2.2 RTPS HistoryCache
HistoryCache是DDS和RTPS之间接口的一部分,在DataReader和DataWriter两侧扮演不同的角色。
在DataWriter侧,HistoryCache包含了由相应DDS DataWriter对数据对象所做的部分历史更改,这些更改是为了服务现有和未来匹配的RTPS DataReader端点所必需的。所需的部分历史取决于DDS QoS和与匹配的RTPS DataReader端点的通信状态。
在DataReader侧,它包含了所有匹配的RTPS DataWriter端点对数据对象所做更改的部分重叠。
这里使用“部分”这个词是为了表明,并不需要维护所有曾经做出的更改的完整历史。相反,所需的是历史的一个子集,这个子集需要满足RTPS协议的行为需求和相关DDS实体的QoS需求。定义这个子集的规则由RTPS协议规定,并且取决于通信协议的状态和相关DDS实体的QoS。
HistoryCache是DDS和RTPS之间接口的一部分。换句话说,RTPS实体及其相关的DDS实体都能够调用其关联的HistoryCache上的操作。
HistoryCache的属性列在表8.3中。
表8.3 - RTPS HistoryCache属性
RTPS HistoryCache | ||
属性类型 | 含义 | 与DDS的关系 |
changes | CacheChange[*] | 历史缓存中包含的CacheChange列表。 |
RTPS实体和相关的DDS实体通过使用表8.4中的操作与HistoryCache进行交互。
表8.4 - RTPS HistoryCache操作
以下小节提供了操作的详细信息。
8.2.2.1 new
此操作创建一个新的RTPS HistoryCache。新创建的历史缓存将初始化为一个空的更改列表。
8.2.2.2 add_change
此操作将CacheChange a_change插入HistoryCache中。
如果没有足够的资源将更改添加到HistoryCache,此操作将失败。DDS服务实现的责任是以与DDS实体的RESOURCE_LIMITS QoS一致的方式配置HistoryCache,并以DDS规范指定的方式向DDS用户传达任何错误。
此操作执行以下逻辑步骤:
ADD a_change TO this.changes;
8.2.2.3 remove_change
此操作指出先前添加的CacheChange已变得不再相关,且不需要在HistoryCache中保持有关CacheChange的详细信息。根据相关DDS实体的QoS和CacheChange的确认状态,做出不再相关的判定。这在8.4.1节中有所描述。
此操作执行以下逻辑步骤:
REMOVE a_change FROM this.changes;
8.2.2.4 get_seq_num_min
此操作检索存储在HistoryCache中的CacheChange::sequenceNumber属性的最小值。
此操作执行以下逻辑步骤:
min_seq_num := MIN { change.sequenceNumber WHERE (change IN this.changes) } return min_seq_num;
8.2.2.5 get_seq_num_max
此操作检索存储在HistoryCache中的CacheChange::sequenceNumber属性的最大值。
此操作执行以下逻辑步骤:
max_seq_num := MAX { change.sequenceNumber WHERE (change IN this.changes) }
return max_seq_num;
8.2.3 RTPS CacheChange
用于表示添加到HistoryCache的每个更改的类。CacheChange的属性列在表8.5中。
表8.5 RTPS CacheChange 属性
RTPS CacheChange | |||
属性类型 | 类型 | 含义 | 与DDS的关系 |
kind | ChangeKind_t | 标识更改的类型。参见表8.2 | DDS实例状态种类 |
writerGuid | GUID_t | 标识做出更改的RTPS写者的GUID_t | 不适用。 |
instanceHandle | InstanceHandle_t | 标识更改所适用的数据对象实例。 | 在DDS中,标记为“键”的字段的值唯一地标识每个数据对象。 |
sequenceNumber | SequenceNumber_t | 由RTPS写者分配的序列号,用于唯一标识更改。 | 不适用。 |
data_value | Data | 与更改相关联的数据值。根据CacheChange的类型,可能没有关联的数据。参见表8.2。 | 不适用。 |
inlineQos | ParameterList | 包含可能影响CacheChange::data_value解释的QoS。 | 影响数据的DDS特定信息。 |
8.2.4 RTPS Entity
RTPS Entity是所有RTPS实体的基类,并映射到一个DDS实体。实体配置属性列在表8.6中。
表格8.6 RTPS Entity属性
RTPS Entity | ||
属性类型 | 含义 | 与DDS的关系 |
guid | 全球唯一标识DDS域中的RTPS实体 | 映射到用于描述相应DDS实体的DDS BuiltinTopicKey_t的值。有关更多详细信息,请参阅DDS规范。 |
8.2.4.1 识别 RTPS 实体:全局唯一标识符(GUID)
全局唯一标识符(GUID)是所有 RTPS 实体的一个属性,它在 DDS 领域内唯一标识该实体。
GUID 是由 GuidPrefix_t 前缀和 EntityId_t 实体标识符组合而成的元组 <prefix, entityId> 构建的。
图8.4 RTPS 的 GUID_t 是用于唯一标识实体的,由一个前缀和一个后缀组成。
表8.7 GUID_t的结构
字段 | 类型 | 含义 |
prefix | GuidPrefix_t | 在域内唯一标识参与者 |
entityId | EntityId_t | 在参与者内唯一标识实体 |
8.2.4.2 RTPS 参与者的 GUID
每个参与者都有一个 GUID <prefix, ENTITYID_PARTICIPANT>,其中常量 ENTITYID_PARTICIPANT 是 RTPS 协议定义的特殊值。其实际值取决于 PSM。
实现可以自由选择前缀,只要域中的每个参与者都有一个唯一的 GUID。
8.2.4.3 参与者内 RTPS 端点的 GUID
具有 GUID <participantPrefix, ENTITYID_PARTICIPANT> 的参与者包含的端点拥有 GUID <participantPrefix, entityId>。entityId 是端点相对于参与者的唯一标识。这有几个后果:
-
参与者内所有端点的 GUID 都有相同的前缀。
-
一旦知道了端点的 GUID,也就知道了包含该端点的参与者的 GUID。
-
任何端点的 GUID 都可以从其所属参与者的 GUID 和它的 entityId 推断出来。每个 RTPS 实体的 entityId 的选择取决于 PSM。
8.2.4.4 参与者内端点组的 GUID
DDS 规范定义了发布者和订阅者实体。这两个实体的 GUID 正如上述第 8.2.4.3 条所述。
8.2.5 RTPS 参与者
RTPS 参与者是 RTPS 端点实体的容器,并映射到 DDS 的 DomainParticipant。此外,RTPS 参与者促进了单个 RTPS 参与者内的 RTPS 端点实体可能共享共同属性的事实。
RTPS 参与者包含表 8.8 中显示的属性。
表 8.8 - RTPS 参与者属性
RTPS 参与者:RTPS 实体 | |||
属性 | 类型 | 含义 | 与DDS的关系 |
defaultUnicastLocatorList | Locator_t[] | 默认单播定位器列表(传输、地址、端口组合),可用于向参与者中包含的端点发送消息。这些是在端点未指定其自己的定位器集合时将使用的单播定位器。 | N/A。由发现模块配置。 |
defaultMulticastLocatorList | Locator_t[] | 默认组播定位器列表(传输、地址、端口组合),可用于向参与者中包含的端点发送消息。这些是在端点未指定其自己的定位器集合时将使用的组播定位器。 | N/A。由发现模块配置。 |
protocolVersion | ProtocolVersion_t | 标识参与者使用的 RTPS 协议版本。 | N/A。 为协议的每个版本指定。 |
vendorId | VendorId_t | 标识包含参与者的 RTPS 中间件的供应商。 | N/A。 由每个供应商配置。 |
8.2.6 RTPS 端点
RTPS 端点代表从 RTPS 协议的角度看可能的通信端点。RTPS 端点实体有两种类型:写端点和读端点。
RTPS 写端点向 RTPS 读端点发送 CacheChange 消息,并可能接收到其发送的更改的确认。RTPS 读端点接收来自写端点的 CacheChange 和更改可用性通知,并可能确认更改和/或请求遗漏的更改。
RTPS 端点包含表 8.9 中显示的属性。
表 8.9 - RTPS 端点配置属性
RTPS 端点:RTPS 实体 | |||
属性 | 类型 | 含义 | 与 DDS 的关系 |
unicastLocatorList | Locator_t[*] | 单播定位器列表(传输、地址、端口组合),可用于向端点发送消息。列表可能为空。 | 无关。由发现配置。 |
multicastLocatorList | Locator_t[*] | 组播定位器列表(传输、地址、端口组合),可用于向端点发送消息。列表可能为空。 | 无关。由发现配置。 |
reliabilityLevel | ReliabilityKind_t | 端点支持的可靠性等级。 | 映射到 RELIABILITY QoS ‘kind’。 |
topicKind | TopicKind_t | 用于指示端点是否支持实例生命周期管理操作(见 8.7.4)。 | 由与 RTPS 端点相关的 DDS 主题相关联的数据类型定义。指示端点是否与定义了某些字段为 DDS 键的数据类型相关联。 |
8.2.7 RTPS 写入器
RTPS 写入器是 RTPS 端点的特化,代表向匹配的 RTPS 读取器端点发送 CacheChange 消息的行为体。其角色是将其 HistoryCache 中的所有 CacheChange 更改传输到匹配的远程 RTPS 读取器的 HistoryCache 中。
RTPS 写入器属于一个 RTPS 组。
配置 RTPS 写入器的属性与协议行为紧密相关,将在行为模块(8.4节)中介绍。
8.2.8 RTPS 读取器
RTPS 读取器是 RTPS 端点的特化,代表从匹配的 RTPS 写入器端点接收 CacheChange 消息的行为体。
RTPS 读取器属于一个 RTPS 组。
配置 RTPS 读取器的属性与协议行为紧密相关,将在行为模块(8.4节)中介绍。
8.2.9 与 DDS 实体的关系
如8.2.2节所述,HistoryCache 形成了 DDS 实体与其对应的 DDSI-RTPS 实体之间的接口。例如,DDS DataWriter 通过共享的 HistoryCache 将数据传递给其匹配的 RTPS 写入器。
然而,DDS 实体如何确切地与 HistoryCache 交互,是特定于实现的,不是 RTPS 协议正式建模的内容。相反,RTPS 协议的行为模块仅指定了如何从 RTPS 写入器的 HistoryCache 将 CacheChange 更改传输到每个匹配的 RTPS 读取器的 HistoryCache。
尽管这不是 RTPS 协议的一部分,了解 DDS 实体如何与 HistoryCache 交互对于完全理解协议来说非常重要。这个主题构成了本小节的主题。
交互使用 UML 状态图描述。用于指代 DDS 和 RTPS 实体的缩写在下面的表 8.10 中列出。
表 8.10 - 序列图和状态图中使用的缩写
缩写 | 含义 | 示例用法 |
DW | DDS 数据写入器(DataWriter) | DW::write |
DR | DDS 数据读取器(DataReader) | DR::read |
W | RTPS 写入器(Writer) | W::heartbeatPeriod |
R | RTPS 读取器(Reader) | R::heartbeatResponseDelay |
WHC | RTPS 写入器的历史缓存(HistoryCache) | WHC::changes |
RHC | RTPS 读取器的历史缓存(HistoryCache) | RHC::changes |
8.2.9.1 DDS 数据写入器
在 DDS 数据写入器上的 write 操作会将 CacheChange 更改添加到其关联的 RTPS 写入器的 HistoryCache 中。因此,HistoryCache 包含了最近写入更改的历史记录。更改的数量由 DDS 数据写入器上的 QoS 设置决定,例如 HISTORY 和 RESOURCE_LIMITS QoS。
默认情况下,HistoryCache 中的所有更改都被认为对每个匹配的远程 RTPS 读取器都是相关的。也就是说,写入器应该尝试将 HistoryCache 中的所有更改发送给匹配的远程读取器。如何做到这一点是 RTPS 协议的行为模块的主题。
更改可能由于两个原因而未被发送给远程读取器:
-
它们已经被 DDS 数据写入器从 HistoryCache 中移除,不再可用。
-
它们被认为对这个读取器不相关。
DDS 数据写入器可能出于几个原因决定从 HistoryCache 中移除更改。例如,根据 HISTORY QoS 设置,可能只需要存储有限数量的更改。或者,由于 LIFESPAN QoS,一个样本可能已经过期。在使用严格的可靠通信时,只有当一个更改被发送给的所有读取器都已确认,并且这些读取器仍然活跃和存在时,才能被移除。
并非所有更改对每个匹配的远程读取器都是相关的,这可能由 TIME_BASED_FILTER QoS 决定,或者通过使用 DDS 内容过滤主题。注意,在这种情况下,必须针对每个读取器分别确定更改是否相关。如果可能的话,实现可能通过在写入器端进行过滤来优化带宽和/或 CPU 使用。这是否可能取决于实现是否跟踪每个单独的远程读取器及其适用的 QoS 和过滤器。读取器本身将始终进行过滤。
QoS 或基于内容的过滤在本文档中使用 DDS_FILTER(reader, change) 表示,这种表示法反映了过滤是依赖于读取器的。根据写入器存储的特定于读取器的信息,DDS_FILTER 可能是一个无操作。对于基于内容的过滤,RTPS 规范允许发送每个更改的信息,列出已应用于更改的过滤器以及它通过了哪些过滤器。如果有可用的信息,读取器可以使用这些信息过滤更改,而无需调用 DDS_FILTER。这种方法通过在写入器端一次过滤样本来节省 CPU 周期,而不是在每个读取器上过滤。
以下状态图说明了 DDS 数据写入器如何将更改添加到 HistoryCache。
图 8.6 - DDS 数据写入器对 HistoryCache 的添加
表 8.11 - DDS 数据写入器对 HistoryCache 添加的过渡
转换 | 状态 | 事件 | 下一个状态 |
T1 | 初始initial | 新建 DDS 数据写入器 | 存活alive |
T2 | 存活alive | DataWriter::write 写数据 | 存活alive |
T3 | 存活alive | DataWriter::dispose | 存活alive |
T4 | 存活alive | DataWriter::unregister 注销 | alive存活 |
T5 | 存活alive | 删除 DDS 数据写入器 | 终止final |
8.2.9.1.1 转换 T1
这个转换由创建一个 DDS 数据写入器 'the_dds_writer' 触发。转换在虚拟机中执行以下逻辑操作:
the_rtps_writer = new RTPS::Writer; the_dds_writer.related_rtps_writer := the_rtps_writer;
8.2.9.1.2 转换 T2
这个转换由使用 DDS 数据写入器 'the_dds_writer' 写入数据的行为触发。DataWriter::write() 操作接受 'data' 和用于区分不同数据对象的 InstanceHandle_t 'handle' 作为参数。
转换在虚拟机中执行以下逻辑操作:
the_rtps_writer := the_dds_writer.related_rtps_writer; a_change := the_rtps_writer.new_change(ALIVE, data, inlineQos, handle); the_rtps_writer.writer_cache.add_change(a_change);
转换之后,以下后置条件成立:
the_rtps_writer.writer_cache.get_seq_num_max() == a_change.sequenceNumber
8.2.9.1.3 转换 T3
这个转换由用 DDS 数据写入器 'the_dds_writer' 处理之前写入的数据对象的行为触发。DataWriter::dispose() 操作接受用于区分不同数据对象的 InstanceHandle_t 'handle' 作为参数。
如果 topicKind==NO_KEY,此操作无效。
转换在虚拟机中执行以下逻辑操作:
the_rtps_writer := the_dds_writer.related_rtps_writer; if (the_rtps_writer.topicKind == WITH_KEY) { a_change := the_rtps_writer.new_change(NOT_ALIVE_DISPOSED, <nil>, inlineQos, handle); the_rtps_writer.writer_cache.add_change(a_change); }
转换之后,以下后置条件成立:
if (the_rtps_writer.topicKind == WITH_KEY) then the_rtps_writer.writer_cache.get_seq_num_max() == a_change.sequenceNumber
8.2.9.1.4 转换 T4
这个转换由用 DDS 数据写入器 'the_dds_writer' 注销之前写入的数据对象的行为触发。DataWriter::unregister() 操作接受用于区分不同数据对象的 InstanceHandle_t 'handle' 作为参数。
如果 topicKind==NO_KEY,此操作无效。
转换在虚拟机中执行以下逻辑操作:
the_rtps_writer := the_dds_writer.related_rtps_writer; if (the_rtps_writer.topicKind == WITH_KEY) { a_change := the_rtps_writer.new_change(NOT_ALIVE_UNREGISTERED, <nil>, inlineQos, handle); the_rtps_writer.writer_cache.add_change(a_change); }
转换之后,以下后置条件成立:
if (the_rtps_writer.topicKind == WITH_KEY) then the_rtps_writer.writer_cache.get_seq_num_max() == a_change.sequenceNumber
8.2.9.1.5 转换 T5
这个转换由销毁一个 DDS 数据写入器 'the_dds_writer' 触发。转换在虚拟机中执行以下逻辑操作:
delete the_dds_writer.related_rtps_writer;
8.2.9.2 DDS 数据读取器
DDS 数据读取器从对应 RTPS 读取器的 HistoryCache 中获取其数据。HistoryCache 中存储的更改数量由 QoS 设置决定,如 HISTORY 和 RESOURCE_LIMITS QoS。
每个匹配的写入器都会尝试将其 HistoryCache 中的所有相关样本传输到读取器的 HistoryCache 中。在 DDS 数据读取器上实现 read 或 take 调用时,会访问 HistoryCache。返回给用户的更改是那些通过了所有特定于读取器的过滤器(如果有的话)的 HistoryCache 中的更改。
读取器过滤器同样由 DDS_FILTER(reader, change) 表示。如上所述,实现可能能够在写入器端执行大部分过滤。在这种情况下,样本要么从未发送(因此不在读取器的 HistoryCache 中),要么包含有关应用了哪些过滤器及其相应结果的信息(对于基于内容的过滤)。
DDS 数据读取器也可能决定从 HistoryCache 中移除更改,以满足 TIME_BASED_FILTER 等 QoS。这种确切的行为再次是特定于实现的,并不是由 RTPS 协议建模的。
以下状态图说明了 DDS 数据读取器如何访问 HistoryCache 中的更改。
图 8.7 - DDS 数据读取器访问 HistoryCache
表 8.12 - DDS 数据读取器访问 HistoryCache 的状态转换
转换 | 状态 | 事件 | 下一个状态 |
T1 | 初始 | 新建 DDS 数据读取器 | 存活 |
T2 | 存活 | DDS 数据读取器::read | 存活 |
T3 | 存活 | DDS 数据读取器::take | 存活 |
T4 | 存活 | 删除 DDS 数据读取器 | 终止 |
8.2.9.2.1 转换 T1
此转换由 DDS 数据读取器 'the_dds_reader' 的创建触发。转换在虚拟机中执行以下逻辑操作:
the_rtps_reader = new RTPS::Reader; the_dds_reader.related_rtps_reader := the_rtps_reader;
8.2.9.2.2 转换 T2
此转换由通过 'read' 操作从 DDS 数据读取器 'the_dds_reader' 读取数据的行为触发。返回给应用程序的更改仍然保留在 RTPS 读取器的 HistoryCache 中,以便后续的 read 或 take 操作可以再次找到它们。
转换在虚拟机中执行以下逻辑操作:
the_rtps_reader := the_dds_reader.related_rtps_reader; a_change_list := new(); FOREACH change IN the_rtps_reader.reader_cache.changes { if DDS_FILTER(the_rtps_reader, change) ADD change TO a_change_list; } RETURN a_change_list;
DDS_FILTER() 操作反映了 DDS 数据读取器 API 选择更改子集的能力,基于 CacheChange::kind、QoS、内容过滤器和其他机制。请注意,上述逻辑操作仅反映行为,并不一定是协议的实际实现。
8.2.9.2.3 转换 T3
此转换由通过 'take' 操作从 DDS 数据读取器 'the_dds_reader' 读取数据的行为触发。返回给应用程序的更改从 RTPS 读取器的 HistoryCache 中移除,以便后续的 read 或 take 操作不会找到相同的更改。
转换在虚拟机中执行以下逻辑操作:
the_rtps_reader := the_dds_reader.related_rtps_reader; a_change_list := new(); FOREACH change IN the_rtps_reader.reader_cache.changes { if DDS_FILTER(the_rtps_reader, change) { ADD change TO a_change_list; } the_rtps_reader.reader_cache.remove_change(a_change); } RETURN a_change_list;
DDS_FILTER() 操作反映了 DDS 数据读取器 API 选择更改子集的能力,基于 CacheChange::kind、QoS、内容过滤器和其他机制。请注意,上述逻辑操作仅反映行为,并不一定是协议的实际实现。
转换之后,以下后置条件成立:
FOREACH change IN a_change_list change BELONGS_TO the_rtps_reader.reader_cache.changes == FALSE
8.2.9.2.4 转换 T4
此转换由 DDS 数据读取器 'the_dds_reader' 的销毁触发。转换在虚拟机中执行以下逻辑操作:
delete the_dds_reader.related_rtps_reader;