温度上下限告警处理

master
lld 2026-03-31 21:25:00 +08:00
parent 780a4456e5
commit c2d651d38d
9 changed files with 111 additions and 31 deletions

View File

@ -4,8 +4,15 @@ import com.agri.common.utils.wechat.WxUtil;
import com.agri.framework.config.MqttConfig;
import com.agri.framework.manager.MqttAutoOffManager;
import com.agri.framework.manager.MqttSubscriptionManager;
import com.agri.system.domain.SysAgriInfo;
import com.agri.system.domain.SysMessage;
import com.agri.system.domain.SysUserAgri;
import com.agri.system.service.AgriService;
import com.agri.system.service.ISysAgriInfoService;
import com.agri.system.util.UrlEncodeUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.collections4.CollectionUtils;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -15,6 +22,7 @@ import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@ -58,11 +66,15 @@ public class DeviceStatusHandler {
@Autowired
private DeviceAckHandler deviceAckHandler;
@Autowired
private ISysAgriInfoService agriInfoService;
// 新增最新状态缓存TTL设备每10秒上报一次缓存一小段时间即可
@Value("${spring.mqtt.latest-ttl-seconds:120}")
private int latestTtlSeconds;
@Autowired
private AgriService agriService;
private static final String AUTO_MODE = "auto_mode";
private static final String AUTO_OFF = "autooff";
/**
@ -134,10 +146,60 @@ public class DeviceStatusHandler {
// 转发消息
forwardPayload(deviceId, payload,payloadObj,action, sendObj, isAck);
// 获取第二个动态段,如"up"或"ack"
if ("ack".equals(action)) {
deviceAckHandler.isStartAutoOffTask(payloadObj,deviceId,payload);
return;
}
// payload全部为数字
if ("up".equals(action) && isAllKeysDigit(payloadObj)) {
SysAgriInfo agriInfo = agriInfoService.lambdaQuery()
.eq(SysAgriInfo::getImei, deviceId)
.one();
if (agriInfo == null) {
return;
}
// 温度上下限
BigDecimal tempUp = agriInfo.getTempUp();
BigDecimal tempLow = agriInfo.getTempLow();
// 湿度上下限
BigDecimal humiUp = agriInfo.getHumiUp();
BigDecimal humiLow = agriInfo.getHumiLow();
List<SysAgriInfo> msgList = new ArrayList<>();
// 仅当up且所有key为数字时才更新最新状态缓存
for (String key : payloadObj.keySet()) {
BigDecimal value = payloadObj.getBigDecimal(key);
// 温度
if (key.startsWith("20")) {
// 温度大于温度上限
if (value.compareTo(tempUp) > 0 ) {
agriInfo.setTitle("温度异常");
agriInfo.setMsg("温度"+key.substring(2)+ "异常!高于上限"+tempUp+"℃!");
}
// 温度小于温度下限
if (value.compareTo(tempLow) < 0) {
agriInfo.setTitle("温度异常");
agriInfo.setMsg("温度"+key.substring(2)+ "异常!低于下限"+tempLow+"℃!");
}
msgList.add(agriInfo);
}
// 湿度
if (key.startsWith("10")) {
// 湿度大于湿度上限
if (value.compareTo(humiUp) > 0 ) {
agriInfo.setTitle("湿度异常");
agriInfo.setMsg("湿度"+key.substring(2)+ "异常!高于上限"+humiUp+"RH%");
}
// 湿度小于湿度下限
if (value.compareTo(humiLow) < 0) {
agriInfo.setTitle("湿度异常");
agriInfo.setMsg("湿度"+key.substring(2)+ "异常!低于下限"+humiLow+"RH%");
}
msgList.add(agriInfo);
}
}
agriService.saveMessage(msgList);
}
}
@ -236,4 +298,10 @@ public class DeviceStatusHandler {
return false;
}
public static boolean isAllKeysDigit(JSONObject obj) {
return obj != null && !obj.isEmpty()
&& obj.keySet().stream().allMatch(k -> k.matches("\\d+"));
}
}

View File

@ -15,6 +15,7 @@ import com.agri.system.service.ISysUserService;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.ObjectUtils;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger;
@ -73,7 +74,7 @@ public class FrontendControlHandler {
@Autowired
private ISysUserService sysUserService;
private final ObjectMapper objectMapper = new ObjectMapper();
@Value("${spring.mqtt.dtu-ctl-lock-ttl}")
private int dtuCtlLockTTL;
private static final Map<String, Function<SysAgriLimit, Integer>> LIMIT_MAP = new HashMap<>();
@ -256,4 +257,15 @@ public class FrontendControlHandler {
// 普通用户权限校验Redis中是否绑定该设备
return Boolean.TRUE;
}
public void sendAlarmMessage(String msg, String imei, String timeStr) throws Exception {
Map<String, Object> alarmMsg = new HashMap<>();
alarmMsg.put("online", msg);
alarmMsg.put("time", timeStr);
alarmMsg.put("imei", imei);
String alarmMessage = objectMapper.writeValueAsString(alarmMsg);
mqttMessageSender.publish("frontend/" + imei + "/alarm", alarmMessage);
}
}

View File

@ -1,6 +1,7 @@
package com.agri.framework.manager;
import com.agri.framework.config.MqttConfig;
import com.agri.framework.interceptor.FrontendControlHandler;
import com.agri.system.domain.SysAgriInfo;
import com.agri.system.service.AgriService;
import com.agri.system.service.ISysAgriInfoService;
@ -44,6 +45,8 @@ public class AgriStatusManager {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Resource
private FrontendControlHandler frontendControlHandler;
@Autowired
private AgriService agriService;
@ -131,7 +134,7 @@ public class AgriStatusManager {
}
// 无论设备是否在线 只要离线就推送设备状态
if (!imeiMap.get("imeiOnline")) {
agriService.sendAlarmMessage("设备离线", imei, dateNow);
frontendControlHandler.sendAlarmMessage("设备离线", imei, dateNow);
}
successCount++;
} catch (Exception e) {

View File

@ -89,7 +89,7 @@ public class AgriStatusTask {
// 4. 保存离线设备
List<SysAgriInfo> offlineDevices = findOfflineDevices(agriInfos, statusMap);
// 5. 保存消息中心
agriService.saveMessage(offlineDevices,"怀疑设备离线");
agriService.saveMessage(offlineDevices);
} catch (Exception e) {
log.error("设备在线状态推送任务异常", e);
@ -110,6 +110,8 @@ public class AgriStatusTask {
List<SysAgriInfo> offlineDevices = new ArrayList<>();
for (SysAgriInfo agriInfo : agriInfos) {
if (!statusMap.containsKey(agriInfo.getImei())) {
agriInfo.setTitle("设备离线告警");
agriInfo.setMsg("怀疑设备离线!请及时检查");
offlineDevices.add(agriInfo);
log.info("设备{} 不存在设备状态", agriInfo.getImei());
}

View File

@ -1,5 +1,6 @@
package com.agri.quartz.task;
import com.agri.framework.interceptor.FrontendControlHandler;
import com.agri.system.service.AgriService;
import com.agri.system.domain.SysAgriInfo;
import com.agri.system.service.ISysAgriInfoService;
@ -40,6 +41,9 @@ public class AgriTempTask {
@Autowired
private AgriService agriService;
@Resource
private FrontendControlHandler frontendControlHandler;
public void checkTempStatus() {
if (!acquireLock()) {
return;
@ -54,7 +58,7 @@ public class AgriTempTask {
Map<String, Object> latestDataMap = queryLatestDtuData(imeiList);
List<SysAgriInfo> offlineDevices = findOfflineDevices(agriInfos, latestDataMap);
pushOfflineAlarm(offlineDevices);
agriService.saveMessage(offlineDevices,"怀疑温度离线");
agriService.saveMessage(offlineDevices);
} catch (Exception e) {
log.error("设备在线状态推送任务异常", e);
} finally {
@ -104,6 +108,8 @@ public class AgriTempTask {
List<SysAgriInfo> offlineList = new ArrayList<>();
for (SysAgriInfo agriInfo : agriInfos) {
if (!latestDataMap.containsKey(agriInfo.getImei())) {
agriInfo.setTitle("温度离线");
agriInfo.setMsg("怀疑温度离线!请检查");
offlineList.add(agriInfo);
log.info("设备{} 不存在温湿度数据", agriInfo.getImei());
}
@ -119,7 +125,7 @@ public class AgriTempTask {
String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
for (SysAgriInfo agriInfo : offlineList) {
try {
agriService.sendAlarmMessage("温度离线",agriInfo.getImei(), timeStr);
frontendControlHandler.sendAlarmMessage("温度离线",agriInfo.getImei(), timeStr);
} catch (Exception e) {
log.error("发送设备离线告警失败, imei={}", agriInfo.getImei(), e);
}

View File

@ -112,4 +112,10 @@ public class SysAgriInfo extends BaseEntity
/** 湿度下限(%RH) */
@Excel(name = "湿度下限(%RH)")
private BigDecimal humiLow;
@TableField(exist = false)
private String title;
@TableField(exist = false)
private String msg;
}

View File

@ -90,4 +90,7 @@ public class SysUserAgri extends BaseEntity
public SysUserAgri(List<Long> idList) {
this.idList = idList;
}
public SysUserAgri() {
}
}

View File

@ -7,9 +7,7 @@ import java.util.Map;
public interface AgriService {
void sendAlarmMessage(String msg, String imei, String timeStr) throws Exception;
void saveMessage(List<SysAgriInfo> offlineList,String msg);
void saveMessage(List<SysAgriInfo> offlineList);
List<String> queryAllGreenhouseImei(List<SysAgriInfo> agriInfos);

View File

@ -1,6 +1,5 @@
package com.agri.system.service.impl;
import com.agri.framework.config.MqttConfig;
import com.agri.system.service.AgriService;
import com.agri.system.domain.SysAgriInfo;
import com.agri.system.domain.SysMessage;
@ -19,8 +18,6 @@ import java.util.stream.Collectors;
@Service
public class AgriServiceImpl implements AgriService {
@Resource
private MqttConfig.MqttMessageSender mqttMessageSender;
@Resource
private ISysUserAgriService userAgriService;
@ -28,24 +25,9 @@ public class AgriServiceImpl implements AgriService {
@Resource
private ISysMessageService messageService;
private final ObjectMapper objectMapper = new ObjectMapper();
// 发送离线告警消息
@Override
public void sendAlarmMessage(String msg, String imei, String timeStr) throws Exception {
Map<String, Object> alarmMsg = new HashMap<>();
alarmMsg.put("online", msg);
alarmMsg.put("time", timeStr);
alarmMsg.put("imei", imei);
String alarmMessage = objectMapper.writeValueAsString(alarmMsg);
mqttMessageSender.publish("frontend/" + imei + "/alarm", alarmMessage);
}
// 保存离线设备消息
@Override
public void saveMessage(List<SysAgriInfo> offlineList,String msg) {
public void saveMessage(List<SysAgriInfo> offlineList) {
// 离线是否为空
if (CollectionUtils.isEmpty(offlineList)) {
return;
@ -59,10 +41,10 @@ public class AgriServiceImpl implements AgriService {
List<SysMessage> msgList = new ArrayList<>();
for (SysAgriInfo agriInfo : offlineList) {
SysMessage message = new SysMessage();
message.setTitle(msg);
message.setTitle(agriInfo.getTitle());
message.setMsgType("status");
message.setReadStatus(0L);
message.setContent("大棚【" + agriInfo.getAgriName() + "】" + msg + "!请检查设备状态。");
message.setContent("大棚【" + agriInfo.getAgriName() + "】" + agriInfo.getMsg());
message.setImgUrl("");
message.setLinkUrl(UrlEncodeUtil.buildControlPageUrl(agriInfo, "/pages/home/control/index?agriInfo="));
for (SysUserAgri userAgri : agriUser){