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.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 { /** * 计算溢洪量 * * @param rz 计算溢洪的起始水位 * @param seconds 溢洪时间,是两条水位数据的时间差,单位秒 * @param zqrlList 水位流量关系曲线 * @return 溢洪量 * @author lyf */ public BigDecimal calculateSpilledVolume(BigDecimal rz, long seconds, List 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 calculateSpilledVolumeList(List rsvrList, List zqrlList) { rsvrList.sort(Comparator.comparing(StRsvrR::getTm).reversed()); List ret = new ArrayList<>(); 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 = getQFromZqrl(rz, zqrlList); //TODO 这个需要公式进行对水位->流量的转换 if (i == 0) { vo.setQ(getQFromZqrl(rz, zqrlList)); vo.setSv(BigDecimal.ZERO); } else { StRsvrR rsvrPrev = rsvrList.get(i - 1); Date tmPrev = rsvrPrev.getTm(); long seconds = tm.getTime() - tmPrev.getTime(); vo.setQ(getQFromZqrl(rz, zqrlList)); //TODO 这里有个函数调用,记得到时候也替换一下计算公式 vo.setSv(calculateSpilledVolume(rz, seconds)); } ret.add(vo); } return ret; } /** * 批量计算溢洪量 * * @param rsvrList 要计算的水位数据 * @return 计算后的水位数据(时间倒序),含每条水位对应的流量和与上一条水位对应的溢洪量 * @author lyf */ public List calculateSpilledVolumeList(List 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 zqrlList) { return toMap(zqrlList).getOrDefault(rz.setScale(3, RoundingMode.DOWN), BigDecimal.ZERO); } /** * 将水位流量曲线列表转为字典,水位作为键,注意BigDecimal做键的时候要匹配值和精度 * * @param zqrlList 水位流量曲线列表 * @return 水位流量曲线字典 * @author lyf */ private Map toMap(List 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 zqrlBList, BigDecimal rz) { int l =0; int r = zqrlBList.size() - 1; if(rz.compareTo(zqrlBList.get(0).getZ()) < 0){ return BigDecimal.ZERO; } //使用二分法,来获取 避免On的查询时间复杂度 while(l < r){ int mid = l + (r - l) / 2; if(zqrlBList.get(mid).getZ().compareTo(rz) ==0){ return zqrlBList.get(mid).getQ(); }else if(zqrlBList.get(mid).getZ().compareTo(rz) >0){ r = mid - 1; }else{ l = mid + 1; } } // 现在 l 是最接近的索引,但需要比较前后三个值 int closestIndex = l; // 处理边界情况 if (l == 0) { // 只有第一个元素,或者比较第一个和第二个哪个更接近 if (zqrlBList.size() > 1) { BigDecimal diff1 = rz.subtract(zqrlBList.get(0).getZ()).abs(); BigDecimal diff2 = rz.subtract(zqrlBList.get(1).getZ()).abs(); closestIndex = diff1.compareTo(diff2) <= 0 ? 0 : 1; } //TODO 这里找到最接近的值后,仍然要通过公式,计算出真正的流量 return zqrlBList.get(closestIndex).getQ(); } if (l == zqrlBList.size() - 1) { // 只有最后一个元素,或者比较最后两个哪个更接近 if (zqrlBList.size() > 1) { BigDecimal diff1 = rz.subtract(zqrlBList.get(l - 1).getZ()).abs(); BigDecimal diff2 = rz.subtract(zqrlBList.get(l).getZ()).abs(); closestIndex = diff1.compareTo(diff2) <= 0 ? l - 1 : l; } //TODO 这里找到最接近的值后,仍然要通过公式,计算出真正的流量 return zqrlBList.get(closestIndex).getQ(); } // 正常情况:比较前一个、当前、后一个三个值,找到最接近的 BigDecimal preVal = zqrlBList.get(l - 1).getZ(); BigDecimal curVal = zqrlBList.get(l).getZ(); BigDecimal nextVal = zqrlBList.get(l + 1).getZ(); //计算他们与目标的插值 BigDecimal diffPre = rz.subtract(preVal).abs(); BigDecimal diffCur = rz.subtract(curVal).abs(); BigDecimal diffNext = rz.subtract(nextVal).abs(); // 找到最小的差值 if (diffPre.compareTo(diffCur) <= 0 && diffPre.compareTo(diffNext) <= 0) { closestIndex = l - 1; } else if (diffCur.compareTo(diffPre) <= 0 && diffCur.compareTo(diffNext) <= 0) { closestIndex = l; } else { closestIndex = l + 1; } //TODO 这里找到最接近的值后,仍然要通过公式,计算出真正的流量 return zqrlBList.get(closestIndex).getQ(); } /** * 计算累计溢洪量 * @param complexList * @return */ public BigDecimal getFlowQSum(List 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 { RsvrComplexVo previous = complexList.get(i - 1); // 计算时间间隔(秒) long timeDiffSeconds = Duration.between(previous.getTm().toInstant(), current.getTm().toInstant()).getSeconds(); if (timeDiffSeconds > 0) { // 使用梯形法则计算这段时间内的溢洪量:(上一个流量 + 当前流量) / 2 * 时间间隔 BigDecimal avgFlow = previous.getSv().add(current.getSv()) .divide(BigDecimal.valueOf(2), 10, RoundingMode.HALF_UP); BigDecimal increment = avgFlow.multiply(BigDecimal.valueOf(timeDiffSeconds)); sum = sum.add(increment); } } } return sum; } }