vsomeip —— 10分钟快速了解 vsomeip (vsomeip wiki 文档翻译)

文档声明:
以下资料均属于本人在学习过程中产出的学习笔记,如果错误或者遗漏之处,请多多指正。并且该文档在后期会随着学习的深入不断补充完善。感谢各位的参考查看。


笔记资料仅供学习交流使用,转载请标明出处,谢谢配合。
如果存在相关知识点的遗漏,可以在评论区留言,看到后将在第一时间更新。
作者:Aliven888

vsomeip in 10 minutes

  这篇文章是对 vsomeip 官方 wiki 文档的一个翻译。属于个人学习开发笔记的一个记录。

SOME/IP 简介

  SOME/IP 是 Scalable service-Oriented middleware over IP 的缩写。 该中间件专为典型的汽车用例而设计,并与 AUTOSAR 兼容(至少在有线格式级别上)。 可在 SOME/IP 上获得可公开访问的规范。 在这个 wiki 中,我们不想进一步深入探讨另一个中间件规范的原因,而是想对 SOME/IP 规范的基本结构及其开源实现 vsomeip 给出一个粗略的概述,而不要求任何完整性。

让我们从 SOME/IP 规范的三个主要部分开始:

  • On-wire format
  • Protocol
  • Service Discovery
SOME/IP On-Wire Format

  原则上,SOME/IP 通信由设备或订阅者之间通过 IP 发送的消息组成。 详见如下图示:

在这里插入图片描述
  这里你会看到两个设备(A 和 B); 设备 A 向 B 发送一条 SOME/IP 消息,并收到一条回复消息。

  底层传输协议可以是 TCP 或 UDP; 对于消息本身,这没有什么区别。

 现在我们假设设备 B 上正在运行一个服务,该服务提供了一个由该消息从设备 A 调用的函数,而返回的消息就是答案。

  SOME/IP 的消息由两部分组成: headerpayload

  在图片中,您可以看到 header 主要由标识符组成:

  • Service ID: unique identifier for each service
  • Method ID: 0-32767 for methods, 32768-65535 for events
  • Length: length of payload in byte (covers also the next IDs, that means 8 additional bytes)
  • Client ID: unique identifier for the calling client inside the ECU; has to be unique in the overall vehicle
  • Session ID: identifier for session handling; has to be incremented for each call
  • Protocol Version: 0x01
  • Interface Version: major version of the service interface
  • Message Type:
    – REQUEST (0x00) A request expecting a response (even void)
    – REQUEST_NO_RETURN (0x01) A fire&forget request
    – NOTIFICATION (0x02) A request of a notification/event callback expecting no response
    – RESPONSE (0x80) The response message
  • Return Code:
    – E_OK (0x00) No error occurred
    – E_NOT_OK (0x01) An unspecified error occurred
    – E_WRONG_INTERFACE_VERSION (0x08) Interface version mismatch
    – E_MALFORMED_MESSAGE (0x09) Deserialization error, so that payload cannot be deseria-lized
    – E_WRONG_MESSAGE_TYPE (0x0A) An unexpected message type was received (e.g. RE-QUEST_NO_RETURN for a method defined as RE-QUEST)

  我们可以看到,对于客户端已订阅的事件,正常函数的调用和通知消息都有“请求”和“响应”。 错误报告为正常响应或通知,并且会返回错误代码。

  payload 包含序列化数据。 如上图示的传输数据结构,只有基本数据类型的嵌套结构下的简单序列化。 在这种情况下,结构元素只是被展平,然后它们被一个接一个地写入 payload 中。

SOME/IP Protocol

  在本节中,主要有两点很重要,现在将对其进行描述:

  • 传输绑定(UDP 和 TCP)
  • 基本的通信模式发布/订阅和请求/响应。

  如上所述,底层传输协议可以是 UDP 或 TCP。 在 UDP 情况下,SOME/IP 消息没有分段; 一个 UDP 数据包中可能包含多条消息,但一条消息的长度不能超过 UDP 数据包的长度(最多 1400 字节)。 更大的消息必须通过 TCP 传输。 在这种情况下,使用了 TCP 的所有健壮性特征。 如果 TCP 流中发生同步错误,SOME/IP 规范允许所谓的 magic cookies 以便再次找到下一条消息的开头。

  请注意,必须实例化服务接口,并且因为可能存在同一接口的多个实例,所以必须为定义的实例添加一个附加标识符(instance ID)。 但是,instance ID 不是 SOME/IP 消息的标头的一部分。 实例通过传输协议的端口号识别; 这意味着不可能在同一端口上提供同一接口的多个实例。

现在请看下图,它显示了基本的 SOME/IP 通信模式:

在这里插入图片描述

  除了用于远程过程调用的标准 请求-响应 机制之外,还有用于事件的 发布-订阅 模式。 请注意,SOME/IP 协议中的事件总是分组在一个事件组中; 因此只能订阅事件组而不是事件本身。

  SOME/IP 规范也有 fields; 在这种情况下,setter/getter 方法遵循 请求-响应 模式,并且更改的通知消息是事件。 订阅本身是通过 SOME/IP 服务发现完成的。

SOME/IP Service discovery

SOME/IP 服务发现用于定位服务实例并检测服务实例是否正在运行以及实现发布/订阅处理。 这主要是通过所谓的报价消息来完成的; 同时也意味着每个设备都广播(多播)消息,并在广播的消息中包含该设备提供的所有服务信息。

SOME/IP SD 消息通过 UDP 发送。

  如果客户端应用程序需要服务但目前没有提供,那么也可以发送查找消息(find messages)。 其他 SOME/IP SD 消息可用于发布或订阅事件组。

SOME/IP SD 消息的结构显示如下:

在这里插入图片描述

  这应该足够开始了。 更多细节将在后面的示例中讨论,或者可以在说明书中阅读。

vsomeip 简述

  在我们开始实现介绍示例之前,让我们简要了解一下 SOME/IPGENIVI实现的基本结构,即vsomeip

在这里插入图片描述
  如图所示,vsomeip 不仅涵盖了设备之间的 SOME/IP 通信(外部通信),还涵盖了内部进程间通信。 两个设备通过所谓的通信端点进行通信,这些端点将使用的传输协议(TCP 或 UDP)及其参数确定为端口号或其他参数。 所有这些参数都是可以在 vsomeip 配置文件中设置的配置参数(json 文件,请参阅 vsomeip 用户指南)。 内部通信是通过本地端点完成的,这些端点由 unix 域套接字使用Boost.Asio库实现。 由于这种内部通信不是通过中央组件(例如,像 D-Bus 守护程序)路由的,因此它非常快。

vsomeip 中央路由管理器仅在必须将消息发送到外部设备时才获取消息,并且他分发来自外部的消息。 每台设备只有一个路由管理器; 如果未配置任何内容,则第一个运行的 vsomeip 应用程序也会启动路由管理器。

  ❗️ vsomeip 没有实现数据结构的序列化! CommonAPISOME/IP 绑定涵盖了这一点。vsomeip仅涵盖 SOME/IP协议和服务发现。

  现在这是对 SOME/IPvsomeip 的非常、非常简短的概述。 但是对于第一次开始就足够了; 更多细节在示例中直接解释。

Preparation / Prerequisites(准备 / 先决条件)

  如前所述,vsomeip 需要 Boost.Asio 库,因此请确保您已在系统上安装了 BOOST(至少 1.55 版)。 成功安装 Boost 后,您可以像往常一样轻松构建 vsomeip

$ cd vsomeip
<.>/vsomeip$ mkdir build
<.>/vsomeip$ cd build
<.>/vsomeip/build$ cmake ..
<.>/vsomeip/build$ make

This works, but in order to avoid some special problems afterwards I recommend to add at least one parameter to your CMake call:

  虽然这样就搞定了,但为了避免之后出现一些特殊问题,我建议在您的 CMake 调用中添加至少一个参数:

> cmake -DENABLE_SIGNAL_HANDLING=1 ..

  此参数确保您可以毫无问题地终止您的 vsomeip 应用程序(否则可能是当您使用 Ctrl-C 停止应用程序时,共享内存段 /dev/shm/vsomeip 未正确删除)

First Application(首次申请)

  创建第一个 vsomeip 程序,我们称它为 service-example:

/*service-example.cpp*/

#include <vsomeip/vsomeip.hpp>

std::shared_ptr< vsomeip::application > app;

int main() {

    app = vsomeip::runtime::get()->create_application("World");
    app->init();
    app->start();
}

  这很容易:您必须首先创建一个应用程序对象,然后初始化并启动它。 创建 vsomeip 应用程序后必须首先调用 init 方法,并执行以下步骤对其进行初始化:

  • Loading the configuration (加载配置)
  • Determining routing configuration and initialization of the routing (确定路由配置和路由初始化)
  • Installing signal handlers (安装信号处理程序)

The start method has to be called after init in order to start the message processing. The received messages are processed via sockets and registered callbacks are used to pass them to the user application.

  为了启动消息处理,必须在 init 之后调用 start 方法。 接收到的消息通过套接字进行处理,并使用注册的回调将它们传递给用户应用程序。

READY 👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍

  为了构建我们的程序,需要创建一个CMake 文件,文件如下:

CMakeLists.txt (Example)

cmake_minimum_required (VERSION 2.8)

set (CMAKE_CXX_FLAGS "-g -std=c++0x")

find_package (vsomeip 2.6.0 REQUIRED)
find_package( Boost 1.55 COMPONENTS system thread log REQUIRED )

include_directories (
    ${Boost_INCLUDE_DIR}
    ${VSOMEIP_INCLUDE_DIRS}
)

add_executable(service-example ../src/service-example.cpp)
target_link_libraries(service-example vsomeip ${Boost_LIBRARIES})

  像往常一样继续(创建构建目录,运行 CMake 并构建)。 然后启动它(service-example)。 您应该在控制台上获得以下输出(或类似输出):

2017-03-20 10:38:20.885390 [info] Parsed vsomeip configuration in 0ms
2017-03-20 10:38:20.889637 [info] Default configuration module loaded.
2017-03-20 10:38:20.889797 [info] Initializing vsomeip application "World".
2017-03-20 10:38:20.890120 [info] SOME/IP client identifier configured. Using 0001 (was: 0000)
2017-03-20 10:38:20.890259 [info] No routing manager configured. Using auto-configuration.
2017-03-20 10:38:20.890367 [info] Instantiating routing manager [Host].
2017-03-20 10:38:20.890641 [info] init_routing_endpoint Routing endpoint at /tmp/vsomeip-0
2017-03-20 10:38:20.890894 [info] Client [1] is connecting to [0] at /tmp/vsomeip-0
2017-03-20 10:38:20.891039 [info] Service Discovery enabled. Trying to load module.
2017-03-20 10:38:20.891647 [info] Service Discovery module loaded.
2017-03-20 10:38:20.892045 [info] Application(World, 1001) is initialized (11, 100).
2017-03-20 10:38:20.892210 [info] Starting vsomeip application "World" using 2 threads
2017-03-20 10:38:20.892668 [info] Watchdog is disabled!
2017-03-20 10:38:20.893312 [info] Network interface "lo" is up and running.
2017-03-20 10:38:20.898471 [info] vSomeIP 2.6.2
2017-03-20 10:38:20.898708 [info] Sent READY to systemd watchdog
2017-03-20 10:38:20.898854 [info] SOME/IP routing ready.

请注意:

  • 这些步骤对于服务和客户端是相同的; 没有区别。 它只是一个 vsomeip 应用程序。
  • 到目前为止,您不需要任何配置文件。

让我们详细讨论一些要点:

  • 首先你看到已经加载的配置;您没有配置,因此使用默认值。

  • 您没有为您的应用程序配置客户端 ID;因此,vsomeip 功能(自动配置)会找到适当的client ID。第一个数字是 0x0001

  • 路由管理器也没有配置;因此,路由管理器会随着系统中的第一个 vsomeip 应用程序自动启动,这就是 service-example

  • 默认情况下启用服务发现,没有静态路由。这将需要一些配置参数。

  • 最后一个 init() 输出是 Application(World, 1) is initialized (11, 100)。最后的两个数字意味着如果回调阻塞超过 100 毫秒,则 vsomeip 使用的最大调度程序数量为 11。可以配置这些参数。

  • 默认创建两个线程来接收 SOME/IP 消息;这允许 vsomeip 并行处理长消息。

  • 然后您会看到当前的 vsomeip 版本,并且 SOME/IP 路由已准备就绪。

可用性

  到目前为止,该应用程序并没有做太多工作 😃 客户端和服务之间也没有区别。 现在让我们假设我们的 service-example 是服务,并且我们想要编写一个想要使用该服务的客户端。 第一步,我们必须触发应用程序提供服务实例。 这可以通过在我们的第一个示例中添加offer_service命令来完成:

service-example.cpp with offer

#include <vsomeip/vsomeip.hpp>

#define SAMPLE_SERVICE_ID 0x1234
#define SAMPLE_INSTANCE_ID 0x5678

std::shared_ptr< vsomeip::application > app;

int main() {

    app = vsomeip::runtime::get()->create_application("World");
    app->init();
    app->offer_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID);
    app->start();
}

  在下一步中,我们编写一个应用程序来检查正在运行的 “World” 应用程序是否可用。 考虑下面的 client-example 代码,它创建了一个名为 Hello 的应用程序:

client-example.cpp

#include <iomanip>
#include <iostream>

#include <vsomeip/vsomeip.hpp>

#define SAMPLE_SERVICE_ID 0x1234
#define SAMPLE_INSTANCE_ID 0x5678

std::shared_ptr< vsomeip::application > app;

void on_availability(vsomeip::service_t _service, vsomeip::instance_t _instance, bool _is_available) {
    std::cout << "Service ["
            << std::setw(4) << std::setfill('0') << std::hex << _service << "." << _instance
            << "] is " << (_is_available ? "available." : "NOT available.")  << std::endl;
}

int main() {

    app = vsomeip::runtime::get()->create_application("Hello");
    app->init();
    app->register_availability_handler(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, on_availability);
    app->request_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID);
    app->start();
}

  为了使其尽可能简单,我们省略了所有可能的检查,例如注册是否成功。 作为客户端,您必须告诉 vsomeip 您想要使用该服务,并且您需要注册一个回调以便在该服务可用时获得呼叫。 客户端输出现在应该类如下:

Service [1234.5678] is NOT available.
2017-03-21 04:14:37.720313 [info] REQUEST(0002): [1234.5678:255.4294967295]
Service [1234.5678] is available.

  当 app->start() 启动 vsomeip 事件循环时,将调用可用性回调 (on_availability(...) 函数)

在服务端打印如下语句(log)

2017-03-21 04:14:33.850964 [info] OFFER(0001): [1234.5678:0.0]

请求 / 响应

 从一个常见的 vsomeip 应用程序开始,我们创建了一个服务,它提供了一个服务接口的实例和一个想要使用这个接口的客户端。 下一步现在是在服务端实现一个可由客户端调用的功能。

  服务示例必须准备好接收消息; 这可以通过注册消息处理程序来完成。 请看下面的代码:

service-example.cpp with offer and message handler

#include <iomanip>
#include <iostream>
#include <sstream>

#include <vsomeip/vsomeip.hpp>

#define SAMPLE_SERVICE_ID 0x1234
#define SAMPLE_INSTANCE_ID 0x5678
#define SAMPLE_METHOD_ID 0x0421

std::shared_ptr<vsomeip::application> app;

void on_message(const std::shared_ptr<vsomeip::message> &_request) {

    std::shared_ptr<vsomeip::payload> its_payload = _request->get_payload();
    vsomeip::length_t l = its_payload->get_length();

    // Get payload
    std::stringstream ss;
    for (vsomeip::length_t i=0; i<l; i++) {
       ss << std::setw(2) << std::setfill('0') << std::hex
          << (int)*(its_payload->get_data()+i) << " ";
    }

    std::cout << "SERVICE: Received message with Client/Session ["
        << std::setw(4) << std::setfill('0') << std::hex << _request->get_client() << "/"
        << std::setw(4) << std::setfill('0') << std::hex << _request->get_session() << "] "
        << ss.str() << std::endl;

    // Create response
    std::shared_ptr<vsomeip::message> its_response = vsomeip::runtime::get()->create_response(_request);
    its_payload = vsomeip::runtime::get()->create_payload();
    std::vector<vsomeip::byte_t> its_payload_data;
    for (int i=9; i>=0; i--) {
        its_payload_data.push_back(i % 256);
    }
    its_payload->set_data(its_payload_data);
    its_response->set_payload(its_payload);
    app->send(its_response, true);
}

int main() {

   app = vsomeip::runtime::get()->create_application("World");
   app->init();
   app->register_message_handler(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_METHOD_ID, on_message);
   app->offer_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID);
   app->start();
}

在客户端,它有点复杂:

client-example.cpp with message handler and send function

#include <iomanip>
#include <iostream>
#include <sstream>

#include <condition_variable>
#include <thread>

#include <vsomeip/vsomeip.hpp>

#define SAMPLE_SERVICE_ID 0x1234
#define SAMPLE_INSTANCE_ID 0x5678
#define SAMPLE_METHOD_ID 0x0421

std::shared_ptr< vsomeip::application > app;
std::mutex mutex;
std::condition_variable condition;

void run() {
  std::unique_lock<std::mutex> its_lock(mutex);
  condition.wait(its_lock);

  std::shared_ptr< vsomeip::message > request;
  request = vsomeip::runtime::get()->create_request();
  request->set_service(SAMPLE_SERVICE_ID);
  request->set_instance(SAMPLE_INSTANCE_ID);
  request->set_method(SAMPLE_METHOD_ID);

  std::shared_ptr< vsomeip::payload > its_payload = vsomeip::runtime::get()->create_payload();
  std::vector< vsomeip::byte_t > its_payload_data;
  for (vsomeip::byte_t i=0; i<10; i++) {
      its_payload_data.push_back(i % 256);
  }
  its_payload->set_data(its_payload_data);
  request->set_payload(its_payload);
  app->send(request, true);
}

void on_message(const std::shared_ptr<vsomeip::message> &_response) {

  std::shared_ptr<vsomeip::payload> its_payload = _response->get_payload();
  vsomeip::length_t l = its_payload->get_length();

  // Get payload
  std::stringstream ss;
  for (vsomeip::length_t i=0; i<l; i++) {
     ss << std::setw(2) << std::setfill('0') << std::hex
        << (int)*(its_payload->get_data()+i) << " ";
  }

  std::cout << "CLIENT: Received message with Client/Session ["
      << std::setw(4) << std::setfill('0') << std::hex << _response->get_client() << "/"
      << std::setw(4) << std::setfill('0') << std::hex << _response->get_session() << "] "
      << ss.str() << std::endl;
}

void on_availability(vsomeip::service_t _service, vsomeip::instance_t _instance, bool _is_available) {
    std::cout << "CLIENT: Service ["
            << std::setw(4) << std::setfill('0') << std::hex << _service << "." << _instance
            << "] is "
            << (_is_available ? "available." : "NOT available.")
            << std::endl;
    condition.notify_one();
}

int main() {

    app = vsomeip::runtime::get()->create_application("Hello");
    app->init();
    app->register_availability_handler(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, on_availability);
    app->request_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID);
    app->register_message_handler(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_METHOD_ID, on_message);
    std::thread sender(run);
    app->start();
}

  就像在服务端一样,我们需要注册一个消息处理程序来接收我们调用的响应。 原则上,创建发送消息 (request) 非常容易。 只需通过调用 create_request() 获取请求对象,设置服务 ID、实例 ID 和方法 ID,最后使用您的序列化数据写入 payload。 在此处的示例中,我们将 0 到 9 的值写入有效负载 (std::vector< vsomeip::byte_t >)。

  当我们尝试将我们的请求从客户端发送到服务时,我们遇到了一个小问题。 在我们发送消息之前,必须启动应用程序(app->start()),因为我们需要一个正在运行的事件循环来处理消息。 但是 app->start() 方法不会返回,因为它内部有正在运行的事件循环。 因此,我们启动一个线程 (run) 并在此线程中等待可用性回调的返回,然后再调用 app->send(request, true)

现在我们应该得到输出(首先启动服务端):

2017-03-21 08:08:08.033710 [info] REQUEST(1002): [1234.5678:255.4294967295]
CLIENT: Service [1234.5678] is available.
2017-03-21 08:08:08.034182 [info] Client [1002] is connecting to [1001] at /tmp/vsomeip-1001
SERVICE: Received message with Client/Session [1002/0001] 00 01 02 03 04 05 06 07 08 09 
CLIENT: Received message with Client/Session [1002/0001] 09 08 07 06 05 04 03 02 01 00 

订阅 / 通知

  到目前为止,我们已经创建了一个实现方法的服务和调用该方法的客户端。 但这并不是所有可能的☀️。 SOME/IP 规范还描述了事件处理。 这意味着应用程序可以发送订阅者可以订阅的事件,如果他们感兴趣的话。 连同 settergetter 方法的定义,可以实现提供属性的服务。

  为了使它不太复杂,我们在示例中删除了方法调用的实现并实现了事件处理。 首先让我们看一下服务。

  请在您的主要功能中添加以下几行:

const vsomeip::byte_t its_data[] = { 0x10 };
payload = vsomeip::runtime::get()->create_payload();
payload->set_data(its_data, sizeof(its_data));

std::set<vsomeip::eventgroup_t> its_groups;
its_groups.insert(SAMPLE_EVENTGROUP_ID);
app->offer_event(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_EVENT_ID, its_groups, true);
app->notify(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_EVENT_ID, payload);

❗️ 请注意:

  • 您必须提供活动才能向世界其他地方宣布此活动的存在(You must offer the event in order to announce the existence of this event to the rest of the world)

  • 使用通知方法,您可以将事件发送给任何订阅的人。

  • 每个事件都属于一个事件组! 但它也可以属于多个事件组。

  • 事件不独立于服务而存在; 如果该服务尚未提供,则该服务对客户端不可用且客户端无法订阅。

在客户端实现以下内容(为了更好地理解,我省略了之前讨论过的所有内容):

...

void run() {
  std::unique_lock<std::mutex> its_lock(mutex);
  condition.wait(its_lock);

  std::set<vsomeip::eventgroup_t> its_groups;
  its_groups.insert(SAMPLE_EVENTGROUP_ID);
  app->request_event(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_EVENT_ID, its_groups, true);
  app->subscribe(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_EVENTGROUP_ID);

}

void on_message(const std::shared_ptr<vsomeip::message> &_response) {
    std::stringstream its_message;
    its_message << "CLIENT: received a notification for event ["
            << std::setw(4) << std::setfill('0') << std::hex
            << _response->get_service() << "."
            << std::setw(4) << std::setfill('0') << std::hex
            << _response->get_instance() << "."
            << std::setw(4) << std::setfill('0') << std::hex
            << _response->get_method() << "] to Client/Session ["
            << std::setw(4) << std::setfill('0') << std::hex
            << _response->get_client() << "/"
            << std::setw(4) << std::setfill('0') << std::hex
            << _response->get_session()
            << "] = ";
    std::shared_ptr<vsomeip::payload> its_payload = _response->get_payload();
    its_message << "(" << std::dec << its_payload->get_length() << ") ";
    for (uint32_t i = 0; i < its_payload->get_length(); ++i)
        its_message << std::hex << std::setw(2) << std::setfill('0')
            << (int) its_payload->get_data()[i] << " ";
    std::cout << its_message.str() << std::endl;
}

...

int main() {

    app = vsomeip::runtime::get()->create_application("Hello");
    app->init();
    app->register_availability_handler(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, on_availability);
    app->request_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID);

    app->register_message_handler(vsomeip::ANY_SERVICE, vsomeip::ANY_INSTANCE, vsomeip::ANY_METHOD, on_message);

    std::thread sender(run);
    app->start();
}

按照如下步骤,你会发现该功能实现起来会很简单:

  • 您再次需要事件组来订阅该事件。
  • 您必须先请求活动,然后才能订阅。
  • 接收事件只需注册一个标准消息处理程序; 在这种情况下,您可以使用非常方便的通配符。

在控制台中,你将要看到以下几行日志:

2017-04-06 03:47:46.424942 [info] REGISTER EVENT(0001): [1234.5678.8778:is_provider=true]
2017-04-06 03:47:51.851654 [info] REGISTER EVENT(0002): [1234.5678.8778:is_provider=false]
...
2017-04-06 03:47:51.856821 [info] SUBSCRIBE(0002): [1234.5678.4465:ffff:0]
2017-04-06 03:47:51.861330 [info] SUBSCRIBE ACK(0001): [1234.5678.4465.ffff]

The numbers in the round brackets are again the client IDs; I started the service first, therfore the service has got from the autoconfiguration the number 1 and the client the number 2.

  圆括号中的数字也是客户端 ID; 我首先启动了服务,因此服务从自动配置中获得了数字 0001,客户端获得了数字 0002

两个设备之间的通信

SOME/IP 不是为一个设备内的进程间通信而发明的(例如,作为 D-Bus),而是为几个设备之间基于 IP 的通信而发明的。 如果您想使用迄今为止为两个设备之间的通信开发的示例,则无需更改 C++ 代码,但是你必须编写 vsomeip 配置文件。 有关详细信息,请查看 vsomeip 用户指南; 在这里,我们只讨论如何让你的系统程序运行的起来。

I will first lose some introductory words about the vsoemip configuration generally.

  我将首先失去一些关于 vsoemip 配置的介绍性文字。

  • 堆栈由一个或多个json格式的文件配置 The stack is configured by one or more files in json format(http://www.json.org/)

  • json 文件的标准文件夹是/etc/vsomeip

  • 也可以通过设置环境变量VSOMEIP_CONFIGURATION来更改此文件夹或定义单个配置文件。

  • 也可以将配置文件复制到包含可执行应用程序的文件夹(本地配置)。

对于以下配置示例,我假设服务在地址为172.17.0.2的设备上运行,客户端的地址为172.17.0.1

首先让我们看一个服务配置的例子:

{
    "unicast" : "172.17.0.2",
    "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"
    }
}

  对于通过 IP 进行的通信,单播地址是强制性的。 让我们讨论其他条目:

  • logging:这些设置是可选的;设置 “console”=true 以查看控制台上的日志消息。

  • applications:您可以为每个应用程序(您通过create_application(<name>) 创建它)定义一个固定的 client ID,而不是让它由自动配置确定。这将帮助您稍后在跟踪中识别您的应用程序。 此处必须设置client ID,因为 client ID 在您的网络中必须是唯一的。 如果您不设置 client ID ,自动配置将在每个设备上计算 client ID + 1,并且无法进行通信。

  • services:对于每个服务实例,必须定义它可以通过哪个端口到达。如果端口为“不可靠”,则为 UDP 端口,如果为“可靠”,则 TCP 将是传输层。

  • routing:每个设备只有一个路由管理器。此路由管理器将附加到第一个启动的 vsomeip 应用程序或此处定义的应用程序。

  • service-discovery:所有这些参数只有在启用服务发现时才有意义。在这种情况下,强制参数是用于发送服务发现消息的多播地址以及端口和协议。其他参数确定发送报价消息的频率、延迟等。请查看用户指南或 SOME/IP 规范。

❗️ 确保您的设备已配置为接收多播消息(例如,通过 route add -nv 224.224.224.245 dev eth0 或类似方法;这取决于您的以太网设备的名称)。

考虑客户端的以下配置:

{
    "unicast" : "172.17.0.1",
    "logging" :
    {
        "level" : "debug",
        "console" : "true",
        "file" : { "enable" : "false", "path" : "/var/log/vsomeip.log" },
        "dlt" : "false"
    },
    "applications" : 
    [
        {
            "name" : "Hello",
            "id" : "0x1313"
        } 
    ],
    "routing" : "Hello",
    "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"
    }
}

由于客户端不提供服务,因此不需要“服务”设置。

  • 17
    点赞
  • 118
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值