一.RTPS协议概述
RTPS协议主要由四个部分组成:
1.发现模块(Discovery)
发现模块是定义了RTPS的参与者(Participant)获取其他RTPS的参与者(Participant),端点(Endpoint)的协议,使得每个参与者(Participant)能够了解到整个网络中其他参与者的存在并且相互匹配。
metatraffic通信使得RTPS的参与者(Participant)可以获取到所有Participant,Reader以及Writer的快照并且让本地Reader和远端Writer以及本地Writer和远端Reader之间通信。
RTPS规范将发现模块拆为两个部分:
1.1 参与者发现协议(PDP):指定了参与者如何在网络中发现彼此。
1.2 端点发现协议(EDP):一旦发现彼此,则两个参与者就使用端点发现协议(EDP)交换起包含的端点的信息。
所有RTPS的实现必须提供简单参与者发现协议(Simple PDP)和简单端点发现协议(Simple EDP)。
2.结构(Structor)
结构定义了RTPS的各类实体(Entity)与域(Domain)的关系以及各类实体间的关联:
Entity:所有RTPS实体的父类,每个Entity对象拥有Guid,并且对其他Entity对象可见。Guid属性如下:
struct RTPS_DllAPI GUID_t // 唯一标识一个Entity
{
//!Guid prefix 每个Participant和其下的Endpoint都拥有相同的Guid Prefix
GuidPrefix_t guidPrefix;
//!Entity id 每个实体的EntityID不同
EntityId_t entityId;
}
Endpoint:特殊的Entity,是RTPS消息的起点或者终点。Endpoint涉及的属性如下:
属性 | 类型 | 含义 | 和DDS的关系 |
---|---|---|---|
unicastLocatorList | Locator_t[*] | 发送RTPS消息的目标Endpoint的单播地址的列表 | 在Discovery过程中配置 |
multicastLocatorLi st | Locator_t[*] | 发送RTPS消息的多播地址的列表(可以为空) | 在Discovery过程中配置 |
ReliabilityKind | ReliabilityKind_t | 可靠性等级(BestEffort / Reliable) | Qos中指定的 |
topicKind | TopicKind_t | 标识当前Endpoint上传输的数据是否有key,key数据的主要类型是InstanceHandle_t,对应了Guid | |
endpointGroup | EntityId_t | 标记了Endpoint属于哪个RTPSGroup | 和DDS中的Subscriber/Publisher关联 |
Participant:是所有Endpoint的容器,共享属性。Participant涉及到的属性如下:
属性 | 类型 | 含义 | 和DDS的关系 |
---|---|---|---|
defaultUnicastLocatorList | Locator_t[*] | 默认的单播地址列表(地址+端口) | 在Discovery过程中配置 |
defaultMulticastLocat orList | Locator_t[*] | 默认的组播地址列表(地址+端口) | 在Discovery过程中配置 |
ProtocolVersion | ProtocolVersion_t | RTPS协议版本号 | |
VendorId | VendorId_t | RTPS中间商代码 | 需要向OMG组织申请 |
Writer:RTPS消息的起点,发送HistoryChange中的CacheChange消息到匹配的Reader的HistoryChange。
Reader:RTPS消息的终点,接受匹配的Writer发送的CacheChange数据。
RTPSGroup:主要有两种Group(Publisher和Subscriber)
3.消息(Message)
消息模块定义了Reader和Writer之间交换的消息格式,主要有消息头(Header)和一系列子消息(SubMessage组成),每个子消息都由一系列子消息元素组成:
Header:
一个消息(Message)中可能包含多个子消息(SubMessage),这些子消息之间可能存在依赖关系/解释关系。
子消息分为实体子消息(Entity SubMessage)和解释子消息(Interpreter SubMessage)。
4.行为(Behavior)
行为定义了Reader和Writer交换RTPS消息的顺序以及消息引起的相关实体(主要是Reader和Writer)状态的变化,RTPS中主要有行为模块的视线(有状态 Stateful 和 无状态 Stateless)。
RTPS Writer不控制什么时候从Writer的HistoryCache中删除CacheChange,删除CacheChange的动作是由DDS的Writer来完成的,这个和Qos配置有关,例如如果配置了KEEP_LAST,那么DataWriter只会保留最后一次发送的CacheChange数据,删除之前的Change数据。
必须实现RTPS MessageReceiver接口(因为涉及到解析子消息时上下文的处理和状态机的切换)
StatefulWriter和StatefulReader交互数据的流程如下图:
4.1 通用要求(General Requirement)
Writer:
RTPSWriter发送HistoryCache中的CacheChange数据时,必须按照SequenceNumber的顺序进行发送。
如果Reader要求inline Qos,则Writer发送数据的时候必须带上Qos数据(in-line Qos)。
对于StatefulWriter,必须定周期发送HeartBeat子消息(如果HistoryCache中还有数据)。如果HistoryCache中没有数据了,那么StatefulWriter就不需要发送HeartBeat子消息了。StatefulWriter必须持续发送HeartBeat消息给StatefulReader(如果Reader返回的时候NACK)直到Reader读取了所有数据。
Writer对于Reader的NACK消息进行响应处理(因为这代表Reader有miss的数据需要Writer进行重发)
Reader:
因为Stateless的Reader是完全被动的,只接受数据,不发送响应数据。因此,对Reader的通用要求大部分都是针对StatefulReader的。
如果reader收到的HeartBeat包没有设置final flag标志位,则Reader对于这包HeartBeat必须做出响应,回复ACK或者NACK SubMessage。
如果reader有数据没有收到(收到HeartBeat比对seq以后),必须回复Nack包告知Writer有数据包丢失需要重传。这个回复可以延迟以避免网络数据风暴。
4.2 具体实现
Writer:
StatelessWriter和StatefulWriter的主要区别在于持有对端Reader的方式和信息有所不同,类结构的区别如下图:
RTPSWriter的主要属性有下面8种:
属性 | 类型 | 含义 | 和DDS的关系 |
---|---|---|---|
pushMode | bool | 当属性为true时,Writer主动将change数据推送给reader,反之,change数据随着心跳包一起发送,并且是作为对Reader请求的应答 | N/A |
heartbeatPeriod | Duration_t | 周期发送心跳包消息的间隔(心跳包包含可被reader读取的数据的lastchangenumber) | N/A |
nackResponseDelay | Duration_t | 对于Reader发送的NACK消息,允许Writer进行回复的最大延时 | N/A |
lastChangeSequenceNumber | SequenceNumber | 用于分配给下一个Change数据的内部递增的序列号 | N/A |
writer_cache | HistoryCache | 包含历史CacheChange的容器,每个Writer内部保存 | N/A |
nackSuppressionDuration | Duration_t | 针对已经发送的CacheChange,如果在nackSuppressionDuration时间内收到了Reader发送的NACK报文,则可以忽略。 | N/A |
dataMaxSize | 可选属性 | N/A |
RTPSWriter主要提供new构造函数以及new_change函数,new_change函数返回新构造的CacheChange对象。
函数名 | 参数 | 类型 |
---|---|---|
new | < return value> | Writer |
attribute_values | 创建Writer以及其父类Endpoint时需要的属性集合 | |
New_change | < return value> | CacheChange |
kind | ChangeKind_t | |
data | Data | |
inlineQos | ParameterList | |
handle | InstanceHandle_t |
new操作大致步骤:
1.给如下成员赋值:
GUID:Entity的GUID
unicastlocatorlist:数据通信的单播地址列表
multicastlocatorlist:数据通信的组播地址列表
reliabilityLevel:可靠性等级(Reliable, BestEffort)
topicKind: NoKey, WithKey
pushMode: 主动推送数据还是跟随HeartBeat一起发送数据
heartbeatPeriod:心跳包间隔
nackResponseDelay:对于Reader的Nack包的最长回复延时
nackSuppressionDuration:发送Change数据多少时间内收到Nack子消息是可以忽略的
lastChangeSequenceNumber:数据包的序列号(递增)
- 创建writer_cache:Writer的HistoryCache(用于存放要发送的CacheChange)
New_change的操作步骤大致如下:
递增lastChangeSequenceNumber
新建CacheChange对象
a_change := new CacheChange(kind, this.guid, this.lastChangeSequenceNumber,
data, inlineQos, handle);
返回CacheChange对象
StatelessWriter:
1.StatelessWriter不知道匹配的Reader的数量,也不保存每个匹配的RTPSReader的状态。StatelessWriter只保留要发送数据的Reader的Loator信息以便发送数据。
属性 | 类型 | 含义 | 和DDS的关系 |
---|---|---|---|
reader_locators | ReaderLocator[*] | StatelessWriter保存发送数据的匹配Reader的locator列表,其中包括单播地址和组播地址 | N/A |
StatelessWriter适用于内存较小(HistoryCache可以分配的小一点)或者对传输性能高的场合(例如组播的通信方式就适合数据大的场景)
函数名 | 参数 | 类型 |
---|---|---|
new | < return value> | StatelessWriter |
Attribute_values | 创建Statelesswriter和父类Endpint需要的参数集合 | |
reader_locator_add | < return value> | void |
a_locator | Locator_t | |
reader_locator_remove | < return value> | void |
a_locator | Locator_t | |
Unsent_change_reset | < return value> | Void |
New函数主要是创建了一个内部空的ReaderLocator列表用于后续添加Reader的Locator
reader_locator_add 函数功能是添加一个远端Reader的Locator(单播或者组播)到内部的ReaderLocatorList中
ADD a_locator TO {this.reader_locators};
reader_locator_remove 函数的功能是从内部的ReaderLocatorList中移除一个Locator
REMOVE a_locator FROM {this.reader_locators};
Unsent_change_reset函数的功能是重置内部ReaderLocatorList中每个ReaderLocator中的highestSentChangeSN的值
FOREACH readerLocator in {this.reader_locators} DO
readerLocator. highestSentChangeSN := 0
ReaderLocator
ReaderLocator是StatelessWriter用于记录所有匹配的远端Reader位置信息的值类型
ReaderLocator的属性主要如下:
属性 | 类型 | 含义 | 和DDS的关系 |
---|---|---|---|
hightestSentSN | SequenceNumber_t | Writer发给该ReaderLocator代表的Reader的所有Change中序列号的最大值 | NA |
requestChanges | SequenceNumber_t[*] | 该ReaderLocator代表的Reader所请求的Change的序列号的集合 | N/A |
locator | Locator_t | 通过locator中的单播或者组播地址可以访问到其代表的远端Reader | NA |
expectsInlineQos | bool | 代表远端Reader是否希望发送的Data Message中带有Qos信息 | N/A |
ReaderLocator对外部的接口如下:
接口名 | 参数 | 类型 |
---|---|---|
new | < return value > | ReaderLocator |
attribute_values | ReaderLocator构造函数需要的参数集合 | |
next_requested_change | < return value > | SequenceNumber_t |
next_unsent_change | < return value > | SequenceNumber_t |
requested_changes | < return value > | SequenceNumber_t[*] |
requested_changes_set | < return value > | void |
req_seq_num_set | SequenceNumber_t[*] | |
unsent_changes | < return value > | boolean |
new 接口:
创建并且初始化一个ReaderLocator最后返回改ReaderLocator
this.requested_changes := <empty>;
this.highestSentChangeSN := SEQUENCE_NUMBER_INVALID;
this.locator := <as specified in the constructor>;
this.expectsInlineQos := <as specified in the constructor;
next_requested_change 接口:
返回远端Reader通过ACKNACK消息回复中请求的Change记录中最小的序列号
return MIN( this.requested_changes() )
next_unsent_change接口:
writerHistory中下一个要向该ReaderLocator发送的CacheChange的序列号
ReaderLocator的highestSentChangeSN代表发送给该ReaderLocator的CacheChage中的序列号的最大值
WriterHistory中CacheChange的序列号大于ReaderLocator的highestSentChangeSN的就是需要后续发送给该ReaderLocator的记录。
unsent_changes :=
{ changes SUCH_THAT change.sequenceNumber > this.highestSentChangeSN }
IF unsent_changes == <empty> return SEQUENCE_NUMBER_INVALID
ELSE return MIN { unsent_changes.sequenceNumber }
requested_changes接口:
返回该ReaderLocator的requested_changes中的SequenceNumber集合(Reader通过ACKNACK子消息请求Writer重新发送的Change的序列号集合)
return this.requested_changes;
requested_changes_set 接口:
该接口将参数中的序列号集合添加到ReaderLocator的requested_changes中(个人认为是收到ACKNACK报文时将Reader请求重发的Change的SequenceNumber集合添加到该ReaderLocator的requested_changes中)
FOR_EACH seq_num IN req_seq_num_set DO
ADD seq_num TO this.requested_changes;
END
unsent_changes 接口:
该接口返回是否WriterHistory中还有ReaderLocator代表的远端Reader没有读取确认的Change
return this.next_unsent_change() != SEQUENCE_NUMBER_INVALID;
RTPS StatefulWriter
StatefulWriter是RTPS Writer的stateful版本实现,StatuefulWriter知晓所有匹配的远端RTPS Reader端点并且维护了每个匹配的远端RTPS Reader端点的状态。
通过维护每个远端RTPS Reader端点的状态,StatefulWriter可以匹配的某个远端RTPS Reader是否收到了具体某个CacheChange,并且可以优化网络传输,避免出现发送广播数据将某个Reader已经收到的CacheChange再次发给这个Reader。StatefulWriter的主要属性如下:
属性 | 类型 | 含义 | 和DDS的关系 |
---|---|---|---|
matched_readers | ReaderProxy[*] | 每个ReaderProxy代表某个和当前StatefulWriter匹配的远端Reader。 | N/A |
StatefulWriter 提供的对外接口如下:
接口名 | 参数 | 类型 |
---|---|---|
new | < return value > | StatefulWriter |
attribute_values | StatefulWriter以及父类RTPSWriter/Endpoint构造需要的参数集合 | |
matched_reader_add | < return value> | void |
a_reader_proxy | ReaderProxy | |
matched_reader_remove | < return value> | void |
a_reader_proxy | ReaderProxy | |
matched_reader_lookup | < return value> | void |
a_reader_guid | GUID_t | |
is_acked_by_all | < return value> | bool |
a_change_seq_num | SequenceNumber_t |
接口详解:
new接口:
创建StatefulWriter对象,构造函数中创建空的matched_readers数组用于后期存放匹配的远端ReaderProxy。
this.matched_readers := <empty>;
is_acked_by_all接口:
判断某个CacheChange(根据序列号SequenceNumber)是否被所有匹配的Reader确认读取到,全部读取返回true,否则返回false
return true IF and only IF
FOREACH proxy IN this.matched_readers
a_change_seq_num IN proxy.acknowledged_changes
matched_reader_add接口:
添加一个匹配的远端Reader的Proxy到matched_readers
ADD a_reader_proxy TO {this.matched_readers};
matched_reader_remove接口:
从matched_readers中删除指定的ReaderProxy
REMOVE a_reader_proxy FROM {this.matched_readers}; delete proxy;
matched_reader_lookup接口:
通过远端Reader的GUID查找matched_readers中是否有该GUID对应的ReaderProxy
FIND proxy IN this.matched_readers
SUCH-THAT (proxy.remoteReaderGuid == a_reader_guid); return proxy;
RTPS ReaderProxy
ReaderProxy由RTPS Writer维护,每一个ReaderProxy对象代表和该Writer匹配的某个远端Reader的信息。ReaderProxy的属性如下:
属性 | 类型 | 含义 | 和DDS的关系 |
---|---|---|---|
remoteReaderGuid | GUID_t | ReaderProxy所代表的远端RTPS Reader的GUID | N/A |
remoteGroupEntityId | EntityId_t | 远端RTPS Reader所属Group的EntityID | 这个EntityID代表的是RTPS Reader对应的DDS DataReader所属的Subscriber的EntityID |
unicastLocatorList | Locator_t[*] | 通过该单播地址列表Writer可将Change数据发送给远端匹配的Reader,这个列表可以为空 | N/A |
multicastLocatorList | Locator_t[*] | 通过该组播地址列表Writer可以将Change数据发送给远端匹配的Reader,这个列表可以为空 | N/A |
highestSentChangeSN | SequenceNumber_t | 所有发送给Proxy代表的远端Reader的Change的最大序列号 | N/A |
requestedChanges | SequenceNumber_t[*] | Proxy代表的远端Reader通过NACK子所消息请求重发的Change的序列号集合 | N/A |
acknowledgedChanges | SequenceNumber_t[*] | Proxy代表的远端Reader通过ACK子消息确认已经收到的Change的序列号的集合 | N/A |
expectsInlineQos | bool | Proxy代表的远端Reader是否希望Writer发送的DataMessage中待用Qos信息 | N/A |
isActive | Proxy代表的远端Reader是否还能正常应答Writer的心跳包 | N/A |
当StatefulWriter和StatefulReader匹配后,StatefulWriter会将HistoryCache中的Change发送给ReaderProxy代表StatefulReader。 这个匹配的过程也收到DDS协议层实体匹配过程的影响,DDS DataWriter根据主题,Qos来匹配DDS DataReader的。
ReaderProxy的对外接口如下表格:
接口名 | 参数 | 类型 |
---|---|---|
new | < return value > | ReaderProxy |
attribute_values | ReaderProxy构造函数参数 | |
acked_changes_set | < return value > | void |
committed_seq_num | SequenceNumber_t | |
next_requested_change | < return value > | SequenceNumber_t |
next_unsent_change | < return value > | SequenceNumber_t |
unsent_changes | < return value > | bool |
requested_changes | < return value > | SequenceNumber_t[*] |
requested_changes_set | < return value > | void |
req_seq_num_set | < return value > | |
unacked_changes | < return value > | bool |
new接口:
创建并且返回ReaderProxy对象,并且初始化内部request_changes,acknowledgedChanges容器和highestSentChangeSN初始值
this.attributes := <as specified in the constructor>;
this.requested_changes := <empty>;
this.acknowledged_changes := <empty>;
this.highest_sent_seq_num := 0;
acked_changes_set接口:
参数committed_seq_num是SequenceNumber,WriterHistory中CacheChange的序列号如果小于committed_seq_num,那么该序列号就被添加到ReaderProxy的acknowledgedChanges中,代表这个序列号的Change已经被该ReaderProxy代表的远端Reader确认读取(通过ACK子消息)
FOR_EACH seq_num <= committed_seq_num DO
ADD seq_num TO this.acknowledged_changes
next_requested_change接口:
该接口返回下一个要被发送给远端Reader的Change的序列号(requested_changes中的最小序号)
return MIN( this.requested_changes() );
next_unsent_change接口:
返回WriterHistory中下一个未被发送的Change的序列号(是没有发送过,而不是通过NACK返回的需要重新发送的Change的序列号),也就是History中比ReaderProxy的highestSentChangeSN大的序列号集合中最小的那个序列号。
unsent_changes :=
{ changes SUCH-THAT change.sequenceNumber > this.higuest_sent_seq_num }
IF unsent_changes == <empty> return SEQUENCE_NUMBER_INVALID
ELSE return MIN { unsent_changes.sequenceNumber }
requested_changes接口:
返回ReaderProxy所代表的远端StatefulReader通过ACKNACK子消息请求发送的Change的序列号的集合
return this.requested_changes
requested_changes_set接口:
将参数req_seq_num_set中的序列号添加到ReaderProxy的requestedChanges中
appear in the parameter FOR_EACH seq_num IN req_seq_num_set DO
ADD seq_num TO this.requested_changes;
END
unsent_changes接口:
如果WriterHistory中的所有CacheChange都发给过ReaderProxy代表的远端Reader,则范围true,否则返回false
return ( this.next_unsent_change() != SEQUENCE_NUMBER_INVALID )
unacked_changes接口:
如果WriterHistory中存在没有被ReaderProxy代表的远端Reader确认的Change的序列号(通过ACK报文),返回true,否则返回false
highest_available_seq_num := MAX { change.sequenceNumber }
highest_acked_seq_num := MAX { this.acknowledged_changes }
return ( highest_available_seq_num > highest_acked_seq_num )
RTPS StatelessWriter Behavior
BestEffort对应的StatelessWriter的交互流程如下:
Best-Effort的StatelessWriter的状态机如下:
状态迁移 | 状态 | 事件 | 下一个状态 |
---|---|---|---|
T1 | initial | RTPS Writer和代表远端Reader的ReaderLocator匹配 | idle |
T2 | idle | 触发事件: WriterHistory中的存在没有发送给RL的Change RL::unsent_changes() == false | pushing |
T3 | pushing | 触发事件: WriterHistory中的没有未发送给RL的Change RL::unsent_changes() == true | idle |
T4 | pushing | 触发事件: 发送过程中发现存在Change的序列号不连续,需要发送GAP通知Reader有Change的状态为unavailable RL::can_send() == true | pushing |
T5 | any state | 和远端Reader不再匹配 | final |
下面是每个状态的具体说明:
状态迁移1:
当Best-Effort类型的StatelessWriter和远端RTPS Reader匹配上(Best-Effort或者Reliable类型的StatelessReader)的时候触发该状态,这个是由EDP机制来完成匹配的。
a_locator := new ReaderLocator( locator, expectsInlineQos );
the_rtps_writer.reader_locator_add( a_locator );
状态迁移2:
当前Writer状态为idle,如果此时WriterHistory中存在没有发给过ReadLocator对应的远端Reader的Change(ReaderLocator中的highestSentChangeSN 小于WriterHistory中CacheChange中的最大序列号),此时状态迁移到pushing,开始发送Change
状态迁移3:
当前Writrer状态为pushing(Change发送中),如果ReaderLocator的highestSentChangeSN等于WriterHistory的Changes的最大序列号,说明WriterHistory中所有的Change都已经发给该ReaderLocator对应的远端Reader(注意,这里的发送不代表远端Reader一定收到了,只是Writer已经尽力发出去了)。此时,状态迁移到idle。
状态迁移4:
当前Writrer状态为pushing(Change发送中),如果WriterHistory中存在大于ReaderLocator::highestSentChangeSN的Change记录,并且这些记录中存在序列号不连续的情况时,当发送到不连续的序列号的Change时,需要发送GAP子消息代替原来的Change,用于告知远端Reader该序列号对应的Change已经不在WriterHistory中了。
a_change_seq_num := the_reader_locator.next_unsent_change();
// 这个序列号是WriterHistory中可以发给readerlocator的Changes中的最小序列号
// 需要先查看这个最小的序列号是否和ReaderLocator的最大序列号连续
IF ( a_change_seq_num > the_reader_locator.higuest_sent_seq_num +1 ) {
// 如果序列号不连续,需要发送GAP子消息通知Reader有该区间的Change不可获取
GAP = new GAP(the_reader_locator.higuest_sent_seq_num + 1,
a_change_seq_num - 1);
GAP.readerId := ENTITYID_UNKNOWN;
GAP.filteredCount := 0;
sendto the_reader_locator.locator, GAP;
}
// 然后发送WriterHistory中可以获取的Change
a_change := the_writer.writer_cache.get_change(a_change_seq_num);
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;
// 更新RL的最大发送序列号
the_reader_locator.higuest_sent_seq_num := a_change_seq_num;
最后一定要更新ReaderLocatror的higuest_sent_seq_num ,防止发送的Change后面又重发。
状态迁移5:
当远端StatelessReader(Reliable或者Best-Effort类型的)不再和StatelessWriter匹配,则需要从reader_locators移除该ReaderLocator。
Reliable的StatelessWriter的状态机如下:
Reliable类型的StatelessWriter的状态迁移如下图:
Reliable类型的StatelessWriter有如下状态:
状态迁移 | 状态 | 事件 | 下一个状态 |
---|---|---|---|
T1 | initial | RTPSWriter和远端Reader匹配 | announcing |
T2 | announcing | 触发条件: WriterHistory中有未发送给该Reader的Change RL::unsent() == true | pushing |
T3 | pushing | 触发条件: WriterHistory中没有未发送给该Reader的Change RL::unsent() == false | announcing |
T4 | pushing | 触发条件: WriterHistory中存在未发送给该Reader的Change,并且可能存在不连续的情况(丢失Change) | pushing |
T5 | announcing | 触发条件: 心跳包间隔时间到达 after(W::heartbeatPeriod) | announcing |
T6 | waiting | 触发条件: 收到Reader发送的ACKNACK子消息 | waiting |
T7 | waiting | 触发条件: Reader有请求发送Change RL::request_changes() != | must_repair |
T8 | must_repair | 触发条件: 收到Reader发送的ACKNACK子消息 | must_repair |
T9 | must_repair | 触发条件: 从收到第一包Reader发送的ACKNACK子消息开始已经超过了Writer设定的重发Change的延时时间 after(W::nackResponseDelay) | repairing |
T10 | repairing | 触发条件: WriterHistory中存在可以发给Reader的Change(如果不连续,则补GAP子消息) | repairing |
T11 | repairing | 触发条件: Reader发送的ACKNACK子消息中请求的Change都已经发送完成 RL::request_changes() == | waiting |
T12 | any state | 触发条件: 远端Reader和Writer不再匹配 | final |
状态迁移1:
该状态迁移是由远端StatelessReader(无论Reliable还是Best-Effort类型的)和当前StatelessWriter匹配后触发的,由EDP机制完成。匹配后生成本地ReaderLocator添加到matched_readers中
a_locator := new ReaderLocator( locator, expectsInlineQos );
the_rtps_writer.reader_locator_add( a_locator );
完成匹配后Writer的状态变为announcing
状态迁移2:
当前Writer状态为idle,此时WriterHistory中如果存在未发送给ReaderLocator对应的远端Reader,则Writer进入pushing状态
状态迁移3:
当前Writer状态为pushing
状态迁移4:
状态迁移5:
状态迁移6:
状态迁移7:
状态迁移8:
状态迁移9:
状态迁移10:
状态迁移11:
状态迁移12: