本小节演示项目是如何从连接器到获取Tank4C9服务上的设备对象的值,并通过Connector服务的url返回给UI端请求的。另外,实际项目中考虑websocket中间可能因为网络通信等原因出现中断情况,我们增加一个“心跳”设计来定时发送连接请求,确保出现冲断的情况下,能够重新连接到服务端。
1. OPCUA-Client读取OPCUA服务Tag值
现在重构Connector APP views文件里的pushCollectorData函数,通过引入OPCUA-Client来读取OPCUA服务端的Tag值,代码如下:
from django.shortcuts import render from django.http import HttpResponse import json from dwebsocket.decorators import accept_websocket from opcua import Client #引入 opcua-client @accept_websocket def pushCollectorData(request): tank4C9={ 'DeviceId': 1, 'DeviceName':'1#反应罐', 'Status': 0, #设备运行状态 'OverheadFlow':0 ,#'顶流量', 'ButtomsFlow': 0, #'低流量' 'Power': 0, #功率 } Collector={ 'CollectorId': 1, 'CollectorName':'1#采集器', 'Status': 0, 'DeviceList':[tank4C9], } Collector={ 'CollectorId': 1, 'CollectorName':'1#采集器', 'Status': 0, 'DeviceList':[tank4C9], } if request.is_websocket(): #创建opcua-client client = Client("opc.tcp://localhost:4840/freeopcua/server/") client.connect() #连接服务器 try: while True: root = client.get_root_node() #获取OPCUA服务根节点 #获取Tank4C9子节点的所有变量 items = root.get_child(["0:Objects", "2:Tank4C9"]).get_children() for item in items: #给字典赋读取opcua服务tag当前值 tank4C9[item.get_display_name().Text]=item.get_value() request.websocket.send(\ json.dumps( {"rows":[Collector],'total':1})) time.sleep(2) finally: client.disconnect()
代码改动并不大,数据获取从原来的OPC DA切换到了OPC UA组件,从前面的章节起笔者就奉行迭代推进的思路,好的代码结构(组织)会让后面的升级重构非常的方便,实际项目中减少很多工作量。
2. 运行调试
首先,我们在集成开发环境VS Community 2019 选择CollectorSvr项目,右键在python环境启动运行CollectorSvr项目,如下图:
然后,F5调试默认服务端项目Tank4C9Svr。我们即可通过浏览器访问监控页面
url:http://127.0.0.1:8090/tank4C9/ 运行效果如下图:
3. 客户端心跳机制
本章节我们在UI端增加一个与服务端心跳检测函数,当websocket网络通信故障或其它原因不能正常传输数据时,UI端能够重新创建连接来确保系统可靠性的手段。UI端保存一个最后更新时间的变量,心跳检测时查看当前时间-最后更新时间大于30秒时,就关闭当前连接重新创建一个新得websocket连接获取数据,这样就确保了websocket出现其它原因断开后,保证能够自动重新连接并刷新数据得机制。
更多得看代码吧如下
<script> //JQuery 代码入口 $(document).ready(function () { getData() //设置心跳检测时间 setInterval("heartbeat(window.ws,window.lastUpdateTime)",30000); }); function heartbeat(ws,lastUpdateTime) { var time = new Date(); if ((time.getTime() - lastUpdateTime > 30000)) { //最后更新时间与当前检查时间大于30秒,关闭连接重新创建并获取数据 if(ws) ws.close();//关闭连接 getData(); } } function getData() { if ("WebSocket" in window) { //连接server--TagCurValue window.ws = new WebSocket("ws://127.0.0.1:8090/pushCollectorData/"); window.lastUpdateTime = new Date().getTime() ws.onmessage = function (evt) { //最后更新时间戳 lastUpdateTime = new Date().getTime() // 接收数据 d = JSON.parse(evt.data); collector = d.rows[0] tank4C9 = collector.DeviceList[0] $("#OverheadFlow").html(tank4C9.OverheadFlow); $("#ButtomsFlow").html(tank4C9.ButtomsFlow); $("#Power").html(tank4C9.Power); }; } } </script>
4. 小结
本章节代码的改动不大,一个改动是切换到了OPC UA,另一个是增加了心跳检测函数,这个两个看似不大的改动,尤其心跳函数在实际项目中作用非常大,工业监控画面,如果出现长时间的数据不刷新(仅仅是插拔了一下电脑网线)画面不能自动连接到服务端刷新数据,现场管理员就看不到数据的变化,画面就不能真实的反应设备运行情况,这个在实际项目中是不能被客户接受的。OPC UA 的升级让体系可以逐步演进到新的架构下,却不会导致客户端任何调整,这个就是好的分层架构的优势,早些年笔者经历的很多项目都是从后台改到前台,并测试发布才能更新功能。
5.后记
python开发工业监控系列文章到本章节就告以段落了,这个系列带着大家从技术原型的探索到项目实战,从一个简单监控页面开始一步一步的也演示了项目迭代的实际过程,笔者的项目最佳实践准则:敏捷开发,有一个基本的功能蓝图后,从简设计、开发、迭代、重构、单元测试、集成测试和发布等环环相扣。同时,采用一个好的可繁可简的技术体系对持续改进非常重要,笔者转换python体系以来,越发喜欢上开源社区,互联网开发架构也会给传统企业软件开发领域带来新的机遇,尤其国家大力推进的工业互联网时代,期待python开发生态会带给我们更多的惊喜。
这里的案例UI比较简单,实际的项目可能还会设计APP UI,或者更发杂的web 3D界面等,但是总体来说服务端的架构思路是不变的,开发设计的分层(逻辑分层)是好的技术架构的重要体现。本序列文章我们更多的着重在监控界面上,没有交代业务逻辑和模型层面的知识,下一个系列笔者计划更多的着重在业务逻辑和模型层。