不改WinCC和PLC,用Node-RED边缘网关加报警的完整方案
2026/9/19 6:40:49 网站建设 项目流程

1. 为什么说“不改系统加报警”这件事能成立

做工业现场的都知道,WinCC和PLC这套组合一旦稳定跑起来,就是“祖宗级”待遇——轻易没人敢动。改PLC程序要停机、要重新调试,还得担心逻辑改动后影响原有工艺;改WinCC画面和脚本同样麻烦,版本授权、编译下载、上位机重启,每一步都可能惹出新问题。但现场需求往往不会等你准备好:设备报警要推送到手机、第三方系统要拿数据、现场要加一路声音报警、某个关键参数超限得有人马上知道。

这个“又不想动老系统、又必须加报警”的矛盾,我见到太多了。方案其实就一句话:在PLC和WinCC之间,或者WinCC的上位链路里,旁路一个Node-RED边缘计算网关,通过OPC UA把数据读出来,报警判断和通知分发全部放在网关侧完成。它不写PLC,不改WinCC,甚至WinCC重启、画面操作都跟它没关系。

项目标题里那句“不改WinCC和PLC加报警功能”并不是噱头。Node-RED作为一个开源的低代码流式编程工具,对工控人来说门槛不算高。它跑在一台小主机上,可以是工控机、瘦客户机、树莓派甚至虚拟机,通过OPC UA协议和西门子S7系列PLC通讯(也支持Modbus、三菱、欧姆龙等各类协议),把采集到的数据在本地完成滤波、判断、锁存,然后通过MQTT、HTTP、邮件、企业微信机器人等方式把报警送出去。

这套方案的核心思路是:让“报警”这件事从控制系统里解耦出来。原来的PLC和WinCC继续干它们的老本行——控制逻辑、工艺画面、历史归档;报警检测变成一种“旁挂”能力,由边缘网关独立承载。谁也不会干扰谁,谁也不用停机配合谁。

我最早接触这个思路是接手一个改造项目:现场是S7-300配WinCC 7.4,要加三个模拟量高位报警和一个设备状态监控,但客户的生产计划排得很满,停机窗口只有到一个多月以后才有。当时我就用一台旧工控机跑了Node-RED,从PLC侧把数据读出来做报警推送,一周不到就上线试运行,完全没碰过原系统。这个经历让我确信,这套思路在很多存量项目里都值得复制。

2. 先搞明白:数据从哪来,往哪去

2.1 数据采集链路的确定

要在不动PLC程序的前提下拿到数据,最常见也最稳的路径就是通过OPC UA服务器走西门子S7协议或者通过S7-300/400的以太网口直接读取。实际情况里又有两种接法:

方案接法优点缺点
ANode-RED通过S7协议直接读PLC不依赖额外软件,接法最简节点库较杂,需注意通讯负载;对S7-1200/1500需要设“允许来自远程对象的PUT/GET通讯访问”,可能被安全策略卡住
BNode-RED通过OPC UA读WinCC或独立OPC UA服务器数据点管理集中,WinCC自带OPC UA服务(从V7.2起支持)依赖WinCC服务的稳定性和授权;WinCC侧OPC UA服务需要单独配置,部分版本有连接数限制

我在大多数项目里推荐方案B——利用WinCC自带的OPC UA服务器功能。原因很简单:WinCC本来就要连接PLC,数据点、变量名都已经配置好了,直接在WinCC的OPC UA配置里把需要暴露的变量放出来,Node-RED这边只需要按变量节点ID去订阅就行。这样连PLC的变量表都不用翻。

但如果你用的是S7-1200/1500这类自带OPC UA服务器的PLC,那就更简单了。PLC本身就是OPC UA Server,Node-RED直接去连PLC的IP地址加端口4840,不需要WinCC参与。这种方式我一般推荐给“现场没有WinCC、只有触摸屏”的场景——同样能实现“不动HMI、不改PLC”的报警旁挂。

2.2 报警消息最终送到哪

采集侧定了,发送侧也得先想清楚。报警不止是“屏幕上弹一条消息”,不同场景对送达渠道的要求完全不一样:

  • 操作工在车间现场:需要现场声光报警器动作,这类可以走Node-RED输出Modbus TCP或直接驱动一个继电器模块;
  • 维护人员在办公室:桌面弹窗、浏览器看板最方便,走Node-RED的HTTP接口推送页面或者WebSocket实时刷新;
  • 管理层不在现场:需要微信、钉钉、邮件推送,Node-RED自带丰富的通知节点,或者通过HTTP方式调用企业微信机器人、钉钉自定义机器人;
  • 系统间集成:把报警写入另一个系统的数据库、通过MQTT转发给上层平台,Node-RED的MQTT节点可以轻松接入EMQX、VerneMQ等消息中间件。

这个“先定出口,再定入口”的顺序很重要。很多人一上来就急着连PLC读数据,结果数据读上来了,发给谁、用什么发还没想好,流写到一半就卡住了。我建议第一步先梳理一张表:报警事件名称、触发条件、恢复条件、通知对象、通知方式、是否记录历史。这张表就是你后面写流的需求说明书。

3. Node-RED边缘网关的落地硬件与运行环境

3.1 硬件的选择逻辑

边缘计算网关听起来高大上,落到硬件上其实就是一个“能联网、能跑Linux、能稳定长时间运行”的小主机。我列一下几个层次的选项,大家按项目预算和对可靠性的要求来选:

硬件方案参考配置适用场景注意点
工控机/瘦客户机4核CPU, 8GB内存, 2个千兆网口, SSD生产现场正式部署选无风扇机型,注意DC供电稳定性
树莓派4B/54核ARM, 4~8GB内存, 千兆网口测试验证、非关键场景SD卡容易损坏,建议用SSD启动
软路由盒子/x86小主机N100/J4125级别, 8GB内存性价比高的现场方案要选支持长时间运行的工业级电源
Docker部署在现有工控机上复用现场服务器资源不适合单独加硬件的场合注意资源隔离,避免影响WinCC所在服务器性能

我做现场项目时,功耗、散热、供电稳定这三个因素往往比绝对性能更重要。Node-RED单体运行所需资源并不高,一般设备跑几十个流,CPU占用率也就百分之几到十几。真正吃资源的是历史数据存储和看板渲染,这一块我通常会把数据转发给独立的时序数据库,主节点只保留队列缓存,避免因为自身故障丢数据。

3.2 运行环境的安装与基础加固

系统层面,我用Debian或者Ubuntu Server的占比最多,也有用Windows宿主机跑Docker的——如果现场IT团队只会管Windows,用Windows也未尝不可。但你要是问我的个人倾向,纯Linux + Docker Compose是最省心的组合:升级、备份、迁移都方便。

安装Node-RED在Linux下只需要一行命令:

# 安装Node-RED(官方脚本,主要用于本地/边缘节点) npm install -g --unsafe-perm node-red

如果用Docker部署,我更推荐这样跑:

# 创建持久化目录 mkdir -p /opt/node-red/data # 启动容器,映射1880端口用于编辑器,映射1881端口用于HTTP API接收 docker run -d \ --name node-red \ --restart=always \ -p 1880:1880 \ -p 1881:1881 \ -v /opt/node-red/data:/data \ -e TZ=Asia/Shanghai \ nodered/node-red:latest

装完第一件事不是急着连PLC,而是先把两件事做了:

  1. 打开设置文件 settings.js,修改 adminAuth 配置,为编辑器设置用户名密码。Node-RED默认不设密码,只要网络可达,任何人都能打开1880端口改你的流,这在工厂内网里是高危漏洞。
  2. 设置httpNodeRoot和路径安全。如果HTTP接口要对外提供数据,建议放在单独的端口、子路径下,并在前面加一层反向代理做访问控制。

我在现场见过不少工程师在调试机上把Node-RED跑起来以后,就长期不设密码放在那里,后面被不知情的人改乱了流,排查起来非常痛苦。这个坑一定要提前避开。

4. OPC UA接入的几种节点选型与关键配置

4.1 节点库选择

Node-RED连OPC UA,常用的是node-red-contrib-opcua这个节点包。它在工业场景里算是社区活跃度最高、维护比较频繁的了。安装方式:

# 在Node-RED安装目录下执行,或者通过编辑器右上角菜单的“管理面板”搜索安装 npm install node-red-contrib-opcua

除了OPC UA,如果你走S7协议直连西门子PLC,也有一个很关键的节点node-red-contrib-s7,是老牌的S7通讯节点,支持S7-200/300/400/1200/1500,但S7-1200/1500需要PLC侧打开“允许来自远程对象的PUT/GET通讯访问”。这个设置对很多安全要求高的项目不开放,所以我在大多数场景下还是优先OPC UA。

4.2 OPC UA Endpoint 的填写细节

说一个我见过最多人填错的地方。在node-red-contrib-opcua的Server节点里,Endpoint URL大多数人直接填:

opc.tcp://192.168.0.10:4840

但实际连接时经常报BadCertificateUntrusted或者BadSecurityModeRejected。原因一般是安全策略不匹配。西门子PLC侧的OPC UA服务器默认的安全策略是Basic256Sha256签名加密,而Node-RED端默认会用None来尝试连接。解决办法是在节点的 Security Policy 下拉框里选Basic256Sha256,并设置对应的Security Mode;如果是连接WinCC的OPC UA服务器,还要注意WinCC侧配置的用户名密码认证(如果开了)也要一并填进去。

配置项建议取值说明
Endpoint URLopc.tcp://IP:4840端口以实际服务器为准
Security PolicyBasic256Sha256兼容S7-1500默认策略
Security ModeSign and EncryptWinCC/R uw rz大多支持
Username / Password按服务器设置如开启用户认证则必须填
连接超时10~15秒不要设太短,PLC重启后会恢复重连

4.3 变量节点ID的获取方式

这一步是新手最容易卡住的地方:OPC UA的节点ID不是简单填个“DB1.0”就能读,它的格式是类似ns=2;s=DeviceSet.Device1.Temperature或者ns=3;s=“DB1”.TagName这种命名空间加标识符的组合。

我的建议是:先用Node-RED OPC UA节点里的Browse功能去浏览一下服务器地址空间。具体操作是在opcua-client节点的配置界面里,先配置好Server连接,然后点开Browser页签,就能看到服务器上可访问的节点树。从树里找到你要读的量,右键复制NodeId,或者直接用Browse测通以后再填写到订阅列表里。这个方法比翻手册猜ID要快得多。

如果把PLC侧配置了符号名,西门子S7-1500的OPC UA节点结构里往往能直接看到带设备名的变量,非常直观。如果用WinCC,注意WinCC的OPC UA服务里暴露的变量路径跟WinCC内部变量管理器的结构有关,最好在WinCC侧先建一个专门的“报警数据导出”变量组,把需要报警判断的量集中挂进去,后续维护会省很多事。

5. 一步步搭出“报警判断+去抖+锁存+通知”完整流

5.1 流的整体骨架

这是一个我实际项目里用过的报警流结构,思路非常通用。Node-RED里用OpC UA节点订阅过来的数据,会经过下面这几个环节:

OPC UA数据流 ↓ 功能函数处理(状态解析、报警条件判断) ↓ 去抖与锁存逻辑 ↓ 报警事件分发(新报警、恢复、持续) ↓ 通知输出(MQTT / HTTP / 邮件 / 微信)

5.2 报警条件判断的Function节点

新建一个Function节点,把每个变量的报警上下限、报警级别做一张配置表,类似下面这样:

变量单位报警低限报警高限恢复低限通知级别
空压机温度8575
冷却水压力MPa0.150.25
料位%209025/85

注意“报警低限/高限”和“恢复低限/恢复高限”并不相同,这就是上面提到过的报警回差(死区)。如果报警值和恢复值设成同一个数,现场信号在临界点来回抖动时,系统就会反复触发“报警-恢复-报警”,不仅烦人,还会造成通知轰炸。所以好的报警逻辑一定要带回差。

Function节点里写的核心逻辑大致是这样:

// 假设msg.payload为当前读取到的数据,包含多个属性值 const temp = msg.payload.CompressorTemp; let alarmState = false; let alarmDesc = ""; // 高温报警:85度触发,75度恢复,这里用lastTemp做状态记忆 if (context.get("tempAlarm") === true) { // 处于报警中,检查是否恢复 if (temp <= 75) { context.set("tempAlarm", false); alarmState = false; alarmDesc = "空压机温度已恢复"; } else { alarmState = true; alarmDesc = "空压机温度高报警持续中"; } } else { // 正常状态,检查是否触发 if (temp >= 85) { context.set("tempAlarm", true); alarmState = true; alarmDesc = "空压机温度高报警触发"; } } // 组合成报警事件 if (alarmState || alarmDesc.indexOf("已恢复") >= 0) { msg.payload = { event: alarmState ? "alarm" : "recover", tag: "CompressorTemp", value: temp, desc: alarmDesc, time: new Date().toISOString() }; return msg; } else { return null; // 没有事件产生,丢弃 }

这个结构就是报警状态机的最小实现:用context记住每个报警变量当前是否处于报警态,输入一个新值后,先判是“报警中”还是“正常态”,再决定是触发新报警、维持报警还是发出恢复事件。这样比每次拿到数据都粗暴比较大小要可靠得多。

5.3 去抖与防抖的必要性

工业现场的模拟量信号,尤其是温度、压力、液位这些,天然带有噪声。哪怕用OPC UA接到了数据,也经常出现某个扫描周期瞬时值越限、下个周期又恢复正常的情况。如果不做滤波,一天能推几百条假报警出去,没人受得了。

我习惯在OPC UA订阅和报警判断之间加一个滑动窗口均值滤波,或者在报警判断之后加一个持续时间确认。比如:

  • 温度连续3个采样周期(每周期1秒)都超过85℃才确认报警;
  • 恢复也一样,连续3个周期都低于75℃才发“恢复”事件。

用Function节点实现“连续确认”很简单,记录一个类似以下结构的计数器:

// 初始为0,越限+1,正常则清零 let cnt = context.get("cnt") || 0; if (msg.payload.CompressorTemp >= 85) { cnt = cnt + 1; if (cnt >= 3) { cnt = 0; // 确认报警后,交给状态机处理 msg.payload = { event: "preAlarm", tag: "CompressorTemp" }; return msg; } } return null;

这个去抖环节看着不起眼,但对报警质量的影响是决定性的。我在很多项目里把报警推送从“一天几十条无效推送”降到“一天几条有效推送”,靠的就是这两个小改进。

5.4 将报警事件发送到通知渠道

状态判断和去抖做完以后,后面就是纯粹的输出部分了。我用过的组合大致有这几种:

  • MQTT转发:Node-RED的mqtt out节点配置一个本地或云端的Broker地址,上层系统订阅Topic,是一种非常适合系统间异步解耦的方式。Topic建议按层级规划,如factory/site1/line2/alarm
  • 企业微信/钉钉机器人:用HTTP Request节点POST到机器人Webhook地址,注意企业微信有每分钟20条的频率限制,如果报警量大要做合并发送(把多条报警拼成一条消息发)。
  • 邮件发送:配置SMTP服务后,用email节点发出,这个适合不需要实时提醒的日报汇总。
  • HTTP API:给第三方系统提供一个POST接口,Node-RED作为HTTP Server,收到报警事件转推给别的平台。

提醒一句:通知渠道的失败要兜底。邮件发失败、机器人Webhook失效、网络抖动导致MQTT断开,这些情况在工业现场一定会遇到。我通常在消息输出前加一个delay节点的重试机制,或者在通知下游加一个失败分支,把失败的消息重新放进一个队列,后续错峰再发,避免报警静默丢失。

6. 历史数据的就地留存与轻量看板

6.1 为什么边缘节点要自带历史

很多人在设计报警系统时只关注“当前的报警推出去”,忽略了“历史报警要可追溯”这个硬需求。尤其是现场出现设备事故后,业主第一个问题一定是:刚才报警了吗?报警信息在哪?当时的工艺参数是多少?

如果把所有历史都只放在WinCC或第三方平台里,那么“旁路报警”方案就失去了意义——既然还是要靠WinCC查历史,那不如直接在WinCC里写报警脚本。所以边缘网关一定要自带一套轻量级的数据留存方案。

常用做法是Node-RED把报警事件和周期采样数据写入SQLite或者InfluxDB。这两个都够轻,跑在边缘主机上毫无压力:

数据库类型适合的场景Node-RED节点
SQLite关系型报警事件表、操作日志表node-red-node-sqlite
InfluxDB时序型模拟量趋势、长时间历史曲线@influxdata/influxdb-client

我一般把报警事件存SQLite——每条记录有事件时间、Tag名、报警值、描述、是否恢复、恢复时间,这个结构用关系型查起来最顺手;把周期采样的模拟量存InfluxDB——测点、时间、数值的模型天然匹配时序库,查询趋势曲线也简单。

6.2 一张免开发的报警查询看板

数据有了,给人看还得有界面。很多团队一想到看板就以为要开发Web前端,其实Node-RED的HTTP API配合一小段HTML就够用了。最简单的做法是:

  1. Node-RED里建一个HTTP In节点,监听GET /alarm/list
  2. 在Function节点里执行SQLite查询,把最近N条报警查出来;
  3. template节点拼一个HTML表格页面返回。

这段页面代码不需要复杂框架,纯原生HTML+简单CSS就能实现一个支持按时间筛选、按报警级别筛选的列表页面。如果还想看趋势,ECharts的折线图组件也可以直接在前端页面里引入,后端用Node-RED提供JSON数据接口。

我做过一个项目,现场维护人员就是用这样一个页面,配合手机浏览器直接打开网关地址,随时能看上一天的报警记录和参数曲线,连专门的App都省了。对于很多中小型产线,这就是成本最低的“报警管理系统”。

7. 部署上线时的几个边界问题与排查经验

7.1 网关IP规划与防火墙规则

边缘网关要接入现场控制网络,第一件事就是网络规划。我建议网关至少配两个网口:一个口接PLC/控制网,一个口接办公网或者上层数据网。控制网段和办公网段物理隔离,网关做数据单向或者按需双向转发,这样即使办公网里的电脑中毒,也不至于直接打到PLC上。

如果现场只有一张网,也要在网关上用防火墙规则限制访问来源。Ubuntu上UFW开启后只放行必要端口:

# 开放Node-RED编辑器端口(只给内网管理IP) sudo ufw allow from 192.168.1.100 to any port 1880 proto tcp # 开放OPC UA客户端出站连接(S7-1500默认4840) sudo ufw allow out to 192.168.0.10 port 4840 proto tcp

7.2 无线/4G场景下的断线重连

有些项目把边缘网关部署在无固定IP的站点,通过4G/5G上云。这个时候,OPC UA、MQTT这类TCP长连接都需要重点考虑“连接断了能不能自动恢复”。

Node-RED自带的一些节点,如MQTT Out节点,本身有自动重连机制,但要设置好Keep Alive周期和Clean Session。OPC UA的客户端节点也支持断线重连,但我在实际测试中发现,如果网线被拔掉超过几分钟,OPC UA通道可能需要手动重启才能恢复。所以有个技巧:在Node-RED里加一个定时器流,每隔几十秒ping一次PLC地址,一旦ping不通就触发一条通知,同时通过Node-RED的API重新部署OPC UA相关流程,强制重新建立连接。

7.3 网关自身的“看门狗”

边缘网关承担了报警推送职责,它自己反而不能死掉。所以我通常会做三层防护:

  1. 系统级看门狗systemd里设置服务崩溃自动重启;
  2. 容器级守护:Docker的--restart=always保证容器异常退出后自动拉起;
  3. 业务级心跳:Node-RED里定时发一条心跳消息到MQTT Broker或者企业微信群里,如果上层的监控平台连续几个周期收不到心跳,就说明边缘网关挂了,自动通知IT/OT管理员。

后两点很多人容易忽略。尤其第三点——报警系统的故障要能被感知。网关自己断了,报警全都不发,现场还以为一切正常,这是最危险的情况。

7.4 最麻烦的一类问题:OPC UA连接“时而能连时而不能连”

我在排查这类问题时,最终定位到的原因五花八门,这里把最常遇到的几种列出来:

排查方向常见原因解决办法
安全策略Endpoint和安全模式不匹配用UA Expert这类工具先连一次,查看服务器支持的安全策略列表
证书信任客户端证书不被服务器信任在PLC或WinCC侧导入Node-RED生成的证书/指纹
连接数限制WinCC的OPC UA服务有Session数上限检查服务器端配置,限制单Session订阅量
网络MTU大包被交换机分片丢弃把扫描周期调长,单次采集的点数不要太多
防火墙状态端口被办公网防火墙拦截在网关上tcpdump抓包确认连接建立情况

遇到OPC UA连接问题,不要直接猜,先在网关上抓包:

# 抓取到PLC 4840端口的包,确认TLS握手是否完成 sudo tcpdump -i eth0 host 192.168.0.10 and port 4840 -w opcua.pcap

再用Wireshark打开pcap文件,看TLS层有没有报错、Server侧的Hello消息有没有返回,基本就能定位是策略还是网络层的问题了。

8. 报警流上线后,我建议补充的几个增强功能

基础报警跑通以后,如果你还有余力,下面这几个增强方向是实际使用效果最好的,按性价比排序:

第一个是报警等级分级与值班规则。把报警分成“提示”“一般”“严重”三级:提示级只在看板里显示,不发通知;一般级推送给当班人员;严重级在一般级之外还要额外电话或短信通知值班经理。实现上不难,报警判断时带上等级信息,输出端根据等级分流到不同通知节点就行。

第二个是报警汇总日报。用一个Cron定时器(比如每天早上8点),查询前一天所有报警事件,生成一张汇总表发到管理群或者邮箱。这个功能上线后,很多管理者反而觉得比实时报警更有价值,它能让管理层对设备运行状态有一个全景式的判断。

第三个是报警与工单系统的对接。如果现场运维已经用了工单系统,Node-RED可以把报警事件作为自动创建工单的触发源,通过HTTP API调用工单系统的创建接口。这样一来,报警不再只是“一条消息”,而是进入了实际处理流转的闭环里。

这三个功能的实现都在Node-RED的流层面完成,不需要动PLC和WinCC。这也是边缘网关方案最有魅力的地方——系统的能力扩展从原来的“改上位机程序”变成了“加流、接API”,工程效率完全不在一个量级。

9. 回看这个方案的得失,说几句实在话

折腾了这么多项目,我对“不改WinCC和PLC加报警功能”这套做法最大的体会是:它把“约束”变成了“设计边界”。因为不能动老系统,你被迫把所有新能力全部外置,这反而让系统边界越来越清晰——控制归控制,信息归信息,后期的维护责任划分也省了很多扯皮。

每个项目能不能用这套方案,我建议先自评三个条件:一是现场有没有可用的OPC UA接口(或者至少是支持S7协议的以太网连接);二是能否接受报警逻辑放在边缘侧而不是PLC里;三是有没有人能维护这台边缘网关。三个条件如果都满足,那这个方案几乎可以闭眼选。

至于那些问我“PLC和WinCC迟早要换代升级,这套网关到时候怎么办”的人,我的回答是:网关本身就是中立的,PLC换成别的品牌、WinCC升级成别的SCADA,只要OPC UA这个标准还在,Node-RED这边的流只需要改一下服务器地址和节点ID就能接着用。这份“可迁移性”是我当初选OPC UA而不是走私有协议的根本原因。

最后再分享一个纯个人经验:真到现场调试那几天,不要盯着Node-RED编辑器里那条绿线发呆,要盯的是PLC扫描周期里那几十毫秒的变化。你把采样周期调到1秒,PLC侧基本无感;但如果把OPC UA的采样周期调到100毫秒以下,又订阅了几百个节点,老款PLC的通讯负载可能会明显升高,这时候手摸PLC的CPU模块面板,温度都能感觉出来。工业现场的“温柔”就在这些细节里——数据能用就行,别把它榨干。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询