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

RTPS协议之Behavior Module

目录

  • 交互要求
    • 基本要求
    • RTPS Writer 行为
    • RTPS Reader行为
  • RTPS协议的实现
  • 与Reader匹配的Writer的行为
  • 涉及到的类型
  • RTPS Writer实现
    • RTPS Writer
    • RTPS StatelessWriter
    • RTPS ReaderLocator
    • RTPS StatefulWriter
    • RTPS ReaderProxy
    • RTPS ChangeForReader
  • RTPS StatelessWriter Behavior
    • Best-Effort StatelessWriter Behavior
      • Reliable StatelessWriter Behavior
  • RTPS StatefulWriter Behavior
    • Best-Effort StatefulWriter
    • Reliable StatefulWriter
  • Writer Liveliness Protocol
    • built-in Endpoints
      • Qos
    • Data Types
    • 使用BuiltinParticipantMessageWriter和BuiltinParticipantMessageReader实现Writer Liveliness Protocol
  • Optional Behavior
    • Large Data
      • 如何选择fragment的size
      • 如何发送片段
      • 如何重组分片
      • Reliable通信

主要描述rtps实体的动态行为,主要记录rtps writer和rtps reader之间的有效的序列消息交换,和这些消息交换的时间限制。RTPS Writer和RTPS Reader的时序交互如下:
RTPS Endpoints交互
说明:

  1. DDS DataWriter使用writer发送数据
  2. DataWriter调用RTPS Writer的new_change创建一个新的CacheChange,每个CacheChange使用SequenceNumber唯一标识;
  3. new_change返回;
  4. DDS的DataWriter操作使用add_change操作将CacheChange添加到RTPS的Writer的HistoryCache中;
  5. add_change操作返回
  6. writer操作返回,用户完成写数据
  7. RTPS writer使用Data Submessage发送CacheChange的内容给RTPS reader,并通过同时发送一个Heartbeat的Submessage的ack请求;
  8. RTPS reader接收到Data消息,通过add_change操作将CacheChange添加到RTPS reader的HistoryCache中;
  9. add_change返回,此时CacheChange对于DDS DataReader和DDS user是有效且可见的,这依赖于RTPS reader的reliabilityLevel属性;
    a. 对于RELIABLE 的DDS DataReader,只有当之前的RTPS Reader’s HistoryCache的changes可见时,当前的changes可见;
    b. 对于BEST_EFFORT DDS DataReader,只有没有后面的changes是可见的时候即可(i.e., if there are no changes in the RTPS Receiver’s HistoryCache with a higher sequence number)
  10. DDS user收到通知(通过listener或者WaitSet)并通过DataReader的take函数读取数据
  11. DDS 的DataReader通过HistoryCache的get_change函数读取change;
  12. get_change操作返回CacheChange给DataReader;
  13. take操作返回数据给DDS user;
  14. RTPS Reader发送AckNack消息表示CacheChange已存放到Reader的HistoryCache中,AckNack消息中包含RTPS reader的GUID,和change的SequenceNumber。这些操作和通知DDS user或take操作是独立的(即并行的);
  15. StatefulWriter记录RTPS Reader已经收到CacheChange并使用acked_changes_set操作将其加入到由ReaderProxy维护的acked_changes的set中。
  16. DDS user在DataReader上调用return_loan操作表明不再使用前面通过take操作获取到的数据;
  17. DataReader使用remove_change操作从HistoryCache中删除数据;
  18. remove_change操作返回;
  19. return_loan操作返回;
  20. DataWriter通过is_acked_by_all查看和该StatefulWriter匹配的哪些RTPS reader的CacheChanges接收到了;
  21. DataWriter通过remove_change从RTPS Writer的HistoryCache中删除指定seq_num的change;这一步操作也需要考虑到DDS Qos如DURABILITY
  22. remove_change返回

交互要求

基本要求

  • 所有的通信必须使用RTPS Messages
  • 必须实现RTPS Message Receiver

RTPS Writer 行为

  • writers发送数据不能out-of-order,即必须要按添加到HistoryCache中的数据发送
  • 如果reader要求,Writers必须包含 in-line Qos
  • Writers只有在reliable下必须周期性发送HEARTBEAT Messages。
    • Writer必须通过周期性的发送HEARTBEAT message通知每个匹配的reliable readers存在有效的data sample,这个HEARTBEAT message中包含有效sample的Sequence number;如果没有有效的samples,就没有HEARTBEAT Message需要发送;
    • 对于严格使用reliable通信的情况,Writer必须持续性给Readers发送HEARTBEAT Messages,直到Reader要么确认收到了所有有效的samples,要么消失
    • 其他情况下,HEARTBEAT消息的发送可以是特定实现的,也可以是有限的
  • 在reliable模式下(且只在该模式下),Writer必须对negative acknowledgment作出回应。当收到ACKNACK Message,表明Reader丢失了一些data sample,Writer必须要么发送丢失的data sample,发送一个GAP message当sample是无关的时候;要么当sample不再有效时发送一个HEARTBEAT message;Writer可以立即回应,或者选择未来的某个时间点发送;可以合并多个相关的response一起发送;
  • 属于一个Group的Writer会发送HEARTBEAT or GAP Submessages给和他匹配的Readers,即使是Reader接收到了Writer的所有的samples。This is necessary for the Subscriber to detect the
    group sequence numbers that are not available in that Writer. The exception to this rule is when the Writer has
    sent DATA or DATA_FRAG Submessages that contain the same information.

RTPS Reader行为

best-effort Reader只接收数据,不会发送任何数据;所以下面描述的要求仅限于reliable Reader。

  • Readers在收到一个没有设置最终flag的HEARTBEAT消息时必须作出响应。一旦收到一个未设置最终flag的HEARTBEAT消息时,Reader必须回应一个ACKNACK Message。这个ACKNACK Message消息可以用于回应收到了所有的data sample,或者一些data sample丢失了。响应可能会延迟,以避免消息风暴(在RTPS协议中,当一个节点收到一个HEARTBEAT消息时,它并不一定立即作出响应。相反,它可能会延迟一段时间后才做出响应,以防止在短时间内收到大量的HEARTBEAT消息,导致网络拥塞或过载。通过延迟响应,系统可以更有效地管理通信流量,确保通信的稳定性和可靠性)。
  • Readers在收到一个HEARTBEAT消息后发现data sample丢失了的时候必须响应一个ACKNACK Message。这一要求只有在Reader的缓存可以容纳这些丢失的data sample时才适用,并且与HEARTBEAT消息中的最终标志的设置无关。换句话说,在RTPS协议中,如果接收者的缓存能够处理缺失的数据样本,并且即使在HEARTBEAT消息中未设置最终标志,接收者仍然需要按照先前提到的规则做出响应。这强调了接收者在处理数据丢失时的一致性和可靠性。
    • 这个消息可以延迟,以避免消息风暴
    • 当一个liveness HEARTBEAT同时设置了liveness标志和final标志时,表明这是一个仅包含liveness信息的消息,此时不需要进行响应。换句话说,在RTPS协议中,如果一个HEARTBEAT消息被设置了liveness标志和flag标志,那么接收者不需要做出响应,因为这个消息只是用来表明发送者的存活状态,而不需要接收者采取任何措施。
  • 一旦一个Reader使用ACKNACK消息积极地确认接收了一个数据样本,那么它就不能在之后的时间点再次使用负面确认(NAK)消息来否定地确认相同的数据样本。
    • 当一个Writer从所有Readers那里都收到了积极的确认消息(positive acknowledgement),表示所有Readers都成功接收了相应的数据样本后,Writer可以收回与该数据样本相关的任何资源。这意味着Readers可以释放内存或其他资源,因为数据已经成功传输给了所有的Readers。
    • 如果一个Writer之前收到了积极的确认消息,表示某个数据样本已经成功接收,然后在之后又收到了一个负面的确认消息(negative acknowledgement),表示该样本未成功接收,但Readers仍能够处理这个请求,那么Readers应该重新发送这个数据样本。这样做是为了确保数据的完整性和可靠性,即使在之前已经收到积极确认的情况下,如果有必要,仍然应该重新发送未成功接收的数据样本。——?TODO 是否矛盾?
  • Readers只能发送ACKNACK message用于回应HEARTBEAT Message。在稳定状态下,ACKNACK消息只能作为对来自Writer的HEARTBEAT消息的响应而发送。当Reader首次发现写入者时,可以发送ACKNACK消息作为一种优化。Reader不需要对这些预先发送的ACKNACK消息做出响应。

RTPS协议的实现

  • Stateless实现:被优化用于可伸缩性。它在远端entities上几乎不保存任何状态,因此在大型系统中具有非常好的可伸缩性。这涉及到一种权衡,因为改进的可伸缩性和减少的内存使用可能需要额外的带宽使用。Stateless的实现非常适合通过组播进行best-effort通信。
  • Stateful实现:在远端entities上维护完整状态。这种方法最小化了带宽使用,但需要更多的内存,并可能导致可伸缩性降低。与Stateless实现相比,它可以保证严格可靠的通信,并能够在写入者端应用基于QoS或基于内容的过滤。
  • 实际的实现不需要完全遵守上面的规则,可以根据需要结合以上特点。Stateless Reference Implementation在远程实体上保持了最少的信息和状态。因此,它无法在写入端执行基于时间的过滤,因为这需要跟踪每个远程读取器及其属性。它也无法在读取器端丢弃无序样本,因为这需要跟踪从每个远程写入器接收到的最大序列号。一些实现可能模仿 Stateless Reference Implementation,但选择存储足够的额外状态以避免上述一些限制。所需的额外信息可以永久性地存储,这种情况下,实现接近于状态参考实现,或者可以慢慢老化并在需要时保留,以尽可能地模拟保持状态时的行为。

与Reader匹配的Writer的行为

与Reader匹配的RTPS Writer的行为,取决于RTPS Writer和RTPS Reader的reliabilityLevel属性(reliable和best-effort)的设置。
Writer和Reader允许的匹配:

  1. RTPS Writer的reliabilityLevel属性设置为RELIABLE
  2. RTPS Writer和RTPS Reader的reliabilityLevel属性都设置为BEST_EFFORT

这是因为DDS标准要求,BEST_EFFORT DDS DataWriter只能BEST_EFFORT DDS DataReader,RELIABLE DDS DataWriter可以匹配RELIABLE 和 BEST_EFFORT DDS DataReader
以上说明只限定于匹配,对于Stateful和Stateless来说,它们是可以通信的。

涉及到的类型

Attribute typePurpose
Duration_t时间
ChangeForReaderStatusKindChangeForReader.的状态,有以下值:UNSENT, UNACKNOWLEDGED, REQUESTED, ACKNOWLEDGED, UNDERWAY
ChangeFromWriterStatusKindChangeFromWriter.的状态值,有以下:LOST, MISSING, RECEIVED, UNKNOWN
InstanceHandle_t
ParticipantMessageData

RTPS Writer实现

RTPS Writer和RTPS Reader是对RTPS Endpoint的特化实现,StatelessWriter和StatefulWriter是RTPS Reader的特化,它们的区别在于处理匹配的Reader端点的信息时,它们的方法有所不同
TODO P70

RTPS Writer

RTPS Writer属性

attributetypemeaningrelation to DDS
pushModebool
heartbeatPeriodDuration_t
nackResponseDelayDuration_t
nackSuppression DurationDuration_t
lastChangeSequenceNumberSequenceNumber_t
writer_cacheHistoryCache
dataMaxSize Serialized
  1. 默认的时间相关的值
  2. new:new一个RTPS Writer
  3. new_change:new一个CacheChange并添加到RTPS Writer的HistoryCache,sequenceNumber相对上次自动+1;
++this.lastChangeSequenceNumber;
a_change := new CacheChange(kind, this.guid, this.lastChangeSequenceNumber,data, inlineQos, handle);
RETURN a_change;

RTPS StatelessWriter

StatelessWriter不需要知道匹配的readers的数量,也不需要维护匹配的Reader的任何状态;只维护RTPS ReaderLocator,用于向已经匹配的readers发送信息
StatelessWriter属性:

attributetypemeaningrelation to DDS
reader_locatorsReaderLocator[*]StatelessWriter维护一个locators列表,用于发送CacheChanges.N/A

StatelessWriter用于以下场景:

  • writer的HistoryCache交小
  • 通信方式是best-effort
  • writer通过multicast和很多readers通信

StatelessWriter的方法:

  1. new:创建一个StatelessWriter,并进行初始化:this.readerlocators := <empty>;
  2. reader_locator_add:将ReaderLocator对象添加到StatelessWriter::reader_locators中,伪代码:ADD a_locator TO {this.reader_locators};
  3. reader_locator_remove:将ReaderLocator对象从StatelessWriter::reader_locators中移除,伪代码:REMOVE a_locator FROM {this.reader_locators};
  4. unsent_changes_reset:This operation modifies the set of ‘unsent_changes’ for all the ReaderLocators in the StatelessWriter::reader_locators. The list of unsent changes is reset to match the complete list of changes available in the writer’s HistoryCache. This operation is useful when called periodically to cause the StatelessWriter to keep re-sending all available changes in its HistoryCache.伪代码:FOREACH readerLocator in {this.reader_locators} DO readerLocator.unsent_changes := {this.writer_cache.changes}

RTPS ReaderLocator

用于跟踪所有匹配的Readers的locators,用于跟踪和管理与特定Writer匹配的所有远程Reader的位置信息。这使得Writer能够有效地定位和发送数据到正确的Reader。具体来说,ReaderLocator的主要职责是存储和管理远程Reader的网络地址(通常是IP地址和端口号)
RTPS ReaderLocator属性:

attributetypemeaningrelation to DDS
requested_changesCacheChange[*]A list of changes in the writer’s HistoryCache that were requested by remote Readers at this ReaderLocator.
unsent_changesCacheChange[*]A list of changes in the writer’s HistoryCache that have not been sent yet to this ReaderLocator.
locatorLocator_tUnicast or multicast locator through which the readers represented by this ReaderLocator can be reached.
expectsInlineQosboolSpecifies whether the readers represented by this ReaderLocator expect inline QoS to be sent with every Data Message.

RTPS ReaderLocator operations:

  1. new
  2. next_requested_change
  3. next_unsent_change
  4. requested_changes
  5. requested_changes_set
  6. unsent_changes

RTPS StatefulWriter

RTPS StatefulWriter用于配置所有匹配的RTPS Reader,维护每个RTPS Reader的状态,这包括每个匹配的Reader的状态,包括序列号,确认的变化,缓冲的更改,过去的交互等等。这种状态使得StatefulWriter能够提供丰富的增强功能,如确保可靠的信息交付,处理不同的发送速率和网络故障,以及跟踪未确认的更改。
通过维护每个匹配的RTPS Reader端点的状态,RTPS StatefulWriter可以确定所有匹配的RTPS Reader端点是否已收到特定的CacheChange,并且可以通过避免向已接收到Writer的HistoryCache中所有changes的reader发送announcements,从而最优化地使用网络带宽。它维护的信息也简化了写入端的基于QoS的过滤。
RTPS StatefulWriter Attributes

attributetypemeaningrelation to DDS
matched_readersReaderProxy[*]StatefulWriter使用该字段跟踪与其匹配的所有RTPS readers。每个匹配的reader由ReaderProxy类的一个实例表示。

StatefulWriter Operations

  • new:new一个StatefulWriter,并将matched_readers初始化为空:this.matched_readers := <empty>;
  • is_acked_by_all:
  • matched_reader_add
  • matched_reader_remove
  • matched_reader_lookup

RTPS ReaderProxy

用于维护和StatefulWriter匹配的每一个Reader的信息;
RTPS ReaderProxy Attributes:

attributetypemeaningrelation to DDS
remoteReaderGuidGUID_t远端匹配的RTPS Reader的guid
remoteGroupEntityIdEntityId_tgroup中匹配的reader的EntityIdDataReader所属的Subscriber的EntityId
unicastLocatorListLocator_t[*]用于向匹配的RTPS reader发送消息的unicast locators (transport, address, port combinations),该列表可能是空的
multicastLocatorListLocator_t[*]用于向匹配的RTPS reader发送的multicast locators (transport, address, port combinations),可能为空
changes_for_readerCacheChange[*]和RTPS Reader相关的CacheChange的列表——什么作用?
expectsInlineQosboolSpecifies whether the remote matched RTPS Reader expects in-line QoS to be sent along with any data.
isActivebool远端Reader是否需要向writer回应

StatefulWriter将把Writer的HistoryCache中的CacheChange changes发送到由ReaderProxy表示的匹配的RTPS reader中。

ReaderProxy Operations

  • new:创建一个ReaderProxy对象
  • acked_changes_set
  • next_requested_change
  • next_unsent_change
  • requested_changes
  • requested_changes_set
  • unsent_changes
  • unacked_changes

RTPS ChangeForReader

RTPS的ChangeForReader是一个关联类,它保存了RTPS Writer的HistoryCache中的CacheChange信息,这些信息与ReaderProxy所代表的RTPS Reader有关。换句话说,这个类是用来记录和管理RTPS Writer中的缓存改变(CacheChange)对于特定的RTPS Reader(由ReaderProxy代表)的相关信息。

RTPS StatelessWriter Behavior

Best-Effort StatelessWriter Behavior

Best-Effort StatelessWriter和每个ReaderLocator的交互

Transitionstateeventnext state
T1initial给RTPS Best-Effort StatelessWriter配置RTPS ReaderLocatoridle
T2idleGuardCondition: RL::unsent_changes() != <empty>pushing
T3pushingGuardCondition: RL::unsent_changes() == <empty>idle
T4pushingGuardCondition: RL::can_send() == truepushing
T5any stateRTPS Writer is configured to no longer have the ReaderLocatorfinal
  1. T1:这一步是服务发现阶段完成,给RTPS Best-Effort StatelessWriter配置DataReader到RTPS ReaderLocator。
a_locator := new ReaderLocator( locator, expectsInlineQos ); 
the_rtps_writer.reader_locator_add( a_locator );
  1. 表示RTPS Writer的HistoryCache中有一些changes还没有被发送给RTPS ReaderLocator
  2. [RL::unsent_changes() == <empty>] 表明所有RTPS Writer的HistoryCache中的changes被发送到RTPS ReaderLocator中。但这并不意味着这些changes被接收到。
  3. [RL::can_send() == true]表示RTPS Writer有一些resources需要发送change到RTPS ReaderLocator 对象中,伪代码如下:
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;
}
  1. RTPS Writer不再发送消息给RTPS ReaderLocator。这一步也是由服务发现完成。
the_rtps_writer.reader_locator_remove(the_reader_locator);
delete the_reader_locator;

Reliable StatelessWriter Behavior

Reliable StatelessWriter with respect to each ReaderLocator

Transitionstateeventnext state
T1initialRTPS Writer is configured with a ReaderLocatorannouncing
T2announcingGuardCondition: RL::unsent_changes() != <empty>pushing
T3pushingGuardCondition: RL::unsent_changes() == <empty>announcing
T4pushingGuardCondition: RL::can_send() == truepushing
T5announcingafter(W::heartbeatPeriod)announcing
T6waitingACKNACK message is receivedwaiting
T7waitingGuardCondition: RL::requested_changes() != must_repair
T8must_repairACKNACK message is receivedmust_repair
T9must_repairafter(W::nackResponseDelay)repairing
T10repairingGuardCondition: RL::can_send() == truerepairing
T11repairingGuardCondition: RL::requested_changes() == waiting
T12any stateRTPS Writer is configured to no longer have the ReaderLocatorfinal
  1. 通过服务发现,配置Reliable StatelessWriter的ReaderLocator,将所有匹配的DataReader添加到ReaderLocator.
a_locator := new ReaderLocator( locator, expectsInlineQos ); 
the_rtps_writer.reader_locator_add( a_locator );
  1. [RL::unsent_changes() != ] 表明,RTPS Writer HistoryCache中还有changes没有发送给ReaderLocator;
  2. [RL::unsent_changes == ] 表明,所有RTPS Writer HistoryCache中的消息都已发送到ReaderLocator,但这并不保证这些changes已经被接收;
  3. [RL::can_send() == true]表明RTPS Writer有一些resources需要发送给ReaderLocator对象。执行流程如下:
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;

After the transition the following post-conditions hold:

( a_change BELONGS-TO the_reader_locator.unsent_changes() ) == FALSE
  1. 这个状态转变由W::heartbeatPeriod.的timer触发,执行逻辑如下:
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;
  1. 这个状态转换是由于RTPS StatelessWriter接收到一个由某个RTPS Reader发送的ACKNACK message而触发的,执行以下逻辑:
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,以便正确设置这些字段。

  1. 这个状态转换由guard condition[RL::requested_changes() != <empty>]触发,表明RTPS ReaderLocator中的有一些RTPS Reader的request
  2. 这个状态转换是由于RTPS StatelessWriter收到由RTPS Reader发送的ACKNACK消息。
  3. 这个状态转换是由一个定时器触发的,表示自从进入must_repair状态以来,W::nackResponseDelay的持续时间已经过去。不执行任何逻辑操作。
  4. todo
  5. todo
  6. todo

RTPS StatefulWriter Behavior

Best-Effort StatefulWriter

Best-Effort StatefulWriter和与其匹配的Reader

Transitionstateeventnext state
T1initialRTPS Writer匹配到了RTPS Readeridle
T2idleGuardCondition: RP::unsent_changes() != <empty>pushing
T3pushingGuardCondition: RP::unsent_changes() == <empty>idle
T4pushingGuardCondition: RP::can_send() == truepushing
T5ready一个新的change被添加到RTPS Writer’s HistoryCache中ready
T6any stateRTPS Writer不再有匹配的RTPS Readerfinal
  1. 这个状态的转变是由RTPS Writer配置到了匹配的RTPS Reader。配置动作是由服务发现完成DataReader匹配到了DataWriter。服务发现提供了初始化一个ReaderProxy对象的参数;执行如下操作:
a_reader_proxy := new ReaderProxy( remoteReaderGuid,remoteGroupEntityId, expectsInlineQos, unicastLocatorList, multicastLocatorList);
the_rtps_writer.matched_reader_add(a_reader_proxy);
  1. 这个状态转变由[RP::unsent_changes() != <empty>]触发,表明RTPS HistoryCache中有一些changes没有发送给代表RTPS Reader的ReaderProxy
  2. 这个状态转变由[RP::unsent_changes() == <empty>]触发,表示RTPS Writer HistoryCache中所有changes都被发送给了代表RTPS Reader的ReaderProxy。但这并不意味着changes被接收到,
  3. 这个状态转变由[RP::can_send() == true]触发,表示RTPS Writer有一些resources需要发送给代表RTPS Reader的ReaderProxy。执行以下操作:
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 Submessage以分离的形式发送,相反多个子消息可以组合成一个RTPS message。

  1. 这个转变是由于添加了一个新的CacheChange到HistoryCache中。这个change是否和代表RTPS Reader的ReaderProxy有关,是由DDS_FILTER决定。执行逻辑如下:
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;
  1. 这个状态转变是由于RTPS Writer不再和ReaderProxy代表的RTPS Reader匹配。执行如下操作:
the_rtps_writer.matched_reader_remove(the_reader_proxy);
delete the_reader_proxy;

Reliable StatefulWriter

Reliable StatefulWriter与其匹配的Reader

Transitionstateeventnext state
T1initialRTPS Writer匹配到了RTPS Readerannouncing
T2announcingGuardCondition: RP::unsent_changes() != <empty>pushing
T3pushingGuardCondition: RP::unsent_changes() == <empty>announcing
T4pushingGuardCondition: RP::can_send() == truepushing
T5announcingGuardCondition: RP::unacked_changes() == <empty>idle
T6idleGuardCondition: RP::unacked_changes() != <empty>announcing
T7announcingafter(W::heartbeatPeriod)announcing
T8waitingACKNACK消息被接收waiting
T9waitingGuardCondition: RP::requested_changes() != <empty>must_repair
T10must_repairACKNACK message被接收must_repair
T11must_repairafter(W::nackResponseDelay)repairing
T12repairingGuardCondition: RP::can_send() == truerepairing
T13repairingGuardCondition: RP::requested_changes() == <empty>waiting
T14ready一个change添加到RTPS Writer’s HistoryCacheready
T15ready一个change从RTPS Writer’s HistoryCache中移除ready
T16any stateRTPS Writer不再和RTPS Reader匹配final

Writer Liveliness Protocol

DDS标准要求存在一个活跃性机制,RTPS用来实现这个要求的就是Writer Liveliness Protocol。这个协议定义了两个participant之间,为了确认它们所包含的Writer的活跃性所需要的信息交换。在这里,Writer通常指的是在数据传输过程中,负责产生和发送数据的实体或组件。
Writer Liveliness Protocol使用预先定义的内置的Endpoints。使用内置端点意味着一旦一个参与者知道了另一个参与者的存在,它可以假定远程参与者提供的内置端点的存在,并与本地匹配的内置端点建立关联。——?用于内置端点之间通信的协议与用于应用定义端点的协议相同。

built-in Endpoints

Writer Liveliness Protocol要求的built-in Endpoints是BuiltinParticipantMessageWriter和BuiltinParticipantMessageReader.这些Endpoints被用于liveliness,但是在以后也可以被用于其他数据;
RTPS 协议为这些built-in Endpoints预留的EntityId_t:

  • ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER
  • ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_READER

Qos

为了交互,BuiltinParticipantMessageWriter 和 BuiltinParticipantMessageReader使用如下Qos:

  • durability.kind = TRANSIENT_LOCAL_DURABILITY
  • history.kind = KEEP_LAST_HISTORY_QOS
  • history.depth = 1

BuiltinParticipantMessageWriter shall use reliability.kind = RELIABLE_RELIABILITY_QOS,BuiltinParticipantMessageReader既可以使用RELIABLE_RELIABILITY_QOS 也可以使用 BEST_EFFORT_RELIABILITY_QOS. 如果BuiltinParticipantMessageReader使用BEST_EFFORT_RELIABILITY_QOS,那么ParticipantProxy::builtinEndpointQos中就会设置BEST_EFFORT_PARTICIPANT_MESSAGE_DATA_READER flag.

If the ParticipantProxy::builtinEndpointQos is included in the SPDPdiscoveredParticipantData, then the BuiltinParticipantMessageWriter shall treat the BuiltinParticipantMessageReader as indicated by the flags. If the ParticipantProxy::builtinEndpointQos is not included then the BuiltinParticipantMessageWriter shall treat the BuiltinParticipantMessageReader as if it is configured with RELIABLE_RELIABILITY_QOS.

Data Types

同其他RTPS Endpoint一样,RTPS built-in Endpoints也有HistoryCache存储changes。
RTPS built-in Endpoint中DCPSParticipantMessage Topic使用的Data type:
Participant Message Data

使用BuiltinParticipantMessageWriter和BuiltinParticipantMessageReader实现Writer Liveliness Protocol

通过向BuiltinParticipantMessageWriter写入一个sample,就可以判断一个participant的一部分writer的liveliness。如果Participant包含一个或多个liveliness为AUTOMATIC_LIVELINESS_QOS的Writers,

Optional Behavior

Large Data

传输可能会限制最大数据包大小(例如,UDP的最大值为64K),从而限制最大的RTPS子消息大小。这主要影响数据子消息,因为它限制了serializedData的最大大小,或者使用的数据类型的最大序列化大小。为了解决这个限制,以下子消息实现了对大数据的分段:

  • DataFrag
  • HeartbeatFrag
  • NackFrag

下面列出了交互所需的相应行为

如何选择fragment的size

fragment size的大小由Writer以及下面条件:

  • 所有Writer有效的传输方式必须至少能容纳包含一个片段的DataFrag子消息,这意味着具有最小最大消息大小的传输方式决定了片段的大小。
  • 对于特定的编写者,片段大小必须是固定的,并且对于所有远程readers都是相同的。通过固定片段大小,片段编号所指的数据不依赖于特定的远程reader。这简化了来自reader的NackFrag的处理。
  • 片段大小必须 <= 65536字节

注意,片段大小由writer可用的所有传输方式确定,而不仅仅是到达所有当前已知reader所需的传输方式的子集(什么意思?)。这确保了新发现的readers,无论他们可以通过哪种传输方式到达,都可以在不改变片段大小的情况下进行适应,否则将违反上述要求。

如何发送片段

如果分段要求将Data Submessage分成若干DataFrag Submessages,需要满足以下要求:

  • DataFrag Submessages需要按顺序发送,序号是fragment numbers递增的,但注意这并不保证按顺序达到
  • Data只能在被要求的时候分段。如果多种传输方式对Writer有效,其中一些传输方式不要求分段,那么就应该在这些传输方式上发送Data Submessage。同样,对于可变大小的数据类型,如果对于特定的序列号不需要进行分段,那么就应该使用常规的Data Submessage(后一句啥意思)
  • 对于给定的序列号,如果使用了内联的QoS(服务质量)参数,那么必须将这些参数与第一个DataFrag子消息(包含碎片编号等于1的碎片)一起包含。对于该序列号的后续DataFrag子消息,也可以包含这些参数,但这并不是必须的。这段说明主要指出了在使用QoS参数时的处理方式和要求。

如何重组分片

DataFrag Submessages包含了所有需要重组data的信息,一旦所有片段都收到,就可以作为常规Data Submessage处理。实现上也必须要能够处理乱序到达的DataFrag submessages。

Reliable通信

在可靠地发送DataFrag Submessage的协议行为上,除了要满足发送常规Data Submessage的要求外,还需满足以下条件:

  • 心跳子消息的语义保持不变:心跳消息只能包括所有片段都可用的序列号。也就是说,心跳消息的发送应该仅限于那些所有数据片段都已经准备就绪,可以进行发送的序列号,不能包含那些尚未准备就绪的序列号。这就确保了数据传输的完整性和可靠性。
  • AckNack子消息的语义保持不变:只有在接收到特定序列号的所有片段时,AckNack消息才能对该序列号进行肯定确认。同样,只有在所有片段都丢失时,才能对序列号进行否定确认。这就意味着,AckNack消息只有在数据完全收到或完全丢失的情况下才能进行确认,确保了数据传输的准确性和可靠性。
  • 为了对某一给定序列号的片段子集进行否定确认,必须使用NackFrag Submessage。当数据被分片时,心跳可能会触发AckNack和NackFrag Submessage。

另外,

  • As mentioned above, a Heartbeat Submessage can only include a sequence number once all fragments for that sequence number are available. If a Writer wants to inform a Reader on the partial availability of fragments for a given sequence number, a HeartbeatFrag Submessage can be used instead. Fragment level reliability may be helpful for very large data and when using flow control.
  • NackFrag Submessage消息只能用来回应Heartbeat或HeartbeatFrag submessage.

相关文章:

RTPS协议之Behavior Module

目录 交互要求基本要求RTPS Writer 行为RTPS Reader行为 RTPS协议的实现与Reader匹配的Writer的行为涉及到的类型RTPS Writer实现RTPS WriterRTPS StatelessWriterRTPS ReaderLocatorRTPS StatefulWriterRTPS ReaderProxyRTPS ChangeForReader RTPS StatelessWriter BehaviorBe…...

Socket网络通讯入门(一)

提示&#xff1a;能力有限&#xff0c;不足以及错误之处还请指出&#xff01; 文章目录 前言一、 计算机网络 OSI、TCP/IP、五层协议 体系结构1.OSI七层模型每层的作用2.TCP/IP协议分成3.五层协议体系结构 二、Socket服务端和客户端 简单通信1.服务端代码2.客户端 总结 前言 简…...

第十五课,海龟画图:抬笔与落笔函数、画曲线函数

一&#xff0c;turtle.penup()和turtle.pendown()&#xff1a;抬起与落下画笔函数 当使用上节课学习的这个turtle.forward()&#xff1a;画笔前进函数时&#xff0c;画笔会朝着当前方向在画布上留下一条指定&#xff08;像素&#xff09;长度的直线&#xff0c;但你可能发现&a…...

【机器学习】让大模型变得更聪明

文章目录 前言1. 理解大模型的局限性1.1 理解力的挑战1.2 泛化能力的挑战1.3 适应性的挑战 2. 算法创新&#xff1a;提高模型学习和推理能力2.1 自监督学习2.2 强化学习2.3 联邦学习 3. 数据质量与多样性&#xff1a;增强模型的泛化能力3.1 高质量数据的获取3.2 数据多样性的重…...

5.26机器人基础-DH参数 正解

1.建立DH坐标系 1.确定Zi轴&#xff08;关节轴&#xff09; 2.确定基础坐标系 3.确定Xi方向&#xff08;垂直于zi和zi1的平面&#xff09; 4.完全确定各个坐标系 例子&#xff1a; 坐标系的布局是由个人决定的&#xff0c;可以有不同的选择 标准坐标系布局&#xff1a; …...

Vue3项目练习详细步骤(第五部分:用户模块的功能)

顶部导航栏个人信息显示 接口文档 接口请求与绑定 导航栏下拉菜单功能 路由实现 退出登录和路由跳转实现 基本资料修改 页面结构 接口文档 接口请求与绑定 修改头像 页面结构 头像回显 头像上传 接口文档 重置密码 页面结构 接口文档 接口请求与绑定 顶部导航…...

测试onlyoffice在线预览文件功能

HTML示例代码 <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><title>测试onlyoffice在线预览文件功能</title><script type"text/javascript" src"http://onlyoffice服务器ip:端口/…...

Day57 每日温度 + 下一个更大元素Ⅰ

739 每日温度 题目链接&#xff1a;739.每日温度 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0c;…...

nuxt3 api如何透传(不引第3方库)

背景: nuxt做为一个vue的服务端渲染框架,本身就具备服务端的功能,理论上可以完整做一个系统功能,包括对数据库等等操作,但更合理的做法是nuxt应该定位只做服务端渲染的事情,更偏向ui层面,而非数据curd,业务逻辑,权限等等偏向服务端的逻辑。本身基于vue的服务端渲染已…...

list常用接口模拟实现

文章目录 一、模拟list类的框架二、函数接口实现1、迭代器接口2、常用删除、插入接口3、常用其他的一些函数接口4、默认成员函数 一、模拟list类的框架 1、使用带哨兵的双向链表实现。 2、链表结点&#xff1a; // List的结点类 template<class T> struct ListNode {Li…...

前端工程化工具系列(三) —— Stylelint(v16.6.1):CSS/SCSS 代码质量工具

Stylelint 是 CSS/SCSS 代码的静态分析工具&#xff0c;用于检查代码中的错误和样式违规。 1. 环境要求 v16 以上的 Stylelint&#xff0c;支持 Node.js 的版本为 v18.12.0。 在命令行中输入以下内容来查看当前系统中 node 的版本。 node -vNode.js 推荐使用 v18.20.3 或者 …...

crossover mac好用吗 CrossOver Mac怎么下载 Mac用crossover损害电脑吗

CrossOver 是一款可以让Mac用户能够自由运行和游戏windows游戏软件的虚拟机类应用&#xff0c;虽然能够虚拟windows但是却并不是一款虚拟机&#xff0c;也不需要重启系统或者启动虚拟机&#xff0c;类似于一种能够让mac系统直接运行windows软件的插件。它以其出色的跨平台兼容性…...

PHP模块pdo_sqlite.so: undefined symbol: sqlite3_column_table_name

安装 php-sqlite3 之后&#xff0c;执行php -m 命令有警告&#xff0c;如下 PHP Warning: PHP Startup: Unable to load dynamic library pdo_sqlite (tried: /usr/lib64/php/modules/pdo_sqlite (/usr/lib64/php/modules/pdo_sqlite: cannot open shared object file: No su…...

卷积神经网络-奥特曼识别

数据集 四种奥特曼图片_数据集-飞桨AI Studio星河社区 (baidu.com) 中间的隐藏层 已经使用参数的空间 Conv2D卷积层 ReLU激活层 MaxPool2D最大池化层 AdaptiveAvgPool2D自适应的平均池化 Linear全链接层 Dropout放置过拟合&#xff0c;随机丢弃神经元 -----------------…...

VB.net进行CAD二次开发(四)

netload不能弹出对话框&#xff0c;参考文献2 参考文献1说明了自定义菜单的问题&#xff0c;用的是cad的系统命令 只要加载了dll&#xff0c;自定义的命令与cad的命令同等地位。 这时&#xff0c;可以将自定义菜单的系统命令替换为自定义命令。 <CommandMethod("Add…...

3步轻松月入过万,APP广告新模式大揭秘!

万万没想到&#xff1a;用这个APP广告模式&#xff0c;月入过万竟然如此简单&#xff01; 在移动应用开发的世界里&#xff0c;变现一直是一道难题。 许多APP开发者和产品经理为了提高收益、增强用户黏性&#xff0c;不断尝试各种策略。 然而&#xff0c;很多时候&#xff0c…...

java项目之智能家居系统源码(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的智能家居系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于Springboot的智能家居系…...

前端 JS 经典:读取文件原始内容

前言&#xff1a;有些时候在工程化开发中&#xff0c;我们需要读取文件里面的原始内容&#xff0c;比如&#xff0c;你有一个文件&#xff0c;后缀名为 .myfile&#xff0c;你需要拿到这个文件里的内容&#xff0c;该怎么处理呢。 在 vue2 中&#xff0c;因为 vue2 使用 vue-c…...

汇编概论和实践

一 汇编第一例 C代码 #include <stdio.h>int main() {printf("Hello, World!\n");return 0; }对应的汇编 .LC0:.string "Hello, World!"main:pushq %rbpmovq %rsp, %rbpleaq .LC0(%rip), %rdicall puts@PLTmovl $0, %eaxpopq %rbpret 二 CPU架构…...

铁塔基站用能监控能效解决方案

截至2023年10月&#xff0c;我国5G基站总数达321.5万个&#xff0c;占全国通信基站总数的28.1%。然而&#xff0c;随着5G基站数量的快速增长&#xff0c;基站的能耗问题也逐渐日益凸显&#xff0c;基站的用电给运营商带来了巨大的电费开支压力&#xff0c;降低5G基站的能耗成为…...

keepalived安装文档

目录 1、安装环境 2、安装keepalived 2.1 上传keepalived安装文件 2.2 解压 2.3 安装keepalived 2.4 加入开机启动&#xff1a; 2.5 配置日志文件 2.6 打开防火墙的通讯地址 1、安装环境 su - root yum -y install kernel-devel* yum -y install openssl-* yum -y …...

Spring Security

Spring Security spring提供的安全框架。主要提供了认证和授权的功能。简单梳理看看。   原理简单说就是Spring Security在基于Servlet应用中,其底层采用了Filter机制实现了对请求的认证,授权和漏洞防御等功能。 DelegatingFilterProxy 我们知道,Filter是Servlet规范里面…...

vue中大屏可视化适配所有屏幕大小

1. 外部盒子 .screenBox {width: 100vw;height: 100vh;background: url("/assets/images/bg.png") no-repeat;background-size: cover; }2.比例盒子 外层盒子css定义 .boxScale {width: 1920px;height: 1080px;background-color: orange;transform-origin: left top;…...

AI大模型探索之路-实战篇12: 构建互动式Agent智能数据分析平台:实现多轮对话控制

系列篇章&#x1f4a5; AI大模型探索之路-实战篇4&#xff1a;深入DB-GPT数据应用开发框架调研 AI大模型探索之路-实战篇5&#xff1a;探索Open Interpreter开放代码解释器调研 AI大模型探索之路-实战篇6&#xff1a;掌握Function Calling的详细流程 AI大模型探索之路-实战篇7…...

深入理解文件系统和日志分析

文件是存储在硬盘上的&#xff0c;硬盘上的最小存储单位是扇区&#xff0c;每个扇区的大小是512字节。 inode&#xff1a;存储元信息&#xff08;包括文件的属性&#xff0c;权限&#xff0c;创建者&#xff0c;创建日期等等&#xff09; block&#xff1a;块&#xff0c;连续…...

vue+vant移动端显示table表格加横向滚动条

vant移动端显示table效果&#xff0c;增加复选框&#xff0c;可以进行多选和全选。加横向滚动条&#xff0c;可以看全部内容。 <template><div class"app-container"><div class"nav_text" style"position: relative;"><…...

webserver服务器从零搭建到上线(八)|EpollPoller事件分发器类

文章目录 EpollPoller事件分发器类成员变量和成员函数解释私有的成员函数和成员变量成员函数 具体实现常量的作用构造函数和析构函数⭐️poll函数updateChannel函数removeChannel 函数removeChannel 和updateChannel⭐️fillActiveChannels 函数⭐️update 函数 总结 终于要开始…...

SD-WAN:企业网络转型的必然趋势

随着SD-WAN技术的不断进步和完善&#xff0c;越来越多的企业选择利用SD-WAN进行网络转型。根据IDC的研究&#xff0c;47%的企业已经成功迁移到SD-WAN&#xff0c;另有48%的公司计划在未来两个月内部署这一技术。 据Channel Futures报道&#xff0c;一位合作伙伴透露&#xff0c…...

构建高效稳定的短视频直播系统架构

随着短视频直播的迅猛发展&#xff0c;构建一个高效稳定的短视频直播系统架构成为了互联网企业的重要挑战。本文将探讨如何构建高效稳定的短视频直播系统架构&#xff0c;以提供优质的用户体验和满足日益增长的用户需求。 ### 1. 短视频直播系统的背景 短视频直播近年来蓬勃发…...

python分别保存聚类分析结果+KeyError: ‘CustomerID‘报错

如何在完成聚类分析后按聚类编号保存数据并且带上原数据所属ID # 将每个聚类的数据保存到不同的文件中 for cluster_id in range(6): # 假设共有6个聚类cluster_data data[data[cluster] cluster_id]cluster_data_with_customer_id cluster_data.copy()cluster_data_with_…...

Sui与Atoma合作为开发者提供AI支持

AI初创公司Atoma宣布其即将推出的推理网络将与Sui集成&#xff0c;该网络将使开发者能够在他们的应用程序中使用AI工具。Atoma选择Sui作为其第一个区块链集成对象是由于Sui的可扩展性和性能。 尽管生成式AI在过去几年中引起了轰动&#xff0c;但它尚未进入许多消费者应用程序。…...

go-gin中session实现redis前缀和db库选择+单点登录

分别实现了redigo中自动加前缀和session中自动加前缀 等有空了整理一个demo放到github上&#xff0c;到时候求个小星星 在gin-contrib/sessions/redis库中redis的前缀是被封装起来了&#xff0c;所以自定义前缀没有内部方法在这里我们自己实现一下NewStoreWithDBPrefix方法配…...

python-双胞胎字符串

[问题描述]&#xff1a;给定两个字符串s和t&#xff0c;每次可以任意交换s的奇数位和偶数位的字符&#xff0c;即奇数位的字符可以与任意其它奇数位的字符交换&#xff0c;偶数位的字符同样也可以与任意偶数位的字符的字符交换&#xff0c;问能否在有限的次数的交换下使s变为t?…...

万字长文,小白新手怎么开始做YOLO实验,从零开始教!整体思路在这里,科研指南针!

最近专栏来了很多的新手小白&#xff0c;对科研实验的过程感到困惑和无从下手&#xff0c;这篇文章就来讲解一下整体的科研流程&#xff0c;从选择数据集到发表论文的各个步骤&#xff0c;并针对大家在实验中常犯的错误进行解答。并且为大家提供通向我其他相关博客的指引&#…...

MDR-1A用什么前端:深度解析与实用指南

MDR-1A用什么前端&#xff1a;深度解析与实用指南 索尼MDR-1A作为一款备受瞩目的音乐耳机&#xff0c;其音质表现与前端设备的搭配息息相关。那么&#xff0c;MDR-1A用什么前端才能达到最佳的音效体验呢&#xff1f;本文将从四个方面、五个方面、六个方面和七个方面进行深入探…...

01Linux以及操作系统概述

课程目标 1.了解现代操作系统的整体构成及发展历史 2.了解Linux操作系统及其分支版本 3.直观上理解服务器端与桌面端版本的区别 课程实验 1.通过对CentOS和Ubuntu的演示&#xff0c;直观理解Linux与Windows的异同 课堂引入 本章内容主要为大家详细讲解Linux操作系统(以下简…...

华为OD刷题C卷 - 每日刷题 1

1、&#xff08;两数之和&#xff09;&#xff1a; 这段代码是针对力扣&#xff08;LeetCode&#xff09;上的“两数之和”问题。它提供了一个Java类Solution&#xff0c;其中包含一个方法twoSum&#xff0c;该方法接收一个整数数组nums和一个整数目标值target。目的是找出数组…...

基于ELK的日志管理【开发实践】

文章目录 一、ELK简介1.1 ELK的作用与应用1.2 ELK的组成1.3 Elasticsearch1.4 Logstash1.5 Kibana1.6 ELK架构简述1.7 基础知识1.7.1 数据格式1.7.2 正排索引和倒排索引1.7.3 全文搜索 二、ES入门---基于HTTP的使用方式&#xff08;了解&#xff09;2.1 索引操作2.1.1 创建索引…...

音视频开发—音频相关概念:数模转换、PCM数据与WAV文件详解

文章目录 前言1.模拟数字转换&#xff08;ADC&#xff09;1.1ADC的关键步骤&#xff1a; 2.数字模拟转换&#xff08;DAC&#xff09;2.1DAC 的基本流程包括&#xff1a; 3.PCM数据3.1PCM 数据的关键要素包括&#xff1a; 4.WAV文件4.1 WAV的构成4.2WAV文件的标准块结构4.3WAV的…...

Elasticsearch 8.1官网文档梳理 - 十三、Search your data(数据搜索)

Search your data 这里有两个比较有用的参数需要注意一下 Search timeout&#xff1a;设置每个分片的搜索超时时间。从集群级别可以通过 search.default_search_timeout 来设置超时时间。如果在 search.default_search_timeout 设置的时间段内未完成搜索请求&#xff0c;就会…...

笔墨挥毫如游龙 最是经典铁线篆——记著名书法家王子彬

真正的书法大家,必是经历了日积月累的求索磨炼,毕竟书法从来都不是一蹴而就的艺术,因此但凡是急功近利者,其人也是远远无法达到书入臻境的创作高度。而纵观当代书坛界内,其中王子彬先生的艺术声誉可谓是广为人知,作为一名深具传统功底的实力派书法大家,王子彬先生的取法历途无疑…...

智慧校园有哪些特征

随着科技的飞速进步&#xff0c;教育领域正经历着一场深刻的变革。智慧校园&#xff0c;作为这场变革的前沿代表&#xff0c;正在逐步重塑我们的教育理念和实践方式。它不仅仅是一个概念&#xff0c;而是一个集成了物联网、大数据、人工智能等先进技术的综合生态系统&#xff0…...

day25回溯算法part02| 216.组合总和III 17.电话号码的字母组合

216.组合总和III 题目链接/文章讲解 | 视频讲解 class Solution { public:vector<vector<int>> result;vector<int> path;int sum;void backtracking(int n, int k, int startindex) {// int sum accumulate(path.begin(), path.end(), 0);if (sum n &am…...

AWS联网和内容分发服务

概况 VPC Amazon Virtual Private Cloud (Amazon VPC) 让您能够全面地控制自己的虚拟网络环境&#xff0c;包括资源放置、连接性和安全性。首先在 AWS 服务控制台中设置 VPC。然后&#xff0c;向其中添加资源&#xff0c;例如 Amazon Elastic Compute Cloud (EC2) 和 Amazon …...

vscode设置编辑器文件自动保存

步骤 1.打开vscode的设置 2.在搜索栏输入关键字“保存”&#xff1b; 在 Files: Auto Save 设置项&#xff0c;选择自动保存的模式...

SJ705C安全帽高温预处理箱

一、仪器用途 安全帽高温预处理箱是我公司根据安全帽新国家标准检测试验要求而自主设计研发制造。是安全帽检测前做高温预处理的专用设备。 二、仪器特征 1、有PID自整定温度控制仪&#xff0c;控制准确。 2、数显计时、计温器。 3、石英灯管加热系统;。 …...

AI盒子在智慧加油站的应用

方案背景 为规范加油站作业&#xff0c;保障人民生命财产安全&#xff0c;《加油站作业安全规范》&#xff08;AQ 3010-2007&#xff09;中第五条规定&#xff1a;卸油作业基本要求&#xff0c;明确防静电、防雷电、防火、人员值守、禁止其他车辆及非工作人员进入卸油区。 痛点…...

IC开发——VCS基本用法

1. 简介 VCS是编译型verilog仿真器&#xff0c;处理verilog的源码过程如下&#xff1a; VCS先将verilog/systemverilog文件转化为C文件&#xff0c;在linux下编译链接生成可执行文件&#xff0c;在linux下运行simv即可得到仿真结果。 VCS使用步骤&#xff0c;先编译verilog源…...

MongoDB~存储引擎了解

存储引擎 存储引擎是一个数据库的核心&#xff0c;主要负责内存、磁盘里数据的管理和维护。 MongoBD的优势&#xff0c;在于其数据模型定义的灵活性、以及可拓展性。但不要忽略&#xff0c;其存储引擎也是插件式的存在&#xff0c;支持不同类型的存储引擎&#xff0c;使用不同…...

JavaScript实现粒子数字倒计时效果附完整注释

<!DOCTYPE html> <html lang="en"><head><meta charset...