修改速度计算

parent 01953779
This source diff could not be displayed because it is too large. You can view the blob instead.
package com.aps.service.plan;
import com.aps.common.util.FileHelper;
import com.aps.entity.basic.*;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
public class AlgorithmScheduler8 {
private final List<Product> _products;
private final List<Machine> _machines;
private final List<Order> _orders;
private final Random _random;
private final MachineSchedulerService _machineScheduler;
private int _populationSize = 100;
private double _crossoverRate = 0.8;
private double _mutationRate = 0.1;
private int _maxGenerations = 500;
private int _elitismCount = 5;
private final LocalDateTime baseTime = LocalDateTime.of(2025, 10, 1, 0, 0, 0);
// 是否平滑换型参数
private boolean _smoothSetup = false; // 默认true,不占用设备时长
public AlgorithmScheduler8(List<Product> products, List<Machine> machines, List<Order> orders,
MachineSchedulerService machineScheduler) {
this(products, machines, orders, machineScheduler, false);
}
public AlgorithmScheduler8(List<Product> products, List<Machine> machines, List<Order> orders,
MachineSchedulerService machineScheduler, boolean smoothSetup) {
this._products = deepCopyProductList(products);
this._machines = deepCopyMachineList(machines);
this._orders = deepCopyOrderList(orders);
this._machineScheduler = machineScheduler;
this._random = new Random();
}
public void setSmoothSetup(boolean smoothSetup) {
this._smoothSetup = smoothSetup;
}
public boolean isSmoothSetup() {
return _smoothSetup;
}
public ScheduleChromosome Run() {
List<ScheduleChromosome> population = InitializePopulation();
ScheduleChromosome bestSolution = null;
ScheduleChromosome currentBest = null;
double bestFitness = Double.MIN_VALUE;
currentBest = population.stream()
.max(Comparator.comparingDouble(ScheduleChromosome::getFitness))
.orElse(null);
if (currentBest != null) {
bestFitness = currentBest.getFitness();
bestSolution = deepCopyScheduleChromosome(currentBest);
}
return bestSolution;
}
public List<ScheduleChromosome> RunAll() {
List<ScheduleChromosome> scheduleChromosomes = InitializePopulation();
return scheduleChromosomes;
}
private List<ScheduleChromosome> InitializePopulation() {
List<ScheduleChromosome> population = new ArrayList<>();
System.out.println("开始初始化种群,目标大小: " + _populationSize);
System.out.println("换型模式: " + (_smoothSetup ? "平滑模式(换型不占用设备时长)" : "标准模式(换型占用设备时长)"));
for (int i = 0; i < _populationSize; i++) {
ScheduleChromosome chromosome = new ScheduleChromosome(baseTime);
chromosome.setGenes(new ArrayList<>());
chromosome.setObjectiveValues(new HashMap<>());
chromosome.setOrders(deepCopyOrderList(_orders));
chromosome.setMachines(deepCopyMachineList(_machines));
// Reset machine states for each new chromosome
for (Machine machine : chromosome.getMachines()) {
machine.setEarliestTime(0);
machine.setTotalTaskTime(0);
}
System.out.println("=== 初始化染色体 " + (i + 1) + " ===");
// 为每个订单分配工序
for (Order order : _orders) {
Product product = _products.stream()
.filter(p -> p.getId() == order.getProductId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("Product not found: " + order.getProductId()));
int remainingQuantity = (int) order.getQuantity();
System.out.println("处理订单 " + order.getId() + ", 产品 " + product.getId() + ", 数量: " + remainingQuantity);
// 订单拆分逻辑
while (remainingQuantity > 0) {
int batchSize;
if (order.isCanSplit() && remainingQuantity > 1) {
int maxSplit = Math.min(remainingQuantity, Math.max(1, remainingQuantity / 2));
batchSize = _random.nextInt(maxSplit) + 1;
} else {
batchSize = remainingQuantity;
}
if (batchSize > remainingQuantity) {
batchSize = remainingQuantity;
}
remainingQuantity -= batchSize;
System.out.println(" 批次大小: " + batchSize + ", 剩余数量: " + remainingQuantity);
// 为当前批次记录每道工序的结束时间(包含后处理时间)
Map<Integer, Integer> operationEndTimes = new HashMap<>();
// 按工序顺序处理当前批次
List<Operation> sortedOperations = product.getOperations().stream()
.sorted(Comparator.comparingInt(Operation::getSequence))
.collect(Collectors.toList());
for (Operation operation : sortedOperations) {
System.out.println(" 处理工序 " + operation.getId() + " (序列: " + operation.getSequence() + ")");
// 获取前一道工序的结束时间(包含后处理时间)
int prevOperationEndTime = operation.getId() > 1 ?
operationEndTimes.getOrDefault(operation.getId() - 1, 0) : 0;
System.out.println(" 前序工序结束时间(含后处理): " + prevOperationEndTime);
MachineOption machineOption;
Machine machine;
// 多设备选择逻辑
if (operation.getMachineOptions().size() > 1) {
Set<Long> machineIds = operation.getMachineOptions().stream()
.map(MachineOption::getMachineId)
.collect(Collectors.toSet());
List<Machine> availableMachines = chromosome.getMachines().stream()
.filter(t -> machineIds.contains(t.getId()))
.collect(Collectors.toList());
if (!availableMachines.isEmpty()) {
Collections.shuffle(availableMachines, _random);
machine = availableMachines.get(0);
Machine finalMachine = machine;
machineOption = operation.getMachineOptions().stream()
.filter(t -> t.getMachineId() == finalMachine.getId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("MachineOption not found for machine: " + finalMachine.getId()));
} else {
machine = chromosome.getMachines().stream()
.filter(t -> machineIds.contains(t.getId()))
.sorted(Comparator.comparingInt(Machine::getEarliestTime)
.thenComparingDouble(Machine::getTotalTaskTime)
.thenComparing(t -> _random.nextDouble()))
.findFirst()
.orElseThrow(() -> new NoSuchElementException("No available machine for operation: " + operation.getId()));
Machine finalMachine = machine;
machineOption = operation.getMachineOptions().stream()
.filter(t -> t.getMachineId() == finalMachine.getId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("MachineOption not found for machine: " + finalMachine.getId()));
}
} else {
machineOption = operation.getMachineOptions().get(0);
MachineOption finalMachineOption = machineOption;
machine = chromosome.getMachines().stream()
.filter(m -> m.getId() == finalMachineOption.getMachineId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("Machine not found: " + finalMachineOption.getMachineId()));
}
System.out.println(" 选择设备: " + machine.getId() + ", 机器选项: " + machineOption.getMachineId());
int processingTime = (int) (machineOption.getProcessingTime() * batchSize);
int teardownTime = machineOption.getTeardownTime();
int preTime = machineOption.getPreTime();
int setupTime = calculateSetupTime(chromosome.getGenes(), order, machine, machineOption);
System.out.println(" 处理时间: " + processingTime + ", 后处理: " + teardownTime +
", 前处理: " + preTime + ", 换型: " + setupTime);
// 根据换型模式计算总处理时间
int totalProcessingTime;
if (_smoothSetup) {
// 平滑模式:换型不占用设备时长
totalProcessingTime = processingTime; // 只计算主处理时间
System.out.println(" 平滑模式:换型时间 " + setupTime + " 分钟不占用设备时长");
} else {
// 标准模式:换型占用设备时长
totalProcessingTime = processingTime + setupTime; // 主处理+换型时间
System.out.println(" 标准模式:换型时间 " + setupTime + " 分钟占用设备时长");
}
// 前处理和后处理都不占用设备时长,只在工序间协调时考虑
System.out.println(" 前处理 " + preTime + " 分钟和后处理 " + teardownTime + " 分钟在非工作时间进行");
// 确定任务的最早开始时间(基于前一道工序的完整结束时间,包含后处理)
int earliestStartTime = prevOperationEndTime;
// 检查设备上是否有前一个任务
Gene lastGeneOnMachine = chromosome.getGenes().stream()
.filter(g -> g.getMachineId() == machine.getId())
.max(Comparator.comparingInt(Gene::getEndTime))
.orElse(null);
if (lastGeneOnMachine != null) {
// 设备上已有任务,当前任务必须在设备可用后开始
// 设备可用时间 = 前一个任务的主处理结束时间(后处理不占用设备)
int machineAvailableTime = lastGeneOnMachine.getEndTime();
if (setupTime > 0) {
if (_smoothSetup) {
machineAvailableTime += setupTime;
// 平滑模式:换型在非工作时间进行,不额外占用设备时间
System.out.println(" 平滑模式换型:在非工作时间进行,设备可用时间不变");
} else {
// 标准模式:换型需要额外占用设备时间
machineAvailableTime += setupTime;
System.out.println(" 标准模式换型:需要额外占用设备 " + setupTime + " 分钟");
}
}
earliestStartTime = Math.max(earliestStartTime, machineAvailableTime);
}
System.out.println(" 最终最早开始时间: " + earliestStartTime +
" (前序工序结束含后处理: " + prevOperationEndTime + ", 设备可用: " +
(lastGeneOnMachine != null ? lastGeneOnMachine.getEndTime() : 0) +
", 换型: " + setupTime + ")");
// 根据换型模式调整处理时间
int processingTimeForScheduling;
if (_smoothSetup) {
// 平滑模式:只需要安排主处理时间
processingTimeForScheduling = processingTime;
System.out.println(" 平滑模式:安排主处理时间 " + processingTime + " 分钟");
} else {
// 标准模式:需要安排主处理时间+换型时间
processingTimeForScheduling = processingTime + setupTime;
System.out.println(" 标准模式:安排主处理+" + setupTime + "分钟换型=" + processingTimeForScheduling + "分钟");
}
// 获取可用时间段(只考虑主处理和换型时间)
List<GeneDetail> geneDetails = GetNextAvailableTime(
machine, earliestStartTime, -1, processingTimeForScheduling,
chromosome.getGenes(), false, true
);
if (geneDetails.isEmpty()) {
System.out.println(" ⚠️ 未找到可用时间段,尝试调整时间");
// 尝试在当前时间基础上增加一些时间重新查找
geneDetails = GetNextAvailableTime(
machine, earliestStartTime + 60, -1, processingTimeForScheduling,
chromosome.getGenes(), false, true
);
}
int startTime = geneDetails.stream().mapToInt(GeneDetail::getStartTime).min().orElse(0);
int endTime = geneDetails.stream().mapToInt(GeneDetail::getEndTime).max().orElse(0);
if (startTime == 0 && endTime == 0) {
System.out.println(" ❌ 无法安排任务,跳过此工序");
continue;
}
System.out.println(" 安排时间: " + ConvertTime(startTime) + " - " + ConvertTime(endTime) +
" (" + (endTime - startTime) + "分钟)");
// 冲突检测和解决
final int finalStartTime = startTime;
final int finalEndTime = endTime;
Gene conflictingGene = chromosome.getGenes().stream()
.filter(g -> g.getMachineId() == machine.getId())
.filter(g -> (finalStartTime < g.getEndTime() && finalEndTime > g.getStartTime()))
.findFirst()
.orElse(null);
if (conflictingGene != null) {
System.out.println(" ⚠️ 检测到时间冲突,重新调度");
int conflictSetupStartTime = conflictingGene.getEndTime();
int conflictSetupTime = calculateSetupTimeForConflict(chromosome.getGenes(), order, machine, machineOption, conflictingGene);
int conflictEarliestStartTime = conflictSetupStartTime + conflictSetupTime;
geneDetails = GetNextAvailableTime(
machine, conflictEarliestStartTime, -1, processingTimeForScheduling,
chromosome.getGenes(), false, true
);
if (!geneDetails.isEmpty()) {
startTime = geneDetails.stream().mapToInt(GeneDetail::getStartTime).min().orElse(0);
endTime = geneDetails.stream().mapToInt(GeneDetail::getEndTime).max().orElse(0);
System.out.println(" 重新安排时间: " + ConvertTime(startTime) + " - " + ConvertTime(endTime));
}
}
// 记录当前工序的结束时间(包含后处理时间)
int currentOperationEndTime = endTime + teardownTime;
operationEndTimes.put(operation.getId(), currentOperationEndTime);
System.out.println(" 当前工序结束时间(含后处理): " + currentOperationEndTime +
" (主处理结束: " + endTime + ", 后处理: " + teardownTime + ")");
// 更新设备状态(后处理不占用设备,所以设备可用时间只到主处理结束)
int machineAvailableTime = endTime;
machine.setEarliestTime(machineAvailableTime);
machine.setTotalTaskTime(machine.getTotalTaskTime() + totalProcessingTime);
System.out.println(" 设备 " + machine.getId() + " 下次可用时间: " + machineAvailableTime);
// 创建基因
Gene gene = new Gene();
gene.setOrderId(order.getId());
gene.setProductId(order.getProductId());
gene.setOperationId(operation.getId());
gene.setMachineId(machine.getId());
gene.setBatchSize(batchSize);
gene.setSetupTime(setupTime);
gene.setStartTime(startTime);
gene.setEndTime(endTime);
gene.setProcessingTime(processingTime);
gene.setTeardownTime(teardownTime);
gene.setPreTime(preTime);
gene.setGeneDetails(deepCopyGeneDetailList(geneDetails));
gene.setId(chromosome.getGenes().size() + 1);
gene.setSequenceId(operation.getSequence());
// 关键修复:根据换型模式设置开始时间
if (!_smoothSetup && setupTime>0) {
// 标准模式:换型占用工作时间,开始时间不包含换型时间
gene.setAbsoluteSetupTime(calculateAbsoluteSetupTime(gene,machine));
gene.setAbsoluteStartTime(calculateMainProcessingStartTime(machine,startTime, calculateAbsoluteSetupTime(gene,machine),geneDetails) );
}else {
gene.setAbsoluteStartTime(startTime);
gene.setAbsoluteSetupTime(setupTime);
}
chromosome.getGenes().add(gene);
System.out.println(" ✅ 工序 " + operation.getId() + " 安排完成");
}
System.out.println(" ✅ 批次 " + batchSize + " 处理完成");
}
}
population.add(chromosome);
System.out.println("✅ 染色体 " + (i + 1) + " 初始化完成,基因数量: " + chromosome.getGenes().size());
}
System.out.println("生成种群大小: " + population.size());
// 去重逻辑
Map<String, List<ScheduleChromosome>> grouped = population.stream()
.collect(Collectors.groupingBy(ScheduleChromosome::getGenesStr));
population = grouped.values().stream()
.map(group -> group.get(0))
.collect(Collectors.toList());
System.out.println("去重后种群大小: " + population.size());
// 调试输出染色体多样性
System.out.println("染色体多样性分析:");
Map<String, Integer> keyCounts = new HashMap<>();
for (ScheduleChromosome chrom : population) {
String key = chrom.getGenesStr();
keyCounts.put(key, keyCounts.getOrDefault(key, 0) + 1);
}
System.out.println("唯一染色体数量: " + keyCounts.size());
System.out.println("染色体分布: " + keyCounts);
// 并行计算适应度
population.parallelStream().forEach(this::CalculateFitness);
// 打印摘要
population.forEach(this::WriteScheduleSummary);
return population;
}
private void CalculateFitness(ScheduleChromosome chromosome) {
// 1. 最大完工时间
double makespan = chromosome.getGenes().stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
// 2. 总延迟时间
double tardiness = 0.0;
Map<Integer, List<Gene>> orderGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
int orderCompletion = group.getValue().stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
Order order = _orders.stream()
.filter(o -> o.getId() == group.getKey())
.findFirst()
.orElse(null);
if (order != null) {
LocalDateTime completionTime = chromosome.getBaseTime().plusMinutes(orderCompletion);
LocalDateTime dueDateTime = order.getDueDate().toLocalDateTime();
if (completionTime.isAfter(dueDateTime)) {
long hours = ChronoUnit.HOURS.between(dueDateTime, completionTime);
long minutes = ChronoUnit.MINUTES.between(dueDateTime, completionTime) % 60;
tardiness += hours + (double) minutes / 60;
}
}
}
// 3. 总换型时间
double totalSetupTime = CalculateTotalSetupTime(chromosome);
// 4. 总流程时间
double totalFlowTime = CalculateTotalFlowTime(chromosome);
// 5. 机器负载均衡
double machineLoadBalance = CalculateMachineLoadBalance(chromosome);
// 存储目标值
HashMap<Integer, Double> objMap = new HashMap<>();
objMap.put(0, makespan);
objMap.put(1, tardiness);
objMap.put(2, totalSetupTime);
objMap.put(3, totalFlowTime);
objMap.put(4, machineLoadBalance);
chromosome.setObjectiveValues(objMap);
// 计算适应度
chromosome.setFitness(
0.4 * (1.0 / (1 + makespan)) +
0.3 * (1.0 / (1 + tardiness)) +
0.1 * (1.0 / (1 + totalSetupTime)) +
0.1 * (1.0 / (1 + totalFlowTime)) +
0.1 * machineLoadBalance
);
}
private double CalculateTotalSetupTime(ScheduleChromosome chromosome) {
double totalSetupTime = 0.0;
Map<Long, List<Gene>> machineGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getMachineId));
for (Map.Entry<Long, List<Gene>> machineGroup : machineGroups.entrySet()) {
List<Gene> sortedGenes = machineGroup.getValue().stream()
.sorted(Comparator.comparingInt(Gene::getStartTime))
.collect(Collectors.toList());
Integer lastProductId = null;
for (Gene gene : sortedGenes) {
if (lastProductId != null && lastProductId != gene.getProductId()) {
Product product = _products.stream()
.filter(p -> p.getId() == gene.getProductId())
.findFirst()
.orElse(null);
if (product == null) continue;
Operation operation = product.getOperations().stream()
.filter(o -> o.getId() == gene.getOperationId())
.findFirst()
.orElse(null);
if (operation == null) continue;
MachineOption machineOption = operation.getMachineOptions().stream()
.filter(m -> m.getMachineId() == gene.getMachineId())
.findFirst()
.orElse(null);
if (machineOption != null) {
totalSetupTime += machineOption.getSetupTime();
}
}
lastProductId = gene.getProductId();
}
}
return totalSetupTime;
}
private double CalculateTotalFlowTime(ScheduleChromosome chromosome) {
double totalFlowTime = 0.0;
Map<Integer, List<Gene>> orderGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
int minStartTime = group.getValue().stream().mapToInt(Gene::getStartTime).min().orElse(0);
int maxEndTime = group.getValue().stream().mapToInt(Gene::getEndTime).max().orElse(0);
LocalDateTime start = chromosome.getBaseTime().plusMinutes(minStartTime);
LocalDateTime end = chromosome.getBaseTime().plusMinutes(maxEndTime);
long hours = ChronoUnit.HOURS.between(start, end);
long minutes = ChronoUnit.MINUTES.between(start, end) % 60;
totalFlowTime += hours + (double) minutes / 60;
}
return totalFlowTime;
}
private double CalculateMachineLoadBalance(ScheduleChromosome chromosome) {
Map<Integer, Double> machineUtilization = new HashMap<>();
int maxEndTime = chromosome.getGenes().stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
if (maxEndTime == 0) return 0.0;
for (Machine machine : _machines) {
double busyTime = chromosome.getGenes().stream()
.filter(g -> g.getMachineId() == machine.getId())
.mapToInt(g -> g.getEndTime() - g.getStartTime())
.sum();
machineUtilization.put((int) machine.getId(), busyTime / maxEndTime);
}
double avgUtilization = machineUtilization.values().stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
double variance = machineUtilization.values().stream()
.mapToDouble(u -> Math.pow(u - avgUtilization, 2))
.average()
.orElse(0.0);
return 1.0 / (1 + variance);
}
private int calculateSetupTime(List<Gene> existingGenes, Order currentOrder, Machine machine, MachineOption machineOption) {
Gene lastGeneOnMachine = existingGenes.stream()
.filter(g -> g.getMachineId() == machine.getId())
.max(Comparator.comparingInt(Gene::getEndTime))
.orElse(null);
if (lastGeneOnMachine == null) {
System.out.println("设备 " + machine.getId() + " 上无历史任务,换型时间为0");
return 0;
}
int setupTime = (lastGeneOnMachine.getProductId() != currentOrder.getProductId())
? machineOption.getSetupTime()
: 0;
if (setupTime > 0) {
System.out.println("设备 " + machine.getId() + " 需要换型,因为产品从 " + lastGeneOnMachine.getProductId() + " 变更为 " + currentOrder.getProductId());
}
return setupTime;
}
/**
* 计算主处理开始时间,确保在工作时间内
*/
/**
* 计算主处理开始时间,基于设备日历确保在工作时间内
*/
/**
* 计算主处理开始时间,基于设备日历确保在工作时间内
*/
/**
* 计算主处理开始时间
*/
private int calculateMainProcessingStartTime(Machine machine, int setupStartTime, int absoluteSetupTime, List<GeneDetail> geneDetails) {
System.out.println(" 计算主处理开始时间:");
System.out.println(" 换型开始时间: " + ConvertTime(setupStartTime) + " (" + baseTime.plusMinutes(setupStartTime) + ")");
System.out.println(" 绝对换型时长: " + absoluteSetupTime + "分钟 (" + (absoluteSetupTime / 60.0) + "小时)");
// 主处理开始时间 = 换型开始时间 + 绝对换型时长
int mainProcessingStartTime = setupStartTime + absoluteSetupTime;
System.out.println(" 计算主处理开始时间: " + ConvertTime(mainProcessingStartTime) + " (" + baseTime.plusMinutes(mainProcessingStartTime) + ")");
return mainProcessingStartTime;
}
/**
* 调整时间到设备的下一个可用工作时间
*/
private int adjustToMachineWorkingTime(Machine machine, int minute) {
LocalDateTime time = baseTime.plusMinutes(minute);
System.out.println(" 调整时间到工作时间: " + ConvertTime(minute));
// 查找包含或最接近给定时间的可用时间段
for (TimeSegment segment : machine.getAvailability()) {
if (segment.getType() != SegmentType.MAINTENANCE && !segment.isUsed()) {
LocalDateTime segmentStart = segment.getStart();
LocalDateTime segmentEnd = segment.getEnd();
// 如果时间在时间段内,直接返回
if ((time.isAfter(segmentStart) || time.equals(segmentStart)) && time.isBefore(segmentEnd)) {
System.out.println(" 时间 " + ConvertTime(minute) + " 在工作时间内,无需调整");
return minute;
}
// 如果时间在时间段之前,返回时间段的开始时间
if (time.isBefore(segmentStart)) {
int adjustedMinute = (int) ChronoUnit.MINUTES.between(baseTime, segmentStart);
System.out.println(" 时间 " + ConvertTime(minute) + " 在工作时间前,调整到 " + ConvertTime(adjustedMinute));
return adjustedMinute;
}
}
}
// 如果没有找到合适的时间段,生成新的时间段
System.out.println(" 未找到合适的工作时间段,生成新时间段");
List<TimeSegment> timeSegments = _machineScheduler.generateTimeSegment(machine, time);
machine.getAvailability().addAll(timeSegments);
// 查找新生成的时间段
for (TimeSegment segment : timeSegments) {
if (segment.getType() != SegmentType.MAINTENANCE && !segment.isUsed()) {
LocalDateTime segmentStart = segment.getStart();
int adjustedMinute = (int) ChronoUnit.MINUTES.between(baseTime, segmentStart);
System.out.println(" 生成新时间段,调整到 " + ConvertTime(adjustedMinute));
return adjustedMinute;
}
}
// 如果仍然没有找到,返回原时间
System.out.println(" ⚠️ 无法找到合适的工作时间,使用原时间");
return minute;
}
/**
* 计算绝对换型时间(仅用于标准模式)
* 绝对换型时间 = 计划换型时间 + 非工作时间间隔
*/
/**
* 计算绝对换型时间(仅用于标准模式)
* 绝对换型时间 = 计划换型时间 + 非工作时间间隔
* @return 绝对换型时长(分钟)
*/
/**
* 计算绝对换型时间(仅用于标准模式)
* 绝对换型时间 = 计划换型时间 + 非工作时间间隔
* @return 绝对换型时长(分钟)
*/
/**
* 计算绝对换型时间(仅用于标准模式)
* 绝对换型时间 = 计划换型时间 + 非工作时间间隔
* @return 绝对换型时长(分钟)
*/
private int calculateAbsoluteSetupTime(Gene gene, Machine machine) {
if (gene.getGeneDetails() == null || gene.getGeneDetails().isEmpty()) {
System.out.println(" 计算绝对换型时间: 无时间段详情,使用计划时间 " + gene.getSetupTime() + "分钟");
return gene.getSetupTime();
}
// 按开始时间排序时间段
List<GeneDetail> sortedDetails = gene.getGeneDetails().stream()
.sorted(Comparator.comparingInt(GeneDetail::getStartTime))
.collect(Collectors.toList());
int remainingSetupTime = gene.getSetupTime();
int accumulatedAbsoluteTime = 0;
int currentTime = gene.getStartTime(); // 当前时间,从换型开始时间开始
System.out.println(" 计算绝对换型时间:");
System.out.println(" 开始时间: " + ConvertTime(currentTime) + " (" + baseTime.plusMinutes(currentTime) + ")");
System.out.println(" 计划换型时间: " + remainingSetupTime + "分钟");
// 遍历时间段,分配换型时间
for (GeneDetail detail : sortedDetails) {
int segmentStart = detail.getStartTime();
int segmentEnd = detail.getEndTime();
int segmentDuration = segmentEnd - segmentStart;
System.out.println(" 处理时间段: " + ConvertTime(segmentStart) + " - " + ConvertTime(segmentEnd) +
" (" + segmentDuration + "分钟)");
// 如果当前时间在时间段之前,需要等待到时间段开始
if (currentTime < segmentStart) {
int waitTime = segmentStart - currentTime;
accumulatedAbsoluteTime += waitTime;
System.out.println(" 等待时间段开始: " + waitTime + "分钟 (" +
ConvertTime(currentTime) + " → " + ConvertTime(segmentStart) + ")");
currentTime = segmentStart;
}
// 如果当前时间在时间段内
if (currentTime >= segmentStart && currentTime < segmentEnd) {
int availableTimeInSegment = segmentEnd - currentTime;
System.out.println(" 时间段内可用时间: " + availableTimeInSegment + "分钟");
if (availableTimeInSegment >= remainingSetupTime) {
// 当前时间段可以完成剩余换型
accumulatedAbsoluteTime += remainingSetupTime;
currentTime += remainingSetupTime;
remainingSetupTime = 0;
System.out.println(" 换型完成于: " + ConvertTime(currentTime));
break;
} else {
// 使用整个时间段的剩余时间
accumulatedAbsoluteTime += availableTimeInSegment;
remainingSetupTime -= availableTimeInSegment;
currentTime = segmentEnd;
System.out.println(" 使用时间段剩余时间: " + availableTimeInSegment + "分钟");
System.out.println(" 剩余换型时间: " + remainingSetupTime + "分钟");
}
}
}
// 如果换型未完成,使用计划时间
if (remainingSetupTime > 0) {
System.out.println(" 换型未完成,剩余时间: " + remainingSetupTime + "分钟,使用计划时间");
accumulatedAbsoluteTime += remainingSetupTime;
}
System.out.println(" 最终绝对换型时长: " + accumulatedAbsoluteTime + "分钟 (" + (accumulatedAbsoluteTime / 60.0) + "小时)");
System.out.println(" 非工作时间间隔: " + (accumulatedAbsoluteTime - gene.getSetupTime()) + "分钟");
return accumulatedAbsoluteTime;
}
private int calculateSetupTimeForConflict(List<Gene> existingGenes, Order currentOrder, Machine machine,
MachineOption machineOption, Gene conflictingGene) {
int setupTime = (conflictingGene.getProductId() != currentOrder.getProductId())
? machineOption.getSetupTime()
: 0;
if (existingGenes.stream().filter(g -> g.getMachineId() == machine.getId()).count() <= 1) {
setupTime = 0;
}
if (setupTime > 0) {
System.out.println("设备 " + machine.getId() + " 需要换型,因为产品从 " + conflictingGene.getProductId() + " 变更为 " + currentOrder.getProductId());
}
return setupTime;
}
private List<GeneDetail> GetNextAvailableTime(
Machine machine, int proposedStartTime, int prevtime,
int processingTime, List<Gene> existingTasks,
boolean IsInterrupt, boolean istask
) {
LocalDateTime startTime = baseTime.plusMinutes(proposedStartTime);
String prevtimeStr = prevtime > -1 ? baseTime.plusMinutes(prevtime).toString() : "";
return FindEarliestStart(machine, processingTime, startTime, prevtimeStr, existingTasks, istask);
}
private List<GeneDetail> FindEarliestStart(
Machine machine, int processingTime, LocalDateTime currentTime,
String prevtime, List<Gene> existingTasks, boolean checkprevtime
) {
List<Gene> machineTasks = existingTasks.stream()
.filter(t -> t.getMachineId() == machine.getId())
.sorted(Comparator.comparingInt(Gene::getStartTime))
.collect(Collectors.toList());
List<GeneDetail> times = new ArrayList<>();
TimeSegment slot = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime);
if (slot == null) return times;
LocalDateTime prevTimeDateTime = StringUtils.isEmpty(prevtime) ? null : LocalDateTime.parse(prevtime);
LocalDateTime startCandidate = slot.getStart().isAfter(prevTimeDateTime != null ? prevTimeDateTime : currentTime)
? slot.getStart()
: (prevTimeDateTime != null ? prevTimeDateTime : currentTime);
LocalDateTime endCandidate = startCandidate.plusMinutes(processingTime);
if (endCandidate.isAfter(slot.getEnd())) {
return CaldEarliestStart(machine, processingTime, currentTime, prevtime, machineTasks, checkprevtime);
} else {
GeneDetail time = new GeneDetail();
time.setKey(slot.getKey());
time.setStartTime((int) ChronoUnit.MINUTES.between(baseTime, startCandidate));
time.setEndTime((int) ChronoUnit.MINUTES.between(baseTime, endCandidate));
times.add(time);
RemoveMachineAvailable(machine, time);
return times;
}
}
private List<GeneDetail> CaldEarliestStart(
Machine machine, int processingTime, LocalDateTime currentTime,
String prevtime, List<Gene> machineTasks, boolean checkprevtime
) {
int remainingTime = processingTime;
LocalDateTime st = StringUtils.isEmpty(prevtime) ? currentTime : LocalDateTime.parse(prevtime);
LocalDateTime prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
List<GeneDetail> times = new ArrayList<>();
List<GeneDetail> oldTimes = new ArrayList<>();
while (remainingTime > 0) {
TimeSegment shift = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime);
if (shift == null) break;
LocalDateTime shiftStart = shift.getStart();
LocalDateTime shiftEnd = shift.getEnd();
// 检查班次间冲突
if (!prevEnd.isEqual(LocalDateTime.of(2000, 1, 1, 0, 0, 0))) {
if (prevEnd.isBefore(currentTime)) {
// 检查班次间任务
LocalDateTime finalPrevEnd = prevEnd;
boolean hasTask = machineTasks.stream()
.anyMatch(t -> {
LocalDateTime taskStart = baseTime.plusMinutes(t.getStartTime());
return taskStart.isAfter(finalPrevEnd) && taskStart.isBefore(shiftStart);
});
if (hasTask) {
// 重置状态
currentTime = shiftStart;
st = shiftStart;
remainingTime = processingTime;
prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
oldTimes.addAll(deepCopyGeneDetailList(times));
times.clear();
continue;
}
// 检查班次间维修窗口
if (machine.getMaintenanceWindows() != null) {
LocalDateTime finalPrevEnd1 = prevEnd;
boolean hasMaintenance = machine.getMaintenanceWindows().stream()
.anyMatch(w -> w.getStartTime().isAfter(finalPrevEnd1) && w.getStartTime().isBefore(shiftStart));
if (hasMaintenance) {
currentTime = shiftStart;
st = shiftStart;
remainingTime = processingTime;
prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
times.clear();
continue;
}
}
}
}
prevEnd = shiftEnd;
// 计算有效时间
LocalDateTime effectiveStart = st.isAfter(shiftStart) ? st : shiftStart;
long availableMinutes = ChronoUnit.MINUTES.between(effectiveStart, shiftEnd);
// 处理当前班次
int processable = Math.min(remainingTime, (int) availableMinutes);
remainingTime -= processable;
currentTime = effectiveStart.plusMinutes(processable);
// 添加时间详情
GeneDetail time = new GeneDetail();
time.setKey(shift.getKey());
time.setStartTime((int) ChronoUnit.MINUTES.between(baseTime, effectiveStart));
time.setEndTime((int) ChronoUnit.MINUTES.between(baseTime, currentTime));
times.add(time);
RemoveMachineAvailable(machine, time);
}
// 还原未使用的时间段
AddMachineAvailable(machine, oldTimes);
return times;
}
private TimeSegment GetCurrentOrNextShift(Machine machine, LocalDateTime time, String prevtime, boolean checkprevtime) {
TimeSegment start = null;
// 查找有效班次
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);
if (start == null) {
// 生成新时间段
List<TimeSegment> timeSegments = _machineScheduler.generateTimeSegment(machine, time);
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);
}
// 递归查找
return GetCurrentOrNextShift(machine, time, prevtime, checkprevtime);
}
return start;
}
private void RemoveMachineAvailable(Machine machine, GeneDetail geneDetails) {
List<TimeSegment> timeSegments = new ArrayList<>();
// 查找对应时间段
int index = machine.getAvailability().stream()
.filter(t -> t.getKey().equals(geneDetails.getKey()))
.findFirst()
.map(machine.getAvailability()::indexOf)
.orElse(-1);
if (index > -1) {
TimeSegment targetSegment = machine.getAvailability().get(index);
LocalDateTime geneEndTime = baseTime.plusMinutes(geneDetails.getEndTime());
if (targetSegment.getEnd().isAfter(geneEndTime)) {
// 拆分时间段
TimeSegment usedSegment = new TimeSegment();
usedSegment.setStart(baseTime.plusMinutes(geneDetails.getStartTime()));
usedSegment.setEnd(geneEndTime);
usedSegment.setHoliday(false);
usedSegment.setKey(UUID.randomUUID().toString());
usedSegment.setType(SegmentType.REGULAR);
usedSegment.setUsed(true);
timeSegments.add(usedSegment);
// 更新基因详情Key
geneDetails.setKey(usedSegment.getKey());
// 更新原时间段开始时间
targetSegment.setStart(geneEndTime);
} else {
targetSegment.setUsed(true);
}
}
// 添加新段并排序
if (!timeSegments.isEmpty()) {
machine.getAvailability().addAll(timeSegments);
}
machine.getAvailability().sort(Comparator.comparing(TimeSegment::getStart));
}
private void AddMachineAvailable(Machine machine, List<GeneDetail> geneDetails) {
if (geneDetails == null || geneDetails.isEmpty()) return;
// 标记时间段为未占用
for (GeneDetail detail : geneDetails) {
machine.getAvailability().stream()
.filter(t -> t.getKey().equals(detail.getKey()))
.findFirst()
.ifPresent(t -> t.setUsed(false));
}
// 合并时间段
machine.setAvailability(MergeSegments(machine.getAvailability()));
}
private List<TimeSegment> MergeSegments(List<TimeSegment> segments) {
// 分离维护段、未占用段、已占用段
List<TimeSegment> maintenanceSegments = segments.stream()
.filter(t -> t.getType() == SegmentType.MAINTENANCE)
.collect(Collectors.toList());
List<TimeSegment> unusedRegularSegments = segments.stream()
.filter(t -> t.getType() != SegmentType.MAINTENANCE && !t.isUsed())
.collect(Collectors.toList());
List<TimeSegment> usedRegularSegments = segments.stream()
.filter(t -> t.getType() != SegmentType.MAINTENANCE && t.isUsed())
.collect(Collectors.toList());
// 排序未占用段
unusedRegularSegments.sort(Comparator.comparing(TimeSegment::getStart));
// 合并未占用段
Deque<TimeSegment> mergedUnused = new ArrayDeque<>();
for (TimeSegment seg : unusedRegularSegments) {
if (!mergedUnused.isEmpty() && mergedUnused.peekLast().getEnd().isAfter(seg.getStart())) {
TimeSegment last = mergedUnused.pollLast();
last.setEnd(last.getEnd().isAfter(seg.getEnd()) ? last.getEnd() : seg.getEnd());
mergedUnused.offerLast(last);
} else {
mergedUnused.offerLast(seg);
}
}
// 重组结果
List<TimeSegment> result = new ArrayList<>(mergedUnused);
result.addAll(usedRegularSegments);
result.addAll(maintenanceSegments);
result.sort(Comparator.comparing(TimeSegment::getStart));
return result;
}
public void PrintScheduleSummary(ScheduleChromosome schedule) {
System.out.println("\n=== Schedule Summary ===");
// 按订单分组打印
Map<Integer, List<Gene>> orderGroups = schedule.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
System.out.printf("\nOrder %d Schedule:%n", group.getKey());
// 按工序排序
List<Gene> sortedJobs = group.getValue().stream()
.sorted(Comparator.comparingInt(Gene::getOperationId))
.collect(Collectors.toList());
for (Gene job : sortedJobs) {
Product product = _products.stream()
.filter(p -> p.getId() == job.getProductId())
.findFirst()
.orElse(null);
Operation operation = product != null ? product.getOperations().stream()
.filter(o -> o.getId() == job.getOperationId())
.findFirst()
.orElse(null) : null;
if (product != null && operation != null) {
String print = String.format(
"[%s-%s] Order %d, Product %s, Machine %d, Operation %d, Batch %d, processingTime %d, setupTime %d, teardownTime %d",
ConvertTime(job.getStartTime()),
ConvertTime(job.getEndTime()),
job.getOrderId(),
product.getName(),
job.getMachineId(),
operation.getSequence(),
job.getBatchSize(),
job.getProcessingTime(),
job.getSetupTime(),
job.getTeardownTime()
);
System.out.println(print);
}
}
}
}
public void WriteScheduleSummary(ScheduleChromosome schedule) {
// 写入日志
FileHelper.writeLogFile(String.format("\n=== Schedule Summary === %f", schedule.getFitness()));
FileHelper.writeLogFile(String.format("Makespan: %f minutes", schedule.getObjectiveValues().get(0)));
FileHelper.writeLogFile(String.format("Total Tardiness: %f hours", schedule.getObjectiveValues().get(1)));
FileHelper.writeLogFile(String.format("Setup Time: %f minutes", schedule.getObjectiveValues().get(2)));
FileHelper.writeLogFile(String.format("Flow Time: %f minutes", schedule.getObjectiveValues().get(3)));
FileHelper.writeLogFile(String.format("Machine Load Balance: %.2f%%", schedule.getObjectiveValues().get(4) * 100));
System.out.println(schedule.getSceneId());
FileHelper.writeLogFile("-------------------------");
// 按订单分组写入
Map<Integer, List<Gene>> orderGroups = schedule.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
List<Gene> sortedJobs = group.getValue().stream()
.sorted(Comparator.comparingInt(Gene::getOperationId))
.collect(Collectors.toList());
for (Gene job : sortedJobs) {
Product product = _products.stream()
.filter(p -> p.getId() == job.getProductId())
.findFirst()
.orElse(null);
Operation operation = product != null ? product.getOperations().stream()
.filter(o -> o.getId() == job.getOperationId())
.findFirst()
.orElse(null) : null;
if (product != null && operation != null) {
StringBuilder sb = new StringBuilder();
sb.append(String.format(
"[%d-%d]:[%s-%s] Order %d, Product %s, Machine %d, Operation %d, Batch %d, processingTime %d, setupTime %d, teardownTime %d",
job.getStartTime(),
job.getEndTime(),
ConvertTime(job.getStartTime()),
ConvertTime(job.getEndTime()),
job.getOrderId(),
product.getName(),
job.getMachineId(),
operation.getSequence(),
job.getBatchSize(),
job.getProcessingTime(),
job.getSetupTime(),
job.getTeardownTime()
));
// 追加基因详情
for (GeneDetail 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("");
}
}
private String ConvertTime(int minute) {
return baseTime.plusMinutes(minute).format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm"));
}
// ========================== 深拷贝工具方法 ==========================
private List<Product> deepCopyProductList(List<Product> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyProduct).collect(Collectors.toList());
}
private Product deepCopyProduct(Product source) {
if (source == null) return null;
Product copy = new Product();
copy.setId(source.getId());
copy.setName(source.getName());
copy.setOperations(deepCopyOperationList(source.getOperations()));
return copy;
}
private List<Operation> deepCopyOperationList(List<Operation> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyOperation).collect(Collectors.toList());
}
private Operation deepCopyOperation(Operation source) {
if (source == null) return null;
Operation copy = new Operation();
copy.setId(source.getId());
copy.setProductId(source.getProductId());
copy.setSequence(source.getSequence());
copy.setInterrupt(source.isInterrupt());
copy.setMachineOptions(deepCopyMachineOptionList(source.getMachineOptions()));
return copy;
}
private List<MachineOption> deepCopyMachineOptionList(List<MachineOption> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyMachineOption).collect(Collectors.toList());
}
private MachineOption deepCopyMachineOption(MachineOption source) {
if (source == null) return null;
MachineOption copy = new MachineOption();
copy.setMachineId(source.getMachineId());
copy.setProcessingTime(source.getProcessingTime());
copy.setSetupTime(source.getSetupTime());
copy.setTeardownTime(source.getTeardownTime());
copy.setPreTime(source.getPreTime());
return copy;
}
private List<Machine> deepCopyMachineList(List<Machine> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyMachine).collect(Collectors.toList());
}
private Machine deepCopyMachine(Machine source) {
if (source == null) return null;
Machine copy = new Machine();
copy.setId(source.getId());
copy.setName(source.getName());
copy.setEarliestTime(source.getEarliestTime());
copy.setTotalTaskTime(source.getTotalTaskTime());
copy.setShifts(deepCopyShiftList(source.getShifts()));
copy.setMaintenanceWindows(deepCopyMaintenanceWindowList(source.getMaintenanceWindows()));
copy.setAvailability(deepCopyTimeSegmentList(source.getAvailability()));
copy.setShiftsChanged(source.getShiftsChanged());
copy.setMaintenanceWindowsChanged(source.getMaintenanceWindowsChanged());
return copy;
}
private List<Shift> deepCopyShiftList(List<Shift> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyShift).collect(Collectors.toList());
}
private Shift deepCopyShift(Shift source) {
if (source == null) return null;
Shift copy = new Shift();
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
copy.setDays(source.getDays() != null ? new HashSet<>(source.getDays()) : null);
copy.setShiftDate(source.getShiftDate());
copy.setTemporaryShift(source.isTemporaryShift());
copy.setPriority(source.getPriority());
return copy;
}
private List<MaintenanceWindow> deepCopyMaintenanceWindowList(List<MaintenanceWindow> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyMaintenanceWindow).collect(Collectors.toList());
}
private MaintenanceWindow deepCopyMaintenanceWindow(MaintenanceWindow source) {
if (source == null) return null;
MaintenanceWindow copy = new MaintenanceWindow();
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
copy.setReason(source.getReason());
return copy;
}
private List<TimeSegment> deepCopyTimeSegmentList(List<TimeSegment> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyTimeSegment).collect(Collectors.toList());
}
private TimeSegment deepCopyTimeSegment(TimeSegment source) {
if (source == null) return null;
TimeSegment copy = new TimeSegment();
copy.setKey(source.getKey());
copy.setStart(source.getStart());
copy.setEnd(source.getEnd());
copy.setEarliestTime(source.getEarliestTime());
copy.setTotalTaskTime(source.getTotalTaskTime());
copy.setType(source.getType());
copy.setHoliday(source.isHoliday());
copy.setUsed(source.isUsed());
return copy;
}
private List<Order> deepCopyOrderList(List<Order> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyOrder).collect(Collectors.toList());
}
private Order deepCopyOrder(Order source) {
if (source == null) return null;
Order copy = new Order();
copy.setId(source.getId());
copy.setProductId(source.getProductId());
copy.setQuantity(source.getQuantity());
copy.setDueDate(source.getDueDate());
copy.setOrderCompletion(source.getOrderCompletion());
copy.setTardiness(source.getTardiness());
copy.setPriority(source.getPriority());
copy.setCanSplit(source.isCanSplit());
copy.setCanInterrupt(source.isCanInterrupt());
return copy;
}
private List<Gene> deepCopyGeneList(List<Gene> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyGene).collect(Collectors.toList());
}
private Gene deepCopyGene(Gene source) {
if (source == null) return null;
Gene copy = new Gene();
copy.setOrderId(source.getOrderId());
copy.setProductId(source.getProductId());
copy.setOperationId(source.getOperationId());
copy.setMachineId(source.getMachineId());
copy.setBatchSize(source.getBatchSize());
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
copy.setProcessingTime(source.getProcessingTime());
copy.setTeardownTime(source.getTeardownTime());
copy.setSetupTime(source.getSetupTime());
copy.setPreTime(source.getPreTime());
copy.setGeneDetails(deepCopyGeneDetailList(source.getGeneDetails()));
return copy;
}
private List<GeneDetail> deepCopyGeneDetailList(List<GeneDetail> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyGeneDetail).collect(Collectors.toList());
}
private GeneDetail deepCopyGeneDetail(GeneDetail source) {
if (source == null) return null;
GeneDetail copy = new GeneDetail();
copy.setKey(source.getKey());
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
return copy;
}
private ScheduleChromosome deepCopyScheduleChromosome(ScheduleChromosome source) {
if (source == null) return null;
ScheduleChromosome copy = new ScheduleChromosome(source.getBaseTime());
copy.setGenes(deepCopyGeneList(source.getGenes()));
copy.setOrders(deepCopyOrderList(source.getOrders()));
copy.setMachines(deepCopyMachineList(source.getMachines()));
copy.setObjectiveValues(new HashMap<>(source.getObjectiveValues()));
copy.setFitness(source.getFitness());
copy.setTardiness(source.getTardiness());
return copy;
}
// ========================== 辅助类:StringUtils(对应C#字符串工具) ==========================
private static class StringUtils {
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment