|
|
|
@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
|
|
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
|
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
|
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
|
|
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
|
|
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
|
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.controller.admin.device.enums.JavaToTdengineTypeEnum;
|
|
|
|
import cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo.HourEnergyValueVO;
|
|
|
|
import cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo.HourEnergyValueVO;
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO;
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO;
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactModelMapper;
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactModelMapper;
|
|
|
|
@ -28,11 +29,13 @@ import org.springframework.validation.annotation.Validated;
|
|
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
|
|
|
import java.math.RoundingMode;
|
|
|
|
import java.text.DecimalFormat;
|
|
|
|
import java.text.DecimalFormat;
|
|
|
|
import java.time.LocalDate;
|
|
|
|
import java.time.LocalDate;
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
import java.util.*;
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
import java.util.function.Function;
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
|
@ -252,53 +255,54 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
|
|
|
|
return energyDeviceMapper.selectBatchIds(ids);
|
|
|
|
return energyDeviceMapper.selectBatchIds(ids);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
// @Override
|
|
|
|
public List<EnergyDeviceRespVO> queryDataRecords(EnergyDeviceConsumptionReqVO req) {
|
|
|
|
// public List<EnergyDeviceRespVO> queryDataRecords(EnergyDeviceConsumptionReqVO req) {
|
|
|
|
List<EnergyDeviceRespVO> result = new ArrayList<>();
|
|
|
|
// List<EnergyDeviceRespVO> result = new ArrayList<>();
|
|
|
|
|
|
|
|
//
|
|
|
|
// 查询设备列表
|
|
|
|
// // 查询设备列表
|
|
|
|
List<EnergyDeviceDO> devices = queryDevices(req);
|
|
|
|
// List<EnergyDeviceDO> devices = queryDevices(req);
|
|
|
|
if (devices.isEmpty()) return result;
|
|
|
|
// if (devices.isEmpty()) return result;
|
|
|
|
|
|
|
|
//
|
|
|
|
// 解析规则 & 收集所有 ruleDeviceIds 和点位ID
|
|
|
|
// // 解析规则 & 收集所有 ruleDeviceIds 和点位ID
|
|
|
|
Map<Long, List<OperationRulesVO>> deviceRulesMap = new HashMap<>();
|
|
|
|
// Map<Long, List<OperationRulesVO>> deviceRulesMap = new HashMap<>();
|
|
|
|
Set<Long> ruleDeviceIds = new HashSet<>();
|
|
|
|
// Set<Long> ruleDeviceIds = new HashSet<>();
|
|
|
|
Set<Long> pointIds = new HashSet<>();
|
|
|
|
// Set<Long> pointIds = new HashSet<>();
|
|
|
|
for (EnergyDeviceDO device : devices) {
|
|
|
|
// for (EnergyDeviceDO device : devices) {
|
|
|
|
List<OperationRulesVO> rules = parseRules(device);
|
|
|
|
// List<OperationRulesVO> rules = parseRules(device);
|
|
|
|
if (!rules.isEmpty()) {
|
|
|
|
// if (!rules.isEmpty()) {
|
|
|
|
deviceRulesMap.put(device.getId(), rules);
|
|
|
|
// deviceRulesMap.put(device.getId(), rules);
|
|
|
|
rules.forEach(r -> {
|
|
|
|
// rules.forEach(r -> {
|
|
|
|
if (r.getDeviceId() != null) ruleDeviceIds.add(r.getDeviceId());
|
|
|
|
// if (r.getDeviceId() != null) ruleDeviceIds.add(r.getDeviceId());
|
|
|
|
if (r.getPointId() != null) pointIds.add(r.getPointId());
|
|
|
|
// if (r.getPointId() != null) pointIds.add(r.getPointId());
|
|
|
|
});
|
|
|
|
// });
|
|
|
|
}
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (ruleDeviceIds.isEmpty()) return result;
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // TDengine 查询所有设备首末数据
|
|
|
|
|
|
|
|
// Map<Long, DeviceEdgeData> edgeDataMap =
|
|
|
|
|
|
|
|
// tDengineService.queryDeviceFirstAndLast(ruleDeviceIds, req.getStartTime(), req.getEndTime());
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // 构建缓存
|
|
|
|
|
|
|
|
// Map<Long, TimePointCache> firstCache = buildCache(edgeDataMap, true);
|
|
|
|
|
|
|
|
// Map<Long, TimePointCache> lastCache = buildCache(edgeDataMap, false);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // 批量获取点位信息(名称/单位)避免 N+1 查询
|
|
|
|
|
|
|
|
// Map<Long, DeviceContactModelDO> pointInfoMap = batchGetPointInfoFromLocalDB(new ArrayList<>(pointIds));
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // 逐设备计算 EnergyDeviceRespVO
|
|
|
|
|
|
|
|
// for (EnergyDeviceDO device : devices) {
|
|
|
|
|
|
|
|
// List<OperationRulesVO> rules = deviceRulesMap.get(device.getId());
|
|
|
|
|
|
|
|
// if (rules == null || rules.isEmpty()) continue;
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// EnergyDeviceRespVO vo = calculateDevice(device, rules, firstCache, lastCache, pointInfoMap, req);
|
|
|
|
|
|
|
|
// result.add(vo);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// return result;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
if (ruleDeviceIds.isEmpty()) return result;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TDengine 查询所有设备首末数据
|
|
|
|
|
|
|
|
Map<Long, DeviceEdgeData> edgeDataMap =
|
|
|
|
|
|
|
|
tDengineService.queryDeviceFirstAndLast(ruleDeviceIds, req.getStartTime(), req.getEndTime());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 构建缓存
|
|
|
|
|
|
|
|
Map<Long, TimePointCache> firstCache = buildCache(edgeDataMap, true);
|
|
|
|
|
|
|
|
Map<Long, TimePointCache> lastCache = buildCache(edgeDataMap, false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 批量获取点位信息(名称/单位)避免 N+1 查询
|
|
|
|
|
|
|
|
Map<Long, DeviceContactModelDO> pointInfoMap = batchGetPointInfoFromLocalDB(new ArrayList<>(pointIds));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 逐设备计算 EnergyDeviceRespVO
|
|
|
|
|
|
|
|
for (EnergyDeviceDO device : devices) {
|
|
|
|
|
|
|
|
List<OperationRulesVO> rules = deviceRulesMap.get(device.getId());
|
|
|
|
|
|
|
|
if (rules == null || rules.isEmpty()) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EnergyDeviceRespVO vo = calculateDevice(device, rules, firstCache, lastCache, pointInfoMap, req);
|
|
|
|
|
|
|
|
result.add(vo);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
public List<HourEnergyValueVO> lastEnergyStatistics(Long deviceTypeId, Long orgId) {
|
|
|
|
public List<HourEnergyValueVO> lastEnergyStatistics(Long deviceTypeId, Long orgId) {
|
|
|
|
@ -316,7 +320,6 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
|
|
|
|
return buildEmptyLast7Hours(LocalDateTime.now());
|
|
|
|
return buildEmptyLast7Hours(LocalDateTime.now());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 解析规则
|
|
|
|
// 2. 解析规则
|
|
|
|
List<OperationRulesVO> rules = JsonUtils.parseArray(energyDevice.getRules(), OperationRulesVO.class);
|
|
|
|
List<OperationRulesVO> rules = JsonUtils.parseArray(energyDevice.getRules(), OperationRulesVO.class);
|
|
|
|
if (rules.isEmpty()) return Collections.emptyList();
|
|
|
|
if (rules.isEmpty()) return Collections.emptyList();
|
|
|
|
@ -328,43 +331,86 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
|
|
|
|
.collect(Collectors.toSet());
|
|
|
|
.collect(Collectors.toSet());
|
|
|
|
if (deviceIds.isEmpty()) return Collections.emptyList();
|
|
|
|
if (deviceIds.isEmpty()) return Collections.emptyList();
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 时间范围:最近 7 小时
|
|
|
|
// 4. 查询设备点位信息 DeviceContactModelDO
|
|
|
|
|
|
|
|
List<Long> pointIds = rules.stream()
|
|
|
|
|
|
|
|
.map(OperationRulesVO::getPointId)
|
|
|
|
|
|
|
|
.filter(Objects::nonNull)
|
|
|
|
|
|
|
|
.distinct()
|
|
|
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
|
|
Map<Long, DeviceContactModelDO> pointInfoMap = deviceContactModelMapper.selectList(
|
|
|
|
|
|
|
|
Wrappers.<DeviceContactModelDO>lambdaQuery().in(DeviceContactModelDO::getId, pointIds)
|
|
|
|
|
|
|
|
).stream().collect(Collectors.toMap(DeviceContactModelDO::getId, Function.identity()));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 时间范围:最近 7 小时
|
|
|
|
LocalDateTime end = LocalDateTime.now();
|
|
|
|
LocalDateTime end = LocalDateTime.now();
|
|
|
|
LocalDateTime start = end.minusHours(7);
|
|
|
|
LocalDateTime start = end.minusHours(7);
|
|
|
|
DateTimeFormatter hourFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");
|
|
|
|
DateTimeFormatter hourFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 批量查询所有设备数据
|
|
|
|
// 6. 批量查询 TDengine 数据
|
|
|
|
List<Map<String, Object>> allRows = tDengineService.queryLastDataByHourBatch(deviceIds, start, end);
|
|
|
|
List<Map<String, Object>> allRows = tDengineService.newQueryLastDataByHourBatch(deviceIds, start, end);
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 按小时 + 设备构建缓存(每小时最新覆盖)
|
|
|
|
// 7. 按小时 + 设备构建缓存
|
|
|
|
Map<String, Map<Long, TimePointCache>> hourCacheMap = new LinkedHashMap<>();
|
|
|
|
Map<String, Map<Long, TimePointCache>> hourCacheMap = new LinkedHashMap<>();
|
|
|
|
|
|
|
|
|
|
|
|
for (Map<String, Object> row : allRows) {
|
|
|
|
for (Map<String, Object> row : allRows) {
|
|
|
|
LocalDateTime ts = (LocalDateTime) row.get("timestamp");
|
|
|
|
LocalDateTime ts = (LocalDateTime) row.get("timestamp");
|
|
|
|
if (ts == null) continue;
|
|
|
|
if (ts == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
String hourKey = ts.format(hourFormatter);
|
|
|
|
String hourKey = ts.format(hourFormatter);
|
|
|
|
Long deviceId = (Long) row.get("deviceId");
|
|
|
|
Long deviceId = (Long) row.get("deviceId");
|
|
|
|
String queryData = (String) row.get("queryData");
|
|
|
|
|
|
|
|
|
|
|
|
// 过滤该设备的规则
|
|
|
|
|
|
|
|
List<OperationRulesVO> deviceRules = rules.stream()
|
|
|
|
|
|
|
|
.filter(r -> deviceId.equals(r.getDeviceId()))
|
|
|
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
|
|
|
|
|
|
TimePointCache cache = new TimePointCache();
|
|
|
|
TimePointCache cache = new TimePointCache();
|
|
|
|
cache.setTimestamp(ts);
|
|
|
|
cache.setTimestamp(ts);
|
|
|
|
cache.setQueryData(queryData);
|
|
|
|
|
|
|
|
|
|
|
|
Map<Long, Double> pointValueMap = new HashMap<>();
|
|
|
|
|
|
|
|
for (OperationRulesVO rule : deviceRules) {
|
|
|
|
|
|
|
|
Long pointId = rule.getPointId();
|
|
|
|
|
|
|
|
DeviceContactModelDO pointInfo = pointInfoMap.get(pointId);
|
|
|
|
|
|
|
|
if (pointInfo == null || !deviceId.equals(pointInfo.getDeviceId())) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String attributeCode = pointInfo.getAttributeCode();
|
|
|
|
|
|
|
|
if (attributeCode == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Object value = row.get(attributeCode.toLowerCase());
|
|
|
|
|
|
|
|
if (value != null) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 保留2位小数
|
|
|
|
|
|
|
|
BigDecimal bd = new BigDecimal(value.toString());
|
|
|
|
|
|
|
|
Double finalValue = bd.setScale(2, RoundingMode.HALF_UP).doubleValue();
|
|
|
|
|
|
|
|
pointValueMap.put(pointId, finalValue);
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
log.warn("点位值转换失败, pointId={}, value={}, dataType={}", pointId, value, pointInfo.getDataType(), e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cache.setPointValueMap(pointValueMap);
|
|
|
|
|
|
|
|
|
|
|
|
hourCacheMap.computeIfAbsent(hourKey, k -> new HashMap<>()).put(deviceId, cache);
|
|
|
|
hourCacheMap.computeIfAbsent(hourKey, k -> new HashMap<>()).put(deviceId, cache);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 生成每小时结果,补齐缺失小时
|
|
|
|
// 8. 生成每小时结果,补齐缺失小时
|
|
|
|
List<HourEnergyValueVO> result = new ArrayList<>();
|
|
|
|
List<HourEnergyValueVO> result = new ArrayList<>();
|
|
|
|
for (int i = 0; i < 7; i++) {
|
|
|
|
for (int i = 0; i < 7; i++) {
|
|
|
|
LocalDateTime hourTime = start.plusHours(i);
|
|
|
|
LocalDateTime hourTime = start.plusHours(i);
|
|
|
|
String hourKey = hourTime.format(hourFormatter);
|
|
|
|
String hourKey = hourTime.format(hourFormatter);
|
|
|
|
|
|
|
|
|
|
|
|
Map<Long, TimePointCache> deviceCache = hourCacheMap.getOrDefault(hourKey, new HashMap<>());
|
|
|
|
Map<Long, TimePointCache> deviceCache = hourCacheMap.getOrDefault(hourKey, new HashMap<>());
|
|
|
|
Double value = calculateByRules(rules, deviceCache);
|
|
|
|
|
|
|
|
|
|
|
|
double total = 0.0;
|
|
|
|
|
|
|
|
for (Long deviceId : deviceIds) {
|
|
|
|
|
|
|
|
total += calculateByRules(deviceId, rules, deviceCache);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HourEnergyValueVO vo = new HourEnergyValueVO();
|
|
|
|
HourEnergyValueVO vo = new HourEnergyValueVO();
|
|
|
|
vo.setHour(hourKey);
|
|
|
|
vo.setHour(hourKey);
|
|
|
|
vo.setValue(formatDouble(value));
|
|
|
|
vo.setValue(String.valueOf(total));
|
|
|
|
|
|
|
|
|
|
|
|
result.add(vo);
|
|
|
|
result.add(vo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -373,6 +419,7 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private List<HourEnergyValueVO> buildEmptyLast7Hours(LocalDateTime end) {
|
|
|
|
private List<HourEnergyValueVO> buildEmptyLast7Hours(LocalDateTime end) {
|
|
|
|
List<HourEnergyValueVO> result = new ArrayList<>();
|
|
|
|
List<HourEnergyValueVO> result = new ArrayList<>();
|
|
|
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");
|
|
|
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");
|
|
|
|
@ -404,7 +451,7 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
public List<DayEnergyValueVO> latestSevenDaysStatistics(Long deviceTypeId, Long orgId) {
|
|
|
|
public List<DayEnergyValueVO> latestSevenDaysStatistics(Long deviceTypeId, Long orgId) {
|
|
|
|
|
|
|
|
|
|
|
|
// 找最新的能耗设备配置
|
|
|
|
// 1. 找最新能耗设备
|
|
|
|
EnergyDeviceDO energyDevice = energyDeviceMapper.selectOne(
|
|
|
|
EnergyDeviceDO energyDevice = energyDeviceMapper.selectOne(
|
|
|
|
Wrappers.<EnergyDeviceDO>lambdaQuery()
|
|
|
|
Wrappers.<EnergyDeviceDO>lambdaQuery()
|
|
|
|
.eq(EnergyDeviceDO::getDeviceTypeId, deviceTypeId)
|
|
|
|
.eq(EnergyDeviceDO::getDeviceTypeId, deviceTypeId)
|
|
|
|
@ -413,85 +460,260 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
|
|
|
|
.last("LIMIT 1")
|
|
|
|
.last("LIMIT 1")
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (energyDevice == null || StringUtils.isBlank(energyDevice.getRules())) {
|
|
|
|
if (energyDevice == null || StringUtils.isBlank(energyDevice.getRules())) {
|
|
|
|
return buildEmptyLast7Days(LocalDate.now());
|
|
|
|
return buildEmptyLast7Days(LocalDate.now());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析规则
|
|
|
|
// 2. 解析规则
|
|
|
|
List<OperationRulesVO> rules =
|
|
|
|
List<OperationRulesVO> rules = JsonUtils.parseArray(energyDevice.getRules(), OperationRulesVO.class);
|
|
|
|
JsonUtils.parseArray(energyDevice.getRules(), OperationRulesVO.class);
|
|
|
|
if (rules.isEmpty()) return Collections.emptyList();
|
|
|
|
|
|
|
|
|
|
|
|
if (rules.isEmpty()) {
|
|
|
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 收集 deviceId
|
|
|
|
// 3. 收集设备列表
|
|
|
|
Set<Long> deviceIds = rules.stream()
|
|
|
|
Set<Long> deviceIds = rules.stream()
|
|
|
|
.map(OperationRulesVO::getDeviceId)
|
|
|
|
.map(OperationRulesVO::getDeviceId)
|
|
|
|
.filter(Objects::nonNull)
|
|
|
|
.filter(Objects::nonNull)
|
|
|
|
.collect(Collectors.toSet());
|
|
|
|
.collect(Collectors.toSet());
|
|
|
|
|
|
|
|
if (deviceIds.isEmpty()) return Collections.emptyList();
|
|
|
|
|
|
|
|
|
|
|
|
if (deviceIds.isEmpty()) {
|
|
|
|
// 4. 查询设备点位信息 DeviceContactModelDO
|
|
|
|
return Collections.emptyList();
|
|
|
|
List<Long> pointIds = rules.stream()
|
|
|
|
}
|
|
|
|
.map(OperationRulesVO::getPointId)
|
|
|
|
|
|
|
|
.filter(Objects::nonNull)
|
|
|
|
|
|
|
|
.distinct()
|
|
|
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Map<Long, DeviceContactModelDO> pointInfoMap = deviceContactModelMapper.selectList(
|
|
|
|
|
|
|
|
Wrappers.<DeviceContactModelDO>lambdaQuery().in(DeviceContactModelDO::getId, pointIds)
|
|
|
|
|
|
|
|
).stream().collect(Collectors.toMap(DeviceContactModelDO::getId, Function.identity()));
|
|
|
|
|
|
|
|
|
|
|
|
// 时间范围:前 7 天
|
|
|
|
// 5. 时间范围:最近 7 天
|
|
|
|
LocalDate startDate = LocalDate.now().minusDays(7);
|
|
|
|
LocalDate startDate = LocalDate.now().minusDays(7);
|
|
|
|
int days = 7;
|
|
|
|
int days = 7;
|
|
|
|
|
|
|
|
|
|
|
|
// 查询 TDengine(方案 B)
|
|
|
|
// 6. 批量查询 TDengine 数据(按天)
|
|
|
|
List<Map<String, Object>> rows =
|
|
|
|
List<Map<String, Object>> allRows = tDengineService.queryLastDataByDayBatch(deviceIds, startDate, days);
|
|
|
|
tDengineService.queryLastDataByDaySafe(deviceIds, startDate, days);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 构建按天 + 设备缓存
|
|
|
|
Map<String, Map<Long, TimePointCache>> dayCacheMap = new LinkedHashMap<>();
|
|
|
|
Map<String, Map<Long, TimePointCache>> dayCacheMap = new LinkedHashMap<>();
|
|
|
|
|
|
|
|
|
|
|
|
for (Map<String, Object> row : rows) {
|
|
|
|
for (Map<String, Object> row : allRows) {
|
|
|
|
String day = (String) row.get("day");
|
|
|
|
String dayKey = (String) row.get("day");
|
|
|
|
Long deviceId = (Long) row.get("deviceId");
|
|
|
|
Long deviceId = (Long) row.get("deviceId");
|
|
|
|
LocalDateTime ts = (LocalDateTime) row.get("timestamp");
|
|
|
|
LocalDateTime ts = (LocalDateTime) row.get("timestamp");
|
|
|
|
|
|
|
|
if (dayKey == null || deviceId == null || ts == null) continue;
|
|
|
|
if (day == null || deviceId == null || ts == null) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TimePointCache cache = new TimePointCache();
|
|
|
|
TimePointCache cache = new TimePointCache();
|
|
|
|
cache.setTimestamp(ts);
|
|
|
|
cache.setTimestamp(ts);
|
|
|
|
cache.setQueryData((String) row.get("queryData"));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dayCacheMap
|
|
|
|
Map<Long, Double> pointValueMap = new HashMap<>();
|
|
|
|
.computeIfAbsent(day, k -> new HashMap<>())
|
|
|
|
|
|
|
|
.put(deviceId, cache);
|
|
|
|
// 设备对应规则
|
|
|
|
|
|
|
|
List<OperationRulesVO> deviceRules = rules.stream()
|
|
|
|
|
|
|
|
.filter(r -> deviceId.equals(r.getDeviceId()))
|
|
|
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (OperationRulesVO rule : deviceRules) {
|
|
|
|
|
|
|
|
Long pointId = rule.getPointId();
|
|
|
|
|
|
|
|
DeviceContactModelDO pointInfo = pointInfoMap.get(pointId);
|
|
|
|
|
|
|
|
if (pointInfo == null || !deviceId.equals(pointInfo.getDeviceId())) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String attributeCode = pointInfo.getAttributeCode();
|
|
|
|
|
|
|
|
if (attributeCode == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Object value = row.get(attributeCode.toLowerCase());
|
|
|
|
|
|
|
|
if (value != null) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
BigDecimal bd = new BigDecimal(value.toString());
|
|
|
|
|
|
|
|
pointValueMap.put(pointId, bd.setScale(2, RoundingMode.HALF_UP).doubleValue());
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
log.warn("点位值转换失败, pointId={}, value={}, dataType={}", pointId, value, pointInfo.getDataType(), e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cache.setPointValueMap(pointValueMap);
|
|
|
|
|
|
|
|
dayCacheMap.computeIfAbsent(dayKey, k -> new HashMap<>()).put(deviceId, cache);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生成结果(补齐缺失日期)
|
|
|
|
// 8. 生成结果,补齐缺失日期
|
|
|
|
List<DayEnergyValueVO> result = new ArrayList<>();
|
|
|
|
List<DayEnergyValueVO> result = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < days; i++) {
|
|
|
|
for (int i = 0; i < days; i++) {
|
|
|
|
LocalDate day = startDate.plusDays(i);
|
|
|
|
LocalDate day = startDate.plusDays(i);
|
|
|
|
String dayKey = day.toString();
|
|
|
|
String dayKey = day.toString();
|
|
|
|
|
|
|
|
|
|
|
|
Map<Long, TimePointCache> deviceCache =
|
|
|
|
Map<Long, TimePointCache> deviceCache = dayCacheMap.getOrDefault(dayKey, Collections.emptyMap());
|
|
|
|
dayCacheMap.getOrDefault(dayKey, Collections.emptyMap());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Double value = calculateByRules(rules, deviceCache);
|
|
|
|
double total = 0.0;
|
|
|
|
|
|
|
|
for (Long deviceId : deviceIds) {
|
|
|
|
|
|
|
|
total += calculateByRules(deviceId, rules, deviceCache);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DayEnergyValueVO vo = new DayEnergyValueVO();
|
|
|
|
DayEnergyValueVO vo = new DayEnergyValueVO();
|
|
|
|
vo.setDay(dayKey);
|
|
|
|
vo.setDay(dayKey);
|
|
|
|
vo.setValue(formatDouble(value));
|
|
|
|
vo.setValue(String.valueOf(total));
|
|
|
|
|
|
|
|
|
|
|
|
result.add(vo);
|
|
|
|
result.add(vo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public List<EnergyDeviceRespVO> queryDataRecords(EnergyDeviceConsumptionReqVO req) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<EnergyDeviceRespVO> result = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 查询能耗设备列表
|
|
|
|
|
|
|
|
List<EnergyDeviceDO> devices = queryDevices(req);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (devices.isEmpty()) {
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 解析规则
|
|
|
|
|
|
|
|
Map<Long, List<OperationRulesVO>> deviceRulesMap = new HashMap<>();
|
|
|
|
|
|
|
|
List<Long> ruleDeviceIds = new ArrayList<>();
|
|
|
|
|
|
|
|
List<Long> pointIds = new ArrayList<>();
|
|
|
|
|
|
|
|
for (EnergyDeviceDO device : devices) {
|
|
|
|
|
|
|
|
List<OperationRulesVO> rules = parseRules(device);
|
|
|
|
|
|
|
|
if (rules.isEmpty()) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
deviceRulesMap.put(device.getId(), rules);
|
|
|
|
|
|
|
|
for (OperationRulesVO r : rules) {
|
|
|
|
|
|
|
|
if (r.getDeviceId() != null) {
|
|
|
|
|
|
|
|
ruleDeviceIds.add(r.getDeviceId());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r.getPointId() != null) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pointIds.add(r.getPointId());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (ruleDeviceIds.isEmpty()) {
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 查询每个 device 最早数据
|
|
|
|
|
|
|
|
Map<Long, Map<String, Object>> firstRowMap = tDengineService.queryDevicesEarliestRow(ruleDeviceIds, req.getStartTime(), req.getEndTime());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 查询每个 device 最新数据
|
|
|
|
|
|
|
|
Map<Long, Map<String, Object>> lastRowMap = tDengineService.queryDevicesLatestRow(ruleDeviceIds, req.getStartTime(), req.getEndTime());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 查询点位信息(名称/单位)
|
|
|
|
|
|
|
|
Map<Long, DeviceContactModelDO> pointInfoMap = batchGetPointInfoFromLocalDB(new ArrayList<>(pointIds));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 构建缓存
|
|
|
|
|
|
|
|
Map<Long, TimePointCache> firstCache = buildCacheFromRow(devices, firstRowMap, pointInfoMap);
|
|
|
|
|
|
|
|
Map<Long, TimePointCache> lastCache = buildCacheFromRow(devices, lastRowMap, pointInfoMap);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 逐个能耗设备计算
|
|
|
|
|
|
|
|
for (EnergyDeviceDO device : devices) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<OperationRulesVO> rules = deviceRulesMap.get(device.getId());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (rules == null || rules.isEmpty()) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EnergyDeviceRespVO vo = calculateDevice(device, rules, firstCache, lastCache, pointInfoMap, req);
|
|
|
|
|
|
|
|
result.add(vo);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Map<Long, TimePointCache> buildCacheFromRow(
|
|
|
|
|
|
|
|
List<EnergyDeviceDO> devices,
|
|
|
|
|
|
|
|
Map<Long, Map<String, Object>> rowMap,
|
|
|
|
|
|
|
|
Map<Long, DeviceContactModelDO> pointInfoMap) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Map<Long, TimePointCache> cacheMap = new HashMap<>();
|
|
|
|
|
|
|
|
if (devices == null || devices.isEmpty()) return cacheMap;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (EnergyDeviceDO device : devices) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<OperationRulesVO> rules = parseRules(device);
|
|
|
|
|
|
|
|
if (rules == null || rules.isEmpty()) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TimePointCache cache = new TimePointCache();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Map<Long, Double> valueMap = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 时间戳变量
|
|
|
|
|
|
|
|
LocalDateTime timestamp = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历规则
|
|
|
|
|
|
|
|
for (OperationRulesVO rule : rules) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Long ruleDeviceId = rule.getDeviceId();
|
|
|
|
|
|
|
|
Long pointId = rule.getPointId();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (ruleDeviceId == null || pointId == null)
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DeviceContactModelDO pointInfo = pointInfoMap.get(pointId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pointInfo == null)
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 规则设备和点位设备匹配
|
|
|
|
|
|
|
|
if (!ruleDeviceId.equals(pointInfo.getDeviceId()))
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String attributeCode = pointInfo.getAttributeCode();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (attributeCode == null)
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 从 TDengine 数据里取值
|
|
|
|
|
|
|
|
Map<String, Object> row = rowMap.get(ruleDeviceId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (row == null)
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置 timestamp(只需要一次)
|
|
|
|
|
|
|
|
if (timestamp == null) {
|
|
|
|
|
|
|
|
Object ts = row.get("ts");
|
|
|
|
|
|
|
|
if (ts instanceof LocalDateTime) {
|
|
|
|
|
|
|
|
timestamp = (LocalDateTime) ts;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Object value = row.get(attributeCode);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (value != null) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
//计算类型统一转换为通用double类型
|
|
|
|
|
|
|
|
Object converted = JavaToTdengineTypeEnum.convertValue(
|
|
|
|
|
|
|
|
pointInfo.getDataType(),
|
|
|
|
|
|
|
|
value.toString()
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
Double finalValue = convertToDouble(converted);
|
|
|
|
|
|
|
|
valueMap.put(pointId, finalValue);
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
log.warn("点位值转换失败, pointId={}, value={}, dataType={}", pointId, value, pointInfo.getDataType(), e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置 timestamp
|
|
|
|
|
|
|
|
cache.setTimestamp(timestamp);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cache.setPointValueMap(valueMap);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cacheMap.put(device.getId(), cache);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return cacheMap;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// =================== 核心方法 ===================
|
|
|
|
// =================== 核心方法 ===================
|
|
|
|
|
|
|
|
|
|
|
|
@ -521,17 +743,7 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 构建缓存 */
|
|
|
|
|
|
|
|
private Map<Long, TimePointCache> buildCache(Map<Long, DeviceEdgeData> dataMap, boolean first) {
|
|
|
|
|
|
|
|
Map<Long, TimePointCache> cache = new HashMap<>();
|
|
|
|
|
|
|
|
for (DeviceEdgeData d : dataMap.values()) {
|
|
|
|
|
|
|
|
TimePointCache c = new TimePointCache();
|
|
|
|
|
|
|
|
c.setTimestamp(first ? d.getFirstTs() : d.getLastTs());
|
|
|
|
|
|
|
|
c.setQueryData(first ? d.getFirstData() : d.getLastData());
|
|
|
|
|
|
|
|
cache.put(d.getDeviceId(), c);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return cache;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 单设备计算 */
|
|
|
|
/** 单设备计算 */
|
|
|
|
private EnergyDeviceRespVO calculateDevice(EnergyDeviceDO device,
|
|
|
|
private EnergyDeviceRespVO calculateDevice(EnergyDeviceDO device,
|
|
|
|
@ -541,8 +753,9 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
|
|
|
|
Map<Long, DeviceContactModelDO> pointInfoMap,
|
|
|
|
Map<Long, DeviceContactModelDO> pointInfoMap,
|
|
|
|
EnergyDeviceConsumptionReqVO req) {
|
|
|
|
EnergyDeviceConsumptionReqVO req) {
|
|
|
|
|
|
|
|
|
|
|
|
Double firstTotal = calculateByRules(rules, firstCache);
|
|
|
|
Double firstTotal = calculateByRules(device.getId(), rules, firstCache);
|
|
|
|
Double lastTotal = calculateByRules(rules, lastCache);
|
|
|
|
Double lastTotal = calculateByRules(device.getId(), rules, lastCache);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EnergyDeviceRespVO vo = new EnergyDeviceRespVO();
|
|
|
|
EnergyDeviceRespVO vo = new EnergyDeviceRespVO();
|
|
|
|
vo.setId(device.getId());
|
|
|
|
vo.setId(device.getId());
|
|
|
|
@ -559,138 +772,144 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
|
|
|
|
vo.setLatestDataTime(req.getEndTime());
|
|
|
|
vo.setLatestDataTime(req.getEndTime());
|
|
|
|
|
|
|
|
|
|
|
|
// 构建点位详情
|
|
|
|
// 构建点位详情
|
|
|
|
vo.setPointDetails(buildPointDetails(rules, firstCache, lastCache, pointInfoMap));
|
|
|
|
vo.setPointDetails(buildPointDetails(device.getId(), rules, firstCache, lastCache, pointInfoMap));
|
|
|
|
|
|
|
|
|
|
|
|
return vo;
|
|
|
|
return vo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 根据规则计算总值 */
|
|
|
|
/** 根据规则计算总值 */
|
|
|
|
private Double calculateByRules(List<OperationRulesVO> rules, Map<Long, TimePointCache> cache) {
|
|
|
|
private Double calculateByRules(
|
|
|
|
Double total = null;
|
|
|
|
Long energyDeviceId,
|
|
|
|
|
|
|
|
List<OperationRulesVO> rules,
|
|
|
|
|
|
|
|
Map<Long, TimePointCache> cache) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TimePointCache c = cache.get(energyDeviceId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (c == null) {
|
|
|
|
|
|
|
|
return 0.0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BigDecimal total = null;
|
|
|
|
String lastOp = "+";
|
|
|
|
String lastOp = "+";
|
|
|
|
|
|
|
|
|
|
|
|
for (OperationRulesVO r : rules) {
|
|
|
|
for (OperationRulesVO r : rules) {
|
|
|
|
TimePointCache c = cache.get(r.getDeviceId());
|
|
|
|
|
|
|
|
if (c == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Double value = getPointValue(c, r.getPointId());
|
|
|
|
Double value = getPointValue(c, r.getPointId());
|
|
|
|
if (value == null) continue;
|
|
|
|
if (value == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BigDecimal bdValue = BigDecimal.valueOf(value);
|
|
|
|
|
|
|
|
|
|
|
|
if (total == null) {
|
|
|
|
if (total == null) {
|
|
|
|
total = value;
|
|
|
|
total = bdValue.setScale(2, RoundingMode.HALF_UP);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
total = applyOperator(total, value, StringUtils.defaultIfBlank(r.getOperator(), lastOp));
|
|
|
|
total = applyOperator(total, bdValue, StringUtils.defaultIfBlank(r.getOperator(), lastOp));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
lastOp = r.getOperator();
|
|
|
|
lastOp = r.getOperator();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return total != null ? total : 0.0;
|
|
|
|
return total != null ? total.doubleValue() : 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 延迟解析 JSON 获取点位值 */
|
|
|
|
/** 延迟解析 JSON 获取点位值 */
|
|
|
|
private Double getPointValue(TimePointCache cache, Long pointId) {
|
|
|
|
private Double getPointValue(
|
|
|
|
if (cache == null || pointId == null) return null;
|
|
|
|
TimePointCache cache,
|
|
|
|
if (cache.getPointIndex() == null) {
|
|
|
|
Long pointId) {
|
|
|
|
cache.setPointIndex(buildPointIndex(cache.getQueryData()));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
JSONObject obj = cache.getPointIndex().get(pointId);
|
|
|
|
|
|
|
|
if (obj == null) return null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Object v = obj.get("addressValue");
|
|
|
|
if (cache == null) {
|
|
|
|
if (v instanceof Number) return ((Number) v).doubleValue();
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
return Double.parseDouble(String.valueOf(v));
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
return null;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 构建 JSON 点位索引 */
|
|
|
|
Map<Long, Double> map =
|
|
|
|
private Map<Long, JSONObject> buildPointIndex(String jsonData) {
|
|
|
|
cache.getPointValueMap();
|
|
|
|
Map<Long, JSONObject> index = new HashMap<>();
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(jsonData)) return index;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Object obj = JSON.parse(jsonData);
|
|
|
|
|
|
|
|
JSONArray array;
|
|
|
|
|
|
|
|
if (obj instanceof JSONArray) {
|
|
|
|
|
|
|
|
array = (JSONArray) obj;
|
|
|
|
|
|
|
|
} else if (obj instanceof String) {
|
|
|
|
|
|
|
|
// 外层字符串 -> 再解析一次
|
|
|
|
|
|
|
|
array = JSON.parseArray((String) obj);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
array = new JSONArray();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (map == null) {
|
|
|
|
for (int i = 0; i < array.size(); i++) {
|
|
|
|
return null;
|
|
|
|
JSONObject jsonObject = array.getJSONObject(i);
|
|
|
|
|
|
|
|
Long id = jsonObject.getLong("id");
|
|
|
|
|
|
|
|
if (id != null) index.put(id, jsonObject);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
|
|
|
|
|
|
|
|
return map.get(pointId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 应用运算符 */
|
|
|
|
/** 应用运算符 */
|
|
|
|
private Double applyOperator(Double current, Double value, String operator) {
|
|
|
|
private BigDecimal applyOperator(BigDecimal current, BigDecimal value, String operator) {
|
|
|
|
if (current == null || value == null || StringUtils.isBlank(operator)) return current;
|
|
|
|
if (current == null || value == null || StringUtils.isBlank(operator)) return current;
|
|
|
|
switch (operator.trim()) {
|
|
|
|
switch (operator.trim()) {
|
|
|
|
case "+": return current + value;
|
|
|
|
case "+":
|
|
|
|
case "-": return current - value;
|
|
|
|
return current.add(value).setScale(2, RoundingMode.HALF_UP);
|
|
|
|
case "*": return current * value;
|
|
|
|
case "-":
|
|
|
|
case "/": return Math.abs(value) > 1e-6 ? current / value : current;
|
|
|
|
return current.subtract(value).setScale(2, RoundingMode.HALF_UP);
|
|
|
|
default: return current + value;
|
|
|
|
case "*":
|
|
|
|
|
|
|
|
return current.multiply(value).setScale(2, RoundingMode.HALF_UP);
|
|
|
|
|
|
|
|
case "/":
|
|
|
|
|
|
|
|
return value.abs().compareTo(BigDecimal.valueOf(1e-6)) > 0
|
|
|
|
|
|
|
|
? current.divide(value, 2, RoundingMode.HALF_UP)
|
|
|
|
|
|
|
|
: current;
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
return current.add(value).setScale(2, RoundingMode.HALF_UP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 构建点位详情列表并使用本地数据库批量获取点位信息 */
|
|
|
|
/** 构建点位详情列表并使用本地数据库批量获取点位信息 */
|
|
|
|
private List<PointDetailVO> buildPointDetails(List<OperationRulesVO> rules,
|
|
|
|
private List<PointDetailVO> buildPointDetails(
|
|
|
|
Map<Long, TimePointCache> firstCache,
|
|
|
|
Long energyDeviceId,
|
|
|
|
Map<Long, TimePointCache> lastCache,
|
|
|
|
List<OperationRulesVO> rules,
|
|
|
|
Map<Long, DeviceContactModelDO> pointInfoMap) {
|
|
|
|
Map<Long, TimePointCache> firstCache,
|
|
|
|
|
|
|
|
Map<Long, TimePointCache> lastCache,
|
|
|
|
|
|
|
|
Map<Long, DeviceContactModelDO> pointInfoMap) {
|
|
|
|
|
|
|
|
|
|
|
|
List<PointDetailVO> result = new ArrayList<>();
|
|
|
|
List<PointDetailVO> result = new ArrayList<>();
|
|
|
|
if (rules == null || rules.isEmpty()) return result;
|
|
|
|
if (rules == null || rules.isEmpty()) return result;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TimePointCache first = firstCache.get(energyDeviceId);
|
|
|
|
|
|
|
|
TimePointCache last = lastCache.get(energyDeviceId);
|
|
|
|
|
|
|
|
|
|
|
|
for (OperationRulesVO r : rules) {
|
|
|
|
for (OperationRulesVO r : rules) {
|
|
|
|
|
|
|
|
|
|
|
|
PointDetailVO p = new PointDetailVO();
|
|
|
|
PointDetailVO p = new PointDetailVO();
|
|
|
|
|
|
|
|
|
|
|
|
p.setDeviceId(r.getDeviceId());
|
|
|
|
p.setDeviceId(r.getDeviceId());
|
|
|
|
p.setPointId(r.getPointId());
|
|
|
|
p.setPointId(r.getPointId());
|
|
|
|
p.setOperator(r.getOperator());
|
|
|
|
p.setOperator(r.getOperator());
|
|
|
|
|
|
|
|
|
|
|
|
// ====== 这里是改动点 ======
|
|
|
|
|
|
|
|
TimePointCache first = firstCache.get(r.getDeviceId());
|
|
|
|
|
|
|
|
TimePointCache last = lastCache.get(r.getDeviceId());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Double firstVal = getPointValue(first, r.getPointId());
|
|
|
|
Double firstVal = getPointValue(first, r.getPointId());
|
|
|
|
Double lastVal = getPointValue(last, r.getPointId());
|
|
|
|
Double lastVal = getPointValue(last, r.getPointId());
|
|
|
|
|
|
|
|
|
|
|
|
p.setEarliestValue(formatDouble(firstVal));
|
|
|
|
p.setEarliestValue(formatDouble(firstVal));
|
|
|
|
p.setLatestValue(formatDouble(lastVal));
|
|
|
|
p.setLatestValue(formatDouble(lastVal));
|
|
|
|
|
|
|
|
|
|
|
|
p.setDifference(formatDouble(
|
|
|
|
p.setDifference(formatDouble(
|
|
|
|
(lastVal != null ? lastVal : 0.0) - (firstVal != null ? firstVal : 0.0)
|
|
|
|
(lastVal != null ? lastVal : 0.0)
|
|
|
|
|
|
|
|
- (firstVal != null ? firstVal : 0.0)
|
|
|
|
));
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
if (first != null) {
|
|
|
|
if (first != null)
|
|
|
|
p.setEarliestTime(formatToSecond(first.getTimestamp()));
|
|
|
|
p.setEarliestTime(formatToSecond(first.getTimestamp()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last != null) {
|
|
|
|
if (last != null)
|
|
|
|
p.setLatestTime(formatToSecond(last.getTimestamp()));
|
|
|
|
p.setLatestTime(formatToSecond(last.getTimestamp()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 从本地数据库获取点位名称和单位
|
|
|
|
|
|
|
|
DeviceContactModelDO pointInfo = pointInfoMap.get(r.getPointId());
|
|
|
|
DeviceContactModelDO pointInfo = pointInfoMap.get(r.getPointId());
|
|
|
|
|
|
|
|
|
|
|
|
if (pointInfo != null) {
|
|
|
|
if (pointInfo != null) {
|
|
|
|
p.setPointName(StringUtils.isNotBlank(pointInfo.getAttributeName())
|
|
|
|
|
|
|
|
? pointInfo.getAttributeName()
|
|
|
|
p.setPointName(
|
|
|
|
: pointInfo.getAttributeCode());
|
|
|
|
StringUtils.isNotBlank(pointInfo.getAttributeName())
|
|
|
|
|
|
|
|
? pointInfo.getAttributeName()
|
|
|
|
|
|
|
|
: pointInfo.getAttributeCode()
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
p.setUnit(pointInfo.getDataUnit());
|
|
|
|
p.setUnit(pointInfo.getDataUnit());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result.add(p);
|
|
|
|
result.add(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private String formatToSecond(Object timestamp) {
|
|
|
|
private String formatToSecond(Object timestamp) {
|
|
|
|
if (timestamp == null) {
|
|
|
|
if (timestamp == null) {
|
|
|
|
return "";
|
|
|
|
return "";
|
|
|
|
@ -741,7 +960,13 @@ public class EnergyDeviceServiceImpl implements EnergyDeviceService {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Double convertToDouble(Object value) {
|
|
|
|
|
|
|
|
if (value == null) return null;
|
|
|
|
|
|
|
|
if (value instanceof Number) return ((Number) value).doubleValue();
|
|
|
|
|
|
|
|
if (value instanceof Boolean) return (Boolean) value ? 1.0 : 0.0;
|
|
|
|
|
|
|
|
if (value instanceof Character) return (double) ((Character) value);
|
|
|
|
|
|
|
|
return Double.valueOf(value.toString());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|