Commit 05e9bbcc authored by ZWT's avatar ZWT

feat(吉林演示): 松原

1.开发间开优化结果统计功能,创建间开优化效果统计表,生成对应代码;
2.修改15天,10天,3天,1天间开优化功能,修改代码结构;

BREAKING CHANGE: 无

Closes 无

[skip ci]
parent 601cb24a
package pps.core.common.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
/**
* 收集器util
*
* @author ZWT
* @date 2024/06/03
*/
public class CollectorsUtil {
static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
private CollectorsUtil() {
}
@SuppressWarnings("unchecked")
private static <I, R> Function<I, R> castingIdentity() {
return i -> (R) i;
}
//求和方法
public static <T> Collector<T, ?, BigDecimal> summingBigDecimal(ToBigDecimalFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new BigDecimal[]{BigDecimal.ZERO},
(a, t) -> {
a[0] = a[0].add(mapper.applyAsBigDecimal(t));
},
(a, b) -> {
a[0] = a[0].add(b[0]);
return a;
},
a -> a[0], CH_NOID);
}
//求最大值
public static <T> Collector<T, ?, BigDecimal> maxBy(ToBigDecimalFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new BigDecimal[]{new BigDecimal(Long.MIN_VALUE)},
(a, t) -> {
a[0] = a[0].max(mapper.applyAsBigDecimal(t));
},
(a, b) -> {
a[0] = a[0].max(b[0]);
return a;
},
a -> a[0], CH_NOID);
}
//求最小值
public static <T> Collector<T, ?, BigDecimal> minBy(ToBigDecimalFunction<? super T> mapper) {
return new CollectorImpl<>(
() -> new BigDecimal[]{new BigDecimal(Long.MAX_VALUE)},
(a, t) -> {
a[0] = a[0].min(mapper.applyAsBigDecimal(t));
},
(a, b) -> {
a[0] = a[0].min(b[0]);
return a;
},
a -> a[0], CH_NOID);
}
/**
* 求平均值
*
* @param mapper
* @param newScale 保留newScale位小数
* @param roundingMode 舍去规则(0 <= roundingMode <= 7)
* @param <T>
* @return
*/
public static <T> Collector<T, ?, BigDecimal> averagingBigDecimal(ToBigDecimalFunction<? super T> mapper, int newScale, int roundingMode) {
return new CollectorImpl<>(
() -> new BigDecimal[]{BigDecimal.ZERO, BigDecimal.ZERO},
(a, t) -> {
a[0] = a[0].add(mapper.applyAsBigDecimal(t));
a[1] = a[1].add(BigDecimal.ONE);
},
(a, b) -> {
a[0] = a[0].add(b[0]);
return a;
},
a -> a[0].divide(a[1], RoundingMode.HALF_UP).setScale(newScale, roundingMode), CH_NOID);
}
static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
private final Supplier<A> supplier;
private final BiConsumer<A, T> accumulator;
private final BinaryOperator<A> combiner;
private final Function<A, R> finisher;
private final Set<Characteristics> characteristics;
CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner,
Function<A, R> finisher, Set<Characteristics> characteristics) {
this.supplier = supplier;
this.accumulator = accumulator;
this.combiner = combiner;
this.finisher = finisher;
this.characteristics = characteristics;
}
@Override
public BiConsumer<A, T> accumulator() {
return accumulator;
}
@Override
public Supplier<A> supplier() {
return supplier;
}
@Override
public BinaryOperator<A> combiner() {
return combiner;
}
@Override
public Function<A, R> finisher() {
return finisher;
}
@Override
public Set<Characteristics> characteristics() {
return characteristics;
}
}
}
\ No newline at end of file
......@@ -10,8 +10,6 @@ import com.github.pagehelper.PageInfo;
import com.github.pagehelper.page.PageMethod;
import pps.cloud.space.service.ISpaceInstitutionDetailCloudService;
import pps.cloud.system.service.SysOrganizationCloudService;
import pps.cloud.system.service.data.sys_organization.GetOuListByOuIdsInput;
import pps.cloud.system.service.data.sys_organization.GetOuListTreeOutput;
import pps.cloud.system.service.data.sys_organization.GetSysOrganizationViewInput;
import pps.cloud.system.service.data.sys_organization.GetSysOrganizationViewOutput;
import pps.core.base.entity.*;
......@@ -43,7 +41,6 @@ import xstartup.feature.api.annotation.XApiPost;
import xstartup.helper.XTransactionHelper;
import java.util.*;
import java.util.stream.Collectors;
/**
* 输电线路配置模块
......@@ -383,25 +380,6 @@ public class BasePowerLineService {
.orElse(null);
}
/**
* 通过组织机构ID集合获取详情列表
*
* @param context 上下文
* @param ouIds ou id
* @return {@link Map}<{@link String}, {@link String}>
*/
private Map<String, String> getOuMapByOuIds(XContext context, List<String> ouIds) {
GetOuListByOuIdsInput input = new GetOuListByOuIdsInput();
input.setOuIdList(ouIds);
SysOrganizationCloudService organizationCloudService = context.getBean(SysOrganizationCloudService.class);
XListResult<GetOuListTreeOutput> ouListByOuIds = organizationCloudService.getOuListByOuIds(context, input);
ouListByOuIds.throwIfFail();
return ouListByOuIds.getResult().stream()
.collect(Collectors.toMap(
GetOuListTreeOutput::getOuId, GetOuListTreeOutput::getOuName
));
}
/**
* 通过线路ID获取线路信息
*
......
......@@ -275,7 +275,6 @@ public class BaseWeatherCloudServiceImpl implements IBaseWeatherCloudService {
if (CollUtil.isNotEmpty(batchList)) {
WeatherBureauDataViewMapper dataViewMapper = context.getBean(WeatherBureauDataViewMapper.class);
int size = batchList.size();
context.getLogger().info("------ insertWindTurbineList batchList insert size: {}", size);
if (size > BaseUtils.BATCH_SIZE) {
List<List<WeatherBureauDataView>> subList = BaseUtils.getSubList(batchList);
subList.forEach(dataViewMapper::insertBatch);
......
......@@ -13,7 +13,6 @@ import pps.cloud.base.service.IBasePowerLineCloudService;
import pps.cloud.base.service.data.base_power_line.GetBasePowerLineInput;
import pps.cloud.base.service.data.base_power_line.GetBasePowerLineViewOutput;
import pps.cloud.base.service.data.base_power_line_wellhead.DynamicQueryBasePowerLineWellheadInput;
import pps.cloud.base.service.data.base_power_line_wellhead.DynamicQueryBasePowerLineWellheadViewOutput;
import pps.core.common.constant.BusinessConstant;
import pps.core.common.entity.BaseModel;
import pps.core.common.session.PpsUserSession;
......@@ -31,8 +30,6 @@ import xstartup.annotation.XService;
import xstartup.annotation.XText;
import xstartup.base.XContext;
import xstartup.base.util.XCopyUtils;
import xstartup.core.base.helper.XThreadHelper;
import xstartup.data.XListResult;
import xstartup.data.XPageResult;
import xstartup.data.XServiceResult;
import xstartup.data.XSingleResult;
......@@ -54,23 +51,6 @@ import java.util.stream.Collectors;
@XText("基础间开配置模块")
public class SpaceInstitutionDetailService extends SpaceOptimizeBaseService {
@XApiGet(anonymous = true)
public XServiceResult test(XContext context) {
XThreadHelper.async(() -> {
context.getLogger().info("------------- optimize space begin -------------");
SpaceOptimizeUltraCloudServiceImpl bean3 = context.getBean(SpaceOptimizeUltraCloudServiceImpl.class);
bean3.optimizeUltraJob(context);
SpaceOptimizeLongCloudServiceImpl bean = context.getBean(SpaceOptimizeLongCloudServiceImpl.class);
bean.optimizeLongJob(context);
SpaceOptimizeMidCloudServiceImpl bean1 = context.getBean(SpaceOptimizeMidCloudServiceImpl.class);
bean1.optimizeMidJob(context);
SpaceOptimizeShortCloudServiceImpl bean2 = context.getBean(SpaceOptimizeShortCloudServiceImpl.class);
bean2.optimizeShortJob(context);
context.getLogger().info("------------- optimize space end -------------");
});
return XServiceResult.OK;
}
/**
* 基础间开配置--新增
*
......@@ -384,33 +364,6 @@ public class SpaceInstitutionDetailService extends SpaceOptimizeBaseService {
@XText("校准历史--重新优化")
public XServiceResult anewOptimizeInstitution(XContext context, UpdateSpaceInstitutionDetailInput input) {
return XServiceResult.error(context, BusinessError.UnOpen);
// String historyId = input.getHistoryId();
// if (CharSequenceUtil.isEmpty(historyId)) {
// return XServiceResult.error(context, BusinessError.LackOfHistoryRecord);
// }
// String institutionId = input.getId();
// SpaceInstitutionDetailMapper mapper = context.getBean(SpaceInstitutionDetailMapper.class);
// SpaceInstitutionDetailEnt detail = this.getInstitutionDetail(mapper, institutionId);
// if (ObjectUtil.isNull(detail)) {
// return XServiceResult.error(context, XError.NotFound);
// }
// //查校准历史详情
// SpaceCalibrationHistoryMapper historyMapper = context.getBean(SpaceCalibrationHistoryMapper.class);
// SpaceCalibrationHistoryEnt historyEnt = historyMapper.selectById(historyId);
// //过了校准日期不能再校准
// Date calibrationDate = historyEnt.getCalibrationDate();
// if (DateUtil.date().compareTo(calibrationDate) > 0) {
// return XServiceResult.error(context, BusinessError.Expired);
// }
// PpsUserSession session = context.getSession(PpsUserSession.class);
// return XTransactionHelper.begin(context, () -> {
// this.updateInstitutionDetail(context, session, mapper, institutionId, input, detail);
// //制度优化
// this.institutionOptimizeInitialize(context, detail);
// //校准周期初始化
// this.calibrationHistoryInitialize(context, detail, session);
// return XServiceResult.OK;
// });
}
/**
......@@ -678,20 +631,6 @@ public class SpaceInstitutionDetailService extends SpaceOptimizeBaseService {
return historyEnt;
}
/**
* 条件查询输电线路井口列表
*
* @param context 上下文
* @param input 输入
* @return {@link List}<{@link DynamicQueryBasePowerLineWellheadViewOutput}>
*/
private List<DynamicQueryBasePowerLineWellheadViewOutput> getPowerLineWellheadList(XContext context, DynamicQueryBasePowerLineWellheadInput input) {
IBasePowerLineCloudService lineService = context.getBean(IBasePowerLineCloudService.class);
XListResult<DynamicQueryBasePowerLineWellheadViewOutput> result = lineService.getPowerLineWellheadList(context, input);
result.throwIfFail();
return result.getResult();
}
/**
* 保存策略关联信息
*
......
......@@ -7,7 +7,6 @@ import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
......@@ -36,7 +35,9 @@ import pps.core.common.utils.BaseUtils;
import pps.core.space.entity.*;
import pps.core.space.enums.BusinessError;
import pps.core.space.mapper.*;
import pps.core.space.service.data.*;
import pps.core.space.service.data.SpaceOptimizeDateDuration;
import pps.core.space.service.data.SpaceOptimizeLineRelation;
import pps.core.space.service.data.SpaceOptimizeWeight;
import pps.core.space.service.data.space_institution_detail.OptimizeWellResult;
import pps.core.space.service.data.space_optimize_period.GetPredictedPowerOutput;
import pps.core.space.service.data.space_optimize_period.GetSpaceOptimizePeriodInput;
......@@ -158,7 +159,6 @@ public class SpaceOptimizeBaseService {
String periodId;
Map<Boolean, List<SpaceInstitutionWellheadView>> collect;
//时间间隔
Integer startInterval;
Date institutionEndDate;
DateTime optimizeDeadline;
//取优化截至日期
......@@ -264,7 +264,6 @@ public class SpaceOptimizeBaseService {
wellheadList.remove(removeIndex);
}
SpaceInstitutionWellheadView wellhead;
startInterval = detail.getStartInterval();
if (CollUtil.isNotEmpty(wellheadList)) {
//计算权重
electricityCostList = this.electricityCostList(rangeToList, monthStrategyList);
......@@ -359,524 +358,6 @@ public class SpaceOptimizeBaseService {
}
}
/**
* 动态优化
*
* @param context 上下文
* @param periodDTOList 时期数据学家
* @param wellheadDTOList 井口dtolist
* @param durationDTOList 持续时间dtolist
* @param unOptimizeDurationList 取消优化工期列表
* @param dateType 日期类型
* @param flag 旗帜
* @param offset 抵消
*/
public void dynamicOptimize(XContext context, List<SpaceOptimizePeriodDTO> periodDTOList, List<SpaceOptimizeWellheadDTO> wellheadDTOList,
List<SpaceOptimizeDurationDTO> durationDTOList, List<SpaceOptimizeDurationDTO> unOptimizeDurationList,
Integer dateType, Integer flag, Integer offset) {
//取当前时间
DateTime startDate = DateUtil.beginOfDay(DateTime.now());
//取生效中的基础间开
List<SpaceInstitutionDetailEnt> detailEntList = this.getEffectiveSpaceInstitution(context, startDate.toString());
//取线路ID和制度ID
List<String> lineIds = new ArrayList<>(32);
List<String> institutionIds = new ArrayList<>(32);
detailEntList.forEach(detail -> {
lineIds.add(detail.getLineId());
institutionIds.add(detail.getId());
});
//取间开的所有井口
List<SpaceInstitutionWellheadView> spaceWellheadList = this.getSpaceWellheadList(context, institutionIds);
if (CollUtil.isEmpty(spaceWellheadList)) {
throw new XServiceException(BusinessError.DidNotFindWellhead);
}
//取间开对应的线路
List<DynamicQueryBasePowerLineOutput> powerLineList = ServiceUtil.getPowerLineList(context, DynamicQueryBasePowerLineInput.builder()
.lineIds(lineIds)
.build());
//查线路下所有电站
List<DynamicQueryBasePowerLinePlantViewOutput> powerLinePlantList = ServiceUtil.getPowerLinePlantList(context, DynamicQueryBasePowerLinePlantInput.builder()
.lineIds(lineIds)
.build());
//取当前月
int month = startDate.monthBaseOne();
//查井口配置时间段
SpaceInstitutionDurationMapper durationMapper = context.getBean(SpaceInstitutionDurationMapper.class);
List<SpaceInstitutionDurationEnt> durationList = durationMapper.selectList(new QueryWrapper<SpaceInstitutionDurationEnt>()
.select("*",
"STR_TO_DATE( CONCAT( open_well_time, ':00' ), '%H:%i:%s' ) AS openTime",
"STR_TO_DATE( CONCAT( close_well_time, ':00' ), '%H:%i:%s' ) AS closeTime"
)
.lambda()
.eq(BaseModel::getIsDeleted, BusinessConstant.ONE)
.in(SpaceInstitutionDurationEnt::getWellheadId, spaceWellheadList.stream().map(SpaceInstitutionWellheadView::getWellheadId).collect(Collectors.toList()))
.orderByAsc(SpaceInstitutionDurationEnt::getOpenWellTime)
);
if (CollUtil.isEmpty(durationList)) {
throw new XServiceException(BusinessError.DidNotFindCalibration);
}
//间开制度ID/井口LIST Map
Map<String, List<SpaceInstitutionWellheadView>> wellheadMap = spaceWellheadList.stream()
.collect(Collectors.groupingBy(SpaceInstitutionWellheadView::getInstitutionId));
//线路ID/峰谷策略ID Map
Map<String, String> lineMap = powerLineList.stream()
.collect(Collectors.toMap(DynamicQueryBasePowerLineOutput::getId, DynamicQueryBasePowerLineOutput::getStrategyId));
//线路ID/电站ID LIST Map
Map<String, List<String>> plantIdsMap = powerLinePlantList.stream()
.collect(Collectors.groupingBy(DynamicQueryBasePowerLinePlantViewOutput::getLineId,
Collectors.mapping(DynamicQueryBasePowerLinePlantViewOutput::getPlantId, Collectors.toList())));
//峰谷策略ID/峰谷策略LIST Map
Map<String, List<GetBasePriceStrategyDetailOutput>> strategyMap = ServiceUtil.getStrategyDetailList(context, GetBasePriceStrategyDetailInput.builder()
.strategyMonth(String.valueOf(month))
.build()).stream()
.collect(Collectors.groupingBy(GetBasePriceStrategyDetailOutput::getStrategyId));
//间开制度ID/区间配置Map Map
Map<String, Map<String, List<SpaceInstitutionDurationEnt>>> durationMap = durationList.stream()
.collect(Collectors.groupingBy(SpaceInstitutionDurationEnt::getInstitutionId,
Collectors.groupingBy(SpaceInstitutionDurationEnt::getWellheadId)));
//初始化时间轴
List<DateTime> rangeToList = DateUtil.rangeToList(DateUtil.offsetDay(BusinessConstant.DATE_FLAG, -1), BusinessConstant.DATE_FLAG, DateField.MINUTE, 30);
//初始化时间轴(5分钟间隔)
List<DateTime> timerShaft_5 = DateUtil.rangeToList(DateUtil.offsetDay(BusinessConstant.DATE_FLAG, -1), BusinessConstant.DATE_FLAG, DateField.MINUTE, 5);
//遍历
List<DynamicQueryPlantPredictedPowerOutput> powerList;
List<GetBasePriceStrategyDetailOutput> strategyList;
List<SpaceInstitutionWellheadView> wellheadList;
List<SpaceInstitutionWellheadView> unOptimizeWellhead;
List<String> plantIds;
String periodId;
Map<Boolean, List<SpaceInstitutionWellheadView>> collect;
//时间间隔
Integer startInterval;
Date institutionEndDate;
DateTime optimizeDeadline;
//取优化截至日期
DateTime optimizeDate;
DateTime begin;
DateTime recordTime;
boolean isOffGrid;
List<SpaceOptimizeDurationDTO> offGridDTOList = null;
for (SpaceInstitutionDetailEnt detail : detailEntList) {
//是否离网型线路
isOffGrid = CharSequenceUtil.equals(detail.getGridTypeKey(), "0");
//查最新间开历史
if (this.isOptimize(context, detail.getId(), flag, startDate)) {
continue;
}
//制度结束时间
institutionEndDate = detail.getInstitutionEndDate();
optimizeDeadline = DateUtil.offsetDay(startDate, offset);
if (optimizeDeadline.compareTo(institutionEndDate) > 0) {
optimizeDeadline = DateUtil.endOfDay(institutionEndDate);
}
//取峰谷策略
strategyList = strategyMap.get(lineMap.get(detail.getLineId()));
//按是否需要优化分组
if (!wellheadMap.containsKey(detail.getId())) {
continue;
}
collect = wellheadMap.get(detail.getId()).stream()
.sorted(Comparator.comparing(SpaceInstitutionWellheadView::getStartSeq))
.collect(
Collectors.partitioningBy(w ->
/*
待优化井口:
小间开,间抽井,
最小开井时间<=每日运行时间,
运行时长>=单次最高开井时长>=单次最低开井时长,
单次最低开井时长+单次最低关井时长<=24
*/
CharSequenceUtil.equals(BusinessConstant.INTERVAL_PUMPING_WELL, w.getRunTypeKey()) &&
CharSequenceUtil.equals("0", w.getIntervalTypeKey()) &&
w.getRunDuration().compareTo(w.getMinOpen()) >= 0 &&
w.getRunDuration().compareTo(w.getMaxOpen()) >= 0 &&
w.getMaxOpen().compareTo(w.getMinOpen()) >= 0 &&
BusinessConstant.BIG_DECIMAL_24.compareTo(w.getMinOpen().add(w.getMinClose())) >= 0
)
);
//取待优化井口并遍历
wellheadList = collect.get(Boolean.TRUE);
//保存不需要优化的井口
unOptimizeWellhead = collect.get(Boolean.FALSE);
//电站ID列表
plantIds = plantIdsMap.get(detail.getLineId());
if (CollUtil.isEmpty(plantIds) || (CollUtil.isEmpty(wellheadList) && CollUtil.isEmpty(unOptimizeWellhead))) {
//没有电站不优化
continue;
}
//创建记录
periodId = this.createOptimizePeriod(periodDTOList, detail.getId(), detail.getLineId(),
BaseUtils.getExecutionCycleForCalibration(startDate, DateUtil.offsetDay(optimizeDeadline, -1)), optimizeDeadline);
//按天优化
for (int d = 0; d < offset; d++) {
begin = DateUtil.offsetDay(startDate, d);
optimizeDate = DateUtil.offsetDay(startDate, d + 1);
recordTime = offset > 1 ? begin : optimizeDate;
//查预测光伏出力
powerList = this.getAveragePower(context, DynamicQueryPlantPredictedPowerInput.builder()
.plantIds(plantIds)
.startTime(begin.toString(BusinessConstant.DATE_FORMAT_DAY))
.endTime(optimizeDate.toString(BusinessConstant.DATE_FORMAT_DAY))
.dateType(dateType)
.build());
this.setUnOptimizeWellheadConfig(durationDTOList, unOptimizeDurationList, wellheadDTOList,
unOptimizeWellhead, durationMap.get(detail.getId()), periodId, recordTime);
//防冻堵策略(一口井就全开不走优化)
SpaceInstitutionWellheadView tundraStrategyWellhead = null;
List<SpaceOptimizeDurationDTO> tundraStrategyList = new ArrayList<>(32);
Integer tundraStrategy = detail.getTundraStrategy();
//防冻堵条件:不存在常开井(不需要优化的井)&&需要优化的井口不能为空&&开启防冻堵
if (CollUtil.isEmpty(unOptimizeWellhead) && CollUtil.isNotEmpty(wellheadList) && ObjectUtil.isNotNull(tundraStrategy) && tundraStrategy.equals(0)) {
int removeIndex = 0;
BigDecimal serviceRating = wellheadList.get(0).getServiceRating();
if (wellheadList.size() == 1) {
SpaceInstitutionWellheadView wellhead = wellheadList.get(0);
String wellheadId = wellhead.getWellheadId();
String recordId = this.createOptimizeWellhead(wellheadDTOList, periodId, wellheadId, wellhead.getWellNumber(), wellhead.getStartSeq(), recordTime, BusinessConstant.ZERO);
//保存间开原始记录
for (SpaceInstitutionDurationEnt durationEnt : durationMap.get(detail.getId()).get(wellheadId)) {
this.createUnOptimizeDuration(unOptimizeDurationList, durationEnt, periodId, recordId, wellheadId, recordTime);
}
//设置间开优化24小时全开
this.createOptimizeDuration(durationDTOList, periodId, recordId, wellheadId, null,
BusinessConstant.START_OF_DAY_TIME,
BusinessConstant.END_OF_DAY_TIME,
BusinessConstant.ZERO, recordTime
);
} else {
for (int i = 1; i < wellheadList.size(); i++) {
//找运行功率最小的井口(功率都一样取启动顺序最晚的)
if (serviceRating.compareTo(wellheadList.get(i).getServiceRating()) >= 0) {
removeIndex = i;
}
}
//取出防冻井
tundraStrategyWellhead = wellheadList.get(removeIndex);
}
//排除防冻井
wellheadList.remove(removeIndex);
}
BigDecimal serviceRating = BigDecimal.ZERO;
SpaceInstitutionWellheadView wellhead;
//记录第一次开井时间
DateTime firstOpenWellTime = null;
startInterval = detail.getStartInterval();
if (CollUtil.isNotEmpty(wellheadList)) {
for (int i = 0; i < wellheadList.size(); i++) {
if (isOffGrid) {
offGridDTOList = new ArrayList<>(32);
}
wellhead = wellheadList.get(i);
String wellheadId = wellhead.getWellheadId();
String recordId = this.createOptimizeWellhead(wellheadDTOList, periodId, wellheadId, wellhead.getWellNumber(), wellhead.getStartSeq(), recordTime, BusinessConstant.ONE);
//保存间开原始记录
if (!durationMap.containsKey(detail.getId()) || !durationMap.get(detail.getId()).containsKey(wellheadId)) {
continue;
}
for (SpaceInstitutionDurationEnt durationEnt : durationMap.get(detail.getId()).get(wellheadId)) {
this.createUnOptimizeDuration(unOptimizeDurationList, durationEnt, periodId, recordId, wellheadId, recordTime);
}
//累加运行功率
serviceRating = serviceRating.add(wellhead.getServiceRating());
//计算权重
List<SpaceOptimizeWeight> weightList = this.calculateWeightList(serviceRating, rangeToList, powerList, strategyList, isOffGrid);
//每日最小开井时长(分钟)
int minOpenMinute = wellhead.getMinOpen()
.multiply(BusinessConstant.BIG_DECIMAL_60)
.intValue();
//分级取时间段
List<SpaceOptimizeWeightDuration> weightDurationList = this.weightListProcessing(weightList, minOpenMinute);
//时间段优化
List<SpaceOptimizeDurationDTO> optimizeDurationDTOList = new ArrayList<>(12);
firstOpenWellTime = this.timePeriodOptimization(optimizeDurationDTOList, weightDurationList, firstOpenWellTime, i,
startInterval, minOpenMinute, wellhead, weightList);
//创建优化后的间开区间
if (isOffGrid) {
this.addOptimizeDuration(optimizeDurationDTOList, offGridDTOList, tundraStrategyList, periodId, recordId, wellheadId, recordTime);
//标注发电类型
this.addOffGridOptimizeDuration(durationDTOList, offGridDTOList, weightList);
} else {
this.addOptimizeDuration(optimizeDurationDTOList, durationDTOList, tundraStrategyList, periodId, recordId, wellheadId, recordTime);
}
}
//防冻井
if (ObjectUtil.isNotNull(tundraStrategyWellhead) && CollUtil.isNotEmpty(tundraStrategyList)) {
List<SpaceOptimizeDurationDTO> closeList = new ArrayList<>(32);
long totalOpenTime = 0;
if (tundraStrategyList.size() == 1) {
//记录第一段开始时间之前的关井时间
if (BusinessConstant.DATE_FLAG_BEGIN.compareTo(tundraStrategyList.get(0).getOpenTime()) != 0) {
long between = DateUtil.between(BusinessConstant.DATE_FLAG_BEGIN, tundraStrategyList.get(0).getOpenTime(), DateUnit.MINUTE);
totalOpenTime += between;
closeList.add(
SpaceOptimizeDurationDTO.builder()
.openTime(BusinessConstant.DATE_FLAG_BEGIN)
.closeTime(tundraStrategyList.get(0).getOpenTime())
.duration(between)
.build()
);
}
//记录最后一段停井时间之后的关井时间
if (BusinessConstant.DATE_FLAG.compareTo(tundraStrategyList.get(0).getCloseTime()) != 0) {
long between = DateUtil.between(tundraStrategyList.get(0).getCloseTime(), BusinessConstant.DATE_FLAG, DateUnit.MINUTE);
totalOpenTime += between;
closeList.add(
SpaceOptimizeDurationDTO.builder()
.openTime(tundraStrategyList.get(0).getCloseTime())
.closeTime(BusinessConstant.DATE_FLAG)
.duration(between)
.build()
);
}
} else {
//初始化时间轴
List<SpaceOptimizeTundraStrategy> axis = new ArrayList<>(timerShaft_5.size());
for (DateTime dateTime : timerShaft_5) {
axis.add(
SpaceOptimizeTundraStrategy.builder()
.timestamp(dateTime)
.beUsing(Boolean.FALSE)
.build()
);
}
//先按开井时间再按关井时间排序
tundraStrategyList.sort(Comparator.comparing(SpaceOptimizeDurationDTO::getOpenTime).thenComparing(SpaceOptimizeDurationDTO::getCloseTime));
//渲染时间轴
int nextBegin = 0;
for (SpaceOptimizeDurationDTO durationDTO : tundraStrategyList) {
Date openTime = durationDTO.getOpenTime();
Date closeTime = durationDTO.getCloseTime();
for (int i = nextBegin; i < axis.size(); i++) {
SpaceOptimizeTundraStrategy z = axis.get(i);
if (DateUtil.isIn(z.getTimestamp(), openTime, closeTime)) {
z.setBeUsing(Boolean.TRUE);
}
//记录下一次开始位置
if (z.getTimestamp().compareTo(closeTime) > 0) {
nextBegin = i;
break;
}
}
}
//取关井时间段
int beginIndex = 0;
boolean isClose = Boolean.FALSE;
for (int i = 0; i < axis.size(); i++) {
SpaceOptimizeTundraStrategy z = axis.get(i);
Boolean beUsing = z.getBeUsing();
if (isClose && beUsing) {
long between = DateUtil.between(axis.get(beginIndex).getTimestamp(), axis.get(i).getTimestamp(), DateUnit.MINUTE);
totalOpenTime += between;
//存时间段,重置状态
closeList.add(
SpaceOptimizeDurationDTO.builder()
.openTime(axis.get(beginIndex).getTimestamp())
.closeTime(axis.get(i).getTimestamp())
.duration(between)
.build()
);
isClose = Boolean.FALSE;
} else if (!isClose && !beUsing) {
//确定开始位置
if (i > 0) {
beginIndex = i - 1;
}
isClose = Boolean.TRUE;
}
}
//补充最后一段
if (isClose) {
long between = DateUtil.between(axis.get(beginIndex).getTimestamp(), axis.get(axis.size() - 1).getTimestamp(), DateUnit.MINUTE);
totalOpenTime += between;
closeList.add(
SpaceOptimizeDurationDTO.builder()
.openTime(axis.get(beginIndex).getTimestamp())
.closeTime(axis.get(axis.size() - 1).getTimestamp())
.duration(between)
.build()
);
}
}
//计算权重时间轴
//每日最小开井时长(分钟)
int minOpenMinute;
BigDecimal minOpen = tundraStrategyWellhead.getMinOpen();
if (ObjectUtil.isNull(minOpen)) {
minOpenMinute = 0;
} else {
minOpenMinute = minOpen
.multiply(BusinessConstant.BIG_DECIMAL_60)
.intValue();
}
serviceRating = serviceRating.add(tundraStrategyWellhead.getServiceRating());
List<SpaceOptimizeWeight> weightList = this.calculateWeightList(serviceRating, rangeToList, powerList, strategyList, isOffGrid);
List<SpaceOptimizeWeightDuration> weightDurationList = this.weightListProcessing(weightList, minOpenMinute);
//创建优化记录
String wellheadId = tundraStrategyWellhead.getWellheadId();
String recordId = this.createOptimizeWellhead(wellheadDTOList, periodId, wellheadId, tundraStrategyWellhead.getWellNumber(), tundraStrategyWellhead.getStartSeq(), recordTime, BusinessConstant.ZERO);
//保存间开原始记录
for (SpaceInstitutionDurationEnt durationEnt : durationMap.get(detail.getId()).get(wellheadId)) {
this.createUnOptimizeDuration(unOptimizeDurationList, durationEnt, periodId, recordId, wellheadId, recordTime);
}
//关井时间优化
if (CollUtil.isNotEmpty(closeList)) {
//每日开井时长
int dayOpenMinute = tundraStrategyWellhead.getRunDuration()
.multiply(BusinessConstant.BIG_DECIMAL_60)
.intValue();
//计算需要补的时间
long supplementaryTime = dayOpenMinute - totalOpenTime;
//补时间
for (SpaceOptimizeWeightDuration weightDuration : weightDurationList) {
if (supplementaryTime <= 0) {
break;
}
DateTime openTimeWeight = weightDuration.getOpenTime();
DateTime closeTimeWeight = weightDuration.getCloseTime();
//计算排除次数
int exceptSize = 0;
for (int i = 0; i < closeList.size(); i++) {
SpaceOptimizeDurationDTO close = closeList.get(i);
Date openTime = close.getOpenTime();
Date closeTime = close.getCloseTime();
if (DateUtil.compare(openTime, openTimeWeight) >= 0 && DateUtil.compare(closeTimeWeight, closeTime) >= 0) {
//可优化时间段在开井范围内
if (i > 0) {
//判断开井时间是否小于上一段关井时间
if (DateUtil.compare(openTimeWeight, closeList.get(i - 1).getCloseTime()) < 0) {
//如果小于上一段关井时间,修改当前开井时间
openTimeWeight = DateUtil.date(closeList.get(i - 1).getCloseTime());
}
}
//取后一位
if (i + 1 < closeList.size()) {
//判断关井时间是否大于下一段开井时间
if (DateUtil.compare(closeTimeWeight, closeList.get(i + 1).getOpenTime()) > 0) {
//如果大于下一段开井时间,修改当前关井时间
closeTimeWeight = DateUtil.date(closeList.get(i + 1).getOpenTime());
}
}
//这里需要把时长补回去再计算
supplementaryTime += close.getDuration();
//重新计算结束时间及总开井时长
long between = DateUtil.between(openTimeWeight, closeTimeWeight, DateUnit.MINUTE);
if (between >= supplementaryTime) {
//从后往前计算开井时间
close.setDuration(supplementaryTime);
supplementaryTime = 0;
} else {
close.setDuration(between);
supplementaryTime -= between;
}
close.setOpenTime(openTimeWeight);
close.setCloseTime(closeTimeWeight);
break;
}
if (DateUtil.compare(openTime, closeTimeWeight) >= 0 || DateUtil.compare(closeTime, openTimeWeight) <= 0) {
//判断时间段是否在开井范围之外
exceptSize++;
continue;
}
//从后往前算
if (DateUtil.isIn(openTime, openTimeWeight, closeTimeWeight)) {
//取前一位
if (i > 0) {
//判断开井时间是否小于上一段关井时间
if (DateUtil.compare(openTimeWeight, closeList.get(i - 1).getCloseTime()) < 0) {
//如果小于上一段关井时间,修改当前开井时间
openTimeWeight = DateUtil.date(closeList.get(i - 1).getCloseTime());
}
}
//这里需要把时长补回去再计算
supplementaryTime += close.getDuration();
//重新计算结束时间及总开井时长
long between = DateUtil.between(openTimeWeight, closeTime, DateUnit.MINUTE);
if (between >= supplementaryTime) {
//从后往前计算开井时间
close.setOpenTime(DateUtil.offsetMinute(closeTime, -(int) supplementaryTime));
close.setDuration(supplementaryTime);
supplementaryTime = 0;
break;
} else {
close.setOpenTime(openTimeWeight);
close.setDuration(between);
supplementaryTime -= between;
}
}
//从前往后算
if (DateUtil.isIn(closeTime, openTimeWeight, closeTimeWeight)) {
//取后一位
if (i + 1 < closeList.size()) {
//判断关井时间是否大于下一段开井时间
if (DateUtil.compare(closeTimeWeight, closeList.get(i + 1).getOpenTime()) > 0) {
//如果大于下一段开井时间,修改当前关井时间
closeTimeWeight = DateUtil.date(closeList.get(i + 1).getOpenTime());
}
}
//这里需要把时长补回去再计算
supplementaryTime += close.getDuration();
//重新计算开始时间及总开井时长
long between = DateUtil.between(openTime, closeTimeWeight, DateUnit.MINUTE);
if (between >= supplementaryTime) {
//从后往前计算开井时间
close.setCloseTime(DateUtil.offsetMinute(openTime, (int) supplementaryTime));
close.setDuration(supplementaryTime);
supplementaryTime = 0;
break;
} else {
close.setCloseTime(closeTimeWeight);
close.setDuration(between);
supplementaryTime -= between;
}
}
}
//添加时段,处理开井时长
if (exceptSize == closeList.size()) {
long duration = weightDuration.getDuration();
if (duration >= supplementaryTime) {
//计算关井时间
closeTimeWeight = DateUtil.offsetMinute(openTimeWeight, (int) supplementaryTime);
duration = supplementaryTime;
supplementaryTime = 0;
} else {
supplementaryTime -= duration;
}
closeList.add(
SpaceOptimizeDurationDTO.builder()
.openTime(openTimeWeight)
.closeTime(closeTimeWeight)
.duration(duration)
.build()
);
}
}
//时间段合并优化
closeList.sort(Comparator.comparing(SpaceOptimizeDurationDTO::getOpenTime));
List<SpaceOptimizeDurationDTO> optimizeDurationDTOList = new ArrayList<>(closeList.size());
optimizeDurationDTOList.add(closeList.get(0));
for (int i = 1; i < closeList.size(); i++) {
SpaceOptimizeDurationDTO nowOne = closeList.get(i);
SpaceOptimizeDurationDTO lastOne = optimizeDurationDTOList.get(optimizeDurationDTOList.size() - 1);
if (0 == DateUtil.compare(lastOne.getCloseTime(), nowOne.getOpenTime())) {
lastOne.setCloseTime(nowOne.getCloseTime());
} else {
optimizeDurationDTOList.add(nowOne);
}
}
closeList = optimizeDurationDTOList;
} else {
//不需要补时间,需要优化计算
this.timePeriodOptimization(closeList, weightDurationList, firstOpenWellTime, wellheadList.size(),
startInterval, minOpenMinute, tundraStrategyWellhead, weightList);
}
//创建优化后的间开区间
this.addOptimizeDuration(closeList, durationDTOList, tundraStrategyList, periodId, recordId, wellheadId, recordTime);
}
}
}
}
}
/**
* 进行优化
*
......@@ -2794,24 +2275,6 @@ public class SpaceOptimizeBaseService {
}
}
/**
* 加时
*
* @param durationDTO 持续时间dto
* @param endTime 结束时间
* @param minute 分钟
*/
private void overtime(SpaceOptimizeDurationDTO durationDTO, DateTime endTime, int minute) {
//判断向前补还是向后补
if (DateUtil.compare(durationDTO.getOpenTime(), endTime) == 0) {
//向前补
durationDTO.setOpenTime(DateUtil.offsetMinute(durationDTO.getOpenTime(), -minute));
} else {
//向后补
durationDTO.setCloseTime(DateUtil.offsetMinute(durationDTO.getCloseTime(), minute));
}
}
/**
* 通过时段类型获取权重
*
......@@ -2896,800 +2359,6 @@ public class SpaceOptimizeBaseService {
return weightList;
}
/**
* 计算权重列表
*
* @param serviceRating 服务等级
* @param rangeToList 要列出范围
* @param powerList 电源列表
* @param strategyList 策略列表
* @param isOffGrid 是否离网型
* @return {@link List}<{@link SpaceOptimizeWeight}>
*/
private List<SpaceOptimizeWeight> calculateWeightList(BigDecimal serviceRating, List<DateTime> rangeToList, List<DynamicQueryPlantPredictedPowerOutput> powerList, List<GetBasePriceStrategyDetailOutput> strategyList, boolean isOffGrid) {
int rangeListSize = rangeToList.size();
int powerListSize = powerList.size();
int strategyListSize = isOffGrid ? 0 : strategyList.size();
int rangeIndex = 0;
int powerIndex = 0;
int strategyIndex = 0;
List<SpaceOptimizeWeight> weightList = new ArrayList<>(rangeListSize);
DynamicQueryPlantPredictedPowerOutput firstPower;
DynamicQueryPlantPredictedPowerOutput lastPower;
GetBasePriceStrategyDetailOutput firstStrategy;
GetBasePriceStrategyDetailOutput lastStrategy;
while (rangeListSize > 0 && rangeListSize > rangeIndex && powerListSize > powerIndex) {
boolean powerFlag = false;
SpaceOptimizeWeight firstWeight = new SpaceOptimizeWeight();
DateTime firstTime = rangeToList.get(rangeIndex);
firstWeight.setTimestamp(firstTime);
firstWeight.setSort(rangeIndex);
firstPower = powerList.get(powerIndex);
if (0 == firstTime.compareTo(firstPower.getCreateTime())) {
firstWeight.setPower(firstPower.getPower());
//判断发电量是否满足运行功率
powerFlag = firstPower.getPower().compareTo(serviceRating) >= 0;
if (powerFlag) {
firstWeight.setWeight(10);
firstWeight.setGenerationTypeKey(BusinessConstant.PHOTOVOLTAIC);
}
powerIndex++;
}
if (isOffGrid) {
if (!powerFlag) {
firstWeight.setWeight(1);
firstWeight.setGenerationTypeKey(BusinessConstant.STORED_ENERGY);
}
} else {
firstStrategy = strategyList.get(strategyIndex);
Date closeTime = ObjectUtil.isEmpty(firstStrategy.getCloseTime()) && CharSequenceUtil.equals(BusinessConstant.END_OF_DAY_TIME, firstStrategy.getStartTime()) ? BusinessConstant.DATE_FLAG : firstStrategy.getCloseTime();
//在市电峰谷时段内,且未满足运行功率,设置权重
if (DateUtil.isIn(firstTime, firstStrategy.getOpenTime(), closeTime)) {
if (CharSequenceUtil.equals(firstStrategy.getPeriodTypeKey(), "RUSH")) {
//尖峰时段要停井
firstWeight.setWeight(0);
} else if (!powerFlag) {
firstWeight.setWeight(this.getWeightByPeriodTypeKey(firstStrategy.getPeriodTypeKey()));
}
}
if (firstTime.compareTo(firstStrategy.getCloseTime()) >= 0) {
strategyIndex++;
}
}
weightList.add(firstWeight);
rangeIndex++;
rangeListSize--;
//防止重复插入
if (rangeIndex >= rangeListSize) {
continue;
}
/*----------------------------------------------------*/
powerFlag = false;
SpaceOptimizeWeight lastWeight = new SpaceOptimizeWeight();
DateTime lastTime = rangeToList.get(rangeListSize);
lastWeight.setTimestamp(lastTime);
lastWeight.setSort(rangeListSize);
lastPower = powerList.get(powerListSize - 1);
if (0 == lastTime.compareTo(lastPower.getCreateTime())) {
lastWeight.setPower(lastPower.getPower());
//判断发电量是否满足运行功率
powerFlag = lastPower.getPower().compareTo(serviceRating) >= 0;
if (powerFlag) {
lastWeight.setWeight(10);
lastWeight.setGenerationTypeKey(BusinessConstant.PHOTOVOLTAIC);
}
powerListSize--;
}
if (isOffGrid) {
if (!powerFlag) {
lastWeight.setWeight(1);
lastWeight.setGenerationTypeKey(BusinessConstant.STORED_ENERGY);
}
} else {
lastStrategy = strategyList.get(strategyListSize - 1);
Date closeTime = ObjectUtil.isEmpty(lastStrategy.getCloseTime()) && CharSequenceUtil.equals(BusinessConstant.END_OF_DAY_TIME, lastStrategy.getEndTime()) ? BusinessConstant.DATE_FLAG : lastStrategy.getCloseTime();
//在市电峰谷时段内,且未满足运行功率,设置权重
if (DateUtil.isIn(lastTime, lastStrategy.getOpenTime(), closeTime)) {
if (CharSequenceUtil.equals(lastStrategy.getPeriodTypeKey(), "RUSH")) {
//尖峰时段要停井
lastWeight.setWeight(0);
} else if (!powerFlag) {
lastWeight.setWeight(this.getWeightByPeriodTypeKey(lastStrategy.getPeriodTypeKey()));
}
}
if (lastTime.compareTo(lastStrategy.getOpenTime()) <= 0) {
strategyListSize--;
}
}
weightList.add(lastWeight);
}
//排序
weightList.sort(Comparator.comparing(SpaceOptimizeWeight::getSort));
return weightList;
}
/**
* 权重时间处理
*
* @param weightList 重量清单
* @param minOpenMinute 最小开放分钟
* @return {@link List}<{@link SpaceOptimizeWeightDuration}>
*/
private List<SpaceOptimizeWeightDuration> weightListProcessing(List<SpaceOptimizeWeight> weightList, int minOpenMinute) {
SpaceOptimizeWeight weight;
//分级取时间段
List<SpaceOptimizeWeightDuration> weightDurationList = new ArrayList<>(32);
long between;
for (int i1 = 0; i1 < BusinessConstant.LADDER.size(); i1++) {
int begin = -1;
//取每级时间
for (int i2 = 0; i2 < weightList.size(); i2++) {
weight = weightList.get(i2);
//过滤条件:权重相同
if (BusinessConstant.LADDER.get(i1) == weight.getWeight()) {
//确定开始时间位置
if (begin == -1) {
begin = i2;
}
} else if (begin != -1) {
//如果开井时间不满足最小开井时间则舍弃
between = DateUtil.between(weightList.get(begin).getTimestamp(), weightList.get(i2).getTimestamp(), DateUnit.MINUTE);
//创建区间
weightDurationList.add(
SpaceOptimizeWeightDuration.builder()
.openTime(weightList.get(begin).getTimestamp())
.closeTime(weightList.get(i2).getTimestamp())
.openIndex(begin)
.closeIndex(i2)
.duration(between)
.weight(BusinessConstant.LADDER.get(i1))
.build()
);
begin = -1;
}
}
}
//时间处理并排序(处理后第一次开井时间的索引位置为0)
weightDurationList.sort((o1, o2) -> o2.getWeight() - o1.getWeight());
return weightDurationList;
}
/**
* 时间段优化
*
* @param optimizeDurationDTOList 优化工期dtolist
* @param weightDurationList 重量持续时间列表
* @param firstOpenWellTime 首次开井时间
* @param i 我
* @param startInterval 开始间隔
* @param minOpenMinute 最小开放分钟
* @param wellhead 井口
* @param weightList 重量清单
* @return {@link DateTime}
*/
private DateTime timePeriodOptimization(List<SpaceOptimizeDurationDTO> optimizeDurationDTOList, List<SpaceOptimizeWeightDuration> weightDurationList, DateTime firstOpenWellTime,
int i, Integer startInterval, int minOpenMinute, SpaceInstitutionWellheadView wellhead, List<SpaceOptimizeWeight> weightList) {
//每日开井时长
int dayOpenMinute = wellhead.getRunDuration()
.multiply(BusinessConstant.BIG_DECIMAL_60)
.intValue();
//每日最大开井时长(分钟)
int maxOpenMinute = wellhead.getMaxOpen()
.multiply(BusinessConstant.BIG_DECIMAL_60)
.intValue();
//每日最小关井时长(分钟)
int minCloseMinute = wellhead.getMinClose()
.multiply(BusinessConstant.BIG_DECIMAL_60)
.intValue();
//每日最大关井时长(分钟)
int maxCloseMinute = wellhead.getMaxClose()
.multiply(BusinessConstant.BIG_DECIMAL_60)
.intValue();
//总开井时间
long sumOpenTime = 0;
for (int i1 = 0; i1 < weightDurationList.size(); i1++) {
SpaceOptimizeWeightDuration weightDuration = weightDurationList.get(i1);
long duration = weightDuration.getDuration();
DateTime openTime = weightDuration.getOpenTime();
//偏移其他井口开井时间,并修改时间间隔
if (i > 0 && 0 == i1 && 0 >= DateUtil.compare(openTime, firstOpenWellTime)) {
duration -= (long) startInterval * i;
openTime = DateUtil.offsetMinute(openTime, startInterval * i);
}
DateTime closeTime = weightDuration.getCloseTime();
//正向标识
boolean forwardFlag = true;
//偏移开/关井时间
if (CollUtil.isNotEmpty(optimizeDurationDTOList)) {
SpaceOptimizeDurationDTO durationDTO = optimizeDurationDTOList.get(optimizeDurationDTOList.size() - 1);
Date firstOpenTime = optimizeDurationDTOList.get(0).getOpenTime();
Date lastCloseTime = durationDTO.getCloseTime();
long l;
//如果关井时间在第一次开井时间之前,判断是否需要偏移关井时间
if (DateUtil.compare(firstOpenTime, closeTime) >= 0) {
forwardFlag = false;
l = closeTime.between(firstOpenTime, DateUnit.MINUTE);
long l1 = l - minCloseMinute;
//小于0,说明不满足最小停井时间,需要补
if (l1 < 0) {
//偏移关井时间
closeTime = closeTime.offsetNew(DateField.MINUTE, (int) l1);
duration += l1;
if (duration < minOpenMinute) {
continue;
}
} else if (l1 > 0) {
//判断时间间隔是否超过最大停井时间
if (l > maxCloseMinute) {
//调整当前权重
if (i1 > 0) {
//计算出本次应该的开/关井时间
closeTime = DateUtil.offsetMinute(closeTime, (int) l - maxCloseMinute);
openTime = DateUtil.offsetMinute(openTime, (int) l - maxCloseMinute);
}
}
}
} else {
//计算当前开井时间和上一次关井时间的时间间隔
l = openTime.between(lastCloseTime, DateUnit.MINUTE);
//比较时间
if (DateUtil.compare(lastCloseTime, openTime) > 0) {
//时间间隔-(超出部分时间+最小关井时长)
duration -= (l + minCloseMinute);
if (duration >= minOpenMinute) {
//如果剩余时长能满足最小开井时长,则计算开井时间(用关井时间往前推)
openTime = closeTime.offsetNew(DateField.MINUTE, (int) -duration);
} else {
continue;
}
} else {
long l1 = l - minCloseMinute;
//小于0,说明不满足最小停井时间,需要补
if (l1 < 0) {
openTime = openTime.offsetNew(DateField.MINUTE, (int) -l1);
//偏移启动时间,判断时间区间是否满足最小开井时长
duration += l1;
if (duration < minOpenMinute) {
continue;
}
} else if (l1 > 0) {
//判断时间间隔是否超过最大停井时间
if (l > maxCloseMinute) {
//调整当前权重
if (i1 > 0) {
//计算出本次应该的开井时间
openTime = DateUtil.offsetMinute(lastCloseTime, maxCloseMinute);
//修改本次区间的开始时间及时间间隔
duration = DateUtil.between(openTime, weightDuration.getCloseTime(), DateUnit.MINUTE);
}
}
}
}
}
}
//判断时间间隔是否能满足最大开井时间
if (duration >= maxOpenMinute) {
//满足,判断能满足几次(最大开井时间)
//todo : 能力有限,只能用(最大开井时间+最小停井时间)固定范围,求不了最优排布
int div = (int) NumberUtil.div(duration, maxOpenMinute, 0, RoundingMode.UP);
if (forwardFlag) {
//正向拆分
for (int i2 = 1; i2 <= div; i2++) {
DateTime closeTimeNew;
if (duration >= maxOpenMinute) {
sumOpenTime += maxOpenMinute;
closeTimeNew = openTime.offsetNew(DateField.MINUTE, maxOpenMinute);
duration = duration - maxOpenMinute - minCloseMinute;
} else if (duration >= minOpenMinute) {
sumOpenTime += duration;
closeTimeNew = openTime.offsetNew(DateField.MINUTE, (int) duration);
duration = -minCloseMinute;
} else {
//时间不够,舍弃
continue;
}
optimizeDurationDTOList.add(SpaceOptimizeDurationDTO.builder()
.openTime(openTime)
.closeTime(closeTimeNew)
.build());
//下次启动时间为本次关井时间向后移动最小关井时间
openTime = closeTimeNew.offsetNew(DateField.MINUTE, minCloseMinute);
}
} else {
//反向拆分
for (int i2 = 1; i2 <= div; i2++) {
DateTime openTimeNew;
if (duration >= maxOpenMinute) {
sumOpenTime += maxOpenMinute;
openTimeNew = closeTime.offsetNew(DateField.MINUTE, -maxOpenMinute);
duration = duration - maxOpenMinute - minCloseMinute;
} else if (duration >= minOpenMinute) {
sumOpenTime += duration;
openTimeNew = closeTime.offsetNew(DateField.MINUTE, -(int) duration);
duration = -minCloseMinute;
} else {
//时间不够,舍弃
continue;
}
optimizeDurationDTOList.add(SpaceOptimizeDurationDTO.builder()
.openTime(openTimeNew)
.closeTime(closeTime)
.build());
//下次启动时间为本次关井时间向后移动最小关井时间
closeTime = openTimeNew.offsetNew(DateField.MINUTE, -minCloseMinute);
}
}
} else {
//不满足,取全部
optimizeDurationDTOList.add(SpaceOptimizeDurationDTO.builder()
.openTime(openTime)
.closeTime(closeTime)
.build());
sumOpenTime += duration;
}
//判断开井总时间是否大于每日最大开井时间
if (sumOpenTime >= dayOpenMinute) {
long outdo = sumOpenTime - dayOpenMinute;
if (outdo > 0) {
SpaceOptimizeDurationDTO durationDTO = optimizeDurationDTOList.get(optimizeDurationDTOList.size() - 1);
if (forwardFlag) {
DateTime closeTimeNew = DateUtil.offsetMinute(durationDTO.getCloseTime(), (int) -outdo);
int compare = closeTimeNew.compareTo(durationDTO.getOpenTime());
if (compare <= 0) {
optimizeDurationDTOList.remove(optimizeDurationDTOList.size() - 1);
sumOpenTime -= outdo;
} else {
if (DateUtil.between(durationDTO.getOpenTime(), closeTimeNew, DateUnit.MINUTE) < minOpenMinute) {
//如果缩短后时间间隔不满足最小开井时长则删除
sumOpenTime -= DateUtil.between(durationDTO.getOpenTime(), durationDTO.getCloseTime(), DateUnit.MINUTE);
optimizeDurationDTOList.remove(optimizeDurationDTOList.size() - 1);
} else {
//需要补时间,下面统一补
durationDTO.setCloseTime(closeTimeNew);
}
}
} else {
DateTime openTimeNew = DateUtil.offsetMinute(durationDTO.getOpenTime(), (int) outdo);
int compare = openTimeNew.compareTo(durationDTO.getCloseTime());
if (compare >= 0) {
optimizeDurationDTOList.remove(optimizeDurationDTOList.size() - 1);
sumOpenTime -= outdo;
} else {
if (DateUtil.between(openTimeNew, durationDTO.getCloseTime(), DateUnit.MINUTE) < minOpenMinute) {
//如果缩短后时间间隔不满足最小开井时长则删除
sumOpenTime -= DateUtil.between(durationDTO.getOpenTime(), durationDTO.getCloseTime(), DateUnit.MINUTE);
optimizeDurationDTOList.remove(optimizeDurationDTOList.size() - 1);
} else {
//需要补时间,下面统一补
durationDTO.setOpenTime(openTimeNew);
}
}
}
}
break;
}
//重新按开井时间排序
optimizeDurationDTOList.sort(Comparator.comparing(SpaceOptimizeDurationDTO::getOpenTime));
//记录上一次第一次开井时间
if (0 == i1) {
firstOpenWellTime = DateUtil.date(optimizeDurationDTOList.get(0).getOpenTime());
}
}
//判断是否需要补时间
if (sumOpenTime < dayOpenMinute) {
//创建需要补时间的时间范围
List<SpaceOptimizeDurationDTO> replenishList = new ArrayList<>(12);
int size = optimizeDurationDTOList.size();
//记录下一个索引位置
int indexNextRecord = 0;
//记录上一个索引位置
int indexLastRecord;
for (int i1 = 0; i1 < size; i1++) {
//计算需要补的时间
long wantMinute = dayOpenMinute - sumOpenTime;
//取当前结果
SpaceOptimizeDurationDTO current = optimizeDurationDTOList.get(i1);
Date currentOpenTime = current.getOpenTime();
Date currentCloseTime = current.getCloseTime();
//计算开井时长
long openMinute = DateUtil.between(currentOpenTime, currentCloseTime, DateUnit.MINUTE);
//取下一段
if (indexNextRecord == size - 1) {
//如果到最后,下一段则取开头
indexNextRecord = 0;
} else {
indexNextRecord++;
}
SpaceOptimizeDurationDTO next = optimizeDurationDTOList.get(indexNextRecord);
Date nextOpenTime = next.getOpenTime();
//计算和下一段的关井时长
long nextCloseMinute;
if (indexNextRecord == 0) {
//跨天计算
nextCloseMinute = DateUtil.between(currentCloseTime, DateUtil.offsetDay(nextOpenTime, 1), DateUnit.MINUTE);
} else {
nextCloseMinute = DateUtil.between(currentCloseTime, nextOpenTime, DateUnit.MINUTE);
}
//取上一段
indexLastRecord = i1 - 1;
if (indexLastRecord == -1) {
//如果到最后,下一段则取开头
indexLastRecord = size - 1;
}
SpaceOptimizeDurationDTO last = optimizeDurationDTOList.get(indexLastRecord);
Date lastCloseTime = last.getCloseTime();
//计算和上一段的关井时长
long lastCloseMinute;
if (indexLastRecord == size - 1) {
//跨天计算
lastCloseMinute = DateUtil.between(currentOpenTime, DateUtil.offsetDay(lastCloseTime, -1), DateUnit.MINUTE);
} else {
lastCloseMinute = DateUtil.between(currentOpenTime, lastCloseTime, DateUnit.MINUTE);
}
//----------------------------------数据处理----------------------------------
//计算下一段可优化开井时长
long nextWantMinute = this.getCanMinute(nextCloseMinute, minCloseMinute, wantMinute);
//计算上一段可优化开井时长
long lastWantMinute = this.getCanMinute(lastCloseMinute, minCloseMinute, wantMinute);
//记录(左右移动标识位,可补时间)
boolean rightFlag = false;
boolean leftFlag = false;
long rightMinute = 0;
long leftMinute = 0;
//当前开井时长能否满足最小开井时长
if (openMinute >= minOpenMinute) {
//还能开多少
long spaceMinute = maxOpenMinute - openMinute;
if (spaceMinute <= 0) {
//没有优化空间 todo:研究研究是否需要往回缩 往回缩spaceMinute为负数
continue;
}
//补齐:先补小
boolean isNext = nextWantMinute < lastWantMinute;
if (isNext && nextWantMinute > 0) {
rightFlag = true;
if (nextWantMinute >= spaceMinute) {
//在范围内,直接补
rightMinute = spaceMinute;
} else {
rightMinute = nextWantMinute;
}
} else if (!isNext && lastWantMinute > 0) {
leftFlag = true;
if (lastWantMinute >= spaceMinute) {
//在范围内,直接补
leftMinute = spaceMinute;
} else {
leftMinute = lastWantMinute;
}
}
//补大:谁大取谁 ---- 计算一大一小,不冲突,不用重置变量
if (nextWantMinute > lastWantMinute) {
//往后移关井时间
rightFlag = true;
if (nextWantMinute >= spaceMinute) {
//在范围内,直接补
rightMinute = spaceMinute;
} else {
rightMinute = nextWantMinute;
}
} else {
//往前移开井时间
leftFlag = true;
if (lastWantMinute >= spaceMinute) {
//在范围内,直接补
leftMinute = spaceMinute;
} else {
leftMinute = lastWantMinute;
}
}
} else {
//不满足最小开井时长,计算一个可补时间的区间
long minLackMinute = minOpenMinute - openMinute;
long maxLackMinute = maxOpenMinute - openMinute;
//补齐:先补小
boolean isNext = nextWantMinute < lastWantMinute;
if (isNext && nextWantMinute > 0) {
rightFlag = true;
if (nextWantMinute >= minLackMinute && nextWantMinute <= maxLackMinute) {
//在区间范围内,直接补
rightMinute = nextWantMinute;
} else {
if (nextWantMinute >= minLackMinute) {
rightMinute = minLackMinute;
} else {
rightMinute = nextWantMinute;
}
}
} else if (!isNext && lastWantMinute > 0) {
leftFlag = true;
if (lastWantMinute >= minLackMinute && lastWantMinute <= maxLackMinute) {
//在区间范围内,直接补
leftMinute = lastWantMinute;
} else {
if (lastWantMinute >= minLackMinute) {
leftMinute = minLackMinute;
} else {
leftMinute = lastWantMinute;
}
}
}
//补大:谁大取谁 ---- 计算一大一小,不冲突,不用重置变量
if (nextWantMinute > lastWantMinute) {
//往后移关井时间
rightFlag = true;
if (nextWantMinute >= minLackMinute && nextWantMinute <= maxLackMinute) {
//在区间范围内,直接补
rightMinute = nextWantMinute;
} else {
if (nextWantMinute >= maxLackMinute) {
rightMinute = maxLackMinute;
} else {
rightMinute = nextWantMinute;
}
}
} else {
//往前移开井时间
leftFlag = true;
if (lastWantMinute >= minLackMinute && lastWantMinute <= maxLackMinute) {
//在区间范围内,直接补
leftMinute = lastWantMinute;
} else {
if (lastWantMinute >= maxLackMinute) {
leftMinute = maxLackMinute;
} else {
leftMinute = lastWantMinute;
}
}
}
}
//算时间补,修改总时间
DateTime optimizeTime;
if (rightFlag) {
optimizeTime = DateUtil.offsetMinute(currentCloseTime, (int) rightMinute);
if (DateUtil.compare(optimizeTime, BusinessConstant.DATE_FLAG) >= 0) {
//跨天转换,加到优化数组里
SpaceOptimizeDurationDTO copy = XCopyUtils.copyNewObject(current, SpaceOptimizeDurationDTO.class);
copy.setOpenTime(BusinessConstant.DATE_FLAG_BEGIN);
copy.setCloseTime(DateUtil.offsetDay(optimizeTime, -1));
replenishList.add(copy);
//重置结束时间到第二天0点
optimizeDurationDTOList.get(i1).setCloseTime(BusinessConstant.DATE_FLAG);
} else {
optimizeDurationDTOList.get(i1).setCloseTime(optimizeTime);
}
dayOpenMinute = dayOpenMinute - (int) rightMinute;
}
if (leftFlag) {
optimizeTime = DateUtil.offsetMinute(currentOpenTime, (int) -leftMinute);
if (DateUtil.compare(optimizeTime, BusinessConstant.DATE_FLAG_BEGIN) <= 0) {
//跨天转换,加到优化数组里
SpaceOptimizeDurationDTO copy = XCopyUtils.copyNewObject(current, SpaceOptimizeDurationDTO.class);
copy.setOpenTime(DateUtil.offsetDay(optimizeTime, 1));
copy.setCloseTime(BusinessConstant.DATE_FLAG);
replenishList.add(copy);
//重置开始时间到第一天0点
optimizeDurationDTOList.get(i1).setOpenTime(BusinessConstant.DATE_FLAG_BEGIN);
} else {
optimizeDurationDTOList.get(i1).setOpenTime(optimizeTime);
}
dayOpenMinute = dayOpenMinute - (int) leftMinute;
}
}
//将优化结果加入结果集中,并重新排序
if (CollUtil.isNotEmpty(replenishList)) {
//记录未使用的间开
List<SpaceOptimizeDurationDTO> tempList = new ArrayList<>(12);
//标识:0_未使用,1_开井用了,2_关井用了
int isIn;
for (SpaceOptimizeDurationDTO rep : replenishList) {
Date repOpenTime = rep.getOpenTime();
Date repCloseTime = rep.getCloseTime();
isIn = 0;
for (SpaceOptimizeDurationDTO dto : optimizeDurationDTOList) {
Date openTime = dto.getOpenTime();
Date closeTime = dto.getCloseTime();
if (DateUtil.isIn(repOpenTime, openTime, closeTime) && DateUtil.compare(repCloseTime, closeTime) >= 0) {
dto.setCloseTime(repCloseTime);
if (isIn == 1) {
continue;
}
isIn = 1;
}
if (DateUtil.isIn(repCloseTime, openTime, closeTime) && DateUtil.compare(repOpenTime, openTime) <= 0) {
dto.setOpenTime(repOpenTime);
if (isIn == 2) {
continue;
}
isIn = 2;
}
}
if (isIn == 0) {
tempList.add(rep);
}
}
if (CollUtil.isNotEmpty(tempList)) {
for (SpaceOptimizeDurationDTO temp : tempList) {
optimizeDurationDTOList.add(temp);
}
}
}
}
return firstOpenWellTime;
}
/**
* 可优化时长计算
*
* @param closeMinute 关闭分钟
* @param minCloseMinute 分钟关闭分钟
* @param wantMinute 需要补的总时间
* @return long
*/
private long getCanMinute(long closeMinute, long minCloseMinute, long wantMinute) {
long canMinute;
if (closeMinute == minCloseMinute) {
//满足最小关井时长舍弃
canMinute = 0;
} else if (closeMinute > minCloseMinute) {
//关井时长比最小设定值大
canMinute = closeMinute - minCloseMinute;
} else {
//不满足最小关井时长,能否合并
canMinute = closeMinute;
}
if (canMinute > wantMinute) {
canMinute = wantMinute;
}
if (wantMinute <= 0) {
canMinute = 0;
}
return canMinute;
}
/**
* 添加优化结果
*
* @param optimizeDurationDTOList 优化工期dtolist
* @param durationDTOList 持续时间dtolist
* @param tundraStrategyList 苔原战略清单
* @param periodId 期间id
* @param recordId 记录id
* @param wellheadId 井口id
* @param startDate 开始日期
*/
private void addOptimizeDuration(List<SpaceOptimizeDurationDTO> optimizeDurationDTOList, List<SpaceOptimizeDurationDTO> durationDTOList, List<SpaceOptimizeDurationDTO> tundraStrategyList,
String periodId, String recordId, String wellheadId, DateTime startDate) {
if (CollUtil.isNotEmpty(optimizeDurationDTOList)) {
//重新按开井时间排序
optimizeDurationDTOList.sort(Comparator.comparing(SpaceOptimizeDurationDTO::getOpenTime));
//时间段优化
List<SpaceOptimizeDurationDTO> newDurationDTOList = new ArrayList<>(optimizeDurationDTOList.size());
newDurationDTOList.add(optimizeDurationDTOList.get(0));
for (int i = 1; i < optimizeDurationDTOList.size(); i++) {
SpaceOptimizeDurationDTO nowOne = optimizeDurationDTOList.get(i);
SpaceOptimizeDurationDTO lastOne = newDurationDTOList.get(newDurationDTOList.size() - 1);
if (0 == DateUtil.compare(lastOne.getCloseTime(), nowOne.getOpenTime())) {
lastOne.setCloseTime(nowOne.getCloseTime());
} else {
newDurationDTOList.add(nowOne);
}
}
optimizeDurationDTOList = newDurationDTOList;
SpaceOptimizeDurationDTO durationDTO;
for (int i1 = 0; i1 < optimizeDurationDTOList.size(); i1++) {
durationDTO = optimizeDurationDTOList.get(i1);
DateTime startOffset = DateUtil.date(durationDTO.getOpenTime());
DateTime endOffset = DateUtil.date(durationDTO.getCloseTime());
if (startOffset.compareTo(BusinessConstant.DATE_FLAG) < 0 && endOffset.compareTo(BusinessConstant.DATE_FLAG) > 0) {
//如果时间超过当天,舍弃
this.createOptimizeDuration(durationDTOList, periodId, recordId, wellheadId, null,
startOffset.toString(BusinessConstant.MINUTES_FORMAT),
BusinessConstant.END_OF_DAY_TIME,
BusinessConstant.ONE, startDate
);
this.createOptimizeDuration(durationDTOList, periodId, recordId, wellheadId, null,
BusinessConstant.START_OF_DAY_TIME,
endOffset.toString(BusinessConstant.MINUTES_FORMAT),
BusinessConstant.ONE, startDate
);
tundraStrategyList.add(
SpaceOptimizeDurationDTO.builder()
.wellheadId(wellheadId)
.openTime(startOffset)
.closeTime(BusinessConstant.DATE_FLAG)
.build()
);
tundraStrategyList.add(
SpaceOptimizeDurationDTO.builder()
.wellheadId(wellheadId)
.openTime(BusinessConstant.DATE_FLAG_BEGIN)
.closeTime(endOffset)
.build()
);
} else if (endOffset.compareTo(BusinessConstant.DATE_FLAG) == 0) {
this.createOptimizeDuration(durationDTOList, periodId, recordId, wellheadId, null,
startOffset.toString(BusinessConstant.MINUTES_FORMAT),
BusinessConstant.END_OF_DAY_TIME,
BusinessConstant.ONE, startDate
);
tundraStrategyList.add(
SpaceOptimizeDurationDTO.builder()
.wellheadId(wellheadId)
.openTime(startOffset)
.closeTime(BusinessConstant.DATE_FLAG)
.build()
);
} else {
//计算偏移
this.createOptimizeDuration(durationDTOList, periodId, recordId, wellheadId, null,
startOffset.toString(BusinessConstant.MINUTES_FORMAT),
endOffset.toString(BusinessConstant.MINUTES_FORMAT),
BusinessConstant.ONE, startDate
);
tundraStrategyList.add(
SpaceOptimizeDurationDTO.builder()
.wellheadId(wellheadId)
.openTime(startOffset)
.closeTime(endOffset)
.build()
);
}
}
}
}
/**
* 添加离网优化时间
*
* @param durationDTOList 持续时间dtolist
* @param offGridDTOList 脱离网格dtolist
* @param weightList 重量清单
*/
private void addOffGridOptimizeDuration(List<SpaceOptimizeDurationDTO> durationDTOList, List<SpaceOptimizeDurationDTO> offGridDTOList, List<SpaceOptimizeWeight> weightList) {
if (CollUtil.isNotEmpty(offGridDTOList)) {
SpaceOptimizeDurationDTO durationDTO;
int index = 0;
DateTime beginTime = null;
String beginTypeKey = null;
SpaceOptimizeWeight weight;
for (int i1 = 0; i1 < offGridDTOList.size(); i1++) {
durationDTO = offGridDTOList.get(i1);
DateTime startTime = DateUtil.parse(durationDTO.getOpenWellTime() + BusinessConstant.INITIALIZATION_SECOND, BusinessConstant.TIME_FORMAT);
DateTime endTime = DateUtil.parse(durationDTO.getCloseWellTime() + BusinessConstant.INITIALIZATION_SECOND, BusinessConstant.TIME_FORMAT);
for (int i2 = index; i2 < weightList.size(); i2++) {
weight = weightList.get(i2);
if (DateUtil.isIn(weight.getTimestamp(), startTime, endTime)) {
if (CharSequenceUtil.isBlank(beginTypeKey)) {
beginTypeKey = weight.getGenerationTypeKey();
beginTime = startTime;
}
if (!CharSequenceUtil.equals(beginTypeKey, weight.getGenerationTypeKey()) && ObjectUtil.isNotNull(beginTime)) {
this.createOptimizeDuration(durationDTOList, durationDTO.getPeriodId(), durationDTO.getRecordId(), durationDTO.getWellheadId(), beginTypeKey,
beginTime.toString(BusinessConstant.MINUTES_FORMAT),
weight.getTimestamp().toString(BusinessConstant.MINUTES_FORMAT),
BusinessConstant.ONE, durationDTO.getOptimizeDate()
);
beginTime = weight.getTimestamp();
beginTypeKey = weight.getGenerationTypeKey();
}
}
//时间戳大于结束时间,跳出循环设置下次索引位置
if (DateUtil.compare(weight.getTimestamp(), endTime) > 0 && ObjectUtil.isNotNull(beginTime)) {
this.createOptimizeDuration(durationDTOList, durationDTO.getPeriodId(), durationDTO.getRecordId(), durationDTO.getWellheadId(), beginTypeKey,
beginTime.toString(BusinessConstant.MINUTES_FORMAT),
endTime.toString(BusinessConstant.MINUTES_FORMAT),
BusinessConstant.ONE, durationDTO.getOptimizeDate()
);
index = i2;
beginTypeKey = null;
break;
}
}
}
}
}
/**
* 获取间开井口列表
*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment