opendds编译开发(c#封装)
opendds是对DDS协议的开源实现,具体可以查阅官网。
我使用的是源码编译开发,博文只针对windows平台。
环境准备
环境安装
1.visual studio 2022,我是企业版,安装需要选择c++
2.Per下载安装,下载链接:https://strawberryperl.com/
源码
1.opendds源码
下载发布的版本。
下载:https://github.com/OpenDDS/OpenDDS
2.ACE源码
下载发布版本。看清楚说明,下对版本,我下载的ACE+TAO-8.0.2.zip
下载地址:https://github.com/DOCGroup/ACE_TAO/releases/tag/ACE%2BTAO-8_0_2
配置系统环境
以我的开发环境为例。
解压opendds源码,解压ace源码以后放在一起,也可以不放一起,弄清楚文件夹。
1.配置环境系统变量
ACE_ROOT :ace源码位置
MPC_ROOT :MPC位置,在ACE源码里面的MPC
TAO_ROOT : %ACE_ROOT%\TAO
DDS_ROOT :DDS源码位置
以为我为例分别是:
ACE_ROOT:E:\OpenDDS-3.31.0\ACE_wrappers
DDS_ROOT:E:\OpenDDS-3.31.0
TAO_ROOT:E:\OpenDDS-3.31.0\ACE_wrappers\TAO
MPC_ROOT:E:\OpenDDS-3.31.0\MPC
其中ACE_wrappers就是拷贝的ACE源码
2.Path变量
%ACE_ROOT%\lib
%ACE_ROOT%\bin
%DDS_ROOT%\lib
%DDS_ROOT%\bin
3.其它
如果你还在参考网上其它资料,可能需要添加一些其它路径,
Path变量中添加perl路径,例如:E:\strawberry-perl-5.40.0.1-64bit-portable\perl\bin
Path变量中添加编译器路径,例如:D:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.43.34808\bin\Hostx64\x64
生成opendds项目
这部分主要是编译opendds源码,生成依赖库。我推荐博文给大家参考。
Windows下的OpenDDS编译(超详细)-CSDN博客
OPenDDS程序 的 实现+运行-CSDN博客
最主要的官方文档:Building and Installing - OpenDDS 3.31.0
官方文档构建和安装一节。
编译ACE
1.配置修改
进入ACE源码里面,找到ace文件夹,在ace文件下添加文件config.h,输入内容:#include "ace/config-win32.h"。
2.编译
在ACE源码中找到ACE_wrappers_vs2022.sln,编译。
编译opendds
进入opendds源码文件夹,运行“configure.cmd”。双击即可。
正常情况下会生成DDS_no_tests.sln,编译。
所有编译的,都可以用vs2022打开.sln编译,也可以用命令行编译。
正常生成则opendds依赖库有了。
开发项目
这里主要是使用opendds库,opendds作为传输工具,在我们自己的项目中肯定是库,库就需要使用。这里以c#封装使用为例看看项目开发。
读懂官网文档是很重要的,其中“开始”一节演练了项目使用。
这里你需要简单立即idl数据结构定义,可以不管
tao_idl Messenger.idl
opendds_idl Messenger.idl
这部分是介绍idl的使用生成,就是中间件。
项目开始
准备文件
在你平时创建项目的位置,建立一个项目文件夹,名称:DllForOpendds。
在该文件夹下面再建立一个文件夹OpenddsWrappers。
在OpenddsWrappers里面建立2个文件,分别是DDSMessenger.idl,DDS.MPC。
其中DDSMessenger.idl的内容就和官网例子差不多,刚刚开始就一样也可以。
这是我的:
module DDSMessenger{@topicstruct Message {//来源string from;//主题string subject;@key long subject_id;//附属信息string text;//传输内容sequence <octet> content;//域IDlong domain_id;};
@topic
struct Heartbeat
{@key long use_id;long index;
};
};
官网例子里面分离了项目,一个订阅一个发布,这里是为了封装库我就不分开了,DDS.mpc文件就是为了用MPC工具构建项目的文件。里面的内容是:
project(*idl): dcps {// This project ensures the common components get built first.TypeSupport_Files {DDSMessenger.idl}custom_only = 1
}project(*OpenddsWrappers): dcpsexe_with_tcp {exename = OpenddsWrappersafter += *idlTypeSupport_Files {DDSMessenger.idl}Header_Files {OpenddsExport.hDataReaderListenerImpl.hOpenddsWarp.hPublisher.hSubscriber.hSubscriber.h}Source_Files {DataReaderListenerImpl.cppOpenddsExport.cpp}
}
有了这两个文件,就可以生成VC的项目了,在当前文件下,执行命令:
perl %ACE_ROOT%\MPC\mwc.pl -type vs2022,这样就生成了项目OpenddsWrappers.sln,
用vs2022打开项目,里面就是2个c++项目,当前还没有c#的项目,对DDS_OpenddsWrappers做一点修改。
1.
1.右键DDS_OpenddsWrappers,打开属性,“常规”中修改配置类型为dll
2.继续,“链接器”中输出文件修改文件后缀.dll
如图效果:
生成一下OpenddsWrappers解决方案,就会有一些DDSMessengerTypeSupportC.h等文件产生。
开发功能
1.opendds使用开发
在DDS_OpenddsWrappers项目中,添加方法,处理opendds传输,主要有:
1、创建工厂
2、创建域
3、创建主题
4、创建发布者
5、创建订阅者
6、创建写入器
7.创建监听器
8、创建读取器
我这里就不一一贴代码了,有点多还是。这里主要一个分点是,一个域可以有多个发布者,一个发布者可以有多个写入器,每个写入器都有一个主题。这里开发时可以根据自己需要,我是每一个主题绑定一个写入器或则读取器,一个域中有唯一一个发布者或者订阅者。
理论上一个域可以有多个发布者或订阅者,这里有什么影响目前我还不知道,暂时就一个发布者和订阅者吧。
开发完以上代码,就是封装导出方法了,提供给c#使用。
#pragma once
#ifdef MYLIB_EXPORTS
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif#include"publisher.h"
#include"Subscriber.h"
#include"OpenddsWarp.h"
extern "C" {MYLIB_API int Addition(int a, int b); MYLIB_API int Initopendds(int argc, char* argv[]);MYLIB_API int Createdomain(long id);MYLIB_API int PublishMsg(const char* topicname,const unsigned char* content,int lengh, int status, const char* from);MYLIB_API int PublishDomainMsg(long domainid, const char* topicname, const unsigned char* content, int lengh, int status, const char* from);MYLIB_API int SubscribeMsg(const char* topicname, void (*func)(const char* , unsigned char*,long,long, const char* ));MYLIB_API int SubscribeDomainMsg(long domainid, const char* topicname, void (*func)(const char*, unsigned char*,long ,long, const char*));
}
在项目上,加入预定义,然后编译。
2.添加c#项目DotNetForDDS,这是一个库,里面提供对c++导出的调用。
新建Wrappers类。
using System.Runtime.InteropServices;namespace DotNetForDDS
{public class Wrappers{/// <summary>/// 接收回调/// </summary>/// <param name="topic">主题</param>/// <param name="data">数据</param>/// <param name="datalen">数据长度</param>/// <param name="id">域ID</param>/// <param name="from">来源</param>[UnmanagedFunctionPointer(CallingConvention.Cdecl)]public delegate void FuncPtr(string topic, IntPtr data,long datalen,long id,string from);/// <summary>/// 测试函数/// </summary>/// <param name="a"></param>/// <param name="b"></param>/// <returns></returns>[DllImport("OpenddsWrappers.dll", CallingConvention = CallingConvention.Cdecl)]public static extern int Addition(int a, int b);/// <summary>/// 初始化,程序只调用一次/// </summary>/// <param name="argc"></param>/// <param name="agv"></param>/// <returns></returns>[DllImport("OpenddsWrappers.dll", CallingConvention = CallingConvention.Cdecl,CharSet =CharSet.Ansi)]public static extern int Initopendds(int argc, string[] agv);/// <summary>/// 创建域/// </summary>/// <param name="id">域ID</param>/// <returns></returns>[DllImport("OpenddsWrappers.dll", CallingConvention = CallingConvention.Cdecl)]public static extern int Createdomain(long id);/// <summary>/// 发布数据,在最新创建的域发布,一般程序中唯一域时/// </summary>/// <param name="topicname">主题</param>/// <param name="content">内容</param>/// <param name="len">内容长度</param>/// <param name="status">发布方式,0立即发布,1等待有订阅方初始化</param>/// <param name="from">标记发布源</param>/// <returns></returns>[DllImport("OpenddsWrappers.dll", CallingConvention = CallingConvention.Cdecl)]public static extern int PublishMsg(string topicname, byte[] content,int len, int status, string from);/// <summary>/// 指定域发布数据,没有域时会自动创建/// </summary>/// <param name="domainid">域ID</param>/// <param name="topicname">主题</param>/// <param name="content">内容</param>/// /// <param name="status">发布方式</param>/// <param name="from">标记源</param>/// <returns></returns>[DllImport("OpenddsWrappers.dll", CallingConvention = CallingConvention.Cdecl)]public static extern int PublishDomainMsg(long domainid, string topicname, byte[] content,int len, int status, string from);/// <summary>/// 订阅数据,最新创建域上订阅,一般一个域时/// </summary>/// <param name="topicname">主题</param>/// <param name="func">接收回调</param>[DllImport("OpenddsWrappers.dll", CallingConvention = CallingConvention.Cdecl)]public static extern void SubscribeMsg(string topicname, FuncPtr func);/// <summary>/// 指定域上订阅/// </summary>/// <param name="domainid">域ID</param>/// <param name="topicname">主题</param>/// <param name="func">接收回调</param>[DllImport("OpenddsWrappers.dll", CallingConvention = CallingConvention.Cdecl)]public static extern void SubscribeDomainMsg(long domainid, string topicname, FuncPtr func);}
}
这个调用库就完成了。
3.使用测试
添加c#控制台项目Test,引入DotNetForDDS性能,添加测试方法,验证订阅发布。
这里因为在同一个解决方案里面,可以通过c#直接调试到c++代码,具体可以网上搜索方法。
这里就不详细写了,有问题留言。测试效果:
当然这不完善,只是一个样例,目前是可以调用运行的,我还没有研究多线程的影响,内存释放这些,毕竟我对c++不熟悉。