上一节《Fast DDS入门五、在Windows平台创建一个简单的FastDDS示例程序》,该示例程序介绍了采用Fast DDS-Gen工具通过接口定义语言(IDL)来生成数据结构类,该数据结构类具备序列化反序列化处理,用户只需要关注编写IDL的结构体接口以及在发布类和订阅类处理好结构体类数据的发送和接收就可以了。
本节介绍一个更直接、更灵活的数据类型构造方法,在发布类和订阅类中直接构造动态类型,可以省略IDL的繁琐生成过程以及系列数据结构类文件。本节的动态类型方式实现了如下IDL结构体同等效果:
struct HelloWorld
{
unsigned long index;
string message;
};
我们直接采用Fast DDS的一个动态类型实例DynamicHelloWorldExample,可以看到只有发布类、订阅类和main主函数cpp文件,少了很多结构体类相关文件:
int main(int argc,char** argv)
{
std::cout << "Starting " << std::endl;
if(!parseArgs(argc, argv))
{
usage();
return 0;
}
switch (type)
{
case 1:
{
HelloWorldPublisher mypub;
if (mypub.init())
{
mypub.run(count, sleep);
}
break;
}
case 2:
{
HelloWorldSubscriber mysub;
if (mysub.init())
{
mysub.run();
}
break;
}
}
Domain::stopAll();
Log::Reset();
return 0;
}
首先通过命令行方式,判断用户输入“publisher”或“subscriber”字符,判断启动发布过程还是订阅过程。发布过程:初始化发布类对象,之后运行发布类的run()函数,实际是启动了一个发送数据的线程。订阅过程:初始化订阅类对象,之后运行订阅类的run()函数,该run()函数不是一个线程,只是输出提示订阅过程已经开始。
发布类主要有两个函数,一个是init()初始化函数,一个是run()函数。
bool HelloWorldPublisher::init()
{
// Create basic builders
DynamicTypeBuilder_ptr struct_type_builder(DynamicTypeBuilderFactory::get_instance()->create_struct_builder());
// Add members to the struct.
struct_type_builder->add_member(0, "index", DynamicTypeBuilderFactory::get_instance()->create_uint32_type());
struct_type_builder->add_member(1, "message", DynamicTypeBuilderFactory::get_instance()->create_string_type());
struct_type_builder->set_name("HelloWorld");
DynamicType_ptr dynType= struct_type_builder->build();
m_DynType.SetDynamicType(dynType);
m_DynHello = DynamicDataFactory::get_instance()->create_data(dynType);
m_DynHello->set_uint32_value(0, 0);
m_DynHello->set_string_value("HelloWorld", 1);
ParticipantAttributes PParam;
PParam.rtps.setName("DynHelloWorld_pub");
mp_participant = Domain::createParticipant(PParam, (ParticipantListener*)&m_part_list);
if(mp_participant == nullptr)
{
return false;
}
//REGISTER THE TYPE
Domain::registerDynamicType(mp_participant,&m_DynType);
//CREATE THE PUBLISHER
PublisherAttributes Wparam;
Wparam.topic.topicKind = NO_KEY;
Wparam.topic.topicDataType = "HelloWorld";
Wparam.topic.topicName = "HelloWorldTopic";
mp_publisher = Domain::createPublisher(mp_participant, Wparam, (PublisherListener*)&m_listener);
if(mp_publisher == nullptr)
{
return false;
}
return true;
}
Init()函数,首先构造了结构体builder,在结构体builder中添加了两个成员index和message,然后通过结构体builder设置动态发布订阅类型DynamicPubSubType m_DynType,并生成动态发布订阅数据DynamicData m_DynHello。创建participant之后,注册动态发布订阅类型DynamicPubSubTypem_DynType,最后创建publisher。
void HelloWorldPublisher::run(uint32_t samples,uint32_t sleep)
{
stop = false;
std::threadthread(&HelloWorldPublisher::runThread, this, samples, sleep);
if (samples == 0)
{
std::cout << "Publisher running. Please press enter to stop thePublisher at any time." << std::endl;
std::cin.ignore();
stop = true;
}
else
{
std::cout << "Publisher running " << samples << " samples." << std::endl;
}
thread.join();
}
run()函数启动了一个线程,该线程的工作内容是不断发送m_DynHello数据,每发送一次数据,m_DynHello的index成员变量增1,如下:
bool HelloWorldPublisher::publish(bool waitForListener)
{
if(m_listener.firstConnected || !waitForListener || m_listener.n_matched > 0)
{
uint32_t index;
m_DynHello->get_uint32_value(index, 0);
m_DynHello->set_uint32_value(index + 1, 0);
mp_publisher->write((void*)m_DynHello);
return true;
}
return false;
}
订阅类也主要有两个函数,一个是init()初始化函数,一个是监听类SubListener::onNewDataMessage ()函数。
bool HelloWorldSubscriber::init()
{
ParticipantAttributes PParam;
PParam.rtps.setName("DynHelloWorld_sub");
mp_participant = Domain::createParticipant(PParam, (ParticipantListener*)&m_part_list);
if(mp_participant == nullptr)
{
return false;
}
// Create basic types and add members to the struct.
DynamicTypeBuilder_ptr created_type_ulong = DynamicTypeBuilderFactory::get_instance()->create_uint32_builder();
DynamicTypeBuilder_ptr created_type_string = DynamicTypeBuilderFactory::get_instance()->create_string_builder();
DynamicTypeBuilder_ptr struct_type_builder = DynamicTypeBuilderFactory::get_instance()->create_struct_builder();
struct_type_builder->add_member(0, "index", created_type_ulong.get());
struct_type_builder->add_member(1, "message", created_type_string.get());
struct_type_builder->set_name("HelloWorld");
DynamicType_ptr dynType= struct_type_builder->build();
m_DynType.SetDynamicType(dynType);
m_listener.m_DynHello = DynamicDataFactory::get_instance()->create_data(dynType);
//REGISTER THE TYPE
Domain::registerDynamicType(mp_participant,&m_DynType);
//CREATE THE SUBSCRIBER
SubscriberAttributes Rparam;
Rparam.topic.topicKind = NO_KEY;
Rparam.topic.topicDataType = "HelloWorld";
Rparam.topic.topicName = "HelloWorldTopic";
mp_subscriber = Domain::createSubscriber(mp_participant, Rparam, (SubscriberListener*)&m_listener);
if(mp_subscriber == nullptr)
{
return false;
}
return true;
}
订阅类的初始化函数与发布类型的初始化函数基本类似。首先构造了结构体builder,在结构体builder中添加了两个成员index和message,然后通过结构体builder设置动态发布订阅类型DynamicPubSubType m_DynType,并生成动态发布订阅数据DynamicData m_DynHello。创建participant之后,注册动态发布订阅类型DynamicPubSubTypem_DynType,最后创建subscriber,同时设置好监听实例m_listener,监听实例实现了SubListener::onNewDataMessage ()回调函数,可以接收订阅的数据。
void HelloWorldSubscriber::SubListener::onNewDataMessage(
Subscriber* sub)
{
if (sub->takeNextData((void*)m_DynHello,&m_info))
{
if(m_info.sampleKind == ALIVE)
{
this->n_samples++;
// Printyour structure data here.
std::string message;
m_DynHello->get_string_value(message, 1);
uint32_t index;
m_DynHello->get_uint32_value(index, 0);
std::cout << "Message:" << message << " with index:" << index << "RECEIVED" << std::endl;
}
}
}
监听类SubListener::onNewDataMessage ()函数,实现了DynamicData m_DynHello数据的接收,分别取出index和message字段数据,并输出显示屏。
将所需的库拷贝到Debug目录下:
命令行分别运行Debug目录下的exe程序,通过后面附加“subscriber”或“publisher”分别启动订阅程序和发布程序,publisher将连续发送10条数据,subscriber全部收到,如下图所示:
CSDN下载:免费下载Fast DDS动态类型示例程序源码
github下载:git clone https://github.com/FantasticSaltedFish/Fast-DDS.git