package com.ruoyi.energymeter.service.impl;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.ruoyi.energymeter.constant.EnergyMeterConstants;
|
import com.ruoyi.energymeter.mapper.EnergyMeterDeviceMapper;
|
import com.ruoyi.energymeter.mapper.EnergyMeterMeasurementMapper;
|
import com.ruoyi.energymeter.pojo.EnergyMeterDevice;
|
import com.ruoyi.energymeter.pojo.EnergyMeterMeasurement;
|
import com.ruoyi.energymeter.service.EnergyMeterDemoService;
|
import com.ruoyi.energymeter.util.EnergyMeterSupport;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.util.List;
|
|
/**
|
* 演示数据实现:气表按天生成模拟表码与用量。
|
*
|
* <p>模拟值是“设备ID + 日期”的确定性函数,重复生成同一天结果一致,便于核对。</p>
|
*/
|
@Slf4j
|
@Service
|
@RequiredArgsConstructor
|
public class EnergyMeterDemoServiceImpl implements EnergyMeterDemoService {
|
|
private final EnergyMeterDeviceMapper deviceMapper;
|
private final EnergyMeterMeasurementMapper measurementMapper;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public int generateGasDemo(LocalDate date) {
|
if (date == null) {
|
return 0;
|
}
|
LambdaQueryWrapper<EnergyMeterDevice> wrapper = new LambdaQueryWrapper<>();
|
wrapper.eq(EnergyMeterDevice::getMeterType, EnergyMeterConstants.TYPE_GAS)
|
.eq(EnergyMeterDevice::getSimulated, 1)
|
.eq(EnergyMeterDevice::getEnabled, 1);
|
List<EnergyMeterDevice> devices = deviceMapper.selectList(wrapper);
|
if (devices.isEmpty()) {
|
return 0;
|
}
|
|
int generated = 0;
|
LocalDateTime measureTime = date.atStartOfDay();
|
for (EnergyMeterDevice device : devices) {
|
if (hasUsage(device.getId(), measureTime)) {
|
// 数据已存在时只刷新采集时间,保证“最近采集/在线”反映最近一次自动生成
|
deviceMapper.markCollectSuccess(device.getId(), EnergyMeterSupport.now());
|
continue;
|
}
|
BigDecimal usage = simulatedUsage(device.getId(), date);
|
BigDecimal previous = previousReading(device, measureTime);
|
BigDecimal reading = previous.add(usage);
|
|
EnergyMeterMeasurement usagePoint = new EnergyMeterMeasurement();
|
usagePoint.setTenantId(device.getTenantId());
|
usagePoint.setMeterId(device.getId());
|
usagePoint.setMetricCode(EnergyMeterConstants.METRIC_GAS_USAGE);
|
usagePoint.setGranularity(EnergyMeterConstants.GRANULARITY_DAY);
|
usagePoint.setMeasureTime(measureTime);
|
usagePoint.setMetricValue(usage);
|
usagePoint.setUnit(EnergyMeterConstants.UNIT_GAS);
|
usagePoint.setSourceApi(EnergyMeterConstants.SOURCE_MANUAL);
|
usagePoint.setSourceRecordTime(measureTime);
|
usagePoint.setCollectedAt(EnergyMeterSupport.now());
|
usagePoint.setRemark("模拟数据:每日用量");
|
|
EnergyMeterMeasurement readingPoint = new EnergyMeterMeasurement();
|
readingPoint.setTenantId(device.getTenantId());
|
readingPoint.setMeterId(device.getId());
|
readingPoint.setMetricCode(EnergyMeterConstants.METRIC_GAS_READING);
|
readingPoint.setGranularity(EnergyMeterConstants.GRANULARITY_POINT);
|
readingPoint.setMeasureTime(measureTime);
|
readingPoint.setMetricValue(reading);
|
readingPoint.setUnit(EnergyMeterConstants.UNIT_GAS);
|
readingPoint.setSourceApi(EnergyMeterConstants.SOURCE_MANUAL);
|
readingPoint.setSourceRecordTime(measureTime);
|
readingPoint.setCollectedAt(EnergyMeterSupport.now());
|
readingPoint.setRemark("模拟数据:每日表码");
|
|
measurementMapper.upsertBatch(List.of(usagePoint, readingPoint));
|
|
// 采集时间按实际执行时间记录,而不是数据所属日期的 00:00
|
deviceMapper.markCollectSuccess(device.getId(), EnergyMeterSupport.now());
|
generated++;
|
}
|
log.info("气表演示数据生成完成 date={} 设备数={}", date, generated);
|
return generated;
|
}
|
|
private boolean hasUsage(Long meterId, LocalDateTime measureTime) {
|
LambdaQueryWrapper<EnergyMeterMeasurement> wrapper = new LambdaQueryWrapper<>();
|
wrapper.eq(EnergyMeterMeasurement::getMeterId, meterId)
|
.eq(EnergyMeterMeasurement::getMetricCode, EnergyMeterConstants.METRIC_GAS_USAGE)
|
.eq(EnergyMeterMeasurement::getMeasureTime, measureTime);
|
return measurementMapper.selectCount(wrapper) > 0;
|
}
|
|
/**
|
* 上一期表码:取该日期之前的最近一条示值,没有就用设备起始表码。
|
*/
|
private BigDecimal previousReading(EnergyMeterDevice device, LocalDateTime measureTime) {
|
EnergyMeterMeasurement previous = measurementMapper.selectPreviousPoint(device.getId(),
|
EnergyMeterConstants.METRIC_GAS_READING, measureTime);
|
if (previous != null && previous.getMetricValue() != null) {
|
return previous.getMetricValue();
|
}
|
return device.getInitialReading() == null ? BigDecimal.ZERO : device.getInitialReading();
|
}
|
|
/**
|
* 确定性模拟用量:按食品厂蒸煮/灭菌的实际用气量估算。
|
*
|
* <p>估算口径:蒸煮锅按 4~6 m³/h、每天 6~8 小时,单台约 25~35 m³/天;
|
* 灭菌锅、小煮锅约 14~20 m³/天。基础值按设备区分,再叠加季节波动与噪点。</p>
|
*/
|
private BigDecimal simulatedUsage(Long meterId, LocalDate date) {
|
long seed = meterId * 131L + date.toEpochDay();
|
double base = 14 + (meterId % 5) * 5;
|
double season = base * 0.1 * Math.sin(2 * Math.PI * date.getDayOfYear() / 365);
|
double noise = ((seed * 37 % 200) / 200.0 - 0.5) * 3;
|
double usage = Math.max(2, base + season + noise);
|
return BigDecimal.valueOf(usage).setScale(2, RoundingMode.HALF_UP);
|
}
|
}
|