Commit bdfee182 authored by Tong Li's avatar Tong Li

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
parents 539b80b3 e931afa6
2026-03-25 23:38:18.378 | main | INFO | com.aps.ApsApplication | Starting ApsApplication using Java 17.0.18 on GJS-20250302HBH with PID 36188 (E:\ai\hyh.apsj\target\classes started by Administrator in E:\ai\hyh.apsj)
2026-03-25 23:38:18.382 | main | INFO | com.aps.ApsApplication | No active profile set, falling back to 1 default profile: "default"
2026-03-25 23:38:19.601 | main | INFO | org.apache.coyote.http11.Http11NioProtocol | Initializing ProtocolHandler ["http-nio-8181"]
2026-03-25 23:38:19.602 | main | INFO | org.apache.catalina.core.StandardService | Starting service [Tomcat]
2026-03-25 23:38:19.602 | main | INFO | org.apache.catalina.core.StandardEngine | Starting Servlet engine: [Apache Tomcat/9.0.83]
2026-03-25 23:38:19.686 | main | INFO | o.a.c.core.ContainerBase.[Tomcat].[localhost].[/] | Initializing Spring embedded WebApplicationContext
2026-03-25 23:38:20.545 | main | INFO | c.b.dynamic.datasource.DynamicRoutingDataSource | dynamic-datasource - add a datasource named [oracle] success
2026-03-25 23:38:20.546 | main | INFO | c.b.dynamic.datasource.DynamicRoutingDataSource | dynamic-datasource initial loaded [1] datasource,primary datasource named [oracle]
2026-03-25 23:38:20.941 | main | WARN | c.b.mybatisplus.core.injector.DefaultSqlInjector | class com.aps.entity.ApsTimeConfig ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.
2026-03-25 23:38:21.654 | main | WARN | c.b.mybatisplus.core.injector.DefaultSqlInjector | class com.aps.entity.Algorithm.OrderMaterialRequirement ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.
2026-03-25 23:38:21.717 | main | INFO | com.aps.controller.gantt.FileUploadController | 创建上传目录: E:\ai\hyh.apsj\uploads
2026-03-25 23:38:21.717 | main | INFO | com.aps.controller.gantt.FileUploadController | 创建上传目录: E:\ai\hyh.apsj\uploads
2026-03-25 23:38:22.388 | main | INFO | org.apache.coyote.http11.Http11NioProtocol | Starting ProtocolHandler ["http-nio-8181"]
2026-03-25 23:38:22.404 | main | INFO | com.aps.ApsApplication | Started ApsApplication in 4.45 seconds (JVM running for 5.14)
...@@ -58,7 +58,7 @@ public class GeneticDecoder { ...@@ -58,7 +58,7 @@ public class GeneticDecoder {
private DiscreteParameterMatrixService discreteParameterMatrixService; private DiscreteParameterMatrixService discreteParameterMatrixService;
private String sceneId; private String sceneId;
private boolean rebuildStructureForCurrentDecode = false; private boolean rebuildStructureForCurrentDecode = true;
...@@ -1106,12 +1106,18 @@ public class GeneticDecoder { ...@@ -1106,12 +1106,18 @@ public class GeneticDecoder {
int setupTime=0; int setupTime=0;
long machineId = machine.getId(); long machineId = machine.getId();
// 只有存在物料约束(或本次被强制要求重算 BOM)的工序,才需要联立求解“机台何时能排”和“物料何时可用”。
// targetFinishedOperationId != null 的工序通常由前置成品工序驱动,这里不再额外触发一轮 BOM 试算。
boolean needMaterialCheck = (operation.getMaterialRequirements()!=null&&operation.getMaterialRequirements().size()>0&&operation.getTargetFinishedOperationId()==null)||calbom; boolean needMaterialCheck = (operation.getMaterialRequirements()!=null&&operation.getMaterialRequirements().size()>0&&operation.getTargetFinishedOperationId()==null)||calbom;
needMaterialCheck=false; // 正式落排后还要再做一次带提交的物料校验,把试算阶段推导出的 BOM 状态真正写回 chromosome。
boolean commitMaterialCheck = needMaterialCheck; boolean commitMaterialCheck = needMaterialCheck;
// baseEarliestStartTime:不考虑 BOM 推迟时的理论最早开工时间。
// candidateStartTime:固定点迭代时本轮尝试的开工时间。
// bomtime:按当前候选开工时间试算出来的物料最早可开工时间。
int baseEarliestStartTime = earliestStartTime; int baseEarliestStartTime = earliestStartTime;
int candidateStartTime = earliestStartTime; int candidateStartTime = earliestStartTime;
int bomtime=0; int bomtime=0;
// 机台时间和物料时间可能会互相推动,最多迭代 maxSolveIterations 次来找收敛点。
int maxSolveIterations = Math.max(1, _globalParam.getMaterialSolveMaxIterations()); int maxSolveIterations = Math.max(1, _globalParam.getMaterialSolveMaxIterations());
boolean converged = !needMaterialCheck; boolean converged = !needMaterialCheck;
Entry scheduledOperation = operation; Entry scheduledOperation = operation;
...@@ -1121,8 +1127,13 @@ public class GeneticDecoder { ...@@ -1121,8 +1127,13 @@ public class GeneticDecoder {
MachineSchedulePreview machinePreview = null; MachineSchedulePreview machinePreview = null;
CopyOnWriteArrayList<ScheduleResultDetail> geneDetails=new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<ScheduleResultDetail> geneDetails=new CopyOnWriteArrayList<>();
// 固定点求解过程:
// 1. 先按 candidateStartTime 试排机台;
// 2. 再按试排结果试算物料/BOM 的最早可开工时间;
// 3. 如果物料时间继续把开工点往后推,就用新时间再次试排,直到收敛。
if(needMaterialCheck) { if(needMaterialCheck) {
for (int iteration = 0; iteration < maxSolveIterations; iteration++) { for (int iteration = 0; iteration < maxSolveIterations; iteration++) {
// trial BOM 试算会在内部恢复快照,因此每轮都重新从最新 chromosome 中取对象,避免拿到旧引用。
Entry refreshedOperation = entryIndexById.get(operationId); Entry refreshedOperation = entryIndexById.get(operationId);
if (refreshedOperation != null) { if (refreshedOperation != null) {
scheduledOperation = refreshedOperation; scheduledOperation = refreshedOperation;
...@@ -1138,13 +1149,15 @@ public class GeneticDecoder { ...@@ -1138,13 +1149,15 @@ public class GeneticDecoder {
{ {
candidateStartTime = Math.max(candidateStartTime,lastGeneOnMachine.getEndTime()); candidateStartTime = Math.max(candidateStartTime,lastGeneOnMachine.getEndTime());
} }
// 0 884400 // 先在机台侧做一次“试排”,geneDetails 中会记录本轮临时占用的机台时间段。
machinePreview = buildMachinePreview(scheduledOperation, scheduledMachine, processingTime, processingTimeTotal, machinePreview = buildMachinePreview(scheduledOperation, scheduledMachine, processingTime, processingTimeTotal,
candidateStartTime, lastGeneOnMachine, machineTasks, chromosome); candidateStartTime, lastGeneOnMachine, machineTasks, chromosome);
try { try {
//864000 // 物料侧只做试算,不提交最终状态;本轮只关心 BOM 约束会把开工时间推迟到哪里。
bomtime = EditOperationBOMTime(scheduledOperation,chromosome,machinePreview.getStartTime(),machineTasksCache, entryIndexById, scheduleIndexById, false); bomtime = EditOperationBOMTime(scheduledOperation,chromosome,machinePreview.getStartTime(),machineTasksCache, entryIndexById, scheduleIndexById, false);
} finally { } finally {
// buildMachinePreview 会临时锁住机台 availability,这里无论成功或异常都必须释放,
// 否则下一轮迭代会被本轮的试排残留状态污染。
Machine liveMachineAfterTrial = getMachineById(chromosome, machineId); Machine liveMachineAfterTrial = getMachineById(chromosome, machineId);
if (liveMachineAfterTrial != null) { if (liveMachineAfterTrial != null) {
AddMachineAvailable(liveMachineAfterTrial, machinePreview.getGeneDetails()); AddMachineAvailable(liveMachineAfterTrial, machinePreview.getGeneDetails());
...@@ -1153,7 +1166,9 @@ public class GeneticDecoder { ...@@ -1153,7 +1166,9 @@ public class GeneticDecoder {
} }
} }
// 下一轮候选开工时间取“基准最早开工”和“物料最早可开工”两者中的较晚值。
int nextCandidateStartTime = Math.max(baseEarliestStartTime, bomtime); int nextCandidateStartTime = Math.max(baseEarliestStartTime, bomtime);
// 如果物料时间已经不再把机台试排结果往后推,说明机台时间与物料时间达成一致。
if (nextCandidateStartTime <= machinePreview.getStartTime()) { if (nextCandidateStartTime <= machinePreview.getStartTime()) {
converged = true; converged = true;
candidateStartTime = nextCandidateStartTime; candidateStartTime = nextCandidateStartTime;
...@@ -1162,6 +1177,7 @@ public class GeneticDecoder { ...@@ -1162,6 +1177,7 @@ public class GeneticDecoder {
candidateStartTime = nextCandidateStartTime; candidateStartTime = nextCandidateStartTime;
} }
// 固定点求解结束后,用收敛得到的候选开工时间作为正式排产的最早开工时间。
earliestStartTime = candidateStartTime; earliestStartTime = candidateStartTime;
Entry refreshedOperation = entryIndexById.get(operationId); Entry refreshedOperation = entryIndexById.get(operationId);
if (refreshedOperation != null) { if (refreshedOperation != null) {
...@@ -1177,35 +1193,35 @@ public class GeneticDecoder { ...@@ -1177,35 +1193,35 @@ public class GeneticDecoder {
lastGeneOnMachine = getLastMachineTask(machineTasks); lastGeneOnMachine = getLastMachineTask(machineTasks);
needMaterialCheck = false; needMaterialCheck = false;
} }
// 旧兜底单次试算路径:当前固定点迭代执行完后会把 needMaterialCheck 置为 false,
// 因此这段分支实际上不会再进入。先注释保留,后续若确认无用可直接删除,
// 或按真实意图改成 !converged 时的兜底处理。
if(needMaterialCheck) { // if(needMaterialCheck) {
int earliestStartTimeold=earliestStartTime; // int earliestStartTimeold=earliestStartTime;
if (_globalParam.is_smoothChangeOver()) { // if (_globalParam.is_smoothChangeOver()) {
//
Map<Integer, Object> reslte = calculateSetupTime(lastGeneOnMachine, operation, machine, earliestStartTime, processingTimeTotal, _globalParam.is_smoothChangeOverInWeek(), chromosome.getAllOperations()); // Map<Integer, Object> reslte = calculateSetupTime(lastGeneOnMachine, operation, machine, earliestStartTime, processingTimeTotal, _globalParam.is_smoothChangeOverInWeek(), chromosome.getAllOperations());
// setupTime = (int) reslte.get(1);//换型时间 // // setupTime = (int) reslte.get(1);//换型时间
// int setupStartTime = (int) reslte.get(2);//换型开始时间 // // int setupStartTime = (int) reslte.get(2);//换型开始时间
//earliestStartTime=(int)reslte.get(3);//上个任务的结束时间 // //earliestStartTime=(int)reslte.get(3);//上个任务的结束时间
earliestStartTime = (int) reslte.get(4);//最早开工时间 // earliestStartTime = (int) reslte.get(4);//最早开工时间
} // }
LocalDateTime startTime1 = baseTime.plus(earliestStartTime, ChronoUnit.SECONDS); // LocalDateTime startTime1 = baseTime.plus(earliestStartTime, ChronoUnit.SECONDS);
//
TimeSegment slot = machineCalculator.GetCurrentOrNextShift(machine, startTime1, "", false); // TimeSegment slot = machineCalculator.GetCurrentOrNextShift(machine, startTime1, "", false);
//
if(slot!=null) // if(slot!=null)
{ // {
earliestStartTime = slot.getStart().isAfter(startTime1) // earliestStartTime = slot.getStart().isAfter(startTime1)
?(int) ChronoUnit.SECONDS.between(baseTime, slot.getStart()) // ?(int) ChronoUnit.SECONDS.between(baseTime, slot.getStart())
: earliestStartTime; // : earliestStartTime;
//
} // }
bomtime= EditOperationBOMTime(operation,chromosome,earliestStartTime,machineTasksCache, entryIndexById, scheduleIndexById, false); // bomtime= EditOperationBOMTime(operation,chromosome,earliestStartTime,machineTasksCache, entryIndexById, scheduleIndexById, false);
//
//
earliestStartTime = Math.max(earliestStartTime, bomtime); // earliestStartTime = Math.max(earliestStartTime, bomtime);
} // }
// machineTasks =chromosome.getResult().stream() // machineTasks =chromosome.getResult().stream()
...@@ -1215,11 +1231,14 @@ public class GeneticDecoder { ...@@ -1215,11 +1231,14 @@ public class GeneticDecoder {
// //
// machineTasksCache.put(machine.getId(), machineTasks); // machineTasksCache.put(machine.getId(), machineTasks);
// 正式落排前,再取一次当前机台最后一道工序,保证换型计算基于最新排程结果。
if(machineTasks!=null&&machineTasks.size()>0&&_globalParam.is_smoothChangeOver()) if(machineTasks!=null&&machineTasks.size()>0&&_globalParam.is_smoothChangeOver())
{ {
lastGeneOnMachine=machineTasks.get(machineTasks.size()-1); lastGeneOnMachine=machineTasks.get(machineTasks.size()-1);
} }
// 下面开始生成正式的 geneDetails。
// 与 buildMachinePreview 的区别是:这里得到的结果会继续向下写入 GAScheduleResult,成为真正排程结果。
if (_globalParam.is_smoothChangeOver()) { if (_globalParam.is_smoothChangeOver()) {
//是否考虑换型时间 //是否考虑换型时间
Map<Integer,Object> reslte = calculateSetupTime(lastGeneOnMachine, operation, machine,earliestStartTime,processingTimeTotal, _globalParam.is_smoothChangeOverInWeek(),chromosome.getAllOperations()); Map<Integer,Object> reslte = calculateSetupTime(lastGeneOnMachine, operation, machine,earliestStartTime,processingTimeTotal, _globalParam.is_smoothChangeOverInWeek(),chromosome.getAllOperations());
...@@ -1291,6 +1310,9 @@ public class GeneticDecoder { ...@@ -1291,6 +1310,9 @@ public class GeneticDecoder {
// 准备工时 加入到加工时间里 // 准备工时 加入到加工时间里
// 后处理时间 相同任务的前一工序的结束时间+后处理时间 =当前工序最早开工时间 // 后处理时间 相同任务的前一工序的结束时间+后处理时间 =当前工序最早开工时间
// 正式提交 BOM 校验:
// 1. 按最终 startTime 重新计算一次物料占用,并把状态真正写回;
// 2. 如果提交后的 BOM 时间仍然晚于 startTime,说明机台时间与物料时间没有收敛,需要直接报错。
if (commitMaterialCheck) { if (commitMaterialCheck) {
int committedBomTime = EditOperationBOMTime(operation,chromosome,startTime,machineTasksCache, entryIndexById, scheduleIndexById, true); int committedBomTime = EditOperationBOMTime(operation,chromosome,startTime,machineTasksCache, entryIndexById, scheduleIndexById, true);
bomtime = Math.max(bomtime, committedBomTime); bomtime = Math.max(bomtime, committedBomTime);
...@@ -1408,6 +1430,10 @@ public class GeneticDecoder { ...@@ -1408,6 +1430,10 @@ public class GeneticDecoder {
} }
} }
/**
* 按当前候选开工时间做一次机台侧试排。
* 注意:该方法会临时占用 machine.availability 中对应的时段,调用方用完后必须配合 AddMachineAvailable 释放。
*/
private MachineSchedulePreview buildMachinePreview(Entry operation, Machine machine, double processingTime, int processingTimeTotal, private MachineSchedulePreview buildMachinePreview(Entry operation, Machine machine, double processingTime, int processingTimeTotal,
int earliestStartTime, GAScheduleResult lastGeneOnMachine, int earliestStartTime, GAScheduleResult lastGeneOnMachine,
CopyOnWriteArrayList<GAScheduleResult> machineTasks, Chromosome chromosome) { CopyOnWriteArrayList<GAScheduleResult> machineTasks, Chromosome chromosome) {
...@@ -1417,6 +1443,7 @@ public class GeneticDecoder { ...@@ -1417,6 +1443,7 @@ public class GeneticDecoder {
CopyOnWriteArrayList<ScheduleResultDetail> geneDetails; CopyOnWriteArrayList<ScheduleResultDetail> geneDetails;
if (_globalParam.is_smoothChangeOver()) { if (_globalParam.is_smoothChangeOver()) {
// 先把换型时间、换型起点以及加工总时长修正到当前候选时间点。
Map<Integer,Object> reslte = calculateSetupTime(lastGeneOnMachine, operation, machine, previewStart, previewProcessingTotal, _globalParam.is_smoothChangeOverInWeek(),chromosome.getAllOperations()); Map<Integer,Object> reslte = calculateSetupTime(lastGeneOnMachine, operation, machine, previewStart, previewProcessingTotal, _globalParam.is_smoothChangeOverInWeek(),chromosome.getAllOperations());
setupTime=(int)reslte.get(1); setupTime=(int)reslte.get(1);
int setupStartTime=(int)reslte.get(2); int setupStartTime=(int)reslte.get(2);
...@@ -1424,10 +1451,12 @@ public class GeneticDecoder { ...@@ -1424,10 +1451,12 @@ public class GeneticDecoder {
previewProcessingTotal=(int)reslte.get(5); previewProcessingTotal=(int)reslte.get(5);
if(setupTime==0) if(setupTime==0)
{ {
// 没有换型占用时,直接查机台下一段可排的可用窗口。
geneDetails = machineCalculator.getNextAvailableTime(machine, previewStart, -1, geneDetails = machineCalculator.getNextAvailableTime(machine, previewStart, -1,
previewProcessingTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true); previewProcessingTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true);
}else { }else {
// 有换型占用时,需要把换型段和加工段一起拼成完整的试排结果。
CopyOnWriteArrayList<TimeSegment> AvailableTimeSegment = (CopyOnWriteArrayList<TimeSegment>) reslte.get(6); CopyOnWriteArrayList<TimeSegment> AvailableTimeSegment = (CopyOnWriteArrayList<TimeSegment>) reslte.get(6);
Map<Integer,Object> result = machineCalculator.CreateScheduleResult(machine, previewProcessingTotal, previewStart, Map<Integer,Object> result = machineCalculator.CreateScheduleResult(machine, previewProcessingTotal, previewStart,
AvailableTimeSegment, processingTime, operation.getQuantity(), operation.getIsInterrupt() != 1, setupTime, _globalParam.is_smoothChangeOverInWeek(), setupStartTime); AvailableTimeSegment, processingTime, operation.getQuantity(), operation.getIsInterrupt() != 1, setupTime, _globalParam.is_smoothChangeOverInWeek(), setupStartTime);
...@@ -1436,10 +1465,12 @@ public class GeneticDecoder { ...@@ -1436,10 +1465,12 @@ public class GeneticDecoder {
geneDetails=(CopyOnWriteArrayList<ScheduleResultDetail>) result.get(2); geneDetails=(CopyOnWriteArrayList<ScheduleResultDetail>) result.get(2);
} }
}else { }else {
// 不考虑平滑换型时,只需要找加工段可用窗口。
geneDetails = machineCalculator.getNextAvailableTime(machine, previewStart, -1, geneDetails = machineCalculator.getNextAvailableTime(machine, previewStart, -1,
previewProcessingTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true); previewProcessingTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true);
} }
// geneDetails 可能跨多个班次/时段,开始时间取最小值,结束时间取最大值。
int startTime = geneDetails.stream() int startTime = geneDetails.stream()
.mapToInt(ScheduleResultDetail::getStartTime) .mapToInt(ScheduleResultDetail::getStartTime)
.min() .min()
...@@ -1451,6 +1482,7 @@ public class GeneticDecoder { ...@@ -1451,6 +1482,7 @@ public class GeneticDecoder {
return new MachineSchedulePreview(startTime, endTime, setupTime, geneDetails); return new MachineSchedulePreview(startTime, endTime, setupTime, geneDetails);
} }
// 按机台缓存已排结果,避免固定点迭代期间反复全表扫描 chromosome.getResult()。
private CopyOnWriteArrayList<GAScheduleResult> getMachineTasks(long machineId, Chromosome chromosome, private CopyOnWriteArrayList<GAScheduleResult> getMachineTasks(long machineId, Chromosome chromosome,
Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache) { Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache) {
if (machineTasksCache.containsKey(machineId)) { if (machineTasksCache.containsKey(machineId)) {
...@@ -1464,6 +1496,7 @@ public class GeneticDecoder { ...@@ -1464,6 +1496,7 @@ public class GeneticDecoder {
return machineTasks; return machineTasks;
} }
// 当前机台最后一道已排工序,用于约束连续排程和换型起点。
private GAScheduleResult getLastMachineTask(CopyOnWriteArrayList<GAScheduleResult> machineTasks) { private GAScheduleResult getLastMachineTask(CopyOnWriteArrayList<GAScheduleResult> machineTasks) {
if(machineTasks!=null&&machineTasks.size()>0) if(machineTasks!=null&&machineTasks.size()>0)
{ {
...@@ -1472,6 +1505,7 @@ public class GeneticDecoder { ...@@ -1472,6 +1505,7 @@ public class GeneticDecoder {
return null; return null;
} }
// trial BOM 恢复快照后,旧的 Machine 引用可能已经失效,因此统一从 chromosome 中重新取当前有效对象。
private Machine getMachineById(Chromosome chromosome, long machineId) { private Machine getMachineById(Chromosome chromosome, long machineId) {
return chromosome.getMachines().stream() return chromosome.getMachines().stream()
.filter(m -> m.getId() == machineId) .filter(m -> m.getId() == machineId)
...@@ -1630,6 +1664,10 @@ public class GeneticDecoder { ...@@ -1630,6 +1664,10 @@ public class GeneticDecoder {
} }
/**
* 汇总当前工序的物料约束,返回所有原料/半成品中最晚满足的那个时刻。
* 返回值含义是“这道工序最早允许因物料而开工的秒数”。
*/
private int getOperationBOMTime(Entry currentOp, Chromosome chromosome) { private int getOperationBOMTime(Entry currentOp, Chromosome chromosome) {
List<OrderMaterialRequirement> opboms= currentOp.getMaterialRequirements(); List<OrderMaterialRequirement> opboms= currentOp.getMaterialRequirements();
...@@ -1697,10 +1735,16 @@ public class GeneticDecoder { ...@@ -1697,10 +1735,16 @@ public class GeneticDecoder {
return Math.max(rawTime, sfTime); return Math.max(rawTime, sfTime);
} }
// 默认走正式提交路径,调用方不传 commitChanges 时认为要把物料状态真实写回。
private int EditOperationBOMTime(Entry currentOp, Chromosome chromosome,int earliestStartTime,Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,Map<Integer, Entry> entryIndexById,Map<Integer, GAScheduleResult> scheduleIndexById) { private int EditOperationBOMTime(Entry currentOp, Chromosome chromosome,int earliestStartTime,Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,Map<Integer, Entry> entryIndexById,Map<Integer, GAScheduleResult> scheduleIndexById) {
return EditOperationBOMTime(currentOp, chromosome, earliestStartTime, machineTasksCache, entryIndexById, scheduleIndexById, true); return EditOperationBOMTime(currentOp, chromosome, earliestStartTime, machineTasksCache, entryIndexById, scheduleIndexById, true);
} }
/**
* 按指定 earliestStartTime 重新计算当前工序的 BOM 可开工时间。
* commitChanges=true:正式提交,真实修改物料/订单/排程状态。
* commitChanges=false:只做试算,方法结束后恢复 chromosome 和相关索引缓存。
*/
private int EditOperationBOMTime(Entry currentOp, Chromosome chromosome,int earliestStartTime,Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,Map<Integer, Entry> entryIndexById,Map<Integer, GAScheduleResult> scheduleIndexById, boolean commitChanges) { private int EditOperationBOMTime(Entry currentOp, Chromosome chromosome,int earliestStartTime,Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,Map<Integer, Entry> entryIndexById,Map<Integer, GAScheduleResult> scheduleIndexById, boolean commitChanges) {
List<OrderMaterialRequirement> opboms= currentOp.getMaterialRequirements(); List<OrderMaterialRequirement> opboms= currentOp.getMaterialRequirements();
if(opboms==null) if(opboms==null)
...@@ -1709,20 +1753,24 @@ public class GeneticDecoder { ...@@ -1709,20 +1753,24 @@ public class GeneticDecoder {
} }
LocalDateTime earliestStartTime1=baseTime.plusSeconds(earliestStartTime); LocalDateTime earliestStartTime1=baseTime.plusSeconds(earliestStartTime);
if(commitChanges) { if(commitChanges) {
// 正式提交模式:直接把当前开工时间对应的物料占用/供应结果写回业务对象。
materialRequirementService.EditOperationBOM(currentOp,chromosome,earliestStartTime1,this,machineTasksCache, entryIndexById, scheduleIndexById, baseTime, true); materialRequirementService.EditOperationBOM(currentOp,chromosome,earliestStartTime1,this,machineTasksCache, entryIndexById, scheduleIndexById, baseTime, true);
return getOperationBOMTime(currentOp, chromosome); return getOperationBOMTime(currentOp, chromosome);
} }
// 试算模式:先复制一份完整状态,等 BOM 计算完成后再整体恢复。
TrialDecodeSnapshot snapshot = createTrialDecodeSnapshot(chromosome); TrialDecodeSnapshot snapshot = createTrialDecodeSnapshot(chromosome);
try { try {
materialRequirementService.EditOperationBOM(currentOp,chromosome,earliestStartTime1,this,machineTasksCache, entryIndexById, scheduleIndexById, baseTime, true); materialRequirementService.EditOperationBOM(currentOp,chromosome,earliestStartTime1,this,machineTasksCache, entryIndexById, scheduleIndexById, baseTime, true);
return getOperationBOMTime(currentOp, chromosome); return getOperationBOMTime(currentOp, chromosome);
} finally { } finally {
// 试算期间 materialRequirementService 可能会改动 machine/order/material/result,这里统一回滚。
restoreTrialDecodeSnapshot(snapshot, chromosome, machineTasksCache, entryIndexById, scheduleIndexById); restoreTrialDecodeSnapshot(snapshot, chromosome, machineTasksCache, entryIndexById, scheduleIndexById);
} }
} }
// 试算 BOM 会改动多类状态对象,因此先把排程求解会依赖的核心集合做一次深拷贝快照。
private TrialDecodeSnapshot createTrialDecodeSnapshot(Chromosome chromosome) { private TrialDecodeSnapshot createTrialDecodeSnapshot(Chromosome chromosome) {
TrialDecodeSnapshot snapshot = new TrialDecodeSnapshot(); TrialDecodeSnapshot snapshot = new TrialDecodeSnapshot();
snapshot.machines = ProductionDeepCopyUtil.deepCopyList(chromosome.getMachines(), Machine.class); snapshot.machines = ProductionDeepCopyUtil.deepCopyList(chromosome.getMachines(), Machine.class);
...@@ -1734,6 +1782,7 @@ public class GeneticDecoder { ...@@ -1734,6 +1782,7 @@ public class GeneticDecoder {
return snapshot; return snapshot;
} }
// 恢复试算前的 chromosome 状态,并同步重建几个按 id/机台组织的缓存索引。
private void restoreTrialDecodeSnapshot(TrialDecodeSnapshot snapshot, Chromosome chromosome, private void restoreTrialDecodeSnapshot(TrialDecodeSnapshot snapshot, Chromosome chromosome,
Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache, Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,
Map<Integer, Entry> entryIndexById, Map<Integer, Entry> entryIndexById,
...@@ -2551,6 +2600,7 @@ public class GeneticDecoder { ...@@ -2551,6 +2600,7 @@ public class GeneticDecoder {
return reslte; return reslte;
} }
// 释放试排阶段占用的机台 availability 段,避免临时状态污染下一轮排程/BOM 试算。
public void AddMachineAvailable(Machine machine, List<ScheduleResultDetail> geneDetails) { public void AddMachineAvailable(Machine machine, List<ScheduleResultDetail> geneDetails) {
if (geneDetails == null || geneDetails.isEmpty()) return; if (geneDetails == null || geneDetails.isEmpty()) return;
......
...@@ -41,6 +41,7 @@ public class MachineCalculator { ...@@ -41,6 +41,7 @@ public class MachineCalculator {
/** /**
* 获取机器下一个可用时间窗口(考虑班次约束) * 获取机器下一个可用时间窗口(考虑班次约束)
*/ */
// 从 proposedStartTime 开始查找机台下一段可排窗口;必要时会把命中的 availability 临时标记为已占用。
public CopyOnWriteArrayList<ScheduleResultDetail> getNextAvailableTime(Machine machine, int proposedStartTime, public CopyOnWriteArrayList<ScheduleResultDetail> getNextAvailableTime(Machine machine, int proposedStartTime,
int prevtime, int processingTime, int prevtime, int processingTime,
CopyOnWriteArrayList<GAScheduleResult> existingTasks, CopyOnWriteArrayList<GAScheduleResult> existingTasks,
...@@ -70,6 +71,7 @@ public class MachineCalculator { ...@@ -70,6 +71,7 @@ public class MachineCalculator {
return findAvailableSegments(machine, startTime, existingTasks, processingTime, isInterrupt); return findAvailableSegments(machine, startTime, existingTasks, processingTime, isInterrupt);
} }
// 基于已选可用时段组装排程结果,并把换型段/加工段对应的 availability 临时占用。
public Map<Integer,Object> CreateScheduleResult( public Map<Integer,Object> CreateScheduleResult(
Machine machine, int processingTime, int proposedStartTime,CopyOnWriteArrayList<TimeSegment> timeSegments, Machine machine, int processingTime, int proposedStartTime,CopyOnWriteArrayList<TimeSegment> timeSegments,
double oneTime,double quantity double oneTime,double quantity
...@@ -1289,6 +1291,7 @@ public class MachineCalculator { ...@@ -1289,6 +1291,7 @@ public class MachineCalculator {
return times; return times;
} }
// 把目标 availability 按本次排程切成“剩余可用段”和“本次已占用段”,并把占用段 key 回写到 geneDetails。
private CopyOnWriteArrayList<TimeSegment> RemoveMachineAvailable(Machine machine, ScheduleResultDetail geneDetails,TimeSegment targetSegment) { private CopyOnWriteArrayList<TimeSegment> RemoveMachineAvailable(Machine machine, ScheduleResultDetail geneDetails,TimeSegment targetSegment) {
......
...@@ -75,6 +75,7 @@ public class MaterialRequirementService { ...@@ -75,6 +75,7 @@ public class MaterialRequirementService {
private static final int cachetimeout = 60; private static final int cachetimeout = 60;
private static final int ORACLE_IN_BATCH_SIZE = 1000;
private String cacheKey="_MR_"; private String cacheKey="_MR_";
private String routingHeaderCacheKey=cacheKey+ "RoutingHeader"; private String routingHeaderCacheKey=cacheKey+ "RoutingHeader";
private String routingsupportingCacheKey=cacheKey+ "Routingsupporting"; private String routingsupportingCacheKey=cacheKey+ "Routingsupporting";
...@@ -100,22 +101,13 @@ public class MaterialRequirementService { ...@@ -100,22 +101,13 @@ public class MaterialRequirementService {
.map(Order::getRoutingId) .map(Order::getRoutingId)
.distinct() .distinct()
.collect(Collectors.toList()); .collect(Collectors.toList());
List<RoutingHeader> routingHeaders=null; List<RoutingHeader> routingHeaders = getRoutingHeadersByIds(routingIds);
LambdaQueryWrapper<RoutingHeader> wrapper = new LambdaQueryWrapper<>();
wrapper.in(RoutingHeader::getId, routingIds);
// .eq(RoutingHeader::getIsDeleted, 0)
// .eq(RoutingHeader::getApprovalStatus, 1);
routingHeaders = routingHeaderMapper.selectList(wrapper);
List<Routingsupporting> routingsupportings=null; List<Routingsupporting> routingsupportings=null;
// 查询并缓存Routingsupporting // 查询并缓存Routingsupporting
LambdaQueryWrapper<Routingsupporting> routingsupportingwrapper = new LambdaQueryWrapper<>(); routingsupportings = getRoutingsupportingsByRoutingHeaderIds(routingIds);
routingsupportingwrapper.in(Routingsupporting::getRoutingHeaderId, routingIds)
.eq(Routingsupporting::getIsdeleted, 0);
routingsupportings = routingsupportingMapper.selectList(routingsupportingwrapper);
// 查询并缓存RoutingSupportingReplace // 查询并缓存RoutingSupportingReplace
...@@ -126,10 +118,7 @@ public class MaterialRequirementService { ...@@ -126,10 +118,7 @@ public class MaterialRequirementService {
.distinct() .distinct()
.collect(Collectors.toList()); .collect(Collectors.toList());
LambdaQueryWrapper<RoutingSupportingReplace> routingsupportingreplacewrapper = new LambdaQueryWrapper<>(); replaces = getRoutingSupportingReplacesByStrsupids(routingsupportingids);
routingsupportingreplacewrapper.in(RoutingSupportingReplace::getStrsupid, routingsupportingids)
.eq(RoutingSupportingReplace::getIsdeleted, 0);
replaces = routingSupportingReplaceMapper.selectList(routingsupportingreplacewrapper);
} }
List<Material> useMaterials=new ArrayList<>(); List<Material> useMaterials=new ArrayList<>();
...@@ -279,11 +268,7 @@ if(Operations==null) ...@@ -279,11 +268,7 @@ if(Operations==null)
LambdaQueryWrapper<Routingsupporting> routingsupportingwrapper = new LambdaQueryWrapper<>(); routingsupportings1 = getRoutingsupportingsByRoutingHeaderIds(routingIds);
routingsupportingwrapper.in(Routingsupporting::getRoutingHeaderId, routingIds)
.eq(Routingsupporting::getIsdeleted, 0);
routingsupportings1 = routingsupportingMapper.selectList(routingsupportingwrapper);
if(routingsupportings1!=null&&routingsupportings1.size()>0) if(routingsupportings1!=null&&routingsupportings1.size()>0)
{ {
routingsupportings.addAll(routingsupportings1); routingsupportings.addAll(routingsupportings1);
...@@ -293,11 +278,7 @@ if(Operations==null) ...@@ -293,11 +278,7 @@ if(Operations==null)
.distinct() .distinct()
.collect(Collectors.toList()); .collect(Collectors.toList());
LambdaQueryWrapper<RoutingSupportingReplace> routingsupportingreplacewrapper = new LambdaQueryWrapper<>(); routingsupportingreplaces1 = getRoutingSupportingReplacesByStrsupids(routingsupportingids);
routingsupportingreplacewrapper.in(RoutingSupportingReplace::getStrsupid, routingsupportingids)
.eq(RoutingSupportingReplace::getIsdeleted, 0);
routingsupportingreplaces1 = routingSupportingReplaceMapper.selectList(routingsupportingreplacewrapper);
routingsupportingreplaces.addAll(routingsupportingreplaces1); routingsupportingreplaces.addAll(routingsupportingreplaces1);
...@@ -1543,6 +1524,62 @@ if(demand==null) ...@@ -1543,6 +1524,62 @@ if(demand==null)
return RoutingSupportingReplaces; return RoutingSupportingReplaces;
} }
private List<RoutingHeader> getRoutingHeadersByIds(List<Integer> routingIds) {
if (CollectionUtils.isEmpty(routingIds)) {
return new ArrayList<>();
}
List<RoutingHeader> routingHeaders = new ArrayList<>();
for (List<Integer> batchIds : partitionList(routingIds, ORACLE_IN_BATCH_SIZE)) {
LambdaQueryWrapper<RoutingHeader> wrapper = new LambdaQueryWrapper<>();
wrapper.in(RoutingHeader::getId, batchIds);
routingHeaders.addAll(routingHeaderMapper.selectList(wrapper));
}
return routingHeaders;
}
private List<Routingsupporting> getRoutingsupportingsByRoutingHeaderIds(List<? extends Number> routingIds) {
if (CollectionUtils.isEmpty(routingIds)) {
return new ArrayList<>();
}
List<Routingsupporting> routingsupportings = new ArrayList<>();
for (List<? extends Number> batchIds : partitionList(routingIds, ORACLE_IN_BATCH_SIZE)) {
LambdaQueryWrapper<Routingsupporting> routingsupportingwrapper = new LambdaQueryWrapper<>();
routingsupportingwrapper.in(Routingsupporting::getRoutingHeaderId, batchIds)
.eq(Routingsupporting::getIsdeleted, 0);
routingsupportings.addAll(routingsupportingMapper.selectList(routingsupportingwrapper));
}
return routingsupportings;
}
private List<RoutingSupportingReplace> getRoutingSupportingReplacesByStrsupids(List<String> routingSupportingIds) {
if (CollectionUtils.isEmpty(routingSupportingIds)) {
return new ArrayList<>();
}
List<RoutingSupportingReplace> replaces = new ArrayList<>();
for (List<String> batchIds : partitionList(routingSupportingIds, ORACLE_IN_BATCH_SIZE)) {
LambdaQueryWrapper<RoutingSupportingReplace> routingsupportingreplacewrapper = new LambdaQueryWrapper<>();
routingsupportingreplacewrapper.in(RoutingSupportingReplace::getStrsupid, batchIds)
.eq(RoutingSupportingReplace::getIsdeleted, 0);
replaces.addAll(routingSupportingReplaceMapper.selectList(routingsupportingreplacewrapper));
}
return replaces;
}
private <T> List<List<T>> partitionList(List<T> source, int batchSize) {
List<List<T>> partitions = new ArrayList<>();
if (CollectionUtils.isEmpty(source) || batchSize <= 0) {
return partitions;
}
for (int i = 0; i < source.size(); i += batchSize) {
partitions.add(source.subList(i, Math.min(i + batchSize, source.size())));
}
return partitions;
}
private List<RoutingDetail> GetRoutingDetails(String sceneId) { private List<RoutingDetail> GetRoutingDetails(String sceneId) {
List<RoutingDetail> RoutingDetails=(List<RoutingDetail>)GlobalCacheUtil.get(sceneId+routingDetailCacheKey); List<RoutingDetail> RoutingDetails=(List<RoutingDetail>)GlobalCacheUtil.get(sceneId+routingDetailCacheKey);
if(RoutingDetails==null) if(RoutingDetails==null)
......
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
<!-- 针对你的包结构进行配置 --> <!-- 针对你的包结构进行配置 -->
<!-- 1. 算法包(你最关心的) --> <!-- 1. 算法包(你最关心的) -->
<logger name="com.aps.service.Algorithm" level="DEBUG"> <logger name="com.aps.service.Algorithm" level="INFO">
<appender-ref ref="FILE"/> <appender-ref ref="FILE"/>
</logger> </logger>
...@@ -81,7 +81,7 @@ ...@@ -81,7 +81,7 @@
</logger> </logger>
<!-- 5. Mapper层(SQL日志) --> <!-- 5. Mapper层(SQL日志) -->
<logger name="com.aps.mapper" level="DEBUG"> <logger name="com.aps.mapper" level="INFO">
<appender-ref ref="FILE"/> <appender-ref ref="FILE"/>
</logger> </logger>
...@@ -91,26 +91,26 @@ ...@@ -91,26 +91,26 @@
</logger> </logger>
<!-- 第三方框架日志控制 --> <!-- 第三方框架日志控制 -->
<logger name="org.springframework" level="WARN"/> <logger name="org.springframework" level="INFO"/>
<logger name="org.mybatis" level="WARN"/> <logger name="org.mybatis" level="INFO"/>
<logger name="com.zaxxer.hikari" level="WARN"/> <logger name="com.zaxxer.hikari" level="INFO"/>
<!-- 根据不同环境调整 --> <!-- 根据不同环境调整 -->
<springProfile name="dev"> <springProfile name="dev">
<root level="DEBUG"> <root level="INFO">
<appender-ref ref="CONSOLE"/> <appender-ref ref="CONSOLE"/>
</root> </root>
<logger name="com.aps" level="DEBUG"/> <logger name="com.aps" level="INFO"/>
</springProfile> </springProfile>
<springProfile name="prod"> <springProfile name="prod">
<root level="WARN"> <root level="INFO">
<appender-ref ref="FILE"/> <appender-ref ref="FILE"/>
<appender-ref ref="ERROR_FILE"/> <appender-ref ref="ERROR_FILE"/>
</root> </root>
<!-- 生产环境关闭DEBUG日志 --> <!-- 生产环境关闭DEBUG日志 -->
<logger name="com.aps.mapper" level="WARN"/> <logger name="com.aps.mapper" level="INFO"/>
<logger name="com.aps.service.Algorithm" level="INFO"/> <logger name="com.aps.service.Algorithm" level="INFO"/>
</springProfile> </springProfile>
</configuration> </configuration>
\ No newline at end of file
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