fastdds是一套开源的又C++开发的基于DDS通讯协议的中间件。因为项目需要,需要在B/S项目中使用,我们采用的是C#开发,所以如何打通如何在C#中使用fastdds,下面就是实现的过程:
首先,我们需要下载fastdds的源码,我是直接下载到一个eProsima_Fast-DDS-2.10.0-Windows.exe,安装包的方式安装代码以及需要的东西,这个方式其实就是把代码包装成安装包,所以下载源码和这个是基本一致的。
1)前提条件
在Windows环境中从源安装FastDDS需要在系统中安装以下工具:
Visual Studio
Chocolatey
CMake、pip3、wget和git
Gtest[可选]
具体安装就不做过多介绍,其实我主要用到的是vs2022,和CMake。
vs2022需要安装支持c++开发的功能模块
2)安装
eProsima_Fast-DDS-2.10.0-Windows.exe 安装完成后,会在目录下生成项目文件夹,如下图所示:
examples就是该中间件使用的详细例子文件夹。例子路径:
F:\Program Files\eProsima\fastrtps 2.10.0\examples\cpp\dds
接下来,我们需要对所有的例子进行生成可以以vs打开的项目,我们需要在上免得路径下进入命令窗体,
然后依次执行如下命令:
mkdir build
cd build
cmake ..
//cmake ..可以换成下面这句,这是官网的推荐
cmake -Bbuildexample -DFASTDDS_STATIC=ON .
就可以在build文件夹下生成所有实例的项目文件,用vs可以打开。
关于上面项目能否正常运行的问题,请参考进行处理:Windows下运行Fast DDS示例程序(包含.idl文件的使用方法)_fastdds_Eliza_Her的博客-CSDN博客
接下来,就是对一些可以用到的实例进行简单处理,生成可以被C#调用的动态库。
我们新建一个C++的动态库项目,将HelloWorldExample的例子进行处理,引入需要的文件,结构如下:
其中FastDDSWrapper.h,FastDDSWrapper.cpp文件时新建的,其他的都是用的 HelloWorldExample的文件。
FastDDSWrapper.h
#pragma once
#include "pch.h"
#define FASTDDSWRAPPER_EXPORTS true
#ifdef FASTDDSWRAPPER_EXPORTS
#define FASTDDSWRAPPER_API __declspec(dllexport)
#else
#define FASTDDSWRAPPER_API __declspec(dllimport)
#endif
extern "C" {
FASTDDSWRAPPER_API int init_publisher(bool use_env);
FASTDDSWRAPPER_API int publish(const char* message);
FASTDDSWRAPPER_API int init_subscriber();
FASTDDSWRAPPER_API int subscribe(char* buffer, int bufferSize);
}
FastDDSWrapper.cpp
// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @file HelloWorldPublisher.cpp
*
*/
#include "pch.h"
#include "HelloWorldPublisher.h"
#include <fastrtps/attributes/ParticipantAttributes.h>
#include <fastrtps/attributes/PublisherAttributes.h>
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/publisher/qos/PublisherQos.hpp>
#include <fastdds/dds/publisher/DataWriter.hpp>
#include <fastdds/dds/publisher/qos/DataWriterQos.hpp>
#include <thread>
using namespace eprosima::fastdds::dds;
HelloWorldPublisher::HelloWorldPublisher()
: participant_(nullptr)
, publisher_(nullptr)
, topic_(nullptr)
, writer_(nullptr)
, type_(new HelloWorldPubSubType())
{
}
bool HelloWorldPublisher::init(
bool use_env)
{
hello_.index(0);
hello_.message("HelloWorld");
DomainParticipantQos pqos = PARTICIPANT_QOS_DEFAULT;
pqos.name("Participant_pub");
auto factory = DomainParticipantFactory::get_instance();
if (use_env)
{
factory->load_profiles();
factory->get_default_participant_qos(pqos);
}
participant_ = factory->create_participant(0, pqos);
if (participant_ == nullptr)
{
return false;
}
//REGISTER THE TYPE
type_.register_type(participant_);
//CREATE THE PUBLISHER
PublisherQos pubqos = PUBLISHER_QOS_DEFAULT;
if (use_env)
{
participant_->get_default_publisher_qos(pubqos);
}
publisher_ = participant_->create_publisher(
pubqos,
nullptr);
if (publisher_ == nullptr)
{
return false;
}
//CREATE THE TOPIC
TopicQos tqos = TOPIC_QOS_DEFAULT;
if (use_env)
{
participant_->get_default_topic_qos(tqos);
}
topic_ = participant_->create_topic(
"HelloWorldTopic",
"HelloWorld",
tqos);
if (topic_ == nullptr)
{
return false;
}
// CREATE THE WRITER
DataWriterQos wqos = DATAWRITER_QOS_DEFAULT;
if (use_env)
{
publisher_->get_default_datawriter_qos(wqos);
}
writer_ = publisher_->create_datawriter(
topic_,
wqos,
&listener_);
if (writer_ == nullptr)
{
return false;
}
return true;
}
HelloWorldPublisher::~HelloWorldPublisher()
{
if (writer_ != nullptr)
{
publisher_->delete_datawriter(writer_);
}
if (publisher_ != nullptr)
{
participant_->delete_publisher(publisher_);
}
if (topic_ != nullptr)
{
participant_->delete_topic(topic_);
}
DomainParticipantFactory::get_instance()->delete_participant(participant_);
}
void HelloWorldPublisher::PubListener::on_publication_matched(
eprosima::fastdds::dds::DataWriter*,
const eprosima::fastdds::dds::PublicationMatchedStatus& info)
{
if (info.current_count_change == 1)
{
matched_ = info.total_count;
firstConnected_ = true;
std::cout << "Publisher matched." << std::endl;
}
else if (info.current_count_change == -1)
{
matched_ = info.total_count;
std::cout << "Publisher unmatched." << std::endl;
}
else
{
std::cout << info.current_count_change
<< " is not a valid value for PublicationMatchedStatus current count change" << std::endl;
}
}
void HelloWorldPublisher::runThread(
uint32_t samples,
uint32_t sleep)
{
if (samples == 0)
{
while (!stop_)
{
if (publish(false))
{
std::cout << "Message: " << hello_.message() << " with index: " << 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: " << hello_.message() << " with index: " << hello_.index()
<< " SENT" << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(sleep));
}
}
}
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();
}
bool HelloWorldPublisher::publish(
bool waitForListener)
{
if (listener_.firstConnected_ || !waitForListener || listener_.matched_ > 0)
{
hello_.index(hello_.index() + 1);
writer_->write(&hello_);
return true;
}
return false;
}
这两个时对方法的再次封装处理。
都处理完成后,最重要的步骤需要对项目进行配置,要不然无法编译通过,如下所示:
以上需要配置的都是添加,不要删除项目原有的,不要删除项目原有的,不要删除项目原有的,添加的东西依次为:
包含目录:F:\Program Files\eProsima\fastrtps 2.10.0\include
库目录:F:\Program Files\eProsima\fastrtps 2.10.0\lib\x64Win64VS2019
链接器-输入-附加依赖项:F:\Program Files\eProsima\fastrtps 2.10.0\lib\x64Win64VS2019\fastrtps-2.10.lib;F:\Program Files\eProsima\fastrtps 2.10.0\lib\x64Win64VS2019\libfastcdr-1.0.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib
上面标红的都是根据自己安装fastdds的实例调整路径。
都配置完成后,修改
需要release ,然后编译。到此,就可以生成需要的.dll文件。
3)C#调用
建立C# 控制台程序,调用生成的.dll文件,
同时运行两个项目,就可以使用了。
内容补充:关于在windows下如何根据.idl 文件内容的结构体生成对应的fastdds核心的代码,基于上面已经通过步骤2 “eProsima_Fast-DDS-2.10.0-Windows.exe”安装的,安装完成后可以在安装路径下看到如下内容:
到此之后,就可以新建一个文件夹,里面建立一个HelloWorld.idl文件(需要生成代码的结构体),在该文件的路径下通过cmd命令进入到命令窗体,执行如下命令:
fastddsgen.bat -example CMake HelloWorld.idl -ppDisable
注意后面的参数“ -ppDisable” ,意思是
- 如果您的 idl 上没有预处理器指令,请使用
-ppDisable
如上图所示,表示已经自动生成了需要的代码文件,如下图所示:
到此时,就可以直接在继续在cmd命令窗体执行如下命令:
cmake -Bbuildexample -DFASTDDS_STATIC=ON .
注意参数:-Bbuildexample 是将生成的vs代码放在当前路径下新建的buildexample文件夹下。
生成的代码如下:
,可以以用vs2022打开,安装上面的步骤进行编译。
过程中需要的安装文件下载地址如下:
链接:https://pan.baidu.com/s/1xVN_mDjb3pIRJynx0bHluQ?pwd=ewfn
提取码:ewfn