package com.iailab.module.data.point.collection.handler; import com.alibaba.fastjson.JSON; import com.iailab.framework.common.util.string.StrUtils; import com.iailab.module.data.common.enums.CommonConstant; import com.iailab.module.data.common.enums.DataTypeEnum; import com.iailab.module.data.common.enums.JsErrorCode; import com.iailab.module.data.common.utils.JavaScriptHandler; import com.iailab.module.data.enums.DataPointFreqEnum; import com.iailab.module.data.point.collection.PointCollector; import com.iailab.module.data.point.collection.utils.GenInfluxPointValueUtils; import com.iailab.module.data.point.dto.DaPointDTO; import com.iailab.module.data.point.service.DaPointService; import com.iailab.module.data.influxdb.pojo.InfluxPointValuePOJO; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import javax.annotation.Resource; import javax.validation.constraints.Max; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.math.BigDecimal; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; /** * 计算点处理 * * @author PanZhibao * @Description * @createTime 2023年05月03日 17:40:00 */ @Slf4j @Component public class CalculateHandle { @Resource private DaPointService daPointService; @Resource private MeasureHandle measureHandle; @Resource private ConstantHandle constantHandle; @Resource private CumulateHandle cumulateHandle; @Resource private ExtremalHandle extremalHandle; @Resource private JavaScriptHandler javaScriptHandler; @Autowired private RedisTemplate redisTemplate; public final static String regex = "[+\\-\\*/()\\&\\|\\>\\<]"; private final static String POINT_PREFIX = "C"; private final static String PENDING_FLAG = "pending"; private final static int MAX_RECURSION = 10; public List handle(Date collectTime, List dtos, Map dataMap, List listGood, List listBad) { List result = new ArrayList<>(); try { log.info("计算点处理开始"); if (CollectionUtils.isEmpty(dtos)) { return result; } Map pendingMap = new HashMap<>(); log.info(JSON.toJSONString(listBad)); dtos.forEach(dto -> { try { Object rawValue = singleCompute(dto, dataMap, listGood, listBad); if (PENDING_FLAG.equals(rawValue.toString())) { pendingMap.put(dto.getPointNo(), dto); } else { BigDecimal coefficient = dto.getUnittransfactor() == null ? BigDecimal.ONE : dto.getUnittransfactor(); BigDecimal calValue = new BigDecimal(rawValue.toString()).multiply(coefficient); if (dto.getMaxValue() != null && calValue.compareTo(dto.getMaxValue()) > 0) { calValue = dto.getMaxValue(); } else if (dto.getMinValue() != null && calValue.compareTo(dto.getMinValue()) < 0) { calValue = dto.getMinValue(); } dataMap.put(dto.getPointNo(), calValue); InfluxPointValuePOJO pojo = GenInfluxPointValueUtils.getByPoint(dto, calValue); pojo.setTimestamp(GenInfluxPointValueUtils.getByMin(collectTime, DataPointFreqEnum.getEumByCode(dto.getMinfreqid()))); result.add(pojo); } } catch (Exception ex) { ex.printStackTrace(); log.info("计算点异常!PointNo=" + dto.getPointNo()); } }); Map valueResult = new HashMap<>(); handPending(pendingMap, dataMap, listGood, listBad, valueResult, 1); log.info("valueResult size=" + valueResult.size()); valueResult.forEach((key, value) -> { InfluxPointValuePOJO pojo = GenInfluxPointValueUtils.getByPoint(key, value); pojo.setTimestamp(GenInfluxPointValueUtils.getByMin(collectTime, DataPointFreqEnum.getEumByCode(key.getMinfreqid()))); result.add(pojo); }); log.info("计算点处理结束"); } catch (Exception ex) { ex.printStackTrace(); log.info("计算点处理异常!"); } return result; } private void handPending(Map pendingMap, Map dataMap, List listGood, List listBad, Map valueResult, int count) { Map tempMap = new HashMap<>(); if (CollectionUtils.isEmpty(pendingMap)) { log.info("pendingMap is empty"); return; } log.info("处理包含计算点的"); log.info("handPending count=" + count); if (count > MAX_RECURSION) { log.info("最多递归10次"); return; } count = count + 1; for(String key : pendingMap.keySet()) { DaPointDTO dto = pendingMap.get(key); Object rawValue = singleCompute(dto, dataMap, listGood, listBad); if (PENDING_FLAG.equals(rawValue.toString())) { tempMap.put(key, dto); } else { BigDecimal coefficient = dto.getUnittransfactor() == null ? BigDecimal.ONE : dto.getUnittransfactor(); BigDecimal calValue = new BigDecimal(rawValue.toString()).multiply(coefficient); if (dto.getMaxValue() != null && calValue.compareTo(dto.getMaxValue()) > 0) { calValue = dto.getMaxValue(); } else if (dto.getMinValue() != null && calValue.compareTo(dto.getMinValue()) < 0) { calValue = dto.getMinValue(); } dataMap.put(dto.getPointNo(), calValue); valueResult.put(dto, calValue); } } if (!CollectionUtils.isEmpty(tempMap)) { this.handPending(tempMap, dataMap, listGood, listBad, valueResult, count); } } private Object singleCompute(DaPointDTO dto, Map dataMap, List listGood, List listBad) { String expression = dto.getExpression(); log.info("PointNo=" + dto.getPointNo() + ";SourceExpression=" + expression); String[] arr = expression.split(regex); // 去掉arr中的空格 arr = Stream.of(arr).filter(StringUtils::isNotBlank).toArray(String[]::new); // 判断arr都在dataMap中包含 if (!Arrays.stream(arr).allMatch(dataMap::containsKey)) { log.info("dataMap not contains key"); listBad.add(dto.getPointNo()); return CommonConstant.BAD_VALUE; } for (int i = 0; i < arr.length; i++) { String s = arr[i]; if (StringUtils.isNotBlank(s) && dataMap.containsKey(s)) { // 对每个数加(),否则负值报错 expression = expression.replace(s, "(" + dataMap.get(s).toString() + ")"); } else if(StringUtils.isNotBlank(s) && s.contains(POINT_PREFIX)) { log.info("包含计算点,先挂起"); return PENDING_FLAG; } } expression = expression.replace("&", "&&"); expression = expression.replace("|", "||"); expression = expression.replace("False", "false"); expression = expression.replace("True", "true"); log.info("PointNo=" + dto.getPointNo() + ";expression=" + expression); String result = javaScriptHandler.eval(expression); log.info("result=" + result); if (result == null || result.contains(JsErrorCode.Infinity.name()) || result.contains(JsErrorCode.NaN.name())) { listBad.add(dto.getPointNo()); return CommonConstant.BAD_VALUE; } else { if (DataTypeEnum.INT.getCode().equals(dto.getDataType())) { listGood.add(dto.getPointNo()); return new BigDecimal(result).intValue(); } else if (DataTypeEnum.FLOAT.getCode().equals(dto.getDataType())) { listGood.add(dto.getPointNo()); return new BigDecimal(result).setScale(4, BigDecimal.ROUND_UP).doubleValue(); } else if (DataTypeEnum.BOOLEAN.getCode().equals(dto.getDataType())) { listGood.add(dto.getPointNo()); return Boolean.parseBoolean(result); } else { listBad.add(dto.getPointNo()); throw new RuntimeException("计算异常,未知数据类型"); } } } public Map getCurrent(List pointNos) { Map data = new HashMap<>(); List pointMathList = daPointService.getMathPoint(pointNos); if (CollectionUtils.isEmpty(pointMathList)) { return data; } pointMathList.forEach(item -> { Object value = CommonConstant.BAD_VALUE; if (redisTemplate.hasKey(PointCollector.PV + item.getPointNo())) { value = redisTemplate.opsForValue().get(PointCollector.PV + item.getPointNo()); } else { Object rawValue = singleCompute(item, 1); BigDecimal coefficient = item.getUnittransfactor() == null ? BigDecimal.ONE : item.getUnittransfactor(); value = new BigDecimal(rawValue.toString()).multiply(coefficient); } data.put(item.getPointNo(), value); }); return data; } private Object singleCompute(DaPointDTO dto, int count) { String result = CommonConstant.BAD_VALUE.toString(); Map dataMap = new HashMap<>(); String expression = dto.getExpression(); String[] arr = expression.split(regex); for (int i = 0; i < arr.length; i++) { String s = arr[i]; if (StringUtils.isBlank(s)) { continue; } List pointNos = new ArrayList<>(); pointNos.add(s); dataMap.putAll(measureHandle.getCurrent(pointNos)); dataMap.putAll(constantHandle.getCurrent(pointNos)); dataMap.putAll(cumulateHandle.getCurrent(pointNos)); dataMap.putAll(extremalHandle.getCurrent(pointNos)); if (s.contains(POINT_PREFIX)) { log.info("计算点递归查询"); List pointMathList = daPointService.getMathPoint(pointNos); if (CollectionUtils.isEmpty(pointMathList)) { return result; } log.info("count = " + count); if (count > MAX_RECURSION) { return result; } Object v = this.singleCompute(pointMathList.get(0), count); dataMap.put(s, v); count = count + 1; } if (dataMap.get(s) == null) { log.info("计算点数据异常"); log.info("pointNo=" + dto.getPointNo() + ";dataMap.key=" + s); return CommonConstant.BAD_VALUE; } String valueStr = dataMap.get(s).toString(); if (StrUtils.isNumeric(valueStr) && new BigDecimal(valueStr).compareTo(CommonConstant.BAD_VALUE) == 0) { log.info("BAD_VALUE:" + s); } if (StrUtils.isNumeric(valueStr) && new BigDecimal(valueStr).compareTo(BigDecimal.ZERO) < 0) { valueStr = "(" + valueStr + ")"; } expression = expression.replace(s, valueStr); } expression = expression.replace("&", "&&"); expression = expression.replace("|", "||"); expression = expression.replace("False", "false"); expression = expression.replace("True", "true"); log.info("PointNo=" + dto.getPointNo() + ";expression=" + expression); result = javaScriptHandler.eval(expression); log.info("result=" + result); if (result == null) { return CommonConstant.BAD_VALUE; } else if (result.contains(JsErrorCode.Infinity.name()) || result.contains(JsErrorCode.NaN.name())) { log.info("计算异常,使用默认值"); return dto.getDefaultValue() == null ? BigDecimal.ZERO : dto.getDefaultValue(); } else { if (DataTypeEnum.INT.getCode().equals(dto.getDataType())) { return new BigDecimal(result).intValue(); } else if (DataTypeEnum.FLOAT.getCode().equals(dto.getDataType())) { return new BigDecimal(result).setScale(2, BigDecimal.ROUND_UP).doubleValue(); } else if (DataTypeEnum.BOOLEAN.getCode().equals(dto.getDataType())) { return Boolean.parseBoolean(result); } } return result; } }