gunshi-project-ss/src/main/java/com/gunshi/project/hsz/service/StZqrlBService.java

246 lines
8.2 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package com.gunshi.project.hsz.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gunshi.project.hsz.entity.vo.RsvrComplexVo;
import com.gunshi.project.hsz.mapper.StZqrlBMapper;
import com.gunshi.project.hsz.common.model.StRsvrR;
import com.gunshi.project.hsz.model.StZqrlB;
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.Duration;
import java.util.*;
import java.util.stream.Collectors;
/**
* 描述: 水位流量关系曲线表
* author: xusan
* date: 2024-07-08 17:30:38
*/
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class StZqrlBService extends ServiceImpl<StZqrlBMapper, StZqrlB> {
/**
* 计算溢洪量
*
* @param rz 计算溢洪的起始水位
* @param seconds 溢洪时间,是两条水位数据的时间差,单位秒
* @param zqrlList 水位流量关系曲线
* @return 溢洪量
* @author lyf
*/
public BigDecimal calculateSpilledVolume(BigDecimal rz, long seconds, List<StZqrlB> zqrlList) {
return getQFromZqrl(rz, zqrlList).multiply(BigDecimal.valueOf(seconds));
}
/**
* 计算溢洪量
*
* @param rz 计算溢洪的起始水位
* @param seconds 溢洪时间,是两条水位数据的时间差,单位秒
* @return 溢洪量
* @author lyf
*/
public BigDecimal calculateSpilledVolume(BigDecimal rz, long seconds) {
return calculateSpilledVolume(rz, seconds, list());
}
/**
* 批量计算溢洪量
*
* @param rsvrList 要计算的水位数据
* @param zqrlList 水位流量关系曲线
* @return 计算后的水位数据(时间倒序),含每条水位对应的流量和与上一条水位对应的溢洪量
* @author lyf
*/
public List<RsvrComplexVo> calculateSpilledVolumeList(List<StRsvrR> rsvrList, List<StZqrlB> zqrlList) {
rsvrList.sort(Comparator.comparing(StRsvrR::getTm));
List<RsvrComplexVo> ret = new ArrayList<>();
BigDecimal sum = BigDecimal.ZERO;
for (int i = 0; i < rsvrList.size(); i++) {
StRsvrR rsvr = rsvrList.get(i);
BigDecimal rz = new BigDecimal(rsvr.getRz());
Date tm = rsvr.getTm();
RsvrComplexVo vo = RsvrComplexVo.of(rz, tm);
BigDecimal q = getQByZqrl(zqrlList,rz);
//TODO 这个需要公式进行对水位->流量的转换
vo.setQ(q);
if (i == 0) {
vo.setTotalSv(BigDecimal.ZERO);
sum = BigDecimal.ZERO;
} else {
StRsvrR rsvrPrev = rsvrList.get(i - 1);
Date tmPrev = rsvrPrev.getTm();
long seconds = tm.getTime() - tmPrev.getTime();
sum = sum.add(vo.getQ().multiply(BigDecimal.valueOf(60 * 60)));
vo.setTotalSv(sum);
}
ret.add(vo);
}
return ret;
}
/**
* 批量计算溢洪量
*
* @param rsvrList 要计算的水位数据
* @return 计算后的水位数据(时间倒序),含每条水位对应的流量和与上一条水位对应的溢洪量
* @author lyf
*/
public List<RsvrComplexVo> calculateSpilledVolumeList(List<StRsvrR> rsvrList) {
return calculateSpilledVolumeList(rsvrList, list());
}
/**
* 根据水位查表得流量
*
* @param rz 水位
* @return 查表流量
* @author lyf
*/
public BigDecimal getQFromZqrl(BigDecimal rz) {
return getQFromZqrl(rz, list());
}
/**
* 根据水位查表得流量
*
* @param rz 水位
* @param zqrlList 水位流量关系曲线
* @return 查表流量
* @author lyf
*/
public BigDecimal getQFromZqrl(BigDecimal rz, List<StZqrlB> zqrlList) {
return toMap(zqrlList).getOrDefault(rz.setScale(3, RoundingMode.DOWN), BigDecimal.ZERO);
}
/**
* 将水位流量曲线列表转为字典水位作为键注意BigDecimal做键的时候要匹配值和精度
*
* @param zqrlList 水位流量曲线列表
* @return 水位流量曲线字典
* @author lyf
*/
private Map<BigDecimal, BigDecimal> toMap(List<StZqrlB> zqrlList) {
return zqrlList.stream()
.map(item -> Map.entry(item.getZ().setScale(3, RoundingMode.DOWN), item.getQ()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
public BigDecimal getQByZqrl(List<StZqrlB> zqrlBList, BigDecimal rz) {
// 1. 参数校验
if (zqrlBList == null || zqrlBList.isEmpty()) {
throw new IllegalArgumentException("水位-流量关系列表不能为空");
}
if (rz == null) {
throw new IllegalArgumentException("水位值不能为空");
}
// 2. 检查边界情况:如果目标水位低于最低水位或高于最高水位
BigDecimal minRz = zqrlBList.get(0).getZ();
BigDecimal maxRz = zqrlBList.get(zqrlBList.size() - 1).getZ();
if (rz.compareTo(minRz) < 0) {
// 低于最低水位返回0流量
return BigDecimal.ZERO;
}
if (rz.compareTo(maxRz) > 0) {
// 高于最高水位返回0流量
return BigDecimal.ZERO;
}
// 3. 二分查找水位区间
int l = 0;
int r = zqrlBList.size() - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
BigDecimal midRz = zqrlBList.get(mid).getZ();
int compareResult = midRz.compareTo(rz);
if (compareResult == 0) {
// 找到完全匹配的水位,直接返回对应的流量
return zqrlBList.get(mid).getQ();
} else if (compareResult < 0) {
l = mid + 1;
} else {
r = mid - 1;
}
}
/**
* 此时:
* l 指向第一个大于rz的位置
* r 指向最后一个小于rz的位置
* 即r为 (x1, y1) - 下限水位和流量
* l为 (x2, y2) - 上限水位和流量
*/
if (l >= zqrlBList.size() || r < 0) {
return BigDecimal.ZERO;
}
// 获取前后相邻的两个点
StZqrlB lowerPoint = zqrlBList.get(r);
StZqrlB upperPoint = zqrlBList.get(l);
// 使用线性插值计算流量
return linearInterpolation(
lowerPoint.getZ(), lowerPoint.getQ(),
upperPoint.getZ(), upperPoint.getQ(),
rz
).setScale(3, RoundingMode.HALF_UP);
}
/**
* 线性插值辅助方法(可以复用之前的)
* @param x1 已知点1的水位
* @param y1 已知点1的流量
* @param x2 已知点2的水位
* @param y2 已知点2的流量
* @param x 目标水位
* @return 目标水位对应的流量
*/
private BigDecimal linearInterpolation(BigDecimal x1, BigDecimal y1,
BigDecimal x2, BigDecimal y2,
BigDecimal x) {
// 使用公式: y = y1 + ( (y2 - y1) / (x2 - x1) ) * (x - x1)
// 计算斜率: (y2 - y1) / (x2 - x1)
BigDecimal slope = y2.subtract(y1)
.divide(x2.subtract(x1), 10, RoundingMode.HALF_UP);
// 计算: slope * (x - x1)
BigDecimal xDiff = x.subtract(x1);
BigDecimal product = slope.multiply(xDiff);
// 计算最终结果: y1 + product
return y1.add(product);
}
/**
* 计算累计溢洪量
* @param complexList
* @return
*/
public BigDecimal getFlowQSum(List<RsvrComplexVo> complexList) {
BigDecimal sum = BigDecimal.ZERO;
for (int i = 0; i < complexList.size(); i++) {
RsvrComplexVo current = complexList.get(i);
if (i == 0) {
// 第一条数据累计量为0
sum = BigDecimal.ZERO;
} else {
BigDecimal multiply = current.getQ().multiply(new BigDecimal(60 * 60));
sum = sum.add(multiply);
}
}
return sum;
}
}