|
|
@@ -3,8 +3,15 @@ package com.zksy.telemetry.utils;
|
|
|
import static com.zksy.common.utils.CollectionSideEffects.*;
|
|
|
|
|
|
import com.zksy.common.exception.InvalidMessageException;
|
|
|
+import com.zksy.api.domain.AlarmData;
|
|
|
+import com.zksy.api.domain.WarningThreshold;
|
|
|
+import com.zksy.api.service.AlarmDataService;
|
|
|
+import com.zksy.api.service.EarlyWarningAutoService;
|
|
|
+import com.zksy.api.service.WarningThresholdService;
|
|
|
+import com.zksy.api.utils.AlarmLevelUtil;
|
|
|
import com.zksy.telemetry.domain.TelemetryData;
|
|
|
import com.zksy.telemetry.service.TelemetryDataService;
|
|
|
+import com.zksy.utils.DevicePhoneFetchUtil;
|
|
|
import io.netty.buffer.ByteBuf;
|
|
|
import io.netty.buffer.Unpooled;
|
|
|
import io.netty.channel.ChannelHandler;
|
|
|
@@ -12,6 +19,7 @@ import io.netty.channel.ChannelHandlerContext;
|
|
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
|
|
import io.netty.handler.timeout.ReadTimeoutException;
|
|
|
import io.netty.util.ReferenceCountUtil;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
@@ -19,6 +27,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
import java.util.Date;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import com.zksy.api.utils.SmsUtil;
|
|
|
|
|
|
@ChannelHandler.Sharable
|
|
|
@Slf4j
|
|
|
@@ -29,6 +41,16 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
|
|
|
@Autowired
|
|
|
private DeviceOfflineCheckTask deviceOfflineCheckTask;
|
|
|
@Autowired
|
|
|
+ private WarningThresholdService warningThresholdService;
|
|
|
+ @Autowired
|
|
|
+ private AlarmDataService alarmDataService;
|
|
|
+ @Autowired
|
|
|
+ private EarlyWarningAutoService earlyWarningAutoService;
|
|
|
+ @Autowired
|
|
|
+ private SmsUtil smsUtil;
|
|
|
+ @Autowired
|
|
|
+ private DevicePhoneFetchUtil devicePhoneFetchUtil;
|
|
|
+ @Autowired
|
|
|
public MessageHandler(TelemetryDataService telemetryDataService) {
|
|
|
this.service = telemetryDataService;
|
|
|
}
|
|
|
@@ -91,6 +113,7 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
|
|
|
}
|
|
|
});
|
|
|
run(deviceCode, "在线状态同步", () -> deviceOfflineCheckTask.markDeviceOnline(deviceCode));
|
|
|
+ run(deviceCode, "压力报警预警评估", () -> evaluatePressure(resultData, resolveDeviceCode(resultData)));
|
|
|
|
|
|
} else if (frameType == 0x34) {
|
|
|
/*logger.info("收到结束通讯帧,准备发送回应包");
|
|
|
@@ -114,6 +137,87 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 平升遥测终端的供水压力使用数值阈值判断 WARN-PRESSURE。
|
|
|
+ * 压力值来自本次采集报文,阈值范围来自 warning_threshold 表。
|
|
|
+ */
|
|
|
+ private void evaluatePressure(TelemetryData data, String deviceCode) {
|
|
|
+ if (warningThresholdService == null || alarmDataService == null || earlyWarningAutoService == null) {
|
|
|
+ throw new IllegalStateException("压力告警依赖未注入");
|
|
|
+ }
|
|
|
+ if (data.getPressure() == null || data.getPressure().trim().isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("采集报文缺少压力值");
|
|
|
+ }
|
|
|
+ BigDecimal actual = new BigDecimal(data.getPressure().trim());
|
|
|
+ String warningCode = "WARN-PRESSURE";
|
|
|
+ WarningThreshold threshold = warningThresholdService
|
|
|
+ .getWarningThresholdByDeviceAndCode(deviceCode, warningCode);
|
|
|
+ if (threshold == null || (threshold.getMinValue() == null && threshold.getMaxValue() == null)) {
|
|
|
+ log.warn("未配置供水压力阈值,跳过 WARN-PRESSURE 评估: deviceCode={}", deviceCode);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ BigDecimal min = threshold.getMinValue() == null ? null : BigDecimal.valueOf(threshold.getMinValue());
|
|
|
+ BigDecimal max = threshold.getMaxValue() == null ? null : BigDecimal.valueOf(threshold.getMaxValue());
|
|
|
+ String warningType = threshold.getWarningType() == null ? "压力预警" : threshold.getWarningType();
|
|
|
+ boolean over = (min != null && actual.compareTo(min) <= 0)
|
|
|
+ || (max != null && actual.compareTo(max) >= 0);
|
|
|
+ if (over) {
|
|
|
+ int level = AlarmLevelUtil.calculateLevel(actual, min, max);
|
|
|
+ run(deviceCode, warningCode + ":报警入库", () -> {
|
|
|
+ AlarmData alarm = new AlarmData();
|
|
|
+ alarm.setDeviceCode(deviceCode);
|
|
|
+ alarm.setWarningType(warningType);
|
|
|
+ alarm.setWarningCode(warningCode);
|
|
|
+ alarm.setMinValue(min);
|
|
|
+ alarm.setMaxValue(max);
|
|
|
+ alarm.setActualValue(actual);
|
|
|
+ alarm.setAlarmLevel(level);
|
|
|
+ alarm.setDeviationRatio(AlarmLevelUtil.calculateDeviationRatio(actual, min, max));
|
|
|
+ alarm.setAlarmStatus(0);
|
|
|
+ alarm.setAlarmTime(java.time.LocalDateTime.now());
|
|
|
+ alarm.setRemark(threshold.getRemark());
|
|
|
+ alarm.setCreateTime(java.time.LocalDateTime.now());
|
|
|
+ requireSaved(alarmDataService.saveAlarmData(alarm));
|
|
|
+ });
|
|
|
+ run(deviceCode, warningCode + ":关闭预警", () ->
|
|
|
+ earlyWarningAutoService.closeAutoWarning(deviceCode, warningCode, "已达报警阈值,转报警处理"));
|
|
|
+ if (level == 1) {
|
|
|
+ run(deviceCode, warningCode + ":一级告警短信", () -> sendAlarmSms(deviceCode, warningType, actual, min, max));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ run(deviceCode, warningCode + ":生成预警", () ->
|
|
|
+ earlyWarningAutoService.checkAndGenerate(deviceCode, warningType, warningCode,
|
|
|
+ min, max, actual, threshold.getRemark()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String resolveDeviceCode(TelemetryData data) {
|
|
|
+ if (data.getDeviceCode() != null && !data.getDeviceCode().trim().isEmpty()) {
|
|
|
+ return data.getDeviceCode().trim();
|
|
|
+ }
|
|
|
+ return data.getSystemIdentifier();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void sendAlarmSms(String deviceCode, String warningType, BigDecimal actual,
|
|
|
+ BigDecimal min, BigDecimal max) {
|
|
|
+ if (smsUtil == null || devicePhoneFetchUtil == null) {
|
|
|
+ throw new IllegalStateException("一级告警短信依赖未注入");
|
|
|
+ }
|
|
|
+ List<String> phones = devicePhoneFetchUtil.getPhoneListByDeviceId(deviceCode);
|
|
|
+ if (phones == null || phones.isEmpty()) {
|
|
|
+ log.warn("一级告警短信跳过:未找到设备联系人手机号, deviceCode={}", deviceCode);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ JSONObject params = new JSONObject();
|
|
|
+ params.put("deviceNo", deviceCode);
|
|
|
+ params.put("alarmType", warningType);
|
|
|
+ params.put("location", "当前值:" + actual + ",正常范围:" + min + "~" + max);
|
|
|
+ Map<String, Boolean> result = smsUtil.sendBatchSms(phones, params.toJSONString());
|
|
|
+ long successCount = result == null ? 0 : result.values().stream().filter(Boolean.TRUE::equals).count();
|
|
|
+ log.info("一级压力告警短信发送完成: deviceCode={}, success={}/{}", deviceCode, successCount, phones.size());
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
// 工具方法:发送错误响应
|
|
|
private void sendErrorResponse(ChannelHandlerContext ctx, String msg) {
|
|
|
@@ -168,4 +272,4 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
|
|
|
logger.error("发送数据失败: {}", e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
+}
|