需求概述
本章要实现一个流程:eg8200采集西门子s7-200smart的数据,并组装成json格式通过http上报应用平台。
源-(s7转modbus-https://www.iotrouter.com/news/2150.html)
要采集的plc点位表如下:
plc | s7-200 smart |
ip | 192.168.0.34/102 |
点表(db1) |
地址 | 数据类型 | 属性 | 名称 |
v0.0 | boolean | 只读 | 风机1接触器异常 |
v0.6 | boolean | 只读 | 风机2接触器异常 |
v1.6 | boolean | 只读 | 风机3接触器异常 |
v2.5 | boolean | 只读 | 变频器通讯故障 |
i0.0 | boolean | 只读 | 电机高温报警 |
i0.1 | boolean | 只读 | 温度状态异常 |
vd4 | float | 只读 | 油压工程值 |
vd8 | float | 只读 | 吸入压力工程值 |
vd12 | float | 只读 | 燃油油位工程值 |
vd16 | float | 只读 | 机柜温度值 |
vd20 | float | 只读 | 发动机电压值 |
vd24 | float | 只读 | 发动机电流值 |
vd28 | float | 只读 | 发动机工作时间 |
vd32 | float | 只读 | 泵压工程值 |
vw120 | unsigned16 | 只读 | 本机编号 |
vw750 | unsigned16 | 只读 | 本机组号 |
vw2402 | unsigned16 | 只读 | 电机1温度 |
vw2404 | unsigned16 | 只读 | 电机2温度 |
vw2406 | unsigned16 | 只读 | 电机3温度 |
vw2408 | unsigned16 | 只读 | 风机1温度 |
vw2410 | unsigned16 | 只读 | 风机2温度 |
vw2412 | unsigned16 | 只读 | 风机3温度 |
http通信相关参数格式如下:
##数据推送url:192.168.0.32:1880/api/device/reportdatamethod:postjson格式:{ "deviceinfo": { "machinenumber": 9301, "machinegroupnumber": 1 }, "status": { "fan1contacterror": true, "fan2contacterror": false, "fan3contacterror": false, "invertercommerror": false, "motorhightempalarm": false, "temperaturestatuserror": false }, "data": { "oilpressure": 17.83, "suctionpressure": 3.7, "fuellevel": 0.23, "cabinettemperature": 237.2, "enginevoltage": 415.64, "enginecurrent": 65.1, "engineruntime": 72.6, "pumppressure": 85.3, "motor1temperature": 20, "motor2temperature": 19, "motor3temperature": 19, "fan1temperature": 21, "fan2temperature": 33, "fan3temperature": 26 }, "timestamp": "2022-7-08 11:28:55"}
需求分析
在制作流程时,基础的逻辑是根据数据走向来制作流程。其中主要工作分为三步:
步:通过s7协议读取plc变量数据,得到的数据存储在内存中(西门子节点)
第二步:将数据按照json格式进行格式化(函数节点)
第三步:配置http 请求参数
需求实现
1. 采集plc数据
从节点库拖入一个西门子节点,以及一个调试节点,调试节点用于查看读取到的plc数据集,方便定位问
双击西门子节点,根据需求概述的内容填写对应的设置参数,如下图所示:
ip:plc的ip端口:102(s7协议通信默认端口102)采集周期:1000ms(默认2000ms,可自定义)超时周期:2000ms(默认2000ms,可自定义)数据点配置(根据帮助文档可以找到对应关系)v0.0v0.6v1.6v2.5i0.0i0.1vd4vd8vd12vd16vd20vd24vd28vd32vw120vw750vw2402vw2404vw2408vw2410vw2412如果是连续地址,可配置起始地址并填写读取长度即可
如果设置正确,调试窗口会有日志打印,显示的是读取到的数据内容:
有的时候plc数据点比较多,手动依次录入比较繁琐。节点支持数据点的导入导出或者参数传递的方式来读取:
本例程用到的传参方案,函数节点内的代码如下:
msg.payload = [ { func: "readboolarray", body: { name: "", address: "v0", length: 8 } }, { func: "readboolarray", body: { name: "", address: "v1", length: 8 } }, { func: "readboolarray", body: { name: "", address: "v2", length: 8 } }, { func: "readfloatarray", body: { name: "", address: "vd4", length: 8 } }, { func: "readuint16", body: { name: "", address: "vw120" } }, { func: "readuint16", body: { name: "", address: "vw750" } }, { func: "readuint16array", body: { name: "", address: "vw2402", length: 6 } }, { func: "readboolarray", body: { name: "", address: "i0", length: 8 } }]return msg;
2. 数据格式化
根据步骤引导,在调试窗口可以看到读到的plc数据如下:
因为应用平台已经规定了数据必须按照json格式上报。接下来使用函数节点javascrip代码将数据进行格式化,代码如下:
class devicereport { constructor(machinenumber, machinegroupnumber) { this.deviceinfo = { machinenumber: machinenumber, machinegroupnumber: machinegroupnumber }; this.status = { fan1contacterror: 0, fan2contacterror: 0, fan3contacterror: 0, invertercommerror: 0, motorhightempalarm: 0, temperaturestatuserror: 0 }; this.data = { oilpressure: 0, suctionpressure: 0, fuellevel: 0, cabinettemperature: 0, enginevoltage: 0, enginecurrent: 0, engineruntime: 0, pumppressure: 0, motor1temperature: 0, motor2temperature: 0, motor3temperature: 0, fan1temperature: 0, fan2temperature: 0, fan3temperature: 0 }; this.timestamp = null; } https:// 设置状态数据(1表示异常,0表示正常) setstatusdata(statusdata) { this.status = statusdata; } https:// 设置传感器数据 setsensordata(sensordata) { this.data = sensordata; } https:// 设置时间戳 settimestamp(timestamp) { this.timestamp = timestamp; } generatereport() { return { deviceinfo: this.deviceinfo, status: this.status, data: this.data, timestamp: this.timestamp }; }}function dateformat(fmt, timestamp) { let ret; const opt = { "y+": timestamp.getfullyear().tostring(), https:// 年 "m+": (timestamp.getmonth() + 1).tostring(), https:// 月 "d+": timestamp.getdate().tostring(), https:// 日 "h+": timestamp.gethours().tostring(), https:// 时 "m+": timestamp.getminutes().tostring(), https:// 分 "s+": timestamp.getseconds().tostring() https:// 秒 }; for (let k in opt) { ret = new regexp("(" + k + ")").exec(fmt); if (ret) { fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padstart(ret[1].length, "0"))) }; }; return fmt;}function setvalue(data) { var mydevicereport = new devicereport(data.vw120, data.vw750) var v0 = data.v0 var v1 = data.v1 var v2 = data.v2 var i0 = data.i0 var vd4 = data.vd4 var vd2402 = data.vw2402 const fmt = dateformat("yyyy-mm-dd hh:mm:ss", new date()) try { https:// 设置状态数据 const statusdata = { fan1contacterror: v0[0], fan2contacterror: v0[6], fan3contacterror: v1[6], invertercommerror: v2[5], motorhightempalarm: i0[0], temperaturestatuserror: i0[1] } mydevicereport.setstatusdata(statusdata) https:// 设置传感器数据 const sensordata = { oilpressure: vd4[0], suctionpressure: vd4[1], fuellevel: vd4[2], cabinettemperature: vd4[3], enginevoltage: vd4[4], enginecurrent: vd4[5], engineruntime: vd4[6], pumppressure: vd4[7], motor1temperature: vd2402[0], motor2temperature: vd2402[1], motor3temperature: vd2402[2], fan1temperature: vd2402[3], fan2temperature: vd2402[4], fan3temperature: vd2402[5] } mydevicereport.setsensordata(sensordata) https:// 设置时间戳 mydevicereport.settimestamp(fmt) https://生成json对象返回 return mydevicereport.generatereport() } catch (err) { node.error(err.message); return null }}var plcdata = msg.payloadif (setvalue(plcdata)){ msg.payload = json.stringify(setvalue(plcdata),null,2) return msg}
复制以上代码,粘贴到函数节点内,部署后即可看到效果:
可以看到,已经将读到的plc数据,按照需求要求转换成了终的json格式,且对数据进行了一定程度的计算(两位小数)。此处只是函数节点的冰山一角,因为支持javascrip语言编程,几乎你能想到的任何功能都可以在这里实现。
3. 通过http周期上报
拖入一个http节点,根据提示进行配置地址和请求方式,即可实现数据上报:
服务端返回成功,至此,数据上报已经完成,很简单几步即可实现:采集plc数据并按照自定义json格式上报。
源码
所有的流程支持以json格式导入导出,方便与其他人分享做好的流程。本章节的流程json文件如下,复制后在菜单栏右上角选择导入粘贴即可,导出同理
[{"id":"edf0c4c538469caa","type":"siemens","z":"5bb1b94e3403e2f2","name":"","protocol":"iplink_siemenss7","ipaddress":"192.168.0.34","destport":"102","iplinktimeout":"2000","ipcommtimeout":"2000","spname":"com10","baudrate":"9600","databits":"8","stopbit":"1","paritybit":"0","rtsenable":false,"splinktimeout":"2000","spintertimeout":"-1","workmode":"read","readmode":"trigger","readcyc":"1000","vartable":[],"siemenss7model":"5","siemenss7connectiontype":"1","siemenss7rack":"0","siemenss7slot":"0","siemenss7localtsap":"102","siemenss7desttsap":"4d57","siemensppistation":"2","stringvar":[],"customname":"siemens:s7-s200smart","x":510,"y":1080,"wires":[["be890e0383a04306"]]},{"id":"90be80aa485c48ae","type":"debug","z":"5bb1b94e3403e2f2","name":"调试 22","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusval":"","statustype":"auto","x":1020,"y":1080,"wires":[]},{"id":"2485d94e74e61614","type":"function","z":"5bb1b94e3403e2f2","name":"函数计算 18","func":"msg.payload = [n { func: "readboolarray", body: { name: "", address: "v0", length: 8 } },n { func: "readboolarray", body: { name: "", address: "v1", length: 8 } },n { func: "readboolarray", body: { name: "", address: "v2", length: 8 } },n { func: "readfloatarray", body: { name: "", address: "vd4", length: 8 } },n { func: "readuint16", body: { name: "", address: "vw120" } },n { func: "readuint16", body: { name: "", address: "vw750" } },n { func: "readuint16array", body: { name: "", address: "vw2402", length: 6 } },n { func: "readboolarray", body: { name: "", address: "i0", length: 8 } }n]nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":310,"y":1080,"wires":[["edf0c4c538469caa"]]},{"id":"8db3c718ecac3ed7","type":"inject","z":"5bb1b94e3403e2f2","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"oncedelay":0.1,"topic":"","payload":"","payloadtype":"date","x":150,"y":1080,"wires":[["2485d94e74e61614"]]},{"id":"be890e0383a04306","type":"function","z":"5bb1b94e3403e2f2","name":"函数计算 19","func":"class devicereport {n constructor(machinenumber, machinegroupnumber) {n this.deviceinfo = {n machinenumber: machinenumber,n machinegroupnumber: machinegroupnumbern };n this.status = {n fan1contacterror: 0,n fan2contacterror: 0,n fan3contacterror: 0,n invertercommerror: 0,n motorhightempalarm: 0,n temperaturestatuserror: 0n };n this.data = {n oilpressure: 0,n suctionpressure: 0,n fuellevel: 0,n cabinettemperature: 0,n enginevoltage: 0,n enginecurrent: 0,n engineruntime: 0,n pumppressure: 0,n motor1temperature: 0,n motor2temperature: 0,n motor3temperature: 0,n fan1temperature: 0,n fan2temperature: 0,n fan3temperature: 0n };n this.timestamp = null;n }nn https:// 设置状态数据(1表示异常,0表示正常)n setstatusdata(statusdata) {n this.status = statusdata;n }nn https:// 设置传感器数据n setsensordata(sensordata) {n this.data = sensordata;n }nn https:// 设置时间戳n settimestamp(timestamp) {n this.timestamp = timestamp;n }n generatereport() {n return {n deviceinfo: this.deviceinfo,n status: this.status,n data: this.data,n timestamp: this.timestampn };n }n}nnfunction dateformat(fmt, timestamp) {n let ret;n const opt = {n "y+": timestamp.getfullyear().tostring(), https:// 年n "m+": (timestamp.getmonth() + 1).tostring(), https:// 月n "d+": timestamp.getdate().tostring(), https:// 日n "h+": timestamp.gethours().tostring(), https:// 时n "m+": timestamp.getminutes().tostring(), https:// 分n "s+": timestamp.getseconds().tostring() https:// 秒n };n for (let k in opt) {n ret = new regexp("(" + k + ")").exec(fmt);n if (ret) {n fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padstart(ret[1].length, "0")))n };n };n return fmt;n}nfunction setvalue(data) {n var mydevicereport = new devicereport(data.vw120, data.vw750)n var v0 = data.v0n var v1 = data.v1n var v2 = data.v2n var i0 = data.i0n var vd4 = data.vd4n var vd2402 = data.vw2402n const fmt = dateformat("yyyy-mm-dd hh:mm:ss", new date())n try {n https:// 设置状态数据n const statusdata = {n fan1contacterror: v0[0],n fan2contacterror: v0[6],n fan3contacterror: v1[6],n invertercommerror: v2[5],n motorhightempalarm: i0[0],n temperaturestatuserror: i0[1]n }n mydevicereport.setstatusdata(statusdata)nn https:// 设置传感器数据n const sensordata = {n oilpressure: vd4[0],n suctionpressure: vd4[1],n fuellevel: vd4[2],n cabinettemperature: vd4[3],n enginevoltage: vd4[4],n enginecurrent: vd4[5],n engineruntime: vd4[6],n pumppressure: vd4[7],n motor1temperature: vd2402[0],n motor2temperature: vd2402[1],n motor3temperature: vd2402[2],n fan1temperature: vd2402[3],n fan2temperature: vd2402[4],n fan3temperature: vd2402[5]n }n mydevicereport.setsensordata(sensordata)n https:// 设置时间戳n mydevicereport.settimestamp(fmt)n https://生成json对象返回n return mydevicereport.generatereport()n } catch (err) {n node.error(err.message);n return nulln }nn}nvar plcdata = msg.payloadnif (setvalue(plcdata)) {n msg.payload = json.stringify(setvalue(plcdata), null, 2)n return msgn}n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":710,"y":1080,"wires":[["d140dc012c0cd68f"]]},{"id":"d140dc012c0cd68f","type":"http request","z":"5bb1b94e3403e2f2","name":"","method":"post","ret":"txt","paytoqs":"ignore","url":"192.168.0.32:1880/api/device/reportdata","tls":"","persist":false,"proxy":"","insecurehttpparser":false,"authtype":"","senderr":false,"headers":[],"x":860,"y":1080,"wires":[["90be80aa485c48ae"]]}]
西门子PLC代理,全新原装正品,SIEMENS,湖南总代理,中国授权代理商