Commit 6f7aeeba authored by Tong Li's avatar Tong Li

倒排

parent ef3a51e0
......@@ -157,6 +157,7 @@ private Long isInterrupt = 1l;
private String equipName;//设备编码
@Schema(description = "指定开始时间")
@JsonInclude(JsonInclude.Include.ALWAYS)
private LocalDateTime designatedStartTime;
......@@ -164,4 +165,12 @@ private Long isInterrupt = 1l;
private LocalDateTime jitPreferredStartTime;
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 {
/// </summary>
private boolean IsOverlap = false;
/// <summary>
/// 是否全局倒排
/// </summary>
private boolean isJit = true;
private boolean _smoothSetup = false; // 设置时间平滑 工序的前处理是否提前
private boolean _smoothChangeOver = true; // 默认true,设置时间 是否考虑换型时间
......
......@@ -20,6 +20,7 @@ import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.*;
......@@ -66,7 +67,7 @@ public class GeneticDecoder {
private DiscreteParameterMatrixService discreteParameterMatrixService;
private String sceneId;
private boolean rebuildStructureForCurrentDecode = true;
private boolean rebuildStructureForCurrentDecode = false;
......@@ -327,8 +328,10 @@ public class GeneticDecoder {
Entry entry=allOperations.stream()
.filter(t->t.getGroupId()==num&&t.getSequence()==scheduledCount1)
.findFirst().orElse(null);
entry.setSchedulingMode(Entry.SchedulingMode.FORWARD.name());
if(entry!=null&&entry.getDependentOnOrderIds().size()>0)
{
for (int order : entry.getDependentOnOrderIds()) {
for (int num1 : sfSequence.get(order)){
......@@ -358,7 +361,7 @@ public class GeneticDecoder {
Entry entry=allOperations.stream()
.filter(t->t.getGroupId()==orderid&&t.getSequence()==scheduledCount1)
.findFirst().orElse(null);
entry.setSchedulingMode(Entry.SchedulingMode.BACKWARD.name());
if(entry!=null&&entry.getDependentOnOrderIds().size()>0) {
for (int order : entry.getDependentOnOrderIds()) {
......@@ -411,7 +414,7 @@ public class GeneticDecoder {
public void serialDecode(Chromosome chromosome) {
long decodeStart = System.nanoTime();
chromosome.setScenarioID(sceneId);
boolean isJit=_globalParam.isJit();
if (rebuildStructureForCurrentDecode) {
long t1 = System.nanoTime();
//创建半成品订单
......@@ -486,25 +489,20 @@ public class GeneticDecoder {
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顺序调度工序
Map<Integer, Integer> orderProcessCounter = new HashMap<>();
Map<Integer, Integer> orderLastEndTime = new HashMap<>();
Map<Integer, Integer> orderDueDate = new HashMap<>();
Map<Integer, Entry> entryIndexById = new HashMap<>();
Map<Integer, List<Entry>> entrysBygroupId = new HashMap<>();
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) {
int groupId = op.getGroupId();
orderProcessCounter.putIfAbsent(groupId, 0);
......@@ -525,9 +523,25 @@ public class GeneticDecoder {
for (int groupId : chromosome.getOperationSequencing()) {
int scheduledCount = orderProcessCounter.get(groupId);
List<Entry> orderOps = entrysBygroupId.get(groupId).stream()
List<Entry> orderOps=new ArrayList<>();
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()) {
throw new IllegalStateException(String.format(
......@@ -555,16 +569,11 @@ public class GeneticDecoder {
double processTime = machineOption.getProcessingTime();
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;
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);
orderLastEndTime.put(groupId, actualEndTime);
......@@ -649,6 +658,13 @@ public class GeneticDecoder {
Map<Integer, Entry> entryIndexById = new HashMap<>();
Map<Integer, List<Entry>> entrysBygroupId = new HashMap<>();
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) {
int groupId = op.getGroupId();
orderProcessCounter.putIfAbsent(groupId, 0);
......@@ -790,11 +806,11 @@ public class GeneticDecoder {
// 工序超过500的设备直接在主线程串行处理
if (ops.size() > 500) {
// 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 {
// 工序少的设备创建线程处理
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();
threads.add(thread);
......@@ -824,7 +840,7 @@ public class GeneticDecoder {
*/
private void processMachineOps(Chromosome chromosome, Long machineId, List<Entry> ops,
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();
for (int i = 0; i < ops.size(); i++) {
......@@ -858,9 +874,10 @@ public class GeneticDecoder {
double processTime = machineOption.getProcessingTime();
int orderDueDate1=orderDueDate.get(op.getGroupId());
// 处理当前工序
// 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();
......@@ -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);
int prevtime = 0;
//后处理时间
int teardownTime = currentOp.getTeardownTime();
// int teardownTime = currentOp.getTeardownTime();
if(isJit)
{
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);
prevtime = CalPrevtime(prevtime, currentOp, chromosome, processTime, targetMachine, entryIndexById, scheduleIndexById);
}
}
int prevendtime=prevtime;
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;
}
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();
int processingTimeTotal=0;
int earliestStartTime = prevOperationEndTime;
......@@ -1097,18 +1123,84 @@ public class GeneticDecoder {
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;
long machineId = machine.getId();
// 只有存在物料约束(或本次被强制要求重算 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。
boolean commitMaterialCheck = needMaterialCheck;
// baseEarliestStartTime:不考虑 BOM 推迟时的理论最早开工时间。
// candidateStartTime:固定点迭代时本轮尝试的开工时间。
// bomtime:按当前候选开工时间试算出来的物料最早可开工时间。
int baseEarliestStartTime = earliestStartTime;
int candidateStartTime = earliestStartTime;
baseEarliestStartTime = earliestStartTime;
candidateStartTime = earliestStartTime;
int bomtime=0;
// 机台时间和物料时间可能会互相推动,最多迭代 maxSolveIterations 次来找收敛点。
int maxSolveIterations = Math.max(1, _globalParam.getMaterialSolveMaxIterations());
......@@ -1197,44 +1289,8 @@ public class GeneticDecoder {
lastGeneOnMachine = getLastMachineTask(machineTasks);
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())
{
......@@ -1243,7 +1299,8 @@ public class GeneticDecoder {
// 下面开始生成正式的 geneDetails。
// 与 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());
setupTime=(int)reslte.get(1);//换型时间
......@@ -1253,14 +1310,14 @@ public class GeneticDecoder {
processingTimeTotal=(int)reslte.get(5);//processingTimeTotal
if(setupTime==0)
{
geneDetails = machineCalculator.getNextAvailableTime(machine, earliestStartTime, -1,
processingTimeTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true);
geneDetails = machineCalculator.getNextAvailableTime(machine, operation, earliestStartTime, -1,
processingTimeTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true,isJit);
}else {
CopyOnWriteArrayList<TimeSegment> AvailableTimeSegment = (CopyOnWriteArrayList<TimeSegment>) reslte.get(6);
Map<Integer,Object> result = machineCalculator.CreateScheduleResult(machine, processingTimeTotal, earliestStartTime,
AvailableTimeSegment, processingTime, operation.getQuantity(), operation.getIsInterrupt() != 1, setupTime, _globalParam.is_smoothChangeOverInWeek(), setupStartTime);
Map<Integer,Object> result = machineCalculator.CreateScheduleResult(machine, operation, processingTimeTotal, earliestStartTime,
AvailableTimeSegment, processingTime, operation.getQuantity(), operation.getIsInterrupt() != 1, setupTime, _globalParam.is_smoothChangeOverInWeek(), setupStartTime,isJit);
......@@ -1269,8 +1326,8 @@ public class GeneticDecoder {
geneDetails=(CopyOnWriteArrayList<ScheduleResultDetail>) result.get(2);
}
}else {
geneDetails = machineCalculator.getNextAvailableTime(machine, earliestStartTime, -1,
processingTimeTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true);
geneDetails = machineCalculator.getNextAvailableTime(machine, operation, earliestStartTime, -1,
processingTimeTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true,isJit);
}
......@@ -1397,7 +1454,8 @@ public class GeneticDecoder {
}
int processingTimeTotal1=0;
if(geneDetails!=null&&geneDetails.size()>0)
{
processingTimeTotal1=(int) geneDetails.stream().filter(t->t.getUsedSegment()==null).mapToDouble(ScheduleResultDetail::getProcessingTime) // 替换为实际字段名
.sum();
......@@ -1413,9 +1471,13 @@ public class GeneticDecoder {
.sum();
}
result.setProcessingTime(processingTimeTotal1);
result.setGeneDetails(geneDetails);
}else {
processingTimeTotal1=endTime-startTime;
}
result.setProcessingTime(processingTimeTotal1);
return result;
}
......@@ -1476,22 +1538,22 @@ public class GeneticDecoder {
if(setupTime==0)
{
// 没有换型占用时,直接查机台下一段可排的可用窗口。
geneDetails = machineCalculator.getNextAvailableTime(machine, previewStart, -1,
previewProcessingTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true);
geneDetails = machineCalculator.getNextAvailableTime(machine, operation, previewStart, -1,
previewProcessingTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true,false);
}else {
// 有换型占用时,需要把换型段和加工段一起拼成完整的试排结果。
CopyOnWriteArrayList<TimeSegment> AvailableTimeSegment = (CopyOnWriteArrayList<TimeSegment>) reslte.get(6);
Map<Integer,Object> result = machineCalculator.CreateScheduleResult(machine, previewProcessingTotal, previewStart,
AvailableTimeSegment, processingTime, operation.getQuantity(), operation.getIsInterrupt() != 1, setupTime, _globalParam.is_smoothChangeOverInWeek(), setupStartTime);
Map<Integer,Object> result = machineCalculator.CreateScheduleResult(machine, operation, previewProcessingTotal, previewStart,
AvailableTimeSegment, processingTime, operation.getQuantity(), operation.getIsInterrupt() != 1, setupTime, _globalParam.is_smoothChangeOverInWeek(), setupStartTime,false);
setupTime=(int)result.get(1);
geneDetails=(CopyOnWriteArrayList<ScheduleResultDetail>) result.get(2);
}
}else {
// 不考虑平滑换型时,只需要找加工段可用窗口。
geneDetails = machineCalculator.getNextAvailableTime(machine, previewStart, -1,
previewProcessingTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true);
geneDetails = machineCalculator.getNextAvailableTime(machine, operation, previewStart, -1,
previewProcessingTotal, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), true,false);
}
// geneDetails 可能跨多个班次/时段,开始时间取最小值,结束时间取最大值。
......@@ -1648,6 +1710,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)
{
int prevperationEndTime = newScheduleResult.stream()
......@@ -2021,7 +2113,7 @@ public class GeneticDecoder {
.findFirst()
.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;
}
}
......@@ -8,6 +8,7 @@ import com.aps.entity.basic.*;
import com.aps.service.plan.MachineSchedulerService;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.sun.org.apache.xpath.internal.objects.XBoolean;
import java.math.BigDecimal;
import java.math.RoundingMode;
......@@ -42,12 +43,12 @@ public class MachineCalculator {
* 获取机器下一个可用时间窗口(考虑班次约束)
*/
// 从 proposedStartTime 开始查找机台下一段可排窗口;必要时会把命中的 availability 临时标记为已占用。
public CopyOnWriteArrayList<ScheduleResultDetail> getNextAvailableTime(Machine machine, int proposedStartTime,
public CopyOnWriteArrayList<ScheduleResultDetail> getNextAvailableTime(Machine machine,Entry operation, int proposedStartTime,
int prevtime, int processingTime,
CopyOnWriteArrayList<GAScheduleResult> existingTasks,
boolean isInterrupt, boolean istask,
double oneTime,double quantity,
boolean islockMachineTime) {
boolean islockMachineTime,boolean isJit) {
LocalDateTime startTime = baseTime.plus(proposedStartTime, ChronoUnit.SECONDS);
String prevtimestr = "";
......@@ -57,8 +58,8 @@ public class MachineCalculator {
}
// 查找合适的班次窗口
return findEarliestStart(machine, processingTime, startTime, prevtimestr,
existingTasks, oneTime, quantity, istask, islockMachineTime,isInterrupt);
return findEarliestStart(machine, operation,processingTime, startTime, prevtimestr,
existingTasks, oneTime, quantity, istask, islockMachineTime,isInterrupt,isJit);
}
public CopyOnWriteArrayList<TimeSegment> getMachineAvailableTime(Machine machine, int proposedStartTime,
......@@ -73,9 +74,9 @@ public class MachineCalculator {
// 基于已选可用时段组装排程结果,并把换型段/加工段对应的 availability 临时占用。
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
,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);
......@@ -165,8 +166,8 @@ public class MachineCalculator {
}
times= CaldScheduleResult(
machine, processingTime, startTime,
timeSegments, oneTime, quantity
machine, operation, processingTime, startTime,
timeSegments, oneTime, quantity,isJit
);
}else {
......@@ -236,8 +237,8 @@ public class MachineCalculator {
.filter(t->t.getStart().compareTo(startCandidate)>=0)
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
times= CaldScheduleResult(
machine, processingTime, startTime,
timeSegments1, oneTime, quantity);
machine, operation, processingTime, startTime,
timeSegments1, oneTime, quantity,isJit);
} else {
double e= (double)processingTime/slot.getEfficiency();
......@@ -271,9 +272,9 @@ public class MachineCalculator {
// 查找最早可用开始时间
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
,boolean isInterrupt) {
,boolean isInterrupt,boolean isJit) {
CopyOnWriteArrayList<GAScheduleResult> machineTasks=null;
if(existingTasks!=null&&existingTasks.size()>0) {
machineTasks = existingTasks.stream()
......@@ -284,16 +285,29 @@ public class MachineCalculator {
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;
LocalDateTime startCandidate=null;
LocalDateTime endCandidate1= null;
LocalDateTime prevTimeDateTime = StringUtils.isEmpty(prevtime) ? null : LocalDateTime.parse(prevtime);
LocalDateTime startCandidate = slot.getStart().isAfter(prevTimeDateTime != null ? prevTimeDateTime : currentTime)
if(isJit)
{
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);
long availableSeconds = ChronoUnit.SECONDS.between(startCandidate, slot.getEnd());
long availableSeconds = ChronoUnit.SECONDS.between(startCandidate, endCandidate1);
//实际可用时间
long availableSeconds_e =Math.round(availableSeconds*slot.getEfficiency()) ;
......@@ -301,7 +315,7 @@ public class MachineCalculator {
// if (endCandidate.isAfter(slot.getEnd())) {
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 {
......@@ -340,7 +354,7 @@ public class MachineCalculator {
List<ScheduleResultDetail> oldTimes = new ArrayList<>();
while (remainingTime > 0) {
TimeSegment shift = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime);
TimeSegment shift = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime,false);
if (shift == null) break;
LocalDateTime shiftStart = shift.getStart();
......@@ -403,19 +417,19 @@ public class MachineCalculator {
}
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
,boolean isInterrupt) {
,boolean isInterrupt,boolean isJit) {
int remainingTime = processingTime;
LocalDateTime st = StringUtils.isEmpty(prevtime) ? currentTime : LocalDateTime.parse(prevtime);
LocalDateTime prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
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(
machine, processingTime, currentTime,
timeSegments, oneTime, quantity
machine, operation, processingTime, currentTime,
timeSegments, oneTime, quantity,isJit
);
// int estimateIndex= (int) Math.ceil(remainingTime / (double) ONE_DAY_MINUTES);
......@@ -456,18 +470,26 @@ public class MachineCalculator {
}
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
) {
,boolean isJit) {
int remainingTime = processingTime;
CopyOnWriteArrayList<ScheduleResultDetail> times = new CopyOnWriteArrayList<>();
int estimateIndex= (int) Math.ceil(remainingTime / (double) ONE_DAY_MINUTES);
CopyOnWriteArrayList<TimeSegment> timeSegments1=null;
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) {
......@@ -476,7 +498,7 @@ public class MachineCalculator {
while (remainingTime > 0) {
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);
ScheduleResultDetail time = (ScheduleResultDetail) outMap.get(2);
times.add(time);
......@@ -494,16 +516,16 @@ public class MachineCalculator {
machine.getAvailability().sort(Comparator.comparing(TimeSegment::getStart));
}
}else {
times= CaldScheduleResultDetail(timeSegments1,machine,currentTime,remainingTime,oneTime);
times= CaldScheduleResultDetail(timeSegments1,machine, operation,currentTime,remainingTime,oneTime,isJit);
}
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<>();
......@@ -512,7 +534,7 @@ public class MachineCalculator {
//第一个数据
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);
ScheduleResultDetail time1=(ScheduleResultDetail)outMap.get(2);
times.add(time1);
......@@ -524,13 +546,16 @@ public class MachineCalculator {
//中间的数据
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 effectiveend=timeSegments2.get(timeSegments2.size()-1).getEnd();
int processable =(int)calculateTotalAvailableSecond(timeSegments2, st);
int processable =(int)calculateTotalAvailableSecond(timeSegments2, st,isJit);
ScheduleResultDetail time = new ScheduleResultDetail();
time.setKey(UUID.randomUUID().toString());
time.setStartTime((int) ChronoUnit.SECONDS.between(baseTime, effectiveStart));
......@@ -554,7 +579,7 @@ public class MachineCalculator {
//最后的时间段
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);
ScheduleResultDetail timelast=(ScheduleResultDetail)outMaplast.get(2);
times.add(timelast);
......@@ -571,15 +596,32 @@ public class MachineCalculator {
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 shiftEnd = shift.getEnd();
// 计算有效时间
LocalDateTime effectiveStart = null;
LocalDateTime effectiveEnd=null;
if(isJit)
{
effectiveStart=shiftStart;
effectiveEnd = st.isBefore(shiftEnd) ? st : shiftEnd;
}else {
// 计算有效时间
LocalDateTime effectiveStart = st.isAfter(shiftStart) ? st : shiftStart;
long availableSeconds = ChronoUnit.SECONDS.between(effectiveStart, shiftEnd);
effectiveStart = st.isAfter(shiftStart) ? st : shiftStart;
effectiveEnd=shiftEnd;
}
long availableSeconds = ChronoUnit.SECONDS.between(effectiveStart, effectiveEnd);
long availableSeconds_e =Math.round(availableSeconds*shift.getEfficiency()) ;
// 处理当前班次
......@@ -610,7 +652,6 @@ public class MachineCalculator {
return outMap;
}
/**
* 查找满足时长要求的无冲突可用时段
* @param machine 设备
......@@ -626,6 +667,169 @@ public class MachineCalculator {
CopyOnWriteArrayList<GAScheduleResult> machineTasks,
double requiredMinutes,
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<>();
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,isJit);
if (useSegments != null && !useSegments.isEmpty()) {
// 计算可用时间总和(保留原有逻辑)
double totalUseTime = calculateTotalAvailableSecond(useSegments, current,isJit);
//倒排,可用时间不够,应该重新正排
if(totalUseTime < requiredMinutes&&isJit)
{
return null;
}
// 不足所需时长时,自动生成未来片段并补充
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(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;
......@@ -642,7 +846,7 @@ public class MachineCalculator {
if (useSegments != null && !useSegments.isEmpty()) {
// 计算可用时间总和(保留原有逻辑)
double totalUseTime = calculateTotalAvailableSecond(useSegments, current);
double totalUseTime = calculateTotalAvailableSecond(useSegments, current,isJit);
// 不足所需时长时,自动生成未来片段并补充
while (totalUseTime < requiredMinutes) {
......@@ -663,7 +867,7 @@ public class MachineCalculator {
// 重新过滤可用片段
useSegments = filterValidUseSegments(machine.getAvailability(), current, requireContinuous, requiredMinutes);
// 重新计算总可用时间
totalUseTime = calculateTotalAvailableSecond(useSegments, current);
totalUseTime = calculateTotalAvailableSecond(useSegments, current,isJit);
}
// 若总可用时间满足要求,处理冲突
......@@ -677,16 +881,16 @@ public class MachineCalculator {
if (conflictSegments == null || conflictSegments.isEmpty()) {
// 无冲突:直接返回可用片段(保留原有逻辑)
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);
}
return useSegments;
} 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.get(0).getStart().compareTo(current) < 0) {
spiltMachineAvailable(machine, resultSegments.get(0), current);
spiltMachineAvailable(machine, resultSegments.get(0), current,isJit);
resultSegments.get(0).setStart(current);
}
return resultSegments;
......@@ -712,6 +916,8 @@ public class MachineCalculator {
}
}
private static final int ONE_DAY_MINUTES = 24 * 60*60; // 固定锚点:一天1440分钟,永不改变
/**
......@@ -725,7 +931,7 @@ public class MachineCalculator {
public CopyOnWriteArrayList<TimeSegment> getEnoughSegmentsByEstimateIndex(
CopyOnWriteArrayList<TimeSegment> availableSegments,
LocalDateTime currentTime,
int requiredMinutes) {
int requiredMinutes,boolean isJit) {
// 基础校验
if (availableSegments == null || availableSegments.isEmpty()) {
throw new IllegalArgumentException("可用时段列表不能为空");
......@@ -749,13 +955,13 @@ public class MachineCalculator {
int targetIndex = estimateIndex;
while (true) {
// 计算当前索引的总有效时长
double currentTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, targetIndex);
double currentTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, targetIndex, isJit);
if (currentTotal >= requiredMinutes) {
// 总时长满足,尝试往前减索引,找「最小满足索引」(减少后续裁剪的工作量)
if (targetIndex > 0) {
double prevTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, targetIndex - 1);
double prevTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, targetIndex - 1, isJit);
if (prevTotal >= requiredMinutes) {
targetIndex--;
continue;
......@@ -770,11 +976,11 @@ public class MachineCalculator {
targetIndex +=Math.max(syday, 5);
if (targetIndex >= totalSegmentCount) {
// 所有片段总时长不足,抛出异常
double allTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, totalSegmentCount - 1);
double allTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, totalSegmentCount - 1, isJit);
if(allTotal>requiredMinutes)
{
double prevTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, targetIndex - 1);
double prevTotal = calculateTotalMinutesByIndex(availableSegments, currentTime, targetIndex - 1, isJit);
if (prevTotal >= requiredMinutes) {
targetIndex--;
continue;
......@@ -799,14 +1005,13 @@ public class MachineCalculator {
/**
* 计算从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);
}
/**
* 统一过滤有效可用片段
*/
......@@ -814,23 +1019,54 @@ public class MachineCalculator {
LocalDateTime currentTime,
boolean requireContinuous,
double requiredMinutes) {
return filterValidUseSegments( allSegments,
currentTime,
requireContinuous,
requiredMinutes,false);
}
/**
* 统一过滤有效可用片段
*/
private CopyOnWriteArrayList<TimeSegment> filterValidUseSegments(CopyOnWriteArrayList<TimeSegment> allSegments,
LocalDateTime currentTime,
boolean requireContinuous,
double requiredMinutes,boolean isJit) {
// 空判断
if (allSegments == null || allSegments.isEmpty()) {
return new CopyOnWriteArrayList<>();
}
// 统一过滤条件
CopyOnWriteArrayList<TimeSegment> baseValidSegments = allSegments.stream()
CopyOnWriteArrayList<TimeSegment> baseValidSegments=new CopyOnWriteArrayList<>();
if(isJit)
{
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
&& 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) {
return baseValidSegments;
}
if(isJit)
{
return filterContinuousCompliantSegmentsJit(baseValidSegments, currentTime, requiredMinutes);
}
// 要求不可中断时,过滤符合连续时长要求的片段
return filterContinuousCompliantSegments(baseValidSegments, currentTime, requiredMinutes);
}
......@@ -841,13 +1077,24 @@ public class MachineCalculator {
* @param currentTime 当前时间
* @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) {
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
......@@ -859,6 +1106,43 @@ public class MachineCalculator {
.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()
.mapToDouble(segment -> {
// 计算有效的起始时间:取 segment.start 和 startTime 的最大值
LocalDateTime effectiveStart = segment.getStart().compareTo(startTime) < 0 ? startTime : segment.getStart();
// 计算有效的结束时间:取 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();
})
.sum(); // 累加所有片段的有效时长
}
/**
* 辅助方法:过滤出满足连续时长要求的片段(仅不可中断配置生效时调用)
* 前置剔除零散无效片段,减少后续逻辑处理量
......@@ -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,11 +1341,56 @@ public class MachineCalculator {
CopyOnWriteArrayList<TimeSegment> useSegments,
CopyOnWriteArrayList<TimeSegment> conflictSegments,
LocalDateTime currentTime,
double requiredMinutes) {
double requiredMinutes,boolean isJit) {
if(isJit)
{
// 将冲突片段按结束时间倒序排序(优先处理结束时间晚的冲突)
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));
// 计算冲突后片段的总可用时长
double preConflictTotalTime = calculateTotalAvailableSecond(preConflictSegments,conflict.getEnd(), currentTime1);
// 冲突后时长超过所需时长则返回(保留原有判断逻辑),否则更新当前时间跳过冲突
if (preConflictTotalTime > requiredMinutes) {
return preConflictSegments;
} else {
//更新当前时间为冲突开始时间-1秒
currentTime = conflict.getStart().plusSeconds(-1);
}
}
}
else {
// 遍历所有冲突片段
for (TimeSegment conflict : conflictSegments) {
LocalDateTime currentTime1=currentTime;
LocalDateTime currentTime1 = currentTime;
// 过滤冲突前的可用片段(
CopyOnWriteArrayList<TimeSegment> preConflictSegments = useSegments.stream()
.filter(slot ->
......@@ -1003,7 +1400,7 @@ public class MachineCalculator {
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
// 计算冲突前片段的总可用时长
double preConflictTotalTime = calculateTotalAvailableSecond(preConflictSegments, currentTime);
double preConflictTotalTime = calculateTotalAvailableSecond(preConflictSegments, currentTime,isJit);
// 冲突前时长超过所需时长则返回(保留原有判断逻辑),否则更新当前时间跳过冲突
if (preConflictTotalTime > requiredMinutes) {
......@@ -1013,6 +1410,7 @@ public class MachineCalculator {
currentTime = conflict.getEnd().plusSeconds(1);
}
}
}
// 所有冲突前片段均不足所需时长,返回null(与原C#逻辑一致)
return null;
......@@ -1027,7 +1425,7 @@ public class MachineCalculator {
}
machine.getAvailability().addAll(newSegments);
CopyOnWriteArrayList<TimeSegment> mergedSegments = MergeSegments(machine.getAvailability());
CopyOnWriteArrayList<TimeSegment> mergedSegments = MergeSegments(machine.getAvailability(),false);
// synchronized (machine.getAvailability()) {
// 合并片段(去重+排序),
......@@ -1062,17 +1460,54 @@ 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;
if(isJit)
{
if (!machine.getAvailability().isEmpty())
{
LocalDateTime machineValidTo= machineScheduler.GetEndTime(machine.getId());
if(machineValidTo.compareTo(time)<0)
{
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 {
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 {
} else {
machine.setAvailability(new CopyOnWriteArrayList<>());
}
// 查找有效班次
......@@ -1080,7 +1515,7 @@ public class MachineCalculator {
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);
......@@ -1095,7 +1530,8 @@ public class MachineCalculator {
}
// 递归查找
return GetCurrentOrNextShift(machine, time, prevtime, checkprevtime);
return GetCurrentOrNextShift(machine, time, prevtime, checkprevtime,isJit);
}
}
return start;
......@@ -1446,7 +1882,7 @@ if(keys1!=null&&keys1.size()>0) {
}
}
availabilitySnapshot= MergeSegments(availabilitySnapshot);
availabilitySnapshot= MergeSegments(availabilitySnapshot,false);
synchronized (machine.getAvailability()) {
machine.setAvailability(availabilitySnapshot);
}
......@@ -1457,7 +1893,7 @@ if(keys1!=null&&keys1.size()>0) {
* @param timeSegment 待分割的时间片段
* @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<>();
// 查找待分割片段在设备可用列表中的索引,对应C#的 FindIndex
......@@ -1467,16 +1903,27 @@ if(keys1!=null&&keys1.size()>0) {
.map(machine.getAvailability()::indexOf)
.orElse(-1);
if (inx > -1) {
// 创建分割后的前半段时间片段,对应原C#逻辑
// 创建分割后的前半段时间片段
TimeSegment time = new TimeSegment();
if(isJit)
{
time.setStart(start.plusSeconds(1));
// 对应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); // 对应C#的 IsHoliday = false
}
time.setHoliday(false); // IsHoliday = false
// 对应C#的 Guid.NewGuid().ToString(),生成唯一标识
time.setKey(UUID.randomUUID().toString());
time.setType(SegmentType.REGULAR);
time.setUsed(false); // 对应C#的 IsUsed = false
time.setUsed(false); // IsUsed = false
timeSegments.add(time);
// 更新原片段的起始时间为分割时间,对应C#的 machine.Availability[inx].Start = start
......@@ -1488,11 +1935,11 @@ if(keys1!=null&&keys1.size()>0) {
machine.getAvailability().addAll(timeSegments);
}
// 按起始时间排序,对应C#的 Sort((a, b) => a.Start.CompareTo(b.Start))
// 按起始时间排序
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()
.filter(t -> t.getType() == SegmentType.MAINTENANCE)
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
......@@ -1521,8 +1968,12 @@ if(keys1!=null&&keys1.size()>0) {
CopyOnWriteArrayList<TimeSegment> result = new CopyOnWriteArrayList<>(mergedUnused);
result.addAll(usedRegularSegments);
result.addAll(maintenanceSegments);
if(isJit)
{
result.sort(Comparator.comparing(TimeSegment::getEnd).reversed());
}else {
result.sort(Comparator.comparing(TimeSegment::getStart));
}
return result;
}
......
......@@ -58,6 +58,17 @@ public class MachineSchedulerService {
}
public MachineTimeline getOrCreateTimeline(Machine machine) {
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) {
......
......@@ -149,6 +149,7 @@ public class PlanResultService {
ScheduleParams param = InitScheduleParams();
this.baseTime=param.getBaseTime();
// 1. 读取数据
// List<Machine> machines = loadData("machines.json", Machine.class);
......@@ -240,7 +241,7 @@ public class PlanResultService {
_sceneService.saveChromosomeToFile(chromosome, SceneId);
// WriteScheduleSummary(chromosome);
WriteScheduleSummary(chromosome);
return chromosome;
......@@ -1909,8 +1910,8 @@ public class PlanResultService {
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",
......@@ -1921,6 +1922,9 @@ public class PlanResultService {
d.getEndTime() - d.getStartTime()
));
}
}
// 追加基因详情
FileHelper.writeLogFile(sb.toString());
......
......@@ -56,11 +56,11 @@ spring:
oracle:
driver-class-name: oracle.jdbc.OracleDriver
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用户名
password: root_mes123456 # 替换为你的Oracle密码
# url: jdbc:oracle:thin:@//39.100.78.207:7002/ORCLPDB1 # ORCL为你的Oracle实例名
# username: mes # 替换为你的Oracle用户名
# password: root_mes123456 # 替换为你的Oracle密码
# sqlserver:
# driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
# url: jdbc:sqlserver://192.168.0.181:1434;databaseName=mes;encrypt=false
......
......@@ -40,7 +40,9 @@ public class PlanResultServiceTest {
// sortService.test1();
// nsgaiiUtils.Test();
planResultService.execute2("AD62106303684459949A7323D114BF60");//2000
// planResultService.execute2("AD62106303684459949A7323D114BF60");//2000
planResultService.execute2("15210B13B88A453F8B84AAC7F16C7541");//2000
// planResultService.execute2("E29F2B3ADA8149F6B916B5119296A92B");//2000
// planResultService.execute2("E2CD1FC6FF9B4B19A59FEC7F846D4952");//600
......
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