Commit e3a2cdde authored by Tong Li's avatar Tong Li

MP

parent 8dc6c378
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
package com.aps.macroplanner;
import com.aps.common.util.FileHelper;
import com.google.ortools.Loader;
import com.google.ortools.linearsolver.MPSolver;
import com.aps.macroplanner.constraint.ConstraintFactory;
......@@ -89,7 +90,7 @@ public class MacroPlannerOptimizer {
private static final String LP_FILE_PATH = LOG_DIR + "/lp/model.lp";
/** 运行日志文件路径 */
private static final String LOG_FILE_PATH = LOG_DIR + "/log/log.txt";
private static final String LOG_FILE_PATH = LOG_DIR + "/log/";
/** 获取模型容器 (供 ResultWriter 等外部组件使用)。 */
public MacroPlannerModel getModel() { return model; }
......@@ -148,6 +149,11 @@ public class MacroPlannerOptimizer {
rootLogger.addHandler(handler);
}
private void writeLog(String msg)
{
FileHelper.writeFile(msg,LOG_FILE_PATH,"log.txt");
}
// ==================== 模型构建流程 ====================
/**
......@@ -163,32 +169,32 @@ public class MacroPlannerOptimizer {
// 设为 FINE 可输出每个周期的折算详情
configureLogging();
System.out.println("=== 开始构建 MacroPlanner 优化模型 ===\n");
writeLog("=== 开始构建 MacroPlanner 优化模型 ===\n");
// 0. 数据完整性检查 (在构建模型前验证)
DataValidator validator = new DataValidator(data);
if (!validator.validate()) {
System.out.println(" ⚠️ 数据检查发现错误, 求解结果可能不可靠\n");
writeLog(" ⚠️ 数据检查发现错误, 求解结果可能不可靠\n");
} else {
System.out.println(" [OK] 数据检查通过\n");
writeLog(" [OK] 数据检查通过\n");
}
// 1. 决策变量
VariableFactory.createAll(model, data);
System.out.println(" [OK] 决策变量创建完成");
writeLog(" [OK] 决策变量创建完成");
// 2. 约束 + KPI 汇总 (由 ConstraintFactory 统一调度)
ConstraintFactory.buildAll(model, data);
System.out.println(" [OK] 约束与KPI汇总创建完成");
writeLog(" [OK] 约束与KPI汇总创建完成");
// 3. 目标函数 (加权求和)
ObjectiveBuilder.build(model, data);
System.out.println(" [OK] 目标函数创建完成");
writeLog(" [OK] 目标函数创建完成");
// 4. 导出 LP 模型文件
exportLpModel();
System.out.println("\n模型统计: 变量=" + model.getSolver().numVariables()
writeLog("\n模型统计: 变量=" + model.getSolver().numVariables()
+ ", 约束=" + model.getSolver().numConstraints() + "\n");
}
......@@ -211,9 +217,9 @@ public class MacroPlannerOptimizer {
String lpContent = model.getSolver().exportModelAsLpFormat();
Path lpPath = Paths.get(LP_FILE_PATH).toAbsolutePath();
Files.write(lpPath, lpContent.getBytes(StandardCharsets.UTF_8));
System.out.println(" [OK] LP模型文件已导出: " + lpPath);
writeLog(" [OK] LP模型文件已导出: " + lpPath);
} catch (Exception e) {
System.err.println(" [WARN] LP模型导出失败: " + e.getMessage());
writeLog(" [WARN] LP模型导出失败: " + e.getMessage());
}
}
......@@ -255,7 +261,7 @@ public class MacroPlannerOptimizer {
* 每个层级独立求解, 上层最优值作为下层约束, 确保严格优先级顺序。</p>
*/
public void solve() {
System.out.println("=== 开始分层求解 ===\n");
writeLog("=== 开始分层求解 ===\n");
startTimeMs = System.currentTimeMillis();
KPIWeights w = data.getKpiWeights();
......@@ -300,17 +306,20 @@ public class MacroPlannerOptimizer {
if (i < levels.size() - 1 && level.getRelativeGoalSlack() >= 0.0) {
ObjectiveBuilder.addLevelBoundConstraint(model, level, optimalValue);
System.out.printf(" 已添加边界约束: 上层目标 ≤ %.2f%n",
optimalValue * (1.0 + level.getRelativeGoalSlack()));
ObjectiveBuilder.computeUpperBound(optimalValue, level.getRelativeGoalSlack()));
}
System.out.println();
writeLog("-----------------------------------");
}
// 输出最终结果
MPSolver.ResultStatus finalStatus = model.getSolver().solve();
if (finalStatus == MPSolver.ResultStatus.OPTIMAL
|| finalStatus == MPSolver.ResultStatus.FEASIBLE) {
SolutionPrinter printer = new SolutionPrinter(model, data, startTimeMs);
SolutionPrinter printer = new SolutionPrinter(model, data, startTimeMs,LOG_FILE_PATH);
printer.printAll();
// 构建层级最优值列表
List<Double> levelObjValues = new ArrayList<>();
......@@ -323,10 +332,10 @@ public class MacroPlannerOptimizer {
ResultWriter rw = new ResultWriter(model, data, startTimeMs);
boolean jsonPath = rw.saveResultToFile("1");
if (jsonPath) {
System.out.println("\n[OK] 优化结果JSON已导出: " + jsonPath);
writeLog("\n[OK] 优化结果JSON已导出: " + jsonPath);
}
} else {
System.out.println("求解失败! 状态: " + finalStatus);
writeLog("求解失败! 状态: " + finalStatus);
}
}
......
......@@ -135,7 +135,7 @@ public class InventorySpecConstraint {
} else {
double rhs = spec.getTargetLevel();
MPConstraint c = model.getSolver().makeConstraint(
rhs, rhs, "TargetInv_" + invKey);
rhs, MPSolver.infinity(), "TargetInv_" + invKey);
c.setCoefficient(invVar, 1.0);
MPVariable u = targetUnderVars.get(invKey);
if (u != null) c.setCoefficient(u, 1.0);
......
......@@ -5,6 +5,7 @@ import com.aps.entity.*;
import com.aps.entity.basic.Material;
import com.aps.entity.basic.MaterialSupply;
import com.aps.mapper.EquipShiftCapacityMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.aps.mapper.EquipinfoMapper;
......@@ -190,6 +191,16 @@ public class MacroPlannerDataConverter {
new LambdaQueryWrapper<ApsDemandOrder>()
.ge(ApsDemandOrder::getDeliverytime, ctx.baseTime)
.lt(ApsDemandOrder::getDeliverytime, horizonEndDateTime));
// ctx.apsDemandOrders = apsDemandOrderMapper.selectList(
//
// new LambdaQueryWrapper<ApsDemandOrder>()
// .ge(ApsDemandOrder::getDeliverytime, ctx.baseTime)
// .eq(ApsDemandOrder::getCode,"XQDD_20260812_6")
// .lt(ApsDemandOrder::getDeliverytime, horizonEndDateTime)
// );
log.info("加载需求订单: {} 条 (时间范围过滤)", ctx.apsDemandOrders.size());
// 2. 收集 materialIds (从 ApsDemandOrder.mmid)
......@@ -215,6 +226,40 @@ public class MacroPlannerDataConverter {
// 4. 工序 (通过 LanuchService 批量查询)
if (!routingIds.isEmpty()) {
List<Integer> routingIds1=routingIds;
// while (routingIds1!=null&&routingIds1.size()>0) {
// List<Routingsupporting> rss = routingsupportingMapper.selectList(
// new LambdaQueryWrapper<Routingsupporting>()
// .in(Routingsupporting::getRoutingHeaderId, routingIds)
// .eq(Routingsupporting::getIsdeleted, 0));
// if (rss != null && rss.size() > 0) {
// Set<String> materialIdrss = rss.stream()
// .map(Routingsupporting::getMaterialId)
// .filter(Objects::nonNull)
// .distinct()
// .collect(Collectors.toSet());
// materialIds.addAll(materialIdrss);
// ctx.routingsupportings.addAll(rss);
//
// List<RoutingHeader> rhs = routingHeaderMapper.selectList(
// new LambdaQueryWrapper<RoutingHeader>()
// .in(RoutingHeader::getMaterialId, materialIdrss));
//
// if (rhs != null && rhs.size() > 0) {
// routingIds1 = rhs.stream()
// .map(RoutingHeader::getId)
// .filter(Objects::nonNull)
// .distinct()
// .collect(Collectors.toList());
// routingIds.addAll(routingIds1);
// ctx.routingHeaders.addAll(rhs);
// } else {
// routingIds1 = null;
// }
// } else {
// routingIds1 = null;
// }
// }
List<Long> routingIdsLong = routingIds.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
......@@ -242,6 +287,9 @@ public class MacroPlannerDataConverter {
});
// 7. 物料主数据
if (!materialIds.isEmpty()) {
ctx.materialInfos = materialInfoMapper.selectList(
......@@ -641,7 +689,7 @@ public class MacroPlannerDataConverter {
// 3.1 为每个 Material 创建 Product
for (Material m : ctx.materialByMaterialId.values()) {
String name = pickName(m.getName(), m.getCode(), m.getId());
Product p = new Product(m.getId(), name);
Product p = new Product(m.getCode(), name);
products.add(p);
ctx.productByMaterialId.put(m.getId(), p);
}
......@@ -892,10 +940,10 @@ public class MacroPlannerDataConverter {
if (inputMaterial == null) {
continue;
}
if (!"MP".equals(inputMaterial.getMaterialTypeName())) {
// 半成品/成品不作为投料, 由各自 Routing 产出
continue;
}
// if (!"MP".equals(inputMaterial.getMaterialTypeName())) {
// // 半成品/成品不作为投料, 由各自 Routing 产出
// continue;
// }
if (rs.getMainQty() == null || rs.getMainQty().compareTo(BigDecimal.ZERO) == 0) {
log.warn("跳过主量为0的BOM项: routingDetailId={}, materialId={}",
......@@ -919,7 +967,9 @@ public class MacroPlannerDataConverter {
// - 有 MATERIAL_PURCHASE → 每供应商一个 Operation, leadTimeDays = purchaseCycle
// - 无 MATERIAL_PURCHASE → 通用供应商 unit, relativeDuration=1, leadTimeDays=0, UnitPeriod 无限
for (Material m : ctx.materialByMaterialId.values()) {
if (!"MP".equals(m.getMaterialTypeName())) {
long headercount= ctx.routingHeaders.stream().filter(t->t.getMaterialId()==m.getId()).count();
if(headercount>0)
{
continue;
}
Product p = ctx.productByMaterialId.get(m.getId());
......@@ -1287,7 +1337,7 @@ public class MacroPlannerDataConverter {
String key = m.getProduct().getId() + "_" + m.getStockingPoint().getId() + "_" + p.getIndex();
if (invSpecKeys.add(key)) {
inventorySpecs.add(new InventorySpec(m.getProduct(), m.getStockingPoint(), p,
0.0, 0.0, LOOSE_MAX, true, true, true));
0.0, 0.0, LOOSE_MAX, false, true, true));
}
}
}
......@@ -1305,7 +1355,7 @@ public class MacroPlannerDataConverter {
for (Operation op : operations) {
if (op.getId().startsWith("OP_PROCURE_")) {
supplySpecs.add(new SupplySpec("Supply-" + op.getId(),
0.0, 0.0, LOOSE_MAX, false, Collections.singletonList(op)));
0.0, 0.0, LOOSE_MAX, true, Collections.singletonList(op)));
}
}
log.info("构建 InventorySpec: {}, SupplySpec: {}", inventorySpecs.size(), supplySpecs.size());
......
......@@ -112,7 +112,21 @@ public class ObjectiveBuilder {
// 设置为最小化目标
objective.setMinimization();
}
/**
* 计算层级边界约束的上界。
*
* <p>松弛比例应允许上层目标值<b>变差</b>(对最小化而言即数值增大)。
* 由于目标值可能为负(负系数 KPI 导致),直接使用
* {@code optimalValue × (1 + slack)} 会在负值时反向收紧约束,
* 因此改用 {@code optimalValue + |optimalValue| × slack}。</p>
*
* @param optimalValue 上层最优目标值
* @param relativeGoalSlack 松弛比例 (>= 0)
* @return 放宽后的上界
*/
public static double computeUpperBound(double optimalValue, double relativeGoalSlack) {
return optimalValue + Math.abs(optimalValue) * relativeGoalSlack;
}
// ==================== 分层优化方法 ====================
/**
......@@ -165,7 +179,7 @@ public class ObjectiveBuilder {
*
* <h3>数学公式</h3>
* <pre>
* Σ (effectiveCoeff × KPI_variable) ≤ optimalValue × (1 + relativeGoalSlack)
* Σ (effectiveCoeff × KPI_variable) ≤ optimalValue + |optimalValue| × relativeGoalSlack
* </pre>
*
* @param model 模型容器
......@@ -178,7 +192,7 @@ public class ObjectiveBuilder {
if (level.getRelativeGoalSlack() < 0.0) return; // 负松弛表示不约束
MPSolver solver = model.getSolver();
double upperBound = optimalValue * (1.0 + level.getRelativeGoalSlack());
double upperBound = computeUpperBound(optimalValue, level.getRelativeGoalSlack());
MPConstraint bound = solver.makeConstraint(
-MPSolver.infinity(), upperBound,
......
package com.aps.service.Algorithm;
/**
* 作者:佟礼
* 时间:2026-08-18
*/
public class POAOrToolsModel {
}
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