Commit e90e9ad9 authored by Tong Li's avatar Tong Li

Merge remote-tracking branch 'origin/master' into tl

parents 30d0470a e44843a1
......@@ -31,3 +31,4 @@ build/
### VS Code ###
.vscode/
/src/main/resources/application.yml
......@@ -36,7 +36,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R<Void> handleIllegalArgumentException(IllegalArgumentException e) {
log.debug("参数校验失败: {}", e.getMessage());
log.error("参数校验失败:", e);
return R.failed(500, e.getMessage());
}
......@@ -46,7 +46,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(SceneGenerationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R<Void> handleSceneGenerationException(SceneGenerationException e) {
log.debug("场景生成异常: {}", e.getMessage());
log.error("场景生成异常:", e);
return R.failed(500, e.getMessage());
}
......@@ -56,7 +56,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R<Void> handleRuntimeException(RuntimeException e) {
log.debug("运行时异常: {}", e.getMessage());
log.error("运行时异常:", e);
return R.failed(500, e.getMessage());
}
......@@ -66,7 +66,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(BindingException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R<Void> handleBindingException(BindingException e) {
log.debug("MyBatis绑定异常: {}", e.getMessage());
log.error("MyBatis绑定异常:", e);
return R.failed(500, "数据绑定异常: " + e.getMessage());
}
......@@ -76,7 +76,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(MyBatisSystemException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R<Void> handleMyBatisSystemException(MyBatisSystemException e) {
log.debug("MyBatis系统异常: {}", e.getMessage());
log.error("MyBatis系统异常:", e);
Throwable cause = e.getCause();
if (cause != null && cause.getMessage().contains("ORA-17004")) {
return R.failed(500, "数据库列类型无效,请检查查询参数是否正确");
......@@ -90,7 +90,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(SQLException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R<Void> handleSQLException(SQLException e) {
log.debug("SQL异常: {}", e.getMessage());
log.error("SQL异常:", e);
return R.failed(500, "数据库访问异常: " + e.getMessage());
}
......@@ -100,7 +100,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(DataAccessException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R<Void> handleDataAccessException(DataAccessException e) {
log.debug("数据访问异常: {}", e.getMessage());
log.error("数据访问异常:", e);
return R.failed(500, "数据访问异常: " + e.getMessage());
}
......@@ -110,7 +110,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.debug("参数验证异常: {}", e.getMessage());
log.error("参数验证异常:", e);
FieldError fieldError = e.getBindingResult().getFieldError();
if (fieldError != null) {
return R.failed(400, Objects.requireNonNull(fieldError.getDefaultMessage()));
......@@ -124,7 +124,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R<Void> handleBindException(BindException e) {
log.debug("参数绑定异常: {}", e.getMessage());
log.error("参数绑定异常:", e);
FieldError fieldError = e.getBindingResult().getFieldError();
if (fieldError != null) {
return R.failed(500, Objects.requireNonNull(fieldError.getDefaultMessage()));
......@@ -138,7 +138,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R<Void> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
log.debug("请求参数缺失: {}", e.getMessage());
log.error("请求参数缺失:", e);
return R.failed(500, "缺少必要参数: " + e.getParameterName());
}
......@@ -148,7 +148,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R<Void> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
log.debug("参数类型不匹配: {}", e.getMessage());
log.error("参数类型不匹配:", e);
return R.failed(500, "参数类型不匹配: " + e.getName());
}
......@@ -158,7 +158,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R<Void> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
log.debug("HTTP消息不可读: {}", e.getMessage());
log.error("HTTP消息不可读:", e);
return R.failed(500, "请求体格式错误");
}
......@@ -168,7 +168,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public R<Void> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
log.debug("HTTP请求方法不支持: {}", e.getMessage());
log.error("HTTP请求方法不支持:", e);
return R.failed(405, "请求方法不支持: " + e.getMethod());
}
......@@ -178,7 +178,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
public R<Void> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
log.debug("HTTP媒体类型不支持: {}", e.getMessage());
log.error("HTTP媒体类型不支持:", e);
return R.failed(415, "媒体类型不支持: " + e.getContentType());
}
......@@ -188,7 +188,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public R<Void> handleNoHandlerFoundException(NoHandlerFoundException e) {
log.debug("404异常: {}", e.getMessage());
log.error("404异常:", e);
return R.failed(404, "请求的资源不存在: " + e.getRequestURL());
}
......@@ -198,7 +198,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R<Void> handleException(Exception e) {
log.debug("未知异常: {}", e.getMessage());
log.error("未知异常:", e);
return R.failed(500, "系统异常,请联系管理员");
}
}
\ No newline at end of file
......@@ -5,6 +5,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 生产环境可用的深拷贝工具类
......@@ -109,4 +110,21 @@ public class ProductionDeepCopyUtil {
throw new RuntimeException("列表深拷贝失败", e);
}
}
public static <T> CopyOnWriteArrayList<T> deepCopyList(CopyOnWriteArrayList<T> source, Class<T> elementType) {
if (source == null) {
return new CopyOnWriteArrayList<>();
}
try {
String json = objectMapper.writeValueAsString(source);
return objectMapper.readValue(json,
objectMapper.getTypeFactory().constructCollectionType(CopyOnWriteArrayList.class, elementType));
} catch (Exception e) {
throw new RuntimeException("列表深拷贝失败", e);
}
}
}
\ No newline at end of file
......@@ -167,14 +167,14 @@ public class SwaggerMapParamConfig {
));
break;
case "operationedit":
case "operationEdit":
properties.put("sceneId", new StringSchema().description("场景ID").example("B571EF6682DB463AB2977B1055A74112"));
properties.put("operation", new StringSchema().description("操作对象"));
properties.put("entry", new StringSchema().description("操作对象"));
examples.put("编辑操作示例", createExample(
"编辑指定的操作",
"{\n" +
" \"sceneId\": \"B571EF6682DB463AB2977B1055A74112\",\n" +
" \"operation\": {}\n" +
" \"entry\": {}\n" +
"}"
));
break;
......
package com.aps.controller.gantt;
import cn.hutool.core.bean.BeanUtil;
import com.aps.common.util.NumberUtils;
import com.aps.common.util.ParamValidator;
import com.aps.common.util.R;
......@@ -146,11 +147,31 @@ public class ResourceGanttController {
}
@GetMapping("/SyncMachines")
@Operation(summary = "更新设备信息缓存", description = "更新设备信息缓存")
public void SyncMachines() {
// 调用 PlanResultService 获取 ScheduleChromosome 列表
planResultService.InitCalendarToAllMachines();
@PostMapping("/editOrder")
@Operation(summary = "修改订单", description = "编辑场景中的订单信息",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "编辑订单参数",
content = @io.swagger.v3.oas.annotations.media.Content(
mediaType = "application/json",
examples = @io.swagger.v3.oas.annotations.media.ExampleObject(
name = "编辑订单示例",
value = "{\n \"sceneId\": \"B571EF6682DB463AB2977B1055A74112\",\n \"order\": { }\n}"
)
)
)
)
public R<Chromosome> editOrder(@RequestBody Map<String, Object> params) {
log.info("editOrder 请求参数: {}", params);
String sceneId = ParamValidator.getString(params, "sceneId", "场景ID");
ParamValidator.validateSceneExists(sceneService, sceneId);
// 使用BeanUtil转换LinkedHashMap为Order对象
Order order = BeanUtil.toBean(params.get("order"), Order.class);
Chromosome result = planResultService.EditOrder(sceneId, order);
return R.ok(result);
}
......@@ -231,17 +252,16 @@ public class ResourceGanttController {
@PostMapping("/operationedit")
@PostMapping("/operationEdit")
@Operation(summary = "修改工单", description = "修改工单")
public R<Chromosome> operationedit(@RequestBody Map<String, Object> params) {
log.info("operationedit 请求参数: {}", params);
public R<Chromosome> operationEdit(@RequestBody Map<String, Object> params) {
log.info("operationEdit 请求参数: {}", params);
String sceneId = ParamValidator.getString(params, "sceneId", "场景ID");
ParamValidator.validateSceneExists(sceneService, sceneId);
// 处理Entry对象
Entry entry = (Entry) params.get("operation");
// 使用BeanUtil转换LinkedHashMap为Entry对象
Entry entry = BeanUtil.toBean(params.get("entry"), Entry.class);
Chromosome result = planResultService.EditOperation(sceneId, entry);
return R.ok(result);
......
......@@ -12,6 +12,7 @@ import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
/**
......@@ -113,7 +114,7 @@ public class Chromosome {
/// <summary>
/// 解码后的调度结果
/// </summary>
private List<GAScheduleResult> ResultOld=new ArrayList<>();
private List<GAScheduleResult> ResultOld=new CopyOnWriteArrayList<>();
/// <summary>
/// 最早完工时间
......
......@@ -140,4 +140,7 @@ public class Entry {
private BigDecimal runtime;//持续时间
private BigDecimal singleOut;//单件产出
private double changeLineTime;//换模时间
private BigDecimal setupTime;
private int constTime;
}
......@@ -5,6 +5,7 @@ import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
@Data
......@@ -15,7 +16,7 @@ public class Machine {
private double totalTaskTime=0;
private List<Shift> shifts;
private List<MaintenanceWindow> maintenanceWindows;
private List<TimeSegment> availability;
private CopyOnWriteArrayList<TimeSegment> availability;
private String code;
private List<Holiday> holidays;
......@@ -30,7 +31,7 @@ public class Machine {
public Machine() {
this.shifts = new ArrayList<>();
this.maintenanceWindows = new ArrayList<>();
this.availability = new ArrayList<>();
this.availability = new CopyOnWriteArrayList<>();
}
public Machine(int id, String name) {
......
......@@ -4,17 +4,18 @@ import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
public class MachineTimeline {
private Long machineId;
private List<TimeSegment> segments;
private CopyOnWriteArrayList<TimeSegment> segments;
private LocalDateTime validFrom;
private LocalDateTime validTo;
private LocalDateTime lastUpdated;
private int version = 1;
public MachineTimeline() {
this.segments = new ArrayList<>();
this.segments = new CopyOnWriteArrayList<>();
}
// Getters and Setters
......@@ -26,11 +27,11 @@ public class MachineTimeline {
this.machineId = machineId;
}
public List<TimeSegment> getSegments() {
public CopyOnWriteArrayList<TimeSegment> getSegments() {
return segments;
}
public void setSegments(List<TimeSegment> segments) {
public void setSegments(CopyOnWriteArrayList<TimeSegment> segments) {
this.segments = segments;
}
......
......@@ -14,6 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import java.time.LocalDateTime;
import java.util.*;
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;
......@@ -335,6 +336,8 @@ public class GeneticAlgorithm {
.join();
if(1==2) {
if (population != null && population.size() > 0) {
population.parallelStream().forEach(chromosome -> {
......@@ -345,18 +348,26 @@ public class GeneticAlgorithm {
}
}
}
private void decode(GeneticDecoder decoder,Chromosome chromosome,ScheduleParams param, List<Entry> allOperations,List<GlobalOperationInfo> globalOpList) {
chromosome.setResult(new ArrayList<>());
if (chromosome==null){
System.out.println("chromosome==null");
}
chromosome.setResult(new CopyOnWriteArrayList<>());
// 假设Machine类有拷贝方法,或使用MapStruct等工具进行映射
chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(machines,Machine.class)); // 简单拷贝,实际可能需要深拷贝
chromosome.setAllOperations(allOperations); // 简单拷贝,实际可能需要深拷贝
chromosome.setGlobalOpList(globalOpList); // 简单拷贝,实际可能需要深拷贝
//chromosome.setObjectiveWeights(_objectiveWeights);
chromosome.setBaseTime(param.getBaseTime());
// chromosome.setInitMachines(ProductionDeepCopyUtil.deepCopyList(machines)); // 简单拷贝,实际可能需要深拷贝
// chromosome.setInitMachines(ProductionDeepCopyUtil.deepCopyList(machines,Machine.class)); // 简单拷贝,实际可能需要深拷贝
// _sceneService.saveChromosomeToFile(chromosome, "12345679");
decoder.decodeChromosomeWithCache(chromosome);
......
......@@ -141,10 +141,13 @@ public class KpiCalculator {
*/
private void calculateMachine() {
// 按设备ID分组工序列表(对应C# GroupBy + ToList)
// 按设备ID分组工序列表(对应C# GroupBy + ToList)- Java 8兼容方式,处理null键
List<GAScheduleResult> list= chromosome.getResult() ;
Map<Long, List<GAScheduleResult>> machineTasks = list.stream()
.collect(Collectors.groupingBy(GAScheduleResult::getMachineId));
Map<Long, List<GAScheduleResult>> machineTasks = new HashMap<>();
list.forEach(result -> {
Long machineId = result.getMachineId();
machineTasks.computeIfAbsent(machineId, k -> new ArrayList<>()).add(result);
});
// 计划工作时间:最后一个任务结束时间 - 第一个任务开始时间
int firstTaskStart = list.stream()
......@@ -202,9 +205,12 @@ public class KpiCalculator {
*/
private void calculateOrder() {
List<GAScheduleResult> list= chromosome.getResult();
// 按GroupId(订单ID)分组 - 对应C#的GroupBy
Map<String, List<GAScheduleResult>> orderGroups = list.stream()
.collect(Collectors.groupingBy(GAScheduleResult::getOrderId));
// 按GroupId(订单ID)分组 - Java 8兼容方式,处理null键
Map<String, List<GAScheduleResult>> orderGroups = new HashMap<>();
list.forEach(result -> {
String orderId = result.getOrderId();
orderGroups.computeIfAbsent(orderId, k -> new ArrayList<>()).add(result);
});
for (Map.Entry<String, List<GAScheduleResult>> entry : orderGroups.entrySet()) {
String orderId = entry.getKey();
......
......@@ -5,7 +5,7 @@ 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.AlgorithmScheduler8;
import com.aps.service.plan.MachineSchedulerService;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
......@@ -15,6 +15,7 @@ import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
/**
......@@ -65,7 +66,7 @@ public class MachineCalculator {
Machine machine, int processingTime, LocalDateTime currentTime,
String prevtime, List<GAScheduleResult> existingTasks,double oneTime,double quantity, boolean checkprevtime, boolean islockMachineTime
,boolean isInterrupt) {
List<GAScheduleResult> machineTasks = existingTasks.stream()
List<GAScheduleResult> machineTasks =existingTasks.stream()
.filter(t -> t.getMachineId() == machine.getId())
.sorted(Comparator.comparingInt(GAScheduleResult::getStartTime))
.collect(Collectors.toList());
......@@ -185,12 +186,12 @@ public class MachineCalculator {
List<TimeSegment> timeSegments= findAvailableSegments(machine, currentTime, machineTasks, remainingTime, isInterrupt);
int estimateIndex= (int) Math.ceil(remainingTime / (double) ONE_DAY_MINUTES);
List<TimeSegment> timeSegments1=null;
if(estimateIndex>10)
{
timeSegments1= getEnoughSegmentsByEstimateIndex(timeSegments,currentTime,remainingTime);
}
// if(estimateIndex>10)
// {
// timeSegments1= getEnoughSegmentsByEstimateIndex(timeSegments,currentTime,remainingTime);
// }
if(timeSegments1==null) {
// if(timeSegments1==null) {
int i = 0;
while (remainingTime > 0) {
TimeSegment shift = timeSegments.get(i);
......@@ -203,9 +204,9 @@ public class MachineCalculator {
RemoveMachineAvailable(machine, time,shift);
i++;
}
}else {
times= CaldScheduleResultDetail(timeSegments1,machine,st,remainingTime,oneTime);
}
// }else {
// times= CaldScheduleResultDetail(timeSegments1,machine,st,remainingTime,oneTime);
// }
return times;
}
......@@ -307,10 +308,10 @@ public class MachineCalculator {
LocalDateTime current = start;
// 预先排序设备可用片段,避免后续遍历混乱
List<TimeSegment> allUseTimeSegment = machine.getAvailability().stream()
.filter(slot -> !slot.isUsed() && slot.getType() != SegmentType.MAINTENANCE)
.sorted(Comparator.comparing(TimeSegment::getStart, (a, b) -> a.compareTo(b)))
.collect(Collectors.toList());
// List<TimeSegment> allUseTimeSegment = machine.getAvailability().stream()
// .filter(slot -> !slot.isUsed() && slot.getType() != SegmentType.MAINTENANCE)
// .sorted(Comparator.comparing(TimeSegment::getStart, (a, b) -> a.compareTo(b)))
// .collect(Collectors.toList());
// 替换while(true),增加明确退出条件(原逻辑保留,可根据业务补充退出判断)
while (true) {
......@@ -547,8 +548,8 @@ public class MachineCalculator {
return new ArrayList<>();
}
List<TimeSegment> continuousCompliantSegments = new ArrayList<>();
List<TimeSegment> tempContinuousGroup = new ArrayList<>();
CopyOnWriteArrayList<TimeSegment> continuousCompliantSegments = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList<TimeSegment> tempContinuousGroup = new CopyOnWriteArrayList<>();
double tempContinuous = 0.0;
LocalDateTime lastSegmentEnd = currentTime;
......@@ -697,14 +698,14 @@ public class MachineCalculator {
if (newSegments == null || newSegments.isEmpty()) {
return;
}
List<TimeSegment> availabilitySnapshot = new ArrayList<>(machine.getAvailability());
availabilitySnapshot.addAll(newSegments);
List<TimeSegment> mergedSegments = MergeSegments(availabilitySnapshot);
synchronized (machine.getAvailability()) {
machine.getAvailability().addAll(newSegments);
CopyOnWriteArrayList<TimeSegment> mergedSegments = MergeSegments(machine.getAvailability());
// synchronized (machine.getAvailability()) {
// 合并片段(去重+排序),
machine.setAvailability(mergedSegments);
}
// }
......@@ -735,16 +736,16 @@ public class MachineCalculator {
*/
private TimeSegment GetCurrentOrNextShift(Machine machine, LocalDateTime time, String prevtime, boolean checkprevtime) {
TimeSegment start = null;
List<TimeSegment> availabilitySnapshot = new ArrayList<>(machine.getAvailability());
if (!availabilitySnapshot.isEmpty())
if (!machine.getAvailability().isEmpty())
{
start = availabilitySnapshot.stream()
start = machine.getAvailability().stream()
.filter(slot -> !slot.isUsed() && slot.getType() != SegmentType.MAINTENANCE)
.filter(slot -> slot.getStart().isAfter(time) || slot.getEnd().isAfter(time))
.findFirst()
.orElse(null);
}else {
machine.setAvailability(new ArrayList<>());
machine.setAvailability(new CopyOnWriteArrayList<>());
}
// 查找有效班次
......@@ -753,9 +754,9 @@ public class MachineCalculator {
// 生成新时间段
List<TimeSegment> timeSegments = machineScheduler.generateTimeSegment(machine, time.plusDays(1),0);
synchronized (machine.getAvailability()) {
machine.getAvailability().addAll(timeSegments);
}
// 更新设备时间线
Machine originalMachine = machines.stream()
.filter(t -> t.getId() == machine.getId())
......@@ -1005,19 +1006,19 @@ public class MachineCalculator {
if(usedSegment!=null)
{
synchronized (machine.getAvailability()) {
machine.getAvailability().add(usedSegment);
machine.getAvailability().sort(Comparator.comparing(TimeSegment::getStart));
}
}
}
private void RemoveMachineAvailable1(Machine machine, ScheduleResultDetail geneDetails) {
private void RemoveMachineAvailable1(Machine machine, ScheduleResultDetail geneDetails,TimeSegment targetSegment1) {
List<TimeSegment> timeSegments = new ArrayList<>();
List<TimeSegment> availabilitySnapshot = new ArrayList<>(machine.getAvailability());
CopyOnWriteArrayList<TimeSegment> availabilitySnapshot = new CopyOnWriteArrayList<>(machine.getAvailability());
int index = availabilitySnapshot.stream()
.filter(t -> t.getKey().equals(geneDetails.getKey()))
.findFirst()
......@@ -1050,9 +1051,9 @@ public class MachineCalculator {
}
availabilitySnapshot.sort(Comparator.comparing(TimeSegment::getStart));
// 关键修复2:加锁(若多线程访问),避免并发修改
synchronized (machine.getAvailability()) {
machine.setAvailability(availabilitySnapshot);
}
}
public void AddMachineAvailable(Machine machine, List<ScheduleResultDetail> geneDetails) {
......@@ -1071,9 +1072,12 @@ if(keys1!=null&&keys1.size()>0) {
keys.addAll(keys1);
}
machine.getAvailability().stream()
.filter(t ->keys.contains(t.getKey()))
.forEach(t->t.setUsed(false));
List<TimeSegment> toUpdate = machine.getAvailability().stream()
.filter(t -> keys.contains(t.getKey()))
.collect(Collectors.toList());
// 然后批量修改
toUpdate.forEach(t -> t.setUsed(false));
......@@ -1081,7 +1085,7 @@ if(keys1!=null&&keys1.size()>0) {
public void AddMachineAvailable1(Machine machine, List<ScheduleResultDetail> geneDetails) {
if (geneDetails == null || geneDetails.isEmpty()) return;
List<TimeSegment> availabilitySnapshot = new ArrayList<>(machine.getAvailability());
CopyOnWriteArrayList<TimeSegment> availabilitySnapshot = new CopyOnWriteArrayList<>(machine.getAvailability());
for (ScheduleResultDetail detail : geneDetails) {
if(detail.getUsedSegment()!=null&&detail.getUsedSegment().size()>0)
......@@ -1142,18 +1146,18 @@ if(keys1!=null&&keys1.size()>0) {
machine.getAvailability().sort((a, b) -> a.getStart().compareTo(b.getStart()));
}
private List<TimeSegment> MergeSegments(List<TimeSegment> segments) {
List<TimeSegment> maintenanceSegments = segments.stream()
private CopyOnWriteArrayList<TimeSegment> MergeSegments(CopyOnWriteArrayList<TimeSegment> segments) {
CopyOnWriteArrayList<TimeSegment> maintenanceSegments = segments.stream()
.filter(t -> t.getType() == SegmentType.MAINTENANCE)
.collect(Collectors.toList());
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
List<TimeSegment> unusedRegularSegments = segments.stream()
CopyOnWriteArrayList<TimeSegment> unusedRegularSegments = segments.stream()
.filter(t -> t.getType() != SegmentType.MAINTENANCE && !t.isUsed())
.collect(Collectors.toList());
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
List<TimeSegment> usedRegularSegments = segments.stream()
CopyOnWriteArrayList<TimeSegment> usedRegularSegments = segments.stream()
.filter(t -> t.getType() != SegmentType.MAINTENANCE && t.isUsed())
.collect(Collectors.toList());
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
unusedRegularSegments.sort(Comparator.comparing(TimeSegment::getStart));
......@@ -1168,7 +1172,7 @@ if(keys1!=null&&keys1.size()>0) {
}
}
List<TimeSegment> result = new ArrayList<>(mergedUnused);
CopyOnWriteArrayList<TimeSegment> result = new CopyOnWriteArrayList<>(mergedUnused);
result.addAll(usedRegularSegments);
result.addAll(maintenanceSegments);
result.sort(Comparator.comparing(TimeSegment::getStart));
......
......@@ -179,9 +179,12 @@ public class OrderSortService {
return;
}
// 1. 按当前条件分组
Map<Object, List<Order>> groups = orders.stream()
.collect(Collectors.groupingBy(keyExtractor));
// 1. 按当前条件分组 处理null键
Map<Object, List<Order>> groups = new HashMap<>();
orders.forEach(order -> {
Object key = keyExtractor.apply(order);
groups.computeIfAbsent(key, k -> new ArrayList<>()).add(order);
});
// 2. 对分组键排序(关键:按条件配置的方向排序)
List<Object> sortedKeys = getSortedKeys(groups, currentCondition);
......
......@@ -213,6 +213,8 @@ public class RoutingDataService {
entry.setEquipTypeCode(op.getEquipTypeCode());
entry.setRuntime(op.getRuntime());
entry.setSingleOut(op.getSingleOut());
entry.setSetupTime(op.getSetupTime());
entry.setConstTime(op.getConstTime());
entry.setOrderId(op.getOrderId());
entry.setOrderCode(op.getOrderCode());
entry.setQuantity(op.getPlanQty());
......
package com.aps.service.plan;
import com.aps.common.util.FileHelper;
import com.aps.entity.basic.ScheduleChromosome;
import com.aps.entity.basic.*;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
public class AlgorithmScheduler6 {
private final List<Product> _products;
private final List<Machine> _machines;
private final List<Order> _orders;
private final Random _random;
private final MachineSchedulerService _machineScheduler;
private int _populationSize = 100;
private double _crossoverRate = 0.8;
private double _mutationRate = 0.1;
private int _maxGenerations = 500;
private int _elitismCount = 5;
private final LocalDateTime baseTime = LocalDateTime.of(2025, 10, 1, 0, 0, 0);
public AlgorithmScheduler6(List<Product> products, List<Machine> machines, List<Order> orders, MachineSchedulerService machineScheduler) {
this._products = deepCopyProductList(products);
this._machines = deepCopyMachineList(machines);
this._orders = deepCopyOrderList(orders);
this._machineScheduler = machineScheduler;
this._random = new Random();
}
public ScheduleChromosome Run() {
List<ScheduleChromosome> population = InitializePopulation();
ScheduleChromosome bestSolution = null;
ScheduleChromosome currentBest = null;
double bestFitness = Double.MIN_VALUE;
currentBest = population.stream()
.max(Comparator.comparingDouble(ScheduleChromosome::getFitness))
.orElse(null);
if (currentBest != null) {
bestFitness = currentBest.getFitness();
bestSolution = deepCopyScheduleChromosome(currentBest);
}
return bestSolution;
}
public List<ScheduleChromosome> RunAll() {
return InitializePopulation();
}
private List<ScheduleChromosome> InitializePopulation() {
List<ScheduleChromosome> population = new ArrayList<>();
System.out.println("开始初始化种群,目标大小: " + _populationSize);
for (int i = 0; i < _populationSize; i++) {
ScheduleChromosome chromosome = new ScheduleChromosome(baseTime);
chromosome.setGenes(new ArrayList<>());
chromosome.setObjectiveValues(new HashMap<>());
chromosome.setOrders(deepCopyOrderList(_orders));
chromosome.setMachines(deepCopyMachineList(_machines));
// 为每个订单分配工序
for (Order order : _orders) {
Product product = _products.stream()
.filter(p -> p.getId() == order.getProductId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("Product not found: " + order.getProductId()));
int prevtime = 0;
int remainingQuantity =(int) order.getQuantity();
// 订单拆分逻辑 - 增强随机性
while (remainingQuantity > 0) {
int batchSize;
if (order.isCanSplit() && remainingQuantity > 1) {
// 修改:增强批次拆分的随机性
int maxSplit = Math.min(remainingQuantity, Math.max(1, remainingQuantity / 2));
batchSize = _random.nextInt(maxSplit) + 1;
} else {
batchSize = remainingQuantity;
}
if (batchSize > remainingQuantity) {
batchSize = remainingQuantity;
}
remainingQuantity -= batchSize;
// 按工序顺序处理
for (Operation operation : product.getOperations().stream()
.sorted(Comparator.comparingInt(Operation::getSequence))
.collect(Collectors.toList())) {
if (operation.getId() > 1) {
Gene prevGene = chromosome.getGenes().stream()
.filter(t -> t.getOrderId() == order.getId() && t.getOperationId() == operation.getId() - 1)
.findFirst()
.orElse(null);
prevtime = prevGene != null ? prevGene.getEndTime() : 0;
}
MachineOption machineOption;
Machine machine;
// 多设备选择逻辑 - 增强随机性
if (operation.getMachineOptions().size() > 1) {
Set<Long> machineIds = operation.getMachineOptions().stream()
.map(MachineOption::getMachineId)
.collect(Collectors.toSet());
// 修改:增强设备选择的随机性
List<Machine> availableMachines = chromosome.getMachines().stream()
.filter(t -> machineIds.contains(t.getId()))
.collect(Collectors.toList());
if (!availableMachines.isEmpty()) {
// 随机打乱后选择
Collections.shuffle(availableMachines, _random);
machine = availableMachines.get(0);
Machine finalMachine = machine;
machineOption = operation.getMachineOptions().stream()
.filter(t -> t.getMachineId() == finalMachine.getId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("MachineOption not found for machine: " + finalMachine.getId()));
} else {
// 回退到原始逻辑
machine = chromosome.getMachines().stream()
.filter(t -> machineIds.contains(t.getId()))
.sorted(Comparator.comparingInt(Machine::getEarliestTime)
.thenComparingDouble(Machine::getTotalTaskTime)
.thenComparing(t -> _random.nextDouble()))
.findFirst()
.orElseThrow(() -> new NoSuchElementException("No available machine for operation: " + operation.getId()));
Machine finalMachine = machine;
machineOption = operation.getMachineOptions().stream()
.filter(t -> t.getMachineId() == finalMachine.getId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("MachineOption not found for machine: " + finalMachine.getId()));
}
} else {
machineOption = operation.getMachineOptions().get(0);
MachineOption finalMachineOption = machineOption;
machine = chromosome.getMachines().stream()
.filter(m -> m.getId() == finalMachineOption.getMachineId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("Machine not found: " + finalMachineOption.getMachineId()));
}
double processingTime = machineOption.getProcessingTime() * batchSize;
int preTime = machineOption.getPreTime(); // 前处理时间(常数时间,与批次大小无关)
int setupTime = 0; // 换型时间默认为0,仅在产品变化时计算
int teardownTime = machineOption.getTeardownTime(); // 后处理时间(常数时间,与批次大小无关)
// 计算换型时间
// 查找该机器上一个任务
Gene lastGeneOnMachine = chromosome.getGenes().stream()
.filter(g -> g.getMachineId() == machine.getId())
.max(Comparator.comparingInt(Gene::getEndTime))
.orElse(null);
// 如果上一个任务的产品与当前任务不同,则需要换型时间
if (lastGeneOnMachine != null && lastGeneOnMachine.getProductId() != order.getProductId()) {
setupTime = machineOption.getSetupTime();
}
// 前处理时间可以在上一个工序未完成时开始,所以调整开始时间
int adjustedPrevTime = prevtime;
if (preTime > 0 && lastGeneOnMachine != null) {
// 前处理可以在上一个任务结束前preTime时间开始
adjustedPrevTime = lastGeneOnMachine.getEndTime() - preTime;
}
// List<GeneDetail> geneDetails = GetNextAvailableTime(
// machine, prevtime, -1, processingTime,
// chromosome.getGenes(), false, true
// );
List<GeneDetail> geneDetails = GetNextAvailableTime(
machine, adjustedPrevTime, -1, processingTime + teardownTime + setupTime,
chromosome.getGenes(), false, true
);
int startTime = geneDetails.stream().mapToInt(GeneDetail::getStartTime).min().orElse(0);
int endTime = geneDetails.stream().mapToInt(GeneDetail::getEndTime).max().orElse(0);
machine.setEarliestTime(endTime);
machine.setTotalTaskTime(machine.getTotalTaskTime() + +teardownTime + setupTime);
Gene gene = new Gene();
gene.setOrderId(order.getId());
gene.setProductId(order.getProductId());
gene.setOperationId(operation.getId());
gene.setMachineId(machineOption.getMachineId());
gene.setBatchSize(batchSize);
gene.setStartTime(startTime);
gene.setEndTime(endTime);
gene.setProcessingTime(processingTime);
gene.setGeneDetails(deepCopyGeneDetailList(geneDetails));
gene.setId(chromosome.getGenes().size() + 1);
gene.setSequenceId(operation.getSequence());
chromosome.getGenes().add(gene);
}
}
}
population.add(chromosome);
}
System.out.println("生成种群大小: " + population.size());
Map<String, List<ScheduleChromosome>> grouped = population.stream()
.collect(Collectors.groupingBy(ScheduleChromosome::getGenesStr));
population = grouped.values().stream()
.map(group -> group.get(0)) // 对应 C# 的 g.First()
.collect(Collectors.toList());
//
//
// // 修改:更宽松的去重策略,接近C#的行为
// if (population.size() > _populationSize) {
// Map<String, ScheduleChromosome> uniqueMap = new LinkedHashMap<>();
// for (ScheduleChromosome chrom : population) {
// // 使用更宽松的键:只基于设备分配和批次,忽略时间差异
// String relaxedKey = generateRelaxedKey(chrom);
// uniqueMap.putIfAbsent(relaxedKey, chrom);
// }
//
// // 如果去重后仍然过多,再使用严格键
// if (uniqueMap.size() < _populationSize * 0.8) {
// uniqueMap.clear();
// for (ScheduleChromosome chrom : population) {
// uniqueMap.putIfAbsent(chrom.getGenesStr(), chrom);
// }
// }
//
// population = new ArrayList<>(uniqueMap.values());
// System.out.println("去重后种群大小: " + population.size());
// }
// 调试输出染色体多样性
System.out.println("染色体多样性分析:");
Map<String, Integer> keyCounts = new HashMap<>();
for (ScheduleChromosome chrom : population) {
String key = chrom.getGenesStr();
keyCounts.put(key, keyCounts.getOrDefault(key, 0) + 1);
}
System.out.println("唯一染色体数量: " + keyCounts.size());
System.out.println("染色体分布: " + keyCounts);
// 并行计算适应度
population.parallelStream().forEach(this::CalculateFitness);
// 打印摘要
population.forEach(this::WriteScheduleSummary);
return population;
}
// 新增:生成宽松的键(只关注设备分配模式)
private String generateRelaxedKey(ScheduleChromosome chrom) {
return chrom.getGenes().stream()
.sorted(Comparator.comparing(Gene::getOrderId)
.thenComparing(Gene::getOperationId))
.map(g -> String.format("O%d-Op%d-M%d-B%d",
g.getOrderId(), g.getOperationId(), g.getMachineId(), g.getBatchSize()))
.collect(Collectors.joining("|"));
}
private List<ScheduleChromosome> removeTrueDuplicates(List<ScheduleChromosome> population) {
Map<String, ScheduleChromosome> uniqueMap = new LinkedHashMap<>();
for (ScheduleChromosome chrom : population) {
// 使用更宽松的键:包含批次大小、开始时间等关键信息
String key = generateChromosomeKey(chrom);
uniqueMap.putIfAbsent(key, chrom);
}
return new ArrayList<>(uniqueMap.values());
}
private String generateChromosomeKey(ScheduleChromosome chrom) {
// 与C#一致的键生成逻辑:包含订单、工序、设备、批次、时间等关键信息
return chrom.getGenes().stream()
.sorted(Comparator.comparing(Gene::getOrderId)
.thenComparing(Gene::getOperationId)
.thenComparing(Gene::getStartTime))
.map(g -> String.format("%d-%d-%d-%d-%d-%d",
g.getOrderId(), g.getOperationId(), g.getMachineId(),
g.getBatchSize(), g.getStartTime(), g.getEndTime()))
.collect(Collectors.joining("|"));
}
// ========================== CalculateFitness(参数与C#一致) ==========================
private void CalculateFitness(ScheduleChromosome chromosome) {
// 1. 最大完工时间
double makespan = chromosome.getGenes().stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
// 2. 总延迟时间
double tardiness = 0.0;
Map<Integer, List<Gene>> orderGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
int orderCompletion = group.getValue().stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
Order order = _orders.stream()
.filter(o -> o.getId() == group.getKey())
.findFirst()
.orElse(null);
if (order != null) {
LocalDateTime completionTime = chromosome.getBaseTime().plusMinutes(orderCompletion);
// 修复:正确处理OffsetDateTime到LocalDateTime的转换
LocalDateTime dueDateTime = order.getDueDate();
if (completionTime.isAfter(dueDateTime)) {
// 计算延迟小时数(修复时间计算)
long hours = ChronoUnit.HOURS.between(dueDateTime, completionTime);
long minutes = ChronoUnit.MINUTES.between(dueDateTime, completionTime) % 60;
tardiness += hours + (double) minutes / 60;
}
}
}
// 3. 总换型时间
double totalSetupTime = CalculateTotalSetupTime(chromosome);
// 4. 总流程时间
double totalFlowTime = CalculateTotalFlowTime(chromosome);
// 5. 机器负载均衡
double machineLoadBalance = CalculateMachineLoadBalance(chromosome);
// 存储目标值(键与C#一致)
double finalTardiness = tardiness;
HashMap<Integer, Double> objMap = new HashMap<>();
objMap.put(0, makespan);
objMap.put(1, finalTardiness);
objMap.put(2, totalSetupTime);
objMap.put(3, totalFlowTime);
objMap.put(4, machineLoadBalance);
chromosome.setObjectiveValues(objMap);
// 计算适应度
chromosome.setFitness(
0.4 * (1.0 / (1 + makespan)) +
0.3 * (1.0 / (1 + tardiness)) +
0.1 * (1.0 / (1 + totalSetupTime)) +
0.1 * (1.0 / (1 + totalFlowTime)) +
0.1 * machineLoadBalance
);
}
// ========================== CalculateTotalFlowTime(参数与C#一致) ==========================
private double CalculateTotalFlowTime(ScheduleChromosome chromosome) {
double totalFlowTime = 0.0;
Map<Integer, List<Gene>> orderGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
int minStartTime = group.getValue().stream().mapToInt(Gene::getStartTime).min().orElse(0);
int maxEndTime = group.getValue().stream().mapToInt(Gene::getEndTime).max().orElse(0);
LocalDateTime start = chromosome.getBaseTime().plusMinutes(minStartTime);
LocalDateTime end = chromosome.getBaseTime().plusMinutes(maxEndTime);
// 修复:计算流程时间(小时)
long hours = ChronoUnit.HOURS.between(start, end);
long minutes = ChronoUnit.MINUTES.between(start, end) % 60;
totalFlowTime += hours + (double) minutes / 60;
}
return totalFlowTime;
}
// ========================== CalculateTotalSetupTime(参数与C#一致) ==========================
private double CalculateTotalSetupTime(ScheduleChromosome chromosome) {
double totalSetupTime = 0.0;
Map<Long, List<Gene>> machineGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getMachineId));
for (Map.Entry<Long, List<Gene>> machineGroup : machineGroups.entrySet()) {
List<Gene> sortedGenes = machineGroup.getValue().stream()
.sorted(Comparator.comparingInt(Gene::getStartTime))
.collect(Collectors.toList());
Integer lastProductId = null;
for (Gene gene : sortedGenes) {
if (lastProductId != null && lastProductId != gene.getProductId()) {
// 查找对应工序和设备选项
Product product = _products.stream()
.filter(p -> p.getId() == gene.getProductId())
.findFirst()
.orElse(null);
if (product == null) continue;
Operation operation = product.getOperations().stream()
.filter(o -> o.getId() == gene.getOperationId())
.findFirst()
.orElse(null);
if (operation == null) continue;
MachineOption machineOption = operation.getMachineOptions().stream()
.filter(m -> m.getMachineId() == gene.getMachineId())
.findFirst()
.orElse(null);
if (machineOption != null) {
totalSetupTime += machineOption.getSetupTime();
}
}
lastProductId = gene.getProductId();
}
}
return totalSetupTime;
}
// ========================== CalculateMachineLoadBalance(参数与C#一致) ==========================
private double CalculateMachineLoadBalance(ScheduleChromosome chromosome) {
Map<Long, Double> machineUtilization = new HashMap<>();
int maxEndTime = chromosome.getGenes().stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
if (maxEndTime == 0) return 0.0;
// 计算设备忙碌时间
for (Machine machine : _machines) {
double busyTime = chromosome.getGenes().stream()
.filter(g -> g.getMachineId() == machine.getId())
.mapToInt(g -> g.getEndTime() - g.getStartTime())
.sum();
machineUtilization.put(machine.getId(), busyTime / maxEndTime);
}
// 计算方差
double avgUtilization = machineUtilization.values().stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
double variance = machineUtilization.values().stream()
.mapToDouble(u -> Math.pow(u - avgUtilization, 2))
.average()
.orElse(0.0);
// 负载均衡得分
return 1.0 / (1 + variance);
}
// ========================== RemoveMachineAvailable(参数与C#一致) ==========================
private void RemoveMachineAvailable(Machine machine, GeneDetail geneDetails) {
List<TimeSegment> timeSegments = new ArrayList<>();
// 查找对应时间段
int index = machine.getAvailability().stream()
.filter(t -> t.getKey().equals(geneDetails.getKey()))
.findFirst()
.map(machine.getAvailability()::indexOf)
.orElse(-1);
if (index > -1) {
TimeSegment targetSegment = machine.getAvailability().get(index);
LocalDateTime geneEndTime = baseTime.plusMinutes(geneDetails.getEndTime());
if (targetSegment.getEnd().isAfter(geneEndTime)) {
// 拆分时间段
TimeSegment usedSegment = new TimeSegment();
usedSegment.setStart(baseTime.plusMinutes(geneDetails.getStartTime()));
usedSegment.setEnd(geneEndTime);
usedSegment.setHoliday(false);
usedSegment.setKey(UUID.randomUUID().toString());
usedSegment.setType(SegmentType.REGULAR);
usedSegment.setUsed(true);
timeSegments.add(usedSegment);
// 更新基因详情Key
geneDetails.setKey(usedSegment.getKey());
// 更新原时间段开始时间
targetSegment.setStart(geneEndTime);
} else {
targetSegment.setUsed(true);
}
}
// 添加新段并排序
if (!timeSegments.isEmpty()) {
machine.getAvailability().addAll(timeSegments);
}
machine.getAvailability().sort(Comparator.comparing(TimeSegment::getStart));
}
// ========================== AddMachineAvailable(参数与C#一致) ==========================
private void AddMachineAvailable(Machine machine, List<GeneDetail> geneDetails) {
if (geneDetails == null || geneDetails.isEmpty()) return;
// 标记时间段为未占用
for (GeneDetail detail : geneDetails) {
machine.getAvailability().stream()
.filter(t -> t.getKey().equals(detail.getKey()))
.findFirst()
.ifPresent(t -> t.setUsed(false));
}
// 合并时间段
machine.setAvailability(MergeSegments(machine.getAvailability()));
}
// ========================== MergeSegments(参数与C#一致) ==========================
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;
}
// ========================== GetNextAvailableTime(参数与C#一致) ==========================
private List<GeneDetail> GetNextAvailableTime(
Machine machine, int proposedStartTime, int prevtime,
double processingTime, List<Gene> existingTasks,
boolean IsInterrupt, boolean istask
) {
LocalDateTime startTime = baseTime.plusMinutes(proposedStartTime);
String prevtimeStr = prevtime > -1 ? baseTime.plusMinutes(prevtime).toString() : "";
// 查找最早可用班次
return FindEarliestStart(machine, processingTime, startTime, prevtimeStr, existingTasks, istask);
}
// ========================== FindEarliestStart(参数与C#一致) ==========================
private List<GeneDetail> FindEarliestStart(
Machine machine, double processingTime, LocalDateTime currentTime,
String prevtime, List<Gene> existingTasks, boolean checkprevtime
) {
// 获取设备已有任务
List<Gene> machineTasks = existingTasks.stream()
.filter(t -> t.getMachineId() == machine.getId())
.sorted(Comparator.comparingInt(Gene::getStartTime))
.collect(Collectors.toList());
List<GeneDetail> times = new ArrayList<>();
// 获取当前或下一个班次
TimeSegment slot = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime);
if (slot == null) return times;
// 计算候选时间
LocalDateTime prevTimeDateTime = StringUtils.isEmpty(prevtime) ? null : LocalDateTime.parse(prevtime);
LocalDateTime startCandidate = slot.getStart().isAfter(prevTimeDateTime != null ? prevTimeDateTime : currentTime)
? slot.getStart()
: (prevTimeDateTime != null ? prevTimeDateTime : currentTime);
LocalDateTime endCandidate = startCandidate.plusSeconds((long) processingTime);
// 检查是否容纳
if (endCandidate.isAfter(slot.getEnd())) {
return CaldEarliestStart(machine, processingTime, currentTime, prevtime, machineTasks, checkprevtime);
} else {
// 创建时间详情
GeneDetail time = new GeneDetail();
time.setKey(slot.getKey());
time.setStartTime((int) ChronoUnit.MINUTES.between(baseTime, startCandidate));
time.setEndTime((int) ChronoUnit.MINUTES.between(baseTime, endCandidate));
times.add(time);
// 标记占用
RemoveMachineAvailable(machine, time);
return times;
}
}
// ========================== CaldEarliestStart(参数与C#一致) ==========================
private List<GeneDetail> CaldEarliestStart(
Machine machine, double processingTime, LocalDateTime currentTime,
String prevtime, List<Gene> machineTasks, boolean checkprevtime
) {
int remainingTime =(int) processingTime;
LocalDateTime st = StringUtils.isEmpty(prevtime) ? currentTime : LocalDateTime.parse(prevtime);
LocalDateTime prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
List<GeneDetail> times = new ArrayList<>();
List<GeneDetail> oldTimes = new ArrayList<>();
while (remainingTime > 0) {
TimeSegment shift = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime);
if (shift == null) break;
LocalDateTime shiftStart = shift.getStart();
LocalDateTime shiftEnd = shift.getEnd();
// 检查班次间冲突
if (!prevEnd.isEqual(LocalDateTime.of(2000, 1, 1, 0, 0, 0))) {
if (prevEnd.isBefore(currentTime)) {
// 检查班次间任务
LocalDateTime finalPrevEnd = prevEnd;
boolean hasTask = machineTasks.stream()
.anyMatch(t -> {
LocalDateTime taskStart = baseTime.plusMinutes(t.getStartTime());
return taskStart.isAfter(finalPrevEnd) && taskStart.isBefore(shiftStart);
});
if (hasTask) {
// 重置状态
currentTime = shiftStart;
st = shiftStart;
remainingTime = (int)processingTime;
prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
oldTimes.addAll(deepCopyGeneDetailList(times));
times.clear();
continue;
}
// 检查班次间维修窗口
if (machine.getMaintenanceWindows() != null) {
LocalDateTime finalPrevEnd1 = prevEnd;
boolean hasMaintenance = machine.getMaintenanceWindows().stream()
.anyMatch(w -> w.getStartTime().isAfter(finalPrevEnd1) && w.getStartTime().isBefore(shiftStart));
if (hasMaintenance) {
currentTime = shiftStart;
st = shiftStart;
remainingTime =(int) 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.SECONDS.between(effectiveStart, shiftEnd);
// 处理当前班次
int processable = Math.min(remainingTime, (int) availableMinutes);
remainingTime -= processable;
currentTime = effectiveStart.plusSeconds(processable);
// 添加时间详情
GeneDetail time = new GeneDetail();
time.setKey(shift.getKey());
time.setStartTime((int) ChronoUnit.SECONDS.between(baseTime, effectiveStart));
time.setEndTime((int) ChronoUnit.SECONDS.between(baseTime, currentTime));
times.add(time);
RemoveMachineAvailable(machine, time);
}
// 还原未使用的时间段
AddMachineAvailable(machine, oldTimes);
return times;
}
// ========================== GetCurrentOrNextShift(参数与C#一致) ==========================
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,0);
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;
}
// ========================== PrintScheduleSummary(参数与C#一致) ==========================
public void PrintScheduleSummary(ScheduleChromosome schedule) {
System.out.println("\n=== Schedule Summary ===");
// 按订单分组打印
Map<Integer, List<Gene>> orderGroups = schedule.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
System.out.printf("\nOrder %d Schedule:%n", group.getKey());
// 按工序排序
List<Gene> sortedJobs = group.getValue().stream()
.sorted(Comparator.comparingInt(Gene::getOperationId))
.collect(Collectors.toList());
for (Gene job : sortedJobs) {
Product product = _products.stream()
.filter(p -> p.getId() == job.getProductId())
.findFirst()
.orElse(null);
Operation operation = product != null ? product.getOperations().stream()
.filter(o -> o.getId() == job.getOperationId())
.findFirst()
.orElse(null) : null;
if (product != null && operation != null) {
String print = String.format(
"[%s-%s] Order %d, Product %s, Machine %d, Operation %d, Batch %d, processingTime %d",
ConvertTime(job.getStartTime()),
ConvertTime(job.getEndTime()),
job.getOrderId(),
product.getName(),
job.getMachineId(),
operation.getSequence(),
job.getBatchSize(),
job.getProcessingTime()
);
System.out.println(print);
}
}
}
}
// ========================== WriteScheduleSummary(参数与C#一致) ==========================
public void WriteScheduleSummary(ScheduleChromosome schedule) {
// 写入日志
FileHelper.writeLogFile(String.format("\n=== Schedule Summary === %f", schedule.getFitness()));
FileHelper.writeLogFile(String.format("Makespan: %f minutes", schedule.getObjectiveValues().get(0)));
FileHelper.writeLogFile(String.format("Total Tardiness: %f hours", schedule.getObjectiveValues().get(1)));
FileHelper.writeLogFile(String.format("Setup Time: %f minutes", schedule.getObjectiveValues().get(2)));
FileHelper.writeLogFile(String.format("Flow Time: %f minutes", schedule.getObjectiveValues().get(3)));
FileHelper.writeLogFile(String.format("Machine Load Balance: %.2f%%", schedule.getObjectiveValues().get(4) * 100));
FileHelper.writeLogFile("-------------------------");
// 按订单分组写入
Map<Integer, List<Gene>> orderGroups = schedule.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
List<Gene> sortedJobs = group.getValue().stream()
.sorted(Comparator.comparingInt(Gene::getOperationId))
.collect(Collectors.toList());
for (Gene job : sortedJobs) {
Product product = _products.stream()
.filter(p -> p.getId() == job.getProductId())
.findFirst()
.orElse(null);
Operation operation = product != null ? product.getOperations().stream()
.filter(o -> o.getId() == job.getOperationId())
.findFirst()
.orElse(null) : null;
if (product != null && operation != null) {
StringBuilder sb = new StringBuilder();
sb.append(String.format(
"[%d-%d]:[%s-%s] Order %d, Product %s, Machine %d, Operation %d, Batch %d, processingTime %d",
job.getStartTime(),
job.getEndTime(),
ConvertTime(job.getStartTime()),
ConvertTime(job.getEndTime()),
job.getOrderId(),
product.getName(),
job.getMachineId(),
operation.getSequence(),
job.getBatchSize(),
job.getProcessingTime()
));
// 追加基因详情
for (GeneDetail d : job.getGeneDetails()) {
sb.append(String.format(
"\n\t\t\t\t\t\t\t\t\t\t [%d-%d]:[%s-%s] %d",
d.getStartTime(),
d.getEndTime(),
ConvertTime(d.getStartTime()),
ConvertTime(d.getEndTime()),
d.getEndTime() - d.getStartTime()
));
}
FileHelper.writeLogFile(sb.toString());
}
}
FileHelper.writeLogFile("");
}
}
// ========================== ConvertTime(参数与C#一致) ==========================
private String ConvertTime(int minute) {
return baseTime.plusSeconds(minute).format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm"));
}
// ========================== 深拷贝工具方法(替换C# Mapster,方法名前缀统一为deepCopy) ==========================
private List<Product> deepCopyProductList(List<Product> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyProduct).collect(Collectors.toList());
}
private Product deepCopyProduct(Product source) {
if (source == null) return null;
Product copy = new Product();
copy.setId(source.getId());
copy.setName(source.getName());
copy.setOperations(deepCopyOperationList(source.getOperations()));
return copy;
}
private List<Operation> deepCopyOperationList(List<Operation> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyOperation).collect(Collectors.toList());
}
private Operation deepCopyOperation(Operation source) {
if (source == null) return null;
Operation copy = new Operation();
copy.setId(source.getId());
copy.setProductId(source.getProductId());
copy.setSequence(source.getSequence());
copy.setInterrupt(source.isInterrupt());
copy.setMachineOptions(deepCopyMachineOptionList(source.getMachineOptions()));
return copy;
}
private List<MachineOption> deepCopyMachineOptionList(List<MachineOption> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyMachineOption).collect(Collectors.toList());
}
private MachineOption deepCopyMachineOption(MachineOption source) {
if (source == null) return null;
MachineOption copy = new MachineOption();
copy.setMachineId(source.getMachineId());
copy.setProcessingTime(source.getProcessingTime());
copy.setSetupTime(source.getSetupTime());
return copy;
}
private List<Machine> deepCopyMachineList(List<Machine> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyMachine).collect(Collectors.toList());
}
private Machine deepCopyMachine(Machine source) {
if (source == null) return null;
Machine copy = new Machine();
copy.setId(source.getId());
copy.setName(source.getName());
copy.setEarliestTime(source.getEarliestTime());
copy.setTotalTaskTime(source.getTotalTaskTime());
copy.setShifts(deepCopyShiftList(source.getShifts()));
copy.setMaintenanceWindows(deepCopyMaintenanceWindowList(source.getMaintenanceWindows()));
copy.setAvailability(deepCopyTimeSegmentList(source.getAvailability()));
copy.setShiftsChanged(source.getShiftsChanged());
copy.setMaintenanceWindowsChanged(source.getMaintenanceWindowsChanged());
return copy;
}
private List<Shift> deepCopyShiftList(List<Shift> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyShift).collect(Collectors.toList());
}
private Shift deepCopyShift(Shift source) {
if (source == null) return null;
Shift copy = new Shift();
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
copy.setDays(source.getDays() != null ? new HashSet<>(source.getDays()) : null);
copy.setShiftDate(source.getShiftDate());
copy.setTemporaryShift(source.isTemporaryShift());
copy.setPriority(source.getPriority());
return copy;
}
private List<MaintenanceWindow> deepCopyMaintenanceWindowList(List<MaintenanceWindow> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyMaintenanceWindow).collect(Collectors.toList());
}
private MaintenanceWindow deepCopyMaintenanceWindow(MaintenanceWindow source) {
if (source == null) return null;
MaintenanceWindow copy = new MaintenanceWindow();
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
copy.setReason(source.getReason());
return copy;
}
private List<TimeSegment> deepCopyTimeSegmentList(List<TimeSegment> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyTimeSegment).collect(Collectors.toList());
}
private TimeSegment deepCopyTimeSegment(TimeSegment source) {
if (source == null) return null;
TimeSegment copy = new TimeSegment();
copy.setKey(source.getKey());
copy.setStart(source.getStart());
copy.setEnd(source.getEnd());
copy.setEarliestTime(source.getEarliestTime());
copy.setTotalTaskTime(source.getTotalTaskTime());
copy.setType(source.getType());
copy.setHoliday(source.isHoliday());
copy.setUsed(source.isUsed());
return copy;
}
private List<Order> deepCopyOrderList(List<Order> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyOrder).collect(Collectors.toList());
}
private Order deepCopyOrder(Order source) {
if (source == null) return null;
Order copy = new Order();
copy.setId(source.getId());
copy.setProductId(source.getProductId());
copy.setQuantity(source.getQuantity());
copy.setDueDate(source.getDueDate());
copy.setOrderCompletion(source.getOrderCompletion());
copy.setTardiness(source.getTardiness());
copy.setPriority(source.getPriority());
copy.setCanSplit(source.isCanSplit());
copy.setCanInterrupt(source.isCanInterrupt());
return copy;
}
private List<Gene> deepCopyGeneList(List<Gene> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyGene).collect(Collectors.toList());
}
private Gene deepCopyGene(Gene source) {
if (source == null) return null;
Gene copy = new Gene();
copy.setOrderId(source.getOrderId());
copy.setProductId(source.getProductId());
copy.setOperationId(source.getOperationId());
copy.setMachineId(source.getMachineId());
copy.setBatchSize(source.getBatchSize());
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
copy.setProcessingTime(source.getProcessingTime());
copy.setGeneDetails(deepCopyGeneDetailList(source.getGeneDetails()));
return copy;
}
private List<GeneDetail> deepCopyGeneDetailList(List<GeneDetail> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyGeneDetail).collect(Collectors.toList());
}
private GeneDetail deepCopyGeneDetail(GeneDetail source) {
if (source == null) return null;
GeneDetail copy = new GeneDetail();
copy.setKey(source.getKey());
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
return copy;
}
private ScheduleChromosome deepCopyScheduleChromosome(ScheduleChromosome source) {
if (source == null) return null;
ScheduleChromosome copy = new ScheduleChromosome(source.getBaseTime());
copy.setGenes(deepCopyGeneList(source.getGenes()));
copy.setOrders(deepCopyOrderList(source.getOrders()));
copy.setMachines(deepCopyMachineList(source.getMachines()));
copy.setObjectiveValues(new HashMap<>(source.getObjectiveValues()));
copy.setFitness(source.getFitness());
copy.setTardiness(source.getTardiness());
return copy;
}
// ========================== 辅助类:StringUtils(对应C#字符串工具) ==========================
private static class StringUtils {
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
}
}
\ No newline at end of file
package com.aps.service.plan;
import com.aps.common.util.FileHelper;
import com.aps.entity.basic.ScheduleChromosome;
import com.aps.entity.basic.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
public class AlgorithmScheduler7 {
private final List<Product> _products;
private final List<Machine> _machines;
private final List<Order> _orders;
private final Random _random;
private final MachineSchedulerService _machineScheduler;
private int _populationSize = 100;
private double _crossoverRate = 0.8;
private double _mutationRate = 0.1;
private int _maxGenerations = 500;
private int _elitismCount = 5;
private final LocalDateTime baseTime = LocalDateTime.of(2025, 10, 1, 0, 0, 0);
public AlgorithmScheduler7(List<Product> products, List<Machine> machines, List<Order> orders, MachineSchedulerService machineScheduler) {
this._products = deepCopyProductList(products);
this._machines = deepCopyMachineList(machines);
this._orders = deepCopyOrderList(orders);
this._machineScheduler = machineScheduler;
this._random = new Random();
}
public ScheduleChromosome Run() {
List<ScheduleChromosome> population = InitializePopulation();
ScheduleChromosome bestSolution = null;
ScheduleChromosome currentBest = null;
double bestFitness = Double.MIN_VALUE;
currentBest = population.stream()
.max(Comparator.comparingDouble(ScheduleChromosome::getFitness))
.orElse(null);
if (currentBest != null) {
bestFitness = currentBest.getFitness();
bestSolution = deepCopyScheduleChromosome(currentBest);
}
return bestSolution;
}
public List<ScheduleChromosome> RunAll() {
return InitializePopulation();
}
private List<ScheduleChromosome> InitializePopulation() {
List<ScheduleChromosome> population = new ArrayList<>();
System.out.println("开始初始化种群,目标大小: " + _populationSize);
for (int i = 0; i < _populationSize; i++) {
ScheduleChromosome chromosome = new ScheduleChromosome(baseTime);
chromosome.setGenes(new ArrayList<>());
chromosome.setObjectiveValues(new HashMap<>());
chromosome.setOrders(deepCopyOrderList(_orders));
chromosome.setMachines(deepCopyMachineList(_machines));
// 为每个订单分配工序
for (Order order : _orders) {
Product product = _products.stream()
.filter(p -> p.getId() == order.getProductId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("Product not found: " + order.getProductId()));
int prevtime = 0;
int remainingQuantity =(int) order.getQuantity();
// 订单拆分逻辑 - 增强随机性
while (remainingQuantity > 0) {
Integer batchSize;
if (order.isCanSplit() && remainingQuantity > 1) {
// 修改:增强批次拆分的随机性
int maxSplit = Math.min(remainingQuantity, Math.max(1, remainingQuantity / 2));
batchSize = _random.nextInt(maxSplit) + 1;
} else {
batchSize = remainingQuantity;
}
if (batchSize > remainingQuantity) {
batchSize = remainingQuantity;
}
remainingQuantity -= batchSize;
// 按工序顺序处理
for (Operation operation : product.getOperations().stream()
.sorted(Comparator.comparingInt(Operation::getSequence))
.collect(Collectors.toList())) {
if (operation.getId() > 1) {
Gene prevGene = chromosome.getGenes().stream()
.filter(t -> t.getOrderId() == order.getId() && t.getOperationId() == operation.getId() - 1)
.findFirst()
.orElse(null);
prevtime = prevGene != null ? prevGene.getEndTime() : 0;
}
MachineOption machineOption;
Machine machine;
// 多设备选择逻辑 - 增强随机性
if (operation.getMachineOptions().size() > 1) {
Set<Long> machineIds = operation.getMachineOptions().stream()
.map(MachineOption::getMachineId)
.collect(Collectors.toSet());
// 修改:增强设备选择的随机性
List<Machine> availableMachines = chromosome.getMachines().stream()
.filter(t -> machineIds.contains(t.getId()))
.collect(Collectors.toList());
if (!availableMachines.isEmpty()) {
// 随机打乱后选择
Collections.shuffle(availableMachines, _random);
machine = availableMachines.get(0);
Machine finalMachine = machine;
machineOption = operation.getMachineOptions().stream()
.filter(t -> t.getMachineId() == finalMachine.getId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("MachineOption not found for machine: " + finalMachine.getId()));
} else {
// 回退到原始逻辑
machine = chromosome.getMachines().stream()
.filter(t -> machineIds.contains(t.getId()))
.sorted(Comparator.comparingInt(Machine::getEarliestTime)
.thenComparingDouble(Machine::getTotalTaskTime)
.thenComparing(t -> _random.nextDouble()))
.findFirst()
.orElseThrow(() -> new NoSuchElementException("No available machine for operation: " + operation.getId()));
Machine finalMachine = machine;
machineOption = operation.getMachineOptions().stream()
.filter(t -> t.getMachineId() == finalMachine.getId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("MachineOption not found for machine: " + finalMachine.getId()));
}
} else {
machineOption = operation.getMachineOptions().get(0);
MachineOption finalMachineOption = machineOption;
machine = chromosome.getMachines().stream()
.filter(m -> m.getId() == finalMachineOption.getMachineId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("Machine not found: " + finalMachineOption.getMachineId()));
}
// ==================== 修正的时间计算逻辑开始 ====================
int processingTime =(int)(machineOption.getProcessingTime()*batchSize);
if (machineOption.getContantTime() > 0) {
processingTime =machineOption.getContantTime() ;
}
int preTime = machineOption.getPreTime();
int teardownTime = machineOption.getTeardownTime();
int setupTime = calculateSetupTime(chromosome.getGenes(), order, machine, machineOption);
int effectivePrepTime = Math.max(machineOption.getPreTime(), calculateSetupTime(chromosome.getGenes(), order, machine, machineOption));
// 检查是否是设备上的第一个产品
boolean isFirstProductOnMachine = chromosome.getGenes().stream()
.filter(g -> g.getMachineId() == machine.getId())
.noneMatch(g -> g.getProductId() == order.getProductId());
// 计算调整后的开始时间(使用有效准备时间)
int adjustedPrevTime = calculateAdjustedStartTime(
chromosome.getGenes(), order.getId(), operation.getId(),
effectivePrepTime, prevtime, machine.getId(), isFirstProductOnMachine
);
// 关键修改2:总处理时间 = 有效准备时间(最大值) + 加工时间 + 后处理时间
int totalProcessingDuration =effectivePrepTime+processingTime+teardownTime;
// 关键修改3:调整提议的开始时间,基于有效准备时间提前
int proposedStartTimeWithPreTime = adjustedPrevTime;
if (operation.getId() > 1 && effectivePrepTime > 0) { // 使用有效准备时间判断
// 对于第二道及后续工序,有效准备时间可以提前
Gene lastGeneOnMachine = chromosome.getGenes().stream()
.filter(g -> g.getMachineId() == machine.getId())
.max(Comparator.comparingInt(Gene::getEndTime))
.orElse(null);
int machineEarliestTime = lastGeneOnMachine != null ? lastGeneOnMachine.getEndTime() : 0;
// 提前量基于有效准备时间计算
proposedStartTimeWithPreTime = Math.max(adjustedPrevTime - effectivePrepTime, machineEarliestTime);
}
// 查找可用时间槽(基于有效准备时间的总时长)
List<GeneDetail> geneDetails = GetNextAvailableTime(
machine, proposedStartTimeWithPreTime, -1, totalProcessingDuration,
chromosome.getGenes(), false, true
);
int startTime = geneDetails.stream().mapToInt(GeneDetail::getStartTime).min().orElse(0);
int endTime = geneDetails.stream().mapToInt(GeneDetail::getEndTime).max().orElse(0);
// 验证有效准备时间是否被正确安排
if (operation.getId() > 1 && effectivePrepTime > 0) { // 使用有效准备时间判断
Gene prevOperationGene = chromosome.getGenes().stream()
.filter(g -> g.getOrderId() == order.getId() && g.getOperationId() == operation.getId() - 1)
.findFirst()
.orElse(null);
if (prevOperationGene != null && startTime < prevOperationGene.getEndTime()) {
// 调整提前量,基于有效准备时间
int actualStartTime = Math.max(startTime, prevOperationGene.getEndTime() - effectivePrepTime);
if (actualStartTime != startTime) {
geneDetails = GetNextAvailableTime(
machine, actualStartTime, -1, totalProcessingDuration,
chromosome.getGenes(), false, true
);
startTime = geneDetails.stream().mapToInt(GeneDetail::getStartTime).min().orElse(0);
endTime = geneDetails.stream().mapToInt(GeneDetail::getEndTime).max().orElse(0);
}
}
}
// ==================== 修正的时间计算逻辑结束 ====================
machine.setEarliestTime(endTime);
machine.setTotalTaskTime(machine.getTotalTaskTime() +
processingTime + effectivePrepTime + teardownTime );
Gene gene = new Gene();
gene.setOrderId(order.getId());
gene.setProductId(order.getProductId());
gene.setOperationId(operation.getId());
gene.setMachineId(machineOption.getMachineId());
gene.setBatchSize(batchSize);
gene.setStartTime(startTime); // 包含前处理的开始时间
gene.setEndTime(endTime); // 包含后处理的结束时间
gene.setProcessingTime(processingTime);
gene.setPreTime(preTime);
gene.setSetupTime(setupTime);
gene.setTeardownTime(teardownTime);
gene.setOperationName("工序" + operation.getId());
gene.setGeneDetails((geneDetails));
gene.setId(chromosome.getGenes().size() + 1);
gene.setSequenceId(operation.getSequence());
chromosome.getGenes().add(gene);
int absolutePreparationTime = calculateAbsolutePreparationTime(gene, machineOption);
int absoluteSetupTime =calculateAbsoluteSetupTime(gene, machineOption);
gene.setAbsolutePreparationTime(absolutePreparationTime);
int absoluteStartTime =startTime+Math.max(absolutePreparationTime,absoluteSetupTime);
int absoluteTeardownTime = calculateAbsoluteTeardownTime(gene, machineOption);
int absoluteEndTime = endTime-absoluteTeardownTime;
gene.setAbsoluteStartTime(absoluteStartTime);
gene.setAbsoluteEndTime(absoluteEndTime);
gene.setAbsoluteTeardownTime(absoluteTeardownTime);
// 更新prevtime为当前工序的结束时间
prevtime = endTime;
}
}
}
population.add(chromosome);
}
System.out.println("生成种群大小: " + population.size());
Map<String, List<ScheduleChromosome>> grouped = population.stream()
.collect(Collectors.groupingBy(ScheduleChromosome::getGenesStr));
population = grouped.values().stream()
.map(group -> group.get(0))
.collect(Collectors.toList());
// 调试输出染色体多样性
System.out.println("染色体多样性分析:");
Map<String, Integer> keyCounts = new HashMap<>();
for (ScheduleChromosome chrom : population) {
String key = chrom.getGenesStr();
keyCounts.put(key, keyCounts.getOrDefault(key, 0) + 1);
}
System.out.println("唯一染色体数量: " + keyCounts.size());
// 并行计算适应度
population.parallelStream().forEach(this::CalculateFitness);
// 打印摘要
population.forEach(this::WriteScheduleSummary);
return population;
}
private int calculateSetupTime(List<Gene> existingGenes, Order currentOrder, Machine machine, MachineOption machineOption) {
// 查找设备上最新的任务(按结束时间排序)
Gene lastGeneOnMachine = existingGenes.stream()
.filter(g -> g.getMachineId() == machine.getId())
.max(Comparator.comparingInt(Gene::getEndTime))
.orElse(null);
// 无历史任务 → 换型时间0
if (lastGeneOnMachine == null) {
// System.out.printf("设备%d:无历史任务,换型时间=0%n", machine.getId());
return 0;
}
// 产品不同 → 返回换型时间;产品相同 → 0
int setupTime = (lastGeneOnMachine.getProductId() != currentOrder.getProductId())
? machineOption.getSetupTime()
: 0;
// System.out.printf("设备%d:上一产品ID=%d,当前产品ID=%d,换型时间=%d分钟%n",
// machine.getId(), lastGeneOnMachine.getProductId(), currentOrder.getProductId(), setupTime);
return setupTime;
}
/**
* 计算调整后的开始时间,考虑前处理时间的重叠
*/
private int calculateAdjustedStartTime(List<Gene> existingGenes, int orderId,
int operationId, int preTime, int prevTime,
Long machineId, boolean isFirstProductOnMachine) {
if (preTime <= 0 || operationId == 1) {
return prevTime; // 第一道工序或没有前处理时间,不提前
}
// 查找同一订单的上一个工序
Gene prevOperationGene = existingGenes.stream()
.filter(g -> g.getOrderId() == orderId && g.getOperationId() == operationId - 1)
.max(Comparator.comparingInt(Gene::getEndTime))
.orElse(null);
// 查找设备上一个任务的结束时间
Gene lastGeneOnMachine = existingGenes.stream()
.filter(g -> g.getMachineId() == machineId)
.max(Comparator.comparingInt(Gene::getEndTime))
.orElse(null);
int lastMachineTaskEndTime = lastGeneOnMachine != null ? lastGeneOnMachine.getEndTime() : 0;
if (prevOperationGene != null) {
// 关键修改:允许前处理时间提前开始
// 前处理可以在上一个工序结束前开始,最多提前preTime分钟
int earliestPossibleStart = Math.max(
Math.max(prevTime, lastMachineTaskEndTime),
prevOperationGene.getEndTime() - preTime // 允许提前preTime分钟
);
// 但不能早于上一个工序的开始时间
earliestPossibleStart = Math.max(earliestPossibleStart, prevOperationGene.getStartTime());
return earliestPossibleStart;
}
return Math.max(prevTime, lastMachineTaskEndTime);
}
/**
* 验证时间安排的合理性
*/
/**
* 验证时间安排的合理性
*/
/**
* 验证时间安排的合理性
*/
private boolean validateTimeArrangement(List<Gene> existingGenes, int orderId,
int operationId, int startTime, int endTime,
int preTime, boolean isFirstProductOnMachine) {
// 验证1:开始时间不能小于0
if (startTime < 0) {
return false;
}
// 验证2:第一道工序的前处理时间不能提前
if (operationId == 1 && preTime > 0) {
// 第一道工序的前处理时间应该从开始时间计算,不能提前
// 但需要有足够的时间包含前处理时间
if (endTime - startTime < preTime) {
return false; // 总时间不足以包含前处理时间
}
}
// 验证3:对于非首道工序,前处理时间不能早于上一个工序的开始时间
if (operationId > 1 && preTime > 0) {
Gene prevOperationGene = existingGenes.stream()
.filter(g -> g.getOrderId() == orderId && g.getOperationId() == operationId - 1)
.max(Comparator.comparingInt(Gene::getStartTime))
.orElse(null);
if (prevOperationGene != null) {
// 前处理开始时间不能早于上一个工序的开始时间
if (startTime < prevOperationGene.getStartTime()) {
return false;
}
}
}
// 验证4:结束时间必须大于开始时间
if (endTime <= startTime) {
return false;
}
// 验证5:总时间必须足够包含所有处理时间
int totalRequiredTime = preTime + (endTime - startTime - preTime); // 简化计算
if (endTime - startTime < totalRequiredTime) {
return false;
}
return true;
}
/**
* 计算总处理时间(用于调试和验证)
*/
private int calculateTotalDuration(Gene gene) {
return (gene.getEndTime() - gene.getStartTime()) -
(gene.getPreTime() + gene.getTeardownTime() + gene.getSetupTime());
}
// ==================== 原有方法保持不变 ====================
private List<ScheduleChromosome> removeTrueDuplicates(List<ScheduleChromosome> population) {
Map<String, ScheduleChromosome> uniqueMap = new LinkedHashMap<>();
for (ScheduleChromosome chrom : population) {
String key = generateChromosomeKey(chrom);
uniqueMap.putIfAbsent(key, chrom);
}
return new ArrayList<>(uniqueMap.values());
}
private String generateChromosomeKey(ScheduleChromosome chrom) {
return chrom.getGenes().stream()
.sorted(Comparator.comparing(Gene::getOrderId)
.thenComparing(Gene::getOperationId)
.thenComparing(Gene::getStartTime))
.map(g -> String.format("%d-%d-%d-%d-%d-%d",
g.getOrderId(), g.getOperationId(), g.getMachineId(),
g.getBatchSize(), g.getStartTime(), g.getEndTime()))
.collect(Collectors.joining("|"));
}
private void CalculateFitness(ScheduleChromosome chromosome) {
// 1. 最大完工时间
double makespan = chromosome.getGenes().stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
// 2. 总延迟时间
double tardiness = 0.0;
Map<Integer, List<Gene>> orderGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
int orderCompletion = group.getValue().stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
Order order = _orders.stream()
.filter(o -> o.getId() == group.getKey())
.findFirst()
.orElse(null);
if (order != null) {
LocalDateTime completionTime = chromosome.getBaseTime().plusMinutes(orderCompletion);
LocalDateTime dueDateTime = order.getDueDate();
if (completionTime.isAfter(dueDateTime)) {
long totalMinutes = java.time.temporal.ChronoUnit.MINUTES.between(dueDateTime, completionTime);
double tardinessHours = totalMinutes / 60.0;
order.setTardiness(tardinessHours);
tardiness += tardinessHours;
}
}
}
// 3. 总换型时间
double totalSetupTime = CalculateTotalSetupTime(chromosome);
// 4. 总流程时间
double totalFlowTime = CalculateTotalFlowTime(chromosome);
// 5. 机器负载均衡
double machineLoadBalance = CalculateMachineLoadBalance(chromosome);
// 存储目标值
double finalTardiness = tardiness;
HashMap<Integer, Double> objMap = new HashMap<>();
objMap.put(0, makespan);
objMap.put(1, finalTardiness);
objMap.put(2, totalSetupTime);
objMap.put(3, totalFlowTime);
objMap.put(4, machineLoadBalance);
chromosome.setObjectiveValues(objMap);
// 计算适应度
chromosome.setFitness(
0.4 * (1.0 / (1 + makespan)) +
0.3 * (1.0 / (1 + tardiness)) +
0.1 * (1.0 / (1 + totalSetupTime)) +
0.1 * (1.0 / (1 + totalFlowTime)) +
0.1 * machineLoadBalance
);
}
private double CalculateTotalFlowTime(ScheduleChromosome chromosome) {
double totalFlowTime = 0.0;
Map<Integer, List<Gene>> orderGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
int minStartTime = group.getValue().stream().mapToInt(Gene::getStartTime).min().orElse(0);
int maxEndTime = group.getValue().stream().mapToInt(Gene::getEndTime).max().orElse(0);
LocalDateTime start = chromosome.getBaseTime().plusMinutes(minStartTime);
LocalDateTime end = chromosome.getBaseTime().plusMinutes(maxEndTime);
long hours = ChronoUnit.HOURS.between(start, end);
long minutes = ChronoUnit.MINUTES.between(start, end) % 60;
totalFlowTime += hours + (double) minutes / 60;
}
return totalFlowTime;
}
private double CalculateTotalSetupTime(ScheduleChromosome chromosome) {
double totalSetupTime = 0.0;
double totalTeardownTime = 0.0;
double totalPreTime = 0.0;
Map<Long, List<Gene>> machineGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getMachineId));
for (Map.Entry<Long, List<Gene>> machineGroup : machineGroups.entrySet()) {
List<Gene> sortedGenes = machineGroup.getValue().stream()
.sorted(Comparator.comparingInt(Gene::getStartTime))
.collect(Collectors.toList());
// 累加所有任务的前处理时间和后处理时间
for (Gene gene : sortedGenes) {
totalPreTime += gene.getPreTime();
totalTeardownTime += gene.getTeardownTime();
}
// 计算换型时间(仅在产品变化时触发)
Integer lastProductId = null;
for (Gene gene : sortedGenes) {
if (lastProductId != null && lastProductId != gene.getProductId()) {
Product product = _products.stream()
.filter(p -> p.getId() == gene.getProductId())
.findFirst()
.orElse(null);
if (product == null) continue;
Operation operation = product.getOperations().stream()
.filter(o -> o.getId() == gene.getOperationId())
.findFirst()
.orElse(null);
if (operation == null) continue;
MachineOption machineOption = operation.getMachineOptions().stream()
.filter(m -> m.getMachineId() == gene.getMachineId())
.findFirst()
.orElse(null);
if (machineOption != null) {
totalSetupTime += machineOption.getSetupTime();
}
}
lastProductId = gene.getProductId();
}
}
return totalSetupTime + totalPreTime + totalTeardownTime;
}
private double CalculateMachineLoadBalance(ScheduleChromosome chromosome) {
Map<Long, Double> machineUtilization = new HashMap<>();
int maxEndTime = chromosome.getGenes().stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
if (maxEndTime == 0) return 0.0;
for (Machine machine : _machines) {
double busyTime = chromosome.getGenes().stream()
.filter(g -> g.getMachineId() == machine.getId())
.mapToInt(g -> g.getEndTime() - g.getStartTime())
.sum();
machineUtilization.put(machine.getId(), busyTime / maxEndTime);
}
double avgUtilization = machineUtilization.values().stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
double variance = machineUtilization.values().stream()
.mapToDouble(u -> Math.pow(u - avgUtilization, 2))
.average()
.orElse(0.0);
return 1.0 / (1 + variance);
}
private void RemoveMachineAvailable(Machine machine, GeneDetail geneDetails) {
List<TimeSegment> timeSegments = new ArrayList<>();
int index = machine.getAvailability().stream()
.filter(t -> t.getKey().equals(geneDetails.getKey()))
.findFirst()
.map(machine.getAvailability()::indexOf)
.orElse(-1);
if (index > -1) {
TimeSegment targetSegment = machine.getAvailability().get(index);
LocalDateTime geneEndTime = baseTime.plusMinutes(geneDetails.getEndTime());
if (targetSegment.getEnd().isAfter(geneEndTime)) {
TimeSegment usedSegment = new TimeSegment();
usedSegment.setStart(baseTime.plusMinutes(geneDetails.getStartTime()));
usedSegment.setEnd(geneEndTime);
usedSegment.setHoliday(false);
usedSegment.setKey(UUID.randomUUID().toString());
usedSegment.setType(SegmentType.REGULAR);
usedSegment.setUsed(true);
timeSegments.add(usedSegment);
geneDetails.setKey(usedSegment.getKey());
targetSegment.setStart(geneEndTime);
} else {
targetSegment.setUsed(true);
}
}
if (!timeSegments.isEmpty()) {
machine.getAvailability().addAll(timeSegments);
}
machine.getAvailability().sort(Comparator.comparing(TimeSegment::getStart));
}
private void AddMachineAvailable(Machine machine, List<GeneDetail> geneDetails) {
if (geneDetails == null || geneDetails.isEmpty()) return;
for (GeneDetail detail : geneDetails) {
machine.getAvailability().stream()
.filter(t -> t.getKey().equals(detail.getKey()))
.findFirst()
.ifPresent(t -> t.setUsed(false));
}
machine.setAvailability(MergeSegments(machine.getAvailability()));
}
private List<TimeSegment> MergeSegments(List<TimeSegment> segments) {
List<TimeSegment> maintenanceSegments = segments.stream()
.filter(t -> t.getType() == SegmentType.MAINTENANCE)
.collect(Collectors.toList());
List<TimeSegment> unusedRegularSegments = segments.stream()
.filter(t -> t.getType() != SegmentType.MAINTENANCE && !t.isUsed())
.collect(Collectors.toList());
List<TimeSegment> usedRegularSegments = segments.stream()
.filter(t -> t.getType() != SegmentType.MAINTENANCE && t.isUsed())
.collect(Collectors.toList());
unusedRegularSegments.sort(Comparator.comparing(TimeSegment::getStart));
Deque<TimeSegment> mergedUnused = new ArrayDeque<>();
for (TimeSegment seg : unusedRegularSegments) {
if (!mergedUnused.isEmpty() && mergedUnused.peekLast().getEnd().isAfter(seg.getStart())) {
TimeSegment last = mergedUnused.pollLast();
last.setEnd(last.getEnd().isAfter(seg.getEnd()) ? last.getEnd() : seg.getEnd());
mergedUnused.offerLast(last);
} else {
mergedUnused.offerLast(seg);
}
}
List<TimeSegment> result = new ArrayList<>(mergedUnused);
result.addAll(usedRegularSegments);
result.addAll(maintenanceSegments);
result.sort(Comparator.comparing(TimeSegment::getStart));
return result;
}
private List<GeneDetail> GetNextAvailableTime(
Machine machine, int proposedStartTime, int prevtime,
int processingTime, List<Gene> existingTasks,
boolean IsInterrupt, boolean istask
) {
LocalDateTime startTime = baseTime.plusMinutes(proposedStartTime);
String prevtimeStr = prevtime > -1 ? baseTime.plusMinutes(prevtime).toString() : "";
return FindEarliestStart(machine, processingTime, startTime, prevtimeStr, existingTasks, istask);
}
private List<GeneDetail> FindEarliestStart(
Machine machine, int processingTime, LocalDateTime currentTime,
String prevtime, List<Gene> existingTasks, boolean checkprevtime
) {
List<Gene> machineTasks = existingTasks.stream()
.filter(t -> t.getMachineId() == machine.getId())
.sorted(Comparator.comparingInt(Gene::getStartTime))
.collect(Collectors.toList());
List<GeneDetail> times = new ArrayList<>();
TimeSegment slot = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime);
if (slot == null) return times;
LocalDateTime prevTimeDateTime = StringUtils.isEmpty(prevtime) ? null : LocalDateTime.parse(prevtime);
LocalDateTime startCandidate = slot.getStart().isAfter(prevTimeDateTime != null ? prevTimeDateTime : currentTime)
? slot.getStart()
: (prevTimeDateTime != null ? prevTimeDateTime : currentTime);
LocalDateTime endCandidate = startCandidate.plusSeconds((int)processingTime);
if (endCandidate.isAfter(slot.getEnd())) {
return CaldEarliestStart(machine, processingTime, currentTime, prevtime, machineTasks, checkprevtime);
} else {
GeneDetail time = new GeneDetail();
time.setKey(slot.getKey());
time.setStartTime((int) ChronoUnit.MINUTES.between(baseTime, startCandidate));
time.setEndTime((int) ChronoUnit.MINUTES.between(baseTime, endCandidate));
times.add(time);
RemoveMachineAvailable(machine, time);
return times;
}
}
private List<GeneDetail> CaldEarliestStart(
Machine machine, int processingTime, LocalDateTime currentTime,
String prevtime, List<Gene> machineTasks, boolean checkprevtime
) {
int remainingTime = processingTime;
LocalDateTime st = StringUtils.isEmpty(prevtime) ? currentTime : LocalDateTime.parse(prevtime);
LocalDateTime prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
List<GeneDetail> times = new ArrayList<>();
List<GeneDetail> oldTimes = new ArrayList<>();
while (remainingTime > 0) {
TimeSegment shift = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime);
if (shift == null) break;
LocalDateTime shiftStart = shift.getStart();
LocalDateTime shiftEnd = shift.getEnd();
// 检查班次间冲突
if (!prevEnd.isEqual(LocalDateTime.of(2000, 1, 1, 0, 0, 0))) {
if (prevEnd.isBefore(currentTime)) {
// 检查班次间任务
LocalDateTime finalPrevEnd = prevEnd;
boolean hasTask = machineTasks.stream()
.anyMatch(t -> {
LocalDateTime taskStart = baseTime.plusMinutes(t.getStartTime());
return taskStart.isAfter(finalPrevEnd) && taskStart.isBefore(shiftStart);
});
if (hasTask) {
// 重置状态
currentTime = shiftStart;
st = shiftStart;
remainingTime = processingTime;
prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
oldTimes.addAll(deepCopyGeneDetailList(times));
times.clear();
continue;
}
// 检查班次间维修窗口
if (machine.getMaintenanceWindows() != null) {
LocalDateTime finalPrevEnd1 = prevEnd;
boolean hasMaintenance = machine.getMaintenanceWindows().stream()
.anyMatch(w -> w.getStartTime().isAfter(finalPrevEnd1) && w.getStartTime().isBefore(shiftStart));
if (hasMaintenance) {
currentTime = shiftStart;
st = shiftStart;
remainingTime = processingTime;
prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
times.clear();
continue;
}
}
}
}
prevEnd = shiftEnd;
// 计算有效时间
LocalDateTime effectiveStart = st.isAfter(shiftStart) ? st : shiftStart;
long availableMinutes = ChronoUnit.MINUTES.between(effectiveStart, shiftEnd);
// 处理当前班次
int processable = Math.min(remainingTime, (int) availableMinutes);
remainingTime -= processable;
currentTime = effectiveStart.plusMinutes(processable);
// 添加时间详情
GeneDetail time = new GeneDetail();
time.setKey(shift.getKey());
time.setStartTime((int) ChronoUnit.MINUTES.between(baseTime, effectiveStart));
time.setEndTime((int) ChronoUnit.MINUTES.between(baseTime, currentTime));
times.add(time);
RemoveMachineAvailable(machine, time);
}
// 还原未使用的时间段
AddMachineAvailable(machine, oldTimes);
return times;
}
private TimeSegment GetCurrentOrNextShift(Machine machine, LocalDateTime time, String prevtime, boolean checkprevtime) {
TimeSegment start = null;
// 查找有效班次
start = machine.getAvailability().stream()
.filter(slot -> !slot.isUsed() && slot.getType() != SegmentType.MAINTENANCE)
.filter(slot -> slot.getStart().isAfter(time) || slot.getEnd().isAfter(time))
.findFirst()
.orElse(null);
if (start == null) {
// 生成新时间段
List<TimeSegment> timeSegments = _machineScheduler.generateTimeSegment(machine, time,0);
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;
}
public void PrintScheduleSummary(ScheduleChromosome schedule) {
System.out.println("\n=== Schedule Summary ===");
// 按订单分组打印
Map<Integer, List<Gene>> orderGroups = schedule.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
System.out.printf("\nOrder %d Schedule:%n", group.getKey());
// 按工序排序
List<Gene> sortedJobs = group.getValue().stream()
.sorted(Comparator.comparingInt(Gene::getOperationId))
.collect(Collectors.toList());
for (Gene job : sortedJobs) {
Product product = _products.stream()
.filter(p -> p.getId() == job.getProductId())
.findFirst()
.orElse(null);
Operation operation = product != null ? product.getOperations().stream()
.filter(o -> o.getId() == job.getOperationId())
.findFirst()
.orElse(null) : null;
if (product != null && operation != null) {
String print = String.format(
"[%s-%s] Order %d, Product %s, Machine %d, Operation %d, Batch %d, " +
"processingTime %d, preTime %d, setupTime %d, teardownTime %d, " +
"实际加工时间: %d分钟",
ConvertTime(job.getStartTime()),
ConvertTime(job.getEndTime()),
job.getOrderId(),
product.getName(),
job.getMachineId(),
operation.getSequence(),
job.getBatchSize(),
job.getProcessingTime(),
job.getPreTime(),
job.getSetupTime(),
job.getTeardownTime(),
(job.getEndTime() - job.getStartTime()) -
(job.getPreTime() + job.getTeardownTime() + job.getSetupTime())
);
System.out.println(print);
}
}
}
}
public void WriteScheduleSummary(ScheduleChromosome schedule) {
// 写入日志
FileHelper.writeLogFile(String.format("\n=== Schedule Summary === 适应度: %f", schedule.getFitness()));
FileHelper.writeLogFile(String.format("最大完工时间: %f 分钟", schedule.getObjectiveValues().get(0)));
FileHelper.writeLogFile(String.format("总延迟时间: %f 小时", schedule.getObjectiveValues().get(1)));
FileHelper.writeLogFile(String.format("总换型时间: %f 分钟", schedule.getObjectiveValues().get(2)));
FileHelper.writeLogFile(String.format("总流程时间: %f 分钟", schedule.getObjectiveValues().get(3)));
FileHelper.writeLogFile(String.format("机器负载均衡: %.2f%%", schedule.getObjectiveValues().get(4) * 100));
FileHelper.writeLogFile("-------------------------");
// 按订单分组写入
Map<Integer, List<Gene>> orderGroups = schedule.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
FileHelper.writeLogFile(String.format("\n订单 %d 的调度安排:", group.getKey()));
List<Gene> sortedJobs = group.getValue().stream()
.sorted(Comparator.comparingInt(Gene::getOperationId))
.collect(Collectors.toList());
for (Gene job : sortedJobs) {
Product product = _products.stream()
.filter(p -> p.getId() == job.getProductId())
.findFirst()
.orElse(null);
Operation operation = product != null ? product.getOperations().stream()
.filter(o -> o.getId() == job.getOperationId())
.findFirst()
.orElse(null) : null;
if (product != null && operation != null) {
StringBuilder sb = new StringBuilder();
sb.append(String.format(
"[%d-%d]:[%s-%s] 订单%d, 产品%s, 设备%d, 工序%d, 批次%d, " +
"加工%d分钟, 前处理%d分钟, 换型%d分钟, 后处理%d分钟, 总时长%d分钟",
job.getStartTime(),
job.getEndTime(),
ConvertTime(job.getStartTime()),
ConvertTime(job.getEndTime()),
job.getOrderId(),
product.getName(),
job.getMachineId(),
operation.getSequence(),
job.getBatchSize(),
job.getProcessingTime(),
job.getPreTime(),
job.getSetupTime(),
job.getTeardownTime(),
job.getEndTime() - job.getStartTime()
));
// 追加基因详情
for (GeneDetail d : job.getGeneDetails()) {
sb.append(String.format(
"\n\t\t\t\t\t\t\t\t\t\t [%d-%d]:[%s-%s] 时长%d分钟",
d.getStartTime(),
d.getEndTime(),
ConvertTime(d.getStartTime()),
ConvertTime(d.getEndTime()),
d.getEndTime() - d.getStartTime()
));
}
FileHelper.writeLogFile(sb.toString());
}
}
FileHelper.writeLogFile("");
}
}
private String ConvertTime(int minute) {
return baseTime.plusMinutes(minute).format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm"));
}
// ========================== 深拷贝工具方法 ==========================
private List<Product> deepCopyProductList(List<Product> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyProduct).collect(Collectors.toList());
}
private Product deepCopyProduct(Product source) {
if (source == null) return null;
Product copy = new Product();
copy.setId(source.getId());
copy.setName(source.getName());
copy.setOperations(deepCopyOperationList(source.getOperations()));
return copy;
}
private List<Operation> deepCopyOperationList(List<Operation> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyOperation).collect(Collectors.toList());
}
private Operation deepCopyOperation(Operation source) {
if (source == null) return null;
Operation copy = new Operation();
copy.setId(source.getId());
copy.setProductId(source.getProductId());
copy.setSequence(source.getSequence());
copy.setInterrupt(source.isInterrupt());
copy.setMachineOptions(deepCopyMachineOptionList(source.getMachineOptions()));
return copy;
}
private List<MachineOption> deepCopyMachineOptionList(List<MachineOption> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyMachineOption).collect(Collectors.toList());
}
private MachineOption deepCopyMachineOption(MachineOption source) {
if (source == null) return null;
MachineOption copy = new MachineOption();
copy.setMachineId(source.getMachineId());
copy.setProcessingTime(source.getProcessingTime());
copy.setPreTime(source.getPreTime());
copy.setSetupTime(source.getSetupTime());
copy.setTeardownTime(source.getTeardownTime());
return copy;
}
private List<Machine> deepCopyMachineList(List<Machine> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyMachine).collect(Collectors.toList());
}
private Machine deepCopyMachine(Machine source) {
if (source == null) return null;
Machine copy = new Machine();
copy.setId(source.getId());
copy.setName(source.getName());
copy.setEarliestTime(source.getEarliestTime());
copy.setTotalTaskTime(source.getTotalTaskTime());
copy.setShifts(deepCopyShiftList(source.getShifts()));
copy.setMaintenanceWindows(deepCopyMaintenanceWindowList(source.getMaintenanceWindows()));
copy.setAvailability(deepCopyTimeSegmentList(source.getAvailability()));
copy.setShiftsChanged(source.getShiftsChanged());
copy.setMaintenanceWindowsChanged(source.getMaintenanceWindowsChanged());
return copy;
}
private List<Shift> deepCopyShiftList(List<Shift> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyShift).collect(Collectors.toList());
}
private Shift deepCopyShift(Shift source) {
if (source == null) return null;
Shift copy = new Shift();
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
copy.setDays(source.getDays() != null ? new HashSet<>(source.getDays()) : null);
copy.setShiftDate(source.getShiftDate());
copy.setTemporaryShift(source.isTemporaryShift());
copy.setPriority(source.getPriority());
copy.setStatus(source.getStatus());
copy.setMachineId(source.getMachineId());
copy.setMachineName(source.getMachineName());
return copy;
}
private List<MaintenanceWindow> deepCopyMaintenanceWindowList(List<MaintenanceWindow> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyMaintenanceWindow).collect(Collectors.toList());
}
private MaintenanceWindow deepCopyMaintenanceWindow(MaintenanceWindow source) {
if (source == null) return null;
MaintenanceWindow copy = new MaintenanceWindow();
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
copy.setReason(source.getReason());
return copy;
}
private List<TimeSegment> deepCopyTimeSegmentList(List<TimeSegment> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyTimeSegment).collect(Collectors.toList());
}
private TimeSegment deepCopyTimeSegment(TimeSegment source) {
if (source == null) return null;
TimeSegment copy = new TimeSegment();
copy.setKey(source.getKey());
copy.setStart(source.getStart());
copy.setEnd(source.getEnd());
copy.setEarliestTime(source.getEarliestTime());
copy.setTotalTaskTime(source.getTotalTaskTime());
copy.setType(source.getType());
copy.setHoliday(source.isHoliday());
copy.setUsed(source.isUsed());
return copy;
}
private List<Order> deepCopyOrderList(List<Order> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyOrder).collect(Collectors.toList());
}
private Order deepCopyOrder(Order source) {
if (source == null) return null;
Order copy = new Order();
copy.setId(source.getId());
copy.setProductId(source.getProductId());
copy.setQuantity(source.getQuantity());
copy.setDueDate(source.getDueDate());
copy.setOrderCompletion(source.getOrderCompletion());
copy.setTardiness(source.getTardiness());
copy.setPriority(source.getPriority());
copy.setCanSplit(source.isCanSplit());
copy.setCanInterrupt(source.isCanInterrupt());
return copy;
}
private List<Gene> deepCopyGeneList(List<Gene> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyGene).collect(Collectors.toList());
}
private Gene deepCopyGene(Gene source) {
if (source == null) return null;
Gene copy = new Gene();
copy.setOrderId(source.getOrderId());
copy.setProductId(source.getProductId());
copy.setOperationId(source.getOperationId());
copy.setMachineId(source.getMachineId());
copy.setBatchSize(source.getBatchSize());
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
copy.setProcessingTime(source.getProcessingTime());
copy.setPreTime(source.getPreTime());
copy.setSetupTime(source.getSetupTime());
copy.setTeardownTime(source.getTeardownTime());
copy.setOperationName(source.getOperationName());
copy.setGeneDetails(deepCopyGeneDetailList(source.getGeneDetails()));
return copy;
}
private List<GeneDetail> deepCopyGeneDetailList(List<GeneDetail> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyGeneDetail).collect(Collectors.toList());
}
private GeneDetail deepCopyGeneDetail(GeneDetail source) {
if (source == null) return null;
GeneDetail copy = new GeneDetail();
copy.setKey(source.getKey());
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
return copy;
}
private ScheduleChromosome deepCopyScheduleChromosome(ScheduleChromosome source) {
if (source == null) return null;
ScheduleChromosome copy = new ScheduleChromosome(source.getBaseTime());
copy.setGenes(deepCopyGeneList(source.getGenes()));
copy.setOrders(deepCopyOrderList(source.getOrders()));
copy.setMachines(deepCopyMachineList(source.getMachines()));
copy.setObjectiveValues(new HashMap<>(source.getObjectiveValues()));
copy.setFitness(source.getFitness());
copy.setTardiness(source.getTardiness());
return copy;
}
// ========================== 辅助类:StringUtils ==========================
private static class StringUtils {
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
}
/**
* 计算绝对开始时间(考虑设备日历+前处理+换型时间,跨时段自动拆分)
*/
/**
* 计算绝对开始时间(完善边界处理:结束点自动顺延)
*/
/**
* 计算绝对开始时间(前处理+换型时间不跨设备非工作时段,仅占用当前可用时段)
* 核心:前处理时间最多用到当前可用时段结束点,不顺延到下一个时段
*/
private int calculateAbsoluteStartTime(Gene gene, MachineOption machineOption) {
int startTime = gene.getStartTime(); // 基因开始时间(相对baseTime分钟数)
int setupTime = gene.getSetupTime(); // 换型时间(分钟)
int preTime = gene.getPreTime(); // 前处理时间(分钟)
int totalPrepTime = setupTime + preTime;
if (totalPrepTime <= 0) {
// 无准备时间,直接返回基因开始时间(按日历调整边界)
return adjustTimeByMachineCalendar(gene.getMachineId(), startTime, true);
}
// 获取目标设备(深拷贝,避免污染原状态)
Machine targetMachine = deepCopyMachine(
_machines.stream()
.filter(m -> m.getId() == gene.getMachineId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("Machine not found: " + gene.getMachineId()))
);
// 步骤1:找到基因开始时间所在的“当前可用时段”
LocalDateTime geneStartDateTime = baseTime.plusMinutes(startTime);
TimeSegment currentAvailableSegment = findCurrentAvailableSegment(targetMachine, geneStartDateTime);
if (currentAvailableSegment == null) {
// 无当前可用时段,兜底返回原计算值
int fallbackTime = adjustTimeByMachineCalendar(gene.getMachineId(), startTime, true);
System.out.printf("设备%d:无当前可用时段,兜底绝对开始时间=%d(%s)%n",
targetMachine.getId(), fallbackTime, ConvertTime(fallbackTime));
return fallbackTime;
}
// 步骤2:计算当前可用时段的“剩余时间”(从基因开始时间到时段结束点)
long segmentRemainingMinutes = ChronoUnit.MINUTES.between(
geneStartDateTime,
currentAvailableSegment.getEnd()
);
int usablePrepTime = (int) Math.min(totalPrepTime, segmentRemainingMinutes);
// 步骤3:绝对开始时间 = 基因开始时间 + 实际可用的准备时间(不跨时段)
int absoluteStartTime = startTime + usablePrepTime;
// 步骤4:边界校验(确保不超过当前时段结束点)
LocalDateTime absoluteStartDateTime = baseTime.plusMinutes(absoluteStartTime);
if (absoluteStartDateTime.isAfter(currentAvailableSegment.getEnd())) {
absoluteStartTime = (int) ChronoUnit.MINUTES.between(baseTime, currentAvailableSegment.getEnd());
}
// 步骤5:最终按日历调整(避免落在时段结束点)
absoluteStartTime = adjustTimeByMachineCalendar(gene.getMachineId(), absoluteStartTime, true);
return absoluteStartTime;
}
/**
* 辅助方法:找到指定时间所在的“当前可用时段”(未占用、非维修、包含该时间)
*/
private TimeSegment findCurrentAvailableSegment(Machine machine, LocalDateTime targetTime) {
return machine.getAvailability().stream()
.filter(segment -> !segment.isUsed()) // 未被占用
.filter(segment -> segment.getType() != SegmentType.MAINTENANCE) // 非维修时段
.filter(segment -> {
// 包含目标时间(且目标时间不是时段结束点)
boolean isAfterStart = !targetTime.isBefore(segment.getStart());
boolean isBeforeEnd = targetTime.isBefore(segment.getEnd());
return isAfterStart && isBeforeEnd;
})
.findFirst()
.orElse(null);
}
/**
* 计算绝对结束时间(考虑设备日历+后处理时间)
*/
/**
* 计算绝对结束时间(完善边界处理)
*/
/**
* 计算绝对结束时间(正确逻辑:基因结束时间 - 后处理时间 + 设备日历调整)
* 绝对结束时间 = 加工完成时间(后处理开始前的时间)
*/
private int calculateAbsoluteEndTime(Gene gene, MachineOption machineOption) {
int geneEndTime = gene.getEndTime(); // 基因的结束时间(含后处理,相对baseTime的分钟数)
int teardownTime = gene.getTeardownTime(); // 后处理时间(分钟,加工完成后执行)
// 无后处理时间:绝对结束时间=基因结束时间(直接按日历调整)
if (teardownTime <= 0) {
return adjustTimeByMachineCalendar(gene.getMachineId(), geneEndTime, false);
}
// 核心:加工完成时间 = 基因结束时间 - 后处理时间(后处理在加工完成后执行)
int processCompletionTime = geneEndTime - teardownTime;
// 校验:加工完成时间不能早于基因开始时间(避免逻辑矛盾)
if (processCompletionTime < gene.getStartTime()) {
System.out.printf("设备%d:订单%d工序%d - 后处理时间%d分钟过长,基因结束时间%d < 开始时间%d,兜底为基因开始时间%n",
gene.getMachineId(), gene.getOrderId(), gene.getOperationId(),
teardownTime, geneEndTime, gene.getStartTime());
processCompletionTime = gene.getStartTime();
}
// 按设备日历调整(处理非工作时段、结束点边界)
int absoluteEndTime = adjustTimeByMachineCalendar(gene.getMachineId(), processCompletionTime, false);
// 最终校验:调整后的绝对结束时间不能早于基因开始时间
absoluteEndTime = Math.max(absoluteEndTime, gene.getStartTime());
// 日志输出(清晰展示计算链路)
System.out.printf("设备%d:基因结束时间=%d(%s),后处理%d分钟 → 加工完成时间=%d(%s)→ 调整后绝对结束时间=%d(%s)%n",
gene.getMachineId(),
geneEndTime, ConvertTime(geneEndTime),
teardownTime,
processCompletionTime, ConvertTime(processCompletionTime),
absoluteEndTime, ConvertTime(absoluteEndTime));
return absoluteEndTime;
}
/**
* 根据设备日历调整时间(处理边界情况:时间=时段结束点→顺延到下一个可用时段)
*/
private int adjustTimeByMachineCalendar(Machine machine, int proposedTime, boolean isStartTime) {
LocalDateTime proposedDateTime = baseTime.plusMinutes(proposedTime);
// 查找包含提议时间的可用时段(排除维修时段、已占用时段)
TimeSegment availableSegment = machine.getAvailability().stream()
.filter(segment -> !segment.isUsed())
.filter(segment -> segment.getType() != SegmentType.MAINTENANCE)
.filter(segment -> {
// 关键修改:包含「时段内」但排除「时段结束点」(结束点视为不可用)
boolean isAfterStart = !proposedDateTime.isBefore(segment.getStart());
boolean isBeforeEnd = proposedDateTime.isBefore(segment.getEnd()); // 用isBefore替代!isAfter
return isAfterStart && isBeforeEnd;
})
.findFirst()
.orElse(null);
if (availableSegment != null) {
return proposedTime; // 时间在可用时段内(非结束点),无需调整
}
// 情况1:提议时间是开始时间 → 查找下一个可用时段的开始时间
if (isStartTime) {
TimeSegment nextSegment = machine.getAvailability().stream()
.filter(segment -> !segment.isUsed())
.filter(segment -> segment.getType() != SegmentType.MAINTENANCE)
.filter(segment -> segment.getStart().isAfter(proposedDateTime))
.min(Comparator.comparing(TimeSegment::getStart))
.orElse(null);
if (nextSegment != null) {
int adjustedTime = (int) ChronoUnit.MINUTES.between(baseTime, nextSegment.getStart());
System.out.printf("设备%d:开始时间%d(%s)是时段结束点/非可用时间,顺延到下一个可用时段开始时间%d(%s)%n",
machine.getId(), proposedTime, ConvertTime(proposedTime),
adjustedTime, ConvertTime(adjustedTime));
return adjustedTime;
}
}
// 情况2:提议时间是结束时间 → 查找前一个可用时段的结束时间(避免跨时段)
else {
TimeSegment prevSegment = machine.getAvailability().stream()
.filter(segment -> !segment.isUsed())
.filter(segment -> segment.getType() != SegmentType.MAINTENANCE)
.filter(segment -> segment.getEnd().isBefore(proposedDateTime))
.max(Comparator.comparing(TimeSegment::getEnd))
.orElse(null);
if (prevSegment != null) {
return (int) ChronoUnit.MINUTES.between(baseTime, prevSegment.getEnd());
}
}
// 兜底:无可用时段时返回原时间(理论上不会发生)
return proposedTime;
}
/**
* 按设备ID查询可用时间(复用adjustTimeByMachineCalendar逻辑)
*/
private int adjustTimeByMachineCalendar(Long machineId, int proposedTime, boolean isStartTime) {
Machine machine = _machines.stream()
.filter(m -> m.getId() == machineId)
.findFirst()
.orElse(null);
if (machine == null) {
return proposedTime;
}
return adjustTimeByMachineCalendar(machine, proposedTime, isStartTime);
}
/**
* 计算绝对准备时间(考虑设备日历,跳过非工作时间)
*/
private int calculateAbsolutePreparationTime(Gene gene, MachineOption machineOption) {
int totalPreparationTime = gene.getPreTime(); // 总准备时间
if (totalPreparationTime <= 0) {
return 0; // 没有准备时间
}
// 获取设备
Machine machine = _machines.stream()
.filter(m -> m.getId() == gene.getMachineId())
.findFirst()
.orElse(null);
if (machine == null) {
return totalPreparationTime; // 找不到设备,返回原始时间
}
// 开始时间(基因开始时间)
int startTime = gene.getStartTime();
LocalDateTime currentTime = baseTime.plusMinutes(startTime);
// 计算考虑日历的实际准备时间
return calculateCalendarAdjustedTime(machine, currentTime, totalPreparationTime);
}
/**
* 计算绝对换型时间(考虑设备日历,跳过非工作时间)
*/
private int calculateAbsoluteSetupTime(Gene gene, MachineOption machineOption) {
int totalPreparationTime = gene.getSetupTime(); // 总准备时间
if (totalPreparationTime <= 0) {
return 0; // 没有准备时间
}
// 获取设备
Machine machine = _machines.stream()
.filter(m -> m.getId() == gene.getMachineId())
.findFirst()
.orElse(null);
if (machine == null) {
return totalPreparationTime; // 找不到设备,返回原始时间
}
// 开始时间(基因开始时间)
int startTime = gene.getStartTime();
LocalDateTime currentTime = baseTime.plusMinutes(startTime);
// 计算考虑日历的实际准备时间
return calculateCalendarAdjustedTime(machine, currentTime, totalPreparationTime);
}
/**
* 计算考虑设备日历的时间(跳过非工作时间)
*/
private int calculateCalendarAdjustedTime(Machine machine, LocalDateTime startTime, int requiredMinutes) {
LocalDateTime currentTime = startTime;
int remainingMinutes = requiredMinutes;
int totalElapsedMinutes = 0; // 实际经过的分钟数(包含非工作时间)
System.out.printf(" 开始计算: %s, 需要%d分钟%n",
startTime.format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm")),
requiredMinutes);
while (remainingMinutes > 0) {
// 查找包含当前时间的可用时间段
TimeSegment currentSegment = findAvailableSegmentAtTime(machine, currentTime);
if (currentSegment == null) {
// 没有找到可用时间段,查找下一个可用时间段
TimeSegment nextSegment = findNextAvailableSegment(machine, currentTime);
if (nextSegment == null) {
System.out.printf(" 警告: 找不到后续可用时间段,剩余%d分钟无法安排%n", remainingMinutes);
break; // 无法安排剩余时间
}
// 跳到下一个可用时间段的开始
long jumpMinutes = ChronoUnit.MINUTES.between(currentTime, nextSegment.getStart());
totalElapsedMinutes += (int) jumpMinutes;
currentTime = nextSegment.getStart();
System.out.printf(" 跳过非工作时间: 跳转%d分钟到 %s%n",
jumpMinutes, currentTime.format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm")));
continue;
}
// 计算当前时间段可用的分钟数
long availableMinutesInSegment = ChronoUnit.MINUTES.between(currentTime, currentSegment.getEnd());
int processableMinutes = (int) Math.min(remainingMinutes, availableMinutesInSegment);
// 处理当前时间段
remainingMinutes -= processableMinutes;
totalElapsedMinutes += processableMinutes;
currentTime = currentTime.plusMinutes(processableMinutes);
System.out.printf(" 在当前时间段处理%d分钟: %s → %s, 剩余%d分钟%n",
processableMinutes,
currentTime.minusMinutes(processableMinutes).format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm")),
currentTime.format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm")),
remainingMinutes);
// 如果当前时间段用完但还有剩余时间,移动到时间段结束点
if (remainingMinutes > 0 && currentTime.equals(currentSegment.getEnd())) {
// 查找下一个时间段
TimeSegment nextSegment = findNextAvailableSegment(machine, currentTime);
if (nextSegment == null) {
System.out.printf(" 警告: 当前时间段用完,但找不到后续时间段,剩余%d分钟无法安排%n", remainingMinutes);
break;
}
// 跳到下一个时间段的开始
long jumpMinutes = ChronoUnit.MINUTES.between(currentTime, nextSegment.getStart());
totalElapsedMinutes += (int) jumpMinutes;
currentTime = nextSegment.getStart();
System.out.printf(" 时间段结束,跳转到下一个: 跳转%d分钟到 %s%n",
jumpMinutes, currentTime.format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm")));
}
}
System.out.printf(" 计算完成: 原始需要%d分钟,实际经过%d分钟%n", requiredMinutes, totalElapsedMinutes);
return totalElapsedMinutes;
}
/**
* 查找包含指定时间的可用时间段
*/
private TimeSegment findAvailableSegmentAtTime(Machine machine, LocalDateTime time) {
return machine.getAvailability().stream()
.filter(segment -> !segment.isUsed())
.filter(segment -> segment.getType() != SegmentType.MAINTENANCE)
.filter(segment ->
!segment.getStart().isAfter(time) &&
segment.getEnd().isAfter(time))
.findFirst()
.orElse(null);
}
/**
* 查找下一个可用时间段
*/
private TimeSegment findNextAvailableSegment(Machine machine, LocalDateTime afterTime) {
return machine.getAvailability().stream()
.filter(segment -> !segment.isUsed())
.filter(segment -> segment.getType() != SegmentType.MAINTENANCE)
.filter(segment -> segment.getStart().isAfter(afterTime))
.min(Comparator.comparing(TimeSegment::getStart))
.orElse(null);
}
/**
* 反向查找:找到包含指定时间的可用工作时段(用于向前追溯)
*/
private TimeSegment findAvailableSegmentAtTimeReverse(Machine machine, LocalDateTime time) {
return machine.getAvailability().stream()
.filter(segment -> !segment.isUsed())
.filter(segment -> segment.getType() != SegmentType.MAINTENANCE)
.filter(segment -> !segment.isHoliday())
.filter(segment -> {
boolean isAfterStart = !time.isBefore(segment.getStart());
boolean isBeforeEnd = time.isBefore(segment.getEnd());
return isAfterStart && isBeforeEnd;
})
.findFirst()
.orElse(null);
}
/**
* 查找前一个可用时间段
*/
private TimeSegment findPreviousAvailableSegment(Machine machine, LocalDateTime beforeTime) {
return machine.getAvailability().stream()
.filter(segment -> !segment.isUsed())
.filter(segment -> segment.getType() != SegmentType.MAINTENANCE)
.filter(segment -> segment.getEnd().isBefore(beforeTime))
.max(Comparator.comparing(TimeSegment::getEnd))
.orElse(null);
}
/**
* 计算绝对后处理时间(从结束时间向前计算,考虑设备日历,跳过非工作时间)
* 后处理时间是从加工完成时间向前安排的
*/
private int calculateAbsoluteTeardownTime(Gene gene, MachineOption machineOption) {
int teardownTime = gene.getTeardownTime(); // 后处理时间
if (teardownTime <= 0) {
return 0; // 没有后处理时间
}
// 获取设备
Machine machine = _machines.stream()
.filter(m -> m.getId() == gene.getMachineId())
.findFirst()
.orElse(null);
if (machine == null) {
return teardownTime; // 找不到设备,返回原始时间
}
// 后处理结束时间 = 基因结束时间(包含后处理的时间)
int endTime = gene.getEndTime();
LocalDateTime endDateTime = baseTime.plusMinutes(endTime);
System.out.printf("计算绝对后处理时间 - 设备%d: 结束时间=%d (%s), 后处理时间=%d分钟%n",
machine.getId(), endTime, ConvertTime(endTime), teardownTime);
// 计算考虑日历的实际后处理时间(向前计算)
return calculateCalendarAdjustedTimeBackward(machine, endDateTime, teardownTime);
}
/**
* 向后计算考虑设备日历的时间(从结束时间向前回溯,跳过非工作时间)
*/
private int calculateCalendarAdjustedTimeBackward(Machine machine, LocalDateTime endTime, int requiredMinutes) {
LocalDateTime currentTime = endTime;
int remainingMinutes = requiredMinutes;
int totalElapsedMinutes = 0; // 实际经过的分钟数(包含非工作时间)
System.out.printf(" 开始向后计算后处理时间: 结束时间=%s, 需要%d分钟%n",
endTime.format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm")),
requiredMinutes);
while (remainingMinutes > 0) {
// 查找包含当前时间的可用时间段(向前查找)
TimeSegment currentSegment = findAvailableSegmentAtTimeBackward(machine, currentTime);
if (currentSegment == null) {
// 没有找到可用时间段,查找前一个可用时间段
TimeSegment prevSegment = findPreviousAvailableSegment(machine, currentTime);
if (prevSegment == null) {
System.out.printf(" 警告: 找不到前续可用时间段,剩余%d分钟无法安排%n", remainingMinutes);
break; // 无法安排剩余时间
}
// 跳到前一个可用时间段的结束时间
long jumpMinutes = ChronoUnit.MINUTES.between(prevSegment.getEnd(), currentTime);
totalElapsedMinutes += (int) jumpMinutes;
currentTime = prevSegment.getEnd();
System.out.printf(" 跳过非工作时间: 向后跳转%d分钟到 %s%n",
jumpMinutes, currentTime.format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm")));
continue;
}
// 计算当前时间段可用的分钟数(向前计算)
long availableMinutesInSegment = ChronoUnit.MINUTES.between(currentSegment.getStart(), currentTime);
int processableMinutes = (int) Math.min(remainingMinutes, availableMinutesInSegment);
// 处理当前时间段(向前移动)
remainingMinutes -= processableMinutes;
totalElapsedMinutes += processableMinutes;
currentTime = currentTime.minusMinutes(processableMinutes);
System.out.printf(" 在当前时间段处理%d分钟: %s ← %s, 剩余%d分钟%n",
processableMinutes,
currentTime.format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm")),
currentTime.plusMinutes(processableMinutes).format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm")),
remainingMinutes);
// 如果当前时间段用完但还有剩余时间,移动到时间段开始点
if (remainingMinutes > 0 && currentTime.equals(currentSegment.getStart())) {
// 查找前一个时间段
TimeSegment prevSegment = findPreviousAvailableSegment(machine, currentTime);
if (prevSegment == null) {
System.out.printf(" 警告: 当前时间段用完,但找不到前续时间段,剩余%d分钟无法安排%n", remainingMinutes);
break;
}
// 跳到前一个时间段的结束时间
long jumpMinutes = ChronoUnit.MINUTES.between(prevSegment.getEnd(), currentTime);
totalElapsedMinutes += (int) jumpMinutes;
currentTime = prevSegment.getEnd();
System.out.printf(" 时间段开始,跳转到前一个: 向后跳转%d分钟到 %s%n",
jumpMinutes, currentTime.format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm")));
}
}
System.out.printf(" 后处理时间计算完成: 原始需要%d分钟,实际经过%d分钟%n", requiredMinutes, totalElapsedMinutes);
return totalElapsedMinutes;
}
/**
* 向后查找包含指定时间的可用时间段
*/
private TimeSegment findAvailableSegmentAtTimeBackward(Machine machine, LocalDateTime time) {
return machine.getAvailability().stream()
.filter(segment -> !segment.isUsed())
.filter(segment -> segment.getType() != SegmentType.MAINTENANCE)
.filter(segment -> {
// 包含目标时间(且目标时间不是时段开始点)
boolean isAfterStart = time.isAfter(segment.getStart());
boolean isBeforeEnd = !time.isAfter(segment.getEnd()); // 用!isAfter替代isBefore
return isAfterStart && isBeforeEnd;
})
.findFirst()
.orElse(null);
}
}
\ No newline at end of file
package com.aps.service.plan;
import com.aps.common.util.FileHelper;
import com.aps.entity.basic.*;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
public class AlgorithmScheduler8 {
private final List<Product> _products;
private final List<Machine> _machines;
private final List<Order> _orders;
private final Random _random;
private final MachineSchedulerService _machineScheduler;
private int _populationSize = 100;
private double _crossoverRate = 0.8;
private double _mutationRate = 0.1;
private int _maxGenerations = 500;
private int _elitismCount = 5;
private final LocalDateTime baseTime = LocalDateTime.of(2025, 10, 1, 0, 0, 0);
// 是否平滑换型参数
private boolean _smoothSetup = false; // 默认true,不占用设备时长
public AlgorithmScheduler8(List<Product> products, List<Machine> machines, List<Order> orders,
MachineSchedulerService machineScheduler) {
this(products, machines, orders, machineScheduler, false);
}
public AlgorithmScheduler8(List<Product> products, List<Machine> machines, List<Order> orders,
MachineSchedulerService machineScheduler, boolean smoothSetup) {
this._products = deepCopyProductList(products);
this._machines = deepCopyMachineList(machines);
this._orders = deepCopyOrderList(orders);
this._machineScheduler = machineScheduler;
this._random = new Random();
}
public void setSmoothSetup(boolean smoothSetup) {
this._smoothSetup = smoothSetup;
}
public boolean isSmoothSetup() {
return _smoothSetup;
}
public ScheduleChromosome Run() {
List<ScheduleChromosome> population = InitializePopulation();
ScheduleChromosome bestSolution = null;
ScheduleChromosome currentBest = null;
double bestFitness = Double.MIN_VALUE;
currentBest = population.stream()
.max(Comparator.comparingDouble(ScheduleChromosome::getFitness))
.orElse(null);
if (currentBest != null) {
bestFitness = currentBest.getFitness();
bestSolution = deepCopyScheduleChromosome(currentBest);
}
return bestSolution;
}
public List<ScheduleChromosome> RunAll() {
List<ScheduleChromosome> scheduleChromosomes = InitializePopulation();
return scheduleChromosomes;
}
private List<ScheduleChromosome> InitializePopulation() {
List<ScheduleChromosome> population = new ArrayList<>();
System.out.println("开始初始化种群,目标大小: " + _populationSize);
System.out.println("换型模式: " + (_smoothSetup ? "平滑模式(换型不占用设备时长)" : "标准模式(换型占用设备时长)"));
for (int i = 0; i < _populationSize; i++) {
ScheduleChromosome chromosome = new ScheduleChromosome(baseTime);
chromosome.setGenes(new ArrayList<>());
chromosome.setObjectiveValues(new HashMap<>());
chromosome.setOrders(deepCopyOrderList(_orders));
chromosome.setMachines(deepCopyMachineList(_machines));
// Reset machine states for each new chromosome
for (Machine machine : chromosome.getMachines()) {
machine.setEarliestTime(0);
machine.setTotalTaskTime(0);
}
System.out.println("=== 初始化染色体 " + (i + 1) + " ===");
// 为每个订单分配工序
for (Order order : _orders) {
Product product = _products.stream()
.filter(p -> p.getId()==order.getProductId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("Product not found: " + order.getProductId()));
int remainingQuantity = (int) order.getQuantity();
System.out.println("处理订单 " + order.getId() + ", 产品 " + product.getId() + ", 数量: " + remainingQuantity);
// 订单拆分逻辑
while (remainingQuantity > 0) {
int batchSize;
if (order.isCanSplit() && remainingQuantity > 1) {
int maxSplit = Math.min(remainingQuantity, Math.max(1, remainingQuantity / 2));
batchSize = _random.nextInt(maxSplit) + 1;
} else {
batchSize = remainingQuantity;
}
if (batchSize > remainingQuantity) {
batchSize = remainingQuantity;
}
remainingQuantity -= batchSize;
System.out.println(" 批次大小: " + batchSize + ", 剩余数量: " + remainingQuantity);
// 为当前批次记录每道工序的结束时间(包含后处理时间)
Map<Integer, Integer> operationEndTimes = new HashMap<>();
// 按工序顺序处理当前批次
List<Operation> sortedOperations = product.getOperations().stream()
.sorted(Comparator.comparingInt(Operation::getSequence))
.collect(Collectors.toList());
for (Operation operation : sortedOperations) {
System.out.println(" 处理工序 " + operation.getId() + " (序列: " + operation.getSequence() + ")");
// 获取前一道工序的结束时间(包含后处理时间)
int prevOperationEndTime = operation.getId() > 1 ?
operationEndTimes.getOrDefault(operation.getId() - 1, 0) : 0;
System.out.println(" 前序工序结束时间(含后处理): " + prevOperationEndTime);
MachineOption machineOption;
Machine machine;
// 多设备选择逻辑
if (operation.getMachineOptions().size() > 1) {
Set<Long> machineIds = operation.getMachineOptions().stream()
.map(MachineOption::getMachineId)
.collect(Collectors.toSet());
List<Machine> availableMachines = chromosome.getMachines().stream()
.filter(t -> machineIds.contains(t.getId()))
.collect(Collectors.toList());
if (!availableMachines.isEmpty()) {
Collections.shuffle(availableMachines, _random);
machine = availableMachines.get(0);
Machine finalMachine = machine;
machineOption = operation.getMachineOptions().stream()
.filter(t -> t.getMachineId() == finalMachine.getId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("MachineOption not found for machine: " + finalMachine.getId()));
} else {
machine = chromosome.getMachines().stream()
.filter(t -> machineIds.contains(t.getId()))
.sorted(Comparator.comparingInt(Machine::getEarliestTime)
.thenComparingDouble(Machine::getTotalTaskTime)
.thenComparing(t -> _random.nextDouble()))
.findFirst()
.orElseThrow(() -> new NoSuchElementException("No available machine for operation: " + operation.getId()));
Machine finalMachine = machine;
machineOption = operation.getMachineOptions().stream()
.filter(t -> t.getMachineId() == finalMachine.getId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("MachineOption not found for machine: " + finalMachine.getId()));
}
} else {
machineOption = operation.getMachineOptions().get(0);
MachineOption finalMachineOption = machineOption;
machine = chromosome.getMachines().stream()
.filter(m -> m.getId() == finalMachineOption.getMachineId())
.findFirst()
.orElseThrow(() -> new NoSuchElementException("Machine not found: " + finalMachineOption.getMachineId()));
}
System.out.println(" 选择设备: " + machine.getId() + ", 机器选项: " + machineOption.getMachineId());
int processingTime = (int) (machineOption.getProcessingTime() * batchSize);
int teardownTime = machineOption.getTeardownTime();
int preTime = machineOption.getPreTime();
int setupTime = calculateSetupTime(chromosome.getGenes(), order, machine, machineOption);
System.out.println(" 处理时间: " + processingTime + ", 后处理: " + teardownTime +
", 前处理: " + preTime + ", 换型: " + setupTime);
// 根据换型模式计算总处理时间
int totalProcessingTime;
if (_smoothSetup) {
// 平滑模式:换型不占用设备时长
totalProcessingTime = processingTime; // 只计算主处理时间
System.out.println(" 平滑模式:换型时间 " + setupTime + " 分钟不占用设备时长");
} else {
// 标准模式:换型占用设备时长
totalProcessingTime = processingTime + setupTime; // 主处理+换型时间
System.out.println(" 标准模式:换型时间 " + setupTime + " 分钟占用设备时长");
}
// 前处理和后处理都不占用设备时长,只在工序间协调时考虑
System.out.println(" 前处理 " + preTime + " 分钟和后处理 " + teardownTime + " 分钟在非工作时间进行");
// 确定任务的最早开始时间(基于前一道工序的完整结束时间,包含后处理)
int earliestStartTime = prevOperationEndTime;
// 检查设备上是否有前一个任务
Gene lastGeneOnMachine = chromosome.getGenes().stream()
.filter(g -> g.getMachineId() == machine.getId())
.max(Comparator.comparingInt(Gene::getEndTime))
.orElse(null);
if (lastGeneOnMachine != null) {
// 设备上已有任务,当前任务必须在设备可用后开始
// 设备可用时间 = 前一个任务的主处理结束时间(后处理不占用设备)
int machineAvailableTime = lastGeneOnMachine.getEndTime();
if (setupTime > 0) {
if (_smoothSetup) {
machineAvailableTime += setupTime;
// 平滑模式:换型在非工作时间进行,不额外占用设备时间
System.out.println(" 平滑模式换型:在非工作时间进行,设备可用时间不变");
} else {
// 标准模式:换型需要额外占用设备时间
machineAvailableTime += setupTime;
System.out.println(" 标准模式换型:需要额外占用设备 " + setupTime + " 分钟");
}
}
earliestStartTime = Math.max(earliestStartTime, machineAvailableTime);
}
System.out.println(" 最终最早开始时间: " + earliestStartTime +
" (前序工序结束含后处理: " + prevOperationEndTime + ", 设备可用: " +
(lastGeneOnMachine != null ? lastGeneOnMachine.getEndTime() : 0) +
", 换型: " + setupTime + ")");
// 根据换型模式调整处理时间
int processingTimeForScheduling;
if (_smoothSetup) {
// 平滑模式:只需要安排主处理时间
processingTimeForScheduling = processingTime;
System.out.println(" 平滑模式:安排主处理时间 " + processingTime + " 分钟");
} else {
// 标准模式:需要安排主处理时间+换型时间
processingTimeForScheduling = processingTime + setupTime;
System.out.println(" 标准模式:安排主处理+" + setupTime + "分钟换型=" + processingTimeForScheduling + "分钟");
}
// 获取可用时间段(只考虑主处理和换型时间)
List<GeneDetail> geneDetails = GetNextAvailableTime(
machine, earliestStartTime, -1, processingTimeForScheduling,
chromosome.getGenes(), false, true
);
if (geneDetails.isEmpty()) {
System.out.println(" ⚠️ 未找到可用时间段,尝试调整时间");
// 尝试在当前时间基础上增加一些时间重新查找
geneDetails = GetNextAvailableTime(
machine, earliestStartTime + 60, -1, processingTimeForScheduling,
chromosome.getGenes(), false, true
);
}
int startTime = geneDetails.stream().mapToInt(GeneDetail::getStartTime).min().orElse(0);
int endTime = geneDetails.stream().mapToInt(GeneDetail::getEndTime).max().orElse(0);
if (startTime == 0 && endTime == 0) {
System.out.println(" ❌ 无法安排任务,跳过此工序");
continue;
}
System.out.println(" 安排时间: " + ConvertTime(startTime) + " - " + ConvertTime(endTime) +
" (" + (endTime - startTime) + "分钟)");
// 冲突检测和解决
final int finalStartTime = startTime;
final int finalEndTime = endTime;
Gene conflictingGene = chromosome.getGenes().stream()
.filter(g -> g.getMachineId() == machine.getId())
.filter(g -> (finalStartTime < g.getEndTime() && finalEndTime > g.getStartTime()))
.findFirst()
.orElse(null);
if (conflictingGene != null) {
System.out.println(" ⚠️ 检测到时间冲突,重新调度");
int conflictSetupStartTime = conflictingGene.getEndTime();
int conflictSetupTime = calculateSetupTimeForConflict(chromosome.getGenes(), order, machine, machineOption, conflictingGene);
int conflictEarliestStartTime = conflictSetupStartTime + conflictSetupTime;
geneDetails = GetNextAvailableTime(
machine, conflictEarliestStartTime, -1, processingTimeForScheduling,
chromosome.getGenes(), false, true
);
if (!geneDetails.isEmpty()) {
startTime = geneDetails.stream().mapToInt(GeneDetail::getStartTime).min().orElse(0);
endTime = geneDetails.stream().mapToInt(GeneDetail::getEndTime).max().orElse(0);
System.out.println(" 重新安排时间: " + ConvertTime(startTime) + " - " + ConvertTime(endTime));
}
}
// 记录当前工序的结束时间(包含后处理时间)
int currentOperationEndTime = endTime + teardownTime;
operationEndTimes.put(operation.getId(), currentOperationEndTime);
System.out.println(" 当前工序结束时间(含后处理): " + currentOperationEndTime +
" (主处理结束: " + endTime + ", 后处理: " + teardownTime + ")");
// 更新设备状态(后处理不占用设备,所以设备可用时间只到主处理结束)
int machineAvailableTime = endTime;
machine.setEarliestTime(machineAvailableTime);
machine.setTotalTaskTime(machine.getTotalTaskTime() + totalProcessingTime);
System.out.println(" 设备 " + machine.getId() + " 下次可用时间: " + machineAvailableTime);
// 创建基因
Gene gene = new Gene();
// gene.setOrderId(order.getId());
gene.setProductId(order.getProductId());
gene.setOperationId(operation.getId());
gene.setMachineId(machine.getId());
gene.setBatchSize(batchSize);
gene.setSetupTime(setupTime);
gene.setStartTime(startTime);
gene.setEndTime(endTime);
gene.setProcessingTime(processingTime);
gene.setTeardownTime(teardownTime);
gene.setPreTime(preTime);
gene.setGeneDetails(deepCopyGeneDetailList(geneDetails));
gene.setId(chromosome.getGenes().size() + 1);
gene.setSequenceId(operation.getSequence());
// 关键修复:根据换型模式设置开始时间
if (!_smoothSetup && setupTime>0) {
// 标准模式:换型占用工作时间,开始时间不包含换型时间
gene.setAbsoluteSetupTime(calculateAbsoluteSetupTime(gene,machine));
gene.setAbsoluteStartTime(calculateMainProcessingStartTime(machine,startTime, calculateAbsoluteSetupTime(gene,machine),geneDetails) );
}else {
gene.setAbsoluteStartTime(startTime);
gene.setAbsoluteSetupTime(setupTime);
}
chromosome.getGenes().add(gene);
System.out.println(" ✅ 工序 " + operation.getId() + " 安排完成");
}
System.out.println(" ✅ 批次 " + batchSize + " 处理完成");
}
}
population.add(chromosome);
System.out.println("✅ 染色体 " + (i + 1) + " 初始化完成,基因数量: " + chromosome.getGenes().size());
}
System.out.println("生成种群大小: " + population.size());
// 去重逻辑
Map<String, List<ScheduleChromosome>> grouped = population.stream()
.collect(Collectors.groupingBy(ScheduleChromosome::getGenesStr));
population = grouped.values().stream()
.map(group -> group.get(0))
.collect(Collectors.toList());
System.out.println("去重后种群大小: " + population.size());
// 调试输出染色体多样性
System.out.println("染色体多样性分析:");
Map<String, Integer> keyCounts = new HashMap<>();
for (ScheduleChromosome chrom : population) {
String key = chrom.getGenesStr();
keyCounts.put(key, keyCounts.getOrDefault(key, 0) + 1);
}
System.out.println("唯一染色体数量: " + keyCounts.size());
System.out.println("染色体分布: " + keyCounts);
// 并行计算适应度
population.parallelStream().forEach(this::CalculateFitness);
// 打印摘要
population.forEach(this::WriteScheduleSummary);
return population;
}
private void CalculateFitness(ScheduleChromosome chromosome) {
// 1. 最大完工时间
double makespan = chromosome.getGenes().stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
// 2. 总延迟时间
double tardiness = 0.0;
Map<Integer, List<Gene>> orderGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
int orderCompletion = group.getValue().stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
// Order order = _orders.stream()
// .filter(o -> o.getId() == group.getKey())
// .findFirst()
// .orElse(null);
//
// if (order != null) {
// LocalDateTime completionTime = chromosome.getBaseTime().plusMinutes(orderCompletion);
// LocalDateTime dueDateTime = order.getDueDate();
//
// if (completionTime.isAfter(dueDateTime)) {
// long hours = ChronoUnit.HOURS.between(dueDateTime, completionTime);
// long minutes = ChronoUnit.MINUTES.between(dueDateTime, completionTime) % 60;
// tardiness += hours + (double) minutes / 60;
// }
// }
}
// 3. 总换型时间
double totalSetupTime = CalculateTotalSetupTime(chromosome);
// 4. 总流程时间
double totalFlowTime = CalculateTotalFlowTime(chromosome);
// 5. 机器负载均衡
double machineLoadBalance = CalculateMachineLoadBalance(chromosome);
// 存储目标值
HashMap<Integer, Double> objMap = new HashMap<>();
objMap.put(0, makespan);
objMap.put(1, tardiness);
objMap.put(2, totalSetupTime);
objMap.put(3, totalFlowTime);
objMap.put(4, machineLoadBalance);
chromosome.setObjectiveValues(objMap);
// 计算适应度
chromosome.setFitness(
0.4 * (1.0 / (1 + makespan)) +
0.3 * (1.0 / (1 + tardiness)) +
0.1 * (1.0 / (1 + totalSetupTime)) +
0.1 * (1.0 / (1 + totalFlowTime)) +
0.1 * machineLoadBalance
);
}
private double CalculateTotalSetupTime(ScheduleChromosome chromosome) {
double totalSetupTime = 0.0;
Map<Long, List<Gene>> machineGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getMachineId));
for (Map.Entry<Long, List<Gene>> machineGroup : machineGroups.entrySet()) {
List<Gene> sortedGenes = machineGroup.getValue().stream()
.sorted(Comparator.comparingInt(Gene::getStartTime))
.collect(Collectors.toList());
Integer lastProductId = null;
for (Gene gene : sortedGenes) {
if (lastProductId != null && lastProductId != gene.getProductId()) {
Product product = _products.stream()
.filter(p -> p.getId() == gene.getProductId())
.findFirst()
.orElse(null);
if (product == null) continue;
Operation operation = product.getOperations().stream()
.filter(o -> o.getId() == gene.getOperationId())
.findFirst()
.orElse(null);
if (operation == null) continue;
MachineOption machineOption = operation.getMachineOptions().stream()
.filter(m -> m.getMachineId() == gene.getMachineId())
.findFirst()
.orElse(null);
if (machineOption != null) {
totalSetupTime += machineOption.getSetupTime();
}
}
lastProductId = gene.getProductId();
}
}
return totalSetupTime;
}
private double CalculateTotalFlowTime(ScheduleChromosome chromosome) {
double totalFlowTime = 0.0;
Map<Integer, List<Gene>> orderGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
int minStartTime = group.getValue().stream().mapToInt(Gene::getStartTime).min().orElse(0);
int maxEndTime = group.getValue().stream().mapToInt(Gene::getEndTime).max().orElse(0);
LocalDateTime start = chromosome.getBaseTime().plusMinutes(minStartTime);
LocalDateTime end = chromosome.getBaseTime().plusMinutes(maxEndTime);
long hours = ChronoUnit.HOURS.between(start, end);
long minutes = ChronoUnit.MINUTES.between(start, end) % 60;
totalFlowTime += hours + (double) minutes / 60;
}
return totalFlowTime;
}
private double CalculateMachineLoadBalance(ScheduleChromosome chromosome) {
Map<Integer, Double> machineUtilization = new HashMap<>();
int maxEndTime = chromosome.getGenes().stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
if (maxEndTime == 0) return 0.0;
for (Machine machine : _machines) {
double busyTime = chromosome.getGenes().stream()
.filter(g -> g.getMachineId() == machine.getId())
.mapToInt(g -> g.getEndTime() - g.getStartTime())
.sum();
machineUtilization.put((int) machine.getId(), busyTime / maxEndTime);
}
double avgUtilization = machineUtilization.values().stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
double variance = machineUtilization.values().stream()
.mapToDouble(u -> Math.pow(u - avgUtilization, 2))
.average()
.orElse(0.0);
return 1.0 / (1 + variance);
}
private int calculateSetupTime(List<Gene> existingGenes, Order currentOrder, Machine machine, MachineOption machineOption) {
Gene lastGeneOnMachine = existingGenes.stream()
.filter(g -> g.getMachineId() == machine.getId())
.max(Comparator.comparingInt(Gene::getEndTime))
.orElse(null);
if (lastGeneOnMachine == null) {
return 0;
}
int setupTime = (lastGeneOnMachine.getProductId() != currentOrder.getProductId())
? machineOption.getSetupTime()
: 0;
if (setupTime > 0) {
}
return setupTime;
}
/**
* 计算主处理开始时间,确保在工作时间内
*/
/**
* 计算主处理开始时间,基于设备日历确保在工作时间内
*/
/**
* 计算主处理开始时间,基于设备日历确保在工作时间内
*/
/**
* 计算主处理开始时间
*/
private int calculateMainProcessingStartTime(Machine machine, int setupStartTime, int absoluteSetupTime, List<GeneDetail> geneDetails) {
System.out.println(" 计算主处理开始时间:");
System.out.println(" 换型开始时间: " + ConvertTime(setupStartTime) + " (" + baseTime.plusMinutes(setupStartTime) + ")");
System.out.println(" 绝对换型时长: " + absoluteSetupTime + "分钟 (" + (absoluteSetupTime / 60.0) + "小时)");
// 主处理开始时间 = 换型开始时间 + 绝对换型时长
int mainProcessingStartTime = setupStartTime + absoluteSetupTime;
System.out.println(" 计算主处理开始时间: " + ConvertTime(mainProcessingStartTime) + " (" + baseTime.plusMinutes(mainProcessingStartTime) + ")");
return mainProcessingStartTime;
}
/**
* 调整时间到设备的下一个可用工作时间
*/
private int adjustToMachineWorkingTime(Machine machine, int minute) {
LocalDateTime time = baseTime.plusMinutes(minute);
System.out.println(" 调整时间到工作时间: " + ConvertTime(minute));
// 查找包含或最接近给定时间的可用时间段
for (TimeSegment segment : machine.getAvailability()) {
if (segment.getType() != SegmentType.MAINTENANCE && !segment.isUsed()) {
LocalDateTime segmentStart = segment.getStart();
LocalDateTime segmentEnd = segment.getEnd();
// 如果时间在时间段内,直接返回
if ((time.isAfter(segmentStart) || time.equals(segmentStart)) && time.isBefore(segmentEnd)) {
System.out.println(" 时间 " + ConvertTime(minute) + " 在工作时间内,无需调整");
return minute;
}
// 如果时间在时间段之前,返回时间段的开始时间
if (time.isBefore(segmentStart)) {
int adjustedMinute = (int) ChronoUnit.MINUTES.between(baseTime, segmentStart);
System.out.println(" 时间 " + ConvertTime(minute) + " 在工作时间前,调整到 " + ConvertTime(adjustedMinute));
return adjustedMinute;
}
}
}
// 如果没有找到合适的时间段,生成新的时间段
System.out.println(" 未找到合适的工作时间段,生成新时间段");
List<TimeSegment> timeSegments = _machineScheduler.generateTimeSegment(machine, time,0);
machine.getAvailability().addAll(timeSegments);
// 查找新生成的时间段
for (TimeSegment segment : timeSegments) {
if (segment.getType() != SegmentType.MAINTENANCE && !segment.isUsed()) {
LocalDateTime segmentStart = segment.getStart();
int adjustedMinute = (int) ChronoUnit.MINUTES.between(baseTime, segmentStart);
System.out.println(" 生成新时间段,调整到 " + ConvertTime(adjustedMinute));
return adjustedMinute;
}
}
// 如果仍然没有找到,返回原时间
System.out.println(" ⚠️ 无法找到合适的工作时间,使用原时间");
return minute;
}
/**
* 计算绝对换型时间(仅用于标准模式)
* 绝对换型时间 = 计划换型时间 + 非工作时间间隔
*/
/**
* 计算绝对换型时间(仅用于标准模式)
* 绝对换型时间 = 计划换型时间 + 非工作时间间隔
* @return 绝对换型时长(分钟)
*/
/**
* 计算绝对换型时间(仅用于标准模式)
* 绝对换型时间 = 计划换型时间 + 非工作时间间隔
* @return 绝对换型时长(分钟)
*/
/**
* 计算绝对换型时间(仅用于标准模式)
* 绝对换型时间 = 计划换型时间 + 非工作时间间隔
* @return 绝对换型时长(分钟)
*/
private int calculateAbsoluteSetupTime(Gene gene, Machine machine) {
if (gene.getGeneDetails() == null || gene.getGeneDetails().isEmpty()) {
System.out.println(" 计算绝对换型时间: 无时间段详情,使用计划时间 " + gene.getSetupTime() + "分钟");
return gene.getSetupTime();
}
// 按开始时间排序时间段
List<GeneDetail> sortedDetails = gene.getGeneDetails().stream()
.sorted(Comparator.comparingInt(GeneDetail::getStartTime))
.collect(Collectors.toList());
int remainingSetupTime = gene.getSetupTime();
int accumulatedAbsoluteTime = 0;
int currentTime = gene.getStartTime(); // 当前时间,从换型开始时间开始
System.out.println(" 计算绝对换型时间:");
System.out.println(" 开始时间: " + ConvertTime(currentTime) + " (" + baseTime.plusMinutes(currentTime) + ")");
System.out.println(" 计划换型时间: " + remainingSetupTime + "分钟");
// 遍历时间段,分配换型时间
for (GeneDetail detail : sortedDetails) {
int segmentStart = detail.getStartTime();
int segmentEnd = detail.getEndTime();
int segmentDuration = segmentEnd - segmentStart;
System.out.println(" 处理时间段: " + ConvertTime(segmentStart) + " - " + ConvertTime(segmentEnd) +
" (" + segmentDuration + "分钟)");
// 如果当前时间在时间段之前,需要等待到时间段开始
if (currentTime < segmentStart) {
int waitTime = segmentStart - currentTime;
accumulatedAbsoluteTime += waitTime;
System.out.println(" 等待时间段开始: " + waitTime + "分钟 (" +
ConvertTime(currentTime) + " → " + ConvertTime(segmentStart) + ")");
currentTime = segmentStart;
}
// 如果当前时间在时间段内
if (currentTime >= segmentStart && currentTime < segmentEnd) {
int availableTimeInSegment = segmentEnd - currentTime;
System.out.println(" 时间段内可用时间: " + availableTimeInSegment + "分钟");
if (availableTimeInSegment >= remainingSetupTime) {
// 当前时间段可以完成剩余换型
accumulatedAbsoluteTime += remainingSetupTime;
currentTime += remainingSetupTime;
remainingSetupTime = 0;
System.out.println(" 换型完成于: " + ConvertTime(currentTime));
break;
} else {
// 使用整个时间段的剩余时间
accumulatedAbsoluteTime += availableTimeInSegment;
remainingSetupTime -= availableTimeInSegment;
currentTime = segmentEnd;
System.out.println(" 使用时间段剩余时间: " + availableTimeInSegment + "分钟");
System.out.println(" 剩余换型时间: " + remainingSetupTime + "分钟");
}
}
}
// 如果换型未完成,使用计划时间
if (remainingSetupTime > 0) {
System.out.println(" 换型未完成,剩余时间: " + remainingSetupTime + "分钟,使用计划时间");
accumulatedAbsoluteTime += remainingSetupTime;
}
System.out.println(" 最终绝对换型时长: " + accumulatedAbsoluteTime + "分钟 (" + (accumulatedAbsoluteTime / 60.0) + "小时)");
System.out.println(" 非工作时间间隔: " + (accumulatedAbsoluteTime - gene.getSetupTime()) + "分钟");
return accumulatedAbsoluteTime;
}
private int calculateSetupTimeForConflict(List<Gene> existingGenes, Order currentOrder, Machine machine,
MachineOption machineOption, Gene conflictingGene) {
int setupTime = (conflictingGene.getProductId() != currentOrder.getProductId())
? machineOption.getSetupTime()
: 0;
if (existingGenes.stream().filter(g -> g.getMachineId() == machine.getId()).count() <= 1) {
setupTime = 0;
}
if (setupTime > 0) {
System.out.println("设备 " + machine.getId() + " 需要换型,因为产品从 " + conflictingGene.getProductId() + " 变更为 " + currentOrder.getProductId());
}
return setupTime;
}
private List<GeneDetail> GetNextAvailableTime(
Machine machine, int proposedStartTime, int prevtime,
int processingTime, List<Gene> existingTasks,
boolean IsInterrupt, boolean istask
) {
LocalDateTime startTime = baseTime.plusMinutes(proposedStartTime);
String prevtimeStr = prevtime > -1 ? baseTime.plusMinutes(prevtime).toString() : "";
return FindEarliestStart(machine, processingTime, startTime, prevtimeStr, existingTasks, istask);
}
private List<GeneDetail> FindEarliestStart(
Machine machine, int processingTime, LocalDateTime currentTime,
String prevtime, List<Gene> existingTasks, boolean checkprevtime
) {
List<Gene> machineTasks = existingTasks.stream()
.filter(t -> t.getMachineId() == machine.getId())
.sorted(Comparator.comparingInt(Gene::getStartTime))
.collect(Collectors.toList());
List<GeneDetail> times = new ArrayList<>();
TimeSegment slot = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime);
if (slot == null) return times;
LocalDateTime prevTimeDateTime = StringUtils.isEmpty(prevtime) ? null : LocalDateTime.parse(prevtime);
LocalDateTime startCandidate = slot.getStart().isAfter(prevTimeDateTime != null ? prevTimeDateTime : currentTime)
? slot.getStart()
: (prevTimeDateTime != null ? prevTimeDateTime : currentTime);
LocalDateTime endCandidate = startCandidate.plusMinutes(processingTime);
if (endCandidate.isAfter(slot.getEnd())) {
return CaldEarliestStart(machine, processingTime, currentTime, prevtime, machineTasks, checkprevtime);
} else {
GeneDetail time = new GeneDetail();
time.setKey(slot.getKey());
time.setStartTime((int) ChronoUnit.MINUTES.between(baseTime, startCandidate));
time.setEndTime((int) ChronoUnit.MINUTES.between(baseTime, endCandidate));
times.add(time);
RemoveMachineAvailable(machine, time);
return times;
}
}
private List<GeneDetail> CaldEarliestStart(
Machine machine, int processingTime, LocalDateTime currentTime,
String prevtime, List<Gene> machineTasks, boolean checkprevtime
) {
int remainingTime = processingTime;
LocalDateTime st = StringUtils.isEmpty(prevtime) ? currentTime : LocalDateTime.parse(prevtime);
LocalDateTime prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
List<GeneDetail> times = new ArrayList<>();
List<GeneDetail> oldTimes = new ArrayList<>();
while (remainingTime > 0) {
TimeSegment shift = GetCurrentOrNextShift(machine, currentTime, prevtime, checkprevtime);
if (shift == null) break;
LocalDateTime shiftStart = shift.getStart();
LocalDateTime shiftEnd = shift.getEnd();
// 检查班次间冲突
if (!prevEnd.isEqual(LocalDateTime.of(2000, 1, 1, 0, 0, 0))) {
if (prevEnd.isBefore(currentTime)) {
// 检查班次间任务
LocalDateTime finalPrevEnd = prevEnd;
boolean hasTask = machineTasks.stream()
.anyMatch(t -> {
LocalDateTime taskStart = baseTime.plusMinutes(t.getStartTime());
return taskStart.isAfter(finalPrevEnd) && taskStart.isBefore(shiftStart);
});
if (hasTask) {
// 重置状态
currentTime = shiftStart;
st = shiftStart;
remainingTime = processingTime;
prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
oldTimes.addAll(deepCopyGeneDetailList(times));
times.clear();
continue;
}
// 检查班次间维修窗口
if (machine.getMaintenanceWindows() != null) {
LocalDateTime finalPrevEnd1 = prevEnd;
boolean hasMaintenance = machine.getMaintenanceWindows().stream()
.anyMatch(w -> w.getStartTime().isAfter(finalPrevEnd1) && w.getStartTime().isBefore(shiftStart));
if (hasMaintenance) {
currentTime = shiftStart;
st = shiftStart;
remainingTime = processingTime;
prevEnd = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
times.clear();
continue;
}
}
}
}
prevEnd = shiftEnd;
// 计算有效时间
LocalDateTime effectiveStart = st.isAfter(shiftStart) ? st : shiftStart;
long availableMinutes = ChronoUnit.MINUTES.between(effectiveStart, shiftEnd);
// 处理当前班次
int processable = Math.min(remainingTime, (int) availableMinutes);
remainingTime -= processable;
currentTime = effectiveStart.plusMinutes(processable);
// 添加时间详情
GeneDetail time = new GeneDetail();
time.setKey(shift.getKey());
time.setStartTime((int) ChronoUnit.MINUTES.between(baseTime, effectiveStart));
time.setEndTime((int) ChronoUnit.MINUTES.between(baseTime, currentTime));
times.add(time);
RemoveMachineAvailable(machine, time);
}
// 还原未使用的时间段
AddMachineAvailable(machine, oldTimes);
return times;
}
private TimeSegment GetCurrentOrNextShift(Machine machine, LocalDateTime time, String prevtime, boolean checkprevtime) {
TimeSegment start = null;
// 查找有效班次
start = machine.getAvailability().stream()
.filter(slot -> !slot.isUsed() && slot.getType() != SegmentType.MAINTENANCE)
.filter(slot -> slot.getStart().isAfter(time) || slot.getEnd().isAfter(time))
.findFirst()
.orElse(null);
if (start == null) {
// 生成新时间段
List<TimeSegment> timeSegments = _machineScheduler.generateTimeSegment(machine, time,0);
machine.getAvailability().addAll(timeSegments);
// 更新设备时间线
Machine originalMachine = _machines.stream()
.filter(t -> t.getId() == machine.getId())
.findFirst()
.orElse(null);
if (originalMachine != null) {
MachineTimeline timeline = _machineScheduler.getOrCreateTimeline(originalMachine);
}
// 递归查找
return GetCurrentOrNextShift(machine, time, prevtime, checkprevtime);
}
return start;
}
private void RemoveMachineAvailable(Machine machine, GeneDetail geneDetails) {
List<TimeSegment> timeSegments = new ArrayList<>();
// 查找对应时间段
int index = machine.getAvailability().stream()
.filter(t -> t.getKey().equals(geneDetails.getKey()))
.findFirst()
.map(machine.getAvailability()::indexOf)
.orElse(-1);
if (index > -1) {
TimeSegment targetSegment = machine.getAvailability().get(index);
LocalDateTime geneEndTime = baseTime.plusMinutes(geneDetails.getEndTime());
if (targetSegment.getEnd().isAfter(geneEndTime)) {
// 拆分时间段
TimeSegment usedSegment = new TimeSegment();
usedSegment.setStart(baseTime.plusMinutes(geneDetails.getStartTime()));
usedSegment.setEnd(geneEndTime);
usedSegment.setHoliday(false);
usedSegment.setKey(UUID.randomUUID().toString());
usedSegment.setType(SegmentType.REGULAR);
usedSegment.setUsed(true);
timeSegments.add(usedSegment);
// 更新基因详情Key
geneDetails.setKey(usedSegment.getKey());
// 更新原时间段开始时间
targetSegment.setStart(geneEndTime);
} else {
targetSegment.setUsed(true);
}
}
// 添加新段并排序
if (!timeSegments.isEmpty()) {
machine.getAvailability().addAll(timeSegments);
}
machine.getAvailability().sort(Comparator.comparing(TimeSegment::getStart));
}
private void AddMachineAvailable(Machine machine, List<GeneDetail> geneDetails) {
if (geneDetails == null || geneDetails.isEmpty()) return;
// 标记时间段为未占用
for (GeneDetail detail : geneDetails) {
machine.getAvailability().stream()
.filter(t -> t.getKey().equals(detail.getKey()))
.findFirst()
.ifPresent(t -> t.setUsed(false));
}
// 合并时间段
machine.setAvailability(MergeSegments(machine.getAvailability()));
}
private List<TimeSegment> MergeSegments(List<TimeSegment> segments) {
// 分离维护段、未占用段、已占用段
List<TimeSegment> maintenanceSegments = segments.stream()
.filter(t -> t.getType() == SegmentType.MAINTENANCE)
.collect(Collectors.toList());
List<TimeSegment> unusedRegularSegments = segments.stream()
.filter(t -> t.getType() != SegmentType.MAINTENANCE && !t.isUsed())
.collect(Collectors.toList());
List<TimeSegment> usedRegularSegments = segments.stream()
.filter(t -> t.getType() != SegmentType.MAINTENANCE && t.isUsed())
.collect(Collectors.toList());
// 排序未占用段
unusedRegularSegments.sort(Comparator.comparing(TimeSegment::getStart));
// 合并未占用段
Deque<TimeSegment> mergedUnused = new ArrayDeque<>();
for (TimeSegment seg : unusedRegularSegments) {
if (!mergedUnused.isEmpty() && mergedUnused.peekLast().getEnd().isAfter(seg.getStart())) {
TimeSegment last = mergedUnused.pollLast();
last.setEnd(last.getEnd().isAfter(seg.getEnd()) ? last.getEnd() : seg.getEnd());
mergedUnused.offerLast(last);
} else {
mergedUnused.offerLast(seg);
}
}
// 重组结果
List<TimeSegment> result = new ArrayList<>(mergedUnused);
result.addAll(usedRegularSegments);
result.addAll(maintenanceSegments);
result.sort(Comparator.comparing(TimeSegment::getStart));
return result;
}
public void PrintScheduleSummary(ScheduleChromosome schedule) {
System.out.println("\n=== Schedule Summary ===");
// 按订单分组打印
Map<Integer, List<Gene>> orderGroups = schedule.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
System.out.printf("\nOrder %d Schedule:%n", group.getKey());
// 按工序排序
List<Gene> sortedJobs = group.getValue().stream()
.sorted(Comparator.comparingInt(Gene::getOperationId))
.collect(Collectors.toList());
for (Gene job : sortedJobs) {
Product product = _products.stream()
.filter(p -> p.getId() == job.getProductId())
.findFirst()
.orElse(null);
Operation operation = product != null ? product.getOperations().stream()
.filter(o -> o.getId() == job.getOperationId())
.findFirst()
.orElse(null) : null;
if (product != null && operation != null) {
String print = String.format(
"[%s-%s] Order %d, Product %s, Machine %d, Operation %d, Batch %d, processingTime %d, setupTime %d, teardownTime %d",
ConvertTime(job.getStartTime()),
ConvertTime(job.getEndTime()),
job.getOrderId(),
product.getName(),
job.getMachineId(),
operation.getSequence(),
job.getBatchSize(),
job.getProcessingTime(),
job.getSetupTime(),
job.getTeardownTime()
);
System.out.println(print);
}
}
}
}
public void WriteScheduleSummary(ScheduleChromosome schedule) {
// 写入日志
FileHelper.writeLogFile(String.format("\n=== Schedule Summary === %f", schedule.getFitness()));
FileHelper.writeLogFile(String.format("Makespan: %f minutes", schedule.getObjectiveValues().get(0)));
FileHelper.writeLogFile(String.format("Total Tardiness: %f hours", schedule.getObjectiveValues().get(1)));
FileHelper.writeLogFile(String.format("Setup Time: %f minutes", schedule.getObjectiveValues().get(2)));
FileHelper.writeLogFile(String.format("Flow Time: %f minutes", schedule.getObjectiveValues().get(3)));
FileHelper.writeLogFile(String.format("Machine Load Balance: %.2f%%", schedule.getObjectiveValues().get(4) * 100));
System.out.println(schedule.getSceneId());
FileHelper.writeLogFile("-------------------------");
// 按订单分组写入
Map<Integer, List<Gene>> orderGroups = schedule.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getOrderId));
for (Map.Entry<Integer, List<Gene>> group : orderGroups.entrySet()) {
List<Gene> sortedJobs = group.getValue().stream()
.sorted(Comparator.comparingInt(Gene::getOperationId))
.collect(Collectors.toList());
for (Gene job : sortedJobs) {
Product product = _products.stream()
.filter(p -> p.getId() == job.getProductId())
.findFirst()
.orElse(null);
Operation operation = product != null ? product.getOperations().stream()
.filter(o -> o.getId() == job.getOperationId())
.findFirst()
.orElse(null) : null;
if (product != null && operation != null) {
StringBuilder sb = new StringBuilder();
sb.append(String.format(
"[%d-%d]:[%s-%s] Order %d, Product %s, Machine %d, Operation %d, Batch %d, processingTime %d, setupTime %d, teardownTime %d",
job.getStartTime(),
job.getEndTime(),
ConvertTime(job.getStartTime()),
ConvertTime(job.getEndTime()),
job.getOrderId(),
product.getName(),
job.getMachineId(),
operation.getSequence(),
job.getBatchSize(),
job.getProcessingTime(),
job.getSetupTime(),
job.getTeardownTime()
));
// 追加基因详情
for (GeneDetail d : job.getGeneDetails()) {
sb.append(String.format(
"\n\t\t\t\t\t\t\t\t\t\t [%d-%d]:[%s-%s] %d",
d.getStartTime(),
d.getEndTime(),
ConvertTime(d.getStartTime()),
ConvertTime(d.getEndTime()),
d.getEndTime() - d.getStartTime()
));
}
FileHelper.writeLogFile(sb.toString());
}
}
FileHelper.writeLogFile("");
}
}
private String ConvertTime(int minute) {
return baseTime.plusMinutes(minute).format(java.time.format.DateTimeFormatter.ofPattern("MM-dd HH:mm"));
}
// ========================== 深拷贝工具方法 ==========================
private List<Product> deepCopyProductList(List<Product> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyProduct).collect(Collectors.toList());
}
private Product deepCopyProduct(Product source) {
if (source == null) return null;
Product copy = new Product();
copy.setId(source.getId());
copy.setName(source.getName());
copy.setOperations(deepCopyOperationList(source.getOperations()));
return copy;
}
private List<Operation> deepCopyOperationList(List<Operation> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyOperation).collect(Collectors.toList());
}
private Operation deepCopyOperation(Operation source) {
if (source == null) return null;
Operation copy = new Operation();
copy.setId(source.getId());
copy.setProductId(source.getProductId());
copy.setSequence(source.getSequence());
copy.setInterrupt(source.isInterrupt());
copy.setMachineOptions(deepCopyMachineOptionList(source.getMachineOptions()));
return copy;
}
private List<MachineOption> deepCopyMachineOptionList(List<MachineOption> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyMachineOption).collect(Collectors.toList());
}
private MachineOption deepCopyMachineOption(MachineOption source) {
if (source == null) return null;
MachineOption copy = new MachineOption();
copy.setMachineId(source.getMachineId());
copy.setProcessingTime(source.getProcessingTime());
copy.setSetupTime(source.getSetupTime());
copy.setTeardownTime(source.getTeardownTime());
copy.setPreTime(source.getPreTime());
return copy;
}
private List<Machine> deepCopyMachineList(List<Machine> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyMachine).collect(Collectors.toList());
}
private Machine deepCopyMachine(Machine source) {
if (source == null) return null;
Machine copy = new Machine();
copy.setId(source.getId());
copy.setName(source.getName());
copy.setEarliestTime(source.getEarliestTime());
copy.setTotalTaskTime(source.getTotalTaskTime());
copy.setShifts(deepCopyShiftList(source.getShifts()));
copy.setMaintenanceWindows(deepCopyMaintenanceWindowList(source.getMaintenanceWindows()));
copy.setAvailability(deepCopyTimeSegmentList(source.getAvailability()));
copy.setShiftsChanged(source.getShiftsChanged());
copy.setMaintenanceWindowsChanged(source.getMaintenanceWindowsChanged());
return copy;
}
private List<Shift> deepCopyShiftList(List<Shift> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyShift).collect(Collectors.toList());
}
private Shift deepCopyShift(Shift source) {
if (source == null) return null;
Shift copy = new Shift();
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
copy.setDays(source.getDays() != null ? new HashSet<>(source.getDays()) : null);
copy.setShiftDate(source.getShiftDate());
copy.setTemporaryShift(source.isTemporaryShift());
copy.setPriority(source.getPriority());
return copy;
}
private List<MaintenanceWindow> deepCopyMaintenanceWindowList(List<MaintenanceWindow> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyMaintenanceWindow).collect(Collectors.toList());
}
private MaintenanceWindow deepCopyMaintenanceWindow(MaintenanceWindow source) {
if (source == null) return null;
MaintenanceWindow copy = new MaintenanceWindow();
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
copy.setReason(source.getReason());
return copy;
}
private List<TimeSegment> deepCopyTimeSegmentList(List<TimeSegment> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyTimeSegment).collect(Collectors.toList());
}
private TimeSegment deepCopyTimeSegment(TimeSegment source) {
if (source == null) return null;
TimeSegment copy = new TimeSegment();
copy.setKey(source.getKey());
copy.setStart(source.getStart());
copy.setEnd(source.getEnd());
copy.setEarliestTime(source.getEarliestTime());
copy.setTotalTaskTime(source.getTotalTaskTime());
copy.setType(source.getType());
copy.setHoliday(source.isHoliday());
copy.setUsed(source.isUsed());
return copy;
}
private List<Order> deepCopyOrderList(List<Order> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyOrder).collect(Collectors.toList());
}
private Order deepCopyOrder(Order source) {
if (source == null) return null;
Order copy = new Order();
copy.setId(source.getId());
copy.setProductId(source.getProductId());
copy.setQuantity(source.getQuantity());
copy.setDueDate(source.getDueDate());
copy.setOrderCompletion(source.getOrderCompletion());
copy.setTardiness(source.getTardiness());
copy.setPriority(source.getPriority());
copy.setCanSplit(source.isCanSplit());
copy.setCanInterrupt(source.isCanInterrupt());
return copy;
}
private List<Gene> deepCopyGeneList(List<Gene> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyGene).collect(Collectors.toList());
}
private Gene deepCopyGene(Gene source) {
if (source == null) return null;
Gene copy = new Gene();
copy.setOrderId(source.getOrderId());
copy.setProductId(source.getProductId());
copy.setOperationId(source.getOperationId());
copy.setMachineId(source.getMachineId());
copy.setBatchSize(source.getBatchSize());
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
copy.setProcessingTime(source.getProcessingTime());
copy.setTeardownTime(source.getTeardownTime());
copy.setSetupTime(source.getSetupTime());
copy.setPreTime(source.getPreTime());
copy.setGeneDetails(deepCopyGeneDetailList(source.getGeneDetails()));
return copy;
}
private List<GeneDetail> deepCopyGeneDetailList(List<GeneDetail> source) {
return source == null ? new ArrayList<>() : source.stream().map(this::deepCopyGeneDetail).collect(Collectors.toList());
}
private GeneDetail deepCopyGeneDetail(GeneDetail source) {
if (source == null) return null;
GeneDetail copy = new GeneDetail();
copy.setKey(source.getKey());
copy.setStartTime(source.getStartTime());
copy.setEndTime(source.getEndTime());
return copy;
}
private ScheduleChromosome deepCopyScheduleChromosome(ScheduleChromosome source) {
if (source == null) return null;
ScheduleChromosome copy = new ScheduleChromosome(source.getBaseTime());
copy.setGenes(deepCopyGeneList(source.getGenes()));
copy.setOrders(deepCopyOrderList(source.getOrders()));
copy.setMachines(deepCopyMachineList(source.getMachines()));
copy.setObjectiveValues(new HashMap<>(source.getObjectiveValues()));
copy.setFitness(source.getFitness());
copy.setTardiness(source.getTardiness());
return copy;
}
// ========================== 辅助类:StringUtils(对应C#字符串工具) ==========================
private static class StringUtils {
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
}
}
\ No newline at end of file
......@@ -14,6 +14,7 @@ import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
......@@ -50,7 +51,7 @@ public class MachineSchedulerService {
// 创建新时间线(60天范围)
MachineTimeline newTimeline = generateTimeline(machine);
newTimeline.setSegments(mergeSegments(newTimeline.getSegments()));
// newTimeline.setSegments(mergeSegments(newTimeline.getSegments()));
timelineCache.put(machineId, newTimeline);
return newTimeline;
......@@ -66,14 +67,14 @@ public class MachineSchedulerService {
LocalDate currentDate = currentTime.toLocalDate();
LocalDate endDate = currentTime.plusDays(200).toLocalDate();
List<TimeSegment> allSegments = new ArrayList<>();
CopyOnWriteArrayList<TimeSegment> allSegments = new CopyOnWriteArrayList<>();
while (!currentDate.isAfter(endDate)) {
// 检查是否在假期内
boolean isHolidayPeriod = isHoliday(machine,currentDate);
// 生成当日时间段
List<TimeSegment> daySegments = calculateDaySegments(machine, currentDate, isHolidayPeriod);
CopyOnWriteArrayList<TimeSegment> daySegments = calculateDaySegments(machine, currentDate, isHolidayPeriod);
allSegments.addAll(daySegments);
// 跳到下一天
......@@ -101,7 +102,7 @@ public class MachineSchedulerService {
}
if(maintenanceWindow.getStartTime().isBefore(currentTime)&&maintenanceWindow.getEndTime().isAfter(maxEnd))
{
timeline.setSegments(new ArrayList<>());
timeline.setSegments(new CopyOnWriteArrayList<>());
currentTime=maintenanceWindow.getEndTime();
timeline= generateTimeline(machine);
break;
......@@ -115,7 +116,7 @@ public class MachineSchedulerService {
return timeline;
}
public List<TimeSegment> generateTimeSegment(Machine machine, LocalDateTime currentTime,int days) {
public CopyOnWriteArrayList<TimeSegment> generateTimeSegment(Machine machine, LocalDateTime currentTime,int days) {
if(days==0)
{
days=200;
......@@ -138,7 +139,7 @@ try {
timeline.setValidFrom(currentTime);
timeline.setValidTo(currentTime.plusDays(days));
timeline.setLastUpdated(LocalDateTime.now());
timeline.setSegments(new ArrayList<>());
timeline.setSegments(new CopyOnWriteArrayList<>());
} else {
if(currentTime==null)
{
......@@ -146,9 +147,9 @@ try {
}
if (timeline.getValidTo().compareTo(currentTime) > 0) {
LocalDateTime currentTime1=currentTime;
List<TimeSegment> timeSegments= timeline.getSegments().stream()
CopyOnWriteArrayList<TimeSegment> timeSegments= timeline.getSegments().stream()
.filter(t->t.getStart().isAfter(currentTime1)||t.getStart().isBefore(currentTime1))
.collect(Collectors.toList());
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
return ProductionDeepCopyUtil.deepCopyList(timeSegments,TimeSegment.class);
} else {
......@@ -163,7 +164,7 @@ try {
LocalDate currentDate = currentTime.toLocalDate();
LocalDate endDate = currentTime.plusDays(days).toLocalDate();
List<TimeSegment> segments = new ArrayList<>();
CopyOnWriteArrayList<TimeSegment> segments = new CopyOnWriteArrayList<>();
while (!currentDate.isAfter(endDate)) {
// 检查是否在假期内
......@@ -235,7 +236,7 @@ try {
machine.getAvailability().addAll(newSegments);
// 合并片段(去重+排序),并重新赋值给设备的可用片段列表,对应C#的 machine.Availability = MergeSegments(...)
List<TimeSegment> mergedSegments = mergeSegments(machine.getAvailability());
CopyOnWriteArrayList<TimeSegment> mergedSegments = mergeSegments(machine.getAvailability());
machine.setAvailability(mergedSegments);
}
......@@ -259,8 +260,8 @@ try {
return false;
}
private List<TimeSegment> calculateDaySegments(Machine machine, LocalDate date, boolean isHoliday) {
List<TimeSegment> segments = new ArrayList<>();
private CopyOnWriteArrayList<TimeSegment> calculateDaySegments(Machine machine, LocalDate date, boolean isHoliday) {
CopyOnWriteArrayList<TimeSegment> segments = new CopyOnWriteArrayList<>();
if (isHoliday) {
// 假期:只处理特定日期的班次
......@@ -358,29 +359,29 @@ if(shifts==null||shifts.size()==0) {
return shiftDays.contains(targetDayValue);
}
private List<TimeSegment> mergeSegments(List<TimeSegment> segments1) {
private CopyOnWriteArrayList<TimeSegment> mergeSegments(CopyOnWriteArrayList<TimeSegment> segments1) {
List<TimeSegment> segments= ProductionDeepCopyUtil.deepCopyList(segments1,TimeSegment.class);
CopyOnWriteArrayList<TimeSegment> segments= ProductionDeepCopyUtil.deepCopyList(segments1,TimeSegment.class);
if(segments==null||segments.size()==0)
{
return null;
}
List<TimeSegment> maintenanceSegments1 = segments.stream()
CopyOnWriteArrayList<TimeSegment> maintenanceSegments1 = segments.stream()
.filter(t ->t.getType()==null)
.collect(Collectors.toList());
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
if(maintenanceSegments1.size()>0)
{
int i=0;
}
List<TimeSegment> maintenanceSegments = segments.stream()
CopyOnWriteArrayList<TimeSegment> maintenanceSegments = segments.stream()
.filter(t ->t.getType()!=null&& t.getType() == SegmentType.MAINTENANCE)
.collect(Collectors.toList());
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
List<TimeSegment> otherSegments = segments.stream()
CopyOnWriteArrayList<TimeSegment> otherSegments = segments.stream()
.filter(t ->t.getType()!=null&& t.getType() != SegmentType.MAINTENANCE)
.collect(Collectors.toList());
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
// 按开始时间排序
otherSegments.sort(Comparator.comparing(TimeSegment::getStart));
......@@ -399,7 +400,7 @@ if(shifts==null||shifts.size()==0) {
}
}
List<TimeSegment> result = new ArrayList<>(merged);
CopyOnWriteArrayList<TimeSegment> result = new CopyOnWriteArrayList<>(merged);
result.addAll(maintenanceSegments);
// 按开始时间排序
......@@ -433,7 +434,7 @@ if(shifts==null||shifts.size()==0) {
}
}
List<MaintenanceWindow> result = new ArrayList<>(merged);
List<MaintenanceWindow> result = new CopyOnWriteArrayList<>(merged);
// 按开始时间排序
result.sort(Comparator.comparing(MaintenanceWindow::getStartTime));
......
......@@ -120,69 +120,7 @@ public class PlanResultService {
private LocalDateTime baseTime = LocalDateTime.of(2025, 11, 1, 0, 0, 0);
public List<ScheduleChromosome> execute() {
try {
// 1. 读取数据
List<Machine> machines = loadData("machines.json", Machine.class);
List<Product> products = loadData("products.json", Product.class);
List<Order> orders = loadData("orders.json", Order.class);
// 设置机器信息到班次中
for (Machine machine : machines) {
if (machine.getShifts() != null) {
for (Shift shift : machine.getShifts()) {
shift.setMachineId(machine.getId());
shift.setMachineName(machine.getName());
}
}
// 调试:打印机器和班次信息
System.out.println("Machine: " + machine.getId() + ", Name: " + machine.getName());
if (machine.getShifts() != null) {
for (Shift shift : machine.getShifts()) {
System.out.println(" Shift: " + shift.getStartTime() + " - " + shift.getEndTime() +
", Status: " + shift.getStatus() +
", MachineId: " + shift.getMachineId() +
", MachineName: " + shift.getMachineName());
}
}
}
// 创建节假日
List<Holiday> holidays = Arrays.asList(
new Holiday(LocalDateTime.of(2025, 10, 1, 0, 0),
LocalDateTime.of(2025, 10, 7, 23, 59))
);
// 将节假日添加到所有设备中
addHolidaysToAllMachines(machines, holidays);
// 3. 创建调度服务
MachineSchedulerService machineScheduler = new MachineSchedulerService(
holidays, LocalDateTime.of(2025, 10, 1, 0, 0, 0));
// 4. 初始化机器时间线
for (Machine machine : machines) {
MachineTimeline timeline = machineScheduler.getOrCreateTimeline(machine);
machine.setAvailability(timeline.getSegments());
}
// 5. 执行调度算法
AlgorithmScheduler7 scheduler = new AlgorithmScheduler7(products, machines, orders, machineScheduler);
List<ScheduleChromosome> scheduleChromosomes = scheduler.RunAll();
// 对调度结果按照 fitness 由高到低排序
scheduleChromosomes.sort((c1, c2) -> Double.compare(c2.getFitness(), c1.getFitness()));
// 为每个 ScheduleChromosome 分配场景ID(基于排序后的位置)
for (int i = 0; i < scheduleChromosomes.size(); i++) {
scheduleChromosomes.get(i).setSceneId(i + 1); // 场景ID从1开始
}
return scheduleChromosomes;
} catch (Exception e) {
throw new RuntimeException("调度执行失败", e);
}
return null;
}
......@@ -388,30 +326,92 @@ order.setDueDate(LocalDateTime.of(2025, 12, 1,0,0,0));
public Chromosome EditOperation(String SceneId,Entry operation) {
Chromosome chromosome= _sceneService.loadChromosomeFromFile(SceneId);
public Chromosome EditOperation(String SceneId, Entry operation) {
Chromosome chromosome = _sceneService.loadChromosomeFromFile(SceneId);
if (chromosome == null || chromosome.getAllOperations() == null) {
return chromosome; // 直接返回,空值由上层处理
return chromosome;
}
List<Entry> operations = chromosome.getAllOperations();
// 直接查找匹配元素的索引(避免先找元素再查索引的冗余)
int position = IntStream.range(0, operations.size())
.filter(i -> {
Entry t = operations.get(i);
return t.getId()==operation.getId();
})
.filter(i -> operations.get(i).getId() == operation.getId())
.findFirst()
.orElse(-1);
// 索引有效时替换
if (position != -1) {
operations.set(position, operation);
Entry oldEntry = operations.set(position, operation);
List<GlobalOperationInfo> globalOpList = chromosome.getGlobalOpList();
if (globalOpList != null) {
globalOpList.stream()
.filter(opInfo -> opInfo.getOp() == oldEntry)
.forEach(opInfo -> opInfo.setOp(operation));
}
}
ScheduleOperationService ScheduleOperation=new ScheduleOperationService();
return redecodeChromosome(chromosome);
}
GlobalParam globalParam=new GlobalParam();
ScheduleOperation.redecode(chromosome,chromosome.getBaseTime(), globalParam);
public Chromosome EditOrder(String SceneId, Order order) {
Chromosome chromosome = _sceneService.loadChromosomeFromFile(SceneId);
if (chromosome == null || chromosome.getOrders() == null) {
return chromosome;
}
List<Order> orders = chromosome.getOrders();
int position = IntStream.range(0, orders.size())
.filter(i -> orders.get(i).getId() == order.getId())
.findFirst()
.orElse(-1);
if (position != -1) {
orders.set(position, order);
}
orderSortService.initializeFieldExtractors();
OrderSortRule rule = createMultiConditionRule(orders);
orderSortService.assignPriority(orders, rule);
updateOrderRelatedEntries(chromosome, order);
return redecodeChromosome(chromosome);
}
/**
* 更新订单相关的所有Entry的数量和优先级
*/
private void updateOrderRelatedEntries(Chromosome chromosome, Order order) {
// 更新所有操作中的Entry
List<Entry> allOperations = chromosome.getAllOperations();
if (allOperations != null) {
allOperations.stream()
.filter(entry -> entry.getOrderId() != null && entry.getOrderId().equals(order.getOrderId()))
.forEach(entry -> {
entry.setQuantity(order.getQuantity());
entry.setPriority(order.getActualPriority());
});
}
// 更新GlobalOperationInfo中的Entry
List<GlobalOperationInfo> globalOpList = chromosome.getGlobalOpList();
if (globalOpList != null) {
globalOpList.stream()
.map(GlobalOperationInfo::getOp)
.filter(entry -> entry != null && entry.getOrderId() != null && entry.getOrderId().equals(order.getOrderId()))
.forEach(entry -> {
entry.setQuantity(order.getQuantity());
entry.setPriority(order.getActualPriority());
});
}
}
/**
* 重新解码染色体
*/
private Chromosome redecodeChromosome(Chromosome chromosome) {
GlobalParam globalParam = new GlobalParam();
ScheduleOperationService scheduleOperation = new ScheduleOperationService();
scheduleOperation.redecode(chromosome, chromosome.getBaseTime(), globalParam);
return chromosome;
}
......
......@@ -19,80 +19,82 @@ public class PlanSchedulerService {
private final LocalDateTime baseTime = LocalDateTime.of(2025, 10, 1, 0, 0, 0);
public List<PlanResourceTaskGanttVO> execute() {
try {
// 1. 读取数据
List<Machine> machines = JsonFileReader.readListFromResources("machines.json", Machine.class);
List<Product> products = JsonFileReader.readListFromResources("products.json", Product.class);
List<Order> orders = JsonFileReader.readListFromResources("orders.json", Order.class);
// 2. 创建节假日
List<Holiday> holidays = Arrays.asList(
new Holiday(LocalDateTime.of(2025, 10, 1, 0, 0),
LocalDateTime.of(2025, 10, 7, 23, 59))
);
// 3. 创建调度服务
MachineSchedulerService machineScheduler = new MachineSchedulerService(
holidays, LocalDateTime.of(2025, 10, 1, 0, 0, 0));
// 4. 初始化机器时间线
for (Machine machine : machines) {
MachineTimeline timeline = machineScheduler.getOrCreateTimeline(machine);
machine.setAvailability(timeline.getSegments());
}
// 5. 执行调度算法
AlgorithmScheduler6 scheduler = new AlgorithmScheduler6(products, machines, orders, machineScheduler);
List<ScheduleChromosome> scheduleChromosomes = scheduler.RunAll();
List<PlanResourceTaskGanttVO> ganttlist = new ArrayList<>();
long sceneId = 1L;
for (ScheduleChromosome chromosome : scheduleChromosomes
) {
// 转换基因列表和设备列表
List<GenVO> genVOS = convertGeneListToGenVO(chromosome.getGenes(), baseTime);
List<MachineVO> machineVOS = machines.stream()
.map(this::convertToVO)
.collect(Collectors.toList());
// 按设备ID分组任务
Map<Long, List<GenVO>> taskMap = genVOS.stream()
.collect(Collectors.groupingBy(GenVO::getEquipId));
// 为每个设备设置任务
for (MachineVO machine : machineVOS) {
Integer equipId = Integer.valueOf(machine.getEquipId());
List<GenVO> tasks = taskMap.get(equipId);
if (tasks != null) {
// 按开始时间排序
tasks.sort(Comparator.comparing(GenVO::getStartTime));
machine.setTasks(tasks);
} else {
machine.setTasks(new ArrayList<>()); // 如果没有任务,设置为空列表
}
}
// 现在machineVOS中的每个MachineVO都包含了对应的任务列表
PlanResourceTaskGanttVO ganttVO = new PlanResourceTaskGanttVO()
.setResources(machineVOS)
.setEarliestTaskStartTime(LocalDateTime.of(2025, 9, 1, 0, 0, 0))
.setLastTaskAssignmentTime(LocalDateTime.of(2025, 12, 1, 0, 0, 0))
.setSceneId(sceneId);
sceneId++;
ganttlist.add(ganttVO);
}
return ganttlist;
} catch (Exception e) {
throw new RuntimeException("调度执行失败", e);
}
// try {
// // 1. 读取数据
// List<Machine> machines = JsonFileReader.readListFromResources("machines.json", Machine.class);
// List<Product> products = JsonFileReader.readListFromResources("products.json", Product.class);
// List<Order> orders = JsonFileReader.readListFromResources("orders.json", Order.class);
//
// // 2. 创建节假日
// List<Holiday> holidays = Arrays.asList(
// new Holiday(LocalDateTime.of(2025, 10, 1, 0, 0),
// LocalDateTime.of(2025, 10, 7, 23, 59))
// );
//
// // 3. 创建调度服务
// MachineSchedulerService machineScheduler = new MachineSchedulerService(
// holidays, LocalDateTime.of(2025, 10, 1, 0, 0, 0));
//
// // 4. 初始化机器时间线
// for (Machine machine : machines) {
// MachineTimeline timeline = machineScheduler.getOrCreateTimeline(machine);
// machine.setAvailability(timeline.getSegments());
// }
//
// // 5. 执行调度算法
// AlgorithmScheduler6 scheduler = new AlgorithmScheduler6(products, machines, orders, machineScheduler);
// List<ScheduleChromosome> scheduleChromosomes = scheduler.RunAll();
// List<PlanResourceTaskGanttVO> ganttlist = new ArrayList<>();
// long sceneId = 1L;
//
//
// for (ScheduleChromosome chromosome : scheduleChromosomes
// ) {
//
// // 转换基因列表和设备列表
// List<GenVO> genVOS = convertGeneListToGenVO(chromosome.getGenes(), baseTime);
// List<MachineVO> machineVOS = machines.stream()
// .map(this::convertToVO)
// .collect(Collectors.toList());
//
//// 按设备ID分组任务
// Map<Long, List<GenVO>> taskMap = genVOS.stream()
// .collect(Collectors.groupingBy(GenVO::getEquipId));
//
//// 为每个设备设置任务
// for (MachineVO machine : machineVOS) {
// Integer equipId = Integer.valueOf(machine.getEquipId());
// List<GenVO> tasks = taskMap.get(equipId);
//
// if (tasks != null) {
// // 按开始时间排序
// tasks.sort(Comparator.comparing(GenVO::getStartTime));
// machine.setTasks(tasks);
// } else {
// machine.setTasks(new ArrayList<>()); // 如果没有任务,设置为空列表
// }
// }
//
//// 现在machineVOS中的每个MachineVO都包含了对应的任务列表
// PlanResourceTaskGanttVO ganttVO = new PlanResourceTaskGanttVO()
// .setResources(machineVOS)
// .setEarliestTaskStartTime(LocalDateTime.of(2025, 9, 1, 0, 0, 0))
// .setLastTaskAssignmentTime(LocalDateTime.of(2025, 12, 1, 0, 0, 0))
// .setSceneId(sceneId);
// sceneId++;
// ganttlist.add(ganttVO);
// }
//
//
//
//
// return ganttlist;
//
// } catch (Exception e) {
// throw new RuntimeException("调度执行失败", e);
// }
return null;
}
public GenVO convertGeneToGenVO(Gene gene, LocalDateTime baseTime) {
......
server:
port: 8181 # 修改为你想要的端口号
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
mvc:
pathmatch:
matching-strategy: ant_path_matcher # Spring Boot 2.6+ 需要这个配置
redis:
host: 39.100.88.40
port: 6379
timeout: 120000
database: 10
password: redis@228!
# Swagger 配置
doc:
swagger-ui:
path: /swagger-ui.html
api-docs:
path: /v3/api-docs
datasource:
dynamic:
primary: oracle
# 默认数据源
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密码
# Oracle数据源
oracle:
driver-class-name: oracle.jdbc.OracleDriver
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
# 文件上传配置
file:
upload:
dir: uploads
mybatis-plus:
mapper-locations: classpath:mapper/**/*.xml # Mapper XML路径
type-aliases-package: com.aps.entity # 实体类包路径
configuration:
# 开启自动转义关键字
auto-delimited-keywords: true
map-underscore-to-camel-case: true # 下划线转驼峰
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl # 打印SQL日志(调试用)
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启SQL打印
global-config:
db-config:
id-type: auto # 主键自增策略
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="LOG_PATH" value="./logs" />
<!-- 1. 控制台输出(开发用) -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{30}) - %msg%n%ex</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 2. 主日志文件(所有日志都放这里) -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 当前日志文件 -->
<file>${LOG_PATH}/aps.log</file>
<encoder>
<!-- 格式:时间 | 线程 | 级别 | 类名 | 消息 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | %thread | %-5level | %logger{50} | %msg%n%ex</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 滚动策略:按天滚动,最多保留30天 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/archived/aps.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<!-- 3. 错误日志文件(只记录ERROR级别) -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/aps_error.log</file>
<!-- 只记录ERROR级别 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | %thread | %logger{50} | %msg%n%ex</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/archived/aps_error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>60</maxHistory>
</rollingPolicy>
</appender>
<!-- 日志级别配置 -->
<!-- 根日志配置 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
<!-- 针对你的包结构进行配置 -->
<!-- 1. 算法包(你最关心的) -->
<logger name="com.aps.service.Algorithm" level="DEBUG">
<appender-ref ref="FILE"/>
</logger>
<!-- 2. 你的业务service包 -->
<logger name="com.aps.service" level="INFO">
<appender-ref ref="FILE"/>
</logger>
<!-- 3. 代码生成器 -->
<logger name="com.aps.service.codeGeneratorService" level="INFO">
<appender-ref ref="FILE"/>
</logger>
<!-- 4. Controller层 -->
<logger name="com.aps.controller" level="INFO">
<appender-ref ref="FILE"/>
</logger>
<!-- 5. Mapper层(SQL日志) -->
<logger name="com.aps.mapper" level="DEBUG">
<appender-ref ref="FILE"/>
</logger>
<!-- 6. 工具类 -->
<logger name="com.aps.common.util" level="INFO">
<appender-ref ref="FILE"/>
</logger>
<!-- 第三方框架日志控制 -->
<logger name="org.springframework" level="WARN"/>
<logger name="org.mybatis" level="WARN"/>
<logger name="com.zaxxer.hikari" level="WARN"/>
<!-- 根据不同环境调整 -->
<springProfile name="dev">
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
</root>
<logger name="com.aps" level="DEBUG"/>
</springProfile>
<springProfile name="prod">
<root level="WARN">
<appender-ref ref="FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
<!-- 生产环境关闭DEBUG日志 -->
<logger name="com.aps.mapper" level="WARN"/>
<logger name="com.aps.service.Algorithm" level="INFO"/>
</springProfile>
</configuration>
\ No newline at end of file
package com.aps.demo;
import com.aps.common.util.JsonFileReader;
import com.aps.entity.basic.ScheduleChromosome;
import com.aps.entity.basic.*;
import com.aps.service.plan.AlgorithmScheduler7;
import com.aps.service.plan.MachineSchedulerService;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
public class MachineSchedulerTest {
public static void main(String[] args) {
try {
// // 1. 读取JSON文件
// String jsonContent = new String(Files.readAllBytes(Paths.get("src/main/resources/machines.json")));
// // 2. 配置ObjectMapper支持Java 8时间API
// ObjectMapper objectMapper = new ObjectMapper();
// objectMapper.registerModule(new JavaTimeModule());
// objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
List<Machine> machines = JsonFileReader.readListFromResources("machines.json", Machine.class);
// 读取产品数据
List<Product> products = JsonFileReader.readListFromResources("products.json", Product.class);
// 读取订单数据
List<Order> orders = JsonFileReader.readListFromResources("orders.json", Order.class);
// 3. 反序列化JSON到Machine列表
// List<Machine> machines = objectMapper.readValue(jsonContent,
// objectMapper.getTypeFactory().constructCollectionType(List.class, Machine.class));
// 4. 创建节假日数据
List<Holiday> holidays = Arrays.asList(
new Holiday(LocalDateTime.of(2025, 10, 1, 0, 0),
LocalDateTime.of(2025, 10, 7, 23, 59))
);
// 5. 创建调度服务
MachineSchedulerService machineScheduler = new MachineSchedulerService(
holidays, LocalDateTime.of(2025, 10, 1, 0, 0, 0));
// 6. 为每个设备生成时间线
for (Machine machine : machines) {
System.out.println("处理设备: " + machine.getName());
MachineTimeline machineTimeline = machineScheduler.getOrCreateTimeline(machine);
machine.setAvailability(machineTimeline.getSegments());
// 打印结果
System.out.println("设备 " + machine.getName() + " 的时间段数量: " +
(machine.getAvailability() != null ? machine.getAvailability().size() : 0));
if (machine.getAvailability() != null && !machine.getAvailability().isEmpty()) {
System.out.println("时间段:");
machine.getAvailability().stream().forEach(segment -> {
System.out.printf(" %s - %s (%s)%n",
segment.getStart(), segment.getEnd(), segment.getType());
});
}
System.out.println("---");
}
// 7. 测试缓存功能
System.out.println("缓存中的设备数量: " + MachineSchedulerService.getCacheSize());
// 8. 测试特定设备的时间段生成
if (!machines.isEmpty()) {
Machine firstMachine = machines.get(0);
List<TimeSegment> segments = machineScheduler.generateTimeSegment(
firstMachine, LocalDateTime.of(2025, 10, 1, 0, 0, 0),0);
System.out.println("直接生成的时间段数量: " + segments.size());
}
AlgorithmScheduler7 scheduler = new AlgorithmScheduler7(products,machines,orders, machineScheduler);
List<ScheduleChromosome> scheduleChromosomes = scheduler.RunAll();
for (ScheduleChromosome run:scheduleChromosomes
) {
System.out.println("------------------"+run.getFitness()+"------------------");
scheduler.PrintScheduleSummary(run);
}
ScheduleChromosome run = scheduler.Run();
scheduler.PrintScheduleSummary(run);
System.out.println(run.getFitness());
System.out.println("------------------"+scheduleChromosomes.size()+"------------------");
} catch (Exception e) {
e.printStackTrace();
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment