mirror of
https://codeup.aliyun.com/64f7d6b8ce01efaafef1e678/coal/coal.git
synced 2026-07-25 07:07:37 +08:00
feat(CoalBlend): add reserve fields to DTOs for extended data handling
This commit is contained in:
@@ -57,4 +57,19 @@ public class CoalBlendDto extends OrgCommonDto {
|
||||
|
||||
@Comment("求目标参数最大值 -1, 最小值 1")
|
||||
private Integer targetOrder = -1;
|
||||
|
||||
@Comment("备用字段1")
|
||||
private String reserve1;
|
||||
|
||||
@Comment("备用字段2")
|
||||
private String reserve2;
|
||||
|
||||
@Comment("备用字段3")
|
||||
private String reserve3;
|
||||
|
||||
@Comment("备用字段4")
|
||||
private String reserve4;
|
||||
|
||||
@Comment("备用字段5")
|
||||
private String reserve5;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,21 @@ public class CoalBlendRequest {
|
||||
@Comment("煤类型")
|
||||
private String coalType;
|
||||
|
||||
@Comment("备用字段1")
|
||||
private String reserve1;
|
||||
|
||||
@Comment("备用字段2")
|
||||
private String reserve2;
|
||||
|
||||
@Comment("备用字段3")
|
||||
private String reserve3;
|
||||
|
||||
@Comment("备用字段4")
|
||||
private String reserve4;
|
||||
|
||||
@Comment("备用字段5")
|
||||
private String reserve5;
|
||||
|
||||
|
||||
public Map<String, CoalConstraint> getConstraintMap() {
|
||||
if (constraintMap == null) {
|
||||
|
||||
@@ -52,4 +52,19 @@ public class CreateCoalBlendDto extends OrgCommonDto {
|
||||
|
||||
@Comment("求目标参数最大值 -1, 最小值 1")
|
||||
private Integer targetOrder = -1;
|
||||
|
||||
@Comment("备用字段1")
|
||||
private String reserve1;
|
||||
|
||||
@Comment("备用字段2")
|
||||
private String reserve2;
|
||||
|
||||
@Comment("备用字段3")
|
||||
private String reserve3;
|
||||
|
||||
@Comment("备用字段4")
|
||||
private String reserve4;
|
||||
|
||||
@Comment("备用字段5")
|
||||
private String reserve5;
|
||||
}
|
||||
|
||||
@@ -50,4 +50,21 @@ public class UpdateCoalBlendDto extends OrgCommonDto {
|
||||
|
||||
@Comment("求目标参数最大值 -1, 最小值 1")
|
||||
private Integer targetOrder = -1;
|
||||
|
||||
|
||||
@Comment("备用字段1")
|
||||
private String reserve1;
|
||||
|
||||
@Comment("备用字段2")
|
||||
private String reserve2;
|
||||
|
||||
@Comment("备用字段3")
|
||||
private String reserve3;
|
||||
|
||||
@Comment("备用字段4")
|
||||
private String reserve4;
|
||||
|
||||
@Comment("备用字段5")
|
||||
private String reserve5;
|
||||
|
||||
}
|
||||
|
||||
@@ -27,14 +27,17 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@Comment("配煤记录")
|
||||
@Slf4j
|
||||
|
||||
@jakarta.persistence.Table(indexes = @jakarta.persistence.Index(name ="idx_coal_blend_org_id", columnList = "organization_id"))
|
||||
|
||||
@jakarta.persistence.Table(
|
||||
indexes =
|
||||
@jakarta.persistence.Index(
|
||||
name = "idx_coal_blend_org_id",
|
||||
columnList = "organization_id"))
|
||||
public class CoalBlendEntity extends OrgCommonEntity {
|
||||
|
||||
@ElementCollection
|
||||
@@ -55,13 +58,9 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
@Comment("煤类型")
|
||||
private String coalType;
|
||||
|
||||
|
||||
|
||||
@Comment("配煤类型")
|
||||
private String blendType;
|
||||
|
||||
|
||||
|
||||
@Comment("结果个数")
|
||||
private Integer count = 20;
|
||||
|
||||
@@ -77,6 +76,23 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
@Comment("求目标参数最大值 -1, 最小值 1")
|
||||
private Integer targetOrder = -1;
|
||||
|
||||
|
||||
@Comment("备用字段1")
|
||||
private String reserve1;
|
||||
|
||||
@Comment("备用字段2")
|
||||
private String reserve2;
|
||||
|
||||
@Comment("备用字段3")
|
||||
private String reserve3;
|
||||
|
||||
@Comment("备用字段4")
|
||||
private String reserve4;
|
||||
|
||||
@Comment("备用字段5")
|
||||
private String reserve5;
|
||||
|
||||
|
||||
public void blend() {
|
||||
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
@@ -267,12 +283,22 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
// 原煤配煤逻辑 / 人工配煤
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
// 使用新的CpSolver工具类计算原煤配煤
|
||||
AtomicBoolean[] paramFlags = {
|
||||
param1, param2, param3, param4, param5, param6, param7, param8, param9, param10,
|
||||
param11, param12, param13, param14, param15, param16, param17, param18, param19, param20
|
||||
};
|
||||
|
||||
this.results = cn.lihongjie.coal.coalBlend.util.RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
coals, constrains, coalType, count, maxTime, paramFlags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtils.equals(coalType, "2") || StringUtils.equals(coalType, "3")) {
|
||||
|
||||
if (StringUtils.equals(coalType, "3")) {
|
||||
// 参与配比的煤的可能性 1 到 煤的数量之间
|
||||
// <editor-fold desc="Description">
|
||||
int startIndex = StringUtils.equals(coalType, "2") ? 0 : coals.size() - 1;
|
||||
int startIndex = coals.size() - 1;
|
||||
for (int i = startIndex; i < coals.size(); i++) {
|
||||
|
||||
// todo 这个循环可以删除?
|
||||
@@ -282,7 +308,7 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
CoalBlendCoalInfoEntity coalJ = coals.get(j);
|
||||
|
||||
int maxP = (int) (coalJ.getYmPercent() * 100);
|
||||
int startK = StringUtils.equals(coalType, "2") ? 0 : maxP;
|
||||
int startK = maxP;
|
||||
for (int k = startK; k <= maxP; k++) {
|
||||
// 计算各个参数是否满足条件
|
||||
CoalBlendResultInfoEntity vo = new CoalBlendResultInfoEntity();
|
||||
@@ -298,6 +324,18 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
|
||||
sum += k * coalJ.getParam1() / 10000.0;
|
||||
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -319,7 +357,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam2() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -341,7 +389,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam3() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -363,7 +421,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam4() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -385,7 +453,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam5() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -407,7 +485,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam6() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -429,7 +517,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam7() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -451,7 +549,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam8() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -473,7 +581,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam9() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -495,7 +613,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam10() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -517,7 +645,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam11() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -539,7 +677,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam12() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -561,7 +709,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam13() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -583,7 +741,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam14() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -605,7 +773,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam15() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -627,7 +805,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam16() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -649,7 +837,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam17() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -671,7 +869,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam18() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -693,7 +901,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam19() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -715,7 +933,17 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
sum += k * coalJ.getParam20() / 10000.0;
|
||||
if (StringUtils.equals(coalType, "2")) {
|
||||
|
||||
// 截止目前所有比例之和 <=100
|
||||
double psum =
|
||||
IntStream.rangeClosed(0, j)
|
||||
.mapToDouble(x -> coals.get(x).getYmPercent())
|
||||
.sum()
|
||||
+ k / 100.0;
|
||||
|
||||
sum = sum * 100 / psum;
|
||||
}
|
||||
sum =
|
||||
BigDecimal.valueOf(sum)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
@@ -1566,4 +1794,4 @@ public class CoalBlendEntity extends OrgCommonEntity {
|
||||
}
|
||||
|
||||
public static class BlendGroup {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
package cn.lihongjie.coal.coalBlend.util;
|
||||
|
||||
import cn.lihongjie.coal.coalBlend.entity.CoalBlendCoalInfoEntity;
|
||||
import cn.lihongjie.coal.coalBlend.entity.CoalBlendConstrainVo;
|
||||
import cn.lihongjie.coal.coalBlend.entity.CoalBlendResultInfoEntity;
|
||||
import cn.lihongjie.coal.coalBlend.entity.CoalPercentVo;
|
||||
|
||||
import com.google.ortools.Loader;
|
||||
import com.google.ortools.sat.*;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* 原煤配煤计算器
|
||||
* 专门处理coalType=2的原煤配煤逻辑,使用CpSolver进行求解
|
||||
*/
|
||||
@Slf4j
|
||||
public class RawCoalBlendCalculator { /**
|
||||
* 计算原煤配煤方案
|
||||
*
|
||||
* @param coals 煤种信息列表
|
||||
* @param constrains 约束条件
|
||||
* @param coalType 煤类型
|
||||
* @param count 结果数量限制
|
||||
* @param maxTime 最大求解时间(秒)
|
||||
* @param paramFlags 参数标志位数组 (param1-param20)
|
||||
* @return 配煤结果列表
|
||||
*/
|
||||
public static List<CoalBlendResultInfoEntity> calculateRawCoalBlend(
|
||||
List<CoalBlendCoalInfoEntity> coals,
|
||||
CoalBlendConstrainVo constrains,
|
||||
String coalType,
|
||||
Integer count,
|
||||
Integer maxTime,
|
||||
AtomicBoolean[] paramFlags) {
|
||||
|
||||
List<CoalBlendResultInfoEntity> results = new ArrayList<>();
|
||||
|
||||
try {
|
||||
Loader.loadNativeLibraries();
|
||||
|
||||
// 创建CP模型
|
||||
CpModel model = new CpModel();
|
||||
|
||||
// 创建决策变量:每种煤的使用比例(整数,单位为0.01%)
|
||||
List<IntVar> coalPercents = new ArrayList<>();
|
||||
for (int i = 0; i < coals.size(); i++) {
|
||||
CoalBlendCoalInfoEntity coal = coals.get(i);
|
||||
int maxPercent = (int) (coal.getYmPercent() * 100); // 转换为0.01%单位
|
||||
coalPercents.add(model.newIntVar(0, maxPercent, "coal_" + i));
|
||||
}
|
||||
|
||||
// 添加基本约束:总和必须大于0(至少要有一种煤被使用)
|
||||
model.addGreaterThan(LinearExpr.sum(coalPercents.toArray(new IntVar[0])), 0);
|
||||
|
||||
// 添加原煤配煤的核心约束:只有前一种煤完全使用后才能使用下一种煤
|
||||
addSequentialConstraints(model, coalPercents, coals);
|
||||
|
||||
// 添加参数约束
|
||||
addParameterConstraints(model, coalPercents, coals, constrains, paramFlags);
|
||||
|
||||
// 创建求解器
|
||||
CpSolver solver = new CpSolver();
|
||||
SatParameters.Builder parameters = solver.getParameters();
|
||||
parameters.setEnumerateAllSolutions(true);
|
||||
parameters.setMaxTimeInSeconds(maxTime);
|
||||
parameters.setLogSearchProgress(false);
|
||||
|
||||
HashSet<String> seen = new HashSet<>();
|
||||
|
||||
// 求解
|
||||
solver.solve(model, new CpSolverSolutionCallback() {
|
||||
@Override
|
||||
public void onSolutionCallback() {
|
||||
// 检查解的有效性
|
||||
if (!isValidSolution(this, coalPercents)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CoalBlendResultInfoEntity result = createResult(this, coalPercents, coals, paramFlags);
|
||||
|
||||
// 去重
|
||||
String key = generateSolutionKey(result);
|
||||
if (seen.contains(key)) {
|
||||
return;
|
||||
}
|
||||
seen.add(key);
|
||||
|
||||
results.add(result);
|
||||
|
||||
if (results.size() >= count) {
|
||||
stopSearch();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("原煤配煤计算失败", e);
|
||||
}
|
||||
|
||||
return results;
|
||||
} /**
|
||||
* 添加顺序约束:只有前一种煤完全使用后才能使用下一种煤
|
||||
*/
|
||||
private static void addSequentialConstraints(CpModel model, List<IntVar> coalPercents, List<CoalBlendCoalInfoEntity> coals) {
|
||||
// 实现严格的顺序约束:如果使用后面的煤,前面的煤必须完全使用
|
||||
for (int i = 1; i < coalPercents.size(); i++) {
|
||||
IntVar currentCoal = coalPercents.get(i);
|
||||
IntVar previousCoal = coalPercents.get(i - 1);
|
||||
|
||||
// 获取前一种煤的最大使用量(ymPercent * 100,因为我们用的是百分比*100的整数)
|
||||
Double previousMaxPercent = coals.get(i - 1).getYmPercent();
|
||||
if (previousMaxPercent == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long previousMaxValue = (long) (previousMaxPercent * 100);
|
||||
|
||||
// 创建布尔变量表示当前煤是否被使用
|
||||
BoolVar isCurrentUsed = model.newBoolVar("is_coal_" + i + "_used");
|
||||
|
||||
// 定义当前煤是否被使用的条件
|
||||
model.addGreaterThan(currentCoal, 0).onlyEnforceIf(isCurrentUsed);
|
||||
model.addEquality(currentCoal, 0).onlyEnforceIf(isCurrentUsed.not());
|
||||
|
||||
// 如果当前煤被使用,前一种煤必须完全使用
|
||||
model.addEquality(previousCoal, previousMaxValue).onlyEnforceIf(isCurrentUsed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加参数约束条件
|
||||
*/
|
||||
private static void addParameterConstraints(CpModel model, List<IntVar> coalPercents,
|
||||
List<CoalBlendCoalInfoEntity> coals, CoalBlendConstrainVo constrains, AtomicBoolean[] paramFlags) {
|
||||
|
||||
for (int paramIndex = 1; paramIndex <= 20; paramIndex++) {
|
||||
if (!paramFlags[paramIndex - 1].get()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String paramName = "param" + paramIndex;
|
||||
try {
|
||||
// 获取约束的最小值和最大值
|
||||
Double minValue = getConstraintValue(constrains, paramName, "Min");
|
||||
Double maxValue = getConstraintValue(constrains, paramName, "Max");
|
||||
|
||||
if (minValue != null || maxValue != null) {
|
||||
addSingleParameterConstraint(model, coalPercents, coals, paramName, minValue, maxValue);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("添加参数{}约束失败", paramName, e);
|
||||
}
|
||||
}
|
||||
} /**
|
||||
* 添加单个参数的约束条件
|
||||
*/
|
||||
private static void addSingleParameterConstraint(CpModel model, List<IntVar> coalPercents,
|
||||
List<CoalBlendCoalInfoEntity> coals, String paramName, Double minValue, Double maxValue) {
|
||||
|
||||
try {
|
||||
// 计算加权参数值 = Σ(煤i的比例 * 煤i的参数值) / 总比例
|
||||
long[] coefficients = new long[coalPercents.size()];
|
||||
boolean hasValidCoal = false;
|
||||
|
||||
for (int i = 0; i < coals.size(); i++) {
|
||||
Double paramValue = coals.get(i).getParam(paramName);
|
||||
if (paramValue != null) {
|
||||
// 系数需要放大100倍来处理小数(比例是百分比*100)
|
||||
coefficients[i] = (long) (paramValue * 100);
|
||||
hasValidCoal = true;
|
||||
} else {
|
||||
coefficients[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasValidCoal) {
|
||||
return;
|
||||
}
|
||||
|
||||
LinearExpr weightedSum = LinearExpr.weightedSum(coalPercents.toArray(new IntVar[0]), coefficients);
|
||||
LinearExpr totalPercent = LinearExpr.sum(coalPercents.toArray(new IntVar[0]));
|
||||
|
||||
// 验证约束是否可能满足
|
||||
if (minValue != null) {
|
||||
// 计算所有煤种的参数值范围
|
||||
double minPossibleValue = Double.MAX_VALUE;
|
||||
double maxPossibleValue = Double.MIN_VALUE;
|
||||
|
||||
for (CoalBlendCoalInfoEntity coal : coals) {
|
||||
Double paramValue = coal.getParam(paramName);
|
||||
if (paramValue != null) {
|
||||
minPossibleValue = Math.min(minPossibleValue, paramValue);
|
||||
maxPossibleValue = Math.max(maxPossibleValue, paramValue);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有煤种的参数值都小于最小约束值,则无解
|
||||
if (maxPossibleValue < minValue) {
|
||||
log.info("参数{}约束无法满足:最大可能值{}小于最小约束值{}", paramName, maxPossibleValue, minValue);
|
||||
model.addEquality(model.newConstant(0), 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加最小值约束:weightedSum >= minValue * totalPercent
|
||||
long minBound = (long) (minValue * 100);
|
||||
LinearExpr minConstraint = LinearExpr.newBuilder()
|
||||
.addTerm(totalPercent, minBound)
|
||||
.build();
|
||||
model.addGreaterOrEqual(weightedSum, minConstraint);
|
||||
}
|
||||
|
||||
if (maxValue != null) {
|
||||
// 计算所有煤种的参数值范围
|
||||
double minPossibleValue = Double.MAX_VALUE;
|
||||
double maxPossibleValue = Double.MIN_VALUE;
|
||||
|
||||
for (CoalBlendCoalInfoEntity coal : coals) {
|
||||
Double paramValue = coal.getParam(paramName);
|
||||
if (paramValue != null) {
|
||||
minPossibleValue = Math.min(minPossibleValue, paramValue);
|
||||
maxPossibleValue = Math.max(maxPossibleValue, paramValue);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有煤种的参数值都大于最大约束值,则无解
|
||||
if (minPossibleValue > maxValue) {
|
||||
log.info("参数{}约束无法满足:最小可能值{}大于最大约束值{}", paramName, minPossibleValue, maxValue);
|
||||
model.addEquality(model.newConstant(0), 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加最大值约束:weightedSum <= maxValue * totalPercent
|
||||
long maxBound = (long) (maxValue * 100);
|
||||
LinearExpr maxConstraint = LinearExpr.newBuilder()
|
||||
.addTerm(totalPercent, maxBound)
|
||||
.build();
|
||||
model.addLessOrEqual(weightedSum, maxConstraint);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("添加参数{}约束失败", paramName, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取约束值
|
||||
*/
|
||||
private static Double getConstraintValue(CoalBlendConstrainVo constrains, String paramName, String type) {
|
||||
try {
|
||||
String methodName = "get" + paramName.substring(0, 1).toUpperCase() +
|
||||
paramName.substring(1) + type;
|
||||
return (Double) constrains.getClass().getMethod(methodName).invoke(constrains);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 检查解的有效性
|
||||
*/
|
||||
private static boolean isValidSolution(CpSolverSolutionCallback callback, List<IntVar> coalPercents) {
|
||||
// 至少要有一种煤被使用
|
||||
for (IntVar percent : coalPercents) {
|
||||
if (callback.value(percent) > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 创建结果对象
|
||||
*/
|
||||
private static CoalBlendResultInfoEntity createResult(CpSolverSolutionCallback callback, List<IntVar> coalPercents,
|
||||
List<CoalBlendCoalInfoEntity> coals, AtomicBoolean[] paramFlags) {
|
||||
|
||||
CoalBlendResultInfoEntity result = new CoalBlendResultInfoEntity();
|
||||
|
||||
// 设置比例信息
|
||||
List<CoalPercentVo> percentInfos = new ArrayList<>();
|
||||
double totalPercent = 0;
|
||||
|
||||
for (int i = 0; i < coalPercents.size(); i++) {
|
||||
long percentValue = callback.value(coalPercents.get(i));
|
||||
if (percentValue > 0) {
|
||||
double percent = percentValue / 100.0; // 转换回百分比
|
||||
percentInfos.add(new CoalPercentVo(
|
||||
coals.get(i).getName(),
|
||||
percent,
|
||||
0.0,
|
||||
percent));
|
||||
totalPercent += percent;
|
||||
}
|
||||
}
|
||||
|
||||
result.setPercentInfos(percentInfos);
|
||||
result.setPercentSum(totalPercent);
|
||||
|
||||
// 计算各参数值
|
||||
calculateParameterValues(result, coals, callback, coalPercents, paramFlags, totalPercent);
|
||||
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* 计算参数值
|
||||
*/
|
||||
private static void calculateParameterValues(CoalBlendResultInfoEntity result,
|
||||
List<CoalBlendCoalInfoEntity> coals, CpSolverSolutionCallback callback, List<IntVar> coalPercents,
|
||||
AtomicBoolean[] paramFlags, double totalPercent) {
|
||||
|
||||
if (totalPercent <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int paramIndex = 1; paramIndex <= 20; paramIndex++) {
|
||||
if (!paramFlags[paramIndex - 1].get()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String paramName = "param" + paramIndex;
|
||||
double weightedSum = 0;
|
||||
|
||||
for (int i = 0; i < coals.size(); i++) {
|
||||
long percentValue = callback.value(coalPercents.get(i));
|
||||
if (percentValue > 0) {
|
||||
Double paramValue = coals.get(i).getParam(paramName);
|
||||
if (paramValue != null) {
|
||||
weightedSum += (percentValue / 100.0) * paramValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double averageValue = weightedSum / totalPercent;
|
||||
|
||||
try {
|
||||
String setterName = "setParam" + paramIndex;
|
||||
result.getClass().getMethod(setterName, Double.class).invoke(result, averageValue);
|
||||
} catch (Exception e) {
|
||||
log.warn("设置参数{}值失败", paramName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成解的唯一标识
|
||||
*/
|
||||
private static String generateSolutionKey(CoalBlendResultInfoEntity result) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (CoalPercentVo percent : result.getPercentInfos()) {
|
||||
sb.append(percent.getName()).append(":").append(percent.getPercent()).append(";");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,623 @@
|
||||
package cn.lihongjie.coal.coalBlend.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import cn.lihongjie.coal.coalBlend.entity.CoalBlendCoalInfoEntity;
|
||||
import cn.lihongjie.coal.coalBlend.entity.CoalBlendConstrainVo;
|
||||
import cn.lihongjie.coal.coalBlend.entity.CoalBlendResultInfoEntity;
|
||||
import cn.lihongjie.coal.coalBlend.entity.CoalPercentVo;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* 原煤配煤计算器测试类
|
||||
* 测试RawCoalBlendCalculator的各种场景
|
||||
*/
|
||||
class RawCoalBlendCalculatorTest {
|
||||
|
||||
private List<CoalBlendCoalInfoEntity> testCoals;
|
||||
private CoalBlendConstrainVo testConstrains;
|
||||
private AtomicBoolean[] testParamFlags;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// 初始化测试数据
|
||||
testCoals = createTestCoals();
|
||||
testConstrains = createTestConstrains();
|
||||
testParamFlags = createTestParamFlags();
|
||||
}
|
||||
|
||||
// 辅助方法:创建测试数据
|
||||
private List<CoalBlendCoalInfoEntity> createTestCoals() {
|
||||
List<CoalBlendCoalInfoEntity> coals = new ArrayList<>();
|
||||
coals.add(createCoal("煤A", 30.0, 12.0, 25.0, 8.0));
|
||||
coals.add(createCoal("煤B", 40.0, 15.0, 30.0, 10.0));
|
||||
coals.add(createCoal("煤C", 50.0, 18.0, 35.0, 12.0));
|
||||
return coals;
|
||||
}
|
||||
|
||||
private List<CoalBlendCoalInfoEntity> createSimpleTestCoals() {
|
||||
List<CoalBlendCoalInfoEntity> coals = new ArrayList<>();
|
||||
coals.add(createCoal("煤A", 50.0, 12.0, 25.0, 8.0));
|
||||
coals.add(createCoal("煤B", 30.0, 15.0, 30.0, 10.0));
|
||||
return coals;
|
||||
}
|
||||
|
||||
private List<CoalBlendCoalInfoEntity> createTestCoalsWithConstraints() {
|
||||
List<CoalBlendCoalInfoEntity> coals = new ArrayList<>();
|
||||
coals.add(createCoal("煤A", 40.0, 10.0, 20.0, 5.0));
|
||||
coals.add(createCoal("煤B", 30.0, 20.0, 40.0, 15.0));
|
||||
coals.add(createCoal("煤C", 30.0, 30.0, 60.0, 25.0));
|
||||
return coals;
|
||||
}
|
||||
|
||||
private List<CoalBlendCoalInfoEntity> createTestCoalsForSequentialTest() {
|
||||
List<CoalBlendCoalInfoEntity> coals = new ArrayList<>();
|
||||
coals.add(createCoal("煤A", 25.0, 10.0, 20.0, 5.0));
|
||||
coals.add(createCoal("煤B", 35.0, 15.0, 30.0, 8.0));
|
||||
coals.add(createCoal("煤C", 40.0, 20.0, 40.0, 10.0));
|
||||
return coals;
|
||||
}
|
||||
|
||||
private List<CoalBlendCoalInfoEntity> createLargeCoalList() {
|
||||
List<CoalBlendCoalInfoEntity> coals = new ArrayList<>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
coals.add(createCoal("煤" + i, 20.0 + i * 5, 10.0 + i * 2, 20.0 + i * 3, 5.0 + i));
|
||||
}
|
||||
return coals;
|
||||
}
|
||||
|
||||
private List<CoalBlendCoalInfoEntity> createCoalsWithExtremeValues() {
|
||||
List<CoalBlendCoalInfoEntity> coals = new ArrayList<>();
|
||||
coals.add(createCoal("极小值煤", 10.0, 0.1, 1.0, 0.1));
|
||||
coals.add(createCoal("极大值煤", 90.0, 99.9, 100.0, 50.0));
|
||||
return coals;
|
||||
}
|
||||
|
||||
private CoalBlendCoalInfoEntity createCoal(String name, Double ymPercent, Double param1, Double param2, Double param3) {
|
||||
CoalBlendCoalInfoEntity coal = new CoalBlendCoalInfoEntity();
|
||||
coal.setName(name);
|
||||
coal.setYmPercent(ymPercent);
|
||||
coal.setParam1(param1);
|
||||
coal.setParam2(param2);
|
||||
coal.setParam3(param3);
|
||||
return coal;
|
||||
}
|
||||
|
||||
private CoalBlendConstrainVo createTestConstrains() {
|
||||
CoalBlendConstrainVo constrains = new CoalBlendConstrainVo();
|
||||
constrains.setParam1Min(10.0);
|
||||
constrains.setParam1Max(20.0);
|
||||
constrains.setParam2Min(20.0);
|
||||
constrains.setParam2Max(40.0);
|
||||
constrains.setParam3Min(5.0);
|
||||
constrains.setParam3Max(15.0);
|
||||
return constrains;
|
||||
}
|
||||
|
||||
private CoalBlendConstrainVo createSimpleConstrains() {
|
||||
CoalBlendConstrainVo constrains = new CoalBlendConstrainVo();
|
||||
constrains.setParam1Min(5.0);
|
||||
constrains.setParam1Max(25.0);
|
||||
return constrains;
|
||||
}
|
||||
|
||||
private CoalBlendConstrainVo createStrictConstrains() {
|
||||
CoalBlendConstrainVo constrains = new CoalBlendConstrainVo();
|
||||
constrains.setParam1Min(12.0);
|
||||
constrains.setParam1Max(18.0);
|
||||
constrains.setParam2Min(25.0);
|
||||
constrains.setParam2Max(35.0);
|
||||
return constrains;
|
||||
}
|
||||
|
||||
private CoalBlendConstrainVo createImpossibleConstrains() {
|
||||
CoalBlendConstrainVo constrains = new CoalBlendConstrainVo();
|
||||
constrains.setParam1Min(100.0);
|
||||
constrains.setParam1Max(200.0);
|
||||
return constrains;
|
||||
}
|
||||
|
||||
private AtomicBoolean[] createTestParamFlags() {
|
||||
AtomicBoolean[] flags = new AtomicBoolean[20];
|
||||
for (int i = 0; i < 20; i++) {
|
||||
flags[i] = new AtomicBoolean(i < 3); // 只开启前3个参数
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
private AtomicBoolean[] createSimpleParamFlags() {
|
||||
AtomicBoolean[] flags = new AtomicBoolean[20];
|
||||
for (int i = 0; i < 20; i++) {
|
||||
flags[i] = new AtomicBoolean(i == 0); // 只开启第一个参数
|
||||
}
|
||||
return flags;
|
||||
} // 验证方法
|
||||
|
||||
private void verifySequentialConstraints(CoalBlendResultInfoEntity result, List<CoalBlendCoalInfoEntity> coals) {
|
||||
List<CoalPercentVo> percentInfos = result.getPercentInfos();
|
||||
|
||||
// 构建煤种名称到索引的映射
|
||||
java.util.Map<String, Integer> coalIndexMap = new java.util.HashMap<>();
|
||||
for (int i = 0; i < coals.size(); i++) {
|
||||
coalIndexMap.put(coals.get(i).getName(), i);
|
||||
}
|
||||
|
||||
// 检查顺序约束
|
||||
for (CoalPercentVo percentInfo : percentInfos) {
|
||||
int currentIndex = coalIndexMap.get(percentInfo.getName());
|
||||
|
||||
// 如果当前煤种被使用,检查前面的煤种是否完全使用
|
||||
if (percentInfo.getPercent() > 0) {
|
||||
for (int i = 0; i < currentIndex; i++) {
|
||||
String previousCoalName = coals.get(i).getName();
|
||||
Double previousMaxPercent = coals.get(i).getYmPercent();
|
||||
|
||||
// 查找前一种煤的使用情况
|
||||
CoalPercentVo previousPercent = percentInfos.stream()
|
||||
.filter(p -> p.getName().equals(previousCoalName))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
if (previousPercent != null) {
|
||||
// 前一种煤应该完全使用(允许小的误差)
|
||||
double tolerance = 0.01;
|
||||
assertTrue(Math.abs(previousPercent.getPercent() - previousMaxPercent) < tolerance,
|
||||
String.format("煤种 %s 被使用时,前一种煤 %s 应该完全使用。期望: %f, 实际: %f",
|
||||
percentInfo.getName(), previousCoalName, previousMaxPercent, previousPercent.getPercent()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyParameterConstraints(CoalBlendResultInfoEntity result, CoalBlendConstrainVo constrains) {
|
||||
// 这里可以通过反射获取result的参数值,并验证是否满足约束条件
|
||||
// 由于CoalBlendResultInfoEntity的结构未知,这里只做基本验证
|
||||
|
||||
assertNotNull(result.getPercentInfos());
|
||||
assertTrue(result.getPercentSum() > 0);
|
||||
assertTrue(result.getPercentSum() <= 100);
|
||||
|
||||
// 验证每个煤种的比例都在合理范围内
|
||||
for (CoalPercentVo percentInfo : result.getPercentInfos()) {
|
||||
assertTrue(percentInfo.getPercent() >= 0);
|
||||
assertTrue(percentInfo.getPercent() <= 100);
|
||||
}
|
||||
}
|
||||
|
||||
// 打印方法
|
||||
private void printCoalInfo(List<CoalBlendCoalInfoEntity> coals) {
|
||||
System.out.println("--- 煤种信息 ---");
|
||||
for (int i = 0; i < coals.size(); i++) {
|
||||
CoalBlendCoalInfoEntity coal = coals.get(i);
|
||||
System.out.printf("煤种[%d]: %s\n", i, coal.getName());
|
||||
System.out.printf(" 原煤比例(ymPercent): %.2f%%\n", coal.getYmPercent());
|
||||
System.out.printf(" 参数1(param1): %.2f\n", coal.getParam1());
|
||||
System.out.printf(" 参数2(param2): %.2f\n", coal.getParam2());
|
||||
System.out.printf(" 参数3(param3): %.2f\n", coal.getParam3());
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
private void printConstraints(CoalBlendConstrainVo constrains) {
|
||||
System.out.println("--- 约束条件 ---");
|
||||
if (constrains.getParam1Min() != null || constrains.getParam1Max() != null) {
|
||||
System.out.printf("参数1约束: [%.2f, %.2f]\n",
|
||||
constrains.getParam1Min(), constrains.getParam1Max());
|
||||
}
|
||||
if (constrains.getParam2Min() != null || constrains.getParam2Max() != null) {
|
||||
System.out.printf("参数2约束: [%.2f, %.2f]\n",
|
||||
constrains.getParam2Min(), constrains.getParam2Max());
|
||||
}
|
||||
if (constrains.getParam3Min() != null || constrains.getParam3Max() != null) {
|
||||
System.out.printf("参数3约束: [%.2f, %.2f]\n",
|
||||
constrains.getParam3Min(), constrains.getParam3Max());
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private void printParamFlags(AtomicBoolean[] paramFlags) {
|
||||
System.out.println("--- 参数标志 ---");
|
||||
for (int i = 0; i < Math.min(paramFlags.length, 5); i++) {
|
||||
System.out.printf("参数%d: %s\n", i + 1, paramFlags[i].get() ? "启用" : "禁用");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private void printResults(List<CoalBlendResultInfoEntity> results) {
|
||||
System.out.println("--- 计算结果 ---");
|
||||
System.out.printf("找到 %d 个解决方案\n", results.size());
|
||||
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
CoalBlendResultInfoEntity result = results.get(i);
|
||||
System.out.printf("\n方案 %d:\n", i + 1);
|
||||
System.out.printf("总比例: %.2f%%\n", result.getPercentSum());
|
||||
|
||||
System.out.println("煤种配比:");
|
||||
for (CoalPercentVo percentInfo : result.getPercentInfos()) {
|
||||
System.out.printf(" %s: %.2f%% (原煤比例: %.2f%%)\n",
|
||||
percentInfo.getName(),
|
||||
percentInfo.getPercent(),
|
||||
percentInfo.getYmPercent());
|
||||
}
|
||||
|
||||
// 打印参数值(如果有的话)
|
||||
printResultParameters(result);
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private void printResultParameters(CoalBlendResultInfoEntity result) {
|
||||
System.out.println("计算得到的参数值:");
|
||||
try {
|
||||
// 使用反射获取参数值
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
String getterName = "getParam" + i;
|
||||
try {
|
||||
java.lang.reflect.Method getter = result.getClass().getMethod(getterName);
|
||||
Object value = getter.invoke(result);
|
||||
if (value != null) {
|
||||
System.out.printf(" 参数%d: %.4f\n", i, (Double) value);
|
||||
}
|
||||
} catch (Exception e) { // 忽略方法不存在的情况
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println(" 无法获取参数值: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("基本功能测试")
|
||||
class BasicFunctionTests { @Test
|
||||
@DisplayName("测试正常的原煤配煤计算")
|
||||
void testNormalRawCoalBlendCalculation() {
|
||||
// 准备测试数据
|
||||
List<CoalBlendCoalInfoEntity> coals = createSimpleTestCoals();
|
||||
CoalBlendConstrainVo constrains = createSimpleConstrains();
|
||||
AtomicBoolean[] paramFlags = createSimpleParamFlags();
|
||||
|
||||
// 输出测试输入信息
|
||||
System.out.println("=== 正常原煤配煤计算测试 ===");
|
||||
printCoalInfo(coals);
|
||||
printConstraints(constrains);
|
||||
printParamFlags(paramFlags);
|
||||
|
||||
// 执行计算
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
coals, constrains, "2", 5, 10, paramFlags);
|
||||
|
||||
// 输出结果
|
||||
printResults(results);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(results);
|
||||
assertFalse(results.isEmpty());
|
||||
|
||||
// 验证第一个结果
|
||||
CoalBlendResultInfoEntity result = results.get(0);
|
||||
assertNotNull(result.getPercentInfos());
|
||||
assertTrue(result.getPercentSum() > 0);
|
||||
|
||||
// 验证顺序约束:如果第二种煤被使用,第一种煤必须完全使用
|
||||
verifySequentialConstraints(result, coals);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试空煤种列表")
|
||||
void testEmptyCoalList() {
|
||||
List<CoalBlendCoalInfoEntity> emptyCoals = new ArrayList<>();
|
||||
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
emptyCoals, testConstrains, "2", 5, 10, testParamFlags);
|
||||
|
||||
assertNotNull(results);
|
||||
assertTrue(results.isEmpty());
|
||||
} @Test
|
||||
@DisplayName("测试单一煤种")
|
||||
void testSingleCoalType() {
|
||||
List<CoalBlendCoalInfoEntity> singleCoal = new ArrayList<>();
|
||||
singleCoal.add(createCoal("煤A", 50.0, 12.0, 25.0, 8.0));
|
||||
|
||||
System.out.println("=== 单一煤种测试 ===");
|
||||
printCoalInfo(singleCoal);
|
||||
printConstraints(testConstrains);
|
||||
printParamFlags(testParamFlags);
|
||||
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
singleCoal, testConstrains, "2", 5, 10, testParamFlags);
|
||||
|
||||
printResults(results);
|
||||
|
||||
assertNotNull(results);
|
||||
if (!results.isEmpty()) {
|
||||
CoalBlendResultInfoEntity result = results.get(0);
|
||||
assertEquals(1, result.getPercentInfos().size());
|
||||
assertEquals("煤A", result.getPercentInfos().get(0).getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("约束条件测试")
|
||||
class ConstraintTests { @Test
|
||||
@DisplayName("测试参数约束条件")
|
||||
void testParameterConstraints() {
|
||||
// 创建有严格约束的测试数据
|
||||
List<CoalBlendCoalInfoEntity> coals = createTestCoalsWithConstraints();
|
||||
CoalBlendConstrainVo constrains = createStrictConstrains();
|
||||
AtomicBoolean[] paramFlags = createTestParamFlags();
|
||||
|
||||
System.out.println("=== 参数约束测试 ===");
|
||||
printCoalInfo(coals);
|
||||
printConstraints(constrains);
|
||||
printParamFlags(paramFlags);
|
||||
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
coals, constrains, "2", 5, 10, paramFlags);
|
||||
|
||||
printResults(results);
|
||||
|
||||
assertNotNull(results);
|
||||
|
||||
// 验证结果是否满足约束条件
|
||||
for (CoalBlendResultInfoEntity result : results) {
|
||||
verifyParameterConstraints(result, constrains);
|
||||
}
|
||||
}@Test
|
||||
@DisplayName("测试顺序约束")
|
||||
void testSequentialConstraints() {
|
||||
List<CoalBlendCoalInfoEntity> coals = createTestCoalsForSequentialTest();
|
||||
|
||||
System.out.println("=== 顺序约束测试 ===");
|
||||
printCoalInfo(coals);
|
||||
printConstraints(testConstrains);
|
||||
printParamFlags(testParamFlags);
|
||||
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
coals, testConstrains, "2", 10, 10, testParamFlags);
|
||||
|
||||
printResults(results);
|
||||
|
||||
assertNotNull(results);
|
||||
|
||||
// 验证每个结果都满足顺序约束
|
||||
for (CoalBlendResultInfoEntity result : results) {
|
||||
verifySequentialConstraints(result, coals);
|
||||
}
|
||||
} @Test
|
||||
@DisplayName("测试无解的约束条件")
|
||||
void testImpossibleConstraints() {
|
||||
List<CoalBlendCoalInfoEntity> coals = createSimpleTestCoals();
|
||||
CoalBlendConstrainVo impossibleConstrains = createImpossibleConstrains();
|
||||
|
||||
System.out.println("=== 无解约束测试 ===");
|
||||
printCoalInfo(coals);
|
||||
printConstraints(impossibleConstrains);
|
||||
printParamFlags(testParamFlags);
|
||||
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
coals, impossibleConstrains, "2", 5, 5, testParamFlags);
|
||||
|
||||
printResults(results);
|
||||
|
||||
assertNotNull(results);
|
||||
// 无解的情况下,结果应该为空
|
||||
assertTrue(results.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("性能和边界测试")
|
||||
class PerformanceAndBoundaryTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("测试超时处理")
|
||||
void testTimeoutHandling() {
|
||||
List<CoalBlendCoalInfoEntity> largeCoalList = createLargeCoalList();
|
||||
|
||||
// 设置很短的超时时间
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
largeCoalList, testConstrains, "2", 100, 1, testParamFlags);
|
||||
|
||||
assertNotNull(results);
|
||||
// 即使超时,也应该返回部分结果
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试结果数量限制")
|
||||
void testResultCountLimit() {
|
||||
List<CoalBlendCoalInfoEntity> coals = createSimpleTestCoals();
|
||||
int maxCount = 3;
|
||||
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
coals, testConstrains, "2", maxCount, 10, testParamFlags);
|
||||
|
||||
assertNotNull(results);
|
||||
assertTrue(results.size() <= maxCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试极端参数值")
|
||||
void testExtremeParameterValues() {
|
||||
List<CoalBlendCoalInfoEntity> coals = createCoalsWithExtremeValues();
|
||||
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
coals, testConstrains, "2", 5, 10, testParamFlags);
|
||||
|
||||
assertNotNull(results);
|
||||
// 验证结果的合理性
|
||||
for (CoalBlendResultInfoEntity result : results) {
|
||||
assertTrue(result.getPercentSum() >= 0);
|
||||
assertTrue(result.getPercentSum() <= 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("参数标志测试")
|
||||
class ParamFlagTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("测试所有参数标志关闭")
|
||||
void testAllParamFlagsOff() {
|
||||
AtomicBoolean[] allOffFlags = new AtomicBoolean[20];
|
||||
for (int i = 0; i < 20; i++) {
|
||||
allOffFlags[i] = new AtomicBoolean(false);
|
||||
}
|
||||
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
testCoals, testConstrains, "2", 5, 10, allOffFlags);
|
||||
|
||||
assertNotNull(results);
|
||||
// 没有参数约束时,应该能找到更多解
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试部分参数标志开启")
|
||||
void testPartialParamFlagsOn() {
|
||||
AtomicBoolean[] partialFlags = new AtomicBoolean[20];
|
||||
for (int i = 0; i < 20; i++) {
|
||||
partialFlags[i] = new AtomicBoolean(i < 3); // 只开启前3个参数
|
||||
}
|
||||
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
testCoals, testConstrains, "2", 5, 10, partialFlags);
|
||||
|
||||
assertNotNull(results);
|
||||
|
||||
// 验证结果中只有前3个参数被计算
|
||||
for (CoalBlendResultInfoEntity result : results) {
|
||||
// 这里可以进一步验证只有param1-param3被设置
|
||||
assertNotNull(result.getPercentInfos());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("用户提供数据测试")
|
||||
class UserProvidedDataTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("测试1:参数1约束 [13-13]")
|
||||
void testCase1_Param1_Constraint_13_13() {
|
||||
// 准备测试数据
|
||||
List<CoalBlendCoalInfoEntity> coals = createUserProvidedCoals();
|
||||
CoalBlendConstrainVo constrains = createConstraint1();
|
||||
AtomicBoolean[] paramFlags = createParam1OnlyFlags();
|
||||
|
||||
System.out.println("=== 测试1:参数1约束 [13-13] ===");
|
||||
printCoalInfo(coals);
|
||||
printConstraints(constrains);
|
||||
printParamFlags(paramFlags);
|
||||
|
||||
// 执行计算
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
coals, constrains, "2", 10, 10, paramFlags);
|
||||
|
||||
printResults(results);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(results);
|
||||
|
||||
// 验证每个结果都满足约束条件
|
||||
for (CoalBlendResultInfoEntity result : results) {
|
||||
// 验证参数1值在约束范围内
|
||||
Double param1Value = result.getParam1();
|
||||
assertNotNull(param1Value);
|
||||
assertTrue(param1Value >= 12.9 && param1Value <= 13.1,
|
||||
String.format("参数1值 %f 应该在 [13, 13] 范围内", param1Value));
|
||||
|
||||
// 验证顺序约束
|
||||
verifySequentialConstraints(result, coals);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试2:参数1约束 [13-25]")
|
||||
void testCase2_Param1_Constraint_13_25() {
|
||||
// 准备测试数据
|
||||
List<CoalBlendCoalInfoEntity> coals = createUserProvidedCoals();
|
||||
CoalBlendConstrainVo constrains = createConstraint2();
|
||||
AtomicBoolean[] paramFlags = createParam1OnlyFlags();
|
||||
|
||||
System.out.println("=== 测试2:参数1约束 [13-25] ===");
|
||||
printCoalInfo(coals);
|
||||
printConstraints(constrains);
|
||||
printParamFlags(paramFlags);
|
||||
|
||||
// 执行计算
|
||||
List<CoalBlendResultInfoEntity> results = RawCoalBlendCalculator.calculateRawCoalBlend(
|
||||
coals, constrains, "2", 10, 10, paramFlags);
|
||||
|
||||
printResults(results);
|
||||
|
||||
// 验证结果
|
||||
assertNotNull(results);
|
||||
assertFalse(results.isEmpty());
|
||||
|
||||
// 验证每个结果都满足约束条件
|
||||
for (CoalBlendResultInfoEntity result : results) {
|
||||
// 验证参数1值在约束范围内
|
||||
Double param1Value = result.getParam1();
|
||||
assertNotNull(param1Value);
|
||||
assertTrue(param1Value >= 12.9 && param1Value <= 25.1,
|
||||
String.format("参数1值 %f 应该在 [13, 25] 范围内", param1Value));
|
||||
|
||||
// 验证顺序约束
|
||||
verifySequentialConstraints(result, coals);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建用户提供的煤种数据
|
||||
*/
|
||||
private List<CoalBlendCoalInfoEntity> createUserProvidedCoals() {
|
||||
List<CoalBlendCoalInfoEntity> coals = new ArrayList<>();
|
||||
coals.add(createCoal("1.4", 1.07, 10.3, null, null));
|
||||
coals.add(createCoal("1.5", 26.64, 17.89, null, null));
|
||||
coals.add(createCoal("1.6", 29.41, 25.01, null, null));
|
||||
coals.add(createCoal("1.8", 32.65, 39.63, null, null));
|
||||
coals.add(createCoal("矸石", 10.23, 56.8, null, null));
|
||||
return coals;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建测试1的约束条件:参数1 [13-13]
|
||||
*/
|
||||
private CoalBlendConstrainVo createConstraint1() {
|
||||
CoalBlendConstrainVo constrains = new CoalBlendConstrainVo();
|
||||
constrains.setParam1Min(13.0);
|
||||
constrains.setParam1Max(13.0);
|
||||
return constrains;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建测试2的约束条件:参数1 [13-25]
|
||||
*/
|
||||
private CoalBlendConstrainVo createConstraint2() {
|
||||
CoalBlendConstrainVo constrains = new CoalBlendConstrainVo();
|
||||
constrains.setParam1Min(13.0);
|
||||
constrains.setParam1Max(25.0);
|
||||
return constrains;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建只启用参数1的标志位
|
||||
*/ private AtomicBoolean[] createParam1OnlyFlags() {
|
||||
AtomicBoolean[] flags = new AtomicBoolean[20];
|
||||
for (int i = 0; i < 20; i++) {
|
||||
flags[i] = new AtomicBoolean(i == 0); // 只开启第一个参数(param1)
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user