石油管道SCADA系统里,压力是最核心的监控参数之一。泄漏、堵塞或者泵机组故障,往往先表现为压力曲线的异常波动。如果前端页面只把数字摆在那里,值班员一旦走神,几秒钟的延误就可能酿成事故。把压力异常与声光报警联动起来,让系统主动"喊人",才是工业监控软件该有的样子。本文用jQuery来实现这套联动逻辑,重点是数据获取、阈值判断、报警触发和工程化处理四个环节。

一、实时压力数据的两种获取方式
报警联动的前提是数据要"活"。传统做法是前端定时向后端接口轮询,jQuery的$.ajax配合setInterval就能实现,简单直接,兼容老款工控机上的浏览器。后端接口返回JSON数组,包含测点编号、压力值、采集时间戳等信息。
// 轮询方式:每2秒请求一次压力数据
function fetchPressure() {
$.ajax({
url: '/api/scada/pressure',
type: 'GET',
dataType: 'json',
timeout: 5000,
success: function (data) {
$.each(data.points, function (i, pt) {
renderPoint(pt);
checkAlarm(pt);
});
},
error: function () {
markOffline(); // 标记通信中断
}
});
}
setInterval(fetchPressure, 2000);
轮询的缺点也很明显:2秒的间隔对于快速瞬态压力波动来说太慢,而且测点多的时候,频繁的HTTP请求对服务器是负担。更推荐的做法是WebSocket推送,后端PLC数据一更新就推给前端,延迟可以压到毫秒级。jQuery本身不封装WebSocket,但两者配合毫无问题。
var ws = new WebSocket('ws://192.168.0.1:8080/scada/pressure');
ws.onmessage = function (e) {
var pt = JSON.parse(e.data);
renderPoint(pt);
checkAlarm(pt);
};
ws.onclose = function () {
markOffline();
setTimeout(reconnect, 3000); // 断线3秒后重连
};
工程实践中建议两者结合:WebSocket为主,轮询做兜底。WebSocket断开后自动降级到轮询模式,恢复后再切回来,保证页面数据不中断。
二、分级阈值判断与报警状态管理
压力报警不能简单粗暴地设一个阈值。石油管道通常分三级:预警(黄色)、报警(橙色)、紧急(红色),对应不同处置流程。判断逻辑本身不难,难在状态管理——压力在阈值附近抖动时,不能让报警反复触发又反复消除,否则声光设备频繁启停,值班员会烦不胜烦,最后干脆把报警静音,这是安全管理的大忌。
var ALARM_LEVELS = {
warn: { min: 4.0, max: 6.5, cls: 'level-warn' },
alarm: { min: 3.5, max: 7.0, cls: 'level-alarm' },
danger: { min: 3.0, max: 7.5, cls: 'level-danger' }
};
function judgeLevel(pressure) {
if (pressure <= ALARM_LEVELS.danger.min || pressure >= ALARM_LEVELS.danger.max) {
return 'danger';
}
if (pressure <= ALARM_LEVELS.alarm.min || pressure >= ALARM_LEVELS.alarm.max) {
return 'alarm';
}
if (pressure <= ALARM_LEVELS.warn.min || pressure >= ALARM_LEVELS.warn.max) {
return 'warn';
}
return 'normal';
}
// 连续3次超限才确认报警,避免瞬时抖动误报
var counters = {};
function checkAlarm(pt) {
var lv = judgeLevel(pt.value);
if (lv === 'normal') {
counters[pt.id] = 0;
clearAlarm(pt.id);
return;
}
counters[pt.id] = (counters[pt.id] || 0) + 1;
if (counters[pt.id] >= 3) {
triggerAlarm(pt, lv);
}
}
这里的连续确认机制很关键。压力变送器偶尔会蹦出一个野值,如果一次超限就报警,误报率会很高。连续三个周期都超限才触发,能过滤掉绝大部分干扰。另外还要加一个滞回区间(比如报警下限3.5,恢复条件设为3.6),消除报警和恢复之间来回跳变的问题。
三、声光报警的联动实现
视觉报警最直观的做法是让对应测点的DOM元素闪烁并变色。用jQuery控制class切换,CSS动画负责闪烁效果,性能开销小。紧急级别还要叠加全屏红色边框,确保值班员无论在看哪个画面都能注意到。
@keyframes blink {
0%, 100% { background-color: #fff; }
50% { background-color: #ff3030; }
}
.level-danger { animation: blink 0.8s infinite; color: #fff; }
.level-alarm { background-color: #ff9f30; color: #000; }
.level-warn { background-color: #ffe000; }
.screen-danger { box-shadow: inset 0 0 80px rgba(255,0,0,0.6); }
声音报警在Web端有两种方案。老办法是嵌入一个Flash或ActiveX控件驱动现场蜂鸣器,现在早已淘汰。现代浏览器用Audio标签播放报警音,代码简单,但要注意:浏览器 autoplay 策略要求用户先与页面有过交互,所以要在值班员登录时预加载并"预热"一次音频对象。真正驱动现场声光报警器的,一般是通过后端下发指令给PLC输出模块,前端只负责展示和确认。
var alarmSound = new Audio('/audio/alarm.mp3');
alarmSound.preload = 'auto';
// 登录后点击任意位置预热,绕过autoplay限制
$(document).one('click', function () { alarmSound.play().then(alarmSound.pause); });
function triggerAlarm(pt, level) {
$('#' + pt.id).addClass(ALARM_LEVELS[level].cls);
if (level === 'danger') {
$('body').addClass('screen-danger');
alarmSound.loop = true;
alarmSound.play();
// 通知后端驱动现场声光报警器
$.post('/api/scada/alarm/start', { pointId: pt.id, level: level });
showDialog(pt, level);
}
appendAlarmLog(pt, level);
}
弹窗确认环节不能省。报警发生后必须有人点"确认"才能消音,否则值班员离开座位时报警就被无声忽略。确认动作只做消音,报警状态要等压力恢复正常才解除,这两个动作必须分开,很多自建系统把两者混在一起,是典型的设计缺陷。
四、报警风暴抑制与记录落库
管道发生水击时,几十个测点可能在同一秒集体报警,这就是报警风暴。如果每个测点都独立播放声音、独立弹窗,系统会瘫痪。解决办法是给报警合并留一个时间窗口,同一时刻多个报警合并成一条汇总提示,声音只触发一次。
var pendingAlarm = null;
function scheduleAlarmDialog(pt, level) {
if (!pendingAlarm) {
pendingAlarm = { items: [] };
setTimeout(function () {
showMergedDialog(pendingAlarm.items);
pendingAlarm = null;
}, 1000); // 1秒窗口内合并
}
pendingAlarm.items.push(pt);
}
报警记录同样重要。每一条报警都要在前端即时缓存,再批量提交到后端数据库,形成可追溯的报警历史。断网时先存localStorage,通信恢复后补传。这些记录是事后事故分析的原始依据,也是考核值班响应速度的数据来源。用JSON.stringify序列化后$.post批量上传即可,注意加时间戳和值班员工号,确认动作和消音动作也要分别记录。
最后提一句可靠性:报警页面要禁止浏览器缓存,避免显示过期数据;页面放在值班室大屏上长期运行,还得处理浏览器内存泄漏问题,长连接、DOM节点都要定期清理。把这几个细节做到位,这套jQuery报警联动方案在实际管道站场跑上几年不出问题,是完全做得到的。