最新消息: USBMI致力于为网友们分享Windows、安卓、IOS等主流手机系统相关的资讯以及评测、同时提供相关教程、应用、软件下载等服务。

【FastDDS学习笔记】HelloWorld示例程序编译和运行

IT圈 admin 4浏览 0评论

【FastDDS学习笔记】HelloWorld示例程序编译和运行

目录

第一章:【FastDDS学习笔记】Ubuntu22上安装fastDDS环境
第二章:【FastDDS学习笔记】HelloWorld示例程序编译和运行
第三章:【FastDDS学习笔记】Fast-DDS-Gen安装记录

代码位置:~/Fast-DDS/Fast-DDS/examples/C++/HelloWorldExample

设置相应的环境变量

因为上一章节中,我们编译的库并没有放到系统中,而是放在了一个文件夹中,所以这里我们要先设定以下对应的环境变量:

export LD_LIBRARY_PATH=/home/xiaoqing/Fast-DDS/install/lib

这里的路径就是编译时候,填写的路径。

如果想要永久生效,执行以下命令:

echo 'export LD_LIBRARY_PATH=/home/xiaoqing/Fast-DDS/install/lib' >> ~/.bashrc

这里我是用的是永久生效的。

编译HelloWorld

在ubuntu中执行以下命令:

mkdir build && cd build
cmake .. -DCMAKE_PREFIX_PATH=~/Fast-DDS/install/

运行效果:

继续编译:

make

生成运行实例:

运行HelloWorld

接下来我们来运行HelloWorld,看一下效果。

新开一个终端,运行一个publisher:

./HelloWorldExample publisher

再新开一个终端,运行一个subscriber:

./HelloWorldExample subscriber

执行效果:
publisher:

subscriber:

源码分析

源码的文件目录:

HelloWorldExample
├── build
│  ├── CMakeCache.txt
│  ├── CMakeFiles
│  ├── cmake_install.cmake
│  ├── HelloWorldExample
│  └── Makefile
├── CMakeLists.txt
├── HelloWorld.cxx // 由idl生成
├── HelloWorld.h // 由idl生成
├── HelloWorld.idl      //idl 文件
├── HelloWorld_main.cpp
├── HelloWorldPublisher.cpp
├── HelloWorldPublisher.h
├── HelloWorldPubSubTypes.cxx // 由idl生成
├── HelloWorldPubSubTypes.h // 由idl生成
├── HelloWorldSubscriber.cpp
├── HelloWorldSubscriber.h
└── README.txt

代码中
HelloWorld.idl 定义payload,会生成HelloWorld.hHelloWorld.cxxHelloWorldPubSubTypes.hHelloWorldPubSubTypes.cxx四个文件,包含了序列化与反序列化的逻辑。

先来看一下HelloWorld.idl:

struct HelloWorld
{unsigned long index;string message;
};

只是简单地定义了下数据传输的结构体。

结构体包含一个id和一个字符串类型的message。

而这,也就是DDS概念模型中,TopicDataType的定义。

主题在概念上适合发布和订阅。

从OMG的DDS官网我们可以知道,IDL(DDS基于的是IDL4 v4.2标准)是一种不依赖于编程语言的,用于定义数据类型和接口的描述性语言。他同时也是OMG组织制定的标准。Fast DDS-Gen作为一个Java应用程序,就是用来解析idl文件,生成数据类型定义的(即HelloWorld.hHelloWorld.cxxHelloWorldPubSubTypes.hHelloWorldPubSubTypes.cxx)。

生成的文件HelloWorld.hHelloWorld.cxx`中会根据这个结构体生成类,并添加一些序列化使用的函数:

class HelloWorld
{
public:eProsima_user_DllExport HelloWorld& operator =(const HelloWorld& x);eProsima_user_DllExport HelloWorld& operator =(HelloWorld&& x);eProsima_user_DllExport bool operator ==(const HelloWorld& x) const;eProsima_user_DllExport bool operator !=(const HelloWorld& x) const;eProsima_user_DllExport void index(uint32_t _index);eProsima_user_DllExport uint32_t index() const;eProsima_user_DllExport uint32_t& index();eProsima_user_DllExport void message(const std::string& _message);eProsima_user_DllExport void message(std::string&& _message);eProsima_user_DllExport const std::string& message() const;eProsima_user_DllExport std::string& message();eProsima_user_DllExport static size_t getMaxCdrSerializedSize(size_t current_alignment = 0);eProsima_user_DllExport static size_t getCdrSerializedSize(const HelloWorld& data,size_t current_alignment = 0);eProsima_user_DllExport void deserialize(eprosima::fastcdr::Cdr& cdr);eProsima_user_DllExport static size_t getKeyMaxCdrSerializedSize(size_t current_alignment = 0);eProsima_user_DllExport static bool isKeyDefined();eProsima_user_DllExport void serializeKey(eprosima::fastcdr::Cdr& cdr) const;
private:uint32_t m_index;std::string m_message;
};

main函数中主要是根据参数创建相应的对象:

int main(int argc, char** argv)
{std::cout << "Starting "<< std::endl;int type = 1;int count = 10;long sleep = 100;if(argc > 1){if(strcmp(argv[1],"publisher")==0){type = 1;if (argc >= 3){count = atoi(argv[2]);if (argc == 4){sleep = atoi(argv[3]);}}}else if(strcmp(argv[1],"subscriber")==0)type = 2;}else{std::cout << "publisher OR subscriber argument needed" << std::endl;Log::Reset();return 0;}switch(type){case 1:{//创建publisher对象HelloWorldPublisher mypub;//初始化if(mypub.init()){//运行publisher,发布10次mypub.run(count, sleep);}break;}case 2:{//创建subscriber对象HelloWorldSubscriber mysub;//初始化if(mysub.init()){//运行subscribermysub.run();}break;}}Domain::stopAll();Log::Reset();return 0;
}

真正的逻辑实现放在了HelloWorldPublisherHelloWorldSubscriber的实现中。

来看一下初始化部分。

HelloWorldPublisher流程分析

HelloWorldPublisherc初始化基本步骤:

  • 初始化m_Hello的内容,m_Hello即为需要发送的data
  • 初始化参与者的参数对象
  • 创建participant
  • 初始化writer
  • 创建publisher

代码分析如下:
publisher

bool HelloWorldPublisher::init()
{/*1. Init m_Hello */// 设置初始的index = 0m_Hello.index(0);// 设置初始值messagem_Hello.message("HelloWorld");/*2. 初始化参与者的参数对象 */// 创建参与者的相关属性ParticipantAttributes PParam;PParam.rtps.builtin.discovery_config.discoveryProtocol = DiscoveryProtocol_t::SIMPLE;PParam.rtps.builtin.discovery_config.use_SIMPLE_EndpointDiscoveryProtocol = true;PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter = true;PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationWriterANDSubscriptionReader = true;PParam.rtps.builtin.discovery_config.leaseDuration = c_TimeInfinite;PParam.rtps.setName("Participant_pub");/*3. 创建participant */mp_participant = Domain::createParticipant(PParam);if (mp_participant == nullptr){return false;}
;/*4. 注册数据类型 */Domain::registerType(mp_participant, &m_type);/*5. 初始化writer */PublisherAttributes Wparam;Wparam.topic.topicKind = NO_KEY;Wparam.topic.topicDataType = "HelloWorld";Wparam.topic.topicName = "HelloWorldTopic";Wparam.topic.historyQos.kind = KEEP_LAST_HISTORY_QOS;Wparam.topic.historyQos.depth = 30;Wparam.topic.resourceLimitsQos.max_samples = 50;Wparam.topic.resourceLimitsQos.allocated_samples = 20;Wparam.times.heartbeatPeriod.seconds = 2;Wparam.times.heartbeatPeriod.nanosec = 200 * 1000 * 1000;Wparam.qos.m_reliability.kind = RELIABLE_RELIABILITY_QOS;/*6. 创建publisher */mp_publisher = Domain::createPublisher(mp_participant, Wparam, (PublisherListener*)&m_listener);if (mp_publisher == nullptr){return false;}return true;
}

其中m_Hello类型维HelloWorld, 调用index是设置其值:

void HelloWorld::index(uint32_t _index)
{m_index = _index;
}

然后开始执行其run函数:

void HelloWorldPublisher::run(uint32_t samples,uint32_t sleep)
{stop = false;/* 启动线程 */std::thread thread(&HelloWorldPublisher::runThread, this, samples, sleep);if (samples == 0){std::cout << "Publisher running. Please press enter to stop the Publisher at any time." << std::endl;std::cin.ignore();stop = true;}else{std::cout << "Publisher running " << samples << " samples." << std::endl;}thread.join();
}

可以看出run函数中只是启动了个线程,接着便进入线程中:

void HelloWorldPublisher::runThread(uint32_t samples,uint32_t sleep)
{/* 这里samples传入值为10 */if (samples == 0){while (!stop){/* 执行函数publish */if (publish(false)){std::cout << "Message: " << m_Hello.message() << " with index: " << m_Hello.index() << " SENT" <<std::endl;}std::this_thread::sleep_for(std::chrono::milliseconds(sleep));}}else{for (uint32_t i = 0; i < samples; ++i){if (!publish()){--i;}else{std::cout << "Message: " << m_Hello.message() << " with index: " << m_Hello.index() << " SENT" <<std::endl;}std::this_thread::sleep_for(std::chrono::milliseconds(sleep));}}
}

进入线程后主要是去执行了publish函数:

bool HelloWorldPublisher::publish(bool waitForListener)
{/* waitForListener传入为false */if (m_listener.firstConnected || !waitForListener || m_listener.n_matched > 0){// 设定index的值,index初始化为0m_Hello.index(m_Hello.index() + 1);// 发布数据m_Hellomp_publisher->write((void*)&m_Hello);return true;}return false;
}

其中mp_publisher的类型是eprosima::fastrtps::Publisher.(头文件路径:Fast-DDS/include/fastrtps/publisher/Publisher.h)

write函数实现部分在代码:Fast-DDS/src/cpp/fastrtps_deprecated/publisher/Publisher.cpp

bool Publisher::write(void* Data)
{logInfo(PUBLISHER, "Writing new data");return mp_impl->create_new_change(ALIVE, Data);
}
HelloWorldsubscriber流程分析

HelloWorldSubscriber初始化基本步骤:

  • 初始化参与者的参数对象
  • 创建participant
  • 初始化reader
  • 创建publisher

代码分析如下

bool HelloWorldSubscriber::init()
{/*1. 初始化参与者的参数对象 */ParticipantAttributes PParam;PParam.rtps.builtin.discovery_config.discoveryProtocol = DiscoveryProtocol_t::SIMPLE;PParam.rtps.builtin.discovery_config.use_SIMPLE_EndpointDiscoveryProtocol = true;PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter = true;PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationWriterANDSubscriptionReader = true;PParam.rtps.builtin.discovery_config.leaseDuration = c_TimeInfinite;PParam.rtps.setName("Participant_sub");/*2. 创建参与者 */mp_participant = Domain::createParticipant(PParam);if (mp_participant == nullptr){return false;}/*3. 注册数据类型 */Domain::registerType(mp_participant, &m_type);/*4. 初始化reader */SubscriberAttributes Rparam;Rparam.topic.topicKind = NO_KEY;Rparam.topic.topicDataType = "HelloWorld";Rparam.topic.topicName = "HelloWorldTopic";Rparam.topic.historyQos.kind = KEEP_LAST_HISTORY_QOS;Rparam.topic.historyQos.depth = 30;Rparam.topic.resourceLimitsQos.max_samples = 50;Rparam.topic.resourceLimitsQos.allocated_samples = 20;Rparam.qos.m_reliability.kind = RELIABLE_RELIABILITY_QOS;Rparam.qos.m_durability.kind = TRANSIENT_LOCAL_DURABILITY_QOS;/*4. 创建subscriber */mp_subscriber = Domain::createSubscriber(mp_participant, Rparam, (SubscriberListener*)&m_listener);if (mp_subscriber == nullptr){return false;}return true;
}

继续看一下run函数:

void HelloWorldSubscriber::run()
{std::cout << "Subscriber running. Please press enter to stop the Subscriber" << std::endl;std::cin.ignore();
}

可以看出HelloWorldSubscriber的run函数中几乎什么都没做,只是等待一个按键后退出。

这里的主要读取数据的操作是由init函数中注册的&m_listener来完成的。

m_listener的相关代码:

   class SubListener : public eprosima::fastrtps::SubscriberListener{public:SubListener(): n_matched(0), n_samples(0){}~SubListener(){}void onSubscriptionMatched(eprosima::fastrtps::Subscriber* sub, eprosima::fastrtps::rtps::MatchingInfo& info);void onNewDataMessage(eprosima::fastrtps::Subscriber* sub);HelloWorld m_Hello;eprosima::fastrtps::SampleInfo_t m_info;int n_matched;uint32_t n_samples;} m_listener;

SubscriberListener的代码路径是Fast-DDS/include/fastrtps/subscriber/SubscriberListener.h

class RTPS_DllAPI SubscriberListener
{public:SubscriberListener(){}virtual ~SubscriberListener(){}/*** 由用户实现的功能,包含接收到新数据消息时要执行的操作。*/virtual void onNewDataMessage(Subscriber* sub){(void)sub;}/*** Virtual 当订阅者与新的 Writer 匹配(或不匹配)时调用的方法。*/virtual void onSubscriptionMatched(Subscriber* sub,rtps::MatchingInfo& info){(void)sub;(void)info;}/*** 当主题错过最后期限时调用的方法*/virtual void on_requested_deadline_missed(Subscriber* sub,const RequestedDeadlineMissedStatus& status){(void)sub;(void)status;}/*** 当与订阅服务器关联的活动状态更改时调用的方法*/virtual void on_liveliness_changed(Subscriber* sub,const LivelinessChangedStatus& status){(void)sub;(void)status;}
};

可以看出onNewDataMessage函数是消息相应的函数:

void HelloWorldSubscriber::SubListener::onNewDataMessage(Subscriber* sub)
{if (sub->takeNextData((void*)&m_Hello, &m_info)){if (m_info.sampleKind == ALIVE){this->n_samples++;// Print your structure data here.std::cout << "Message " << m_Hello.message() << " " << m_Hello.index() << " RECEIVED" << std::endl;}}
}

这里的实现就是获取数据后并打印。

sub的类型为eprosima::fastrtps::Subscriber*(头文件路径:Fast-DDS/include/fastrtps/subscriber/Subscriber.h).

其中定义了``函数:

    /*** @brief 从订阅者处获取下一个样本。 从订户中删除样本。* @param sample 指向您希望存储样本的对象的指针。* @param info 指向 SampleInfo_t 结构的指针,该结构通知您有关您的样本。* @return 如果取样则为真。* @note 该方法被阻塞一段时间。* SubscriberAttributes 上的 ReliabilityQosPolicy.max_blocking_time 定义了这段时间。*/bool takeNextData(void* sample,SampleInfo_t* info);

好了今天的分享就到这里,明天继续。_

【FastDDS学习笔记】HelloWorld示例程序编译和运行

目录

第一章:【FastDDS学习笔记】Ubuntu22上安装fastDDS环境
第二章:【FastDDS学习笔记】HelloWorld示例程序编译和运行
第三章:【FastDDS学习笔记】Fast-DDS-Gen安装记录

代码位置:~/Fast-DDS/Fast-DDS/examples/C++/HelloWorldExample

设置相应的环境变量

因为上一章节中,我们编译的库并没有放到系统中,而是放在了一个文件夹中,所以这里我们要先设定以下对应的环境变量:

export LD_LIBRARY_PATH=/home/xiaoqing/Fast-DDS/install/lib

这里的路径就是编译时候,填写的路径。

如果想要永久生效,执行以下命令:

echo 'export LD_LIBRARY_PATH=/home/xiaoqing/Fast-DDS/install/lib' >> ~/.bashrc

这里我是用的是永久生效的。

编译HelloWorld

在ubuntu中执行以下命令:

mkdir build && cd build
cmake .. -DCMAKE_PREFIX_PATH=~/Fast-DDS/install/

运行效果:

继续编译:

make

生成运行实例:

运行HelloWorld

接下来我们来运行HelloWorld,看一下效果。

新开一个终端,运行一个publisher:

./HelloWorldExample publisher

再新开一个终端,运行一个subscriber:

./HelloWorldExample subscriber

执行效果:
publisher:

subscriber:

源码分析

源码的文件目录:

HelloWorldExample
├── build
│  ├── CMakeCache.txt
│  ├── CMakeFiles
│  ├── cmake_install.cmake
│  ├── HelloWorldExample
│  └── Makefile
├── CMakeLists.txt
├── HelloWorld.cxx // 由idl生成
├── HelloWorld.h // 由idl生成
├── HelloWorld.idl      //idl 文件
├── HelloWorld_main.cpp
├── HelloWorldPublisher.cpp
├── HelloWorldPublisher.h
├── HelloWorldPubSubTypes.cxx // 由idl生成
├── HelloWorldPubSubTypes.h // 由idl生成
├── HelloWorldSubscriber.cpp
├── HelloWorldSubscriber.h
└── README.txt

代码中
HelloWorld.idl 定义payload,会生成HelloWorld.hHelloWorld.cxxHelloWorldPubSubTypes.hHelloWorldPubSubTypes.cxx四个文件,包含了序列化与反序列化的逻辑。

先来看一下HelloWorld.idl:

struct HelloWorld
{unsigned long index;string message;
};

只是简单地定义了下数据传输的结构体。

结构体包含一个id和一个字符串类型的message。

而这,也就是DDS概念模型中,TopicDataType的定义。

主题在概念上适合发布和订阅。

从OMG的DDS官网我们可以知道,IDL(DDS基于的是IDL4 v4.2标准)是一种不依赖于编程语言的,用于定义数据类型和接口的描述性语言。他同时也是OMG组织制定的标准。Fast DDS-Gen作为一个Java应用程序,就是用来解析idl文件,生成数据类型定义的(即HelloWorld.hHelloWorld.cxxHelloWorldPubSubTypes.hHelloWorldPubSubTypes.cxx)。

生成的文件HelloWorld.hHelloWorld.cxx`中会根据这个结构体生成类,并添加一些序列化使用的函数:

class HelloWorld
{
public:eProsima_user_DllExport HelloWorld& operator =(const HelloWorld& x);eProsima_user_DllExport HelloWorld& operator =(HelloWorld&& x);eProsima_user_DllExport bool operator ==(const HelloWorld& x) const;eProsima_user_DllExport bool operator !=(const HelloWorld& x) const;eProsima_user_DllExport void index(uint32_t _index);eProsima_user_DllExport uint32_t index() const;eProsima_user_DllExport uint32_t& index();eProsima_user_DllExport void message(const std::string& _message);eProsima_user_DllExport void message(std::string&& _message);eProsima_user_DllExport const std::string& message() const;eProsima_user_DllExport std::string& message();eProsima_user_DllExport static size_t getMaxCdrSerializedSize(size_t current_alignment = 0);eProsima_user_DllExport static size_t getCdrSerializedSize(const HelloWorld& data,size_t current_alignment = 0);eProsima_user_DllExport void deserialize(eprosima::fastcdr::Cdr& cdr);eProsima_user_DllExport static size_t getKeyMaxCdrSerializedSize(size_t current_alignment = 0);eProsima_user_DllExport static bool isKeyDefined();eProsima_user_DllExport void serializeKey(eprosima::fastcdr::Cdr& cdr) const;
private:uint32_t m_index;std::string m_message;
};

main函数中主要是根据参数创建相应的对象:

int main(int argc, char** argv)
{std::cout << "Starting "<< std::endl;int type = 1;int count = 10;long sleep = 100;if(argc > 1){if(strcmp(argv[1],"publisher")==0){type = 1;if (argc >= 3){count = atoi(argv[2]);if (argc == 4){sleep = atoi(argv[3]);}}}else if(strcmp(argv[1],"subscriber")==0)type = 2;}else{std::cout << "publisher OR subscriber argument needed" << std::endl;Log::Reset();return 0;}switch(type){case 1:{//创建publisher对象HelloWorldPublisher mypub;//初始化if(mypub.init()){//运行publisher,发布10次mypub.run(count, sleep);}break;}case 2:{//创建subscriber对象HelloWorldSubscriber mysub;//初始化if(mysub.init()){//运行subscribermysub.run();}break;}}Domain::stopAll();Log::Reset();return 0;
}

真正的逻辑实现放在了HelloWorldPublisherHelloWorldSubscriber的实现中。

来看一下初始化部分。

HelloWorldPublisher流程分析

HelloWorldPublisherc初始化基本步骤:

  • 初始化m_Hello的内容,m_Hello即为需要发送的data
  • 初始化参与者的参数对象
  • 创建participant
  • 初始化writer
  • 创建publisher

代码分析如下:
publisher

bool HelloWorldPublisher::init()
{/*1. Init m_Hello */// 设置初始的index = 0m_Hello.index(0);// 设置初始值messagem_Hello.message("HelloWorld");/*2. 初始化参与者的参数对象 */// 创建参与者的相关属性ParticipantAttributes PParam;PParam.rtps.builtin.discovery_config.discoveryProtocol = DiscoveryProtocol_t::SIMPLE;PParam.rtps.builtin.discovery_config.use_SIMPLE_EndpointDiscoveryProtocol = true;PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter = true;PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationWriterANDSubscriptionReader = true;PParam.rtps.builtin.discovery_config.leaseDuration = c_TimeInfinite;PParam.rtps.setName("Participant_pub");/*3. 创建participant */mp_participant = Domain::createParticipant(PParam);if (mp_participant == nullptr){return false;}
;/*4. 注册数据类型 */Domain::registerType(mp_participant, &m_type);/*5. 初始化writer */PublisherAttributes Wparam;Wparam.topic.topicKind = NO_KEY;Wparam.topic.topicDataType = "HelloWorld";Wparam.topic.topicName = "HelloWorldTopic";Wparam.topic.historyQos.kind = KEEP_LAST_HISTORY_QOS;Wparam.topic.historyQos.depth = 30;Wparam.topic.resourceLimitsQos.max_samples = 50;Wparam.topic.resourceLimitsQos.allocated_samples = 20;Wparam.times.heartbeatPeriod.seconds = 2;Wparam.times.heartbeatPeriod.nanosec = 200 * 1000 * 1000;Wparam.qos.m_reliability.kind = RELIABLE_RELIABILITY_QOS;/*6. 创建publisher */mp_publisher = Domain::createPublisher(mp_participant, Wparam, (PublisherListener*)&m_listener);if (mp_publisher == nullptr){return false;}return true;
}

其中m_Hello类型维HelloWorld, 调用index是设置其值:

void HelloWorld::index(uint32_t _index)
{m_index = _index;
}

然后开始执行其run函数:

void HelloWorldPublisher::run(uint32_t samples,uint32_t sleep)
{stop = false;/* 启动线程 */std::thread thread(&HelloWorldPublisher::runThread, this, samples, sleep);if (samples == 0){std::cout << "Publisher running. Please press enter to stop the Publisher at any time." << std::endl;std::cin.ignore();stop = true;}else{std::cout << "Publisher running " << samples << " samples." << std::endl;}thread.join();
}

可以看出run函数中只是启动了个线程,接着便进入线程中:

void HelloWorldPublisher::runThread(uint32_t samples,uint32_t sleep)
{/* 这里samples传入值为10 */if (samples == 0){while (!stop){/* 执行函数publish */if (publish(false)){std::cout << "Message: " << m_Hello.message() << " with index: " << m_Hello.index() << " SENT" <<std::endl;}std::this_thread::sleep_for(std::chrono::milliseconds(sleep));}}else{for (uint32_t i = 0; i < samples; ++i){if (!publish()){--i;}else{std::cout << "Message: " << m_Hello.message() << " with index: " << m_Hello.index() << " SENT" <<std::endl;}std::this_thread::sleep_for(std::chrono::milliseconds(sleep));}}
}

进入线程后主要是去执行了publish函数:

bool HelloWorldPublisher::publish(bool waitForListener)
{/* waitForListener传入为false */if (m_listener.firstConnected || !waitForListener || m_listener.n_matched > 0){// 设定index的值,index初始化为0m_Hello.index(m_Hello.index() + 1);// 发布数据m_Hellomp_publisher->write((void*)&m_Hello);return true;}return false;
}

其中mp_publisher的类型是eprosima::fastrtps::Publisher.(头文件路径:Fast-DDS/include/fastrtps/publisher/Publisher.h)

write函数实现部分在代码:Fast-DDS/src/cpp/fastrtps_deprecated/publisher/Publisher.cpp

bool Publisher::write(void* Data)
{logInfo(PUBLISHER, "Writing new data");return mp_impl->create_new_change(ALIVE, Data);
}
HelloWorldsubscriber流程分析

HelloWorldSubscriber初始化基本步骤:

  • 初始化参与者的参数对象
  • 创建participant
  • 初始化reader
  • 创建publisher

代码分析如下

bool HelloWorldSubscriber::init()
{/*1. 初始化参与者的参数对象 */ParticipantAttributes PParam;PParam.rtps.builtin.discovery_config.discoveryProtocol = DiscoveryProtocol_t::SIMPLE;PParam.rtps.builtin.discovery_config.use_SIMPLE_EndpointDiscoveryProtocol = true;PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter = true;PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationWriterANDSubscriptionReader = true;PParam.rtps.builtin.discovery_config.leaseDuration = c_TimeInfinite;PParam.rtps.setName("Participant_sub");/*2. 创建参与者 */mp_participant = Domain::createParticipant(PParam);if (mp_participant == nullptr){return false;}/*3. 注册数据类型 */Domain::registerType(mp_participant, &m_type);/*4. 初始化reader */SubscriberAttributes Rparam;Rparam.topic.topicKind = NO_KEY;Rparam.topic.topicDataType = "HelloWorld";Rparam.topic.topicName = "HelloWorldTopic";Rparam.topic.historyQos.kind = KEEP_LAST_HISTORY_QOS;Rparam.topic.historyQos.depth = 30;Rparam.topic.resourceLimitsQos.max_samples = 50;Rparam.topic.resourceLimitsQos.allocated_samples = 20;Rparam.qos.m_reliability.kind = RELIABLE_RELIABILITY_QOS;Rparam.qos.m_durability.kind = TRANSIENT_LOCAL_DURABILITY_QOS;/*4. 创建subscriber */mp_subscriber = Domain::createSubscriber(mp_participant, Rparam, (SubscriberListener*)&m_listener);if (mp_subscriber == nullptr){return false;}return true;
}

继续看一下run函数:

void HelloWorldSubscriber::run()
{std::cout << "Subscriber running. Please press enter to stop the Subscriber" << std::endl;std::cin.ignore();
}

可以看出HelloWorldSubscriber的run函数中几乎什么都没做,只是等待一个按键后退出。

这里的主要读取数据的操作是由init函数中注册的&m_listener来完成的。

m_listener的相关代码:

   class SubListener : public eprosima::fastrtps::SubscriberListener{public:SubListener(): n_matched(0), n_samples(0){}~SubListener(){}void onSubscriptionMatched(eprosima::fastrtps::Subscriber* sub, eprosima::fastrtps::rtps::MatchingInfo& info);void onNewDataMessage(eprosima::fastrtps::Subscriber* sub);HelloWorld m_Hello;eprosima::fastrtps::SampleInfo_t m_info;int n_matched;uint32_t n_samples;} m_listener;

SubscriberListener的代码路径是Fast-DDS/include/fastrtps/subscriber/SubscriberListener.h

class RTPS_DllAPI SubscriberListener
{public:SubscriberListener(){}virtual ~SubscriberListener(){}/*** 由用户实现的功能,包含接收到新数据消息时要执行的操作。*/virtual void onNewDataMessage(Subscriber* sub){(void)sub;}/*** Virtual 当订阅者与新的 Writer 匹配(或不匹配)时调用的方法。*/virtual void onSubscriptionMatched(Subscriber* sub,rtps::MatchingInfo& info){(void)sub;(void)info;}/*** 当主题错过最后期限时调用的方法*/virtual void on_requested_deadline_missed(Subscriber* sub,const RequestedDeadlineMissedStatus& status){(void)sub;(void)status;}/*** 当与订阅服务器关联的活动状态更改时调用的方法*/virtual void on_liveliness_changed(Subscriber* sub,const LivelinessChangedStatus& status){(void)sub;(void)status;}
};

可以看出onNewDataMessage函数是消息相应的函数:

void HelloWorldSubscriber::SubListener::onNewDataMessage(Subscriber* sub)
{if (sub->takeNextData((void*)&m_Hello, &m_info)){if (m_info.sampleKind == ALIVE){this->n_samples++;// Print your structure data here.std::cout << "Message " << m_Hello.message() << " " << m_Hello.index() << " RECEIVED" << std::endl;}}
}

这里的实现就是获取数据后并打印。

sub的类型为eprosima::fastrtps::Subscriber*(头文件路径:Fast-DDS/include/fastrtps/subscriber/Subscriber.h).

其中定义了``函数:

    /*** @brief 从订阅者处获取下一个样本。 从订户中删除样本。* @param sample 指向您希望存储样本的对象的指针。* @param info 指向 SampleInfo_t 结构的指针,该结构通知您有关您的样本。* @return 如果取样则为真。* @note 该方法被阻塞一段时间。* SubscriberAttributes 上的 ReliabilityQosPolicy.max_blocking_time 定义了这段时间。*/bool takeNextData(void* sample,SampleInfo_t* info);

好了今天的分享就到这里,明天继续。_

发布评论

评论列表 (0)

  1. 暂无评论