Commit ee581dc1 authored by Tong Li's avatar Tong Li

遗传算法

parent 4d69e6bd
......@@ -91,6 +91,7 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
package com.aps.controller.gantt;
import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.basic.ScheduleChromosome;
import com.aps.entity.Gantt.ProductGanttVO;
import com.aps.entity.Gantt.ResourceGanttVO;
......@@ -102,6 +103,16 @@ public class ResourceGanttController {
.map(ScheduleChromosome::getSceneId)
.collect(Collectors.toList());
}
@GetMapping("/getScene")
@Operation(summary = "获取所有场景ID", description = "获取所有场景ID")
public List<Chromosome> getScene() {
// 调用 PlanResultService 获取 ScheduleChromosome 列表
List<Chromosome> scheduleChromosomes = planResultService.execute1();
// 提取所有场景ID
return scheduleChromosomes;
}
/**
* 将 ScheduleChromosome 转换为 ResourceGanttVO 列表
......
package com.aps.entity.Algorithm;
import com.aps.entity.Algorithm.ScheduleResult;
import com.aps.entity.basic.Machine;
import lombok.Data;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* 作者:佟礼
......@@ -12,16 +13,41 @@ import java.util.List;
*/
@Data
public class Chromosome {
private String ID = UUID.randomUUID().toString();
/// <summary>
/// 机器选择部分(可选机器集中的顺序号)
/// </summary>
private List<Integer> MachineSelection;
/// <summary>
/// 工序排序部分(工件/订单ID)
/// </summary>
private String MachineStr;
public String getMachineStr() {
return MachineSelection.stream()
.map(String::valueOf) // 将每个 Integer 转换为 String
.collect(Collectors.joining(","));
};
/// <summary>
/// 工序排序部分(工件/订单ID)
/// </summary>
private List<Integer> OperationSequencing;
/// <summary>
/// 工序排序部分(工件/订单ID)
/// </summary>
private String OperationStr;
public String getOperationStr() {
return OperationSequencing.stream()
.map(String::valueOf) // 将每个 Integer 转换为 String
.collect(Collectors.joining("|"));
};
/// <summary>
/// 适应度值
/// </summary>
......@@ -35,7 +61,7 @@ public class Chromosome {
/// <summary>
/// 解码后的调度结果
/// </summary>
private List<ScheduleResult> Result;
private List<GAScheduleResult> Result;
/// <summary>
/// 最早完工时间
......
......@@ -9,7 +9,7 @@ import java.util.List;
* 时间:2025-11-21
*/
@Data
public class ScheduleResult {
public class GAScheduleResult {
private int GroupId;
private int ProductId;
private int OperationId;
......@@ -21,4 +21,9 @@ public class ScheduleResult {
private int OneTime; // 单件工时
private int ProcessingTime; // 绝对处理时间(分钟)
private int ChangeoverTime;
public int getFlowTime() {
return EndTime - StartTime;
};
}
package com.aps.entity.Algorithm;
import lombok.Data;
/**
* 作者:佟礼
* 时间:2025-11-25
*/
@Data
public class OpMachine {
/**
* 所属组ID
*/
private int GroupId;
/**
* 订单内工序顺序号
*/
private int Sequence;
/**
* 设备ID
*/
private int machineId;
/**
* 单件工时
*/
private int processingTime; // 加工时间
}
package com.aps.entity.Algorithm;
/**
* 作者:佟礼
* 时间:2025-11-24
*/
public class Pair<A, B> {
private final A first;
private final B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
public A getFirst() {
return first;
}
public B getSecond() {
return second;
}
}
......@@ -3,6 +3,7 @@ package com.aps.entity.Algorithm;
import com.aps.entity.basic.Entry;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
/**
......@@ -11,12 +12,9 @@ import java.util.Date;
*/
@Data
public class ScheduleParams {
private Date BaseTime = new Date(); // 当前基准时间
private LocalDateTime BaseTime ; // 当前基准时间
/// <summary>
/// 设备总数
/// </summary>
private int MachineCount; // 设备总数
/// <summary>
/// 交叉概率
......
......@@ -8,7 +8,7 @@ import lombok.Data;
@Data
public class MachineOption {
private int machineId;
private int processingTime; // 加工时间
private int processingTime; // 加工时间 (秒)
private int setupTime; // 换型时间(如果与前一个产品不同)
private int teardownTime; // 收尾时间(后处理时间)
private int contantTime; // 常数时间
......
......@@ -13,6 +13,8 @@ public class Order {
private int id;
private int productId;
private int quantity = 100; // 100个
private int sYQuantity;
private OffsetDateTime dueDate;
private LocalDateTime orderCompletion;
private double tardiness;
......
package com.aps.service.Algorithm;
import com.aps.entity.Algorithm.Chromosome;
/**
* 作者:佟礼
* 时间:2025-11-24
*/
public class FitnessCalculator {
/**
* 多目标适应度计算(加权求和)
*/
public double calculateFitness(Chromosome chromosome) {
// 权重可根据实际需求调整
double w1 = 0.3; // 最早完工时间(越小越好,归一化后取反)
double w2 = 0.2; // 总流程时间(越小越好)
double w3 = 0.15; // 总换型时间(越小越好)
double w4 = 0.2; // 机器负载均衡(标准差越小越好)
double w5 = 0.15; // 交付期延迟(越小越好)
// 归一化(假设最大可能值,实际应根据问题规模调整)
double normMakespan =1/ (1 +(double) chromosome.getMakespan());
double normFlowTime = 1/ (1 + (double) chromosome.getTotalFlowTime() );
double normChangeover = 1/ (1 + (double) chromosome.getTotalChangeoverTime());
double normLoadStd = chromosome.getMachineLoadStd();
double normDelay = 1/ (1 + (double) chromosome.getDelayTime() );
// 适应度值(越大越好)
return w1 * normMakespan + w2 * normFlowTime + w3 * normChangeover + w4 * normLoadStd + w5 * normDelay;
}
}
package com.aps.service.Algorithm;
import com.aps.common.util.DeepCopyUtil;
import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GlobalOperationInfo;
import com.aps.entity.Algorithm.Pair;
import com.aps.entity.Algorithm.ScheduleParams;
import com.aps.entity.basic.Entry;
import com.aps.entity.basic.Machine;
import com.aps.entity.basic.Material;
import com.aps.entity.basic.Order;
import com.aps.service.plan.MachineSchedulerService;
import java.util.*;
import java.util.stream.Collectors;
/**
* 作者:佟礼
* 时间:2025-11-21
*/
public class GeneticAlgorithm {
public void Run() {
System.out.println("");
private final Random rnd = new Random();
private final List<Machine> machines;
private final MachineSchedulerService machineScheduler;
private final List<Order> orders;
private final List<Material> materials;
public GeneticAlgorithm(List<Machine> machines, List<Order> orders,
List<Material> materials, MachineSchedulerService machineScheduler) {
this.machines = machines;
this.orders = orders;
this.materials = materials;
this.machineScheduler = machineScheduler;
}
public List<Chromosome> Run(ScheduleParams param, List<Entry> allOperations) {
System.out.println("开始");
Initialization initialization = new Initialization(allOperations);
GeneticOperations geneticOps = new GeneticOperations(allOperations);
// 预生成全局工序列表(所有初始化方法共享同一顺序)
List<GlobalOperationInfo> globalOpList = initialization.generateGlobalOpList();
// 步骤1:初始化种群
List<Chromosome> population = initialization.generateInitialPopulation(param, globalOpList);
Chromosomedecode(param,allOperations,globalOpList,population);
int ordercount = globalOpList.stream()
.mapToInt(GlobalOperationInfo::getGroupId)
.max()
.orElse(0);
Chromosome best=new Chromosome();
double bestFitness=0;
int Iteration=0;
// 步骤2:迭代进化
for (int iter = 0; iter < param.getMaxIterations(); iter++) {
// 解码并计算适应度
// 检查终止条件(此处简化为迭代次数)
if (iter == param.getMaxIterations() - 1) {
break;
}
// 选择操作
List<Chromosome> selected = geneticOps.tournamentSelection(population);
// 交叉操作
List<Chromosome> nextPopulation = new ArrayList<>();
for (int i = 0; i < selected.size(); i += 2) {
if (i + 1 >= selected.size()) {
nextPopulation.add(selected.get(i));
break;
}
Chromosome parent1 = selected.get(i);
Chromosome parent2 = selected.get(i + 1);
if (rnd.nextDouble() < param.getCrossoverProb()) {
// 假设PoxCrossover返回包含两个子染色体的数组
Pair<Chromosome, Chromosome> children = geneticOps.poxCrossover(parent1, parent2, ordercount);
nextPopulation.add(children.getFirst());
nextPopulation.add(children.getSecond());
} else {
nextPopulation.add(parent1);
nextPopulation.add(parent2);
}
}
// 变异操作
for (Chromosome chromosome : nextPopulation) {
if (rnd.nextDouble() < param.getMutationProb()) {
geneticOps.mutate(chromosome, globalOpList);
}
}
// 精英保留
List<Chromosome> population1 = population.stream()
.sorted((c1, c2) -> Double.compare(c2.getFitness(), c1.getFitness()))
.collect(Collectors.toList()); // 降序排序
List<Chromosome> elites = population1.subList(0, param.getElitismCount());
List<Chromosome> newPopulation = new ArrayList<>();
newPopulation.addAll(elites);
newPopulation.addAll(nextPopulation);
// 更新种群
population = newPopulation.stream()
.limit(param.getPopulationSize() )
.collect(Collectors.toList());
Chromosomedecode(param,allOperations,globalOpList,population);
best= population.stream()
.max(Comparator.comparingDouble(Chromosome::getFitness))
.orElse(null);
if(bestFitness<best.getFitness())
{
bestFitness=best.getFitness();
}else {
Iteration++;
}
if(Iteration>20)
{
break;
}
}
// 步骤3:返回最优解
return population.stream()
.sorted((c1, c2) -> Double.compare(c2.getFitness(), c1.getFitness()))
.limit(10)
.collect(Collectors.toList());
}
private void Chromosomedecode(ScheduleParams param, List<Entry> allOperations,List<GlobalOperationInfo> globalOpList,List<Chromosome> population)
{
GeneticDecoder decoder = new GeneticDecoder(param.getBaseTime(), machines, orders, materials, machineScheduler);
FitnessCalculator fitnessCalc = new FitnessCalculator();
population.parallelStream().forEach(chromosome -> {
chromosome.setResult(new ArrayList<>());
// 假设Machine类有拷贝方法,或使用MapStruct等工具进行映射
chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(machines)); // 简单拷贝,实际可能需要深拷贝
decoder.decodeChromosomeWithCache(chromosome, globalOpList, allOperations);
chromosome.setFitness(fitnessCalc.calculateFitness(chromosome));
});
}
}
package com.aps.service.Algorithm;
import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.*;
import com.aps.entity.basic.*;
import com.aps.service.plan.MachineSchedulerService;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
import java.util.concurrent.locks.ReentrantLock;
/**
* 作者:佟礼
* 时间:2025-11-24
*/
public class GeneticDecoder {
private final Map<String, Chromosome> decodingCache = new HashMap<>();
// 缓存大小限制
private static final int MAX_CACHE_SIZE = 1000;
// 线程安全锁:避免多线程下缓存操作冲突(可选,若单线程可移除)
private final ReentrantLock cacheLock = new ReentrantLock();
private LocalDateTime baseTime;
private final List<Machine> machines;
private final MachineSchedulerService machineScheduler;
private final List<Order> orders;
private int orderMaxID;
private final List<Material> materials;
public GeneticDecoder(LocalDateTime baseTime, List<Machine> machines, List<Order> orders,
List<Material> materials, MachineSchedulerService machineScheduler) {
this.baseTime = baseTime;
this.machines = machines;
this.orders = orders;
this.orders.forEach(t -> t.setSYQuantity(t.getQuantity()));
this.materials = materials;
this.machineScheduler = machineScheduler;
this.orderMaxID = orders.stream().mapToInt(Order::getId).max().orElse(0);
}
public void decodeChromosomeWithCache(Chromosome chromosome, List<GlobalOperationInfo> globalOpList,
List<Entry> allOperations) {
// 1. 创建缓存键
String cacheKey = createCacheKey(chromosome);
// 2. 尝试从缓存获取(加锁保证线程安全)
cacheLock.lock();
try {
if (decodingCache.containsKey(cacheKey)) {
// 缓存命中:复用缓存结果
Chromosome cachedResult = decodingCache.get(cacheKey);
// 赋值给染色体
String ID=chromosome.getID();
chromosome= ProductionDeepCopyUtil.deepCopy(cachedResult);
chromosome.setID(ID);
return;
}
} finally {
cacheLock.unlock(); // 确保锁释放
}
// 3. 缓存未命中:执行解码逻辑
decode(chromosome, globalOpList,
allOperations);
// 4. 将解码结果存入缓存(加锁保证线程安全)
cacheLock.lock();
try {
// 存储缓存:封装为 CacheValue 对象
decodingCache.put(
cacheKey,
chromosome
);
// 5. 限制缓存大小(超过 1000 移除最早插入的键)
if (decodingCache.size() > MAX_CACHE_SIZE) {
Iterator<String> keyIterator = decodingCache.keySet().iterator();
if (keyIterator.hasNext()) {
keyIterator.next(); // 获取首个键
keyIterator.remove(); // 移除首个键(对应 C# _decodingCache.Keys.First())
}
}
} finally {
cacheLock.unlock();
}
}
/**
* 染色体解码为调度方案
*/
public void decode(Chromosome chromosome, List<GlobalOperationInfo> globalOpList,
List<Entry> allOperations) {
Map<Integer, GlobalOperationInfo> globalOpMap = globalOpList.stream()
.collect(Collectors.toMap(GlobalOperationInfo::getGlobalOpId, g -> g));
// 验证:MachineSelection长度必须与全局工序数一致
if (chromosome.getMachineSelection().size() != globalOpList.size()) {
throw new IllegalArgumentException(String.format(
"MachineSelection长度(%d)与全局工序数(%d)不匹配!",
chromosome.getMachineSelection().size(), globalOpList.size()));
}
// 步骤1:生成“工序→机器/加工时间”映射
List<OpMachine> opMachineMap = new ArrayList<>();
for (GlobalOperationInfo globalOp : globalOpList) {
int globalOpId = globalOp.getGlobalOpId();
Entry op = globalOp.getOp();
int groupId = globalOp.getGroupId();
int sequence = globalOp.getSequence();
// 从MachineSelection中获取当前工序的机器选择顺序号
int machineSeq = chromosome.getMachineSelection().get(globalOpId);
List<MachineOption> optionalMachines = op.getMachineOptions();
// 验证:机器顺序号必须在可选范围内
if (machineSeq < 1 || machineSeq > optionalMachines.size()) {
throw new IllegalStateException(String.format(
"全局工序%d(订单%d工序%d)的机器顺序号%d超出范围(1-%d)",
globalOpId, groupId, sequence, machineSeq, optionalMachines.size()));
}
// 获取选择的设备ID和加工时间
MachineOption selectedMachine = optionalMachines.get(machineSeq - 1);
OpMachine opMachine=new OpMachine();
opMachine.setGroupId(groupId);
opMachine.setSequence(sequence);
opMachine.setMachineId(selectedMachine.getMachineId());
opMachine.setProcessingTime(selectedMachine.getProcessingTime());
opMachineMap.add(opMachine);
}
// 步骤2:按OperationSequencing顺序调度工序
Map<Integer, Integer> orderProcessCounter = allOperations.stream()
.collect(Collectors.groupingBy(Entry::getGroupId, Collectors.collectingAndThen(
Collectors.counting(), Long::intValue)))
.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> 0));
Map<Integer, Integer> orderLastEndTime = allOperations.stream()
.collect(Collectors.groupingBy(Entry::getGroupId))
.keySet().stream()
.collect(Collectors.toMap(k -> k, k -> 0));
Map<Integer, String> machineState = chromosome.getMachines().stream()
.collect(Collectors.toMap(Machine::getId, m -> ""));
List<Entry> allScheduledOps = new ArrayList<>();
for (int groupId : chromosome.getOperationSequencing()) {
int scheduledCount = orderProcessCounter.get(groupId);
List<Entry> orderOps = allOperations.stream()
.filter(t -> t.getGroupId() == groupId)
.collect(Collectors.toList());
if (scheduledCount >= orderOps.size()) {
throw new IllegalStateException(String.format(
"订单%d的工序已全部调度(共%d道),无需重复处理!",
groupId, orderOps.size()));
}
Entry currentOp = orderOps.get(scheduledCount);
int opSequence = currentOp.getSequence();
// 从映射表中获取机器和加工时间
OpMachine machineInfo=opMachineMap.stream()
.filter(m -> m.getGroupId() == groupId&&m.getSequence()==opSequence)
.findFirst()
.orElse(null);
int machineId = machineInfo.getMachineId();
int processTime = machineInfo.getProcessingTime();
Machine targetMachine = chromosome.getMachines().stream()
.filter(m -> m.getId() == machineId)
.findFirst()
.orElse(null);
int prevtime = 0;
if (!currentOp.getPrevEntryIds().isEmpty()) {
// 处理多个前工序
for (int opid : currentOp.getPrevEntryIds()) {
List<GAScheduleResult> prevOperations = chromosome.getResult().stream()
.filter(t -> t.getGroupId() == groupId && t.getOperationId() == opid)
.collect(Collectors.toList());
for (GAScheduleResult prevOp : prevOperations) {
prevtime = Math.max(prevtime, prevOp.getEndTime());
}
}
}
// 上个离散参数
String lastDiscreteParameter = machineState.get(machineId);
int bomtime = 0;
prevtime = Math.max(prevtime, bomtime);
Machine machine = chromosome.getMachines().stream()
.filter(m -> m.getId() == machineId)
.findFirst()
.orElse(null);
int changeoverTime =0; //(lastDiscreteParameter.isEmpty() ||
// lastDiscreteParameter.equals(currentOp.getDiscreteParameter())) ? 0 : 0;
int actualEndTime = processWithSingleMachine(currentOp, machine, processTime, prevtime, chromosome);
orderProcessCounter.put(groupId, orderProcessCounter.get(groupId) + 1);
orderLastEndTime.put(groupId, actualEndTime);
machineState.put(machineId, currentOp.getDiscreteParameter());
}
// 步骤4:计算调度指标
calculateScheduleResult(chromosome);
}
private int processWithSingleMachine(Entry operation, Machine machine, int processingTime,
int prevtime, Chromosome chromosome) {
int processingTimeTotal = processingTime * operation.getQuantity();
MachineCalculator machineCalculator=new MachineCalculator(baseTime,machines,machineScheduler);
List<ScheduleResultDetail> geneDetails = machineCalculator.getNextAvailableTime(machine, prevtime, -1,
processingTimeTotal, chromosome.getResult(), false, true, true);
int startTime = geneDetails.stream()
.mapToInt(ScheduleResultDetail::getStartTime)
.min()
.orElse(0);
int endTime = geneDetails.stream()
.mapToInt(ScheduleResultDetail::getEndTime)
.max()
.orElse(0);
GAScheduleResult result=new GAScheduleResult();
result.setGroupId(operation.getGroupId());
result.setOperationId(operation.getId());
result.setMachineId(machine.getId());
result.setQuantity(operation.getQuantity());
result.setStartTime(startTime);
result.setEndTime(endTime);
result.setOneTime(processingTime);
result.setProcessingTime(processingTimeTotal);
result.setGeneDetails(geneDetails);
chromosome.getResult().add(result);
return endTime;
}
private void calculateScheduleResult(Chromosome chromosome) {
// 1. 最早完工时间(最小化)
double makespan = chromosome.getResult().stream()
.mapToInt(GAScheduleResult::getEndTime)
.max()
.orElse(0);
// 2. 交付期满足情况(最小化延迟)
double tardiness = 0;
Map<Integer, List<GAScheduleResult>> orderGroups = chromosome.getResult().stream()
.collect(Collectors.groupingBy(GAScheduleResult::getGroupId));
for (Map.Entry<Integer, List<GAScheduleResult>> group : orderGroups.entrySet()) {
int groupId = group.getKey();
int orderCompletion = group.getValue().stream()
.mapToInt(GAScheduleResult::getEndTime)
.max()
.orElse(0);
Order order = orders.stream()
.filter(t -> t.getId() == groupId)
.findFirst()
.orElse(null);
LocalDateTime completionTime =baseTime.plusMinutes(orderCompletion);
// 修复:正确处理OffsetDateTime到LocalDateTime的转换
LocalDateTime dueDateTime = order.getDueDate().toLocalDateTime();
if (completionTime.isAfter(dueDateTime)) {
// 计算延迟小时数(修复时间计算)
long hours = ChronoUnit.HOURS.between(dueDateTime, completionTime);
long minutes = ChronoUnit.MINUTES.between(dueDateTime, completionTime) % 60;
tardiness += hours*60 + (double) minutes ;
}
}
// 3. 最小总换型时间
double totalSetupTime = calculateTotalSetupTime(chromosome);
// 4. 最小化总流程时间
double totalFlowTime = calculateTotalFlowTime(chromosome);
// 5. 机器负载均衡
double machineLoadBalance = calculateMachineLoadBalance(chromosome);
// 存储各目标值
chromosome.setMakespan(makespan);
chromosome.setTotalFlowTime(totalFlowTime);
chromosome.setTotalChangeoverTime(totalSetupTime);
chromosome.setMachineLoadStd(machineLoadBalance);
chromosome.setDelayTime(tardiness);
// 加权求和作为适应度(权重可根据需求调整)
// chromosome.setFitness(0.4 * (1.0 / (1 + makespan)) +
// 0.3 * (1.0 / (1 + tardiness)) +
// 0.1 * (1.0 / (1 + totalSetupTime)) +
// 0.1 * (1.0 / (1 + totalFlowTime)) +
// 0.1 * machineLoadBalance);
}
private double calculateTotalFlowTime(Chromosome chromosome) {
return chromosome.getResult().stream()
.mapToDouble(GAScheduleResult::getFlowTime)
.sum();
}
// 计算总换型时间
private double calculateTotalSetupTime(Chromosome chromosome) {
return chromosome.getResult().stream()
.mapToDouble(GAScheduleResult::getChangeoverTime)
.sum();
}
// 计算机器负载均衡指标
private double calculateMachineLoadBalance(Chromosome chromosome) {
Map<Integer, Double> machineUtilization = new HashMap<>();
int maxEndTime = chromosome.getResult().stream()
.mapToInt(GAScheduleResult::getEndTime)
.max()
.orElse(0);
if (maxEndTime == 0) return 0;
for (Machine machine : chromosome.getMachines()) {
List<GAScheduleResult> machineGenes = chromosome.getResult().stream()
.filter(g -> g.getMachineId() == machine.getId())
.collect(Collectors.toList());
double busyTime = machineGenes.stream()
.mapToInt(g -> g.getEndTime() - g.getStartTime())
.sum();
machineUtilization.put(machine.getId(), busyTime / maxEndTime);
}
double avgUtilization = machineUtilization.values().stream()
.mapToDouble(Double::doubleValue)
.average()
.orElse(0);
double variance = machineUtilization.values().stream()
.mapToDouble(u -> Math.pow(u - avgUtilization, 2))
.sum() / chromosome.getMachines().size();
// 方差越小,负载越均衡
return 1.0 / (1 + variance);
}
/**
* 补全:创建缓存键(核心逻辑,需与原 C# CreateCacheKey 一致)
* 思路:将染色体的核心特征(机器选择+工序排序)拼接为字符串,确保相同染色体生成相同键
*/
private String createCacheKey(Chromosome chromosome) {
// 拼接机器选择:用 "," 分隔(例:1,3,2,4)
String machineStr = chromosome.getMachineStr();
// 拼接工序排序:用 "|" 分隔(例:2|1|3|4)
String operationStr = chromosome.getOperationStr();
// 组合最终键(用 "_" 分隔两部分,避免冲突)
return machineStr + "_" + operationStr;
}
}
package com.aps.service.Algorithm;
import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GlobalOperationInfo;
import com.aps.entity.Algorithm.OperationSequencingWeight;
import com.aps.entity.Algorithm.Pair;
import com.aps.entity.basic.Entry;
import com.aps.entity.basic.GlobalParam;
import com.aps.entity.basic.MachineOption;
import java.util.*;
import java.util.stream.Collectors;
/**
* 作者:佟礼
* 时间:2025-11-24
*/
public class GeneticOperations {
private final Random rnd = new Random();
private static List<Entry> allOperations;
public GeneticOperations(List<Entry> allOperations) {
GeneticOperations.allOperations = allOperations;
}
/**
* 锦标赛选择
*/
public List<Chromosome> tournamentSelection(List<Chromosome> population, int tournamentSize) {
List<Chromosome> selected = new ArrayList<>();
while (selected.size() < population.size()) {
// 随机选择k个个体
// List<Chromosome> candidates = population.stream()
// .sorted((a, b) -> rnd.nextInt())
// .limit(tournamentSize)
// .collect(Collectors.toList());
// 步骤1: 创建一个临时列表用于存放候选者
List<Chromosome> candidates = new ArrayList<>(population);
// 步骤2: 随机打乱候选者列表
Collections.shuffle(candidates, rnd);
// 步骤3: 截取前 tournamentSize 个元素作为本次锦标赛的参与者
if (tournamentSize < candidates.size()) {
candidates = candidates.subList(0, tournamentSize);
}
// 步骤4: 从参与者中选择适应度最高的个体
Chromosome best = candidates.stream()
.max(Comparator.comparingDouble(Chromosome::getFitness))
.orElseThrow(() -> new IllegalStateException("候选列表为空,无法选择最佳个体。"));
// 复制个体(假设Chromosome有复制方法或通过构造函数复制)
selected.add(ProductionDeepCopyUtil.deepCopy(best));
}
return selected;
}
// 重载,使用默认锦标赛大小3
public List<Chromosome> tournamentSelection(List<Chromosome> population) {
return tournamentSelection(population, 3);
}
/**
* 改进POX交叉(工序排序部分)
*/
public Pair<Chromosome, Chromosome> poxCrossover(Chromosome parent1, Chromosome parent2, int orderCount) {
// 步骤1:随机划分工件集
List<Integer> jobset1 = new ArrayList<>();
for (int i = 0; i < orderCount; i++) {
jobset1.add(i);
}
Collections.shuffle(jobset1, rnd);
jobset1 = jobset1.subList(0, orderCount / 2);
Set<Integer> jobset1Set = new HashSet<>(jobset1);
List<Integer> jobset2 = new ArrayList<>();
for (int i = 0; i < orderCount; i++) {
if (!jobset1Set.contains(i)) {
jobset2.add(i);
}
}
// 步骤2:生成子代1
List<Integer> child1Os = new ArrayList<>();
for (int i = 0; i < parent1.getOperationSequencing().size(); i++) {
int op1 = parent1.getOperationSequencing().get(i);
int op2 = parent2.getOperationSequencing().get(i);
if (jobset1Set.contains(op1) || jobset1Set.contains(op2)) {
child1Os.add(op1);
} else {
child1Os.add(op2);
}
}
// 步骤3:生成子代2
List<Integer> child2Os = new ArrayList<>();
for (int i = 0; i < parent2.getOperationSequencing().size(); i++) {
int op1 = parent1.getOperationSequencing().get(i);
int op2 = parent2.getOperationSequencing().get(i);
if (jobset1Set.contains(op2) || jobset1Set.contains(op1)) {
child2Os.add(op2);
} else {
child2Os.add(op1);
}
}
// 机器选择部分交叉
List<Integer> child1Ms = new ArrayList<>();
List<Integer> child2Ms = new ArrayList<>();
for (int i = 0; i < parent1.getMachineSelection().size(); i++) {
int m1 = parent1.getMachineSelection().get(i);
int m2 = parent2.getMachineSelection().get(i);
if (rnd.nextDouble() < 0.5) {
child1Ms.add(m1);
child2Ms.add(m2);
} else {
child1Ms.add(m2);
child2Ms.add(m1);
}
}
Chromosome child1 = new Chromosome();
child1.setMachineSelection(child1Ms);
child1.setOperationSequencing(child1Os);
Chromosome child2 = new Chromosome();
child2.setMachineSelection(child2Ms);
child2.setOperationSequencing(child2Os);
return new Pair<>(child1, child2);
}
/**
* 变异操作
*/
public void mutate(Chromosome chromosome, List<GlobalOperationInfo> globalOpList, double baseMutationProb) {
// 1. 机器选择部分变异
mutateMachineSelection(chromosome, globalOpList, baseMutationProb);
// 2. 工序排序部分变异
mutateOperationSequencing(chromosome);
}
// 重载,使用默认变异概率
public void mutate(Chromosome chromosome, List<GlobalOperationInfo> globalOpList) {
mutate(chromosome, globalOpList, 0.1);
}
private void mutateMachineSelection(Chromosome chromosome, List<GlobalOperationInfo> globalOpList, double baseMutationProb) {
// 计算变异位置数量
int r = Math.max(1, (int) (chromosome.getMachineSelection().size() * baseMutationProb));
int i = 0;
while (i < r) {
int pos = rnd.nextInt(chromosome.getMachineSelection().size());
// 获取对应工序
GlobalOperationInfo opInfo = globalOpList.get(pos);
Entry operation = opInfo.getOp();
int machineSeq = chromosome.getMachineSelection().get(pos);
if (operation.getMachineOptions().size() == 1) {
continue;
} else {
// 选择当前所选设备外最短加工时间的机器
List<MachineOption> optionalMachines = operation.getMachineOptions();
// 找到当前选中的机器
MachineOption currentMachine = optionalMachines.get(machineSeq - 1);
// 筛选其他机器并找到加工时间最短的
MachineOption minLoadMachine = optionalMachines.stream()
.filter(m -> m.getMachineId() != currentMachine.getMachineId())
.min(Comparator.comparingInt(m -> m.getProcessingTime()))
.orElse(currentMachine); // 如果没有其他机器,保持当前
machineSeq = optionalMachines.indexOf(minLoadMachine) + 1;
chromosome.getMachineSelection().set(pos, machineSeq);
i++;
}
}
}
/**
* 低优先级优先的工序排序变异
*/
private void mutateOperationSequencing(Chromosome chromosome) {
Random rnd = new Random();
// 选择变异方式(交换或反转)
if (rnd.nextDouble() < 0.5) {
// 交换:优先选择高优先级工序的索引
OperationSequencingWeight os1 = selectHighPriorityIndex(chromosome.getOperationSequencing(), 0);
int idx1 = os1.getIndex();
OperationSequencingWeight os2 = selectHighPriorityIndex(chromosome.getOperationSequencing(), os1.getWeight());
int idx2 = os2.getIndex();
// 交换位置
List<Integer> os = chromosome.getOperationSequencing();
Collections.swap(os, idx1, idx2);
} else {
// 反转:仅对高优先级工序集中的子序列反转
OperationSequencingWeight osStart = selectHighPriorityIndex(chromosome.getOperationSequencing(), 0);
int start = osStart.getIndex();
OperationSequencingWeight osEnd = selectHighPriorityIndex(chromosome.getOperationSequencing(), osStart.getWeight());
int end = osEnd.getIndex();
if (start > end) {
int temp = start;
start = end;
end = temp;
}
// 执行反转
List<Integer> os = chromosome.getOperationSequencing();
for (int i = 0; i < (end - start + 1) / 2; i++) {
int pos1 = start + i;
int pos2 = end - i;
Collections.swap(os, pos1, pos2);
}
}
}
/**
* 优先选择高优先级工序的索引(用于变异)
*/
private OperationSequencingWeight selectHighPriorityIndex(List<Integer> os, int weight) {
Random rnd = new Random();
List<OperationSequencingWeight> indexWeights = CommonCalculator.getOsw(os, allOperations);
if (!GlobalParam.IsBreakPriority) {
if (weight == 0) {
return indexWeights.get(rnd.nextInt(indexWeights.size()));
} else {
List<OperationSequencingWeight> filtered = indexWeights.stream()
.filter(t -> t.getWeight() == weight)
.collect(Collectors.toList());
return filtered.get(rnd.nextInt(filtered.size()));
}
}
return indexWeights.get(CommonCalculator.getOsIndex(indexWeights));
}
}
......@@ -43,33 +43,42 @@ public class Initialization {
*/
public List<Chromosome> generateInitialPopulation(ScheduleParams param, List<GlobalOperationInfo> globalOpList) {
List<Chromosome> population = new ArrayList<>();
// 按比例生成不同类型个体(GS:40%, LS:40%, RS:20%)
int gsCount = (int) (param.getPopulationSize() * param.getGsRatio());
int lsCount = (int) (param.getPopulationSize() * param.getLsRatio());
int lsCount =gsCount+ (int) (param.getPopulationSize() * param.getLsRatio());
int rsCount = param.getPopulationSize() - gsCount - lsCount;
int populationSize=param.getPopulationSize();
// 并行循环:对应 Parallel.For(0, PopulationSize, i => { ... })
IntStream.range(0, populationSize)
.parallel() // 开启并行
.forEach(i -> {
Chromosome chromo = new Chromosome(); // 初始化染色体
// 全局选择(GS):按GlobalOpId顺序生成MachineSelection
if (i < gsCount) {
generateGSChromosome(chromo,globalOpList); // 对应 C# GenerateGSChromosome
} else if (i < lsCount) {
// 局部选择(LS):按GlobalOpId顺序生成MachineSelection(仅负载计算范围不同)
generateLSChromosome(chromo,globalOpList);
} else {
// 随机选择(RS):按GlobalOpId顺序生成MachineSelection(仅机器选择随机)
generateRSChromosome(chromo,globalOpList);
}
population.add(chromo); // 赋值到数组,线程安全(数组索引唯一)
});
// 全局选择(GS):按GlobalOpId顺序生成MachineSelection
for (int i = 0; i < gsCount; i++) {
population.add(generateGSChromosome(globalOpList, param.getMachineCount()));
}
// 局部选择(LS):按GlobalOpId顺序生成MachineSelection(仅负载计算范围不同)
for (int i = 0; i < lsCount; i++) {
population.add(generateLSChromosome(globalOpList, param.getMachineCount()));
}
// 随机选择(RS):按GlobalOpId顺序生成MachineSelection(仅机器选择随机)
for (int i = 0; i < rsCount; i++) {
population.add(generateRSChromosome(globalOpList, param.getMachineCount()));
}
return population;
}
/**
* 全局选择(GS)生成染色体(按GlobalOpId顺序生成MachineSelection)
*/
private Chromosome generateGSChromosome(List<GlobalOperationInfo> globalOpList, int machineCount) {
Chromosome chromosome = new Chromosome();
private Chromosome generateGSChromosome(Chromosome chromosome,List<GlobalOperationInfo> globalOpList) {
Map<Integer, Integer> machineLoad = new HashMap<>();
// int[] machineLoad = new int[machineCount + 1]; // 设备负载(1-based,设备ID从1开始)
List<Integer> ms = new ArrayList<>(); // MachineSelection(顺序=GlobalOpId顺序)
......@@ -106,7 +115,7 @@ public class Initialization {
.findFirst();
// 计算该机器在“可选机器列表”中的顺序号(1-based)
int machineSeq =index.orElse(0) ;//findIndex(optionalMachines, minLoadMachine.getMachineId()) + 1;
int machineSeq =index.orElse(0)+1 ;//findIndex(optionalMachines, minLoadMachine.getMachineId()) + 1;
ms.add(machineSeq);
// 更新设备负载
......@@ -132,8 +141,9 @@ public class Initialization {
/**
* 局部选择(LS)生成染色体(按GlobalOpId顺序,每个订单重新初始化负载)
*/
private Chromosome generateLSChromosome(List<GlobalOperationInfo> globalOpList, int machineCount) {
Chromosome chromosome = new Chromosome();
private Chromosome generateLSChromosome(Chromosome chromosome,List<GlobalOperationInfo> globalOpList) {
List<Integer> ms = new ArrayList<>();
List<Integer> os = new ArrayList<>();
Random rnd = new Random();
......@@ -199,8 +209,9 @@ public class Initialization {
/**
* 随机选择(RS)生成染色体(按GlobalOpId顺序,随机选择机器)
*/
private Chromosome generateRSChromosome(List<GlobalOperationInfo> globalOpList, int machineCount) {
Chromosome chromosome = new Chromosome();
private Chromosome generateRSChromosome(Chromosome chromosome,List<GlobalOperationInfo> globalOpList) {
List<Integer> ms = new ArrayList<>();
List<Integer> os = new ArrayList<>();
Random rnd = new Random();
......
package com.aps.service.Algorithm;
import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.GAScheduleResult;
import com.aps.entity.Algorithm.ScheduleResultDetail;
import com.aps.entity.basic.*;
import com.aps.service.plan.MachineSchedulerService;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
/**
* 作者:佟礼
* 时间:2025-11-24
*/
public class MachineCalculator {
private LocalDateTime baseTime;
private final List<Machine> machines;
private final MachineSchedulerService machineScheduler;
public MachineCalculator(LocalDateTime baseTime, List<Machine> machines, MachineSchedulerService machineScheduler) {
this.baseTime = baseTime;
this.machines = machines;
this.machineScheduler = machineScheduler;
}
/**
* 获取机器下一个可用时间窗口(考虑班次约束)
*/
public List<ScheduleResultDetail> getNextAvailableTime(Machine machine, int proposedStartTime,
int prevtime, double processingTime,
List<GAScheduleResult> existingTasks,
boolean isInterrupt, boolean istask,
boolean islockMachineTime) {
LocalDateTime startTime = baseTime.plus(proposedStartTime, ChronoUnit.MINUTES);
String prevtimestr = "";
if (prevtime > -1) {
prevtimestr = baseTime.plus(prevtime, ChronoUnit.MINUTES)
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
// 查找合适的班次窗口
return findEarliestStart(machine, processingTime, startTime, prevtimestr,
existingTasks, istask, islockMachineTime);
}
// 查找最早可用开始时间
private List<ScheduleResultDetail> findEarliestStart(Machine machine, double processingTime,
LocalDateTime currentTime, String prevtime,
List<GAScheduleResult> existingTasks,
boolean checkprevtime, boolean islockMachineTime) {
// 获取设备上已有任务
List<GAScheduleResult> machineTasks = existingTasks.stream()
.filter(t -> t.getMachineId() == machine.getId())
.sorted(Comparator.comparingInt(GAScheduleResult::getStartTime))
.collect(Collectors.toList());
List<ScheduleResultDetail> times = new ArrayList<>();
TimeSegment slot =GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime);
LocalDateTime startCandidate = slot.getStart().isAfter(
(prevtime.isEmpty() ? currentTime : LocalDateTime.parse(prevtime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
) ? slot.getStart() : (prevtime.isEmpty() ? currentTime : LocalDateTime.parse(prevtime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
LocalDateTime endCandidate = startCandidate.plus((long)processingTime, ChronoUnit.MINUTES);
// 检查是否在可用时间段内
if (endCandidate.isAfter(slot.getEnd())) {
// 放不下,继续寻找后续可用时间
return CaldEarliestStart(machine, processingTime, currentTime, prevtime,
machineTasks, checkprevtime, islockMachineTime);
} else {
ScheduleResultDetail time = new ScheduleResultDetail();
time.setKey(slot.getKey());
time.setStartTime((int) ChronoUnit.MINUTES.between(baseTime, startCandidate));
time.setEndTime((int) ChronoUnit.MINUTES.between(baseTime, endCandidate));
times.add(time);
if (islockMachineTime) {
RemoveMachineAvailable(machine, time);
}
return times;
}
}
private List<ScheduleResultDetail> CaldEarliestStart(
Machine machine, double processingTime, LocalDateTime currentTime,
String prevtime, List<GAScheduleResult> machineTasks, boolean checkprevtime, boolean islockMachineTime
) {
double remainingTime = processingTime;
LocalDateTime st = StringUtils.isEmpty(prevtime) ? currentTime : LocalDateTime.parse(prevtime);
LocalDateTime prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
List<ScheduleResultDetail> times = new ArrayList<>();
List<ScheduleResultDetail> oldTimes = new ArrayList<>();
while (remainingTime > 0) {
TimeSegment shift = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime);
if (shift == null) break;
LocalDateTime shiftStart = shift.getStart();
LocalDateTime shiftEnd = shift.getEnd();
// 检查班次间冲突
if (!prevEnd.isEqual(LocalDateTime.of(2000, 1, 1, 0, 0, 0))) {
if (prevEnd.isBefore(currentTime)) {
// 检查班次间任务
LocalDateTime finalPrevEnd = prevEnd;
boolean hasTask = machineTasks.stream()
.anyMatch(t -> {
LocalDateTime taskStart = baseTime.plusMinutes(t.getStartTime());
return taskStart.isAfter(finalPrevEnd) && taskStart.isBefore(shiftStart);
});
if (hasTask) {
// 重置状态
currentTime = shiftStart;
st = shiftStart;
remainingTime = processingTime;
prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
oldTimes.addAll(ProductionDeepCopyUtil.deepCopyList(times));
times.clear();
continue;
}
// 检查班次间维修窗口
if (machine.getMaintenanceWindows() != null) {
LocalDateTime finalPrevEnd1 = prevEnd;
boolean hasMaintenance = machine.getMaintenanceWindows().stream()
.anyMatch(w -> w.getStartTime().isAfter(finalPrevEnd1) && w.getStartTime().isBefore(shiftStart));
if (hasMaintenance) {
currentTime = shiftStart;
st = shiftStart;
remainingTime = processingTime;
prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
times.clear();
continue;
}
}
}
}
prevEnd = shiftEnd;
// 计算有效时间
LocalDateTime effectiveStart = st.isAfter(shiftStart) ? st : shiftStart;
long availableMinutes = ChronoUnit.MINUTES.between(effectiveStart, shiftEnd);
// 处理当前班次
double processable = Math.min(remainingTime, (int) availableMinutes);
remainingTime -= processable;
currentTime = effectiveStart.plusMinutes((long)processable);
// 添加时间详情
ScheduleResultDetail time = new ScheduleResultDetail();
time.setKey(shift.getKey());
time.setStartTime((int) ChronoUnit.MINUTES.between(baseTime, effectiveStart));
time.setEndTime((int) ChronoUnit.MINUTES.between(baseTime, currentTime));
times.add(time);
if (islockMachineTime) {
RemoveMachineAvailable(machine, time);
}
}
if (islockMachineTime) {
// 还原未使用的时间段
AddMachineAvailable(machine, oldTimes);
}
return times;
}
/**
* 获取设备当前或下一个有效班次
*/
private TimeSegment GetCurrentOrNextShift(Machine machine, LocalDateTime time, String prevtime, boolean checkprevtime) {
TimeSegment start = null;
// 查找有效班次
start = machine.getAvailability().stream()
.filter(slot -> !slot.isUsed() && slot.getType() != SegmentType.MAINTENANCE)
.filter(slot -> slot.getStart().isAfter(time) || slot.getEnd().isAfter(time))
.findFirst()
.orElse(null);
if (start == null) {
// 生成新时间段
List<TimeSegment> timeSegments = machineScheduler.generateTimeSegment(machine, time);
machine.getAvailability().addAll(timeSegments);
// 更新设备时间线
Machine originalMachine = machines.stream()
.filter(t -> t.getId() == machine.getId())
.findFirst()
.orElse(null);
if (originalMachine != null) {
MachineTimeline timeline = machineScheduler.getOrCreateTimeline(originalMachine);
}
// 递归查找
return GetCurrentOrNextShift(machine, time, prevtime, checkprevtime);
}
return start;
}
private void RemoveMachineAvailable(Machine machine, ScheduleResultDetail geneDetails) {
List<TimeSegment> timeSegments = new ArrayList<>();
int index = machine.getAvailability().stream()
.filter(t -> t.getKey().equals(geneDetails.getKey()))
.findFirst()
.map(machine.getAvailability()::indexOf)
.orElse(-1);
if (index > -1) {
TimeSegment targetSegment = machine.getAvailability().get(index);
LocalDateTime geneEndTime = baseTime.plusMinutes(geneDetails.getEndTime());
if (targetSegment.getEnd().isAfter(geneEndTime)) {
TimeSegment usedSegment = new TimeSegment();
usedSegment.setStart(baseTime.plusMinutes(geneDetails.getStartTime()));
usedSegment.setEnd(geneEndTime);
usedSegment.setHoliday(false);
usedSegment.setKey(UUID.randomUUID().toString());
usedSegment.setType(SegmentType.REGULAR);
usedSegment.setUsed(true);
timeSegments.add(usedSegment);
geneDetails.setKey(usedSegment.getKey());
targetSegment.setStart(geneEndTime);
} else {
targetSegment.setUsed(true);
}
}
if (!timeSegments.isEmpty()) {
machine.getAvailability().addAll(timeSegments);
}
machine.getAvailability().sort(Comparator.comparing(TimeSegment::getStart));
}
private void AddMachineAvailable(Machine machine, List<ScheduleResultDetail> geneDetails) {
if (geneDetails == null || geneDetails.isEmpty()) return;
for (ScheduleResultDetail detail : geneDetails) {
machine.getAvailability().stream()
.filter(t -> t.getKey().equals(detail.getKey()))
.findFirst()
.ifPresent(t -> t.setUsed(false));
}
machine.setAvailability(MergeSegments(machine.getAvailability()));
}
private List<TimeSegment> MergeSegments(List<TimeSegment> segments) {
List<TimeSegment> maintenanceSegments = segments.stream()
.filter(t -> t.getType() == SegmentType.MAINTENANCE)
.collect(Collectors.toList());
List<TimeSegment> unusedRegularSegments = segments.stream()
.filter(t -> t.getType() != SegmentType.MAINTENANCE && !t.isUsed())
.collect(Collectors.toList());
List<TimeSegment> usedRegularSegments = segments.stream()
.filter(t -> t.getType() != SegmentType.MAINTENANCE && t.isUsed())
.collect(Collectors.toList());
unusedRegularSegments.sort(Comparator.comparing(TimeSegment::getStart));
Deque<TimeSegment> mergedUnused = new ArrayDeque<>();
for (TimeSegment seg : unusedRegularSegments) {
if (!mergedUnused.isEmpty() && mergedUnused.peekLast().getEnd().isAfter(seg.getStart())) {
TimeSegment last = mergedUnused.pollLast();
last.setEnd(last.getEnd().isAfter(seg.getEnd()) ? last.getEnd() : seg.getEnd());
mergedUnused.offerLast(last);
} else {
mergedUnused.offerLast(seg);
}
}
List<TimeSegment> result = new ArrayList<>(mergedUnused);
result.addAll(usedRegularSegments);
result.addAll(maintenanceSegments);
result.sort(Comparator.comparing(TimeSegment::getStart));
return result;
}
}
package com.aps.service.plan;
import com.aps.common.util.FileHelper;
import com.aps.common.util.JsonFileReader;
import com.aps.controller.gantt.FileUploadController;
import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GAScheduleResult;
import com.aps.entity.Algorithm.ScheduleParams;
import com.aps.entity.Algorithm.ScheduleResultDetail;
import com.aps.entity.basic.ScheduleChromosome;
import com.aps.entity.Schedule.GenVO;
import com.aps.entity.Schedule.MachineVO;
......@@ -13,9 +18,7 @@ import org.springframework.stereotype.Service;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
@Service
......@@ -92,10 +95,9 @@ public class PlanResultService {
}
}
public List<ScheduleChromosome> execute1() {
public List<Chromosome> execute1() {
try {
Entry entry=new Entry();
// 1. 读取数据
List<Machine> machines = loadData("machines.json", Machine.class);
......@@ -122,6 +124,11 @@ public class PlanResultService {
}
}
}
ScheduleParams param = new ScheduleParams();
param.setBaseTime(LocalDateTime.of(2025, 10, 1, 0, 0, 0));
param.setPopulationSize(50);
param.setMaxIterations(100);
// 创建节假日
List<Holiday> holidays = Arrays.asList(
......@@ -134,34 +141,118 @@ public class PlanResultService {
// 3. 创建调度服务
MachineSchedulerService machineScheduler = new MachineSchedulerService(
holidays, LocalDateTime.of(2025, 10, 1, 0, 0, 0));
holidays, param.getBaseTime());
// 4. 初始化机器时间线
for (Machine machine : machines) {
MachineTimeline timeline = machineScheduler.getOrCreateTimeline(machine);
machine.setAvailability(timeline.getSegments());
}
// 3. 构建订单-工序数据
List<Entry> allOperations = new ArrayList<>();
Random rnd = new Random(); // 注意:此处变量声明但未使用,可根据实际需求保留或移除
// 5. 执行调度算法
GeneticAlgorithm scheduler =new GeneticAlgorithm(); //new GeneticAlgorithm(products, machines, orders, machineScheduler);
List<ScheduleChromosome> scheduleChromosomes =null;
scheduler.Run();
int id = 1;
for (Order order : orders) {
// 假设products是一个List<Product>,根据Product的Id查找对应的产品
Product product = products.stream()
.filter(p -> p.getId() == order.getProductId())
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("未找到对应产品: " + order.getProductId()));
int sequence = 1;
for (Operation o : product.getOperations()) { // 假设Product类有getOperations()方法返回工序列表
Entry entry = new Entry();
entry.setId(id);
entry.setGroupId(order.getId());
entry.setSequence(sequence);
entry.setMachineOptions(o.getMachineOptions()); // 假设Operation类有获取机器选项的方法
entry.setPriority(order.getPriority());
entry.setQuantity(order.getQuantity());
// entry.setMaterialRequirements(o.getMaterialRequirements()); // 假设Operation类有获取物料需求的方法
// 对调度结果按照 fitness 由高到低排序
scheduleChromosomes.sort((c1, c2) -> Double.compare(c2.getFitness(), c1.getFitness()));
if (sequence != 1) {
entry.getPrevEntryIds().add(id - 1); // 假设Entry类有getPrevEntryIds()返回List<Integer>
}
// 为每个 ScheduleChromosome 分配场景ID(基于排序后的位置)
for (int i = 0; i < scheduleChromosomes.size(); i++) {
scheduleChromosomes.get(i).setSceneId(i + 1); // 场景ID从1开始
allOperations.add(entry);
sequence++;
id++;
}
}
// 5. 执行调度算法
GeneticAlgorithm scheduler =new GeneticAlgorithm(machines,orders,null,machineScheduler); //new GeneticAlgorithm(products, machines, orders, machineScheduler);
List<Chromosome> Chromosomes =scheduler.Run(param,allOperations);
return scheduleChromosomes;
Chromosomes.forEach(this::WriteScheduleSummary);
return Chromosomes;
} catch (Exception e) {
throw new RuntimeException("调度执行失败", e);
}
}
public void WriteScheduleSummary(Chromosome schedule) {
// 写入日志
FileHelper.writeLogFile(String.format("\n=== Schedule Summary === %f", schedule.getFitness()));
FileHelper.writeLogFile(String.format("Operation: %s", schedule.getOperationStr()));
FileHelper.writeLogFile(String.format("Makespan: %f minutes", schedule.getMakespan()));
FileHelper.writeLogFile(String.format("Total Tardiness: %f hours", schedule.getDelayTime()));
FileHelper.writeLogFile(String.format("Setup Time: %f minutes", schedule.getTotalChangeoverTime()));
FileHelper.writeLogFile(String.format("Flow Time: %f minutes", schedule.getTotalFlowTime()));
FileHelper.writeLogFile(String.format("Machine Load Balance: %.2f%%", schedule.getMachineLoadStd() * 100));
FileHelper.writeLogFile("-------------------------");
// 按订单分组写入
Map<Integer, List<GAScheduleResult>> orderGroups = schedule.getResult().stream()
.collect(Collectors.groupingBy(GAScheduleResult::getGroupId));
for (Map.Entry<Integer, List<GAScheduleResult>> group : orderGroups.entrySet()) {
List<GAScheduleResult> sortedJobs = group.getValue().stream()
.sorted(Comparator.comparingInt(GAScheduleResult::getOperationId))
.collect(Collectors.toList());
for (GAScheduleResult job : sortedJobs) {
StringBuilder sb = new StringBuilder();
sb.append(String.format(
"[%d-%d]:[%s-%s] Order %d, Machine %d, Operation %d, Batch %d, processingTime %d",
job.getStartTime(),
job.getEndTime(),
ConvertTime(job.getStartTime()),
ConvertTime(job.getEndTime()),
job.getGroupId(),
job.getMachineId(),
job.getOperationId(),
job.getQuantity(),
job.getProcessingTime()
));
// 追加基因详情
for (ScheduleResultDetail d : job.getGeneDetails()) {
sb.append(String.format(
"\n\t\t\t\t\t\t\t\t\t\t [%d-%d]:[%s-%s] %d",
d.getStartTime(),
d.getEndTime(),
ConvertTime(d.getStartTime()),
ConvertTime(d.getEndTime()),
d.getEndTime() - d.getStartTime()
));
}
FileHelper.writeLogFile(sb.toString());
}
FileHelper.writeLogFile("");
}
}
private String ConvertTime(int minute) {
return baseTime.plusMinutes(minute).format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm"));
}
/**
* 加载数据,优先从上传文件夹加载,如果不存在则从resources加载
......
......@@ -23,23 +23,23 @@ spring:
# 默认数据源
strict: false # 关闭严格模式
datasource:
# MySQL数据源
mysql:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.0.181:3310/mes?useSSL=false&serverTimezone=UTC
username: root # 替换为你的MySQL用户名
password: root_mes@123456~ # 替换为你的MySQL密码
# # MySQL数据源
# mysql:
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://192.168.0.181:3310/mes?useSSL=false&serverTimezone=UTC
# username: root # 替换为你的MySQL用户名
# password: root_mes@123456~ # 替换为你的MySQL密码
# Oracle数据源
oracle:
driver-class-name: oracle.jdbc.OracleDriver
url: jdbc:oracle:thin:@//192.168.0.181:1522/ORCLPDB1 # ORCL为你的Oracle实例名
url: jdbc:oracle:thin:@//39.100.78.207:7002/ORCLPDB1 # ORCL为你的Oracle实例名
username: mes # 替换为你的Oracle用户名
password: root_mes123456 # 替换为你的Oracle密码
sqlserver:
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
url: jdbc:sqlserver://192.168.0.181:1434;databaseName=mes;encrypt=false
username: sa
password: root_mes123456
# sqlserver:
# driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
# url: jdbc:sqlserver://192.168.0.181:1434;databaseName=mes;encrypt=false
# username: sa
# password: root_mes123456
# 文件上传配置
......
......@@ -5,6 +5,8 @@
"startTime": "08:00:00",
"endTime": "18:00:00",
"days": [1, 2, 3, 4, 5],
"startDate": "2025-10-10T00:00:00",
"endDate": "2025-11-10T00:00:00",
"shiftDate": "0001-01-01T00:00:00",
"temporaryShift": false,
"priority": 0,
......
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