2. 自定义IDL消息类型
任务目标
本案例定义一个包含 int32、float、string 三种基本数据类型的自定义 IDL 消息,通过 Fast DDS 的代码生成工具生成对应 C++ 类型,并完成一个最小发布-订阅通信闭环,验证自定义消息可以正常传输。
环境与依赖
- 已安装 Fast DDS(≥2.6)及其 IDL 编译器
fastddsgen,确认命令可用:fastddsgen -version - CMake ≥ 3.16,支持 C++14 的编译器(GCC 7+ / Clang 6+)
实现步骤
1. 定义 IDL 消息
创建文件 SensorData.idl,内容如下:
// SensorData.idl
struct SensorData
{
long id; // int32
float value; // float
string name; // string
};
2. 生成代码
在终端执行以下命令,生成 C++ 类型与支持代码:
fastddsgen SensorData.idl
生成的文件包括:SensorData.h、SensorData.cxx、SensorDataPubSubTypes.h、SensorDataPubSubTypes.cxx。
3. 完整通信代码
创建 main.cpp,实现一个发布者线程和一个订阅者线程,在同一个 DomainParticipant 下完成消息收发:
#include <chrono>
#include <iostream>
#include <thread>
#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>
#include <fastdds/dds/subscriber/Subscriber.hpp>
#include <fastdds/dds/subscriber/DataReader.hpp>
#include <fastdds/dds/subscriber/DataReaderListener.hpp>
#include <fastdds/dds/subscriber/SampleInfo.hpp>
#include "SensorData.h"
#include "SensorDataPubSubTypes.h"
using namespace eprosima::fastdds::dds;
class SubListener : public DataReaderListener
{
public:
bool received_ = false;
void on_data_available(DataReader* reader) override
{
SensorData data;
SampleInfo info;
if (reader->take_next_sample(&data, &info) == ReturnCode_t::RETCODE_OK)
{
if (info.valid_data)
{
std::cout << "Received: id=" << data.id()
<< ", value=" << data.value()
<< ", name=" << data.name() << std::endl;
received_ = true;
}
}
}
};
int main()
{
// 创建参与者
DomainParticipant* participant =
DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT);
if (participant == nullptr) return -1;
// 注册类型
TypeSupport type(new SensorDataPubSubType());
type.register_type(participant);
// 创建主题
Topic* topic = participant->create_topic("SensorDataTopic", "SensorData", TOPIC_QOS_DEFAULT);
if (topic == nullptr) return -1;
// 发布端
Publisher* publisher = participant->create_publisher(PUBLISHER_QOS_DEFAULT);
DataWriter* writer = publisher->create_datawriter(topic, DATAWRITER_QOS_DEFAULT);
// 订阅端
SubListener sub_listener;
Subscriber* subscriber = participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT);
DataReader* reader = subscriber->create_datareader(topic, DATAREADER_QOS_DEFAULT, &sub_listener);
// 等待发现
std::this_thread::sleep_for(std::chrono::seconds(1));
// 构造并发送消息
SensorData msg;
msg.id(1001);
msg.value(3.14f);
msg.name("temperature_sensor");
writer->write(&msg);
// 等待接收
for (int i = 0; i < 10 && !sub_listener.received_; ++i)
{
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
// 清理
participant->delete_contained_entities();
DomainParticipantFactory::get_instance()->delete_participant(participant);
if (sub_listener.received_)
{
std::cout << "TEST PASSED" << std::endl;
return 0;
}
else
{
std::cout << "TEST FAILED: no message received" << std::endl;
return 1;
}
}
4. CMake 构建配置
创建 CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(custom_idl_demo)
set(CMAKE_CXX_STANDARD 14)
find_package(fastdds REQUIRED)
find_package(fastcdr REQUIRED)
add_executable(custom_idl_demo main.cpp SensorData.cxx SensorDataPubSubTypes.cxx)
target_link_libraries(custom_idl_demo fastdds::fastdds fastcdr::fastcdr)
运行与验证
mkdir build && cd build
cmake ..
make -j4
./custom_idl_demo
通过条件:程序输出包含 Received: id=1001, value=3.14, name=temperature_sensor,并以 TEST PASSED 退出(退出码 0)。该输出由代码逻辑直接唯一推出,表示自定义 IDL 消息已完成定义、代码生成、序列化传输与反序列化接收的完整闭环。
常见问题
- 找不到
fastddsgen:确认 Fast DDS 安装路径已加入PATH,或使用路径调用。 - CMake 找不到 FastDDS:设置
CMAKE_PREFIX_PATH指向 Fast DDS 安装目录。 - 收不到消息:确认发布与订阅使用相同的 domain ID(本例为 0)和 topic 名称;若在不同进程运行,需保证网络发现正常。