基于PLC-Recorder数据转发功能的WebSocket客户端设计(高级语言及HTML,通讯内容为Json格式)

本文主要是介绍基于PLC-Recorder数据转发功能的WebSocket客户端设计(高级语言及HTML,通讯内容为Json格式),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近PLC-Recoder推出了V1.6.0版本,其最大的变化是增加了数据转发功能,所有能采集到的数据,都可以通过WebSocket和Json转发出去,为不熟悉PLC底层技术,且需要数据采集的朋友们提供了一个统一的接口。下面将介绍几种客户端创建方法。

1、服务器端设置

1)配置好通道(需要采集数据的设备)和所有需要采集的变量,开启录波测试。如果没有实际的设备,也可以启动录波仿真功能,模拟录波,为所有的变量提供数据。
参考文章:
《欧姆龙、松下、基恩士PLC进行连续数据采集、时序和故障追踪的方法》、
《西门子PLC进行连续数据采集、时序和故障追踪的方法》、
《三菱PLC进行连续数据采集、时序和故障追踪的方法》
2)配置服务器参数,启动服务器:
通过菜单“转发”->“配置…”,打开配置窗口设置端口号和服务器识别码,点击“应用退出”。然后通过“启动服务器”和“停止服务器”来切换服务器的状态。启动后,软件标题中将出现“[转发中]”的字符。

2、用C#标准库实现客户端

界面设计:在这里插入图片描述
设置有连接、关闭、变量查询、订阅等按钮,下面介绍重要实现:
1)主要引用:WebSocket4Net、Newtonsoft.Json及其依赖项;
2)连接命令

private void btConnect_Click(object sender, EventArgs e){string address ="ws://"+ tbIP.Text + ":" + tbPort.Text;try{wsClient = new WebSocket(address);wsClient.MessageReceived += WebSocket_OnWebSocketMessageReceived;wsClient.Opened += WebSocket_OnClientConnected;wsClient.Closed += WsClient_Closed;wsClient.Open();btClose.Enabled = true;btConnect.Enabled = false;//避免多次创建连接}catch (Exception ex){MessageBox.Show("Start Failed : " + ex.Message);}}

关闭程序:

 private void btClose_Click(object sender, EventArgs e){wsClient.Close();btClose.Enabled = false;btConnect.Enabled = true;}

变量查询程序:

private void btGetInfo_Click(object sender, EventArgs e){if (!connected) { return; }QUERY qe = new QUERY();qe.ID = tbID.Text; ;string payload = JsonConvert.SerializeObject(qe);wsClient.Send(payload);tbMessage.AppendText("[DEALED] " + DateTime.Now.ToString() + " " + "发送查询信息:" + payload + Environment.NewLine);}

订阅变量程序

private void btBook_Click(object sender, EventArgs e){if (!connected) { return; }BOOK book = new BOOK();if (!double.TryParse(tbCycle.Text, out book.CYCLE)){MessageBox.Show("更新周期需要是数字!");return;}book.ID = tbID.Text;foreach (chanel c in listChanels){foreach (tag t in c.tags){if (t.selected){tagInfoForBook tag = new tagInfoForBook();tag.TNAME = t.name;tag.CID = t.chanelid;book.listTIB.Add(tag);}}}book.COUNT = book.listTIB.Count;string payload = JsonConvert.SerializeObject(book);wsClient.Send(payload);tbMessage.AppendText("[DEALED] " + DateTime.Now.ToString() + " " + "发送订阅信息:" + payload + Environment.NewLine);}

连接建立和电文处理事件:

private void WebSocket_OnWebSocketMessageReceived(object sender,MessageReceivedEventArgs  message){try{Invoke(new Action(() =>{string msg = message.Message;/// <summary>/// 客户端信息的Json对象/// </summary>JObject payloadGetJobjectNow;if (infoUpdateEnable) { tbMessage.AppendText("[RAW] "+DateTime.Now.ToString() + " " + msg + Environment.NewLine); }payloadGetJobjectNow =(JObject) JsonConvert.DeserializeObject(msg);FS = getFSFromPayload(payloadGetJobjectNow);switch (FS){case 10://验证结果if (payloadGetJobjectNow["RESULT"].ToString() == "1"){tbMessage.AppendText("[DEALED] " + DateTime.Now.ToString() + " " + "验证成功!" + Environment.NewLine);}else{tbMessage.AppendText("[DEALED] " + DateTime.Now.ToString() + " " + "验证失败! " + payloadGetJobjectNow["REASON"].ToString()+ Environment.NewLine);}break;case 20://读取设备信息if (payloadGetJobjectNow.ContainsKey("listChanel") && payloadGetJobjectNow.ContainsKey("listTagInfo")){JArray jaChanel = (JArray)payloadGetJobjectNow["listChanel"];JArray jaTag = (JArray)payloadGetJobjectNow["listTagInfo"];JObject jobect;int CIDn = 0;chanel c;if (jaChanel.Count > 0){List<chanel> listChanelTemp = new List<chanel>();for(int i = 0; i < jaChanel.Count; i++){c = new chanel();listChanelTemp.Add(c);}for (int i = 0; i < jaChanel.Count; i++){jobect =(JObject) jaChanel[i];CIDn = 0;if (jobect.ContainsKey("CID")){if( int.TryParse(jobect["CID"].ToString(),out CIDn)){chanel cTemp = listChanelTemp[CIDn];cTemp.TNAME = jobect["TNAME"].ToString();cTemp.BIGTYPE = jobect["BIGTYPE"].ToString();cTemp.DEVICETYPE = jobect["DEVICETYPE"].ToString();double.TryParse(jobect["CYCLE"].ToString(), out cTemp.CYCLE);}}}for (int i = 0; i < jaTag.Count; i++){CIDn = 0;jobect = (JObject)jaTag[i];if (jobect.ContainsKey("CID")){if (int.TryParse(jobect["CID"].ToString(), out CIDn)){c = listChanelTemp[CIDn];tag t = new tag();c.tags.Add(t);t.chanelid = CIDn;t.name = jobect["TNAME"].ToString();t.type= jobect["TYPE"].ToString();t.comment= jobect["COMMENT"].ToString();}}}listChanels = listChanelTemp;dgvUpdate();valueUpdateEnable = false;}}break;case 30://全新订阅case 31://增量订阅if (payloadGetJobjectNow["RESULT"].ToString() == "1"){tbMessage.AppendText("[DEALED] " + DateTime.Now.ToString() + " " + "订阅成功!" + Environment.NewLine);valueUpdateEnable = true;}else if (payloadGetJobjectNow["RESULT"].ToString() == "3"){tbMessage.AppendText("[DEALED] " + DateTime.Now.ToString() + " " + "服务器请求重新订阅!" + Environment.NewLine);btBook_Click(null, null);}else{tbMessage.AppendText("[DEALED] " + DateTime.Now.ToString() + " " + "订阅失败! " + payloadGetJobjectNow["REASON"].ToString() + Environment.NewLine);}break;case 40://单次更新,更新所有的值case 41://仅更新变化的变量if (valueUpdateEnable){JArray ja = (JArray)payloadGetJobjectNow["listTV"];int CID = 0;String tagName = "";tag tagTemp = null;foreach (JObject jt in ja){if (int.TryParse(jt["CID"].ToString(), out CID)){tagName = jt["TNAME"].ToString();foreach (tag t in listChanels[CID].tags){if (t.name == tagName){tagTemp = t;if (double.TryParse(jt["VALUE"].ToString(), out tagTemp.value)){ }break;}}}}dgvValueUpdate();}break;default:tbMessage.AppendText("[DEALED] " + DateTime.Now.ToString() + " " + msg + Environment.NewLine);break;}}));}catch { }}/// <summary>/// 建立连接后,马上依据现有变量配置进行订阅,并开始数值的更新。/// </summary>private void WebSocket_OnClientConnected(object  sender,EventArgs e){try{Invoke(new Action(() =>{tbMessage.AppendText("[DEALED] " + DateTime.Now.ToString() + " " + "连接成功!" + Environment.NewLine);connected = true;btBook_Click(null, null);}));}catch { }}

完整源码及协议格式下载链接。

3、用HSL库的WebSocket组件构建客户端

利用商业组件库HSL(HslCommunication),可以更加方便地客户端构建(本服务器就是用HSL库来实现),引用该组件后,就不需要再引用WebSocket4Net。主要代码与第2章相同,但是HSL的客户端有一个优势:在服务器异常、网络中断后,都会自动尝试重连,用户不需要考虑重连的问题。
比如,服务器重启后,客户端会自动恢复连接,然后服务器发出需要重新订阅变量的通知,客户端只需要进行响应,重发订阅信息(见下图),就可以完成所有的工作,继续进行数据刷新了。
在这里插入图片描述

4、HTML客户端源代码

HTML天生支持WebSocket和Json,因此,可以方便地实现客户端:
在这里插入图片描述

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>SocketClientDemoWeb(www.hiddenmap.cn提供)</title><script>
var _hmt = _hmt || [];var wsIsOpened=false;var ws;function Book(){//订阅变量if (wsIsOpened){var JsonBook={"FC":30,"COUNT":4,"CYCLE":300,"ID":"abc123","listTIB":[{"CID":0,"TNAME":"tag0"},{"CID":0,"TNAME":"tag1"},{"CID":1,"TNAME":"tag0"},{"CID":1,"TNAME":"tag1"}]};//var JsonBookString=JSON.stringify(JsonBook);ws.send(JsonBookString);}}
</script></head><body style="margin: 0px; font-style: normal; font-family: '微软雅黑';" ><script>window.οnlοad=function bodyloaded(){ if ("WebSocket" in window){ws= new WebSocket("ws://127.0.0.1:1883");//服务器地址         ws.onopen = function(){wsIsOpened=true;                }ws.onmessage = function (evt){var received_msg = evt.data;var count1=0;var count2=0;if(received_msg.length>0){document.getElementById("message_in").innerHTML=received_msg;//完整显示收到的信息内容。var jObject=JSON.parse(received_msg);//解析为JSON对象。var FS=jObject["FS"];//查询信息携带的功能码。if(FS==40 || FS==41){var tagCount=jObject["COUNT"];var listTV=jObject["listTV"];//变量数组for(i=0;i<listTV.length;i++){var chanelID=listTV[i]["CID"];var tagName=listTV[i]["TNAME"];var tagType=listTV[i]["TYPE"];var tagValue=listTV[i]["VALUE"];if(tagType=="Bool"){if(tagValue==1){tagValue="true";}else{tagValue="false";}}//根据数据类型进行数据的呈现。//根据自己的变量进行解析,放在不同的位置进行呈现。if(chanelID==0){if(tagName=="tag0"){document.getElementById("C0tag0").innerHTML=tagValue;}else{document.getElementById("C0tag1").innerHTML=tagValue;}}else{if(tagName=="tag0"){document.getElementById("C1tag0").innerHTML=tagValue;}else{document.getElementById("C1tag1").innerHTML=tagValue;}}}}}}          }      }</script><div align="left"><span style="font-weight: bold; font-size: 24px; font-family: '微软雅黑';">&nbsp;操作&nbsp;&nbsp;<button class="btn-style" οnclick="Book()">订阅</button></span><br>&nbsp;&nbsp;来自于服务器的信息:<br>&nbsp;&nbsp;<span style="font-family: '微软雅黑'; font-size: small"><label id="message_in" >-</label></span>
</div><hr>
<div align="left" font-family= "微软雅黑"> <span style="font-weight: bold; font-size: 24px; font-family: '微软雅黑';">&nbsp;变量信息</span><ol><div align="center"><table width="700px" border="1" cellspacing="0"><tbody><tr><th width="10%" scope="row">&nbsp;序号</th><th width="30%">&nbsp;通道</th><th width="30%">&nbsp;变量名</th><th >&nbsp;值</th></tr><tr> <th scope="row">&nbsp;1</th><td>&nbsp;0</td><td>&nbsp;tag0</td> <td>&nbsp;<label id="C0tag0" >??</label></td> 		</tr><tr> <th scope="row">&nbsp;2</th><td>&nbsp;0</td><td>&nbsp;tag1</td> <td>&nbsp;<label id="C0tag1" >??</label></td> 		</tr><tr> <th scope="row">&nbsp;3</th><td>&nbsp;1</td><td>&nbsp;tag0</td> <td>&nbsp;<label id="C1tag0" >??</label></td> 		</tr><tr> <th scope="row">&nbsp;4</th><td>&nbsp;1</td><td>&nbsp;tag1</td> <td>&nbsp;<label id="C1tag1" >??</label></td> 		</tr></tbody></table> </div></ol>
</div>
</body>
</html>

5、小结

PLC-Recorder的转发功能也是应很多网友的要求开发,配合已经实现的后台功能(关闭界面,缩小为右下角图标),希望能为大家的后续开发工作提供便利。

					2020年7月8日

录波软件、客户端完整源码及协议格式下载链接。

这篇关于基于PLC-Recorder数据转发功能的WebSocket客户端设计(高级语言及HTML,通讯内容为Json格式)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/797876

相关文章

SQL中如何添加数据(常见方法及示例)

《SQL中如何添加数据(常见方法及示例)》SQL全称为StructuredQueryLanguage,是一种用于管理关系数据库的标准编程语言,下面给大家介绍SQL中如何添加数据,感兴趣的朋友一起看看吧... 目录在mysql中,有多种方法可以添加数据。以下是一些常见的方法及其示例。1. 使用INSERT I

Qt使用QSqlDatabase连接MySQL实现增删改查功能

《Qt使用QSqlDatabase连接MySQL实现增删改查功能》这篇文章主要为大家详细介绍了Qt如何使用QSqlDatabase连接MySQL实现增删改查功能,文中的示例代码讲解详细,感兴趣的小伙伴... 目录一、创建数据表二、连接mysql数据库三、封装成一个完整的轻量级 ORM 风格类3.1 表结构

Python使用vllm处理多模态数据的预处理技巧

《Python使用vllm处理多模态数据的预处理技巧》本文深入探讨了在Python环境下使用vLLM处理多模态数据的预处理技巧,我们将从基础概念出发,详细讲解文本、图像、音频等多模态数据的预处理方法,... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

Python中你不知道的gzip高级用法分享

《Python中你不知道的gzip高级用法分享》在当今大数据时代,数据存储和传输成本已成为每个开发者必须考虑的问题,Python内置的gzip模块提供了一种简单高效的解决方案,下面小编就来和大家详细讲... 目录前言:为什么数据压缩如此重要1. gzip 模块基础介绍2. 基本压缩与解压缩操作2.1 压缩文

MySQL 删除数据详解(最新整理)

《MySQL删除数据详解(最新整理)》:本文主要介绍MySQL删除数据的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、前言二、mysql 中的三种删除方式1.DELETE语句✅ 基本语法: 示例:2.TRUNCATE语句✅ 基本语

MySQL查询JSON数组字段包含特定字符串的方法

《MySQL查询JSON数组字段包含特定字符串的方法》在MySQL数据库中,当某个字段存储的是JSON数组,需要查询数组中包含特定字符串的记录时传统的LIKE语句无法直接使用,下面小编就为大家介绍两种... 目录问题背景解决方案对比1. 精确匹配方案(推荐)2. 模糊匹配方案参数化查询示例使用场景建议性能优

mysql表操作与查询功能详解

《mysql表操作与查询功能详解》本文系统讲解MySQL表操作与查询,涵盖创建、修改、复制表语法,基本查询结构及WHERE、GROUPBY等子句,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随... 目录01.表的操作1.1表操作概览1.2创建表1.3修改表1.4复制表02.基本查询操作2.1 SE

Go语言中nil判断的注意事项(最新推荐)

《Go语言中nil判断的注意事项(最新推荐)》本文给大家介绍Go语言中nil判断的注意事项,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.接口变量的特殊行为2.nil的合法类型3.nil值的实用行为4.自定义类型与nil5.反射判断nil6.函数返回的