SomeIP/CommonAPI与Franca IDL使用教程(一)

回顾

SomeIP/CommonAPI环境搭建可以看我上一篇博客:Ubuntu环境下SomeIP/CommonAPI环境搭建详细步骤

什么是SomeIP

SOME/IP(Service-Oriented Middleware over IP)是一种基于IP网络的通信协议,旨在支持汽车电子系统和嵌入式系统之间的通信。它属于AUTOSAR(Automotive Open System Architecture)标准的一部分,被设计用于构建汽车电子领域的分布式系统。

SOME/IP 使用底层的 Socket(套接字)机制来进行通信。SOME/IP的消息是通过UDP或TCP协议传输的

什么是CommonAPI

CommonAPI 是一种用于实现分布式服务的通用框架,旨在促使不同模块或组件之间的通信更加容易。它是一种跨平台的通信框架,适用于嵌入式系统、汽车电子等领域。

我们可以将 CommonAPI 视为一种接口规范,CommonAPI 实际上包括两个层面的概念:

1、CommonAPI 接口规范(IDL): CommonAPI 提供了一种接口定义语言(IDL),用于描述服务接口的结构,包括服务的方法、属性、事件等。这种 IDL 类似于 FIDL(Franca Interface Definition Language),用于规范化描述接口。

2、CommonAPI 运行时环境: CommonAPI 还提供了一个运行时环境,负责实际的服务注册、发现、通信等功能。这个运行时环境是 CommonAPI 框架的核心部分,确保不同的组件可以协同工作,实现松耦合的通信。
在这里插入图片描述

• CommonAPI C++ is divided up in a middleware-independent part (CommonAPI Core) and in a middleware-specific part(CommonAPI Binding).

• CommonAPI uses the interface description language FrancaIDL for the specification of interfaces (logical interface specifica-tion). Code generation from FrancaIDL is an integrated part of CommonAPI.

• The code generator for CommonAPI C++ bindings needs middleware-specific parameters (deployment parameters). These
parameters are defined in Franca deployment files (*.fdepl).

在这里插入图片描述

SomeIP/CommonAPI?

我们可以这样理解:
CommonAPI是一套接口规范,通.fidl文件定义接口,使用CommonAPI Core Runtime代码生成工具来解析fidl文件获得接口文件即(.cpp和.hpp)

someIP是一个利用Socket(套接字)机制的协议库。

我们通过解析fidl文件获得接口文件,并没有IPC通讯功能,为了实现其IPC通讯,引入了fdepl部署文件。

fdepl根据.fidl中的接口,设置每个接口的网络服务ID、网络地址、端口号、通信协议等。然后通过CommonAPI SomeIP Runtime代码生成工具解析fdepl文件,生成可以通过someIP协议通信的接口文件(.cpp和.hpp)。

记要

本文参考sample:E02Attributes

接口的生成

CommonAPI其使用的接口语言为:Franca IDL 语言规范。

什么是Franca?

在这里插入图片描述

Franca 是一个用于定义和转换软件接口的框架。它用于集成来自不同供应商的基于各种运行时框架、平台和IPC(进程间通信)机制构建的软件组件。

在这里插入图片描述

Franca背景简述

接口是任何类型的软件架构中的重要工件。它们代表组件、子系统或系统之间以及组织单位(如团队、部门或公司)之间的合同。因此,在许多开发环境中,使用接口定义语言(简称:IDL)将接口建模为一流实体。然而,对于构建大型平台或系统,现有的IDL至少存在两个缺点:

1、为了集成来自不同来源的软件系统,有必要将一个 IDL 中定义的接口映射到使用另一个 IDL 的软件模型。例如,在国际 GENIVI 联盟中 ,正在通过集成各种开源项目、公司和机构提供的软件组件(如 GENIVI 本身)来开发汽车/信息娱乐系统平台。这些构建块的接口要么根本没有建模(例如,纯 C 或 C++ 标头),要么以几种方法之一正式建模,其中包括 UML、D-Bus Introspection XML、带注释的 Java API。模型级别上所有贡献的集成只能通过在所有这些 IDL 之间建立映射来完成。

2、大多数现有 IDL 方法的另一个常见缺点是接口动态建模缺乏正式性。大多数 IDL 提供了对接口的静态方面(例如,数据类型、信号、方法、属性)进行建模的方法,而不是动态方面(例如,接口上允许的事件序列),动态方面通常被视为可选的附加组件到静态接口定义。
然而,复杂软件系统中的许多严重错误是由接口动态方面的不匹配引起的。这些错误尤其会在项目后期阶段(系统集成期间甚至客户交付后)发生,难以识别且修复成本高昂。因此,动态方面有必要成为原始接口定义的一部分,从而允许对接口的实现和使用进行广泛的形式验证。这可以通过静态分析或在运行时完成。

为了解决这两个问题,GENIVI 启动了 Franca 项目,该项目可以作为 IDL 转换的中心,并允许将动态行为规范作为每个建模接口的组成部分。Franca 的一部分是技术和平台中立的 IDL。Franca 最初由Harman贡献 ,并于 2012 年 3 月在 EclipseLabs 上发布 (根据 Eclipse Public License 1.0)。

Franca目前的应用

Franca 自 2012 年 3 月起就是一个开源项目。因此,无法提供完整的应用程序列表。不过,我们将列出一些示例,以展示 Franca 社区的一部分:

1、GENIVI 联盟将 Franca 用作 IDL 和集成工具.

2、CommonAPI C++ 项目使用 Franca 模型作为所有代码生成器的起点。

3、Joynr基于 Web 的通信框架使用 Franca 作为 IDL 及其 Java 和 C++ 代码生成器的基础。开源 Joynr 框架支持部署在消费设备、车辆或后端基础设施上的应用程序的交互。

4、Yamaica 项目是一个 Eclipse IDE 扩展,为处理 Franca 模型提供了方便的 UI 。

5、多家公司(包括非汽车公司)正在使用或评估 Franca 作为其内部软件平台的 IDL。 <----说还是车企用的多hhhhhh

Franca IDL详细介绍可以参考以下链接:
FrancaGitHubhttps://github.com/franca/franca
eclipse给出的说明:https://www.eclipse.org/proposals/modeling.franca/
Franca官方文档下载:download

fidl

.fidl文件

在这里插入图片描述

这是一个接口定义文件 (Franca Interface Definition Language) 的扩展,用于描述 CommonAPI 接口。它定义了服务接口相关的数据结构。这里使用E02Attributes例子中的fidl来解释。

E02Attributes.fidl

package commonapi.examples

interface E02Attributes {
    version { major 1 minor 0 }

    attribute Int32 x
    attribute CommonTypes.a1Struct a1
}

typeCollection CommonTypes {
    version { major 1 minor 0 }
    
    struct a1Struct {
        String s
        a2Struct a2
    }

    struct a2Struct {
        Int32 a
        Boolean b
        Double d
    }
}
类别诠释
package组织和命名空间化接口定义的关键字
interface用于声明一个接口
version版本号,同命名空间有关,必须包含
attribute通常用于定义读取和写入的属性
typeCollection定义自定义的数据类型集合
struct类似C中的结构体

下面我们看一下fidl(Franca Interface Definition Language)的基本类型.

基本类型对应C++类型
UInt8uint8_t
Int8int8_t
UInt16uint16_t
Int16int16_t
UInt32uint32_t
Int32int32_t
UInt64uint64_t
Int64int64_t
Integer整数属性(这个我也没搞明白啥意思)
Booleanbool
Floatfloat
Doubledouble
Stringstd::string
ByteBufferstd::vector<uint8_t>

全部类型详细解释可参阅:Franca官方文档中5. Franca IDL Reference章节

上述我们已经大概对fidl有了基础的了解,下面我们详细解释一下:E02Attributes.fidl

package commonapi.examples

通俗点的说就是,我们定义的这些接口都在commonapi.examples命名空间下。
生成的C++结构如下:

namespace commonapi
{
    namespace examples
    {
	 //接口代码在其中
    }
}
version { major 1 minor 0 }

使用version说明了这个接口版本主版本是1,次版本是0。生成的C++结构如下:

namespace V1
{
    namespace commonapi
    {
        namespace examples
        {
			//接口代码在其中
        }
    }

}
interface E02Attributes {
    version { major 1 minor 0 }

    attribute Int32 x
    attribute CommonTypes.a1Struct a1
}

使用interface定义了一个接口E02Attributes,这个接口通过attribute修饰了两个属性,x和a1。attribute就表示我们可以通过set或get方法来设置和获取这两个属性

第一个属性:x的数据类型为Int32类型。

typeCollection CommonTypes {
    version { major 1 minor 0 }
    
    struct a1Struct {
        String s
        a2Struct a2
    }

    struct a2Struct {
        Int32 a
        Boolean b
        Double d
    }
}

使用typeCollection定义了一个数据集合CommonTypes,这个集合里面有俩结构体。这就很简单明了和C/C++一样,就不赘述了。

第二个属性: a1类型为struct类型。

整个E02Attributes.fidl就表述完成了,那么这个.fidl文件主要做什么呢?

简单通俗的说就是定义了一个接口E02Attributes,这个接口有两个属性x和a1。我们可以通过接口生成的接口文件中的get方法或者set方法来获取/设置这俩属性。

上述对E02Attributes.fidl解释就完成了。下面我们使用Ubuntu环境下SomeIP/CommonAPI环境搭建详细步骤中下载的,CommonAPI Core Runtime代码生成工具 commonapi_core_generator解析fidl文件,生成接口文件。

./commonapi_core_generator/commonapi-core-generator-linux-x86_64 -sk ./fidl/E02Attributes.fidl

解析完成会在当前目录下生成src-gen文件夹。

./src-gen/
└── v1
    └── commonapi
        └── examples
            ├── CommonTypes.hpp
            ├── E02Attributes.hpp
            ├── E02AttributesProxyBase.hpp
            ├── E02AttributesProxy.hpp
            ├── E02AttributesStubDefault.hpp
            └── E02AttributesStub.hpp

3 directories, 6 files

生成后我们先放着,后面在说使用。下面我们要了解.fdepl文件

fdepl

.fdepl 文件

重述一下:fdepl 文件通常是 CommonAPI 的部署描述文件,用于配置和定义 CommonAPI 服务的运行时参数以及服务的部署信息。这个文件通常包含了与服务的实际运行有关的配置信息,例如服务的ID、网络地址、端口号、通信协议等。

下面我们看一下和E02Attributes.fidl适配的E02Attributes-SomeIP.fdepl文件。

E02Attributes-SomeIP.fdepl

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

define org.genivi.commonapi.someip.deployment for interface commonapi.examples.E02Attributes {
    SomeIpServiceID = 4660

    attribute x {
        SomeIpGetterID = 3000
        SomeIpSetterID = 3001
        SomeIpNotifierID = 33010
        SomeIpNotifierEventGroups = { 33010 }

        SomeIpAttributeReliable = true
    }

    attribute a1 {
        SomeIpGetterID = 3002
        SomeIpSetterID = 3003
        SomeIpNotifierID = 33011
        SomeIpNotifierEventGroups = { 33011 }

        SomeIpAttributeReliable = true
    }
}

define org.genivi.commonapi.someip.deployment for typeCollection commonapi.examples.CommonTypes {
    struct a1Struct {
    }

    struct a2Struct {
    }

}

define org.genivi.commonapi.someip.deployment for provider as Service {
    instance commonapi.examples.E02Attributes {
        InstanceId = "commonapi.examples.Attributes"

        SomeIpInstanceID = 22136

        SomeIpUnicastAddress = "192.168.0.2"
        SomeIpReliableUnicastPort = 30499
        SomeIpUnreliableUnicastPort = 30500
    }
}

注意!!!!fdepl我也不是很熟悉,我尽力诠释

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

第一行导入了 CommonAPI 中 SOME/IP 协议相关的一些规范。

import "E02Attributes.fidl"

导入了FIDL 文件,即 E02Attributes.fidl。

define org.genivi.commonapi.someip.deployment for interface commonapi.examples.E02Attributes
{

}

定义了一个 CommonAPI SOME/IP 部署规范,针对接口 commonapi.examples.E02Attributes

SomeIpServiceID = 4660

定了服务的唯一标识符(Service ID),即 4660。在 SOME/IP 中,这是服务在网络中的唯一标识。

具体来说,SomeIpServiceID 是一个整数值,通常在范围 0x0001 到 0xFFFE 之间。每个服务都应该有一个不同的 ID,以确保在网络中不会发生冲突。这个 ID 会随着服务的注册而分配给服务,并在网络中广播,使得其他节点可以识别它。

    attribute x {
        SomeIpGetterID = 3000
        SomeIpSetterID = 3001
        SomeIpNotifierID = 33010
        SomeIpNotifierEventGroups = { 33010 }

        SomeIpAttributeReliable = true
    }

这部分配置是针对接口中的属性 x 的一些设置,特别是针对 SOME/IP 协议的相关设置。让我逐项解释:

SomeIpGetterID = 3000: 指定了属性 x 的 Getter 方法的 ID。当其他节点希望获取这个属性的值时,会使用这个 ID 进行请求。

SomeIpSetterID = 3001: 指定了属性 x 的 Setter 方法的 ID。当其他节点希望设置这个属性的值时,会使用这个 ID 进行请求。

SomeIpNotifierID = 33010: 指定了属性 x 的 Notifier(通知器)的 ID。通知器用于在属性值发生变化时通知其他节点。

SomeIpNotifierEventGroups = { 33010 }: 指定了 Notifier 所属的 Event Groups。Event Groups 用于在通知时进行分组,这里的设置表示 Notifier 属于 Event Group 33010。

SomeIpAttributeReliable = true: 表示对属性 x 进行通信时要求可靠性。这意味着在进行属性值的读取和写入时,要使用可靠的通信方式,以确保数据的正确传输。

这些设置确保了在 SOME/IP 协议中,对属性 x 的读取、写入和通知等操作有明确的标识和规范,以便其他节点能够正确地进行与该属性相关的通信。

注意:
Setter 操作: 当有节点调用 Setter 方法设置了属性 x 的新值时,如果设置成功,属性的值会更新。

Notifier 发送通知: 如果设置操作导致属性值发生变化,系统会使用指定的 Notifier ID 向订阅了该属性变化的其他节点发送通知。

Getter 操作: 在任何时候,其他节点都可以使用相应的 Getter 方法来获取属性 x 的当前值。这并不要求在属性值发生变化后才能获取,而是可以随时获取当前的属性值。

    attribute a1 {
        SomeIpGetterID = 3002
        SomeIpSetterID = 3003
        SomeIpNotifierID = 33011
        SomeIpNotifierEventGroups = { 33011 }

        SomeIpAttributeReliable = true
    }

属性a1解释同属性x

define org.genivi.commonapi.someip.deployment for typeCollection commonapi.examples.CommonTypes {
    struct a1Struct {
    }

    struct a2Struct {
    }

}

这部分的配置是为类型集合 commonapi.examples.CommonTypes 中的结构体定义 SOME/IP 部署规范.

因为CommonTypes只是一个类型集合,不是一个方法。因此不需要设置ID等属性。

define org.genivi.commonapi.someip.deployment for provider as Service {
    instance commonapi.examples.E02Attributes {
        InstanceId = "commonapi.examples.Attributes"

        SomeIpInstanceID = 22136

        SomeIpUnicastAddress = "192.168.0.2"
        SomeIpReliableUnicastPort = 30499
        SomeIpUnreliableUnicastPort = 30500
    }
}

这部分我们依次解释

define org.genivi.commonapi.someip.deployment for provider as Service 
{

}

这一行的配置指定了服务提供者的 SOME/IP 部署规范,为提供服务的实体进行了配置
provider as Service: 这表明接下来的配置是为服务提供者定义的,它将提供某项服务。

instance commonapi.examples.E02Attributes 
{

}

instance commonapi.examples.E02Attributes: 这一行指定了服务提供者的服务实例,即服务的具体实现。在这里,服务实例的类型是 commonapi.examples.E02Attributes。

InstanceId = "commonapi.examples.Attributes"

InstanceId = “commonapi.examples.Attributes”: 指定了服务实例的唯一标识符,即实例ID。这是服务在网络中的唯一标识。

SomeIpInstanceID = 22136

SomeIpInstanceID = 22136: 指定了服务实例在 SOME/IP 协议中的唯一标识符,即实例的 SOME/IP 实例ID。在网络中,通过这个ID来唯一标识服务。

SomeIpUnicastAddress = "192.168.0.2"

SomeIpUnicastAddress = “192.168.0.2”: 指定了服务的网络地址,即服务提供者在网络中的 IP 地址。

SomeIpReliableUnicastPort = 30499

SomeIpReliableUnicastPort = 30499: 指定了服务提供者使用的可靠单播端口号。可靠单播用于确保通信的可靠性,即保证消息的完整传递。

SomeIpUnreliableUnicastPort = 30500

SomeIpUnreliableUnicastPort = 30500: 指定了服务提供者使用的不可靠单播端口号。不可靠单播通常用于一些对通信可靠性要求较低的情况。

这些配置项共同定义了服务提供者的运行时参数,包括服务实例的唯一标识、网络地址、端口号等信息。这些参数确保了服务能够在网络中被正确注册、定位和访问。

下面我们使用CommonAPI SomeIP Runtime代码生成工具解析E02Attributes-SomeIP.fdepl文件。

./commonapi_someip_generator/commonapi-someip-generator-linux-x86_64 -ll verbose ./fidl/E02Attributes-SomeIP.fdepl

解析完成会在当前目录下生成如下接口文件

./src-gen/
└── v1
    └── commonapi
        └── examples
            ├── CommonTypesSomeIPDeployment.cpp
            ├── CommonTypesSomeIPDeployment.hpp
            ├── E02AttributesSomeIPCatalog.json
            ├── E02AttributesSomeIPDeployment.cpp
            ├── E02AttributesSomeIPDeployment.hpp
            ├── E02AttributesSomeIPProxy.cpp
            ├── E02AttributesSomeIPProxy.hpp
            ├── E02AttributesSomeIPStubAdapter.cpp
            └── E02AttributesSomeIPStubAdapter.hpp

3 directories, 9 files

接口文件分析

我们解析完fidl文件和fdepl文件后当前目录下的src-gen目录中文件结构如下

./src-gen/
└── v1
    └── commonapi
        └── examples
            ├── CommonTypes.hpp
            ├── CommonTypesSomeIPDeployment.cpp
            ├── CommonTypesSomeIPDeployment.hpp
            ├── E02Attributes.hpp
            ├── E02AttributesProxyBase.hpp
            ├── E02AttributesProxy.hpp
            ├── E02AttributesSomeIPCatalog.json
            ├── E02AttributesSomeIPDeployment.cpp
            ├── E02AttributesSomeIPDeployment.hpp
            ├── E02AttributesSomeIPProxy.cpp
            ├── E02AttributesSomeIPProxy.hpp
            ├── E02AttributesSomeIPStubAdapter.cpp
            ├── E02AttributesSomeIPStubAdapter.hpp
            ├── E02AttributesStubDefault.hpp
            └── E02AttributesStub.hpp

3 directories, 15 files

*.fdepl文件生成的粘合代码,凡是带有SomeIP的一般都是我们不需要动的,直接放到项目里面参与编译即可。


├── CommonTypes.hpp   类型集合接口类,不需要动

用于客户端开发:代理是一个提供方法调用的类,
该方法调用将导致对服务的远程方法调用,以及服务可以广播的事件的注册方法。
├── E02Attributes.hpp
├── E02AttributesProxyBase.hpp
├── E02AttributesProxy.hpp

服务器开发:存根是服务的一部分,当来自客户端的远程方法调用到达时,
存根将被调用,它还包含将事件(广播)激发到几个或所有客户端的方法。

├── E02AttributesStubDefault.hpp
└── E02AttributesStub.hpp

阅读接口我们可以发现E02Attributes和E02AttributesProxyBase是E02AttributesProxy的父类
E02AttributesStub是E02AttributesStubDefault的父类。这样我们是不是可以认为有以下关系。

服务器端:以xxxxStubDefault为父类,重写接口
客户端 :以xxxProxy为父类,重写接口。

代码演示

服务端
E02AttributesStubImpl.h

#ifndef E02ATTRIBUTESSTUBIMPL_HPP_
#define E02ATTRIBUTESSTUBIMPL_HPP_

#include <CommonAPI/CommonAPI.hpp>
#include <v1/commonapi/examples/E02AttributesStubDefault.hpp>

class E02AttributesStubImpl: public v1_0::commonapi::examples::E02AttributesStubDefault {

public:
    E02AttributesStubImpl();
    virtual ~E02AttributesStubImpl();
    virtual void incCounter();

private:
    int cnt;
};

#endif // E02ATTRIBUTESSTUBIMPL_HPP_

E02AttributesStubImpl.cpp

#include "E02AttributesStubImpl.hpp"

E02AttributesStubImpl::E02AttributesStubImpl() {
    cnt = 0;
}

E02AttributesStubImpl::~E02AttributesStubImpl() {
}

void E02AttributesStubImpl::incCounter() {
    cnt++;
    setXAttribute((int32_t)cnt);
    std::cout <<  "New counter value = " << cnt << "!" << std::endl;
}

可以看到sample重写了E02AttributesStubDefault类,封装了incCounter函数来更改属性X的数值。

E02AttributesService.cpp

#include <thread>
#include <iostream>

#include <CommonAPI/CommonAPI.hpp>
#include "E02AttributesStubImpl.hpp"

int main() {
    CommonAPI::Runtime::setProperty("LogContext", "E02S");
    CommonAPI::Runtime::setProperty("LogApplication", "E02S");
    CommonAPI::Runtime::setProperty("LibraryBase", "E02Attributes");

    std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get();

    std::string domain = "local";
    std::string instance = "commonapi.examples.Attributes";
    std::string connection = "service-sample";

    std::shared_ptr<E02AttributesStubImpl> myService = std::make_shared<E02AttributesStubImpl>();
    while (!runtime->registerService(domain, instance, myService, connection)) 
    {
        std::cout << "Register Service failed, trying again in 100 milliseconds..." << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    std::cout << "Successfully Registered Service!" << std::endl;

    while (true) 
    {
        myService->incCounter(); // Change value of attribute, see stub implementation
        std::cout << "Waiting for calls... (Abort with CTRL+C)" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(2));
    }
    return 0;
}

服务端写的比较简单,首先获取运行环境

std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get();

然后注册service

    std::string domain = "local";
    std::string instance = "commonapi.examples.Attributes";
    std::string connection = "service-sample";

    std::shared_ptr<E02AttributesStubImpl> myService = std::make_shared<E02AttributesStubImpl>();
    while (!runtime->registerService(domain, instance, myService, connection)) 
    {
        std::cout << "Register Service failed, trying again in 100 milliseconds..." << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    std::cout << "Successfully Registered Service!" << std::endl;

然后循环设置属性X的数值

    while (true) 
    {
        myService->incCounter(); // Change value of attribute, see stub implementation
        std::cout << "Waiting for calls... (Abort with CTRL+C)" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(2));
    }

官方sample写的太抽象了,我们精简一下。我对服务端做了如下修改
E02AttributesStubImpl.cpp

#include "E02AttributesStubImpl.hpp"

E02AttributesStubImpl::E02AttributesStubImpl() {
    cnt = 0;
}

E02AttributesStubImpl::~E02AttributesStubImpl() {
}

void E02AttributesStubImpl::incCounter() {
    cnt++;
    setXAttribute((int32_t)cnt);
    std::cout <<  "server set X : New counter value = " << cnt << "!" << std::endl;

    std::string s_string = getA1Attribute().getS();
    std::cout<<" Rev S val = " << s_string << std::endl;
    
}

E02AttributesService.cpp 修改如下

#include <thread>
#include <iostream>

#include <CommonAPI/CommonAPI.hpp>
#include "E02AttributesStubImpl.hpp"
#include "v1/commonapi/examples/CommonTypes.hpp"

int main() {
    CommonAPI::Runtime::setProperty("LogContext", "E02S");
    CommonAPI::Runtime::setProperty("LogApplication", "E02S");
    CommonAPI::Runtime::setProperty("LibraryBase", "E02Attributes");

    std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get();

    std::string domain = "local";
    std::string instance = "commonapi.examples.Attributes";
    std::string connection = "service-sample";

    std::shared_ptr<E02AttributesStubImpl> myService = std::make_shared<E02AttributesStubImpl>();
    while (!runtime->registerService(domain, instance, myService, connection)) 
    {
        std::cout << "Register Service failed, trying again in 100 milliseconds..." << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    std::cout << "Successfully Registered Service!" << std::endl;

    v1::commonapi::examples::CommonTypes::a1Struct valueStruct;
    valueStruct.setS("abc");
    v1::commonapi::examples::CommonTypes::a2Struct a2Struct = valueStruct.getA2();
    a2Struct.setA(123);
    a2Struct.setB(true);
    a2Struct.setD(1234);
    valueStruct.setA2(a2Struct);

    int i = 0;
    while (true) 
    {
        i++;
        myService->incCounter();

        if(i==10)
        {
            myService->setA1Attribute(valueStruct);
        }

        std::this_thread::sleep_for(std::chrono::seconds(2));
    }
    return 0;
}

客户端
E02AttributesClient.cpp

#include <iostream>
#include <thread>

#ifndef _WIN32
#include <unistd.h>
#endif

#include <CommonAPI/CommonAPI.hpp>
#include <CommonAPI/AttributeCacheExtension.hpp>
#include <v1/commonapi/examples/E02AttributesProxy.hpp>

using namespace v1::commonapi::examples;

int main() {
    CommonAPI::Runtime::setProperty("LogContext", "E02C");
    CommonAPI::Runtime::setProperty("LogApplication", "E02C");
    CommonAPI::Runtime::setProperty("LibraryBase", "E02Attributes");

    std::shared_ptr < CommonAPI::Runtime > runtime = CommonAPI::Runtime::get();

    std::string domain = "local";
    std::string instance = "commonapi.examples.Attributes"; 
    std::string connection = "client-sample";

    //auto myProxy = runtime->buildProxyWithDefaultAttributeExtension<E02AttributesProxy, CommonAPI::Extensions::AttributeCacheExtension>(domain, instance, connection);
    auto myProxy = runtime->buildProxy<E02AttributesProxy>(domain,instance,connection);
    std::cout << "Waiting for service to become available." << std::endl;
    while (!myProxy->isAvailable())
    {
        std::this_thread::sleep_for(std::chrono::microseconds(10));
    }

    // Subscribe for receiving values
    myProxy->getXAttribute().getChangedEvent().subscribe([&](const int32_t& val)
    {
        std::cout << "Received change message: " << val << std::endl;
    });


    myProxy->getA1Attribute().getChangedEvent().subscribe([&](const CommonTypes::a1Struct& val)
    {
        std::cout << "Received change message for A1" << std::endl;

        std::cout << "a1Struct.s = " << val.getS()<<std::endl;

        std::cout << "a1Struct.a1.a2.a = " << val.getA2().getA()<<std::endl;
        std::cout << "a1Struct.a1.a2.b = " << val.getA2().getB()<<std::endl;
        std::cout << "a1Struct.a1.a2.d = " << val.getA2().getD()<<std::endl;
    });

    while (true)
    {
        std::this_thread::sleep_for(std::chrono::microseconds(1000000));
    }
}

这就不需要解释了,对照着service分析,客户端就 “见码知意” 了。

如果想要在client端修改属性可参考下面代码

    // Asynchronous call to set attribute of service
    std::function<void(const CommonAPI::CallStatus&, int32_t)> fcb = recv_cb;
    myProxy->getXAttribute().setValueAsync(value, fcb, &info);

    // Asynchronous call to set attribute of type structure in service
    CommonTypes::a1Struct valueStruct;

    valueStruct.setS("abc");
    CommonTypes::a2Struct a2Struct = valueStruct.getA2();
    a2Struct.setA(123);
    a2Struct.setB(true);
    a2Struct.setD(1234);
    valueStruct.setA2(a2Struct);

    std::function<void(const CommonAPI::CallStatus&, CommonTypes::a1Struct)> fcb_s = recv_cb_s;
    myProxy->getA1Attribute().setValueAsync(valueStruct, fcb_s, &info);

运行

service刚启动时
在这里插入图片描述
client刚启动时
在这里插入图片描述
service启动10s后
在这里插入图片描述
client启10s后
在这里插入图片描述

结语

SomeIP/CommonAPI这个系列我会坚持更下去,本文只粗浅的使用了一下,更多内容后续会出教程(二)

如需要源码资料可联系QQ:918619587

如有错误,感谢指正。

!!!!禁止转载

  • 18
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值