1. Event - Driven vs Polling - Based access
1.1 轮询方式(Polling - Based access)
典型用例:应用程序周期性被触发,在特定截止时间前处理,常见于调节器 / 控制算法,循环激活由实时定时器驱动以确保最小抖动。在每个激活周期调用GetNewSamples(),用更新的缓存数据作为当前处理迭代输入,这种情况下,应用程序按调度获取最新数据处理即可,若有新数据就通知应用程序则可能造成不必要的上下文切换。
1.2 事件驱动方式(Event - Driven access)
适用场景:当应用程序不是周期性、截止时间驱动,而是需在某些事件发生时简单做出反应,周期性调用GetNewSamples()轮询新事件就不合适且效率低下,此时希望ara::com实现通知应用程序进行异步上下文切换。
实现机制
通过ara::core::Result<void> SetReceiveHandler(ara::com::EventReceiveHandler handler) API 注册用户定义的回调函数,当有新事件数据可用时通信管理调用该回调函数,注册的回调函数无需可重入,因为ara::com实现会序列化注册的回调函数,且允许从注册的回调函数内部调用GetNewSamples()。用户可通过事件包装类提供的UnsetReceiveHandler()方法在事件驱动和轮询方法之间切换。
示例代码
#include "RadarServiceProxy.hpp"#include <memory>#include <deque>using namespace com::mycompany::division::radarservice;using namespace ara::com;/*** our radar proxy - initially the unique ptr is invalid.*/std::unique_ptr<proxy::RadarServiceProxy> myRadarProxy;/*** a storage for BrakeEvent samples in fifo style*/std::deque<SamplePtr<const proxy::events::BrakeEvent::SampleType>> stNActiveSamples;/*** \brief application function, which processes current set of BrakeEvent* samples.* \param samples*/void processLastBrakeEvents(std::deque<SamplePtr<const proxy::events::BrakeEvent::SampleType>>&mples) {// do whatever with those BrakeEvent samples ...}/*** \brief event reception handler for BrakeEvent events, which we registerget informed about new events.*/void handleBrakeEventReception() {/*** we get newly arrived BrakeEvent events into our process space.* For each sample we get passed in, we check for a certain property* "active" and if it fulfills the check, we move it into our Last10-orage.* So this few lines basically implement filtering and a LastN policy.*/myRadarProxy->BrakeEvent.GetNewSamples([](SamplePtr<proxy::events::BrakeEvent::SampleType> samplePtr) {if(samplePtr->active) {lastNActiveSamples.push_back(std::move(samplePtr));if (lastNActiveSamples.size() > 10)lastNActiveSamples.pop_front();}});// ... now process those samples ...processLastBrakeEvents(lastNActiveSamples);}int main(int argc, char** argv) {/* Instance Specifier from model */ara::core::InstanceSpecifier instspec {...}auto handles = proxy::RadarServiceProxy::FindService(instspec);if (!handles.empty()) {/* we have at least one valid handle - we are not very particular* here and take the first one to create our proxy */myRadarProxy = std::make_unique<proxy::RadarServiceProxy>(handles[0]);/* we are interested in receiving the event "BrakeEvent" - so we* subscribe for it. We want to access up to 10 events, since our* sample algo averages over at most 10.*/myRadarProxy->BrakeEvent.Subscribe(10);/* whenever new BrakeEvent events come in, we want be called, so we* register a callback for it!* Note: If the entity we would subscribe to, would be a field* instead of an event, it would be crucial, to register our* reception handler BEFORE subscribing, to avoid race conditions.* After a field subscription, you would get instantly so called* "initial events" and to be sure not to miss them, you should care* for that your reception handler is registered before.*/myRadarProxy->BrakeEvent.SetReceiveHandler( handleBrakeEventReception);}// ... wait for application shutdown trigger by application exec mgmt.}
以下是对上述代码的分析:
1. 头文件和命名空间
- 代码包含了
RadarServiceProxy.hpp
头文件,并使用了std
命名空间以及com::mycompany::division::radarservice
和ara::com
命名空间。2. 全局变量
std::unique_ptr<proxy::RadarServiceProxy> myRadarProxy;
:这是一个智能指针,用于管理RadarServiceProxy
类型的对象,初始时指针无效。std::deque<SamplePtr<const proxy::events::BrakeEvent::SampleType>> stNActiveSamples;
:定义了一个双端队列,用于存储BrakeEvent
的样本。3. 函数
processLastBrakeEvents
- 这个函数接受一个
BrakeEvent
样本的双端队列引用作为参数,在函数内部可以对这些样本进行处理,但当前代码中只给出了注释,具体处理逻辑需要在// do whatever with those BrakeEvent samples...
部分实现。4. 函数
handleBrakeEventReception
- 这是
BrakeEvent
事件接收的处理函数。- 在函数内部,通过
myRadarProxy->BrakeEvent.GetNewSamples
获取新的BrakeEvent
样本。- 对于每个样本,检查其
active
属性,如果为true
,则将样本添加到lastNActiveSamples
双端队列中,如果队列大小超过10
,则弹出队首元素。- 最后调用
processLastBrakeEvents
函数处理这些样本。5.
main
函数
- 首先定义了
ara::core::InstanceSpecifier instspec
,用于指定实例。- 通过
proxy::RadarServiceProxy::FindService(instspec)
查找服务句柄。- 如果找到了有效的句柄,创建
RadarServiceProxy
的代理对象。- 对
BrakeEvent
进行订阅,最多订阅10
个事件。- 注册
BrakeEvent
的接收处理函数handleBrakeEventReception
。6. 代码功能总结
- 这段代码的主要功能是与一个雷达服务进行交互,接收
BrakeEvent
事件,并对事件样本进行过滤和存储,最后进行处理。它利用了代理模式来与服务通信,通过事件订阅和回调机制来实时处理事件。
创建RadarService类型的Proxy实例,并注册接收处理程序,当接收到新的BrakeEvent事件时ara::com实现调用该处理程序,在接收处理程序中更新本地缓存、过滤不满足特定属性的BrakeEvent事件并调用处理函数处理决定保留的Sample。
2. Buffering Strategies
2.1 部署情况
一个服务提供一个事件,两个本地服务消费者(SWC)通过各自Proxy的事件包装类订阅该事件,Proxy有本地事件缓存(通过GetNewSamples()填充),服务实现将事件数据发送到通信管理缓冲区,该缓冲区可能在以下位置:
- 内核空间:数据发送到不在应用程序进程内存区域,使用进程间通信原语(如管道或套接字)时常见,数据最终在内核缓冲区空间。
- 共享内存:数据发送到接收者 / 代理可直接读取的内存区域,不同方的写入 / 读取通过内存屏障或显式互斥锁进行轻量级同步。
- 进程间通信守护进程空间:数据发送到明确的非应用程序进程(充当进程间通信 / 绑定实现的守护进程),从技术上讲可基于进程间通信原语(如通过内核空间或共享内存)将数据从服务进程传输到守护进程。
2.2 设计考虑
鼓励汽车开放平台(AP)产品供应商使用基于引用的方法访问事件数据,事件包装类的ara::com API 通过SamplePtr建模访问(传递指针给回调函数而非值),在典型的 1:N 事件通信场景中,本地事件缓存中是指向中央通信管理缓冲区数据的指针 / 引用,GetNewSamples()更新本地缓存可实现为引用更新。虽然传输到应用程序进程的数据在访问前通常需反序列化且共享已反序列化的数据可能棘手,但 API 设计为消费者之间的事件数据共享提供了空间。