Commit bb3dce7b authored by DESKTOP-VKRD9QF\Administration's avatar DESKTOP-VKRD9QF\Administration

合并远端master并保留主计划场景逻辑

parents b0e9cc4a 8ada9aff
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
package com.aps.macroplanner;
import com.aps.common.util.FileHelper;
import com.aps.macroplanner.data.BenchmarkDataBuilder;
import com.aps.macroplanner.data.LargeScaleBomTestDataBuilder;
import com.google.ortools.Loader;
import com.aps.macroplanner.data.MultiLevelBomTestDataBuilder;
import com.aps.macroplanner.data.TestDataBuilder;
......@@ -23,6 +26,18 @@ import com.aps.macroplanner.data.TestDataBuilder;
* </ol>
*/
public class MultiLevelBomTestRunner {
private static final String LOG_DIR = "mp";
/** LP 模型文件路径 */
/** 运行日志文件路径 */
private static final String LOG_FILE_PATH = LOG_DIR + "/log/";
private static void writeLog(String msg)
{
FileHelper.writeFile(msg,LOG_FILE_PATH,"log.txt");
}
public static void main(String[] args) {
Loader.loadNativeLibraries();
......@@ -31,12 +46,21 @@ public class MultiLevelBomTestRunner {
System.out.println("Demand: P1=40/day, P2=30/day");
System.out.println();
TestDataBuilder data = new MultiLevelBomTestDataBuilder();
System.out.println("Data loaded: " + data.getProducts().size() + " products, "
+ data.getOperations().size() + " operations");
MacroPlannerOptimizer optimizer = new MacroPlannerOptimizer(data);
optimizer.buildModel();
optimizer.solve();
System.out.println("===== MULTI-LEVEL BOM TEST RUNNER END =====");
for (int scale : BenchmarkDataBuilder.SUPPORTED_SCALES) {
BenchmarkDataBuilder data =
BenchmarkDataBuilder.forScale(scale);
writeLog("===== TEST RUNNER START "+scale+"=====");
data.init();
writeLog("Data loaded: " + data.getProducts().size() + " products, "
+ data.getOperations().size() + " operations");
MacroPlannerOptimizer optimizer = new MacroPlannerOptimizer(data);
optimizer.buildModel();
optimizer.solve(String.valueOf(scale));
writeLog("===== TEST RUNNER END =====");
}
}
}
\ No newline at end of file
......@@ -53,8 +53,8 @@ public class BalanceConstraint {
}
}
// DemandSlack
MPVariable slackVar = demandSlackVars.get(invKey);
if (slackVar != null) balance.setCoefficient(slackVar, 1.0);
// MPVariable slackVar = demandSlackVars.get(invKey);
// if (slackVar != null) balance.setCoefficient(slackVar, 1.0);
// 上一周期库存 (t>0)
if (p.getIndex() > 0) {
String prevKey = prod.getId() + "_" + sp.getId() + "_" + (p.getIndex() - 1);
......
package com.aps.macroplanner.constraint;
import com.aps.common.util.FileHelper;
import com.aps.macroplanner.data.TestDataBuilder;
import com.aps.macroplanner.model.MacroPlannerModel;
......@@ -117,7 +118,21 @@ public class ConstraintFactory {
logSummary(model);
}
private static final String LOG_DIR = "mp";
/** LP 模型文件路径 */
private static final String LP_FILE_PATH = LOG_DIR + "/lp/";
/** 运行日志文件路径 */
private static final String LOG_FILE_PATH = LOG_DIR + "/log/";
private static void writeLog(String msg)
{
FileHelper.writeFile(msg,LOG_FILE_PATH,"log.txt");
}
private static void writeLog(String msg, Object... args)
{
FileHelper.writeFile(String.format(msg,args),LOG_FILE_PATH,"log.txt");
}
/**
* 记录单个约束构建步骤的结果。
* 输出新增约束数和新增变量数, 如果为 0 则输出 WARNING。
......@@ -129,7 +144,7 @@ public class ConstraintFactory {
int addedVariables = model.getSolver().numVariables() - beforeVariables;
if (addedConstraints == 0 && addedVariables == 0) {
LOG.warning(String.format("[%s] %s → 未创建任何约束或变量! (可能数据为空)",
writeLog(String.format("[%s] %s → 未创建任何约束或变量! (可能数据为空)",
stepName, description));
} else {
StringBuilder sb = new StringBuilder();
......@@ -140,7 +155,7 @@ public class ConstraintFactory {
if (addedVariables > 0) {
sb.append(String.format(" | +%d变量", addedVariables));
}
LOG.info(sb.toString());
writeLog(sb.toString());
}
}
......
package com.aps.macroplanner.data;
import java.util.Arrays;
/** 六档 MacroPlanner Benchmark 规模。 */
public final class BenchmarkScales {
private BenchmarkScales() {}
public static int[] all() {
return Arrays.copyOf(
BenchmarkDataBuilder.SUPPORTED_SCALES,
BenchmarkDataBuilder.SUPPORTED_SCALES.length);
}
/** 顺序创建六档数据,方便现有 Benchmark/性能测试接入。 */
public static void forEach(java.util.function.IntFunction<BenchmarkDataBuilder> consumer) {
for (int scale : all()) consumer.apply(scale);
}
}
......@@ -165,9 +165,10 @@ public class MacroPlannerDataConverter {
// 0. 读取时间配置, 计算 horizonEnd
// endCount = 期数 (与 periodDimension 结合决定实际天数)
ApsTimeConfig timeConfig = apsTimeConfigService.getOne(new LambdaQueryWrapper<>());
ctx.baseTime = (timeConfig != null && timeConfig.getBaseTime() != null)
? timeConfig.getBaseTime() : LocalDateTime.now();
ApsTimeConfig timeConfig = apsTimeConfigService.getOne(
new LambdaQueryWrapper<ApsTimeConfig>()
.eq(ApsTimeConfig::getMpSceneId, "172f545f-f94c-4143-86af-bd65e787e8e6"));
ctx.baseTime = LocalDateTime.of(2026, 9, 28, 0, 0, 0);
ctx.periodDimension = "DAY";
if (timeConfig != null && timeConfig.getPeriodDimension() != null
......
......@@ -73,9 +73,11 @@ public class TestDataBuilder {
* @param skipAutoBuild 如果为 true, 不自动调用 build(), 由子类负责初始化
*/
protected TestDataBuilder(boolean skipAutoBuild) {
if (!skipAutoBuild) {
build();
}
}
public void init() {
build();
}
/** 初始化默认测试数据 (3级BOM, 5产品, 5操作, 4周期) */
......
......@@ -99,14 +99,26 @@ public class MacroPlannerModel {
* 变量创建由 {@link VariableFactory} 负责。
*/
public MacroPlannerModel() {
this.solver = MPSolver.createSolver("SCIP");
this.solver = MPSolver.createSolver("CBC");
if (solver == null) {
throw new RuntimeException("无法加载 SCIP 求解器,请检查 OR-Tools 依赖");
}
solver.enableOutput();
// solver.setSolverSpecificParametersAsString("numerics/feastol = 1e-5");
// solver.setSolverSpecificParametersAsString("numerics/epsilon = 1e-8");
// solver.setSolverSpecificParametersAsString("misc/checkfeastolfac = 1.0");
// solver.setSolverSpecificParametersAsString("presolving/maxrounds = 1");
// solver.setSolverSpecificParametersAsString("crossover = true");
// solver.setSolverSpecificParametersAsString("algorithm = barrier");
solver.setSolverSpecificParametersAsString("log level 2");
// solver.setTimeLimit(5*60*1000);
solver.setNumThreads(4);
solver.enableOutput();
}
// ==================== KPI 变量 setter (由 KpiAggregator 调用) ====================
......
......@@ -422,7 +422,7 @@ public class ResultWriter {
sr.setPeriodStartDate(sd.getPeriod().getStartDate().toString());
sr.setDemandQty(sd.getQuantity());
sr.setPriority(sd.getPriority());
sr.setDemandOrderDate(sd.getDemandOrderDate().toString());
sr.setDemandOrderDate(sd.getDemandOrderDate()==null?"":sd.getDemandOrderDate().toString());
double fulfilled = solutionValue(model.getSalesDemandQtyVars(), sd.getKey()).setScale(3, RoundingMode.HALF_UP).doubleValue();;
double unmet = Math.max(0, sd.getQuantity() - fulfilled);
......@@ -686,7 +686,7 @@ public class ResultWriter {
DemandSummaryResult.PeriodEntry pe = new DemandSummaryResult.PeriodEntry();
pe.periodIndex = sd.getPeriod().getIndex();
pe.periodStartDate = sd.getPeriod().getStartDate().toString();
pe.demandOrderDate= sd.getDemandOrderDate().toString();
pe.demandOrderDate=sd.getDemandOrderDate()==null?"": sd.getDemandOrderDate().toString();
pe.demandQty = demand;
pe.fulfilledQty = fulfilled;
pe.unmetQty = unmet;
......
......@@ -4,6 +4,7 @@ import com.aps.common.util.FileHelper;
import com.aps.macroplanner.data.*;
import com.aps.macroplanner.model.MacroPlannerModel;
import com.aps.macroplanner.objective.StrategyLevel;
import com.google.ortools.linearsolver.MPSolver;
import java.util.*;
import java.time.LocalDate;
......@@ -54,10 +55,10 @@ public class SolutionPrinter {
/** 输出完整求解结果 */
public void printAll() {
printBomStructure();
printHeader();
printDailyViews();
printKpiSummary();
// printBomStructure();
// printHeader();
// printDailyViews();
// printKpiSummary();
printStatistics();
}
......@@ -224,8 +225,8 @@ public class SolutionPrinter {
hasProduction = true;
double capacityUsed = ptQty * uo.getCapacityCoeff();
if (up.isUnlimited()) {
writeLog(" %s(%s): %.0f件 (耗时%.1fh)",
op.getName(), formatOutputProducts(op), ptQty, capacityUsed);
writeLog(" %s(%s): %.0f件",
op.getName(), formatOutputProducts(op), ptQty);
} else {
double utilPercent = up.getMaxCapacity() > 0
? capacityUsed / up.getMaxCapacity() * 100 : 0;
......@@ -635,10 +636,17 @@ public class SolutionPrinter {
private void printStatistics() {
long elapsedMs = System.currentTimeMillis() - startTimeMs;
MPSolver solver= model.getSolver();
writeLog("");
writeLog("═══════════════════ 求解统计 ═══════════════════");
writeLog("变量数:%d", model.getSolver().numVariables());
writeLog("约束数:%d", model.getSolver().numConstraints());
writeLog("变量数:%d", solver.numVariables());
writeLog("约束数:%d", solver.numConstraints());
double primalBound = solver.objective().value(); // 当前最好可行解目标值(原始界)
double dualBound = solver.objective().bestBound(); // 全局理论最优边界(对偶界)
writeLog("当前最好可行解目标值:%f", primalBound);
writeLog("全局理论最优边界:%f", dualBound);
double gap = Math.abs(primalBound - dualBound) / Math.abs(primalBound) * 100;
writeLog("gap:%.2f", gap);
writeLog("耗时:%.3f 秒", elapsedMs / 1000.0);
}
......
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