本期会介绍communicationmanagement通信管理,首先介绍它的特点使用方式,然后介绍模型中的相关元素和c++代码中的相关API,最后我们实现一个应用程序,他有两个Executable组成,一个是提供服务的provider,另一个是使用这个服务的consumer。
从一个adaptive application的视角来看,它能利用的接口可以分为三类,第一类是posix 51和c++标准库中的API;第二类是adaptive platform上的可以直接使用的API,如果要用到这类app 那么链接相应的库即可;第三类是要通过communication management即ara::com来间接使用的API。在adaptive platform 上,应用程序应当使用ara::com来通信并使用服务。服务可能是由用户的另一个应用程序提供的,也可能是由adaptive platform提供的。
提到通信,我们知道在物联网/机器人等领域,已经有多种通信API了,如DDS等。为什么在autoser adaptive platform上我们又要制定一个通信API的规范呢? ara::com有什么特点吗?
首先,在API的设计,ara::com与底层通信协议解耦,他对some-ip协议有很好的支持,同时也有使用其他协议的灵活性;其次,他对事件驱动和轮循这两种风格都有良好的支持;另外,ara::com支持静态的和动态的服务 实例选择。在ara::com的设计中还考虑到了端到端保护等安全方面的需求。
ara::com采用经典的Proxy/Skeleton模式,首先用某种IDL(接口描述语言)来描述一个服务,然后从这个描述生成service Skeleton和service Proxy。
Skeleton在服务端使用,它是一个c++抽象类,我们继承这个类,使用它的成员函数,实现它里面的纯虚函数。
Proxy在客户端使用,它是一个普通的c++类,我们组合这个类的对象,使用它的成员函数。
在建模的时候,想让software component和外界通信,我们就在他身上添加port。
主要有两种port:pport和rport,分别用来提供服务和使用服务。在这里P代表provided,R代表required。就像编程语言中的变量有类型一样,port也有类型 称为PortInterface。PortInterface 是抽象的,我们实际上使用一些具体的类型。例如ServiceInterface/PersistencyInterface等等。
PortInterface 就是对服务的接口的定义,不难看出这类元素起的就是IDL(接口描述语言)的作用,PortInterface有namespace,用于避免名字冲突。
接下来我们看一种具体的portantface,也就是servicientface,一个servicentface就定义了一个service,也就是服务,他可以有一些Method/Event/Field, 也就是方法/事件/字段组成。
field在任意时刻都应当有具体的值,所以 显然field必须要有初始值,而event就没有初始值。我们用servicinterface定义了一个服务之后,可以有多个服务实例都提供这个服务。显然,每个服务实例应当具有某种特殊的id,让我们能区分他们,能精确的找到某个服务实例,这个id就是instanceidentify。在各种具体的通信中间件中都有这个概念。例如,some-ip使用一个16位无符号整数来区分服务的实例,而DDS则使用一个字符串来区分服务的实例。在代码中我们将服务实例id的细节封装在ara::com::instanceidentifier 类中,它里面包含的数据基本上就相当于一个字符串,使用的具体中间件不同,这个字符串的格式不同。
在AUTOSRA adaptive Platform的方法论中,应用程序的开发者不应当关心instanceidentifier里的内容。因为使用哪种具体的中间件,是在部署阶段确定的。应用程序的开发者不应当对instanceidentifier的内容和格式做任何假设。应用开发者应当使用ara::com::instanceidentifier这个类,在本期我们很快就会在代码中接触到它。
接下来开始建模,首先对数据类型进行建模,在的Davinci Developer Adaptive中创建一个新项目时,我们可以在选择导入文件的这一步,勾选copy autosar standard types into project。
这样一来,标准的c++基础数据类型就会被导入项目中,可以直接使用。这包括bool, float, double以及一些固定宽度的整数类型。在dashboard中找到data type editor,我们可以在这里对各种数据类型进行建模,例如,array,vector,struct, enum 等等。
创建好需要的数据类型后,就可以创建service interface来描述一个服务。我们想创建如下图的一个服务
接下来创建excuteable, software component和port。对于服务的provider和consumer 我们分别创建以下元素。在本期中我们选择使用some-ip来进行实际的通信。在计算机网络的tcp-ip模型中,它位于传输层之上,它本身使用tcp或udp协议,它的具体内容是autosa标准的一部分,请参阅相关文档。
在microsaradaptive的实现中,每个machine上应当有一个专门的soem-ip守护进程,来负责实现some-ip通信,所以我们需要为此建模。 例如,我们可以手动创建相应的excuteable, software component,process元素。在Davinci Develop Adaptive中,我们也可以在使用application design center时,自动生成与some-ip守护进程相关的这些元素。
接下来对我们machine-design进行建模
在上一集中我们创建machine的时候,就已经创建过我们machine-design,但是还没有在里面做过任何配置,它里面主要包含网络相关的配置信息,这些配置通常是从一些意义架构设计工具里导出的。具体而言,在machine-design中含有EthernetCommunicationConnector, 它描述machine的网络连接情况。例如,它含有指向networkEndPoint的引用,而一个networkEndPoint就表示一个网络地址,例如 ip地址,在networkEndPoint中,我们可以进行ipv4/ipv6地址的配置,以ipv4地址为例 我们可以配置它的地址,子网掩码,获取地址的方法等等。例如 fixed表示固定地址(也就是要手动分配),dhcpv4表示通过dhcp协议获取地址。
networkEndPoint本身并不能独立存在,它被包含在一层EhternetPhysicalChannel中,而一个EhternetPhysicalChannel在逻辑上就表示一个VLan。 接下来我们利用appication design center来完成建模工作,并生成一个c++项目。
首先进入appication design center这里的前三个子界面,Data Types editor, excitabl editor和service editor。因为这部分配置已经完成,所以我们直接进入第四个界面application configuration。
在application configuration中,首先给我们的Adaptive Application起个名字,然后添加其包含的excutable。例如,添加my_provider_executable和my_consumer_executable。
然后配置port之间的连接,在左侧选择服务消费者使用的port,在右侧选择对应的服务提供者使用的port,然后点击中间的箭头按钮 就建立了两个port之间的连接。
接下来在modelbuild中生成模型,在本教程的第一集 hello adaptive world 我们已经介绍过这部分的相关内容,和当时不同的是,这次我们需要勾选generate for some-ip deployment,还需要配置这台们适应的ip地址,子网掩码,多播地址。
在machine properties这里可以配置一些start value,例如 将tcp port的start value设为3500,那么在待会生成的模型里,服务使用的tcb端口号就会是3501,3502等等。
在loging properties这里,可以分别配置服务的provider和consumer在输出日志时使用的appication id。
配置好这些信息后,点击build model按钮,模型就生成好了。
SOME/IP服务接口有3种:Event、Method、Field。其中,Field相当于Method(Get,Set) + Event,具体使用有细微差别。
下面以一个具体的例子进行说明,该例中配置一个PPort接口
InterfaceName : SomeipAdapterServiceInterface
Namespace : ADCU::someipAdapterNamespace
event1: SomeipAdapterEvent1
type : uint16_t
event2: SomeipAdapterEvent2
type: uint16_t
method : int32_t someipServerMethod(int32_t inArrg1, int32_t inAurg)
这段serviceinterfa ce配置会生成一个adcu::someipadapternamespace::skeleton::SomeipAdapterServiceInterfaceSkeleton类。
这个类定义在someipadapterserviceinterface_skeleton.h中,与xml对应生成的接口截图如下:
我们自己在编写AP应用代码实现somei/ip或者ipc通信时,就是要继承这个类,去实现对应的method和event方法即可。
1> method接口说明
因为在skeleton类中,SomeipServerMethod接口是纯虚函数,在继承skeleton类之后,我们需要实现method方法。
2> event接口说明
event方法已经在skeleton类或者skeleton的基类中已经实现,我们不必去重写,只是在需要的地方去调用event方法即可。
2 serviceInterface cpp文件解析
在server端,event方法和method如何使用?
event和method与ROS中的通信方式对比
所以在server端,event方法需要主动调用,一直往外发送;method方法不需要做任何处理,等待client端调用即可
1 ServiceInterface.arxml分析
1> 在swc中配置RPort,引用上面配置的PPort接口
2> 配置一个RequiredSomeipServiceInstance 实例,并且把这个实例mapping到RPort端口
3> 对应生成的代码如下:
2 根据生成的代码接口手写应用代码
1> serviceInterface 头文件解析
2> serviceInterface cpp文件解析
实现头文件相应的函数即可
3> 在client端,event方法和method如何使用?
Note:一个executable中可以配置多个PPort和多个RPort,一个PPort中可以配置多个event和多个method
通过someip方式通信的client与server,在通信时,需要启动someip_deamon.
1.设置多播
sudo route add -net 224.0.0.0 netmask 240.0.0.0 dev etho
sudo ip link set lo multicast on
sudo ip route add ff01::0/16 dev lo
2.关闭json文件完整性校验,因为在调试的时候经常需要更改json文件
export AMSR_DISABLE_INTEGRITY_CHECK=1
3.运行someip daemon
1> someip daemon不需要自己配置模型,编写应用代码去编译,直接从install/opt/someipd_posix拷贝即可
2> 修改someipd_poxis/etc/someip_posix.json文件
{
"applications" : [
"../SomeipClientExec/etc/someip_config.json",
"../SomeipServerExec/etc/someip_config.json"
]
}
3> ./bin/someipx_posix -c ./etc/someipd_posix.json
4.运行someip_server
1> 修改./etc/someip_config,将这个文件中所有的ip地址修改为本机ip地址
2> 运行程序
./bin/SomeipServer
3> 修改logging_config.json,其中有这么一个字段
"file" : {
"file-path": "./log/log_server.txt"
}
这时候一定要在etc bin的同级创建log目录,不然它自己是不会创建log目录的,跑起来就会报错
5.运行someip_client
1> 修改./etc/someip_config,将这个文件中所有的ip地址修改为本机ip地址
2> 运行程序
./bin/SomeipClient
区别一:IPC Binding主要用于ECU内进程间的数据通信,SOME/IP 主要用于多个ECU之间通信,也可以用于ECU之间通信
区别二:使用IPC Binding,数据传输不用通过SOMEIP daemon,SOMEIP 下必须起SOMEIP daemon应用才能实现数据交互。PS: IPC Binding虽然可以不用通过daemon进行通信,但是其传输协议仍然复用了someip protocal
在同一ECU之间通信,理论上IPC Binding数据传输会比SOME/IPBinding快,因为数据不用经过daemon,而是在server-client之间直接通信。