|
本帖最后由 iotrouter 于 2025-8-18 17:27 编辑
需求概述本章节主要实现一个流程:EG8200采集西门子S7-200 Smart的数据,并组装成JSON格式,通过MQTT上报应用平台;同时能够接收应用平台下发的控制命令,实现远程开关。要实现此流程,需要提前准备好以下材料: 1. PLC的IP/端口/点位表PLC | S7-200 Smart | IP | 192.168.0.24/102 | 点表(DB1) | 地址 | 数据类型 | 属性 | 名称 | V0.0 | Boolean | 只读 | 1号电机启停状态 | V0.1 | Boolean | 只读 | 2号电机启停状态 | V0.2 | Boolean | 只读 | 3号电机启停状态 | V0.3 | Boolean | 只读 | 4号电机启停状态 | V0.4 | Boolean | 读写 | 1号电机启停控制 | V0.5 | Boolean | 读写 | 2号电机启停控制 | V0.6 | Boolean | 读写 | 3号电机启停控制 | V0.7 | Boolean | 读写 | 4号电机启停控制 | VD100 | Unsigned16 | 只读 | 电压(V) | VD200 | Unsigned16 | 只读 | 电流(A) | 2. MQTT通信相关参数Broker:1883.dtuip.com:1883ClientID:820000003058164FUsername:19381903226Password:ZHZK19381903226订阅Topic:820000003058164F/+发布Topic:820000003058164F 3. JSON数据数据JSON格式:{ "sensorDatas": [ { "flag":"em1Status", "switcher":1 }, { "flag":"em2Status", "switcher":1 }, { "flag":"em3Status", "switcher":1 }, { "flag":"em4Status", "switcher":1 }, { "flag":"em1Control", "switcher":0 }, { "flag":"em2Control", "switcher":0 }, { "flag":"em3Control", "switcher":0 }, { "flag":"em4Control", "switcher":0 }, { "flag":"voltage", "value":220.0 }, { "flag":"current", "value":10.2 } ]}
需求分析在制作流程时,基础的逻辑是根据数据走向来制作流程。该需求经过分析可以得出,主要分为数据上行和数据下行两个部分。其中,数据上行主要工作有: 数据下行主要工作有: 需求实现1. 采集PLC数据IP LC的IP端口:102(S7协议通信默认端口102)模式:TASP(S7-200 Smart选择TASP,其余型号选择Rack/Slot)本地TSAP:1002(固定值)远端TSAP:0301(固定值)采集周期:1000ms(默认1000ms,可自定义)超时周期:2000ms(默认2000ms,可自定义)数据点配置(根据帮助文档可以找到对应关系)V0.0-->DB1,X0.0V0.1-->DB1,X0.1V0.2-->DB1,X0.2V0.3-->DB1,X0.3V0.4-->DB1,X0.4V0.5-->DB1,X0.5V0.6-->DB1,X0.6V0.7-->DB1,X0.7VD100 -->DB1,W100VD200 -->DB1,W200输出模式可以根据需求选择,这里选择所有变量输出一条如果设置正确,调试窗口会有日志打印,显示的是读取到的数据内容
2. 数据格式化根据步骤2的引导,在调试窗口可以看到读到的PLC数据如下: 因为应用平台已经规定了数据必须按照JSON格式上报。接下来使用函数节点Javascrip代码将数据进行格式化,代码如下: let jsonArray = []jsonArray.push({ "flag": "em1Status", "switcher": msg.payload.em1Status == true ? 1 : 0 })jsonArray.push({ "flag": "em2Status", "switcher": msg.payload.em2Status == true ? 1 : 0 })jsonArray.push({ "flag": "em3Status", "switcher": msg.payload.em3Status == true ? 1 : 0 })jsonArray.push({ "flag": "em4Status", "switcher": msg.payload.em4Status == true ? 1 : 0 })jsonArray.push({ "flag": "em1Control", "switcher": msg.payload.em1Control == true ? 1 : 0 })jsonArray.push({ "flag": "em2Control", "switcher": msg.payload.em2Control == true ? 1 : 0 })jsonArray.push({ "flag": "em3Control", "switcher": msg.payload.em3Control == true ? 1 : 0 })jsonArray.push({ "flag": "em4Control", "switcher": msg.payload.em4Control == true ? 1 : 0 })jsonArray.push({ "flag": "voltage", "value": msg.payload.voltage / 100 })/*两位小数*/jsonArray.push({ "flag": "current", "value": msg.payload.current / 100 })/*两位小数*/let data = {}data.sensorDatas = jsonArraymsg.payload = JSON.stringify(data)return msg复制以上代码,粘贴到函数节点内,部署后即可看到效果: 可以看到,已经将读到的PLC数据,按照需求要求转换成了最终的JSON格式,且对数据进行了一定程度的计算(两位小数)。此处只是函数节点的冰山一角,因为支持Javascrip语言编程,几乎你能想到的任何功能都可以在这里实现 3. 通过MQTT上报数据拖入一个MQTT发布节点,根据提示进行配置,填入MQTT连接相关信息及MQTT发布主题后(详情见需求概述),即可实现数据上报。可以看到,数据上报成功后,应用平台显示绿色表明设备上线且数据正常: 至此,数据上报已经完成,很简单几步即可实现:采集PLC数据并按照自定义JSON格式上报。 4. 通过MQTT接收数据拖入一个MQTT订阅节点,注意:MQTT发布节点和MQTT订阅节点可以共用一个MQTT连接,如果使用的是同一个连接,只是不同的Topic,拖入节点后选择已经配置好的连接即可。如果选择添加新的mqtt-broker节点,则代表的是新建一个MQTT连接 选择一个调试节点,在平台点击控制按钮后,调试窗口可以看到下发的指令: 5. 数据解析根据S7写数据节点的帮助文档,可以知道,要想实现向PLC写入数据,需要传递的数据携带两个参数,如下: 因此,数据解析时,需要将应用平台下发的数据转换为携带payload和variable两个变量的消息,传递给S7写数据节点,函数节点的代码如下: let cmdDate = msg.payloadmsg.variable = cmdDate.sensorDatas[0].flagmsg.payload = cmdDate.sensorDatas[0].switcher == 1 ? true : falsereturn msg复制以上代码,粘贴到函数节点内,将调试节点设置为输出原始消息,可以看到打印内容如下: 可以看到数据已经转换为S7写数据节点要求的格式 6. 写入PLC拖入一个S7写数据节点,连接函数计算节点,在应用平台操作开关,看演示效果: 总结本章节介绍了一个相对复杂的流程,旨在表述网关所具备的能力,因此并未对程序的安全性、稳定性、灵活性等方面做优化。通过理解本流程,相信你已经对网关的可视化编程有了一定的熟悉。接下来,深入学习每个节点的功能用法吧,掌握它们将会为你制作更复杂的流程提供更多的选择和可能! 源码所有的流程支持以json格式导入导出,方便与其他人分享做好的流程。本章节的流程json文件如下,复制后在菜单栏右上角选择导入粘贴即可,导出同理 [{"id":"c8d138cd30e452fc","type":"s7 in","z":"2ff624305b8cb30b","endpoint":"374f5d9e3d2741c0","mode":"all","variable":"","diff":false,"name":"读PLC数据","x":180,"y":180,"wires":[["03f0b9014af25c1d"]]},{"id":"4abb25e7e75b3447","type":"mqtt out","z":"2ff624305b8cb30b","name":"发布数据","topic":"820000003058164F","qos":"0","retain":"false","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"8d85be060cbc6545","x":520,"y":180,"wires":[]},{"id":"63f2816a781192d5","type":"mqtt in","z":"2ff624305b8cb30b","name":"订阅数据","topic":"820000003058164F/+","qos":"0","datatype":"auto-detect","broker":"8d85be060cbc6545","nl":false,"rap":true,"rh":0,"inputs":0,"x":180,"y":260,"wires":[["1b6776e208355720"]]},{"id":"03f0b9014af25c1d","type":"function","z":"2ff624305b8cb30b","name":"数据格式化","func":"let jsonArray = []\n\njsonArray.push({ \"flag\": \"em1Status\", \"switcher\": msg.payload.em1Status == true ? 1 : 0 })\njsonArray.push({ \"flag\": \"em2Status\", \"switcher\": msg.payload.em2Status == true ? 1 : 0 })\njsonArray.push({ \"flag\": \"em3Status\", \"switcher\": msg.payload.em3Status == true ? 1 : 0 })\njsonArray.push({ \"flag\": \"em4Status\", \"switcher\": msg.payload.em4Status == true ? 1 : 0 })\njsonArray.push({ \"flag\": \"em1Control\", \"switcher\": msg.payload.em1Control == true ? 1 : 0 })\njsonArray.push({ \"flag\": \"em2Control\", \"switcher\": msg.payload.em2Control == true ? 1 : 0 })\njsonArray.push({ \"flag\": \"em3Control\", \"switcher\": msg.payload.em3Control == true ? 1 : 0 })\njsonArray.push({ \"flag\": \"em4Control\", \"switcher\": msg.payload.em4Control == true ? 1 : 0 })\njsonArray.push({ \"flag\": \"voltage\", \"value\": msg.payload.voltage / 100 })/*两位小数*/\njsonArray.push({ \"flag\": \"current\", \"value\": msg.payload.current / 100 })/*两位小数*/\n\nlet data = {}\ndata.sensorDatas = jsonArray\n\nmsg.payload = JSON.stringify(data)\n\nreturn msg\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":350,"y":180,"wires":[["4abb25e7e75b3447"]]},{"id":"1b6776e208355720","type":"function","z":"2ff624305b8cb30b","name":"函数计算 1","func":"let cmdDate = msg.payload\n\nmsg.variable = cmdDate.sensorDatas[0].flag\nmsg.payload = cmdDate.sensorDatas[0].switcher == 1 ? true : false\n\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":350,"y":260,"wires":[["140b525dd4d0608b"]]},{"id":"140b525dd4d0608b","type":"s7 out","z":"2ff624305b8cb30b","endpoint":"374f5d9e3d2741c0","variable":"","name":"","x":510,"y":260,"wires":[]},{"id":"374f5d9e3d2741c0","type":"s7 endpoint","transport":"iso-on-tcp","address":"192.168.0.24","port":"102","rack":"0","slot":"2","localtsaphi":"10","localtsaplo":"02","remotetsaphi":"03","remotetsaplo":"01","connmode":"tsap","adapter":"","busaddr":"2","cycletime":"2000","timeout":"2000","name":" LC","vartable":[{"addr":"DB1,X0.0","name":"em1Status"},{"addr":"DB1,X0.1","name":"em2Status"},{"addr":"DB1,X0.2","name":"em3Status"},{"addr":"DB1,X0.3","name":"em4Status"},{"addr":"DB1,X0.4","name":"em1Control"},{"addr":"DB1,X0.5","name":"em2Control"},{"addr":"DB1,X0.6","name":"em3Control"},{"addr":"DB1,X0.7","name":"em4Control"},{"addr":"DB1,W100","name":"voltage"},{"addr":"DB1,DW200","name":"current"}]},{"id":"8d85be060cbc6545","type":"mqtt-broker","name":"","broker":"1883.dtuip.com:1883","port":"1883","clientid":"820000003058164F","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""}]注意MQTT配置节点导出时,不会携带用户名和密码。因此导入时需要重新录入用户名和密码
|
|