☞返回总目录
4 Fundamentals
4.1 Proxy/Skeleton Architecture
如果你曾经从程序员的角度接触过中间件技术,你可能很熟悉Proxy/Skeleton架构的方法。从使用Proxy/Skeleton范例中间件技术的数量上来看,称之为“经典方法”是名副其实的,所以对于ara::com,我们也决定使用经典的 Proxy/Skeleton 架构。
该模式的基本思想是,从服务定义中生成两个代码工件:
-
服务代理:从远程服务的服务消费者的角度来看,Service Proxy是在代码级别上代理该服务。在面向对象的语言绑定中,这通常是一个生成类的实例(服务提供的所有功能方法)。因此,服务消费者端应用程序代码与这个本地代理进行交互,本地代理知道如何将这些调用传播到远程服务实现并返回。
-
服务框架:从功能的服务实现的角度来看,Service Skeleton是将服务实现连接到通信管理传输层的代码,以便分布式服务消费者可以连接服务实现。在面向对象的语言绑定中,这通常是生成的类的实例。通常,应用程序开发人员的服务实现通过继承关系与这个生成的Service Skeleton类相连接。
因此,服务端应用程序通过实现Service Skeleton的抽象方法或客户端应用程序调用Service Proxy的方法来与AutoSar CM中间件进行交互。
4.2 Means of Communication
现在,我们已经讨论了Proxy/Skeleton体系结构,让我们继续讨论如何在Proxy/Skeleton之间进行通信。ara::com定义了四种不同的机制来在服务器和客户端之间进行通信
-
Methods
-
Events
-
Fields
-
Triggers
在使用这些机制之前,必须实例化服务,并且服务器必须向系统提供自己(OfferService())。然后,客户端需要使用代理( FindService() 或StartFindService())找到并连接到服务实例。
4.3 ara::com Event and Trigger based communication
当Client Application连接到服务器时,它可以订阅(subscribe())服务器提供的服务中的事件,如图4.2所示。

当事件数据可用时, Sever Application将事件数据发送( send())到CM中间件,通知所有订阅的Client Application。然后,订阅者可以使用 GetNewSamples()直接或通过由通知触发回调(由 SetReceiveHandler()定义)获取事件样本。特定条件下,服务器使用Trigger来通知用户何时获取事件样本。它不传输任何数据。它使用与事件相同的订阅-通知机制.
4.4 ara::com Method based communication
使用基于Method的通信,Client Application调用远程服务器上的Method。该Method可能会向客户端返回值。如果提供了返回值,ara::core::Future和ara::core::Promise模式用于为通信提供非阻塞行为。服务器可以针对方法调用的不同处理模式进行配置。这些选项包括:
-
事件驱动,并发(kEvent): 传入的服务方法调用以基于事件的方式处理。
-
事件驱动,顺序(kEventSingleThread):与单线程基础上的kEvent相同。
-
轮询(KPoll):传入的服务方法调用需要通过调用ProcessNextMethodCall以轮询方式进行显式处理。
4.5 ara::com Field based communication
通过基于Field的通信,服务器可以为一些数据(即:变量)提供值,客户端可以随时访问或更新这些值。Field的功能可以被视为事件和方法的组合:
-
像事件一样,客户端可以订阅值的变化。将使用与事件相同的通知机制通知客户端(由SetReceiveHandler()定义)。
-
使用方法,客户端可以通过调用get-operation (Get())来检索值,或者通过为代理中的字段调用Set-operation(Set())来更新值。
在服务器端,Field的处理在Skeleton实现中:
-
定义回调:当客户端更新值时调用回调(由RegisterSetHandler()定义)。
-
当需要向客户端发布新值时,调用更新方法(update())。
4.6 Data Type Abstractions
ara::com API引入了特定的数据类型,这些数据类型在它的各种接口中使用。它们大致可以分为以下几类:
-
指针类型:用于指向中间件传输的数据的指针
-
集合类型:用于中间件传输的数据集合。
-
异步操作结果管理的类型: ara::com依赖于AUTOSAR AP特定数据类型(参见AUTOSAR_SWS_AdaptivePlatformCore.pdf 文档),这是C++ std::future/std::promise的特定版本
-
函数包装器:用于中间件调用各种应用程序端回调或处理函数
标准文文档中的ara::com只是定义了这些类型的签名和预期行为,但没有提供实现。为什么这么做呢?因为平台供应商针对这些类型,可以很容易地提出他们自己的优化实现。这对于集合和指针类型来说是显而易见的,因为IPC实现的主要工作之一是为中间件用户之间交换的数据处理进行内存分配。能够提供他们自己的实现,允许优化他们选择的内存模型。
对于大多数类型,ara::com在ara/com/types.h中提供了到现有C++类型的默认映射。由AUTOSAR_SWS_AdaptivePlatformCore.pdf 提供的类型的默认映射可以在例如ara/core/future.h或ara/core/promise.h中找到。ara::com提供的默认映射甚至对希望实现自己变体的产品供应商来说也有好处:他可以根据默认映射的实现来验证自己实现的功能行为。
4.7 Error Handling
ara::com API遵循AUTOSAR_SWS_AdaptivePlatformCore.pdf 中“错误处理”一章描述的错误处理概念。可恢复的错误将通过嵌入到ara::core::Result中的ara::Core::ErrorCode返回,它包含有效的返回值或ara::Core::error code。
对于ara::com API中的每个函数,定义了一组预定义的ara::Core::ErrorCodes,它们来自错误域ara::com::ComErrorDomain(或者来自ara::com::e2e::E2EErrorDomain,用于E2E检查)。这些错误应该由使用API的应用程序来处理。除此之外,供应商还可能定义额外的错误代码,这些代码也可能需要处理。
ara::com API中的应用程序错误只能发生在调用ServiceInterface方法时,因此在第5.3.6小节和第5.4.6小节中有完整的介绍。 ara::com API中的异常仅在违规或损坏的情况下使用。这些是不可恢复的,通常不应由应用程序开发人员处理。
4.8 Service Connection Approach
4.8.1 Instance Identifiers and Instance Specifiers
在Proxy和Skeleton端使用的Instance Identifiers是一个非常重要的概念,所以在接下来的章节中,在详细描述ara::com的Proxy和Skeleton之前,在这里给出它们的解释。
在ara::com中,当在客户端/代理端需要搜索服务的特定实例时,或者在服务器/框架端创建服务的特定实例时,会使用Instance Identifiers。
在ara::com API级别,Instance Identifiers通常是特定于技术绑定的标识符。
因此,构成这种Instance Identifiers的具体内容/结构完全是技术特定的:
SOME/IP使用16位无符号整数标识符来区分同一服务类型的不同实例,而DDS (DDS-RPC)使用String< 256 >作为service_instance_name。
与绑定技术无关,任何具体Instance Identifiers的抽象外观都应适用于ara::com命名空间中ara::com API级别的签名:
class InstanceIdentifier
{
public:
static ara::core::Result<InstanceIdentifier> Create(StringView serializedFormat) noexcept;
explicit InstanceIdentifier(ara::core::StringView serializedFormat);
ara::core::StringView ToString() const;
bool operator==(const InstanceIdentifier& other) const;
bool operator<(const InstanceIdentifier& other) const;
InstanceIdentifier& operator=(const InstanceIdentifier& other);
};
正如您所看到的,实例标识符接口ara::com::InstanceIdentifier,提供了一个接收字符串的ctor(代表构造函数),这意味着它可以由字符串表示来构造。它还提供了一个ToString()方法,该方法允许获取与ara::com::InstanceIdentifier的字符串。
这对接收字符串表示的ctor和写出字符串表示的可能性使得ara::com::InstanceIdentifier“可序列化”。这允许它被传输、保存、以后重用(稍后将详细介绍潜在的使用案例)。
对于ara::com的用户来说,反省这个字符串(试图解释内容)是没有意义的。如上所述:内容将高度特定于中间件产品/绑定!
由于基于ara::com的应用程序所使用的技术绑定是由集成商在部署期间定义/指定的,因此ara::com软件开发商对其内容/结构的任何期望通常都是无效的。然而,将它记录/跟踪到日志通道可能有助于调试分析。
那么,软件开发人员从哪里得到这样一个高度绑定的ara::com::InstanceIdentifier来用于ara::com API调用呢?
答案是:通过ara::com提供的功能,该功能将软件开发人员在其领域中通常使用的逻辑本地名称转换为特定于技术/绑定的ara::com::InstanceIdentifier。这种间接控制了两种挑战:
-
使用ara::com的开发人员不需要了解任何关于绑定及其细节的知识
-
集成商可以在部署中调整绑定
构造ara::com::InstanceIdentifier的本地名称基本上来自AUTOSAR元模型(描述软件组件模型)。对这个本地名称的要求——从现在开始我们称之为“instance specifier”——是它在可执行文件中是明确的。它的基本形式是:
<context 0>/<context 1>/ ... / <context N> / <port name>
这种“instance specifier”的C++表示是ara::core::InstanceSpecifier类。从结构上看,它类似于ara::com::InstanceIdentifier:
class InstanceSpecifier final
{
public:
// ctor to build specifier from AUTOSAR short name identifier
// with ’/’ as separator between package names
static Result<InstanceSpecifier> Create(StringView metaModelIdentifier);
explicit InstanceSpecifier(StringView metaModelIdentifier);
InstanceSpecifier(const InstanceSpecifier& other);
InstanceSpecifier(InstanceSpecifier&& other) noexcept;
InstanceSpecifier& operator=(const InstanceSpecifier& other);
InstanceSpecifier& operator=(InstanceSpecifier&& other);
~InstanceSpecifier() noexcept;
StringView ToString() const noexcept;
bool operator==(const InstanceSpecifier& other) const noexcept;
bool operator==(StringView other) const noexcept;
bool operator!=(const InstanceSpecifier& other) const noexcept;
bool operator!=(StringView other) const noexcept;
bool operator<(const InstanceSpecifier& other) const noexcept;
};
如果确保了明确性,集成者/部署者可以通过“manifest file”将具有其特定实例id的专用技术绑定分配给那些“实例说明符”,该“manifest file”专门用于Executable的不同实例化/执行。这显式地允许N次启动同一个Executable,每次使用不同的“manifest file”,以不同的方式映射同一个ara::core::InstanceSpecifier。 关于ara::com与元模型的关系以及嵌套内容的性质的细节可以在后续章节中读到。API ara::com提供了以下函数,用于将ara::core::InstanceSpecifier(软件开发人员领域中的本地名称)转换为技术ara::com::InstanceIdentifier:
namespace ara {
namespace com {
namespace runtime {
ara::core::Result<ara::com::InstanceIdentifierContainer> ResolveInstanceIDs
(ara::core::InstanceSpecifier modelName);
}
}
}
为什么这个API确实会返回一个ara::com::InstanceIdentifier container,它代表一个ara::com::InstanceIdentifier的集合, 需要解释一下: AUTOSAR支持集成人员可以在一个软件组件开发者可见的抽象标识符后面配置多个技术绑定。这种特性称为多重绑定,在本文档的不同部分都有提及(在AUTOSAR_EXP_ARAComAPI.pdf 7.3节中有更详细的解释)。 在Skeleton/Server端使用多重绑定是一个常见的用例,因为它只是允许不同的客户机在联系服务器时使用它们的首选绑定。相反,在Proxy/Client使用多重绑定是一种非常奇特的方法。例如,它可以用于支持一些故障转移方法(如果绑定A不起作用,则求助于绑定B)。
因此,调用ResolveInstanceIDs()的可能结果是:
-
空列表:集成器未能提供抽象标识符的映射。这很可能是配置错误。
-
单元素列表:常见案例,映射到一个具体技术绑定的一个具体实例id。
-
多元素的列表:映射到可能具有多个技术绑定的多个技术实例。
从技术上讲,ResolveInstanceIDs()的中间件实现从捆绑在Process中的service instance manifest中查找ara::core::InstanceSpecifier。因此,ara::core::InstanceSpecifier在捆绑的service instance manifest中必须是明确的。
4.8.2 When to use InstanceIdentifier versus InstanceSpecifier
根据前面的解释,可能会产生这样的印象:在使用需要InstanceIdentifier 信息的ara::com API之前,软件开发人员总是必须首先手动(通过调用ResolveInstanceIDs())将ara::core::InstanceSpecifier解析为ara::com::InstanceIdentifier。正如我们已经提到的,对于软件开发人员来说,AutoSar AP SWC的“典型”方法是使用来自软件组件模型领域的抽象“instance specifiers”,这确实有点尴尬。正如您将在接下来的章节中看到的,这些章节详细介绍了Proxy/Skeleton端的API,ara::com提供了典型的函数重载,这些重载要么采用ara::com::InstanceIdentifier,要么采用ara::core::InstanceSpecifier,从而使开发人员在最常见的用例中解放出来,只需使用ara::core::InstanceSpecifier就可以显式调用ResolveInstanceIDs() 。这意味着,ara::com::InstanceIdentifier的直接使用和ara::core::InstanceSpecifier的手动解析更适用于具有特定/特殊用例的高级用户。各章中将给出一些例子, 其中讨论了代理/框架端的相应ara::com API重载。这两种变体的根本区别是ara::com::InstanceIdentifier可以更容易地在AP应用程序/进程之间交换!
因为它们已经准确地包含了所有特定于技术的信息,并且不需要通过service instance manifest的内容进行任何进一步的解析,所以这种序列化的ara::com::InstanceIdentifier可以在不同的进程中重新构建,并且只要该进程能够访问ara::com::InstanceIdentifier所基于的相同绑定技术,就可以使用它。
4.8.2.1 Transfer of an InstanceIdentifier
如前所述,ara::com::InstanceIdentifier应该只用于“高级用户”,因为它的格式依赖于供应商,并且包含技术绑定信息。因此,ara::com::InstanceIdentifier的传输或存储可能非常危险。因为在传输或重新存储之后传输绑定可能不再存在,或者供应商A的ara::com::instance标识符可能被使用供应商B的应用程序解释.