DDSI-RTPS V2.3版本规范中文翻译 part4--行为模块2

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值