Subscription模块数据访问


我们承担ROS,FastDDS等通信中间件,C++,cmake等技术的项目开发和专业指导和培训,有10年+相关工作经验,质量有保证,如有需要请私信联系。

本文主要解读V1.4版本DDS标准Subscription模块的内容。
类图:P58 TODO

数据访问

在DataReader对象上通过以下操作使用数据:read, read_w_condition, take, and take_w_condition。“read”操作的一般语义是应用程序只能访问相应的数据;中间件仍然负责管理和保存数据,并且可以再次读取。 “take”操作的语义是应用程序对数据负责;那些数据将不能再能通过DataReader访问。因此,DataReader可以多次通过read访问同一个样本。
这些操作每一个都返回一个有序的数据值和关联的SampleInfo对象的集合。每个数据值代表一个数据信息的原子(即,一个instance的值)。这个集合可能包含与相同或不同instances (由key识别)相关的样本。如果设置了相关HISTORY Qos(HISTORY QoS,2.2.3.18)允许的话,多个样本可以引用同一个instance。

SampleInfo

SampleInfo包含了与Data值相关的信息:

sample_state

对于每个接收到的样本,中间件内部会针对每个 DataReader 维护一个 sample_state。值有两个:READ 或者 NOT_READ。在由 read 或 take 方法返回的样本集合中,每个样本的 sample_state 通常是不同的。

  • READ:表示 DataReader 已经通过 read 方法访问过该样本
  • NOT_READ 表示 DataReader 之前还没有访问过该样本。

instance_state

对于每个实例,中间件内部维护一个 instance_stateinstance_state 可以是 ALIVENOT_ALIVE_DISPOSEDNOT_ALIVE_NO_WRITERS

  • ALIVE:表示该实例满足以下条件:
    • a. 已经接收到该实例的样本;
    • b. 有活跃的 DataWriter 实体正在写入该实例;
    • c. 该实例没有被显式地处理,或者处理后又收到了更多的样本
  • NOT_ALIVE_DISPOSED:表示该实例被 DataWriter 显式地处理了,即通过 dispose 操作进行了处理
  • NOT_ALIVE_NO_WRITERS:表示DataReader 检测到没有活跃的 DataWriter 实体在写入该实例,因此将其声明为非活跃状态。

导致 instance_state 变化的具体行为事件取决于 OWNERSHIP QoS 的设置:

  • 如果 OWNERSHIP 设置为 EXCLUSIVE,则只有“拥有”该实例的 DataWriter 显式地处置它时,instance_state 才会变为 NOT_ALIVE_DISPOSED。而 instance_state 只有在拥有该实例的 DataWriter 写入该实例时才会再次变为 ALIVE
  • 如果 OWNERSHIP 设置为 SHARED,则只要任何一个 DataWriter 显式地处置该实例,instance_state 就会变为 NOT_ALIVE_DISPOSED。而 instance_state 会在任何一个 DataWriter 再次写入该实例时变为 ALIVE

SampleInfo 中的 instance_state 是在收集数据时(即调用读取 readtake 方法时)实例状态的一个快照。因此,在返回的集合中,所有指向同一个实例的样本其 instance_state 是相同的。

valid_data

通常每个DataSample包含一个SampleInfo和数据Data。但是有些情况下,DataSample只包含SampleInfo,而没有关联的数据,这种情况发生在服务通知应用程序某个instance的状态变化是由某些内部机制(比如超时)引起的,而这种状态变化并没有关联的数据。比如,当服务检测到某个instance没有任何writers时,会将相应的instance_state更改为NOT_ALIVE_NO_WRITERS
中间件在哪些具体场景下返回不包含数据的DataSample是依赖于实现的。应用程序可以通过检查valid_data来区分一个特定的DataSample是否包含数据。如果为TRUE则包含有效的数据,反之则没有数据。
为了确保正确性和可移植性,在访问DataSample关联的数据之前,应用程序必须检查valid_data标志位;如果该标志位被设置为False,应用程序不应该访问与DataSample关联的数据,也就是说,只应该访问SampleInfo。

disposed_generation_count / no_writers_generation_count

对于每个instance,中间件内部维护了两个计数器:disposed_generation_countno_writers_generation_count。这两个计数器是相对于每个DataReader的:

  • 当DataReader第一次检测到一个从未见过的新的instance时,disposed_generation_countno_writers_generation_count 初始化为零。
  • 当instance对应的instance_state从NOT_ALIVE_DISPOSED变为ALIVE时,disposed_generation_count 计数器增加。
  • 当instance的instance_stateNOT_ALIVE_NO_WRITERS 变为ALIVEno_writers_generation_count 计数器增加

SampleInfo 中可用的 disposed_generation_countno_writers_generation_count 在样本接收时捕获了对应计数器的快照。

SampleInfo sample_rank, generation_rank, and absolute_generation_rank

在 SampleInfo 中提供的 sample_rankgeneration_rank 是基于由 readtake 返回的有序集合中的实际样本计算得出的:

  • sample_rank 表示在同一集合中与当前样本相同实例的后续样本的数量。
  • SampleInfo 中的 generation_rank 表示样本(S)与同一实例在返回集合中出现的最新样本(MRSIC)之间的“代数”差异。也就是说,它记录了从接收到样本 S 到接收到最新样本 MRSIC 这段时间内,该实例从非活跃状态转变为活跃状态的次数。generation_rank的计算公式:
generation_rank =
  (MRSIC.disposed_generation_count + MRSIC.no_writers_generation_count)
  - (S.disposed_generation_count + S.no_writers_generation_count)

在 SampleInfo 中提供的 absolute_generation_rank 表示样本(S)与中间件已接收的同一实例的最新样本(MRS)之间的“代数”差异。也就是说,它记录了从接收到样本 S 到调用 readtake 这段时间内,该实例从非活跃状态转变为活跃状态的次数。计算方式如下:

absolute_generation_rank =
  (MRS.disposed_generation_count + MRS.no_writers_generation_count)
  - (S.disposed_generation_count + S.no_writers_generation_count)

view_state

每个instance会维护一个view_state,值可以是NEW 或者 NOT_NEW

  • NEW :表示这是DataReader 第一次访问这个instance的samples,或者DataReader之前已访问过该实例的samples,但是该实例随后又经历了重生(从not-alive到alive)。这两种情况可以通过检查disposed_generation_countno_writers_generation_count 区分;
  • NOT_NEW :表示DataReader已经访问过同一instance的sample,并且之后这个instance一直没有重生

SampleInfo 中提供的 view_state 是在获取集合时(即调用 readtake 时)相对于用于访问样本的 DataReader 的实例 view_state 的快照。因此,在返回的集合中指向同一实例的所有样本的 view_state 是相同的。
一旦检测到某个instance没有任何live的Writers,并且与该实例相关的所有样本都已经被从 DataReader 中取走后,中间件就可以回收所有关于该实例的本地资源。未来的样本将会被当作从未见过来处理。

状态图:TODP P63

数据访问方式

应用程序通过 DataReader 上的 read 或 take 操作来访问数据。这些操作返回一个有序的 DataSamples 的集合,其中每个样本包含 SampleInfo 部分和 Data 部分。中间件构建这个集合的方式取决于设置在 DataReader 和 Subscriber 上的QoS策略,以及sample的源时间戳,还有传递给 read/take 操作的参数,具体包括:

所需的sample states(即 READNOT_READ,或两者)。
所需的view states(即 NEWNOT_NEW,或两者)。
所需的instance states(ALIVENOT_ALIVE_DISPOSEDNOT_ALIVE_NO_WRITERS,或这些状态的组合)。
readtake 操作是非阻塞的,它们只会交付当前可用并与指定状态匹配的数据。

read_w_conditiontake_w_condition操作接受一个ReadCondition 对象作为参数,而不是上述的read的参数如样本状态、视图状态和实例状态。其行为是返回的样本仅限于那些满足该条件为 TRUE 的样本。这些操作结合 ReadCondition 对象和 WaitSet,允许执行等待读取。

一旦数据样本对数据读取者可用,应用程序可以选择读取或获取这些数据。基本规则是应用程序可以按任意顺序进行此操作。这种方法非常灵活,给予应用程序最大的控制权。然而,如果应用程序需要按照接收到的正确顺序检索样本,或者希望访问一组完整的连贯变更,则必须使用特定的访问模式。

为了连贯地或按顺序访问数据,必须正确设置 PRESENTATION QoS(在 2.2.3.6 节中解释),并且应用程序必须遵循下面描述的访问模式。否则,应用程序仍然可以访问数据,但不一定能看到所有连贯的更改一起出现,也无法按正确的顺序看到更改。
有一种通用模式可以在多个 DataReaders 上提供有序和连贯的访问。这种模式适用于任何 PRESENTATION QoS 设置。对于特定的 QoS 设置,更简单的模式也可能适用,如下所述:

  1. 跨DataReader实体以连贯集或按顺序访问samples的通用模式:这种情况适用PRESENTATION QoS指定“access_scope=GROUP
  • 在通知SubscriberListener或启用StatusCondition 后,应用程序使用Subscriber 的begin_access 表明可以通过Subscriber访问data;
  • 然后调用Subscriber 的get_datareaders 方法获取数据样本可用的DataReader的对象列表;
  • 接下来,它按照返回的相同顺序在每个DataReader上调用readtake方法来访问DataReader中的所有changes
  • 一旦在所有DataReader上调用了take或read方法,就调用end_access

注意,如果PRESENTATION QoS指定ordered_access=TRUE,DataReaders 的列表可能会多次返回相同的reader。通过这种方式可以在不同DataReader对象样本之间保持正确的样本顺序。
2. 如果跨DataReader实体不需要保持任何顺序或连贯性,则采用专门的模式。这种情况适用于 PRESENTATION QoS 策略指定 access_scope 为除 GROUP 之外的其他值时。

  • 这种情况下,应用程序无需调用begin_accessend_access。但这样做并没有错,只是没有效果。
  • 应用程序可以通过按任意顺序在每个DataReader上调用readtake方法来访问数据
  • 应用程序仍然可以调用get_datareaders 来确定哪些readers有数据可读,但他不需要读取所有DataReaders,也不需要按特定顺序读取,此外,get_datareaders 的返回值逻辑上是一个集合,这意味着相同的readers不会出现两次,并且返回的readers顺序未作规定。
  1. 如果应用程序在 SubscriberListener 中访问数据,则采用专门的模式。这种情况不管PRESENTATION QoS如何,都适用于在listener 的on_data_on_readers 内部访问数据
  • 类似于上述情况2,应用程序不需要调用begin_accessend_access,即使调用了也不会产生影响
  • 应用程序可以按自己期望的顺序通过每个DataReader的readtake方法访问数据
  • 应用程序还可以通过调用 notify_datareaders 方法将数据的访问委托给每个 DataReader 的 DataReaderListener 对象。
  • 同样地,应用程序仍然可以调用 get_datareaders 来确定哪些 DataReader 有可读的数据,但它不需要读取所有的 DataReader,也不需要按照特定的顺序读取。此外,get_datareaders 的返回结果逻辑上是一个集合。
  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值