ssRenderPlugin数据通信篇--ZMQ

         

        今天给大家带来一篇关于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解决方案!

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值