Commit c4649e95 authored by ZWT's avatar ZWT

feat(能源管理系统): 间开优化定时任务

1.开发间开优化长期间开优化定时任务,了解长期并网型流程算法;
2.定时任务模块新增CRON常量类,定义常用表达式;
3.基础信息管理--井口配置Cloud模块,添加通过井口ID集合获取井口列表接口,完成接口冒烟测试;
4.基础信息管理--输电线路配置Cloud模块,添加通过线路ID集合获取线路配置光伏电站列表接口,完成接口冒烟测试;
5.光伏预测--光伏预测Cloud模块,添加条件查询获取光伏预测时间段平均值列表接口,完成接口冒烟测试;
6.修改prediction模块pom引用,添加接口;

BREAKING CHANGE: 无

Closes 无

[skip ci]
parent 001c639d
...@@ -31,6 +31,7 @@ import xstartup.annotation.XService; ...@@ -31,6 +31,7 @@ import xstartup.annotation.XService;
import xstartup.base.XContext; import xstartup.base.XContext;
import xstartup.data.XListResult; import xstartup.data.XListResult;
import xstartup.data.XServiceResult; import xstartup.data.XServiceResult;
import xstartup.helper.XTransactionHelper;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; import java.util.*;
...@@ -53,214 +54,214 @@ public class SpaceOptimizeLongCloudServiceImpl implements ISpaceOptimizeLongClou ...@@ -53,214 +54,214 @@ public class SpaceOptimizeLongCloudServiceImpl implements ISpaceOptimizeLongClou
*/ */
@Override @Override
public XServiceResult optimizeLongJob(XContext context) { public XServiceResult optimizeLongJob(XContext context) {
//取生效中的基础间开 return XTransactionHelper.begin(context, () -> {
SpaceInstitutionDetailMapper detailMapper = context.getBean(SpaceInstitutionDetailMapper.class); //取生效中的基础间开
List<SpaceInstitutionDetailEnt> detailEntList = detailMapper.selectList(new LambdaQueryWrapper<SpaceInstitutionDetailEnt>() SpaceInstitutionDetailMapper detailMapper = context.getBean(SpaceInstitutionDetailMapper.class);
.eq(BaseModel::getIsDeleted, BusinessConstant.ONE) List<SpaceInstitutionDetailEnt> detailEntList = detailMapper.selectList(new LambdaQueryWrapper<SpaceInstitutionDetailEnt>()
.eq(SpaceInstitutionDetailEnt::getIsCurrentBasic, BusinessConstant.ZERO) .eq(BaseModel::getIsDeleted, BusinessConstant.ONE)
.apply(" institution_end_date >= CURDATE() ") .eq(SpaceInstitutionDetailEnt::getIsCurrentBasic, BusinessConstant.ZERO)
); .apply(" institution_end_date >= CURDATE() ")
if (CollUtil.isEmpty(detailEntList)) { );
//未发现可优化基础间开制度 if (CollUtil.isEmpty(detailEntList)) {
return XServiceResult.error(context, BusinessError.DidNotFindSpace); //未发现可优化基础间开制度
} return XServiceResult.error(context, BusinessError.DidNotFindSpace);
Set<String> lineIds = new HashSet<>(32); }
Set<String> institutionIds = new HashSet<>(32); Set<String> lineIds = new HashSet<>(32);
for (SpaceInstitutionDetailEnt spaceInstitutionDetailEnt : detailEntList) { Set<String> institutionIds = new HashSet<>(32);
lineIds.add(spaceInstitutionDetailEnt.getLineId()); for (SpaceInstitutionDetailEnt spaceInstitutionDetailEnt : detailEntList) {
institutionIds.add(spaceInstitutionDetailEnt.getId()); lineIds.add(spaceInstitutionDetailEnt.getLineId());
} institutionIds.add(spaceInstitutionDetailEnt.getId());
//取生效中间开的所有井口 }
SpaceInstitutionWellheadViewMapper wellheadMapper = context.getBean(SpaceInstitutionWellheadViewMapper.class); //取生效中间开的所有井口
List<SpaceInstitutionWellheadView> spaceWellheadList = wellheadMapper.selectListByInstitutionIds(institutionIds); SpaceInstitutionWellheadViewMapper wellheadMapper = context.getBean(SpaceInstitutionWellheadViewMapper.class);
if (CollUtil.isEmpty(spaceWellheadList)) { List<SpaceInstitutionWellheadView> spaceWellheadList = wellheadMapper.selectListByInstitutionIds(institutionIds);
//未发现可优化井口 if (CollUtil.isEmpty(spaceWellheadList)) {
return XServiceResult.error(context, BusinessError.DidNotFindWellhead); //未发现可优化井口
} return XServiceResult.error(context, BusinessError.DidNotFindWellhead);
this.setServiceRatingForSpaceWellheadList(context, spaceWellheadList); }
//取线路关联的所有光伏电站ID this.setServiceRatingForSpaceWellheadList(context, spaceWellheadList);
List<DynamicQueryBasePowerLinePlantOutput> plantList = this.getPowerLinePlantListByLineIds(context, lineIds); //取线路关联的所有光伏电站ID
if (CollUtil.isEmpty(plantList)) { List<DynamicQueryBasePowerLinePlantOutput> plantList = this.getPowerLinePlantListByLineIds(context, lineIds);
//未发现可用光伏电站 if (CollUtil.isEmpty(plantList)) {
return XServiceResult.error(context, BusinessError.DidNotFindPlant); //未发现可用光伏电站
} return XServiceResult.error(context, BusinessError.DidNotFindPlant);
//取当前时间 }
DateTime date = DateUtil.date(); //取当前时间
int year = date.year(); DateTime date = DateUtil.date();
int monthNum = date.month(); int year = date.year();
String month; int monthNum = date.month();
if (monthNum < BusinessConstant.TEN) { String month;
month = "0" + monthNum; if (monthNum < BusinessConstant.TEN) {
} else { month = "0" + monthNum;
month = String.valueOf(monthNum); } else {
} month = String.valueOf(monthNum);
SpaceInstitutionDurationMapper durationMapper = context.getBean(SpaceInstitutionDurationMapper.class); }
DateTime dateFlag = DateUtil.parse("1970-01-02 00:00:00", "yyyy-mm-dd HH:mm:ss"); SpaceInstitutionDurationMapper durationMapper = context.getBean(SpaceInstitutionDurationMapper.class);
//优化 DateTime dateFlag = DateUtil.parse("1970-01-02 00:00:00", "yyyy-mm-dd HH:mm:ss");
for (SpaceInstitutionDetailEnt detail : detailEntList) { //优化
String detailId = detail.getId(); for (SpaceInstitutionDetailEnt detail : detailEntList) {
switch (detail.getGridTypeKey()) { String detailId = detail.getId();
//并网型优化 switch (detail.getGridTypeKey()) {
case "1": //并网型优化
//获取当前制度对应的光伏预测数据列表 case "1":
List<DynamicQueryPlantPredictedPowerOutput> avgPowerList = this.getAveragePowerGenerationListByPlantIds(context, //获取当前制度对应的光伏预测数据列表
DynamicQueryPlantPredictedPowerInput.builder() List<DynamicQueryPlantPredictedPowerOutput> avgPowerList = this.getAveragePowerGenerationListByPlantIds(context,
.plantIds( DynamicQueryPlantPredictedPowerInput.builder()
plantList.stream() .plantIds(
.filter(p -> StringUtils.equals(detail.getLineId(), p.getLineId())) plantList.stream()
.map(DynamicQueryBasePowerLinePlantOutput::getPlantId) .filter(p -> StringUtils.equals(detail.getLineId(), p.getLineId()))
.collect(Collectors.toList()) .map(DynamicQueryBasePowerLinePlantOutput::getPlantId)
) .collect(Collectors.toList())
.yearTime(String.valueOf(year)) )
.monthTime(month) .yearTime(String.valueOf(year))
.build() .monthTime(month)
); .build()
//取光伏出力峰值
BigDecimal powerMax = avgPowerList.stream()
.map(DynamicQueryPlantPredictedPowerOutput::getPower)
.max(BigDecimal::compareTo)
.orElse(BigDecimal.ZERO);
//取当前制度下井口的总功率
BigDecimal wellheadTotalPower = spaceWellheadList.stream()
.filter(w -> StringUtils.equals(detailId, w.getInstitutionId()))
.map(SpaceInstitutionWellheadView::getServiceRating)
.reduce(BigDecimal.ZERO, BigDecimal::add);
int compare = powerMax.compareTo(wellheadTotalPower);
//光伏出力峰值大于等于井口总功率:绿电消纳优先策略
if (compare >= BusinessConstant.ZERO) {
//根据类型过滤井口:大间开,连抽井不优化
List<SpaceInstitutionWellheadView> wellheadViewList = spaceWellheadList.stream()
.filter(w -> StringUtils.equals(detailId, w.getInstitutionId()) &&
StringUtils.equals("INTERVAL", w.getRunTypeKey()) &&
StringUtils.equals("0", w.getIntervalTypeKey()))
.sorted(Comparator.comparing(SpaceInstitutionWellheadView::getStartSeq))
.collect(Collectors.toList());
if (CollUtil.isEmpty(wellheadViewList)) {
continue;
}
//通过间开ID和井口ID查所有井口时段配置
List<SpaceInstitutionDurationEnt> durationList = durationMapper.selectList(new LambdaQueryWrapper<SpaceInstitutionDurationEnt>()
.eq(BaseModel::getIsDeleted, BusinessConstant.ONE)
.eq(SpaceInstitutionDurationEnt::getInstitutionId, detailId)
.orderByAsc(SpaceInstitutionDurationEnt::getOpenWellTime)
); );
if (CollUtil.isEmpty(durationList)) { //取光伏出力峰值
//没有设置时间段,无法优化 BigDecimal powerMax = avgPowerList.stream()
continue; .map(DynamicQueryPlantPredictedPowerOutput::getPower)
} .max(BigDecimal::compareTo)
Map<String, List<SpaceInstitutionDurationEnt>> durationMap = durationList.stream() .orElse(BigDecimal.ZERO);
.collect(Collectors.groupingBy(SpaceInstitutionDurationEnt::getWellheadId)); //取当前制度下井口的总功率
int between = 0; BigDecimal wellheadTotalPower = spaceWellheadList.stream()
for (int w = 0, wellheadSize = wellheadViewList.size(); w < wellheadSize; w++) { .filter(w -> StringUtils.equals(detailId, w.getInstitutionId()))
SpaceInstitutionWellheadView wellhead = wellheadViewList.get(w); .map(SpaceInstitutionWellheadView::getServiceRating)
String wellheadId = wellhead.getWellheadId(); .reduce(BigDecimal.ZERO, BigDecimal::add);
//取当前井口最大发电量 int compare = powerMax.compareTo(wellheadTotalPower);
BigDecimal serviceRating = wellhead.getServiceRating(); //光伏出力峰值大于等于井口总功率:绿电消纳优先策略
List<SpaceInstitutionDurationEnt> durationConfigList = durationMap.get(wellhead); if (compare >= BusinessConstant.ZERO) {
if (CollUtil.isEmpty(durationConfigList)) { //根据类型过滤井口:大间开,连抽井不优化
List<SpaceInstitutionWellheadView> wellheadViewList = spaceWellheadList.stream()
.filter(w -> StringUtils.equals(detailId, w.getInstitutionId()) &&
StringUtils.equals("INTERVAL", w.getRunTypeKey()) &&
StringUtils.equals("0", w.getIntervalTypeKey()))
.sorted(Comparator.comparing(SpaceInstitutionWellheadView::getStartSeq))
.collect(Collectors.toList());
if (CollUtil.isEmpty(wellheadViewList)) {
continue;
}
//通过间开ID和井口ID查所有井口时段配置
List<SpaceInstitutionDurationEnt> durationList = durationMapper.selectList(new LambdaQueryWrapper<SpaceInstitutionDurationEnt>()
.eq(BaseModel::getIsDeleted, BusinessConstant.ONE)
.eq(SpaceInstitutionDurationEnt::getInstitutionId, detailId)
.orderByAsc(SpaceInstitutionDurationEnt::getOpenWellTime)
);
if (CollUtil.isEmpty(durationList)) {
//没有设置时间段,无法优化 //没有设置时间段,无法优化
continue; continue;
} }
Map<String, List<SpaceInstitutionDurationEnt>> durationMap = durationList.stream()
.collect(Collectors.groupingBy(SpaceInstitutionDurationEnt::getWellheadId));
for (int d = 0, durationSize = durationConfigList.size(); d < durationSize; d++) { int between = 0;
SpaceInstitutionDurationEnt duration = durationConfigList.get(d); for (int w = 0, wellheadSize = wellheadViewList.size(); w < wellheadSize; w++) {
String openWellTime = duration.getOpenWellTime(); SpaceInstitutionWellheadView wellhead = wellheadViewList.get(w);
DateTime startTime = DateUtil.parse(openWellTime + BusinessConstant.INITIALIZATION_SECOND, BusinessConstant.TIME_FORMAT); String wellheadId = wellhead.getWellheadId();
//第一次启动 //取当前井口最大发电量
if (d == 0) { BigDecimal serviceRating = wellhead.getServiceRating();
//累计发电量 List<SpaceInstitutionDurationEnt> durationConfigList = durationMap.get(wellhead);
BigDecimal totalPower = BigDecimal.ZERO; if (CollUtil.isEmpty(durationConfigList)) {
//计算开井时间 //没有设置时间段,无法优化
int endIndex = 0; continue;
DynamicQueryPlantPredictedPowerOutput predictedPower; }
for (int a = 0, avgPowerSize = avgPowerList.size(); a < avgPowerSize; a++) { for (int d = 0, durationSize = durationConfigList.size(); d < durationSize; d++) {
predictedPower = avgPowerList.get(a); SpaceInstitutionDurationEnt duration = durationConfigList.get(d);
if (DateUtil.parse(predictedPower.getHourTime() + predictedPower.getMinTime(), BusinessConstant.TIME_FORMAT).compareTo(startTime) < 0) { String openWellTime = duration.getOpenWellTime();
continue; DateTime startTime = DateUtil.parse(openWellTime + BusinessConstant.INITIALIZATION_SECOND, BusinessConstant.TIME_FORMAT);
//第一次启动
if (d == 0) {
//累计发电量
BigDecimal totalPower = BigDecimal.ZERO;
//计算开井时间
int endIndex = 0;
DynamicQueryPlantPredictedPowerOutput predictedPower;
for (int a = 0, avgPowerSize = avgPowerList.size(); a < avgPowerSize; a++) {
predictedPower = avgPowerList.get(a);
if (DateUtil.parse(predictedPower.getHourTime() + predictedPower.getMinTime(), BusinessConstant.TIME_FORMAT).compareTo(startTime) < 0) {
continue;
}
totalPower.add(predictedPower.getPower());
//当日时间段平均光伏出力总和>第一口井运行负荷时,该时间为第一口井启动时间
if (totalPower.compareTo(serviceRating) >= BusinessConstant.ZERO) {
endIndex = a;
break;
}
} }
totalPower.add(predictedPower.getPower()); DynamicQueryPlantPredictedPowerOutput end = avgPowerList.get(endIndex);
//当日时间段平均光伏出力总和>第一口井运行负荷时,该时间为第一口井启动时间 String endTimeString = end.getHourTime() + end.getMinTime();
if (totalPower.compareTo(serviceRating) >= BusinessConstant.ZERO) { SpaceOptimizeLongDurationEnt.builder()
endIndex = a; .wellheadId(wellheadId)
break; .isOptimize(BusinessConstant.ZERO)
.openWellTime(openWellTime)
.closeWellTime(StringUtils.substringBeforeLast(endTimeString, ":00"))
.build();
//计算
DateTime endTimeOptimize = DateUtil.parse(endTimeString, BusinessConstant.TIME_FORMAT);
DateTime endTime = DateUtil.parse(duration.getCloseWellTime() + BusinessConstant.INITIALIZATION_SECOND, BusinessConstant.TIME_FORMAT);
//取时间间隔(分钟)
between = (int) endTimeOptimize.between(endTime, DateUnit.MINUTE);
if (endTimeOptimize.compareTo(endTime) < 0) {
between = ~between + 1;
} }
}
DynamicQueryPlantPredictedPowerOutput end = avgPowerList.get(endIndex);
String endTimeString = end.getHourTime() + end.getMinTime();
SpaceOptimizeLongDurationEnt.builder()
.wellheadId(wellheadId)
.isOptimize(BusinessConstant.ZERO)
.openWellTime(openWellTime)
.closeWellTime(StringUtils.substringBeforeLast(endTimeString, ":00"))
.build();
//计算
DateTime endTimeOptimize = DateUtil.parse(endTimeString, BusinessConstant.TIME_FORMAT);
DateTime endTime = DateUtil.parse(duration.getCloseWellTime() + BusinessConstant.INITIALIZATION_SECOND, BusinessConstant.TIME_FORMAT);
//取时间间隔(分钟)
between = (int) endTimeOptimize.between(endTime, DateUnit.MINUTE);
if (endTimeOptimize.compareTo(endTime) < 0) {
between = ~between + 1;
}
} else {
DateTime offset = startTime.offset(DateField.MINUTE, between);
if (offset.compareTo(dateFlag) > 0) {
//如果时间超过当天,舍弃
continue;
}
DateTime endDate = DateUtil.parse(duration.getCloseWellTime() + BusinessConstant.INITIALIZATION_SECOND, BusinessConstant.TIME_FORMAT)
.offset(DateField.MINUTE, between);
String closeWellTime;
if (endDate.compareTo(dateFlag) > 0) {
closeWellTime = "23:30";
} else { } else {
closeWellTime = endDate.toString(BusinessConstant.MINUTES_FORMAT); DateTime offset = startTime.offset(DateField.MINUTE, between);
if (offset.compareTo(dateFlag) > 0) {
//如果时间超过当天,舍弃
continue;
}
DateTime endDate = DateUtil.parse(duration.getCloseWellTime() + BusinessConstant.INITIALIZATION_SECOND, BusinessConstant.TIME_FORMAT)
.offset(DateField.MINUTE, between);
String closeWellTime;
if (endDate.compareTo(dateFlag) > 0) {
closeWellTime = "23:30";
} else {
closeWellTime = endDate.toString(BusinessConstant.MINUTES_FORMAT);
}
//计算偏移
SpaceOptimizeLongDurationEnt.builder()
.wellheadId(wellheadId)
.isOptimize(BusinessConstant.ZERO)
.openWellTime(
offset.toString(BusinessConstant.MINUTES_FORMAT)
)
.closeWellTime(closeWellTime)
.build();
} }
//计算偏移
SpaceOptimizeLongDurationEnt.builder()
.wellheadId(wellheadId)
.isOptimize(BusinessConstant.ZERO)
.openWellTime(
offset.toString(BusinessConstant.MINUTES_FORMAT)
)
.closeWellTime(closeWellTime)
.build();
} }
} }
}
} else { } else {
//遍历井口,按发电功率大于等于光伏出力峰值条件分组 //遍历井口,按发电功率大于等于光伏出力峰值条件分组
Map<Boolean, List<SpaceInstitutionWellheadView>> collect = spaceWellheadList.stream() Map<Boolean, List<SpaceInstitutionWellheadView>> collect = spaceWellheadList.stream()
.filter(w -> .filter(w ->
StringUtils.equals(detailId, w.getInstitutionId()) StringUtils.equals(detailId, w.getInstitutionId())
) )
.collect(Collectors.partitioningBy(w -> powerMax.compareTo(w.getServiceRating()) >= BusinessConstant.ZERO)); .collect(Collectors.partitioningBy(w -> powerMax.compareTo(w.getServiceRating()) >= BusinessConstant.ZERO));
List<SpaceInstitutionWellheadView> lowWellheadList = collect.get(false); List<SpaceInstitutionWellheadView> lowWellheadList = collect.get(false);
int size = spaceWellheadList.size(); int size = spaceWellheadList.size();
int lowWellheadListSize = lowWellheadList.size(); int lowWellheadListSize = lowWellheadList.size();
//光伏出力峰值<任何一口井的运行功率:消峰平谷策略 //光伏出力峰值<任何一口井的运行功率:消峰平谷策略
if (size == lowWellheadListSize) { if (size == lowWellheadListSize) {
} }
//光伏出力峰值>=线路哪部分井口运行功率:满足的部分井口采用绿电消纳优先,不满足的井采用消峰平谷 //光伏出力峰值>=线路哪部分井口运行功率:满足的部分井口采用绿电消纳优先,不满足的井采用消峰平谷
else { else {
List<SpaceInstitutionWellheadView> highWellheadList = collect.get(true); List<SpaceInstitutionWellheadView> highWellheadList = collect.get(true);
}
} }
} break;
break; //离网型优化
//离网型优化 case "0":
case "0": break;
break; default:
default: //电网类型不存在
//电网类型不存在 }
} }
} return XServiceResult.OK;
return XServiceResult.OK; });
} }
/*-----------------------------------private-----------------------------------*/ /*-----------------------------------private-----------------------------------*/
......
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