(三) 搞定SOME/IP通信之CommonAPI库

本章主要介绍在SOME/IP通信过程中的另外一个IPC通信利剑,CommonAPI库,文章将从如下几个角度让读者了解什么是CommonAPI, 以及库在实际工作中的作用

文中资源:vsomeip+commonapi+指导文档与demo源码

SOME/IP通信之CommonAPI

  • CommonAPI库是什么
  • CommonAPI库的编译
  • 写个Demo实战一下

CommonAPI库是什么

CommonAPI是GENIVI组织开发的一个基于C++的应用API库,没错,跟vsomeip协议栈是一个爹。其主要提供给使用通讯中间件传输数据的分布式应用来操作通讯中间件的接口。主要的作用是使使用CommonAPI进行IPC通信的的应用能够隔离底层协议栈的差异。比如使用CommonAPI时,我们的底层协议栈可以是vsomeip,也可以是DBUS等. 他的架构如下:
在这里插入图片描述
从图中可以看到,CommonAPI C++ 框架分为两个主要部分:

CommonAPI Core: 这部分与中间件无关,包含了跨不同中间件技术共享的基本 API、类和组件。它提供了构建应用程序所需的基本通信机制和抽象,而不与特定的中间件协议绑定。

CommonAPI Binding: 这部分与特定中间件相关,并包含了每种支持的中间件协议的实现细节。

通过将Core与Binding分开,CommonAPI C++ 允许开发人员使用与中间件无关的核心编写他们的应用程序逻辑,使其代码更具可移植性,不会紧密绑定到特定的中间件。然后,他们可以选择特定的绑定,将他们的应用程序适配到所需的中间件技术,而无需重写整个应用程序逻辑。

这种关注点分离使得开发更容易,可以在具有不同中间件要求的各种汽车系统中部署应用程序。它通过在中间件无关逻辑和特定中间件实现细节之间提供明确的分离,促进了可重用性和模块化。

图右边的四个框架,分别代表了Core层的代码生成工具,以及接口描述语言(fidl), Binding层的代码生成工具,以及接口描述语言(fdepl),即:

Code Generator Tools  <-->   *.fidl
Code Generator Binding Tools <--> *.fdepl

关于fidl跟fdepl文件是什么,如果你做过android开发,那么对aidl文件以及hidl文件会比较熟悉,这个fidl跟fdepl文件也是跟aidl&hidl的作用类似,我们将通信行为的接口以及参数定义在文件中,通过CommonAPI提供的Code Generators,即可将这些文件转成对应的C++接口。上层在在进行IPC接口时,只需要调用这些C++中的接口即可

CommonAPI库的编译

因为我们是基于android系统来使用CommonAPI通信,故使用ndk+gradle来编译库

编译环境

操作系统:win11
Gradle: 7.3.3
cmake版本:3.18.1

下载源码
这里包含两部分,一个是CommonAPI Core,一个是CommonAPI Binding分别对应如下两个库:

capicxx-core-runtime
capicxx-someip-runtime

下载完成后,将文件夹拷贝到上一篇文章提供的附件工程中的external目录下面,放好后如下所示:
在这里插入图片描述

修改CMakeList
然后我们需要修改工程根目录下的CMakeLists.txt文件, 注意是工程根目录,不是模块根目录,修改后内容如下所示:

cmake_minimum_required(VERSION 3.10)

project(SOMEIP)

list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/output/${ANDROID_ABI})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/output/${ANDROID_ABI})
set(FETCHCONTENT_SOURCE_DIR_BOOST ${CMAKE_CURRENT_SOURCE_DIR}/external/boost_1_71_0)
add_subdirectory(external/boost-cmake)
add_subdirectory(external/vsomeip)
add_subdirectory(external/capicxx-core-runtime-master)
add_subdirectory(external/capicxx-someip-runtime-master)
add_subdirectory(app/src/main/cpp)

external/capicxx-core-runtime-master/CMakeLists.txt

#添加编译标记,不然会编译报错(-Wno-ignored-attributes -Wno-deprecated-declarations)
IF(MSVC)
    message("using MSVC Compiler")
    add_definitions(-DCOMMONAPI_INTERNAL_COMPILATION -DCOMMONAPI_DLL_COMPILATION)
    add_compile_options(/EHsc /wd4996)
ELSE ()
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wextra -Wformat -Wformat-security -Wconversion -fexceptions -fstrict-aliasing -fstack-protector -fasynchronous-unwind-tables -fno-omit-frame-pointer -Werror -DCOMMONAPI_INTERNAL_COMPILATION -fvisibility=hidden -Wno-ignored-attributes -Wno-deprecated-declarations")
ENDIF(MSVC)

external/capicxx-core-runtime-master/CMakeLists.txt

if ("${USE_INSTALLED_COMMONAPI}" STREQUAL "ON")
    FIND_PACKAGE(CommonAPI 3.2.0 REQUIRED CONFIG NO_CMAKE_PACKAGE_REGISTRY)
else()
# 注释掉REQUIRED
#    FIND_PACKAGE(CommonAPI 3.2.0 REQUIRED CONFIG NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH)
	FIND_PACKAGE(CommonAPI 3.2.0)
endif()

# maintainer-clean target去掉
if(NOT ANDROID)
add_custom_target(maintainer-clean COMMAND rm -rf *)
endif()

在根目录的cmake文件夹下再按照cmake的find_package规则添加两个文件,FindCommonAPI.cmake, 跟FindCommonAPI-SomeIP.cmake 两个文件

然后启动编译,就可以在工程根目录的output目录下看到库基本已经全部输出了
在这里插入图片描述

写个Demo实战一下

上面库编译完成了,书接上回的vsomeip demo,我们需要将someip_server跟someip_client这两个库改造以下,不直接使用vsomeip协议栈的API, 改为CommonAPI的方式来实现。

在写代码之前,咱们来回顾一下之前使用vsomeip协议栈API写的例子,我们定义了一个天气服务,然后服务中提供了一个获取温度的方法,参数如下:

//服务ID
static vsomeip::service_t  weather_service_id = 0x1001;
//服务实例ID
static vsomeip::instance_t weather_service_instance_id = 0x0001;
//方法ID
static vsomeip::method_t   weather_get_temp_method_id = 0x0001;

这里我们把它改造成CommonAPI的方式,首先在cpp目录下创建如下两个文件,并填入内容:

IWeatherService.fidl

package com.commapi.test

interface IWeatherService {
    version { major 0 minor 1 }

    method getTemp {
        out {
            Int32 temp
        }
    }
}

IWeatherService.fdepl

import "platform:/plugin/org.genivi.commonapi.someip/deployment/CommonAPI-4-SOMEIP_deployment_spec.fdepl"
import "IWeatherService.fidl"

define org.genivi.commonapi.someip.deployment for interface com.commapi.test.IWeatherService {
    SomeIpServiceID = 4097 //0x1001

    method getTemp {
        SomeIpMethodID = 1 //0x0001
        SomeIpReliable = true
    }
}

define org.genivi.commonapi.someip.deployment for provider as Service {
    instance com.commapi.test.IWeatherService {
        InstanceId = "com.commapi.test.IWeatherService"
        SomeIpInstanceID = 1 //0x0001
    }
}

我们这里只展示使用方式,fidl&fdepl的语法,我们后面会专门起文章来介绍。

写好后,需要下载如下两个工具:
commonapi_core_generator-3.2.0
commonapi_someip_generator-3.2.0.1

下载完成后,解压到执行的目录, 然后启动cmd窗口,跳转到我们的fidl & fdepl文件所在目录,然后输入如下命令,即可:

commonapi-core-generator-windows-x86_64.exe -sk  *.fidl
commonapi-someip-generator-windows-x86_64.exe  *.fdepl

在这里插入图片描述
这里我们会发现我们的fidl文件夹下多了自动生成了一个src-gen的文件夹,内容如下:
在这里插入图片描述
接下来改造server端跟client端,第一步先把cpp跟.h文件加入到编译源文件中, 修改cpp目录下的CMakeLists.txt如下:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.18.1)

# Declares and names the project.
project("someip")

find_package (vsomeip3 3.3.8 REQUIRED)
find_library(log-lib log)
include_directories(${VSOMEIP3_INCLUDE_DIRS})

# CommonAPI
find_package(CommonAPI 3.2.0 REQUIRED)
include_directories(${COMMONAPI_INCLUDE_DIRS})

# CommonAPI-SomeIP
find_package(CommonAPI-SomeIP 3.2.0 REQUIRED)
include_directories(${CommonAPI-SomeIP_INCLUDE_DIRS})

# SRC-GEN
include_directories(fidl/src-gen)
file(GLOB SRC-GEN fidl/src-gen/v0/com/commapi/test/*.cpp)

add_executable(someip_server
        # Provides a relative path to your source file(s).
        someip_server.cpp
        ${SRC-GEN})


target_link_libraries(someip_server ${log-lib} vsomeip3 vsomeip3-e2e vsomeip3-sd CommonAPI CommonAPI-SomeIP)

add_executable(someip_client
        # Provides a relative path to your source file(s).
        someip_client.cpp
        ${SRC-GEN})

target_link_libraries(someip_client ${log-lib} vsomeip3 vsomeip3-e2e vsomeip3-sd CommonAPI CommonAPI-SomeIP)

编译文件修改完成后,再将我们之前的vsomeip的两个端代码修改为CommonAPI方式,对应两个文件如下:

服务端实现:someip_server.cpp

#include <string>
#include <csignal>
#include <unistd.h>
#include "CommonAPI/CommonAPI.hpp"
#include "v0/com/commapi/test/IWeatherServiceStubDefault.hpp"

using namespace v0::com::commapi::test;

class WeatherServiceStub : public IWeatherServiceStubDefault {
private:
    int temp = -32;
public:
    WeatherServiceStub() = default;
     ~WeatherServiceStub() override = default;
    void getTemp(const std::shared_ptr<CommonAPI::ClientId> _client, getTempReply_t _reply) override{
        printf("%s : gid[%d], uid[%d]\n", __func__ , _client->getGid(), _client->getUid());
        _reply(temp++);
    }
};

int main(int args, char** argc){
    //设置配置文件路径
    setenv("COMMONAPI_CONFIG", "vendor/etc/commonapi.ini",1);
    setenv("VSOMEIP_CONFIGURATION", "/vendor/etc/local_server.json", 1);
    auto runtime = CommonAPI::Runtime::get();
    auto weather_service = std::make_shared<WeatherServiceStub>();
    auto server_register = runtime->registerService("local",
                                           "com.commapi.test.IWeatherService",
                                           weather_service,
                                           "someip_server");
    printf("register weather service : %d\n", server_register);
    while(true){
        usleep(1000 * 1000);
    }
    return 1;
}

客户端实现:someip_client.cpp

#include <string>
#include <csignal>
#include <unistd.h>
#include "thread"
#include "CommonAPI/CommonAPI.hpp"
#include "v0/com/commapi/test/IWeatherService.hpp"
#include "v0/com/commapi/test/IWeatherServiceProxy.hpp"

using namespace v0::com::commapi::test;

int main(int args, char** argc){
    setenv("COMMONAPI_CONFIG", "vendor/etc/commonapi.ini",1);
    setenv("VSOMEIP_CONFIGURATION", "/vendor/etc/local_client.json", 1);
    auto runtime = CommonAPI::Runtime::get();
    auto app = runtime->buildProxy<IWeatherServiceProxy>("local",
                                                         "com.commapi.test.IWeatherService",
                                                         "someip_client");
    while(true){
        if(!app->isAvailable()){
            printf("connecting failed, retry\n");
            sleep(1);
            continue;
        }
        break;
    }
    app->getProxyStatusEvent().subscribe([&app](CommonAPI::AvailabilityStatus status){
        if(status == CommonAPI::AvailabilityStatus::AVAILABLE){
            printf("service available\n");
            std::thread t1([&app]{
                for(int i=0; i<10; i++){
                    CommonAPI::CallStatus callStatus;
                    int32_t temp;
                    CommonAPI::CallInfo callInfo;
                    app->getTemp(callStatus, temp, &callInfo);
                    printf("CallStatus = %d, getTemp: %d\n",callStatus, temp);
                }
            });
            t1.detach();
        }
    });

    while(true){
        usleep(1000 * 1000);
    }
    return 1;
}

我这里为了方便仅测试单机模式(双机模式一样的,只是配置不一样)
修改服务端配置local_server.json

{
    "unicast":"127.0.0.1",
    "logging":
    {
        "level":"debug",
        "console":"true"
    },

    "applications":
    [
        {
            "name":"someip_server",
            "id":"0x1000"
        }
    ],
    "services" :
    [
        {
            "service" : "0x1001",
            "instance" : "0x0001",
            "reliable" : { "port" : "30509", "enable-magic-cookies" : "false" }
        }
    ],
    "routing":"someip_server",
    "service-discovery" :
    {
        "enable" : "true",
        "multicast" : "239.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"
    }
}

修改客户端配置:local_client.json

{
    "unicast":"127.0.0.1",
    "netmask" : "255.255.255.0",
    "logging":
    {
        "level":"debug",
        "console":"true"
    },

    "applications":
    [
        {
            "name":"someip_client",
            "id":"0x1001"
        }
    ],
    "clients" :
    [
        {
            "service" : "0x1001",
            "instance" : "0x0001",
            "reliable" : [ "41234" ]
        }
    ],
    "service-discovery" :
    {
        "enable" : "false"
    }
}

然后按照上一篇vsomeip的方式,把库跟文件分别push到系统对应的目录,执行后,即可看到通信正常执行了,如下图。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/78889.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java虚拟机(JVM):堆溢出

一、概念 Java堆溢出&#xff08;Java Heap Overflow&#xff09;是指在Java程序中&#xff0c;当创建对象时&#xff0c;无法分配足够的内存空间来存储对象&#xff0c;导致堆内存溢出的情况。 Java堆是Java虚拟机中用于存储对象的一块内存区域。当程序创建对象时&#xff0c…

设计模式之简单工厂模式

一、概述 定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。工厂模式使一个类的实例化延迟到其子类。 简单工厂模式&#xff1a;又叫做静态工厂方法模式&#xff0c;是由一个工厂对象决定创建出哪一种产品类的实例。 二、适用性 1.当一个类不知道它所必须…

MySQL 账号权限

mysql 在安装好后&#xff0c;默认是没有远端管理账号。 一、账号管理 1. 查看账号列表 MySQL用户账号和信息存储在名为 mysql 的数据库中。一般不需要直接访问 mysql 数据库和表&#xff0c;但有时需要直接访问。例如&#xff0c;查看数据库所有用户账号列表时。 USE mysql; …

Matplotlib数据可视化(二)

目录 1.rc参数设置 1.1 lines.linestype取值 1.2 lines.marker参数的取值 1.3 绘图中文预设 1.4 示例 1.4.1 示例1 1.4.2 示例2 1.rc参数设置 利用matplotlib绘图时为了让绘制出的图形更加好看&#xff0c;需要对参数进行设置rc参数设置。可以通过以下代码查看matplotli…

揭秘!体育比赛是如何快人一步购票的

最近&#xff0c;各类体育赛事正如火如荼的进行中&#xff0c;作为资深体育迷&#xff0c;看着赛场上的英雄们正在为荣誉和胜利而拼搏&#xff0c;内心也跟着激情澎湃起来。 为了享受精彩纷呈的赛事&#xff0c;越来越多体育迷选择亲临现场&#xff0c;感受更真实的比赛氛围&a…

VR仿真实训系统编辑平台赋予老师更多自由和灵活性

为了降低院校教师在VR虚拟现实方面应用的门槛&#xff0c;VR公司深圳华锐视点融合多年的VR虚拟仿真实训系统制作经验&#xff0c;制作了VR动物课件编辑器&#xff0c;正在逐渐受到师生们的关注和应用。 简单来说&#xff0c;VR畜牧专业课件编辑器是一种可以制作虚拟现实动物教学…

【WPF】 本地化的最佳做法

【WPF】 本地化的最佳做法 资源文件英文资源文件 en-US.xaml中文资源文件 zh-CN.xaml 资源使用App.xaml主界面布局cs代码 App.config辅助类语言切换操作类资源 binding 解析类 实现效果 应用程序本地化有很多种方式&#xff0c;选择合适的才是最好的。这里只讨论一种方式&#…

HTTP响应状态码大全:从100到511,全面解析HTTP请求的各种情况

文章目录 前言一、认识响应状态码1. 什么是HTTP响应状态码2. Http响应状态码的作用3. 优化和调试HTTP请求的建议 二、1xx 信息响应1. 认识http信息响应2. 常见的信息响应状态码 三、2xx 成功响应1. 认识HTTP成功响应2. 常见的成功响应状态码 四、3xx 重定向1. 认识http重定向2.…

WS2812B————动/静态显示

一&#xff0c;系统架构 二&#xff0c;芯片介绍 1.管脚说明 2.数据传输时间 3.时序波形 4.数据传输方法 5.常用电路连接 三&#xff0c;代码展示及说明 驱动模块 在驱动模块首先选择使用状态机&#xff0c;其中包括&#xff0c;空闲状态&#xff0c;复位清空状态&#xff0c…

LeetCode150道面试经典题-- 合并两个有序链表(简单)

1.题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 2.示例 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输…

STM32 FLASH 读写数据

1. 《STM32 中文参考手册》&#xff0c;需要查看芯片数据手册&#xff0c;代码起始地址一般都是0x8000 0000&#xff0c;这是存放整个项目代码的起始地址 2. 编译信息查看代码大小&#xff0c;修改代码后第一次编译后会有这个提示信息 2.1 修改代码后编译&#xff0c;会有提示…

谈谈IP地址和子网掩码的概念及应用

个人主页&#xff1a;insist--个人主页​​​​​​ 本文专栏&#xff1a;网络基础——带你走进网络世界 本专栏会持续更新网络基础知识&#xff0c;希望大家多多支持&#xff0c;让我们一起探索这个神奇而广阔的网络世界。 目录 一、IP地址的概念 二、IP地址的分类 1、A类 …

centos安装pandoc

1、首先从官网下载安装包(Release pandoc 3.1.6 jgm/pandoc GitHub) 2、上传到服务器(这里放到 /root目录下了)&#xff0c;进行解压 tar -zxvf pandoc-3.1.6-linux-amd64.tar.gz&#xff0c;解压后的文件 3、然后使用命令 ln -s /root/pandoc-3.1.6/bin/pandoc /usr/bin/p…

20230818 数据库自整理部分

并发事务 脏读 一个事务读取到另一事务还没有提交的数据 事务B读取了事务A还没有提交的数据 不可重复读 一个事务先后读取同一条记录&#xff0c;但是两次读取的数据不同&#xff0c;称之为不可重复读 查询出来的数据不一样 1步骤b还没有提交 3步骤b已经提交 幻读 一个…

CSS中的transform属性有哪些值?并分别描述它们的作用。

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ translate()⭐ rotate()⭐ scale()⭐ skew()⭐ matrix()⭐ scaleX() 和 scaleY()⭐ rotateX()、rotateY() 和 rotateZ()⭐ translateX() 和 translateY()⭐ skewX() 和 skewY()⭐ perspective()⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&…

解决生成式AI落地之困,亚马逊云科技提供完整解决方案

生成式AI技术无疑是当前最大的时代想象力之一。 资本、创业者、普通人都在涌入生成式AI里去一探究竟&#xff1a;“百模大战”连夜打响&#xff0c;融资规模连创新高&#xff0c;各种消费类产品概念不断涌现……根据Bloomberg Intelligence 的报告&#xff0c;2022年生成式AI 市…

8.利用matlab完成 符号微积分和极限 (matlab程序)

1.简述 一、符号微积分 微积分的数值计算方法只能求出以数值表示的近似解&#xff0c;而无法得到以函数形式表示的解析解。在 MATLAB 中&#xff0c;可以通过符号运算获得微积分的解析解。 1. 符号极限 MATLAB 中求函数极限的函数是 limit&#xff0c;可用来求函数在指定点的…

java面试基础 -- 方法重载 方法重写

目录 重载 重写 重载 方法的重载是指在同一个类中定义多个方法, 他们具有相同的名称, 但是具有不同的参数列表, 例如: public void myMethod(int arg1) {// 方法体 }public void myMethod(int arg1, int arg2) {// 方法体 }public void myMethod(String arg1) {// 方法体 }…

机器学习中基本的数据结构说明

数据维度或数据结构 当我们在机器学习或深度学习的领域内处理数据&#xff0c;我们通常会遇到四种主要的数据结构&#xff1a;标量&#xff0c;向量&#xff0c;矩阵和张量。理解这些基本数据结构是非常重要的&#xff0c;因为它们是机器学习算法和神经网络的核心。下面是对这…

牛客网华为OD前端岗位,面试题库练习记录02

题目一 删除字符串中出现次数最少的字符(HJ23) JavaScript Node ACM 模式 const rl require("readline").createInterface({ input: process.stdin }); var iter rl[Symbol.asyncIterator](); const readline async () > (await iter.next()).value;void (asyn…