Commit 25c58c5b authored by Tong Li's avatar Tong Li

算法优化

parent 9d08cc4f
......@@ -465,7 +465,7 @@ public class ResourceGanttController {
ParamValidator.validateSceneExists(sceneService, sceneId);
planResultService.InsertOrderAuto(sceneId, newOrderData);
// planResultService.InsertOrderAuto(sceneId, newOrderData);
return R.ok("自动插单成功");
}
......
......@@ -17,7 +17,7 @@ public class ScheduleParams {
private static final int MIN_POPULATION_SIZE = 10;
private static final int MAX_POPULATION_SIZE = 50;
private static final int MIN_MAX_ITERATIONS = 50;
private static final int MAX_MAX_ITERATIONS = 250;
private static final int MAX_MAX_ITERATIONS = 100;
private static final float MIN_CROSSOVER_PROB = 0.6f;
private static final float MAX_CROSSOVER_PROB = 0.9f;
private static final float MIN_MUTATION_PROB = 0.2f;
......
......@@ -417,13 +417,13 @@ public class GeneticAlgorithm {
}
// 加载锁定工单到ResultOld
List<GAScheduleResult> lockedOrders = GlobalCacheUtil.get("locked_orders_" + sceneId);
if (lockedOrders != null && !lockedOrders.isEmpty()) {
chromosome.setResultOld(ProductionDeepCopyUtil.deepCopyList(lockedOrders, GAScheduleResult.class));
FileHelper.writeLogFile("将 " + lockedOrders.size() + " 个锁定工单加载到初始种群中");
} else {
chromosome.setResultOld(new CopyOnWriteArrayList<>());
}
// List<GAScheduleResult> lockedOrders = GlobalCacheUtil.get("locked_orders_" + sceneId);
// if (lockedOrders != null && !lockedOrders.isEmpty()) {
// chromosome.setResultOld(ProductionDeepCopyUtil.deepCopyList(lockedOrders, GAScheduleResult.class));
// FileHelper.writeLogFile("将 " + lockedOrders.size() + " 个锁定工单加载到初始种群中");
// } else {
// chromosome.setResultOld(new CopyOnWriteArrayList<>());
// }
decoder.decodeChromosomeWithCache(chromosome);
if (chromosome.getFitness() == 0) {
......
......@@ -471,10 +471,14 @@ if(finishedOrder==null||finishedOrder.size()==0)
// 步骤2:按OperationSequencing顺序调度工序
Map<Integer, Integer> orderProcessCounter = new HashMap<>();
Map<Integer, Integer> orderLastEndTime = new HashMap<>();
Map<Integer, Entry> entryIndexById = new HashMap<>();
Map<Integer, List<Entry>> entrysBygroupId = new HashMap<>();
for (Entry op : allOperations) {
int groupId = op.getGroupId();
orderProcessCounter.putIfAbsent(groupId, 0);
orderLastEndTime.putIfAbsent(groupId, 0);
entryIndexById.put(op.getId(),op);
entrysBygroupId.computeIfAbsent(groupId, k -> new ArrayList<>()).add(op);
}
// Map<Long, String> machineState = chromosome.getMachines().stream()
......@@ -483,10 +487,12 @@ if(finishedOrder==null||finishedOrder.size()==0)
// List<Entry> allScheduledOps = new ArrayList<>();
// 缓存机器任务
Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache = new HashMap<>();
for (Entry op : allOperations) {
}
for (int groupId : chromosome.getOperationSequencing()) {
int scheduledCount = orderProcessCounter.get(groupId);
List<Entry> orderOps = allOperations.stream()
.filter(t -> t.getGroupId() == groupId)
List<Entry> orderOps = entrysBygroupId.get(groupId).stream()
.sorted(Comparator.comparing(Entry::getSequence))
.collect(Collectors.toList());
......@@ -1243,12 +1249,12 @@ return actualEndTime;
}
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);
// 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())
{
......@@ -1385,7 +1391,9 @@ return actualEndTime;
// FileHelper.writeLogFile(" 结束 "+ConvertTime(startTime)+"--"+ConvertTime(endTime)+" "+operation.getGroupId()+" : "+operation.getId()+",处理时间: " + processingTime + ", 后处理: " + teardownTime +
// ", 前处理: " + preTime + ", 换型: " + setupTime+ ", 数量: " + operation.getQuantity()+ ", 设备: "+machine.getId()+ ", 是否可中断: "+operation.getIsInterrupt());
machineTasksCache.remove(machine.getId());
if (machineTasks != null) {
machineTasks.add(result);
}
return endTime;
}
......@@ -2399,24 +2407,21 @@ if(MaterialRequirements==null||MaterialRequirements.size()==0)
private void calculateScheduleResult(Chromosome chromosome) {
double[] Objectives=new double[_globalParam.getObjectiveWeights().size()];
double[] Objectives = new double[_globalParam.getObjectiveWeights().size()];
int i=0;
int i = 0;
for (ObjectiveConfig config : _globalParam.getObjectiveConfigs()) {
if( config.isEnabled())
{
if(config.getName()==GlobalParam.OBJECTIVE_MAKESPAN)
{
if (config.isEnabled()) {
if (config.getName() == GlobalParam.OBJECTIVE_MAKESPAN) {
// 1. 最早完工时间(最小化)
double makespan = chromosome.getResult().stream()
.mapToInt(GAScheduleResult::getEndTime)
.max()
.orElse(0);
Objectives[i]=makespan;
Objectives[i] = makespan;
chromosome.setMakespan(makespan);
}
if(config.getName()==GlobalParam.OBJECTIVE_TARDINESS)
{
if (config.getName() == GlobalParam.OBJECTIVE_TARDINESS) {
// 2. 交付期满足情况(最小化延迟)
double tardiness = 0;
......@@ -2435,60 +2440,57 @@ if(MaterialRequirements==null||MaterialRequirements.size()==0)
.sorted()
.collect(Collectors.toList());
Order order = chromosome.getOrders().stream()
.filter(t->orderIds.contains(t.getOrderId()))
.filter(t -> orderIds.contains(t.getOrderId()))
.max(Comparator.comparing(Order::getDueDate))
.orElse(null);
if(order.isNewCreate())
{continue;}
LocalDateTime dueDateTime=order.getDueDate();
if (order.isNewCreate()) {
continue;
}
LocalDateTime dueDateTime = order.getDueDate();
LocalDateTime completionTime =baseTime.plusSeconds(orderCompletion);
LocalDateTime completionTime = baseTime.plusSeconds(orderCompletion);
if (completionTime.isAfter(dueDateTime)) {
// 计算延迟小时数(修复时间计算)
long hours = ChronoUnit.HOURS.between(dueDateTime, completionTime);
long minutes = ChronoUnit.MINUTES.between(dueDateTime, completionTime) % 60;
tardiness += hours*60 + (double) minutes ;
tardiness += hours * 60 + (double) minutes;
}
}
Objectives[i]=tardiness;
Objectives[i] = tardiness;
chromosome.setDelayTime(tardiness);
}
if(config.getName()==GlobalParam.OBJECTIVE_SETUP_TIME)
{
if (config.getName() == GlobalParam.OBJECTIVE_SETUP_TIME) {
// 3. 最小总换型时间
double totalSetupTime = calculateTotalSetupTime(chromosome);
chromosome.setTotalChangeoverTime(totalSetupTime);
Objectives[i]=totalSetupTime;
Objectives[i] = totalSetupTime;
}
if(config.getName()==GlobalParam.OBJECTIVE_FLOW_TIME)
{
if (config.getName() == GlobalParam.OBJECTIVE_FLOW_TIME) {
// 4. 最小化总流程时间 所有工序加工时间的总和
double totalFlowTime = calculateTotalFlowTime(chromosome);
chromosome.setTotalFlowTime(totalFlowTime);
Objectives[i]=totalFlowTime;
Objectives[i] = totalFlowTime;
}
if(config.getName()==GlobalParam.OBJECTIVE_MACHINE_LOAD)
{
if (config.getName() == GlobalParam.OBJECTIVE_MACHINE_LOAD) {
// 5. 机器负载均衡
double machineLoadBalance = calculateMachineLoadBalance(chromosome);
chromosome.setMachineLoadStd(machineLoadBalance);
Objectives[i]=machineLoadBalance;
Objectives[i] = machineLoadBalance;
}
i++;
}
}
chromosome.setObjectives(Objectives);
FitnessCalculator fitnessCalculator=new FitnessCalculator();
if (chromosome.getFitnessLevel()==null) {
FitnessCalculator fitnessCalculator = new FitnessCalculator();
chromosome.setFitnessLevel(fitnessCalculator.calculateFitness(chromosome, _globalParam));
}
}
private double calculateTotalFlowTime(Chromosome chromosome) {
......
......@@ -11,6 +11,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
/**
* 爬山法算法
......@@ -38,7 +39,7 @@ public class HillClimbing {
// 按优先级从高到低处理
sortedPriorities= new ArrayList<>(priorityGroups.keySet());
Collections.sort(sortedPriorities);
entrys = buildEntryKey();
}
......@@ -71,13 +72,54 @@ public class HillClimbing {
// 构建位置到Entry的映射:position -> Entry
Map<Integer, Entry> entryIndex = buildEntryIndex(current, entrys);
boolean improved = true;
for (Double priority : sortedPriorities) {
List<Entry> priorityOps = priorityGroups.get(priority);
if (priorityOps.isEmpty()) {
continue;
}
// 1. 优先处理有多设备选项的工序(最有可能带来改进)
List<Entry> multiMachineOps = priorityOps.stream()
.filter(op -> op.getMachineOptions().size() > 1)
.collect(Collectors.toList());
if (!multiMachineOps.isEmpty()) {
for (Entry op : multiMachineOps) {
String key = op.getGroupId() + "_" + op.getSequence();
Integer maPos = MachinePositionIndex.get(key);
if (maPos == null) continue;
Chromosome machineChange = generateMachineChange(current, op.getMachineOptions(), maPos);
if (machineChange != null) {
decode(decoder, machineChange);
if (isBetter(machineChange, current)) {
current = machineChange;
if (isBetter(current, best)) {
best = current;
}
improved = true;
positionIndex = buildPositionIndex(current);
entryIndex = buildEntryIndex(current, entrys);
MachinePositionIndex = buildEntryMachinePositionIndex(current);
break;
}
}
}
if (improved) break;
}
if (improved) continue;
// 2. 检查是否是单一订单(如果只有一个订单,不需要交换位置
Set<Integer> groupIds = priorityOps.stream()
.map(Entry::getGroupId)
.collect(Collectors.toSet());
if (groupIds.size() <= 1) {
continue;
}
// 根据当前优先级的工序数确定迭代次数和step
int priorityIterations = Math.max(5, priorityOps.size() / 2);
// 200/10 =20
......@@ -109,13 +151,7 @@ public class HillClimbing {
List<Chromosome> candidates = new ArrayList<>();
List<Chromosome> candidates1 = new ArrayList<>();
if (MachineOptions.size() > 1) {
Chromosome machineChange = generateMachineChange(current, MachineOptions, maPos);
if (machineChange != null) {
candidates.add(machineChange);
candidates1.add(machineChange);
}
}
// 向左多次移动(step 1, 2, 3...)
for (int s = 1; s <= stepCount; s++) {
......@@ -197,185 +233,161 @@ public class HillClimbing {
Chromosome best = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
// 构建位置索引映射:groupId_sequence -> position
Map<String, Integer> positionIndex = buildPositionIndex(current);
Map<String, Integer> MachinePositionIndex = buildEntryMachinePositionIndex(current);
int totalCount = 0;
int noImproveCount = 0;
int maxNoImprove = 50; // 连续50次没改进就提前停止
// 构建位置到Entry的映射:position -> Entry
Map<Integer, Entry> entryIndex = buildEntryIndex(current,entrys);
boolean improved = true;
Map<Integer, Entry> entryIndex = buildEntryIndex(current, entrys);
boolean improved = false;
for (Double priority : sortedPriorities) {
List<Entry> priorityOps = priorityGroups.get(priority);
if (priorityOps.isEmpty()) {
continue;
}
// 1. 优先处理有多设备选项的工序(最有可能带来改进)
List<Entry> multiMachineOps = priorityOps.stream()
.filter(op -> op.getMachineOptions().size() > 1)
.collect(Collectors.toList());
if (!multiMachineOps.isEmpty()) {
for (Entry op : multiMachineOps) {
String key = op.getGroupId() + "_" + op.getSequence();
Integer maPos = MachinePositionIndex.get(key);
if (maPos == null) continue;
Chromosome machineChange = generateMachineChange(current, op.getMachineOptions(), maPos);
if (machineChange != null) {
decode(decoder, machineChange);
if (isBetter(machineChange, current)) {
current = machineChange;
if (isBetter(current, best)) {
best = ProductionDeepCopyUtil.deepCopy(current, Chromosome.class);
}
improved = true;
noImproveCount=0;
positionIndex = buildPositionIndex(current);
entryIndex = buildEntryIndex(current, entrys);
MachinePositionIndex = buildEntryMachinePositionIndex(current);
break;
}
}
}
if (improved) break;
}
if (improved) continue;
// 2. 检查是否是单一订单(如果只有一个订单,不需要交换位置
Set<Integer> groupIds = priorityOps.stream()
.map(Entry::getGroupId)
.collect(Collectors.toSet());
if (groupIds.size() <= 1) {
continue;
}
// 根据当前优先级的工序数确定迭代次数和step
int priorityIterations = Math.max(5, priorityOps.size()/2);
int priorityIterations = Math.max(5, priorityOps.size() / 2);
// 200/10 =20
// 1
int step = Math.max(1, (int) Math.ceil(priorityOps.size() / 10.0));
// 200/20=10;
//
int stepCount = Math.max(1, (int) Math.ceil(priorityOps.size() / step));
Set<Integer> Groupid=new HashSet<>();
int noImproveCount = 0;
int maxNoImprove = 20; // 连续50次没改进就提前停止
Set<Integer> processedGroupIds = new HashSet<>();
for (int pi = 0; pi < priorityIterations; pi++) {
improved = false;
// 随机取该优先级的工序
Entry randomOp = priorityOps.get(rnd.nextInt(priorityOps.size()));
if (processedGroupIds.contains(randomOp.getGroupId())) {
continue;
}
processedGroupIds.add(randomOp.getGroupId());
// 从索引中快速查找位置(O(1)复杂度)
String key = randomOp.getGroupId() + "_" + randomOp.getSequence();
Integer opPos = positionIndex.get(key);
Integer maPos = MachinePositionIndex.get(key);
List<MachineOption> MachineOptions=randomOp.getMachineOptions();
if (opPos == null) {
continue;
}
List<Integer> os = current.getOperationSequencing();
// 生成多个候选方案
List<Chromosome> candidates = new ArrayList<>();
if(MachineOptions.size()>1) {
Chromosome machineChange = generateMachineChange(current, MachineOptions, maPos);
if (machineChange != null) {
candidates.add(machineChange);
}
}
boolean foundBetter = false;
// 向左多次移动(step 1, 2, 3...)
// 向左逐步尝试
for (int s = 1; s <= stepCount; s++) {
int targetPos = opPos - s * step;
Entry leftOp = entryIndex.get(targetPos);
Entry leftOp = entryIndex.get(opPos - s * step);
if (leftOp != null&&randomOp.getGroupId()!=leftOp.getGroupId() && Math.abs(leftOp.getPriority() - priority) <= 0.001) {
Chromosome newChromosome = swapAtDistance(current, opPos, -s * step);
candidates.add(newChromosome);
if(MachineOptions.size()>1) {
Chromosome machineChange = generateMachineChange(newChromosome, MachineOptions, maPos);
if (machineChange != null) {
candidates.add(machineChange);
}
if (leftOp == null || randomOp.getGroupId() == leftOp.getGroupId()
|| Math.abs(leftOp.getPriority() - priority) > 0.001) {
break;
}
Chromosome newChromosome = swapAtDistance(current, opPos, -s * step);
decode(decoder, newChromosome);
}else {
break;
if (isBetter(newChromosome, current)) {
current = newChromosome;
if (isBetter(current, best)) {
best = ProductionDeepCopyUtil.deepCopy(current, Chromosome.class);
}
if (candidates.size() > 5) {
batchDecode(decoder,candidates,machines);
int bestidx= Getbest(candidates,best);
if(bestidx>-1)
{
best=candidates.get(bestidx);
current=ProductionDeepCopyUtil.deepCopy(best, Chromosome.class);;
improved=true;
// 更新索引
improved = true;
foundBetter = true;
noImproveCount=0;
positionIndex = buildPositionIndex(current);
entryIndex = buildEntryIndex(current,entrys);
entryIndex = buildEntryIndex(current, entrys);
break;
}
candidates.clear();
}
}
if(improved)
{
break;
}
if (foundBetter) break;
// 向右多次移动(step 1, 2, 3...)
// 向右逐步尝试
for (int s = 1; s <= stepCount; s++) {
int targetPos = opPos + s * step;
Entry rightOp = entryIndex.get(targetPos);
Entry rightOp = entryIndex.get(opPos + s * step);
if (rightOp != null && randomOp.getGroupId() != rightOp.getGroupId() && Math.abs(rightOp.getPriority() - priority) <= 0.001) {
Chromosome newChromosome = swapAtDistance(current, opPos, s * step);
candidates.add(newChromosome);
if(MachineOptions.size()>1) {
Chromosome machineChange = generateMachineChange(newChromosome, MachineOptions, maPos);
if (machineChange != null) {
candidates.add(machineChange);
}
if (rightOp == null || randomOp.getGroupId() == rightOp.getGroupId()
|| Math.abs(rightOp.getPriority() - priority) > 0.001) {
break;
}
} else {
break;
Chromosome newChromosome = swapAtDistance(current, opPos, s * step);
decode(decoder, newChromosome);
if (isBetter(newChromosome, current)) {
current = newChromosome;
if (isBetter(current, best)) {
best = ProductionDeepCopyUtil.deepCopy(current, Chromosome.class);
}
if (candidates.size() > 5) {
batchDecode(decoder,candidates,machines);
int bestidx= Getbest(candidates,best);
if(bestidx>-1)
{
best=candidates.get(bestidx);
current=ProductionDeepCopyUtil.deepCopy(best, Chromosome.class);;
// 更新索引
improved = true;
foundBetter = true;
noImproveCount=0;
positionIndex = buildPositionIndex(current);
entryIndex = buildEntryIndex(current,entrys);
improved=true;
break;
}
candidates.clear();
}
}
if(improved)
{
entryIndex = buildEntryIndex(current, entrys);
break;
}
if(candidates.size()== 0)
{
break;
}else {
batchDecode(decoder, candidates,machines);
}
int bestidx= Getbest(candidates,best);
if(bestidx>-1)
if (foundBetter) break;
noImproveCount++;
totalCount++;
if(noImproveCount>maxNoImprove)
{
best=candidates.get(bestidx);
current=ProductionDeepCopyUtil.deepCopy(best, Chromosome.class);;
// 更新索引
noImproveCount=0;
positionIndex = buildPositionIndex(current);
entryIndex = buildEntryIndex(current,entrys);
FileHelper.writeLogFile(String.format("爬山法 - 提前停止:迭代%d次",
totalCount));
improved=true;
break;
}
if (noImproveCount >= maxNoImprove) {
FileHelper.writeLogFile(String.format("爬山法 - 提前停止:连续未优化 迭代%d次", pi + 1));
noImproveCount=0;
}
noImproveCount++;
}
if (improved) break;
}
return best;
}
......@@ -497,7 +509,27 @@ boolean improved = true;
double priority = op.getPriority();
groups.computeIfAbsent(priority, k -> new ArrayList<>()).add(op);
}
return groups;
// 过滤掉:设备只有一个且只有一个GroupId的优先级组
Map<Double, List<Entry>> filteredGroups = new HashMap<>();
for (Map.Entry<Double, List<Entry>> entry : groups.entrySet()) {
List<Entry> ops = entry.getValue();
// 检查是否所有工序都只有一个设备选项
boolean allSingleMachine = ops.stream()
.allMatch(op -> op.getMachineOptions().size() <= 1);
// 检查是否只有一个GroupId
Set<Integer> groupIds = ops.stream()
.map(Entry::getGroupId)
.collect(Collectors.toSet());
// 如果两个条件都满足,过滤掉这个优先级组
if (!(allSingleMachine && groupIds.size() <= 1)) {
filteredGroups.put(entry.getKey(), ops);
}
}
return filteredGroups;
}
/**
......
......@@ -111,7 +111,7 @@ int opcount=allOperations.size();
// 初始化变邻域搜索
_vns = new VariableNeighborhoodSearch(_GlobalParam, allOperations, globalOpList, _fitnessCalculator, _objectiveWeights);
_hillClimbing = new HillClimbing(allOperations);
_simulatedAnnealing = new SimulatedAnnealing(_GlobalParam, allOperations, globalOpList, _fitnessCalculator, _objectiveWeights);
_simulatedAnnealing = new SimulatedAnnealing( allOperations);
_parallelLocalSearch = new ParallelLocalSearch(_GlobalParam, allOperations, globalOpList, _fitnessCalculator, _objectiveWeights);
_tabuSearch = new TabuSearch(_GlobalParam, allOperations, globalOpList, _fitnessCalculator, _objectiveWeights);
_tabuSearchWithSA = new TabuSearchWithSA(_GlobalParam, allOperations, globalOpList, _fitnessCalculator, _objectiveWeights);
......@@ -142,7 +142,7 @@ int opcount=allOperations.size();
// 步骤2:对初始种群进行爬山法局部优化
if(population.size()<5||opcount<10 ) {
if(opcount<20 ) {
FileHelper.writeLogFile("爬山法局部优化-----------开始-------");
GeneticDecoder hillClimbingDecoder = new GeneticDecoder(_GlobalParam, param.getBaseTime(), machines, orders, materials, machineScheduler, materialRequirementService, sceneId);
......@@ -156,7 +156,7 @@ int opcount=allOperations.size();
// 步骤2:对初始种群进行模拟退火+爬山法优化
GeneticDecoder saDecoder1 = new GeneticDecoder(_GlobalParam, param.getBaseTime(), machines, orders, materials, machineScheduler, materialRequirementService, sceneId);
if(population.size()>5||opcount<2000 ) {
if(opcount<800 ) {
FileHelper.writeLogFile("模拟退火+爬山法优化-----------开始-------");
Chromosome saHcOptimized = _simulatedAnnealing.batchSearchGetMax(population, saDecoder1, machines);
FileHelper.writeLogFile("模拟退火+爬山法优化-----------结束-------");
......
......@@ -934,6 +934,9 @@ public class MachineCalculator {
LocalDateTime end) {
CopyOnWriteArrayList<TimeSegment> conflictIntervals = new CopyOnWriteArrayList<>();
// 1. 维护窗口冲突(优化重叠判断,更严谨)
if (machine.getMaintenanceWindows() != null && !machine.getMaintenanceWindows().isEmpty()) {
// 过滤重叠的维护窗口并转换为TimeSegment
......
......@@ -474,18 +474,18 @@ public class OrderSortService {
if (!Objects.equals(prev.getSerie(), order.getSerie())) flags.append("serie变 ");
if (!Objects.equals(prev.getMaterialCode(), order.getMaterialCode())) flags.append("material变");
}
costLog.append(String.format(" %2d. %s, routingId=%s, serie=%s, materialCode=%s, cost=%.0f %s\n",
(i + 1),
order.getOrderCode(),
order.getRoutingId(),
order.getSerie(),
order.getMaterialCode(),
costToPrev,
flags.toString()));
// costLog.append(String.format(" %2d. %s, routingId=%s, serie=%s, materialCode=%s, cost=%.0f %s\n",
// (i + 1),
// order.getOrderCode(),
// order.getRoutingId(),
// order.getSerie(),
// order.getMaterialCode(),
// costToPrev,
// flags.toString()));
totalCost += costToPrev;
}
costLog.append(String.format("[换线明细] 总换线成本 = %.0f\n", totalCost));
log.debug(costLog.toString());
// costLog.append(String.format("[换线明细] 总换线成本 = %.0f\n", totalCost));
// log.debug(costLog.toString());
// ==================================
// 为最小化换线后的订单分配层级序号
......
......@@ -6,27 +6,32 @@ import com.aps.entity.Algorithm.*;
import com.aps.entity.basic.Entry;
import com.aps.entity.basic.GlobalParam;
import com.aps.entity.basic.Machine;
import com.aps.entity.basic.MachineOption;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
/**
* 模拟退火算法
*/
public class SimulatedAnnealing {
private final Random rnd = new Random();
private GlobalParam globalParam;
private List<Entry> allOperations;
private List<GlobalOperationInfo> globalOpList;
private FitnessCalculator fitnessCalculator;
private ObjectiveWeights objectiveWeights;
public SimulatedAnnealing(GlobalParam globalParam, List<Entry> allOperations, List<GlobalOperationInfo> globalOpList, FitnessCalculator fitnessCalculator, ObjectiveWeights objectiveWeights) {
this.globalParam = globalParam;
private Map<String, Entry> entrys;
private Map<Integer, Entry> entrybyids;
public SimulatedAnnealing( List<Entry> allOperations) {
this.allOperations = allOperations;
this.globalOpList = globalOpList;
this.fitnessCalculator = fitnessCalculator;
this.objectiveWeights = objectiveWeights;
Map<Integer, Object> mp = buildEntryKey();
entrys=(Map<String, Entry>)mp.get(1);
entrybyids=(Map<Integer, Entry>)mp.get(2);
}
private final ExecutorService decodeExecutor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors() - 1, // 核心线程数=CPU-1,无切换开销
......@@ -47,7 +52,7 @@ public class SimulatedAnnealing {
for (Chromosome chromosome:chromosomes) {
Chromosome optimized = searchWithHillClimbing(chromosome, decoder, machines);
saHcOptimized.add(optimized);
break;
}
return saHcOptimized;
}
......@@ -63,57 +68,63 @@ public class SimulatedAnnealing {
}
/**
* 模拟退火搜索,当温度降低到一定程度后切换到爬山法
* 流程:模拟退火迭代 → 随机生成新排产 → 按概率接受 → 降温 → 温度低时爬山法求精 → 输出最优
* 流程:模拟退火全局探索(按概率接受劣解)→ 降温 → 温度低时爬山法局部求精 → 输出最优
*/
public Chromosome searchWithHillClimbing(Chromosome chromosome, GeneticDecoder decoder, List<Machine> machines) {
FileHelper.writeLogFile("模拟退火+爬山法 - 开始执行");
Chromosome current = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
Chromosome best = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
writeKpi(best);
// 初始化解码
Map<String, Entry> Entrys = buildEntryKey(current);
FileHelper.writeLogFile("模拟退火+爬山法 - 初始化索引完成");
// FileHelper.writeLogFile("模拟退火+爬山法 - 初始化解码完成");
// 初始化温度 - 优化速度:更快降温,更快切换到爬山法
// 初始化温度
double temperature = 100.0;
double coolingRate = 0.95;
double temperatureThreshold = 1.0; // 提高阈值,更快切换到爬山法
int maxIterations = 300; // 减少最大迭代次数
double temperatureThreshold = 1.0;
int maxIterations = 300;
int noImproveCount = 0;
int maxNoImprove = 50;
FileHelper.writeLogFile(String.format("模拟退火+爬山法 - 参数配置:温度=%.1f, 降温率=%.2f, 阈值=%.1f, 最大迭代=%d",
temperature, coolingRate, temperatureThreshold, maxIterations));
int noImproveCount = 0;
int maxNoImprove = 50; // 连续50次没改进就提前停止
int acceptCount = 0;
int improveCount = 0;
for (int i = 0; i < maxIterations; i++) {
boolean improved = false;
// 1. 随机生成新排产(邻域解
Chromosome neighbor = generateNeighbor(current,Entrys);
// 1. 使用智能策略生成邻域解(找瓶颈工序/设备
Chromosome neighbor = generateNeighbor(current);
// 2. 解码并计算适应度(只解码neighbor,current已经解码过了)
decode(decoder, neighbor, machines);
// 2. 解码
decode(decoder, neighbor,machines);
// 3. 计算能量差(fitness差)
// 3. 计算能量差
double energyDifference = calculateEnergyDifference(neighbor, current);
// 4. 按概率接受新解
// - 更优:直接接受
// - 较差:概率接受(高温更容易接受)
// 4. 按概率接受新解(模拟退火核心:有概率接受劣解)
boolean accepted = false;
if (energyDifference > 0 || rnd.nextDouble() < Math.exp(energyDifference / temperature)) {
current = neighbor;
accepted = true;
acceptCount++;
// 更新全局最优
if (isBetter(current, best)) {
best = ProductionDeepCopyUtil.deepCopy(current, Chromosome.class);
writeKpi(best);
improved = true;
improveCount++;
noImproveCount = 0;
FileHelper.writeLogFile(String.format("模拟退火+爬山法 - 迭代%d:找到更优解,fitness=%.4f", i, best.getFitness()));
}
}
......@@ -123,10 +134,13 @@ public class SimulatedAnnealing {
// 5. 降温
temperature *= coolingRate;
if ((i + 1) % 10 == 0) {
// 每50次迭代输出一次状态
if ((i + 1) % 50 == 0) {
FileHelper.writeLogFile(String.format("模拟退火+爬山法 - 迭代%d/%d:温度=%.4f, 接受数=%d, 改进数=%d, 无改进连续=%d",
i + 1, maxIterations, temperature, acceptCount, improveCount, noImproveCount));
}
// 6. 提前停止条件:温度低于阈值 或 连续多次没改进
if (temperature < temperatureThreshold || noImproveCount >= maxNoImprove) {
String stopReason = temperature < temperatureThreshold ? "温度低于阈值" : "连续无改进达到上限";
......@@ -134,20 +148,31 @@ public class SimulatedAnnealing {
stopReason, i + 1, temperature));
FileHelper.writeLogFile("模拟退火+爬山法 - 切换到爬山法求精");
HillClimbing hillClimbing = new HillClimbing( allOperations );
Chromosome refined = hillClimbing.search(best, decoder,machines);
HillClimbing hillClimbing = new HillClimbing( allOperations);
Chromosome refined = hillClimbing.search(best, decoder, machines);
FileHelper.writeLogFile("模拟退火+爬山法 - 爬山法求精完成");
// 返回爬山法求精后的结果
return refined;
}
}
FileHelper.writeLogFile(String.format("模拟退火+爬山法 - 完成所有%d次迭代,最终fitness=%.4f", maxIterations, best.getFitness()));
FileHelper.writeLogFile(String.format("模拟退火+爬山法 - 完成所有%d次迭代,最终fitness=%.4f",
maxIterations, best.getFitness()));
// 7. 输出全局最优排产
return best;
}
private void writeKpi(Chromosome chromosome) {
String fitness = "";
double[] fitness1 = chromosome.getFitnessLevel();
for (int i = 0; i < fitness1.length; i++) {
fitness += fitness1[i] + ",";
}
FileHelper.writeLogFile(String.format("爬山法 - kpi:%s", fitness));
}
/**
* 计算能量差(基于fitnessLevel数组的比较)
......@@ -163,6 +188,77 @@ public class SimulatedAnnealing {
}
return diff;
}
/**
* 按优先级分组工序
*/
private Map<Double, List<Entry>> groupOperationsByPriority() {
Map<Double, List<Entry>> groups = new HashMap<>();
for (Entry op : allOperations) {
double priority = op.getPriority();
groups.computeIfAbsent(priority, k -> new ArrayList<>()).add(op);
}
// 过滤掉:设备只有一个且只有一个GroupId的优先级组
Map<Double, List<Entry>> filteredGroups = new HashMap<>();
for (Map.Entry<Double, List<Entry>> entry : groups.entrySet()) {
List<Entry> ops = entry.getValue();
// 检查是否所有工序都只有一个设备选项
boolean allSingleMachine = ops.stream()
.allMatch(op -> op.getMachineOptions().size() <= 1);
// 检查是否只有一个GroupId
Set<Integer> groupIds = ops.stream()
.map(Entry::getGroupId)
.collect(Collectors.toSet());
// 如果两个条件都满足,过滤掉这个优先级组
if (!(allSingleMachine && groupIds.size() <= 1)) {
filteredGroups.put(entry.getKey(), ops);
}
}
return filteredGroups;
}
/**
* 构建位置索引:groupId_sequence -> position
*/
private Map<String, Integer> buildPositionIndex(Chromosome chromosome) {
Map<String, Integer> index = new HashMap<>();
List<Integer> os = chromosome.getOperationSequencing();
Map<Integer, Integer> orderProcessCounter = new HashMap<>();
for (int i = 0; i < os.size(); i++) {
int groupId = os.get(i);
int count = orderProcessCounter.getOrDefault(groupId, 0) + 1;
orderProcessCounter.put(groupId, count);
String key = groupId + "_" + count;
index.put(key, i);
}
return index;
}
/**
* 构建位置索引:groupId_sequence -> Machines position
*/
private Map<String, Integer> buildEntryMachinePositionIndex(Chromosome chromosome) {
Map<String, Integer> index = new HashMap<>();
List<GlobalOperationInfo> globalOpList = chromosome.getGlobalOpList();
for (int i = 0; i < globalOpList.size(); i++) {
GlobalOperationInfo globalOp = globalOpList.get(i);
Entry op = globalOp.getOp();
int groupId = op.getGroupId();
int count = op.getSequence();
String key = groupId + "_" + count;
index.put(key, i);
}
return index;
}
/**
* 构建位置->Entry索引
*/
......@@ -188,39 +284,428 @@ public class SimulatedAnnealing {
/**
* 构建Entry索引:op.getGroupId() + "_" + op.getSequence() -> Entry
*/
private Map<String, Entry> buildEntryKey(Chromosome chromosome) {
private Map<Integer, Object> buildEntryKey() {
Map<Integer, Object> index0 = new HashMap<>();
Map<String, Entry> index = new HashMap<>();
List<Entry> allOps = chromosome.getAllOperations();
Map<Integer, Entry> index2 = new HashMap<>();
List<Entry> allOps = this.allOperations;
for (Entry op : allOps) {
String key = op.getGroupId() + "_" + op.getSequence();
index.put(key, op);
index2.put(op.getId(), op);
}
index0.put(1,index);
index0.put(2,index2);
return index0;
}
return index;
/**
* 生成邻域解 - 智能策略:优先处理瓶颈工序/设备
* 策略:
* 1. 优先处理瓶颈设备上的工序(换设备)
* 2. 瓶颈设备上的工序往前移动
* 3. 非瓶颈设备上的工序往后移动(给瓶颈工序腾位置)
*/
private Chromosome generateNeighbor(Chromosome chromosome) {
List<GAScheduleResult> results = chromosome.getResult();
if (results == null || results.isEmpty()) {
return generateRandomNeighbor(chromosome, entrys);
}
// 构建位置索引
Map<String, Integer> positionIndex = buildPositionIndex(chromosome);
Map<Integer, Entry> positionToEntryIndex = buildPositionToEntryIndex(chromosome, entrys);
Map<String, Integer> MachinePositionIndex = buildEntryMachinePositionIndex(chromosome);
// 1. 找出瓶颈设备
Long bottleneckMachineId = findBottleneckMachine(chromosome);
if (bottleneckMachineId != null) {
// 找出瓶颈设备上的工序
List<GAScheduleResult> bottleneckOps = findOpsOnBottleneckMachine(chromosome, bottleneckMachineId);
if (!bottleneckOps.isEmpty()) {
// 70%概率优先处理瓶颈设备
if (rnd.nextDouble() < 0.7) {
int strategy = rnd.nextInt(10);
if(strategy<=3)
{
// 策略1:瓶颈设备上的工序换设备
return tryChangeMachineForBottleneckOp(chromosome, bottleneckOps, entrys, MachinePositionIndex);
}
else if(strategy<=8) {
// 策略2:瓶颈设备上的工序往前移动
return tryMoveBottleneckOpForward(chromosome, bottleneckOps, positionIndex, positionToEntryIndex, entrys);
}else if(strategy<=10) {
// 策略3:非瓶颈设备上的工序往后移动
return tryMoveNonBottleneckOpBackward(chromosome, bottleneckMachineId, positionIndex, positionToEntryIndex, entrys);
}
}
}
}
// 默认策略:随机邻域
return generateRandomNeighbor(chromosome, entrys);
}
/**
* 识别瓶颈设备(利用率最高的设备)
*/
private Long findBottleneckMachine(Chromosome chromosome) {
Map<Long, Double> utilization = calculateMachineUtilization(chromosome);
if (utilization.isEmpty()) {
return null;
}
return utilization.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(null);
}
/**
* 查找在瓶颈设备上的工序
*/
private List<GAScheduleResult> findOpsOnBottleneckMachine(Chromosome chromosome, Long bottleneckMachineId) {
if (bottleneckMachineId == null) {
return new ArrayList<>();
}
return chromosome.getResult().stream()
.filter(r -> r.getMachineId() == bottleneckMachineId)
.collect(Collectors.toList());
}
/**
* 计算设备利用率
*/
private Map<Long, Double> calculateMachineUtilization(Chromosome chromosome) {
Map<Long, Double> utilization = new HashMap<>();
List<GAScheduleResult> results = chromosome.getResult();
if (results == null || results.isEmpty()) {
return utilization;
}
int maxEndTime = results.stream()
.mapToInt(GAScheduleResult::getEndTime)
.max()
.orElse(1);
Map<Long, List<GAScheduleResult>> machineResults = results.stream()
.collect(Collectors.groupingBy(GAScheduleResult::getMachineId));
for (Map.Entry<Long, List<GAScheduleResult>> entry : machineResults.entrySet()) {
long machineId = entry.getKey();
List<GAScheduleResult> machineOps = entry.getValue();
int totalProcessingTime = machineOps.stream()
.mapToInt(r -> r.getEndTime() - r.getStartTime())
.sum();
double util = (double) totalProcessingTime / maxEndTime;
utilization.put(machineId, util);
}
return utilization;
}
/**
* 尝试给瓶颈设备上的工序换设备
*/
private Chromosome tryChangeMachineForBottleneckOp(Chromosome chromosome, List<GAScheduleResult> bottleneckOps, Map<String, Entry> entrys,Map<String, Integer> MachinePositionIndex) {
// 优先选择有多设备选项的工序
List<GAScheduleResult> candidates = bottleneckOps.stream()
.filter(op -> {
Entry entry = entrybyids.get(op.getOperationId());
return entry != null && entry.getMachineOptions().size() > 1;
})
.collect(Collectors.toList());
if (candidates.isEmpty()) {
return generateRandomNeighbor(chromosome, entrys);
}
// 从候选中随机选择一个工序,给它换设备
GAScheduleResult selectedOp = candidates.get(rnd.nextInt(candidates.size()));
// 找到这个工序在globalOpList中的位置
int maPos = -1;
Entry entry= entrybyids.get(selectedOp.getOperationId());
if (entry != null && entry.getMachineOptions().size() > 1) {
maPos = MachinePositionIndex.get(entry.getGroupId() + "_" + entry.getSequence());
if (maPos >= 0) {
return generateMachineChangeForSpecificOp(chromosome, entry.getMachineOptions(), maPos);
}
}
// 直接换设备
return generateMachineChangeNeighbor(chromosome);
}
/**
* 给指定位置的工序换设备
*/
private Chromosome generateMachineChangeForSpecificOp(Chromosome chromosome, List<MachineOption> machineOptions, int idx) {
Chromosome neighbor = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
List<Integer> ms = neighbor.getMachineSelection();
if (!ms.isEmpty() && idx >= 0 && idx < ms.size()) {
if (machineOptions.size() > 1) {
int currentMachineSeq = ms.get(idx);
List<Integer> availableMachines = new ArrayList<>();
for (int i = 1; i <= machineOptions.size(); i++) {
if (i != currentMachineSeq) {
availableMachines.add(i);
}
}
if (!availableMachines.isEmpty()) {
int newMachineSeq = availableMachines.get(rnd.nextInt(availableMachines.size()));
ms.set(idx, newMachineSeq);
neighbor.setMachineSelection(ms);
}
}
}
return neighbor;
}
/**
* 尝试将瓶颈设备上的工序往前移动
*/
private Chromosome tryMoveBottleneckOpForward(Chromosome chromosome, List<GAScheduleResult> bottleneckOps,
Map<String, Integer> positionIndex,
Map<Integer, Entry> positionToEntryIndex,
Map<String, Entry> entrys) {
if (bottleneckOps.isEmpty()) {
return generateSwapNeighbor(chromosome, entrys);
}
// 随机选择一个瓶颈工序
// GAScheduleResult randomOp = bottleneckOps.get(rnd.nextInt(bottleneckOps.size()));
// 计算所有订单的完成时间
List<GAScheduleResult> allResults = chromosome.getResult();
Map<Integer, Integer> orderCompletionTimes = calculateOrderCompletionTimes(allResults);
// 找出最晚完成的订单(延期候选)
int latestCompletionTime = orderCompletionTimes.values().stream()
.mapToInt(Integer::intValue)
.max()
.orElse(0);
// 按优先级和订单完成时间排序:优先选择高优先级+延期订单的工序
List<GAScheduleResult> sortedBottleneckOps = new ArrayList<>(bottleneckOps);
sortedBottleneckOps.sort((a, b) -> {
Entry entryA = entrybyids.get(a.getOperationId());
Entry entryB = entrybyids.get(b.getOperationId());
if (entryA != null && entryB != null) {
// 首先比较优先级(高优先级在前)
int priorityCompare = Double.compare(entryB.getPriority(), entryA.getPriority());
if (priorityCompare != 0) {
return priorityCompare;
}
// 优先级相同,比较订单完成时间(晚完成的在前,即更可能延期)
int endTimeA = orderCompletionTimes.getOrDefault(a.getGroupId(), 0);
int endTimeB = orderCompletionTimes.getOrDefault(b.getGroupId(), 0);
return Integer.compare(endTimeB, endTimeA);
}
return 0;
});
GAScheduleResult selectedOp = sortedBottleneckOps.get(rnd.nextInt(Math.min(5, sortedBottleneckOps.size())));
Entry entry=entrybyids.get(selectedOp.getOperationId()) ;
String key = entry.getGroupId() + "_" + entry.getSequence();
Integer opPos = positionIndex.get(key);
if (opPos == null || opPos <= 0) {
return generateSwapNeighbor(chromosome, entrys);
}
// 往前移动1-3步
// int steps = rnd.nextInt(3) + 1;
// return moveOperationForward(chromosome,positionToEntryIndex, opPos, steps);
// 使用插入方式往前移动(1-5步)
int steps = rnd.nextInt(5) + 1;
return insertOperationForward(chromosome, opPos, steps, entrys);
}
/**
* 计算每个订单的最后完成时间
*/
private Map<Integer, Integer> calculateOrderCompletionTimes(List<GAScheduleResult> allResults) {
Map<Integer, Integer> orderEndTimes = new HashMap<>();
for (GAScheduleResult result : allResults) {
int groupId = result.getGroupId();
int endTime = result.getEndTime();
orderEndTimes.put(groupId, Math.max(orderEndTimes.getOrDefault(groupId, 0), endTime));
}
return orderEndTimes;
}
/**
* 尝试将非瓶颈设备上的工序往后移动(给瓶颈工序腾位置)
*/
private Chromosome tryMoveNonBottleneckOpBackward(Chromosome chromosome, Long bottleneckMachineId,
Map<String, Integer> positionIndex,
Map<Integer, Entry> positionToEntryIndex,
Map<String, Entry> entrys) {
List<GAScheduleResult> results = chromosome.getResult();
if (results == null) {
return generateSwapNeighbor(chromosome, entrys);
}
// 找出非瓶颈设备上的工序
List<GAScheduleResult> nonBottleneckOps = results.stream()
.filter(r -> r.getMachineId() != bottleneckMachineId)
.collect(Collectors.toList());
if (nonBottleneckOps.isEmpty()) {
return generateSwapNeighbor(chromosome, entrys);
}
// 随机选择一个非瓶颈工序
GAScheduleResult randomOp = nonBottleneckOps.get(rnd.nextInt(nonBottleneckOps.size()));
Entry entry=entrybyids.get(randomOp.getOperationId());
String key = entry.getGroupId()+"_"+entry.getSequence();
Integer opPos = positionIndex.get(key);
if (opPos == null || opPos >= chromosome.getOperationSequencing().size() - 1) {
return generateSwapNeighbor(chromosome, entrys);
}
// 往后移动1-3步
int steps = rnd.nextInt(3) + 1;
return moveOperationBackward(chromosome,positionToEntryIndex, opPos, steps);
}
/**
* 生成邻域解
* 将工序往前移动
*/
private Chromosome generateNeighbor(Chromosome chromosome,Map<String, Entry> entrys) {
private Chromosome moveOperationForward(Chromosome chromosome, Map<Integer, Entry> originalPositionIndex,int pos, int steps) {
Chromosome neighbor = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
List<Integer> os = neighbor.getOperationSequencing();
if (pos <= 0 || pos >= os.size()) {
return neighbor;
}
// 获取要移动的工序和目标位置的工序(基于原始chromosome)
Entry opToMove = originalPositionIndex.get(pos);
if (opToMove == null) {
return neighbor;
}
int targetPos = Math.max(0, pos - steps);
Entry targetOp = originalPositionIndex.get(targetPos);
// 检查是否可以交换
if (targetOp != null && opToMove.getGroupId() == targetOp.getGroupId()) {
return neighbor;
}
if (targetOp != null && Math.abs(targetOp.getPriority() - opToMove.getPriority()) > 0.001) {
return neighbor;
}
// 直接交换到目标位置
java.util.Collections.swap(os, pos, targetPos);
neighbor.setOperationSequencing(os);
return neighbor;
}
/**
* 将工序往后移动
*/
private Chromosome moveOperationBackward(Chromosome chromosome,Map<Integer, Entry> originalPositionIndex, int pos, int steps) {
Chromosome neighbor = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
List<Integer> os = neighbor.getOperationSequencing();
if (pos < 0 || pos >= os.size() - 1) {
return neighbor;
}
// 获取要移动的工序和目标位置的工序(基于原始chromosome)
Entry opToMove = originalPositionIndex.get(pos);
if (opToMove == null) {
return neighbor;
}
int targetPos = Math.min(os.size() - 1, pos + steps);
Entry targetOp = originalPositionIndex.get(targetPos);
// 检查是否可以交换
if (targetOp != null && opToMove.getGroupId() == targetOp.getGroupId()) {
return neighbor;
}
if (targetOp != null && Math.abs(targetOp.getPriority() - opToMove.getPriority()) > 0.001) {
return neighbor;
}
// 直接交换到目标位置
java.util.Collections.swap(os, pos, targetPos);
neighbor.setOperationSequencing(os);
return neighbor;
}
/**
* 将工序往前插入(不交换,直接插入到目标位置)
*/
private Chromosome insertOperationForward(Chromosome chromosome, int pos, int steps, Map<String, Entry> entrys) {
Chromosome neighbor = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
List<Integer> os = neighbor.getOperationSequencing();
if (pos <= 0 || pos >= os.size()) {
return neighbor;
}
// 获取要移动的工序(基于原始chromosome)
Map<Integer, Entry> originalPositionIndex = buildPositionToEntryIndex(chromosome, entrys);
Entry opToMove = originalPositionIndex.get(pos);
if (opToMove == null) {
return neighbor;
}
// 找到最远可以插入的位置(不打破优先级)
int targetPos = Math.max(0, pos - steps);
int finalTargetPos = pos;
for (int i = pos - 1; i >= targetPos; i--) {
Entry leftOp = originalPositionIndex.get(i);
if (leftOp != null && opToMove.getGroupId() == leftOp.getGroupId()) {
break;
}
if (leftOp != null && Math.abs(leftOp.getPriority() - opToMove.getPriority()) > 0.001) {
break;
}
finalTargetPos = i;
}
if (finalTargetPos == pos) {
return neighbor;
}
// 执行插入:移除原位置,插入到目标位置
int value = os.remove(pos);
os.add(finalTargetPos, value);
neighbor.setOperationSequencing(os);
return neighbor;
}
/**
* 随机生成邻域解(兜底策略)
*/
private Chromosome generateRandomNeighbor(Chromosome chromosome, Map<String, Entry> entrys) {
int neighborType = rnd.nextInt(4);
switch (neighborType) {
case 0:
return generateSwapNeighbor(chromosome,entrys);
return generateSwapNeighbor(chromosome, entrys);
case 1:
return generateReverseNeighbor(chromosome,entrys);
return generateReverseNeighbor(chromosome, entrys);
case 2:
return generateInsertNeighbor(chromosome,entrys);
return generateInsertNeighbor(chromosome, entrys);
case 3:
return generateMachineChangeNeighbor(chromosome);
default:
return generateSwapNeighbor(chromosome,entrys);
return generateSwapNeighbor(chromosome, entrys);
}
}
/**
* 生成交换邻域解 - 只在相同优先级的工序之间交换
*/
......@@ -332,7 +817,7 @@ public class SimulatedAnnealing {
List<Integer> ms = neighbor.getMachineSelection();
if (!ms.isEmpty()) {
int idx = rnd.nextInt(ms.size());
GlobalOperationInfo globalOp = globalOpList.get(idx);
GlobalOperationInfo globalOp = chromosome.getGlobalOpList().get(idx);
Entry op = globalOp.getOp();
if (op.getMachineOptions().size() > 1) {
......
......@@ -29,6 +29,8 @@ public class TabuSearch {
this.fitnessCalculator = fitnessCalculator;
this.objectiveWeights = objectiveWeights;
this.tabuList = new ArrayList<>();
// 工序越多,禁忌表越长(适配1000+工序)
this.tabuListSize = Math.min(50, allOperations.size() / 30);
}
/**
......
......@@ -3,23 +3,24 @@ package com.aps.service.plan;
import com.aps.common.util.*;
import com.aps.common.util.redis.RedisUtils;
import com.aps.controller.gantt.FileUploadController;
import com.aps.entity.*;
import com.aps.entity.Algorithm.*;
import com.aps.entity.Algorithm.IDAndChildID.GroupResult;
import com.aps.entity.Algorithm.IDAndChildID.NodeInfo;
import com.aps.entity.*;
import com.aps.entity.Gantt.ResourceGanttVO;
import com.aps.entity.Gantt.TaskVO;
import com.aps.entity.Schedule.SceneChromsome;
import com.aps.entity.basic.ScheduleChromosome;
import com.aps.entity.Schedule.GenVO;
import com.aps.entity.Schedule.MachineVO;
import com.aps.entity.Schedule.SceneChromsome;
import com.aps.entity.basic.*;
import com.aps.mapper.*;
import com.aps.service.*;
import com.aps.service.Algorithm.*;
import com.aps.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -35,8 +36,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.Comparator;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
......@@ -99,6 +98,8 @@ public class PlanResultService {
@Autowired
private OrderSortService orderSortService;
@Autowired
private LanuchService lanuchService;
@Autowired
private MaterialInfoMapper materialInfoMapper;
......@@ -124,6 +125,15 @@ public class PlanResultService {
private MaterialRequirementService materialRequirementService;
@Autowired
private EquipMaintainTaskService _equipMaintainTaskService;
@Autowired
private DispatchService dispatchService;
@Autowired
private ApsTimeConfigService apsTimeConfigService;
@Autowired
private LockedOrderProcessorService lockedOrderProcessorService;
private LocalDateTime baseTime = LocalDateTime.of(2025, 11, 1, 0, 0, 0);
public List<ScheduleChromosome> execute() {
......@@ -137,7 +147,6 @@ public class PlanResultService {
public Chromosome execute2(String SceneId) {
try {
ScheduleParams param = InitScheduleParams();
......@@ -208,18 +217,25 @@ public class PlanResultService {
.map(Material::getId)
.distinct()
.collect(Collectors.toList());
globalParam.setPureNSGAIIMode(false);
ApsTimeConfig timeConfig = apsTimeConfigService.getOne(new LambdaQueryWrapper<>());
// 4.5 在排产前标记锁定期工单占用的设备时间段
lockedOrderProcessorService.markLockedOrdersOccupiedTime(machines, timeConfig.getBaseTime());
// 5. 执行调度算法
HybridAlgorithm scheduler =new HybridAlgorithm(globalParam,machines,orders,Materials1,materialIds,machineScheduler,entryRel,materialRequirementService,_sceneService,SceneId); //new GeneticAlgorithm(products, machines, orders, machineScheduler);
GeneticAlgorithm scheduler =new GeneticAlgorithm(globalParam,machines,orders,Materials1,materialIds,machineScheduler,entryRel,materialRequirementService,_sceneService,SceneId); //new GeneticAlgorithm(products, machines, orders, machineScheduler);
param.initAdaptiveParams(entrys.size());
double[] customWeights = new double[] { 0.4, 0.1, 0.1, 0.1, 0.3 }; // 延迟时间权重提升到0.5
//完工时间、总流程时间、总换型时间、机器负载标准差、延迟时间
scheduler.Init();
// scheduler.Init(customWeights,false);
Chromosome chromosome =scheduler.Run(param,entrys);
KpiCalculator kpiCalculator=new KpiCalculator(chromosome);
kpiCalculator.calculatekpi();
// 添加锁定期工单到调度结果中
// 这里会从 Dispatch 表加载锁定期工单,并添加到 chromosome.result 中
lockedOrderProcessorService.addLockedOrdersToResult(chromosome);
_sceneService.saveChromosomeToFile(chromosome, SceneId);
WriteScheduleSummary(chromosome);
......@@ -231,6 +247,809 @@ public class PlanResultService {
}
}
// /**
// * 添加锁定期工单到调度结果中
// * 从上一次排产结果中获取冻结期内的工单,保持时间、数量、设备、Entry等信息不变
// * @param chromosome 染色体对象
// */
// private void addLockedOrdersToResult(Chromosome chromosome) {
// try {
// // 1. 获取时间配置
// ApsTimeConfig timeConfig = apsTimeConfigService.getOne(new LambdaQueryWrapper<>());
// if (timeConfig == null || timeConfig.getBaseTime() == null) {
// log.warn("未找到ApsTimeConfig配置或baseTime为空,无法添加锁定期工单");
// return;
// }
//
// LocalDateTime baseTime = timeConfig.getBaseTime();
// long freezeSeconds = timeConfig.getFreezeDate() != null ? timeConfig.getFreezeDate().longValue() : 0;
//
// // 如果freezeSeconds为0,则不添加锁定期工单
// if (freezeSeconds <= 0) {
// log.debug("冻结期秒数为{},跳过添加锁定期工单", freezeSeconds);
// return;
// }
//
// // 锁定期计算:baseTime 到 baseTime + freezeSeconds
// LocalDateTime lockStartTime = baseTime;
// LocalDateTime lockEndTime = baseTime.plusSeconds(freezeSeconds);
//
// log.info("锁定期范围: {} 到 {} (冻结期: {}秒)", lockStartTime, lockEndTime, freezeSeconds);
//
// // 2. 尝试从已下发的排产结果中获取锁定期工单
// List<GAScheduleResult> lockedResults = new ArrayList<>();
// Map<String, Entry> lockedEntries = new HashMap<>();
// Set<Long> lockedMachineIds = new HashSet<>();
//
// try {
// // 从Dispatch表查询锁定期内的工单,获取它们的sceneId
// List<Dispatch> frozenDispatches = dispatchService.lambdaQuery()
// .ge(Dispatch::getBeginTime, lockStartTime)
// .le(Dispatch::getBeginTime, lockEndTime)
// .eq(Dispatch::getIsDeleted, 0L)
// .orderBy(true, true, Dispatch::getMesCode, Dispatch::getTaskSeq)
// .list();
//
// if (frozenDispatches.isEmpty()) {
// log.info("冻结期内没有工单需要添加");
// return;
// }
//
// log.info("查询到冻结期内的工序数: {}", frozenDispatches.size());
//
// // 获取这些工单的所有不同sceneId(可能来自多个已下发的场景)
// Set<String> dispatchSceneIds = frozenDispatches.stream()
// .map(Dispatch::getSceneId)
// .filter(id -> id != null && !id.isEmpty())
// .collect(Collectors.toSet());
//
// if (!dispatchSceneIds.isEmpty()) {
// log.info("锁定期工单来自 {} 个已下发场景: {}", dispatchSceneIds.size(), dispatchSceneIds);
//
// // 遍历每个场景,尝试加载排产结果
// for (String dispatchSceneId : dispatchSceneIds) {
// try {
// log.info("处理场景: {}", dispatchSceneId);
//
// // 尝试加载已下发场景的排产结果
// SceneChromsome dispatchSceneChromsome = (SceneChromsome) redisUtils.get("SceneId." + dispatchSceneId);
// if (dispatchSceneChromsome != null) {
// int dispatchVersion = dispatchSceneChromsome.getVersion();
// File dispatchFile = new File("result", "chromosome_result_" + dispatchSceneId + "_" + dispatchVersion + "_.json");
//
// if (dispatchFile.exists()) {
// ObjectMapper objectMapper = new ObjectMapper();
// objectMapper.registerModule(new JavaTimeModule());
// objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// objectMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//
// Chromosome dispatchChromosome = objectMapper.readValue(dispatchFile, Chromosome.class);
//
// if (dispatchChromosome.getResult() != null) {
// // 获取已下发场景的baseTime
// LocalDateTime dispatchBaseTime = dispatchChromosome.getBaseTime();
// if (dispatchBaseTime == null) {
// log.warn("场景 {} 的baseTime为空,无法计算时间偏移", dispatchSceneId);
// continue;
// }
//
// log.info("场景 {} - 已下发场景baseTime: {}, 新场景baseTime: {}", dispatchSceneId, dispatchBaseTime, baseTime);
//
// // 筛选属于当前场景的工单
// List<Dispatch> currentSceneDispatches = frozenDispatches.stream()
// .filter(d -> dispatchSceneId.equals(d.getSceneId()))
// .collect(Collectors.toList());
//
// // 第一步:找出所有在锁定期内有工序的订单ID
// Set<String> lockedOrderIds = new HashSet<>();
// log.info("场景 {} 开始分析工序时间,锁定期范围: {} 到 {}", dispatchSceneId, lockStartTime, lockEndTime);
//
// for (GAScheduleResult prevResult : dispatchChromosome.getResult()) {
// LocalDateTime prevStartTime = dispatchBaseTime.plusSeconds(prevResult.getStartTime());
//
// log.debug("分析工序: ExecId={}, OrderId={}, 原始StartTime={}秒, 绝对开始时间={}",
// prevResult.getExecId(), prevResult.getOrderId(), prevResult.getStartTime(), prevStartTime);
//
// // 如果该工序的开始时间在锁定期内,记录其订单ID
// if (!prevStartTime.isBefore(lockStartTime) && !prevStartTime.isAfter(lockEndTime)) {
// log.info("工序在锁定期内: ExecId={}, OrderId={}, 开始时间={}",
// prevResult.getExecId(), prevResult.getOrderId(), prevStartTime);
//
// // 检查是否有对应的Dispatch记录(通过订单号和时间范围匹配,而不是ExecId)
// boolean hasValidDispatchRecord = currentSceneDispatches.stream()
// .anyMatch(d -> d.getMesCode() != null &&
// d.getMesCode().equals(prevResult.getOrderId()) &&
// d.getBeginTime() != null &&
// !d.getBeginTime().isBefore(lockStartTime) &&
// !d.getBeginTime().isAfter(lockEndTime));
//
// if (hasValidDispatchRecord && prevResult.getOrderId() != null) {
// lockedOrderIds.add(prevResult.getOrderId());
// log.info("订单 {} 被标记为锁定期订单(通过订单号匹配)", prevResult.getOrderId());
// } else {
// log.warn("工序 {} 在锁定期内但没有有效的Dispatch记录(订单号: {})",
// prevResult.getExecId(), prevResult.getOrderId());
// }
// } else {
// log.debug("工序不在锁定期内: ExecId={}, 开始时间={}, 锁定期: {} 到 {}",
// prevResult.getExecId(), prevStartTime, lockStartTime, lockEndTime);
// }
// }
//
// log.info("场景 {} 中发现 {} 个订单在锁定期内有工序: {}", dispatchSceneId, lockedOrderIds.size(), lockedOrderIds);
//
// // 第二步:获取这些订单的所有工序(包括锁定期外的工序)
// for (GAScheduleResult prevResult : dispatchChromosome.getResult()) {
// // 如果该工序属于锁定期订单,则保留(不管工序本身是否在锁定期内)
// if (lockedOrderIds.contains(prevResult.getOrderId())) {
// // 检查是否有对应的Dispatch记录(通过订单号匹配)
// boolean hasDispatchRecord = currentSceneDispatches.stream()
// .anyMatch(d -> d.getMesCode() != null &&
// d.getMesCode().equals(prevResult.getOrderId()));
//
// if (hasDispatchRecord) {
// // 将已下发场景的相对时间转换为绝对时间
// LocalDateTime prevStartTime = dispatchBaseTime.plusSeconds(prevResult.getStartTime());
// LocalDateTime prevEndTime = dispatchBaseTime.plusSeconds(prevResult.getEndTime());
//
// // 深拷贝工单信息
// GAScheduleResult lockedResult = copyGAScheduleResult(prevResult);
// lockedResult.setIsLocked(true); // 标记为锁定
//
// // 重新计算相对于新baseTime的时间
// int newStartTime = (int) ChronoUnit.SECONDS.between(baseTime, prevStartTime);
// int newEndTime = (int) ChronoUnit.SECONDS.between(baseTime, prevEndTime);
// lockedResult.setStartTime(newStartTime);
// lockedResult.setEndTime(newEndTime);
//
// lockedResults.add(lockedResult);
// lockedMachineIds.add(prevResult.getMachineId());
//
// boolean isInLockPeriod = !prevStartTime.isBefore(lockStartTime) && !prevStartTime.isAfter(lockEndTime);
// log.info("从场景 {} 获取订单 {} 的工序: ExecId={}, OrderCode={}, MachineId={}, 原始时间=[{} 到 {}], 新相对时间=[{} 到 {}]秒, 是否在锁定期内={}",
// dispatchSceneId, prevResult.getOrderId(), lockedResult.getExecId(), lockedResult.getOrderCode(), lockedResult.getMachineId(),
// prevStartTime, prevEndTime, newStartTime, newEndTime, isInLockPeriod);
// } else {
// log.warn("订单 {} 的工序 {} 没有对应的Dispatch记录", prevResult.getOrderId(), prevResult.getExecId());
// }
// }
// }
//
// // 从已下发场景的排产结果中获取对应的Entry信息
// if (dispatchChromosome.getAllOperations() != null) {
// log.info("场景 {} 中有 {} 个Entry,开始匹配锁定期工单的Entry信息", dispatchSceneId, dispatchChromosome.getAllOperations().size());
//
// for (GAScheduleResult lockedResult : lockedResults) {
// log.debug("处理锁定期工单: ExecId={}, OperationId={}", lockedResult.getExecId(), lockedResult.getOperationId());
//
// if (lockedResult.getOperationId() > 0) {
// Entry prevEntry = dispatchChromosome.getAllOperations().stream()
// .filter(e -> e.getId() == lockedResult.getOperationId())
// .findFirst()
// .orElse(null);
//
// if (prevEntry != null) {
// lockedEntries.put(lockedResult.getExecId(), prevEntry);
// log.debug("找到匹配的Entry: ExecId={}, EntryId={}, RoutingDetailId={}",
// lockedResult.getExecId(), prevEntry.getId(), prevEntry.getRoutingDetailId());
// } else {
// log.warn("未找到OperationId={}对应的Entry", lockedResult.getOperationId());
// }
// } else {
// log.warn("锁定期工单的OperationId为0,无法匹配Entry: ExecId={}", lockedResult.getExecId());
//
// // 尝试通过ExecId匹配Entry
// Entry prevEntry = dispatchChromosome.getAllOperations().stream()
// .filter(e -> e.getExecId().equals(lockedResult.getExecId()))
// .findFirst()
// .orElse(null);
//
// if (prevEntry != null) {
// lockedEntries.put(lockedResult.getExecId(), prevEntry);
// // 同时修正OperationId
// lockedResult.setOperationId(prevEntry.getId());
// log.info("通过ExecId匹配到Entry并修正OperationId: ExecId={}, EntryId={}, RoutingDetailId={}",
// lockedResult.getExecId(), prevEntry.getId(), prevEntry.getRoutingDetailId());
// } else {
// log.warn("通过ExecId也未找到对应的Entry: ExecId={}", lockedResult.getExecId());
// }
// }
// }
//
// log.info("场景 {} 成功匹配 {} 个Entry信息", dispatchSceneId, lockedEntries.size());
// }
//
// } else {
// log.warn("场景 {} 的排产结果为空", dispatchSceneId);
// }
// } else {
// log.warn("场景 {} 的排产结果文件不存在: {}", dispatchSceneId, dispatchFile.getAbsolutePath());
// }
// } else {
// log.warn("未找到场景 {} 的版本信息", dispatchSceneId);
// }
// } catch (Exception ex) {
// log.error("处理场景 {} 的排产结果失败: {}", dispatchSceneId, ex.getMessage(), ex);
// }
// }
//
// log.info("从 {} 个已下发场景排产结果中获取到 {} 个锁定期工单", dispatchSceneIds.size(), lockedResults.size());
// } else {
// log.warn("Dispatch表中的工单没有sceneId信息");
// }
// } catch (Exception ex) {
// log.error("从已下发场景排产结果获取锁定期工单失败: {}", ex.getMessage(), ex);
// }
//
// // 3. 如果无法从上次排产获取,则从Dispatch表查询
// if (lockedResults.isEmpty()) {
// log.info("从Dispatch表查询锁定期工单");
// List<Dispatch> frozenDispatches = dispatchService.lambdaQuery()
// .ge(Dispatch::getBeginTime, lockStartTime)
// .le(Dispatch::getBeginTime, lockEndTime)
// .eq(Dispatch::getIsDeleted, 0L)
// .orderBy(true, true, Dispatch::getMesCode, Dispatch::getTaskSeq)
// .list();
//
// if (frozenDispatches.isEmpty()) {
// log.info("冻结期内没有工单需要添加");
// return;
// }
//
// log.info("查询到冻结期内的工序数: {}", frozenDispatches.size());
//
// for (Dispatch dispatch : frozenDispatches) {
// GAScheduleResult result = new GAScheduleResult();
//
// log.info("处理Dispatch记录: id={}, executeId={}, mesCode={}, equipId={}, beginTime={}, endTime={}",
// dispatch.getId(), dispatch.getExecuteId(), dispatch.getMesCode(),
// dispatch.getEquipId(), dispatch.getBeginTime(), dispatch.getEndTime());
//
// // 设置基本信息
// result.setOperationId(dispatch.getExecuteId() != null ? dispatch.getExecuteId().intValue() : 0);
// result.setExecId(dispatch.getExecuteId() != null ? dispatch.getExecuteId().toString() : "");
// result.setOrderId(dispatch.getMesCode() != null ? dispatch.getMesCode() : "");
// result.setOrderCode(dispatch.getMesCode());
// result.setMachineId(dispatch.getEquipId() != null ? dispatch.getEquipId() : 0L);
// result.setQuantity(dispatch.getQuantity() != null ? dispatch.getQuantity() : 0.0);
//
// // 设置产品信息(从Dispatch表中可能没有,使用默认值)
// result.setProductId("");
// result.setProductName("");
// result.setProductCode("");
//
// log.info("从Dispatch创建锁定期工单: executeId={}, OperationId={}, mesCode={}",
// dispatch.getExecuteId(), result.getOperationId(), dispatch.getMesCode());
//
// // 计算相对时间(相对于baseTime的秒数)
// // 注意:锁定期工单的时间通常在baseTime之前,所以会是负数
// if (dispatch.getBeginTime() != null && dispatch.getEndTime() != null) {
// // 使用Dispatch表中的绝对时间,计算相对于baseTime的偏移(负数表示在baseTime之前)
// int startTime = (int) ChronoUnit.SECONDS.between(baseTime, dispatch.getBeginTime());
// int endTime = (int) ChronoUnit.SECONDS.between(baseTime, dispatch.getEndTime());
//
// log.info("锁定期工单时间计算: ExecId={}, Dispatch时间=[{} 到 {}], baseTime={}, 相对时间=[{} 到 {}]秒",
// dispatch.getExecuteId(), dispatch.getBeginTime(), dispatch.getEndTime(),
// baseTime, startTime, endTime);
//
// result.setStartTime(startTime);
// result.setEndTime(endTime);
// result.setProcessingTime((double) (endTime - startTime) / 60.0); // 转换为分钟
// }
//
// // 设置其他信息
// result.setIsLocked(true); // 标记为锁定
// result.setTeardownTime(0);
// result.setChangeOverTime(0);
// result.setPreTime(0);
// result.setSeq(dispatch.getTaskSeq() != null ? dispatch.getTaskSeq().intValue() : 0);
// result.setBomTime(0);
// result.setDesignatedStartTime(-1);
// result.setLockStartTime(0);
// result.setForcedMachineId(-1L);
//
// // 创建GeneDetails
// List<ScheduleResultDetail> geneDetails = new ArrayList<>();
// ScheduleResultDetail detail = new ScheduleResultDetail();
// detail.setStartTime(result.getStartTime());
// detail.setEndTime(result.getEndTime());
// detail.setQuantity(dispatch.getQuantity() != null ? dispatch.getQuantity() : 0.0);
// geneDetails.add(detail);
// result.setGeneDetails(geneDetails);
//
// lockedResults.add(result);
// lockedMachineIds.add(result.getMachineId());
//
// log.debug("添加锁定期工单: ExecId={}, OrderCode={}, MachineId={}, StartTime={}, EndTime={}",
// result.getExecId(), result.getOrderCode(), result.getMachineId(), result.getStartTime(), result.getEndTime());
// }
// }
//
// // 4. 添加到chromosome的result中
// if (chromosome.getResult() == null) {
// log.warn("chromosome.getResult()为null,创建新的CopyOnWriteArrayList");
// chromosome.setResult(new CopyOnWriteArrayList<>());
// }
//
// int beforeSize = chromosome.getResult().size();
// chromosome.getResult().addAll(lockedResults);
// int afterSize = chromosome.getResult().size();
//
// log.info("成功添加 {} 个锁定期工单到调度结果中,result大小从{}变为{}", lockedResults.size(), beforeSize, afterSize);
//
// // 5. 从已下发场景复制锁定期订单的Order对象
// if (!lockedResults.isEmpty()) {
// if (chromosome.getOrders() == null) {
// chromosome.setOrders(new CopyOnWriteArrayList<>());
// }
//
// int beforeOrderSize = chromosome.getOrders().size();
//
// // 收集锁定期工单的订单ID
// Set<String> lockedOrderIds = lockedResults.stream()
// .map(GAScheduleResult::getOrderId)
// .filter(Objects::nonNull)
// .collect(Collectors.toSet());
//
// log.info("需要复制的锁定期订单ID: {}", lockedOrderIds);
//
// // 从已下发场景中复制对应的Order对象
// try {
// // 获取已下发场景的sceneId
// List<Dispatch> frozenDispatches = dispatchService.lambdaQuery()
// .ge(Dispatch::getBeginTime, lockStartTime)
// .le(Dispatch::getBeginTime, lockEndTime)
// .eq(Dispatch::getIsDeleted, 0L)
// .list();
//
// log.info("查询到 {} 个锁定期Dispatch记录", frozenDispatches.size());
//
// Set<String> dispatchSceneIds = frozenDispatches.stream()
// .map(Dispatch::getSceneId)
// .filter(id -> id != null && !id.isEmpty())
// .collect(Collectors.toSet());
//
// log.info("锁定期Dispatch记录来自 {} 个场景: {}", dispatchSceneIds.size(), dispatchSceneIds);
//
// // 遍历每个已下发场景,复制锁定期订单
// for (String dispatchSceneId : dispatchSceneIds) {
// try {
// log.info("开始从场景 {} 加载Chromosome", dispatchSceneId);
// Chromosome dispatchChromosome = _sceneService.loadChromosomeFromFile(dispatchSceneId);
//
// if (dispatchChromosome != null) {
// log.info("场景 {} 加载成功,Orders数量: {}", dispatchSceneId,
// dispatchChromosome.getOrders() != null ? dispatchChromosome.getOrders().size() : 0);
//
// if (dispatchChromosome.getOrders() != null) {
// // 筛选出锁定期订单
// List<Order> lockedOrders = dispatchChromosome.getOrders().stream()
// .filter(order -> {
// boolean matches = lockedOrderIds.contains(order.getOrderId());
// log.debug("订单 {} 是否匹配锁定期: {}", order.getOrderId(), matches);
// return matches;
// })
// .collect(Collectors.toList());
//
// log.info("场景 {} 中找到 {} 个匹配的锁定期订单", dispatchSceneId, lockedOrders.size());
//
// // 复制到当前场景,避免重复添加
// for (Order lockedOrder : lockedOrders) {
// boolean exists = chromosome.getOrders().stream()
// .anyMatch(order -> lockedOrder.getOrderId().equals(order.getOrderId()));
//
// if (!exists) {
// // 直接添加原Order对象(已包含完整信息)
// chromosome.getOrders().add(lockedOrder);
//
// log.info("从场景 {} 复制锁定期订单: OrderId={}, OrderCode={}",
// dispatchSceneId, lockedOrder.getOrderId(), lockedOrder.getOrderCode());
// } else {
// log.warn("订单 {} 已存在,跳过复制", lockedOrder.getOrderId());
// }
// }
// } else {
// log.warn("场景 {} 的Orders为null", dispatchSceneId);
// }
// } else {
// log.warn("场景 {} 加载失败,Chromosome为null", dispatchSceneId);
// }
// } catch (Exception ex) {
// log.error("从场景 {} 复制锁定期订单失败: {}", dispatchSceneId, ex.getMessage(), ex);
// }
// }
// } catch (Exception e) {
// log.error("复制锁定期订单失败: {}", e.getMessage(), e);
// }
//
// int afterOrderSize = chromosome.getOrders().size();
// log.info("成功复制锁定期订单到Orders列表,大小从{}变为{}", beforeOrderSize, afterOrderSize);
// }
//
// // 5. 添加锁定期Entry到allOperations中
// if (!lockedEntries.isEmpty() && chromosome.getAllOperations() != null) {
// int beforeEntrySize = chromosome.getAllOperations().size();
// chromosome.getAllOperations().addAll(lockedEntries.values());
// log.info("成功添加 {} 个锁定期Entry到allOperations中,大小从{}变为{}",
// lockedEntries.size(), beforeEntrySize, chromosome.getAllOperations().size());
// }
//
// // 5. 确保锁定期工单使用的设备在initMachines中
// // 如果设备在本次排产中没有使用,则从已下发场景中获取设备信息
// if (chromosome.getInitMachines() != null && !lockedMachineIds.isEmpty()) {
// Set<Long> existingMachineIds = chromosome.getInitMachines().stream()
// .map(Machine::getId)
// .collect(Collectors.toSet());
//
// // 筛选出不在本次排产中的设备
// Set<Long> missingMachineIds = lockedMachineIds.stream()
// .filter(id -> id != null && id > 0 && !existingMachineIds.contains(id))
// .collect(Collectors.toSet());
//
// if (!missingMachineIds.isEmpty()) {
// log.info("锁定期工单使用的设备不在本次排产中,需要从已下发场景获取设备信息: {}", missingMachineIds);
//
// // 尝试从已下发场景的排产结果中获取这些设备的信息
// try {
// // 从Dispatch表查询锁定期内的工单,获取它们的所有sceneId
// List<Dispatch> frozenDispatches = dispatchService.lambdaQuery()
// .ge(Dispatch::getBeginTime, lockStartTime)
// .le(Dispatch::getBeginTime, lockEndTime)
// .eq(Dispatch::getIsDeleted, 0L)
// .list();
//
// // 获取所有已下发场景的sceneId
// Set<String> dispatchSceneIds = frozenDispatches.stream()
// .map(Dispatch::getSceneId)
// .filter(id -> id != null && !id.isEmpty())
// .collect(Collectors.toSet());
//
// boolean foundMachines = false;
//
// // 遍历每个场景查找设备信息
// for (String dispatchSceneId : dispatchSceneIds) {
// if (foundMachines) break; // 如果已经找到所有设备,跳出循环
//
// try {
// log.info("从场景 {} 获取设备信息", dispatchSceneId);
//
// // 加载已下发场景的排产结果
// SceneChromsome dispatchSceneChromsome = (SceneChromsome) redisUtils.get("SceneId." + dispatchSceneId);
// if (dispatchSceneChromsome != null) {
// int dispatchVersion = dispatchSceneChromsome.getVersion();
// File dispatchFile = new File("result", "chromosome_result_" + dispatchSceneId + "_" + dispatchVersion + "_.json");
//
// if (dispatchFile.exists()) {
// ObjectMapper objectMapper = new ObjectMapper();
// objectMapper.registerModule(new JavaTimeModule());
// objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// objectMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//
// Chromosome dispatchChromosome = objectMapper.readValue(dispatchFile, Chromosome.class);
//
// if (dispatchChromosome.getInitMachines() != null) {
// // 从已下发场景的排产结果中查找这些设备
// List<Machine> dispatchMachines = dispatchChromosome.getInitMachines().stream()
// .filter(m -> missingMachineIds.contains(m.getId()))
// .collect(Collectors.toList());
//
// if (!dispatchMachines.isEmpty()) {
// log.info("从场景 {} 的排产结果中找到 {} 个锁定期设备", dispatchSceneId, dispatchMachines.size());
// chromosome.getInitMachines().addAll(dispatchMachines);
//
// // 从缺失列表中移除已找到的设备
// Set<Long> foundMachineIds = dispatchMachines.stream()
// .map(Machine::getId)
// .collect(Collectors.toSet());
// missingMachineIds.removeAll(foundMachineIds);
//
// // 如果所有设备都找到了,标记为完成
// if (missingMachineIds.isEmpty()) {
// foundMachines = true;
// }
// }
// }
// } else {
// log.warn("场景 {} 的排产结果文件不存在: {}", dispatchSceneId, dispatchFile.getAbsolutePath());
// }
// } else {
// log.warn("未找到场景 {} 的版本信息", dispatchSceneId);
// }
// } catch (Exception ex) {
// log.error("从场景 {} 获取设备信息失败: {}", dispatchSceneId, ex.getMessage(), ex);
// }
// }
//
// // 如果还有设备没找到,创建默认设备信息
// if (!missingMachineIds.isEmpty()) {
// log.warn("在所有已下发场景中都没有找到锁定期设备,将创建默认设备信息: {}", missingMachineIds);
// createDefaultMachines(chromosome, missingMachineIds);
// }
// } catch (Exception ex) {
// log.error("从已下发场景排产结果获取设备信息失败: {}", ex.getMessage(), ex);
// createDefaultMachines(chromosome, missingMachineIds);
// }
// }
// }
//
// } catch (Exception e) {
// log.error("添加锁定期工单时发生错误: {}", e.getMessage(), e);
// }
// }
//
// /**
// * 为锁定期工单创建默认设备信息
// * @param chromosome 染色体对象
// * @param machineIds 需要创建的设备ID集合
// */
// private void createDefaultMachines(Chromosome chromosome, Set<Long> machineIds) {
// try {
// // 查询这些设备的信息
// List<PlanResource> planResources = _PlanResourceService.lambdaQuery()
// .eq(PlanResource::getIsdeleted, 0)
// .in(PlanResource::getId, machineIds.stream()
// .map(Long::intValue)
// .collect(Collectors.toList()))
// .list();
//
// // 查询班次信息
// List<MesShiftWorkSched> allShiftWorkScheds = _MesShiftWorkSchedService.lambdaQuery()
// .eq(MesShiftWorkSched::getIsdeleted, 0)
// .list();
//
// // 创建Machine对象并添加到initMachines
// for (Long machineId : machineIds) {
// Machine machine = new Machine();
// machine.setId(machineId);
//
// PlanResource planResource = planResources.stream()
// .filter(pr -> pr.getId() == machineId.intValue())
// .findFirst()
// .orElse(null);
//
// if (planResource != null) {
// machine.setCode(planResource.getReferenceCode());
// machine.setName(planResource.getTitle());
// machine.setDepartment(planResource.getDepartTitle());
//
// log.info("为设备 {} 查询班次信息,workSchedId: {}", machineId, planResource.getWorkSchedId());
//
// // 查询设备的真实班次信息
// List<MesShiftWorkSched> shiftWorkScheds = allShiftWorkScheds.stream()
// .filter(t -> (long) t.getWeekWorkSchedId() == planResource.getWorkSchedId())
// .collect(Collectors.toList());
//
// log.info("设备 {} 找到 {} 个班次记录", machineId, shiftWorkScheds.size());
//
// if (!shiftWorkScheds.isEmpty()) {
// // 使用真实的班次信息
// List<Shift> shifts = mergeShiftData(shiftWorkScheds);
// for (Shift shift : shifts) {
// shift.setMachineId(machineId);
// shift.setSpecial(false);
// shift.setStartDate(LocalDateTime.of(2000, 1, 1, 0, 0, 0));
// shift.setEndDate(LocalDateTime.of(2000, 1, 1, 0, 0, 0));
// }
// machine.setShifts(shifts);
// log.info("为设备 {} 设置真实班次信息,共 {} 个班次", machineId, shifts.size());
// } else {
// // 如果没有找到班次信息,使用默认24小时班次
// machine.setShifts(createDefault24HourShift(machineId));
// log.warn("设备 {} 没有找到班次信息,使用默认24小时班次", machineId);
// }
// } else {
// machine.setCode("LOCKED-" + machineId);
// machine.setName("锁定期设备-" + machineId);
// // 使用默认24小时班次
// machine.setShifts(createDefault24HourShift(machineId));
// log.warn("设备 {} 没有找到PlanResource信息,使用默认24小时班次", machineId);
// }
//
// chromosome.getInitMachines().add(machine);
// }
//
// log.info("成功创建 {} 个默认锁定期设备", machineIds.size());
// } catch (Exception e) {
// log.error("创建默认设备信息失败: {}", e.getMessage(), e);
// }
// }
//
// /**
// * 创建默认的24小时班次
// * @param machineId 设备ID
// * @return 24小时班次列表
// */
// private List<Shift> createDefault24HourShift(Long machineId) {
// List<Shift> shifts = new ArrayList<>();
// Shift shift = new Shift();
// shift.setMachineId(machineId);
// shift.setStartTime(LocalTime.of(0, 0, 0));
// shift.setEndTime(LocalTime.of(23, 59, 59));
// HashSet<Integer> days = new HashSet<>();
// for (int i = 0; i <= 6; i++) {
// days.add(i);
// }
// shift.setDays(days);
// shift.setSpecial(false);
// shift.setStartDate(LocalDateTime.of(2000, 1, 1, 0, 0, 0));
// shift.setEndDate(LocalDateTime.of(2000, 1, 1, 0, 0, 0));
// shifts.add(shift);
// return shifts;
// }
//
// /**
// * 深拷贝 GAScheduleResult 对象
// *
// * @param source 源对象
// * @return 拷贝后的新对象
// */
// private GAScheduleResult copyGAScheduleResult(GAScheduleResult source) {
// if (source == null) {
// return null;
// }
//
// GAScheduleResult copy = new GAScheduleResult();
//
// // 拷贝基本字段
// copy.setGroupId(source.getGroupId());
// copy.setOperationId(source.getOperationId());
// copy.setExecId(source.getExecId());
// copy.setOrderId(source.getOrderId());
// copy.setOrderCode(source.getOrderCode());
// copy.setProductId(source.getProductId());
// copy.setProductName(source.getProductName());
// copy.setProductCode(source.getProductCode());
// copy.setMachineId(source.getMachineId());
// copy.setStartTime(source.getStartTime());
// copy.setEndTime(source.getEndTime());
// copy.setTeardownTime(source.getTeardownTime());
// copy.setQuantity(source.getQuantity());
// copy.setDesignatedStartTime(source.getDesignatedStartTime());
// copy.setLockStartTime(source.getLockStartTime());
// copy.setForcedMachineId(source.getForcedMachineId());
// copy.setIsLocked(source.isIsLocked());
// copy.setOneTime(source.getOneTime());
// copy.setProcessingTime(source.getProcessingTime());
// copy.setChangeOverTime(source.getChangeOverTime());
// copy.setPreTime(source.getPreTime());
// copy.setSeq(source.getSeq());
// copy.setBomTime(source.getBomTime());
//
// // 深拷贝 GeneDetails 列表
// if (source.getGeneDetails() != null) {
// List<ScheduleResultDetail> detailsCopy = new ArrayList<>();
// for (ScheduleResultDetail detail : source.getGeneDetails()) {
// ScheduleResultDetail detailCopy = new ScheduleResultDetail();
// detailCopy.setKey(detail.getKey());
// detailCopy.setStartTime(detail.getStartTime());
// detailCopy.setEndTime(detail.getEndTime());
// detailCopy.setOneTime(detail.getOneTime());
// detailCopy.setQuantity(detail.getQuantity());
//
// // 深拷贝 usedSegment
// if (detail.getUsedSegment() != null) {
// List<TimeSegment> segmentsCopy = new ArrayList<>(detail.getUsedSegment());
// detailCopy.setUsedSegment(segmentsCopy);
// }
//
// detailsCopy.add(detailCopy);
// }
// copy.setGeneDetails(detailsCopy);
// }
//
// // 深拷贝 TargetFinishedOperationId 列表
// if (source.getTargetFinishedOperationId() != null) {
// copy.setTargetFinishedOperationId(new ArrayList<>(source.getTargetFinishedOperationId()));
// }
//
// return copy;
// }
//
// /**
// * 在排产前标记锁定期工单占用的设备时间段
// * 将锁定期工单占用的设备时间段标记为不可用,避免新工单与锁定期工单冲突
// *
// * @param machines 设备列表
// * @param baseTime 基准时间
// */
// private void markLockedOrdersOccupiedTime(List<Machine> machines, LocalDateTime baseTime) {
// try {
// // 1. 获取时间配置
// ApsTimeConfig timeConfig = apsTimeConfigService.getOne(new LambdaQueryWrapper<>());
// if (timeConfig == null || timeConfig.getBaseTime() == null) {
// log.warn("未找到ApsTimeConfig配置,跳过标记锁定期设备占用");
// return;
// }
//
// long freezeSeconds = timeConfig.getFreezeDate() != null ? timeConfig.getFreezeDate().longValue() : 0;
// if (freezeSeconds <= 0) {
// log.info("冻结期秒数为{},跳过标记锁定期设备占用", freezeSeconds);
// return;
// }
//
// // 2. 计算锁定期范围
// LocalDateTime lockStartTime = baseTime;
// LocalDateTime lockEndTime = baseTime.plusSeconds(freezeSeconds);
//
// log.info("开始标记锁定期设备占用时间段,锁定期范围: {} 到 {}", lockStartTime, lockEndTime);
//
// // 3. 从Dispatch表查询锁定期内的工单
// List<Dispatch> frozenDispatches = dispatchService.lambdaQuery()
// .ge(Dispatch::getBeginTime, lockStartTime)
// .le(Dispatch::getBeginTime, lockEndTime)
// .eq(Dispatch::getIsDeleted, 0L)
// .isNotNull(Dispatch::getEquipId)
// .isNotNull(Dispatch::getBeginTime)
// .isNotNull(Dispatch::getEndTime)
// .list();
//
// if (frozenDispatches.isEmpty()) {
// log.info("没有锁定期工单需要标记设备占用");
// return;
// }
//
// log.info("查询到 {} 个锁定期工单,开始标记设备占用", frozenDispatches.size());
//
// // 4. 按设备分组
// Map<Long, List<Dispatch>> dispatchByMachine = frozenDispatches.stream()
// .collect(Collectors.groupingBy(Dispatch::getEquipId));
//
// // 5. 为每个设备标记占用时间段
// int markedCount = 0;
// for (Map.Entry<Long, List<Dispatch>> entry : dispatchByMachine.entrySet()) {
// Long machineId = entry.getKey();
// List<Dispatch> dispatches = entry.getValue();
//
// // 查找对应的Machine对象
// Machine machine = machines.stream()
// .filter(m -> m.getId() == machineId)
// .findFirst()
// .orElse(null);
//
// if (machine == null) {
// log.warn("未找到设备 {},跳过标记", machineId);
// continue;
// }
//
// // 为该设备的每个锁定期工单创建占用时间段
// for (Dispatch dispatch : dispatches) {
// LocalDateTime dispatchStart = dispatch.getBeginTime();
// LocalDateTime dispatchEnd = dispatch.getEndTime();
//
// // 只标记结束时间在baseTime之后的工单(这些会占用未来的设备时间)
// if (dispatchEnd.isAfter(baseTime)) {
// // 如果开始时间在baseTime之前,则从baseTime开始标记
// LocalDateTime occupyStart = dispatchStart.isBefore(baseTime) ? baseTime : dispatchStart;
// LocalDateTime occupyEnd = dispatchEnd;
//
// // 创建一个不可用的时间段
// TimeSegment occupiedSegment = new TimeSegment();
// occupiedSegment.setStart(occupyStart);
// occupiedSegment.setEnd(occupyEnd);
// occupiedSegment.setType(SegmentType.MAINTENANCE); // 标记为不可用
// occupiedSegment.setUsed(true); // 标记为已占用
// occupiedSegment.setHoliday(false);
// occupiedSegment.setEfficiency(1.0);
//
// // 添加到设备的可用时间段列表中
// if (machine.getAvailability() == null) {
// machine.setAvailability(new CopyOnWriteArrayList<>());
// }
// machine.getAvailability().add(occupiedSegment);
//
// markedCount++;
// log.debug("标记设备 {} 的锁定期占用时间段: {} 到 {}, 订单: {}",
// machineId, occupyStart, occupyEnd, dispatch.getMesCode());
// }
// }
// }
//
// log.info("成功标记 {} 个锁定期工单的设备占用时间段", markedCount);
//
// } catch (Exception e) {
// log.error("标记锁定期设备占用时间段失败: {}", e.getMessage(), e);
// }
// }
public Chromosome editMachineOption(String SceneId, Entry operation,
Long newMachineId) {
......@@ -424,6 +1243,17 @@ public class PlanResultService {
entry.setPriority(order.getActualPriority());
});
}
// 更新调度结果中的数量(关键:处理锁定期工单)
List<GAScheduleResult> results = chromosome.getResult();
if (results != null) {
results.stream()
.filter(result -> result.getOrderId() != null && result.getOrderId().equals(order.getOrderId()))
.forEach(result -> {
result.setQuantity(order.getQuantity());
log.info("更新调度结果中的订单数量: OrderId={}, 新数量={}", order.getOrderId(), order.getQuantity());
});
}
}
/**
......@@ -458,6 +1288,10 @@ public class PlanResultService {
GlobalParam globalParam=new GlobalParam();
Chromosome chromosome= _sceneService.loadChromosomeFromFile(SceneId);
// 拖拽前清理历史工单在设备上的静态占位段,确保历史工单移动后可释放产能
int releasedCount = releaseLockedOccupancyForDrag(chromosome);
// WriteScheduleSummary(chromosome);
ScheduleOperationService ScheduleOperation=new ScheduleOperationService(materialRequirementService,this);
......@@ -468,6 +1302,150 @@ public class PlanResultService {
_sceneService.saveChromosomeToFile(chromosome, SceneId);
return chromosome;
}
/**
* 释放历史工单占用段(拖拽前调用)
*/
private int releaseLockedOccupancyForDrag(Chromosome chromosome) {
if (chromosome == null || chromosome.getInitMachines() == null || chromosome.getInitMachines().isEmpty()) {
return 0;
}
int releasedCount = 0;
for (Machine machine : chromosome.getInitMachines()) {
if (machine == null || machine.getAvailability() == null || machine.getAvailability().isEmpty()) {
continue;
}
for (TimeSegment segment : machine.getAvailability()) {
if (segment == null) {
continue;
}
boolean lockedOccupyKey = segment.getKey() != null && segment.getKey().startsWith("LOCKED_OCCUPY_");
boolean usedRegularOrMaintenance = segment.isUsed()
&& (segment.getType() == SegmentType.REGULAR || segment.getType() == SegmentType.MAINTENANCE);
if (lockedOccupyKey || usedRegularOrMaintenance) {
releasedCount++;
segment.setUsed(false);
segment.setType(SegmentType.REGULAR);
if (lockedOccupyKey) {
segment.setKey(UUID.randomUUID().toString());
}
}
}
machine.getAvailability().sort(Comparator.comparing(TimeSegment::getStart));
}
return releasedCount;
}
public Chromosome InsertOrderAuto(String sceneId, Map<String, Object> newOrderData) {
if (newOrderData == null) {
throw new RuntimeException("newOrder 不能为空");
}
String orderCode = String.valueOf(newOrderData.get("orderCode"));
String materialId = String.valueOf(newOrderData.get("materialId"));
Object qtyObj = newOrderData.get("quantity");
if (orderCode == null || orderCode.trim().isEmpty()) {
throw new RuntimeException("orderCode 不能为空");
}
if (materialId == null || materialId.trim().isEmpty()) {
throw new RuntimeException("materialId 不能为空");
}
if (qtyObj == null) {
throw new RuntimeException("quantity 不能为空");
}
Double quantity = Double.valueOf(String.valueOf(qtyObj));
// 1. 创建新订单(沿用现有创建逻辑)
R<String> insertResp = lanuchService.insertOrder(sceneId, orderCode, materialId, null, null, 1, quantity);
String insertMsg = insertResp != null ? insertResp.getData() : null;
if (insertMsg == null || insertMsg.trim().isEmpty()) {
throw new RuntimeException("创建订单失败:未返回订单ID");
}
String newOrderId = insertMsg;
if (insertMsg.contains("订单ID:")) {
newOrderId = insertMsg.substring(insertMsg.indexOf("订单ID:") + 5).trim();
}
// 2. 读取场景排产结果
Chromosome chromosome = _sceneService.loadChromosomeFromFile(sceneId);
if (chromosome == null) {
throw new RuntimeException("场景不存在或排产结果未初始化");
}
// 3. 查询新订单及其工序
ProdLaunchOrder newLaunchOrder = _prodLaunchOrderService.lambdaQuery()
.eq(ProdLaunchOrder::getSceneId, sceneId)
.eq(ProdLaunchOrder::getOrderId, newOrderId)
.one();
if (newLaunchOrder == null) {
throw new RuntimeException("新订单不存在:" + newOrderId);
}
List<ProdProcessExec> newProcessExecs = _prodProcessExecService.lambdaQuery()
.eq(ProdProcessExec::getSceneId, sceneId)
.eq(ProdProcessExec::getOrderId, newOrderId)
.orderByAsc(ProdProcessExec::getTaskSeq)
.list();
if (newProcessExecs == null || newProcessExecs.isEmpty()) {
throw new RuntimeException("新订单没有工序:" + newOrderId);
}
// 3.1 查询新工序对应设备(与InitEntrys对齐:execId -> prod_equipment)
List<String> execIds = newProcessExecs.stream()
.map(ProdProcessExec::getExecId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
List<ProdEquipment> newProdEquipments = execIds.isEmpty()
? new ArrayList<>()
: _prodEquipmentService.lambdaQuery()
.eq(ProdEquipment::getSceneId, sceneId)
.in(ProdEquipment::getExecId, execIds)
.list();
if (newProdEquipments == null || newProdEquipments.isEmpty()) {
throw new RuntimeException("自动插单失败:新工单未生成可选设备,请检查PROD_EQUIPMENT");
}
// 4. 计算锚点时间:基准时间 + 冻结期
LocalDateTime baseTime = chromosome.getBaseTime();
LambdaQueryWrapper<ApsTimeConfig> queryWrapper = new LambdaQueryWrapper<>();
ApsTimeConfig apsTimeConfig = apsTimeConfigMapper.selectOne(queryWrapper);
long freezeSeconds = 0L;
if (apsTimeConfig != null && apsTimeConfig.getFreezeDate() != null) {
freezeSeconds = apsTimeConfig.getFreezeDate().longValue();
}
if (apsTimeConfig != null && apsTimeConfig.getBaseTime() != null) {
baseTime = apsTimeConfig.getBaseTime();
}
LocalDateTime anchorTime = baseTime.plusSeconds(Math.max(freezeSeconds, 0L));
// 5. 自动插单(占位后推 + 空挡前移)
GlobalParam globalParam = InitGlobalParam();
ScheduleOperationService scheduleOperation = new ScheduleOperationService(materialRequirementService, this);
scheduleOperation.InsertOrderAuto(
chromosome,
newOrderId,
newLaunchOrder,
newProcessExecs,
newProdEquipments,
anchorTime,
globalParam
);
// 6. 保存
WriteScheduleSummary(chromosome);
_sceneService.saveChromosomeToFile(chromosome, sceneId);
return chromosome;
}
public Chromosome Move(String SceneId,List<Integer> opId, LocalDateTime newStartTime,
Long newMachineId,int lockStartTime) {
......@@ -608,6 +1586,92 @@ public class PlanResultService {
_sceneService.saveChromosomeToFile(chromosome, SceneId);
return chromosome;
}
/**
* 插单功能:在指定订单后插入新订单
* 步骤1:先将新订单数据插入数据库
* 步骤2:使用类似复制订单的方式将新订单加入排产
*
* @param SceneId 场景ID
* @param afterOrderId 插入位置订单ID(新订单将排在此订单后面)
* @param newOrderData 新订单数据
* @return 更新后的Chromosome
*/
public Chromosome InsertOrder(String SceneId, String afterOrderId, Map<String, Object> newOrderData) {
// 步骤1:调用LanuchService的insertOrder方法,将新订单插入数据库
String orderCode = (String) newOrderData.get("orderCode");
String materialId = (String) newOrderData.get("materialId");
Double quantity = ((Number) newOrderData.get("quantity")).doubleValue();
LocalDateTime startDate = null;
LocalDateTime endDate = null;
Integer priority = 1;
if (newOrderData.containsKey("startDate")) {
startDate = LocalDateTime.parse((String) newOrderData.get("startDate"));
}
if (newOrderData.containsKey("endDate")) {
endDate = LocalDateTime.parse((String) newOrderData.get("endDate"));
}
if (newOrderData.containsKey("priority")) {
priority = ((Number) newOrderData.get("priority")).intValue();
}
// 调用insertOrder插入数据库
R<String> insertResult = lanuchService.insertOrder(SceneId, orderCode, materialId,
startDate, endDate, priority, quantity);
// if (!insertResult.isSuccess()) {
// throw new RuntimeException("插单失败: " + insertResult.getMsg());
// }
//
// 从返回消息中提取新订单ID
String message = insertResult.getData();
String newOrderId = message.substring(message.indexOf("订单ID: ") + 6);
log.info("新订单已插入数据库,订单ID: {}", newOrderId);
// 步骤2:使用类似SpiltOrder的方式,将新订单加入排产
// 这里需要重新加载场景,因为数据库已经有新订单了
// 然后使用类似复制的逻辑,将新订单排在afterOrderId后面
GlobalParam globalParam = new GlobalParam();
Chromosome chromosome = _sceneService.loadChromosomeFromFile(SceneId);
// 从数据库加载新插入的订单及其工序
ProdLaunchOrder newLaunchOrder = _prodLaunchOrderService.lambdaQuery()
.eq(ProdLaunchOrder::getSceneId, SceneId)
.eq(ProdLaunchOrder::getOrderId, newOrderId)
.one();
if (newLaunchOrder == null) {
throw new RuntimeException("未找到新插入的订单: " + newOrderId);
}
// 加载新订单的工序
List<ProdProcessExec> newProcessExecs = _prodProcessExecService.lambdaQuery()
.eq(ProdProcessExec::getSceneId, SceneId)
.eq(ProdProcessExec::getOrderId, newOrderId)
.orderBy(true, true, ProdProcessExec::getTaskSeq)
.list();
if (newProcessExecs.isEmpty()) {
throw new RuntimeException("新订单没有工序: " + newOrderId);
}
log.info("加载新订单: OrderId={}, OrderCode={}, 工序数={}",
newOrderId, newLaunchOrder.getOrderCode(), newProcessExecs.size());
// 调用ScheduleOperationService的方法将新订单加入排产
// 设备配置将从插入位置的订单复制,不需要单独加载
ScheduleOperationService scheduleOperation = new ScheduleOperationService(materialRequirementService, this);
scheduleOperation.InsertOrder(chromosome, afterOrderId, newOrderId, newLaunchOrder, newProcessExecs, globalParam);
WriteScheduleSummary(chromosome);
_sceneService.saveChromosomeToFile(chromosome, SceneId);
return chromosome;
}
public Chromosome MergeOrder(String SceneId,String sourceorderId,String targetorderId) {
......@@ -771,7 +1835,6 @@ public class PlanResultService {
param.initAdaptiveParams(entrys.size());
double[] customWeights = new double[] { 0.4, 0.1, 0.1, 0.1, 0.3 }; // 延迟时间权重提升到0.5
//完工时间、总流程时间、总换型时间、机器负载标准差、延迟时间
// scheduler.Init(customWeights,false);
scheduler.Init();
Chromosome chromosomes =scheduler.Run(param,entrys);
KpiCalculator kpiCalculator=new KpiCalculator(chromosomes);
......@@ -863,7 +1926,7 @@ public class PlanResultService {
}
}
private String ConvertTime(int minute) {
return baseTime.plusSeconds(minute).format(java.time.format.DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss"));
return baseTime.plusSeconds(minute).format(DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss"));
}
/**
......@@ -979,8 +2042,8 @@ public class PlanResultService {
FileHelper.writeLogFile("初始化排产参数-----------结束-------");
return param;
}
private GlobalParam InitGlobalParam()
{
private GlobalParam InitGlobalParam()
{
FileHelper.writeLogFile("初始化约束-----------开始-------");
GlobalParam globalParam=new GlobalParam();
......@@ -1035,7 +2098,7 @@ private GlobalParam InitGlobalParam()
}
FileHelper.writeLogFile("初始化约束-----------结束-------");
return globalParam;
}
}
private List<Order> InitOrder(List<ProdLaunchOrder> ProdLaunchOrders)
{
orderSortService.initializeFieldExtractors();
......@@ -1435,13 +2498,19 @@ private GlobalParam InitGlobalParam()
// 找到上一道工序的结束时间
LocalDateTime lastEntryEndTime = getEntryEndTime(lastEntry, results, baseTime);
Order fromOrder1 = orders != null ? orders.stream().filter(o -> o.getOrderId() != null && o.getOrderId().equals(lastEntry.getOrderId())).findFirst().orElse(null) : null;
Order toOrder1 = orders != null ? orders.stream().filter(o -> o.getOrderId() != null && o.getOrderId().equals(currentEntry.getOrderId())).findFirst().orElse(null) : null;
long fromEquipId1 = results.stream().filter(r -> r.getOperationId() == lastEntry.getId()).mapToLong(r -> r.getMachineId()).findFirst().orElse(-1L);
long toEquipId1 = results.stream().filter(r -> r.getOperationId() == currentEntry.getId()).mapToLong(r -> r.getMachineId()).findFirst().orElse(-1L);
Map<String, Object> item = new HashMap<>();
item.put("taskIdFrom", String.valueOf(lastEntry.getOrderId()));
item.put("taskIdTo", String.valueOf(currentEntry.getOrderId()));
item.put("fromId", String.valueOf(lastEntry.getId()));
item.put("fromPlanId", fromOrder1 != null ? String.valueOf(fromOrder1.getId()) : "");
item.put("fromEquipId", String.valueOf(fromEquipId1));
item.put("fromTime", lastEntryEndTime != null ? lastEntryEndTime.toString() : "2025-12-13");
item.put("toId", String.valueOf(currentEntry.getId()));
item.put("toPlanId", toOrder1 != null ? String.valueOf(toOrder1.getId()) : "");
item.put("toEquipId", String.valueOf(toEquipId1));
item.put("toTime", currentStartTime != null ? currentStartTime.toString() : "2026-12-13");
item.put("fromIdFrom", String.valueOf(lastEntry.getId()));
item.put("toIdTo", currentEntry.getId());
supplyRelations.add(item);
// 递归检查上一个订单的依赖关系
......@@ -1461,15 +2530,21 @@ private GlobalParam InitGlobalParam()
// 找到目标工序的开始时间
LocalDateTime targetStartTime = getEntryStartTime(targetEntry, results, baseTime);
Order fromOrder2 = orders != null ? orders.stream().filter(o -> o.getOrderId() != null && o.getOrderId().equals(currentEntry.getOrderId())).findFirst().orElse(null) : null;
Order toOrder2 = orders != null ? orders.stream().filter(o -> o.getOrderId() != null && o.getOrderId().equals(targetEntry.getOrderId())).findFirst().orElse(null) : null;
long fromEquipId2 = results.stream().filter(r -> r.getOperationId() == currentEntry.getId()).mapToLong(r -> r.getMachineId()).findFirst().orElse(-1L);
long toEquipId2 = results.stream().filter(r -> r.getOperationId() == targetEntry.getId()).mapToLong(r -> r.getMachineId()).findFirst().orElse(-1L);
Map<String, Object> item = new HashMap<>();
item.put("taskIdFrom", String.valueOf(currentEntry.getOrderId()));
item.put("taskIdTo", String.valueOf(targetEntry.getOrderId()));
item.put("fromId", String.valueOf(currentEntry.getId()));
item.put("fromPlanId", fromOrder2 != null ? String.valueOf(fromOrder2.getId()) : "");
item.put("fromEquipId", String.valueOf(fromEquipId2));
item.put("fromTime", currentEndTime != null ? currentEndTime.toString() : "2025-12-13");
item.put("toId", String.valueOf(targetEntry.getId()));
item.put("toPlanId", toOrder2 != null ? String.valueOf(toOrder2.getId()) : "");
item.put("toEquipId", String.valueOf(toEquipId2));
item.put("toTime", targetStartTime != null ? targetStartTime.toString() : "2026-12-13");
item.put("fromIdFrom", String.valueOf(currentEntry.getId()));
item.put("toIdTo", targetEntry.getId());
supplyRelations.add(item);
checkAndAddTargetRelations(entries, results, baseTime, targetEntry, supplyRelations); // 检查后置工序
checkAndAddTargetRelations(entries, orders, results, baseTime, targetEntry, supplyRelations); // 检查后置工序
}
}
}
......@@ -1558,13 +2633,19 @@ private GlobalParam InitGlobalParam()
// 找到上一道工序的结束时间
LocalDateTime lastEntryEndTime = getEntryEndTime(lastEntry, results, baseTime);
Order fromOrder3 = orders != null ? orders.stream().filter(o -> o.getOrderId() != null && o.getOrderId().equals(lastEntry.getOrderId())).findFirst().orElse(null) : null;
Order toOrder3 = orders != null ? orders.stream().filter(o -> o.getOrderId() != null && o.getOrderId().equals(currentEntry.getOrderId())).findFirst().orElse(null) : null;
long fromEquipId3 = results.stream().filter(r -> r.getOperationId() == lastEntry.getId()).mapToLong(r -> r.getMachineId()).findFirst().orElse(-1L);
long toEquipId3 = results.stream().filter(r -> r.getOperationId() == currentEntry.getId()).mapToLong(r -> r.getMachineId()).findFirst().orElse(-1L);
Map<String, Object> item = new HashMap<>();
item.put("taskIdFrom", String.valueOf(lastEntry.getId()));
item.put("taskIdTo", String.valueOf(currentEntry.getId()));
item.put("fromId", String.valueOf(lastEntry.getId()));
item.put("fromPlanId", fromOrder3 != null ? String.valueOf(fromOrder3.getId()) : "");
item.put("fromEquipId", String.valueOf(fromEquipId3));
item.put("fromTime", lastEntryEndTime != null ? lastEntryEndTime.toString() : "2025-12-13");
item.put("toId", String.valueOf(currentEntry.getId()));
item.put("toPlanId", toOrder3 != null ? String.valueOf(toOrder3.getId()) : "");
item.put("toEquipId", String.valueOf(toEquipId3));
item.put("toTime", currentStartTime != null ? currentStartTime.toString() : "2026-12-13");
item.put("fromIdFrom", String.valueOf(lastEntry.getId()));
item.put("toIdTo", currentEntry.getId());
supplyRelations.add(item);
// 递归调用,处理上一个订单的最后一道工序的依赖关系
......@@ -1584,7 +2665,7 @@ private GlobalParam InitGlobalParam()
* @param currentEntry 当前工序
* @param supplyRelations 供给关系列表
*/
private void checkAndAddTargetRelations(List<Entry> entries, List<GAScheduleResult> results, LocalDateTime baseTime, Entry currentEntry, List<Object> supplyRelations) {
private void checkAndAddTargetRelations(List<Entry> entries, List<Order> orders, List<GAScheduleResult> results, LocalDateTime baseTime, Entry currentEntry, List<Object> supplyRelations) {
if (currentEntry.getTargetFinishedOperationId() != null && !currentEntry.getTargetFinishedOperationId().isEmpty()) {
for (Integer targetId : currentEntry.getTargetFinishedOperationId()) {
Entry targetEntry = entries.stream().filter(e -> e.getId() == targetId).findFirst().orElse(null);
......@@ -1593,16 +2674,21 @@ private GlobalParam InitGlobalParam()
LocalDateTime currentEndTime = getEntryEndTime(currentEntry, results, baseTime);
// 找到目标工序的开始时间
LocalDateTime targetStartTime = getEntryStartTime(targetEntry, results, baseTime);
Order fromOrder = orders != null ? orders.stream().filter(o -> o.getOrderId() != null && o.getOrderId().equals(currentEntry.getOrderId())).findFirst().orElse(null) : null;
Order toOrder = orders != null ? orders.stream().filter(o -> o.getOrderId() != null && o.getOrderId().equals(targetEntry.getOrderId())).findFirst().orElse(null) : null;
long fromEquipId4 = results.stream().filter(r -> r.getOperationId() == currentEntry.getId()).mapToLong(r -> r.getMachineId()).findFirst().orElse(-1L);
long toEquipId4 = results.stream().filter(r -> r.getOperationId() == targetEntry.getId()).mapToLong(r -> r.getMachineId()).findFirst().orElse(-1L);
Map<String, Object> item = new HashMap<>();
item.put("taskIdFrom", String.valueOf(currentEntry.getId()));
item.put("taskIdTo", String.valueOf(targetEntry.getId()));
item.put("fromId", String.valueOf(currentEntry.getId()));
item.put("fromPlanId", fromOrder != null ? String.valueOf(fromOrder.getId()) : "");
item.put("fromEquipId", String.valueOf(fromEquipId4));
item.put("fromTime", currentEndTime != null ? currentEndTime.toString() : "2025-12-13");
item.put("toId", String.valueOf(targetEntry.getId()));
item.put("toPlanId", toOrder != null ? String.valueOf(toOrder.getId()) : "");
item.put("toEquipId", String.valueOf(toEquipId4));
item.put("toTime", targetStartTime != null ? targetStartTime.toString() : "2026-12-13");
item.put("fromIdFrom", String.valueOf(currentEntry.getId()));
item.put("toIdTo", targetEntry.getId());
supplyRelations.add(item);
checkAndAddTargetRelations(entries, results, baseTime, targetEntry, supplyRelations); // 递归调用
checkAndAddTargetRelations(entries, orders, results, baseTime, targetEntry, supplyRelations); // 递归调用
}
}
}
......@@ -1909,13 +2995,13 @@ private GlobalParam InitGlobalParam()
for (int i = 0; i < machineList.size(); i++) {
Machine machine = machineList.get(i);
com.aps.entity.Gantt.ResourceGanttVO resourceGanttVO = new com.aps.entity.Gantt.ResourceGanttVO();
ResourceGanttVO resourceGanttVO = new ResourceGanttVO();
resourceGanttVO.setId(machine.getId());
resourceGanttVO.setName(machine.getName());
resourceGanttVO.setShift(convertToShiftVO(machine));
resourceGanttVO.setCode(machine.getCode());
// 转换任务列表
List<com.aps.entity.Gantt.TaskVO> taskVOList = new ArrayList<>();
List<TaskVO> taskVOList = new ArrayList<>();
if (scheduleChromosome.getResult() != null) {
// 筛选出属于当前设备的任务
List<GAScheduleResult> machineGenes = scheduleChromosome.getResult().stream()
......@@ -1947,10 +3033,22 @@ private GlobalParam InitGlobalParam()
taskVO.setEquipCooling(0); // 默认值
taskVO.setEquipType(resourceGanttVO.getType());
taskVO.setEquipName(resourceGanttVO.getName());
taskVO.setLocked(gene.isIsLocked()); // 默认值
taskVO.setLocked(gene.isIsLocked()); // 设置锁定状态
// 处理锁定期工单(entry为null的情况)
if (entry != null) {
taskVO.setSeq(Math.toIntExact(entry.getTaskSeq())); // 使用工序ID
taskVO.setSeq(Math.toIntExact(entry.getTaskSeq()));
taskVO.setSeqName(entry.getRoutingDetailName());
taskVO.setDetailId(entry.getRoutingDetailId());
taskVO.setHeaderId(entry.getRoutingId());
taskVO.setHeaderName(entry.getRoutingName());
} else {
// 锁定期工单使用 gene 中的信息
taskVO.setSeq(gene.getSeq());
taskVO.setSeqName("锁定期工序");
taskVO.setDetailId(0L);
taskVO.setHeaderId(0);
taskVO.setHeaderName("锁定期工单");
}
if (gene.getDesignatedStartTime()>0) {
......@@ -1969,11 +3067,6 @@ private GlobalParam InitGlobalParam()
taskVO.setShopId(machine.getId());
taskVO.setShopName(resourceGanttVO.getShopName());
taskVO.setStatus(0); // 默认值
taskVO.setDetailId(entry.getRoutingDetailId()); // 将productId和operationID组合为detailId
taskVO.setHeaderId(entry.getRoutingId()); // 默认值
taskVO.setHeaderName(entry.getRoutingName()); // 默认值
taskVO.setSeq(Math.toIntExact(entry.getTaskSeq())); // 使用工序ID
taskVO.setSeqName(entry.getRoutingDetailName());
taskVO.setProcessingTime(gene.getProcessingTime());
taskVOList.add(taskVO);
......@@ -2004,8 +3097,9 @@ private GlobalParam InitGlobalParam()
// 按产品ID和工单ID分组基因
if (scheduleChromosome.getResult() != null) {
// 按工单ID分组
// 按工单ID分组(过滤掉 OrderId 为 null 的记录)
scheduleChromosome.getResult().stream()
.filter(r -> r.getOrderId() != null && !r.getOrderId().isEmpty())
.collect(Collectors.groupingBy(GAScheduleResult::getOrderId))
.forEach((orderId, genes) -> {
if (!genes.isEmpty()) {
......@@ -2039,11 +3133,11 @@ private GlobalParam InitGlobalParam()
.max()
.orElse(0);
productGanttVO.setStartDate(scheduleChromosome.getBaseTime().plusMinutes(minStartTime));
productGanttVO.setEndDate(scheduleChromosome.getBaseTime().plusMinutes(maxEndTime));
productGanttVO.setStartDate(scheduleChromosome.getBaseTime().plusSeconds(minStartTime));
productGanttVO.setEndDate(scheduleChromosome.getBaseTime().plusSeconds(maxEndTime));
// 转换任务列表
List<com.aps.entity.Gantt.TaskVO> taskVOList = new ArrayList<>();
List<TaskVO> taskVOList = new ArrayList<>();
// // 按工序顺序排序
// genes.sort((g1, g2) -> Integer.compare(g1.getSequenceId(), g2.getSequenceId()));
......@@ -2053,7 +3147,7 @@ private GlobalParam InitGlobalParam()
Entry entry1 = allOperations.stream()
.filter(t -> t.getId() == gene.getOperationId()).findFirst().orElse(null);
com.aps.entity.Gantt.TaskVO taskVO = new com.aps.entity.Gantt.TaskVO();
TaskVO taskVO = new TaskVO();
taskVO.setId(String.valueOf(gene.getOperationId()));
taskVO.setPlanId(gene.getOrderId());
taskVO.setPlanCode(gene.getOrderCode());
......@@ -2145,9 +3239,46 @@ private GlobalParam InitGlobalParam()
List<ProdEquipment> ProdEquipments= _prodEquipmentService.lambdaQuery()
.eq(ProdEquipment::getSceneId,SceneId)
.list();
List<Long> MachineIds = ProdEquipments.stream()
Set<Long> machineIdSet = ProdEquipments.stream()
.map(ProdEquipment::getEquipId)
.distinct()
.collect(Collectors.toSet());
// 添加锁定期工单使用的设备ID
try {
ApsTimeConfig timeConfig = apsTimeConfigService.getOne(new LambdaQueryWrapper<>());
if (timeConfig != null && timeConfig.getBaseTime() != null && timeConfig.getFreezeDate() != null) {
LocalDateTime baseTime = timeConfig.getBaseTime();
long freezeSeconds = timeConfig.getFreezeDate().longValue();
if (freezeSeconds > 0) {
// 锁定期计算:baseTime 到 baseTime + freezeSeconds
LocalDateTime lockStartTime = baseTime;
LocalDateTime lockEndTime = baseTime.plusSeconds(freezeSeconds);
// 查询锁定期内的工单使用的设备ID
List<Dispatch> frozenDispatches = dispatchService.lambdaQuery()
.ge(Dispatch::getBeginTime, lockStartTime)
.le(Dispatch::getBeginTime, lockEndTime)
.eq(Dispatch::getIsDeleted, 0L)
.isNotNull(Dispatch::getEquipId)
.list();
// 提取设备ID并添加到Set中
for (Dispatch dispatch : frozenDispatches) {
if (dispatch.getEquipId() != null) {
machineIdSet.add(dispatch.getEquipId());
}
}
log.info("锁定期工单使用的所有设备ID: {}", machineIdSet);
}
}
} catch (Exception e) {
log.error("加载锁定期设备ID时发生错误: {}", e.getMessage(), e);
}
// 转换为排序后的List
List<Long> MachineIds = machineIdSet.stream()
.sorted()
.collect(Collectors.toList());
......@@ -2351,6 +3482,12 @@ private GlobalParam InitGlobalParam()
public List<Machine> InitCalendarToAllMachines3(Chromosome chromosome) {
List<Machine> machines = chromosome.getInitMachines();
// 如果initMachines为空,返回空列表
if (machines == null || machines.isEmpty()) {
return new ArrayList<>();
}
Set<Long> machineIds = chromosome.getResult().stream()
.map(GAScheduleResult::getMachineId)
.collect(Collectors.toSet());
......@@ -2362,6 +3499,7 @@ private GlobalParam InitGlobalParam()
{
List<Shift> result = new ArrayList<>();
List<Shift> shifts = machine.getShifts();
if (shifts != null) {
for (Shift shift : shifts) {
// 处理跨天班次(开始时间晚于结束时间的情况,如 7:30 到 3:30)
if (shift.getEndTime().isBefore(shift.getStartTime())) {
......@@ -2389,9 +3527,8 @@ private GlobalParam InitGlobalParam()
// 正常班次直接添加
result.add(shift);
}
}
}
machine.setShifts(result);
}
......
......@@ -39,8 +39,8 @@ public class PlanResultServiceTest {
// TestSortService sortService=new TestSortService();
// sortService.test1();
// nsgaiiUtils.Test();
// planResultService.execute2("0428340BB4F540938F1FB5599F03E8A4");//2000
planResultService.execute2("C8B533BD8944405B9A2F8823C575C204");//500
planResultService.execute2("0428340BB4F540938F1FB5599F03E8A4");//2000
// planResultService.execute2("C8B533BD8944405B9A2F8823C575C204");//500
// planResultService.execute2("EFDD34E4B5BC434BAEAE6A84DFCD4E7B");//20
// planResultService.execute2("00E0C5D3E4AD4F36B56C39395906618D");
......
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