温室大棚对环境参数的敏感程度远超一般人的想象。夏季正午棚内温度可能在十分钟内从28摄氏度飙升到42摄氏度,如果放风和湿帘没有及时启动,一茬黄瓜的产量就会受到明显影响。传统的人工抄表方式既慢又容易漏检,而物联网方案把传感器数据推到网页端之后,前端如何判断数据越界、如何把报警及时且不打扰地呈现出来,就成了实际项目里最考验细节的部分。本文以jQuery为前端核心,完整拆解一套大棚温湿度阈值报警的实现思路。

一、温湿度数据特点与阈值设计
动手写代码之前,先要把阈值这件事想清楚。很多初学者会直接写死一个数字,比如温度超过35就报警,但在真实大棚里这样做会带来大量误报。温湿度数据有三个明显特点:一是昼夜波动大,白天和夜间的合理区间完全不同;二是传感器本身有误差和噪声,数值会在真实值附近小幅抖动;三是短时尖峰很常见,比如卷帘机动作瞬间、人员进出大棚时,温度可能瞬间跳变两三度。
针对这些特点,阈值设计通常采用三段式结构:安全区间、预警区间和报警区间。以番茄大棚为例,白天温度22到30摄氏度为安全区,30到33度为预警区,超过33度或低于15度则进入报警区;湿度可以设定为50%到80%安全、80%到90%预警、超过90%报警。前端拿到的数据结构一般是这样的:
var sensorData = {
deviceId: "gh-01-temperature-03",
timestamp: 1718000000,
temperature: 31.6, // 摄氏度
humidity: 83 // 相对湿度百分比
};
// 阈值配置对象,方便后期维护和远程下发
var thresholds = {
temperature: { safe: [22, 30], warn: [30, 33], min: 15, max: 33 },
humidity: { safe: [50, 80], warn: [80, 90], min: 40, max: 90 }
};把阈值集中在一个配置对象里,好处是显而易见的:一是后续可以通过后台接口动态下发,实现不同作物、不同生长阶段的差异化配置;二是代码中所有判断都引用同一份数据,不会出现某处写35、另一处写33的低级不一致。另外要注意传感器噪声问题,单次越界就报警会导致报警灯频繁闪烁,下一节会通过去抖机制解决。
二、用jQuery实现分级报警的核心逻辑
报警的核心是一个纯函数:输入当前数据和阈值,输出报警等级。把这个判断逻辑独立出来,不与任何DOM操作耦合,是保证代码可测试、可复用的关键。等级可以分为normal(正常)、warn(预警)、alarm(报警)三级,温度和湿度分别判断后取较高等级作为最终结果。
function checkLevel(value, conf) {
if (value < conf.min || value > conf.max) {
return "alarm";
}
if (value >= conf.safe[0] && value <= conf.safe[1]) {
return "normal";
}
return "warn"; // 处于预警区间
}
function evaluate(data) {
var tLevel = checkLevel(data.temperature, thresholds.temperature);
var hLevel = checkLevel(data.humidity, thresholds.humidity);
var rank = { normal: 0, warn: 1, alarm: 2 };
var finalLevel = rank[tLevel] >= rank[hLevel] ? tLevel : hLevel;
return {
level: finalLevel,
temperatureLevel: tLevel,
humidityLevel: hLevel
};
}判断出等级之后,用jQuery把结果渲染到页面。这里推荐的写法是用data-level属性驱动样式,而不是在JS里堆砌css()调用,这样主题调整时只需改样式表,JavaScript一行都不用动:
function render(data, result) {
var $temp = $("#temp-value"),
$hum = $("#humidity-value"),
$panel = $("#monitor-panel");
$temp.text(data.temperature.toFixed(1) + " ℃")
.data("level", result.temperatureLevel);
$hum.text(data.humidity + " %")
.data("level", result.humidityLevel);
$panel.attr("data-level", result.level);
// 报警等级达到alarm时弹出确认框,warn只做页面提示
if (result.level === "alarm") {
showAlert(data, result);
}
}
function showAlert(data, result) {
var msg = "大棚" + data.deviceId + " 数据异常:温度 " +
data.temperature + "℃,湿度 " + data.humidity + "%";
// 避免弹窗叠加:页面已有弹窗时先更新内容
if ($("#alarm-dialog").length === 0) {
$("<div id='alarm-dialog'><p>" + msg + "</p></div>")
.dialog({ title: "紧急报警", modal: true });
} else {
$("#alarm-dialog p").text(msg);
}
}去抖处理同样重要。可以用一个计数器记录连续越界的次数,连续三次超过报警线才真正触发弹窗,这样能有效过滤掉传感器瞬时毛刺。同时报警记录应该立即通过$.ajax回传后台落库,即使值班人员当时不在电脑前,事后也能追溯完整的事件链。
三、轮询与WebSocket两种取数方式的取舍
数据怎么到前端,直接决定报警的及时性。最简单的方案是jQuery的setInterval配合$.ajax轮询接口,代码不到十行就能跑起来:
function startPolling(interval) {
setInterval(function () {
$.ajax({
url: "/api/greenhouse/latest",
dataType: "json",
success: function (resp) {
var result = evaluate(resp.data);
render(resp.data, result);
},
error: function () {
$("#monitor-panel").attr("data-level", "offline");
}
});
}, interval || 5000);
}轮询的优点是实现简单、服务端无状态、配合jQuery几乎零学习成本;缺点是实时性受间隔限制,且大量客户端同时轮询会给接口带来压力。如果大棚有几十个监测点、管理人员又需要秒级响应,就应该换用WebSocket方案。jQuery本身不提供WebSocket封装,但两者并不冲突,可以在WebSocket的onmessage回调里继续复用上面写好的evaluate和render函数,判断与渲染逻辑完全不用改,只是数据来源换了一根管道。
实际项目中还有一种折中做法:页面加载时先用一次Ajax拉取当前值和历史曲线,之后切换到WebSocket接收增量更新,连接意外断开时自动降级回轮询。这种机制让前端在弱网环境的大棚园区里依然保持可用,是保障报警不漏报的最后一道防线。无论采用哪种方式,报警去抖、阈值配置下发、报警记录持久化这三个细节都是共通的,把这几块做扎实,整套监控系统才真正经得起生产环境的考验。