今天给大家带来一篇关于ssRender_Plugin关于如何使用zmq与ssRender_editor端实现数据互动。Don't say so much!开搞:
用到的技术栈主要一下几点:
编译zmq动态dll库,创建winform上位机作为zmq的server端,c#使用c++dll动态库。
首先我们需要先下载zmq的源码:https://github.com/zeromq/libzmq
下载zip包
1、我们要安装Cmake环境,打开Cmake,配置我们刚才下载完的zmq源码路径,创建一个编译路径“build”:
然后点击Configure,选择Visual Studio 14 2015,根据自己的Visual Studio的版本进行适配。
然后点击Generate,再点击Open Object。
右键点击libzmq,设置为启动项目。
然后点击解决方案 >> 重新生成解决方案
打开我们创建的编译目录下,找到bin->Debug,libzmq-v140-mt-gd-4_3_5.dll。
2.创建winform程序,作为数据提供端,例ZmqServerApp:
ZMQ通信是基于socket通信协议的基础上,包装出来的轻量进程通信方式,参数需要指定双方通信的端口号。例子中暂定为:40001。 具体的C#工程代码详见:SourceCodeForTest/ZmqServerApp at master · Xiaobd1995/SourceCodeForTest · GitHub
我们把第一步编译完的动态库libzmq-v140-mt-gd-4_3_5.dll,拷贝到winform项目的debug目录下,以便于代码能够引用到动态库:
这里封装了一下c#加载dll库的代码,主要实现的是加载dll和声明引用函数:
public unsafe class libzmq
{
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr zmq_ctx_new();
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr zmq_socket(IntPtr context, int type);
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int zmq_send(IntPtr socket, char[] str, int length);
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int zmq_recv(IntPtr socket, char[] str, int length);
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int zmq_close(IntPtr socket);
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int zmq_bind(IntPtr socket, string addr);
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int zmq_connect(IntPtr socket, string addr);
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int zmq_msg_send(zmq_msg_t* msg, IntPtr socket, int flag);
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int zmq_msg_recv(zmq_msg_t* msg, IntPtr socket, int flag);
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int zmq_msg_init(zmq_msg_t* msg);
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int zmq_msg_init_size(zmq_msg_t* msg, int size);
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int zmq_setsockopt(void* socket, int option, void* optval, int optvallen);
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void* zmq_msg_data(zmq_msg_t* msg);
[DllImport("libzmq-v140-mt-gd-4_3_5.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int zmq_msg_close(zmq_msg_t* msg);
}
//部分功能代码
IntPtr context_req = ZmqMng.libzmq.zmq_ctx_new();
//发布订阅模式
socket_req = ZmqMng.libzmq.zmq_socket(context_req, (int)ZmqMng.Socket_Types.ZMQ_PUB);
//请求-应答模式
//socket_req = ZmqMng.libzmq.zmq_socket(context_req, (int)ZmqMng.Socket_Types.ZMQ_REP);
//建立端口绑定,默认40001
int result = ZmqMng.libzmq.zmq_bind(socket_req, "tcp://*:" + default_port_str);
//发送数据
int result = ZmqMng.libzmq.zmq_send(sockePtr, ptr, ptr.Length);
4.创建plugin工程,编译dll目标文件
我们需要创建一个c++工程,用于创建plugin工程:
我们在init_plugin的接口中创建一个线程,用于创建zmq连接和接受信息。
重点是我们需要创建一个自定义属性Mess_str用于将我们接到的Zmq消息与ssRenderEditor端进行互动。
//UserPlugin.cpp
#ifndef _PLUGIN_TYPE_RENDER_
//#define _PLUGIN_TYPE_RENDER_
#endif
//线程处理函数
void* recServerData(void* arg);
//ssRender_plugin标准接口
void DataSource::init_plugin(RenderEngineBase* renderEngine, func_writelog write_log)
{
PluginBase::init_plugin(renderEngine, write_log);
m_runningFlg = true;
#ifdef _PLUGIN_TYPE_RENDER_
#else
pthread_create(&m_thread, NULL, recServerData, (void *)this);
#endif
}
//属性数据更新接口
int DataSource::updateData(string mess_str)
{
ProptyList::iterator it = DataSource::m_listPropties.begin();
for (; it != DataSource::m_listPropties.end(); it++)
{
Property prop((*it));
Common::printlog(LOG_LEV_INFORMATION, "updateData prop.name:[%s], prop.value:[%s]\n", prop.name, prop.value);
m_renderEngine->getPropertySystem()->setPropertyValue(m_ssrObject, prop.name, mess_str);
}
return 0;
}
//创建Zmq连接和接收信息接口
void* recServerData(void* arg)
{
Common::printlog(LOG_LEV_INFORMATION, "Connecting to hello world server...\n");
void *context = zmq_ctx_new(); // 一个线程
// 连接至服务端的套接字
int ret = zmq_ctx_set(context, ZMQ_MAX_SOCKETS, 1);
void *subscriber = zmq_socket(context, ZMQ_SUB); // 内部创建了2个线程,总共变成了3个线程
//void *requester = zmq_socket(context, ZMQ_REQ); // 内部创建了2个线程,总共变成了3个线程
zmq_connect(subscriber, "tcp://localhost:40001"); // 连接并绑定 绑定对应的io线程
ret = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "", 0);
bool conflate = true; //设置只保留最新入栈的数据
zmq_setsockopt(subscriber, ZMQ_CONFLATE, &conflate, 1);
int max_count = 200; //设置最大缓冲数量
zmq_setsockopt(subscriber, ZMQ_RCVHWM, &max_count, 3);
char buffer[1025] = { 0 };
char bufferBack[1024] = { 0 };
char* ptr = NULL;
DataSource* dataSource = (DataSource*)arg;
zmq_msg_t msg;
zmq_msg_init(&msg);
string mess_str = "";
int size = 0;
while (1)
{
size = zmq_msg_recv(&msg, subscriber, 0); // 收到响应才能再发
memcpy(buffer, zmq_msg_data(&msg), size);
Common::printlog(LOG_LEV_ERROR, "Recieve new str[%s]\n", buffer);
if (strcmp(bufferBack, buffer) && bufferBack != NULL)
{
dataSource->updateData(buffer);
memcpy(bufferBack, buffer, sizeof(buffer));
memset(buffer, 0, sizeof(buffer));
}
Common::sleep(100);
}
zmq_msg_close(&msg);
zmq_close(subscriber);
zmq_ctx_destroy(context);
return 0;
}
//UserPlugin.h
SSR_PLUGIN_PROPERTY_DEF_BEGIN(DataSource)
// grou_name name type default_value min max tooltip
SSR_PLUGIN_PLUGIN_PROPERTY("DataSource", "Mess_str", TYPE_STRING, "None message!", 0, 100, "tips data1")
SSR_PLUGIN_PROPERTY_DEF_END()
我们需要把zmq的静态链接库引用加到工程里,静态库我们可以在ssRender发布的环境编译链中的lib文件夹中找到,具体如下:
右键点击,解决方案,生成出来我们想要的dll文件,
5.创建ssRenderEditor工程
前面我们已经把数据发送端用winform工程创建出来,接下来我们用ssRenderEdior创建一个工程,将数据显示出来:
首先需要把刚才编译完成的dll文件加载到工程中,然后把plugin拖拽到根节点Page下,需要注意的是,因为在ssRenderEditor接在Plugin为动态库,所以需要把zmq的动态链接库放到跟UserPlugi的工程对应的目录下,这样才能正确的加载Plugin,嵌入式平台同理:
接着点击Text控件,将其text属性与UserPlugin节点的自定义属性Mess_str绑定关联:
接下来就是见证奇迹的时刻,我们运行之前创建的winform上位机,然后发送数据!
好了,以上就是我们本篇要介绍的Zmq通信在ssRender中的应用 ,感谢观看,如果对您有帮助,点个赞呗hiahiahia!!
--ssRender力争做国人自己的HMI解决方案!