AUTOSAR_EXP_ARAComAPI的5章笔记(14)
☞返回总目录
关联博文:骨架侧字段的总结
5.4.8 字段(Fields)
在骨架侧,服务实现负责以下内容:
- 更新并通知字段值的变化。
- 处理传入的 Get () 调用。
- 处理传入的 Set () 调用。
如 RadarService Skeleton Class 示例中所示,骨架为每个提供的字段提供一个字段包装类的成员。在骨架 / 字段提供方一侧的字段包装类与在代理 / 字段消费方一侧明显不同。
在服务提供方 / 骨架侧,特定于服务的字段包装类在骨架命名空间下的fields
命名空间内定义。以示例事件UpdateRate
的字段包装类为例:
class UpdateRate
{
public:
// 使用FieldType = uint32_t;
using FieldType = uint32_t;
/**
* Update等同于事件的发送方法。这会触发(如果配置了)通知的传输到订阅的客户端。
*
* 如果配置了Getter,至少必须调用一次以设置初始值。
*/
ara::core::Result<void> Update(const FieldType& data);
/**
* 注册GetHandler是可选的。如果注册,每当接收到get请求时,该函数将被调用。
*
* 如果没有注册Getter,ara::com负责使用update设置的最后一个值响应请求。
*
* 这隐含地要求在服务初始化后,在提供服务之前,至少调用一次update。这取决于服务的实现者。
*
* GetHandler应返回一个future。
*/
ara::core::Result<void> RegisterGetHandler(
std::function<ara::core::Future<FieldType>()> getHandler);
/**
* 如果字段支持,注册SetHandler是强制性的。
*
* 处理程序获取发送方请求设置的数据。
*
* 它必须验证设置并执行其内部数据的更新。然后应在未来设置该字段的新值。
*
* 返回的值将发送给请求者,并通过通知发送给所有订阅的实体。
*/
ara::core::Result<void> RegisterSetHandler(
std::function<ara::core::Future<FieldType>(const FieldType& data)> setHandler);
};
和事件类以及在代理侧一样,using
指令只是为字段的具体数据类型引入通用名称FieldType
。
我们提供了一个Update()
方法,服务实现者可以通过该方法更新字段的当前值。它与事件类中Send()
方法的第一种变体非常相似:字段数据已由服务应用程序开发人员在某处分配,并通过引用传递给Update()
的绑定实现。在对Update()
的调用返回后,调用者一侧的数据可能会被删除或更改。绑定实现将在调用中进行(通常是序列化的)复制。如果为该字段配置了 “变化通知”,则在Update()
调用过程中,绑定实现将触发对此字段订阅者的通知。
5.4.8.1 注册 getter
RegisterGetHandler()
方法为服务实现者提供了注册方法实现的可能性,当任何代理实例发出Get()
调用时,绑定实现将调用该方法。
在生成的骨架代码中,仅在 IDL 中为字段配置了 “字段 getter” 可用性时RegisterGetHandler()
方法才存在!注册这样的 “GetHandler” 是可选的!通常,服务实现者无需提供这样的处理程序。绑定实现始终可以访问通过Update()
设置的最新值。因此,任何传入的Get()
调用都可以由通信管理实现独立处理。
服务实现者仍然提供 “GetHandler” 的一个理论原因可能是:计算字段的新 / 当前值代价高昂 / 耗时。因此,服务实现者 / 字段提供者希望将此过程(计算当前值)推迟到确实需要该值时(由 getter 调用指示)。在这种情况下,他可以在其 “获取处理程序” 实现中计算新的字段值,并通过已知的ara::com
承诺 / 未来模式将其返回。
从更大的角度来看,如果字段也配置了 “on-change-notification”,那么服务实现者提供并注册 “GetHandler” 的这种设置实际上没有意义。on-change-notification 和 GetHandler 都可以获取字段最新值,只是 GetHandler 延时获取了。在这种情况下,新的订阅者在订阅时可能会获得过时的字段值,因为字段值的更新被推迟到显式调用 “获取处理程序”。
你还必须记住:在这种设置中,启用 “on-change-notification” 并注册 “GetHandler” 时,通信管理实现不会自动确保开发人员从 “获取处理程序” 返回的值与订阅者通过 “变化通知” 事件获得的值同步!如果 “获取处理程序” 的实现内部没有使用相同的值调用Update()
,而该值将通过ara::com
承诺返回,那么通过 “变化通知” 事件传递的字段值将与Get()
调用返回的值不同。即,通信管理实现不会自动 / 内部地使用 “获取处理程序” 返回的值调用Update()
。
总之:使用RegisterGetHandler()
是一个相当特殊的用例,开发人员应该意识到其内在影响。
此外,如果用户提供的 “获取处理程序” 仅返回已经由服务实现通过Update()
更新的当前值,那么通常效率非常低!在任何传入的Get()
调用中,通信管理都必须调用用户空间,并对返回的值额外应用字段序列化。如果开发人员不注册 “获取处理程序”,并将Get()
调用的处理完全留给通信管理实现,那么这两件事都可以完全 “优化掉”。
5.4.8.2 注册设置器
与RegisterGetHandler()
相反,如果服务实现存在(即字段启用设置器),则必须调用RegisterSetHandler()
API。
我们决定强制注册 “SetHandler” 的原因很简单:我们期望服务器实现始终需要检查由任何匿名客户端设置的新 / 更新的字段值的有效性。
查看 “SetHandler” 的签名std::function<ara::core::Future<FieldType>(const FieldType& data)>
可以发现,注册的处理程序将新值作为输入参数,并期望返回一个值。其背后的语义是:“SetHandler” 始终必须返回有效的(最终被替换 / 纠正的)值。这允许服务端实现者验证 / 否决客户端提供的新字段值。
“SetHandler” 返回的有效字段值被通信管理实现隐式接管,就好像服务实现者自己使用有效值显式调用了Update()
一样。这意味着:在 “SetHandler” 内部进行显式的Update()
调用是多余的,因为通信管理无论如何都会使用 “SetHandler” 的返回值更新字段值。
5.4.8.3 确保 “SetHandler” 的存在
通过返回可恢复错误来确保已注册 “SetHandler” 的存在:如果开发人员在骨架实现上调用OfferService()
,并且尚未为每个启用设置器的字段注册 “SetHandler”,则通信管理实现应返回一个ComErrc::kSetHandlerNotSet
的错误,在ara::core::Result
中指示此错误。
5.4.8.4 确保有效字段值的存在
由于字段的最基本保证是在任何时候都具有有效值,因此ara::com
必须以某种方式确保,提供字段的服务实现必须在服务(及其字段)对潜在消费者可见之前提供一个值,这些消费者在订阅字段后,期望在(如果字段配置了通知)获得初始值通知事件时,或者在(如果字段启用了 getter 但尚未注册 “GetHandler”)时,使用Get()
调用中获得有效值。
因此,ara::com
通信管理实现需要以下方式行为:如果开发人员在骨架实现上调用OfferService()
,并且尚未在任何字段上调用Update()
,而该字段
- 已启用通知。
- 或已启用 getter 但尚未注册 “GetHandler”。
则通信管理实现应返回一个错误(ComErrc::kFieldValueIsNotValid
),在ara::core::Result
中指示此错误。
注意:AUTOSAR 元模型支持以所谓的PPortPrototype
的FieldSenderComSpec
的形式为字段定义这样的初始值。因此,调用Update()
的应用代码应考虑此模型元素。
5.4.8.5 从 GetHandler/SetHandler 访问当前字段值
由于底层字段值仅为中间件所知,因此当前字段值无法从位于应用程序级别的 “GetHandler/SetHandler” 实现中访问。如果 “GetHandler/SetHandler” 需要读取当前字段值,则骨架实现必须提供一个可从应用程序级别访问的字段值副本。