温度上下限告警处理

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.config.MqttConfig;
import com.agri.framework.manager.MqttAutoOffManager; import com.agri.framework.manager.MqttAutoOffManager;
import com.agri.framework.manager.MqttSubscriptionManager; 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.JSON;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.collections4.CollectionUtils;
import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -15,6 +22,7 @@ import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -58,11 +66,15 @@ public class DeviceStatusHandler {
@Autowired @Autowired
private DeviceAckHandler deviceAckHandler; private DeviceAckHandler deviceAckHandler;
@Autowired
private ISysAgriInfoService agriInfoService;
// 新增最新状态缓存TTL设备每10秒上报一次缓存一小段时间即可 // 新增最新状态缓存TTL设备每10秒上报一次缓存一小段时间即可
@Value("${spring.mqtt.latest-ttl-seconds:120}") @Value("${spring.mqtt.latest-ttl-seconds:120}")
private int latestTtlSeconds; private int latestTtlSeconds;
@Autowired
private AgriService agriService;
private static final String AUTO_MODE = "auto_mode"; private static final String AUTO_MODE = "auto_mode";
private static final String AUTO_OFF = "autooff"; private static final String AUTO_OFF = "autooff";
/** /**
@ -134,10 +146,60 @@ public class DeviceStatusHandler {
// 转发消息 // 转发消息
forwardPayload(deviceId, payload,payloadObj,action, sendObj, isAck); forwardPayload(deviceId, payload,payloadObj,action, sendObj, isAck);
// 获取第二个动态段,如"up"或"ack" // 获取第二个动态段,如"up"或"ack"
if ("ack".equals(action)) { if ("ack".equals(action)) {
deviceAckHandler.isStartAutoOffTask(payloadObj,deviceId,payload); 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; 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.JSON;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference; import com.alibaba.fastjson2.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttException;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -73,7 +74,7 @@ public class FrontendControlHandler {
@Autowired @Autowired
private ISysUserService sysUserService; private ISysUserService sysUserService;
private final ObjectMapper objectMapper = new ObjectMapper();
@Value("${spring.mqtt.dtu-ctl-lock-ttl}") @Value("${spring.mqtt.dtu-ctl-lock-ttl}")
private int dtuCtlLockTTL; private int dtuCtlLockTTL;
private static final Map<String, Function<SysAgriLimit, Integer>> LIMIT_MAP = new HashMap<>(); private static final Map<String, Function<SysAgriLimit, Integer>> LIMIT_MAP = new HashMap<>();
@ -256,4 +257,15 @@ public class FrontendControlHandler {
// 普通用户权限校验Redis中是否绑定该设备 // 普通用户权限校验Redis中是否绑定该设备
return Boolean.TRUE; 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; package com.agri.framework.manager;
import com.agri.framework.config.MqttConfig; import com.agri.framework.config.MqttConfig;
import com.agri.framework.interceptor.FrontendControlHandler;
import com.agri.system.domain.SysAgriInfo; import com.agri.system.domain.SysAgriInfo;
import com.agri.system.service.AgriService; import com.agri.system.service.AgriService;
import com.agri.system.service.ISysAgriInfoService; 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"); private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Resource
private FrontendControlHandler frontendControlHandler;
@Autowired @Autowired
private AgriService agriService; private AgriService agriService;
@ -131,7 +134,7 @@ public class AgriStatusManager {
} }
// 无论设备是否在线 只要离线就推送设备状态 // 无论设备是否在线 只要离线就推送设备状态
if (!imeiMap.get("imeiOnline")) { if (!imeiMap.get("imeiOnline")) {
agriService.sendAlarmMessage("设备离线", imei, dateNow); frontendControlHandler.sendAlarmMessage("设备离线", imei, dateNow);
} }
successCount++; successCount++;
} catch (Exception e) { } catch (Exception e) {

View File

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

View File

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

View File

@ -112,4 +112,10 @@ public class SysAgriInfo extends BaseEntity
/** 湿度下限(%RH) */ /** 湿度下限(%RH) */
@Excel(name = "湿度下限(%RH)") @Excel(name = "湿度下限(%RH)")
private BigDecimal humiLow; 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) { public SysUserAgri(List<Long> idList) {
this.idList = idList; this.idList = idList;
} }
public SysUserAgri() {
}
} }

View File

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

View File

@ -1,6 +1,5 @@
package com.agri.system.service.impl; package com.agri.system.service.impl;
import com.agri.framework.config.MqttConfig;
import com.agri.system.service.AgriService; import com.agri.system.service.AgriService;
import com.agri.system.domain.SysAgriInfo; import com.agri.system.domain.SysAgriInfo;
import com.agri.system.domain.SysMessage; import com.agri.system.domain.SysMessage;
@ -19,8 +18,6 @@ import java.util.stream.Collectors;
@Service @Service
public class AgriServiceImpl implements AgriService { public class AgriServiceImpl implements AgriService {
@Resource
private MqttConfig.MqttMessageSender mqttMessageSender;
@Resource @Resource
private ISysUserAgriService userAgriService; private ISysUserAgriService userAgriService;
@ -28,24 +25,9 @@ public class AgriServiceImpl implements AgriService {
@Resource @Resource
private ISysMessageService messageService; 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 @Override
public void saveMessage(List<SysAgriInfo> offlineList,String msg) { public void saveMessage(List<SysAgriInfo> offlineList) {
// 离线是否为空 // 离线是否为空
if (CollectionUtils.isEmpty(offlineList)) { if (CollectionUtils.isEmpty(offlineList)) {
return; return;
@ -59,10 +41,10 @@ public class AgriServiceImpl implements AgriService {
List<SysMessage> msgList = new ArrayList<>(); List<SysMessage> msgList = new ArrayList<>();
for (SysAgriInfo agriInfo : offlineList) { for (SysAgriInfo agriInfo : offlineList) {
SysMessage message = new SysMessage(); SysMessage message = new SysMessage();
message.setTitle(msg); message.setTitle(agriInfo.getTitle());
message.setMsgType("status"); message.setMsgType("status");
message.setReadStatus(0L); message.setReadStatus(0L);
message.setContent("大棚【" + agriInfo.getAgriName() + "】" + msg + "!请检查设备状态。"); message.setContent("大棚【" + agriInfo.getAgriName() + "】" + agriInfo.getMsg());
message.setImgUrl(""); message.setImgUrl("");
message.setLinkUrl(UrlEncodeUtil.buildControlPageUrl(agriInfo, "/pages/home/control/index?agriInfo=")); message.setLinkUrl(UrlEncodeUtil.buildControlPageUrl(agriInfo, "/pages/home/control/index?agriInfo="));
for (SysUserAgri userAgri : agriUser){ for (SysUserAgri userAgri : agriUser){