fastdds的快速使用
1.1。什么是 DDS?
数据分发服务 (DDS)是一种以 数据为中心的通信协议,用于分布式软件应用程序通信。它描述了支持数据提供者和数据消费者之间通信的通信应用程序编程接口 (API) 和通信语义。
由于它是一个以数据为中心的发布订阅 (DCPS) 模型,因此在其实现中定义了三个关键应用实体:发布实体,定义信息生成对象及其属性;订阅实体,它定义了信息消费对象及其属性;和配置实体,定义作为主题传输的信息类型,并使用其服务质量 (QoS) 属性创建发布者和订阅者,确保上述实体的正确性能。
DDS 使用 QoS 来定义 DDS 实体的行为特征。QoS 由单独的 QoS 策略(源自 QoSPolicy 的类型的对象)组成。这些在Policy中描述。
1.1.1。DCPS 概念模型
在 DCPS 模型中,为开发通信应用系统定义了四个基本要素。
出版商。它是负责创建和配置其实现的DataWriters的 DCPS 实体。DataWriter是负责实际发布消息的实体。每个人都有一个分配的主题,在该主题下发布消息。有关详细信息,请参阅发布者。
订户。它是 DCPS 实体,负责接收在其订阅的主题下发布的数据。它为一个或多个DataReader对象提供服务,这些对象负责将新数据的可用性传达给应用程序。有关详细信息,请参阅订阅者。
主题。它是绑定发布和订阅的实体。它在 DDS 域中是唯一的。通过TopicDescription,它允许发布和订阅的数据类型的统一。有关详细信息,请参阅主题。
域。这是用于链接所有发布者和订阅者的概念,属于一个或多个应用程序,它们在不同主题下交换数据。这些参与域的单个应用程序称为DomainParticipant。DDS 域由域 ID 标识。DomainParticipant 定义域 ID 以指定它所属的 DDS 域。具有不同 ID 的两个 DomainParticipants 不知道彼此在网络中的存在。因此,可以创建多个通信通道。这适用于涉及多个DDS应用程序的场景,它们各自的DomainParticipants相互通信,但这些应用程序不得干扰。域参与者充当其他 DCPS 实体的容器,充当 发布者、订阅者和主题实体的工厂,并在域中提供管理服务。有关详细信息,请参阅域。
1.2. 什么是 RTPS
为支持 DDS 应用程序而开发的实时发布订阅 (RTPS)协议是一种发布订阅通信中间件,它通过 UDP/IP 等尽力传输传输。此外,Fast DDS 还支持 TCP 和共享内存 (SHM) 传输。
它旨在支持单播和多播通信。
在继承自 DDS 的 RTPS 顶部,可以找到域,它定义了一个单独的通信平面。几个域可以同时独立地共存。一个域包含任意数量的RTPSParticipants,即能够发送和接收数据的元素。为此,RTPSParticipants 使用他们的Endpoints:
RTPSWriter:能够发送数据的端点。
RTPSReader:能够接收数据的端点。
RTPSParticipant 可以有任意数量的写入器和读取器端点。
通信围绕主题进行,主题定义和标记正在交换的数据。主题不属于特定参与者。参与者通过 RTPSWriters 对主题下发布的数据进行更改,并通过 RTPSReaders 接收与其订阅的主题相关的数据。通信单元称为Change,它表示在 Topic 下写入的数据的更新。 RTPSReaders/RTPSWriters在其History上注册这些更改,这是一种用作最近更改缓存的数据结构。
在eProsima Fast DDS的默认配置中,当您通过 RTPSWriter 端点发布更改时,会在后台执行以下步骤:
更改将添加到 RTPSWriter 的历史缓存中。
RTPSWriter 将更改发送到它知道的任何 RTPSReaders。
接收到数据后,RTPSReaders 用新的变化更新他们的历史缓存。
但是,Fast DDS 支持多种配置,允许您更改 RTPSWriters/RTPSReaders 的行为。修改 RTPS 实体的默认配置意味着 RTPSWriters 和 RTPSReaders 之间的数据交换流发生变化。此外,通过选择服务质量 (QoS) 策略,您可以通过多种方式影响这些历史缓存的管理方式,但通信循环保持不变。您可以继续阅读RTPS 层部分,了解更多关于快速 DDS 中 RTPS 协议的实现。
1.3. 编写一个简单的 C++ 发布者和订阅者应用程序
1.3.1。背景
DDS 是实现 DCPS 模型的以数据为中心的通信中间件。该模型基于发布者的开发,这是一个数据生成元素;和一个订阅者,一个数据消费元素。这些实体通过主题进行通信,主题是绑定两个 DDS 实体的元素。发布者在主题下生成信息,订阅者订阅同一主题以接收信息
1.3.2. 先决条件
首先,您需要按照安装手册中列出的步骤安装 eProsima Fast DDS及其所有依赖项。您还需要完成安装手册中列出的安装 eProsima Fast DDS-Gen工具的步骤。此外,本教程中提供的所有命令都针对 Linux 环境进行了概述。
1.3.4。导入链接库及其依赖项
DDS 应用程序需要 Fast DDS 和 Fast CDR 库。根据安装过程,使这些库可用于我们的 DDS 应用程序的过程将略有不同。在 Linux 上,可以在目录/usr/include/fastrtps/和 /usr/include/fastcdr/中分别找到 Fast DDS 和 Fast CDR 的头文件。两者的编译库都可以在目录/usr/lib/中找到
1.3.5。配置 CMake 项目
cmake_minimum_required(VERSION 3.12.4)if(NOT CMAKE_VERSION VERSION_LESS 3.0)cmake_policy(SET CMP0048 NEW)
endif()project(DDSHelloWorld)# Find requirements
if(NOT fastcdr_FOUND)find_package(fastcdr REQUIRED)
endif()if(NOT fastrtps_FOUND)find_package(fastrtps REQUIRED)
endif()# Set C++11
include(CheckCXXCompilerFlag)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG ORCMAKE_CXX_COMPILER_ID MATCHES "Clang")check_cxx_compiler_flag(-std=c++11 SUPPORTS_CXX11)if(SUPPORTS_CXX11)add_compile_options(-std=c++11)else()message(FATAL_ERROR "Compiler doesn't support C++11")endif()
endif()
IDL通过生成的数据文件为:
这必须生成以下文件:HelloWorld.cxx:HelloWorld 类型定义。HelloWorld.h:HelloWorld.cxx 的头文件。HelloWorldPubSubTypes.cxx:HelloWorld 类型的序列化和反序列化代码。HelloWorldPubSubTypes.h:HelloWorldPubSubTypes.cxx 的头文件。
pub端的代码
// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.1415/**16 * @file HelloWorldPublisher.cpp17 *18 */1920#include "HelloWorldPubSubTypes.h"2122#include <fastdds/dds/domain/DomainParticipantFactory.hpp>23#include <fastdds/dds/domain/DomainParticipant.hpp>24#include <fastdds/dds/topic/TypeSupport.hpp>25#include <fastdds/dds/publisher/Publisher.hpp>26#include <fastdds/dds/publisher/DataWriter.hpp>27#include <fastdds/dds/publisher/DataWriterListener.hpp>2829using namespace eprosima::fastdds::dds;3031class HelloWorldPublisher32{33private:3435 HelloWorld hello_;3637 DomainParticipant* participant_;3839 Publisher* publisher_;4041 Topic* topic_;4243 DataWriter* writer_;4445 TypeSupport type_;4647 class PubListener : public DataWriterListener48 {49 public:5051 PubListener()52 : matched_(0)53 {54 }5556 ~PubListener() override57 {58 }5960 void on_publication_matched(61 DataWriter*,62 const PublicationMatchedStatus& info) override63 {64 if (info.current_count_change == 1)65 {66 matched_ = info.total_count;67 std::cout << "Publisher matched." << std::endl;68 }69 else if (info.current_count_change == -1)70 {71 matched_ = info.total_count;72 std::cout << "Publisher unmatched." << std::endl;73 }74 else75 {76 std::cout << info.current_count_change77 << " is not a valid value for PublicationMatchedStatus current count change." << std::endl;78 }79 }8081 std::atomic_int matched_;8283 } listener_;8485public:8687 HelloWorldPublisher()88 : participant_(nullptr)89 , publisher_(nullptr)90 , topic_(nullptr)91 , writer_(nullptr)92 , type_(new HelloWorldPubSubType())93 {94 }9596 virtual ~HelloWorldPublisher()97 {98 if (writer_ != nullptr)99 {
100 publisher_->delete_datawriter(writer_);
101 }
102 if (publisher_ != nullptr)
103 {
104 participant_->delete_publisher(publisher_);
105 }
106 if (topic_ != nullptr)
107 {
108 participant_->delete_topic(topic_);
109 }
110 DomainParticipantFactory::get_instance()->delete_participant(participant_);
111 }
112
113 //!Initialize the publisher
114 bool init()
115 {
116 hello_.index(0);
117 hello_.message("HelloWorld");
118
119 DomainParticipantQos participantQos;
120 participantQos.name("Participant_publisher");
121 participant_ = DomainParticipantFactory::get_instance()->create_participant(0, participantQos);
122
123 if (participant_ == nullptr)
124 {
125 return false;
126 }
127
128 // Register the Type
129 type_.register_type(participant_);
130
131 // Create the publications Topic
132 topic_ = participant_->create_topic("HelloWorldTopic", "HelloWorld", TOPIC_QOS_DEFAULT);
133
134 if (topic_ == nullptr)
135 {
136 return false;
137 }
138
139 // Create the Publisher
140 publisher_ = participant_->create_publisher(PUBLISHER_QOS_DEFAULT, nullptr);
141
142 if (publisher_ == nullptr)
143 {
144 return false;
145 }
146
147 // Create the DataWriter
148 writer_ = publisher_->create_datawriter(topic_, DATAWRITER_QOS_DEFAULT, &listener_);
149
150 if (writer_ == nullptr)
151 {
152 return false;
153 }
154 return true;
155 }
156
157 //!Send a publication
158 bool publish()
159 {
160 if (listener_.matched_ > 0)
161 {
162 hello_.index(hello_.index() + 1);
163 writer_->write(&hello_);
164 return true;
165 }
166 return false;
167 }
168
169 //!Run the Publisher
170 void run(
171 uint32_t samples)
172 {
173 uint32_t samples_sent = 0;
174 while (samples_sent < samples)
175 {
176 if (publish())
177 {
178 samples_sent++;
179 std::cout << "Message: " << hello_.message() << " with index: " << hello_.index()
180 << " SENT" << std::endl;
181 }
182 std::this_thread::sleep_for(std::chrono::milliseconds(1000));
183 }
184 }
185};
186
187int main(
188 int argc,
189 char** argv)
190{
191 std::cout << "Starting publisher." << std::endl;
192 int samples = 10;
193
194 HelloWorldPublisher* mypub = new HelloWorldPublisher();
195 if(mypub->init())
196 {
197 mypub->run(static_cast<uint32_t>(samples));
198 }
199
200 delete mypub;
201 return 0;
202}
每一部分的功能:
DomainParticipantFactory. 允许创建和销毁 DomainParticipant 对象。DomainParticipant. 充当所有其他实体对象的容器以及发布者、订阅者和主题对象的工厂。TypeSupport. 为参与者提供序列化、反序列化和获取特定数据类型的密钥的功能。Publisher. 它是负责创建 DataWriters 的对象。DataWriter. 允许应用程序设置要在给定主题下发布的数据的值。DataWriterListener. 允许重新定义 DataWriterListener 的功能。
派生类的私有成员
类的私有数据成员,hello_数据成员被定义为 HelloWorld类的一个对象,它定义了我们用 IDL 文件创建的数据类型。接下来定义参与者、发布者、主题、DataWriter和数据类型对应的私有数据成员。类的type_对象TypeSupport是将用于在 DomainParticipant 中注册主题数据类型的对象
PubListener通过从该类继承来定义DataWriterListener该类。此类覆盖默认的 DataWriter 侦听器回调,允许在发生事件时执行例程。当检测到新的 DataReader 正在侦听 DataWriter 正在发布的主题时,重写的回调on_publication_matched() 允许定义一系列操作。检测与 DataWriter 匹配的 DataReader的info.current_count_change()这些更改。这是MatchedStatus允许跟踪订阅状态更改的结构中的成员。最后,listener_类的对象被定义为 的实例PubListener
类的公共构造函数和析构函数HelloWorldPublisher定义如下。构造函数将类的私有数据成员初始化nullptr为 ,TypeSupport 对象除外,它被初始化为HelloWorldPubSubType类的实例。类析构函数删除这些数据成员,从而清理系统内存
初始化函数
初始化 HelloWorld 类型hello_结构成员的内容。通过 DomainParticipant 的 QoS 为参与者分配名称。使用DomainParticipantFactory创建参与者。注册 IDL 中定义的数据类型。为出版物创建主题。创建发布者。使用先前创建的侦听器创建 DataWriter。
为了发布,实现了公共成员功能publish()。在 DataWriter 的侦听器回调中,表明 DataWriter 已与侦听发布主题的 DataReader 匹配,数据成员matched_被更新。它包含发现的 DataReader 的数量。因此,当发现第一个 DataReader 时,应用程序开始发布。这只是由 DataWriter 对象写入更改
sub订阅者
// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.1415/**16 * @file HelloWorldSubscriber.cpp17 *18 */1920#include "HelloWorldPubSubTypes.h"2122#include <fastdds/dds/domain/DomainParticipantFactory.hpp>23#include <fastdds/dds/domain/DomainParticipant.hpp>24#include <fastdds/dds/topic/TypeSupport.hpp>25#include <fastdds/dds/subscriber/Subscriber.hpp>26#include <fastdds/dds/subscriber/DataReader.hpp>27#include <fastdds/dds/subscriber/DataReaderListener.hpp>28#include <fastdds/dds/subscriber/qos/DataReaderQos.hpp>29#include <fastdds/dds/subscriber/SampleInfo.hpp>3031using namespace eprosima::fastdds::dds;3233class HelloWorldSubscriber34{35private:3637 DomainParticipant* participant_;3839 Subscriber* subscriber_;4041 DataReader* reader_;4243 Topic* topic_;4445 TypeSupport type_;4647 class SubListener : public DataReaderListener48 {49 public:5051 SubListener()52 : samples_(0)53 {54 }5556 ~SubListener() override57 {58 }5960 void on_subscription_matched(61 DataReader*,62 const SubscriptionMatchedStatus& info) override63 {64 if (info.current_count_change == 1)65 {66 std::cout << "Subscriber matched." << std::endl;67 }68 else if (info.current_count_change == -1)69 {70 std::cout << "Subscriber unmatched." << std::endl;71 }72 else73 {74 std::cout << info.current_count_change75 << " is not a valid value for SubscriptionMatchedStatus current count change" << std::endl;76 }77 }7879 void on_data_available(80 DataReader* reader) override81 {82 SampleInfo info;83 if (reader->take_next_sample(&hello_, &info) == ReturnCode_t::RETCODE_OK)84 {85 if (info.valid_data)86 {87 samples_++;88 std::cout << "Message: " << hello_.message() << " with index: " << hello_.index()89 << " RECEIVED." << std::endl;90 }91 }92 }9394 HelloWorld hello_;9596 std::atomic_int samples_;9798 } listener_;99
100public:
101
102 HelloWorldSubscriber()
103 : participant_(nullptr)
104 , subscriber_(nullptr)
105 , topic_(nullptr)
106 , reader_(nullptr)
107 , type_(new HelloWorldPubSubType())
108 {
109 }
110
111 virtual ~HelloWorldSubscriber()
112 {
113 if (reader_ != nullptr)
114 {
115 subscriber_->delete_datareader(reader_);
116 }
117 if (topic_ != nullptr)
118 {
119 participant_->delete_topic(topic_);
120 }
121 if (subscriber_ != nullptr)
122 {
123 participant_->delete_subscriber(subscriber_);
124 }
125 DomainParticipantFactory::get_instance()->delete_participant(participant_);
126 }
127
128 //!Initialize the subscriber
129 bool init()
130 {
131 DomainParticipantQos participantQos;
132 participantQos.name("Participant_subscriber");
133 participant_ = DomainParticipantFactory::get_instance()->create_participant(0, participantQos);
134
135 if (participant_ == nullptr)
136 {
137 return false;
138 }
139
140 // Register the Type
141 type_.register_type(participant_);
142
143 // Create the subscriptions Topic
144 topic_ = participant_->create_topic("HelloWorldTopic", "HelloWorld", TOPIC_QOS_DEFAULT);
145
146 if (topic_ == nullptr)
147 {
148 return false;
149 }
150
151 // Create the Subscriber
152 subscriber_ = participant_->create_subscriber(SUBSCRIBER_QOS_DEFAULT, nullptr);
153
154 if (subscriber_ == nullptr)
155 {
156 return false;
157 }
158
159 // Create the DataReader
160 reader_ = subscriber_->create_datareader(topic_, DATAREADER_QOS_DEFAULT, &listener_);
161
162 if (reader_ == nullptr)
163 {
164 return false;
165 }
166
167 return true;
168 }
169
170 //!Run the Subscriber
171 void run(
172 uint32_t samples)
173 {
174 while(listener_.samples_ < samples)
175 {
176 std::this_thread::sleep_for(std::chrono::milliseconds(100));
177 }
178 }
179};
180
181int main(
182 int argc,
183 char** argv)
184{
185 std::cout << "Starting subscriber." << std::endl;
186 int samples = 10;
187
188 HelloWorldSubscriber* mysub = new HelloWorldSubscriber();
189 if(mysub->init())
190 {
191 mysub->run(static_cast<uint32_t>(samples));
192 }
193
194 delete mysub;
195 return 0;
196}
Subscriber. 它是负责创建和配置 DataReader 的对象。
DataReader. 它是负责实际接收数据的对象。它在应用程序中注册标识要读取的数据的主题(TopicDescription)并访问订阅者接收到的数据。
DataReaderListener. 这是分配给数据读取器的侦听器。
DataReaderQoS. 定义 DataReader 的 QoS 的结构。
SampleInfo. 它是伴随每个样本“读取”或“获取”的信息。
从类的私有数据成员开始,值得一提的是数据读取监听器的实现。类的私有数据成员将是参与者、订阅者、主题、数据读取器和数据类型。与数据写入器的情况一样,侦听器实现了要在事件发生时执行的回调。SubListener 的第一个被覆盖的回调是on_subscription_matched(),它类似于on_publication_matched()DataWriter 的回调
void on_subscription_matched(DataReader*,const SubscriptionMatchedStatus& info) override
{if (info.current_count_change == 1){std::cout << "Subscriber matched." << std::endl;}else if (info.current_count_change == -1){std::cout << "Subscriber unmatched." << std::endl;}else{std::cout << info.current_count_change<< " is not a valid value for SubscriptionMatchedStatus current count change" << std::endl;}
}
第二个被覆盖的回调是on_data_available(). 在此,数据读取器可以访问的下一个接收到的样本被获取并处理以显示其内容。在这里SampleInfo定义了类的对象,它决定了一个样本是否已经被读取或获取。每次读取样本时,接收样本的计数器都会增加
void on_data_available(DataReader* reader) override
{SampleInfo info;if (reader->take_next_sample(&hello_, &info) == ReturnCode_t::RETCODE_OK){if (info.valid_data){samples_++;std::cout << "Message: " << hello_.message() << " with index: " << hello_.index()<< " RECEIVED." << std::endl;}}
}
fastdds的快速使用相关推荐
- FastDDS的服务器记录-译-
discourse.ros.org/t/fastdds-without-discovery-server/26117/9 有疑问如下: 我一直在以多种方式与 FastDDS(在 ROS2 Humble ...
- 优雅的玩转Fast-DDS
优雅的玩转Fast-DDS 安装依赖 sudo apt install cmake g++ python3-pip wget gitpip3 install -U colcon-common-exte ...
- 快速排查feign.FeignException: status 500 …
feign.FeignException: status 500 - 总结一下feign报500的时候快速排查问题的方法, 这个bug容易出现的地方分别为: 1. 远程调用的时候feign的注册信息有 ...
- python中如何对复杂的json数据快速查找key对应的value值(使用JsonSearch包)
前言 之前在实际的项目研发中,需要对一些复杂的json数据进行取值操作,由于json数据的层级很深,所以经常取值的代码会变成类似这样: value = data['store']['book'][0] ...
- 如何利用python的newspaper包快速爬取网页数据
文章目录 前言 一个爬取新闻网页数据的神器 小试牛刀 如何快速安装 windows安装 Debian / Ubuntu安装 OSX安装 体验更多的功能 前言 随着越来的进行自然语言处理相关方面的研究, ...
- 【快速上手mac必备】常用优质mac软件推荐(音视频、办公、软件开发、辅助工具、系统管理、云存储)
本文章的主要内容是我作为一名大四学生.准程序员.up主这三种身份来给大家推荐一下 mac 上好用的软件以及工具.本人也是从去年9月份开始从windows阵营转移到了mac阵营,刚开始使用的时候,也曾主 ...
- 容器云原生DevOps学习笔记——第二期:如何快速高质量的应用容器化迁移
暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...
- 面试高频——JUC并发工具包快速上手(超详细总结)
目录 一.什么是JUC 二.基本知识 2.1.进程和线程 2.2.Java默认有两个进程 2.3.Java能够开启线程吗? 2.4.并发和并行 2.5.线程的状态 2.6.wait和sleep的区别 ...
- Shiro第一个程序:官方快速入门程序Qucickstart详解教程
目录 一.下载解压 二.第一个Shiro程序 1. 导入依赖 2. 配置shiro配置文件 3. Quickstart.java 4. 启动测试 三.shiro.ini分析 四.Quickstart. ...
最新文章
- js如何获得FCKeditor控件的值
- MyBatis1:MyBatis入门
- [javaweb] servlet介绍与servlet的继承关系 和 service 方法 (一)
- VTK:Shaders之SphereMap
- 使用Freemarker来页面静态化,与Spring整合使用
- HTTP协议中的Content-Encoding
- PHP以xml形式获取POST数据
- php和html开发工具,常用的php开发工具有哪些?
- : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server versi
- E: 无法打开锁文件 /var/lib/dpkg/lock-frontend - open (2: 没有那个文件或目录)
- Codeforces 831 A Unimodal Array
- deepinV20 显卡驱动 cuda10.2+cudnn配置
- jbox弹窗_强大的jquery弹出层插件jBox
- 最简单最适合纯小白的postman使用方法(测试接口的不二利器)(从介绍到下载到使用的详细教程)
- 电脑开机蓝屏代码C000021a
- 数据结构基础之迭代法归并排序
- MacOS下iterm,Dracula主题配置
- D3D基本矩阵函数和显卡硬件术语
- android shpe 三角形_在Android中制作三角形按钮
- 2011年30家最能赚钱移动互联公司排行榜