|
|
|
@ -46,6 +46,10 @@ public class DataResultServiceImpl implements DataResultService { |
|
|
|
private static final double ELEC_DAY_VALUE = 1000; |
|
|
|
private static final double ELEC_DAY_VALUE = 1000; |
|
|
|
private static final double OTHER_DAY_VALUE = 100; |
|
|
|
private static final double OTHER_DAY_VALUE = 100; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 仪表最大值常量(用于检测翻转)
|
|
|
|
|
|
|
|
private static final double METER_MAX_VALUE = 999999.99; // 常见仪表最大值
|
|
|
|
|
|
|
|
private static final double METER_ROLLOVER_THRESHOLD = 0.1; // 翻转后新值应该很小
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void saveDataResult(DataResultEntity dataResultEntity) { |
|
|
|
public void saveDataResult(DataResultEntity dataResultEntity) { |
|
|
|
if (dataResultEntity == null) { |
|
|
|
if (dataResultEntity == null) { |
|
|
|
@ -63,7 +67,7 @@ public class DataResultServiceImpl implements DataResultService { |
|
|
|
Date formattedDate = formatToFiveMinuteInterval(dataResultEntity.getCurDate()); |
|
|
|
Date formattedDate = formatToFiveMinuteInterval(dataResultEntity.getCurDate()); |
|
|
|
dataResultEntity.setCurDate(formattedDate); |
|
|
|
dataResultEntity.setCurDate(formattedDate); |
|
|
|
|
|
|
|
|
|
|
|
// 从安装表获取设备信息
|
|
|
|
// 从安装表获取设备信息(使用最新数据)
|
|
|
|
DeviceInstallEntity deviceInstallEntity = deviceInstallMapper.selectDevice( |
|
|
|
DeviceInstallEntity deviceInstallEntity = deviceInstallMapper.selectDevice( |
|
|
|
dataResultEntity.getDeviceAddr(), |
|
|
|
dataResultEntity.getDeviceAddr(), |
|
|
|
dataResultEntity.getDeviceType(), |
|
|
|
dataResultEntity.getDeviceType(), |
|
|
|
@ -87,8 +91,31 @@ public class DataResultServiceImpl implements DataResultService { |
|
|
|
double maxIncrement = isElectricMeter ? ELEC_MAX_INCREMENT : OTHER_MAX_INCREMENT; |
|
|
|
double maxIncrement = isElectricMeter ? ELEC_MAX_INCREMENT : OTHER_MAX_INCREMENT; |
|
|
|
dayValue = isElectricMeter ? ELEC_DAY_VALUE : OTHER_DAY_VALUE; |
|
|
|
dayValue = isElectricMeter ? ELEC_DAY_VALUE : OTHER_DAY_VALUE; |
|
|
|
|
|
|
|
|
|
|
|
// 检查增量是否异常
|
|
|
|
// 检查当前值是否有效(防止0值或负值)
|
|
|
|
if (dataResultEntity.getCurValue() - lastValueFromInstall > maxIncrement) { |
|
|
|
if (dataResultEntity.getCurValue() <= 0) { |
|
|
|
|
|
|
|
logger.warn("saveDataResult: curValue is invalid (<=0), skip. curValue={}, deviceAddr={}", |
|
|
|
|
|
|
|
dataResultEntity.getCurValue(), dataResultEntity.getDeviceAddr()); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检测仪表翻转(rollover):从最大值归零重新计数
|
|
|
|
|
|
|
|
boolean isRollover = false; |
|
|
|
|
|
|
|
if (lastValueFromInstall > 0 && dataResultEntity.getCurValue() < lastValueFromInstall) { |
|
|
|
|
|
|
|
// 判断是否为正常翻转:旧值接近最大值,新值很小
|
|
|
|
|
|
|
|
if (lastValueFromInstall >= (METER_MAX_VALUE * 0.9) && dataResultEntity.getCurValue() <= METER_ROLLOVER_THRESHOLD) { |
|
|
|
|
|
|
|
isRollover = true; |
|
|
|
|
|
|
|
logger.info("saveDataResult: detected meter rollover. lastValue={}, curValue={}, deviceAddr={}", |
|
|
|
|
|
|
|
lastValueFromInstall, dataResultEntity.getCurValue(), dataResultEntity.getDeviceAddr()); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// 非翻转情况,视为异常数据回退
|
|
|
|
|
|
|
|
logger.warn("saveDataResult: curValue less than lastValue (not rollover), skip. curValue={}, lastValue={}, deviceAddr={}", |
|
|
|
|
|
|
|
dataResultEntity.getCurValue(), lastValueFromInstall, dataResultEntity.getDeviceAddr()); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查增量是否异常(翻转时跳过此检查)
|
|
|
|
|
|
|
|
if (!isRollover && lastValueFromInstall > 0 && (dataResultEntity.getCurValue() - lastValueFromInstall) > maxIncrement) { |
|
|
|
logger.info("saveDataResult: increment too large, skip. curValue={}, lastValue={}, deviceAddr={}", |
|
|
|
logger.info("saveDataResult: increment too large, skip. curValue={}, lastValue={}, deviceAddr={}", |
|
|
|
dataResultEntity.getCurValue(), lastValueFromInstall, dataResultEntity.getDeviceAddr()); |
|
|
|
dataResultEntity.getCurValue(), lastValueFromInstall, dataResultEntity.getDeviceAddr()); |
|
|
|
return; |
|
|
|
return; |
|
|
|
@ -118,11 +145,14 @@ public class DataResultServiceImpl implements DataResultService { |
|
|
|
|
|
|
|
|
|
|
|
shouldSave = validateAndSaveData(newData, calcValue, dayValue, days, true); |
|
|
|
shouldSave = validateAndSaveData(newData, calcValue, dayValue, days, true); |
|
|
|
|
|
|
|
|
|
|
|
// if (shouldSave) {
|
|
|
|
|
|
|
|
// lastValue = newData.getLastValue();
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// 修改现有记录
|
|
|
|
// 修改现有记录 - 重新查询最新数据避免并发问题
|
|
|
|
|
|
|
|
DeviceInstallEntity latestDeviceInstall = deviceInstallMapper.selectDevice( |
|
|
|
|
|
|
|
dataResultEntity.getDeviceAddr(), |
|
|
|
|
|
|
|
dataResultEntity.getDeviceType(), |
|
|
|
|
|
|
|
dataResultEntity.getBuildingId()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (latestDeviceInstall != null) { |
|
|
|
DataResultEntity existingData = dataResultMapper.selectDataResult( |
|
|
|
DataResultEntity existingData = dataResultMapper.selectDataResult( |
|
|
|
curDateStr, |
|
|
|
curDateStr, |
|
|
|
dataResultEntity.getDeviceAddr(), |
|
|
|
dataResultEntity.getDeviceAddr(), |
|
|
|
@ -131,40 +161,65 @@ public class DataResultServiceImpl implements DataResultService { |
|
|
|
|
|
|
|
|
|
|
|
if (existingData != null) { |
|
|
|
if (existingData != null) { |
|
|
|
double lastValue = existingData.getLastValue(); |
|
|
|
double lastValue = existingData.getLastValue(); |
|
|
|
calcValue = (dataResultEntity.getCurValue() - lastValue) * ratio; |
|
|
|
|
|
|
|
calcValue = formatDouble(calcValue); |
|
|
|
// 验证lastValue有效性
|
|
|
|
|
|
|
|
if (lastValue <= 0) { |
|
|
|
|
|
|
|
logger.warn("saveDataResult: existing lastValue is invalid, use device install lastValue. lastValue={}, deviceAddr={}", |
|
|
|
|
|
|
|
lastValue, dataResultEntity.getDeviceAddr()); |
|
|
|
|
|
|
|
lastValue = latestDeviceInstall.getLastValue(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检测是否为仪表翻转
|
|
|
|
|
|
|
|
boolean isUpdateRollover = false; |
|
|
|
|
|
|
|
if (dataResultEntity.getCurValue() < lastValue && lastValue >= (METER_MAX_VALUE * 0.9)) { |
|
|
|
|
|
|
|
isUpdateRollover = true; |
|
|
|
|
|
|
|
logger.info("saveDataResult: detected rollover in update. curValue={}, lastValue={}, deviceAddr={}", |
|
|
|
|
|
|
|
dataResultEntity.getCurValue(), lastValue, dataResultEntity.getDeviceAddr()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double updateCalcValue; |
|
|
|
|
|
|
|
if (isUpdateRollover) { |
|
|
|
|
|
|
|
// 翻转情况:计算值 = (最大值 - 上次值 + 当前值) * 倍率
|
|
|
|
|
|
|
|
updateCalcValue = (METER_MAX_VALUE - lastValue + dataResultEntity.getCurValue()) * ratio; |
|
|
|
|
|
|
|
logger.info("saveDataResult: calculated rollover value in update. ({} - {} + {}) * {} = {}", |
|
|
|
|
|
|
|
METER_MAX_VALUE, lastValue, dataResultEntity.getCurValue(), ratio, updateCalcValue); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// 正常情况:计算值 = (当前值 - 上次值) * 倍率
|
|
|
|
|
|
|
|
updateCalcValue = (dataResultEntity.getCurValue() - lastValue) * ratio; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateCalcValue = formatDouble(updateCalcValue); |
|
|
|
|
|
|
|
|
|
|
|
existingData.setCurDate(formattedDate); |
|
|
|
existingData.setCurDate(formattedDate); |
|
|
|
existingData.setCurValue(dataResultEntity.getCurValue()); |
|
|
|
existingData.setCurValue(dataResultEntity.getCurValue()); |
|
|
|
existingData.setCalcValue(calcValue); |
|
|
|
existingData.setCalcValue(updateCalcValue); |
|
|
|
|
|
|
|
|
|
|
|
Date lastDate = existingData.getLastDate(); |
|
|
|
Date lastDate = existingData.getLastDate(); |
|
|
|
int days = (int) ExchangeStringUtil.daysBetween(formattedDate, lastDate); |
|
|
|
int days = (int) ExchangeStringUtil.daysBetween(formattedDate, lastDate); |
|
|
|
|
|
|
|
|
|
|
|
shouldSave = validateAndSaveData(existingData, calcValue, dayValue, days, false); |
|
|
|
shouldSave = validateAndSaveData(existingData, updateCalcValue, dayValue, days, false); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 更新安装表中的lastValue和lastDate
|
|
|
|
// 只有在数据验证通过时才更新安装表中的lastValue和lastDate
|
|
|
|
if (shouldSave) { |
|
|
|
if (shouldSave) { |
|
|
|
deviceInstallMapper.updateLastValue( |
|
|
|
deviceInstallMapper.updateLastValue( |
|
|
|
deviceInstallEntity.getId(), |
|
|
|
deviceInstallEntity.getId(), |
|
|
|
String.valueOf(dataResultEntity.getCurValue()), |
|
|
|
String.valueOf(dataResultEntity.getCurValue()), |
|
|
|
currentDate); |
|
|
|
currentDate); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 第一次采集时初始化initValue
|
|
|
|
// 第一次采集时初始化initValue(必须在更新lastValue成功后)
|
|
|
|
if (initValue == 0) { |
|
|
|
if (initValue == 0 || initValue < 0) { |
|
|
|
deviceInstallMapper.updateLastValue( |
|
|
|
|
|
|
|
deviceInstallEntity.getId(), |
|
|
|
|
|
|
|
String.valueOf(dataResultEntity.getCurValue()), |
|
|
|
|
|
|
|
currentDate); |
|
|
|
|
|
|
|
deviceInstallMapper.updateInitValue( |
|
|
|
deviceInstallMapper.updateInitValue( |
|
|
|
dataResultEntity.getDeviceAddr(), |
|
|
|
dataResultEntity.getDeviceAddr(), |
|
|
|
dataResultEntity.getDeviceType(), |
|
|
|
dataResultEntity.getDeviceType(), |
|
|
|
dataResultEntity.getBuildingId(), |
|
|
|
dataResultEntity.getBuildingId(), |
|
|
|
String.valueOf(dataResultEntity.getCurValue()), |
|
|
|
String.valueOf(dataResultEntity.getCurValue()), |
|
|
|
deviceInstallEntity.getId()); |
|
|
|
deviceInstallEntity.getId()); |
|
|
|
|
|
|
|
logger.info("saveDataResult: initialized initValue for deviceAddr={}, initValue={}", |
|
|
|
|
|
|
|
dataResultEntity.getDeviceAddr(), dataResultEntity.getCurValue()); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
} catch (Exception e) { |
|
|
|
@ -201,12 +256,29 @@ public class DataResultServiceImpl implements DataResultService { |
|
|
|
dataResultEntity.getBuildingId()); |
|
|
|
dataResultEntity.getBuildingId()); |
|
|
|
|
|
|
|
|
|
|
|
double lastValue; |
|
|
|
double lastValue; |
|
|
|
|
|
|
|
boolean isRollover = false; |
|
|
|
|
|
|
|
|
|
|
|
if (lastData != null) { |
|
|
|
if (lastData != null) { |
|
|
|
data.setLastDate(lastData.getCurDate()); |
|
|
|
data.setLastDate(lastData.getCurDate()); |
|
|
|
data.setLastValue(lastData.getCurValue()); |
|
|
|
data.setLastValue(lastData.getCurValue()); |
|
|
|
lastValue = lastData.getCurValue(); |
|
|
|
lastValue = lastData.getCurValue(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 验证lastValue的有效性
|
|
|
|
|
|
|
|
if (lastValue <= 0) { |
|
|
|
|
|
|
|
logger.warn("createNewDataResult: lastData has invalid lastValue={}, use device install value", |
|
|
|
|
|
|
|
lastValue); |
|
|
|
|
|
|
|
lastValue = deviceInstallEntity.getLastValue(); |
|
|
|
|
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
lastValue = deviceInstallEntity.getLastValue(); |
|
|
|
lastValue = deviceInstallEntity.getLastValue(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 如果deviceInstall的lastValue也无效,使用当前值作为初始值
|
|
|
|
|
|
|
|
if (lastValue <= 0) { |
|
|
|
|
|
|
|
logger.info("createNewDataResult: device install lastValue is invalid, use curValue as initial. curValue={}, deviceAddr={}", |
|
|
|
|
|
|
|
dataResultEntity.getCurValue(), dataResultEntity.getDeviceAddr()); |
|
|
|
|
|
|
|
lastValue = dataResultEntity.getCurValue(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Date lastDate = deviceInstallEntity.getLastDate(); |
|
|
|
Date lastDate = deviceInstallEntity.getLastDate(); |
|
|
|
if (lastDate == null) { |
|
|
|
if (lastDate == null) { |
|
|
|
lastDate = new Date(); |
|
|
|
lastDate = new Date(); |
|
|
|
@ -215,8 +287,32 @@ public class DataResultServiceImpl implements DataResultService { |
|
|
|
data.setLastValue(lastValue); |
|
|
|
data.setLastValue(lastValue); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
double calcValue = (dataResultEntity.getCurValue() - lastValue) * ratio; |
|
|
|
// 检测是否为仪表翻转(当前值 < 上次值)
|
|
|
|
|
|
|
|
if (dataResultEntity.getCurValue() < lastValue && lastValue >= (METER_MAX_VALUE * 0.9)) { |
|
|
|
|
|
|
|
isRollover = true; |
|
|
|
|
|
|
|
logger.info("createNewDataResult: detected rollover in calculation. curValue={}, lastValue={}, deviceAddr={}", |
|
|
|
|
|
|
|
dataResultEntity.getCurValue(), lastValue, dataResultEntity.getDeviceAddr()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double calcValue; |
|
|
|
|
|
|
|
if (isRollover) { |
|
|
|
|
|
|
|
// 翻转情况:计算值 = (最大值 - 上次值 + 当前值) * 倍率
|
|
|
|
|
|
|
|
calcValue = (METER_MAX_VALUE - lastValue + dataResultEntity.getCurValue()) * ratio; |
|
|
|
|
|
|
|
logger.info("createNewDataResult: calculated rollover value. ({} - {} + {}) * {} = {}", |
|
|
|
|
|
|
|
METER_MAX_VALUE, lastValue, dataResultEntity.getCurValue(), ratio, calcValue); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// 正常情况:计算值 = (当前值 - 上次值) * 倍率
|
|
|
|
|
|
|
|
calcValue = (dataResultEntity.getCurValue() - lastValue) * ratio; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
calcValue = formatDouble(calcValue); |
|
|
|
calcValue = formatDouble(calcValue); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 验证计算值(翻转时允许较大的正值)
|
|
|
|
|
|
|
|
if (calcValue < 0) { |
|
|
|
|
|
|
|
logger.warn("createNewDataResult: calcValue is negative after calculation. curValue={}, lastValue={}, ratio={}, calcValue={}, isRollover={}", |
|
|
|
|
|
|
|
dataResultEntity.getCurValue(), lastValue, ratio, calcValue, isRollover); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
data.setCalcValue(calcValue); |
|
|
|
data.setCalcValue(calcValue); |
|
|
|
|
|
|
|
|
|
|
|
return data; |
|
|
|
return data; |
|
|
|
@ -231,11 +327,44 @@ public class DataResultServiceImpl implements DataResultService { |
|
|
|
double dayValue, |
|
|
|
double dayValue, |
|
|
|
int days, |
|
|
|
int days, |
|
|
|
boolean isNew) { |
|
|
|
boolean isNew) { |
|
|
|
|
|
|
|
// 验证calcValue有效性
|
|
|
|
if (calcValue < 0) { |
|
|
|
if (calcValue < 0) { |
|
|
|
logger.warn("calcValue is negative: {}", calcValue); |
|
|
|
logger.warn("validateAndSaveData: calcValue is negative: {}, deviceAddr={}", |
|
|
|
|
|
|
|
calcValue, dataResultEntity.getDeviceAddr()); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 验证curValue有效性
|
|
|
|
|
|
|
|
if (dataResultEntity.getCurValue() <= 0) { |
|
|
|
|
|
|
|
logger.warn("validateAndSaveData: curValue is invalid: {}, deviceAddr={}", |
|
|
|
|
|
|
|
dataResultEntity.getCurValue(), dataResultEntity.getDeviceAddr()); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检测是否为翻转情况(calcValue可能很大)
|
|
|
|
|
|
|
|
boolean isRollover = calcValue > dayValue * 10; // 如果计算值超过日值的10倍,可能是翻转
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (isRollover) { |
|
|
|
|
|
|
|
// 翻转情况:只要calcValue为正且合理,就允许保存
|
|
|
|
|
|
|
|
// 这里假设翻转后的用量不会超过30天的正常用量
|
|
|
|
|
|
|
|
double maxRolloverValue = dayValue * 30; |
|
|
|
|
|
|
|
if (calcValue <= maxRolloverValue) { |
|
|
|
|
|
|
|
if (isNew) { |
|
|
|
|
|
|
|
dataResultMapper.saveDataResult(dataResultEntity); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
dataResultMapper.updateDataResult(dataResultEntity); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
logger.info("validateAndSaveData: saved rollover data. calcValue={}, dayValue={}, deviceAddr={}", |
|
|
|
|
|
|
|
calcValue, dayValue, dataResultEntity.getDeviceAddr()); |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
logger.warn("validateAndSaveData: rollover calcValue too large: calcValue={}, maxValue={}, deviceAddr={}", |
|
|
|
|
|
|
|
calcValue, maxRolloverValue, dataResultEntity.getDeviceAddr()); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 正常情况的验证逻辑
|
|
|
|
if (calcValue <= dayValue) { |
|
|
|
if (calcValue <= dayValue) { |
|
|
|
if (isNew) { |
|
|
|
if (isNew) { |
|
|
|
dataResultMapper.saveDataResult(dataResultEntity); |
|
|
|
dataResultMapper.saveDataResult(dataResultEntity); |
|
|
|
@ -248,7 +377,8 @@ public class DataResultServiceImpl implements DataResultService { |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
logger.warn("calcValue exceeds dayValue: calcValue={}, dayValue={}, days={}", calcValue, dayValue, days); |
|
|
|
logger.warn("validateAndSaveData: calcValue exceeds dayValue: calcValue={}, dayValue={}, days={}, deviceAddr={}", |
|
|
|
|
|
|
|
calcValue, dayValue, days, dataResultEntity.getDeviceAddr()); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|