Cyber RT解析与实践

第二章:Cyber RT通信机制解析与实践


Cyber RT解析与实践

  • Cyber RT解析与实践
  • 一、Cyber RT 通讯机制简介
    • 1. 话题
    • 2. 服务
    • 3. 参数
  • 二、数据通信基础Protobuf
    • 1. Protobuf 简介
    • 2. Protobuf 创建
    • 3. Protobuf 编译
    • 4. Protobuf 案例实战
      • 1. 创建test功能包结构:
      • 2. test_proto/BUILD
      • 3. test_proto/car.cc
      • 4. proto/BUILD
      • 5. proto/car_msg.proto
      • 6. 编译运行
  • 三、Cyber RT 话题通信实践案例
    • 1. Listener 和 Talker通讯原理
    • 2. 通信实现流程讲解
    • 3. 编写C++话题通信实现
      • 1. proto文件编写
      • 2. talker.cc 文件编写
      • 3. listener.cc 文件编写
    • 4. 运行与测试
      • 1. 编写bazel编译文件
      • 2. 编译:
      • 3. 运行:
    • 5. 编写Python话题通信实现(小练习)
    • 6. 运行与测试
  • 总结


一、Cyber RT 通讯机制简介

1. 话题

在这里插入图片描述
我们想要⼀直获取⻋的速度,该需求不需要向发送⽅返回什么消息,也不需要发送⽅对消息进⾏进⼀步处理。所以我们选择了Listener-Talker通信⽅式实现该功能。
Listener-Talker通信⼀⽅主动送消息,⼀⽅被动接收。如图2-1所示,Listener-Talker通信⾸先创建了两个Node,分别是Talker Node和 Listener Node。每个Node实例化Writer类和Reader类对Channel进⾏消息的读写。Writer和Reader通过Topic连接,对同⼀块共享内存(Channel)进⾏读写处理。在这⾥,Talker Node 为了实现其“诉说”的功能,选择实例化Writer,通过Writer来对Channel进⾏消息写操
作。⽽istener Node为了实现其“聆听”功能,选择实例化reader类,通过Reader来对channel进⾏读操作。在这⾥,通信中⽤的的数据格式的定义car message 定义在car.proto中。该通信⽅式适合于持续性通信的应⽤场景,⽐如雷达信号,摄像头图像信息这类数据的传输。

2. 服务

在这里插入图片描述
我们想要获得⼩⻋的详细信息,⽐如⻋牌这些,但是⼜不需要⼀直获得该信息,想要在需要知道这些信息的时候请求⼀下就好,于是考虑⽤Server- Client通信实现该功能。如图2-1所示,Server-Client通信可以在客户端发出消息请求时,服务端才进⾏请求回应,并将客户端所需的数据返回给客户端。该通信模式适合临时的消息传输,适⽤于不需要持续性发送数据的场景。其传输的数据定义依然在对应的proto⽂件中。

3. 参数

在这里插入图片描述
有⼀些参数⽐如该⻋的最⾼限速,最多乘客以及是否⾃动驾驶等需要被各个模块所使⽤,⽐如是否⾃动驾驶这个参数可能同时影响这很多模块,也可能被很多模块运⾏时所更改。我们希望有⼀个类似于“全局变量”的⽅式来存储这些参数,并定义⼀些⾃定义参数来进⾏使⽤。Cyber中设计了全局参数服务器来实现这个功能,其通信依然基于RTPS协议,如图2-8所示。该通信⽅式服务端和客户端都可以设置参数和更改参数。

二、数据通信基础Protobuf

1. Protobuf 简介

Protobuf是Google公司开发的一种跨语言和平台的序列化数据结构的方式,是一个灵活的、高效的用于序列化数据的协议,与XML和JSON格式相比,Protobuf更小、更快、更便捷。
Protobuf是跨语言的,并且自带一个编译器(protoc),只需要用protoc进行编译,就可以编译成Java、Python、C++、C#、Go等多种语言代码,然后可以直接使用,不需要再写其它代码,自带有解析的代码。只需要将要被序列化的结构化数据定义一次(在.proto文件定义),便可以使用特别生成的源代码(使用protobuf提供的生成工具)轻松的使用不同的数据流完成对结构数据的读写操作。甚至可以更新.proto文件中对数据结构的定义而不会破坏依赖旧格式编译出来的程序。
其优点如下:

  • 性能效率高:序列化后字节占用空间比XML少3-10倍,序列化的时间效率比XML快20-100倍。
  • 使用便捷便捷:将对结构化数据的操作封装成一个类,便于使用。
  • 兼容性高:通信两方使用同一数据协议,当有一方修改了数据结构,不会影响另一方的使用。
  • 跨语言:支持Java,C++,Python、Go、Ruby等多种语言。

2. Protobuf 创建

为了方便讲解,使用cyber/examples/proto/examples.proto文件来讲解,syntax 表示使用Protobuf的版本,目前Protobuf支持proto3,但在Apollo中使用的是proto2;package 表示该文件的路径;每一个message都表示一种数据结构,message后面跟的是数据结构名字,括号里的字段定义格式为:字段规则 数据类型 字段名称 字段编号。
字段规则主要有三种:

  • required:调用时必须提供该字段的值,否则该消息被视为“未初始化”,官方不建议使用,当把字段规则改为其他规则会存在兼容性问题。
  • optional:该字段的值可以设置也可以不设置,会根据数据类型生成一个默认的值。
  • repeated:类似于动态数组,可以存储多个同类型的数据。
# examples.proto
syntax = "proto2";
package apollo.cyber.examples.proto;
message SamplesTest1 {
  optional string class_name = 1;
  optional string case_name = 2;
};
message Chatter {
  optional uint64 timestamp = 1;
  optional uint64 lidar_timestamp = 2;
  optional uint64 seq = 3;
  optional bytes content = 4;
};
message Driver {
  optional string content = 1;
  optional uint64 msg_id = 2;
  optional uint64 timestamp = 3;
};

3. Protobuf 编译

Protobuf的编译要分为两个步骤,首先要根据.proto文件生成proto库,然后再根据生产的proto库生成C++相关的源文件。这个源文件是C++语言自动编写的,可以被C++程序自动识别。每一个message会被解析生一个类,里面的字段就相当于这个类的属性。在源文件中也会根据属性生成额外的成员,如获取和设置属性的函数。

package(default_visibility = ["//visibility:public"])
#1、生成proto库
proto_library(
    name = "examples_proto",
    srcs = ["examples.proto"],
)
#2、生成源文件
cc_proto_library(
    name = "examples_cc_proto",
    deps = [
        ":examples_proto",
    ],
)

4. Protobuf 案例实战

目的:使用Protobuf来定义数据格式,在main程序中设置数据值并输出。
流程:1、创建目录并编写相关文件;2、编译执行。
内容如下:

1. 创建test功能包结构:

test
|-- test_proto
    |-- BUILD
    |-- car.cc
|-- proto
    |-- BUILD
    |-- car_msg.proto

各个结构内容如下:

2. test_proto/BUILD

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("//tools/install:install.bzl", "install", "install_src_files")
load("//tools:cpplint.bzl", "cpplint")

package(default_visibility = ["//visibility:public"])

cc_binary(
    name = "car",
    srcs = ["car.cc"],
    deps = ["//test/proto:car_msg_cc_proto"], 
)

install(
    name = "install",
    runtime_dest = "test/bin",
    targets = [
        ":car"
    ],
)

install_src_files(
    name = "install_src",
    src_dir = ["."],
    dest = "test/src/cyberatest",
    filter = "*",
)

3. test_proto/car.cc

#include "test/proto/car_msg.pb.h"
using namespace std;

int main()
{
    apollo::cyber::test::proto::CarMsg car;

    car.set_owner("apollo");
    car.set_license_plate("京A88888");
    car.set_max_passenger(6);
    car.add_car_info("SUV"); //车型
    car.add_car_info("Red"); //车身颜色
    car.add_car_info("electric"); //电动

    string owner = car.owner();
    string license_plate = car.license_plate();
    uint64_t max_passenger = car.max_passenger();
    
    cout << "owner:" << owner << endl;
    cout << "license_plate:" << license_plate << endl;
    cout << "max_passenger:" << max_passenger << endl;

    for (int i = 0; i < car.car_info_size(); ++i){
        string info = car.car_info(i);
        cout << info << " ";
    }
    cout << endl;
    return 0;
}

4. proto/BUILD

load("@rules_proto//proto:defs.bzl", "proto_library")
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("//tools:python_rules.bzl", "py_proto_library")

package(default_visibility = ["//visibility:public"])

proto_library(
    name = "car_msg_proto",
    srcs = ["car_msg.proto"],
)

cc_proto_library(
    name = "car_msg_cc_proto",
    deps = [":car_msg_proto"],
)

5. proto/car_msg.proto

syntax = "proto2";

package apollo.cyber.test.proto;

message CarMsg {
    required string owner = 1;
    optional string license_plate = 2;
    optional uint64 max_passenger = 3;
    repeated string car_info = 4;
}

6. 编译运行

代码到这里就可以进行编译运行:

// 编译
cd /apollo_workspace
buildtool build -p test

// 执行
cd /apollo_workspace/bazel-bin/test/test_proto
./car

运行结果如下图所示:
在这里插入图片描述
所有者:apollo
车牌号:京A88888
最大乘客人数:6
SUV红色电动


三、Cyber RT 话题通信实践案例

1. Listener 和 Talker通讯原理

在第一章中,我们已经说明了Node、Channel等Cyber的基础概念,本章我们基于这些基础概念,进一步介绍cyber中的三种通信方式,也即发送接收信息的三种方式。
为了更好的说明这三种通信机制的用法,该章我们通过实现一个案例来说明该部分内容。
案例如下,假设我们拥有了一辆自己的“无人车“,apollo号,现在我们想通过通信,获得该车辆的“实时”速度、车的详细信息以及车的通用参数。那么现在会有几个问题:

  • 车本身有的变量有哪些,应该在哪里定义?
  • 实现“实时”车速的获取和实现车的详细信息等应该用什么通信方式?在哪里实现?
  • 实现的代码应该如何运行起来?
    不要着急,接来下我们就开始用Cyber实现这个小demo,并一一解答以上疑问。
    该案例我们创建在/apollo/workspace/test文件下,命名为communication整体目录结构如下:

目录结构:

/apollo_workspace/
  |--test
  |   |--communication
  |   |  |--BUILD //cyber_test编译文件
      |  |--talker.cc //talker-listener通信实现
      |  |--listener.cc
      |  |--server.cc //server-client通信实现
      |  |--client.cc
      |  |--param_server.cc //parameter server-client通信实现
      |  |--param_client.cc 
      |--proto 
         |--BUILD //car.proto 编译文件
         |--car.proto  //小车数据定义的文件

通过第一节内容,我们知道了proto文件的使用方法,那么该章我们来自己编写一个proto文件,来实现我们“车”的变量定义,在该章的三种通信方式的案例中都是用这一数据定义。对car.proto文件进行编写,其内容如下:

2. 通信实现流程讲解

3. 编写C++话题通信实现

1. proto文件编写

首先我们要通过第一节内容,编写一个自己的proto文件,来实现我们“车”的变量定义,在本节中的三种通信方式的案例中都是用这一数据定义。对car.proto文件进行编写,其内容如下:

car.proto

// 定义proto使用的版本
syntax = "proto2";

//定义包名,在cc文件中调用
package apollo.cyber.test.proto;

//定义一个车的消息,车的型号,车主,车的车牌号,已跑公里数,车速
message Car{
    optional string plate = 1; 
    optional string type = 2;
    optional string owner = 3;
    optional uint64 kilometers = 4;
    optional uint64 speed = 5;
};

2. talker.cc 文件编写

我们来编写一个talker.cc来实现主动对话

talker.cc

//头文件引用
#include "test/proto/car.pb.h"
#include "cyber/cyber.h"
#include "cyber/time/rate.h"

//car数据定义的引用,可以看出其定义来源于一个proto
using apollo::cyber::examples::cyber_test_proto::Car;

int main(int argc, char *argv[]) {
  // 初始化一个cyber框架
    apollo::cyber::Init(argv[0]);
  
  // 创建talker节点
    auto talker_node = apollo::cyber::CreateNode("talker");
    
    // 从节点创建一个Topic,来实现对车速的查看
    auto talker = talker_node->CreateWriter<Car>("car_speed");
    AINFO << "I'll start telling you the current speed of the car.";
    
    //设置初始速度为0,然后速度每秒增加5km/h
    uint64_t speed = 0;
    while (apollo::cyber::OK()) {
        auto msg = std::make_shared<Car>();
        msg->set_speed(speed);
        //假设车速持续增加
        speed += 5;
        talker->Write(msg);
        sleep(1);
    }
    return 0;
}

3. listener.cc 文件编写

编写一个listener来实现对talker发送过来的内容进行接收。

listener.cc

#include "test/proto/car.pb.h"
#include "cyber/cyber.h"

using apollo::cyber::examples::cyber_test_proto::Car;

//接收到消息后的响应函数
void message_callback(
        const std::shared_ptr<Car>& msg) {
    AINFO << "now speed is: " << msg->speed();
}

int main(int argc, char* argv[]) {
    //初始化cyber框架
    apollo::cyber::Init(argv[0]);
 
    //创建监听节点
    auto listener_node = apollo::cyber::CreateNode("listener");
   
    //创建监听响应进行消息读取
    auto listener = listener_node->CreateReader<Car>(
            "car_speed", message_callback);
    apollo::cyber::WaitForShutdown();
    return 0;
}

4. 运行与测试

1. 编写bazel编译文件

编写在cyber/examples/cyber_test/BUILD中。

talker 和 listener 编译文件。

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("//tools/install:install.bzl", "install", "install_src_files")
load("//tools:cpplint.bzl", "cpplint")

package(default_visibility = ["//visibility:public"])

cc_binary(
    name = "talker",
    srcs = ["talker.cc"],
    deps = [
        "//cyber",
        "//test/proto:car_cc_proto",
    ],
    linkstatic = True,
)

cc_binary(
    name = "listener",
    srcs = ["listener.cc"],
    deps = [
        "//cyber",
        "//test/proto:car_cc_proto",
    ],
    linkstatic = True,
)

2. 编译:

aem start //启动docker环境
aem enter //进入docker环境

使用apollo 包管理开发方式提供的

buildtool build -p test/communication/

编译成功显示:
在这里插入图片描述

3. 运行:

首先,先将输出方法改为控制台输出。

export GLOG_alsologtostderr=1

打开两个终端,都进入Apollo的docker环境,一个终端运行talker,另一个运行listener,会发现listener运行后开始接收talker发送的小车速度的消息。

运行talker

./bazel-bin/test/communication/talker

终端显示:
在这里插入图片描述

运行listener

./bazel-bin/test/communication/listener

结果显示:
在这里插入图片描述
恭喜你,完成Apollo Cyber RT 的C++ 的话题通信实验喽!!!

5. 编写Python话题通信实现(小练习)

小测试!!!

6. 运行与测试

期待结果!!!

7. Apollo Cyber​​ RT框架文档



Apollo Cyber​​ RT框架是基于组件的概念构建的。作为Apollo Cyber​​ RT框架的基本构建块,每个组件都包含一个特定的算法模块,该模块处理一组数据输入并生成一组输出。

为了成功创建和启动新组件,需要执行四个基本步骤:

设置组件文件结构
实现组件类
设置配置文件
启动组件
下面的示例演示如何创建一个简单的组件,然后在屏幕上生成,运行和观看最终输出。如果您想了解有关Apollo Cyber​​ RT的更多信息,您可以在directory目录下找到几个示例,展示如何使用框架的不同功能/apollo/cyber/examples/。

注意:该示例必须在apollo docker环境中运行,并使用Bazel进行编译。

1、设置组件文件结构
请创建以下文件,假定位于目录下/apollo/cyber/examples/common_component_example/:

头文件:common_component_example.h
源文件:common_component_example.cc
构建文件:BUILD
DAG依赖项文件:common.dag
启动文件:common.launch
2、实现组件类
实现组件的头文件
实施common_component_example.h:

继承Component类
定义你自己Init和Proc功能。Proc函数需要指定其输入数据类型
通过使用将您的组件类注册为全局组件 CYBER_REGISTER_COMPONENT
#include <memory>
#include "cyber/class_loader/class_loader.h"
#include "cyber/component/component.h"
#include "cyber/examples/proto/examples.pb.h"

using apollo::cyber::examples::proto::Driver;
using apollo::cyber::Component;
using apollo::cyber::ComponentBase;

class CommonComponentSample : public Component<Driver, Driver> {
 public:
  bool Init() override;
  bool Proc(const std::shared_ptr<Driver>& msg0,
            const std::shared_ptr<Driver>& msg1) override;
};

CYBER_REGISTER_COMPONENT(CommonComponentSample)



Proc类似于回调函数;

实现示例组件的源文件
对于common_component_example.cc,都必须同时实现Init和Proc功能。

#include "cyber/examples/common_component_example/common_component_example.h"
#include "cyber/class_loader/class_loader.h"
#include "cyber/component/component.h"

bool CommonComponentSample::Init() {
  AINFO << "Commontest component init";
  return true;
}

bool CommonComponentSample::Proc(const std::shared_ptr<Driver>& msg0,
                               const std::shared_ptr<Driver>& msg1) {
  AINFO << "Start common component Proc [" << msg0->msg_id() << "] ["
        << msg1->msg_id() << "]";
  return true;
}

为示例组件创建构建文件
创建bazel BUILD文件。

load("//tools:cpplint.bzl", "cpplint")

package(default_visibility = ["//visibility:public"])

cc_binary(
    name = "libcommon_component_example.so",
    deps = [":common_component_example_lib"],
    linkopts = ["-shared"],
    linkstatic = False,
)

cc_library(
    name = "common_component_example_lib",
    srcs = [
        "common_component_example.cc",
    ],
    hdrs = [
        "common_component_example.h",
    ],
    deps = [
        "//cyber",
        "//cyber/examples/proto:examples_cc_proto",
    ],
)

cpplint()


3、设置配置文件
配置DAG依赖文件
要配置DAG依赖文件(common.dag),请指定以下各项:

通道名称:用于数据输入和输出
库路径:从组件类构建的库
类名:组件的类名
# Define all coms in DAG streaming.
component_config {
    component_library : "/apollo/bazel-bin/cyber/examples/common_component_example/libcommon_component_example.so"
    components {
        class_name : "CommonComponentSample"
        config {
            name : "common"
            readers {
                channel: "/apollo/prediction"
            }
            readers {
                channel: "/apollo/test"
            }
        }
    }
}

配置启动文件
要配置启动(common.launch)文件,请指定以下各项:

组件名称
您在上一步中刚刚创建的dag文件。
组件在其中运行的进程的名称
<cyber>
    <component>
        <name>common</name>
        <dag_conf>/apollo/cyber/examples/common_component_example/common.dag</dag_conf>
        <process_name>common</process_name>
    </component>
</cyber>


4、启动组件
通过运行以下命令来构建组件:

bash /apollo/apollo.sh build

注意:确保示例组件构建良好

然后配置环境:

cd /apollo/cyber
source setup.bash

有两种启动组件的方法:

与启动文件一起启动(推荐)

cyber_launch start /apollo/cyber/examples/common_component_example/common.launch

使用DAG文件启动

mainboard -d /apollo/cyber/examples/common_component_example/common.dag

参考案例:

https://github.com/gruminions/apollo/blob/record/cyber/examples/talker.cc

四、Apollo Cyber RT下服务通信的实现

么叫服务通信
        定义:以请求响应的方式实现不同节点间的数据交互通信模式,用于偶然触发,高实时性要求的场景。

        俗解:不同于前面的不间断收发,假设存在客户端与服务端,他们之间是通过话题通信链接起来,此时客户端产生了新的需求并发送给服务端,服务端进行内部响应,响应完成后在传送回客户端。

        实列分析:自动驾驶过程中,客户端通过手机发出修改目的地的请求,服务端接收请求,并进行路径的规划,规划完毕后再返还给服务端,服务端在发送结果给客户端。

 案例分析以及解决方案
        案例需求
        需求:客户端发送数字,服务端接收数据并进行数据的求和处理,将结果响应并返还至客户端。

         流程思路:该流程适用于CPP与PY的程序编写

        •编写消息载体protobuf文件并进行配置

        •编写服务端代码

        •编写客户端代码

        •编译并执行

Protobuf文件的编写
        新建一个demo_pro的文件夹,这个文件夹是用来存放protobuf的相关文件,且在该文件夹下面创建昊BUILD文件和protobuf文件。

 创建proto文件


// 申明版本号
syntax = "proto2";
//申明包
package apollo.cyber.demo_SC.demo_SC_pro;
 
//创建请求消息
message AddInts_Request {
    //两个整形数据
    required int64 num1 =1;
    required int64 num2 =2;
}
//创建响应消息
message AddInts_Response {
    //一个整形数据
    required int64 sum =1;
}
        他的主要功能是创建一个发布方与响应方的变量名

        Build的内容如下:

load ("//tools:python_rules.bzl", "py_proto_library")
package (default_visibility = ["//visibility:public"])
 
proto_library(
    name = "addints_proto",
    srcs = ["SC_addinits.proto"]
)
 
cc_proto_library(
   name = "addints_cc",
    deps = [":addints_proto"]
)
 
py_proto_library(
    name = "addints_py",
   deps = [":addints_proto"]
)

 客户端代码分析

        客户端主要由BUILD文件demo_cc_SC.cpp组成

        其中核心部分是demo_cc_SC.cpp其详细代码如下

/**
 * @file demo_cc_SC.cpp
 * @author bobo
 * @brief 客户端发送请求,提交两个整数,服务端接收请求,提取数字相加后将结果响应回客户端
 * @version 0.1
 * @date 2022-07-05
 * 
 *      实现流程
 *      头文件包含
 *      初始化
 *      创建节点
 *      创建服务方
 *      数据处理
 *      等待关闭
 */
 
#include "cyber/cyber.h"
#include "cyber/demo_SC/demo_SC_pro/SC_addinits.pb.h"
 
//调用了proto中的两个message
using apollo::cyber::demo_SC::demo_SC_pro::AddInts_Request;
using apollo::cyber::demo_SC::demo_SC_pro::AddInts_Response;
 
void cb(const std::shared_ptr<AddInts_Request>&request , const std::shared_ptr<AddInts_Response>&response){
 
    int64_t num1 =request ->num1();
    int64_t num2 =request ->num2();
    
    AINFO << "客户请求num1 = "<< num1 << "num2 = "<<num2;
    //设置求和并响应
    int16_t sum = num1 +num2;
 
    response ->set_sum(sum);
}
 
int main(int argc, char*argv[]){
    //初始化
    apollo::cyber::Init(argv[0]);
    AINFO << "初始化完成";
    //创建节点
    auto server_node = apollo::cyber::CreateNode("server_node");
    //设置服务方                               提交的数据类型       响应的数据类型     字符串(话题)通过话题服务方和客户端关联在一起
    auto server = server_node ->CreateService<AddInts_Request, AddInts_Response>("addints",cb);
 
    //数据处理
 
    //等待关闭
    apollo::cyber::WaitForShutdown();
 
    return 0;
}
服务端代码分析 

         服务端主要实现接收数据并进行计算的功能,其主要流程本质上是与客户端代码一致的

/**
 * @file demo_cc_CS.cc
 * @author bobo
 * @brief 
 * @version 0.1
 * @date 2022-07-05
 * 包含头文件
 * 初始化cyber
 * 创建客户端节点
 * 创建发布方
 * 发送数据
 * 等待关闭 
 */
#include "cyber/cyber.h"
#include "cyber/demo_SC/demo_SC_pro/SC_addinits.pb.h"
 
using apollo::cyber::demo_SC::demo_SC_pro::AddInts_Request;
using apollo::cyber::demo_SC::demo_SC_pro::AddInts_Response;
 
int main(int argc, char* argv[]){
 
    apollo::cyber::Init(argv[0]);
    //处理输入的参数
        if(argc != 3){
            AINFO<<"执行程序时请输入两个数字作为参数";
            return 1;
        }
 
        AINFO<<"客户端。。。。。。。。。。。。。。。。。。";
        //创建客户端节点
        auto client_node = apollo::cyber::CreateNode("client_node");
        //创建发布方
        auto client =client_node->CreateClient<AddInts_Request,AddInts_Response>("addints");
        //发送数据
        AINFO <<"等待服务启动";
 
        auto request = std::make_shared<AddInts_Request>();
 
        request ->set_num1(atoll(argv[1]));
        request ->set_num2(atoll(argv[2]));
 
        AINFO<<"发送num1"<<request ->num1() <<"num2 = " << request ->num2() ;
        auto response = client ->SendRequest(request);
        //处理响应
        AINFO << "响应结果: sum = "<<response ->sum() ;
 
        //等待关闭
        apollo::cyber::WaitForShutdown();
 
        return 0;
}
         BUILD的内容如下所示:

# https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary
#***********************服务通信************************ #
cc_binary(
    name = "demo_SC",
    srcs = ["demo_cc_SC.cc"],
    deps = ["//cyber",
                "//cyber/demo_SC/demo_SC_pro:addints_cc",
               ],
)
 
cc_binary(
    name = "demo_CS",
    srcs = ["demo_cc_CS.cc"],
    deps = ["//cyber",
                "//cyber/demo_SC/demo_SC_pro:addints_cc",
               ],

)

编译上述程序得到如下文件
        执行可执行文件 demo_SC与demo_CS,这里需要注意,要先执行服务端,再执行客户端,要不然会产生错误,这种错误是因为代码本身的执行逻辑就不支持这两者调换顺序,此外执行demo_CS时需要给定 两个 int64的参数,这样demo_SC才能有显示。
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值