|
@@ -6,8 +6,12 @@ import cn.hutool.core.lang.UUID;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import com.aliyun.apache.hc.client5.http.utils.Hex;
|
|
import com.aliyun.apache.hc.client5.http.utils.Hex;
|
|
|
import com.zksy.api.domain.AlarmData;
|
|
import com.zksy.api.domain.AlarmData;
|
|
|
|
|
+import com.zksy.api.domain.WarningThreshold;
|
|
|
import com.zksy.api.service.AlarmDataService;
|
|
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.api.utils.AlarmLevelUtil;
|
|
|
|
|
+import com.zksy.api.utils.EarlyWarningLevelUtil;
|
|
|
import com.zksy.api.utils.SmsUtil;
|
|
import com.zksy.api.utils.SmsUtil;
|
|
|
import com.zksy.common.exception.InvalidMessageException;
|
|
import com.zksy.common.exception.InvalidMessageException;
|
|
|
import com.zksy.pressure.domain.FirefightingPressure;
|
|
import com.zksy.pressure.domain.FirefightingPressure;
|
|
@@ -43,10 +47,19 @@ import java.util.Map;
|
|
|
@Component
|
|
@Component
|
|
|
public class MessageHandler extends ChannelInboundHandlerAdapter {
|
|
public class MessageHandler extends ChannelInboundHandlerAdapter {
|
|
|
private static Logger logger = LoggerFactory.getLogger(MessageHandler.class);
|
|
private static Logger logger = LoggerFactory.getLogger(MessageHandler.class);
|
|
|
|
|
+
|
|
|
|
|
+ /** 压力值预警编码,阈值取自 app_user.warning_threshold(device_code + warning_code) */
|
|
|
|
|
+ private static final String PRESSURE_WARNING_CODE = "WARN-FIRE-PRESSURE";
|
|
|
|
|
+ private static final String PRESSURE_WARNING_TYPE = "消防压力监测";
|
|
|
|
|
+
|
|
|
private final FirefightingPressureService firefightingPressureService;
|
|
private final FirefightingPressureService firefightingPressureService;
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private AlarmDataService alarmDataService;
|
|
private AlarmDataService alarmDataService;
|
|
|
@Autowired
|
|
@Autowired
|
|
|
|
|
+ private WarningThresholdService warningThresholdService;
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private EarlyWarningAutoService earlyWarningAutoService;
|
|
|
|
|
+ @Autowired
|
|
|
private SmsUtil smsUtil;
|
|
private SmsUtil smsUtil;
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private DeviceContactService devicePhoneFetchUtil;
|
|
private DeviceContactService devicePhoneFetchUtil;
|
|
@@ -239,36 +252,73 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
|
|
|
logger.error("发送数据失败: {}", e.getMessage());
|
|
logger.error("发送数据失败: {}", e.getMessage());
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- //检查是否会触发短信预警
|
|
|
|
|
- public void checkIfSmsAlertNeeded(FirefightingPressure data) {
|
|
|
|
|
- String deviceCode = data.getTelemeteringStation();
|
|
|
|
|
- Integer status = alarmStatus(
|
|
|
|
|
- handleReportedAlarm(data, data.getD14(), "压力变幅报警", "WARN-PRESSURE-VARY"),
|
|
|
|
|
- handleReportedAlarm(data, data.getD13(), "压力下下限报警", "WARN-PRESSURE-LOW-LOW"),
|
|
|
|
|
- handleReportedAlarm(data, data.getD12(), "压力上上限报警", "WARN-PRESSURE-HIGH-HIGH"),
|
|
|
|
|
- handleReportedAlarm(data, data.getD11(), "压力下线报警", "WARN-PRESSURE-LOW"),
|
|
|
|
|
- handleReportedAlarm(data, data.getD10(), "压力上限报警", "WARN-PRESSURE-HIGH"));
|
|
|
|
|
- if (status != null) {
|
|
|
|
|
- updateDeviceAlarmStatus(deviceCode, status);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 压力值阈值评估:不再使用设备上报的 D14~D10 五种压力报警标志,
|
|
|
|
|
+ * 统一按 pressure_value 与 WARN-FIRE-PRESSURE 配置阈值判定。
|
|
|
|
|
+ */
|
|
|
|
|
+ public void checkIfSmsAlertNeeded(FirefightingPressure data) {
|
|
|
|
|
+ if (data == null) {
|
|
|
|
|
+ log.warn("消防压力数据为空,跳过阈值预警评估");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ String deviceCode = data.getTelemeteringStation() == null ? null : data.getTelemeteringStation().trim();
|
|
|
|
|
+ if (deviceCode == null || deviceCode.isEmpty()) {
|
|
|
|
|
+ log.warn("消防压力数据缺少遥测站地址,跳过阈值预警评估");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 评估失败(阈值未配置、压力值缺失)时返回 null,保留设备上一报警状态
|
|
|
|
|
+ Boolean status = evaluate(deviceCode, PRESSURE_WARNING_CODE + ":压力阈值评估",
|
|
|
|
|
+ () -> evaluatePressure(deviceCode, data));
|
|
|
|
|
+ if (status != null) {
|
|
|
|
|
+ updateDeviceAlarmStatus(deviceCode, status ? 1 : 0);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- private Boolean handleReportedAlarm(FirefightingPressure data, Integer flag, String type, String code) {
|
|
|
|
|
- if (flag == null || (flag != 0 && flag != 1)) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- if (flag == 1) {
|
|
|
|
|
- String deviceCode = data.getTelemeteringStation();
|
|
|
|
|
- BigDecimal actualValue = data.getPressureValue() == null
|
|
|
|
|
- ? null : BigDecimal.valueOf(data.getPressureValue());
|
|
|
|
|
- run(deviceCode, code + ":主动报警入库", () -> saveAlarmDataLevelOne(deviceCode, type, code, actualValue, type));
|
|
|
|
|
- run(deviceCode, code + ":短信通知", () -> sendMessage(deviceCode, type, data.getLongitude(), data.getLatitude()));
|
|
|
|
|
- } else {
|
|
|
|
|
- run(data.getTelemeteringStation(), code + ":告警恢复",
|
|
|
|
|
- () -> alarmDataService.recoverAlarm(data.getTelemeteringStation(), code));
|
|
|
|
|
- }
|
|
|
|
|
- return flag == 1;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 按阈值判定本次压力值:越限写报警并关闭预警;未越限则处理告警恢复并生成接近阈值预警。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return true 报警,false 正常/预警区
|
|
|
|
|
+ */
|
|
|
|
|
+ private boolean evaluatePressure(String deviceCode, FirefightingPressure data) {
|
|
|
|
|
+ if (warningThresholdService == null || alarmDataService == null || earlyWarningAutoService == null) {
|
|
|
|
|
+ throw new IllegalStateException("阈值预警依赖未注入");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (data.getPressureValue() == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("采集报文缺少压力值,无法判定报警状态");
|
|
|
|
|
+ }
|
|
|
|
|
+ BigDecimal actual = BigDecimal.valueOf(data.getPressureValue());
|
|
|
|
|
+ WarningThreshold threshold = warningThresholdService
|
|
|
|
|
+ .getWarningThresholdByDeviceAndCode(deviceCode, PRESSURE_WARNING_CODE);
|
|
|
|
|
+ if (threshold == null || (threshold.getMinValue() == null && threshold.getMaxValue() == null)) {
|
|
|
|
|
+ throw new IllegalStateException("消防压力阈值未配置,无法判定报警状态: deviceCode=" + deviceCode);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ BigDecimal min = threshold.getMinValue() == null ? null : BigDecimal.valueOf(threshold.getMinValue());
|
|
|
|
|
+ BigDecimal max = threshold.getMaxValue() == null ? null : BigDecimal.valueOf(threshold.getMaxValue());
|
|
|
|
|
+ // 单侧阈值时补齐等级/预警区计算基线:压力不会为负,仅上限以 0 作下限;仅下限以两倍下限作上限,
|
|
|
|
|
+ // 保证只配一侧时等级计算仍有可比区间(与可燃气体仅按上限判定的处理保持一致)。
|
|
|
|
|
+ BigDecimal levelMin = min != null ? min : BigDecimal.ZERO;
|
|
|
|
|
+ BigDecimal levelMax = max != null ? max : min.multiply(BigDecimal.valueOf(2));
|
|
|
|
|
+
|
|
|
|
|
+ if (EarlyWarningLevelUtil.isOverThreshold(actual, min, max)) {
|
|
|
|
|
+ int level = saveAlarmData(deviceCode, PRESSURE_WARNING_TYPE, PRESSURE_WARNING_CODE,
|
|
|
|
|
+ levelMin, levelMax, actual, "消防压力值越限报警");
|
|
|
|
|
+ run(deviceCode, PRESSURE_WARNING_CODE + ":关闭预警",
|
|
|
|
|
+ () -> earlyWarningAutoService.closeAutoWarning(deviceCode, PRESSURE_WARNING_CODE, "已达报警阈值,转报警处理"));
|
|
|
|
|
+ if (level == 1) {
|
|
|
|
|
+ run(deviceCode, PRESSURE_WARNING_CODE + ":一级告警短信",
|
|
|
|
|
+ () -> sendMessage(deviceCode, PRESSURE_WARNING_TYPE, data.getLongitude(), data.getLatitude()));
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ run(deviceCode, PRESSURE_WARNING_CODE + ":告警恢复",
|
|
|
|
|
+ () -> alarmDataService.recoverThresholdAlarm(deviceCode, PRESSURE_WARNING_CODE, levelMin, levelMax, actual));
|
|
|
|
|
+ run(deviceCode, PRESSURE_WARNING_CODE + ":生成预警",
|
|
|
|
|
+ () -> earlyWarningAutoService.checkAndGenerate(deviceCode, PRESSURE_WARNING_TYPE, PRESSURE_WARNING_CODE,
|
|
|
|
|
+ levelMin, levelMax, actual, null));
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
private int saveAlarmData(String deviceCode, String warningType, String warningCode,
|
|
private int saveAlarmData(String deviceCode, String warningType, String warningCode,
|
|
|
BigDecimal minValue, BigDecimal maxValue,
|
|
BigDecimal minValue, BigDecimal maxValue,
|
|
@@ -287,10 +337,11 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
|
|
|
BigDecimal deviationRatio = AlarmLevelUtil.calculateDeviationRatio(actualValue, minValue, maxValue);
|
|
BigDecimal deviationRatio = AlarmLevelUtil.calculateDeviationRatio(actualValue, minValue, maxValue);
|
|
|
alarmData.setAlarmLevel(alarmLevel);
|
|
alarmData.setAlarmLevel(alarmLevel);
|
|
|
alarmData.setDeviationRatio(deviationRatio);
|
|
alarmData.setDeviationRatio(deviationRatio);
|
|
|
|
|
+ alarmData.setAlarmContent(buildAlarmContent(warningType, actualValue, minValue, maxValue, remark));
|
|
|
|
|
|
|
|
alarmData.setAlarmStatus(0);
|
|
alarmData.setAlarmStatus(0);
|
|
|
alarmData.setCreateTime(LocalDateTime.now());
|
|
alarmData.setCreateTime(LocalDateTime.now());
|
|
|
- alarmData.setCreateTime(LocalDateTime.now());
|
|
|
|
|
|
|
+ alarmData.setUpdateTime(LocalDateTime.now());
|
|
|
boolean saved = run(deviceCode, warningCode + ":报警入库", () -> requireSaved(alarmDataService.saveAlarmData(alarmData)));
|
|
boolean saved = run(deviceCode, warningCode + ":报警入库", () -> requireSaved(alarmDataService.saveAlarmData(alarmData)));
|
|
|
|
|
|
|
|
if (alarmLevel == 1) {
|
|
if (alarmLevel == 1) {
|
|
@@ -304,24 +355,22 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
|
|
|
return alarmLevel;
|
|
return alarmLevel;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private void saveAlarmDataLevelOne(String deviceCode, String warningType, String warningCode,
|
|
|
|
|
- BigDecimal actualValue, String remark) {
|
|
|
|
|
- try {
|
|
|
|
|
- AlarmData alarmData = new AlarmData();
|
|
|
|
|
- alarmData.setDeviceCode(deviceCode);
|
|
|
|
|
-
|
|
|
|
|
- alarmData.setWarningCode(warningCode);
|
|
|
|
|
- alarmData.setActualValue(actualValue);
|
|
|
|
|
- alarmData.setAlarmLevel(1);
|
|
|
|
|
- alarmData.setAlarmStatus(0);
|
|
|
|
|
- alarmData.setCreateTime(LocalDateTime.now());
|
|
|
|
|
- alarmData.setCreateTime(LocalDateTime.now());
|
|
|
|
|
- boolean saved = run(deviceCode, warningCode + ":报警入库", () -> requireSaved(alarmDataService.saveAlarmData(alarmData)));
|
|
|
|
|
- if (saved) { log.info("设备主动告警入库(1级): deviceCode={}, warningCode={}", deviceCode, warningCode); }
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- log.error("保存告警数据失败", e);
|
|
|
|
|
|
|
+ /** 告警内容与其他采集服务保持一致:指标名称 + 越限方向,便于前端直接展示。 */
|
|
|
|
|
+ private String buildAlarmContent(String warningType, BigDecimal actualValue,
|
|
|
|
|
+ BigDecimal minValue, BigDecimal maxValue, String remark) {
|
|
|
|
|
+ String content = warningType == null || warningType.trim().isEmpty() ? remark : warningType.trim();
|
|
|
|
|
+ if (actualValue == null) {
|
|
|
|
|
+ return content;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (minValue != null && actualValue.compareTo(minValue) <= 0) {
|
|
|
|
|
+ return content + "达到或低于下限";
|
|
|
}
|
|
}
|
|
|
|
|
+ if (maxValue != null && actualValue.compareTo(maxValue) >= 0) {
|
|
|
|
|
+ return content + "达到或超过上限";
|
|
|
|
|
+ }
|
|
|
|
|
+ return content;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
//构造发送短信参数&&发送短信
|
|
//构造发送短信参数&&发送短信
|
|
|
public void sendMessage(String deviceNo,String alarmType,BigDecimal lng,BigDecimal lat){
|
|
public void sendMessage(String deviceNo,String alarmType,BigDecimal lng,BigDecimal lat){
|
|
|
// 构造短信参数
|
|
// 构造短信参数
|
|
@@ -329,7 +378,9 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
|
|
|
params.put("deviceNo", deviceNo);
|
|
params.put("deviceNo", deviceNo);
|
|
|
params.put("alarmType", alarmType);
|
|
params.put("alarmType", alarmType);
|
|
|
// 经纬度处理(为执行信息处理)
|
|
// 经纬度处理(为执行信息处理)
|
|
|
- params.put("location", String.format("经纬度:%.6f,%.8f", lng, lat));
|
|
|
|
|
|
|
+ params.put("location", (lng != null && lat != null)
|
|
|
|
|
+ ? String.format("经纬度:%.6f,%.8f", lng, lat)
|
|
|
|
|
+ : "位置未知");
|
|
|
List<String> phoneListByDeviceId = devicePhoneFetchUtil.getPhoneListByDeviceCode(deviceNo);
|
|
List<String> phoneListByDeviceId = devicePhoneFetchUtil.getPhoneListByDeviceCode(deviceNo);
|
|
|
Map<String, Boolean> sendResults = smsUtil.sendBatchSms(phoneListByDeviceId, params.toJSONString());
|
|
Map<String, Boolean> sendResults = smsUtil.sendBatchSms(phoneListByDeviceId, params.toJSONString());
|
|
|
log.info("发送信息完成");
|
|
log.info("发送信息完成");
|