|
@@ -0,0 +1,197 @@
|
|
|
+package com.persagy.netty.collect;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.scheduling.annotation.Scheduled;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.googlecode.aviator.AviatorEvaluator;
|
|
|
+import com.googlecode.aviator.Expression;
|
|
|
+import com.persagy.cache.CurrentDataCache;
|
|
|
+import com.persagy.entity.ReturnAlarmDefine;
|
|
|
+import com.persagy.init.InitReturnAlarmConfig;
|
|
|
+
|
|
|
+import cn.hutool.core.date.DateUnit;
|
|
|
+import cn.hutool.core.date.DateUtil;
|
|
|
+import cn.hutool.core.util.BooleanUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 控返不一致报警
|
|
|
+ *
|
|
|
+ * @version 1.0.0
|
|
|
+ * @company persagy
|
|
|
+ * @author zhangqiankun
|
|
|
+ * @date 2021年12月9日 上午10:09:00
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Component
|
|
|
+public class ReturnDifferentAlarm {
|
|
|
+
|
|
|
+ /** 从左到右:项目,时间,表号,功能号,值 */
|
|
|
+ public static final String COMMAND = "({};1;report;{};123;{};1;{};{})";
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IotCollectNettyClient iotCollectNettyClient;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 启动后 延时 10s触发,以5s的间隔时间触发控返不一致的信息点实时数据写入
|
|
|
+ *
|
|
|
+ * 每5s的间隔,代表着人为抑制了5s的持续性报警
|
|
|
+ *
|
|
|
+ * @throws Exception
|
|
|
+ * @date 2021年12月9日 下午8:11:36
|
|
|
+ */
|
|
|
+ @Scheduled(initialDelay = 10 * 1000, fixedRate = 5 * 1000)
|
|
|
+ public void returnDifferentAlarm() throws Exception {
|
|
|
+ if (InitReturnAlarmConfig.alarmCode2Alarm.size() == 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String now = DateUtil.date().toString("yyyyMMddHHmmss");
|
|
|
+
|
|
|
+ // 根据数据,拼接出指令信息,发送入库
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ Set<String> alarmCodes = InitReturnAlarmConfig.alarmCode2IsAlarm.keySet();
|
|
|
+ for (String alarmCode : alarmCodes) {
|
|
|
+ Integer isAlarm = InitReturnAlarmConfig.alarmCode2IsAlarm.get(alarmCode);
|
|
|
+ if (isAlarm != null && isAlarm != -1) {
|
|
|
+ ReturnAlarmDefine alarmDefine = InitReturnAlarmConfig.alarmCode2Alarm.get(alarmCode);
|
|
|
+ // 0-正常,1-报警
|
|
|
+ BigDecimal dataValue = isAlarm == 1 ? new BigDecimal("1") : new BigDecimal("0");
|
|
|
+ String command = StrUtil.format(COMMAND, alarmDefine.getBuilding(), now, alarmDefine.getAlarmInfo().getMeter(), alarmDefine.getAlarmInfo().getFuncid(), dataValue.toPlainString());
|
|
|
+ sb.append(command).append("&");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ String command = sb.toString();
|
|
|
+ if (command != null && command.endsWith("&")) {
|
|
|
+ command = command.substring(0, command.length() - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 数据上报
|
|
|
+ if (StrUtil.isNotBlank(command)) {
|
|
|
+ this.iotCollectNettyClient.sendMessage(command);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * {"data":"20211209150133;10346;903;1.0","type":"pointset"}
|
|
|
+ * {"data":"20211209155421;CW20379;902;0.0","type":"iot"}
|
|
|
+ *
|
|
|
+ * @param message
|
|
|
+ * @date 2021年12月9日 下午3:54:35
|
|
|
+ */
|
|
|
+ public void putMsg(String message) {
|
|
|
+ if (InitReturnAlarmConfig.infoCode2Alarm.size() == 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Set<String> infoCodes = InitReturnAlarmConfig.infoCode2Alarm.keySet();
|
|
|
+ Set<String> returnCodes = InitReturnAlarmConfig.returnCode2Alarm.keySet();
|
|
|
+ try {
|
|
|
+ // 解析此条iot数据详情
|
|
|
+ JSONObject messageObj = JSONObject.parseObject(message);
|
|
|
+ String data = messageObj.getString("data");
|
|
|
+ String[] split = data.split(";");
|
|
|
+ String dataTime = split[0];
|
|
|
+ String meter = split[1];
|
|
|
+ String funcid = split[2];
|
|
|
+ BigDecimal dataValue = new BigDecimal(split[3]);
|
|
|
+
|
|
|
+ // 根据表号和功能号判断是否需要进行控返不一致的报警验证
|
|
|
+ ReturnAlarmDefine returnAlarmDefine = null;
|
|
|
+ String code = InitReturnAlarmConfig.meterFunid2Code.get(CurrentDataCache.getKey(meter, funcid));
|
|
|
+ if (infoCodes.contains(code)) {
|
|
|
+ returnAlarmDefine = InitReturnAlarmConfig.infoCode2Alarm.get(code);
|
|
|
+ // 防止频繁的控制,导致无法进入2分钟后的逻辑判断,这里如果值相同的话,取上一次的上报时间使用
|
|
|
+ BigDecimal oldValue = returnAlarmDefine.getSetInfo().getDataValue();
|
|
|
+ if (oldValue == null || dataValue.compareTo(oldValue) != 0) {
|
|
|
+ returnAlarmDefine.getSetInfo().setDataTime(dataTime);
|
|
|
+ returnAlarmDefine.getSetInfo().setDataValue(dataValue);
|
|
|
+ }
|
|
|
+ } else if (returnCodes.contains(code)) {
|
|
|
+ returnAlarmDefine = InitReturnAlarmConfig.returnCode2Alarm.get(code);
|
|
|
+ returnAlarmDefine.getReturnInfo().setDataTime(dataTime);
|
|
|
+ returnAlarmDefine.getReturnInfo().setDataValue(dataValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (returnAlarmDefine == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 正常数据下,判断此条数据,是否会影响控返不一致的报警信息点
|
|
|
+ this.returnAlarmHandler(returnAlarmDefine);
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ log.error("控返不一致的报警,逻辑处理异常", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断此条数据,是否会影响控返不一致的报警信息点的值
|
|
|
+ *
|
|
|
+ * 因为在服务启动的时候,iot-project会推送一次全量数据,所以在数据正常的情况下,一定有值
|
|
|
+ *
|
|
|
+ * 是否报警说明:
|
|
|
+ * 1.新的指令和旧的iot数据,无论是否满足报警定义都不做任何处理,等待最新的iot数据上传后在做判断
|
|
|
+ *
|
|
|
+ * 2.旧的指令和新的iot数据
|
|
|
+ * 不是2分钟后上传上来的iot数据,这里不做任何处理
|
|
|
+ * 满足报警条件,产生报警,不满足,恢复
|
|
|
+ *
|
|
|
+ * @param returnAlarmDefine
|
|
|
+ * @date 2021年12月9日 下午5:29:37
|
|
|
+ */
|
|
|
+ private void returnAlarmHandler(ReturnAlarmDefine returnAlarmDefine) {
|
|
|
+ String setTimeStr = returnAlarmDefine.getSetInfo().getDataTime();
|
|
|
+ String returnTimeStr = returnAlarmDefine.getReturnInfo().getDataTime();
|
|
|
+ if (!StrUtil.isAllNotBlank(setTimeStr, returnTimeStr)) {
|
|
|
+ InitReturnAlarmConfig.alarmCode2IsAlarm.put(returnAlarmDefine.getAlarmInfo().getInfoCode(), -1);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ long setTime = Long.valueOf(setTimeStr);
|
|
|
+ long returnTime = Long.valueOf(returnTimeStr);
|
|
|
+ if (setTime > returnTime) {
|
|
|
+ // 新的指令和旧的iot数据,无论是否满足报警定义都不做任何处理,等待最新的iot数据上传后在做判断
|
|
|
+ InitReturnAlarmConfig.alarmCode2IsAlarm.put(returnAlarmDefine.getAlarmInfo().getInfoCode(), -1);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 先判断一下这两条数据之间是否时差超过2分钟
|
|
|
+ long minute = DateUtil.between(DateUtil.parse(setTimeStr, "yyyyMMddHHmmss"), DateUtil.parse(returnTimeStr, "yyyyMMddHHmmss"), DateUnit.MINUTE, true);
|
|
|
+ boolean newest = minute > 1;
|
|
|
+
|
|
|
+ // 报警条件初次验证
|
|
|
+ String alarmCondition = returnAlarmDefine.getAlarmCondition();
|
|
|
+ Expression triggerExp = AviatorEvaluator.compile(alarmCondition, true);
|
|
|
+
|
|
|
+ // 判断实际是否应该产生报警
|
|
|
+ String infoCode = returnAlarmDefine.getSetInfo().getInfoCode();
|
|
|
+ String returnCode = returnAlarmDefine.getReturnInfo().getInfoCode();
|
|
|
+ Map<String, Object> paramMap = new HashMap<String, Object>();
|
|
|
+ paramMap.put(infoCode, returnAlarmDefine.getSetInfo().getDataValue());
|
|
|
+ paramMap.put(returnCode, returnAlarmDefine.getReturnInfo().getDataValue());
|
|
|
+ Boolean triggerResult = (Boolean) triggerExp.execute(paramMap);
|
|
|
+
|
|
|
+ // 旧的指令和新的iot数据
|
|
|
+ if (!newest) {
|
|
|
+ // 不是2分钟后上传上来的iot数据,这里不做任何处理
|
|
|
+ InitReturnAlarmConfig.alarmCode2IsAlarm.put(returnAlarmDefine.getAlarmInfo().getInfoCode(), -1);
|
|
|
+ } else if (BooleanUtil.isTrue(triggerResult)) {
|
|
|
+ // 满足报警条件,产生报警
|
|
|
+ InitReturnAlarmConfig.alarmCode2IsAlarm.put(returnAlarmDefine.getAlarmInfo().getInfoCode(), 1);
|
|
|
+ } else if (BooleanUtil.isFalse(triggerResult)) {
|
|
|
+ // 不满足报警条件,如果已报警,则需恢复
|
|
|
+ InitReturnAlarmConfig.alarmCode2IsAlarm.put(returnAlarmDefine.getAlarmInfo().getInfoCode(), 0);
|
|
|
+ } else {
|
|
|
+ InitReturnAlarmConfig.alarmCode2IsAlarm.put(returnAlarmDefine.getAlarmInfo().getInfoCode(), -1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|