温度上下限告警处理

master
lld 2026-03-31 22:17:37 +08:00
parent c2d651d38d
commit 50c191a9a6
8 changed files with 66 additions and 49 deletions

View File

@ -16,6 +16,7 @@ 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;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
@ -23,10 +24,8 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.time.LocalDateTime;
import java.util.HashSet; import java.util.*;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -77,6 +76,9 @@ public class DeviceStatusHandler {
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";
@Resource
private FrontendControlHandler frontendControlHandler;
/** /**
* / * /
*/ */
@ -169,17 +171,20 @@ public class DeviceStatusHandler {
// 仅当up且所有key为数字时才更新最新状态缓存 // 仅当up且所有key为数字时才更新最新状态缓存
for (String key : payloadObj.keySet()) { for (String key : payloadObj.keySet()) {
BigDecimal value = payloadObj.getBigDecimal(key); BigDecimal value = payloadObj.getBigDecimal(key);
String valueIndex = key.substring(2);
SysAgriInfo sysAgriInfo = new SysAgriInfo();
BeanUtils.copyProperties(agriInfo, sysAgriInfo);
// 温度 // 温度
if (key.startsWith("20")) { if (key.startsWith("20")) {
// 温度大于温度上限 // 温度大于温度上限
if (value.compareTo(tempUp) > 0 ) { if (value.compareTo(tempUp) > 0 ) {
agriInfo.setTitle("温度异常"); agriInfo.setTitle("温度异常");
agriInfo.setMsg("温度"+key.substring(2)+ "异常!高于上限"+tempUp+"℃!"); agriInfo.setMsg("温度"+valueIndex+ "异常!高于上限"+tempUp+"℃!");
} }
// 温度小于温度下限 // 温度小于温度下限
if (value.compareTo(tempLow) < 0) { if (value.compareTo(tempLow) < 0) {
agriInfo.setTitle("温度异常"); agriInfo.setTitle("温度异常");
agriInfo.setMsg("温度"+key.substring(2)+ "异常!低于下限"+tempLow+"℃!"); agriInfo.setMsg("温度"+valueIndex+ "异常!低于下限"+tempLow+"℃!");
} }
msgList.add(agriInfo); msgList.add(agriInfo);
} }
@ -188,18 +193,24 @@ public class DeviceStatusHandler {
// 湿度大于湿度上限 // 湿度大于湿度上限
if (value.compareTo(humiUp) > 0 ) { if (value.compareTo(humiUp) > 0 ) {
agriInfo.setTitle("湿度异常"); agriInfo.setTitle("湿度异常");
agriInfo.setMsg("湿度"+key.substring(2)+ "异常!高于上限"+humiUp+"RH%"); agriInfo.setMsg("湿度"+valueIndex+ "异常!高于上限"+humiUp+"RH%");
} }
// 湿度小于湿度下限 // 湿度小于湿度下限
if (value.compareTo(humiLow) < 0) { if (value.compareTo(humiLow) < 0) {
agriInfo.setTitle("湿度异常"); agriInfo.setTitle("湿度异常");
agriInfo.setMsg("湿度"+key.substring(2)+ "异常!低于下限"+humiLow+"RH%"); agriInfo.setMsg("湿度"+valueIndex+ "异常!低于下限"+humiLow+"RH%");
} }
msgList.add(agriInfo); msgList.add(agriInfo);
} }
} }
if (!msgList.isEmpty()) {
agriService.saveMessage(msgList); List<SysMessage> messages = agriService.saveMessage(msgList);
try {
frontendControlHandler.sendAlarmMessage(messages);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} }
} }

View File

@ -7,6 +7,7 @@ import com.agri.framework.manager.MqttAutoOffManager;
import com.agri.system.domain.SysAgriInfo; import com.agri.system.domain.SysAgriInfo;
import com.agri.system.domain.SysAgriLimit; import com.agri.system.domain.SysAgriLimit;
import com.agri.system.domain.SysDevOperLog; import com.agri.system.domain.SysDevOperLog;
import com.agri.system.domain.SysMessage;
import com.agri.system.mapper.SysUserMapper; import com.agri.system.mapper.SysUserMapper;
import com.agri.system.service.ISysAgriInfoService; import com.agri.system.service.ISysAgriInfoService;
import com.agri.system.service.ISysAgriLimitService; import com.agri.system.service.ISysAgriLimitService;
@ -16,6 +17,7 @@ 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 com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections4.CollectionUtils;
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;
@ -28,7 +30,9 @@ import org.springframework.util.StringUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Function; import java.util.function.Function;
@ -71,6 +75,7 @@ public class FrontendControlHandler {
private ISysAgriInfoService sysAgriInfoService; private ISysAgriInfoService sysAgriInfoService;
@Autowired @Autowired
private ISysDevOperLogService sysDevOperLogService; private ISysDevOperLogService sysDevOperLogService;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Autowired @Autowired
private ISysUserService sysUserService; private ISysUserService sysUserService;
@ -258,14 +263,16 @@ public class FrontendControlHandler {
return Boolean.TRUE; return Boolean.TRUE;
} }
public void sendAlarmMessage(String msg, String imei, String timeStr) throws Exception { public void sendAlarmMessage(List<SysMessage> messages) throws Exception {
if (CollectionUtils.isEmpty(messages)) {
Map<String, Object> alarmMsg = new HashMap<>(); return;
alarmMsg.put("online", msg); }
alarmMsg.put("time", timeStr); for (SysMessage message : messages) {
alarmMsg.put("imei", imei); Map<String, Object> alarmMsg = new HashMap<>();
String alarmMessage = objectMapper.writeValueAsString(alarmMsg); alarmMsg.put("msg",message.getContent());
mqttMessageSender.publish("frontend/" + imei + "/alarm", alarmMessage); alarmMsg.put("time", LocalDateTime.now().format(DATE_TIME_FORMATTER));
String alarmMessage = objectMapper.writeValueAsString(alarmMsg);
mqttMessageSender.publish("frontend/" + message.getImei() + "/alarm", alarmMessage);
}
} }
} }

View File

@ -132,10 +132,6 @@ public class AgriStatusManager {
String onlineMessage = objectMapper.writeValueAsString(onlineMsg); String onlineMessage = objectMapper.writeValueAsString(onlineMsg);
mqttMessageSender.publish("device/" + imei + "/status", onlineMessage); mqttMessageSender.publish("device/" + imei + "/status", onlineMessage);
} }
// 无论设备是否在线 只要离线就推送设备状态
if (!imeiMap.get("imeiOnline")) {
frontendControlHandler.sendAlarmMessage("设备离线", imei, dateNow);
}
successCount++; successCount++;
} catch (Exception e) { } catch (Exception e) {
failCount++; failCount++;

View File

@ -1,7 +1,9 @@
package com.agri.quartz.task; package com.agri.quartz.task;
import com.agri.framework.interceptor.FrontendControlHandler;
import com.agri.framework.manager.AgriStatusManager; import com.agri.framework.manager.AgriStatusManager;
import com.agri.system.domain.SysAgriInfo; import com.agri.system.domain.SysAgriInfo;
import com.agri.system.domain.SysMessage;
import com.agri.system.service.AgriService; import com.agri.system.service.AgriService;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.agri.framework.config.MqttConfig; import com.agri.framework.config.MqttConfig;
@ -52,6 +54,9 @@ public class AgriStatusTask {
@Autowired @Autowired
private AgriService agriService; private AgriService agriService;
@Resource
private FrontendControlHandler frontendControlHandler;
/** /**
* 10 * 10
* 1. sub: key IMEI * 1. sub: key IMEI
@ -84,12 +89,14 @@ public class AgriStatusTask {
} }
Map<String, Map<String, Boolean>> statusMap Map<String, Map<String, Boolean>> statusMap
= agriStatusManager.batchCheckDeviceOnline(imeiList); = agriStatusManager.batchCheckDeviceOnline(imeiList);
// 3. 批量查询设备在线状态Redis Pipeline一次网络往返 // 3. 首页状态
agriStatusManager.asyncBatchPushMqtt(statusMap); agriStatusManager.asyncBatchPushMqtt(statusMap);
// 4. 保存离线设备 // 4. 离线设备
List<SysAgriInfo> offlineDevices = findOfflineDevices(agriInfos, statusMap); List<SysAgriInfo> offlineDevices = findOfflineDevices(agriInfos, statusMap);
// 5. 保存消息中心 // 5. 保存消息中心
agriService.saveMessage(offlineDevices); List<SysMessage> messages = agriService.saveMessage(offlineDevices);
// 6. 发送告警消息
frontendControlHandler.sendAlarmMessage(messages);
} catch (Exception e) { } catch (Exception e) {
log.error("设备在线状态推送任务异常", e); log.error("设备在线状态推送任务异常", e);
@ -107,9 +114,15 @@ public class AgriStatusTask {
log.info("不存在任何imei"); log.info("不存在任何imei");
return new ArrayList<>(); return new ArrayList<>();
} }
Map<String, Object> offlineMap = new HashMap<>();
for (Map.Entry<String, Map<String, Boolean>> map : statusMap.entrySet()) {
if (!map.getValue().get("imeiOnline")) {
offlineMap.put(map.getKey(), map.getValue());
}
}
List<SysAgriInfo> offlineDevices = new ArrayList<>(); List<SysAgriInfo> offlineDevices = new ArrayList<>();
for (SysAgriInfo agriInfo : agriInfos) { for (SysAgriInfo agriInfo : agriInfos) {
if (!statusMap.containsKey(agriInfo.getImei())) { if (!offlineMap.containsKey(agriInfo.getImei())) {
agriInfo.setTitle("设备离线告警"); agriInfo.setTitle("设备离线告警");
agriInfo.setMsg("怀疑设备离线!请及时检查"); agriInfo.setMsg("怀疑设备离线!请及时检查");
offlineDevices.add(agriInfo); offlineDevices.add(agriInfo);

View File

@ -1,6 +1,7 @@
package com.agri.quartz.task; package com.agri.quartz.task;
import com.agri.framework.interceptor.FrontendControlHandler; import com.agri.framework.interceptor.FrontendControlHandler;
import com.agri.system.domain.SysMessage;
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;
@ -57,8 +58,9 @@ 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); List<SysMessage> messages = agriService.saveMessage(offlineDevices);
agriService.saveMessage(offlineDevices); // 推送离线告警
frontendControlHandler.sendAlarmMessage(messages);
} catch (Exception e) { } catch (Exception e) {
log.error("设备在线状态推送任务异常", e); log.error("设备在线状态推送任务异常", e);
} finally { } finally {
@ -117,20 +119,4 @@ public class AgriTempTask {
return offlineList; return offlineList;
} }
// 推送离线告警
private void pushOfflineAlarm(List<SysAgriInfo> offlineList) {
if (CollectionUtils.isEmpty(offlineList)) {
return;
}
String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
for (SysAgriInfo agriInfo : offlineList) {
try {
frontendControlHandler.sendAlarmMessage("温度离线",agriInfo.getImei(), timeStr);
} catch (Exception e) {
log.error("发送设备离线告警失败, imei={}", agriInfo.getImei(), e);
}
}
}
} }

View File

@ -25,6 +25,7 @@ public class SysMessage extends BaseEntity
/** 消息主键ID */ /** 消息主键ID */
private Long id; private Long id;
private String imei;
/** 接收人all=全体用户,其他=用户ID */ /** 接收人all=全体用户,其他=用户ID */
@Excel(name = "接收人all=全体用户,其他=用户ID") @Excel(name = "接收人all=全体用户,其他=用户ID")
private Long receiver; private Long receiver;

View File

@ -1,13 +1,14 @@
package com.agri.system.service; package com.agri.system.service;
import com.agri.system.domain.SysAgriInfo; import com.agri.system.domain.SysAgriInfo;
import com.agri.system.domain.SysMessage;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public interface AgriService { public interface AgriService {
void saveMessage(List<SysAgriInfo> offlineList); List<SysMessage> saveMessage(List<SysAgriInfo> offlineList);
List<String> queryAllGreenhouseImei(List<SysAgriInfo> agriInfos); List<String> queryAllGreenhouseImei(List<SysAgriInfo> agriInfos);

View File

@ -27,10 +27,10 @@ public class AgriServiceImpl implements AgriService {
// 保存离线设备消息 // 保存离线设备消息
@Override @Override
public void saveMessage(List<SysAgriInfo> offlineList) { public List<SysMessage> saveMessage(List<SysAgriInfo> offlineList) {
// 离线是否为空 // 离线是否为空
if (CollectionUtils.isEmpty(offlineList)) { if (CollectionUtils.isEmpty(offlineList)) {
return; return Collections.emptyList();
} }
List<Long> idList List<Long> idList
= offlineList.stream().map(SysAgriInfo::getId).collect(Collectors.toList()); = offlineList.stream().map(SysAgriInfo::getId).collect(Collectors.toList());
@ -41,10 +41,11 @@ 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.setImei(agriInfo.getImei());
message.setTitle(agriInfo.getTitle()); message.setTitle(agriInfo.getTitle());
message.setMsgType("status"); message.setMsgType("status");
message.setReadStatus(0L); message.setReadStatus(0L);
message.setContent("大棚【" + agriInfo.getAgriName() + "】" + agriInfo.getMsg()); message.setContent("大棚【" + agriInfo.getAgriName() + " - " + agriInfo.getImei() + "】" + 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){
@ -55,6 +56,7 @@ public class AgriServiceImpl implements AgriService {
if (CollectionUtils.isNotEmpty(msgList)) { if (CollectionUtils.isNotEmpty(msgList)) {
messageService.saveBatch(msgList); messageService.saveBatch(msgList);
} }
return msgList;
} }