基于electron-node-red的PLC调试工具
在使用三方PLC或特殊应用时,受软件包及授权的局限,做界面软件的门槛很高。使用Node-Red可以简化这一编程过程,而Electron可以辅助打包和跨平台。
1 起因:
我们的一个客户使用KEBA的系统,不支持Modbus,有受限制的OPC UA。这在BECKHOFF系统上也常见,倍福一般是需要加授权费。原始的CoDeSys支持NetVar,但一般仅在PLC与PLC之间通讯,如果没有Visualization就不太好做用户界面。有些PLC如西门子有特有的通讯方式,一般用C#编写界面。而我们推出的CommissioningTool可以实现全平台兼容,只需要PLC支持TCP通讯即可。如果使用西门子PLC,也可以参考第6步 从源构建,Node-Red有针对西门子等PLC的模块。
C#和C++等应用确实在速度上有优势,但它们开发环境不好架构,并且界面美观程度稍差一些。Python也不错,但Python依赖包不好解决,TK难看而且控件少,QT环境也不好架构。
这个工具主要面向对象为自动化应用监控及设置,可用内置的OPCUA直接对产线监控,也可以对标准化机器进行PLC内置变量的设置和调试。
在我们的CODEPI产品上内置了Node-Red,可以直接使用。本文主要针对一般Windows环境下的GUI调试工具。
2 架构:
在原始的electron-node-red上,我们修改了部分选项卡和自动隐藏菜单栏的代码,并添加了实例化验证(仅允许一个实例运行)。我们还添加了一些适用于实际应用的Node-Red包,比如OPC UA、Modbus、UI等实用节点,并统一打包。
我们还内置了一个DEMO程序帮助用户理解Node-Red程序的架构。一般情况下,我们使用DINT数组和String字节作为数据的载体,在PLC上搭建TCP服务器,使用TCP作为基础传输协议。
3 相关知识:
3.1 DINT
首先我们要知道数据的本质是什么。在PLC中,我们常用INT(16位整数)、DINT(32位整数)、REAL(16位浮点型)、BOOL(布尔型)等数据类型。具体的数据类型可以在网上查到,我们针对DINT数据分析一下:
DINT一共有32位,即32个bit。在CoDeSys类软件中(西门子类软件操作位比较麻烦),我们如下命名一个变量并给初始值2:
diData : DINT := 2;
我们可以用diData.0来读取diData的第0位数据,在没有写入的情况下,diData的第0位是false,第1位是true。
以此类推,diData可以作为32个bit,可以存储32个BOOL数据。
有时我们需要REAL类型的,一般界面对精度要求都不会很高,将REAL数据乘以100发送,接收端再除以100即可。
3.2 NetworkBaseServices(NBS)
NBS是一个CAA库,主要提供TCP与UDP的服务器和客户端。在倍福系统上被称为SocketConnect(实际上是TCP,倍福丧心病狂地把它独立出来收费)。以下是一段基于NBS的单客户端TCP服务器:
//Server
Server(xEnable:=NOT ton_LostConnect.Q , ipAddr:=ip , uiPort:=port );
//Connect
Connection(xEnable:=Server.xBusy AND NOT ton_LostConnect.Q , hServer:=Server.hServer );
3.3 Node-Red与Electron
Node-Red是基于NodeJS的一个图形化编程软件,它可以提供先进的架构和漂亮的界面。
Electron负责对Node-Red打包,Electron是基于Chromium的JS解释器。
实际上在Node-Red中写的程序都会转成JS代码执行,而JS代码是解释型语言而非编译型,这给我们编程及调试带来很大的便利。在打包后的CommissioningTool上,无需其它任何工具即可开始编程和调试。
4 开始使用
下载CommissioningTool并解压,双击打开Node-RED Electron.exe即可。您会看到如下界面:
您在实际运行时看着会有点不一样,因为没有连接到PLC,所以一些数字显示和趋势是无法使用的。不过您可以试试点击按钮和输入框查看动态效果。
如果要开始编程,在界面任何位置按下键盘上的ALT键,界面左上方会出现View菜单,点View-Editor即可进入编程界面。
5 程序结构
在自带的DEMO中已经提供了PLC通讯程序,这里不多说。我们主要看一下整体架构:
5.1 接收数据
打开软件后,会自动作为TCP客户端开始监听Localhost:54957。收到的数据为TCP流或Buffer,先用Split对其拆包,长度为4(一个BYTE是8×4 =32=DINT),之后用Switch对数据进行拆包,即可得到PLC发过来的ARRAY [0..50] OF DINT;。
再进一步,我们对DINT做处理。当前的payload(数据流,下同)只是4个BYTE,如果要把它转成数字,则需要用NodeJS的一些功能:msg.payload = msg.payload.readInt32LE(0);。具体的处理可以参考Node.js Buffer
而对于bit位的处理,我本人也是js苦手,查阅了一些资料后确定方案:
var b0 = msg.payload[ 0 ] >>> 0 & 0x01;
var b1 = msg.payload[ 0 ] >>> 1 & 0x01;
... ...
刚刚说过,一个payload是4个byte,一个byte是8个bit。所以我们对payload进行移位比较,判断它的每一位写入到bx中,再输出payload即为0或1的数字。
5.2 发送数据
一般的PLC都会带保持变量功能(PersistentVars),所以对于设置性变量只需要写入一次就可以保持,而对操作性变量则需要实时性的保证。我们使用两种方式发送变量:
对于设置性变量,我们创建了一个全局变量表(Global Context Data),并把它写入到Settings.json文件中,每次打开软件会自动读取一次并写入到设置表中。
我们把要发送的设置内容转为字符串,以[957,开头做为特征,在PLC中对字符串进行拆分,每个变量以逗号分隔。每次按下发送到PLC按钮时触发发送数据。
除了发送设置外,每次触发界面上的调试按钮时也要发送一些数据到PLC,这时用的另一种方法,将数据打包,以str,开头作为特征,后面跟序号和内容,再在PLC中做拆分。这种方法可以保证按钮的响应时间小于20ms。
5.3 PLC程序
PLC程序在DEMO中已经提供,比较简单,只需要创建两个服务器,其中一个对客户端周期发送数据,另一个接收到数据后按规则拆包即可。
6 从源构建
我比较讨厌西门子系列的PLC,博图软件比较难用。Twincat3差强人意,2就放弃算了。在Node-Red上有S7通讯和倍福ADS通讯的Node,但如果不是从源构建,则后安装的Node无法随着CommissioningTool移动而跟随,换句话说就是在多台电脑上部署比较困难。
如果您使用西门子或倍福等不易使用TCP的PLC,或者您需要修改默认窗口尺寸等设置,就可以考虑从源构建应用。
首先,您需要安装NodeJS,并需要安装yarn等常用包。按照原始的electron-node-red说明进行架构和编译。
yarn start可以让您在不编译exe的情况下运行,这时可以在node-red中安装您需要的软件包,安装完成后使用yarn dist -w生成exe文件,生成的文件在dist文件夹中。
比较棘手的问题是bcrypt生成需要msvc编译环境,自动安装很可能失败,需要手动安装或修改代码。有时electron也会失败,需要切换其它软件源或手动解压electron。js和python类似,您需要在修复前人的bug上花大量时间。
将以下代码放在main.js可以确保只有单个实例:
//Only allow one instance
let isSingleInstance = app.requestSingleInstanceLock()
if (!isSingleInstance) {
app.quit()
}
此外还需要修改createWindow中的autoHideMenuBar改为TRUE,并屏蔽掉对osx系统的autohidewindow判断。
7 截图
8 总结
在支持Visualization的设备上,比如我们的CodePi,可以直接用浏览器访问webvisualization。
倍福的TargetVisu授权应该在一千多RMB,加上motion和TCP授权够买个小工控机了。一般的组态屏操作和修改比较麻烦,而且屏幕太小、没有动态效果。
一般应用可以让客户自备电脑,这个只作为PLC的监控,从源编译屏蔽掉程序部分。如果系统有windows工控机则可以直接装上去用作界面显示。
非常复杂的工业应用仍然推荐C#或C++,性能和自由度高一些。本文的调试工具在DEMO环境下,I5 6400占用约4-9%的CPU资源和约300M内存资源。
No Comments