【FastDDS】FastDDS RTPS层通信实例 使用InitialPeers跨网段通信

本文目标是实现FastDDS的跨网段通信,其中配置仅在RTPS层可用,所以需要实现一个RTPS层的Demo,再次基础上进行配置,实现通信。
故本文使用FastDDSv2.14.2版本的RTPS层进行发布订阅的简单例子,并在此基础上进行可跨网段通信的配置,使其完成通信。

1.RTPS简单Demo

FastDDS提供两层通信模型,DDS层和RTPS层,他们的对应关系如下,官方给出了参考的AsSocket例子和Registered例子。

DDS LayerRTPS Layer
DomainRTPSDomain
DomainParticipantRTPSParticipant
DataWriterRTPSWriter
DataReaderRTPSReader

1.1.环境说明

VMware的Ubuntu22.04LTS虚拟机*1

1.2.目录结构

项目目录结构如下,其中三个脚本文件*.sh方便测试。RTPSPublisher.ccRTPSSubscriber.cc分别是RTPS层的发布者代码文件和订阅者代码文件。

.
├── CMakeLists.txt
├── pub.sh
├── restart.sh
├── src
│   ├── RTPSPublisher.cc
│   └── RTPSSubscriber.cc
└── sub.sh

1.3.运行方式

# 编译
./restart.sh
# 运行
./pub.sh # 一个窗口
./sub.sh # 另一个窗口

1.4.执行结果

发布者发布完10条消息后自动结束,订阅者可以回车结束
简单RTPS通信

1.5.文件内容

先解释各个文件的运行逻辑,最后补充代码部分

1.5.1.文件逻辑

  1. 总体部分
    1. 发布者和订阅者之间需要通过Topic名称的一致性进行匹配,之后再进行传输,两边约定的内容一致,在本文中指代HelloWorld类。
    2. 代码运行时有时需要通过Ctrl+C中止程序,signal_int_handler用于中止后的垃圾回收,即RTPSDomain::stopAll()
    3. 自定义监听器类用于生成具体的Writer/Reader,方便对发布者状态进行跟踪
  2. 发布者代码
    1. 自定义监听器类用于生成具体的Writer,其中需要重写onWriterMatched,当发布者匹配到订阅者时调用该函数。
    2. 实体创建顺序如下图
    3. 生成HelloWorld实例,并将它们序列化为CacheChange_t对象添加到写历史中以进行发布。每次迭代都会打印一条消息,并休眠一秒。
ParticipantAttributes
participant
HistoryAttributes
Writer
WriterAttributes
PublisherListener
WriterQos
Register
Topic
  1. 订阅者代码
    1. 自定义监听器类用于生成具体的Reader,其中需要重写onReaderMatchedonNewCacheChangeAdded,前者表示订阅者匹配到发布者时调用该函数,后者表示接收队列有元素添加时调用该函数,所以应该在第二个函数中写入读取的逻辑。
    2. 实体创建顺序如下图,与发布者大同小异
    3. 等待回车结束
ParticipantAttributes
participant
HistoryAttributes
Reader
ReaderAttributes
SubscriberListener
ReaderQos
Register
Topic

1.5.2.文件具体内容

restart.sh文件内容

# /usr/bin
rm -rf ./build
mkdir build
cd build 
cmake ..
cmake --build .

pub.sh文件内容

# /usr/bin
./build/RTPSPublisher

sub.sh文件内容

# /usr/bin
./build/RTPSSubscriber

CMakeLists.txt文件内容

cmake_minimum_required(VERSION 3.20)

project(RTPSHelloWorld)

# 添加可调式选项
set(CMAKE_BUILD_TYPE Debug)

if(NOT fastrtps_FOUND)
    find_package(fastrtps REQUIRED)
endif()
if(NOT fastcdr_FOUND)
    find_package(fastcdr REQUIRED)
endif()


# Set C++11
include(CheckCXXCompilerFlag)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG OR
        CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    check_cxx_compiler_flag(-std=c++11 SUPPORTS_CXX11)
    if(SUPPORTS_CXX11)
        add_compile_options(-std=c++11)
    else()
        message(FATAL_ERROR "Compiler doesn't support C++11")
    endif()
endif()

message(STATUS "Configuring HelloWorld publisher/subscriber example...")
# 注意 将src/*.cxx添加到DDS_HELLOWORLD_SOURCES_CXX变量中
file(GLOB DDS_HELLOWORLD_SOURCES_CXX "src/*.cxx")


add_executable(RTPSPublisher src/RTPSPublisher.cc ${DDS_HELLOWORLD_SOURCES_CXX})
target_link_libraries(RTPSPublisher fastrtps fastcdr)
add_executable(RTPSSubscriber src/RTPSSubscriber.cc ${DDS_HELLOWORLD_SOURCES_CXX})
target_link_libraries(RTPSSubscriber fastrtps fastcdr)



# 如果需要链接库或者包含目录,可以添加以下命令
# include_directories(include)
# target_link_libraries(${PROJECT_NAME} libname)

RTPSPublisher.cc文件内容


#include <iostream>
#include <thread>
#include <chrono>

#include <signal.h>
#include <unistd.h>

#include <fastrtps/rtps/participant/RTPSParticipant.h>
#include <fastrtps/rtps/RTPSDomain.h>

#include <fastrtps/rtps/writer/RTPSWriter.h>
#include <fastrtps/rtps/common/CacheChange.h>
#include <fastrtps/rtps/history/WriterHistory.h>
#include <fastrtps/rtps/writer/WriterListener.h>
#include <fastrtps/rtps/attributes/RTPSParticipantAttributes.h>
#include <fastrtps/rtps/attributes/WriterAttributes.h>
#include <fastrtps/utils/fixed_size_string.hpp>

using namespace eprosima::fastrtps;
using namespace eprosima::fastrtps::rtps;

class MyListener : public WriterListener
{
public:
    MyListener()
        : n_matched(0)
    {
    }

    ~MyListener()
    {
    }

    void onWriterMatched(
        RTPSWriter *,
        MatchingInfo &info) override
    {
        if (info.status == MATCHED_MATCHING)
        {
            ++n_matched;
        }
    }

    int n_matched;

private:
    using eprosima::fastrtps::rtps::WriterListener::onWriterMatched;
} m_listener;

class HelloWorld
{
public:
    int32_t index;
    char message[100];

    void set_index(int32_t _index)
    {
        index = _index;
    }

    int32_t get_index() const
    {
        return index;
    }

    void set_message(const char *_message)
    {
        strncpy(message, _message, sizeof(message) - 1);
        message[sizeof(message) - 1] = '\0';
    }

    const char *get_message() const
    {
        return message;
    }
};

void signal_int_handler(int signum)
{
    if (signum == SIGINT)
    {
        RTPSDomain::stopAll();
        exit(0);
    }
}
int main()
{

    signal(SIGINT, signal_int_handler);

    RTPSParticipantAttributes PParam;
    PParam.participantID = 0;
    PParam.builtin.discovery_config.discoveryProtocol = DiscoveryProtocol::SIMPLE;
    PParam.builtin.use_WriterLivelinessProtocol = true;
    PParam.setName("publisherParticipant");
    RTPSParticipant *participant = RTPSDomain::createParticipant(0, PParam);

    if (participant == nullptr)
    {
        std::cerr << "Error creating participant" << std::endl;
        return -1;
    }

    HistoryAttributes hatt;
    hatt.payloadMaxSize = 255;
    hatt.maximumReservedCaches = 50;
    WriterHistory *history = new WriterHistory(hatt);

    WriterAttributes WParam;
    WParam.endpoint.topicKind = NO_KEY;
    WParam.endpoint.reliabilityKind = BEST_EFFORT;

    RTPSWriter *writer = RTPSDomain::createRTPSWriter(participant, WParam, history, &m_listener);

    if (writer == nullptr)
    {
        std::cerr << "Error creating writer" << std::endl;
        delete history;
        RTPSDomain::removeRTPSParticipant(participant);
        return -1;
    }
    std::cout << "Registering Writer" << std::endl;

    // Topic
    TopicAttributes Tatt;
    Tatt.topicKind = NO_KEY;
    Tatt.topicDataType = "string";
    Tatt.topicName = "exampleTopic";
    WriterQos Wqos;
    if (!participant->registerWriter(writer, Tatt, Wqos))
    {
        std::cout << "ERROR: registerWriter" << std::endl;
        return -1;
    }

    while (m_listener.n_matched == 0)
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        std::cout << "Not Matched" << std::endl;
    }

    HelloWorld hello;
    uint32_t samples_count = 10;
    for (uint32_t count = 0; (count < samples_count); ++count)
    {
        hello.index = count;
        sprintf(hello.message, "HelloWorld %d", count);
        printf("Sending: %d, msg: %s\n", hello.index, hello.message);

        // 向 History 添加内容
        CacheChange_t *change = writer->new_change([]() -> uint32_t
                                                   { return 255; }, ALIVE);

        if (!change) // In the case history is full, remove some old changes
        {
            std::cout << "cleaning history...";
            writer->remove_older_changes(20);
            change = writer->new_change([]() -> uint32_t
                                        { return 255; }, ALIVE);
        }

        change->serializedPayload.length = static_cast<uint32_t>(sizeof(HelloWorld));
        memcpy(change->serializedPayload.data, &hello, sizeof(HelloWorld));
        history->add_change(change);

        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    RTPSDomain::removeRTPSWriter(writer);
    delete history;
    RTPSDomain::removeRTPSParticipant(participant);
    return 0;
}

RTPSSubscriber.cc文件内容


#include <iostream>
#include <thread>
#include <chrono>

#include <signal.h>
#include <unistd.h>

#include <fastrtps/rtps/RTPSDomain.h>
#include <fastrtps/rtps/participant/RTPSParticipant.h>
#include <fastrtps/rtps/reader/ReaderListener.h>
#include <fastrtps/rtps/reader/RTPSReader.h>
#include <fastrtps/rtps/history/ReaderHistory.h>
#include <fastrtps/rtps/attributes/RTPSParticipantAttributes.h>
#include <fastrtps/rtps/attributes/ReaderAttributes.h>
#include <fastrtps/utils/fixed_size_string.hpp>

using namespace eprosima::fastrtps;
using namespace eprosima::fastrtps::rtps;

class HelloWorld
{
public:
    int32_t index;
    char message[100];

    void set_index(int32_t _index)
    {
        index = _index;
    }

    int32_t get_index() const
    {
        return index;
    }

    void set_message(const char *_message)
    {
        strncpy(message, _message, sizeof(message) - 1);
        message[sizeof(message) - 1] = '\0';
    }

    const char *get_message() const
    {
        return message;
    }
};

class MyListener : public ReaderListener
{
public:
    void onNewCacheChangeAdded(RTPSReader *reader, const CacheChange_t *const change) override
    {
        if (change->kind != ALIVE)
        {
            return;
        }
        ++n_received;
        HelloWorld hello;
        memcpy(&hello, change->serializedPayload.data, sizeof(HelloWorld));
        printf("Received: %d, msg: %s\n", hello.index, hello.message);
        reader->getHistory()->remove_change((CacheChange_t *)change);
    }
    void onReaderMatched(
        RTPSReader *,
        MatchingInfo &info) override
    {
        if (info.status == MATCHED_MATCHING)
        {
            n_matched++;
        }
    }

    uint32_t n_received;
    uint32_t n_matched;

private:
    using eprosima::fastrtps::rtps::ReaderListener::onReaderMatched;
} m_listener;

void signal_int_handler(int signum)
{
    if (signum == SIGINT)
    {
        RTPSDomain::stopAll();
        exit(0);
    }
}
int main()
{
    signal(SIGINT, signal_int_handler);

    RTPSParticipantAttributes PParam;
    PParam.builtin.discovery_config.discoveryProtocol = DiscoveryProtocol::SIMPLE;
    PParam.builtin.use_WriterLivelinessProtocol = true;
    PParam.participantID = 0;
    PParam.setName("publisherParticipant");

    RTPSParticipant *participant = RTPSDomain::createParticipant(0, PParam);

    if (participant == nullptr)
    {
        std::cerr << "Error creating participant" << std::endl;
        return -1;
    }

    ReaderAttributes RParam;
    RParam.endpoint.topicKind = NO_KEY;
    RParam.endpoint.reliabilityKind = RELIABLE;
    RParam.endpoint.durabilityKind = TRANSIENT_LOCAL;

    HistoryAttributes hatt;
    hatt.payloadMaxSize = 255;
    ReaderHistory *history = new ReaderHistory(hatt);

    MyListener listener;
    RTPSReader *reader = RTPSDomain::createRTPSReader(participant, RParam, history, &listener);

    if (reader == nullptr)
    {
        std::cerr << "Error creating reader" << std::endl;
        delete history;
        // delete change_pool;
        RTPSDomain::removeRTPSParticipant(participant);
        return -1;
    }

    // topic
    TopicAttributes Tatt;
    Tatt.topicKind = NO_KEY;
    Tatt.topicDataType = "string";
    Tatt.topicName = "exampleTopic";
    ReaderQos Rqos;
    if (!participant->registerReader(reader, Tatt, Rqos))
    {
        std::cout << "ERROR: registerReader" << std::endl;
        return -1;
    }

    std::cout << "Waiting for publications..." << std::endl;

    printf("Press Enter to stop the Reader.\n");
    std::cin.ignore();

    RTPSDomain::removeRTPSReader(reader);
    delete history;
    // delete change_pool;
    RTPSDomain::removeRTPSParticipant(participant);
    return 0;
}

2.RTPS跨网段通信Demo

2.1.环境说明

VMware的Ubuntu22.04LTS虚拟机*3,已经可以跨网段通信,参考这篇跨网段通信的内容,其中网络IP仍是那片博客的内容

2.2.修改逻辑

在第一段程序的基础上进行添加
添加d_partcipantIDd_domainID的指定
Linux1(192.168.10.10)修改RTPSPublisher.ccRTPSParticipantAttributes和创建逻辑部分为如下

const int d_domainID = 0;
const int d_partcipantID = 0;

RTPSParticipantAttributes PParam;
PParam.participantID = d_partcipantID;
Locator_t initial_peer;
IPLocator::setIPv4(initial_peer,"192.168.11.11");
initial_peer.port = 7412;
PParam.builtin.initialPeersList.push_back(initial_peer);

RTPSParticipant* participant = RTPSDomain::createParticipant(d_domainID,PParam);

Linux3(192.168.11.11)修改RTPSPublisher.ccRTPSParticipantAttributes和创建逻辑部分为如下

const int d_domainID = 0;
const int d_partcipantID = 1;

RTPSParticipantAttributes PParam;
PParam.participantID = d_partcipantID;
Locator_t initial_peer;
IPLocator::setIPv4(initial_peer,"192.168.10.10");
initial_peer.port = 7410;
PParam.builtin.initialPeersList.push_back(initial_peer);
  
RTPSParticipant* participant = RTPSDomain::createParticipant(d_domainID,PParam);

2.3.执行结果

RTPS跨网段通信

3.参考

跨网段通信的内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值