8.4.7 RTPS Writer参考实现
RTPS Writer参考实现基于RTPS Writer类的特化,该类首次在8.2中引入。本子条款描述了RTPS Writer及其所有附加类的模型,用于建模RTPS Writer参考实现。实际行为在8.4.8和8.4.9中描述。
8.4.7.1 RTPS Writer
RTPS Writer是RTPS端点的特化实现,代表将CacheChange消息发送到匹配的RTPS Reader端点的执行者。参考实现StatelessWriter和StatefulWriter是RTPS Writer的特化,它们在维护与匹配的Reader端点相关的知识方面存在差异。
图 8.15 - RTPS Writer端点
表 8.47 描述了RTPS Writer的属性。
表 8.47 RTPS Writer属性
RTPS Writer: RTPS端点 | 属性类型 | 含义 | 与DDS的关系 |
pushMode | bool | 配置Writer的操作模式。如果pushMode==true,则Writer将向Reader推送更改。如果pushMode==false,则只会通过心跳公告更改,并且仅作为对Reader请求的响应发送。 | N/A(自动配置) |
heartbeatPeriod | Duration_t | 协议调优参数,允许RTPS Writer通过发送心跳消息重复宣布数据的可用性。 | N/A(自动配置) |
nackResponseDelay | Duration_t | 协议调优参数,允许RTPS Writer延迟对来自负面确认的数据请求的响应。 | N/A(自动配置) |
nackSuppressionDuration | Duration_t | 协议调优参数,允许RTPS Writer忽略负面确认‘太快’到达的数据请求。 | N/A(自动配置) |
lastChangeSequenceNumber | SequenceNumber_t | 用于为Writer所做的每个更改分配递增序列号的内部计数器。 | N/A(作为虚拟机逻辑的一部分使用) |
writer_cache | HistoryCache | 包含该Writer的CacheChange更改历史。 | N/A |
dataMaxSizeSerialized | Serialized | 可选属性,指示Writer可以发送的任何SerializedPayload的最大大小。 | N/A(自动配置) |
RTPS Writer的属性允许对协议行为进行精细调整。RTPS Writer的操作在表8.48中描述。
表 8.48 - RTPS Writer操作
RTPS Writer操作 | ||
操作名称 | 参数列表 | 类型 |
new | <返回值> | Writer |
attribute_values | Writer和所有父类所需的属性值的集合。 | |
new_change | <返回值> | CacheChange |
kind | ChangeKind_t | |
data | Data | |
inlineQos | ParameterList | |
handle | InstanceHandle_t |
以下子条款提供有关这些操作的详细信息。
8.4.7.1.1 默认与时间相关的数值
以下与时间相关的数值被用作默认值,以促进各种实现之间的即插即用互操作性。
nackResponseDelay.sec = 0; nackResponseDelay.nanosec = 200 * 1000 * 1000; // 200毫秒 nackSuppressionDuration.sec = 0; nackSuppressionDuration.nanosec = 0;
8.4.7.1.2 new
此操作创建一个新的RTPS Writer。
新创建的Writer 'this' 初始化如下:
this.guid := <as specified in the constructor>; this.unicastLocatorList := <as specified in the constructor>; this.multicastLocatorList := <as specified in the constructor>; this.reliabilityLevel := <as specified in the constructor>; this.topicKind := <as specified in the constructor>; this.pushMode := <as specified in the constructor>; this.heartbeatPeriod := <as specified in the constructor>; this.nackResponseDelay := <as specified in the constructor>; this.nackSuppressionDuration := <as specified in the constructor>; this.lastChangeSequenceNumber := 0; this.writer_cache := new HistoryCache;
8.4.7.1.3 new_change
此操作创建一个新的CacheChange,将其附加到RTPS Writer的HistoryCache。 CacheChange的序列号自动设置为前一个更改的sequenceNumber加一。此操作返回新的更改。
此操作执行以下逻辑步骤:
++this.lastChangeSequenceNumber; a_change := new CacheChange(kind, this.guid, this.lastChangeSequenceNumber, data, inlineQos, handle); RETURN a_change;
8.4.7.2 RTPS StatelessWriter
RTPS StatelessWriter是RTPS Writer的特化实现,用于无状态参考实现。RTPS StatelessWriter对匹配的Reader数量一无所知,并且不为每个匹配的RTPS Reader端点维护任何状态。RTPS StatelessWriter仅维护RTPS ReaderLocator列表,该列表应用于向匹配的Reader发送信息。
表 8.49 - RTPS StatelessWriter属性
RTPS StatelessWriter:RTPS Writer | |||
属性 | 类型 | 含义 | 与DDS的关系 |
reader_locators | ReaderLocator[*] | StatelessWriter维护用于发送CacheChanges的定位器列表。此列表可能包括单播和多播定位器。 | N/A(自动配置) |
RTPS StatelessWriter在以下情况下很有用:(a) Writer的HistoryCache较小,或者 (b) 通信是尽力而为(best-effort)的,或者 (c) Writer通过组播与大量读Reader通信。虚拟机使用表8.50中的操作与StatelessWriter进行交互。
表 8.50 - StatelessWriter操作
StatelessWriter操作 | ||
操作名称 | 参数列表 | 类型 |
new | <返回值> | StatelessWriter |
attribute_values | StatelessWriter和所有父类所需的属性值的集合。 | |
reader_locator_add | <返回值> | void |
a_locator | Locator_t | |
reader_locator_remove | <返回值> | void |
a_locator | Locator_t | |
unsent_changes_reset | <返回值> | void |
8.4.7.2.1 new
此操作创建一个新的RTPS StatelessWriter。除了在RTPS Writer超类上执行的初始化(8.4.7.1.2)之外,新创建的StatelessWriter 'this' 初始化如下:
this.reader_locators := <empty>;
8.4.7.2.2 reader_locator_add
此操作将ReaderLocator a_locator 添加到StatelessWriter::reader_locators中。
ADD a_locator TO {this.reader_locators};
8.4.7.2.3 reader_locator_remove
此操作从StatelessWriter::reader_locators中移除ReaderLocator a_locator。
REMOVE a_locator FROM {this.reader_locators};
8.4.7.2.4 unsent_changes_reset
此操作修改StatelessWriter::reader_locators中所有ReaderLocator的“unsent_changes”集合。未发送更改的列表将重置为与Writer的HistoryCache中可用更改的完整列表相匹配。定期调用此操作可使StatelessWriter持续重新发送其HistoryCache中的所有可用更改。
FOREACH readerLocator IN {this.reader_locators} DO readerLocator.unsent_changes := {this.writer_cache.changes}
8.4.7.3 RTPS ReaderLocator
RTPS StatelessWriter使用的值类型,用于跟踪所有匹配的远程Reader的定位器。
表 8.51 - RTPS ReaderLocator属性
RTPS ReaderLocator | |||
属性 | 类型 | 含义 | 与DDS的关系 |
requested_changes | CacheChange[*] | Writer的HistoryCache中由远程Reader在此ReaderLocator处请求的更改列表。 | N/A(自动配置) |
unsent_changes | CacheChange[*] | Writer的HistoryCache中尚未发送到此ReaderLocator的更改列表。 | N/A(自动配置) |
locator | Locator_t | 表示通过该ReaderLocator可以到达的Reader的单播或多播定位器。 | N/A(自动配置) |
expectsInlineQos | bool | 指定由该ReaderLocator表示的Reader是否希望在每个数据消息中发送内联QoS。 | N/A |
虚拟机使用表8.52中的操作与ReaderLocator进行交互。
表 8.52 - RTPS ReaderLocator操作
ReaderLocator操作 | ||
操作名称 | 参数列表 | 类型 |
new | <返回值> | ReaderLocator |
attribute_values | ReaderLocator所需的属性值的集合。 | |
next_requested_change | <返回值> | CacheChange |
next_unsent_change | <返回值> | CacheChange |
requested_changes | <返回值> | CacheChange[*] |
requested_changes_set | <返回值> | void |
req_seq_num_set | SequenceNumber_t[*] | |
unsent_changes | <返回值> | CacheChange[*] |
8.4.7.3.1 new
此操作创建一个新的RTPS ReaderLocator。新创建的ReaderLocator 'this' 初始化如下:
this.requested_changes := <empty>; this.unsent_changes := RTPS::Writer.writer_cache.changes; this.locator := <as specified in the constructor>; this.expectsInlineQos := <as specified in the constructor;
8.4.7.3.2 next_requested_change
此操作返回requested_changes中具有最低序列号的ReaderLocator的CacheChange。这表示应该发送到此ReaderLocator处的RTPS Reader的下一个修复数据包,以响应来自Reader的先前AckNack消息(参见8.3.7.1)。
next_seq_num := MIN {change.sequenceNumber SUCH-THAT change IN this.requested_changes()}; return change IN this.requested_changes() SUCH-THAT (change.sequenceNumber == next_seq_num);
8.4.7.3.3 next_unsent_change
此操作返回unsent_changes中具有最低序列号的ReaderLocator的CacheChange。这表示应该发送到此ReaderLocator处的RTPS Reader的下一个更改。
next_seq_num := MIN { change.sequenceNumber SUCH-THAT change IN this.unsent_changes()}; return change IN this.unsent_changes() SUCH-THAT (change.sequenceNumber == next_seq_num);
8.4.7.3.4 requested_changes
此操作返回此ReaderLocator的requested_changes列表。此列表表示通过ACKNACK消息由RTPS Reader在此ReaderLocator处请求的更改集。
8.4.7.3.5 requested_changes_set
此操作将带有序列号'req_seq_num_set'的更改集添加到requested_changes列表中。
FOR_EACH seq_num IN req_seq_num_set DO FIND cache_change IN RTPS::Writer.writer_cache.changes SUCH-THAT (cache_change.sequenceNumber==seq_num) ADD cache_change TO this.requested_changes; END
8.4.7.3.6 unsent_changes
此操作返回此ReaderLocator的unsent_changes列表。此列表表示Writer的HistoryCache中尚未发送到此ReaderLocator的更改集。
8.4.7.4 RTPS StatefulWriter
RTPS StatefulWriter是RTPS Writer的特化版本,用于Stateful Reference Implementation。RTPS StatefulWriter配置了所有匹配的RTPS Reader端点的信息,并在每个匹配的RTPS Reader端点上维护状态。
通过在每个匹配的RTPS Reader端点上维护状态,RTPS StatefulWriter可以确定所有匹配的RTPS Reader端点是否已接收特定的CacheChange,并且可以在使用网络带宽时避免向已接收Writer的HistoryCache中的所有更改的Reader发送通告。它维护的信息还简化了Writer端基于QoS的过滤。特定于StatefulWriter的属性如表8.53所述。
表 8.53 - RTPS StatefulWriter属性
RTPS StatefulWriter: RTPS Writer | |||
属性 | 类型 | 含义 | 与DDS的关系 |
matched_readers | ReaderProxy[*] | StatefulWriter追踪与其匹配的所有RTPS Readers。每个匹配的读取器是由ReaderProxy类的一个实例表示。 | N/A(自动配置) |
虚拟机使用表8.54中的操作与StatefulWriter进行交互。
表 8.54 - StatefulWriter操作
StatefulWriter操作 | ||
操作名称 | 参数列表 | 类型 |
new | <返回值> | StatefulWriter |
attribute_values | StatefulWriter和所有超类所需的属性值的集合。 | |
matched_reader_add | <返回值> | void |
a_reader_proxy | ReaderProxy | |
matched_reader_remove | <返回值> | void |
a_reader_proxy | ReaderProxy | |
matched_reader_lookup | <返回值> | ReaderProxy |
a_reader_guid | GUID_t | |
is_acked_by_all | <返回值> | bool |
a_change | CacheChange |
8.4.7.4.1 new
此操作创建一个新的RTPS StatefulWriter。除了在RTPS Writer超类(8.4.7.1.2)上执行的初始化外,新创建的StatefulWriter 'this' 初始化如下:
this.matched_readers := <empty>;
8.4.7.4.2 is_acked_by_all
此操作以CacheChange a_change 作为参数,并确定所有的ReaderProxy是否已经确认了CacheChange。如果所有的ReaderProxy都确认了相应的CacheChange,则该操作将返回true,否则返回false。
return true IF and only IF FOREACH proxy IN this.matched_readers IF change IN proxy.changes_for_reader THEN change.is_relevant == TRUE AND change.status == ACKNOWLEDGED
8.4.7.4.3 matched_reader_add
此操作将ReaderProxy a_reader_proxy 添加到集合StatefulWriter::matched_readers中。
ADD a_reader_proxy TO {this.matched_readers};
8.4.7.4.4 matched_reader_remove
此操作从集合StatefulWriter::matched_readers中移除ReaderProxy a_reader_proxy。
REMOVE a_reader_proxy FROM {this.matched_readers}; delete proxy;
8.4.7.4.5 matched_reader_lookup
此操作从集合StatefulWriter::matched_readers中找到具有GUID_t a_reader_guid的ReaderProxy。
FIND proxy IN this.matched_readers SUCH-THAT (proxy.remoteReaderGuid == a_reader_guid); return proxy;
8.4.7.5 RTPS ReaderProxy
RTPS ReaderProxy类表示RTPS StatefulWriter在每个匹配的RTPS Reader上维护的信息。RTPS ReaderProxy的属性如表8.55所述。
表 8.55 - RTPS ReaderProxy属性
RTPS ReaderProxy | |||
属性 | 类型 | 含义 | 与DDS的关系 |
remoteReaderGuid | GUID_t | 标识由ReaderProxy表示的匹配的远程RTPS Reader。 | N/A(由发现配置) |
remoteGroupEntityId | EntityId_t | 标识匹配的Reader所属的组。 | 此DataReader所属的Subscriber的EntityId。 |
unicastLocatorList | Locator_t[*] | 可用于将消息发送到匹配的RTPS Reader的单播定位器(传输,地址,端口组合)列表。该列表可能为空。 | N/A(由发现配置) |
multicastLocatorList | Locator_t[*] | 可用于将消息发送到匹配的RTPS Reader的多播定位器(传输,地址,端口组合)列表。该列表可能为空。 | N/A(由发现配置) |
changes_for_reader | CacheChange[*] | 与匹配的RTPS Reader相关的CacheChange更改列表。 | 用于实现RTPS协议的行为。 |
expectsInlineQos | bool | 指定远程匹配的RTPS Reader是否希望在任何数据中发送内联QoS。 | N/A |
isActive | bool | 指定远程Reader是否对Writer响应。 | N/A |
RTPS StatefulWriter与RTPS Reader的匹配意味着RTPS StatefulWriter将Writer的HistoryCache中的CacheChange更改发送到由ReaderProxy表示的匹配的RTPS Reader。匹配是相应DDS实体的匹配的结果。也就是说,DDS DataWriter通过主题与DDS DataReader匹配,具有兼容的QoS,并且未被使用DDS的应用程序明确忽略。
虚拟机使用表8.56中的操作与ReaderProxy进行交互。
表 8.56 - ReaderProxy操作
ReaderProxy操作 | ||
操作名称 | 参数列表 | 参数类型 |
new | <返回值> | ReaderProxy |
attribute_values | ReaderProxy所需的属性值的集合。 | |
acked_changes_set | <返回值> | void |
next_requested_change | <返回值> | SequenceNumber_t |
next_unsent_change | <返回值> | ChangeForReader |
unsent_changes | <返回值> | ChangeForReader |
requested_changes | <返回值> | ChangeForReader[*] |
requested_changes_set | <返回值> | void |
req_seq_num_set | SequenceNumber_t[*] | |
unacked_changes | <返回值> | ChangeForReader[*] |
8.4.7.5.1 new
此操作创建一个新的RTPS ReaderProxy。新创建的ReaderProxy 'this' 初始化如下:
this.attributes := <as specified in the constructor>; this.changes_for_reader := RTPS::Writer.writer_cache.changes; FOR_EACH change IN (this.changes_for_reader) DO { IF ( DDS_FILTER(this, change) ) THEN change.is_relevant := FALSE; ELSE change.is_relevant := TRUE; IF ( RTPS::Writer.pushMode == true) THEN change.status := UNSENT; ELSE change.status := UNACKNOWLEDGED; }
上述逻辑表明,新创建的ReaderProxy将其“changes_for_reader”集初始化为包含Writer的HistoryCache中的所有CacheChanges。
如果应用了DDS-DataReader过滤器中的任何一个,并且该更改对于特定的读取器不相关,那么该更改将被标记为“不相关”。DDS规范指出,DataReader可以提供基于时间和基于内容的过滤器。这些过滤器应以与DDS规范一致的方式应用,以选择对于DataReader不相关的任何更改。
状态设置取决于RTPS Writer属性'pushMode'的值。
8.4.7.5.2 acked_changes_set
此操作更改由ReaderProxy 'the_reader_proxy'表示的读取器的一组更改的ChangeForReader状态。具有序列号小于或等于值“committed_seq_num”的更改的状态将更改为ACKNOWLEDGED。
FOR_EACH change IN this.changes_for_reader SUCH-THAT (change.sequenceNumber <= committed_seq_num) DO change.status := ACKNOWLEDGED;
8.4.7.5.3 next_requested_change
此操作返回具有状态'REQUESTED'的ReaderProxy的ChangeForReader中具有最低序列号的更改。这表示应该发送到由ReaderProxy表示的RTPS Reader的下一个修复数据包,以响应来自Reader的先前AckNack消息(见8.3.7.1)。
next_seq_num := MIN {change.sequenceNumber SUCH-THAT change IN this.requested_changes()} return change IN this.requested_changes() SUCH-THAT (change.sequenceNumber == next_seq_num);
8.4.7.5.4 next_unsent_change
此操作返回具有状态'UNSENT'的ReaderProxy的ChangeForReader中具有最低序列号的更改。这表示应该发送到由ReaderProxy表示的RTPS Reader的下一个更改。
next_seq_num := MIN { change.sequenceNumber SUCH-THAT change IN this.unsent_changes() }; return change IN this.unsent_changes() SUCH-THAT (change.sequenceNumber == next_seq_num);
8.4.7.5.5 requested_changes
此操作返回具有状态'REQUESTED'的ReaderProxy的ChangeForReader的子集。这表示由ReaderProxy表示的RTPS Reader使用ACKNACK消息请求的更改集。
return change IN this.changes_for_reader SUCH-THAT (change.status == REQUESTED);
8.4.7.5.6 requested_changes_set
此操作修改由ReaderProxy 'this'表示的RTPS Reader的ChangeForReader状态的一组更改。具有序列号集'req_seq_num_set'的更改的状态将更改为REQUESTED。
FOR_EACH seq_num IN req_seq_num_set DO FIND change_for_reader IN this.changes_for_reader SUCH-THAT (change_for_reader.sequenceNumber==seq_num) change_for_reader.status := REQUESTED; END
8.4.7.5.7 unsent_changes
此操作返回具有状态'UNSENT'的ReaderProxy的ChangeForReader的子集。这表示尚未发送到由ReaderProxy表示的RTPS Reader的更改集。
return change IN this.changes_for_reader SUCH-THAT (change.status == UNSENT);
8.4.7.5.8 unacked_changes
此操作返回具有状态'UNACKNOWLEDGED'的ReaderProxy的ChangeForReader的子集。这表示尚未由由ReaderProxy表示的RTPS Reader确认的更改集。
return change IN this.changes_for_reader SUCH-THAT (change.status == UNACKNOWLEDGED);
8.4.7.6 RTPS ChangeForReader
RTPS ChangeForReader是一个关联类,它保持与由ReaderProxy表示的RTPS Reader相关的RTPS Writer HistoryCache中CacheChange的信息。RTPS ChangeForReader的属性如表8.57所述。
表 8.57 - RTPS ChangeForReader属性
RTPS ReaderProxy | |||
属性 | 类型 | 含义 | 与DDS的关系 |
status | ChangeForReaderStatus Kind | 指示与由ReaderProxy表示的RTPS Reader相关的CacheChange相对于ReaderProxy的状态。 | 无。 由协议使用 |
isRelevant | bool | 指示更改是否与由ReaderProxy表示的RTPS Reader相关。 | 不相关更改的确定受DDS DataReader TIME_BASED_FILTER QoS的影响,还受DDS ContentFilteredTopics的使用影响。 |
8.4.8 RTPS StatelessWriter 行为
8.4.8.1 Best-effort StatelessWriter行为
对于每个ReaderLocator,Best-effort的RTPS StatelessWriter的行为如图8.16所示。
图8.16 - Best-effort的StatelessWriter对每个ReaderLocator的行为
状态机的转换列在表8.58中。
表8.58 - 关于每个ReaderLocator的最佳效果StatelessWriter行为的转换
转换 | 状态 | 事件 | 下一个状态 |
T1 | initial | RTPS Writer配置了一个ReaderLocator | idle |
T2 | idle | 守卫条件:RL::unsent_changes() != <empty> | pushing |
T3 | pushing | 守卫条件:RL::unsent_changes() == <empty> | idle |
T4 | pushing | 守卫条件:RL::can_send() == true | pushing |
T5 | Any state | RTPS Writer配置为不再持有该ReaderLocator | final |
8.4.8.1.1 状态转换T1
这个转换是由配置RTPS best-effort的无状态Writer‘the_rtps_writer’与RTPS ReaderLocator触发的。此配置是由Discovery协议(8.5)触发的,作为发现与‘the_rtps_writer’相关的DDS DataWriter匹配的DDS DataReader的结果。
发现协议提供ReaderLocator构造函数参数的值。该过渡在虚拟机中执行以下逻辑操作:
a_locator := new ReaderLocator( locator, expectsInlineQos ); the_rtps_writer.reader_locator_add( a_locator );
8.4.8.1.2 状态转换T2
这个转换由守卫条件[RL::unsent_changes() != <empty>]触发,表明RTPS Writer HistoryCache中有一些未发送到RTPS ReaderLocator的更改。
在虚拟机中,此过渡不执行逻辑操作。
8.4.8.1.3 状态转换T3
此状态转换由守卫条件[RL::unsent_changes() == <empty>]触发,表明RTPS Writer HistoryCache中的所有更改都已发送到RTPS ReaderLocator。请注意,这并不表示已经接收到更改,只是尝试发送它们。
在虚拟机中,此状态转换不执行逻辑操作。
8.4.8.1.4 状态转换T4
此状态转换由守卫条件[RL::can_send() == true]触发,表明RTPS Writer‘the_writer’具有发送更改到RTPS ReaderLocator‘the_reader_locator’所需的资源。
在虚拟机中,此状态转换执行以下逻辑操作:
a_change := the_reader_locator.next_unsent_change(); IF a_change IN the_writer.writer_cache.changes { DATA = new DATA(a_change); IF (the_reader_locator.expectsInlineQos) { DATA.inlineQos := the_writer.related_dds_writer.qos; DATA.inlineQos += a_change.inlineQos; } DATA.readerId := ENTITYID_UNKNOWN; sendto the_reader_locator.locator, DATA; } ELSE { GAP = new GAP(a_change.sequenceNumber); GAP.readerId := ENTITYID_UNKNOWN; sendto the_reader_locator.locator, GAP; }
过渡后,以下后置条件成立:
( a_change BELONGS-TO the_reader_locator.unsent_changes() ) == FALSE
8.4.8.1.5 状态转换T5
此状态转换由配置RTPS Writer‘the_rtps_writer’不再发送到RTPS ReaderLocator‘the_reader_locator’触发。此配置是由Discovery协议(8.5)触发的,因为破坏了与与‘the_rtps_writer’相关的DDS DataWriter的DDS DataReader的预先存在的匹配。
在虚拟机中,此转换执行以下逻辑操作:
the_rtps_writer.reader_locator_remove(the_reader_locator); delete the_reader_locator;
8.4.8.2 可靠的无状态Writer行为
就每个ReaderLocator而言,可靠的RTPS StatelessWriter的行为如图8.17所示。
图8.17 - 可靠的无状态Writer相对于每个ReaderLocator的行为
状态机的转换列在表8.59中。
表8.59 - 相对于每个Reader Locator的可靠无状态Writer行为的转换
过渡状态 | 事件 | 下一个状态 |
T1 | 初始 | RTPS Writer 配置为带有宣布 ReaderLocator |
T2 | 宣布中 | GuardCondition: RL::unsent_changes() != <empty>,推动中 |
T3 | 推动中 | GuardCondition: RL::unsent_changes() == <empty>,宣布中 |
T4 | 推动中 | GuardCondition: RL::can_send() == true,推动中 |
T5 | 宣布中 | after(W::heartbeatPeriod),宣布中 |
T6 | 等待中 | 收到 ACKNACK 消息,等待中 |
T7 | 等待中 | GuardCondition: RL::requested_changes() != <empty>,必须修复 |
T8 | 必须修复 | 收到 ACKNACK 消息,必须修复 |
T9 | 必须修复 | after(W::nackResponseDelay),修复中 |
T10 | 修复中 | GuardCondition: RL::can_send() == true,修复中 |
T11 | 修复中 | GuardCondition: RL::requested_changes() == <empty>,等待中 |
T12 | 任何状态 | RTPS Writer 配置为不再具有 ReaderLocator,最终 |
8.4.8.1.1过渡 T1
该过渡由对具有 RTPS ReaderLocator 的 RTPS Reliable StatelessWriter 'the_rtps_writer' 的配置触发。此配置由Discovery协议(8.5,“Discovery Module”)完成,作为发现与“the_rtps_writer”相关的DDS DataWriter的DDS DataReader的结果。
Discovery协议提供ReaderLocator构造函数参数的值。该过渡在虚拟机中执行以下逻辑操作:
a_locator := new ReaderLocator( locator, expectsInlineQos ); the_rtps_writer.reader_locator_add( a_locator );
8.4.8.1.2过渡 T2
该过渡由 [RL::unsent_changes() != <empty>] 的守卫条件触发,表明RTPS Writer HistoryCache中有尚未发送到ReaderLocator的一些更改。该过渡在虚拟机中不执行逻辑操作。
8.4.8.1.3过渡 T3
该过渡由 [RL::unsent_changes == <empty>] 的守卫条件触发,表明RTPS Writer HistoryCache中的所有更改都已发送到ReaderLocator。请注意,这并不表示已接收更改,只是已尝试发送它们。该过渡在虚拟机中不执行逻辑操作。
8.4.8.1.4过渡 T4
该过渡由 [RL::can_send() == true] 的守卫条件触发,表明RTPS Writer 'the_writer'具有向RTPS ReaderLocator 'the_reader_locator'发送更改所需的资源。该过渡在虚拟机中执行以下逻辑操作:
a_change := the_reader_locator.next_unsent_change(); DATA = new DATA(a_change); IF (the_reader_locator.expectsInlineQos) { DATA.inlineQos := the_writer.related_dds_writer.qos; } DATA.readerId := ENTITYID_UNKNOWN; sendto the_reader_locator.locator, DATA;
执行过渡后,以下后置条件成立:
( a_change BELONGS-TO the_reader_locator.unsent_changes() ) == FALSE
8.4.8.1.5过渡 T5
该过渡由配置为每个 W::heartbeatPeriod 触发的周期定时器的触发而触发。该过渡在虚拟机中执行以下逻辑操作,适用于Writer 'the_rtps_writer'和ReaderLocator 'the_reader_locator':
seq_num_min := the_rtps_writer.writer_cache.get_seq_num_min(); seq_num_max := the_rtps_writer.writer_cache.get_seq_num_max(); HEARTBEAT := new HEARTBEAT(the_rtps_writer.writerGuid, seq_num_min, seq_num_max); HEARTBEAT.FinalFlag := SET; HEARTBEAT.readerId := ENTITYID_UNKNOWN; sendto the_reader_locator, HEARTBEAT;
8.4.8.1.6过渡 T6
该过渡由接收到发送至RTPS StatelessWriter 'the_rtps_writer'的ACKNACK消息触发,该消息源自某个RTPS Reader。该过渡在虚拟机中执行以下逻辑操作:
FOREACH reply_locator_t IN { Receiver.unicastReplyLocatorList, Receiver.multicastReplyLocatorList } reader_locator := the_rtps_writer.reader_locator_lookup(reply_locator_t); reader_locator.requested_changes_set(ACKNACK.readerSNState.set);
请注意,处理此消息使用了RTPS Receiver中的reply locators。这是StatelessWriter确定将回复发送到何处的唯一信息源。协议的正常功能要求RTPS Reader在AckNack之前插入InfoReply Submessage,以便适当设置这些字段。
8.4.8.1.7过渡 T7
该过渡由 [RL::requested_changes() != <empty>] 的守卫条件触发,表明有一些RTPS Reader在RTPS ReaderLocator可达处请求的更改。该过渡在虚拟机中不执行逻辑操作。
8.4.8.1.8过渡 T8
该过渡由接收到发送至RTPS StatelessWriter 'the_rtps_writer'的ACKNACK消息触发,该消息源自某个RTPS Reader。该过渡执行与Transition T6 (8.4.8.2.6) 执行的相同的逻辑操作。
8.4.8.1.9过渡 T9
该过渡由计时器触发,指示自进入状态 must_repair 以来经过的持续时间已达到 W::nackResponseDelay。该过渡在虚拟机中不执行逻辑操作。
8.4.8.1.10过渡 T10
该过渡由 [RL::can_send() == true] 的守卫条件触发,表明RTPS Writer 'the_writer' 具有向RTPS ReaderLocator 'the_reader_locator' 发送更改所需的资源。该过渡在虚拟机中执行以下逻辑操作。
a_change := the_reader_locator.next_requested_change(); IF a_change IN the_writer.writer_cache.changes { DATA = new DATA(a_change);IF (the_reader_locator.expectsInlineQos) { DATA.inlineQos := the_writer.related_dds_writer.qos; DATA.inlineQos += a_change.inlineQos; } DATA.readerId := ENTITYID_UNKNOWN; sendto the_reader_locator.locator, DATA; } ELSE { GAP = new GAP(a_change.sequenceNumber); GAP.readerId := ENTITYID_UNKNOWN; sendto the_reader_locator.locator, GAP; }
执行过渡后,以下后置条件成立:
( a_change BELONGS-TO the_reader_locator.requested_changes() ) == FALSE
请注意,可能已经由DDS DataWriter从HistoryCache中删除请求的更改。在这种情况下,StatelessWriter将发送GAP消息。
8.4.8.1.11过渡 T11
此过渡由守卫条件 [RL::requested_changes() == <empty>] 触发,表示在RTPS Reader定位器上没有进一步由RTPS Reader请求的更改。该过渡在虚拟机中不执行任何逻辑操作。
8.4.8.1.12过渡 T12
此过渡由一个名为‘the_rtps_writer’的RTPS Writer的配置触发,不再向名为‘the_reader_locator’的RTPS ReaderLocator发送。此配置由Discovery协议(8.5)执行,作为破坏与与‘the_rtps_writer’相关的DDS DataWriter的预先存在的匹配的结果。
该过渡在虚拟机中执行以下逻辑操作:
the_rtps_writer.reader_locator_remove(the_reader_locator); delete the_reader_locator;
8.4.9 RTPS StatefulWriter 行为
8.4.9.1 best-effort StatefulWriter 行为
有关每个匹配的RTPS Reader的Best-Effort RTPS StatefulWriter的行为详见图8.18。
图8.18 - best-effort StatefulWriter 与每个匹配的 Reader 相关的行为
状态机过渡详见表8.60。
表8.60 - best-effort Stateful Writer 行为与每个匹配的 Reader 相关的过渡
过渡 | 状态事件 | 下一状态 |
T1 | 初始状态 | RTPS Writer 配置为匹配的 RTPS Reader,空闲状态 |
T2 | 空闲状态 | GuardCondition: RP::unsent_changes() != <empty>,执行 T3 过渡,推进中状态 |
T3 | 推进中状态 | GuardCondition: RP::unsent_changes() == <empty>,返回空闲状态 |
T4 | 推进中状态 | GuardCondition: RP::can_send() == true,执行 T4 过渡,推进中状态 |
T5 | 准备状态 | RTPS Writer 的 HistoryCache 添加了新的更改,保持准备状态 |
T6 | 任意状态 | RTPS Writer 配置为不再与 RTPS Reader 匹配,进入最终状态 |
8.4.9.1.1 过渡 T1
此过渡由对匹配的 RTPS Reader 进行配置的 RTPS Writer ‘the_rtps_writer’ 触发。此配置是由Discovery协议(8.5)执行的,是在发现与‘the_rtps_writer’相关的DDS DataWriter匹配的DDS DataReader后的结果。
发现协议提供了ReaderProxy构造函数参数的值。该过渡在虚拟机中执行以下逻辑操作:
a_reader_proxy := new ReaderProxy(remoteReaderGuid, remoteGroupEntityId, expectsInlineQos, unicastLocatorList, multicastLocatorList); the_rtps_writer.matched_reader_add(a_reader_proxy);
ReaderProxy ‘a_reader_proxy’ 的初始化如8.4.7.5中讨论的一样。这包括初始化未发送更改的集合,并对每个更改应用 DDS_FILTER。
8.4.9.1.2 过渡 T2
此过渡由守卫条件 [RP::unsent_changes() != <empty>] 触发,表示在RTPS Writer HistoryCache中有一些未发送到由ReaderProxy表示的RTPS Reader的更改。
请注意,对于Best-Effort Writer,W::pushMode == true,因为没有确认。因此,只要数据可用,Writer 就会推送数据。
该过渡在虚拟机中不执行任何逻辑操作。
8.4.9.1.3 过渡 T3
此过渡由守卫条件 [RP::unsent_changes() == <empty>] 触发,表示RTPS Writer HistoryCache中的所有更改都已发送到由ReaderProxy表示的RTPS Reader。请注意,这并不表示已经接收到更改,只是已经尝试发送它们。
该过渡在虚拟机中不执行任何逻辑操作。
8.4.9.1.4 过渡 T4
此过渡由守卫条件 [RP::can_send() == true] 触发,表示RTPS Writer ‘the_rtps_writer’具有向由ReaderProxy ‘the_reader_proxy’表示的RTPS Reader发送更改所需的资源。
该过渡在虚拟机中执行以下逻辑操作:
a_change := the_reader_proxy.next_unsent_change(); a_change.status := UNDERWAY; if (a_change.is_relevant) { DATA = new DATA(a_change); IF (the_reader_proxy.expectsInlineQos) { DATA.inlineQos := the_rtps_writer.related_dds_writer.qos; DATA.inlineQos += a_change.inlineQos; } DATA.readerId := ENTITYID_UNKNOWN; send DATA; } else { GAP = new GAP(a_change.sequenceNumber); GAP.readerId := ENTITYID_UNKNOWN; Send GAP; }
上述逻辑并不意味着每个DATA子消息都在单独的RTPS消息中发送。相反,多个子消息可以合并成单个RTPS消息。
过渡后,以下后置条件成立:
(a_change BELONGS-TO the_reader_proxy.unsent_changes()) == FALSE
8.4.9.1.5 过渡 T5
此过渡由DDS DataWriter相应地将新的CacheChange ‘a_change’ 添加到RTPS Writer ‘the_rtps_writer’的HistoryCache触发。由DDS_FILTER确定更改是否与由ReaderProxy ‘the_reader_proxy’表示的RTPS Reader相关。
该过渡在虚拟机中执行以下逻辑操作:
codeCopy code ADD a_change TO the_reader_proxy.changes_for_reader; IF (DDS_FILTER(the_reader_proxy, change)) THEN change.is_relevant := FALSE; ELSE change.is_relevant := TRUE; IF (the_rtps_writer.pushMode == true) THEN change.status := UNSENT; ELSE change.status := UNACKNOWLEDGED;
8.4.9.1.6 过渡 T6
此过渡由RTPS Writer ‘the_rtps_writer’的配置触发,不再与由ReaderProxy ‘the_reader_proxy’表示的RTPS Reader匹配。此配置是由Discovery协议(8.5)执行的,是破坏与‘the_rtps_writer’相关的DDS DataWriter与DDS DataReader之间预先存在的匹配的结果。
该过渡在虚拟机中执行以下逻辑操作:
codeCopy code the_rtps_writer.matched_reader_remove(the_reader_proxy); delete the_reader_proxy;
8.4.9.2 可靠 StatefulWriter 行为
可靠 RTPS StatefulWriter 与每个匹配的 RTPS Reader 相关的行为详见图8.19。
图8.19 - Reliable StatefulWriter相对于每个匹配的Reader的行为
状态机转换列在表8.61中。
表8.61 - Reliable StatefulWriter相对于每个匹配的Reader的行为的转换
过渡 | 当前状态 | 事件 | 下一状态 |
T1 | 初始状态 | RTPS Writer 配置为匹配的 RTPS Reader | |
T2 | 宣告中状态 | GuardCondition: RP::unsent_changes() != <empty> | 推进中状态 |
T3 | 推进中状态 | GuardCondition: RP::unsent_changes() == <empty> | 宣告中状态 |
T4 | 推进中状态 | GuardCondition: RP::can_send() == true | 推进中状态 |
T5 | 宣告中状态 | GuardCondition: RP::unacked_changes() == <empty> | 空闲状态 |
T6 | 空闲状态 | GuardCondition: RP::unacked_changes() != <empty> | 宣告中状态 |
T7 | 宣告中状态 | 在(W::heartbeatPeriod)后,执行宣告中状态,T8 过渡,等待接收 ACKNACK 消息 | 等待状态 |
T8 | 等待状态 | 收到ACKNACK消息后,进入等待状态 | 等待状态 |
T9 | 等待状态 | GuardCondition: RP::requested_changes() != <empty>,执行 T10 过渡 | 必须修复状态 |
T10 | 必须修复状态 | 收到ACKNACK消息后,进入修复必要状态 | 必须修复状态 |
T11 | 必须修复状态 | 在(W::nackResponseDelay)后,执行 T12 过渡,修复中状态 | 修复中状态 |
T12 | 修复中状态 | GuardCondition: RP::can_send() == true,执行 T13 过渡,修复中状态 | 修复中状态 |
T13 | 修复中状态 | GuardCondition: RP::requested_changes() == <empty> | 等待状态 |
T14 | 准备状态 | RTPS Writer 的 HistoryCache 添加了新的更改,保持准备状态 | 准备状态 |
T15 | 准备状态 | RTPS Writer 的 HistoryCache 移除了一项更改,保持准备状态 | 准备状态 |
T16 | 任意状态 | RTPS Writer 配置为不再与 RTPS Reader 匹配,进入最终状态 | 终止状态 |
8.4.9.2.1 过渡 T1
此过渡由对匹配的 RTPS Reader 进行配置的 RTPS Reliable StatefulWriter ‘the_rtps_writer’ 触发。此配置是由Discovery协议(8.5)执行的,是在发现与‘the_rtps_writer’相关的DDS DataWriter匹配的DDS DataReader后的结果。
发现协议提供了ReaderProxy构造函数参数的值。该过渡在虚拟机中执行以下逻辑操作:
a_reader_proxy := new ReaderProxy(remoteReaderGuid, remoteGroupEntityId, expectsInlineQos, unicastLocatorList, multicastLocatorList); the_rtps_writer.matched_reader_add(a_reader_proxy);
ReaderProxy ‘a_reader_proxy’ 的初始化如8.4.7.5中讨论的一样。这包括初始化未发送更改的集合并对每个更改应用过滤器。
8.4.9.2.2 过渡 T2
此过渡由守卫条件 [RP::unsent_changes() != <empty>] 触发,表示在RTPS Writer HistoryCache中有一些未发送到由ReaderProxy表示的RTPS Reader的更改。
该过渡在虚拟机中不执行任何逻辑操作。
8.4.9.2.3 过渡 T3
此过渡由守卫条件 [RP::unsent_changes() == <empty>] 触发,表示RTPS Writer HistoryCache中的所有更改都已发送到由ReaderProxy表示的RTPS Reader。请注意,这并不表示已经接收到更改,只是已经尝试发送它们。
该过渡在虚拟机中不执行任何逻辑操作。
8.4.9.2.4 过渡 T4
此过渡由守卫条件 [RP::can_send() == true] 触发,表示RTPS Writer ‘the_rtps_writer’具有向由ReaderProxy ‘the_reader_proxy’表示的RTPS Reader发送更改所需的资源。
该过渡在虚拟机中执行以下逻辑操作:
a_change := the_reader_proxy.next_unsent_change(); a_change.status := UNDERWAY; if (a_change.is_relevant) { DATA = new DATA(a_change); IF (the_reader_proxy.expectsInlineQos) { DATA.inlineQos := the_rtps_writer.related_dds_writer.qos; DATA.inlineQos += a_change.inlineQos; } DATA.readerId := ENTITYID_UNKNOWN; send DATA; } else { GAP = new GAP(a_change.sequenceNumber); GAP.readerId := ENTITYID_UNKNOWN; send GAP; }
上述逻辑并不意味着每个DATA或GAP子消息都在单独的RTPS消息中发送。相反,多个子消息可以合并成单个RTPS消息。
过渡后,以下后置条件成立:
(a_change BELONGS-TO the_reader_proxy.unsent_changes()) == FALSE
8.4.9.2.5 过渡 T5
此过渡由守卫条件 [RP::unacked_changes() == <empty>] 触发,表示RTPS Writer HistoryCache中的所有更改都已由由ReaderProxy表示的RTPS Reader确认。
该过渡在虚拟机中不执行任何逻辑操作。
8.4.9.2.6 过渡 T6
此过渡由守卫条件 [RP::unacked_changes() != <empty>] 触发,表示RTPS Writer HistoryCache中有未被由ReaderProxy表示的RTPS Reader确认的更改。
该过渡在虚拟机中不执行任何逻辑操作。
8.4.9.2.7 过渡 T7
此过渡由定期计时器触发,该计时器配置为每隔 W::heartbeatPeriod 触发一次。
该过渡在虚拟机中为 StatefulWriter ‘the_rtps_writer’ 执行以下逻辑操作:
seq_num_min := the_rtps_writer.writer_cache.get_seq_num_min(); seq_num_max := the_rtps_writer.writer_cache.get_seq_num_max(); HEARTBEAT := new HEARTBEAT(the_rtps_writer.writerGuid, seq_num_min, seq_num_max); HEARTBEAT.FinalFlag := NOT_SET; HEARTBEAT.readerId := ENTITYID_UNKNOWN; send HEARTBEAT;
8.4.9.2.8 过渡 T8
此过渡由接收到一个ACKNACK消息触发,该消息由由ReaderProxy ‘the_reader_proxy’表示的RTPS Reader发送到RTPS StatefulWriter ‘the_rtps_writer’。该过渡在虚拟机中执行以下逻辑操作:
the_rtps_writer.acked_changes_set(ACKNACK.readerSNState.base - 1); the_reader_proxy.requested_changes_set(ACKNACK.readerSNState.set);
过渡后,以下后置条件成立:
MIN { change.sequenceNumber IN the_reader_proxy.unacked_changes() } >= ACKNACK.readerSNState.base - 1
8.4.9.2.9 过渡 T9
此过渡由守卫条件 [RP::requested_changes() != <empty>] 触发,表示由ReaderProxy表示的RTPS Reader请求了一些更改。
该过渡在虚拟机中不执行任何逻辑操作。
8.4.9.2.10 过渡 T10
此过渡由接收到一个ACKNACK消息触发,该消息由由ReaderProxy ‘the_reader_proxy’表示的RTPS Reader发送到RTPS StatefulWriter ‘the_writer’。该过渡执行与过渡 T8 (8.4.9.2.8) 相同的逻辑操作。
8.4.9.2.11 过渡 T11
此过渡由一个计时器触发,该计时器指示自进入状态 must_repair 以来已经过去了W::nackResponseDelay的持续时间。
该过渡在虚拟机中不执行任何逻辑操作。
8.4.9.2.12 过渡 T12
此过渡由守卫条件 [RP::can_send() == true] 触发,表示RTPS Writer ‘the_rtps_writer’具有向由ReaderProxy ‘the_reader_proxy’表示的RTPS Reader发送更改所需的资源。
该过渡在虚拟机中执行以下逻辑操作:
codeCopy code a_change := the_reader_proxy.next_requested_change(); a_change.status := UNDERWAY; if (a_change.is_relevant) { DATA = new DATA(a_change, the_reader_proxy.remoteReaderGuid); IF (the_reader_proxy.expectsInlineQos) { DATA.inlineQos := the_rtps_writer.related_dds_writer.qos; DATA.inlineQos += a_change.inlineQos; } send DATA; } else { GAP = new GAP(a_change.sequenceNumber, the_reader_proxy.remoteReaderGuid); send GAP; }
上述逻辑并不意味着每个DATA或GAP子消息都在单独的RTPS消息中发送。相反,多个子消息可以合并成单个RTPS消息。
过渡后,以下后置条件成立:
(a_change BELONGS-TO the_reader_proxy.requested_changes()) == FALSE
8.4.9.2.13 过渡 T13
此过渡由守卫条件 [RP::requested_changes() == <empty>] 触发,表示由ReaderProxy表示的RTPS Reader不再请求更改。
该过渡在虚拟机中不执行任何逻辑操作。
8.4.9.2.14 过渡 T14
此过渡由相应的DDS DataWriter将新的CacheChange ‘a_change’ 添加到RTPS Writer ‘the_rtps_writer’的HistoryCache触发。变化是否与由ReaderProxy ‘the_reader_proxy’表示的RTPS Reader相关,由DDS_FILTER确定。
该过渡在虚拟机中执行以下逻辑操作:
codeCopy code ADD a_change TO the_reader_proxy.changes_for_reader; IF (DDS_FILTER(the_reader_proxy, change)) THEN a_change.is_relevant := FALSE; ELSE a_change.is_relevant := TRUE; IF (the_rtps_writer.pushMode == true) THEN a_change.status := UNSENT; ELSE a_change.status := UNACKNOWLEDGED;
8.4.9.2.15 过渡 T15
此过渡由相应的DDS DataWriter从RTPS Writer ‘the_rtps_writer’的HistoryCache中移除CacheChange ‘a_change’ 触发。例如,当使用HISTORY QoS设置为KEEP_LAST且depth == 1时,新更改将导致DDS DataWriter从HistoryCache中删除先前的更改。
该过渡在虚拟机中执行以下逻辑操作:
a_change.is_relevant := FALSE;
8.4.9.2.16 过渡 T16
此过渡由配置RTPS Writer ‘the_rtps_writer’不再与由ReaderProxy ‘the_reader_proxy’表示的RTPS Reader匹配触发。这是由Discovery协议(8.5)执行的,是由于破坏与‘the_rtps_writer’相关的DDS DataReader与DDS DataWriter之间的预先存在的匹配关系。
该过渡在虚拟机中执行以下逻辑操作:
the_rtps_writer.matched_reader_remove(the_reader_proxy); delete the_reader_proxy;
8.4.9.3 ChangeForReader 说明
ChangeForReader 跟踪每个CacheChange相对于特定远程RTPS Reader(由相应的ReaderProxy标识)的通信状态(属性 status)和相关性(属性 is_relevant)。
当创建ChangeForReader时,属性 is_relevant 根据可能应用的DDS QoS和过滤器设置为TRUE或FALSE。当相应的CacheChange由于后续CacheChange而对于RTPS Reader不再相关时,可能会将ChangeForReader的is_relevant设置为FALSE。例如,当相关的DDS DataWriter的DDS QoS指定HISTORY kind为KEEP_LAST,并且后续CacheChange修改相同数据对象的值(由CacheChange的instanceHandle属性标识),使得先前的CacheChange变得不相关时,就会发生这种情况。
RTPS StatefulWriter的行为如图8.20和图8.21所示,会在协议操作的副作用下修改每个ChangeForReader。为了进一步定义协议,审查表示任何给定ChangeForReader的status属性值的有限状态机是有益的,如下图8.22所示,适用于可靠的StatefulWriter。Best-Effort StatefulWriter仅使用状态图的子集。
图8.20 - 每个ChangeForReader的status属性值的变化
这些状态具有以下含义:
-
<New>:RTPS StatefulWriter的HistoryCache中存在具有SequenceNumber_t 'seq_num'的CacheChange,但尚未宣布或发送到由ReaderProxy表示的RTPS Reader。
-
<Unsent>:StatefulWriter从未向RTPS Reader发送过具有此seq_num的DATA或GAP,但打算将来发送。
-
<Requested>:RTPS Reader通过ACKNACK消息请求再次发送更改。StatefulWriter打算在将来再次发送更改。
-
<Underway>:CacheChange已发送,StatefulWriter将忽略对此CacheChange的新请求。
-
<Unacknowledged>:CacheChange应该被RTPS Reader接收,但尚未得到RTPS Reader的确认。由于消息可能已丢失,RTPS Reader可能会请求重新发送CacheChange。
-
<Acknowledged>:RTPS StatefulWriter知道RTPS Reader已收到具有SequenceNumber_t 'seq_num'的CacheChange。 以下描述了触发状态机过渡的主要事件。请注意,此状态机仅跟踪特定ChangeForReader的'status'属性,并不执行任何特定操作,也不发送任何消息。
-
new ChangeForReader(seq_num):ReaderProxy已创建一个ChangeForReader关联类,以跟踪具有SequenceNumber_t seq_num的CacheChange的状态。
-
[W::pushMode == true]:StatefulWriter的属性W::pushMode的设置确定状态是否更改为<Unsent>,否则更改为<Unacknowledged>。BestEffort Writer始终使用W::pushMode == true。
-
received NACK(seq_num):StatefulWriter已收到包含在ACKNACK.readerSNState中的seq_num的ACKNACK消息,表明RTPS Reader尚未接收CacheChange,并希望StatefulWriter重新发送它。
-
sent DATA(seq_num):StatefulWriter已发送包含具有SequenceNumber_t seq_num的CacheChange的DATA消息。
-
sent GAP(seq_num):StatefulWriter已发送GAP,其中seq_num在GAP的irrelevant_sequence_number_list中,这意味着seq_num对于RTPS Reader不相关。
-
received ACK(seq_num):Writer已收到带有ACKNACK.readerSNState.base > seq_num的ACKNACK。这意味着RTPS Reader已收到具有序列号seq_num的CacheChange。