Commit b1b2a85e authored by Tong Li's avatar Tong Li

Merge remote-tracking branch 'origin/tl'

parents ccc8685f cb1d4c68
...@@ -157,6 +157,7 @@ private Long isInterrupt = 1l; ...@@ -157,6 +157,7 @@ private Long isInterrupt = 1l;
private String equipName;//设备编码 private String equipName;//设备编码
@Schema(description = "指定开始时间") @Schema(description = "指定开始时间")
@JsonInclude(JsonInclude.Include.ALWAYS) @JsonInclude(JsonInclude.Include.ALWAYS)
private LocalDateTime designatedStartTime; private LocalDateTime designatedStartTime;
...@@ -164,4 +165,12 @@ private Long isInterrupt = 1l; ...@@ -164,4 +165,12 @@ private Long isInterrupt = 1l;
private LocalDateTime jitPreferredStartTime; private LocalDateTime jitPreferredStartTime;
private boolean jitTemporary = false; private boolean jitTemporary = false;
private String schedulingMode = SchedulingMode.FORWARD.name();
private int anchorTimeSecond = 0;
public enum SchedulingMode {
FORWARD, BACKWARD
}
} }
...@@ -39,6 +39,11 @@ public class GlobalParam { ...@@ -39,6 +39,11 @@ public class GlobalParam {
/// </summary> /// </summary>
private boolean IsOverlap = false; private boolean IsOverlap = false;
/// <summary>
/// 是否全局倒排
/// </summary>
private boolean isJit = true;
private boolean _smoothSetup = false; // 设置时间平滑 工序的前处理是否提前 private boolean _smoothSetup = false; // 设置时间平滑 工序的前处理是否提前
private boolean _smoothChangeOver = true; // 默认true,设置时间 是否考虑换型时间 private boolean _smoothChangeOver = true; // 默认true,设置时间 是否考虑换型时间
......
...@@ -20,6 +20,7 @@ public class TimeSegment { ...@@ -20,6 +20,7 @@ public class TimeSegment {
private boolean isHoliday; // 是否节假日(true=节假日) private boolean isHoliday; // 是否节假日(true=节假日)
private boolean isUsed; // 是否已被占用(true=已分配任务) private boolean isUsed; // 是否已被占用(true=已分配任务)
private double efficiency=1;//效率 private double efficiency=1;//效率
private int processingTime;
// 无参构造(Lombok默认生成) // 无参构造(Lombok默认生成)
public TimeSegment() {} public TimeSegment() {}
......
...@@ -20,6 +20,7 @@ import org.springframework.stereotype.Component; ...@@ -20,6 +20,7 @@ import org.springframework.stereotype.Component;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
...@@ -66,7 +67,7 @@ public class GeneticDecoder { ...@@ -66,7 +67,7 @@ public class GeneticDecoder {
private DiscreteParameterMatrixService discreteParameterMatrixService; private DiscreteParameterMatrixService discreteParameterMatrixService;
private String sceneId; private String sceneId;
private boolean rebuildStructureForCurrentDecode = true; private boolean rebuildStructureForCurrentDecode = false;
...@@ -327,8 +328,10 @@ public class GeneticDecoder { ...@@ -327,8 +328,10 @@ public class GeneticDecoder {
Entry entry=allOperations.stream() Entry entry=allOperations.stream()
.filter(t->t.getGroupId()==num&&t.getSequence()==scheduledCount1) .filter(t->t.getGroupId()==num&&t.getSequence()==scheduledCount1)
.findFirst().orElse(null); .findFirst().orElse(null);
entry.setSchedulingMode(Entry.SchedulingMode.FORWARD.name());
if(entry!=null&&entry.getDependentOnOrderIds().size()>0) if(entry!=null&&entry.getDependentOnOrderIds().size()>0)
{ {
for (int order : entry.getDependentOnOrderIds()) { for (int order : entry.getDependentOnOrderIds()) {
for (int num1 : sfSequence.get(order)){ for (int num1 : sfSequence.get(order)){
...@@ -358,7 +361,7 @@ public class GeneticDecoder { ...@@ -358,7 +361,7 @@ public class GeneticDecoder {
Entry entry=allOperations.stream() Entry entry=allOperations.stream()
.filter(t->t.getGroupId()==orderid&&t.getSequence()==scheduledCount1) .filter(t->t.getGroupId()==orderid&&t.getSequence()==scheduledCount1)
.findFirst().orElse(null); .findFirst().orElse(null);
entry.setSchedulingMode(Entry.SchedulingMode.BACKWARD.name());
if(entry!=null&&entry.getDependentOnOrderIds().size()>0) { if(entry!=null&&entry.getDependentOnOrderIds().size()>0) {
for (int order : entry.getDependentOnOrderIds()) { for (int order : entry.getDependentOnOrderIds()) {
...@@ -411,7 +414,7 @@ public class GeneticDecoder { ...@@ -411,7 +414,7 @@ public class GeneticDecoder {
public void serialDecode(Chromosome chromosome) { public void serialDecode(Chromosome chromosome) {
long decodeStart = System.nanoTime(); long decodeStart = System.nanoTime();
chromosome.setScenarioID(sceneId); chromosome.setScenarioID(sceneId);
boolean isJit=_globalParam.isJit();
if (rebuildStructureForCurrentDecode) { if (rebuildStructureForCurrentDecode) {
long t1 = System.nanoTime(); long t1 = System.nanoTime();
//创建半成品订单 //创建半成品订单
...@@ -486,25 +489,20 @@ public class GeneticDecoder { ...@@ -486,25 +489,20 @@ public class GeneticDecoder {
opMachineKeyMap.put(key, opMachine); opMachineKeyMap.put(key, opMachine);
} }
// 步骤2:按OperationSequencing顺序调度工序
// Map<Integer, Integer> orderProcessCounter = allOperations.stream()
// .collect(Collectors.groupingBy(Entry::getGroupId, Collectors.collectingAndThen(
// Collectors.counting(), Long::intValue)))
// .entrySet().stream()
// .collect(Collectors.toMap(Map.Entry::getKey, e -> 0));
//
// Map<Integer, Integer> orderLastEndTime = allOperations.stream()
// .collect(Collectors.groupingBy(Entry::getGroupId))
// .keySet().stream()
// .collect(Collectors.toMap(k -> k, k -> 0));
// 步骤2:按OperationSequencing顺序调度工序 // 步骤2:按OperationSequencing顺序调度工序
Map<Integer, Integer> orderProcessCounter = new HashMap<>(); Map<Integer, Integer> orderProcessCounter = new HashMap<>();
Map<Integer, Integer> orderLastEndTime = new HashMap<>(); Map<Integer, Integer> orderLastEndTime = new HashMap<>();
Map<Integer, Integer> orderDueDate = new HashMap<>();
Map<Integer, Entry> entryIndexById = new HashMap<>(); Map<Integer, Entry> entryIndexById = new HashMap<>();
Map<Integer, List<Entry>> entrysBygroupId = new HashMap<>(); Map<Integer, List<Entry>> entrysBygroupId = new HashMap<>();
Map<Integer, GAScheduleResult> scheduleIndexById = new ConcurrentHashMap<>(); Map<Integer, GAScheduleResult> scheduleIndexById = new ConcurrentHashMap<>();
for (Order order : chromosome.getOrders()) {
int end = (int) ChronoUnit.SECONDS.between(baseTime, order.getDueDate());
orderDueDate.putIfAbsent(order.getId(), end);
}
for (Entry op : allOperations) { for (Entry op : allOperations) {
int groupId = op.getGroupId(); int groupId = op.getGroupId();
orderProcessCounter.putIfAbsent(groupId, 0); orderProcessCounter.putIfAbsent(groupId, 0);
...@@ -525,9 +523,25 @@ public class GeneticDecoder { ...@@ -525,9 +523,25 @@ public class GeneticDecoder {
for (int groupId : chromosome.getOperationSequencing()) { for (int groupId : chromosome.getOperationSequencing()) {
int scheduledCount = orderProcessCounter.get(groupId); int scheduledCount = orderProcessCounter.get(groupId);
List<Entry> orderOps = entrysBygroupId.get(groupId).stream()
.sorted(Comparator.comparing(Entry::getSequence)) List<Entry> orderOps=new ArrayList<>();
.collect(Collectors.toList());
boolean orderIsJit=orderDueDate.get(groupId)>0;
if(isJit&&orderIsJit)
{
orderOps = entrysBygroupId.get(groupId).stream()
.sorted(Comparator.comparing(Entry::getSequence).reversed())
.collect(Collectors.toList());
orderIsJit=true;
}else {
orderOps = entrysBygroupId.get(groupId).stream()
.sorted(Comparator.comparing(Entry::getSequence))
.collect(Collectors.toList());
orderIsJit=false;
}
if (scheduledCount >= orderOps.size()) { if (scheduledCount >= orderOps.size()) {
throw new IllegalStateException(String.format( throw new IllegalStateException(String.format(
...@@ -555,16 +569,11 @@ public class GeneticDecoder { ...@@ -555,16 +569,11 @@ public class GeneticDecoder {
double processTime = machineOption.getProcessingTime(); double processTime = machineOption.getProcessingTime();
long opStart = System.nanoTime(); long opStart = System.nanoTime();
int actualEndTime = processOperation(currentOp,machineId,processTime,machineOption,chromosome,machineIdMap,machineTasksCache,entryIndexById,scheduleIndexById);
int actualEndTime = processOperation(currentOp,machineId,processTime,machineOption,chromosome,machineIdMap,machineTasksCache,entryIndexById,scheduleIndexById,(int)orderDueDate.get(groupId),orderIsJit);
long opElapsed = System.nanoTime() - opStart; long opElapsed = System.nanoTime() - opStart;
opCount++; opCount++;
// if (opElapsed > slowOpThresholdNs) {
// FileHelper.writeLogFile("[PERF] serialDecode 慢工序 opId=" + currentOp.getId()
// + ", groupId=" + groupId + ", seq=" + opSequence
// + ", machineId=" + machineId + ", hasBOM="
// + (currentOp.getMaterialRequirements() != null && currentOp.getMaterialRequirements().size() > 0)
// + ", 耗时=" + fmtMs(opElapsed));
// }
orderProcessCounter.put(groupId, orderProcessCounter.get(groupId) + 1); orderProcessCounter.put(groupId, orderProcessCounter.get(groupId) + 1);
orderLastEndTime.put(groupId, actualEndTime); orderLastEndTime.put(groupId, actualEndTime);
...@@ -649,6 +658,13 @@ public class GeneticDecoder { ...@@ -649,6 +658,13 @@ public class GeneticDecoder {
Map<Integer, Entry> entryIndexById = new HashMap<>(); Map<Integer, Entry> entryIndexById = new HashMap<>();
Map<Integer, List<Entry>> entrysBygroupId = new HashMap<>(); Map<Integer, List<Entry>> entrysBygroupId = new HashMap<>();
Map<Integer, GAScheduleResult> scheduleIndexById= new ConcurrentHashMap<>(); Map<Integer, GAScheduleResult> scheduleIndexById= new ConcurrentHashMap<>();
Map<Integer, Integer> orderDueDate = new HashMap<>();
for (Order order : chromosome.getOrders()) {
int end = (int) ChronoUnit.SECONDS.between(baseTime, order.getDueDate());
orderDueDate.putIfAbsent(order.getId(), end);
}
for (Entry op : allOperations) { for (Entry op : allOperations) {
int groupId = op.getGroupId(); int groupId = op.getGroupId();
orderProcessCounter.putIfAbsent(groupId, 0); orderProcessCounter.putIfAbsent(groupId, 0);
...@@ -790,11 +806,11 @@ public class GeneticDecoder { ...@@ -790,11 +806,11 @@ public class GeneticDecoder {
// 工序超过500的设备直接在主线程串行处理 // 工序超过500的设备直接在主线程串行处理
if (ops.size() > 500) { if (ops.size() > 500) {
// FileHelper.writeLogFile("设备 " + machineId + " 有 " + ops.size() + " 个工序,直接串行处理"); // FileHelper.writeLogFile("设备 " + machineId + " 有 " + ops.size() + " 个工序,直接串行处理");
processMachineOps(chromosome, machineId, ops, dependencyGraph, opCompleted, lock, opMachineKeyMap, machineIdMap, machineTasksCache, entryIndexById,scheduleIndexById); processMachineOps(chromosome, machineId, ops, dependencyGraph, opCompleted, lock, opMachineKeyMap, machineIdMap, machineTasksCache, entryIndexById,scheduleIndexById,orderDueDate);
} else { } else {
// 工序少的设备创建线程处理 // 工序少的设备创建线程处理
Thread thread = new Thread(() -> { Thread thread = new Thread(() -> {
processMachineOps(chromosome, machineId, ops, dependencyGraph, opCompleted, lock, opMachineKeyMap, machineIdMap, machineTasksCache, entryIndexById,scheduleIndexById); processMachineOps(chromosome, machineId, ops, dependencyGraph, opCompleted, lock, opMachineKeyMap, machineIdMap, machineTasksCache, entryIndexById,scheduleIndexById,orderDueDate);
}); });
thread.start(); thread.start();
threads.add(thread); threads.add(thread);
...@@ -824,7 +840,7 @@ public class GeneticDecoder { ...@@ -824,7 +840,7 @@ public class GeneticDecoder {
*/ */
private void processMachineOps(Chromosome chromosome, Long machineId, List<Entry> ops, private void processMachineOps(Chromosome chromosome, Long machineId, List<Entry> ops,
Map<Integer, List<Integer>> dependencyGraph, Map<Integer, List<Integer>> dependencyGraph,
Map<Integer, Boolean> opCompleted, Object lock,Map<String, OpMachine> opMachineKeyMap,Map<Long, Machine> machineIdMap,Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,Map<Integer, Entry> entryIndexById,Map<Integer, GAScheduleResult> scheduleIndexById) { Map<Integer, Boolean> opCompleted, Object lock,Map<String, OpMachine> opMachineKeyMap,Map<Long, Machine> machineIdMap,Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,Map<Integer, Entry> entryIndexById,Map<Integer, GAScheduleResult> scheduleIndexById,Map<Integer, Integer> orderDueDate) {
//long machineStartTime = System.currentTimeMillis(); //long machineStartTime = System.currentTimeMillis();
for (int i = 0; i < ops.size(); i++) { for (int i = 0; i < ops.size(); i++) {
...@@ -858,9 +874,10 @@ public class GeneticDecoder { ...@@ -858,9 +874,10 @@ public class GeneticDecoder {
double processTime = machineOption.getProcessingTime(); double processTime = machineOption.getProcessingTime();
int orderDueDate1=orderDueDate.get(op.getGroupId());
// 处理当前工序 // 处理当前工序
// long processStartTime = System.currentTimeMillis(); // long processStartTime = System.currentTimeMillis();
int actualEndTime = processOperation(op,machineId,processTime,machineOption,chromosome,machineIdMap,machineTasksCache, entryIndexById,scheduleIndexById); int actualEndTime = processOperation(op,machineId,processTime,machineOption,chromosome,machineIdMap,machineTasksCache, entryIndexById,scheduleIndexById,orderDueDate1,false);
// long processEndTime = System.currentTimeMillis(); // long processEndTime = System.currentTimeMillis();
...@@ -1018,28 +1035,37 @@ public class GeneticDecoder { ...@@ -1018,28 +1035,37 @@ public class GeneticDecoder {
private int processOperation(Entry currentOp,Long machineId,double processTime,OpMachine machineOption,Chromosome chromosome,Map<Long, Machine> machineIdMap,Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,Map<Integer, Entry> entryIndexById,Map<Integer, GAScheduleResult> scheduleIndexById) private int processOperation(Entry currentOp,Long machineId,double processTime,OpMachine machineOption,Chromosome chromosome,Map<Long, Machine> machineIdMap,Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,Map<Integer, Entry> entryIndexById,Map<Integer, GAScheduleResult> scheduleIndexById,int orderDueDate, boolean isJit)
{ {
Machine targetMachine = machineIdMap.get(machineId); Machine targetMachine = machineIdMap.get(machineId);
int prevtime = 0; int prevtime = 0;
//后处理时间 //后处理时间
int teardownTime = currentOp.getTeardownTime(); // int teardownTime = currentOp.getTeardownTime();
if (!currentOp.getPrevEntryIds().isEmpty()) { if(isJit)
// 处理多个前工序 {
prevtime= CalPrevtime( prevtime, currentOp, chromosome, processTime, targetMachine,entryIndexById, scheduleIndexById); prevtime=orderDueDate;
if (!currentOp.getNextEntryIds().isEmpty()) {
// 处理多个前工序
prevtime = CalNexttime(prevtime, currentOp, chromosome, processTime, targetMachine, entryIndexById, scheduleIndexById);
}
}else {
if (!currentOp.getPrevEntryIds().isEmpty()) {
// 处理多个前工序
prevtime = CalPrevtime(prevtime, currentOp, chromosome, processTime, targetMachine, entryIndexById, scheduleIndexById);
}
} }
int prevendtime=prevtime; int prevendtime=prevtime;
Machine machine = machineIdMap.get(machineId); Machine machine = machineIdMap.get(machineId);
int actualEndTime = processWithSingleMachine(currentOp, machine, processTime, prevtime,machineOption, chromosome,false,prevendtime,machineTasksCache,entryIndexById, scheduleIndexById); int actualEndTime = processWithSingleMachine(currentOp, machine, processTime, prevtime,machineOption, chromosome,false,prevendtime,machineTasksCache,entryIndexById, scheduleIndexById,isJit);
return actualEndTime; return actualEndTime;
} }
private int processWithSingleMachine(Entry operation, Machine machine, double processingTime, private int processWithSingleMachine(Entry operation, Machine machine, double processingTime,
int prevOperationEndTime,OpMachine machineOption, Chromosome chromosome,boolean calbom,int prevendtime,Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,Map<Integer, Entry> entryIndexById,Map<Integer, GAScheduleResult> scheduleIndexById) { int prevOperationEndTime,OpMachine machineOption, Chromosome chromosome,boolean calbom,int prevendtime,Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,Map<Integer, Entry> entryIndexById,Map<Integer, GAScheduleResult> scheduleIndexById,boolean isJit) {
long pwsStart = System.nanoTime(); long pwsStart = System.nanoTime();
int processingTimeTotal=0; int processingTimeTotal=0;
int earliestStartTime = prevOperationEndTime; int earliestStartTime = prevOperationEndTime;
...@@ -1097,18 +1123,84 @@ public class GeneticDecoder { ...@@ -1097,18 +1123,84 @@ public class GeneticDecoder {
earliestStartTime = Math.max(earliestStartTime, jitPreferredStartTime); earliestStartTime = Math.max(earliestStartTime, jitPreferredStartTime);
} }
if(machine.getCapacityTypeName().equals("Infinite"))
{
int endTime=0;
int startTime=0;
processingTimeTotal=(int)Math.ceil (processingTime);
if(isJit)
{
endTime=earliestStartTime;
startTime=endTime-processingTimeTotal;
}else {
startTime=earliestStartTime;
endTime=startTime+processingTimeTotal;
}
GAScheduleResult result= CreateResult(operation,machine.getId(),startTime,endTime,processingTime,0,preTime,teardownTime,0,null,existingResult);
chromosome.getResult().add(result);
machine.setLastGene(result);
scheduleIndexById.put(operation.getId(),result);
return endTime;
}
// baseEarliestStartTime:不考虑 BOM 推迟时的理论最早开工时间。
// candidateStartTime:固定点迭代时本轮尝试的开工时间。
// bomtime:按当前候选开工时间试算出来的物料最早可开工时间。
int baseEarliestStartTime = earliestStartTime;
int candidateStartTime = earliestStartTime;
if (operation.getTargetFinishedOperationId() != null &&
!operation.getTargetFinishedOperationId().isEmpty()) {
// 1. 计算成品可用时间
GeneticDecoderBom bom=new GeneticDecoderBom();
int finishedAvailableTime = bom.calculateFinishedAvailableTime(
operation, chromosome, baseTime, machineTasksCache, entryIndexById, scheduleIndexById);
if (finishedAvailableTime > 0) {
// 2. 计算半成品最晚开工时间(倒排:半成品结束时间 = 成品可用时间,然后往前推加工时间)
// 加上JIT缓冲时间(如果有)
int semiBufferMinutes = _globalParam.getSemiJitBufferMinutes();
int semiBufferSeconds = semiBufferMinutes * 60;
// 半成品最晚结束时间 = 成品可用时间 - 缓冲
int semiLatestEndTime = Math.max(0, finishedAvailableTime - semiBufferSeconds);
// 半成品最晚开工时间 = 半成品最晚结束时间 - 加工总时长
int semiLatestStartTime = Math.max(0, semiLatestEndTime - processingTimeTotal);
// 3. 计算半成品物料的BOM齐套时间(正向)
int semiBomTime = getOperationBOMTime(operation, chromosome);
// 4. 比较:如果倒排的开工时间早于基准时间或BOM齐套时间,则从基准时间或BOM齐套时间开始正排
int semiForwardStartTime = Math.max(baseEarliestStartTime, semiBomTime);
if (semiLatestStartTime >= semiForwardStartTime) {
// 倒排可行:使用倒排的最晚开工时间
candidateStartTime = semiLatestStartTime;
} else {
// 倒排不可行:使用正排的最早开工时间
candidateStartTime = semiForwardStartTime;
}
// 更新基准最早开工时间
baseEarliestStartTime = candidateStartTime;
}
}
int setupTime=0; int setupTime=0;
long machineId = machine.getId(); long machineId = machine.getId();
// 只有存在物料约束(或本次被强制要求重算 BOM)的工序,才需要联立求解“机台何时能排”和“物料何时可用”。 // 只有存在物料约束(或本次被强制要求重算 BOM)的工序,才需要联立求解“机台何时能排”和“物料何时可用”。
// targetFinishedOperationId != null 的工序通常由前置成品工序驱动,这里不再额外触发一轮 BOM 试算。 // targetFinishedOperationId != null 的工序通常由前置成品工序驱动,这里不再额外触发一轮 BOM 试算。
boolean needMaterialCheck =(operation.getMaterialRequirements()!=null&&operation.getMaterialRequirements().size()>0&&operation.getTargetFinishedOperationId()==null)||calbom; boolean needMaterialCheck =false;//(operation.getMaterialRequirements()!=null&&operation.getMaterialRequirements().size()>0&&operation.getTargetFinishedOperationId()==null)||calbom;
// 正式落排后还要再做一次带提交的物料校验,把试算阶段推导出的 BOM 状态真正写回 chromosome。 // 正式落排后还要再做一次带提交的物料校验,把试算阶段推导出的 BOM 状态真正写回 chromosome。
boolean commitMaterialCheck = needMaterialCheck; boolean commitMaterialCheck = needMaterialCheck;
// baseEarliestStartTime:不考虑 BOM 推迟时的理论最早开工时间。 // baseEarliestStartTime:不考虑 BOM 推迟时的理论最早开工时间。
// candidateStartTime:固定点迭代时本轮尝试的开工时间。 // candidateStartTime:固定点迭代时本轮尝试的开工时间。
// bomtime:按当前候选开工时间试算出来的物料最早可开工时间。 // bomtime:按当前候选开工时间试算出来的物料最早可开工时间。
int baseEarliestStartTime = earliestStartTime; baseEarliestStartTime = earliestStartTime;
int candidateStartTime = earliestStartTime; candidateStartTime = earliestStartTime;
int bomtime=0; int bomtime=0;
// 机台时间和物料时间可能会互相推动,最多迭代 maxSolveIterations 次来找收敛点。 // 机台时间和物料时间可能会互相推动,最多迭代 maxSolveIterations 次来找收敛点。
int maxSolveIterations = Math.max(1, _globalParam.getMaterialSolveMaxIterations()); int maxSolveIterations = Math.max(1, _globalParam.getMaterialSolveMaxIterations());
...@@ -1197,44 +1289,8 @@ public class GeneticDecoder { ...@@ -1197,44 +1289,8 @@ public class GeneticDecoder {
lastGeneOnMachine = getLastMachineTask(machineTasks); lastGeneOnMachine = getLastMachineTask(machineTasks);
needMaterialCheck = false; needMaterialCheck = false;
} }
// 旧兜底单次试算路径:当前固定点迭代执行完后会把 needMaterialCheck 置为 false,
// 因此这段分支实际上不会再进入。先注释保留,后续若确认无用可直接删除,
// 或按真实意图改成 !converged 时的兜底处理。
// if(needMaterialCheck) {
// int earliestStartTimeold=earliestStartTime;
// if (_globalParam.is_smoothChangeOver()) {
//
// Map<Integer, Object> reslte = calculateSetupTime(lastGeneOnMachine, operation, machine, earliestStartTime, processingTimeTotal, _globalParam.is_smoothChangeOverInWeek(), chromosome.getAllOperations());
// // setupTime = (int) reslte.get(1);//换型时间
// // int setupStartTime = (int) reslte.get(2);//换型开始时间
// //earliestStartTime=(int)reslte.get(3);//上个任务的结束时间
// earliestStartTime = (int) reslte.get(4);//最早开工时间
// }
// LocalDateTime startTime1 = baseTime.plus(earliestStartTime, ChronoUnit.SECONDS);
//
// TimeSegment slot = machineCalculator.GetCurrentOrNextShift(machine, startTime1, "", false);
//
// if(slot!=null)
// {
// earliestStartTime = slot.getStart().isAfter(startTime1)
// ?(int) ChronoUnit.SECONDS.between(baseTime, slot.getStart())
// : earliestStartTime;
//
// }
// bomtime= EditOperationBOMTime(operation,chromosome,earliestStartTime,machineTasksCache, entryIndexById, scheduleIndexById, false);
//
//
// earliestStartTime = Math.max(earliestStartTime, bomtime);
// }
// machineTasks =chromosome.getResult().stream()
// .filter(t -> t.getMachineId() == machine.getId())
// .sorted(Comparator.comparingInt(GAScheduleResult::getStartTime))
// .collect(Collectors.toCollection(CopyOnWriteArrayList::new));
//
// machineTasksCache.put(machine.getId(), machineTasks);
// 正式落排前,再取一次当前机台最后一道工序,保证换型计算基于最新排程结果。 // 正式落排前,再取一次当前机台最后一道工序,保证换型计算基于最新排程结果。
if(machineTasks!=null&&machineTasks.size()>0&&_globalParam.is_smoothChangeOver()) if(machineTasks!=null&&machineTasks.size()>0&&_globalParam.is_smoothChangeOver())
{ {
...@@ -1243,7 +1299,8 @@ public class GeneticDecoder { ...@@ -1243,7 +1299,8 @@ public class GeneticDecoder {
// 下面开始生成正式的 geneDetails。 // 下面开始生成正式的 geneDetails。
// 与 buildMachinePreview 的区别是:这里得到的结果会继续向下写入 GAScheduleResult,成为真正排程结果。 // 与 buildMachinePreview 的区别是:这里得到的结果会继续向下写入 GAScheduleResult,成为真正排程结果。
if (_globalParam.is_smoothChangeOver()) { //if (_globalParam.is_smoothChangeOver()) {
if (false) {
//是否考虑换型时间 //是否考虑换型时间
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);//换型时间
...@@ -1253,14 +1310,14 @@ public class GeneticDecoder { ...@@ -1253,14 +1310,14 @@ public class GeneticDecoder {
processingTimeTotal=(int)reslte.get(5);//processingTimeTotal processingTimeTotal=(int)reslte.get(5);//processingTimeTotal
if(setupTime==0) if(setupTime==0)
{ {
geneDetails = machineCalculator.getNextAvailableTime(machine, earliestStartTime, -1, geneDetails = machineCalculator.getNextAvailableTime(machine, operation, earliestStartTime, -1,
processingTimeTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true); processingTimeTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true,isJit);
}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, processingTimeTotal, earliestStartTime, Map<Integer,Object> result = machineCalculator.CreateScheduleResult(machine, operation, processingTimeTotal, earliestStartTime,
AvailableTimeSegment, processingTime, operation.getQuantity(), operation.getIsInterrupt() != 1, setupTime, _globalParam.is_smoothChangeOverInWeek(), setupStartTime); AvailableTimeSegment, processingTime, operation.getQuantity(), operation.getIsInterrupt() != 1, setupTime, _globalParam.is_smoothChangeOverInWeek(), setupStartTime,isJit);
...@@ -1269,8 +1326,8 @@ public class GeneticDecoder { ...@@ -1269,8 +1326,8 @@ public class GeneticDecoder {
geneDetails=(CopyOnWriteArrayList<ScheduleResultDetail>) result.get(2); geneDetails=(CopyOnWriteArrayList<ScheduleResultDetail>) result.get(2);
} }
}else { }else {
geneDetails = machineCalculator.getNextAvailableTime(machine, earliestStartTime, -1, geneDetails = machineCalculator.getNextAvailableTime(machine, operation, earliestStartTime, -1,
processingTimeTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true); processingTimeTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true,isJit);
} }
...@@ -1287,14 +1344,6 @@ public class GeneticDecoder { ...@@ -1287,14 +1344,6 @@ public class GeneticDecoder {
// (lastGeneOnMachine != null ? lastGeneOnMachine.getEndTime() : 0) + // (lastGeneOnMachine != null ? lastGeneOnMachine.getEndTime() : 0) +
// ", 换型: " + setupTime + ")"); // ", 换型: " + setupTime + ")");
int startTime = geneDetails.stream() int startTime = geneDetails.stream()
.mapToInt(ScheduleResultDetail::getStartTime) .mapToInt(ScheduleResultDetail::getStartTime)
.min() .min()
...@@ -1338,11 +1387,36 @@ public class GeneticDecoder { ...@@ -1338,11 +1387,36 @@ public class GeneticDecoder {
} }
} }
GAScheduleResult result=new GAScheduleResult(); GAScheduleResult result= CreateResult(operation,machine.getId(),startTime,endTime,processingTime,setupTime,preTime,teardownTime,bomtime,geneDetails,existingResult);
// System.out.println("huanxingshijian="+result.getChangeOverTime()+"-------------------"+result.getOrderId()+"--------"+result.getExecId()+"---------"+prev.getOrderId()+"--------"+prev.getExecId());
chromosome.getResult().add(result);
machine.setLastGene(result);
// FileHelper.writeLogFile(" 结束 "+ConvertTime(startTime)+"--"+ConvertTime(endTime)+" "+operation.getGroupId()+" : "+operation.getId()+",处理时间: " + processingTime + ", 后处理: " + teardownTime +
// ", 前处理: " + preTime + ", 换型: " + setupTime+ ", 数量: " + operation.getQuantity()+ ", 设备: "+machine.getId()+ ", 是否可中断: "+operation.getIsInterrupt());
if (machineTasks != null) {
machineTasks.add(result);
}
updateSemiJitTargets(operation, startTime, chromosome);
scheduleIndexById.put(operation.getId(),result);
// long pwsElapsed = System.nanoTime() - pwsStart;
// if (pwsElapsed > 500_000_000L) {
// FileHelper.writeLogFile("[PERF] processWithSingleMachine 总慢 opId=" + operationId
// + ", groupId=" + operation.getGroupId() + ", machineId=" + machineId
// + ", hasBOM=" + commitMaterialCheck + ", 耗时=" + fmtMs(pwsElapsed));
// }
return endTime;
}
private GAScheduleResult CreateResult(Entry operation,long machineId
,int startTime,int endTime,double processingTime
,int setupTime,int preTime,int teardownTime,int bomtime
,CopyOnWriteArrayList<ScheduleResultDetail> geneDetails,GAScheduleResult existingResult )
{
GAScheduleResult result=new GAScheduleResult();
result.setGroupId(operation.getGroupId()); result.setGroupId(operation.getGroupId());
result.setOperationId(operation.getId()); result.setOperationId(operation.getId());
...@@ -1355,7 +1429,7 @@ public class GeneticDecoder { ...@@ -1355,7 +1429,7 @@ public class GeneticDecoder {
result.setTargetFinishedOperationId(operation.getTargetFinishedOperationId() == null result.setTargetFinishedOperationId(operation.getTargetFinishedOperationId() == null
? null ? null
: new ArrayList<>(operation.getTargetFinishedOperationId())); : new ArrayList<>(operation.getTargetFinishedOperationId()));
result.setMachineId(machine.getId()); result.setMachineId(machineId);
result.setStartTime(startTime); result.setStartTime(startTime);
result.setEndTime(endTime); result.setEndTime(endTime);
result.setOneTime(processingTime); result.setOneTime(processingTime);
...@@ -1372,48 +1446,35 @@ public class GeneticDecoder { ...@@ -1372,48 +1446,35 @@ public class GeneticDecoder {
} }
int processingTimeTotal1=0; int processingTimeTotal1=0;
if(geneDetails!=null&&geneDetails.size()>0)
processingTimeTotal1=(int) geneDetails.stream().filter(t->t.getUsedSegment()==null).mapToDouble(ScheduleResultDetail::getProcessingTime) // 替换为实际字段名 {
processingTimeTotal1=(int) geneDetails.stream().filter(t->t.getUsedSegment()==null).mapToDouble(ScheduleResultDetail::getProcessingTime) // 替换为实际字段名
.sum();
List<TimeSegment> UsedSegments=geneDetails.stream().
filter(t->t.getUsedSegment()!=null)
.map(ScheduleResultDetail::getUsedSegment).flatMap(List::stream)
// 收集为最终的 List
.collect(Collectors.toList());
if(UsedSegments!=null&&UsedSegments.size()>0)
{
processingTimeTotal1+=(int)UsedSegments.stream().mapToDouble(TimeSegment::getProcessingTime) // 替换为实际字段名
.sum(); .sum();
}
List<TimeSegment> UsedSegments=geneDetails.stream().
filter(t->t.getUsedSegment()!=null)
.map(ScheduleResultDetail::getUsedSegment).flatMap(List::stream)
// 收集为最终的 List
.collect(Collectors.toList());
if(UsedSegments!=null&&UsedSegments.size()>0)
{
processingTimeTotal1+=(int)UsedSegments.stream().mapToDouble(TimeSegment::getProcessingTime) // 替换为实际字段名
.sum();
}
result.setGeneDetails(geneDetails);
}else {
processingTimeTotal1=endTime-startTime;
}
result.setProcessingTime(processingTimeTotal1); result.setProcessingTime(processingTimeTotal1);
result.setGeneDetails(geneDetails); return result;
// System.out.println("huanxingshijian="+result.getChangeOverTime()+"-------------------"+result.getOrderId()+"--------"+result.getExecId()+"---------"+prev.getOrderId()+"--------"+prev.getExecId());
chromosome.getResult().add(result);
machine.setLastGene(result);
// FileHelper.writeLogFile(" 结束 "+ConvertTime(startTime)+"--"+ConvertTime(endTime)+" "+operation.getGroupId()+" : "+operation.getId()+",处理时间: " + processingTime + ", 后处理: " + teardownTime +
// ", 前处理: " + preTime + ", 换型: " + setupTime+ ", 数量: " + operation.getQuantity()+ ", 设备: "+machine.getId()+ ", 是否可中断: "+operation.getIsInterrupt());
if (machineTasks != null) {
machineTasks.add(result);
}
updateSemiJitTargets(operation, startTime, chromosome);
scheduleIndexById.put(operation.getId(),result);
// long pwsElapsed = System.nanoTime() - pwsStart;
// if (pwsElapsed > 500_000_000L) {
// FileHelper.writeLogFile("[PERF] processWithSingleMachine 总慢 opId=" + operationId
// + ", groupId=" + operation.getGroupId() + ", machineId=" + machineId
// + ", hasBOM=" + commitMaterialCheck + ", 耗时=" + fmtMs(pwsElapsed));
// }
return endTime;
} }
private void updateSemiJitTargets(Entry parentOperation, int parentStartTime, Chromosome chromosome) { private void updateSemiJitTargets(Entry parentOperation, int parentStartTime, Chromosome chromosome) {
if (parentOperation.getDependentOnOrderIds() == null || parentOperation.getDependentOnOrderIds().isEmpty()) { if (parentOperation.getDependentOnOrderIds() == null || parentOperation.getDependentOnOrderIds().isEmpty()) {
return; return;
} }
...@@ -1469,22 +1530,22 @@ public class GeneticDecoder { ...@@ -1469,22 +1530,22 @@ public class GeneticDecoder {
if(setupTime==0) if(setupTime==0)
{ {
// 没有换型占用时,直接查机台下一段可排的可用窗口。 // 没有换型占用时,直接查机台下一段可排的可用窗口。
geneDetails = machineCalculator.getNextAvailableTime(machine, previewStart, -1, geneDetails = machineCalculator.getNextAvailableTime(machine, operation, previewStart, -1,
previewProcessingTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true); previewProcessingTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true,false);
}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, operation, 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,false);
setupTime=(int)result.get(1); setupTime=(int)result.get(1);
geneDetails=(CopyOnWriteArrayList<ScheduleResultDetail>) result.get(2); geneDetails=(CopyOnWriteArrayList<ScheduleResultDetail>) result.get(2);
} }
}else { }else {
// 不考虑平滑换型时,只需要找加工段可用窗口。 // 不考虑平滑换型时,只需要找加工段可用窗口。
geneDetails = machineCalculator.getNextAvailableTime(machine, previewStart, -1, geneDetails = machineCalculator.getNextAvailableTime(machine, operation, previewStart, -1,
previewProcessingTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true); previewProcessingTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true,false);
} }
// geneDetails 可能跨多个班次/时段,开始时间取最小值,结束时间取最大值。 // geneDetails 可能跨多个班次/时段,开始时间取最小值,结束时间取最大值。
...@@ -1641,6 +1702,36 @@ public class GeneticDecoder { ...@@ -1641,6 +1702,36 @@ public class GeneticDecoder {
} }
/**
* 计算后序结束时间
* @param nexttime
* @param currentOp
* @param chromosome
* @return
*/
private int CalNexttime(int nexttime,Entry currentOp,Chromosome chromosome,double processTime,Machine machine,Map<Integer, Entry> entryIndexById,Map<Integer, GAScheduleResult> scheduleIndexById) {
List<OperationDependency> FsOperations = currentOp.getNextEntryIds().stream()
.filter(t -> t.getDependencyType() == DependencyType.FinishToStart)
.collect(Collectors.toList());//串行
if (FsOperations != null && FsOperations.size() > 0) {
for (OperationDependency opid : FsOperations) {
// List<GAScheduleResult> prevOperations = chromosome.getResult().stream()
// .filter(t -> t.getOperationId() == opid.getPrevOperationId())
// .collect(Collectors.toList());//
GAScheduleResult nextOp= scheduleIndexById.get(opid.getNextOperationId());
// for (GAScheduleResult prevOp : prevOperations) {
if(nextOp!=null){
//加上后处理时间
nexttime =Math.min(nexttime,nextOp.getStartTime());
}
}
}
return nexttime;
}
private int CalSSPrevtime(List<GAScheduleResult> newScheduleResult,List<ScheduleResultDetail> newScheduleResultDetails,int prevtime,Entry currentOp,Chromosome chromosome,double processTime,Machine machine) private int CalSSPrevtime(List<GAScheduleResult> newScheduleResult,List<ScheduleResultDetail> newScheduleResultDetails,int prevtime,Entry currentOp,Chromosome chromosome,double processTime,Machine machine)
{ {
int prevperationEndTime = newScheduleResult.stream() int prevperationEndTime = newScheduleResult.stream()
...@@ -2014,7 +2105,7 @@ public class GeneticDecoder { ...@@ -2014,7 +2105,7 @@ public class GeneticDecoder {
.findFirst() .findFirst()
.orElse(null); .orElse(null);
// 缓存机器任务 // 缓存机器任务
int actualEndTime = processWithSingleMachine(currentOp, machine, processTime, prevtime,opMachine, chromosome,true,prevendtime,machineTasksCache, entryIndexById, scheduleIndexById); int actualEndTime = processWithSingleMachine(currentOp, machine, processTime, prevtime,opMachine, chromosome,true,prevendtime,machineTasksCache, entryIndexById, scheduleIndexById,true);
} }
......
package com.aps.service.Algorithm;
import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GAScheduleResult;
import com.aps.entity.Algorithm.OperationDependency;
import com.aps.entity.Algorithm.OrderMaterialRequirement;
import com.aps.entity.basic.Entry;
import com.aps.entity.basic.Machine;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 作者:佟礼
* 时间:2026-04-28
*/
public class GeneticDecoderBom {
/**
* 计算成品可用时间(用于半成品排程)
*
* @param semiFinishedOperation 半成品工序
* @param chromosome 染色体
* @param baseTime 基准时间
* @param machineTasksCache 设备任务缓存
* @param entryIndexById 工序索引
* @param scheduleIndexById 排程索引
* @return 成品可用时间(秒)
*/
public int calculateFinishedAvailableTime(Entry semiFinishedOperation, Chromosome chromosome,
LocalDateTime baseTime,
Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,
Map<Integer, Entry> entryIndexById,
Map<Integer, GAScheduleResult> scheduleIndexById) {
// 如果没有关联的成品工序,返回0
if (semiFinishedOperation.getTargetFinishedOperationId() == null ||
semiFinishedOperation.getTargetFinishedOperationId().isEmpty()) {
return 0;
}
int maxFinishedTime = 0;
// 遍历所有关联的成品工序,计算每个的可用时间,取最大值
for (Integer finishedOperationId : semiFinishedOperation.getTargetFinishedOperationId()) {
Entry finishedOperation = entryIndexById.get(finishedOperationId);
if (finishedOperation == null) {
continue;
}
// 计算该成品工序的可用时间
int finishedAvailableTime = calculateSingleFinishedAvailableTime(
finishedOperation, chromosome, baseTime, machineTasksCache, entryIndexById, scheduleIndexById);
maxFinishedTime = Math.min(maxFinishedTime, finishedAvailableTime);
}
return maxFinishedTime;
}
/**
* 计算单个成品工序的可用时间
*
* @param finishedOperation 成品工序
* @param chromosome 染色体
* @param baseTime 基准时间
* @param machineTasksCache 设备任务缓存
* @param entryIndexById 工序索引
* @param scheduleIndexById 排程索引
* @return 成品工序可用时间(秒)
*/
private int calculateSingleFinishedAvailableTime(Entry finishedOperation, Chromosome chromosome,
LocalDateTime baseTime,
Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,
Map<Integer, Entry> entryIndexById,
Map<Integer, GAScheduleResult> scheduleIndexById) {
int maxTime = 0;
// 1. 计算成品前一序时间
int prevOperationEndTime = 0;
if (!finishedOperation.getPrevEntryIds().isEmpty()) {
for (OperationDependency prevOp : finishedOperation.getPrevEntryIds()) {
GAScheduleResult prevResult = scheduleIndexById.get(prevOp.getPrevOperationId());
if (prevResult != null) {
prevOperationEndTime = Math.max(prevOperationEndTime, prevResult.getEndTime());
}
}
}
//成品时间一定晚于prevOperationEndTime
//半成品库存只能用
maxTime = Math.max(maxTime, prevOperationEndTime);
// 2. 计算成品当前序所用设备最后一个任务的结束时间
Long machineId = finishedOperation.getSelectMachineID();
if (machineId == null && finishedOperation.getMachineOptions() != null &&
!finishedOperation.getMachineOptions().isEmpty()) {
// 如果设备还未选择,使用第一个可用设备
machineId = finishedOperation.getMachineOptions().get(0).getMachineId();
}
if (machineId != null) {
Long machineId1=machineId;
Machine machine= chromosome.getMachines().stream().filter(t->t.getId()==machineId1).findFirst().orElse(null);
GAScheduleResult machineTask = machine.getLastGene();
if (machineTask != null ) {
int lastEndTime =machineTask.getEndTime();
maxTime = Math.max(maxTime, lastEndTime);
}
}
// 3. 原材料0库存计算齐套最大时间(只计算原材料,不计算半成品)
int rawMaterialBomTime = calculateRawMaterialBomTimeForFinished(finishedOperation, chromosome);
maxTime = Math.max(maxTime, rawMaterialBomTime);
return maxTime;
}
/**
* 计算成品工序的原材料齐套时间(只计算原材料MP,不计算半成品)
*/
private int calculateRawMaterialBomTimeForFinished(Entry finishedOperation, Chromosome chromosome) {
List<OrderMaterialRequirement> materialReqs = finishedOperation.getMaterialRequirements();
if (materialReqs == null) {
return 0;
}
int maxTime = 0;
for (OrderMaterialRequirement req : materialReqs) {
if ("MP".equals(req.getMaterialTypeName())) { // 只计算原材料
// 计算原材料采购提前期
int purchaseTime = req.getPurchaseTime() != null ? req.getPurchaseTime() : 0;
int checkLeadTime = req.getCheckLeadTime() != null ? req.getCheckLeadTime() : 0;
int totalDays = purchaseTime + checkLeadTime;
int totalSeconds = totalDays * 24 * 3600;
maxTime = Math.max(maxTime, totalSeconds);
}
}
return maxTime;
}
}
...@@ -453,7 +453,7 @@ public class HybridAlgorithm { ...@@ -453,7 +453,7 @@ public class HybridAlgorithm {
FileHelper.writeLogFile("解码---------------"+population.size() ); FileHelper.writeLogFile("解码---------------"+population.size() );
// GeneticDecoder decoder = new GeneticDecoder(_GlobalParam, param.getBaseTime(), machines, orders, materials, machineScheduler,materialRequirementService, sceneId); // GeneticDecoder decoder = new GeneticDecoder(_GlobalParam, param.getBaseTime(), machines, orders, materials, machineScheduler,materialRequirementService, sceneId);
boolean ismore=true; boolean ismore=false;
if(ismore) { if(ismore) {
CompletableFuture.allOf(population.stream() CompletableFuture.allOf(population.stream()
.map(chromosome -> CompletableFuture.runAsync(() -> decode(sharedDecoder, chromosome, param, allOperations, globalOpList), decodeExecutor)) .map(chromosome -> CompletableFuture.runAsync(() -> decode(sharedDecoder, chromosome, param, allOperations, globalOpList), decodeExecutor))
......
...@@ -8,6 +8,7 @@ import com.aps.entity.basic.*; ...@@ -8,6 +8,7 @@ import com.aps.entity.basic.*;
import com.aps.service.plan.MachineSchedulerService; import com.aps.service.plan.MachineSchedulerService;
import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.sun.org.apache.xpath.internal.objects.XBoolean;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
...@@ -42,12 +43,12 @@ public class MachineCalculator { ...@@ -42,12 +43,12 @@ public class MachineCalculator {
* 获取机器下一个可用时间窗口(考虑班次约束) * 获取机器下一个可用时间窗口(考虑班次约束)
*/ */
// 从 proposedStartTime 开始查找机台下一段可排窗口;必要时会把命中的 availability 临时标记为已占用。 // 从 proposedStartTime 开始查找机台下一段可排窗口;必要时会把命中的 availability 临时标记为已占用。
public CopyOnWriteArrayList<ScheduleResultDetail> getNextAvailableTime(Machine machine, int proposedStartTime, public CopyOnWriteArrayList<ScheduleResultDetail> getNextAvailableTime(Machine machine,Entry operation, int proposedStartTime,
int prevtime, int processingTime, int prevtime, int processingTime,
CopyOnWriteArrayList<GAScheduleResult> existingTasks, CopyOnWriteArrayList<GAScheduleResult> existingTasks,
boolean isInterrupt, boolean istask, boolean isInterrupt, boolean istask,
double oneTime,double quantity, double oneTime,double quantity,
boolean islockMachineTime) { boolean islockMachineTime,boolean isJit) {
LocalDateTime startTime = baseTime.plus(proposedStartTime, ChronoUnit.SECONDS); LocalDateTime startTime = baseTime.plus(proposedStartTime, ChronoUnit.SECONDS);
String prevtimestr = ""; String prevtimestr = "";
...@@ -57,8 +58,8 @@ public class MachineCalculator { ...@@ -57,8 +58,8 @@ public class MachineCalculator {
} }
// 查找合适的班次窗口 // 查找合适的班次窗口
return findEarliestStart(machine, processingTime, startTime, prevtimestr, return findEarliestStart(machine, operation,processingTime, startTime, prevtimestr,
existingTasks, oneTime, quantity, istask, islockMachineTime,isInterrupt); existingTasks, oneTime, quantity, istask, islockMachineTime,isInterrupt,isJit);
} }
public CopyOnWriteArrayList<TimeSegment> getMachineAvailableTime(Machine machine, int proposedStartTime, public CopyOnWriteArrayList<TimeSegment> getMachineAvailableTime(Machine machine, int proposedStartTime,
...@@ -73,9 +74,9 @@ public class MachineCalculator { ...@@ -73,9 +74,9 @@ public class MachineCalculator {
// 基于已选可用时段组装排程结果,并把换型段/加工段对应的 availability 临时占用。 // 基于已选可用时段组装排程结果,并把换型段/加工段对应的 availability 临时占用。
public Map<Integer,Object> CreateScheduleResult( public Map<Integer,Object> CreateScheduleResult(
Machine machine, int processingTime, int proposedStartTime,CopyOnWriteArrayList<TimeSegment> timeSegments, Machine machine,Entry operation, int processingTime, int proposedStartTime,CopyOnWriteArrayList<TimeSegment> timeSegments,
double oneTime,double quantity double oneTime,double quantity
,boolean isInterrupt,int changeOvertTime,boolean changeOverInWeek,int setupStartTime) { ,boolean isInterrupt,int changeOvertTime,boolean changeOverInWeek,int setupStartTime,boolean isJit) {
LocalDateTime startTime = baseTime.plus(proposedStartTime, ChronoUnit.SECONDS); LocalDateTime startTime = baseTime.plus(proposedStartTime, ChronoUnit.SECONDS);
...@@ -165,8 +166,8 @@ public class MachineCalculator { ...@@ -165,8 +166,8 @@ public class MachineCalculator {
} }
times= CaldScheduleResult( times= CaldScheduleResult(
machine, processingTime, startTime, machine, operation, processingTime, startTime,
timeSegments, oneTime, quantity timeSegments, oneTime, quantity,isJit
); );
}else { }else {
...@@ -236,8 +237,8 @@ public class MachineCalculator { ...@@ -236,8 +237,8 @@ public class MachineCalculator {
.filter(t->t.getStart().compareTo(startCandidate)>=0) .filter(t->t.getStart().compareTo(startCandidate)>=0)
.collect(Collectors.toCollection(CopyOnWriteArrayList::new)); .collect(Collectors.toCollection(CopyOnWriteArrayList::new));
times= CaldScheduleResult( times= CaldScheduleResult(
machine, processingTime, startTime, machine, operation, processingTime, startTime,
timeSegments1, oneTime, quantity); timeSegments1, oneTime, quantity,isJit);
} else { } else {
double e= (double)processingTime/slot.getEfficiency(); double e= (double)processingTime/slot.getEfficiency();
...@@ -271,9 +272,9 @@ public class MachineCalculator { ...@@ -271,9 +272,9 @@ public class MachineCalculator {
// 查找最早可用开始时间 // 查找最早可用开始时间
private CopyOnWriteArrayList<ScheduleResultDetail> findEarliestStart( private CopyOnWriteArrayList<ScheduleResultDetail> findEarliestStart(
Machine machine, int processingTime, LocalDateTime currentTime, Machine machine,Entry operation, int processingTime, LocalDateTime currentTime,
String prevtime, CopyOnWriteArrayList<GAScheduleResult> existingTasks,double oneTime,double quantity, boolean checkprevtime, boolean islockMachineTime String prevtime, CopyOnWriteArrayList<GAScheduleResult> existingTasks,double oneTime,double quantity, boolean checkprevtime, boolean islockMachineTime
,boolean isInterrupt) { ,boolean isInterrupt,boolean isJit) {
CopyOnWriteArrayList<GAScheduleResult> machineTasks=null; CopyOnWriteArrayList<GAScheduleResult> machineTasks=null;
if(existingTasks!=null&&existingTasks.size()>0) { if(existingTasks!=null&&existingTasks.size()>0) {
machineTasks = existingTasks.stream() machineTasks = existingTasks.stream()
...@@ -284,16 +285,29 @@ public class MachineCalculator { ...@@ -284,16 +285,29 @@ public class MachineCalculator {
CopyOnWriteArrayList<ScheduleResultDetail> times = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<ScheduleResultDetail> times = new CopyOnWriteArrayList<>();
TimeSegment slot = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime); TimeSegment slot = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime,isJit);
if (slot == null) return times; if (slot == null) return times;
LocalDateTime startCandidate=null;
LocalDateTime endCandidate1= null;
LocalDateTime prevTimeDateTime = StringUtils.isEmpty(prevtime) ? null : LocalDateTime.parse(prevtime); LocalDateTime prevTimeDateTime = StringUtils.isEmpty(prevtime) ? null : LocalDateTime.parse(prevtime);
LocalDateTime startCandidate = slot.getStart().isAfter(prevTimeDateTime != null ? prevTimeDateTime : currentTime)
? slot.getStart() if(isJit)
: (prevTimeDateTime != null ? prevTimeDateTime : currentTime); {
startCandidate= slot.getStart();
endCandidate1 = slot.getEnd().isAfter(prevTimeDateTime != null ? prevTimeDateTime : currentTime)
? (prevTimeDateTime != null ? prevTimeDateTime : currentTime)
: slot.getEnd();
}else {
startCandidate = slot.getStart().isAfter(prevTimeDateTime != null ? prevTimeDateTime : currentTime)
? slot.getStart()
: (prevTimeDateTime != null ? prevTimeDateTime : currentTime);
endCandidate1 = slot.getEnd();
}
// LocalDateTime endCandidate = startCandidate.plusSeconds(processingTime); // LocalDateTime endCandidate = startCandidate.plusSeconds(processingTime);
long availableSeconds = ChronoUnit.SECONDS.between(startCandidate, slot.getEnd()); long availableSeconds = ChronoUnit.SECONDS.between(startCandidate, endCandidate1);
//实际可用时间 //实际可用时间
long availableSeconds_e =Math.round(availableSeconds*slot.getEfficiency()) ; long availableSeconds_e =Math.round(availableSeconds*slot.getEfficiency()) ;
...@@ -301,7 +315,7 @@ public class MachineCalculator { ...@@ -301,7 +315,7 @@ public class MachineCalculator {
// if (endCandidate.isAfter(slot.getEnd())) { // if (endCandidate.isAfter(slot.getEnd())) {
if (processingTime>availableSeconds_e) { if (processingTime>availableSeconds_e) {
return CaldEarliestStart(machine, processingTime, currentTime, prevtime, machineTasks,oneTime,quantity, checkprevtime,islockMachineTime,isInterrupt); return CaldEarliestStart(machine, operation, processingTime, currentTime, prevtime, machineTasks,oneTime,quantity, checkprevtime,islockMachineTime,isInterrupt,isJit);
} else { } else {
...@@ -340,7 +354,7 @@ public class MachineCalculator { ...@@ -340,7 +354,7 @@ public class MachineCalculator {
List<ScheduleResultDetail> oldTimes = new ArrayList<>(); List<ScheduleResultDetail> oldTimes = new ArrayList<>();
while (remainingTime > 0) { while (remainingTime > 0) {
TimeSegment shift = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime); TimeSegment shift = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime,false);
if (shift == null) break; if (shift == null) break;
LocalDateTime shiftStart = shift.getStart(); LocalDateTime shiftStart = shift.getStart();
...@@ -403,19 +417,19 @@ public class MachineCalculator { ...@@ -403,19 +417,19 @@ public class MachineCalculator {
} }
private CopyOnWriteArrayList<ScheduleResultDetail> CaldEarliestStart( private CopyOnWriteArrayList<ScheduleResultDetail> CaldEarliestStart(
Machine machine, int processingTime, LocalDateTime currentTime, Machine machine,Entry operation, int processingTime, LocalDateTime currentTime,
String prevtime, CopyOnWriteArrayList<GAScheduleResult> machineTasks,double oneTime,double quantity, boolean checkprevtime, boolean islockMachineTime String prevtime, CopyOnWriteArrayList<GAScheduleResult> machineTasks,double oneTime,double quantity, boolean checkprevtime, boolean islockMachineTime
,boolean isInterrupt) { ,boolean isInterrupt,boolean isJit) {
int remainingTime = processingTime; int remainingTime = processingTime;
LocalDateTime st = StringUtils.isEmpty(prevtime) ? currentTime : LocalDateTime.parse(prevtime); LocalDateTime st = StringUtils.isEmpty(prevtime) ? currentTime : LocalDateTime.parse(prevtime);
LocalDateTime prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0); LocalDateTime prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
CopyOnWriteArrayList<ScheduleResultDetail> times = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<ScheduleResultDetail> times = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList<TimeSegment> timeSegments= findAvailableSegments(machine, currentTime, machineTasks, remainingTime, isInterrupt); CopyOnWriteArrayList<TimeSegment> timeSegments= findAvailableSegments(machine, currentTime, machineTasks, remainingTime, isInterrupt,isJit);
times= CaldScheduleResult( times= CaldScheduleResult(
machine, processingTime, currentTime, machine, operation, processingTime, currentTime,
timeSegments, oneTime, quantity timeSegments, oneTime, quantity,isJit
); );
// int estimateIndex= (int) Math.ceil(remainingTime / (double) ONE_DAY_MINUTES); // int estimateIndex= (int) Math.ceil(remainingTime / (double) ONE_DAY_MINUTES);
...@@ -456,18 +470,26 @@ public class MachineCalculator { ...@@ -456,18 +470,26 @@ public class MachineCalculator {
} }
private CopyOnWriteArrayList<ScheduleResultDetail> CaldScheduleResult( private CopyOnWriteArrayList<ScheduleResultDetail> CaldScheduleResult(
Machine machine, int processingTime, LocalDateTime currentTime, Machine machine,Entry operation, int processingTime, LocalDateTime currentTime,
CopyOnWriteArrayList<TimeSegment> timeSegments,double oneTime,double quantity CopyOnWriteArrayList<TimeSegment> timeSegments,double oneTime,double quantity
) { ,boolean isJit) {
int remainingTime = processingTime; int remainingTime = processingTime;
CopyOnWriteArrayList<ScheduleResultDetail> times = new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<ScheduleResultDetail> times = new CopyOnWriteArrayList<>();
int estimateIndex= (int) Math.ceil(remainingTime / (double) ONE_DAY_MINUTES); int estimateIndex= (int) Math.ceil(remainingTime / (double) ONE_DAY_MINUTES);
CopyOnWriteArrayList<TimeSegment> timeSegments1=null; CopyOnWriteArrayList<TimeSegment> timeSegments1=null;
if(estimateIndex>10) if(estimateIndex>10)
{ {
timeSegments1= getEnoughSegmentsByEstimateIndex(timeSegments,currentTime,remainingTime); timeSegments1= getEnoughSegmentsByEstimateIndex(timeSegments,currentTime,remainingTime,isJit);
if(timeSegments1.size()==2)
{
timeSegments1= getEnoughSegmentsByEstimateIndex(timeSegments,currentTime,remainingTime,isJit);
int i=0;
}
} }
if(timeSegments1==null) { if(timeSegments1==null) {
...@@ -476,7 +498,7 @@ public class MachineCalculator { ...@@ -476,7 +498,7 @@ public class MachineCalculator {
while (remainingTime > 0) { while (remainingTime > 0) {
TimeSegment shift = timeSegments.get(i); TimeSegment shift = timeSegments.get(i);
Map<Integer, Object> outMap = CreateScheduleResultDetail(shift, currentTime, remainingTime, oneTime); Map<Integer, Object> outMap = CreateScheduleResultDetail(shift, currentTime, remainingTime, oneTime,isJit);
remainingTime = (int) outMap.get(1); remainingTime = (int) outMap.get(1);
ScheduleResultDetail time = (ScheduleResultDetail) outMap.get(2); ScheduleResultDetail time = (ScheduleResultDetail) outMap.get(2);
times.add(time); times.add(time);
...@@ -494,16 +516,16 @@ public class MachineCalculator { ...@@ -494,16 +516,16 @@ public class MachineCalculator {
machine.getAvailability().sort(Comparator.comparing(TimeSegment::getStart)); machine.getAvailability().sort(Comparator.comparing(TimeSegment::getStart));
} }
}else { }else {
times= CaldScheduleResultDetail(timeSegments1,machine,currentTime,remainingTime,oneTime); times= CaldScheduleResultDetail(timeSegments1,machine, operation,currentTime,remainingTime,oneTime,isJit);
} }
return times; return times;
} }
private CopyOnWriteArrayList<ScheduleResultDetail> CaldScheduleResultDetail(CopyOnWriteArrayList<TimeSegment> timeSegments,Machine machine,LocalDateTime st,int remainingTime,double oneTime) private CopyOnWriteArrayList<ScheduleResultDetail> CaldScheduleResultDetail(CopyOnWriteArrayList<TimeSegment> timeSegments,Machine machine,Entry operation,LocalDateTime st,int remainingTime,double oneTime,boolean isJit)
{ {
int processable1 =(int)calculateTotalAvailableSecond(timeSegments, st); int processable1 =(int)calculateTotalAvailableSecond(timeSegments, st,isJit);
CopyOnWriteArrayList<TimeSegment> usedSegments =new CopyOnWriteArrayList<>(); CopyOnWriteArrayList<TimeSegment> usedSegments =new CopyOnWriteArrayList<>();
...@@ -512,7 +534,7 @@ public class MachineCalculator { ...@@ -512,7 +534,7 @@ public class MachineCalculator {
//第一个数据 //第一个数据
TimeSegment shiftfrist= timeSegments.get(0); TimeSegment shiftfrist= timeSegments.get(0);
Map<Integer, Object> outMap= CreateScheduleResultDetail(shiftfrist,st,remainingTime,oneTime); Map<Integer, Object> outMap= CreateScheduleResultDetail(shiftfrist,st,remainingTime,oneTime,isJit);
remainingTime=(int)outMap.get(1); remainingTime=(int)outMap.get(1);
ScheduleResultDetail time1=(ScheduleResultDetail)outMap.get(2); ScheduleResultDetail time1=(ScheduleResultDetail)outMap.get(2);
times.add(time1); times.add(time1);
...@@ -524,13 +546,16 @@ public class MachineCalculator { ...@@ -524,13 +546,16 @@ public class MachineCalculator {
//中间的数据 //中间的数据
CopyOnWriteArrayList<TimeSegment> timeSegments2= new CopyOnWriteArrayList<>(timeSegments.subList(1,timeSegments.size()-1)); CopyOnWriteArrayList<TimeSegment> timeSegments2= new CopyOnWriteArrayList<>(timeSegments.subList(1,timeSegments.size()-1));
if(timeSegments2==null||timeSegments2.size()==0)
{
int i=0;
}
LocalDateTime effectiveStart=timeSegments2.get(0).getStart(); LocalDateTime effectiveStart=timeSegments2.get(0).getStart();
LocalDateTime effectiveend=timeSegments2.get(timeSegments2.size()-1).getEnd(); LocalDateTime effectiveend=timeSegments2.get(timeSegments2.size()-1).getEnd();
int processable =(int)calculateTotalAvailableSecond(timeSegments2, st); int processable =(int)calculateTotalAvailableSecond(timeSegments2, st,isJit);
ScheduleResultDetail time = new ScheduleResultDetail(); ScheduleResultDetail time = new ScheduleResultDetail();
time.setKey(UUID.randomUUID().toString()); time.setKey(UUID.randomUUID().toString());
time.setStartTime((int) ChronoUnit.SECONDS.between(baseTime, effectiveStart)); time.setStartTime((int) ChronoUnit.SECONDS.between(baseTime, effectiveStart));
...@@ -554,7 +579,7 @@ public class MachineCalculator { ...@@ -554,7 +579,7 @@ public class MachineCalculator {
//最后的时间段 //最后的时间段
TimeSegment shiftlast= timeSegments.get(timeSegments.size()-1); TimeSegment shiftlast= timeSegments.get(timeSegments.size()-1);
Map<Integer, Object> outMaplast= CreateScheduleResultDetail(shiftlast,st,remainingTime,oneTime); Map<Integer, Object> outMaplast= CreateScheduleResultDetail(shiftlast,st,remainingTime,oneTime,isJit);
remainingTime=(int)outMaplast.get(1); remainingTime=(int)outMaplast.get(1);
ScheduleResultDetail timelast=(ScheduleResultDetail)outMaplast.get(2); ScheduleResultDetail timelast=(ScheduleResultDetail)outMaplast.get(2);
times.add(timelast); times.add(timelast);
...@@ -571,15 +596,32 @@ public class MachineCalculator { ...@@ -571,15 +596,32 @@ public class MachineCalculator {
return times; return times;
} }
private Map<Integer, Object> CreateScheduleResultDetail(TimeSegment shift,LocalDateTime st,int remainingTime,double oneTime) private Map<Integer, Object> CreateScheduleResultDetail(TimeSegment shift,LocalDateTime st,int remainingTime,double oneTime,boolean isJit)
{ {
LocalDateTime shiftStart = shift.getStart(); LocalDateTime shiftStart = shift.getStart();
LocalDateTime shiftEnd = shift.getEnd(); LocalDateTime shiftEnd = shift.getEnd();
// 计算有效时间 // 计算有效时间
LocalDateTime effectiveStart = st.isAfter(shiftStart) ? st : shiftStart; LocalDateTime effectiveStart = null;
long availableSeconds = ChronoUnit.SECONDS.between(effectiveStart, shiftEnd);
LocalDateTime effectiveEnd=null;
if(isJit)
{
effectiveStart=shiftStart;
effectiveEnd = st.isBefore(shiftEnd) ? st : shiftEnd;
}else {
// 计算有效时间
effectiveStart = st.isAfter(shiftStart) ? st : shiftStart;
effectiveEnd=shiftEnd;
}
long availableSeconds = ChronoUnit.SECONDS.between(effectiveStart, effectiveEnd);
long availableSeconds_e =Math.round(availableSeconds*shift.getEfficiency()) ; long availableSeconds_e =Math.round(availableSeconds*shift.getEfficiency()) ;
// 处理当前班次 // 处理当前班次
...@@ -610,7 +652,6 @@ public class MachineCalculator { ...@@ -610,7 +652,6 @@ public class MachineCalculator {
return outMap; return outMap;
} }
/** /**
* 查找满足时长要求的无冲突可用时段 * 查找满足时长要求的无冲突可用时段
* @param machine 设备 * @param machine 设备
...@@ -626,6 +667,25 @@ public class MachineCalculator { ...@@ -626,6 +667,25 @@ public class MachineCalculator {
CopyOnWriteArrayList<GAScheduleResult> machineTasks, CopyOnWriteArrayList<GAScheduleResult> machineTasks,
double requiredMinutes, double requiredMinutes,
boolean requireContinuous) { boolean requireContinuous) {
return findAvailableSegments(machine, start, machineTasks, requiredMinutes, requireContinuous,false);
}
/**
* 查找满足时长要求的无冲突可用时段
* @param machine 设备
* @param start 起始时间
* @param machineTasks 设备已有任务
* @param requiredMinutes 工单所需总时长(分钟)
* @param requireContinuous 是否不可中断(true=不可中断)
* @return 满足条件的可用片段列表
*/
private CopyOnWriteArrayList<TimeSegment> findAvailableSegments(
Machine machine,
LocalDateTime start,
CopyOnWriteArrayList<GAScheduleResult> machineTasks,
double requiredMinutes,
boolean requireContinuous,boolean isJit) {
List<TimeSegment> availableSegments = new ArrayList<>(); List<TimeSegment> availableSegments = new ArrayList<>();
LocalDateTime current = start; LocalDateTime current = start;
...@@ -638,11 +698,17 @@ public class MachineCalculator { ...@@ -638,11 +698,17 @@ public class MachineCalculator {
// 替换while(true),增加明确退出条件(原逻辑保留,可根据业务补充退出判断) // 替换while(true),增加明确退出条件(原逻辑保留,可根据业务补充退出判断)
while (true) { while (true) {
// 统一过滤逻辑:消除重复的FindAll代码 // 统一过滤逻辑:消除重复的FindAll代码
CopyOnWriteArrayList<TimeSegment> useSegments = filterValidUseSegments(machine.getAvailability(), current, requireContinuous, requiredMinutes); CopyOnWriteArrayList<TimeSegment> useSegments = filterValidUseSegments(machine.getAvailability(), current, requireContinuous, requiredMinutes,isJit);
if (useSegments != null && !useSegments.isEmpty()) { if (useSegments != null && !useSegments.isEmpty()) {
// 计算可用时间总和(保留原有逻辑) // 计算可用时间总和(保留原有逻辑)
double totalUseTime = calculateTotalAvailableSecond(useSegments, current); double totalUseTime = calculateTotalAvailableSecond(useSegments, current,isJit);
//倒排,可用时间不够,应该重新正排
if(totalUseTime < requiredMinutes&&isJit)
{
return null;
}
// 不足所需时长时,自动生成未来片段并补充 // 不足所需时长时,自动生成未来片段并补充
while (totalUseTime < requiredMinutes) { while (totalUseTime < requiredMinutes) {
...@@ -663,7 +729,7 @@ public class MachineCalculator { ...@@ -663,7 +729,7 @@ public class MachineCalculator {
// 重新过滤可用片段 // 重新过滤可用片段
useSegments = filterValidUseSegments(machine.getAvailability(), current, requireContinuous, requiredMinutes); useSegments = filterValidUseSegments(machine.getAvailability(), current, requireContinuous, requiredMinutes);
// 重新计算总可用时间 // 重新计算总可用时间
totalUseTime = calculateTotalAvailableSecond(useSegments, current); totalUseTime = calculateTotalAvailableSecond(useSegments, current,isJit);
} }
// 若总可用时间满足要求,处理冲突 // 若总可用时间满足要求,处理冲突
...@@ -674,19 +740,157 @@ public class MachineCalculator { ...@@ -674,19 +740,157 @@ public class MachineCalculator {
//获取维修和当前任务 //获取维修和当前任务
CopyOnWriteArrayList<TimeSegment> conflictSegments = getConflictIntervals(machine, machineTasks, firstSegmentStart, lastSegmentEnd); CopyOnWriteArrayList<TimeSegment> conflictSegments = getConflictIntervals(machine, machineTasks, firstSegmentStart, lastSegmentEnd);
if (conflictSegments == null || conflictSegments.isEmpty()) {
// 无冲突:直接返回可用片段(保留原有逻辑)
if(isJit)
{
if (useSegments.get(0).getEnd().compareTo(current) > 0) {
spiltMachineAvailable(machine, useSegments.get(0), current,isJit);
useSegments.get(0).setEnd(current);
}
}
else {
if (useSegments.get(0).getStart().compareTo(current) < 0) {
spiltMachineAvailable(machine, useSegments.get(0), current,isJit);
useSegments.get(0).setStart(current);
}
}
return useSegments;
} else {
// 有冲突:可选返回冲突前足够片段或跳过冲突
CopyOnWriteArrayList<TimeSegment> resultSegments = handleConflictsWithSumLogic(useSegments, conflictSegments, current, requiredMinutes,isJit);
if (resultSegments != null && !resultSegments.isEmpty()) {
if(isJit)
{
if (resultSegments.get(0).getEnd().compareTo(current) > 0) {
spiltMachineAvailable(machine, resultSegments.get(0), current, isJit);
resultSegments.get(0).setEnd(current);
}
}
else {
if (resultSegments.get(0).getStart().compareTo(current) < 0) {
spiltMachineAvailable(machine, resultSegments.get(0), current, isJit);
resultSegments.get(0).setStart(current);
}
}
return resultSegments;
} else {
// 冲突前片段不足,重置current为最后一个冲突的结束时间,重新查找
if(isJit) {
LocalDateTime lastConflictEnd = conflictSegments.stream()
.min(Comparator.comparing(TimeSegment::getEnd, (a, b) -> a.compareTo(b)))
.map(TimeSegment::getStart)
.orElse(current);
current = lastConflictEnd;
}else {
LocalDateTime lastConflictEnd = conflictSegments.stream()
.max(Comparator.comparing(TimeSegment::getEnd, (a, b) -> a.compareTo(b)))
.map(TimeSegment::getEnd)
.orElse(current);
current = lastConflictEnd;
}
}
}
}
} else {
if(isJit)
{
return null;
}
// 无可用片段时,生成未来1天的片段
LocalDateTime lastSegmentEnd = machine.getAvailability().stream()
.max(Comparator.comparing(TimeSegment::getEnd, (a, b) -> a.compareTo(b)))
.map(TimeSegment::getEnd)
.orElse(current);
CopyOnWriteArrayList<TimeSegment> newSegments = machineScheduler.generateTimeSegment(machine, lastSegmentEnd.plusDays(1),0);
addSegmentsWithDeduplication(machine, newSegments);
}
}
}
/**
* 查找满足时长要求的无冲突可用时段
* @param machine 设备
* @param start 起始时间
* @param machineTasks 设备已有任务
* @param requiredMinutes 工单所需总时长(分钟)
* @param requireContinuous 是否不可中断(true=不可中断)
* @return 满足条件的可用片段列表
*/
private CopyOnWriteArrayList<TimeSegment> findAvailableSegments(
Machine machine,
LocalDateTime start,
LocalDateTime end,
CopyOnWriteArrayList<GAScheduleResult> machineTasks,
double requiredMinutes,
boolean requireContinuous,boolean isJit) {
List<TimeSegment> availableSegments = new ArrayList<>();
LocalDateTime current = start;
// 预先排序设备可用片段,避免后续遍历混乱
// List<TimeSegment> allUseTimeSegment = machine.getAvailability().stream()
// .filter(slot -> !slot.isUsed() && slot.getType() != SegmentType.MAINTENANCE)
// .sorted(Comparator.comparing(TimeSegment::getStart, (a, b) -> a.compareTo(b)))
// .collect(Collectors.toList());
// 替换while(true),增加明确退出条件(原逻辑保留,可根据业务补充退出判断)
while (true) {
// 统一过滤逻辑:消除重复的FindAll代码
CopyOnWriteArrayList<TimeSegment> useSegments = filterValidUseSegments(machine.getAvailability(), current, requireContinuous, requiredMinutes);
if (useSegments != null && !useSegments.isEmpty()) {
// 计算可用时间总和(保留原有逻辑)
double totalUseTime = calculateTotalAvailableSecond(useSegments, current,isJit);
// 不足所需时长时,自动生成未来片段并补充
while (totalUseTime < requiredMinutes) {
// 生成未来1天的片段(基于当前可用片段的最大结束时间)
LocalDateTime lastSegmentEnd = machine.getAvailability().stream()
.max(Comparator.comparing(TimeSegment::getEnd, (a, b) -> a.compareTo(b)))
.map(TimeSegment::getEnd)
.orElse(current);
int days=((int) ((requiredMinutes-totalUseTime)/24/3600));
if(days<=200)
{
days=0;
}
CopyOnWriteArrayList<TimeSegment> newSegments = machineScheduler.generateTimeSegment(machine, lastSegmentEnd.plusDays(1),days);
addSegmentsWithDeduplication(machine, newSegments);
// 重新过滤可用片段
useSegments = filterValidUseSegments(machine.getAvailability(), current, requireContinuous, requiredMinutes);
// 重新计算总可用时间
totalUseTime = calculateTotalAvailableSecond(useSegments, current,isJit);
}
// 若总可用时间满足要求,处理冲突
if (totalUseTime >= requiredMinutes) {
// 获取当前可用片段范围内的冲突
LocalDateTime firstSegmentStart = useSegments.get(0).getStart().compareTo(current) < 0?current:useSegments.get(0).getStart();
LocalDateTime lastSegmentEnd = useSegments.get(useSegments.size() - 1).getEnd();
//获取维修和当前任务
CopyOnWriteArrayList<TimeSegment> conflictSegments = getConflictIntervals(machine, machineTasks, firstSegmentStart, lastSegmentEnd);
if (conflictSegments == null || conflictSegments.isEmpty()) { if (conflictSegments == null || conflictSegments.isEmpty()) {
// 无冲突:直接返回可用片段(保留原有逻辑) // 无冲突:直接返回可用片段(保留原有逻辑)
if (useSegments.get(0).getStart().compareTo(current) < 0) { if (useSegments.get(0).getStart().compareTo(current) < 0) {
spiltMachineAvailable(machine, useSegments.get(0), current); spiltMachineAvailable(machine, useSegments.get(0), current,isJit);
useSegments.get(0).setStart(current); useSegments.get(0).setStart(current);
} }
return useSegments; return useSegments;
} else { } else {
// 有冲突:可选返回冲突前足够片段或跳过冲突 // 有冲突:可选返回冲突前足够片段或跳过冲突
CopyOnWriteArrayList<TimeSegment> resultSegments = handleConflictsWithSumLogic(useSegments, conflictSegments, current, requiredMinutes); CopyOnWriteArrayList<TimeSegment> resultSegments = handleConflictsWithSumLogic(useSegments, conflictSegments, current, requiredMinutes,isJit);
if (resultSegments != null && !resultSegments.isEmpty()) { if (resultSegments != null && !resultSegments.isEmpty()) {
if (resultSegments.get(0).getStart().compareTo(current) < 0) { if (resultSegments.get(0).getStart().compareTo(current) < 0) {
spiltMachineAvailable(machine, resultSegments.get(0), current); spiltMachineAvailable(machine, resultSegments.get(0), current,isJit);
resultSegments.get(0).setStart(current); resultSegments.get(0).setStart(current);
} }
return resultSegments; return resultSegments;
...@@ -712,6 +916,8 @@ public class MachineCalculator { ...@@ -712,6 +916,8 @@ public class MachineCalculator {
} }
} }
private static final int ONE_DAY_MINUTES = 24 * 60*60; // 固定锚点:一天1440分钟,永不改变 private static final int ONE_DAY_MINUTES = 24 * 60*60; // 固定锚点:一天1440分钟,永不改变
/** /**
...@@ -725,7 +931,7 @@ public class MachineCalculator { ...@@ -725,7 +931,7 @@ public class MachineCalculator {
public CopyOnWriteArrayList<TimeSegment> getEnoughSegmentsByEstimateIndex( public CopyOnWriteArrayList<TimeSegment> getEnoughSegmentsByEstimateIndex(
CopyOnWriteArrayList<TimeSegment> availableSegments, CopyOnWriteArrayList<TimeSegment> availableSegments,
LocalDateTime currentTime, LocalDateTime currentTime,
int requiredMinutes) { int requiredMinutes,boolean isJit) {
// 基础校验 // 基础校验
if (availableSegments == null || availableSegments.isEmpty()) { if (availableSegments == null || availableSegments.isEmpty()) {
throw new IllegalArgumentException("可用时段列表不能为空"); throw new IllegalArgumentException("可用时段列表不能为空");
...@@ -749,13 +955,13 @@ public class MachineCalculator { ...@@ -749,13 +955,13 @@ public class MachineCalculator {
int targetIndex = estimateIndex; int targetIndex = estimateIndex;
while (true) { while (true) {
// 计算当前索引的总有效时长 // 计算当前索引的总有效时长
double currentTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, targetIndex); double currentTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, targetIndex, isJit);
if (currentTotal >= requiredMinutes) { if (currentTotal >= requiredMinutes) {
// 总时长满足,尝试往前减索引,找「最小满足索引」(减少后续裁剪的工作量) // 总时长满足,尝试往前减索引,找「最小满足索引」(减少后续裁剪的工作量)
if (targetIndex > 0) { if (targetIndex > 0) {
double prevTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, targetIndex - 1); double prevTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, targetIndex - 1, isJit);
if (prevTotal >= requiredMinutes) { if (prevTotal >= requiredMinutes) {
targetIndex--; targetIndex--;
continue; continue;
...@@ -770,11 +976,11 @@ public class MachineCalculator { ...@@ -770,11 +976,11 @@ public class MachineCalculator {
targetIndex +=Math.max(syday, 5); targetIndex +=Math.max(syday, 5);
if (targetIndex >= totalSegmentCount) { if (targetIndex >= totalSegmentCount) {
// 所有片段总时长不足,抛出异常 // 所有片段总时长不足,抛出异常
double allTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, totalSegmentCount - 1); double allTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, totalSegmentCount - 1, isJit);
if(allTotal>requiredMinutes) if(allTotal>requiredMinutes)
{ {
double prevTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, targetIndex - 1); double prevTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, targetIndex - 1, isJit);
if (prevTotal >= requiredMinutes) { if (prevTotal >= requiredMinutes) {
targetIndex--; targetIndex--;
continue; continue;
...@@ -799,38 +1005,68 @@ public class MachineCalculator { ...@@ -799,38 +1005,68 @@ public class MachineCalculator {
/** /**
* 计算从0到指定索引的所有时段的总有效分钟数 * 计算从0到指定索引的所有时段的总有效分钟数
*/ */
private double calculateTotalMinutesByIndex(CopyOnWriteArrayList<TimeSegment> segments, LocalDateTime currentTime, int endIndex) { private double calculateTotalMinutesByIndex(CopyOnWriteArrayList<TimeSegment> segments, LocalDateTime currentTime, int endIndex,boolean isJit) {
return calculateTotalAvailableSecond(new CopyOnWriteArrayList(segments.subList(0, endIndex)), currentTime); return calculateTotalAvailableSecond(new CopyOnWriteArrayList(segments.subList(0, endIndex)), currentTime, isJit);
} }
/**
* 统一过滤有效可用片段
*/
private CopyOnWriteArrayList<TimeSegment> filterValidUseSegments(CopyOnWriteArrayList<TimeSegment> allSegments,
LocalDateTime currentTime,
boolean requireContinuous,
double requiredMinutes) {
return filterValidUseSegments( allSegments,
currentTime,
requireContinuous,
requiredMinutes,false);
}
/** /**
* 统一过滤有效可用片段 * 统一过滤有效可用片段
*/ */
private CopyOnWriteArrayList<TimeSegment> filterValidUseSegments(CopyOnWriteArrayList<TimeSegment> allSegments, private CopyOnWriteArrayList<TimeSegment> filterValidUseSegments(CopyOnWriteArrayList<TimeSegment> allSegments,
LocalDateTime currentTime, LocalDateTime currentTime,
boolean requireContinuous, boolean requireContinuous,
double requiredMinutes) { double requiredMinutes,boolean isJit) {
// 空判断 // 空判断
if (allSegments == null || allSegments.isEmpty()) { if (allSegments == null || allSegments.isEmpty()) {
return new CopyOnWriteArrayList<>(); return new CopyOnWriteArrayList<>();
} }
// 统一过滤条件 // 统一过滤条件
CopyOnWriteArrayList<TimeSegment> baseValidSegments = allSegments.stream() CopyOnWriteArrayList<TimeSegment> baseValidSegments=new CopyOnWriteArrayList<>();
.filter(slot -> !slot.isUsed() // 对应 !slot.IsUsed
&& slot.getType() != SegmentType.MAINTENANCE // 对应 slot.Type != SegmentType.Maintenance
&& slot.getEnd().compareTo(currentTime) > 0) // 对应 slot.End > currentTime
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
if(isJit)
{
baseValidSegments = allSegments.stream()
.filter(slot -> !slot.isUsed() // 对应 !slot.IsUsed
&& slot.getType() != SegmentType.MAINTENANCE // 对应 slot.Type != SegmentType.Maintenance
&& slot.getStart().compareTo(currentTime) < 0)
.sorted(Comparator.comparing(TimeSegment::getEnd).reversed()) // 对应 slot.End > currentTime
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
}else {
baseValidSegments = allSegments.stream()
.filter(slot -> !slot.isUsed() // 对应 !slot.IsUsed
&& slot.getType() != SegmentType.MAINTENANCE // 对应 slot.Type != SegmentType.Maintenance
&& slot.getEnd().compareTo(currentTime) > 0) // 对应 slot.End > currentTime
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
}
// 无需不可中断或所需时长无效时,直接返回基础有效片段 // 无需不可中断或所需时长无效时,直接返回基础有效片段
if (!requireContinuous || requiredMinutes <= 0) { if (!requireContinuous || requiredMinutes <= 0) {
return baseValidSegments; return baseValidSegments;
} }
if(isJit)
{
return filterContinuousCompliantSegmentsJit(baseValidSegments, currentTime, requiredMinutes);
}
// 要求不可中断时,过滤符合连续时长要求的片段 // 要求不可中断时,过滤符合连续时长要求的片段
return filterContinuousCompliantSegments(baseValidSegments, currentTime, requiredMinutes); return filterContinuousCompliantSegments(baseValidSegments, currentTime, requiredMinutes);
} }
...@@ -841,19 +1077,67 @@ public class MachineCalculator { ...@@ -841,19 +1077,67 @@ public class MachineCalculator {
* @param currentTime 当前时间 * @param currentTime 当前时间
* @return 总可用时长(分钟) * @return 总可用时长(分钟)
*/ */
private double calculateTotalAvailableSecond(CopyOnWriteArrayList<TimeSegment> useSegments, LocalDateTime currentTime) { private double calculateTotalAvailableSecond(CopyOnWriteArrayList<TimeSegment> useSegments, LocalDateTime currentTime,boolean isJit) {
// 空判断: // 空判断:
if (useSegments == null || useSegments.size()==0) { if (useSegments == null || useSegments.size()==0) {
return 0.0; return 0.0;
} }
if(isJit)
{
return useSegments.stream()
.mapToDouble(segment -> {
// 取片段起始时间和当前时间的最大值,t.End > currentTime ? t.End : currentTime
LocalDateTime effectiveend = segment.getEnd().compareTo(currentTime) > 0 ? currentTime :segment.getEnd() ;
// 计算单个片段的有效时长(分钟),并乘以效率值,对应C#的 (t.End - effectiveStart).TotalMinutes * t.Efficiency
double segmentSeconds =ChronoUnit.SECONDS.between(segment.getStart(), effectiveend);
return segmentSeconds * segment.getEfficiency();
})
.sum(); // 累加所有片段的有效时长
}else {
return useSegments.stream()
.mapToDouble(segment -> {
// 取片段起始时间和当前时间的最大值,t.Start < currentTime ? currentTime : t.Start
LocalDateTime effectiveStart = segment.getStart().compareTo(currentTime) < 0 ? currentTime : segment.getStart();
// 计算单个片段的有效时长(分钟),并乘以效率值,对应C#的 (t.End - effectiveStart).TotalMinutes * t.Efficiency
double segmentSeconds =ChronoUnit.SECONDS.between(effectiveStart, segment.getEnd());
return segmentSeconds * segment.getEfficiency();
})
.sum(); // 累加所有片段的有效时长
}
}
/**
* 计算可用片段在指定时间范围内的总有效时长(考虑效率系数)
* @param useSegments 可用片段列表
* @param startTime 起始时间(包含)
* @param endTime 结束时间(包含,可为空表示无上限)
* @return 总可用时长(分钟)
*/
private double calculateTotalAvailableSecond(CopyOnWriteArrayList<TimeSegment> useSegments, LocalDateTime startTime, LocalDateTime endTime) {
// 空判断:
if (useSegments == null || useSegments.size()==0) {
return 0.0;
}
return useSegments.stream() return useSegments.stream()
.mapToDouble(segment -> { .mapToDouble(segment -> {
// 取片段起始时间和当前时间的最大值,t.Start < currentTime ? currentTime : t.Start // 计算有效的起始时间:取 segment.start 和 startTime 的最大值
LocalDateTime effectiveStart = segment.getStart().compareTo(currentTime) < 0 ? currentTime : segment.getStart(); LocalDateTime effectiveStart = segment.getStart().compareTo(startTime) < 0 ? startTime : segment.getStart();
// 计算单个片段的有效时长(分钟),并乘以效率值,对应C#的 (t.End - effectiveStart).TotalMinutes * t.Efficiency
double segmentSeconds =ChronoUnit.SECONDS.between(effectiveStart, segment.getEnd()); // 计算有效的结束时间:取 segment.end 和 endTime 的最小值(如果 endTime 为空,就用 segment.end)
LocalDateTime effectiveEnd = endTime != null ?
(segment.getEnd().compareTo(endTime) > 0 ? endTime : segment.getEnd())
: segment.getEnd();
// 如果有效的起始时间 >= 有效的结束时间,这个片段在范围内没有可用时间
if (effectiveStart.compareTo(effectiveEnd) >= 0) {
return 0.0;
}
// 计算单个片段的有效时长(秒),并乘以效率值
double segmentSeconds = ChronoUnit.SECONDS.between(effectiveStart, effectiveEnd);
return segmentSeconds * segment.getEfficiency(); return segmentSeconds * segment.getEfficiency();
}) })
.sum(); // 累加所有片段的有效时长 .sum(); // 累加所有片段的有效时长
...@@ -922,7 +1206,75 @@ public class MachineCalculator { ...@@ -922,7 +1206,75 @@ public class MachineCalculator {
} }
// 去重并排序(避免重复片段) // 去重并排序(避免重复片段)
return MergeSegments(continuousCompliantSegments); return MergeSegments(continuousCompliantSegments,false);
}
/**
* 辅助方法:过滤出满足连续时长要求的片段(仅不可中断配置生效时调用)
* 前置剔除零散无效片段,减少后续逻辑处理量
*/
private CopyOnWriteArrayList<TimeSegment> filterContinuousCompliantSegmentsJit(
CopyOnWriteArrayList<TimeSegment> baseValidSegments,
LocalDateTime currentTime,
double requiredContinuous) {
// 空判断
if (baseValidSegments == null || baseValidSegments.isEmpty()) {
return new CopyOnWriteArrayList<>();
}
CopyOnWriteArrayList<TimeSegment> continuousCompliantSegments = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList<TimeSegment> tempContinuousGroup = new CopyOnWriteArrayList<>();
double tempContinuous = 0.0;
LocalDateTime lastSegmentStart = currentTime;
// baseValidSegments.sort(Comparator.comparing(TimeSegment::getEnd).reversed());
// 遍历基础有效片段,筛选出可拼接成连续时长的片段组
for (TimeSegment segment : baseValidSegments) {
// 确定有效起始时间:
LocalDateTime effectiveEnd = segment.getEnd().compareTo(currentTime) > 0 ?currentTime : segment.getEnd();
if (effectiveEnd.compareTo(segment.getStart()) <= 0) {
// 该片段无有效时长,跳过,结束时间跑到了开始时间的前面,所以没有可用时长
continue;
}
// 判断是否与临时连续组无缝衔接
if (!tempContinuousGroup.isEmpty() && effectiveEnd.compareTo(lastSegmentStart) != 0) {
// 不衔接:先判断临时组是否满足连续时长,满足则加入结果
if (tempContinuous >= requiredContinuous) {
continuousCompliantSegments.addAll(tempContinuousGroup);
}
// 重置临时组
tempContinuousGroup.clear();
tempContinuous = 0.0;
}
// 加入临时连续组,累加时长
tempContinuousGroup.add(segment);
// 计算当前片段有效时长(分钟)并累加
double segmentEffective = ChronoUnit.SECONDS.between(segment.getStart(),effectiveEnd ) * segment.getEfficiency();
tempContinuous += segmentEffective;
// 更新最后一个片段开始时间(-1秒)
lastSegmentStart = segment.getStart().plusSeconds(-1);
// 临时组满足连续时长,直接加入结果(提前终止当前组遍历)
if (tempContinuous >= requiredContinuous) {
continuousCompliantSegments.addAll(tempContinuousGroup);
// 可选择是否继续遍历(此处继续,获取所有符合条件的片段;也可break直接返回第一个组)
tempContinuousGroup.clear();
tempContinuous = 0.0;
}
}
// 遍历结束后,检查最后一个临时组是否满足连续时长
if (tempContinuous >= requiredContinuous) {
continuousCompliantSegments.addAll(tempContinuousGroup);
}
// 去重并排序(避免重复片段)
return MergeSegments(continuousCompliantSegments,true);
} }
/** /**
...@@ -989,28 +1341,74 @@ public class MachineCalculator { ...@@ -989,28 +1341,74 @@ public class MachineCalculator {
CopyOnWriteArrayList<TimeSegment> useSegments, CopyOnWriteArrayList<TimeSegment> useSegments,
CopyOnWriteArrayList<TimeSegment> conflictSegments, CopyOnWriteArrayList<TimeSegment> conflictSegments,
LocalDateTime currentTime, LocalDateTime currentTime,
double requiredMinutes) { double requiredMinutes,boolean isJit) {
// 遍历所有冲突片段
for (TimeSegment conflict : conflictSegments) {
LocalDateTime currentTime1=currentTime;
// 过滤冲突前的可用片段(
CopyOnWriteArrayList<TimeSegment> preConflictSegments = useSegments.stream()
.filter(slot ->
(slot.getStart().compareTo(currentTime1) >= 0 || slot.getEnd().compareTo(currentTime1) > 0)
&& slot.getEnd().compareTo(conflict.getStart()) <= 0
)
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
// 计算冲突前片段的总可用时长 if(isJit)
double preConflictTotalTime = calculateTotalAvailableSecond(preConflictSegments, currentTime); {
// 将冲突片段按结束时间倒序排序(优先处理结束时间晚的冲突)
CopyOnWriteArrayList<TimeSegment> sortedConflicts = new CopyOnWriteArrayList<>(conflictSegments);
sortedConflicts.sort((a, b) -> b.getEnd().compareTo(a.getEnd()));
for (TimeSegment conflict : sortedConflicts) {
LocalDateTime currentTime1 = currentTime;
//需要获取从conflict.getEnd() 到 currentTime1的
CopyOnWriteArrayList<TimeSegment> preConflictSegments = useSegments.stream()
.filter(slot -> {
// 情况1: 整个片段完全在 [conflict.getEnd(), currentTime1] 之间
if (slot.getStart().isAfter(conflict.getEnd()) && slot.getEnd().isBefore(currentTime1)) {
return true;
}
// 情况2: 片段的开始在 currentTime1 之前,但结束在 currentTime1 之后(右端重叠)
if (slot.getStart().isAfter(conflict.getEnd()) && slot.getStart().isBefore(currentTime1) && slot.getEnd().isAfter(currentTime1)) {
return true;
}
// 情况3: 片段的开始在 conflict.getEnd() 之前,但结束在 conflict.getEnd() 之后(左端重叠)
if (slot.getStart().isBefore(conflict.getEnd()) && slot.getEnd().isAfter(conflict.getEnd()) && slot.getEnd().isBefore(currentTime1)) {
return true;
}
// 情况4: 片段完全包含整个 [conflict.getEnd(), currentTime1] 范围
if (slot.getStart().isBefore(conflict.getEnd()) && slot.getEnd().isAfter(currentTime1)) {
return true;
}
return false;
})
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
// 冲突前时长超过所需时长则返回(保留原有判断逻辑),否则更新当前时间跳过冲突 // 计算冲突后片段的总可用时长
if (preConflictTotalTime > requiredMinutes) { double preConflictTotalTime = calculateTotalAvailableSecond(preConflictSegments,conflict.getEnd(), currentTime1);
return preConflictSegments;
} else { // 冲突后时长超过所需时长则返回(保留原有判断逻辑),否则更新当前时间跳过冲突
//更新当前时间为冲突结束时间+1秒 if (preConflictTotalTime > requiredMinutes) {
currentTime = conflict.getEnd().plusSeconds(1); return preConflictSegments;
} else {
//更新当前时间为冲突开始时间-1秒
currentTime = conflict.getStart().plusSeconds(-1);
}
}
}
else {
// 遍历所有冲突片段
for (TimeSegment conflict : conflictSegments) {
LocalDateTime currentTime1 = currentTime;
// 过滤冲突前的可用片段(
CopyOnWriteArrayList<TimeSegment> preConflictSegments = useSegments.stream()
.filter(slot ->
(slot.getStart().compareTo(currentTime1) >= 0 || slot.getEnd().compareTo(currentTime1) > 0)
&& slot.getEnd().compareTo(conflict.getStart()) <= 0
)
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
// 计算冲突前片段的总可用时长
double preConflictTotalTime = calculateTotalAvailableSecond(preConflictSegments, currentTime,isJit);
// 冲突前时长超过所需时长则返回(保留原有判断逻辑),否则更新当前时间跳过冲突
if (preConflictTotalTime > requiredMinutes) {
return preConflictSegments;
} else {
//更新当前时间为冲突结束时间+1秒
currentTime = conflict.getEnd().plusSeconds(1);
}
} }
} }
...@@ -1027,7 +1425,7 @@ public class MachineCalculator { ...@@ -1027,7 +1425,7 @@ public class MachineCalculator {
} }
machine.getAvailability().addAll(newSegments); machine.getAvailability().addAll(newSegments);
CopyOnWriteArrayList<TimeSegment> mergedSegments = MergeSegments(machine.getAvailability()); CopyOnWriteArrayList<TimeSegment> mergedSegments = MergeSegments(machine.getAvailability(),false);
// synchronized (machine.getAvailability()) { // synchronized (machine.getAvailability()) {
// 合并片段(去重+排序), // 合并片段(去重+排序),
...@@ -1062,40 +1460,78 @@ public class MachineCalculator { ...@@ -1062,40 +1460,78 @@ public class MachineCalculator {
/** /**
* 获取设备当前或下一个有效班次 * 获取设备当前或下一个有效班次
*/ */
public TimeSegment GetCurrentOrNextShift(Machine machine, LocalDateTime time, String prevtime, boolean checkprevtime) { public TimeSegment GetCurrentOrNextShift(Machine machine, LocalDateTime time, String prevtime, boolean checkprevtime,boolean isJit) {
TimeSegment start = null; TimeSegment start = null;
if (!machine.getAvailability().isEmpty()) if(isJit)
{ {
start = machine.getAvailability().stream() if (!machine.getAvailability().isEmpty())
.filter(slot -> !slot.isUsed() && slot.getType() != SegmentType.MAINTENANCE) {
.filter(slot -> slot.getStart().isAfter(time) || slot.getEnd().isAfter(time)) LocalDateTime machineValidTo= machineScheduler.GetEndTime(machine.getId());
.findFirst() if(machineValidTo.compareTo(time)<0)
.orElse(null); {
int day = (int) ChronoUnit.DAYS.between(machineValidTo, time);
List<TimeSegment> timeSegments = machineScheduler.generateTimeSegment(machine, machineValidTo.plusDays(1), day);
machine.getAvailability().addAll(timeSegments);
// 更新设备时间线
Machine originalMachine = machines.stream()
.filter(t -> t.getId() == machine.getId())
.findFirst()
.orElse(null);
if (originalMachine != null) {
MachineTimeline timeline = machineScheduler.getOrCreateTimeline(originalMachine);
}
}
start = machine.getAvailability().stream()
.filter(slot -> !slot.isUsed() && slot.getType() != SegmentType.MAINTENANCE)
.filter(slot -> slot.getStart().compareTo(time)<0)
.reduce((prev, current) -> current)
.orElse(null); // 没有符合条件的元素时返回null
if (start == null) {
//倒排,没有可用时间
return null;
}
}
}else { }else {
machine.setAvailability(new CopyOnWriteArrayList<>());
}
// 查找有效班次
if (!machine.getAvailability().isEmpty()) {
start = machine.getAvailability().stream()
.filter(slot -> !slot.isUsed() && slot.getType() != SegmentType.MAINTENANCE)
.filter(slot -> slot.getStart().isAfter(time) || slot.getEnd().isAfter(time))
.findFirst()
.orElse(null);
} else {
machine.setAvailability(new CopyOnWriteArrayList<>());
}
// 查找有效班次
if (start == null) {
// 生成新时间段 if (start == null) {
List<TimeSegment> timeSegments = machineScheduler.generateTimeSegment(machine, time.plusDays(1),0); // 生成新时间段
List<TimeSegment> timeSegments = machineScheduler.generateTimeSegment(machine, time.plusDays(1), 0);
machine.getAvailability().addAll(timeSegments); machine.getAvailability().addAll(timeSegments);
// 更新设备时间线 // 更新设备时间线
Machine originalMachine = machines.stream() Machine originalMachine = machines.stream()
.filter(t -> t.getId() == machine.getId()) .filter(t -> t.getId() == machine.getId())
.findFirst() .findFirst()
.orElse(null); .orElse(null);
if (originalMachine != null) { if (originalMachine != null) {
MachineTimeline timeline = machineScheduler.getOrCreateTimeline(originalMachine); MachineTimeline timeline = machineScheduler.getOrCreateTimeline(originalMachine);
} }
// 递归查找 // 递归查找
return GetCurrentOrNextShift(machine, time, prevtime, checkprevtime); return GetCurrentOrNextShift(machine, time, prevtime, checkprevtime,isJit);
}
} }
return start; return start;
...@@ -1446,7 +1882,7 @@ if(keys1!=null&&keys1.size()>0) { ...@@ -1446,7 +1882,7 @@ if(keys1!=null&&keys1.size()>0) {
} }
} }
availabilitySnapshot= MergeSegments(availabilitySnapshot); availabilitySnapshot= MergeSegments(availabilitySnapshot,false);
synchronized (machine.getAvailability()) { synchronized (machine.getAvailability()) {
machine.setAvailability(availabilitySnapshot); machine.setAvailability(availabilitySnapshot);
} }
...@@ -1457,7 +1893,7 @@ if(keys1!=null&&keys1.size()>0) { ...@@ -1457,7 +1893,7 @@ if(keys1!=null&&keys1.size()>0) {
* @param timeSegment 待分割的时间片段 * @param timeSegment 待分割的时间片段
* @param start 分割起始时间 * @param start 分割起始时间
*/ */
private void spiltMachineAvailable(Machine machine, TimeSegment timeSegment, LocalDateTime start) { private void spiltMachineAvailable(Machine machine, TimeSegment timeSegment, LocalDateTime start,boolean isJit) {
List<TimeSegment> timeSegments = new ArrayList<>(); List<TimeSegment> timeSegments = new ArrayList<>();
// 查找待分割片段在设备可用列表中的索引,对应C#的 FindIndex // 查找待分割片段在设备可用列表中的索引,对应C#的 FindIndex
...@@ -1467,16 +1903,27 @@ if(keys1!=null&&keys1.size()>0) { ...@@ -1467,16 +1903,27 @@ if(keys1!=null&&keys1.size()>0) {
.map(machine.getAvailability()::indexOf) .map(machine.getAvailability()::indexOf)
.orElse(-1); .orElse(-1);
if (inx > -1) { if (inx > -1) {
// 创建分割后的前半段时间片段,对应原C#逻辑
// 创建分割后的前半段时间片段
TimeSegment time = new TimeSegment(); TimeSegment time = new TimeSegment();
time.setStart(timeSegment.getStart()); if(isJit)
// 对应C#的 start.AddSeconds(-1) {
time.setEnd(start.plusSeconds(-1)); time.setStart(start.plusSeconds(1));
time.setHoliday(false); // 对应C#的 IsHoliday = false // 对应C#的 start.AddSeconds(-1)
time.setEnd(timeSegment.getEnd());
}else {
time.setStart(timeSegment.getStart());
// 对应C#的 start.AddSeconds(-1)
time.setEnd(start.plusSeconds(-1));
}
time.setHoliday(false); // IsHoliday = false
// 对应C#的 Guid.NewGuid().ToString(),生成唯一标识 // 对应C#的 Guid.NewGuid().ToString(),生成唯一标识
time.setKey(UUID.randomUUID().toString()); time.setKey(UUID.randomUUID().toString());
time.setType(SegmentType.REGULAR); time.setType(SegmentType.REGULAR);
time.setUsed(false); // 对应C#的 IsUsed = false time.setUsed(false); // IsUsed = false
timeSegments.add(time); timeSegments.add(time);
// 更新原片段的起始时间为分割时间,对应C#的 machine.Availability[inx].Start = start // 更新原片段的起始时间为分割时间,对应C#的 machine.Availability[inx].Start = start
...@@ -1488,11 +1935,11 @@ if(keys1!=null&&keys1.size()>0) { ...@@ -1488,11 +1935,11 @@ if(keys1!=null&&keys1.size()>0) {
machine.getAvailability().addAll(timeSegments); machine.getAvailability().addAll(timeSegments);
} }
// 按起始时间排序,对应C#的 Sort((a, b) => a.Start.CompareTo(b.Start)) // 按起始时间排序
machine.getAvailability().sort((a, b) -> a.getStart().compareTo(b.getStart())); machine.getAvailability().sort((a, b) -> a.getStart().compareTo(b.getStart()));
} }
private CopyOnWriteArrayList<TimeSegment> MergeSegments(CopyOnWriteArrayList<TimeSegment> segments) { private CopyOnWriteArrayList<TimeSegment> MergeSegments(CopyOnWriteArrayList<TimeSegment> segments,boolean isJit) {
CopyOnWriteArrayList<TimeSegment> maintenanceSegments = segments.stream() CopyOnWriteArrayList<TimeSegment> maintenanceSegments = segments.stream()
.filter(t -> t.getType() == SegmentType.MAINTENANCE) .filter(t -> t.getType() == SegmentType.MAINTENANCE)
.collect(Collectors.toCollection(CopyOnWriteArrayList::new)); .collect(Collectors.toCollection(CopyOnWriteArrayList::new));
...@@ -1521,8 +1968,12 @@ if(keys1!=null&&keys1.size()>0) { ...@@ -1521,8 +1968,12 @@ if(keys1!=null&&keys1.size()>0) {
CopyOnWriteArrayList<TimeSegment> result = new CopyOnWriteArrayList<>(mergedUnused); CopyOnWriteArrayList<TimeSegment> result = new CopyOnWriteArrayList<>(mergedUnused);
result.addAll(usedRegularSegments); result.addAll(usedRegularSegments);
result.addAll(maintenanceSegments); result.addAll(maintenanceSegments);
result.sort(Comparator.comparing(TimeSegment::getStart)); if(isJit)
{
result.sort(Comparator.comparing(TimeSegment::getEnd).reversed());
}else {
result.sort(Comparator.comparing(TimeSegment::getStart));
}
return result; return result;
} }
......
...@@ -58,6 +58,17 @@ public class MachineSchedulerService { ...@@ -58,6 +58,17 @@ public class MachineSchedulerService {
} }
public MachineTimeline getOrCreateTimeline(Machine machine) { public MachineTimeline getOrCreateTimeline(Machine machine) {
return getOrCreateTimeline(machine,true); return getOrCreateTimeline(machine,true);
}
public LocalDateTime GetEndTime(Long machineId) {
MachineTimeline timeline = timelineCache.get(machineId);
if(timeline!=null)
{
return timeline.getValidTo();
}
return null;
} }
private MachineTimeline generateTimeline(Machine machine) { private MachineTimeline generateTimeline(Machine machine) {
......
...@@ -168,6 +168,7 @@ public class PlanResultService { ...@@ -168,6 +168,7 @@ public class PlanResultService {
ScheduleParams param = InitScheduleParams(); ScheduleParams param = InitScheduleParams();
this.baseTime=param.getBaseTime();
// 1. 读取数据 // 1. 读取数据
// List<Machine> machines = loadData("machines.json", Machine.class); // List<Machine> machines = loadData("machines.json", Machine.class);
...@@ -259,7 +260,7 @@ public class PlanResultService { ...@@ -259,7 +260,7 @@ public class PlanResultService {
_sceneService.saveChromosomeToFile(chromosome, SceneId); _sceneService.saveChromosomeToFile(chromosome, SceneId);
// WriteScheduleSummary(chromosome); WriteScheduleSummary(chromosome);
return chromosome; return chromosome;
...@@ -1928,18 +1929,21 @@ public class PlanResultService { ...@@ -1928,18 +1929,21 @@ public class PlanResultService {
TargetFinishedOperationIds TargetFinishedOperationIds
)); ));
if(job.getGeneDetails()!=null)
{
for (ScheduleResultDetail d : job.getGeneDetails()) {
sb.append(String.format(
"\n\t\t\t\t\t\t\t\t\t\t [%d-%d]:[%s-%s] %d",
d.getStartTime(),
d.getEndTime(),
ConvertTime(d.getStartTime()),
ConvertTime(d.getEndTime()),
d.getEndTime() - d.getStartTime()
));
}
}
// 追加基因详情 // 追加基因详情
for (ScheduleResultDetail d : job.getGeneDetails()) {
sb.append(String.format(
"\n\t\t\t\t\t\t\t\t\t\t [%d-%d]:[%s-%s] %d",
d.getStartTime(),
d.getEndTime(),
ConvertTime(d.getStartTime()),
ConvertTime(d.getEndTime()),
d.getEndTime() - d.getStartTime()
));
}
FileHelper.writeLogFile(sb.toString()); FileHelper.writeLogFile(sb.toString());
......
...@@ -55,7 +55,10 @@ spring: ...@@ -55,7 +55,10 @@ spring:
# Oracle数据源 # Oracle数据源
oracle: oracle:
driver-class-name: oracle.jdbc.OracleDriver driver-class-name: oracle.jdbc.OracleDriver
url: jdbc:oracle:thin:@//39.100.78.207:7002/ORCLPDB1 # ORCL为你的Oracle实例名 url: jdbc:oracle:thin:@//39.100.77.7:1522/ORCLPDB1 # ORCL为你的Oracle实例名
# username: mes # 替换为你的Oracle用户名
# password: root_mes123456 # 替换为你的Oracle密码
# url: jdbc:oracle:thin:@//39.100.78.207:7002/ORCLPDB1 # ORCL为你的Oracle实例名
username: mes # 替换为你的Oracle用户名 username: mes # 替换为你的Oracle用户名
password: root_mes123456 # 替换为你的Oracle密码 password: root_mes123456 # 替换为你的Oracle密码
# sqlserver: # sqlserver:
......
...@@ -40,10 +40,14 @@ public class PlanResultServiceTest { ...@@ -40,10 +40,14 @@ public class PlanResultServiceTest {
// sortService.test1(); // sortService.test1();
// nsgaiiUtils.Test(); // nsgaiiUtils.Test();
// planResultService.execute2("E29F2B3ADA8149F6B916B5119296A92B");//2000 // planResultService.execute2("AD62106303684459949A7323D114BF60");//2000
planResultService.execute2("15210B13B88A453F8B84AAC7F16C7541");//2000
// planResultService.execute2("E29F2B3ADA8149F6B916B5119296A92B");//2000
// planResultService.execute2("E2CD1FC6FF9B4B19A59FEC7F846D4952");//600 // planResultService.execute2("E2CD1FC6FF9B4B19A59FEC7F846D4952");//600
// planResultService.execute2("EAF3C94B8F3345278F226C94FB0FED86");//bom // planResultService.execute2("EAF3C94B8F3345278F226C94FB0FED86");//bom
planResultService.execute2("6D63146BE5C84A78B5AB044327BA55BD");//2000 // planResultService.execute2("6D63146BE5C84A78B5AB044327BA55BD");//2000
// planResultService.execute2("6D63146BE5C84A78B5AB044327BA55BD");//5000 // planResultService.execute2("6D63146BE5C84A78B5AB044327BA55BD");//5000
// planResultService.execute2("C8B533BD8944405B9A2F8823C575C204");//500 // planResultService.execute2("C8B533BD8944405B9A2F8823C575C204");//500
// planResultService.execute2("EFDD34E4B5BC434BAEAE6A84DFCD4E7B");//20 // planResultService.execute2("EFDD34E4B5BC434BAEAE6A84DFCD4E7B");//20
......
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