当前位置: 首页 > news >正文

win10下Qt应用程序使用FastDDS

Qt与Fast DDS通信实现指南

文章目录

  • Qt与Fast DDS通信实现指南
    • 概述
    • 发布端实现
      • 发布数据类定义
        • 头文件
        • 实现源文件
      • 发布数据使用示例
    • 订阅端实现
      • 订阅数据类定义
        • 头文件
        • 实现源文件
      • 订阅数据使用示例
    • 总结

概述

库的导入请参考:Qt开发的应用程序编译链接Fast DDS库

本篇主要介绍主程序和子程序如何使用Fast DDS进行通信的代码实现,仿照官方示例。


发布端实现

发布数据类定义

头文件
#ifndef DDSPUBLISHERMANAGER_H
#define DDSPUBLISHERMANAGER_H// Qt 头文件
#include <QObject>
#include <QString>// Fast DDS 头文件
#include <fastdds/dds/publisher/DataWriterListener.hpp>
#include <fastdds/dds/topic/TypeSupport.hpp>
#include <fastdds/dds/domain/DomainParticipant.hpp>// 包含自定义类型及其序列化/反序列化支持
#include "../framePubSubTypes.h"/*** @class DDSPublisherManager* @brief 负责管理 FastDDS 发布者的生命周期,并发布自定义数据** 该类封装了 DDS 发布者的初始化、资源释放以及数据发布功能,* 通过 Qt 信号槽机制可与 UI 或其他模块交互。*/
class DDSPublisherManager : public QObject
{Q_OBJECTpublic:static DDSPublisherManager *instance();/*** @brief 构造函数* @param parent 父对象,用于 Qt 对象树管理*/explicit DDSPublisherManager(QObject *parent = nullptr);/*** @brief 析构函数* 负责清理 DDS 资源(参与者、发布者、数据写入者等)*/~DDSPublisherManager();/*** @brief 初始化 DDS 发布者* 创建 DomainParticipant、Topic、Publisher、DataWriter 等对象* @return true 初始化成功;false 初始化失败*/bool initialize(bool use_env);/*** @brief 检查是否已完成初始化* @return true 已初始化;false 未初始化*/bool isInitialized() const;/*** @brief 发布一条数据* @param data 要发布的数据实例*/void publishdata(const data_struct &data);private:static DDSPublisherManager *s_inst;eprosima::fastdds::dds::DomainParticipant *participant_; ///< DDS 域参与者,用于管理通信端点eprosima::fastdds::dds::Topic *topic_;                   ///< DDS 主题,关联数据类型与名称eprosima::fastdds::dds::Publisher *publisher_;           ///< DDS 发布者,负责创建 DataWritereprosima::fastdds::dds::DataWriter *writer_;             ///< DDS 数据写入者,实际发送数据eprosima::fastdds::dds::TypeSupport data_type_;    ///< 数据类型的 TypeSupport,用于注册序列化/反序列化bool initialized_;                                       ///< 标记是否成功完成初始化class PubListener : public eprosima::fastdds::dds::DataWriterListener{public:PubListener(): matched_(0), firstConnected_(false){}~PubListener() override{}void on_publication_matched(eprosima::fastdds::dds::DataWriter *writer,const eprosima::fastdds::dds::PublicationMatchedStatus &info) override;int matched_;bool firstConnected_;} listener_;
};#endif // DDSPUBLISHERMANAGER_H
实现源文件
#include "dds_publisher_manager.h"
#include <fastrtps/attributes/ParticipantAttributes.h>
#include <fastrtps/attributes/PublisherAttributes.h>
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/publisher/qos/PublisherQos.hpp>
#include <fastdds/dds/publisher/DataWriter.hpp>
#include <fastdds/dds/publisher/qos/DataWriterQos.hpp>
#include <fastdds/dds/log/Log.hpp>
#include <regex>
#include <QMutex>
#include <iostream>
using namespace eprosima::fastdds::dds;// 设计为单例类,方便在多个地方发送数据,也可以不设计为单例类
DDSPublisherManager *DDSPublisherManager::s_inst = nullptr;DDSPublisherManager *DDSPublisherManager::instance()
{static QMutex mtx;if (!s_inst){QMutexLocker lock(&mtx);if (!s_inst){s_inst = new DDSPublisherManager(); // Qt 程序随进程生命周期s_inst->initialize(false);}}return s_inst;
}DDSPublisherManager::DDSPublisherManager(QObject *parent): QObject(parent), participant_(nullptr), topic_(nullptr), publisher_(nullptr), writer_(nullptr), data_type_(new data_struct::DATAPubSubType()), initialized_(false)
{// 开启日志Log::SetVerbosity(Log::Kind::Info);Log::SetCategoryFilter(std::regex(".*"));
}DDSPublisherManager::~DDSPublisherManager()
{if (writer_){publisher_->delete_datawriter(writer_);}if (publisher_){participant_->delete_publisher(publisher_);}if (topic_){participant_->delete_topic(topic_);}if (participant_){DomainParticipantFactory::get_instance()->delete_participant(participant_);}
}bool DDSPublisherManager::initialize(bool use_env)
{try{// 1. 创建 DomainParticipantDomainParticipantQos participant_qos = PARTICIPANT_QOS_DEFAULT;participant_qos.name("Participant_pub");auto factory = DomainParticipantFactory::get_instance();// (可选)加载配置文件或使用环境变量中的 QoSif (use_env){factory->load_profiles();factory->get_default_participant_qos(participant_qos);}// 创建 DomainParticipant(通信参与者)participant_ = factory->create_participant(0, participant_qos);if (participant_ == nullptr){return false;}// 注册数据类型(关键步骤) REGISTER THE TYPEtrajectory_type_.register_type(participant_);// CREATE THE PUBLISHER 创建 Publisher(发布者)PublisherQos pubqos = PUBLISHER_QOS_DEFAULT;if (use_env){participant_->get_default_publisher_qos(pubqos);}publisher_ = participant_->create_publisher(pubqos, nullptr);if (publisher_ == nullptr){return false;}// CREATE THE TOPIC 创建 Topic(主题)TopicQos tqos = TOPIC_QOS_DEFAULT;if (use_env){participant_->get_default_topic_qos(tqos);}topic_ = participant_->create_topic("PointDataTopic",data_type_.get_type_name(),tqos);if (topic_ == nullptr){return false;}// CREATE THE WRITER 创建 DataWriter(数据写入者,真正发送数据的对象)DataWriterQos wqos = DATAWRITER_QOS_DEFAULT;if (use_env){publisher_->get_default_datawriter_qos(wqos);}writer_ = publisher_->create_datawriter(topic_, wqos, &listener_);if (writer_ == nullptr){return false;}initialized_ = true;return true;}catch (const std::exception &e){return false;}
}bool DDSPublisherManager::isInitialized() const
{return initialized_;
}void DDSPublisherManager::publishTrajectory(const data_struct::PointData &data)
{if (!initialized_ || !writer_){return;}if (listener_.firstConnected_ || listener_.matched_ > 0){writer_->write(const_cast<data_struct::PointData *>(&data));}
}void DDSPublisherManager::PubListener::on_publication_matched(eprosima::fastdds::dds::DataWriter *writer, const eprosima::fastdds::dds::PublicationMatchedStatus &info)
{if (info.current_count_change == 1){matched_ = info.total_count;firstConnected_ = true;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;}
}

发布数据使用示例

data_struct::PointData dds_point;// 填充数据 —— 从 navigation_detail 中映射字段
dds_point.time() = static_cast<double>(QDateTime::currentSecsSinceEpoch()); // 当前时间,单位:秒
dds_point.longitude() = navigation_detail.longitude; // 经度
dds_point.latitude() = navigation_detail.latitude;   // 纬度
dds_point.depth() = navigation_detail.depth;         // 深度(负值)
DDSPublisherManager::instance()->publishTrajectory(dds_point);

说明:这里直接在接收数据的回调直接调用单例类发送数据,可以写定时器定时发送什么数据,具体可以参考官方示例文档。


订阅端实现

订阅数据类定义

头文件
/*** @file threed_subscriber.h**/#ifndef THREEDSUBSCRIBER_H_
#define THREEDSUBSCRIBER_H_#include "trajectory/trajectory_framePubSubTypes.h"#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/subscriber/DataReaderListener.hpp>
#include <fastrtps/subscriber/SampleInfo.h>
#include <fastdds/dds/core/status/SubscriptionMatchedStatus.hpp>class ThreeDSubscriber
{
public:ThreeDSubscriber();virtual ~ThreeDSubscriber();//! Initialize the subscriberbool init(bool use_env);//! RUN the subscribervoid run();//! Run the subscriber until number samples have been received.void run(uint32_t number);void stop();private:eprosima::fastdds::dds::DomainParticipant *participant_;eprosima::fastdds::dds::Subscriber *subscriber_;eprosima::fastdds::dds::Topic *topic_;eprosima::fastdds::dds::DataReader *reader_;eprosima::fastdds::dds::TypeSupport type_;std::atomic<bool> stop_flag_;class SubListener : public eprosima::fastdds::dds::DataReaderListener{public:SubListener(): matched_(0), samples_(0){}~SubListener() override{}void on_data_available(eprosima::fastdds::dds::DataReader *reader) override;void on_subscription_matched(eprosima::fastdds::dds::DataReader *reader,const eprosima::fastdds::dds::SubscriptionMatchedStatus &info) override;data_struct::TrajectoryPoint trajectory_point_;int matched_;uint32_t samples_;} listener_;
};#endif /* THREEDSUBSCRIBER_H_ */
实现源文件
/*** @file threed_subscriber.cpp**/#include "threed_subscriber.h"#include <chrono>
#include <thread>#include <fastrtps/attributes/ParticipantAttributes.h>
#include <fastrtps/attributes/SubscriberAttributes.h>
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/subscriber/Subscriber.hpp>
#include <fastdds/dds/subscriber/DataReader.hpp>
#include <fastdds/dds/subscriber/SampleInfo.hpp>
#include <fastdds/dds/subscriber/qos/DataReaderQos.hpp>using namespace eprosima::fastdds::dds;ThreeDSubscriber::ThreeDSubscriber(): participant_(nullptr), subscriber_(nullptr), topic_(nullptr), reader_(nullptr), type_(new data_struct::TrajectoryPointPubSubType()), stop_flag_(false)
{
}bool ThreeDSubscriber::init(bool use_env)
{DomainParticipantQos pqos = PARTICIPANT_QOS_DEFAULT;pqos.name("Participant_sub");auto factory = DomainParticipantFactory::get_instance();if (use_env){factory->load_profiles();factory->get_default_participant_qos(pqos);}participant_ = factory->create_participant(0, pqos);if (participant_ == nullptr){return false;}// REGISTER THE TYPEtype_.register_type(participant_);// CREATE THE SUBSCRIBERSubscriberQos sqos = SUBSCRIBER_QOS_DEFAULT;if (use_env){participant_->get_default_subscriber_qos(sqos);}subscriber_ = participant_->create_subscriber(sqos, nullptr);if (subscriber_ == nullptr){return false;}// CREATE THE TOPICTopicQos tqos = TOPIC_QOS_DEFAULT;if (use_env){participant_->get_default_topic_qos(tqos);}topic_ = participant_->create_topic("TrajectoryTopic",type_.get_type_name(),tqos);if (topic_ == nullptr){return false;}// CREATE THE READERDataReaderQos rqos = DATAREADER_QOS_DEFAULT;rqos.reliability().kind = RELIABLE_RELIABILITY_QOS;if (use_env){subscriber_->get_default_datareader_qos(rqos);}reader_ = subscriber_->create_datareader(topic_, rqos, &listener_);if (reader_ == nullptr){return false;}return true;
}ThreeDSubscriber::~ThreeDSubscriber()
{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_);
}void ThreeDSubscriber::SubListener::on_subscription_matched(DataReader *,const SubscriptionMatchedStatus &info)
{if (info.current_count_change == 1){matched_ = info.total_count;std::cout << "Subscriber matched." << std::endl;}else if (info.current_count_change == -1){matched_ = info.total_count;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 ThreeDSubscriber::SubListener::on_data_available(DataReader *reader)
{SampleInfo info;if (reader->take_next_sample(&trajectory_point_, &info) == ReturnCode_t::RETCODE_OK){if (info.instance_state == ALIVE_INSTANCE_STATE){samples_++;// Print your structure data here.// === 获取当前系统时间(带毫秒) ===auto now = std::chrono::system_clock::now();std::time_t now_c = std::chrono::system_clock::to_time_t(now);auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) %1000;std::tm local_tm;
#ifdef _WIN32localtime_s(&local_tm, &now_c); // Windows 安全版
#elselocaltime_r(&now_c, &local_tm); // Linux 安全版
#endifstd::cout << std::fixed << std::setprecision(6);std::cout << "=== TrajectoryPoint RECEIVED ===" << std::endl;// 打印当前时间戳std::cout << "recv_time: "<< std::put_time(&local_tm, "%Y-%m-%d %H:%M:%S")<< "." << std::setw(3) << std::setfill('0') << ms.count()<< std::endl;std::cout << "time: " << trajectory_point_.time() << " s" << std::endl;std::cout << "longitude: " << trajectory_point_.longitude() << std::endl;std::cout << "latitude: " << trajectory_point_.latitude() << std::endl;std::cout << "depth: " << trajectory_point_.depth() << " m" << std::endl;std::cout << "================================" << std::endl;}}
}void ThreeDSubscriber::run()
{std::cout << "Subscriber running in background..." << std::endl;while (!stop_flag_){std::this_thread::sleep_for(std::chrono::milliseconds(200));// 检查 DDS 数据或调用 take_next_sample()}std::cout << "Subscriber stopped." << std::endl;
}void ThreeDSubscriber::run(uint32_t number)
{std::cout << "Subscriber running until " << number << "samples have been received" << std::endl;while (number > listener_.samples_){std::this_thread::sleep_for(std::chrono::milliseconds(500));}
}void ThreeDSubscriber::stop()
{stop_flag_ = true;
}

订阅数据使用示例

// 创建订阅者对象
auto subscriber = std::make_shared<ThreeDSubscriber>();// 启动后台线程接收实时轨迹数据
std::thread sub_thread([subscriber](){if (subscriber->init(false)){subscriber->run();  // 在后台阻塞运行,不影响UI} });// 分离线程,主线程继续执行 UI 事件循环
sub_thread.detach();
QObject::connect(&app, &QApplication::aboutToQuit, [subscriber](){ subscriber->stop(); });

总结

本文详细介绍了在Qt应用程序中使用Fast DDS实现发布-订阅通信的完整代码实现,包括:

  1. 发布端:单例模式的发布管理器,封装了DDS发布者的完整生命周期管理
  2. 订阅端:订阅者类实现,支持后台线程运行和优雅停止
  3. 使用示例:具体的调用方法和集成到Qt应用程序的方式

这种实现方式确保了DDS通信的高效性和Qt应用程序的响应性,适合在需要实时数据交换的Qt应用中使用。

http://www.dtcms.com/a/478176.html

相关文章:

  • 链表相关的知识以及算法题
  • 模板网站建站步骤微信公众号和小程序的区别
  • Shell 使用指南
  • 重庆网站seo服务没效果
  • 开源项目重构我们应该怎么做-以 SQL 血缘系统开源项目为例
  • Sora2:AIGC的技术革命与生态重构
  • Modbus RTU 数据结构(发送和返回/读/写)
  • Nginx IP 透传
  • 海外IP的主要应用业务
  • 门户网站建设工序做微信网站要多少钱
  • 南阳网站优化费用推进网站 集约化建设
  • 算法训练之BFS实现FloodFill算法
  • Typescript - 枚举类型 enum,详细介绍与使用教程(快速入门)
  • 机器视觉2D贴合引导项目从哪里入手,案例知识分享
  • 家庭烹饪用油选择
  • 「工具设计」JS字段信息加密解密工具设计
  • 注意力机制-10.1.3注意力可视化
  • 网站维护公司苏州网站推广优化
  • Codeforces Educational 183(ABCD)
  • 为什么建设网站要年年交钱石家庄最新今天消息
  • 2025年语音识别(ASR)与语音合成(TTS)技术趋势分析对比
  • TortoiseSVN-1.8.10.26129-x64-svn-1.8.11.msi
  • 鸿蒙NEXT应用接入快捷栏:一键直达,提升用户体验
  • 前端接EXCEL
  • 深圳企业网站建设推荐公司网站开发的方法
  • 网站建设 价格wordpress管理员改为投稿者
  • 2025程序综合实践第三次DFS2
  • 记录一次前端文件缓存问题
  • 深度预测调和网络(DFRN)医疗应用编程路径分析
  • bkhtmltopdf - 高性能 HTML 转 PDF 工具(代替 wkhtmltopdf)