vsomeip与commonAPI的移植与应用

vsomeip与CommonAPI的移植与应用

1. vsomeip 交叉编译与安装

1.1 说明

vsomeip是一个SOME/IP开源协议栈。如图1所示,boost库为vsomeip的必须依赖项,因此编译安装vsomeip的前提是将boost依赖库交叉编译完成。

图1 vsomeip编译安装步骤

1.2 安装版本与资源下载

本次编译安装的软件版本是boost1.74和vsomeip3.1.20。如图2所示。

boost库下载地址

图2 下载boost库

进入boost压缩包放置文件夹,使用以下指令解压boost压缩包

1
tar -zxvf ./boost1_74_0.tar.gz

打开终端,输入以下指令下载vsomeip源文件

1
git clone https://github.com/GENIVI/vsomeip.git

1.3 交叉编译boost库

运行 sh ./bootstrap.sh,程序将在boost文件夹生成b2可执行程序和配置文件project-config.jam

image-20230414144307974

图3 在终端中运行bootstrap.sh

然后编辑project-config.jam,修改地方有两处,第一处为将工具链设置为S32G处理器的arm64编译工具链;第二处为简化安装,只编译vsomeip所依赖的库,log,thread和system。

tips:请注意空格

1
2
3
4
5
6
7
8
9
10
11
12
13
# @file :project-config.jam
# ......
if ! gcc in [ feature.values <toolset> ]
{
#using gcc ;
# 修改处1:使用S32G的arm-g++编译器
# 注意空格
using gcc : arm : <your dir>/gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-g++ ;
}
# ......
# 修改处2:只编译vsomeip需要用到的库,log thread system
libraries = --with-log --with-thread --with-system ;
# ......

运行b2程序

1
2
3
4
./b2 abi=aapcs toolset=gcc-arm
# 最后将boost库安装到你的目标文件夹
mkdir install
./b2 install --prefix=./install

安装完成后,install文件夹将包含include与lib两个文件夹。

使用file指令可以查看是否正确编译为arm架构boost库

1.4 交叉编译vsomeip库

修改CMakeLists.txt,如下所示,也可以将boost库的安装路径添加到环境变量PATH中。

1
2
3
4
5
6
7
8
9
# @file: CMakeList.txt

# step1 指定boost路径
# 修改 cmakelists.txt

# Boost
# 指定 ARM libboost库路径
# <PackageName>_ROOT 优先在当前路径下找
set(BOOST_ROOT <your libboost_1.74_installed dir>)

然后将工具链设置为S32G处理器的arm64编译工具链,可以在终端中输入如下指令,临时修改系统默认编译器,这种方法在关闭终端后,需要重新设置。用户也可以修改.bashrc文件来永久将S32G编译器设置为系统默认编译器。使用指令sudo vim ~/.bashrc,然后在bashrc中输入上述指令。

1
2
3
4
# step2:修改编译器
# 在 terminal
export CC=<your dir>/gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc
export CXX=<your dir>/gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-g++

cmake编译,编译安装完成后,install文件夹包含 etc, include与lib三个文件夹。

1
2
3
4
5
6
# step3 cmake
mkdir build install
cd ./build
cmake -DENABLE_SIGNAL_HANDLING=1 -DDIAGNOSIS_ADDRESS=0x10 -DCMAKE_INSTALL_PREFIX=./install ..
# step4 make
make

编译测试例程并测试

1
2
3
4
5
6
7
# 在helloworld文件夹
make

# 可以通过 file 指令 查看 可执行文件是否为 arm 架构
file ./hello_world_service
# 结果:
./hello_world_service: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, with debug_info, not stripped

1.5 S32G开发板例程演示

将文件导入开发板有两种方式,用户选择其中之一即可,如图4所示。(scp命令亦可)

图4 传输文件的两种方式

通过filezilla传输文件时,连接网线后,输入开发板的IP地址和登录用户名,点击下方的连接即可,如图5。需要注意的是,开发板IP地址和PC的IP地址需要在同一网段。与此同时,建议将需传输文件打包成压缩包,否则可能会出现程序动态链接库强制转换为静态库问题。

图5 filezilla连接设置

将boost和vsomeip中的库文件添加到系统 /usr/lib下,然后分终端同时打开hello_world_service和hello_world_client,运行结果如图6所示。

image-20230414134538447

图6 例程运行结果

2. vsomeip跨设备通信

2.1 说明

vsomeip的通信过程如图7所示。在上一节的例程中,服务端与客户端位于同一台主机上,两者的通信过程即图中的左半部分,底层为通过socket完成的进程间通信。本节将简述使用vsomeip进行两台嵌入式linux设备之间的通信例程,演示真正意义上的SOME/IP通信。

image-20230414134804582

图7 vsomeip通信过程

本次使用的两台嵌入式设备分别为NXP S32G开发板以及基于NXP iMX8芯片的飞凌嵌入式开发板。S32G作为客户端,iMX8作为服务端。使用一台TP-Link路由器将两者连接,再使用一根网线连接路由器至电脑,如图8所示。

image-20230414134854586

图8 设备连接图

2.2 IP设置与通信测试

将各设备分别设置为以下IP地址:

  • 笔记本电脑:192.168.100.50

  • S32G开发板:192.168.100.100

  • iMX8开发板:192.168.100.60

IP地址可通过ifconfig设置。硬件连接与IP地址设置完成后,使用ping和arping指令进行通信测试,然后使用ssh指令分别远程登录2台嵌入式linux设备。

2.3 vsomeip通信参数设置

服务端与客户端分别需要配置一个json文件。以iMX8服务端为例,service.json文件如下。其中192.168.100.50为当前嵌入式设备的IP地址。

“name” : “World”:名称需要与VSOMEIP_APPLICATION_NAME读入的名称一致,注意不是程序文件名称。

“service” : “0x1234”, “instance” : “0x5678” :需要与程序中的地址对应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{
"unicast" : "192.168.100.50",
"logging" :
{
"level" : "debug",
"console" : "true",
"file" : { "enable" : "false", "path" : "/tmp/vsomeip.log" },
"dlt" : "false"
},
"applications" :
[
{
"name" : "World",
"id" : "0x1212"
}
],
"services" :
[
{
"service" : "0x1234",
"instance" : "0x5678",
"unreliable" : "30509"
}
],
"routing" : "World",
"service-discovery" :
{
"enable" : "true",
"multicast" : "224.224.224.245",
"port" : "30490",
"protocol" : "udp",
"initial_delay_min" : "10",
"initial_delay_max" : "100",
"repetitions_base_delay" : "200",
"repetitions_max" : "3",
"ttl" : "3",
"cyclic_offer_delay" : "2000",
"request_response_delay" : "1500"
}
}

为了方便运行,可以参考如下写一个快速运行的脚本。touch run_service.sh

1
2
3
4
5
6
7
# @file: run_service.sh
# run vsomeip service
# set multicast
sudo route add -nv 224.224.224.245 dev enxd03745a6a072
export VSOMEIP_CONFIGURATION=./service.json
export VSOMEIP_APPLICATION_NAME=World
./response-sample

其中,第一行的作用为设置多播地址,enxd03745a6a072为设备网卡名称,通过ifconfig查看,并做对应修改。

2.4 实例演示

在服务端终端中输入 sudo sh ./run_service.sh启动vsomeip服务端程序。类似的在另一个设备中启动客户端程序。需要传输的数据自行在程序中设定,本程序运行结果如下。

image-20230414135716470

图9 程序运行结果

3. commonAPI交叉编译与安装

3.1 说明

CommonAPI是一个开源的RPC(远程过程调用)框架,其封装了底层通信协议,使得上层应用接口与底层通信协议解耦,具体原理可参考官方文档。目前底层可以支持SOME/IP和D-Bus两种协议,本文进行底层为SOME/IP协议的CommonAPI编译安装与演示。

整体编译安装过程如图10所示,首先通过官方提供的代码生成器commonapi_core_generator和commonapi_someip_generator将FDL接口描述文件转换为C++文件,此步在X86_64平台上完成;然后将boost、vsomeip、capicxx-core-runtime以及capicxx-someip-runtime交叉编译至ARM64平台上;最后借助ARM交叉编译工具链,即可编译出可在S32G开发板上运行的程序。

image-20230414135941356

图10 交叉编译安装CommonAPI流程

3.2 安装版本与资源下载

时间 2021-08-25
安装软件版本 capicxx-core-runtime 版本 3.2.0
capicxx-someip-runtime 版本 3.2.0
commonapi_core_generator 版本 3.2.0.1
commonapi_someip_generator 版本 3.2.0.1
vsomeip 3.1.20.3

下载地址:

3.3 交叉编译capicxx-core-runtime

首先需要设置交叉编译工具链,如下。

1
2
export CC=<S32G编译器目录>/gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc
export CXX=<S32G编译器目录>/gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-g++

然后进行构建,如下。

1
2
3
4
5
mkdir build arm64_core_install
cd build
cmake -DCMAKE_INSTALL_PREFIX=../arm64_core_install ..
make
sudo make install

编译完成后,使用tree指令查看arm64_core_install文件下,如下。

图11 install目录

3.4 交叉编译capicxx-someip-runtime

capicxx-someip-runtime依赖于boost库与vsomeip库,因此需要修改CMakeLists.txt文件,使之可以顺利找到交叉编译完成的boost和vsomeip库。参照1.3节及1.4节。

然后进行构建,如下。

1
2
3
4
5
mkdir build arm64_someip_install
cd build
cmake -DCMAKE_INSTALL_PREFIX=../arm64_someip_install ..
make
sudo make install

3.5 安装Java环境

commonapi_core_generator和commonapi_someip_generator代码生成器依赖于JAVA8环境,因此首先需要安装jdk,在终端中输入下述指令。

1
sudo apt install openjdk-8-jdk

使用 java -version可以查看当前安装的java版本,若已安装高版本java,请降级,高版本java会导致代码生成出错。仅针对目前安装版本

3.6 S32G开发板例程演示

本例程为helloworld演示。建立helloworld例程目录如下,将代码生成器commonapi_core_generatorcommonapi_someip_generator放置在cgen文件夹下,新建HelloWorld.fidlHelloWorld.fdel文件并放置于fidl文件,src-gen文件夹在运行代码生成器后自动生成,相关信息参考图12。

图12 helloworld例程目录释义

  • FDL文件代码生成
1
2
3
4
5
6
7
8
9
10
11
12
13
# @file: HelloWorld.fidl
package commonapi
interface HelloWorld {
version {major 1 minor 0}
method sayHello {
in {
String name
}
out {
String message
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# @file: HelloWorld.fdel
import "platform:/plugin/org.genivi.commonapi.someip/deployment/CommonAPI-SOMEIP_deployment_spec.fdepl"
import "HelloWorld.fidl"
define org.genivi.commonapi.someip.deployment for interface commonapi.HelloWorld {
SomeIpServiceID = 4660
method sayHello {
SomeIpMethodID = 123
}
}
# 官方例程有误,按照如下修改
define org.genivi.commonapi.someip.deployment for provider as MyService {
instance commonapi.HelloWorld {
InstanceId = "test"
SomeIpInstanceID = 22136
}
}

代码生成器commonapi_core_generator目录如图13所示,可根据平台选择对应的生成器,64位ubuntu选择commonapi-core-generator-linux-x86_64。然后运行如下指令,生成C++代码。

1
2
./commonapi_core_generator/commonapi-core-generator-linux-x86_64 -sk ./fidl/HelloWorld.fidl
./commonapi_someip_generator/commonapi-someip-generator-linux-x86_64 ./fidl/HelloWorld.fdepl

图13 commonapi_core_generator目录

  • 指定交叉编译工具链

修改CMakeLists.txt,如图14所示。

image-20230414144023413

图14 helloworld CMakeLists.txt文件

修改arm_linux_setup.cmake,ARM_DIR等针对修改。

图15 helloworld arm_linux_setup.cmake文件

  • 可执行文件生成及运行

安装如下指令构建可执行文件

1
2
3
4
5
cd build
cmake -DCMAKE_TOOLCHAIN_FILE=../arm_linux_setup.cmake ..
#若上述指令提示无法找到boost库,可换用export C和CXX方式,然后再# 执行cmake
cmake..
make

可使用file指令查看可执行文件类型信息

将文件传输至开发板中,并将编译好的库文件添加到开发板 /usr/lib文件夹中,然后分终端同时打开HelloWorldService和HelloWorldClient,运行结果如图14所示

image-20230414142807201

图14 helloworld运行结果

4. 附录1—问题及解决方案

本节将记录在vsomeip和commonAPI的安装编译过程中,碰到的一些问题及对应的解决方案,仅供参考。

  • 可执行文件生成及运行X86-64环境下安装libboost库时的boost版本问题

vsomeip官方安装文档过旧,ubuntu20.04源中boost版本已经更新,不能直接使用文档中的指令。解决方案有两个,其一是在boost库中下载对应版本,编译运行;其二则是在终端中使用 sudo apt-cache search libboost 查找新版本并安装。

  • 在终端中 “ctrl+c” 停止vsomeip程序后,再次启动程序,提示vsomeip无法初始化成功

原因是在停止程序后,/tmp/vsomeip.lck没有被正确删除。解决方案有以下几种,其一是手动删除与/tmp文件下与vsomeip相关的内容;其二是关闭shell,在重新打开;最后是在安装vsomeip时,cmake阶段使能ENABLE_SIGNAL_HANDLING,这一步在前述安装指南中已默认操作。

  • 启动vsomeip,程序报 configuration module could not be load 错误

其原因是新安装vsomeip后 ,没有在动态链接库配置目录中创建连接及缓存文件,可以使用 sudo ldconfig 命令刷新库缓存。

  • CMakeList中 find_package() 提示找不到vsomeip包

    其原因是vsomeip3.x版本后,包名变更为vsomeip3,因此可使用 find_package(vsomeip3 REQUIRED)

  • CommonAPI中,使用代码生成器无法将FDL文件转换为C++代码,或者转换后,工程无法编译通过

其原因是CommonAPI C++代码生成器与CommonAPI runtime的版本不匹配,此原因非常隐蔽,程序不会给出任何关于此方面的提示。解决方案是不要在GENIVI官网下载,去github上均下载最新版本。

  • CommonAPI官方例程中,FDL接口描述文件出错,无法顺利生成C++代码

原因是例程FDL文件中语法有误,按照前述文档中红色标记代码修改。

  • CommonAPI Java版本问题

目前CommonAPI代码生成器只支持java8环境,若安装java高版本,则需要手动降级,相关教程可参考:

请点击

  • CommonAPI 直接运行例程,无法通过cmake生成makefile

CMakeLists.txt附加了需要D-Bus相关内容,而系统中未安装,可以对应修改删减CMakeLists.txt,也可参考如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# @file: example_E2,Attributes,CMakeLists
cmake_minimum_required(VERSION 2.8)
set(PRJ_NAME E02Attributes)
project(PRJ_NAME)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++0x")
include_directories(
src-gen
/usr/local/include/CommonAPI-3.2
/usr/local/include/vsomeip
${COMMONAPI_INCLUDE_DIRS}
${COMMONAPI_SOMEIP_INCLUDE_DIRS}
${DBus_INCLUDE_DIRS}
${VSOMEIP_INCLUDE_DIRS}
)
link_directories(
/usr/local/lib
)
# 找包
find_package(CommonAPI 3.2.0 REQUIRED CONFIG NO_CMAKE_PACKAGE_REGISTRY)
find_package(CommonAPI-SomeIP 3.2.0 REQUIRED)
find_package(vsomeip3 3.1.0 REQUIRED)
# Source Files
set(PRJ_SRC_PATH src)
set(PRJ_SRC_GEN_PATH src-gen)
set(PRJ_SRC_GEN_COMMONAPI_PATH ${PRJ_SRC_GEN_PATH}/v1/commonapi/examples)
set(PRJ_NAME_CLIENT ${PRJ_NAME}Client)
set(PRJ_NAME_SERVICE ${PRJ_NAME}Service)
# Application
FILE(GLOB PRJ_PROXY_GEN_SRCS ${PRJ_SRC_GEN_COMMONAPI_PATH}/*Proxy.cpp)
FILE(GLOB PRJ_STUB_GEN_SRCS ${PRJ_SRC_GEN_COMMONAPI_PATH}/*Stub*.cpp)
FILE(GLOB PRJ_STUB_IMPL_SRCS ${PRJ_SRC_PATH}/*Stub*.cpp)
set(PRJ_CLIENT_SRCS ${PRJ_SRC_PATH}/${PRJ_NAME_CLIENT}.cpp ${PRJ_PROXY_GEN_SRCS})
set(PRJ_SERVICE_SRCS ${PRJ_SRC_PATH}/${PRJ_NAME_SERVICE}.cpp ${PRJ_SRC_PATH}/${PRJ_NAME}StubImpl.cpp ${PRJ_STUB_GEN_SRCS} ${PRJ_STUB_IMPL_SRCS})
# 链接库名
set(LINK_LIBRARIES CommonAPI CommonAPI-SomeIP vsomeip3)
# Build Client
add_executable(${PRJ_NAME_CLIENT} ${PRJ_CLIENT_SRCS})
target_link_libraries(${PRJ_NAME_CLIENT} ${LINK_LIBRARIES})
# Build service
add_executable(${PRJ_NAME_SERVICE} ${PRJ_SERVICE_SRCS})
target_link_libraries(${PRJ_NAME_SERVICE} ${LINK_LIBRARIES})
  • 可执行文件传输问题

如前所述,可执行文件通过filezilla直接传输文件时,会不明原因的将动态链接库转换为静态链接库,运行时提示“格式错误”。使用file指令查看时,可发现如下差异。

1
2
3
4
# A 使用压缩包传输
ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=0c126f1b17601d33513b52c02a5e63358d912512, for GNU/Linux 3.14.0, with debug_info, not stripped
# B 直接传输
ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, interpreter *empty*, stripped

解决方案则是使用压缩包传输或者直接通过TF卡拷贝。

  • 部分网络经验

在编写fdepl文件时,要先写attribute,在写method,再写broadcast,不能像fidl文件,穿插着写,否则编译不过。

在运行程序时,有时候会遇到无法连接的问题,需要把 /tmp/vsomeip-0 这一系列文件删除,否则无法连接。

5. 附录2—vsomeip与commonAPI的对比

commonAPI vsomeip
基本描述 基于vsomeip(可选)实现的RPC框架(远程过程调用)
CommonAPI C++是用于开发分布式应用程序的标准C++ API规范,该分布式应用程序通过中间件进行进程间通信
实现了SOME/IP协议栈,构建了SOME/IP的共享库(libvsomeip3.so)
依赖项 多,以vsomeip底层为例:
1. vsomeip
2. commonAPI runtime
3. commonAPI-SomeIP runtime
4. C++代码生成工具

boost
接口描述语言 需要
FrancalDL接口描述语言
不需要
优劣势对比 优点:
1. 接口使用IDL描述,基本通信不需要联调,主要开发工作是实现服务的接口,相当于填充业务逻辑,工作量小
2. 封装通信协议与中间件,使得应用程序的C++接口独立于底层IPC,也就是IP Common API允许针对开发的应用程序(即使用C++的客户端和服务器)可以与不同的IPC后端链接(someip,或D-Bus),而无需更改应用程序代码
3. 屏蔽了网络通信的实现,让客户端可以像调用本地接口一样调用服务端提供的接口
缺点:
依赖项多,自由度低
优点:
依赖项少,自由度高
劣势:
1. 没有实现数据结构的序列化,仅仅涵盖了一些IP协议与服务发现,即Payload的打包与解析需要自己写,传递消息内容为复杂数据结构时,打包、解析和联调麻烦,且工作量大
2. 对于AUTOSAR系统,Payload要遵循AUTOSAR规范进行序列化,对于非AUTOSAR系统,可以遵循AUTOSAR规范进行序列化,也可以采用其他序列化方式如常用的Google Protocol Buffer、JSON等

6. 附录3—如何写fidl和fdepl文件

主要参考Franca语法以及官方提供的7个例程

  • 写fidl和fdepl文件的最好方法是下载一个 Eclipse,并使用它的自动配置功能,它将插入所有必须的元素,剩下的自己填一下即可。
  • 方法标识符:SomeIpMethodID,SomeIpGetterID,SomeIpSetterID 必须唯一,且在1~32767之间。
  • 事件标识符:SomeIpEventID,SomeIpNotifierID,唯一,且在 32769~65534之间。
  • SomeIpEventGroups 至少为1

vsomeip与commonAPI的移植与应用
http://ziyangfu.github.io/2023/04/14/vsomeip与commonAPI的移植与应用/
作者
FZY
发布于
2023年4月14日
许可协议