前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++】DDS:FastDDS环境配置与使用示例

【C++】DDS:FastDDS环境配置与使用示例

作者头像
DevFrank
发布2024-07-24 15:18:03
770
发布2024-07-24 15:18:03
举报
文章被收录于专栏:C++开发学习交流

1. FastDDS介绍

官方地址:https://www.eprosima.com/index.php/company-all/news/146-fast-rtps-is-now-fast-dds

API地址:https://fast-dds.docs.eprosima.com/en/latest/

FastDDS的前身是Fast-RTPS,实现了许多 DDS 规范。它是一种高性能的实时发布订阅框架。

FastDDS(Fast Data Distribution Service)是一种高性能、可扩展的数据分发服务,它实现了 OMG DDS(Object Management Group Data Distribution Service)标准。它是一个开源项目,旨在提供实时数据通信和消息传递的解决方案。

FastDDS 的主要特点和功能包括:

1.高性能:Fast DDS 使用基于发布-订阅模式的数据分发机制,支持快速、可靠的数据交换。它的设计目标是提供低延迟和高吞吐量的数据传输,以满足实时性要求高的应用场景。

2.可扩展性:Fast DDS 具有良好的可扩展性,可以适应不同规模和复杂度的系统。它支持多种通信模式和拓扑结构,并提供灵活的配置选项,以满足各种应用需求。

3.安全性:Fast DDS 提供了可靠的数据传输和身份验证机制,以确保数据的机密性和完整性。它支持加密和访问控制,保护敏感数据不受未授权方访问。

4.多语言支持:Fast DDS 支持多种编程语言,包括 C++、Java、Python 等,使得开发人员可以在不同的编程环境中使用 Fast DDS 进行开发。

5.高度可定制:Fast DDS 提供了丰富的配置选项和可扩展的插件机制,使用户能够根据具体需求进行自定义扩展和功能增强。

FastDDS 在实时数据通信领域具有广泛的应用,特别适用于分布式系统、实时控制和监控系统、机器人技术、物联网等领域。它为开发人员提供了一个可靠、高性能的数据分发平台,简化了实时数据交换的开发和集成过程。

2. 环境安装

FastDDS有bin、source、docker image三种安装方式。

这里采用bin安装,版本2.8.1。

下载地址:https://www.eprosima.com/index.php/component/ars/repository/eprosima-fast-dds/eprosima-fast-dds-2-8-1

在这里插入图片描述
在这里插入图片描述

安装包里,install.sh会自动安装各种依赖,然后进入src目录下,分别构建以下库:

  • foonathan_memory_vendor,一个 STL 兼容的 C++ 内存分配器 库。
  • fastcdr,一个根据 CDR 标准进行数据序列化的 C++ 库。
  • fastrtps,eProsima Fast DDS库的核心库。
  • fastddsgen,一个使用 IDL 文件中定义的数据类型生成源代码的 Java 应用程序。

执行install.h需要cmake 3.11以上的版本,如果版本低的话需要先升级cmake:http://t.csdn.cn/LezV9

代码语言:javascript
复制
# 下载cmake
wget https://cmake.org/files/v3.22/cmake-3.22.1.tar.gz
sudo tar -xvzf cmake-3.22.1.tar.gz -C /usr/share
cd /usr/share/cmake-3.22.1
# 安装cmake
sudo chmod 777 ./configure
sudo ./configure
sudo make
sudo make install
sudo update-alternatives --install /usr/bin/cmake cmake /usr/local/bin/cmake 1 --force
cmake --version

安装fastdds:

代码语言:javascript
复制
sudo ./install.sh
# 安装了:git、build-essential、cmake、libssl-dev、libasio-dev、libtinyxml2-dev、openjdk-8-jre-headless、foonathan_memory_vendor、fastcdr、fastrtps(Fast DDS)、fastddsgen。
# 如果要测试FastDDS中的examples,需要在install.sh脚本脚本中打开该选项,默认为OFF。
在这里插入图片描述
在这里插入图片描述

安装包也提供了./uninstall.sh脚本,可随时卸载。

参考:https://www.jianshu.com/p/b9eb5dd9559f

3. 应用示例

官方示例
代码语言:javascript
复制
# 下载示例
git clone https://ghproxy.com/https://github.com/wanghuohuo0716/fastdds_helloworld.git
cd fastdds_helloworld
mkdir -p include/idl_generate/
cd idl/
fastddsgen -d ../include/idl_generate/  HelloWorld.idl	# -d选项指示生成的头文件保存目录
# 根据IDL文件生成接口文件后,同一个终端内接着编译FastDDS程序。
cd ..
mkdir build && cd build
cmake .. 
make

运行Publisher和Subscriber节点:

代码语言:javascript
复制
cd build/
./DDSHelloWorldPublisher
./DDSHelloWorldSubscriber
创建发布和订阅示例
代码语言:javascript
复制
mkdir ddstest
touch HelloWorld.idl
touch HelloWorldPublisher.cpp
touch HelloWorldSubscriber.cpp
touch CMakeLists.txt

HelloWorld.idl

代码语言:javascript
复制
module HelloWorldModule {

struct HelloWorld
{
    unsigned long index;
    string message;
};

};

HelloWorldPublisher.cpp

代码语言:javascript
复制
/**
 * @file HelloWorldPublisher.cpp
 *
 */

#include "./build/HelloWorld.h"
#include "./build/HelloWorldPubSubTypes.h"

#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/topic/TypeSupport.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/publisher/DataWriter.hpp>
#include <fastdds/dds/publisher/DataWriterListener.hpp>

using namespace eprosima::fastdds::dds;

class HelloWorldPublisher
{
private:
    HelloWorld hello_;

    DomainParticipant *participant_;

    Publisher *publisher_;

    Topic *topic_;

    DataWriter *writer_;

    TypeSupport type_;

    class PubListener : public DataWriterListener
    {
    public:
        PubListener()
            : matched_(0)
        {
        }

        ~PubListener() override
        {
        }

        void on_publication_matched(
            DataWriter *,
            const PublicationMatchedStatus &info) override
        {
            if (info.current_count_change == 1)
            {
                matched_ = info.total_count;
                std::cout << "Publisher matched." << std::endl;
            }
            else if (info.current_count_change == -1)
            {
                matched_ = info.total_count;
                std::cout << "Publisher unmatched." << std::endl;
            }
            else
            {
                std::cout << info.current_count_change << " is not a valid value for PublicationMatchedStatus current count change." << std::endl;
            }
        }

        std::atomic_int matched_;

    } listener_;

public:
    HelloWorldPublisher()
        : participant_(nullptr), publisher_(nullptr), topic_(nullptr), writer_(nullptr), type_(new HelloWorldPubSubType())
    {
    }

    virtual ~HelloWorldPublisher()
    {
        if (writer_ != nullptr)
        {
            publisher_->delete_datawriter(writer_);
        }
        if (publisher_ != nullptr)
        {
            participant_->delete_publisher(publisher_);
        }
        if (topic_ != nullptr)
        {
            participant_->delete_topic(topic_);
        }
        DomainParticipantFactory::get_instance()->delete_participant(participant_);
    }

    // Initialize the publisher
    bool init()
    {
        hello_.index(0);
        hello_.message("HelloWorld, this is FastDDS."); // define message

        DomainParticipantQos participantQos;
        participantQos.name("Participant_publisher");
        participant_ = DomainParticipantFactory::get_instance()->create_participant(0, participantQos);

        if (participant_ == nullptr)
        {
            return false;
        }

        // Register the Type
        type_.register_type(participant_);

        // Create the publications Topic
        topic_ = participant_->create_topic("HelloWorldTopic", "HelloWorld", TOPIC_QOS_DEFAULT);

        if (topic_ == nullptr)
        {
            return false;
        }

        // Create the Publisher
        publisher_ = participant_->create_publisher(PUBLISHER_QOS_DEFAULT, nullptr);

        if (publisher_ == nullptr)
        {
            return false;
        }

        // Create the DataWriter
        writer_ = publisher_->create_datawriter(topic_, DATAWRITER_QOS_DEFAULT, &listener_);

        if (writer_ == nullptr)
        {
            return false;
        }
        return true;
    }

    // Send a publication
    bool publish()
    {
        if (listener_.matched_ > 0)
        {
            hello_.index(hello_.index() + 1);
            writer_->write(&hello_);
            return true;
        }
        return false;
    }

    // Run the Publisher
    void run(uint32_t samples)
    {
        uint32_t samples_sent = 0;
        while (samples_sent < samples)
        {
            if (publish())
            {
                samples_sent++;
                std::cout << "Message: " << hello_.message() << " with index: " << hello_.index() << " SENT" << std::endl;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
    }
};

int main(int argc, char **argv)
{
    std::cout << "Starting publisher." << std::endl;
    int samples = 10; // pub count

    HelloWorldPublisher *mypub = new HelloWorldPublisher();
    if (mypub->init())
    {
        mypub->run(static_cast<uint32_t>(samples));
    }

    delete mypub;
    return 0;
}

HelloWorldSubscriber.cpp

代码语言:javascript
复制
/**
 * @file HelloWorldSubscriber.cpp
 *
 */
#include "./build/HelloWorld.h"
#include "./build/HelloWorldPubSubTypes.h"

#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/topic/TypeSupport.hpp>
#include <fastdds/dds/subscriber/Subscriber.hpp>
#include <fastdds/dds/subscriber/DataReader.hpp>
#include <fastdds/dds/subscriber/DataReaderListener.hpp>
#include <fastdds/dds/subscriber/qos/DataReaderQos.hpp>
#include <fastdds/dds/subscriber/SampleInfo.hpp>

using namespace eprosima::fastdds::dds;

class HelloWorldSubscriber
{
private:
    DomainParticipant *participant_;

    Subscriber *subscriber_;

    DataReader *reader_;

    Topic *topic_;

    TypeSupport type_;

    class SubListener : public DataReaderListener
    {
    public:
        SubListener()
            : samples_(0)
        {
        }

        ~SubListener() override
        {
        }

        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;
            }
        }

        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;
                }
            }
        }

        HelloWorld hello_;

        std::atomic_int samples_;

    } listener_;

public:
    HelloWorldSubscriber()
        : participant_(nullptr), subscriber_(nullptr), topic_(nullptr), reader_(nullptr), type_(new HelloWorldPubSubType())
    {
    }

    virtual ~HelloWorldSubscriber()
    {
        if (reader_ != nullptr)
        {
            subscriber_->delete_datareader(reader_);
        }
        if (topic_ != nullptr)
        {
            participant_->delete_topic(topic_);
        }
        if (subscriber_ != nullptr)
        {
            participant_->delete_subscriber(subscriber_);
        }
        DomainParticipantFactory::get_instance()->delete_participant(participant_);
    }

    // Initialize the subscriber
    bool init()
    {
        DomainParticipantQos participantQos;
        participantQos.name("Participant_subscriber");
        participant_ = DomainParticipantFactory::get_instance()->create_participant(0, participantQos);

        if (participant_ == nullptr)
        {
            return false;
        }

        // Register the Type
        type_.register_type(participant_);

        // Create the subscriptions Topic
        topic_ = participant_->create_topic("HelloWorldTopic", "HelloWorld", TOPIC_QOS_DEFAULT);

        if (topic_ == nullptr)
        {
            return false;
        }

        // Create the Subscriber
        subscriber_ = participant_->create_subscriber(SUBSCRIBER_QOS_DEFAULT, nullptr);

        if (subscriber_ == nullptr)
        {
            return false;
        }

        // Create the DataReader
        reader_ = subscriber_->create_datareader(topic_, DATAREADER_QOS_DEFAULT, &listener_);

        if (reader_ == nullptr)
        {
            return false;
        }

        return true;
    }

    // Run the Subscriber
    void run(uint32_t samples)
    {
        while (listener_.samples_ < samples)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    }
};

int main(int argc, char **argv)
{
    std::cout << "Starting subscriber." << std::endl;
    int samples = 10; // sub count

    HelloWorldSubscriber *mysub = new HelloWorldSubscriber();
    if (mysub->init())
    {
        /* instant sub */
        while (1)
        {
            mysub->run(static_cast<uint32_t>(samples));
        }
    }

    delete mysub;
    return 0;
}

CMakeLists.txt

代码语言:javascript
复制
cmake_minimum_required(VERSION 3.5)
project(HelloWorldExample)

set(CMAKE_CXX_STANDARD 11)

find_package(fastcdr REQUIRED)
find_package(fastrtps REQUIRED)

# generate idl_gen
file(GLOB IDL_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.idl")

foreach(IDL_FILE ${IDL_SOURCES})
    get_filename_component(IDL_BASE_NAME ${IDL_FILE} NAME_WE)
    set(GENERATED_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/${IDL_BASE_NAME}.cxx" "${CMAKE_CURRENT_BINARY_DIR}/${IDL_BASE_NAME}.h")
    add_custom_command(
        OUTPUT ${GENERATED_SOURCES}
        COMMAND fastddsgen -d ./ ${IDL_FILE}
        DEPENDS ${IDL_FILE}
        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
        COMMENT "Generating C++ files from ${IDL_FILE}"
    )
    list(APPEND GENERATED_CPP_SOURCES ${GENERATED_SOURCES})
endforeach()

include_directories(${CMAKE_CURRENT_BINARY_DIR})

# generate lib
file(GLOB DDS_HELLOWORLD_SOURCES_CXX "./build/*.cxx")
add_library(HelloWorld_IDL_lib ${DDS_HELLOWORLD_SOURCES_CXX})

add_executable(HelloWorldPublisher HelloWorldPublisher.cpp ${GENERATED_CPP_SOURCES})
target_link_libraries(HelloWorldPublisher HelloWorld_IDL_lib fastcdr fastrtps)

add_executable(HelloWorldSubscriber HelloWorldSubscriber.cpp ${GENERATED_CPP_SOURCES})
target_link_libraries(HelloWorldSubscriber HelloWorld_IDL_lib fastcdr fastrtps)
代码语言:javascript
复制
# 编译运行
mkdir build && cd build
cmake ..
make
./HelloWorldPublisher
./HelloWorldSubscriber
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-01-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. FastDDS介绍
  • 2. 环境安装
  • 3. 应用示例
    • 官方示例
      • 创建发布和订阅示例
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档