1 本文的介绍
1.1 内容
虽然FC的SWS 是ARA接口规范,但有些接口需要如何使用的“指南”。这份指南确实跟规范相关,但是有些是间接的,并且在每个SWS中都包含这些信息,因此读者很难理解其用法。另一个重要的角度是这些指南是AA 遵守的需求,但是FC的SWS是FC的需求规范。因此,在SWS中包含这些内容不太合适,这就是这本文档的目的。
如上所述背景,这份文档的主要内容是应用程序遵守的指南。不是所有的FC在这份文档中有内。当它认为有效时,它们将被添加。
内容是按照相关的话题组织的,但是一般情况下,按照FC组合一起,每个都有独立的章节。并且,请注意这些内容提供在单独的AUTOSAR AP 文档中。如果是这种情况,这种文档会被列出来或引用这份指南。
1.2 预读
这份文档是AP SWS的补充文档。因此,这份文档里的话题相关的SWS应该同步阅读。并且,应该阅读的第一份文档是Explanations of Adaptive Platform Design, AUTOSAR_EXP_PlatformDesign.pdf.,这份文档介绍了AP的架构
1.3 和其他AUTOSAR规范的关系
参考内容和预读
2. 核心类型
2.1 错误处理
错误处理对任何软件开发都是重要的话题。对于安全关键软件,它更重要,因为软件生命要依靠它。但是,目前的标准对安全关键软件的开发加了很多的限制,比如创建工具链,特别是关于C++异常。对ASIL应用程序,使用C++异常通常是不可能的因为缺乏对asil认证的c++编译器的异常支持。
Adaptive平台介绍了一个概念使没有C++异常的错误处理成为可能并定义了大量的c++数据类型来辅助这个概念。
从应用程序编码者的观点来看,核心类型实现这个概念的是 ara::core::ErrorCode和ara::core::Result.
2.1.1 ErrorCode
ara::core::ErrorCode的实例表示软件内的一个特定的错误条件。和std::error_code类似,但是在很多方面都不相同。
ErrorCode一直包含一个枚举值(类型擦除为整型)和一个错误域的引用。这个枚举值描述了错误的特定类型。错误域引用定义了错误应用的上下文。其他可选的成员一个是用户定义的消息字符串,一个是厂商定义的补充错误描述值。
2.1.2 Result
类ara::core::Result遵守来自C++建议 p0786中“ValueOrError”的概念。它包含一个value或一个error。由于模板特性,value和error可以是任何类型。但是,错误类型默认为ara::core::ErrorCode,而且希望整个Adaptive平台都能保持这个分配。
因为ErrorType默认为ara::core::ErrorCode,ara::core::Result的大部分声明仅需要给一个ValueType,比如对Result类型ara::core::Result<int>包含一个int变量或一个ErrorCode.
ARA接口使用ara::core::Result作为可恢复错误函数的返回类型。这个类型或者用来从对象中生成C++异常,或通过未使用异常的观察者方法中提取错误信息。
这部分指导你在应用程序代码中如何处理从ARA接口中返回Result对象。并且指导你如何在自己的Adaptive应用程序中创建新的Result对象。
2.1.2.1 Result的创建
使用嵌入值创建Result,有构造函数允许从ValueType隐式转换。这样用value来定义Result非常直接。
Result<int> res1(42);
Result<int> res2 = 42;
从声明返回Result的函数中返回一个value也是同样的直接。
Result<int> myfunction()
{
return 42;
}
把error放进Result中需要调用显示构造函数,比如:
ErrorCode ec = MyEnum::some_error;
Result<int> res2(ec);
另外,在静态成员函数中构造Result对象也是可能的。比如:
Result<int> res1 = Result<int>::FromValue(42);
Result<int> res2 = Result<int>::FromError(ec);
当Value Type或ErrorType Copy 代价非常昂贵的时候,这些形式是非常有益的。比如,返回的Reslut包换一个BigClass的实例。这个BigClass有两个构造参数"a1"和"a2"构建。像这样:
return Result<BigClass>::FromValue(a1, a2);
对于ErrorType,也允许ErrorCode实例的隐式构建,包含一个自定义错误消息和/或一个支持数据值。
return Result<BigClass>::FromError(
MyEnum::some_error, // ErrorCode enum value
"this operation did not work", // custom error message
0x12345678 // support data value
);
这种形式构建,只需要 执行一次构造函数。不像普通的构造函数调用,至少要执行两次构造操作,因为预先创建的值必须copy或move到Result实例中。
2.1.2.2 提取value和error
当试图从Result中提取value和error的时候,首先要考虑的是value或error是否真实可用。通常情况下,这是未知的,所以必须小心处理者两种情况。
当没有使用异常工作的时候, 要先查询Result对象是否包含value或error.
Result<int> some_function() { … }
Result<int> res = some_function();
if (res.HasValue()) {
int theValue = res.Value();
} else {
ErrorCode const& ec = res.Error();
}
这段代码在没有异常的环境中也可以完全工作,包括编译器完全不支持异常操作。
当处理异常工作流是,查询代码看起来更像普通的异常代码:
Result<int> some_function() { … }
int theValue = some_function().ValueOrThrow();
由some_function()返回的Result对象通过调用它的ValueOrThrow()成员函数快速归到它的ValueType(int)。事实上,如果Result确实包含一个ErrorCode,会立刻抛出一个对应到嵌入的ErrorCode对象的异常类型。自然,try...catch快应该添加到代码中一个合适的位置。
2.1.2.3 高级话题
提取嵌入的value或error两个借本的方法是Result::Value()和Result::Error()。但是,在调用任何这些函数时,必须确定Result对象确实包含调用这些函数之一所隐含的内容。在前面的部分,首先调用Result::HasValue(), Value()或Error()的调用依赖这个调用的输出。
另外一个访问嵌入value更方便的方式,之前的部分已经提过,通过调用Result::ValueOrThrow, 不需要if语句,整个调用只需要单行语句(不包含try...catch块,这个块可能在其他地方存在)。
其他方便的方法,比如Result::ValueOr提取value, 如果不存在就取默认值。比如:
int res = some_function().ValueOr(42);
Result :: ValueOr的一般化称为Result :: Resolve,它不使用默认值作为参数,而是一个Callable,它可按需创建默认值:
int res = some_function().Resolve([](ErrorCode const& ec){ return 42; });
对这种特殊的例子,使用Result::Resolve而不是Result::ValueOr没有意义。但是当常见默认值代价很昂贵的时候,可能是有意的。通过使用Result::Resolve,只有当确实需要的时候才会创建默认值。
另外一个方便的方法是Result::Bind,允许将包含的value转成另外一个value,甚至是另外一个类型,比如:
Result<String> res = some_function().Bind([](int v){ return v + 1; }).Bind([](int v){ return std::to_string(v); }).Bind([](String const& s) { return "'" + s + "'"); });
第一次调用Result::Bind()获取包含在Result对象中的value,加1之后,放入一个新的Result对象中并返回。
第二次调用Result::Bind()获取包含在Result对象中的value,将它转换成String,返回新的带有此字符串的Result<String>。
最后一次调用Result::Bind()获取包含在新的Result对象中的String对象,加上引号,放入其中并返回新的Result对象。
如果Result没有包含value, 那么这些可调用项都没有,Result对象仅进行类型转换,但保留原始的ErrorCode。
传递给Result :: Bind的Callables必须采用合适的类型作为参数,并且可以直接返回ValueType(如之前所说,或者和前面ValueType相同,或者一个新的,不同的ValueType),或Result<ValueType>。
3 执行管理
3.1 执行状态
执行状态描述了任何进程的内部生命周期。每个进程都需要给执行管理报告执行状态的变化,使用ExecutionClient::ReportExecutionState()接口。
一旦进程启动,当报告了KRunning状态,执行管理就认为进程初始化完成(见[SWS_EM_01053])。请注意Service Discovery会引入不确定性的延迟,因此建议在报告kRunning状态后进行;因此进程有可能在报告KRunning状态时初始化还没有完成。
执行管理通过向进程发送SIGTERM信号来启动进程终止。一旦收到SIGTERM,进程通过向执行管理报告kTerminating状态确认状态变化请求(见SWS_EM_01070)。
如果是自终止进程,进程会通过想执行管理报告kTerminating状态来启动自终止(见[SWS_EM_01071)。
报告kTerminating之后,进程会保存持久数据并释放所有内部使用的资源。进程通过简单的推出表明Terminating状态的完成(伴随合适的退出码)。执行管理不需要进程本身明确的进程终止的通知。
3.2 确定性执行
执行管理支持完全确定的进程多线程执行,因此处理给定的输入数据会在限定的时间内产生一直的输出,比如行为是可重现的。
在AUTOSAR Adaptive平台中,要求这种确定性的用例包括在高安全目标系统的软件Lockstep框架中的冗余执行和重用已验证的软件。更多的细节可以见Specification of Execution Management, AUTOSAR_SWS_StateManagement.pdf,“Deterministic Execution”部分。
可以完全确定地执行的进程必须以某种方式设计,实施和集成,使其独立于其他功能和计算,零星无关事件,竞争条件,偏差随机数等导致的处理器负载。
不确定行为可能有不同的原因造成,比如,计算资源不足或数据的不协调访问,很可能是由多个处理器内核上运行的多个线程造成的。线程访问数据的顺序也会影响结果,是结果不确定。
完全确定的执行包括:
*时间确定。计算的输出一直在给定的最后期限前产生。进程的资源需求需要以标准的方式描述,所以集成者能给进程分配足够的资源。(见Specification of Execution Management, AUTOSAR_SWS_StateManagement.pdf,“Real-Time Resources”部分)。
*数据确定。给定相同的输入和内部状态,计算也一直会产生相同的输出。这个部分剩下的部分将会描述如何达到数据确定。
执行管理提供DeterministicClient库功能来支持确定执行。
* 通过等待点API控制流程内部周期WaitForNextActivation()([SWS_EM_01301])。当API返回时,进程应执行一个循环,然后再次调用API以等待下一次激活。这个API的返回值控制进程的内部生命周期(比如:初始化,运行,终止),必须对其进行相应的准备。([SWS_EM_01302], [SWS_EM_01303] and [SWS_EM_01304]).
* 阻塞确定性工作程序池API RunWorkerPool()([SWS_EM_01305]),用于执行一组容器元素([SWS_EM_01306]),这些容器元素由同一工作程序运行对象(即应用程序功能)并行或顺序处理。
* APIs GetActivationTime() ([SWS_EM_01310]) 和 GetNextActivationTime() ([SWS_EM_01311]) 提供激活时间戳。这个时间戳不会改变指导进程达到下一个等待点。
* API GetRandom()提供随机数([SWS_EM_01308])。如果从工作程序池中使用,则将随机数分配给特定的容器元素,以允许确定性的冗余执行。
为了保证确定行为,确定性用户进程只可使用只有所有可用API的“确定性”子集,包括工作程序运行对象:
* 进程不允许通过自己使用普通POSIX进程创建线程或直接访问任何其他POSIX APIs,以规避包含不确定行为的风险。
* 进程只允许使用所有可用的ara::com机制的“确定性子集”。稍后将提供此类API和机制的详细列表。
* 只有下面的ara::exec接口被使用:DeterministicClient和ExecutionClient。
* 用户进程不允许访问其他ARA接口。
如果使用工作程序池API RunWorkerPool(),处理容器元素的工作程序运行对象,比如计算工作,需要满足某些实现规则来确保数据确定性:
* 运行对象在它运行的时候不允许交换任何信息。比如,它不会访问可运行对象的其他实例可以更改的数据,以避免出现竞争情况。
原理:运行对象可以物理的并行运行或以任何顺序有序运行。不能保证单个工作程序的时间。操作系统单个调度线程。相同数据的同时影响会导致不确定的结果。
* 除了通过从RunWorkerPool()返回对于所有工作程序来说通用的联接,没有锁和同步点(例如,没有信号/互斥量,没有锁/阻塞)。
原理:锁定/阻止使Process运行时不确定。提供了工作程序以增加运行时的利用率。 如果需要同步,则必须从RunWorkerPool()返回。
工作程序池不能用来并行处理多个不同的任务。使用多个可能不同的显式函数(工作程序可运行的对象)可能会增加不必要的复杂性,并可能导致运行时间利用率极不相同,因为每个工作程序有不同的计算时间。这会使资源部署的计划复杂化,这对于黑盒集成来说是必需的。
工作程序池用户的实现例子。比如:工作程序运行对象的例子:
class MyWorker1
: public DeterministicClient::WorkerrunnableBase<myContainer::
value_type, MyWorker1>
{
public:
void worker_runnable(myContainer::value_type& container_element,
DeterministicClient::WorkerThread& t)
{
// Get a unique and deterministic pseudo-random number}
uint64_t random_number = t.GetRandom();
}
};
工作程序线程对象:
class DeterministicClient::WorkerThread
{
// returns a deterministic pseudo-random number}
// which is unique for each container element}
uint64_t GetRandom();
...
};
4 状态管理
4.1 AUTOSAR Adaptive(平台)应用程序的交互
4.1.1 基本的状态管理功能
状态管理通过ara :: com提供了一组“Trigger”和“Notifier”字段。状态管理本质上侦听“Trigger”,并在内部执行特定实现的状态机处理,并在“Notifier”字段(如果有)中提供效果。状态管理还通过它们提供的标准接口与其他FC进行交互。
使用这个机制还可以达到下面的效果:
* 可以要求将功能组设置为专用状态。
* (部分)可以请求取消/激活网络
* 可以请求机器关闭或重启。
* 其他Adaptive(平台)应用程序的行为可能会受到影响
* 可以执行项目特定的动作
其中一些功能至关重要。因此,集成者必须通过IAM管理适当地保护对“Trigger”字段的访问,以免意外更改状态管理的内部状态(并因此而改变相关影响)。
状态管理的内部状态通过其提供的“Notifier”字段通知到系统。对这些字段的读取访问权限不太重要,因此,每当状态管理的内部状态发生更改时,每个Adaptive(平台)应用程序都可以注册其事件以收到通知。
因此,当状态管理的状态发生变化时,每个Adaptive(平台)应用程序都可以执行操作(需要时)。
4.1.2 高级状态管理功能
AUTOSAR Adaptive中的一些用例需要在状态管理所管理的状态中支持同步行为。。比如低功耗模式:一个示例可能是低功耗模式:只有当所有参与此低功耗模式场景的Adaptive(平台)应用程序都最终准备好用于低功耗时,状态管理才能最终切换到低功耗状态(例如,持续存在 其信息)。
因此每个要求支持这种同步工作模式的Adaptive(平台)应用程序必须通过ara::com提供这个字段。状态管理器可以通过FindService找到这个字段,状态管理器可以在以后注册,以便在Adaptive(平台)应用程序执行其操作时收到通知。
Adaptive(平台)应用程序必须完成的三件事,才能支持同步方案:
* Adaptive(平台)应用程序应注册状态管理器的相应“触发”现场事件(在满足状态管理中的条件时通知)
* Adaptive(平台)应用程序应为最终状态做任何准备工作
* Adaptive(平台)应用程序应将先前操作的结果写入其提供的字段
为了使状态管理认识到一个Adaptive(平台)应用程序想要成为同步场景的一部分,Adaptive(平台)应用程序不得不尽可能的提供它的字段服务使非常有必要的。并且当Adaptive(平台)应用程序不想再成为其中的一部分时,它不得不停止提供服务(例如 申请将完成)。
Adaptive(平台)应用程序的接口应该遵循状态管理的“TriggerOut”接口的模式。
5. 参考文档
[1]Explanations of Adaptive Platform Design, AUTOSAR_EXP_PlatformDesign.pdf.
[2]Specification of Execution Management, AUTOSAR_SWS_StateManagement.pdf.