设备日历和运算

parent fee62722
......@@ -8,7 +8,7 @@
<version>2.7.18</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<groupId>com.aps</groupId>
<artifactId>java-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo2</name>
......@@ -73,16 +73,11 @@
<version>10.2.1.jre8</version>
</dependency>
<!-- Swagger -->
<!-- SpringDoc OpenAPI -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
......@@ -120,8 +115,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>9</source>
<target>9</target>
<source>8</source>
<target>8</target>
<encoding>${project.build.sourceEncoding}</encoding>
<annotationProcessorPaths>
<path>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
package com.example;
package com.aps;
import org.mybatis.spring.annotation.MapperScan;
......@@ -7,11 +7,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.mapper") // 扫描Mapper接口
public class Demo1Application {
@MapperScan("com.aps.mapper") // 扫描Mapper接口
public class ApsApplication {
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
SpringApplication.run(ApsApplication.class, args);
}
}
\ No newline at end of file
package com.example;
package com.aps;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
......
package com.aps.common.util;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class FileHelper {
private static final String LOG_FILE_PATH = "schedule_log.txt";
public static void writeLogFile(String message) {
try (PrintWriter writer = new PrintWriter(new FileWriter(LOG_FILE_PATH, true))) {
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
writer.println("[" + timestamp + "] " + message);
} catch (IOException e) {
System.err.println("Failed to write log: " + e.getMessage());
}
}
}
\ No newline at end of file
package com.aps.common.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Scanner;
public class JsonFileReader {
public static <T> List<T> readListFromResources(String fileName, Class<T> clazz) throws IOException {
// 1. 从classpath读取文件
try (InputStream inputStream = JsonFileReader.class.getClassLoader().getResourceAsStream(fileName)) {
if (inputStream == null) {
throw new IOException("File not found in resources: " + fileName);
}
// 使用Scanner替代readAllBytes以兼容Java 8
Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A");
String jsonContent = scanner.hasNext() ? scanner.next() : "";
// 2. 配置ObjectMapper支持Java 8时间API
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 3. 反序列化JSON到对象列表
return objectMapper.readValue(jsonContent,
objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
}
}
}
\ No newline at end of file
package com.aps.common.util;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
@Builder
@ToString
@Accessors(chain = true)
@AllArgsConstructor
public class R<T> implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "code码,通常为0为正常,500为异常,其它code码见异常对照表,为规范code码报错异常操作。所有异常报错码以数据表映射相关")
@Getter
@Setter
private int code = 0;
@Getter
@Setter
@Schema(description = "返回code码为0 时, 默认为success,其它的情况为具体的消息")
private String msg = "success";
@Getter
@Setter
@Schema(description = "业务返回具体的数据", example = "示例值有很多,不一一列举")
private T data;
@Getter
@Setter
@Schema(description = "接口返回时间,默认带有毫秒数值,用于数据排查问题")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss SSS")
private LocalDateTime timestamp = LocalDateTime.now();
public static <T> R<T> ok() {
return restResult(null, 0, "success");
}
public static <T> R<T> ok(T data) {
return restResult(data, 0, "success");
}
public static <T> R<T> ok(T data, String msg) {
return restResult(data, 0, msg);
}
public static <T> R<T> failed() {
return restResult(null, 1, "error");
}
public static <T> R<T> failed(String msg) {
return restResult(null, 1, msg);
}
public static <T> R<T> failed(T data) {
return restResult(data, 1, "error");
}
public static <T> R<T> failed(T data, String msg) {
return restResult(data, 1, msg);
}
public static <T> R<T> failed(int code, String msg) {
return restResult(null, code, msg);
}
public static <T> R<T> failed(T data, int code) {
return restResult(data, code, null);
}
public static <T> R<T> failed(T data, int code, String msg) {
return restResult(data, code, msg);
}
public R() {
super();
}
public R(T data) {
super();
this.data = data;
}
public R(T data, String msg) {
super();
this.data = data;
this.msg = msg;
}
public R(Throwable e) {
super();
this.msg = e.getMessage();
this.code = 1;
}
private static <T> R<T> restResult(T data, int code, String msg) {
R<T> apiResult = new R<>();
apiResult.setCode(code);
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}
/**
* 判断是否成功
*
* @return
*/
public boolean is() {
return this.code == 0;
}
}
\ No newline at end of file
package com.aps.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.License;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("Demo2 API 文档")
.version("1.0.0")
.description("多数据库项目接口文档")
.contact(new Contact()
.name("APS Team")
.email("contact@example.com"))
.license(new License()
.name("Apache 2.0")
.url("http://www.apache.org/licenses/LICENSE-2.0.html")));
}
}
\ No newline at end of file
package com.example.controller;
package com.aps.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
......
package com.example.controller;
package com.aps.controller;
import com.example.entity.MaterialInfo;
import com.example.service.MaterialInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import com.aps.entity.MaterialInfo;
import com.aps.service.MaterialInfoService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
......@@ -20,19 +19,25 @@ import java.util.List;
*/
@RestController
@RequestMapping("/materialInfo")
@Api(tags = "测试接口")
@Tag(name = "test", description = "物料信息相关接口")
public class MaterialInfoController {
@Autowired
MaterialInfoService materialInfoService;
@ApiOperation(value = "获取物料列表", notes = "返回所有用物料据", httpMethod = "GET")
@RequestMapping("/test")
@Operation(summary = "获取单个物料信息", description = "返回第一个物料数据")
@GetMapping("/test")
public MaterialInfo test(){
List<MaterialInfo> list = materialInfoService.list();
// 检查列表是否为空,避免IndexOutOfBoundsException
if (list == null || list.isEmpty()) {
return null; // 自定义异常
}
return list.get(0);
}
}
\ No newline at end of file
package com.aps.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 班次表 前端控制器
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
@RestController
@RequestMapping("/mesShiftWorkSched")
public class MesShiftWorkSchedController {
}
\ No newline at end of file
package com.aps.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 前端控制器
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
@RestController
@RequestMapping("/planResource")
public class PlanResourceController {
}
\ No newline at end of file
package com.aps.controller;
import com.aps.entity.MaterialInfo;
import com.aps.entity.RoutingHeader;
import com.aps.service.MaterialInfoService;
import com.aps.service.RoutingHeaderService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-09
*/
@RestController
@RequestMapping("/routingHeader")
@Tag(name = "测试接口", description = "工艺相关接口")
public class RoutingHeaderController {
@Autowired
RoutingHeaderService routingHeaderService;
@Operation(summary = "获取工艺列表", description = "返回所有工艺数据")
@GetMapping("/test")
public RoutingHeader test(){
return routingHeaderService.list().get(0);
}
}
\ No newline at end of file
package com.aps.controller;
import com.aps.entity.*;
import com.aps.mapper.EquipinfoMapper;
import com.aps.mapper.MpsplannedorderMapper;
import com.aps.mapper.RoutingDetailMapper;
import com.aps.mapper.RoutingHeaderMapper;
import com.aps.service.*;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/schedule")
@Tag(name = "调度任务", description = "调度任务相关接口")
public class ScheduleController {
@Autowired
private ScheduleService schedulerService;
@Autowired
private MpsplannedorderService orderService;
@Autowired
private RoutingHeaderService processService;
@Autowired
private RoutingDetailService operationService;
@Autowired
private RoutingDetailEquipService operationEquipService;
@Autowired
private EquipinfoService equipmentService;
@GetMapping("/run")
public List<ScheduledTask> runScheduling() {
try {
// 获取routingid为'SY1102507-0056202车间-21号线'和'SY1102507-0060202车间-21号线'的待调度工单
List<Mpsplannedorder> orders = orderService.lambdaQuery()
.in(Mpsplannedorder::getRoutingid, "SY1102507-0056202车间-21号线", "SY1102507-0060202车间-21号线")
.list();
if (orders.isEmpty()) {
return new ArrayList<>();
}
// 提取所需的工艺ID列表
Set<String> routingIds = orders.stream()
.map(Mpsplannedorder::getRoutingid)
.collect(Collectors.toSet());
// 根据工单获取相关工艺
List<RoutingHeader> processes = processService.lambdaQuery()
.in(RoutingHeader::getCode, routingIds)
.list();
if (processes.isEmpty()) {
throw new RuntimeException("未找到相关工艺信息");
}
// 提取工艺ID用于后续查询
Set<Integer> processIds = processes.stream()
.map(RoutingHeader::getId)
.collect(Collectors.toSet());
// 根据工艺获取相关工序
List<RoutingDetail> operations = operationService.lambdaQuery()
.in(RoutingDetail::getRoutingHeaderId, processIds)
.orderByAsc(RoutingDetail::getTaskSeq)
.list();
if (operations.isEmpty()) {
throw new RuntimeException("未找到相关工序信息");
}
// 提取工序ID用于后续查询
Set<Integer> operationIds = operations.stream()
.map(RoutingDetail::getId)
.collect(Collectors.toSet());
// 获取相关工序设备关系
List<RoutingDetailEquip> operationEquipments = operationEquipService.lambdaQuery()
.in(RoutingDetailEquip::getRoutingDetailId, operationIds)
.list();
if (operationEquipments.isEmpty()) {
throw new RuntimeException("未找到工序设备关系信息");
}
// 提取设备ID用于后续查询
Set<Integer> equipmentIds = operationEquipments.stream()
.map(RoutingDetailEquip::getEquipId)
.collect(Collectors.toSet());
// 获取相关设备
List<Equipinfo> equipments = equipmentService.lambdaQuery()
.in(Equipinfo::getId, equipmentIds)
.list();
// 添加空值检查
if (orders == null || processes == null || operations == null || operationEquipments == null || equipments == null) {
throw new RuntimeException("获取调度数据失败,存在空数据");
}
List<ScheduledTask> result = schedulerService.scheduleOrders(
orders, processes, operations, operationEquipments, equipments);
return result;
} catch (Exception e) {
throw new RuntimeException("调度执行失败: " + e.getMessage(), e);
}
}
}
\ No newline at end of file
package com.aps.controller;
import com.aps.common.util.R;
import com.aps.entity.*;
import com.aps.entity.Gantt.PlanResourceTaskGanttVO;
import com.aps.entity.Gantt.SimpleEquipinfo;
import com.aps.service.*;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/ganttest")
@Tag(name = "甘特图", description = "甘特图相关接口")
public class ScheduleResultController {
@Autowired
private ScheduleService schedulerService;
@Autowired
private MpsplannedorderService orderService;
@Autowired
private RoutingHeaderService processService;
@Autowired
private RoutingDetailService operationService;
@Autowired
private RoutingDetailEquipService operationEquipService;
@Autowired
private EquipinfoService equipmentService;
@Operation(summary = "资源甘特图", description = "资源甘特图")
@GetMapping("/resource")
public R<PlanResourceTaskGanttVO> runScheduling() {
try {
// 获取routingid为'SY1102507-0056202车间-21号线'和'SY1102507-0060202车间-21号线'的待调度工单
List<Mpsplannedorder> orders = orderService.lambdaQuery()
.in(Mpsplannedorder::getRoutingid, "SY1102507-0056202车间-21号线", "SY1102507-0060202车间-21号线")
.list();
if (orders.isEmpty()) {
// 当没有订单时,返回空的甘特图VO
PlanResourceTaskGanttVO ganttVO = new PlanResourceTaskGanttVO()
.setTasks(new ArrayList<>())
.setResources(new ArrayList<>())
.setEarliestTaskStartTime(LocalDateTime.of(2025, 6, 1, 0, 0, 0))
.setLastTaskAssignmentTime(LocalDateTime.of(2025, 12, 1, 0, 0, 0));
return R.ok(ganttVO);
}
// 提取所需的工艺ID列表
Set<String> routingIds = orders.stream()
.map(Mpsplannedorder::getRoutingid)
.collect(Collectors.toSet());
// 根据工单获取相关工艺
List<RoutingHeader> processes = processService.lambdaQuery()
.in(RoutingHeader::getCode, routingIds)
.list();
if (processes.isEmpty()) {
return R.failed("未找到相关工艺信息");
}
// 提取工艺ID用于后续查询
Set<Integer> processIds = processes.stream()
.map(RoutingHeader::getId)
.collect(Collectors.toSet());
// 根据工艺获取相关工序
List<RoutingDetail> operations = operationService.lambdaQuery()
.in(RoutingDetail::getRoutingHeaderId, processIds)
.orderByAsc(RoutingDetail::getTaskSeq)
.list();
if (operations.isEmpty()) {
return R.failed("未找到相关工序信息");
}
// 提取工序ID用于后续查询
Set<Integer> operationIds = operations.stream()
.map(RoutingDetail::getId)
.collect(Collectors.toSet());
// 获取相关工序设备关系
List<RoutingDetailEquip> operationEquipments = operationEquipService.lambdaQuery()
.in(RoutingDetailEquip::getRoutingDetailId, operationIds)
.list();
if (operationEquipments.isEmpty()) {
return R.failed("未找到工序设备关系信息");
}
// 提取设备ID用于后续查询
Set<Integer> equipmentIds = operationEquipments.stream()
.map(RoutingDetailEquip::getEquipId)
.collect(Collectors.toSet());
// 获取相关设备
List<Equipinfo> equipments = equipmentService.lambdaQuery()
.in(Equipinfo::getId, equipmentIds)
.list();
// 添加空值检查
if (orders == null || processes == null || operations == null || operationEquipments == null || equipments == null) {
return R.failed("获取调度数据失败,存在空数据");
}
List<ScheduledTask> result = schedulerService.scheduleOrders(
orders, processes, operations, operationEquipments, equipments);
// 转换设备信息为简要信息,并关联相关任务
List<SimpleEquipinfo> simpleEquipinfos = equipments.stream()
.map(equip -> {
// 查找与该设备关联的任务
List<ScheduledTask> equipTasks = result.stream()
.filter(task -> task.getEquipId() != null && task.getEquipId().equals(equip.getId()))
.collect(Collectors.toList());
return new SimpleEquipinfo()
.setId(equip.getId())
.setEquipId(equip.getEquipId())
.setEquipName(equip.getEquipName())
.setTasks(equipTasks);
})
.collect(Collectors.toList());
// 设置甘特图VO的最早时间为2025年6月1日,最晚时间为2025年12月1日
PlanResourceTaskGanttVO ganttVO = new PlanResourceTaskGanttVO()
.setTasks(result)
.setResources(simpleEquipinfos)
.setEarliestTaskStartTime(LocalDateTime.of(2025, 6, 1, 0, 0, 0))
.setLastTaskAssignmentTime(LocalDateTime.of(2025, 12, 1, 0, 0, 0));
return R.ok(ganttVO);
} catch (Exception e) {
return R.failed("调度执行失败: " + e.getMessage());
}
}
}
\ No newline at end of file
package com.aps.controller;
import com.aps.common.util.R;
import com.aps.entity.Gantt.PlanResourceTaskGanttVO1;
import com.aps.entity.Gantt.SimpleEquipinfo1;
import com.aps.entity.Mpsplannedorder;
import com.aps.entity.RoutingDetail;
import com.aps.entity.RoutingDetailEquip;
import com.aps.entity.RoutingHeader;
import com.aps.entity.ScheduledTask;
import com.aps.entity.Equipinfo;
import com.aps.service.ScheduleService;
import com.aps.service.MpsplannedorderService;
import com.aps.service.RoutingHeaderService;
import com.aps.service.RoutingDetailService;
import com.aps.service.RoutingDetailEquipService;
import com.aps.service.EquipinfoService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/ganttest")
@Tag(name = "甘特图", description = "甘特图相关API")
public class ScheduleResultController1 {
@Autowired
private ScheduleService schedulerService;
@Autowired
private MpsplannedorderService orderService;
@Autowired
private RoutingHeaderService processService;
@Autowired
private RoutingDetailService operationService;
@Autowired
private RoutingDetailEquipService operationEquipService;
@Autowired
private EquipinfoService equipmentService;
@GetMapping("/resourcetest")
@Operation(summary = "资源甘特图", description = "获取资源甘特图数据")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "成功返回甘特图数据"),
@ApiResponse(responseCode = "500", description = "内部服务器错误")
})
public R<PlanResourceTaskGanttVO1> runScheduling() {
try {
// 获取routingid为'SY1102507-0056202车间-21号线'和'SY1102507-0060202车间-21号线'的待调度工单
List<Mpsplannedorder> orders = orderService.lambdaQuery()
.in(Mpsplannedorder::getRoutingid, "SY1102507-0056202车间-21号线", "SY1102507-0060202车间-21号线")
.list();
if (orders.isEmpty()) {
// 当没有订单时,返回空的甘特图VO
PlanResourceTaskGanttVO1 ganttVO = new PlanResourceTaskGanttVO1()
.setResources(new ArrayList<>())
.setEarliestTaskStartTime(LocalDateTime.of(2025, 6, 1, 0, 0, 0))
.setLastTaskAssignmentTime(LocalDateTime.of(2025, 12, 1, 0, 0, 0));
return R.ok(ganttVO);
}
// 提取所需的工艺ID列表
Set<String> routingIds = orders.stream()
.map(Mpsplannedorder::getRoutingid)
.collect(Collectors.toSet());
// 根据工单获取相关工艺
List<RoutingHeader> processes = processService.lambdaQuery()
.in(RoutingHeader::getCode, routingIds)
.list();
if (processes.isEmpty()) {
return R.failed("未找到相关工艺信息");
}
// 提取工艺ID用于后续查询
Set<Integer> processIds = processes.stream()
.map(RoutingHeader::getId)
.collect(Collectors.toSet());
// 根据工艺获取相关工序
List<RoutingDetail> operations = operationService.lambdaQuery()
.in(RoutingDetail::getRoutingHeaderId, processIds)
.orderByAsc(RoutingDetail::getTaskSeq)
.list();
if (operations.isEmpty()) {
return R.failed("未找到相关工序信息");
}
// 提取工序ID用于后续查询
Set<Integer> operationIds = operations.stream()
.map(RoutingDetail::getId)
.collect(Collectors.toSet());
// 获取相关工序设备关系
List<RoutingDetailEquip> operationEquipments = operationEquipService.lambdaQuery()
.in(RoutingDetailEquip::getRoutingDetailId, operationIds)
.list();
if (operationEquipments.isEmpty()) {
return R.failed("未找到工序设备关系信息");
}
// 提取设备ID用于后续查询
Set<Integer> equipmentIds = operationEquipments.stream()
.map(RoutingDetailEquip::getEquipId)
.collect(Collectors.toSet());
// 获取相关设备
List<Equipinfo> equipments = equipmentService.lambdaQuery()
.in(Equipinfo::getId, equipmentIds)
.list();
// 添加空值检查
if (orders == null || processes == null || operations == null || operationEquipments == null || equipments == null) {
return R.failed("获取调度数据失败,存在空数据");
}
List<ScheduledTask> result = schedulerService.scheduleOrders(
orders, processes, operations, operationEquipments, equipments);
// 转换设备信息为简要信息,并关联相关任务
List<SimpleEquipinfo1> simpleEquipinfos = equipments.stream()
.map(equip -> {
// 查找与该设备关联的任务
List<ScheduledTask> equipTasks = result.stream()
.filter(task -> task.getEquipId() != null && task.getEquipId().equals(equip.getId()))
.collect(Collectors.toList());
return new SimpleEquipinfo1()
.setId(equip.getId())
.setEquipId(equip.getEquipId())
.setEquipName(equip.getEquipName())
.setTasks(equipTasks);
})
.collect(Collectors.toList());
// 设置甘特图VO的最早时间为2025年6月1日,最晚时间为2025年12月1日
PlanResourceTaskGanttVO1 ganttVO = new PlanResourceTaskGanttVO1()
.setResources(simpleEquipinfos)
.setEarliestTaskStartTime(LocalDateTime.of(2025, 6, 1, 0, 0, 0))
.setLastTaskAssignmentTime(LocalDateTime.of(2025, 12, 1, 0, 0, 0));
return R.ok(ganttVO);
} catch (Exception e) {
return R.failed("调度执行失败: " + e.getMessage());
}
}
}
\ No newline at end of file
package com.example.controller.codeGeneratorController;
package com.aps.controller.codeGeneratorController;
import com.example.entity.codeGeneratorEntity.DatabaseConfig;
import com.example.entity.codeGeneratorEntity.GenerateRequest;
import com.example.service.codeGeneratorService.CodeGeneratorService;
import com.example.service.codeGeneratorService.DatabaseConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import com.aps.entity.codeGeneratorEntity.DatabaseConfig;
import com.aps.entity.codeGeneratorEntity.GenerateRequest;
import com.aps.service.codeGeneratorService.CodeGeneratorService;
import com.aps.service.codeGeneratorService.DatabaseConfigService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
......@@ -15,7 +15,7 @@ import java.util.Map;
@RestController
@RequestMapping("/api/code-generator")
@Api(tags = "代码生成器")
@Tag(name = "代码生成器", description = "代码生成相关接口")
public class CodeGeneratorController {
@Autowired
......@@ -25,7 +25,7 @@ public class CodeGeneratorController {
private DatabaseConfigService databaseConfigService;
@GetMapping("/config")
@ApiOperation("获取当前主数据源配置")
@Operation(summary = "获取当前主数据源配置", description = "获取当前主数据源配置")
public DatabaseConfig getDatabaseConfig() {
DatabaseConfig config = databaseConfigService.getPrimaryDatabaseConfig();
if (config == null) {
......@@ -35,7 +35,7 @@ public class CodeGeneratorController {
}
@GetMapping("/config/{dataSourceName}")
@ApiOperation("获取指定数据源配置")
@Operation(summary = "获取指定数据源配置", description = "获取指定数据源配置")
public DatabaseConfig getDatabaseConfig(@PathVariable String dataSourceName) {
DatabaseConfig config = databaseConfigService.getDatabaseConfig(dataSourceName);
if (config == null) {
......@@ -45,7 +45,7 @@ public class CodeGeneratorController {
}
@GetMapping("/tables/{dataSourceName}")
@ApiOperation("获取指定数据源的表列表")
@Operation(summary = "获取指定数据源的表列表", description = "获取指定数据源的表列表")
public List<String> getTables(@PathVariable String dataSourceName) {
DatabaseConfig config = databaseConfigService.getDatabaseConfig(dataSourceName);
if (config == null || config.getUrl() == null) {
......@@ -55,7 +55,7 @@ public class CodeGeneratorController {
}
@GetMapping("/test-connection/{dataSourceName}")
@ApiOperation("测试指定数据源连接")
@Operation(summary = "测试指定数据源连接", description = "测试指定数据源连接")
public String testConnection(@PathVariable String dataSourceName) {
DatabaseConfig config = databaseConfigService.getDatabaseConfig(dataSourceName);
if (config == null) {
......@@ -67,7 +67,7 @@ public class CodeGeneratorController {
}
@PostMapping("/generate/{dataSourceName}")
@ApiOperation("使用指定数据源生成代码")
@Operation(summary = "使用指定数据源生成代码", description = "使用指定数据源生成代码")
public String generateCode(@PathVariable String dataSourceName,
@RequestBody GenerateRequest request) {
try {
......
// MachineSchedulerController.java
package com.aps.controller.plan;
import com.aps.common.util.R;
import com.aps.entity.Schedule.PlanResourceTaskGanttVO;
import com.aps.service.plan.PlanSchedulerService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/gantt")
@Tag(name = "机器调度管理", description = "机器调度相关API")
public class PlanSchedulerController {
@Autowired
private PlanSchedulerService schedulingService;
@GetMapping("/scheduleresource")
@Operation(summary = "执行机器调度", description = "执行机器调度并返回甘特图数据")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "成功返回调度结果")
})
public R<List<PlanResourceTaskGanttVO>> runScheduling() {
List<PlanResourceTaskGanttVO> execute = schedulingService.execute();
return R.ok(execute);
}
@GetMapping("/health")
@Operation(summary = "健康检查", description = "检查服务是否正常运行")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "服务正常运行")
})
public String healthCheck() {
return "Machine Scheduler Service is running!";
}
}
\ No newline at end of file
package com.example.entity;
package com.aps.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
......
package com.aps.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
*
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-09
*/
@Getter
@Setter
public class Equipinfo implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private Integer shopId;
private String equipId;
private Integer equipType;
private String equipName;
private String equipStatus;
private String location;
private Integer shaftQty;
private BigDecimal capabilityValue;
private Integer securityclassid;
private String equipIp;
private String equipmentSc;
private Integer operatorId;
private String equipVersion;
private String equipSystem;
private String equipPic;
private Integer locationx;
private Integer isimportant;
private String filepath;
private LocalDate equipStatusUpdatetime;
private Integer hot;
private BigDecimal capacity;
private BigDecimal standard;
private String usedepartment;
private String installplace;
private String mainnumber;
private String manufacturer;
private String enabledate;
private String equipWinpwd;
private String equipWinname;
private String equipPort;
private Integer status;
private String property;
private LocalDate creationtime;
private Long creatoruserid;
private LocalDate lastmodificationtime;
private Long lastmodifieruserid;
private Boolean isdeleted;
private Long deleteruserid;
private LocalDate deletiontime;
private Integer head;
private Integer runningstatus;
private String systemInfo;
private Integer ismdc;
private Integer workPattern;
private String workPatternName;
private String measureUnitName;
private String currencyTypeName;
private String capacityTypeName;
private Integer measureUnit;
private Integer currencyType;
private Integer capacityType;
private String fequipid;
private BigDecimal minTime;
private BigDecimal minDurationTime;
private BigDecimal maxDurationTime;
private BigDecimal jpExpecationTime;
}
package com.aps.entity;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.Queue;
@Data
public class EquipmentScheduleState {
private Integer equipId; // 设备ID
private String equipName; // 设备名称
private Queue<ScheduledTask> taskQueue = new LinkedList<>(); // 任务队列
private LocalDateTime nextAvailableTime; // 下次可用时间
// 从Equipinfo初始化
public EquipmentScheduleState(Equipinfo equipinfo, LocalDateTime now) {
this.equipId = equipinfo.getId();
this.equipName = equipinfo.getEquipName(); // 添加设备名称
// 确保设备创建时间不为空
if (equipinfo.getCreationtime() != null) {
this.nextAvailableTime = equipinfo.getCreationtime().atStartOfDay();
} else {
this.nextAvailableTime = now != null ? now : LocalDateTime.now();
}
}
}
\ No newline at end of file
package com.aps.entity.Gantt;
import com.aps.entity.Equipinfo;
import com.aps.entity.ScheduledTask;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.apache.ibatis.type.Alias;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author jvs
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Schema(name = "排产结果可视化——资源任务甘特图", description = "排产结果可视化——资源任务甘特图")
@Alias("PlanResourceTaskGanttVO")
public class PlanResourceTaskGanttVO {
@Schema(description = "任务计划最早开始时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime earliestTaskStartTime;
@Schema(description = "最近任务派工截止时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime lastTaskAssignmentTime;
@Schema(description = "资源集合")
private List<SimpleEquipinfo> resources;
@Schema(description = "任务集合")
private List<ScheduledTask> tasks;
}
\ No newline at end of file
package com.aps.entity.Gantt;
import com.aps.entity.ScheduledTask;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.apache.ibatis.type.Alias;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author jvs
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Schema(name = "排产结果可视化——资源任务甘特图1", description = "排产结果可视化——资源任务甘特图1")
@Alias("PlanResourceTaskGanttVO1")
public class PlanResourceTaskGanttVO1 {
@Schema(description = "任务计划最早开始时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime earliestTaskStartTime;
@Schema(description = "最近任务派工截止时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime lastTaskAssignmentTime;
@Schema(description = "资源任务集合")
private List<SimpleEquipinfo1> resources;
}
\ No newline at end of file
package com.aps.entity.Gantt;
import com.aps.entity.ScheduledTask;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@Accessors(chain = true)
@Schema(name = "设备简要信息", description = "设备简要信息")
public class SimpleEquipinfo {
@Schema(description = "设备内部ID")
private Integer id;
@Schema(description = "设备编码")
private String equipId;
@Schema(description = "设备名称")
private String equipName;
@Schema(description = "关联的任务列表")
private List<ScheduledTask> tasks;
}
\ No newline at end of file
package com.aps.entity.Gantt;
import com.aps.entity.ScheduledTask;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@Accessors(chain = true)
@Schema(name = "设备简要信息", description = "设备简要信息")
public class SimpleEquipinfo1 {
@Schema(description = "设备内部ID")
private Integer id;
@Schema(description = "设备编码")
private String equipId;
@Schema(description = "设备名称")
private String equipName;
@Schema(description = "任务集合")
private List<ScheduledTask> tasks;
}
\ No newline at end of file
package com.example.entity;
package com.aps.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
......@@ -175,13 +175,13 @@ public class MaterialInfo implements Serializable {
private Long latest;
/**
* 是否创建原材料供应供应
* 是否创建原材料供应供应?
*/
@TableField("iscreatesupplyrouting")
private Long iscreatesupplyrouting;
/**
* 是否创建原材料检验供应
* 是否创建原材料检验供应?
*/
@TableField("iscreatecheckrouting")
private Long iscreatecheckrouting;
......@@ -250,13 +250,13 @@ public class MaterialInfo implements Serializable {
private String ser;
/**
* 是否共用
* 是否共用?
*/
@TableField("iscommon")
private Long iscommon;
/**
* 是否创建汇总供应
* 是否创建汇总供应?
*/
@TableField("iscreatepoolrouting")
private Long iscreatepoolrouting;
......@@ -358,7 +358,7 @@ public class MaterialInfo implements Serializable {
private Long smallpro;
/**
* 周三后排
* 周三后排?
*/
@TableField("isthree")
private Long isthree;
......@@ -403,13 +403,13 @@ public class MaterialInfo implements Serializable {
private String fullName;
/**
* 是否包含库存,默认1,1是,0否
* 是否包含库存,默认是,0否
*/
@TableField("is_include_store")
private Long isIncludeStore;
/**
* 是否平分到未来四周,默认0,0否,1是
* 是否平分到未来四周,默认0否,1是
*/
@TableField("is_average_four_week")
private Long isAverageFourWeek;
......
package com.aps.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
* 班次表
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
@Getter
@Setter
@TableName("mes_shift_work_sched")
public class MesShiftWorkSched implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 编号
*/
private String shiftWorkSchedCode;
/**
* 班次名称
*/
private String shiftName;
/**
* 班次开始时间
*/
private LocalDateTime shiftStart;
/**
* 班次结束时间
*/
private LocalDateTime shiftEnd;
/**
* 状态
*/
private Integer status;
/**
* 工作时长
*/
private Integer workingLong;
/**
* 班次id
*/
private Integer shiftWorkSchedPk1;
private LocalDateTime creationtime;
private Integer creatoruserid;
private LocalDateTime lastmodificationtime;
private Integer lastmodifieruserid;
private Integer isdeleted;
private LocalDateTime deletiontime;
private Integer deleteruserid;
/**
* 开始周几值
*/
private Integer startWeekDay;
/**
* 开始周几名称
*/
private String startWeekDayName;
/**
* 班次模板id
*/
private Integer weekWorkSchedId;
/**
* 结束周几值
*/
private Integer endWeekDay;
/**
* 结束周几名称
*/
private String endWeekDayName;
/**
* (字符duration)working_long
*/
private String strWorkingLong;
}
\ No newline at end of file
package com.aps.entity;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class Mpsplannedorder {
private String routingid;
private Integer quantity;
private LocalDateTime startdate;
private LocalDateTime end;
private LocalDateTime duadate;
private Integer orderid;
private String mpplanid;
private String id;
private Integer priority;
private LocalDateTime addtime;
private String serie;
private String bigorder;
private String unitid;
private String productid;
}
package com.aps.entity;
import lombok.Data;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
public class PlanResource {
private Integer id;
private String title;
private String code;
private String type1;
private Integer referenceId;
private Integer departId;
private String departTitle;
private Integer isimportant;
private BigDecimal capabilityValue;
private LocalDate creationtime;
private Long creatoruserid;
private LocalDate lastmodificationtime;
private Long lastmodifieruserid;
private Boolean isdeleted;
private LocalDate deletiontime;
private Long deleteruserid;
private String calId;
private String calName;
private String holidayCalId;
private String holidayCalName;
private String referenceCode;
private Integer equipTypeId;
private String equipType;
private Integer workSchedId;
private Long nrofunitsopen;
private Integer isstop;
private LocalDate stoptime;
private LocalDate stopendtime;
}
package com.aps.entity;
import lombok.Data;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
public class RoutingDetail {
private Integer id;
private LocalDate creationTime;
private Long creatorUserId;
private LocalDate lastModificationTime;
private Long lastModifierUserId;
private Boolean isDeleted;
private Long deleterUserId;
private LocalDate deletionTime;
private Integer classId;
private Integer routingHeaderId;
private String name;
private BigDecimal taskSeq;
private String description;
private String taskContent;
private Integer resourceId;
private Short resourceType;
private BigDecimal runtime;
private BigDecimal setupTime;
private BigDecimal transportTime;
private BigDecimal checkTime;
private Short checkFlag;
private BigDecimal efficiencyValue;
private BigDecimal singleOut;
private Short isOutside;
private Integer departmentId;
private Short isImportant;
private Integer milestoneId;
private Integer phaseId;
private Short status;
private String remark;
private String extend;
private BigDecimal outsideTime;
private BigDecimal performanceHours;
private String resourceCode;
private Integer isImportantResources;
private BigDecimal schedulingWorkingHours;
private BigDecimal realWorkingHours;
private BigDecimal realRuntime;
private BigDecimal performanceWorkingHours;
private Integer isParticipateIntime;
private String equipType;
private Integer equipTypeId;
private String note;
private String fileId;
private Integer measureUnit;
private String measureUnitName;
private BigDecimal afterProcessTime;
private Integer isGeneral;
private Integer canInterrupt;
private Integer canStartEarly;
private BigDecimal previousStartTimeBegin;
private String code;
private String tool;
private BigDecimal changeLineTime;
private Integer preDetailId;
private Integer connectType;
private Integer connectProperty;
private String connectTypeName;
private String connectPropertyName;
private String strSetupTime;
private Long isync;
private BigDecimal constTime;
private String usable;
private BigDecimal leadTime;
}
package com.aps.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
* 工序可用设备
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
@Getter
@Setter
@TableName("routing_detail_equip")
public class RoutingDetailEquip implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 创建时间
*/
private LocalDateTime creationtime;
/**
* 创建人
*/
private Integer creatoruserid;
/**
* 更新时间
*/
private LocalDateTime lastmodificationtime;
/**
* 更新人
*/
private Integer lastmodifieruserid;
/**
* 删除人
*/
private Integer isdeleted;
/**
* 删除时间
*/
private LocalDateTime deletiontime;
/**
* 删除人
*/
private Integer deleteruserid;
/**
* 设备类型
*/
private Integer type1;
/**
* 设备类型名
*/
private String typeName;
/**
* 设备名称
*/
private String name;
/**
* 产出数量
*/
private BigDecimal outputQuantity;
/**
* 计量单位
*/
private Integer measureUnit;
/**
* 计量单位名称
*/
private String measureUnitName;
/**
* 持续时间
*/
private BigDecimal duration;
private String exp1;
private String exp2;
/**
* 设备的类型:1--设备 2--班组
*/
private Integer exp3;
private Integer exp4;
/**
* 工序id
*/
private Integer routingDetailId;
/**
* 设备id
*/
private Integer equipId;
/**
* 工艺id
*/
private Integer routingHeaderId;
/**
* 单次批量产出
*/
private BigDecimal oneBatchQuantity;
private String strId;
}
\ No newline at end of file
package com.aps.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
*
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-09
*/
@Getter
@Setter
@TableName("routing_header")
public class RoutingHeader implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private LocalDate creationTime;
private Long creatorUserId;
private LocalDate lastModificationTime;
private Long lastModifierUserId;
private Integer deleterUserId;
private LocalDate deletionTime;
private Integer classId;
private String unicode;
private String name;
private String code;
private Integer productId;
private String version;
private Integer author;
private Integer departmentId;
private Integer isMain;
private Integer upId;
private Integer upDetailId;
private Integer routingType;
private Short status;
private Short approvalStatus;
private String remark;
private String approvalStatusRemark;
private String auditUserId1;
private String auditUserId2;
private Boolean isDeleted;
private Double platesnum;
private String isEffect;
private String versionnotes;
private Integer phase;
private Integer versionid;
private Integer isSendPpm;
private String fileId;
private String productName;
private String departmentName;
private String drawingNo;
private Integer productBomId;
private String routingChangeorderCode;
private Integer routingChangeheaderId;
private String auditUserId3;
private String materialId;
private Double outputQuantity;
private String routingTypeName;
private Double leadTime;
private Integer quintiqOrtems;
private Integer iscreatecheckrouting;
private Integer iscreateoutbom;
private Integer issupportingstore;
private Long isync;
private Integer routingUsetype;
private Integer linkroutingid;
private LocalDate effectEnd;
private Integer year;
private LocalDate effectBegin;
private Integer cost;
private Integer erptag;
private LocalDate createoutbomtime;
private Integer materialPlanType;
private BigDecimal pcost;
private Integer invisable;
}
package com.aps.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDate;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
*
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
@Getter
@Setter
@TableName("routing_step")
public class RoutingStep implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private LocalDate creationTime;
private Long creatorUserId;
private LocalDate lastModificationTime;
private Long lastModifierUserId;
private Long isDeleted;
private Long deleterUserId;
private LocalDate deletionTime;
private Integer routingHeaderId;
private Integer routingDetailId;
private Integer classId;
private Integer stepSeq;
private String name;
private String description;
private Short status;
private String remark;
private String stepContent;
private String extend;
}
package com.aps.entity.Schedule;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class GenVO {
private String orderId; // 工单ID
private Integer operationId; // 工序ID
private Integer equipId; // 设备ID
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startTime; // 实际开始时间
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime endTime; // 实际结束时间
private BigDecimal quantity; // 加工数量
private String operationName; // 工序名称
private String equipName; // 设备名称
}
\ No newline at end of file
package com.aps.entity.Schedule;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@Schema(name = "节假日信息", description = "节假日信息")
public class HolidayDTO {
@Schema(description = "开始时间")
private LocalDateTime start;
@Schema(description = "结束时间")
private LocalDateTime end;
}
\ No newline at end of file
package com.aps.entity.Schedule;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
public class MachineVO {
@Schema(description = "设备内部ID")
private Integer id;
@Schema(description = "设备编码")
private String equipId;
@Schema(description = "设备名称")
private String equipName;
@Schema(description = "任务集合")
private List<GenVO> tasks;
}
\ No newline at end of file
package com.aps.entity.Schedule;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.apache.ibatis.type.Alias;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author jvs
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Schema(name = "排产结果可视化——资源任务甘特图", description = "排产结果可视化——资源任务甘特图")
@Alias("SchedulePlanResourceTaskGanttVO")
public class PlanResourceTaskGanttVO {
@Schema(description = "任务计划最早开始时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime earliestTaskStartTime;
@Schema(description = "最近任务派工截止时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime lastTaskAssignmentTime;
@Schema(description = "资源任务集合")
private List<MachineVO> resources;
@Schema(description = "场景id")
private Long sceneId;
}
\ No newline at end of file
// ScheduleRequest.java
package com.aps.entity.Schedule;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Data
@Schema(name = "调度请求参数", description = "调度请求参数")
public class ScheduleRequest {
@Schema(description = "开始时间", example = "2025-10-01T00:00:00")
private LocalDateTime startTime;
@Schema(description = "节假日列表")
private List<HolidayDTO> holidays;
@Schema(description = "是否运行所有算法", example = "true")
private boolean runAll = false;
}
package com.aps.entity.Schedule;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
@Schema(name = "调度响应结果", description = "调度响应结果")
public class ScheduleResponse {
@Schema(description = "成功标志")
private boolean success;
@Schema(description = "返回消息")
private String message;
@Schema(description = "单个调度结果")
private ScheduleResult singleResult;
@Schema(description = "所有调度结果")
private List<ScheduleResultList> allResults;
@Schema(description = "缓存大小")
private Integer cacheSize;
}
\ No newline at end of file
package com.aps.entity.Schedule;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(name = "调度结果详情", description = "调度结果详情")
public class ScheduleResult {
@Schema(description = "适应度分数")
private Double fitness;
@Schema(description = "调度摘要")
private String summary;
@Schema(description = "算法运行ID")
private String runId;
}
\ No newline at end of file
package com.aps.entity.Schedule;
import com.aps.entity.basic.ScheduleChromosome;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(name = "调度结果", description = "调度结果")
public class ScheduleResultList {
@Schema(description = "计划详情")
private ScheduleChromosome chromosomeList;
@Schema(description = "运算ID")
private String runId;
}
\ No newline at end of file
package com.aps.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class ScheduledTask {
private String orderId; // 工单ID
private Integer operationId; // 工序ID
private Integer equipId; // 设备ID
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startTime; // 实际开始时间
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime endTime; // 实际结束时间
private BigDecimal quantity; // 加工数量
private BigDecimal utilization; // 加工效率
private String routeId; // 工艺路线ID
private String operationName; // 工序名称
private String equipName; // 设备名称
}
\ No newline at end of file
package com.aps.entity.basic;
import lombok.Data;
import java.util.List;
/**
* 基因表示
*/
@Data
public class Gene {
private int orderId;
private int productId;
private int operationId;
private int machineId;
private int startTime; // 相对开始时间(分钟)
private int endTime; // 相对结束时间(分钟)
private int batchSize; // 批次大小(订单可拆分)
private List<GeneDetail> geneDetails; // 时间详情
private int processingTime; // 绝对处理时间(分钟)
}
\ No newline at end of file
package com.aps.entity.basic;
import lombok.Data;
@Data
public class GeneDetail {
private String key;
private int startTime; // 相对开始时间(分钟)
private int endTime; // 相对结束时间(分钟)
}
\ No newline at end of file
package com.aps.entity.basic;
import java.time.LocalDateTime;
public class Holiday {
private LocalDateTime start;
private LocalDateTime end;
public Holiday() {}
public Holiday(LocalDateTime start, LocalDateTime end) {
this.start = start;
this.end = end;
}
public LocalDateTime getStart() {
return start;
}
public void setStart(LocalDateTime start) {
this.start = start;
}
public LocalDateTime getEnd() {
return end;
}
public void setEnd(LocalDateTime end) {
this.end = end;
}
}
\ No newline at end of file
package com.aps.entity.basic;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@Data
public class Machine {
private int id;
private String name;
private int earliestTime=0;
private int totalTaskTime=0;
private List<Shift> shifts;
private List<MaintenanceWindow> maintenanceWindows;
private List<TimeSegment> availability;
// 事件回调
private Consumer<Void> shiftsChanged;
private Consumer<Void> maintenanceWindowsChanged;
public Machine() {
this.shifts = new ArrayList<>();
this.maintenanceWindows = new ArrayList<>();
this.availability = new ArrayList<>();
}
public Machine(int id, String name) {
this();
this.id = id;
this.name = name;
}
// 添加维护窗口的方法
public void addMaintenanceWindow(MaintenanceWindow window) {
maintenanceWindows.add(window);
if (maintenanceWindowsChanged != null) {
maintenanceWindowsChanged.accept(null);
}
}
public void addShift(Shift shift) {
shifts.add(shift);
if (shiftsChanged != null) {
shiftsChanged.accept(null);
}
}
// 重写equals和hashCode方法,只基于id进行比较
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Machine machine = (Machine) o;
return id == machine.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
\ No newline at end of file
package com.aps.entity.basic;
import lombok.Data;
/**
* 设备选项
*/
@Data
public class MachineOption {
private int machineId;
private int processingTime; // 加工时间
private int setupTime; // 换型时间(如果与前一个产品不同)
}
\ No newline at end of file
package com.aps.entity.basic;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class MachineTimeline {
private int machineId;
private List<TimeSegment> segments;
private LocalDateTime validFrom;
private LocalDateTime validTo;
private LocalDateTime lastUpdated;
private int version = 1;
public MachineTimeline() {
this.segments = new ArrayList<>();
}
// Getters and Setters
public int getMachineId() {
return machineId;
}
public void setMachineId(int machineId) {
this.machineId = machineId;
}
public List<TimeSegment> getSegments() {
return segments;
}
public void setSegments(List<TimeSegment> segments) {
this.segments = segments;
}
public LocalDateTime getValidFrom() {
return validFrom;
}
public void setValidFrom(LocalDateTime validFrom) {
this.validFrom = validFrom;
}
public LocalDateTime getValidTo() {
return validTo;
}
public void setValidTo(LocalDateTime validTo) {
this.validTo = validTo;
}
public LocalDateTime getLastUpdated() {
return lastUpdated;
}
public void setLastUpdated(LocalDateTime lastUpdated) {
this.lastUpdated = lastUpdated;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MachineTimeline that = (MachineTimeline) o;
return machineId == that.machineId;
}
@Override
public int hashCode() {
return Objects.hash(machineId);
}
}
\ No newline at end of file
package com.aps.entity.basic;
import java.time.LocalDateTime;
import java.util.Objects;
public class MaintenanceWindow {
private LocalDateTime startTime;
private LocalDateTime endTime;
private String reason;
public MaintenanceWindow() {}
public MaintenanceWindow(LocalDateTime startTime, LocalDateTime endTime, String reason) {
this.startTime = startTime;
this.endTime = endTime;
this.reason = reason;
}
// Getters and Setters
public LocalDateTime getStartTime() {
return startTime;
}
public void setStartTime(LocalDateTime startTime) {
this.startTime = startTime;
}
public LocalDateTime getEndTime() {
return endTime;
}
public void setEndTime(LocalDateTime endTime) {
this.endTime = endTime;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MaintenanceWindow that = (MaintenanceWindow) o;
return Objects.equals(startTime, that.startTime) &&
Objects.equals(endTime, that.endTime);
}
@Override
public int hashCode() {
return Objects.hash(startTime, endTime);
}
}
\ No newline at end of file
package com.aps.entity.basic;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* 工序定义
*/
@Data
public class Operation {
@JsonProperty("id")
private int id;
@JsonProperty("productId")
private int productId;
@JsonProperty("sequence")
private int sequence; // 工序顺序
@JsonProperty("machineOptions")
private List<MachineOption> machineOptions; // 可选设备列表
@JsonProperty("isInterrupt")
private boolean isInterrupt = false; // 是否可中断,间缝插针
}
\ No newline at end of file
package com.aps.entity.basic;
import lombok.Data;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
/**
* 订单定义
*/
@Data
public class Order {
private int id;
private int productId;
private int quantity = 100; // 100个
private OffsetDateTime dueDate;
private LocalDateTime orderCompletion;
private double tardiness;
private int priority;
private boolean canSplit = false;
private boolean canInterrupt = false;
}
\ No newline at end of file
package com.aps.entity.basic;
import lombok.Data;
import java.util.List;
/**
* 产品定义
*/
@Data
public class Product {
private int id;
private String name;
private List<Operation> operations; // 工序列表,顺序代表工序顺序约束
}
\ No newline at end of file
package com.aps.entity.basic;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@Data
public class ScheduleChromosome {
private List<Gene> genes;
private List<Order> orders;
private List<Machine> machines;
private double fitness;
private double tardiness;
private Map<Integer, Double> objectiveValues;
private LocalDateTime baseTime;
public ScheduleChromosome() {
this.genes = new ArrayList<>();
this.objectiveValues = new HashMap<>();
}
public ScheduleChromosome(LocalDateTime baseTime) {
this();
this.baseTime = baseTime;
}
// 修改:放宽去重条件,包含更多关键信息
// public String getGenesStr() {
// return genes.stream()
// .sorted(Comparator.comparing(Gene::getOrderId)
// .thenComparing(Gene::getOperationId)
// .thenComparing(Gene::getStartTime))
// .map(p -> String.format("%d:%d:%d:%d:%d:%d",
// p.getOrderId(), p.getOperationId(), p.getMachineId(),
// p.getBatchSize(), p.getStartTime(), p.getEndTime()))
// .collect(Collectors.joining(";"));
// }
public String getGenesStr() {
return getGenes().stream()
.sorted(Comparator.comparing(Gene::getOrderId)
.thenComparing(Gene::getOperationId)
.thenComparing(Gene::getMachineId))
.map(g -> String.format("%d:%d:%d",
g.getOrderId(), g.getOperationId(), g.getMachineId()))
.collect(Collectors.joining("; "));
}
// 新增:生成更详细的键用于调试
public String getDetailedKey() {
return genes.stream()
.sorted(Comparator.comparing(Gene::getOrderId)
.thenComparing(Gene::getOperationId)
.thenComparing(Gene::getStartTime))
.map(g -> String.format("O%d-P%d-M%d-B%d-S%d-E%d",
g.getOrderId(), g.getOperationId(), g.getMachineId(),
g.getBatchSize(), g.getStartTime(), g.getEndTime()))
.collect(Collectors.joining("_"));
}
public List<Order> getOrders() {
return calTardiness();
}
// private List<Order> calTardiness() {
// tardiness = 0;
//
// for (Order order : orders) {
// final int orderId = order.getId();
// List<Gene> orderGroups = genes.stream()
// .filter(g -> g.getOrderId() == orderId)
// .collect(Collectors.toList());
//
// int orderCompletion = orderGroups.stream()
// .mapToInt(Gene::getEndTime)
// .max()
// .orElse(0);
//
// LocalDateTime orderCompletionTime = baseTime.plusMinutes(orderCompletion);
// order.setOrderCompletion(orderCompletionTime);
// order.setTardiness(0);
//
// if (orderCompletionTime.isAfter(order.getDueDate().toLocalDateTime())) {
// long diffHours = java.time.temporal.ChronoUnit.HOURS.between(order.getDueDate(), orderCompletionTime);
// double tardinessHours = diffHours + (double) java.time.temporal.ChronoUnit.MINUTES.between(order.getDueDate(), orderCompletionTime) % 60 / 60;
// order.setTardiness(tardinessHours);
// tardiness += tardinessHours;
// }
// }
// return orders;
// }
private List<Order> calTardiness() {
tardiness = 0;
for (Order order : orders) {
final int orderId = order.getId();
List<Gene> orderGroups = genes.stream()
.filter(g -> g.getOrderId() == orderId)
.collect(Collectors.toList());
int orderCompletion = orderGroups.stream()
.mapToInt(Gene::getEndTime)
.max()
.orElse(0);
LocalDateTime orderCompletionTime = baseTime.plusMinutes(orderCompletion);
order.setOrderCompletion(orderCompletionTime);
order.setTardiness(0);
// 修复:统一时间类型
LocalDateTime dueDateTime = order.getDueDate().toLocalDateTime();
if (orderCompletionTime.isAfter(dueDateTime)) {
// 方法1:使用分钟计算再转换为小时(推荐)
long totalMinutes = java.time.temporal.ChronoUnit.MINUTES.between(dueDateTime, orderCompletionTime);
double tardinessHours = totalMinutes / 60.0;
// 方法2:或者保持原有逻辑但使用统一的时间类型
// long diffHours = java.time.temporal.ChronoUnit.HOURS.between(dueDateTime, orderCompletionTime);
// long remainingMinutes = java.time.temporal.ChronoUnit.MINUTES.between(dueDateTime, orderCompletionTime) % 60;
// double tardinessHours = diffHours + (double) remainingMinutes / 60;
order.setTardiness(tardinessHours);
tardiness += tardinessHours;
}
}
return orders;
}
}
\ No newline at end of file
package com.aps.entity.basic;
public enum SegmentType {
REGULAR, // 常规班次
TEMP, // 临时班次(优先级高)
MAINTENANCE // 维护窗口(不可用)
}
\ No newline at end of file
package com.aps.entity.basic;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Set;
public class Shift {
private LocalTime startTime;
private LocalTime endTime;
private Set<Integer> days; // 改为Integer类型,便于JSON反序列化
private LocalDateTime shiftDate;
private boolean isTemporaryShift;
private int priority;
public Shift() {}
public LocalTime getStartTime() {
return startTime;
}
public void setStartTime(LocalTime startTime) {
this.startTime = startTime;
}
public LocalTime getEndTime() {
return endTime;
}
public void setEndTime(LocalTime endTime) {
this.endTime = endTime;
}
public Set<Integer> getDays() {
return days;
}
public void setDays(Set<Integer> days) {
this.days = days;
}
// 辅助方法:将数字转换为DayOfWeek
public Set<DayOfWeek> getDaysAsEnum() {
if (days == null) return null;
return days.stream()
.map(day -> DayOfWeek.of(day == 0 ? 7 : day)) // 处理周日(0)转换为7
.collect(java.util.stream.Collectors.toSet());
}
public LocalDateTime getShiftDate() {
return shiftDate;
}
public void setShiftDate(LocalDateTime shiftDate) {
this.shiftDate = shiftDate;
}
public boolean isTemporaryShift() {
return isTemporaryShift;
}
public void setTemporaryShift(boolean temporaryShift) {
isTemporaryShift = temporaryShift;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
}
\ No newline at end of file
package com.aps.entity.basic;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.UUID;
/**
* 时间段(设备的时间片段,标记可用/不可用状态)
*/
@Data
public class TimeSegment {
private String key = UUID.randomUUID().toString(); // 时间段唯一标识(如"Machine-1_20251008_1000")
private LocalDateTime start; // 时间段开始时间
private LocalDateTime end; // 时间段结束时间
private int earliestTime; // 该时段内最早可用时间(分钟)
private int totalTaskTime; // 该时段内已分配任务时间(分钟)
private SegmentType type; // 时间段类型(关联SegmentType枚举)
private boolean isHoliday; // 是否节假日(true=节假日)
private boolean isUsed; // 是否已被占用(true=已分配任务)
// 无参构造(Lombok默认生成)
public TimeSegment() {}
// 全参构造(按需添加)
public TimeSegment(LocalDateTime start, LocalDateTime end, SegmentType type, boolean isHoliday) {
this.start = start;
this.end = end;
this.type = type;
this.isHoliday = isHoliday;
}
public TimeSegment(String key, LocalDateTime start, LocalDateTime end, int earliestTime,
int totalTaskTime, SegmentType type, boolean isHoliday, boolean isUsed) {
this.key = key;
this.start = start;
this.end = end;
this.earliestTime = earliestTime;
this.totalTaskTime = totalTaskTime;
this.type = type;
this.isHoliday = isHoliday;
this.isUsed = isUsed;
}
}
\ No newline at end of file
package com.example.entity.codeGeneratorEntity;
package com.aps.entity.codeGeneratorEntity;
public class DatabaseConfig {
private String url;
......
package com.example.entity.codeGeneratorEntity;
package com.aps.entity.codeGeneratorEntity;
import java.util.List;
......
package com.example.generator;
package com.aps.generator;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
......@@ -21,7 +21,7 @@ public class CodeGenerator {
})
// 3. 包配置(指定代码存放的包路径)
.packageConfig(builder -> {
builder.parent("com.example") // 父包名
builder.parent("com.aps") // 父包名
.entity("entity") // 实体类包
.mapper("mapper") // Mapper接口包
.service("service") // Service接口包
......
package com.example.generator;
package com.aps.generator;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
......@@ -21,7 +21,7 @@ public class CodeGenerator1 {
})
// 3. 包配置
.packageConfig(builder -> {
builder.parent("com.example") // 父包名
builder.parent("com.aps") // 父包名
.entity("entity") // 实体类包
.mapper("mapper") // Mapper接口包
.service("service") // Service接口包
......@@ -56,6 +56,6 @@ public class CodeGenerator1 {
// 执行生成
.execute();
System.out.println("代码生成完成");
System.out.println("代码生成完成!");
}
}
\ No newline at end of file
package com.example.mapper;
package com.aps.mapper;
import com.example.entity.Department;
import com.aps.entity.Department;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
......
package com.aps.mapper;
import com.aps.entity.Equipinfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-09
*/
public interface EquipinfoMapper extends BaseMapper<Equipinfo> {
}
package com.example.mapper;
package com.aps.mapper;
import com.example.entity.MaterialInfo;
import com.aps.entity.MaterialInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
......
package com.aps.mapper;
import com.aps.entity.MesShiftWorkSched;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 班次表 Mapper 接口
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
public interface MesShiftWorkSchedMapper extends BaseMapper<MesShiftWorkSched> {
}
\ No newline at end of file
package com.aps.mapper;
import com.aps.entity.Mpsplannedorder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-14
*/
public interface MpsplannedorderMapper extends BaseMapper<Mpsplannedorder> {
}
package com.aps.mapper;
import com.aps.entity.PlanResource;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
public interface PlanResourceMapper extends BaseMapper<PlanResource> {
}
package com.aps.mapper;
import com.aps.entity.RoutingDetailEquip;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 工序可用设备 Mapper 接口
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
public interface RoutingDetailEquipMapper extends BaseMapper<RoutingDetailEquip> {
}
package com.aps.mapper;
import com.aps.entity.RoutingDetail;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-14
*/
public interface RoutingDetailMapper extends BaseMapper<RoutingDetail> {
}
package com.aps.mapper;
import com.aps.entity.RoutingHeader;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-09
*/
public interface RoutingHeaderMapper extends BaseMapper<RoutingHeader> {
}
package com.aps.mapper;
import com.aps.entity.RoutingStep;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
public interface RoutingStepMapper extends BaseMapper<RoutingStep> {
}
package com.example.service;
package com.aps.service;
import com.example.entity.Department;
import com.aps.entity.Department;
import com.baomidou.mybatisplus.extension.service.IService;
/**
......
package com.aps.service;
import com.aps.entity.Equipinfo;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-09
*/
public interface EquipinfoService extends IService<Equipinfo> {
}
\ No newline at end of file
package com.example.service;
package com.aps.service;
import com.example.entity.MaterialInfo;
import com.aps.entity.MaterialInfo;
import com.baomidou.mybatisplus.extension.service.IService;
/**
......
package com.aps.service;
import com.aps.entity.MesShiftWorkSched;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 班次表 服务类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
public interface MesShiftWorkSchedService extends IService<MesShiftWorkSched> {
}
\ No newline at end of file
package com.aps.service;
import com.aps.entity.Mpsplannedorder;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-14
*/
public interface MpsplannedorderService extends IService<Mpsplannedorder> {
}
\ No newline at end of file
package com.aps.service;
import com.aps.entity.PlanResource;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
public interface PlanResourceService extends IService<PlanResource> {
}
\ No newline at end of file
package com.aps.service;
import com.aps.entity.RoutingDetailEquip;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 工序可用设备 服务类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
public interface RoutingDetailEquipService extends IService<RoutingDetailEquip> {
}
\ No newline at end of file
package com.aps.service;
import com.aps.entity.RoutingDetail;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-14
*/
public interface RoutingDetailService extends IService<RoutingDetail> {
}
\ No newline at end of file
package com.aps.service;
import com.aps.entity.RoutingHeader;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-09
*/
public interface RoutingHeaderService extends IService<RoutingHeader> {
}
\ No newline at end of file
package com.aps.service;
import com.aps.entity.RoutingStep;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
public interface RoutingStepService extends IService<RoutingStep> {
}
\ No newline at end of file
package com.aps.service;
import com.aps.entity.*;
import java.util.List;
public interface ScheduleService {
public List<ScheduledTask> scheduleOrders( List<Mpsplannedorder> orders,
List<RoutingHeader> processHeaders,
List<RoutingDetail> operations,
List<RoutingDetailEquip> operationEquipments,
List<Equipinfo> equipments);
}
package com.example.service.codeGeneratorService;
package com.aps.service.codeGeneratorService;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import com.example.entity.codeGeneratorEntity.DatabaseConfig;
import com.aps.entity.codeGeneratorEntity.DatabaseConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
@Service
public class CodeGeneratorService {
......@@ -65,6 +67,9 @@ public class CodeGeneratorService {
.enableBaseColumnList();
})
.templateEngine(new VelocityTemplateEngine())
// .templateConfig(builder -> {
// builder.entity("/templates/entity.java.vm"); // 指向自定义模板
// })
.execute();
}
......
package com.example.service.codeGeneratorService;
package com.aps.service.codeGeneratorService;
import com.example.entity.codeGeneratorEntity.DatabaseConfig;
import com.aps.entity.codeGeneratorEntity.DatabaseConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.env.Environment;
......
package com.example.service.codeGeneratorService;
package com.aps.service.codeGeneratorService;
import com.example.entity.codeGeneratorEntity.DatabaseConfig;
import com.aps.entity.codeGeneratorEntity.DatabaseConfig;
import org.springframework.stereotype.Service;
import java.sql.Connection;
......@@ -25,7 +25,41 @@ public class DatabaseService {
DatabaseMetaData metaData = connection.getMetaData();
String[] types = {"TABLE"};
ResultSet resultSet = metaData.getTables(null, null, "%", types);
// 根据不同数据库类型设置不同的schemaPattern参数
String schemaPattern = null;
String catalog = null;
if ("oracle".equals(dbConfig.getDbType())) {
// Oracle数据库使用用户名作为schema
schemaPattern = dbConfig.getUsername().toUpperCase();
} else if ("mysql".equals(dbConfig.getDbType())) {
// MySQL从URL中提取数据库名作为catalog
String url = dbConfig.getUrl();
if (url.contains("?")) {
url = url.substring(0, url.indexOf("?"));
}
if (url.contains("/")) {
catalog = url.substring(url.lastIndexOf("/") + 1);
}
} else if ("sqlserver".equals(dbConfig.getDbType())) {
// SQL Server使用用户名作为schema,同时catalog设置为数据库名
schemaPattern = "dbo"; // SQL Server默认schema通常是dbo
// 从URL中提取数据库名作为catalog
String url = dbConfig.getUrl();
if (url.contains(";")) {
String[] parts = url.split(";");
for (String part : parts) {
if (part.toLowerCase().startsWith("databasename=")) {
catalog = part.substring(part.indexOf("=") + 1);
break;
}
}
}
}
ResultSet resultSet = metaData.getTables(catalog, schemaPattern, "%", types);
while (resultSet.next()) {
String tableName = resultSet.getString("TABLE_NAME");
......
package com.example.service.impl;
package com.aps.service.impl;
import com.example.entity.Department;
import com.example.mapper.DepartmentMapper;
import com.example.service.DepartmentService;
import com.aps.entity.Department;
import com.aps.mapper.DepartmentMapper;
import com.aps.service.DepartmentService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
......
package com.aps.service.impl;
import com.aps.entity.Equipinfo;
import com.aps.mapper.EquipinfoMapper;
import com.aps.service.EquipinfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-09
*/
@Service
public class EquipinfoServiceImpl extends ServiceImpl<EquipinfoMapper, Equipinfo> implements EquipinfoService {
}
\ No newline at end of file
package com.example.service.impl;
package com.aps.service.impl;
import com.example.entity.MaterialInfo;
import com.example.mapper.MaterialInfoMapper;
import com.example.service.MaterialInfoService;
import com.aps.entity.MaterialInfo;
import com.aps.mapper.MaterialInfoMapper;
import com.aps.service.MaterialInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
......
package com.aps.service.impl;
import com.aps.entity.MesShiftWorkSched;
import com.aps.mapper.MesShiftWorkSchedMapper;
import com.aps.service.MesShiftWorkSchedService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 班次表 服务实现类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
@Service
public class MesShiftWorkSchedServiceImpl extends ServiceImpl<MesShiftWorkSchedMapper, MesShiftWorkSched> implements MesShiftWorkSchedService {
}
\ No newline at end of file
package com.aps.service.impl;
import com.aps.entity.Mpsplannedorder;
import com.aps.mapper.MpsplannedorderMapper;
import com.aps.service.MpsplannedorderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-14
*/
@Service
public class MpsplannedorderServiceImpl extends ServiceImpl<MpsplannedorderMapper, Mpsplannedorder> implements MpsplannedorderService {
}
\ No newline at end of file
package com.aps.service.impl;
import com.aps.entity.PlanResource;
import com.aps.mapper.PlanResourceMapper;
import com.aps.service.PlanResourceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
@Service
public class PlanResourceServiceImpl extends ServiceImpl<PlanResourceMapper, PlanResource> implements PlanResourceService {
}
\ No newline at end of file
package com.aps.service.impl;
import com.aps.entity.RoutingDetailEquip;
import com.aps.mapper.RoutingDetailEquipMapper;
import com.aps.service.RoutingDetailEquipService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 工序可用设备 服务实现类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
@Service
public class RoutingDetailEquipServiceImpl extends ServiceImpl<RoutingDetailEquipMapper, RoutingDetailEquip> implements RoutingDetailEquipService {
}
\ No newline at end of file
package com.aps.service.impl;
import com.aps.entity.RoutingDetail;
import com.aps.mapper.RoutingDetailMapper;
import com.aps.service.RoutingDetailService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-14
*/
@Service
public class RoutingDetailServiceImpl extends ServiceImpl<RoutingDetailMapper, RoutingDetail> implements RoutingDetailService {
}
\ No newline at end of file
package com.aps.service.impl;
import com.aps.entity.RoutingHeader;
import com.aps.mapper.RoutingHeaderMapper;
import com.aps.service.RoutingHeaderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-09
*/
@Service
public class RoutingHeaderServiceImpl extends ServiceImpl<RoutingHeaderMapper, RoutingHeader> implements RoutingHeaderService {
}
\ No newline at end of file
package com.aps.service.impl;
import com.aps.entity.RoutingStep;
import com.aps.mapper.RoutingStepMapper;
import com.aps.service.RoutingStepService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author MyBatis-Plus
* @since 2025-10-10
*/
@Service
public class RoutingStepServiceImpl extends ServiceImpl<RoutingStepMapper, RoutingStep> implements RoutingStepService {
}
\ No newline at end of file
package com.aps.service.impl;
import com.aps.entity.*;
import com.aps.service.ScheduleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@Service
public class ScheduleServiceImpl implements ScheduleService {
private static final Logger logger = LoggerFactory.getLogger(ScheduleServiceImpl.class);
// 缓存设备速度计算结果
private final Map<RoutingDetailEquip, BigDecimal> equipmentSpeedCache = new ConcurrentHashMap<>();
@Override
public List<ScheduledTask> scheduleOrders(
List<Mpsplannedorder> orders,
List<RoutingHeader> processes,
List<RoutingDetail> operations,
List<RoutingDetailEquip> operationEquipments,
List<Equipinfo> equipments) {
long startTime = System.currentTimeMillis();
logger.info("开始调度,订单数量: {}", orders.size());
// 1. 初始化设备状态(只使用可用设备)
Map<Integer, EquipmentScheduleState> equipmentStates = equipments.stream()
.filter(e -> e.getStatus() != null && e.getStatus() == 0) // 只使用状态为0(可用)的设备
.collect(Collectors.toMap(
Equipinfo::getId,
e -> new EquipmentScheduleState(e, LocalDateTime.now()),
(existing, replacement) -> existing
));
// 2. 构建工序-设备关系映射
Map<Integer, List<RoutingDetailEquip>> operationToEquipments = operationEquipments.stream()
.collect(Collectors.groupingBy(RoutingDetailEquip::getRoutingDetailId));
// 3. 按优先级和截止时间排序工单
orders.sort(Comparator.comparing(Mpsplannedorder::getPriority)
.thenComparing(Mpsplannedorder::getDuadate));
List<ScheduledTask> scheduledTasks = new ArrayList<>();
// 4. 处理每个工单
for (Mpsplannedorder order : orders) {
try {
processOrder(order, processes, operations, operationToEquipments, equipmentStates, scheduledTasks);
} catch (Exception e) {
logger.error("工单 {} 调度失败: {}", order.getOrderid(), e.getMessage(), e);
throw new RuntimeException("工单调度失败: " + order.getOrderid(), e);
}
}
long endTime = System.currentTimeMillis();
logger.info("调度完成,生成任务数量: {}, 耗时: {}ms", scheduledTasks.size(), (endTime - startTime));
return scheduledTasks;
}
private void processOrder(Mpsplannedorder order,
List<RoutingHeader> processes,
List<RoutingDetail> operations,
Map<Integer, List<RoutingDetailEquip>> operationToEquipments,
Map<Integer, EquipmentScheduleState> equipmentStates,
List<ScheduledTask> scheduledTasks) {
// 1. 匹配工艺
RoutingHeader process = findProcess(order.getRoutingid(), processes);
// 2. 获取该工艺的所有工序(严格按taskSeq排序)
List<RoutingDetail> processOperations = operations.stream()
.filter(op -> op.getRoutingHeaderId() != null &&
op.getRoutingHeaderId().equals(process.getId()))
.sorted(Comparator.comparing(RoutingDetail::getTaskSeq))
.collect(Collectors.toList());
// 3. 记录上一道工序的结束时间(初始为工单计划开始时间)
LocalDateTime lastOperationEndTime = order.getStartdate();
// 4. 按顺序处理每道工序
for (RoutingDetail operation : processOperations) {
ScheduledTask task = scheduleOperation(
order,
operation,
operationToEquipments,
equipmentStates,
lastOperationEndTime
);
scheduledTasks.add(task);
lastOperationEndTime = task.getEndTime(); // 更新上一道工序的结束时间
}
}
private ScheduledTask scheduleOperation(Mpsplannedorder order,
RoutingDetail operation,
Map<Integer, List<RoutingDetailEquip>> operationToEquipments,
Map<Integer, EquipmentScheduleState> equipmentStates,
LocalDateTime lastOperationEndTime) {
// 1. 获取该工序可用的设备
List<RoutingDetailEquip> availableEquipments = operationToEquipments.getOrDefault(
operation.getId(), Collections.emptyList());
if (availableEquipments.isEmpty()) {
throw new RuntimeException("工序 " + operation.getId() + " 没有配置可用设备");
}
// 2. 选择最优设备(按产出速度降序)
RoutingDetailEquip selectedEquipment = selectFastestEquipment(availableEquipments);
EquipmentScheduleState equipmentState = equipmentStates.get(selectedEquipment.getEquipId());
if (equipmentState == null) {
throw new RuntimeException("设备 " + selectedEquipment.getEquipId() + " 状态异常");
}
// 3. 计算时间范围(关键修改:考虑工序顺序约束)
LocalDateTime startTime = calculateOperationStartTime(
lastOperationEndTime,
order.getStartdate(),
equipmentState.getNextAvailableTime()
);
LocalDateTime endTime = calculateOperationEndTime(
startTime,
order.getQuantity(),
selectedEquipment,
operation.getSetupTime()
);
// 4. 更新设备状态
equipmentState.getTaskQueue().add(new ScheduledTask()); // 只用于记录设备占用
equipmentState.setNextAvailableTime(endTime);
// 4. 创建任务
ScheduledTask task = new ScheduledTask();
task.setOrderId(order.getOrderid() != null ? order.getOrderid().toString() : "");
task.setOperationId(operation.getId());
task.setEquipId(selectedEquipment.getEquipId());
task.setEquipName(equipmentState.getEquipName()); // 设置设备名称
task.setStartTime(startTime);
task.setEndTime(endTime);
task.setQuantity(order.getQuantity() != null ? BigDecimal.valueOf(order.getQuantity()) : BigDecimal.ZERO);
task.setOperationName(operation.getName());
task.setRouteId(order.getRoutingid());
return task;
}
// 关键方法:计算工序的实际开始时间
private LocalDateTime calculateOperationStartTime(
LocalDateTime lastOperationEndTime, // 上一道工序的结束时间
LocalDateTime orderPlannedStartTime, // 工单计划开始时间
LocalDateTime equipmentAvailableTime // 设备可用时间
) {
// 取最晚的时间:上一道工序结束时间、工单计划开始时间、设备可用时间
return Collections.max(Arrays.asList(
lastOperationEndTime,
orderPlannedStartTime,
equipmentAvailableTime
));
}
// 计算工序结束时间
private LocalDateTime calculateOperationEndTime(
LocalDateTime startTime,
Integer quantity,
RoutingDetailEquip equipment,
BigDecimal setupTime
) {
// 总时间= (数量 × 单件时间) + 准备时间
BigDecimal totalHours = calculateTotalProcessingTime(quantity, equipment, setupTime);
return startTime.plusMinutes(totalHours.multiply(BigDecimal.valueOf(60)).longValue());
}
private BigDecimal calculateTotalProcessingTime(
Integer quantity,
RoutingDetailEquip equipment,
BigDecimal setupTime
) {
// 参数校验
if (equipment.getOutputQuantity() == null || equipment.getOutputQuantity().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("设备产出数量必须大于0");
}
if (equipment.getDuration() == null || equipment.getDuration().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("设备持续时间必须大于0");
}
if (quantity == null || quantity <= 0) {
throw new IllegalArgumentException("工单数量必须大于0");
}
// 单件时间(小时)= (持续时间(秒)/产出数量)/3600
BigDecimal singleItemTimeHours = equipment.getDuration()
.divide(equipment.getOutputQuantity(), 10, RoundingMode.HALF_UP)
.divide(BigDecimal.valueOf(3600), 10, RoundingMode.HALF_UP);
// 总加工时间= 数量 × 单件时间
BigDecimal processingTime = BigDecimal.valueOf(quantity)
.multiply(singleItemTimeHours);
// 加上准备时间(如果存在)
if (setupTime != null && setupTime.compareTo(BigDecimal.ZERO) > 0) {
return processingTime.add(setupTime);
}
return processingTime;
}
// 其他辅助方法保持不变
private RoutingDetailEquip selectFastestEquipment(List<RoutingDetailEquip> equipments) {
return equipments.stream()
.max(Comparator.comparing(this::calculateEquipmentSpeed))
.orElseThrow(() -> new RuntimeException("没有可用的设备"));
}
private BigDecimal calculateEquipmentSpeed(RoutingDetailEquip equipment) {
return equipmentSpeedCache.computeIfAbsent(equipment, eq -> {
if (eq.getOutputQuantity() == null || eq.getDuration() == null ||
eq.getOutputQuantity().compareTo(BigDecimal.ZERO) <= 0 ||
eq.getDuration().compareTo(BigDecimal.ZERO) <= 0) {
return BigDecimal.ZERO;
}
return eq.getOutputQuantity()
.divide(eq.getDuration(), 10, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(3600));
});
}
private RoutingHeader findProcess(String routingCode, List<RoutingHeader> processes) {
return processes.stream()
.filter(p -> p.getCode() != null && p.getCode().equals(routingCode))
.min(Comparator.comparing(RoutingHeader::getCreationTime))
.orElseThrow(() -> new RuntimeException("工艺不存在: " + routingCode));
}
}
\ 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 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 = 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<Integer> 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)
.thenComparingInt(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 = machineOption.getProcessingTime() * batchSize;
List<GeneDetail> geneDetails = GetNextAvailableTime(
machine, prevtime, -1, processingTime,
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() + processingTime);
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));
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().toLocalDateTime();
if (completionTime.isAfter(dueDateTime)) {
// 计算延迟小时数(修复时间计算)
long hours = ChronoUnit.HOURS.between(dueDateTime, completionTime);
long minutes = ChronoUnit.MINUTES.between(dueDateTime, completionTime) % 60;
tardiness += hours + (double) minutes / 60;
}
}
}
// 3. 总换型时间
double totalSetupTime = CalculateTotalSetupTime(chromosome);
// 4. 总流程时间
double totalFlowTime = CalculateTotalFlowTime(chromosome);
// 5. 机器负载均衡
double machineLoadBalance = CalculateMachineLoadBalance(chromosome);
// 存储目标值(键与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<Integer, List<Gene>> machineGroups = chromosome.getGenes().stream()
.collect(Collectors.groupingBy(Gene::getMachineId));
for (Map.Entry<Integer, 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<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(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,
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);
}
// ========================== FindEarliestStart(参数与C#一致) ==========================
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;
}
}
// ========================== CaldEarliestStart(参数与C#一致) ==========================
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;
}
// ========================== 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);
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.plusMinutes(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.entity.basic.*;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class MachineSchedulerService {
// 全局缓存(线程安全)
private static final ConcurrentHashMap<Integer, MachineTimeline> timelineCache = new ConcurrentHashMap<>();
private LocalDateTime currentTime;
private List<Holiday> holidays;
public MachineSchedulerService(List<Holiday> holidays, LocalDateTime currentTime) {
this.holidays = holidays != null ? holidays : new ArrayList<>();
this.currentTime = currentTime;
}
public MachineTimeline getOrCreateTimeline(Machine machine) {
int machineId = machine.getId();
// 尝试从缓存获取
MachineTimeline timeline = timelineCache.get(machineId);
if (timeline != null) {
// 检查有效期(4小时刷新)
if (Duration.between(timeline.getLastUpdated(), LocalDateTime.now()).toHours() < 4) {
return timeline;
}
}
// 创建新时间线(60天范围)
MachineTimeline newTimeline = generateTimeline(machine);
timelineCache.put(machineId, newTimeline);
return newTimeline;
}
private MachineTimeline generateTimeline(Machine machine) {
MachineTimeline timeline = new MachineTimeline();
timeline.setMachineId(machine.getId());
timeline.setValidFrom(currentTime);
timeline.setValidTo(currentTime.plusDays(60));
timeline.setLastUpdated(LocalDateTime.now());
LocalDate currentDate = currentTime.toLocalDate();
LocalDate endDate = currentTime.plusDays(60).toLocalDate();
List<TimeSegment> allSegments = new ArrayList<>();
while (!currentDate.isAfter(endDate)) {
// 检查是否在假期内
boolean isHolidayPeriod = isHoliday(currentDate);
// 生成当日时间段
List<TimeSegment> daySegments = calculateDaySegments(machine, currentDate, isHolidayPeriod);
allSegments.addAll(daySegments);
// 跳到下一天
currentDate = currentDate.plusDays(1);
}
// 合并连续时间段
timeline.setSegments(mergeSegments(allSegments));
// 处理维护窗口
if (machine.getMaintenanceWindows() != null && !timeline.getSegments().isEmpty()) {
LocalDateTime maxEnd = timeline.getSegments().stream()
.map(TimeSegment::getEnd)
.max(LocalDateTime::compareTo)
.orElse(LocalDateTime.MIN);
for (MaintenanceWindow maintenanceWindow : machine.getMaintenanceWindows()) {
if (maintenanceWindow.getStartTime().isAfter(maxEnd)) {
continue;
}
updateSegmentsForMaintenance(timeline.getSegments(),
maintenanceWindow.getStartTime(), maintenanceWindow.getEndTime());
}
}
return timeline;
}
public List<TimeSegment> generateTimeSegment(Machine machine, LocalDateTime currentTime) {
int machineId = machine.getId();
MachineTimeline timeline = timelineCache.get(machineId);
if (timeline == null) {
timeline = new MachineTimeline();
timeline.setMachineId(machine.getId());
timeline.setValidFrom(currentTime);
timeline.setValidTo(currentTime.plusDays(60));
timeline.setLastUpdated(LocalDateTime.now());
timeline.setSegments(new ArrayList<>());
} else {
timeline.setValidTo(currentTime.plusDays(60));
timeline.setLastUpdated(LocalDateTime.now());
}
LocalDate currentDate = currentTime.toLocalDate();
LocalDate endDate = currentTime.plusDays(60).toLocalDate();
List<TimeSegment> segments = new ArrayList<>();
while (!currentDate.isAfter(endDate)) {
// 检查是否在假期内
boolean isHolidayPeriod = isHoliday(currentDate);
// 生成当日时间段
List<TimeSegment> daySegments = calculateDaySegments(machine, currentDate, isHolidayPeriod);
segments.addAll(daySegments);
// 跳到下一天
currentDate = currentDate.plusDays(1);
}
// 处理维护窗口
if (machine.getMaintenanceWindows() != null && !segments.isEmpty()) {
LocalDateTime maxEnd = segments.stream()
.map(TimeSegment::getEnd)
.max(LocalDateTime::compareTo)
.orElse(LocalDateTime.MIN);
for (MaintenanceWindow maintenanceWindow : machine.getMaintenanceWindows()) {
if (maintenanceWindow.getStartTime().isAfter(maxEnd) ||
maintenanceWindow.getStartTime().isBefore(currentTime)) {
continue;
}
updateSegmentsForMaintenance(segments, maintenanceWindow.getStartTime(), maintenanceWindow.getEndTime());
}
}
// 添加新生成的段到时间线
timeline.getSegments().addAll(copyTimeSegments(segments));
// 合并连续时间段
timeline.setSegments(mergeSegments(timeline.getSegments()));
segments = mergeSegments(segments);
return segments;
}
private boolean isHoliday(LocalDate currentDate) {
for (Holiday holiday : holidays) {
LocalDateTime holidayStart = holiday.getStart();
LocalDateTime holidayEnd = holiday.getEnd();
if (holidayStart != null && holidayEnd != null) {
LocalDate holidayStartDate = holidayStart.toLocalDate();
LocalDate holidayEndDate = holidayEnd.toLocalDate();
if ((currentDate.isEqual(holidayStartDate) || currentDate.isAfter(holidayStartDate)) &&
(currentDate.isEqual(holidayEndDate) || currentDate.isBefore(holidayEndDate))) {
return true;
}
}
}
return false;
}
private List<TimeSegment> calculateDaySegments(Machine machine, LocalDate date, boolean isHoliday) {
List<TimeSegment> segments = new ArrayList<>();
if (isHoliday) {
// 假期:只处理特定日期的班次
List<Shift> shifts = machine.getShifts().stream()
.filter(s -> s.getDays() == null &&
s.getShiftDate() != null &&
s.getShiftDate().toLocalDate().equals(date))
.collect(Collectors.toList());
for (Shift shift : shifts) {
LocalDateTime shiftStart = date.atTime(shift.getStartTime());
LocalDateTime shiftEnd = shift.getEndTime().isBefore(shift.getStartTime()) ?
date.plusDays(1).atTime(shift.getEndTime()) :
date.atTime(shift.getEndTime());
segments.add(new TimeSegment(
shiftStart,
shiftEnd,
shift.isTemporaryShift() ? SegmentType.TEMP : SegmentType.REGULAR,
false
));
}
} else {
// 非假期:处理常规班次
List<Shift> shifts = machine.getShifts().stream()
.filter(s -> s.getDays() != null && containsDay(s.getDays(), date.getDayOfWeek()))
.collect(Collectors.toList());
for (Shift shift : shifts) {
LocalDateTime shiftStart = date.atTime(shift.getStartTime());
LocalDateTime shiftEnd = shift.getEndTime().isBefore(shift.getStartTime()) ?
date.plusDays(1).atTime(shift.getEndTime()) :
date.atTime(shift.getEndTime());
segments.add(new TimeSegment(
shiftStart,
shiftEnd,
shift.isTemporaryShift() ? SegmentType.TEMP : SegmentType.REGULAR,
false
));
}
}
return segments;
}
/**
* 检查班次是否包含指定的星期几
* JSON中的Day映射:0=周日, 1=周一, 2=周二, ..., 6=周六
* Java DayOfWeek: MONDAY=1, TUESDAY=2, ..., SUNDAY=7
*/
private boolean containsDay(Set<Integer> shiftDays, DayOfWeek targetDay) {
if (shiftDays == null || shiftDays.isEmpty()) {
return false;
}
// 将Java的DayOfWeek转换为JSON格式的数字
int targetDayValue = targetDay.getValue();
// 特殊处理周日:JSON中0表示周日,Java中Sunday是7
if (targetDay == DayOfWeek.SUNDAY) {
return shiftDays.contains(0) || shiftDays.contains(7);
}
return shiftDays.contains(targetDayValue);
}
private List<TimeSegment> mergeSegments(List<TimeSegment> segments) {
List<TimeSegment> maintenanceSegments = segments.stream()
.filter(t -> t.getType() == SegmentType.MAINTENANCE)
.collect(Collectors.toList());
List<TimeSegment> otherSegments = segments.stream()
.filter(t -> t.getType() != SegmentType.MAINTENANCE)
.collect(Collectors.toList());
// 按开始时间排序
otherSegments.sort(Comparator.comparing(TimeSegment::getStart));
Stack<TimeSegment> merged = new Stack<>();
for (TimeSegment seg : otherSegments) {
if (!merged.isEmpty() && !merged.peek().getEnd().isBefore(seg.getStart())) {
TimeSegment top = merged.peek();
if (top.getEnd().isBefore(seg.getEnd())) {
top.setEnd(seg.getEnd());
}
} else {
seg.setKey(UUID.randomUUID().toString());
merged.push(seg);
}
}
List<TimeSegment> result = new ArrayList<>(merged);
result.addAll(maintenanceSegments);
// 按开始时间排序
result.sort(Comparator.comparing(TimeSegment::getStart));
return result;
}
private void updateSegmentsForMaintenance(List<TimeSegment> segments, LocalDateTime maintenanceStart, LocalDateTime maintenanceEnd) {
if (segments.isEmpty()) return;
// 1. 二分查找
int firstIndex = binarySearchSegment(segments, maintenanceStart);
if (firstIndex == segments.size()) return;
// 2. 收集有交集的段
List<Integer> indicesToProcess = new ArrayList<>();
int i = firstIndex;
while (i < segments.size() && segments.get(i).getStart().isBefore(maintenanceEnd)) {
if (segments.get(i).getEnd().isAfter(maintenanceStart)) {
indicesToProcess.add(i);
}
i++;
}
// 3. 倒序处理
for (int j = indicesToProcess.size() - 1; j >= 0; j--) {
int index = indicesToProcess.get(j);
TimeSegment seg = segments.get(index);
LocalDateTime segStart = seg.getStart();
LocalDateTime segEnd = seg.getEnd();
// 完全覆盖
if ((maintenanceStart.isBefore(segStart) || maintenanceStart.equals(segStart)) &&
(maintenanceEnd.isAfter(segEnd) || maintenanceEnd.equals(segEnd))) {
segments.remove(index);
}
// 维护时间段在段内部
else if (maintenanceStart.isAfter(segStart) && maintenanceEnd.isBefore(segEnd)) {
// 分割:原段变成前半段 [segStart, maintenanceStart)
seg.setEnd(maintenanceStart);
// 添加后半段 [maintenanceEnd, segEnd)
segments.add(index + 1, new TimeSegment(
maintenanceEnd,
segEnd,
seg.getType(),
seg.isHoliday()
));
}
// 维护时间段覆盖开始部分
else if (maintenanceStart.isBefore(segStart) || maintenanceStart.equals(segStart)) {
// 保留:维护时间段之后的部分 [maintenanceEnd, segEnd)
seg.setStart(maintenanceEnd);
}
// 维护时间段覆盖结束部分
else if (maintenanceEnd.isAfter(segEnd) || maintenanceEnd.equals(segEnd)) {
// 保留:维护时间段之前的部分 [segStart, maintenanceStart)
seg.setEnd(maintenanceStart);
}
}
segments.add(new TimeSegment(
maintenanceStart,
maintenanceEnd,
SegmentType.MAINTENANCE,
false
));
}
private int binarySearchSegment(List<TimeSegment> segments, LocalDateTime maintenanceStart) {
int left = 0;
int right = segments.size() - 1;
int result = segments.size();
while (left <= right) {
int mid = left + (right - left) / 2;
TimeSegment seg = segments.get(mid);
if ((seg.getStart().isBefore(maintenanceStart) || seg.getStart().equals(maintenanceStart)) &&
maintenanceStart.isBefore(seg.getEnd())) {
return mid;
}
if (seg.getEnd().isBefore(maintenanceStart) || seg.getEnd().equals(maintenanceStart)) {
left = mid + 1;
} else {
result = mid;
right = mid - 1;
}
}
return result;
}
private List<TimeSegment> copyTimeSegments(List<TimeSegment> originals) {
return originals.stream()
.map(seg -> {
TimeSegment copy = new TimeSegment();
copy.setStart(seg.getStart());
copy.setEnd(seg.getEnd());
copy.setType(seg.getType());
copy.setHoliday(seg.isHoliday());
copy.setUsed(seg.isUsed());
copy.setKey(seg.getKey());
return copy;
})
.collect(Collectors.toList());
}
// 清理缓存的方法
public static void clearCache() {
timelineCache.clear();
}
public static int getCacheSize() {
return timelineCache.size();
}
}
\ No newline at end of file
package com.aps.service.plan;
import com.aps.common.util.JsonFileReader;
import com.aps.entity.Schedule.GenVO;
import com.aps.entity.Schedule.MachineVO;
import com.aps.entity.Schedule.PlanResourceTaskGanttVO;
import com.aps.entity.basic.*;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@Service
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<Integer, 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);
}
}
public GenVO convertGeneToGenVO(Gene gene, LocalDateTime baseTime) {
GenVO genVO = new GenVO();
genVO.setOrderId(String.valueOf(gene.getOrderId()));
genVO.setOperationId(gene.getOperationId());
genVO.setEquipId(gene.getMachineId());
genVO.setQuantity(BigDecimal.valueOf(gene.getBatchSize()));
genVO.setStartTime(baseTime.plusMinutes(gene.getStartTime()));
genVO.setEndTime(baseTime.plusMinutes(gene.getEndTime()));
genVO.setOperationName(""); // 从其他数据源获取
genVO.setEquipName(""); // 从其他数据源获取
return genVO;
}
// 批量转换
public List<GenVO> convertGeneListToGenVO(List<Gene> geneList, LocalDateTime baseTime) {
return geneList.stream()
.map(gene -> convertGeneToGenVO(gene, baseTime))
.collect(Collectors.toList());
}
private MachineVO convertToVO(Machine machine) {
MachineVO machineVO = new MachineVO();
machineVO.setId(machine.getId());
machineVO.setEquipId(String.valueOf(machine.getId()));
machineVO.setEquipName(machine.getName());
return machineVO;
}
}
\ No newline at end of file
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.controller")) // 修改为您的包名
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Demo2 API 文档")
.description("多数据库项目接口文档")
.version("1.0.0")
.build();
}
}
\ No newline at end of file
package com.example.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/test")
@Api(tags = "测试接口")
public class TestController {
@GetMapping("/hello")
@ApiOperation("Hello World 测试接口")
public String hello() {
return "Hello, Swagger!";
}
@GetMapping("/info")
@ApiOperation("获取应用信息")
public String info() {
return "Demo2 Application - Multi Database Project";
}
}
\ No newline at end of file
server:
port: 8181 # 修改为你想要的端口号
spring:
mvc:
......@@ -5,11 +7,15 @@ spring:
matching-strategy: ant_path_matcher # Spring Boot 2.6+ 需要这个配置
# Swagger 配置
swagger:
enabled: true
doc:
swagger-ui:
path: /swagger-ui.html
api-docs:
path: /v3/api-docs
datasource:
dynamic:
primary: sqlserver
primary: oracle
# 默认数据源
strict: false # 关闭严格模式
datasource:
......@@ -34,10 +40,11 @@ spring:
# MyBatis-Plus配置
mybatis-plus:
mapper-locations: classpath:mapper/**/*.xml # Mapper XML路径
type-aliases-package: com.example.entity # 实体类包路径
type-aliases-package: com.aps.entity # 实体类包路径
configuration:
map-underscore-to-camel-case: true # 下划线转驼峰
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志(调试用)
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
[{
"id": 1,
"name": "Machine-1",
"shifts": [{
"startTime": "08:00:00",
"endTime": "18:00:00",
"days": [1, 2, 3, 4, 5],
"shiftDate": "0001-01-01T00:00:00",
"temporaryShift": false,
"priority": 0
},{
"startTime": "18:00:00",
"endTime": "20:00:00",
"days": [1, 2, 3, 4, 5],
"shiftDate": "0001-01-01T00:00:00",
"temporaryShift": true,
"priority": 0
},{
"startTime": "08:00:00",
"endTime": "18:00:00",
"days": [0],
"shiftDate": "0001-01-01T00:00:00",
"temporaryShift": true,
"priority": 0
},{
"startTime": "08:00:00",
"endTime": "18:00:00",
"days": null,
"shiftDate": "2025-10-01T00:00:00",
"temporaryShift": true,
"priority": 0
}],
"maintenanceWindows": [{
"startTime": "2025-10-08T10:00:00",
"endTime": "2025-10-10T12:00:00"
}]
}, {
"id": 2,
"name": "Machine-2",
"shifts": [{
"startTime": "08:00:00",
"endTime": "18:00:00",
"days": [1, 2, 3,4,5],
"shiftDate": "0001-01-01T00:00:00",
"temporaryShift": false,
"priority": 0
}],
"maintenanceWindows": null
}, {
"id": 3,
"name": "Machine-3",
"shifts": [{
"startTime": "08:00:00",
"endTime": "18:00:00",
"days": [1, 2, 3, 4, 5],
"shiftDate": "0001-01-01T00:00:00",
"temporaryShift": false,
"priority": 0
}],
"maintenanceWindows": null
}, {
"id": 4,
"name": "Machine-4",
"shifts": [{
"startTime": "08:00:00",
"endTime": "18:00:00",
"days": [1, 2, 3, 4, 5],
"shiftDate": "0001-01-01T00:00:00",
"temporaryShift": false,
"priority": 0
}],
"maintenanceWindows": null
}, {
"id": 5,
"name": "Machine-5",
"shifts": [{
"startTime": "08:00:00",
"endTime": "18:00:00",
"days": [1, 2, 3, 4, 5],
"shiftDate": "0001-01-01T00:00:00",
"temporaryShift": false,
"priority": 0
}],
"maintenanceWindows": null
}]
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.DepartmentMapper">
<mapper namespace="com.aps.mapper.DepartmentMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.example.entity.Department">
<resultMap id="BaseResultMap" type="com.aps.entity.Department">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="organization_id" property="organizationId" />
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aps.mapper.EquipinfoMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.aps.entity.Equipinfo">
<id column="id" property="id" />
<result column="shop_id" property="shopId" />
<result column="equip_id" property="equipId" />
<result column="equip_type" property="equipType" />
<result column="equip_name" property="equipName" />
<result column="equip_status" property="equipStatus" />
<result column="location" property="location" />
<result column="shaft_qty" property="shaftQty" />
<result column="capability_value" property="capabilityValue" />
<result column="securityclassid" property="securityclassid" />
<result column="equip_ip" property="equipIp" />
<result column="equipment_sc" property="equipmentSc" />
<result column="operator_id" property="operatorId" />
<result column="equip_version" property="equipVersion" />
<result column="equip_system" property="equipSystem" />
<result column="equip_pic" property="equipPic" />
<result column="locationx" property="locationx" />
<result column="isimportant" property="isimportant" />
<result column="filepath" property="filepath" />
<result column="equip_status_updatetime" property="equipStatusUpdatetime" />
<result column="hot" property="hot" />
<result column="capacity" property="capacity" />
<result column="standard" property="standard" />
<result column="usedepartment" property="usedepartment" />
<result column="installplace" property="installplace" />
<result column="mainnumber" property="mainnumber" />
<result column="manufacturer" property="manufacturer" />
<result column="enabledate" property="enabledate" />
<result column="equip_winpwd" property="equipWinpwd" />
<result column="equip_winname" property="equipWinname" />
<result column="equip_port" property="equipPort" />
<result column="status" property="status" />
<result column="property" property="property" />
<result column="creationtime" property="creationtime" />
<result column="creatoruserid" property="creatoruserid" />
<result column="lastmodificationtime" property="lastmodificationtime" />
<result column="lastmodifieruserid" property="lastmodifieruserid" />
<result column="isdeleted" property="isdeleted" />
<result column="deleteruserid" property="deleteruserid" />
<result column="deletiontime" property="deletiontime" />
<result column="head" property="head" />
<result column="runningstatus" property="runningstatus" />
<result column="system_info" property="systemInfo" />
<result column="ismdc" property="ismdc" />
<result column="work_pattern" property="workPattern" />
<result column="work_pattern_name" property="workPatternName" />
<result column="measure_unit_name" property="measureUnitName" />
<result column="currency_type_name" property="currencyTypeName" />
<result column="capacity_type_name" property="capacityTypeName" />
<result column="measure_unit" property="measureUnit" />
<result column="currency_type" property="currencyType" />
<result column="capacity_type" property="capacityType" />
<result column="fequipid" property="fequipid" />
<result column="min_time" property="minTime" />
<result column="min_duration_time" property="minDurationTime" />
<result column="max_duration_time" property="maxDurationTime" />
<result column="jp_expecation_time" property="jpExpecationTime" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, shop_id, equip_id, equip_type, equip_name, equip_status, location, shaft_qty, capability_value, securityclassid, equip_ip, equipment_sc, operator_id, equip_version, equip_system, equip_pic, locationx, isimportant, filepath, equip_status_updatetime, hot, capacity, standard, usedepartment, installplace, mainnumber, manufacturer, enabledate, equip_winpwd, equip_winname, equip_port, status, property, creationtime, creatoruserid, lastmodificationtime, lastmodifieruserid, isdeleted, deleteruserid, deletiontime, head, runningstatus, system_info, ismdc, work_pattern, work_pattern_name, measure_unit_name, currency_type_name, capacity_type_name, measure_unit, currency_type, capacity_type, fequipid, min_time, min_duration_time, max_duration_time, jp_expecation_time
</sql>
</mapper>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.MaterialInfoMapper">
<mapper namespace="com.aps.mapper.MaterialInfoMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.example.entity.MaterialInfo">
<resultMap id="BaseResultMap" type="com.aps.entity.MaterialInfo">
<id column="id" property="id" />
<result column="creationtime" property="creationtime" />
<result column="creatoruserid" property="creatoruserid" />
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aps.mapper.MesShiftWorkSchedMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.aps.entity.MesShiftWorkSched">
<id column="id" property="id" />
<result column="shift_work_sched_code" property="shiftWorkSchedCode" />
<result column="shift_name" property="shiftName" />
<result column="shift_start" property="shiftStart" />
<result column="shift_end" property="shiftEnd" />
<result column="status" property="status" />
<result column="working_long" property="workingLong" />
<result column="shift_work_sched_pk1" property="shiftWorkSchedPk1" />
<result column="creationtime" property="creationtime" />
<result column="creatoruserid" property="creatoruserid" />
<result column="lastmodificationtime" property="lastmodificationtime" />
<result column="lastmodifieruserid" property="lastmodifieruserid" />
<result column="isdeleted" property="isdeleted" />
<result column="deletiontime" property="deletiontime" />
<result column="deleteruserid" property="deleteruserid" />
<result column="start_week_day" property="startWeekDay" />
<result column="start_week_day_name" property="startWeekDayName" />
<result column="week_work_sched_id" property="weekWorkSchedId" />
<result column="end_week_day" property="endWeekDay" />
<result column="end_week_day_name" property="endWeekDayName" />
<result column="str_working_long" property="strWorkingLong" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, shift_work_sched_code, shift_name, shift_start, shift_end, status, working_long, shift_work_sched_pk1, creationtime, creatoruserid, lastmodificationtime, lastmodifieruserid, isdeleted, deletiontime, deleteruserid, start_week_day, start_week_day_name, week_work_sched_id, end_week_day, end_week_day_name, str_working_long
</sql>
</mapper>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aps.mapper.MpsplannedorderMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.aps.entity.Mpsplannedorder">
<id column="ID" property="id" />
<result column="ROUTINGID" property="routingid" />
<result column="QUANTITY" property="quantity" />
<result column="STARTDATE" property="startdate" />
<result column="END" property="end" />
<result column="DUADATE" property="duadate" />
<result column="ORDERID" property="orderid" />
<result column="MPPLANID" property="mpplanid" />
<result column="PRIORITY" property="priority" />
<result column="ADDTIME" property="addtime" />
<result column="SERIE" property="serie" />
<result column="BIGORDER" property="bigorder" />
<result column="UNITID" property="unitid" />
<result column="PRODUCTID" property="productid" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
ROUTINGID, QUANTITY, STARTDATE, END, DUADATE, ORDERID, MPPLANID, ID, PRIORITY, ADDTIME, SERIE, BIGORDER, UNITID, PRODUCTID
</sql>
</mapper>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aps.mapper.PlanResourceMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.aps.entity.PlanResource">
<id column="id" property="id" />
<result column="title" property="title" />
<result column="code" property="code" />
<result column="type1" property="type1" />
<result column="reference_id" property="referenceId" />
<result column="depart_id" property="departId" />
<result column="depart_title" property="departTitle" />
<result column="isimportant" property="isimportant" />
<result column="capability_value" property="capabilityValue" />
<result column="creationtime" property="creationtime" />
<result column="creatoruserid" property="creatoruserid" />
<result column="lastmodificationtime" property="lastmodificationtime" />
<result column="lastmodifieruserid" property="lastmodifieruserid" />
<result column="isdeleted" property="isdeleted" />
<result column="deletiontime" property="deletiontime" />
<result column="deleteruserid" property="deleteruserid" />
<result column="cal_id" property="calId" />
<result column="cal_name" property="calName" />
<result column="holiday_cal_id" property="holidayCalId" />
<result column="holiday_cal_name" property="holidayCalName" />
<result column="reference_code" property="referenceCode" />
<result column="equip_type_id" property="equipTypeId" />
<result column="equip_type" property="equipType" />
<result column="work_sched_id" property="workSchedId" />
<result column="nrofunitsopen" property="nrofunitsopen" />
<result column="isstop" property="isstop" />
<result column="stoptime" property="stoptime" />
<result column="stopendtime" property="stopendtime" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, title, code, type1, reference_id, depart_id, depart_title, isimportant, capability_value, creationtime, creatoruserid, lastmodificationtime, lastmodifieruserid, isdeleted, deletiontime, deleteruserid, cal_id, cal_name, holiday_cal_id, holiday_cal_name, reference_code, equip_type_id, equip_type, work_sched_id, nrofunitsopen, isstop, stoptime, stopendtime
</sql>
</mapper>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aps.mapper.RoutingDetailEquipMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.aps.entity.RoutingDetailEquip">
<id column="id" property="id" />
<result column="creationtime" property="creationtime" />
<result column="creatoruserid" property="creatoruserid" />
<result column="lastmodificationtime" property="lastmodificationtime" />
<result column="lastmodifieruserid" property="lastmodifieruserid" />
<result column="isdeleted" property="isdeleted" />
<result column="deletiontime" property="deletiontime" />
<result column="deleteruserid" property="deleteruserid" />
<result column="type1" property="type1" />
<result column="type_name" property="typeName" />
<result column="name" property="name" />
<result column="output_quantity" property="outputQuantity" />
<result column="measure_unit" property="measureUnit" />
<result column="measure_unit_name" property="measureUnitName" />
<result column="duration" property="duration" />
<result column="exp1" property="exp1" />
<result column="exp2" property="exp2" />
<result column="exp3" property="exp3" />
<result column="exp4" property="exp4" />
<result column="routing_detail_id" property="routingDetailId" />
<result column="equip_id" property="equipId" />
<result column="routing_header_id" property="routingHeaderId" />
<result column="one_batch_quantity" property="oneBatchQuantity" />
<result column="str_id" property="strId" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, creationtime, creatoruserid, lastmodificationtime, lastmodifieruserid, isdeleted, deletiontime, deleteruserid, type1, type_name, name, output_quantity, measure_unit, measure_unit_name, duration, exp1, exp2, exp3, exp4, routing_detail_id, equip_id, routing_header_id, one_batch_quantity, str_id
</sql>
</mapper>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aps.mapper.RoutingDetailMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.aps.entity.RoutingDetail">
<id column="id" property="id" />
<result column="creation_time" property="creationTime" />
<result column="creator_user_id" property="creatorUserId" />
<result column="last_modification_time" property="lastModificationTime" />
<result column="last_modifier_user_id" property="lastModifierUserId" />
<result column="is_deleted" property="isDeleted" />
<result column="deleter_user_id" property="deleterUserId" />
<result column="deletion_time" property="deletionTime" />
<result column="class_id" property="classId" />
<result column="routing_header_id" property="routingHeaderId" />
<result column="name" property="name" />
<result column="task_seq" property="taskSeq" />
<result column="description" property="description" />
<result column="task_content" property="taskContent" />
<result column="resource_id" property="resourceId" />
<result column="resource_type" property="resourceType" />
<result column="runtime" property="runtime" />
<result column="setup_time" property="setupTime" />
<result column="transport_time" property="transportTime" />
<result column="check_time" property="checkTime" />
<result column="check_flag" property="checkFlag" />
<result column="efficiency_value" property="efficiencyValue" />
<result column="single_out" property="singleOut" />
<result column="is_outside" property="isOutside" />
<result column="department_id" property="departmentId" />
<result column="is_important" property="isImportant" />
<result column="milestone_id" property="milestoneId" />
<result column="phase_id" property="phaseId" />
<result column="status" property="status" />
<result column="remark" property="remark" />
<result column="extend" property="extend" />
<result column="outside_time" property="outsideTime" />
<result column="performance_hours" property="performanceHours" />
<result column="resource_code" property="resourceCode" />
<result column="is_important_resources" property="isImportantResources" />
<result column="scheduling_working_hours" property="schedulingWorkingHours" />
<result column="real_working_hours" property="realWorkingHours" />
<result column="real_runtime" property="realRuntime" />
<result column="performance_working_hours" property="performanceWorkingHours" />
<result column="is_participate_intime" property="isParticipateIntime" />
<result column="equip_type" property="equipType" />
<result column="equip_type_id" property="equipTypeId" />
<result column="note" property="note" />
<result column="file_id" property="fileId" />
<result column="measure_unit" property="measureUnit" />
<result column="measure_unit_name" property="measureUnitName" />
<result column="after_process_time" property="afterProcessTime" />
<result column="is_general" property="isGeneral" />
<result column="can_interrupt" property="canInterrupt" />
<result column="can_start_early" property="canStartEarly" />
<result column="previous_start_time_begin" property="previousStartTimeBegin" />
<result column="code" property="code" />
<result column="tool" property="tool" />
<result column="change_line_time" property="changeLineTime" />
<result column="pre_detail_id" property="preDetailId" />
<result column="connect_type" property="connectType" />
<result column="connect_property" property="connectProperty" />
<result column="connect_type_name" property="connectTypeName" />
<result column="connect_property_name" property="connectPropertyName" />
<result column="str_setup_time" property="strSetupTime" />
<result column="isync" property="isync" />
<result column="const_time" property="constTime" />
<result column="usable" property="usable" />
<result column="lead_time" property="leadTime" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, creation_time, creator_user_id, last_modification_time, last_modifier_user_id, is_deleted, deleter_user_id, deletion_time, class_id, routing_header_id, name, task_seq, description, task_content, resource_id, resource_type, runtime, setup_time, transport_time, check_time, check_flag, efficiency_value, single_out, is_outside, department_id, is_important, milestone_id, phase_id, status, remark, extend, outside_time, performance_hours, resource_code, is_important_resources, scheduling_working_hours, real_working_hours, real_runtime, performance_working_hours, is_participate_intime, equip_type, equip_type_id, note, file_id, measure_unit, measure_unit_name, after_process_time, is_general, can_interrupt, can_start_early, previous_start_time_begin, code, tool, change_line_time, pre_detail_id, connect_type, connect_property, connect_type_name, connect_property_name, str_setup_time, isync, const_time, usable, lead_time
</sql>
</mapper>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aps.mapper.RoutingHeaderMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.aps.entity.RoutingHeader">
<id column="id" property="id" />
<result column="creation_time" property="creationTime" />
<result column="creator_user_id" property="creatorUserId" />
<result column="last_modification_time" property="lastModificationTime" />
<result column="last_modifier_user_id" property="lastModifierUserId" />
<result column="deleter_user_id" property="deleterUserId" />
<result column="deletion_time" property="deletionTime" />
<result column="class_id" property="classId" />
<result column="unicode" property="unicode" />
<result column="name" property="name" />
<result column="code" property="code" />
<result column="product_id" property="productId" />
<result column="version" property="version" />
<result column="author" property="author" />
<result column="department_id" property="departmentId" />
<result column="is_main" property="isMain" />
<result column="up_id" property="upId" />
<result column="up_detail_id" property="upDetailId" />
<result column="routing_type" property="routingType" />
<result column="status" property="status" />
<result column="approval_status" property="approvalStatus" />
<result column="remark" property="remark" />
<result column="approval_status_remark" property="approvalStatusRemark" />
<result column="audit_user_id1" property="auditUserId1" />
<result column="audit_user_id2" property="auditUserId2" />
<result column="is_deleted" property="isDeleted" />
<result column="platesnum" property="platesnum" />
<result column="is_effect" property="isEffect" />
<result column="versionnotes" property="versionnotes" />
<result column="phase" property="phase" />
<result column="versionid" property="versionid" />
<result column="is_send_ppm" property="isSendPpm" />
<result column="file_id" property="fileId" />
<result column="product_name" property="productName" />
<result column="department_name" property="departmentName" />
<result column="drawing_no" property="drawingNo" />
<result column="product_bom_id" property="productBomId" />
<result column="routing_changeorder_code" property="routingChangeorderCode" />
<result column="routing_changeheader_id" property="routingChangeheaderId" />
<result column="audit_user_id3" property="auditUserId3" />
<result column="material_id" property="materialId" />
<result column="output_quantity" property="outputQuantity" />
<result column="routing_type_name" property="routingTypeName" />
<result column="lead_time" property="leadTime" />
<result column="quintiq_ortems" property="quintiqOrtems" />
<result column="iscreatecheckrouting" property="iscreatecheckrouting" />
<result column="iscreateoutbom" property="iscreateoutbom" />
<result column="issupportingstore" property="issupportingstore" />
<result column="isync" property="isync" />
<result column="routing_usetype" property="routingUsetype" />
<result column="linkroutingid" property="linkroutingid" />
<result column="effect_end" property="effectEnd" />
<result column="year" property="year" />
<result column="effect_begin" property="effectBegin" />
<result column="cost" property="cost" />
<result column="erptag" property="erptag" />
<result column="createoutbomtime" property="createoutbomtime" />
<result column="material_plan_type" property="materialPlanType" />
<result column="pcost" property="pcost" />
<result column="invisable" property="invisable" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, creation_time, creator_user_id, last_modification_time, last_modifier_user_id, deleter_user_id, deletion_time, class_id, unicode, name, code, product_id, version, author, department_id, is_main, up_id, up_detail_id, routing_type, status, approval_status, remark, approval_status_remark, audit_user_id1, audit_user_id2, is_deleted, platesnum, is_effect, versionnotes, phase, versionid, is_send_ppm, file_id, product_name, department_name, drawing_no, product_bom_id, routing_changeorder_code, routing_changeheader_id, audit_user_id3, material_id, output_quantity, routing_type_name, lead_time, quintiq_ortems, iscreatecheckrouting, iscreateoutbom, issupportingstore, isync, routing_usetype, linkroutingid, effect_end, year, effect_begin, cost, erptag, createoutbomtime, material_plan_type, pcost, invisable
</sql>
</mapper>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aps.mapper.RoutingStepMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.aps.entity.RoutingStep">
<id column="id" property="id" />
<result column="creation_time" property="creationTime" />
<result column="creator_user_id" property="creatorUserId" />
<result column="last_modification_time" property="lastModificationTime" />
<result column="last_modifier_user_id" property="lastModifierUserId" />
<result column="is_deleted" property="isDeleted" />
<result column="deleter_user_id" property="deleterUserId" />
<result column="deletion_time" property="deletionTime" />
<result column="routing_header_id" property="routingHeaderId" />
<result column="routing_detail_id" property="routingDetailId" />
<result column="class_id" property="classId" />
<result column="step_seq" property="stepSeq" />
<result column="name" property="name" />
<result column="description" property="description" />
<result column="status" property="status" />
<result column="remark" property="remark" />
<result column="step_content" property="stepContent" />
<result column="extend" property="extend" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, creation_time, creator_user_id, last_modification_time, last_modifier_user_id, is_deleted, deleter_user_id, deletion_time, routing_header_id, routing_detail_id, class_id, step_seq, name, description, status, remark, step_content, extend
</sql>
</mapper>
\ No newline at end of file
[
{
"id": 1,
"productId": 1,
"quantity": 50,
"dueDate": "2025-10-27T16:21:31.0756427+08:00",
"priority": 1,
"canSplit": false,
"canInterrupt": false
},
{
"id": 2,
"productId": 1,
"quantity": 100,
"dueDate": "2025-10-18T16:21:31.0756427+08:00",
"priority": 2,
"canSplit": false,
"canInterrupt": false
},
{
"id": 3,
"productId": 2,
"quantity": 100,
"dueDate": "2025-10-31T16:21:31.0756427+08:00",
"priority": 2,
"canSplit": false,
"canInterrupt": false
}
]
\ No newline at end of file
[
{
"id": 1,
"name": "Product-1",
"operations": [
{
"id": 1,
"productId": 1,
"sequence": 1,
"machineOptions": [
{
"machineId": 2,
"processingTime": 11,
"setupTime": 59
}
],
"isInterrupt": false
},
{
"id": 2,
"productId": 1,
"sequence": 2,
"machineOptions": [
{
"machineId": 3,
"processingTime": 5,
"setupTime": 45
},
{
"machineId": 5,
"processingTime": 21,
"setupTime": 11
}
],
"isInterrupt": false
},
{
"id": 3,
"productId": 1,
"sequence": 3,
"machineOptions": [
{
"machineId": 5,
"processingTime": 6,
"setupTime": 40
}
],
"isInterrupt": false
},
{
"id": 4,
"productId": 1,
"sequence": 4,
"machineOptions": [
{
"machineId": 1,
"processingTime": 19,
"setupTime": 48
},
{
"machineId": 4,
"processingTime": 20,
"setupTime": 51
}
],
"isInterrupt": false
}
]
},
{
"id": 2,
"name": "Product-2",
"operations": [
{
"id": 1,
"productId": 2,
"sequence": 1,
"machineOptions": [
{
"machineId": 1,
"processingTime": 5,
"setupTime": 30
}
],
"isInterrupt": false
},
{
"id": 2,
"productId": 2,
"sequence": 2,
"machineOptions": [
{
"machineId": 2,
"processingTime": 22,
"setupTime": 47
}
],
"isInterrupt": false
},
{
"id": 3,
"productId": 2,
"sequence": 3,
"machineOptions": [
{
"machineId": 1,
"processingTime": 8,
"setupTime": 35
},
{
"machineId": 5,
"processingTime": 28,
"setupTime": 31
}
],
"isInterrupt": false
}
]
}
]
\ No newline at end of file
......@@ -348,7 +348,7 @@
<div class="mb-3">
<label class="form-label fw-bold"><i class="fas fa-code me-2"></i>包名</label>
<input type="text" class="form-control" id="packageName" value="com.example"
<input type="text" class="form-control" id="packageName" value="com.aps"
placeholder="例如: com.company.project">
</div>
......
package ${package.Entity};
import lombok.Data;
#foreach($pkg in ${table.importPackages})
#if(!$pkg.startsWith("java.lang."))
import ${pkg};
#end
#end
@Data
public class ${entity} {
#foreach($field in ${table.fields})
private ${field.propertyType} ${field.propertyName};
#end
}
\ No newline at end of file
package com.example.demo;
package com.aps.demo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
......
package com.aps.demo;
import com.aps.common.util.JsonFileReader;
import com.aps.entity.basic.*;
import com.aps.service.plan.AlgorithmScheduler6;
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));
System.out.println("直接生成的时间段数量: " + segments.size());
}
AlgorithmScheduler6 scheduler = new AlgorithmScheduler6(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