|
|
@@ -0,0 +1,451 @@
|
|
|
+package com.yunfeiyun.agmp.iots.warn.service;
|
|
|
+
|
|
|
+import com.alibaba.fastjson2.JSONArray;
|
|
|
+import com.alibaba.fastjson2.JSONObject;
|
|
|
+import com.yunfeiyun.agmp.common.utils.StringUtils;
|
|
|
+import com.yunfeiyun.agmp.iot.common.constant.device.ElementFormatUtil;
|
|
|
+import com.yunfeiyun.agmp.iot.common.constant.devicetype.IotDeviceDictEnum;
|
|
|
+import com.yunfeiyun.agmp.iot.common.constant.devicetype.IotDeviceTypeLv1Enum;
|
|
|
+import com.yunfeiyun.agmp.iot.common.domain.IotDevice;
|
|
|
+import com.yunfeiyun.agmp.iot.common.domain.IotDevicefactor;
|
|
|
+import com.yunfeiyun.agmp.iot.common.domain.IotWarnconfig;
|
|
|
+import com.yunfeiyun.agmp.iot.common.enums.EnumWarnRuleOp;
|
|
|
+import com.yunfeiyun.agmp.iots.service.IIotDevicefactorService;
|
|
|
+import com.yunfeiyun.agmp.iots.warn.model.WarnConfigInfo;
|
|
|
+import com.yunfeiyun.agmp.iots.warn.model.WarnQxSqInfoDto;
|
|
|
+import com.yunfeiyun.agmp.iots.warn.model.WarnResult;
|
|
|
+import com.yunfeiyun.agmp.iots.warn.model.WarnStatusDto;
|
|
|
+import com.yunfeiyun.agmp.iots.warn.util.CompareUtil;
|
|
|
+import com.yunfeiyun.agmp.iots.warn.util.WarnMessageBuilderUtil;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.BeanUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.util.*;
|
|
|
+import java.util.concurrent.CompletableFuture;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 预警核心服务类
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class WarnService {
|
|
|
+
|
|
|
+ @Resource(name = "threadPoolTaskExecutor")
|
|
|
+ private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ReCountService reCountService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IotWarnBussinessService iotWarnBussinessService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IIotDevicefactorService iotDevicefactorService;
|
|
|
+
|
|
|
+ private Map<String, IotDevicefactor> getDevicefactorMap(String devBid){
|
|
|
+ IotDevicefactor param = new IotDevicefactor();
|
|
|
+ param.setDevBid(devBid);
|
|
|
+ List<IotDevicefactor> factorList = iotDevicefactorService.selectIotDevicefactorList(param);
|
|
|
+ Map<String, IotDevicefactor> factorMap = new HashMap<>();
|
|
|
+ for(IotDevicefactor factor : factorList){
|
|
|
+ String dfCode = factor.getDfCode();
|
|
|
+ String dfAddress = factor.getDfAddress();
|
|
|
+ String key = dfAddress + dfCode;
|
|
|
+ factorMap.put(key, factor);
|
|
|
+ }
|
|
|
+ return factorMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 统一处理上报数据
|
|
|
+ *
|
|
|
+ * @param data 上报的数据对象
|
|
|
+ * param ext 上报的原始数据
|
|
|
+ */
|
|
|
+ public void processWarningReportData(IotDevice iotDevice, JSONObject data) {
|
|
|
+ //转异步处理
|
|
|
+ processWarningReportDataSyn(iotDevice, data);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 统一处理上报数据:异步处理
|
|
|
+ *
|
|
|
+ * @param data 上报的数据对象
|
|
|
+ */
|
|
|
+ private void processWarningReportDataSyn(IotDevice iotDevice, JSONObject data) {
|
|
|
+ String devBid = iotDevice.getDevBid();
|
|
|
+ String devtypeBid = iotDevice.getDevtypeBid();
|
|
|
+ String devClass = IotDeviceDictEnum.getLv1CodeByCode(devtypeBid);
|
|
|
+ IotDeviceTypeLv1Enum iotDeviceTypeLv1Enum = IotDeviceTypeLv1Enum.findEnumByCode(devClass);
|
|
|
+ if (iotDeviceTypeLv1Enum == null) {
|
|
|
+ log.error("[设备告警] 设备大类不存在,devBid:{}, devtypeBid:{}", devBid, devtypeBid);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ CompletableFuture.runAsync(() -> {
|
|
|
+ // 获取该设备有哪些告警配置
|
|
|
+ WarnConfigInfo warnConfigInfo = new WarnConfigInfo();
|
|
|
+ warnConfigInfo.setTid(iotDevice.getTid());
|
|
|
+ warnConfigInfo.setDevBid(devBid);
|
|
|
+
|
|
|
+ Map<String, List<WarnConfigInfo>> configMap = iotWarnBussinessService.selectIotWarnConfigInfoMap(warnConfigInfo);
|
|
|
+ if (configMap == null || configMap.size() == 0) {
|
|
|
+ log.info("[设备告警] 该设备没有配置,devBid:{}", devBid);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, IotDevicefactor> factorMap = getDevicefactorMap(devBid);
|
|
|
+
|
|
|
+ //配置一个个检查
|
|
|
+ for(Map.Entry<String, List<WarnConfigInfo>> entry : configMap.entrySet()) {
|
|
|
+ List<WarnConfigInfo> configList = entry.getValue();
|
|
|
+ WarnResult warnResult = null;
|
|
|
+
|
|
|
+ WarnQxSqInfoDto warnQxSqInfoDto = new WarnQxSqInfoDto();
|
|
|
+ warnQxSqInfoDto.setIotDevice(iotDevice);
|
|
|
+ warnQxSqInfoDto.setConfigList(configList);
|
|
|
+ warnQxSqInfoDto.setJsonObject(data);
|
|
|
+ warnQxSqInfoDto.setFactorMap(factorMap);
|
|
|
+ warnQxSqInfoDto.setIotDeviceTypeLv1Enum(iotDeviceTypeLv1Enum);
|
|
|
+
|
|
|
+ try{
|
|
|
+ switch (iotDeviceTypeLv1Enum) {
|
|
|
+ case QXZ: {
|
|
|
+ warnResult = comparableQxzReportData(warnQxSqInfoDto);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+// case SQZ: {
|
|
|
+// warnResult = comparableSqzReportData(warnQxSqInfoDto);
|
|
|
+// break;
|
|
|
+// }
|
|
|
+// case GSSQ: {
|
|
|
+// warnResult = comparableGssqReportData(warnQxSqInfoDto);
|
|
|
+// break;
|
|
|
+// }
|
|
|
+// case "病虫害": {
|
|
|
+// warnResult = comparableBchReportData(devBid, config, data);
|
|
|
+// break;
|
|
|
+// }
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("[设备告警] 设备上报数据 异常,devBid:{}, config:{}, data:{}", devBid, configList, data, e);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (warnResult != null) {
|
|
|
+ handleWarnRecord(warnResult);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, threadPoolTaskExecutor);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 基于预警结果统一处理入库操作
|
|
|
+ * 注:不用管是否预警,直接调用,内部有针对不触发预警的逻辑
|
|
|
+ *
|
|
|
+ * @param warnResult
|
|
|
+ */
|
|
|
+ void handleWarnRecord(WarnResult warnResult) {
|
|
|
+ reCountService.handlerMessage(warnResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据设备id获取策略,可能有多个
|
|
|
+ *
|
|
|
+ * @param devBid
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ List<Object> getConfigByDevId(String devBid) {
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, String> getQxzCurrentValueMap(IotDevice iotDevice, JSONArray jsonArray) {
|
|
|
+ String devCode = iotDevice.getDevCode();
|
|
|
+ BigDecimal errorValue = new BigDecimal("-99");
|
|
|
+ Map<String, String> currentValueMap = new HashMap<>();
|
|
|
+ for(Object obj : jsonArray) {
|
|
|
+ JSONObject ob;
|
|
|
+ try {
|
|
|
+ ob = JSONObject.from(obj);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[设备告警] 数据类型转换异常,devCode:{}, obj:{}", devCode, obj);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ String eNum = ob.getString("eNum");
|
|
|
+ String eName = ob.getString("eName");
|
|
|
+ String eKey = ob.getString("eKey");
|
|
|
+ String address = ElementFormatUtil.getAddress(eName, eKey);
|
|
|
+ String key = address + eNum;
|
|
|
+
|
|
|
+ BigDecimal eValue = null;
|
|
|
+ try {
|
|
|
+ eValue = ob.getBigDecimal("eValue");
|
|
|
+ if (eValue.compareTo(errorValue) <= 0) {
|
|
|
+ throw new Exception();
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[设备告警] 数据类型转换异常,devCode:{}, eNum:{}, eName:{}, eKey:{}", devCode, eNum, eName, eKey);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ currentValueMap.put(key, String.valueOf(eValue));
|
|
|
+ }
|
|
|
+ return currentValueMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getDisplayName(String key, Map<String, IotDevicefactor> factorMap){
|
|
|
+ String wiName = "";
|
|
|
+ if(factorMap.containsKey(key)){
|
|
|
+ IotDevicefactor iotDevicefactor = factorMap.get(key);
|
|
|
+ // 如果禁用,直接返回null,不进行预警判断
|
|
|
+ if(Objects.equals(iotDevicefactor.getDfDisable(), "1")){
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String displayname = iotDevicefactor.getDfDisplayname();
|
|
|
+ if(StringUtils.isNotEmpty(displayname)){
|
|
|
+ wiName = displayname;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return wiName;
|
|
|
+ }
|
|
|
+
|
|
|
+ private WarnStatusDto getQxzWarnStatusDto(WarnConfigInfo config, WarnQxSqInfoDto warnQxSqInfoDto) {
|
|
|
+ IotDevice iotDevice = warnQxSqInfoDto.getIotDevice();
|
|
|
+ String devCode = iotDevice.getDevCode();
|
|
|
+ Map<String, String> currentValueMap = warnQxSqInfoDto.getCurrentValueMap();
|
|
|
+ Map<String, IotDevicefactor> factorMap = warnQxSqInfoDto.getFactorMap();
|
|
|
+ IotDeviceTypeLv1Enum iotDeviceTypeLv1Enum = warnQxSqInfoDto.getIotDeviceTypeLv1Enum();
|
|
|
+
|
|
|
+ String targetValue = config.getWiValue();
|
|
|
+ String wiAddress = config.getWiAddress();
|
|
|
+ String wiCode = config.getWiCode();
|
|
|
+ String wcStatus = config.getWcStatus();
|
|
|
+ String wiStatus = config.getWiStatus();
|
|
|
+ String key = wiAddress + wiCode;
|
|
|
+ String expression = config.getWiExpression();
|
|
|
+
|
|
|
+ // 如果配置禁用,或者要素禁用,直接返回null,不进行预警判断
|
|
|
+ boolean status = "0".equals(wcStatus) && "0".equals(wiStatus);
|
|
|
+ if(!status){
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果上报数据中没有预警配置中的要素,直接返回null,不进行预警判断
|
|
|
+ if (!currentValueMap.containsKey(key)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果没有配置预警条件和值,直接返回null,不进行预警判断
|
|
|
+ if(StringUtils.isEmpty(expression) || StringUtils.isEmpty(targetValue)){
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ String wiName = config.getWiName();
|
|
|
+ // 如果禁用,直接返回null,不进行预警判断
|
|
|
+ String displayname = getDisplayName(key, factorMap);
|
|
|
+ if(displayname == null){
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(StringUtils.isNotEmpty(displayname)){
|
|
|
+ wiName = displayname;
|
|
|
+ }
|
|
|
+ String wiUnit = config.getWiUnit();
|
|
|
+ String currentValue = currentValueMap.get(key);
|
|
|
+
|
|
|
+ EnumWarnRuleOp warnRuleOp = EnumWarnRuleOp.findEnumByCode(expression);
|
|
|
+ if(warnRuleOp == null){
|
|
|
+ log.error("[设备告警] 表达式不正确,devCode:{}, wiAddress:{}, config:{}", devCode, wiAddress, config);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ String ruleName = warnRuleOp.getName();
|
|
|
+ boolean tempSuccess = CompareUtil.comp(currentValue, expression, targetValue);
|
|
|
+
|
|
|
+ WarnStatusDto warnStatusDto = new WarnStatusDto();
|
|
|
+ warnStatusDto.setDevType(iotDeviceTypeLv1Enum.getName());
|
|
|
+ warnStatusDto.setDevCode(devCode);
|
|
|
+ warnStatusDto.setName(wiName);
|
|
|
+ warnStatusDto.setValue(currentValue);
|
|
|
+ warnStatusDto.setUnit(wiUnit);
|
|
|
+ warnStatusDto.setOpt(ruleName);
|
|
|
+ warnStatusDto.setIndicatorValue(targetValue);
|
|
|
+ warnStatusDto.setWarn(tempSuccess);
|
|
|
+ return warnStatusDto;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 【气象站】比较该设备上报的任一要素否达到预警条件
|
|
|
+ * @param warnResult
|
|
|
+ * @param configList
|
|
|
+ * @param factorMap
|
|
|
+ * @param currentValueMap
|
|
|
+ * @param devCode
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public WarnResult comparableQxzSingleIndicator(WarnResult warnResult, WarnQxSqInfoDto warnQxSqInfoDto) {
|
|
|
+ List<WarnConfigInfo> configList = warnQxSqInfoDto.getConfigList();
|
|
|
+ WarnStatusDto warnStatusDto = null;
|
|
|
+ for (WarnConfigInfo config : configList) {
|
|
|
+ warnStatusDto = getQxzWarnStatusDto(config, warnQxSqInfoDto);
|
|
|
+ // 如果没有预警配置,直接返回null,不进行预警判断
|
|
|
+ if(warnStatusDto == null){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(warnStatusDto.isWarn()){
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(warnStatusDto != null && warnStatusDto.isWarn()){
|
|
|
+ String message = WarnMessageBuilderUtil.buildQxzWarningMessage(
|
|
|
+ warnStatusDto.getDevType(),
|
|
|
+ warnStatusDto.getDevCode(),
|
|
|
+ warnStatusDto.getName(),
|
|
|
+ warnStatusDto.getValue(),
|
|
|
+ warnStatusDto.getUnit(),
|
|
|
+ warnStatusDto.getOpt(),
|
|
|
+ warnStatusDto.getIndicatorValue()
|
|
|
+ );
|
|
|
+ warnResult.setTriggered(true);
|
|
|
+ warnResult.setMessage(message);
|
|
|
+ }
|
|
|
+
|
|
|
+ return warnResult;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理多个指标是否同时满足预警条件。
|
|
|
+ *
|
|
|
+ * @param devId 设备ID
|
|
|
+ * @param config 告警规则
|
|
|
+ * @param jsonObject 上报的数据
|
|
|
+ * @return WarnResult 包含是否触发告警的信息
|
|
|
+ */
|
|
|
+ public WarnResult comparableQxzMultipleIndicators(WarnResult warnResult, WarnQxSqInfoDto warnQxSqInfoDto) {
|
|
|
+ List<WarnConfigInfo> configList = warnQxSqInfoDto.getConfigList();
|
|
|
+ WarnStatusDto warnStatusDto = null;
|
|
|
+ List<WarnStatusDto> warnStatusDtos = new ArrayList<>();
|
|
|
+ for (WarnConfigInfo config : configList) {
|
|
|
+ warnStatusDto = getQxzWarnStatusDto(config, warnQxSqInfoDto);
|
|
|
+ // 如果没有预警配置,直接返回null,不进行预警判断
|
|
|
+ if(warnStatusDto == null){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(!warnStatusDto.isWarn()){
|
|
|
+ return warnResult;
|
|
|
+ }
|
|
|
+ warnStatusDtos.add(warnStatusDto);
|
|
|
+ }
|
|
|
+ if(warnStatusDtos.isEmpty()){
|
|
|
+ return warnResult;
|
|
|
+ }
|
|
|
+
|
|
|
+ StringBuilder messageBuilder = new StringBuilder();
|
|
|
+ for(int i = 0; i < warnStatusDtos.size(); i++){
|
|
|
+ warnStatusDto = warnStatusDtos.get(i);
|
|
|
+ String devType = null;
|
|
|
+ String dCode = null;
|
|
|
+ if(i == 0){
|
|
|
+ devType = warnStatusDto.getDevType();
|
|
|
+ dCode = warnStatusDto.getDevCode();
|
|
|
+ }
|
|
|
+ String message = WarnMessageBuilderUtil.buildQxzWarningMessage(
|
|
|
+ devType,
|
|
|
+ dCode,
|
|
|
+ warnStatusDto.getName(),
|
|
|
+ warnStatusDto.getValue(),
|
|
|
+ warnStatusDto.getUnit(),
|
|
|
+ warnStatusDto.getOpt(),
|
|
|
+ warnStatusDto.getIndicatorValue()
|
|
|
+ );
|
|
|
+ messageBuilder.append(message).append("\n");
|
|
|
+ }
|
|
|
+ warnResult.setTriggered(true);
|
|
|
+ warnResult.setMessage(messageBuilder.toString());
|
|
|
+ return warnResult;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 【气象站】比较该设备上报的要素和配置是否达到预警条件
|
|
|
+ *
|
|
|
+ * @param devBid 设备id
|
|
|
+ * @param config 对应的配置
|
|
|
+ * @param jsonObject 上报的数据
|
|
|
+ */
|
|
|
+ private WarnResult comparableQxzReportData(WarnQxSqInfoDto warnQxSqInfoDto) {
|
|
|
+ IotDevice iotDevice = warnQxSqInfoDto.getIotDevice();
|
|
|
+ List<WarnConfigInfo> configList = warnQxSqInfoDto.getConfigList();
|
|
|
+ JSONObject jsonObject = warnQxSqInfoDto.getJsonObject();
|
|
|
+ JSONArray jsonArray = jsonObject.getJSONArray("data");
|
|
|
+ Map<String, String> currentValueMap = getQxzCurrentValueMap(iotDevice, jsonArray);
|
|
|
+
|
|
|
+ warnQxSqInfoDto.setCurrentValueMap(currentValueMap);
|
|
|
+
|
|
|
+ String devBid = iotDevice.getDevBid();
|
|
|
+ WarnConfigInfo configInfo = configList.get(0);
|
|
|
+ String wcCondition = configInfo.getWcCondition();
|
|
|
+
|
|
|
+ IotWarnconfig iotWarnconfig = new IotWarnconfig();
|
|
|
+ BeanUtils.copyProperties(configInfo, iotWarnconfig);
|
|
|
+
|
|
|
+ WarnResult warnResult = new WarnResult();
|
|
|
+ warnResult.setMessageId(warnResult.getUUId());
|
|
|
+ warnResult.setDevId(devBid);
|
|
|
+ warnResult.setTid(iotDevice.getTid());
|
|
|
+ warnResult.setConfigId(configInfo.getWcBid());
|
|
|
+ warnResult.setReportData(jsonObject.toJSONString());
|
|
|
+ warnResult.setTargetReCount(configInfo.getWcRepeatnum());
|
|
|
+ warnResult.setDevtypeBid(iotDevice.getDevtypeBid());
|
|
|
+ warnResult.setConfig(iotWarnconfig);
|
|
|
+ warnResult.setTriggered(false);
|
|
|
+
|
|
|
+ if("0".equals(wcCondition)){
|
|
|
+ warnResult = comparableQxzSingleIndicator(warnResult, warnQxSqInfoDto);
|
|
|
+ } else {
|
|
|
+ warnResult = comparableQxzMultipleIndicators(warnResult, warnQxSqInfoDto);
|
|
|
+ }
|
|
|
+ return warnResult;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 【墒情站】比较该设备上报的要素和配置是否达到预警条件
|
|
|
+ *
|
|
|
+ */
|
|
|
+ WarnResult comparableSqzReportData(WarnQxSqInfoDto warnQxSqInfoDto) {
|
|
|
+ return comparableQxzReportData(warnQxSqInfoDto);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 【管式墒情】比较该设备上报的要素和配置是否达到预警条件
|
|
|
+ *
|
|
|
+ */
|
|
|
+ WarnResult comparableGssqReportData(WarnQxSqInfoDto warnQxSqInfoDto) {
|
|
|
+ return comparableQxzReportData(warnQxSqInfoDto);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 【病虫害】比较该设备上报的要素和配置是否达到预警条件,暂不实现,预留
|
|
|
+ *
|
|
|
+ * @param devId
|
|
|
+ * @param config
|
|
|
+ * @param jsonObject
|
|
|
+ */
|
|
|
+ WarnResult comparableBchReportData(String devId, Object config, JSONObject jsonObject) {
|
|
|
+ return null;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|