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 名称;若在不同进程运行,需保证网络发现正常。