|
|
@@ -0,0 +1,1191 @@
|
|
|
+package com.ruoyi.ems.ba.handle;
|
|
|
+
|
|
|
+import com.alibaba.fastjson2.JSON;
|
|
|
+import com.alibaba.fastjson2.JSONArray;
|
|
|
+import com.alibaba.fastjson2.JSONObject;
|
|
|
+import com.huashe.common.exception.Assert;
|
|
|
+import com.huashe.common.exception.BusinessException;
|
|
|
+import com.huashe.common.utils.DateUtils;
|
|
|
+import com.ruoyi.common.redis.service.RedisService;
|
|
|
+import com.ruoyi.ems.ba.config.BaCtlConfig;
|
|
|
+import com.ruoyi.ems.ba.retrofit.BaCtlEnergyTemplate;
|
|
|
+import com.ruoyi.ems.domain.ElecMeterH;
|
|
|
+import com.ruoyi.ems.domain.ElecPgSupplyH;
|
|
|
+import com.ruoyi.ems.domain.EmsDevice;
|
|
|
+import com.ruoyi.ems.domain.EmsObjAttrValue;
|
|
|
+import com.ruoyi.ems.domain.FdEnergyPriceConfig;
|
|
|
+import com.ruoyi.ems.domain.MeterDevice;
|
|
|
+import com.ruoyi.ems.domain.WaterMeterH;
|
|
|
+import com.ruoyi.ems.enums.DevObjType;
|
|
|
+import com.ruoyi.ems.common.handle.BaseMeterDevHandler;
|
|
|
+import com.ruoyi.ems.model.AbilityPayload;
|
|
|
+import com.ruoyi.ems.model.BoundaryObj;
|
|
|
+import com.ruoyi.ems.common.model.CallData;
|
|
|
+import com.ruoyi.ems.model.CallResponse;
|
|
|
+import com.ruoyi.ems.model.Price;
|
|
|
+import com.ruoyi.ems.model.QueryDevice;
|
|
|
+import com.ruoyi.ems.ba.model.CodesVal;
|
|
|
+import com.ruoyi.ems.ba.model.CodesValReq;
|
|
|
+import com.ruoyi.ems.ba.model.CodesValSetReq;
|
|
|
+import com.ruoyi.ems.service.IEmsObjAttrValueService;
|
|
|
+import org.apache.commons.collections4.CollectionUtils;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
+import java.util.function.Function;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * BA楼控处理类
|
|
|
+ * <功能详细描述>
|
|
|
+ *
|
|
|
+ * @author lvwenbin
|
|
|
+ * @version [版本号, 2025/8/15]
|
|
|
+ * @see [相关类/方法]
|
|
|
+ * @since [产品/模块版本]
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class BaCtlHandler extends BaseMeterDevHandler {
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(BaCtlHandler.class);
|
|
|
+
|
|
|
+ // 网关模型代码
|
|
|
+ private static final String GATEWAY_MODEL = "M_W4_DEV_BA_GA";
|
|
|
+
|
|
|
+ // 电表测点模型代码
|
|
|
+ private static final String METER_MODEL_E = "M_W4_DEV_BA_METER_E";
|
|
|
+
|
|
|
+ // 水表测点模型代码
|
|
|
+ private static final String METER_MODEL_W = "M_W4_DEV_BA_METER_W";
|
|
|
+
|
|
|
+ // 新风设备模型代码
|
|
|
+ private static final String METER_MODEL_XF = "M_Z020_DEV_BA_XF";
|
|
|
+
|
|
|
+ // 空调设备模型代码
|
|
|
+ private static final String METER_MODEL_AHU = "M_Z020_DEV_BA_AHU";
|
|
|
+
|
|
|
+ // 水箱设备模型代码
|
|
|
+ private static final String METER_MODEL_WT = "M_Z020_DEV_BA_WT";
|
|
|
+
|
|
|
+ // 水泵设备模型代码
|
|
|
+ private static final String METER_MODEL_WP = "M_Z020_DEV_BA_WP";
|
|
|
+
|
|
|
+ // 照明模型代码
|
|
|
+ private static final String METER_MODEL_LIGHT = "M_Z020_DEV_BA_LIGHT";
|
|
|
+
|
|
|
+ // 设备子系统代码
|
|
|
+ private static final String SUBSYSTEM_CODE = "SYS_NHJC";
|
|
|
+
|
|
|
+ // 单小时最大增长量(度)
|
|
|
+ private static final double MAX_HOUR_INCREASE_RATE = 1000.0;
|
|
|
+
|
|
|
+ // 允许的最小负增长(考虑精度误差)
|
|
|
+ private static final double MIN_REASONABLE_DECREASE_RATE = -0.1;
|
|
|
+
|
|
|
+ // 异常数据连续确认次数
|
|
|
+ private static final int ABNORMAL_DATA_CONFIRM_COUNT = 3;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private IEmsObjAttrValueService objAttrValueService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private BaCtlConfig config;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RedisService redisService;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<MeterDevice> getMeterDeviceList() {
|
|
|
+ QueryDevice queryDevice = new QueryDevice();
|
|
|
+ queryDevice.setDeviceModel(METER_MODEL_E);
|
|
|
+ queryDevice.setDeviceEnable(1);
|
|
|
+ List<MeterDevice> list = new ArrayList<>(meterDeviceService.selectMeterDeviceList(queryDevice));
|
|
|
+
|
|
|
+ QueryDevice queryDevice2 = new QueryDevice();
|
|
|
+ queryDevice2.setDeviceModel(METER_MODEL_W);
|
|
|
+ queryDevice2.setDeviceEnable(1);
|
|
|
+ list.addAll(meterDeviceService.selectMeterDeviceList(queryDevice2));
|
|
|
+
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<MeterDevice> getMeterDeviceList(String modelCode) {
|
|
|
+ QueryDevice queryDevice = new QueryDevice();
|
|
|
+ queryDevice.setDeviceModel(modelCode);
|
|
|
+ queryDevice.setDeviceEnable(1);
|
|
|
+ return meterDeviceService.selectMeterDeviceList(queryDevice);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<EmsDevice> getDeviceList() {
|
|
|
+ throw new UnsupportedOperationException();
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<EmsDevice> getDeviceList(String modelCode) {
|
|
|
+ QueryDevice queryDevice = new QueryDevice();
|
|
|
+ queryDevice.setDeviceModel(modelCode);
|
|
|
+ return deviceService.selectList(queryDevice);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public CallResponse<Void> call(AbilityPayload param) {
|
|
|
+ CallResponse<Void> callResponse = null;
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (DevObjType.SYSTEM.getCode() == param.getObjType()) {
|
|
|
+ if (StringUtils.equals("MeterReadingTotal", param.getAbilityKey())) {
|
|
|
+ List<EmsDevice> deviceList = getDeviceList(GATEWAY_MODEL);
|
|
|
+
|
|
|
+ for (EmsDevice device : deviceList) {
|
|
|
+ meterReadingGw(device.getDeviceModel(), "MeterReadingGw", device.getDeviceCode());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (StringUtils.equals("SyncXfDevAttr", param.getAbilityKey())) {
|
|
|
+ xfCollect();
|
|
|
+ }
|
|
|
+ else if (StringUtils.equals("SyncAuhDevAttr", param.getAbilityKey())) {
|
|
|
+ ahuCollect();
|
|
|
+ }
|
|
|
+ else if (StringUtils.equals("SyncWtDevAttr", param.getAbilityKey())) {
|
|
|
+ wtCollect();
|
|
|
+ }
|
|
|
+ else if (StringUtils.equals("SyncWpDevAttr", param.getAbilityKey())) {
|
|
|
+ wpCollect();
|
|
|
+ }
|
|
|
+ else if (StringUtils.equals("SyncLightDevAttr", param.getAbilityKey())) {
|
|
|
+ lightCollect();
|
|
|
+ }
|
|
|
+
|
|
|
+ callResponse = new CallResponse<>(0, "成功");
|
|
|
+ }
|
|
|
+ else if (DevObjType.DEVC.getCode() == param.getObjType()) {
|
|
|
+ if (StringUtils.equals("MeterReadingGw", param.getAbilityKey()) && StringUtils.equals(
|
|
|
+ param.getModelCode(), GATEWAY_MODEL)) {
|
|
|
+ meterReadingGw(param.getModelCode(), param.getAbilityKey(), param.getObjCode());
|
|
|
+ }
|
|
|
+ else if (StringUtils.equals("StartStopCtl", param.getAbilityKey()) && StringUtils.equals(
|
|
|
+ param.getModelCode(), METER_MODEL_XF)) {
|
|
|
+ Map<String, String> xfMapper = config.getXfMapper();
|
|
|
+ String pointId = xfMapper.get(String.format("%s.%s", param.getObjCode(), "setCtl-StartStop"));
|
|
|
+ Assert.notEmpty(pointId, -1, "该设备未配置控制测点pointId.");
|
|
|
+ devAbilityCall(METER_MODEL_XF, param.getObjCode(), pointId, param.getAbilityKey(),
|
|
|
+ param.getAbilityParam());
|
|
|
+ }
|
|
|
+ else if (StringUtils.equals("StartStopCtl", param.getAbilityKey()) && StringUtils.equals(
|
|
|
+ param.getModelCode(), METER_MODEL_AHU)) {
|
|
|
+ Map<String, String> ahuMapper = config.getAhuMapper();
|
|
|
+ String pointId = ahuMapper.get(String.format("%s.%s", param.getObjCode(), "setCtl-StartStop"));
|
|
|
+ Assert.notEmpty(pointId, -1, "该设备未配置控制测点pointId.");
|
|
|
+ devAbilityCall(METER_MODEL_AHU, param.getObjCode(), pointId, param.getAbilityKey(),
|
|
|
+ param.getAbilityParam());
|
|
|
+ }
|
|
|
+ else if (StringUtils.equals("OnOffCtl", param.getAbilityKey()) && StringUtils.equals(
|
|
|
+ param.getModelCode(), METER_MODEL_AHU)) {
|
|
|
+ Map<String, String> lightMapper = config.getLightMapper();
|
|
|
+ String pointId = lightMapper.get(String.format("%s.%s", param.getObjCode(), "setCtl-OnOff"));
|
|
|
+ Assert.notEmpty(pointId, -1, "该设备未配置控制测点pointId.");
|
|
|
+ devAbilityCall(METER_MODEL_LIGHT, param.getObjCode(), pointId, param.getAbilityKey(),
|
|
|
+ param.getAbilityParam());
|
|
|
+ }
|
|
|
+
|
|
|
+ callResponse = new CallResponse<>(0, "成功");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ callResponse = new CallResponse<>(-1, "不支持的能力key:" + param.getAbilityKey());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (BusinessException e) {
|
|
|
+ callResponse = new CallResponse<>(e.getCode(), e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ return callResponse;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void refreshOnline() {
|
|
|
+ }
|
|
|
+
|
|
|
+ public void devAbilityCall(String modelCode, String deviceId, String pointId, String abilityKey,
|
|
|
+ String paramValue) {
|
|
|
+ CodesVal codesVal = new CodesVal();
|
|
|
+ codesVal.setPointId(pointId);
|
|
|
+ codesVal.setValue(paramValue);
|
|
|
+
|
|
|
+ BaCtlEnergyTemplate template = new BaCtlEnergyTemplate(config.getUrl());
|
|
|
+ CallData<String> callData = template.setCodesVal(new CodesValSetReq(codesVal));
|
|
|
+
|
|
|
+ saveCallLog(deviceId, modelCode, abilityKey, callData.getCallStatus(), callData.getCallPayload(),
|
|
|
+ callData.getResPayload());
|
|
|
+ }
|
|
|
+
|
|
|
+ private void meterReadingGw(String modeCode, String abilityKey, String objCode) {
|
|
|
+ // 查询当前设备的接口属性值
|
|
|
+ EmsObjAttrValue subDevAttr = objAttrValueService.selectObjAttrValue(modeCode, objCode, "subDev");
|
|
|
+
|
|
|
+ if (null != subDevAttr && StringUtils.isNotEmpty(subDevAttr.getAttrValue())) {
|
|
|
+ JSONArray subDevs = JSON.parseArray(subDevAttr.getAttrValue());
|
|
|
+ Set<String> pointIdSet = subDevs.stream().map(item -> ((JSONObject) item).getString("deviceCode"))
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ CodesValReq req = new CodesValReq(pointIdSet);
|
|
|
+
|
|
|
+ // 调用能耗数据接口获取实时数据
|
|
|
+ CallData<String> callData = new BaCtlEnergyTemplate(config.getUrl()).getCodesVal(req);
|
|
|
+ String callRes = callData.getResPayload();
|
|
|
+ JSONObject resJson = JSONObject.parseObject(callRes);
|
|
|
+
|
|
|
+ if (resJson.getInteger("code") == 200) {
|
|
|
+ saveCallLog(objCode, modeCode, abilityKey, 0, JSON.toJSONString(req), callRes);
|
|
|
+
|
|
|
+ String dataStr = resJson.getString("ResultPointObjArr");
|
|
|
+ List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
|
|
|
+ // 更新设备属性
|
|
|
+ updateMeterDeviceAttrList(objCode, subDevs, retList);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ saveCallLog(objCode, modeCode, abilityKey, 2, JSON.toJSONString(req), callRes);
|
|
|
+ throw new BusinessException(-1, resJson.getString("error"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 采集室内能耗计量数据
|
|
|
+ *
|
|
|
+ * @return 采集条数
|
|
|
+ */
|
|
|
+ public int meterCollect() {
|
|
|
+ int cnt = 0;
|
|
|
+
|
|
|
+ try {
|
|
|
+ List<EmsObjAttrValue> gwDeviceList = objAttrValueService.selectByAttrKey(GATEWAY_MODEL, "subDev");
|
|
|
+
|
|
|
+ // 遍历每个网关设备
|
|
|
+ if (CollectionUtils.isNotEmpty(gwDeviceList)) {
|
|
|
+ // 调用能耗数据接口获取实时数据
|
|
|
+ BaCtlEnergyTemplate template = new BaCtlEnergyTemplate(config.getUrl());
|
|
|
+
|
|
|
+ for (EmsObjAttrValue gwDevice : gwDeviceList) {
|
|
|
+ cnt += meterDevCollect(template, gwDevice);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ log.error("能耗数据抄报异常", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ return cnt;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void xfCollect() {
|
|
|
+ try {
|
|
|
+ List<EmsDevice> deviceList = getDeviceList(METER_MODEL_XF);
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(deviceList)) {
|
|
|
+ for (EmsDevice device : deviceList) {
|
|
|
+ xfDeviceCollect(device);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ log.error("新风采集异常", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void ahuCollect() {
|
|
|
+ try {
|
|
|
+ List<EmsDevice> deviceList = getDeviceList(METER_MODEL_AHU);
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(deviceList)) {
|
|
|
+ for (EmsDevice device : deviceList) {
|
|
|
+ auhDeviceCollect(device);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ log.error("新风采集异常", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void wtCollect() {
|
|
|
+ try {
|
|
|
+ List<EmsDevice> deviceList = getDeviceList(METER_MODEL_WT);
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(deviceList)) {
|
|
|
+ for (EmsDevice device : deviceList) {
|
|
|
+ wtDeviceCollect(device);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ log.error("水箱采集异常", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void wpCollect() {
|
|
|
+ try {
|
|
|
+ List<EmsDevice> deviceList = getDeviceList(METER_MODEL_WP);
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(deviceList)) {
|
|
|
+ for (EmsDevice device : deviceList) {
|
|
|
+ wpDeviceCollect(device);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ log.error("水泵采集异常", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void lightCollect() {
|
|
|
+ try {
|
|
|
+ List<EmsDevice> deviceList = getDeviceList(METER_MODEL_LIGHT);
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(deviceList)) {
|
|
|
+ for (EmsDevice device : deviceList) {
|
|
|
+ lightDeviceCollect(device);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ log.error("照明采集异常", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private int meterDevCollect(BaCtlEnergyTemplate template, EmsObjAttrValue subDevAttr) {
|
|
|
+ int cnt = 0;
|
|
|
+
|
|
|
+ try {
|
|
|
+
|
|
|
+ if (null != subDevAttr && StringUtils.isNotEmpty(subDevAttr.getAttrValue())) {
|
|
|
+ JSONArray subDevs = JSON.parseArray(subDevAttr.getAttrValue());
|
|
|
+ Set<String> pointIdSet = subDevs.stream().map(item -> ((JSONObject) item).getString("deviceCode"))
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ CodesValReq req = new CodesValReq(pointIdSet);
|
|
|
+
|
|
|
+ // 调用能耗数据接口获取实时数据
|
|
|
+ CallData<String> callData = template.getCodesVal(req);
|
|
|
+ String callRes = callData.getResPayload();
|
|
|
+ JSONObject resJson = JSONObject.parseObject(callRes);
|
|
|
+ Assert.isTrue(StringUtils.equals(resJson.getString("code"), "200"), resJson.getInteger("code"),
|
|
|
+ resJson.getString("error"));
|
|
|
+ int callStatus = StringUtils.equals(resJson.getString("code"), "200") ? 0 : 2;
|
|
|
+ saveCallLog(subDevAttr.getObjCode(), GATEWAY_MODEL, "MeterReadingGw", callStatus,
|
|
|
+ JSON.toJSONString(req), callRes);
|
|
|
+
|
|
|
+ String dataStr = resJson.getString("ResultPointObjArr");
|
|
|
+ List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
|
|
|
+
|
|
|
+ // 更新设备属性
|
|
|
+ updateMeterDeviceAttrList(subDevAttr.getObjCode(), subDevs, retList);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ log.error("meterDevHourProd error! deviceCode:{}", subDevAttr.getObjCode(), e);
|
|
|
+ }
|
|
|
+
|
|
|
+ return cnt;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void xfDeviceCollect(EmsDevice device) {
|
|
|
+ String deviceCode = device.getDeviceCode();
|
|
|
+ Map<String, String> paramKeys = config.getXfMapper();
|
|
|
+
|
|
|
+ Set<String> pointIds = paramKeys.entrySet().stream().filter(
|
|
|
+ entry -> StringUtils.startsWith(entry.getKey(), deviceCode) && !StringUtils.startsWith(entry.getKey(),
|
|
|
+ deviceCode + ".setCtl-")).flatMap(entry -> Arrays.stream(StringUtils.split(entry.getValue(), ",")))
|
|
|
+ .map(String::trim).collect(Collectors.toSet());
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(pointIds)) {
|
|
|
+ CodesValReq req = new CodesValReq(pointIds);
|
|
|
+
|
|
|
+ // 调用能耗数据接口获取实时数据
|
|
|
+ CallData<String> callData = new BaCtlEnergyTemplate(config.getUrl()).getCodesVal(req);
|
|
|
+ String callRes = callData.getResPayload();
|
|
|
+ saveCallLog(device.getDeviceCode(), device.getDeviceModel(), "SyncXfDevAttr", 0, JSON.toJSONString(req), callRes);
|
|
|
+
|
|
|
+ JSONObject resJson = JSONObject.parseObject(callRes);
|
|
|
+ Assert.isTrue(StringUtils.equals(resJson.getString("code"), "200"), resJson.getInteger("code"),
|
|
|
+ resJson.getString("error"));
|
|
|
+ String dataStr = resJson.getString("ResultPointObjArr");
|
|
|
+ List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
|
|
|
+ Map<String, CodesVal> retMap = retList.stream()
|
|
|
+ .collect(Collectors.toMap(CodesVal::getPointId, Function.identity()));
|
|
|
+
|
|
|
+ List<EmsObjAttrValue> dbAttrList = objAttrValueService.selectByObjCode(METER_MODEL_XF, deviceCode);
|
|
|
+ Map<String, EmsObjAttrValue> dbMap = dbAttrList.stream()
|
|
|
+ .collect(Collectors.toMap(EmsObjAttrValue::getAttrKey, Function.identity()));
|
|
|
+
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "xfTemp", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "sfTemp", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "hfTemp", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "pfTemp", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "ppm", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "wCv", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "xfCv", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "pfCv", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "lwDpAlarm", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "fjUvAlarm", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "afAlarm", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "xfStatus", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "xfMA", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "xfFault", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "pfStatus", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "pfMA", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "pfFault", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "xfTempSetVal", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "LnSu", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "timeSetTag", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "manualTag", paramKeys, dbMap, retMap);
|
|
|
+ baDevTimeStatueCheckAndUpdate(METER_MODEL_XF, deviceCode, "timeStatus", paramKeys, dbMap, retMap);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void auhDeviceCollect(EmsDevice device) {
|
|
|
+ String deviceCode = device.getDeviceCode();
|
|
|
+ Map<String, String> paramKeys = config.getAhuMapper();
|
|
|
+
|
|
|
+ Set<String> pointIds = paramKeys.entrySet().stream().filter(
|
|
|
+ entry -> StringUtils.startsWith(entry.getKey(), deviceCode) && !StringUtils.startsWith(entry.getKey(),
|
|
|
+ deviceCode + ".setCtl-")).flatMap(entry -> Arrays.stream(StringUtils.split(entry.getValue(), ",")))
|
|
|
+ .map(String::trim).collect(Collectors.toSet());
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(pointIds)) {
|
|
|
+ CodesValReq req = new CodesValReq(pointIds);
|
|
|
+
|
|
|
+ // 调用能耗数据接口获取实时数据
|
|
|
+ CallData<String> callData = new BaCtlEnergyTemplate(config.getUrl()).getCodesVal(req);
|
|
|
+ String callRes = callData.getResPayload();
|
|
|
+ saveCallLog(device.getDeviceCode(), device.getDeviceModel(), "SyncAuhDevAttr", 0, JSON.toJSONString(req), callRes);
|
|
|
+ JSONObject resJson = JSONObject.parseObject(callRes);
|
|
|
+ Assert.isTrue(StringUtils.equals(resJson.getString("code"), "200"), resJson.getInteger("code"),
|
|
|
+ resJson.getString("error"));
|
|
|
+ String dataStr = resJson.getString("ResultPointObjArr");
|
|
|
+ List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
|
|
|
+ Map<String, CodesVal> retMap = retList.stream()
|
|
|
+ .collect(Collectors.toMap(CodesVal::getPointId, Function.identity()));
|
|
|
+
|
|
|
+ List<EmsObjAttrValue> dbAttrList = objAttrValueService.selectByObjCode(METER_MODEL_AHU, deviceCode);
|
|
|
+ Map<String, EmsObjAttrValue> dbMap = dbAttrList.stream()
|
|
|
+ .collect(Collectors.toMap(EmsObjAttrValue::getAttrKey, Function.identity()));
|
|
|
+
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "xfTemp", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "sfTemp", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "hfTemp", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "wCv", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "xfCv", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "lwDpAlarm", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "fjUvAlarm", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "afAlarm", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "xfStatus", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "xfMA", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "xfFault", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "xfTempSetVal", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "LnSu", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "timeSetTag", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "manualTag", paramKeys, dbMap, retMap);
|
|
|
+ baDevTimeStatueCheckAndUpdate(METER_MODEL_AHU, deviceCode, "timeStatus", paramKeys, dbMap, retMap);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void wtDeviceCollect(EmsDevice device) {
|
|
|
+ String deviceCode = device.getDeviceCode();
|
|
|
+ Map<String, String> paramKeys = config.getWtMapper();
|
|
|
+
|
|
|
+ Set<String> pointIds = paramKeys.entrySet().stream()
|
|
|
+ .filter(entry -> StringUtils.startsWith(entry.getKey(), deviceCode))
|
|
|
+ .flatMap(entry -> Arrays.stream(StringUtils.split(entry.getValue(), ","))).map(String::trim)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(pointIds)) {
|
|
|
+ CodesValReq req = new CodesValReq(pointIds);
|
|
|
+
|
|
|
+ // 调用能耗数据接口获取实时数据
|
|
|
+ CallData<String> callData = new BaCtlEnergyTemplate(config.getUrl()).getCodesVal(req);
|
|
|
+ String callRes = callData.getResPayload();
|
|
|
+ saveCallLog(device.getDeviceCode(), device.getDeviceModel(), "SyncWtDevAttr", 0, JSON.toJSONString(req), callRes);
|
|
|
+ JSONObject resJson = JSONObject.parseObject(callRes);
|
|
|
+ Assert.isTrue(StringUtils.equals(resJson.getString("code"), "200"), resJson.getInteger("code"),
|
|
|
+ resJson.getString("error"));
|
|
|
+ String dataStr = resJson.getString("ResultPointObjArr");
|
|
|
+ List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
|
|
|
+ Map<String, CodesVal> retMap = retList.stream()
|
|
|
+ .collect(Collectors.toMap(CodesVal::getPointId, Function.identity()));
|
|
|
+
|
|
|
+ List<EmsObjAttrValue> dbAttrList = objAttrValueService.selectByObjCode(METER_MODEL_WT, deviceCode);
|
|
|
+ Map<String, EmsObjAttrValue> dbMap = dbAttrList.stream()
|
|
|
+ .collect(Collectors.toMap(EmsObjAttrValue::getAttrKey, Function.identity()));
|
|
|
+
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_WT, deviceCode, "highLevelAlarm", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_WT, deviceCode, "lowLevelAlarm", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_WT, deviceCode, "tankLevel", paramKeys, dbMap, retMap);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void wpDeviceCollect(EmsDevice device) {
|
|
|
+ String deviceCode = device.getDeviceCode();
|
|
|
+ Map<String, String> paramKeys = config.getWpMapper();
|
|
|
+
|
|
|
+ Set<String> pointIds = paramKeys.entrySet().stream()
|
|
|
+ .filter(entry -> StringUtils.startsWith(entry.getKey(), deviceCode))
|
|
|
+ .flatMap(entry -> Arrays.stream(StringUtils.split(entry.getValue(), ","))).map(String::trim)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(pointIds)) {
|
|
|
+ CodesValReq req = new CodesValReq(pointIds);
|
|
|
+
|
|
|
+ // 调用能耗数据接口获取实时数据
|
|
|
+ CallData<String> callData = new BaCtlEnergyTemplate(config.getUrl()).getCodesVal(req);
|
|
|
+ String callRes = callData.getResPayload();
|
|
|
+ saveCallLog(device.getDeviceCode(), device.getDeviceModel(), "SyncWpDevAttr", 0, JSON.toJSONString(req), callRes);
|
|
|
+ JSONObject resJson = JSONObject.parseObject(callRes);
|
|
|
+ Assert.isTrue(StringUtils.equals(resJson.getString("code"), "200"), resJson.getInteger("code"),
|
|
|
+ resJson.getString("error"));
|
|
|
+ String dataStr = resJson.getString("ResultPointObjArr");
|
|
|
+ List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
|
|
|
+ Map<String, CodesVal> retMap = retList.stream()
|
|
|
+ .collect(Collectors.toMap(CodesVal::getPointId, Function.identity()));
|
|
|
+
|
|
|
+ List<EmsObjAttrValue> dbAttrList = objAttrValueService.selectByObjCode(METER_MODEL_WP, deviceCode);
|
|
|
+ Map<String, EmsObjAttrValue> dbMap = dbAttrList.stream()
|
|
|
+ .collect(Collectors.toMap(EmsObjAttrValue::getAttrKey, Function.identity()));
|
|
|
+
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_WP, deviceCode, "autoState", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_WP, deviceCode, "runningState", paramKeys, dbMap, retMap);
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_WP, deviceCode, "faultState", paramKeys, dbMap, retMap);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void lightDeviceCollect(EmsDevice device) {
|
|
|
+ String deviceCode = device.getDeviceCode();
|
|
|
+ Map<String, String> paramKeys = config.getLightMapper();
|
|
|
+
|
|
|
+ Set<String> pointIds = paramKeys.entrySet().stream().filter(
|
|
|
+ entry -> StringUtils.startsWith(entry.getKey(), deviceCode) && !StringUtils.startsWith(entry.getKey(),
|
|
|
+ deviceCode + ".setCtl-")).flatMap(entry -> Arrays.stream(StringUtils.split(entry.getValue(), ",")))
|
|
|
+ .map(String::trim).collect(Collectors.toSet());
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(pointIds)) {
|
|
|
+ CodesValReq req = new CodesValReq(pointIds);
|
|
|
+
|
|
|
+ // 调用能耗数据接口获取实时数据
|
|
|
+ CallData<String> callData = new BaCtlEnergyTemplate(config.getUrl()).getCodesVal(req);
|
|
|
+ String callRes = callData.getResPayload();
|
|
|
+ saveCallLog(device.getDeviceCode(), device.getDeviceModel(), "SyncLightDevAttr", 0, JSON.toJSONString(req), callRes);
|
|
|
+ JSONObject resJson = JSONObject.parseObject(callRes);
|
|
|
+ Assert.isTrue(StringUtils.equals(resJson.getString("code"), "200"), resJson.getInteger("code"),
|
|
|
+ resJson.getString("error"));
|
|
|
+ String dataStr = resJson.getString("ResultPointObjArr");
|
|
|
+ List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
|
|
|
+ Map<String, CodesVal> retMap = retList.stream()
|
|
|
+ .collect(Collectors.toMap(CodesVal::getPointId, Function.identity()));
|
|
|
+
|
|
|
+ List<EmsObjAttrValue> dbAttrList = objAttrValueService.selectByObjCode(METER_MODEL_LIGHT, deviceCode);
|
|
|
+ Map<String, EmsObjAttrValue> dbMap = dbAttrList.stream()
|
|
|
+ .collect(Collectors.toMap(EmsObjAttrValue::getAttrKey, Function.identity()));
|
|
|
+
|
|
|
+ baDevAttrCheckAndUpdate(METER_MODEL_LIGHT, deviceCode, "Switch", paramKeys, dbMap, retMap);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void baDevAttrCheckAndUpdate(String devModel, String deviceCode, String attrKey,
|
|
|
+ Map<String, String> kvMap, Map<String, EmsObjAttrValue> dbAttrMap, Map<String, CodesVal> retMap) {
|
|
|
+ EmsObjAttrValue dbAttr = dbAttrMap.get(attrKey);
|
|
|
+ String pointId = kvMap.get(String.format("%s.%s", deviceCode, attrKey));
|
|
|
+
|
|
|
+ if (null != dbAttr && retMap.containsKey(pointId)) {
|
|
|
+ CodesVal codesVal = retMap.get(pointId);
|
|
|
+
|
|
|
+ if (!StringUtils.equals(dbAttr.getAttrValue(), codesVal.getValue())) {
|
|
|
+ objAttrValueService.updateObjAttrValue(devModel, deviceCode, attrKey, codesVal.getValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (null != pointId && retMap.containsKey(pointId)) {
|
|
|
+ CodesVal codesVal = retMap.get(pointId);
|
|
|
+ EmsObjAttrValue attr = new EmsObjAttrValue(deviceCode, devModel, attrKey, codesVal.getValue());
|
|
|
+ objAttrValueService.mergeObjAttrValue(attr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void baDevTimeStatueCheckAndUpdate(String devModel, String deviceCode, String attrKey,
|
|
|
+ Map<String, String> kvMap, Map<String, EmsObjAttrValue> dbAttrMap, Map<String, CodesVal> retMap) {
|
|
|
+ EmsObjAttrValue dbAttr = dbAttrMap.get(attrKey);
|
|
|
+ String pointIdStr = kvMap.get(String.format("%s.%s", deviceCode, attrKey));
|
|
|
+
|
|
|
+ if (StringUtils.isNotEmpty(pointIdStr)) {
|
|
|
+ String[] array = StringUtils.split(pointIdStr, ",");
|
|
|
+ String value = "0";
|
|
|
+
|
|
|
+ // 定时开-小时
|
|
|
+ CodesVal codesVal = retMap.get(array[0]);
|
|
|
+
|
|
|
+ if (null != codesVal && StringUtils.equals(codesVal.getValue(), "1")) {
|
|
|
+ value = "160";
|
|
|
+ }
|
|
|
+
|
|
|
+ codesVal = retMap.get(array[1]);
|
|
|
+
|
|
|
+ if (null != codesVal && StringUtils.equals(codesVal.getValue(), "1")) {
|
|
|
+ value = "101";
|
|
|
+ }
|
|
|
+
|
|
|
+ codesVal = retMap.get(array[2]);
|
|
|
+
|
|
|
+ if (null != codesVal && StringUtils.equals(codesVal.getValue(), "1")) {
|
|
|
+ value = "260";
|
|
|
+ }
|
|
|
+
|
|
|
+ codesVal = retMap.get(array[3]);
|
|
|
+
|
|
|
+ if (null != codesVal && StringUtils.equals(codesVal.getValue(), "1")) {
|
|
|
+ value = "201";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (null != dbAttr && !StringUtils.equals(dbAttr.getAttrValue(), value)) {
|
|
|
+ objAttrValueService.updateObjAttrValue(devModel, deviceCode, attrKey, value);
|
|
|
+ }
|
|
|
+ else if (null == dbAttr) {
|
|
|
+ EmsObjAttrValue attr = new EmsObjAttrValue(deviceCode, devModel, attrKey, value);
|
|
|
+ objAttrValueService.mergeObjAttrValue(attr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateMeterDeviceAttrList(String gwDevieCode, JSONArray subDevs, List<CodesVal> retList) {
|
|
|
+ try {
|
|
|
+ // 将新采集数据转换为Map
|
|
|
+ Map<String, CodesVal> codesValMap = retList.stream()
|
|
|
+ .collect(Collectors.toMap(CodesVal::getPointId, Function.identity()));
|
|
|
+
|
|
|
+ for (Object subDev : subDevs) {
|
|
|
+ JSONObject subDevObj = (JSONObject) subDev;
|
|
|
+ String pointId = subDevObj.getString("deviceCode");
|
|
|
+ String modelCode = subDevObj.getString("modelCode");
|
|
|
+
|
|
|
+ CodesVal syncCodesVal = codesValMap.get(pointId);
|
|
|
+
|
|
|
+ if (null != syncCodesVal) {
|
|
|
+ EmsObjAttrValue dbAttr = objAttrValueService.selectObjAttrValue(modelCode, pointId, "value");
|
|
|
+
|
|
|
+ // 数据验证和处理
|
|
|
+ DataValidationResult validationResult = validateMeterData(gwDevieCode, pointId,
|
|
|
+ dbAttr.getAttrValue(), syncCodesVal.getValue());
|
|
|
+
|
|
|
+ if (validationResult.isValid()) {
|
|
|
+ // 更新缓存中的数据
|
|
|
+ String hKey = NEW_HOUR_READING + "-" + pointId;
|
|
|
+ redisService.setCacheMapValue(gwDevieCode, hKey, validationResult.getValidValue());
|
|
|
+
|
|
|
+ if (!StringUtils.equals(validationResult.getValidValue(), dbAttr.getAttrValue())) {
|
|
|
+ objAttrValueService.updateObjAttrValue(modelCode, pointId, "value",
|
|
|
+ validationResult.getValidValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 记录异常数据但不更新
|
|
|
+ log.warn("检测到异常抄表数据 - 设备:{}, 测点:{}, 旧值:{}, 新值:{}, 原因:{}",
|
|
|
+ dbAttr.getObjCode(), pointId, dbAttr.getAttrValue(), syncCodesVal.getValue(),
|
|
|
+ validationResult.getReason());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ log.error("更新设备属性异常", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 能耗数据抄报(扩展支持市电计量)
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public int meterHourProd() {
|
|
|
+ int cnt = 0;
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 计量设备-网关设备映射集合
|
|
|
+ Map<String, EmsDevice> refParentMap = getRefParentMap();
|
|
|
+
|
|
|
+ // 电表
|
|
|
+ List<MeterDevice> meterDeviceList = getMeterDeviceList(METER_MODEL_E);
|
|
|
+
|
|
|
+ // 计算电表小时数据
|
|
|
+ if (CollectionUtils.isNotEmpty(meterDeviceList)) {
|
|
|
+ Map<String, Price> priceMap = new ConcurrentHashMap<>();
|
|
|
+ Date date = DateUtils.adjustHour(new Date(), -1);
|
|
|
+ List<ElecMeterH> elecMeterHList = new ArrayList<>();
|
|
|
+
|
|
|
+ for (MeterDevice meterDevice : meterDeviceList) {
|
|
|
+ EmsDevice gwDevice = refParentMap.get(meterDevice.getDeviceCode());
|
|
|
+ ElecMeterH elecMeterH = workElecMeterReading(gwDevice, meterDevice);
|
|
|
+
|
|
|
+ if (null != elecMeterH) {
|
|
|
+ Price price = priceMap.computeIfAbsent(gwDevice.getAreaCode(),
|
|
|
+ k -> priceService.getElecHourPrice(gwDevice.getAreaCode(), date));
|
|
|
+ completeElecPrice(elecMeterH, price);
|
|
|
+ elecMeterHList.add(elecMeterH);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(elecMeterHList)) {
|
|
|
+ elecMeterHService.insertBatch(elecMeterHList);
|
|
|
+ cnt += elecMeterHList.size();
|
|
|
+ generatePgSupplyData(elecMeterHList);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 水表
|
|
|
+ meterDeviceList = getMeterDeviceList(METER_MODEL_W);
|
|
|
+
|
|
|
+ // 计算水表小时数据
|
|
|
+ if (CollectionUtils.isNotEmpty(meterDeviceList)) {
|
|
|
+ Map<String, FdEnergyPriceConfig> priceMap = new ConcurrentHashMap<>();
|
|
|
+ List<WaterMeterH> waterMeterHList = new ArrayList<>();
|
|
|
+
|
|
|
+ for (MeterDevice meterDevice : meterDeviceList) {
|
|
|
+ EmsDevice gwDevice = refParentMap.get(meterDevice.getDeviceCode());
|
|
|
+ WaterMeterH waterMeterH = workWaterMeterReading(gwDevice, meterDevice);
|
|
|
+
|
|
|
+ if (null != waterMeterH) {
|
|
|
+ FdEnergyPriceConfig price = priceMap.computeIfAbsent(gwDevice.getAreaCode(),
|
|
|
+ k -> fdEnergyPriceConfigService.selectByAreaCode(gwDevice.getAreaCode(), 70));
|
|
|
+ completeWaterPrice(waterMeterH, price);
|
|
|
+ waterMeterHList.add(waterMeterH);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(waterMeterHList)) {
|
|
|
+ waterMeterHService.insertBatch(waterMeterHList);
|
|
|
+ cnt += waterMeterHList.size();
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ log.error("能耗数据抄报异常", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ return cnt;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, EmsDevice> getRefParentMap() {
|
|
|
+ Map<String, EmsDevice> refParentMap = new HashMap<>();
|
|
|
+
|
|
|
+ List<EmsObjAttrValue> attrValues = objAttrValueService.selectByAttrKey(GATEWAY_MODEL, "subDev");
|
|
|
+ Map<String, String> attrValueMap = attrValues.stream()
|
|
|
+ .collect(Collectors.toMap(EmsObjAttrValue::getObjCode, EmsObjAttrValue::getAttrValue));
|
|
|
+
|
|
|
+ // 网关设备列表
|
|
|
+ List<EmsDevice> gwDevices = getDeviceList(GATEWAY_MODEL);
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(gwDevices)) {
|
|
|
+ for (EmsDevice gwDevice : gwDevices) {
|
|
|
+ String subDevStr = attrValueMap.get(gwDevice.getDeviceCode());
|
|
|
+ if (StringUtils.isNotBlank(subDevStr)) {
|
|
|
+ JSONArray jsonArray = JSON.parseArray(subDevStr);
|
|
|
+ jsonArray.forEach(obj -> {
|
|
|
+ JSONObject jsonObject = (JSONObject) obj;
|
|
|
+ refParentMap.put(jsonObject.getString("deviceCode"), gwDevice);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return refParentMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 修改后的单设备计量产出方法,返回详细结果
|
|
|
+ */
|
|
|
+
|
|
|
+ private ElecMeterH workElecMeterReading(EmsDevice gwDevice, MeterDevice meterDevice) {
|
|
|
+ ElecMeterH elecMeterH = null;
|
|
|
+
|
|
|
+ // 读取最新抄表值
|
|
|
+ String newMeterReading = redisService.getCacheMapValue(gwDevice.getDeviceCode(),
|
|
|
+ NEW_HOUR_READING + "-" + meterDevice.getDeviceCode());
|
|
|
+
|
|
|
+ if (null != newMeterReading) {
|
|
|
+ String lastCacheKey = LAST_HOUR_READING + "-" + meterDevice.getDeviceCode();
|
|
|
+
|
|
|
+ // 读取历史抄表
|
|
|
+ String lastMeterReading = redisService.getCacheMapValue(gwDevice.getDeviceCode(), lastCacheKey);
|
|
|
+
|
|
|
+ elecMeterH = getElecMeterH(gwDevice.getDeviceCode(), meterDevice, lastMeterReading, newMeterReading);
|
|
|
+ }
|
|
|
+
|
|
|
+ return elecMeterH;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ElecMeterH getElecMeterH(String deviceCode, MeterDevice mDevice, String lastMeterReading,
|
|
|
+ String newMeterReading) {
|
|
|
+ ElecMeterH elecMeterH = null;
|
|
|
+ String cacheKey = LAST_HOUR_READING + "-" + mDevice.getDeviceCode();
|
|
|
+ String timestampKey = LAST_HOUR_TIMESTAMP + "-" + mDevice.getDeviceCode();
|
|
|
+
|
|
|
+ // 获取缓存时间戳
|
|
|
+ String cachedTimestamp = redisService.getCacheMapValue(deviceCode, timestampKey);
|
|
|
+ boolean shouldUseCache = false;
|
|
|
+
|
|
|
+ if (StringUtils.isNotEmpty(lastMeterReading) && StringUtils.isNotEmpty(cachedTimestamp)) {
|
|
|
+ try {
|
|
|
+ long lastUpdateTime = Long.parseLong(cachedTimestamp);
|
|
|
+ long currentTime = System.currentTimeMillis();
|
|
|
+ // 只有当缓存数据在2小时内才使用
|
|
|
+ shouldUseCache = (currentTime - lastUpdateTime) <= MAX_CACHE_AGE_MILLIS;
|
|
|
+ }
|
|
|
+ catch (NumberFormatException e) {
|
|
|
+ log.warn("无效的时间戳缓存: {}", cachedTimestamp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (shouldUseCache) {
|
|
|
+ // 缓存有效,使用缓存计算
|
|
|
+ elecMeterH = execElecHourMeter(mDevice, lastMeterReading, newMeterReading);
|
|
|
+ // 更新缓存和时间戳
|
|
|
+ updateCacheAfterSuccess(deviceCode, cacheKey, newMeterReading);
|
|
|
+ redisService.setCacheMapValue(deviceCode, timestampKey, String.valueOf(System.currentTimeMillis()));
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 缓存过期或无效,查询数据库
|
|
|
+ ElecMeterH dbElecMeterH = elecMeterHService.selectLatelyItem(mDevice.getDeviceCode());
|
|
|
+
|
|
|
+ if (null != dbElecMeterH && null != dbElecMeterH.getMeterReading()) {
|
|
|
+ // 验证数据库记录是否为上一小时
|
|
|
+ Date currentHour = DateUtils.adjustHour(new Date(), -1);
|
|
|
+ long timeDiff = currentHour.getTime() - dbElecMeterH.getRecordTime().getTime();
|
|
|
+
|
|
|
+ // 只有时间差在1小时左右(容差30分钟)才计算
|
|
|
+ if (Math.abs(timeDiff) <= 30 * 60 * 1000) {
|
|
|
+ elecMeterH = execElecHourMeter(mDevice, String.valueOf(dbElecMeterH.getMeterReading()),
|
|
|
+ newMeterReading);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ log.warn("数据时间差过大,跳过计算 - 设备:{}, 时间差:{}小时", mDevice.getDeviceCode(),
|
|
|
+ timeDiff / (60 * 60 * 1000));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 无论是否计算,都更新缓存为当前值,作为下次计算基准
|
|
|
+ updateCacheAfterSuccess(deviceCode, cacheKey, newMeterReading);
|
|
|
+ redisService.setCacheMapValue(deviceCode, timestampKey, String.valueOf(System.currentTimeMillis()));
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 首次上报,仅更新缓存
|
|
|
+ if (StringUtils.isNotEmpty(newMeterReading)) {
|
|
|
+ updateCacheAfterSuccess(deviceCode, cacheKey, newMeterReading);
|
|
|
+ redisService.setCacheMapValue(deviceCode, timestampKey, String.valueOf(System.currentTimeMillis()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return elecMeterH;
|
|
|
+ }
|
|
|
+
|
|
|
+ private WaterMeterH workWaterMeterReading(EmsDevice gwDevice, MeterDevice meterDevice) {
|
|
|
+ WaterMeterH waterMeterH = null;
|
|
|
+
|
|
|
+ // 读取最新抄表值
|
|
|
+ String newMeterReading = redisService.getCacheMapValue(gwDevice.getDeviceCode(),
|
|
|
+ NEW_HOUR_READING + "-" + meterDevice.getDeviceCode());
|
|
|
+
|
|
|
+ if (null != newMeterReading) {
|
|
|
+ String lastCacheKey = LAST_HOUR_READING + "-" + meterDevice.getDeviceCode();
|
|
|
+
|
|
|
+ // 读取历史抄表
|
|
|
+ String lastMeterReading = redisService.getCacheMapValue(gwDevice.getDeviceCode(), lastCacheKey);
|
|
|
+
|
|
|
+ // 组装水表抄报数据
|
|
|
+ waterMeterH = getWaterMeterH(gwDevice.getDeviceCode(), meterDevice, lastMeterReading, newMeterReading);
|
|
|
+ }
|
|
|
+
|
|
|
+ return waterMeterH;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public WaterMeterH getWaterMeterH(String deviceCode, MeterDevice mDevice, String lastMeterReading,
|
|
|
+ String newMeterReading) {
|
|
|
+ WaterMeterH waterMeterH = null;
|
|
|
+ String cacheKey = LAST_HOUR_READING + "-" + mDevice.getDeviceCode();
|
|
|
+ String timestampKey = LAST_HOUR_TIMESTAMP + "-" + mDevice.getDeviceCode();
|
|
|
+
|
|
|
+ // 获取缓存时间戳
|
|
|
+ String cachedTimestamp = redisService.getCacheMapValue(deviceCode, timestampKey);
|
|
|
+ boolean shouldUseCache = false;
|
|
|
+
|
|
|
+ if (StringUtils.isNotEmpty(lastMeterReading) && StringUtils.isNotEmpty(cachedTimestamp)) {
|
|
|
+ try {
|
|
|
+ long lastUpdateTime = Long.parseLong(cachedTimestamp);
|
|
|
+ long currentTime = System.currentTimeMillis();
|
|
|
+ // 只有当缓存数据在2小时内才使用
|
|
|
+ shouldUseCache = (currentTime - lastUpdateTime) <= MAX_CACHE_AGE_MILLIS;
|
|
|
+
|
|
|
+ if (!shouldUseCache) {
|
|
|
+ log.warn("水表缓存数据过期 - 设备:{}, 缓存时长:{}小时", mDevice.getDeviceCode(),
|
|
|
+ (currentTime - lastUpdateTime) / (60 * 60 * 1000));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (NumberFormatException e) {
|
|
|
+ log.warn("无效的时间戳缓存: {}", cachedTimestamp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (shouldUseCache) {
|
|
|
+ // 缓存有效,使用缓存计算
|
|
|
+ waterMeterH = execWaterHourMeter(mDevice, lastMeterReading, newMeterReading);
|
|
|
+ if (waterMeterH != null) {
|
|
|
+ // 只有成功计算后才更新缓存
|
|
|
+ updateCacheAfterSuccess(deviceCode, cacheKey, newMeterReading);
|
|
|
+ redisService.setCacheMapValue(deviceCode, timestampKey, String.valueOf(System.currentTimeMillis()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 缓存过期或无效,查询数据库
|
|
|
+ WaterMeterH dbWaterMeterH = waterMeterHService.selectLatelyItem(mDevice.getDeviceCode());
|
|
|
+
|
|
|
+ if (null != dbWaterMeterH && null != dbWaterMeterH.getMeterReading()) {
|
|
|
+ // 验证数据库记录是否为上一小时
|
|
|
+ Date currentHour = DateUtils.adjustHour(new Date(), -1);
|
|
|
+ long timeDiff = currentHour.getTime() - dbWaterMeterH.getRecordTime().getTime();
|
|
|
+
|
|
|
+ // 只有时间差在1小时左右(容差30分钟)才计算
|
|
|
+ if (Math.abs(timeDiff) <= 30 * 60 * 1000) {
|
|
|
+ waterMeterH = execWaterHourMeter(mDevice, String.valueOf(dbWaterMeterH.getMeterReading()),
|
|
|
+ newMeterReading);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ log.warn("水表数据时间差过大,跳过计算 - 设备:{}, 时间差:{}小时", mDevice.getDeviceCode(),
|
|
|
+ timeDiff / (60 * 60 * 1000));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 无论是否计算成功,都更新缓存为当前值,作为下次计算基准
|
|
|
+ if (StringUtils.isNotEmpty(newMeterReading)) {
|
|
|
+ updateCacheAfterSuccess(deviceCode, cacheKey, newMeterReading);
|
|
|
+ redisService.setCacheMapValue(deviceCode, timestampKey, String.valueOf(System.currentTimeMillis()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return waterMeterH;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 产出市电供应计量数据(优化版)
|
|
|
+ */
|
|
|
+ private void generatePgSupplyData(List<ElecMeterH> allElecMeterHList) {
|
|
|
+ try {
|
|
|
+ // 生成市电供应数据
|
|
|
+ List<ElecPgSupplyH> pgSupplyHList = new ArrayList<>();
|
|
|
+
|
|
|
+ // 获取所有区域的市电设施及其绑定的计量设备
|
|
|
+ Set<String> areaCodeSet = allElecMeterHList.stream().map(ElecMeterH::getAreaCode)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ Map<String, Price> priceMap = new ConcurrentHashMap<>();
|
|
|
+ Date recordTime = DateUtils.adjustHour(new Date(), -1);
|
|
|
+
|
|
|
+ // 按区域查询市电设施
|
|
|
+ for (String areaCode : areaCodeSet) {
|
|
|
+ // 查询区域下市电的供应设施(同时获取了设施绑定的表计)
|
|
|
+ List<BoundaryObj> facsMeterBinds = facsService.getFacsWithMeterDev(areaCode, "W", "W2", 45);
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(facsMeterBinds)) {
|
|
|
+ for (BoundaryObj facsMeterBind : facsMeterBinds) {
|
|
|
+ // 过滤出当前设施绑定表计对应的小时计量数据
|
|
|
+ List<ElecMeterH> subElecMeterHs = filterBindElecMeterDevs(facsMeterBind, allElecMeterHList);
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(subElecMeterHs)) {
|
|
|
+ // 直接累加同一设施下的所有电表数据(同一小时电价类型相同)
|
|
|
+ double totalQuantity = subElecMeterHs.stream().mapToDouble(ElecMeterH::getElecQuantity)
|
|
|
+ .sum();
|
|
|
+
|
|
|
+ double totalCost = subElecMeterHs.stream().mapToDouble(ElecMeterH::getUseElecCost).sum();
|
|
|
+
|
|
|
+ // 精度控制:用量保留1位小数,金额保留2位小数
|
|
|
+ totalQuantity = formatToDecimalPlaces(totalQuantity, 1);
|
|
|
+ totalCost = formatToDecimalPlaces(totalCost, 2);
|
|
|
+
|
|
|
+ // 获取电价信息(同一小时所有表计的电价类型和单价都相同,取第一个即可)
|
|
|
+ ElecMeterH firstMeter = subElecMeterHs.get(0);
|
|
|
+ Integer meterType = firstMeter.getMeterType();
|
|
|
+ Double unitPrice = firstMeter.getMeterUnitPrice();
|
|
|
+
|
|
|
+ // 创建市电供应记录
|
|
|
+ ElecPgSupplyH pgSupplyH = new ElecPgSupplyH();
|
|
|
+ pgSupplyH.setAreaCode(areaCode);
|
|
|
+ pgSupplyH.setFacsCode(facsMeterBind.getObjCode());
|
|
|
+ pgSupplyH.setRecordTime(recordTime);
|
|
|
+ pgSupplyH.setDate(recordTime);
|
|
|
+ pgSupplyH.setTime(recordTime);
|
|
|
+ pgSupplyH.setTimeIndex(getHourIndex(recordTime));
|
|
|
+ pgSupplyH.setMeterType(meterType);
|
|
|
+ pgSupplyH.setMeterUnitPrice(unitPrice);
|
|
|
+ pgSupplyH.setUseElecQuantity(totalQuantity);
|
|
|
+ pgSupplyH.setUseElecCost(totalCost);
|
|
|
+ pgSupplyHList.add(pgSupplyH);
|
|
|
+
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ Price price = priceMap.computeIfAbsent(areaCode,
|
|
|
+ k -> priceService.getElecHourPrice(areaCode, recordTime));
|
|
|
+
|
|
|
+ ElecPgSupplyH pgSupplyH = new ElecPgSupplyH();
|
|
|
+ pgSupplyH.setAreaCode(areaCode);
|
|
|
+ pgSupplyH.setFacsCode(facsMeterBind.getObjCode());
|
|
|
+ pgSupplyH.setRecordTime(recordTime);
|
|
|
+ pgSupplyH.setDate(recordTime);
|
|
|
+ pgSupplyH.setTime(recordTime);
|
|
|
+ pgSupplyH.setTimeIndex(getHourIndex(recordTime));
|
|
|
+ pgSupplyH.setMeterType(price.getMeterType());
|
|
|
+ pgSupplyH.setMeterUnitPrice(price.getPriceValue());
|
|
|
+ pgSupplyH.setUseElecQuantity(0.0);
|
|
|
+ pgSupplyH.setUseElecCost(0.0);
|
|
|
+ pgSupplyHList.add(pgSupplyH);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ log.warn("未找到市电设施配置,无法产出市电供应数据");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(pgSupplyHList)) {
|
|
|
+ pgSupplyHService.insertBatch(pgSupplyHList);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ log.error("产出市电供应计量数据异常", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<ElecMeterH> filterBindElecMeterDevs(BoundaryObj facsMeterBind, List<ElecMeterH> allElecMeterHList) {
|
|
|
+ Set<String> bindElecMeterDevs = facsMeterBind.getBindMeterDevs();
|
|
|
+ if (CollectionUtils.isEmpty(bindElecMeterDevs)) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ return allElecMeterHList.stream().filter(elecMeterH -> bindElecMeterDevs.contains(elecMeterH.getDeviceCode()))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证抄表数据的合理性
|
|
|
+ */
|
|
|
+ private DataValidationResult validateMeterData(String deviceCode, String pointId, String oldValue,
|
|
|
+ String newValue) {
|
|
|
+ try {
|
|
|
+ // 新数据为空或无效
|
|
|
+ if (StringUtils.isBlank(newValue)) {
|
|
|
+ return DataValidationResult.invalid("新值为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ double newVal = Double.parseDouble(newValue);
|
|
|
+
|
|
|
+ // 首次数据,直接接受
|
|
|
+ if (StringUtils.isBlank(oldValue)) {
|
|
|
+ return DataValidationResult.valid(newValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ double oldVal = Double.parseDouble(oldValue);
|
|
|
+ double difference = newVal - oldVal;
|
|
|
+
|
|
|
+ // 1. 检查是否为明显的向后跳变(新值比旧值小很多)
|
|
|
+ if (difference < MIN_REASONABLE_DECREASE_RATE) {
|
|
|
+ // 检查是否可能是表计重置(新值很小,旧值很大)
|
|
|
+ if (newVal < 100 && oldVal > 10000) {
|
|
|
+ log.info("检测到可能的表计重置 - 设备:{}, 测点:{}, 旧值:{}, 新值:{}", deviceCode, pointId, oldVal,
|
|
|
+ newVal);
|
|
|
+ // 表计重置的情况,需要人工确认,暂时不更新
|
|
|
+ return DataValidationResult.invalid("疑似表计重置,需人工确认");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return DataValidationResult.invalid(String.format("数据向后跳变,差值:%.2f", difference));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 检查是否增长过快(可能是数据错误)
|
|
|
+ if (difference > MAX_HOUR_INCREASE_RATE) {
|
|
|
+ return DataValidationResult.invalid(String.format("增长过快,差值:%.2f", difference));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 连续异常数据确认机制
|
|
|
+ if (Math.abs(difference) > MAX_HOUR_INCREASE_RATE * 0.1) { // 超过正常增长的10%认为需要关注
|
|
|
+ String abnormalKey = "ABNORMAL_COUNT_" + pointId;
|
|
|
+ String countStr = redisService.getCacheMapValue(deviceCode, abnormalKey);
|
|
|
+ int abnormalCount = StringUtils.isNotBlank(countStr) ? Integer.parseInt(countStr) : 0;
|
|
|
+
|
|
|
+ abnormalCount++;
|
|
|
+ redisService.setCacheMapValue(deviceCode, abnormalKey, String.valueOf(abnormalCount));
|
|
|
+
|
|
|
+ // 如果连续异常次数太多,需要告警但仍然更新数据
|
|
|
+ if (abnormalCount >= ABNORMAL_DATA_CONFIRM_COUNT) {
|
|
|
+ log.warn("连续异常数据 - 设备:{}, 测点:{}, 连续{}次异常, 当前差值:{}", deviceCode, pointId,
|
|
|
+ abnormalCount, difference);
|
|
|
+ // 重置计数
|
|
|
+ redisService.setCacheMapValue(deviceCode, abnormalKey, "0");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 数据正常,清除异常计数
|
|
|
+ String abnormalKey = "ABNORMAL_COUNT_" + pointId;
|
|
|
+ redisService.setCacheMapValue(deviceCode, abnormalKey, "0");
|
|
|
+ }
|
|
|
+
|
|
|
+ return DataValidationResult.valid(newValue);
|
|
|
+
|
|
|
+ }
|
|
|
+ catch (NumberFormatException e) {
|
|
|
+ return DataValidationResult.invalid("数据格式错误: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 数据验证结果内部类
|
|
|
+ */
|
|
|
+ private static class DataValidationResult {
|
|
|
+ private final boolean valid;
|
|
|
+
|
|
|
+ private final String validValue;
|
|
|
+
|
|
|
+ private final String reason;
|
|
|
+
|
|
|
+ private DataValidationResult(boolean valid, String validValue, String reason) {
|
|
|
+ this.valid = valid;
|
|
|
+ this.validValue = validValue;
|
|
|
+ this.reason = reason;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static DataValidationResult valid(String value) {
|
|
|
+ return new DataValidationResult(true, value, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static DataValidationResult invalid(String reason) {
|
|
|
+ return new DataValidationResult(false, null, reason);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean isValid() {
|
|
|
+ return valid;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getValidValue() {
|
|
|
+ return validValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getReason() {
|
|
|
+ return reason;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|