Fast DDS入门五、在Windows平台创建一个简单的Fast DDS示例程序

1、创建简单示例程序

在这里,先建立一个IDL文件,然后通过使用Fast DDS-Gen生成程序生成这个简单示例程序。Fast DDS-Gen程序的编译安装请参考《Fast DDS入门二、Fast DDS在Windows平台的编译安装》,Fast DDS-Gen程序的使用请参考《Fast DDS入门四、Fast DDS-Gen使用介绍》。

(1)创建IDL文件

先建立一个单独的目录HelloWorldExample,在该目录下建立IDL文件和生成示例程序的c++源程序文件。

本示例构建最小的应用程序,必须通过IDL文件定义Topic。在本例中,IDL定义的Topic数据类型只是一个字符串消息。在首选文本编辑器中,创建具有以下内容的HelloWorld.IDL文件,并将其保存在HelloWorldExample目录中。

// HelloWorld.IDL

struct HelloWorld

{

string message;

};

(2)Fast DDS-Gen生成示例程序源码

然后,将此文件转换为Fast DDS能够理解的内容。为此,使用Fast DDS Gen代码生成工具,它可以执行生成示例的操作。执行如下命令:

fastddsgen.bat -example CMake HelloWorld.IDL

执行命令成功结束后,会产生一系列源文件,如下所示:

Fast DDS入门五、在Windows平台创建一个简单的Fast DDS示例程序_第1张图片

(3)CMake配置生成vs解决方案

Fast DDS-Gen生成示例程序源码后,生成了系列的.h、.cxx源文件,同时生成了CMakeLists.txt文件。因此可以通过CMake配置示例程序的依赖项目生成vs解决方案,然后通过vs编译生成可执行程序。相关依赖项的编译生成可参考《Fast DDS入门二、Fast DDS在Windows平台的编译安装》,以下为CMake界面程序的配置参考:

Fast DDS入门五、在Windows平台创建一个简单的Fast DDS示例程序_第2张图片

Configure和generate成功后,在out子目录下生成了项目、解决方案等文件,在out目录下可看到如下:

Fast DDS入门五、在Windows平台创建一个简单的Fast DDS示例程序_第3张图片

2、示例代码分析及编译运行

用vs打开CMake生成的generated_code.sln解决方案,如下图所示:

Fast DDS入门五、在Windows平台创建一个简单的Fast DDS示例程序_第4张图片

可以看到有4个项目,其中源码项目是:HelloWorld_lib和HelloWorld项目。HelloWorld_lib项目包含IDL生成的结构体类HelloWorld.h和HelloWorld.cxx,这个项目生成lib静态库。HelloWorld项目,依赖于HelloWorld_lib项目,可生成发布/订阅可执行程序。

(1)源代码分析

HelloWorld_lib项目的HelloWorld类,主要实现了IDL定义结构体类型的序列化和反序列化,头文件简要如下:

class HelloWorld
{
public: 
   /*!
    * @brief This function serializes an object using CDR serialization.
    * @param cdr CDR serialization object.
    */
   eProsima_user_DllExport void serialize(
            eprosima::fastcdr::Cdr& cdr) const;
 
   /*!
    * @brief This function deserializes an object using CDR serialization.
    * @param cdr CDR serialization object.
    */
   eProsima_user_DllExport void deserialize(
            eprosima::fastcdr::Cdr& cdr);
 
private:
   std::stringm_message;
};

HelloWorld项目的HelloWorldPubSubType类是HelloWorld类的封装类,其继承自TopicDataType类,该类可注册到Fast DDS中,序列化和反序列化函数是vitual函数在父类中声明,在子类中实现,在发布订阅函数中实现回调处理,发布时将HelloWorld类序列化并通过socket发送,订阅时通过socket接收到二进制后执行HelloWorld类的反序列化,以下为HelloWorldPubSubType类的核心代码:

bool HelloWorldPubSubType::serialize( //序列化函数
       void* data,
       SerializedPayload_t* payload)
{
   HelloWorld* p_type= static_cast(data);
   // Object that manages the raw buffer.
   eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload->data), payload->max_size);
   // Object that serializes the data.
   eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN,eprosima::fastcdr::Cdr::DDS_CDR);
   payload->encapsulation= ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE;
   // Serialize encapsulation
   ser.serialize_encapsulation();
   try
   {
       // Serialize the object.
       p_type->serialize(ser);
   }
   catch(eprosima::fastcdr::exception::NotEnoughMemoryException& /*exception*/)
   {
       return false;
   }
 
   // Get the serialized length
   payload->length= static_cast(ser.getSerializedDataLength());
    return true;
}
 
bool HelloWorldPubSubType::deserialize(  //反序列化函数
       SerializedPayload_t* payload,
       void* data)
{
   try
   {
       //Convert DATA to pointer of your type
       HelloWorld* p_type= static_cast(data);
       // Object that manages the raw buffer.
       eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload->data), payload->length);
       // Object that deserializes the data.
       eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN,eprosima::fastcdr::Cdr::DDS_CDR);
       // Deserialize encapsulation.
       deser.read_encapsulation();
       payload->encapsulation= deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE;
       // Deserialize the object.
       p_type->deserialize(deser);
   }
   catch(eprosima::fastcdr::exception::NotEnoughMemoryException& /*exception*/)
   {
       return false;
   }
   return true;
}

HelloWorld项目的发布订阅类分别是:HelloWorldPublisher类和HelloWorldSubscriber类。发布类的核心源码有两个重要的成员函数,初始化函数init()和发送数据函数run(),代码如下所示:

bool HelloWorldPublisher::init()  //初始化函数
{
   /* Initialize data_ here */
   //CREATE THE PARTICIPANT
   DomainParticipantQos pqos;
   pqos.name("Participant_pub");
   participant_ = DomainParticipantFactory::get_instance()->create_participant(0, pqos);
   if(participant_ == nullptr)
   {
       return false;
   }
   //REGISTER THE TYPE
   type_.register_type(participant_);
   //CREATE THE PUBLISHER
   publisher_ = participant_->create_publisher(PUBLISHER_QOS_DEFAULT, nullptr);
   if(publisher_ == nullptr)
   {
       return false;
   }
   //CREATE THE TOPIC
   topic_ = participant_->create_topic(
       "HelloWorldTopic",
       type_.get_type_name(),
       TOPIC_QOS_DEFAULT);
   if (topic_== nullptr)
   {
       return false;
   }
   // CREATE THE WRITER
   writer_ = publisher_->create_datawriter(topic_,DATAWRITER_QOS_DEFAULT, &listener_);
   if(writer_ == nullptr)
   {
       return false;
   }
   std::cout << "HelloWorld DataWriter created." << std::endl;
   return true;
}

发布者的初始化函数,首先创建参与者participant,参与者participant是publisher和subscriber的容器,可用来创建发布者和订阅者。之后参与者注册HelloWorld的数据类型,创建publisher,创建topic主题,最后通过发布者publisher创建写入器writer,通过writer可直接发送数据。

void HelloWorldPublisher::run()  //运行函数发送数据
{
   std::cout << "HelloWorld DataWriter waiting forDataReaders." << std::endl;
   while(listener_.matched == 0)
   {
       std::this_thread::sleep_for(std::chrono::milliseconds(250)); // Sleep 250 ms
   }
   // Publication code
   HelloWorld st;
   /* Initialize your structure here */
   int msgsent= 0;
   char ch = 'y';
   do
   {
       if (ch == 'y')
       {
            writer_->write(&st);
            ++msgsent;
            std::cout << "Sendingsample, count=" << msgsent << ", sendanother sample?(y-yes,n-stop): ";
       }
       else if (ch == 'n')
       {
            std::cout << "Stoppingexecution " << std::endl;
            break;
       }
       else
       {
            std::cout << "Command" << ch << " notrecognized, please enter \"y/n\":";
       }
   } while(std::cin >> ch);
}

发送数据函数通过publisher的写入器writer直接发送HelloWorld类型的数据st。

订阅类的核心源码也有两个重要的成员函数,初始化函数init()和其订阅监听器子类成员函数SubListener::on_data_available(),Init()成员函数雷同于publisher的init()成员函数,只不过是subscriber的init()函数是建立订阅者subscriber和监听器listener,通过监听器回调接收订阅的数据。

监听器SubListener::on_data_available()数据接收代码如下所示:

void HelloWorldSubscriber::SubListener::on_data_available(  //监听接收数据函数
       DataReader* reader)
{
   // Take data
   HelloWorld st;
   SampleInfo info;
 
   if (reader->take_next_sample(&st,&info) == ReturnCode_t::RETCODE_OK)
   {
       if(info.valid_data)
       {
            // Printyour structure data here.
            ++samples;
            std::cout << "Samplereceived, count=" << samples << std::endl;
       }
   }
}

on_data_available()函数,是监听器的回调函数,被订阅者subscriber的阅读器reader接收到数据后,将socket接收的二进制数据反序列化成HelloWorld类数据,是开发人员的订阅接收数据入口。

(2)编译运行可执行程序

在vs开发工具中执行build命令,将在out/debug或release目录下生成可执行二进制文件HelloWorld.exe,同时将其依赖的库文件也拷贝到这里。如下所示:

Fast DDS入门五、在Windows平台创建一个简单的Fast DDS示例程序_第5张图片

然后通过powershell执行两个HelloWorld.exe命令,第一个命令后面空格后输入subscriber字符,第二个命令后面空格后输入publisher字符,发布程序和订阅程序将能够正常运行。运行界面如下所示:

Fast DDS入门五、在Windows平台创建一个简单的Fast DDS示例程序_第6张图片

至此,Fast DDS的简单发布订阅示例程序的创建、编译、运行就成功结束了。

CSDN下载:免费下载Fast DDS简单发布订阅示例程序源码

github下载:git clone https://github.com/FantasticSaltedFish/Fast-DDS.git

你可能感兴趣的:(Fast,DDS入门,网络协议)