4. DDS多主题通信

任务目标

在同一个 DomainParticipant 下创建三个 Topic(车速 VehicleSpeed、胎压 TirePressure、温度 Temperature),由一个发布者(Publisher)通过三个 DataWriter 分别发布数据,并用三个 DataReader 订阅验证。

环境与依赖

  • RTI Connext DDS 6.x(或兼容的 DDS 实现),已配置 NDDSHOME 与 LD_LIBRARY_PATH。
  • IDL 编译器 rtiddsgen 用于生成类型支持代码。
  • C++11 编译器(g++ 或 clang++)。

实现思路

三个主题共用同一 Participant 和 Publisher,各自持有独立 DataWriter;订阅端在同一 Participant 下创建三个 DataReader,通过监听器回调打印收到的数据。IDL 中定义三个结构体,rtiddsgen 会为每个类型生成独立的注册与序列化代码。

Publisher (单发布者) DataWriter: VehicleSpeed DataWriter: TirePressure DataWriter: Temperature DataReader: VehicleSpeed DataReader: TirePressure DataReader: Temperature

IDL 定义

// sensor.idl
struct VehicleSpeed {
    long   id;
    double speed_kmh;
};
struct TirePressure {
    long   id;
    double pressure_kpa;
};
struct Temperature {
    long   id;
    double celsius;
};

生成类型支持代码

rtiddsgen -language C++11 -example x64Linux4gcc7.3.0 sensor.idl

生成 sensor.hpp、sensorPlugin.hpp 等文件,包含三个类型的 TypeSupport。

核心代码:单发布者发布多主题

#include <dds/dds.hpp>
#include "sensor.hpp"
#include <thread>
#include <chrono>

using namespace dds::domain;
using namespace dds::topic;
using namespace dds::pub;
using namespace dds::sub;
using namespace dds::core::policy;

int main() {
    DomainParticipant participant(0);

    // 三个主题
    Topic<VehicleSpeed>  speedTopic(participant, "VehicleSpeed");
    Topic<TirePressure>  tireTopic(participant, "TirePressure");
    Topic<Temperature>   tempTopic(participant, "Temperature");

    // 单发布者
    Publisher publisher(participant);

    DataWriter<VehicleSpeed> speedWriter(publisher, speedTopic);
    DataWriter<TirePressure> tireWriter(publisher, tireTopic);
    DataWriter<Temperature>  tempWriter(publisher, tempTopic);

    // 订阅端:三个 DataReader + 监听器
    Subscriber subscriber(participant);
    DataReader<VehicleSpeed> speedReader(subscriber, speedTopic);
    DataReader<TirePressure> tireReader(subscriber, tireTopic);
    DataReader<Temperature>  tempReader(subscriber, tempTopic);

    // 等待匹配
    std::this_thread::sleep_for(std::chrono::seconds(2));

    // 发布者循环发布三个主题
    for (int i = 0; i < 5; ++i) {
        VehicleSpeed s; s.id = i; s.speed_kmh = 60.0 + i * 5;
        TirePressure t; t.id = i; t.pressure_kpa = 220.0 + i;
        Temperature  c; c.id = i; c.celsius = 25.0 + i * 0.5;

        speedWriter.write(s);
        tireWriter.write(t);
        tempWriter.write(c);

        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }

    // 读取验证
    auto readAll = [](auto& reader, const char* name) {
        auto samples = reader.read();
        for (auto& s : samples)
            std::cout << name << " id=" << s.id() << "\n";
    };
    readAll(speedReader, "Speed");
    readAll(tireReader,  "Tire");
    readAll(tempReader,  "Temp");

    return 0;
}

编译与运行

g++ -std=c++11 main.cpp sensor.cxx sensorPlugin.cxx \
    -I$NDDSHOME/include -I$NDDSHOME/include/ndds \
    -L$NDDSHOME/lib/x64Linux4gcc7.3.0 \
    -lnddscpp2 -lnddsc -lnddscore -lpthread -o multi_topic
export LD_LIBRARY_PATH=$NDDSHOME/lib/x64Linux4gcc7.3.0:$LD_LIBRARY_PATH
./multi_topic

验证方法与通过条件

  • 程序正常退出,无 DDS 异常抛出。
  • 标准输出中依次出现 Speed id=0..4、Tire id=0..4、Temp id=0..4,说明三个主题的数据均被对应 DataReader 收到。
  • 若某主题无输出,检查 Topic 名称是否与 IDL 类型注册一致,以及 sleep_for 是否足够等待端点匹配。

常见问题

  • 匹配超时:同一进程内发布订阅通常秒级匹配,跨主机需确认 Domain ID 与网络配置一致。
  • 类型未注册:rtiddsgen 生成的 register_type 需在创建 Topic 前调用,示例中由 Topic 构造函数自动完成。
  • read() 返回空:确认发布端已 write 且订阅端在发布前已创建,否则历史数据可能未被缓存。