Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
H
HYH.APSJ
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
佟礼
HYH.APSJ
Commits
2d7e8ff6
Commit
2d7e8ff6
authored
Nov 14, 2025
by
DESKTOP-VKRD9QF\Administration
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
设备日历接口修改
parent
1dd2520c
Show whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
1824560 additions
and
31 deletions
+1824560
-31
schedule_log.txt
schedule_log.txt
+1821773
-0
JsonFileReader.java
src/main/java/com/aps/common/util/JsonFileReader.java
+30
-0
CorsConfig.java
src/main/java/com/aps/config/CorsConfig.java
+39
-0
FileUploadController.java
...n/java/com/aps/controller/gantt/FileUploadController.java
+124
-0
ResourceGanttController.java
...ava/com/aps/controller/gantt/ResourceGanttController.java
+315
-0
ProductGanttVO.java
src/main/java/com/aps/entity/Gantt/ProductGanttVO.java
+61
-0
ResourceGanttVO.java
src/main/java/com/aps/entity/Gantt/ResourceGanttVO.java
+52
-0
TaskVO.java
src/main/java/com/aps/entity/Gantt/TaskVO.java
+110
-0
Gene.java
src/main/java/com/aps/entity/basic/Gene.java
+15
-0
Holiday.java
src/main/java/com/aps/entity/basic/Holiday.java
+17
-0
MachineOption.java
src/main/java/com/aps/entity/basic/MachineOption.java
+3
-0
MaintenanceWindow.java
src/main/java/com/aps/entity/basic/MaintenanceWindow.java
+20
-0
ScheduleChromosome.java
src/main/java/com/aps/entity/basic/ScheduleChromosome.java
+1
-1
Shift.java
src/main/java/com/aps/entity/basic/Shift.java
+34
-7
ShiftVO.java
src/main/java/com/aps/entity/basic/ShiftVO.java
+20
-0
AlgorithmScheduler6.java
src/main/java/com/aps/service/plan/AlgorithmScheduler6.java
+31
-2
AlgorithmScheduler7.java
src/main/java/com/aps/service/plan/AlgorithmScheduler7.java
+1685
-0
PlanResultService.java
src/main/java/com/aps/service/plan/PlanResultService.java
+171
-0
application.yml
src/main/resources/application.yml
+9
-1
machines.json
src/main/resources/machines.json
+18
-9
products.json
src/main/resources/products.json
+30
-10
MachineSchedulerTest.java
src/test/java/com/aps/demo/MachineSchedulerTest.java
+2
-1
No files found.
schedule_log.txt
View file @
2d7e8ff6
This source diff could not be displayed because it is too large. You can
view the blob
instead.
src/main/java/com/aps/common/util/JsonFileReader.java
View file @
2d7e8ff6
...
@@ -3,7 +3,9 @@ package com.aps.common.util;
...
@@ -3,7 +3,9 @@ package com.aps.common.util;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.databind.SerializationFeature
;
import
com.fasterxml.jackson.databind.SerializationFeature
;
import
com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
;
import
com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
;
import
com.fasterxml.jackson.databind.DeserializationFeature
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStream
;
import
java.nio.charset.StandardCharsets
;
import
java.nio.charset.StandardCharsets
;
...
@@ -27,10 +29,38 @@ public class JsonFileReader {
...
@@ -27,10 +29,38 @@ public class JsonFileReader {
ObjectMapper
objectMapper
=
new
ObjectMapper
();
ObjectMapper
objectMapper
=
new
ObjectMapper
();
objectMapper
.
registerModule
(
new
JavaTimeModule
());
objectMapper
.
registerModule
(
new
JavaTimeModule
());
objectMapper
.
disable
(
SerializationFeature
.
WRITE_DATES_AS_TIMESTAMPS
);
objectMapper
.
disable
(
SerializationFeature
.
WRITE_DATES_AS_TIMESTAMPS
);
// 添加更多配置确保字段正确映射
objectMapper
.
configure
(
DeserializationFeature
.
FAIL_ON_UNKNOWN_PROPERTIES
,
false
);
objectMapper
.
configure
(
DeserializationFeature
.
ACCEPT_EMPTY_STRING_AS_NULL_OBJECT
,
true
);
// 3. 反序列化JSON到对象列表
// 3. 反序列化JSON到对象列表
return
objectMapper
.
readValue
(
jsonContent
,
return
objectMapper
.
readValue
(
jsonContent
,
objectMapper
.
getTypeFactory
().
constructCollectionType
(
List
.
class
,
clazz
));
objectMapper
.
getTypeFactory
().
constructCollectionType
(
List
.
class
,
clazz
));
}
}
}
}
public
static
<
T
>
List
<
T
>
readListFromFile
(
String
filePath
,
Class
<
T
>
clazz
)
throws
IOException
{
// 1. 检查文件是否存在
File
file
=
new
File
(
filePath
);
if
(!
file
.
exists
())
{
throw
new
IOException
(
"File not found: "
+
filePath
);
}
// 2. 读取文件内容
Scanner
scanner
=
new
Scanner
(
file
,
StandardCharsets
.
UTF_8
.
name
()).
useDelimiter
(
"\\A"
);
String
jsonContent
=
scanner
.
hasNext
()
?
scanner
.
next
()
:
""
;
// 3. 配置ObjectMapper支持Java 8时间API
ObjectMapper
objectMapper
=
new
ObjectMapper
();
objectMapper
.
registerModule
(
new
JavaTimeModule
());
objectMapper
.
disable
(
SerializationFeature
.
WRITE_DATES_AS_TIMESTAMPS
);
// 添加更多配置确保字段正确映射
objectMapper
.
configure
(
DeserializationFeature
.
FAIL_ON_UNKNOWN_PROPERTIES
,
false
);
objectMapper
.
configure
(
DeserializationFeature
.
ACCEPT_EMPTY_STRING_AS_NULL_OBJECT
,
true
);
// 4. 反序列化JSON到对象列表
return
objectMapper
.
readValue
(
jsonContent
,
objectMapper
.
getTypeFactory
().
constructCollectionType
(
List
.
class
,
clazz
));
}
}
}
\ No newline at end of file
src/main/java/com/aps/config/CorsConfig.java
0 → 100644
View file @
2d7e8ff6
package
com
.
aps
.
config
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.web.cors.CorsConfiguration
;
import
org.springframework.web.cors.UrlBasedCorsConfigurationSource
;
import
org.springframework.web.filter.CorsFilter
;
import
org.springframework.web.servlet.config.annotation.CorsRegistry
;
import
org.springframework.web.servlet.config.annotation.WebMvcConfigurer
;
@Configuration
public
class
CorsConfig
implements
WebMvcConfigurer
{
@Override
public
void
addCorsMappings
(
CorsRegistry
registry
)
{
registry
.
addMapping
(
"/**"
)
.
allowedOriginPatterns
(
"*"
)
// 修改这里
.
allowedMethods
(
"GET"
,
"POST"
,
"PUT"
,
"DELETE"
,
"OPTIONS"
)
.
allowedHeaders
(
"*"
)
.
allowCredentials
(
true
)
.
maxAge
(
3600
);
}
@Bean
public
CorsFilter
corsFilter
()
{
UrlBasedCorsConfigurationSource
source
=
new
UrlBasedCorsConfigurationSource
();
CorsConfiguration
config
=
new
CorsConfiguration
();
// 允许所有域名访问,生产环境建议指定具体域名
config
.
addAllowedOriginPattern
(
"*"
);
config
.
addAllowedHeader
(
"*"
);
config
.
addAllowedMethod
(
"*"
);
config
.
setAllowCredentials
(
true
);
config
.
setMaxAge
(
3600L
);
source
.
registerCorsConfiguration
(
"/**"
,
config
);
return
new
CorsFilter
(
source
);
}
}
\ No newline at end of file
src/main/java/com/aps/controller/gantt/FileUploadController.java
0 → 100644
View file @
2d7e8ff6
package
com
.
aps
.
controller
.
gantt
;
import
com.aps.common.util.R
;
import
io.swagger.v3.oas.annotations.tags.Tag
;
import
io.swagger.v3.oas.annotations.Operation
;
import
io.swagger.v3.oas.annotations.Parameter
;
import
io.swagger.v3.oas.annotations.parameters.RequestBody
;
import
io.swagger.v3.oas.annotations.media.Content
;
import
io.swagger.v3.oas.annotations.media.Schema
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RequestParam
;
import
org.springframework.web.bind.annotation.RestController
;
import
org.springframework.web.multipart.MultipartFile
;
import
javax.annotation.PostConstruct
;
import
java.io.File
;
import
java.io.IOException
;
import
java.nio.file.Paths
;
@Slf4j
@RestController
@RequestMapping
(
"/api/files"
)
@Tag
(
name
=
"文件上传管理"
,
description
=
"文件上传管理"
)
public
class
FileUploadController
{
// 从配置文件中获取文件存储目录
@Value
(
"${file.upload.dir:uploads}"
)
private
String
uploadDir
;
private
String
absoluteUploadDir
;
@PostConstruct
public
void
init
()
{
// 获取上传目录的绝对路径
absoluteUploadDir
=
Paths
.
get
(
uploadDir
).
toAbsolutePath
().
toString
();
// 确保上传目录存在
File
directory
=
new
File
(
absoluteUploadDir
);
if
(!
directory
.
exists
())
{
boolean
created
=
directory
.
mkdirs
();
if
(
created
)
{
log
.
info
(
"创建上传目录: {}"
,
absoluteUploadDir
);
}
else
{
log
.
error
(
"创建上传目录失败: {}"
,
absoluteUploadDir
);
}
}
else
{
log
.
info
(
"使用已存在的上传目录: {}"
,
absoluteUploadDir
);
}
}
@PostMapping
(
value
=
"/upload/machines"
,
consumes
=
"multipart/form-data"
)
@Operation
(
summary
=
"上传machines.json文件"
,
description
=
"上传machines.json文件"
)
@RequestBody
(
description
=
"选择要上传的 machines.json 文件"
,
required
=
true
,
content
=
@Content
(
mediaType
=
"multipart/form-data"
))
public
R
<
String
>
uploadMachines
(
@Parameter
(
description
=
"选择要上传的 machines.json 文件"
)
@RequestParam
(
"file"
)
MultipartFile
file
)
{
return
uploadFile
(
file
,
"machines.json"
);
}
@PostMapping
(
value
=
"/upload/orders"
,
consumes
=
"multipart/form-data"
)
@Operation
(
summary
=
"上传orders.json文件"
,
description
=
"上传orders.json文件"
)
@RequestBody
(
description
=
"选择要上传的 orders.json 文件"
,
required
=
true
,
content
=
@Content
(
mediaType
=
"multipart/form-data"
))
public
R
<
String
>
uploadOrders
(
@Parameter
(
description
=
"选择要上传的 orders.json 文件"
)
@RequestParam
(
"file"
)
MultipartFile
file
)
{
return
uploadFile
(
file
,
"orders.json"
);
}
@PostMapping
(
value
=
"/upload/products"
,
consumes
=
"multipart/form-data"
)
@Operation
(
summary
=
"上传products.json文件"
,
description
=
"上传products.json文件"
)
@RequestBody
(
description
=
"选择要上传的 products.json 文件"
,
required
=
true
,
content
=
@Content
(
mediaType
=
"multipart/form-data"
))
public
R
<
String
>
uploadProducts
(
@Parameter
(
description
=
"选择要上传的 products.json 文件"
)
@RequestParam
(
"file"
)
MultipartFile
file
)
{
return
uploadFile
(
file
,
"products.json"
);
}
private
R
<
String
>
uploadFile
(
MultipartFile
file
,
String
expectedFileName
)
{
if
(
file
.
isEmpty
())
{
return
R
.
failed
(
"上传文件不能为空"
);
}
// 检查文件名
if
(!
file
.
getOriginalFilename
().
equals
(
expectedFileName
))
{
return
R
.
failed
(
"请上传正确的文件: "
+
expectedFileName
);
}
try
{
// 保存文件到指定目录
String
filePath
=
Paths
.
get
(
absoluteUploadDir
,
expectedFileName
).
toString
();
file
.
transferTo
(
new
File
(
filePath
));
log
.
info
(
"文件上传成功: {}"
,
filePath
);
return
R
.
ok
(
"文件上传成功: "
+
expectedFileName
);
}
catch
(
IOException
e
)
{
log
.
error
(
"文件上传失败"
,
e
);
return
R
.
failed
(
"文件上传失败: "
+
e
.
getMessage
());
}
}
/**
* 获取上传文件的路径
* @param fileName 文件名
* @return 文件路径
*/
public
String
getUploadedFilePath
(
String
fileName
)
{
return
Paths
.
get
(
absoluteUploadDir
,
fileName
).
toString
();
}
/**
* 检查文件是否存在
* @param fileName 文件名
* @return 是否存在
*/
public
boolean
isFileUploaded
(
String
fileName
)
{
String
filePath
=
Paths
.
get
(
absoluteUploadDir
,
fileName
).
toString
();
return
new
File
(
filePath
).
exists
();
}
}
\ No newline at end of file
src/main/java/com/aps/controller/gantt/ResourceGanttController.java
0 → 100644
View file @
2d7e8ff6
package
com
.
aps
.
controller
.
gantt
;
import
com.aps.entity.Gantt.ProductGanttVO
;
import
com.aps.entity.Gantt.ResourceGanttVO
;
import
com.aps.entity.Gantt.TaskVO
;
import
com.aps.entity.Schedule.MachineVO
;
import
com.aps.entity.basic.*
;
import
com.aps.service.plan.PlanResultService
;
import
com.aps.service.plan.PlanSchedulerService
;
import
io.swagger.v3.oas.annotations.tags.Tag
;
import
io.swagger.v3.oas.annotations.Operation
;
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.RequestParam
;
import
org.springframework.web.bind.annotation.RestController
;
import
java.time.LocalDateTime
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.stream.Collectors
;
@Slf4j
@RestController
@RequestMapping
(
"/Gantt"
)
@Tag
(
name
=
"甘特图管理"
,
description
=
"甘特图管理"
)
public
class
ResourceGanttController
{
@Autowired
private
PlanSchedulerService
schedulingService
;
@Autowired
private
PlanResultService
planResultService
;
@GetMapping
(
"/resourceGantt"
)
@Operation
(
summary
=
"获取资源甘特图数据"
,
description
=
"获取资源甘特图数据"
)
public
List
<
ResourceGanttVO
>
getResourceGantt
(
@RequestParam
(
required
=
false
,
defaultValue
=
"1"
)
Long
sceneId
)
{
// 从PlanResultService获取ScheduleChromosome列表
List
<
ScheduleChromosome
>
scheduleChromosomes
=
planResultService
.
execute
();
// 根据sceneId查找对应的ScheduleChromosome
ScheduleChromosome
targetChromosome
=
null
;
for
(
ScheduleChromosome
chromosome
:
scheduleChromosomes
)
{
if
(
chromosome
.
getSceneId
()
==
sceneId
)
{
targetChromosome
=
chromosome
;
break
;
}
}
// 如果找不到对应的场景,返回空列表
if
(
targetChromosome
==
null
)
{
return
new
ArrayList
<>();
}
// 转换为 ResourceGanttVO 格式
List
<
ResourceGanttVO
>
resourceGanttVOList
=
new
ArrayList
<>();
List
<
ResourceGanttVO
>
resourceGanttVOs
=
convertToResourceGanttVO
(
targetChromosome
);
resourceGanttVOList
.
addAll
(
resourceGanttVOs
);
return
resourceGanttVOList
;
}
@GetMapping
(
"/productGantt"
)
@Operation
(
summary
=
"获取产品甘特图数据"
,
description
=
"获取产品甘特图数据"
)
public
List
<
ProductGanttVO
>
getProductGantt
(
@RequestParam
(
required
=
false
,
defaultValue
=
"1"
)
Long
sceneId
)
{
// 从PlanResultService获取ScheduleChromosome列表
List
<
ScheduleChromosome
>
scheduleChromosomes
=
planResultService
.
execute
();
// 根据sceneId查找对应的ScheduleChromosome
ScheduleChromosome
targetChromosome
=
null
;
for
(
ScheduleChromosome
chromosome
:
scheduleChromosomes
)
{
if
(
chromosome
.
getSceneId
()
==
sceneId
)
{
targetChromosome
=
chromosome
;
break
;
}
}
// 如果找不到对应的场景,返回空列表
if
(
targetChromosome
==
null
)
{
return
new
ArrayList
<>();
}
// 转换为 ProductGanttVO 格式
List
<
ProductGanttVO
>
productGanttVOList
=
new
ArrayList
<>();
List
<
ProductGanttVO
>
productGanttVOs
=
convertToProductGanttVO
(
targetChromosome
);
productGanttVOList
.
addAll
(
productGanttVOs
);
return
productGanttVOList
;
}
@GetMapping
(
"/sceneIds"
)
@Operation
(
summary
=
"获取所有场景ID"
,
description
=
"获取所有场景ID"
)
public
List
<
Long
>
getSceneIds
()
{
// 调用 PlanResultService 获取 ScheduleChromosome 列表
List
<
ScheduleChromosome
>
scheduleChromosomes
=
planResultService
.
execute
();
// 提取所有场景ID
return
scheduleChromosomes
.
stream
()
.
map
(
ScheduleChromosome:
:
getSceneId
)
.
collect
(
Collectors
.
toList
());
}
/**
* 将 ScheduleChromosome 转换为 ResourceGanttVO 列表
* @param scheduleChromosome 调度结果
* @return 转换后的数据
*/
private
List
<
ResourceGanttVO
>
convertToResourceGanttVO
(
ScheduleChromosome
scheduleChromosome
)
{
List
<
ResourceGanttVO
>
resourceGanttVOList
=
new
ArrayList
<>();
// 遍历所有机器资源
if
(
scheduleChromosome
.
getMachines
()
!=
null
)
{
for
(
int
i
=
0
;
i
<
scheduleChromosome
.
getMachines
().
size
();
i
++)
{
Machine
machine
=
scheduleChromosome
.
getMachines
().
get
(
i
);
ResourceGanttVO
resourceGanttVO
=
new
ResourceGanttVO
();
resourceGanttVO
.
setId
(
machine
.
getId
());
resourceGanttVO
.
setName
(
machine
.
getName
()
!=
null
?
machine
.
getName
()
:
"设备-"
+
machine
.
getId
());
resourceGanttVO
.
setType
(
"设备类型"
+
machine
.
getId
());
// 需要从原始数据获取
resourceGanttVO
.
setChange
(
0
);
// 默认值
resourceGanttVO
.
setSplit
(
0
);
// 默认值
resourceGanttVO
.
setDepartmentId
(
machine
.
getId
());
// 默认值
resourceGanttVO
.
setDepartmentName
(
machine
.
getId
()+
"号线"
);
// 默认值
resourceGanttVO
.
setShopName
(
machine
.
getId
()+
"车间"
);
// 默认值
resourceGanttVO
.
setShopId
(
machine
.
getId
());
// 默认值
resourceGanttVO
.
setCode
(
"设备编码-"
+
machine
.
getId
());
// 默认值
resourceGanttVO
.
setShift
(
convertToVO
(
machine
));
// 转换任务列表
List
<
TaskVO
>
taskVOList
=
new
ArrayList
<>();
if
(
scheduleChromosome
.
getGenes
()
!=
null
)
{
// 筛选出属于当前设备的任务
List
<
Gene
>
machineGenes
=
scheduleChromosome
.
getGenes
().
stream
()
.
filter
(
gene
->
gene
.
getMachineId
()==(
machine
.
getId
()))
.
collect
(
Collectors
.
toList
());
// 按开始时间排序
machineGenes
.
sort
((
g1
,
g2
)
->
Integer
.
compare
(
g1
.
getStartTime
(),
g2
.
getStartTime
()));
for
(
Gene
gene
:
machineGenes
)
{
TaskVO
taskVO
=
new
TaskVO
();
taskVO
.
setId
(
gene
.
getId
());
// 临时处理
taskVO
.
setPlanId
(
gene
.
getOrderId
());
// 默认值
taskVO
.
setProductType
(
0
);
// 默认值
taskVO
.
setProductName
(
"产品"
+
gene
.
getProductId
());
taskVO
.
setProductId
(
gene
.
getProductId
());
// 默认值
taskVO
.
setQuantity
(
gene
.
getBatchSize
());
taskVO
.
setStart
(
scheduleChromosome
.
getBaseTime
().
plusMinutes
(
gene
.
getAbsoluteStartTime
()));
taskVO
.
setEnd
(
scheduleChromosome
.
getBaseTime
().
plusMinutes
(
gene
.
getAbsoluteEndTime
()));
taskVO
.
setSetup
(
gene
.
getAbsolutePreparationTime
()*
60
);
// 默认值
taskVO
.
setTeardown
(
gene
.
getAbsoluteTeardownTime
()*
60
);
// 默认值
taskVO
.
setEquipChange
(
gene
.
getSetupTime
()*
60
);
// 默认值
taskVO
.
setEquipCooling
(
0
);
// 默认值
taskVO
.
setEquipType
(
resourceGanttVO
.
getType
());
taskVO
.
setEquipName
(
resourceGanttVO
.
getName
());
// taskVO.setDuration(calculateDuration(
// scheduleChromosome.getBaseTime().plusMinutes(gene.getStartTime()),
// scheduleChromosome.getBaseTime().plusMinutes(gene.getEndTime()))); // 计算持续时间
taskVO
.
setDuration
(
0
);
//
taskVO
.
setEquipId
(
machine
.
getId
());
taskVO
.
setShopId
(
machine
.
getId
());
taskVO
.
setShopName
(
resourceGanttVO
.
getShopName
());
taskVO
.
setStatus
(
0
);
// 默认值
taskVO
.
setDetailId
((
long
)
gene
.
getProductId
()
*
1000
+
gene
.
getOperationId
());
// 将productId和operationID组合为detailId
taskVO
.
setHeaderId
(
gene
.
getProductId
());
// 默认值
taskVO
.
setHeaderName
(
"工艺"
+
gene
.
getProductId
());
// 默认值
taskVO
.
setSeq
(
gene
.
getSequenceId
());
// 使用工序ID
taskVO
.
setSeqName
(
"工序名称"
+
gene
.
getSequenceId
());
taskVO
.
setProcessingTime
(
gene
.
getProcessingTime
()*
60
);
taskVO
.
setAbsoluteStart
(
scheduleChromosome
.
getBaseTime
().
plusMinutes
(
gene
.
getStartTime
()));
taskVO
.
setAbsoluteEnd
(
scheduleChromosome
.
getBaseTime
().
plusMinutes
(
gene
.
getEndTime
()));
taskVOList
.
add
(
taskVO
);
// 调试:检查machine中的shifts状态
if
(
machine
.
getShifts
()
!=
null
)
{
for
(
Shift
shift
:
machine
.
getShifts
())
{
System
.
out
.
println
(
"Before setting shifts - Shift status: "
+
shift
.
getStatus
());
}
}
taskVO
.
setAbsolutePreparationTime
(
gene
.
getTeardownTime
());
}
}
resourceGanttVO
.
setList
(
taskVOList
);
resourceGanttVOList
.
add
(
resourceGanttVO
);
}
}
return
resourceGanttVOList
;
}
/**
* 将 ScheduleChromosome 转换为 ProductGanttVO 列表
* @param scheduleChromosome 调度结果
* @return 转换后的数据
*/
private
List
<
ProductGanttVO
>
convertToProductGanttVO
(
ScheduleChromosome
scheduleChromosome
)
{
List
<
ProductGanttVO
>
productGanttVOList
=
new
ArrayList
<>();
// 按产品ID和工单ID分组基因
if
(
scheduleChromosome
.
getGenes
()
!=
null
)
{
// 按工单ID分组
scheduleChromosome
.
getGenes
().
stream
()
.
collect
(
Collectors
.
groupingBy
(
Gene:
:
getOrderId
))
.
forEach
((
orderId
,
genes
)
->
{
if
(!
genes
.
isEmpty
())
{
ProductGanttVO
productGanttVO
=
new
ProductGanttVO
();
Gene
firstGene
=
genes
.
get
(
0
);
productGanttVO
.
setId
(
firstGene
.
getId
());
productGanttVO
.
setProductName
(
"产品"
+
firstGene
.
getProductId
());
// 默认值,实际应从订单数据获取
productGanttVO
.
setProductType
(
0
);
productGanttVO
.
setProductId
(
firstGene
.
getProductId
());
// 计算总数量(假设同一批次)
productGanttVO
.
setQuantity
(
firstGene
.
getBatchSize
());
productGanttVO
.
setCode
(
"编号"
+
firstGene
.
getProductId
());
// 默认值
productGanttVO
.
setShopId
(
firstGene
.
getMachineId
());
// 默认值
productGanttVO
.
setShopName
(
firstGene
.
getMachineId
()+
"号线"
);
// 默认值
productGanttVO
.
setStatus
(
"已发布"
);
productGanttVO
.
setHeaderId
(
firstGene
.
getProductId
());
productGanttVO
.
setHeaderName
(
"工艺"
+
firstGene
.
getProductId
());
// 默认值
// 计算开始和结束时间
int
minStartTime
=
genes
.
stream
()
.
mapToInt
(
Gene:
:
getStartTime
)
.
min
()
.
orElse
(
0
);
int
maxEndTime
=
genes
.
stream
()
.
mapToInt
(
Gene:
:
getEndTime
)
.
max
()
.
orElse
(
0
);
productGanttVO
.
setStartDate
(
scheduleChromosome
.
getBaseTime
().
plusMinutes
(
minStartTime
));
productGanttVO
.
setEndDate
(
scheduleChromosome
.
getBaseTime
().
plusMinutes
(
maxEndTime
));
// 转换任务列表
List
<
TaskVO
>
taskVOList
=
new
ArrayList
<>();
// 按工序顺序排序
genes
.
sort
((
g1
,
g2
)
->
Integer
.
compare
(
g1
.
getSequenceId
(),
g2
.
getSequenceId
()));
for
(
int
i
=
0
;
i
<
genes
.
size
();
i
++)
{
Gene
gene
=
genes
.
get
(
i
);
TaskVO
taskVO
=
new
TaskVO
();
taskVO
.
setId
(
gene
.
getId
());
// 生成唯一ID
taskVO
.
setPlanId
(
orderId
);
taskVO
.
setProductType
(
0
);
taskVO
.
setProductName
(
"产品"
+
gene
.
getProductId
());
taskVO
.
setProductId
(
gene
.
getProductId
());
taskVO
.
setQuantity
(
gene
.
getBatchSize
());
taskVO
.
setStart
(
scheduleChromosome
.
getBaseTime
().
plusMinutes
(
gene
.
getAbsoluteStartTime
()));
taskVO
.
setEnd
(
scheduleChromosome
.
getBaseTime
().
plusMinutes
(
gene
.
getAbsoluteEndTime
()));
taskVO
.
setSetup
(
gene
.
getAbsolutePreparationTime
()*
60
);
// 默认值
taskVO
.
setTeardown
(
gene
.
getAbsoluteTeardownTime
()*
60
);
// 默认值
taskVO
.
setEquipChange
(
gene
.
getSetupTime
()*
60
);
// 默认值
taskVO
.
setEquipCooling
(
0
);
// 默认值
// taskVO.setEquipType("PTT-" + (i+1) + "-" + gene.getOperationName().toUpperCase().substring(0, Math.min(3, gene.getOperationName().length())));
// taskVO.setEquipName(gene.getOperationName());
taskVO
.
setDuration
(
calculateDuration
(
scheduleChromosome
.
getBaseTime
().
plusMinutes
(
gene
.
getStartTime
()),
scheduleChromosome
.
getBaseTime
().
plusMinutes
(
gene
.
getEndTime
())));
taskVO
.
setEquipId
(
gene
.
getMachineId
());
// 生成设备ID
taskVO
.
setShopId
(
gene
.
getMachineId
());
taskVO
.
setShopName
(
gene
.
getMachineId
()+
"车间"
);
taskVO
.
setStatus
(
0
);
taskVO
.
setDetailId
((
long
)
gene
.
getProductId
()
*
1000
+
gene
.
getOperationId
());
taskVO
.
setHeaderId
(
gene
.
getProductId
());
taskVO
.
setHeaderName
(
"工艺"
+
gene
.
getProductId
());
taskVO
.
setSeq
(
gene
.
getSequenceId
());
taskVO
.
setSeqName
(
"工序名称"
+
gene
.
getSequenceId
());
taskVO
.
setAbsoluteStart
(
scheduleChromosome
.
getBaseTime
().
plusMinutes
(
gene
.
getStartTime
()));
taskVO
.
setAbsoluteEnd
(
scheduleChromosome
.
getBaseTime
().
plusMinutes
(
gene
.
getEndTime
()));
taskVOList
.
add
(
taskVO
);
}
productGanttVO
.
setList
(
taskVOList
);
productGanttVOList
.
add
(
productGanttVO
);
}
});
}
return
productGanttVOList
;
}
/**
* 计算任务持续时间(分钟)
* @param start 开始时间
* @param end 结束时间
* @return 持续时间(分钟)
*/
private
Integer
calculateDuration
(
LocalDateTime
start
,
LocalDateTime
end
)
{
if
(
start
==
null
||
end
==
null
)
{
return
0
;
}
return
Math
.
toIntExact
(
java
.
time
.
Duration
.
between
(
start
,
end
).
toMinutes
());
}
private
ShiftVO
convertToVO
(
Machine
machine
)
{
ShiftVO
shiftVO
=
new
ShiftVO
();
shiftVO
.
setId
(
machine
.
getId
());
shiftVO
.
setName
(
machine
.
getName
());
shiftVO
.
setShifts
(
machine
.
getShifts
());
shiftVO
.
setMaintenanceWindows
(
machine
.
getMaintenanceWindows
());
// 注意:tasks 字段需要在其他地方设置,因为 Machine 类中没有任务信息
return
shiftVO
;
}
}
\ No newline at end of file
src/main/java/com/aps/entity/Gantt/ProductGanttVO.java
0 → 100644
View file @
2d7e8ff6
package
com
.
aps
.
entity
.
Gantt
;
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
java.time.LocalDateTime
;
import
java.util.List
;
@Data
@EqualsAndHashCode
(
callSuper
=
false
)
@Accessors
(
chain
=
true
)
@Schema
(
name
=
"产品甘特图数据模型"
,
description
=
"产品甘特图数据模型"
)
public
class
ProductGanttVO
{
@Schema
(
description
=
"产品ID"
)
private
Integer
id
;
@Schema
(
description
=
"产品名称"
)
private
String
productName
;
@Schema
(
description
=
"产品类型"
)
private
Integer
productType
;
@Schema
(
description
=
"产品编号"
)
private
Integer
productId
;
@Schema
(
description
=
"数量"
)
private
Integer
quantity
;
@Schema
(
description
=
"编码"
)
private
String
code
;
@Schema
(
description
=
"开始日期"
)
@JsonFormat
(
timezone
=
"GMT+8"
,
pattern
=
"yyyy-MM-dd"
)
private
LocalDateTime
startDate
;
@Schema
(
description
=
"结束日期"
)
@JsonFormat
(
timezone
=
"GMT+8"
,
pattern
=
"yyyy-MM-dd"
)
private
LocalDateTime
endDate
;
@Schema
(
description
=
"车间ID"
)
private
Integer
shopId
;
@Schema
(
description
=
"车间名称"
)
private
String
shopName
;
@Schema
(
description
=
"状态"
)
private
String
status
;
@Schema
(
description
=
"头ID"
)
private
Integer
headerId
;
@Schema
(
description
=
"头名称"
)
private
String
headerName
;
@Schema
(
description
=
"任务列表"
)
private
List
<
TaskVO
>
list
;
}
\ No newline at end of file
src/main/java/com/aps/entity/Gantt/ResourceGanttVO.java
0 → 100644
View file @
2d7e8ff6
package
com
.
aps
.
entity
.
Gantt
;
import
com.aps.entity.basic.ShiftVO
;
import
io.swagger.v3.oas.annotations.media.Schema
;
import
lombok.Data
;
import
lombok.EqualsAndHashCode
;
import
lombok.experimental.Accessors
;
import
java.util.List
;
@Data
@EqualsAndHashCode
(
callSuper
=
false
)
@Accessors
(
chain
=
true
)
@Schema
(
name
=
"资源甘特图数据模型"
,
description
=
"资源甘特图数据模型"
)
public
class
ResourceGanttVO
{
@Schema
(
description
=
"设备ID"
)
private
Integer
id
;
@Schema
(
description
=
"设备名称"
)
private
String
name
;
@Schema
(
description
=
"设备类型"
)
private
String
type
;
@Schema
(
description
=
"是否可更换"
)
private
Integer
change
;
@Schema
(
description
=
"是否可拆分"
)
private
Integer
split
;
@Schema
(
description
=
"部门ID"
)
private
Integer
departmentId
;
@Schema
(
description
=
"部门名称"
)
private
String
departmentName
;
@Schema
(
description
=
"车间名称"
)
private
String
shopName
;
@Schema
(
description
=
"车间ID"
)
private
Integer
shopId
;
@Schema
(
description
=
"设备编码"
)
private
String
code
;
@Schema
(
description
=
"任务列表"
)
private
List
<
TaskVO
>
list
;
private
ShiftVO
shift
;
}
\ No newline at end of file
src/main/java/com/aps/entity/Gantt/TaskVO.java
0 → 100644
View file @
2d7e8ff6
package
com
.
aps
.
entity
.
Gantt
;
import
com.aps.entity.basic.Shift
;
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
java.time.LocalDateTime
;
import
java.util.List
;
@Data
@EqualsAndHashCode
(
callSuper
=
false
)
@Accessors
(
chain
=
true
)
@Schema
(
name
=
"任务数据模型"
,
description
=
"任务数据模型"
)
public
class
TaskVO
{
@Schema
(
description
=
"任务ID"
)
private
Integer
id
;
@Schema
(
description
=
"计划ID"
)
private
Integer
planId
;
@Schema
(
description
=
"产品类型"
)
private
Integer
productType
;
@Schema
(
description
=
"产品名称"
)
private
String
productName
;
@Schema
(
description
=
"产品ID"
)
private
Integer
productId
;
@Schema
(
description
=
"数量"
)
private
Integer
quantity
;
@Schema
(
description
=
"开始时间"
)
@JsonFormat
(
timezone
=
"GMT+8"
,
pattern
=
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
)
private
LocalDateTime
start
;
@Schema
(
description
=
"结束时间"
)
@JsonFormat
(
timezone
=
"GMT+8"
,
pattern
=
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
)
private
LocalDateTime
end
;
@Schema
(
description
=
"开始时间"
)
@JsonFormat
(
timezone
=
"GMT+8"
,
pattern
=
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
)
private
LocalDateTime
absoluteStart
;
@Schema
(
description
=
"结束时间"
)
@JsonFormat
(
timezone
=
"GMT+8"
,
pattern
=
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
)
private
LocalDateTime
absoluteEnd
;
@Schema
(
description
=
"准备时间"
)
private
Integer
setup
;
@Schema
(
description
=
"收尾时间"
)
private
Integer
teardown
;
@Schema
(
description
=
"设备更换时间"
)
private
Integer
equipChange
;
@Schema
(
description
=
"设备冷却时间"
)
private
Integer
equipCooling
;
@Schema
(
description
=
"设备类型"
)
private
String
equipType
;
@Schema
(
description
=
"设备名称"
)
private
String
equipName
;
@Schema
(
description
=
"持续时间"
)
private
Integer
duration
;
@Schema
(
description
=
"设备ID"
)
private
Integer
equipId
;
@Schema
(
description
=
"车间ID"
)
private
Integer
shopId
;
@Schema
(
description
=
"车间名称"
)
private
String
shopName
;
//‘0’:已发布 ‘1’:未发布
@Schema
(
description
=
"状态"
)
private
Integer
status
;
@Schema
(
description
=
"明细ID"
)
private
Long
detailId
;
@Schema
(
description
=
"头ID"
)
private
Integer
headerId
;
@Schema
(
description
=
"头名称"
)
private
String
headerName
;
@Schema
(
description
=
"工序序号"
)
private
Integer
seq
;
@Schema
(
description
=
"工序名称"
)
private
String
seqName
;
private
int
processingTime
;
// 绝对处理时间(分钟)
private
int
absolutePreparationTime
;
// 新增:绝对准备时间
}
\ No newline at end of file
src/main/java/com/aps/entity/basic/Gene.java
View file @
2d7e8ff6
...
@@ -12,10 +12,25 @@ public class Gene {
...
@@ -12,10 +12,25 @@ public class Gene {
private
int
orderId
;
private
int
orderId
;
private
int
productId
;
private
int
productId
;
private
int
operationId
;
private
int
operationId
;
private
String
operationName
;
// 添加工序名称
private
int
machineId
;
private
int
machineId
;
private
int
startTime
;
// 相对开始时间(分钟)
private
int
startTime
;
// 相对开始时间(分钟)
private
int
endTime
;
// 相对结束时间(分钟)
private
int
endTime
;
// 相对结束时间(分钟)
private
int
batchSize
;
// 批次大小(订单可拆分)
private
int
batchSize
;
// 批次大小(订单可拆分)
private
List
<
GeneDetail
>
geneDetails
;
// 时间详情
private
List
<
GeneDetail
>
geneDetails
;
// 时间详情
private
int
processingTime
;
// 绝对处理时间(分钟)
private
int
processingTime
;
// 绝对处理时间(分钟)
private
int
Id
;
private
int
sequenceId
;
private
int
setupTime
;
// 换型时
private
int
teardownTime
;
// 后处理时间(收尾时间)
private
int
contantTime
;
// 常数时间
private
int
preTime
;
private
int
absoluteStartTime
;
// 相对开始时间(分钟)
private
int
absoluteEndTime
;
// 相对结束时间(分钟)
private
int
absolutePreparationTime
;
// 新增:绝对前处理时间
private
int
absoluteTeardownTime
;
// 新增:绝对后处理时间
private
int
absoluteSetupTime
;
// 新增:绝对换型时间
}
}
\ No newline at end of file
src/main/java/com/aps/entity/basic/Holiday.java
View file @
2d7e8ff6
...
@@ -28,4 +28,21 @@ public class Holiday {
...
@@ -28,4 +28,21 @@ public class Holiday {
public
void
setEnd
(
LocalDateTime
end
)
{
public
void
setEnd
(
LocalDateTime
end
)
{
this
.
end
=
end
;
this
.
end
=
end
;
}
}
/**
* 将Holiday转换为MaintenanceWindow
* @return 对应的MaintenanceWindow对象
*/
public
MaintenanceWindow
toMaintenanceWindow
()
{
return
new
MaintenanceWindow
(
this
.
start
,
this
.
end
,
"Holiday Period"
);
}
/**
* 将Holiday转换为MaintenanceWindow(可指定原因)
* @param reason 维护原因
* @return 对应的MaintenanceWindow对象
*/
public
MaintenanceWindow
toMaintenanceWindow
(
String
reason
)
{
return
new
MaintenanceWindow
(
this
.
start
,
this
.
end
,
reason
);
}
}
}
\ No newline at end of file
src/main/java/com/aps/entity/basic/MachineOption.java
View file @
2d7e8ff6
...
@@ -10,4 +10,7 @@ public class MachineOption {
...
@@ -10,4 +10,7 @@ public class MachineOption {
private
int
machineId
;
private
int
machineId
;
private
int
processingTime
;
// 加工时间
private
int
processingTime
;
// 加工时间
private
int
setupTime
;
// 换型时间(如果与前一个产品不同)
private
int
setupTime
;
// 换型时间(如果与前一个产品不同)
private
int
teardownTime
;
// 收尾时间(后处理时间)
private
int
contantTime
;
// 常数时间
private
int
preTime
;
// 前处理时间
}
}
\ No newline at end of file
src/main/java/com/aps/entity/basic/MaintenanceWindow.java
View file @
2d7e8ff6
...
@@ -8,6 +8,7 @@ public class MaintenanceWindow {
...
@@ -8,6 +8,7 @@ public class MaintenanceWindow {
private
LocalDateTime
endTime
;
private
LocalDateTime
endTime
;
private
String
reason
;
private
String
reason
;
public
MaintenanceWindow
()
{}
public
MaintenanceWindow
()
{}
public
MaintenanceWindow
(
LocalDateTime
startTime
,
LocalDateTime
endTime
,
String
reason
)
{
public
MaintenanceWindow
(
LocalDateTime
startTime
,
LocalDateTime
endTime
,
String
reason
)
{
...
@@ -16,6 +17,23 @@ public class MaintenanceWindow {
...
@@ -16,6 +17,23 @@ public class MaintenanceWindow {
this
.
reason
=
reason
;
this
.
reason
=
reason
;
}
}
/**
* 从Holiday对象构造MaintenanceWindow
* @param holiday 假期对象
*/
public
MaintenanceWindow
(
Holiday
holiday
)
{
this
(
holiday
.
getStart
(),
holiday
.
getEnd
(),
"Holiday Period"
);
}
/**
* 从Holiday对象构造MaintenanceWindow(可指定原因)
* @param holiday 假期对象
* @param reason 维护原因
*/
public
MaintenanceWindow
(
Holiday
holiday
,
String
reason
)
{
this
(
holiday
.
getStart
(),
holiday
.
getEnd
(),
reason
);
}
// Getters and Setters
// Getters and Setters
public
LocalDateTime
getStartTime
()
{
public
LocalDateTime
getStartTime
()
{
return
startTime
;
return
startTime
;
...
@@ -41,6 +59,8 @@ public class MaintenanceWindow {
...
@@ -41,6 +59,8 @@ public class MaintenanceWindow {
this
.
reason
=
reason
;
this
.
reason
=
reason
;
}
}
@Override
@Override
public
boolean
equals
(
Object
o
)
{
public
boolean
equals
(
Object
o
)
{
if
(
this
==
o
)
return
true
;
if
(
this
==
o
)
return
true
;
...
...
src/main/java/com/aps/entity/basic/ScheduleChromosome.java
View file @
2d7e8ff6
...
@@ -15,7 +15,7 @@ public class ScheduleChromosome {
...
@@ -15,7 +15,7 @@ public class ScheduleChromosome {
private
double
tardiness
;
private
double
tardiness
;
private
Map
<
Integer
,
Double
>
objectiveValues
;
private
Map
<
Integer
,
Double
>
objectiveValues
;
private
LocalDateTime
baseTime
;
private
LocalDateTime
baseTime
;
private
long
sceneId
;
// 新增场景ID字段
public
ScheduleChromosome
()
{
public
ScheduleChromosome
()
{
this
.
genes
=
new
ArrayList
<>();
this
.
genes
=
new
ArrayList
<>();
this
.
objectiveValues
=
new
HashMap
<>();
this
.
objectiveValues
=
new
HashMap
<>();
...
...
src/main/java/com/aps/entity/basic/Shift.java
View file @
2d7e8ff6
package
com
.
aps
.
entity
.
basic
;
package
com
.
aps
.
entity
.
basic
;
import
lombok.Data
;
import
java.time.DayOfWeek
;
import
java.time.DayOfWeek
;
import
java.time.LocalDateTime
;
import
java.time.LocalDateTime
;
import
java.time.LocalTime
;
import
java.time.LocalTime
;
import
java.util.Set
;
import
java.util.Set
;
@Data
public
class
Shift
{
public
class
Shift
{
private
LocalTime
startTime
;
private
LocalTime
startTime
;
private
LocalTime
endTime
;
private
LocalTime
endTime
;
...
@@ -13,6 +16,12 @@ public class Shift {
...
@@ -13,6 +16,12 @@ public class Shift {
private
boolean
isTemporaryShift
;
private
boolean
isTemporaryShift
;
private
int
priority
;
private
int
priority
;
private
Integer
status
;
//0:正常班次 1:临时班次 2:维修
// 添加设备ID和名称字段
private
Integer
machineId
;
private
String
machineName
;
public
Shift
()
{}
public
Shift
()
{}
...
@@ -40,13 +49,7 @@ public class Shift {
...
@@ -40,13 +49,7 @@ public class Shift {
this
.
days
=
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
()
{
public
LocalDateTime
getShiftDate
()
{
return
shiftDate
;
return
shiftDate
;
...
@@ -71,4 +74,28 @@ public class Shift {
...
@@ -71,4 +74,28 @@ public class Shift {
public
void
setPriority
(
int
priority
)
{
public
void
setPriority
(
int
priority
)
{
this
.
priority
=
priority
;
this
.
priority
=
priority
;
}
}
public
Integer
getStatus
()
{
return
status
;
}
public
void
setStatus
(
Integer
status
)
{
this
.
status
=
status
;
}
public
Integer
getMachineId
()
{
return
machineId
;
}
public
void
setMachineId
(
Integer
machineId
)
{
this
.
machineId
=
machineId
;
}
public
String
getMachineName
()
{
return
machineName
;
}
public
void
setMachineName
(
String
machineName
)
{
this
.
machineName
=
machineName
;
}
}
}
\ No newline at end of file
src/main/java/com/aps/entity/basic/ShiftVO.java
0 → 100644
View file @
2d7e8ff6
package
com
.
aps
.
entity
.
basic
;
import
lombok.Data
;
import
java.util.List
;
@Data
public
class
ShiftVO
{
private
int
id
;
private
String
name
;
private
List
<
Shift
>
shifts
;
private
List
<
MaintenanceWindow
>
maintenanceWindows
;
}
src/main/java/com/aps/service/plan/AlgorithmScheduler6.java
View file @
2d7e8ff6
...
@@ -153,9 +153,36 @@ public class AlgorithmScheduler6 {
...
@@ -153,9 +153,36 @@ public class AlgorithmScheduler6 {
}
}
int
processingTime
=
machineOption
.
getProcessingTime
()
*
batchSize
;
int
processingTime
=
machineOption
.
getProcessingTime
()
*
batchSize
;
int
preTime
=
machineOption
.
getPreTime
();
// 前处理时间(常数时间,与批次大小无关)
int
setupTime
=
0
;
// 换型时间默认为0,仅在产品变化时计算
int
teardownTime
=
machineOption
.
getTeardownTime
();
// 后处理时间(常数时间,与批次大小无关)
// 计算换型时间
// 查找该机器上一个任务
Gene
lastGeneOnMachine
=
chromosome
.
getGenes
().
stream
()
.
filter
(
g
->
g
.
getMachineId
()
==
machine
.
getId
())
.
max
(
Comparator
.
comparingInt
(
Gene:
:
getEndTime
))
.
orElse
(
null
);
// 如果上一个任务的产品与当前任务不同,则需要换型时间
if
(
lastGeneOnMachine
!=
null
&&
lastGeneOnMachine
.
getProductId
()
!=
order
.
getProductId
())
{
setupTime
=
machineOption
.
getSetupTime
();
}
// 前处理时间可以在上一个工序未完成时开始,所以调整开始时间
int
adjustedPrevTime
=
prevtime
;
if
(
preTime
>
0
&&
lastGeneOnMachine
!=
null
)
{
// 前处理可以在上一个任务结束前preTime时间开始
adjustedPrevTime
=
lastGeneOnMachine
.
getEndTime
()
-
preTime
;
}
// List<GeneDetail> geneDetails = GetNextAvailableTime(
// machine, prevtime, -1, processingTime,
// chromosome.getGenes(), false, true
// );
List
<
GeneDetail
>
geneDetails
=
GetNextAvailableTime
(
List
<
GeneDetail
>
geneDetails
=
GetNextAvailableTime
(
machine
,
prevtime
,
-
1
,
processing
Time
,
machine
,
adjustedPrevTime
,
-
1
,
processingTime
+
teardownTime
+
setup
Time
,
chromosome
.
getGenes
(),
false
,
true
chromosome
.
getGenes
(),
false
,
true
);
);
...
@@ -163,7 +190,7 @@ public class AlgorithmScheduler6 {
...
@@ -163,7 +190,7 @@ public class AlgorithmScheduler6 {
int
endTime
=
geneDetails
.
stream
().
mapToInt
(
GeneDetail:
:
getEndTime
).
max
().
orElse
(
0
);
int
endTime
=
geneDetails
.
stream
().
mapToInt
(
GeneDetail:
:
getEndTime
).
max
().
orElse
(
0
);
machine
.
setEarliestTime
(
endTime
);
machine
.
setEarliestTime
(
endTime
);
machine
.
setTotalTaskTime
(
machine
.
getTotalTaskTime
()
+
processing
Time
);
machine
.
setTotalTaskTime
(
machine
.
getTotalTaskTime
()
+
+
teardownTime
+
setup
Time
);
Gene
gene
=
new
Gene
();
Gene
gene
=
new
Gene
();
gene
.
setOrderId
(
order
.
getId
());
gene
.
setOrderId
(
order
.
getId
());
...
@@ -175,6 +202,8 @@ public class AlgorithmScheduler6 {
...
@@ -175,6 +202,8 @@ public class AlgorithmScheduler6 {
gene
.
setEndTime
(
endTime
);
gene
.
setEndTime
(
endTime
);
gene
.
setProcessingTime
(
processingTime
);
gene
.
setProcessingTime
(
processingTime
);
gene
.
setGeneDetails
(
deepCopyGeneDetailList
(
geneDetails
));
gene
.
setGeneDetails
(
deepCopyGeneDetailList
(
geneDetails
));
gene
.
setId
(
chromosome
.
getGenes
().
size
()
+
1
);
gene
.
setSequenceId
(
operation
.
getSequence
());
chromosome
.
getGenes
().
add
(
gene
);
chromosome
.
getGenes
().
add
(
gene
);
}
}
}
}
...
...
src/main/java/com/aps/service/plan/AlgorithmScheduler7.java
0 → 100644
View file @
2d7e8ff6
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
AlgorithmScheduler7
{
private
final
List
<
Product
>
_products
;
private
final
List
<
Machine
>
_machines
;
private
final
List
<
Order
>
_orders
;
private
final
Random
_random
;
private
final
MachineSchedulerService
_machineScheduler
;
private
int
_populationSize
=
100
;
private
double
_crossoverRate
=
0.8
;
private
double
_mutationRate
=
0.1
;
private
int
_maxGenerations
=
500
;
private
int
_elitismCount
=
5
;
private
final
LocalDateTime
baseTime
=
LocalDateTime
.
of
(
2025
,
10
,
1
,
0
,
0
,
0
);
public
AlgorithmScheduler7
(
List
<
Product
>
products
,
List
<
Machine
>
machines
,
List
<
Order
>
orders
,
MachineSchedulerService
machineScheduler
)
{
this
.
_products
=
deepCopyProductList
(
products
);
this
.
_machines
=
deepCopyMachineList
(
machines
);
this
.
_orders
=
deepCopyOrderList
(
orders
);
this
.
_machineScheduler
=
machineScheduler
;
this
.
_random
=
new
Random
();
}
public
ScheduleChromosome
Run
()
{
List
<
ScheduleChromosome
>
population
=
InitializePopulation
();
ScheduleChromosome
bestSolution
=
null
;
ScheduleChromosome
currentBest
=
null
;
double
bestFitness
=
Double
.
MIN_VALUE
;
currentBest
=
population
.
stream
()
.
max
(
Comparator
.
comparingDouble
(
ScheduleChromosome:
:
getFitness
))
.
orElse
(
null
);
if
(
currentBest
!=
null
)
{
bestFitness
=
currentBest
.
getFitness
();
bestSolution
=
deepCopyScheduleChromosome
(
currentBest
);
}
return
bestSolution
;
}
public
List
<
ScheduleChromosome
>
RunAll
()
{
return
InitializePopulation
();
}
private
List
<
ScheduleChromosome
>
InitializePopulation
()
{
List
<
ScheduleChromosome
>
population
=
new
ArrayList
<>();
System
.
out
.
println
(
"开始初始化种群,目标大小: "
+
_populationSize
);
for
(
int
i
=
0
;
i
<
_populationSize
;
i
++)
{
ScheduleChromosome
chromosome
=
new
ScheduleChromosome
(
baseTime
);
chromosome
.
setGenes
(
new
ArrayList
<>());
chromosome
.
setObjectiveValues
(
new
HashMap
<>());
chromosome
.
setOrders
(
deepCopyOrderList
(
_orders
));
chromosome
.
setMachines
(
deepCopyMachineList
(
_machines
));
// 为每个订单分配工序
for
(
Order
order
:
_orders
)
{
Product
product
=
_products
.
stream
()
.
filter
(
p
->
p
.
getId
()
==
order
.
getProductId
())
.
findFirst
()
.
orElseThrow
(()
->
new
NoSuchElementException
(
"Product not found: "
+
order
.
getProductId
()));
int
prevtime
=
0
;
int
remainingQuantity
=
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
;
if
(
machineOption
.
getContantTime
()
>
0
)
{
processingTime
=
machineOption
.
getContantTime
();
}
int
preTime
=
machineOption
.
getPreTime
();
int
teardownTime
=
machineOption
.
getTeardownTime
();
int
setupTime
=
calculateSetupTime
(
chromosome
.
getGenes
(),
order
,
machine
,
machineOption
);
int
effectivePrepTime
=
Math
.
max
(
machineOption
.
getPreTime
(),
calculateSetupTime
(
chromosome
.
getGenes
(),
order
,
machine
,
machineOption
));
// 检查是否是设备上的第一个产品
boolean
isFirstProductOnMachine
=
chromosome
.
getGenes
().
stream
()
.
filter
(
g
->
g
.
getMachineId
()
==
machine
.
getId
())
.
noneMatch
(
g
->
g
.
getProductId
()
==
order
.
getProductId
());
// 计算调整后的开始时间(使用有效准备时间)
int
adjustedPrevTime
=
calculateAdjustedStartTime
(
chromosome
.
getGenes
(),
order
.
getId
(),
operation
.
getId
(),
effectivePrepTime
,
prevtime
,
machine
.
getId
(),
isFirstProductOnMachine
);
// 关键修改2:总处理时间 = 有效准备时间(最大值) + 加工时间 + 后处理时间
int
totalProcessingDuration
=
effectivePrepTime
+
processingTime
+
teardownTime
;
// 关键修改3:调整提议的开始时间,基于有效准备时间提前
int
proposedStartTimeWithPreTime
=
adjustedPrevTime
;
if
(
operation
.
getId
()
>
1
&&
effectivePrepTime
>
0
)
{
// 使用有效准备时间判断
// 对于第二道及后续工序,有效准备时间可以提前
Gene
lastGeneOnMachine
=
chromosome
.
getGenes
().
stream
()
.
filter
(
g
->
g
.
getMachineId
()
==
machine
.
getId
())
.
max
(
Comparator
.
comparingInt
(
Gene:
:
getEndTime
))
.
orElse
(
null
);
int
machineEarliestTime
=
lastGeneOnMachine
!=
null
?
lastGeneOnMachine
.
getEndTime
()
:
0
;
// 提前量基于有效准备时间计算
proposedStartTimeWithPreTime
=
Math
.
max
(
adjustedPrevTime
-
effectivePrepTime
,
machineEarliestTime
);
}
// 查找可用时间槽(基于有效准备时间的总时长)
List
<
GeneDetail
>
geneDetails
=
GetNextAvailableTime
(
machine
,
proposedStartTimeWithPreTime
,
-
1
,
totalProcessingDuration
,
chromosome
.
getGenes
(),
false
,
true
);
int
startTime
=
geneDetails
.
stream
().
mapToInt
(
GeneDetail:
:
getStartTime
).
min
().
orElse
(
0
);
int
endTime
=
geneDetails
.
stream
().
mapToInt
(
GeneDetail:
:
getEndTime
).
max
().
orElse
(
0
);
// 验证有效准备时间是否被正确安排
if
(
operation
.
getId
()
>
1
&&
effectivePrepTime
>
0
)
{
// 使用有效准备时间判断
Gene
prevOperationGene
=
chromosome
.
getGenes
().
stream
()
.
filter
(
g
->
g
.
getOrderId
()
==
order
.
getId
()
&&
g
.
getOperationId
()
==
operation
.
getId
()
-
1
)
.
findFirst
()
.
orElse
(
null
);
if
(
prevOperationGene
!=
null
&&
startTime
<
prevOperationGene
.
getEndTime
())
{
// 调整提前量,基于有效准备时间
int
actualStartTime
=
Math
.
max
(
startTime
,
prevOperationGene
.
getEndTime
()
-
effectivePrepTime
);
if
(
actualStartTime
!=
startTime
)
{
geneDetails
=
GetNextAvailableTime
(
machine
,
actualStartTime
,
-
1
,
totalProcessingDuration
,
chromosome
.
getGenes
(),
false
,
true
);
startTime
=
geneDetails
.
stream
().
mapToInt
(
GeneDetail:
:
getStartTime
).
min
().
orElse
(
0
);
endTime
=
geneDetails
.
stream
().
mapToInt
(
GeneDetail:
:
getEndTime
).
max
().
orElse
(
0
);
}
}
}
// ==================== 修正的时间计算逻辑结束 ====================
machine
.
setEarliestTime
(
endTime
);
machine
.
setTotalTaskTime
(
machine
.
getTotalTaskTime
()
+
processingTime
+
effectivePrepTime
+
teardownTime
);
Gene
gene
=
new
Gene
();
gene
.
setOrderId
(
order
.
getId
());
gene
.
setProductId
(
order
.
getProductId
());
gene
.
setOperationId
(
operation
.
getId
());
gene
.
setMachineId
(
machineOption
.
getMachineId
());
gene
.
setBatchSize
(
batchSize
);
gene
.
setStartTime
(
startTime
);
// 包含前处理的开始时间
gene
.
setEndTime
(
endTime
);
// 包含后处理的结束时间
gene
.
setProcessingTime
(
processingTime
);
gene
.
setPreTime
(
preTime
);
gene
.
setSetupTime
(
setupTime
);
gene
.
setTeardownTime
(
teardownTime
);
gene
.
setOperationName
(
"工序"
+
operation
.
getId
());
gene
.
setGeneDetails
((
geneDetails
));
gene
.
setId
(
chromosome
.
getGenes
().
size
()
+
1
);
gene
.
setSequenceId
(
operation
.
getSequence
());
chromosome
.
getGenes
().
add
(
gene
);
int
absolutePreparationTime
=
calculateAbsolutePreparationTime
(
gene
,
machineOption
);
int
absoluteSetupTime
=
calculateAbsoluteSetupTime
(
gene
,
machineOption
);
gene
.
setAbsolutePreparationTime
(
absolutePreparationTime
);
int
absoluteStartTime
=
startTime
+
Math
.
max
(
absolutePreparationTime
,
absoluteSetupTime
);
int
absoluteTeardownTime
=
calculateAbsoluteTeardownTime
(
gene
,
machineOption
);
int
absoluteEndTime
=
endTime
-
absoluteTeardownTime
;
gene
.
setAbsoluteStartTime
(
absoluteStartTime
);
gene
.
setAbsoluteEndTime
(
absoluteEndTime
);
gene
.
setAbsoluteTeardownTime
(
absoluteTeardownTime
);
// 更新prevtime为当前工序的结束时间
prevtime
=
endTime
;
}
}
}
population
.
add
(
chromosome
);
}
System
.
out
.
println
(
"生成种群大小: "
+
population
.
size
());
Map
<
String
,
List
<
ScheduleChromosome
>>
grouped
=
population
.
stream
()
.
collect
(
Collectors
.
groupingBy
(
ScheduleChromosome:
:
getGenesStr
));
population
=
grouped
.
values
().
stream
()
.
map
(
group
->
group
.
get
(
0
))
.
collect
(
Collectors
.
toList
());
// 调试输出染色体多样性
System
.
out
.
println
(
"染色体多样性分析:"
);
Map
<
String
,
Integer
>
keyCounts
=
new
HashMap
<>();
for
(
ScheduleChromosome
chrom
:
population
)
{
String
key
=
chrom
.
getGenesStr
();
keyCounts
.
put
(
key
,
keyCounts
.
getOrDefault
(
key
,
0
)
+
1
);
}
System
.
out
.
println
(
"唯一染色体数量: "
+
keyCounts
.
size
());
// 并行计算适应度
population
.
parallelStream
().
forEach
(
this
::
CalculateFitness
);
// 打印摘要
population
.
forEach
(
this
::
WriteScheduleSummary
);
return
population
;
}
private
int
calculateSetupTime
(
List
<
Gene
>
existingGenes
,
Order
currentOrder
,
Machine
machine
,
MachineOption
machineOption
)
{
// 查找设备上最新的任务(按结束时间排序)
Gene
lastGeneOnMachine
=
existingGenes
.
stream
()
.
filter
(
g
->
g
.
getMachineId
()
==
machine
.
getId
())
.
max
(
Comparator
.
comparingInt
(
Gene:
:
getEndTime
))
.
orElse
(
null
);
// 无历史任务 → 换型时间0
if
(
lastGeneOnMachine
==
null
)
{
System
.
out
.
printf
(
"设备%d:无历史任务,换型时间=0%n"
,
machine
.
getId
());
return
0
;
}
// 产品不同 → 返回换型时间;产品相同 → 0
int
setupTime
=
(
lastGeneOnMachine
.
getProductId
()
!=
currentOrder
.
getProductId
())
?
machineOption
.
getSetupTime
()
:
0
;
System
.
out
.
printf
(
"设备%d:上一产品ID=%d,当前产品ID=%d,换型时间=%d分钟%n"
,
machine
.
getId
(),
lastGeneOnMachine
.
getProductId
(),
currentOrder
.
getProductId
(),
setupTime
);
return
setupTime
;
}
/**
* 计算调整后的开始时间,考虑前处理时间的重叠
*/
private
int
calculateAdjustedStartTime
(
List
<
Gene
>
existingGenes
,
int
orderId
,
int
operationId
,
int
preTime
,
int
prevTime
,
int
machineId
,
boolean
isFirstProductOnMachine
)
{
if
(
preTime
<=
0
||
operationId
==
1
)
{
return
prevTime
;
// 第一道工序或没有前处理时间,不提前
}
// 查找同一订单的上一个工序
Gene
prevOperationGene
=
existingGenes
.
stream
()
.
filter
(
g
->
g
.
getOrderId
()
==
orderId
&&
g
.
getOperationId
()
==
operationId
-
1
)
.
max
(
Comparator
.
comparingInt
(
Gene:
:
getEndTime
))
.
orElse
(
null
);
// 查找设备上一个任务的结束时间
Gene
lastGeneOnMachine
=
existingGenes
.
stream
()
.
filter
(
g
->
g
.
getMachineId
()
==
machineId
)
.
max
(
Comparator
.
comparingInt
(
Gene:
:
getEndTime
))
.
orElse
(
null
);
int
lastMachineTaskEndTime
=
lastGeneOnMachine
!=
null
?
lastGeneOnMachine
.
getEndTime
()
:
0
;
if
(
prevOperationGene
!=
null
)
{
// 关键修改:允许前处理时间提前开始
// 前处理可以在上一个工序结束前开始,最多提前preTime分钟
int
earliestPossibleStart
=
Math
.
max
(
Math
.
max
(
prevTime
,
lastMachineTaskEndTime
),
prevOperationGene
.
getEndTime
()
-
preTime
// 允许提前preTime分钟
);
// 但不能早于上一个工序的开始时间
earliestPossibleStart
=
Math
.
max
(
earliestPossibleStart
,
prevOperationGene
.
getStartTime
());
return
earliestPossibleStart
;
}
return
Math
.
max
(
prevTime
,
lastMachineTaskEndTime
);
}
/**
* 验证时间安排的合理性
*/
/**
* 验证时间安排的合理性
*/
/**
* 验证时间安排的合理性
*/
private
boolean
validateTimeArrangement
(
List
<
Gene
>
existingGenes
,
int
orderId
,
int
operationId
,
int
startTime
,
int
endTime
,
int
preTime
,
boolean
isFirstProductOnMachine
)
{
// 验证1:开始时间不能小于0
if
(
startTime
<
0
)
{
return
false
;
}
// 验证2:第一道工序的前处理时间不能提前
if
(
operationId
==
1
&&
preTime
>
0
)
{
// 第一道工序的前处理时间应该从开始时间计算,不能提前
// 但需要有足够的时间包含前处理时间
if
(
endTime
-
startTime
<
preTime
)
{
return
false
;
// 总时间不足以包含前处理时间
}
}
// 验证3:对于非首道工序,前处理时间不能早于上一个工序的开始时间
if
(
operationId
>
1
&&
preTime
>
0
)
{
Gene
prevOperationGene
=
existingGenes
.
stream
()
.
filter
(
g
->
g
.
getOrderId
()
==
orderId
&&
g
.
getOperationId
()
==
operationId
-
1
)
.
max
(
Comparator
.
comparingInt
(
Gene:
:
getStartTime
))
.
orElse
(
null
);
if
(
prevOperationGene
!=
null
)
{
// 前处理开始时间不能早于上一个工序的开始时间
if
(
startTime
<
prevOperationGene
.
getStartTime
())
{
return
false
;
}
}
}
// 验证4:结束时间必须大于开始时间
if
(
endTime
<=
startTime
)
{
return
false
;
}
// 验证5:总时间必须足够包含所有处理时间
int
totalRequiredTime
=
preTime
+
(
endTime
-
startTime
-
preTime
);
// 简化计算
if
(
endTime
-
startTime
<
totalRequiredTime
)
{
return
false
;
}
return
true
;
}
/**
* 计算总处理时间(用于调试和验证)
*/
private
int
calculateTotalDuration
(
Gene
gene
)
{
return
(
gene
.
getEndTime
()
-
gene
.
getStartTime
())
-
(
gene
.
getPreTime
()
+
gene
.
getTeardownTime
()
+
gene
.
getSetupTime
());
}
// ==================== 原有方法保持不变 ====================
private
List
<
ScheduleChromosome
>
removeTrueDuplicates
(
List
<
ScheduleChromosome
>
population
)
{
Map
<
String
,
ScheduleChromosome
>
uniqueMap
=
new
LinkedHashMap
<>();
for
(
ScheduleChromosome
chrom
:
population
)
{
String
key
=
generateChromosomeKey
(
chrom
);
uniqueMap
.
putIfAbsent
(
key
,
chrom
);
}
return
new
ArrayList
<>(
uniqueMap
.
values
());
}
private
String
generateChromosomeKey
(
ScheduleChromosome
chrom
)
{
return
chrom
.
getGenes
().
stream
()
.
sorted
(
Comparator
.
comparing
(
Gene:
:
getOrderId
)
.
thenComparing
(
Gene:
:
getOperationId
)
.
thenComparing
(
Gene:
:
getStartTime
))
.
map
(
g
->
String
.
format
(
"%d-%d-%d-%d-%d-%d"
,
g
.
getOrderId
(),
g
.
getOperationId
(),
g
.
getMachineId
(),
g
.
getBatchSize
(),
g
.
getStartTime
(),
g
.
getEndTime
()))
.
collect
(
Collectors
.
joining
(
"|"
));
}
private
void
CalculateFitness
(
ScheduleChromosome
chromosome
)
{
// 1. 最大完工时间
double
makespan
=
chromosome
.
getGenes
().
stream
()
.
mapToInt
(
Gene:
:
getEndTime
)
.
max
()
.
orElse
(
0
);
// 2. 总延迟时间
double
tardiness
=
0.0
;
Map
<
Integer
,
List
<
Gene
>>
orderGroups
=
chromosome
.
getGenes
().
stream
()
.
collect
(
Collectors
.
groupingBy
(
Gene:
:
getOrderId
));
for
(
Map
.
Entry
<
Integer
,
List
<
Gene
>>
group
:
orderGroups
.
entrySet
())
{
int
orderCompletion
=
group
.
getValue
().
stream
()
.
mapToInt
(
Gene:
:
getEndTime
)
.
max
()
.
orElse
(
0
);
Order
order
=
_orders
.
stream
()
.
filter
(
o
->
o
.
getId
()
==
group
.
getKey
())
.
findFirst
()
.
orElse
(
null
);
if
(
order
!=
null
)
{
LocalDateTime
completionTime
=
chromosome
.
getBaseTime
().
plusMinutes
(
orderCompletion
);
LocalDateTime
dueDateTime
=
order
.
getDueDate
().
toLocalDateTime
();
if
(
completionTime
.
isAfter
(
dueDateTime
))
{
long
totalMinutes
=
java
.
time
.
temporal
.
ChronoUnit
.
MINUTES
.
between
(
dueDateTime
,
completionTime
);
double
tardinessHours
=
totalMinutes
/
60.0
;
order
.
setTardiness
(
tardinessHours
);
tardiness
+=
tardinessHours
;
}
}
}
// 3. 总换型时间
double
totalSetupTime
=
CalculateTotalSetupTime
(
chromosome
);
// 4. 总流程时间
double
totalFlowTime
=
CalculateTotalFlowTime
(
chromosome
);
// 5. 机器负载均衡
double
machineLoadBalance
=
CalculateMachineLoadBalance
(
chromosome
);
// 存储目标值
double
finalTardiness
=
tardiness
;
HashMap
<
Integer
,
Double
>
objMap
=
new
HashMap
<>();
objMap
.
put
(
0
,
makespan
);
objMap
.
put
(
1
,
finalTardiness
);
objMap
.
put
(
2
,
totalSetupTime
);
objMap
.
put
(
3
,
totalFlowTime
);
objMap
.
put
(
4
,
machineLoadBalance
);
chromosome
.
setObjectiveValues
(
objMap
);
// 计算适应度
chromosome
.
setFitness
(
0.4
*
(
1.0
/
(
1
+
makespan
))
+
0.3
*
(
1.0
/
(
1
+
tardiness
))
+
0.1
*
(
1.0
/
(
1
+
totalSetupTime
))
+
0.1
*
(
1.0
/
(
1
+
totalFlowTime
))
+
0.1
*
machineLoadBalance
);
}
private
double
CalculateTotalFlowTime
(
ScheduleChromosome
chromosome
)
{
double
totalFlowTime
=
0.0
;
Map
<
Integer
,
List
<
Gene
>>
orderGroups
=
chromosome
.
getGenes
().
stream
()
.
collect
(
Collectors
.
groupingBy
(
Gene:
:
getOrderId
));
for
(
Map
.
Entry
<
Integer
,
List
<
Gene
>>
group
:
orderGroups
.
entrySet
())
{
int
minStartTime
=
group
.
getValue
().
stream
().
mapToInt
(
Gene:
:
getStartTime
).
min
().
orElse
(
0
);
int
maxEndTime
=
group
.
getValue
().
stream
().
mapToInt
(
Gene:
:
getEndTime
).
max
().
orElse
(
0
);
LocalDateTime
start
=
chromosome
.
getBaseTime
().
plusMinutes
(
minStartTime
);
LocalDateTime
end
=
chromosome
.
getBaseTime
().
plusMinutes
(
maxEndTime
);
long
hours
=
ChronoUnit
.
HOURS
.
between
(
start
,
end
);
long
minutes
=
ChronoUnit
.
MINUTES
.
between
(
start
,
end
)
%
60
;
totalFlowTime
+=
hours
+
(
double
)
minutes
/
60
;
}
return
totalFlowTime
;
}
private
double
CalculateTotalSetupTime
(
ScheduleChromosome
chromosome
)
{
double
totalSetupTime
=
0.0
;
double
totalTeardownTime
=
0.0
;
double
totalPreTime
=
0.0
;
Map
<
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
());
// 累加所有任务的前处理时间和后处理时间
for
(
Gene
gene
:
sortedGenes
)
{
totalPreTime
+=
gene
.
getPreTime
();
totalTeardownTime
+=
gene
.
getTeardownTime
();
}
// 计算换型时间(仅在产品变化时触发)
Integer
lastProductId
=
null
;
for
(
Gene
gene
:
sortedGenes
)
{
if
(
lastProductId
!=
null
&&
lastProductId
!=
gene
.
getProductId
())
{
Product
product
=
_products
.
stream
()
.
filter
(
p
->
p
.
getId
()
==
gene
.
getProductId
())
.
findFirst
()
.
orElse
(
null
);
if
(
product
==
null
)
continue
;
Operation
operation
=
product
.
getOperations
().
stream
()
.
filter
(
o
->
o
.
getId
()
==
gene
.
getOperationId
())
.
findFirst
()
.
orElse
(
null
);
if
(
operation
==
null
)
continue
;
MachineOption
machineOption
=
operation
.
getMachineOptions
().
stream
()
.
filter
(
m
->
m
.
getMachineId
()
==
gene
.
getMachineId
())
.
findFirst
()
.
orElse
(
null
);
if
(
machineOption
!=
null
)
{
totalSetupTime
+=
machineOption
.
getSetupTime
();
}
}
lastProductId
=
gene
.
getProductId
();
}
}
return
totalSetupTime
+
totalPreTime
+
totalTeardownTime
;
}
private
double
CalculateMachineLoadBalance
(
ScheduleChromosome
chromosome
)
{
Map
<
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
);
}
private
void
RemoveMachineAvailable
(
Machine
machine
,
GeneDetail
geneDetails
)
{
List
<
TimeSegment
>
timeSegments
=
new
ArrayList
<>();
int
index
=
machine
.
getAvailability
().
stream
()
.
filter
(
t
->
t
.
getKey
().
equals
(
geneDetails
.
getKey
()))
.
findFirst
()
.
map
(
machine
.
getAvailability
()::
indexOf
)
.
orElse
(-
1
);
if
(
index
>
-
1
)
{
TimeSegment
targetSegment
=
machine
.
getAvailability
().
get
(
index
);
LocalDateTime
geneEndTime
=
baseTime
.
plusMinutes
(
geneDetails
.
getEndTime
());
if
(
targetSegment
.
getEnd
().
isAfter
(
geneEndTime
))
{
TimeSegment
usedSegment
=
new
TimeSegment
();
usedSegment
.
setStart
(
baseTime
.
plusMinutes
(
geneDetails
.
getStartTime
()));
usedSegment
.
setEnd
(
geneEndTime
);
usedSegment
.
setHoliday
(
false
);
usedSegment
.
setKey
(
UUID
.
randomUUID
().
toString
());
usedSegment
.
setType
(
SegmentType
.
REGULAR
);
usedSegment
.
setUsed
(
true
);
timeSegments
.
add
(
usedSegment
);
geneDetails
.
setKey
(
usedSegment
.
getKey
());
targetSegment
.
setStart
(
geneEndTime
);
}
else
{
targetSegment
.
setUsed
(
true
);
}
}
if
(!
timeSegments
.
isEmpty
())
{
machine
.
getAvailability
().
addAll
(
timeSegments
);
}
machine
.
getAvailability
().
sort
(
Comparator
.
comparing
(
TimeSegment:
:
getStart
));
}
private
void
AddMachineAvailable
(
Machine
machine
,
List
<
GeneDetail
>
geneDetails
)
{
if
(
geneDetails
==
null
||
geneDetails
.
isEmpty
())
return
;
for
(
GeneDetail
detail
:
geneDetails
)
{
machine
.
getAvailability
().
stream
()
.
filter
(
t
->
t
.
getKey
().
equals
(
detail
.
getKey
()))
.
findFirst
()
.
ifPresent
(
t
->
t
.
setUsed
(
false
));
}
machine
.
setAvailability
(
MergeSegments
(
machine
.
getAvailability
()));
}
private
List
<
TimeSegment
>
MergeSegments
(
List
<
TimeSegment
>
segments
)
{
List
<
TimeSegment
>
maintenanceSegments
=
segments
.
stream
()
.
filter
(
t
->
t
.
getType
()
==
SegmentType
.
MAINTENANCE
)
.
collect
(
Collectors
.
toList
());
List
<
TimeSegment
>
unusedRegularSegments
=
segments
.
stream
()
.
filter
(
t
->
t
.
getType
()
!=
SegmentType
.
MAINTENANCE
&&
!
t
.
isUsed
())
.
collect
(
Collectors
.
toList
());
List
<
TimeSegment
>
usedRegularSegments
=
segments
.
stream
()
.
filter
(
t
->
t
.
getType
()
!=
SegmentType
.
MAINTENANCE
&&
t
.
isUsed
())
.
collect
(
Collectors
.
toList
());
unusedRegularSegments
.
sort
(
Comparator
.
comparing
(
TimeSegment:
:
getStart
));
Deque
<
TimeSegment
>
mergedUnused
=
new
ArrayDeque
<>();
for
(
TimeSegment
seg
:
unusedRegularSegments
)
{
if
(!
mergedUnused
.
isEmpty
()
&&
mergedUnused
.
peekLast
().
getEnd
().
isAfter
(
seg
.
getStart
()))
{
TimeSegment
last
=
mergedUnused
.
pollLast
();
last
.
setEnd
(
last
.
getEnd
().
isAfter
(
seg
.
getEnd
())
?
last
.
getEnd
()
:
seg
.
getEnd
());
mergedUnused
.
offerLast
(
last
);
}
else
{
mergedUnused
.
offerLast
(
seg
);
}
}
List
<
TimeSegment
>
result
=
new
ArrayList
<>(
mergedUnused
);
result
.
addAll
(
usedRegularSegments
);
result
.
addAll
(
maintenanceSegments
);
result
.
sort
(
Comparator
.
comparing
(
TimeSegment:
:
getStart
));
return
result
;
}
private
List
<
GeneDetail
>
GetNextAvailableTime
(
Machine
machine
,
int
proposedStartTime
,
int
prevtime
,
int
processingTime
,
List
<
Gene
>
existingTasks
,
boolean
IsInterrupt
,
boolean
istask
)
{
LocalDateTime
startTime
=
baseTime
.
plusMinutes
(
proposedStartTime
);
String
prevtimeStr
=
prevtime
>
-
1
?
baseTime
.
plusMinutes
(
prevtime
).
toString
()
:
""
;
return
FindEarliestStart
(
machine
,
processingTime
,
startTime
,
prevtimeStr
,
existingTasks
,
istask
);
}
private
List
<
GeneDetail
>
FindEarliestStart
(
Machine
machine
,
int
processingTime
,
LocalDateTime
currentTime
,
String
prevtime
,
List
<
Gene
>
existingTasks
,
boolean
checkprevtime
)
{
List
<
Gene
>
machineTasks
=
existingTasks
.
stream
()
.
filter
(
t
->
t
.
getMachineId
()
==
machine
.
getId
())
.
sorted
(
Comparator
.
comparingInt
(
Gene:
:
getStartTime
))
.
collect
(
Collectors
.
toList
());
List
<
GeneDetail
>
times
=
new
ArrayList
<>();
TimeSegment
slot
=
GetCurrentOrNextShift
(
machine
,
currentTime
,
prevtime
,
checkprevtime
);
if
(
slot
==
null
)
return
times
;
LocalDateTime
prevTimeDateTime
=
StringUtils
.
isEmpty
(
prevtime
)
?
null
:
LocalDateTime
.
parse
(
prevtime
);
LocalDateTime
startCandidate
=
slot
.
getStart
().
isAfter
(
prevTimeDateTime
!=
null
?
prevTimeDateTime
:
currentTime
)
?
slot
.
getStart
()
:
(
prevTimeDateTime
!=
null
?
prevTimeDateTime
:
currentTime
);
LocalDateTime
endCandidate
=
startCandidate
.
plusMinutes
(
processingTime
);
if
(
endCandidate
.
isAfter
(
slot
.
getEnd
()))
{
return
CaldEarliestStart
(
machine
,
processingTime
,
currentTime
,
prevtime
,
machineTasks
,
checkprevtime
);
}
else
{
GeneDetail
time
=
new
GeneDetail
();
time
.
setKey
(
slot
.
getKey
());
time
.
setStartTime
((
int
)
ChronoUnit
.
MINUTES
.
between
(
baseTime
,
startCandidate
));
time
.
setEndTime
((
int
)
ChronoUnit
.
MINUTES
.
between
(
baseTime
,
endCandidate
));
times
.
add
(
time
);
RemoveMachineAvailable
(
machine
,
time
);
return
times
;
}
}
private
List
<
GeneDetail
>
CaldEarliestStart
(
Machine
machine
,
int
processingTime
,
LocalDateTime
currentTime
,
String
prevtime
,
List
<
Gene
>
machineTasks
,
boolean
checkprevtime
)
{
int
remainingTime
=
processingTime
;
LocalDateTime
st
=
StringUtils
.
isEmpty
(
prevtime
)
?
currentTime
:
LocalDateTime
.
parse
(
prevtime
);
LocalDateTime
prevEnd
=
LocalDateTime
.
of
(
2000
,
1
,
1
,
0
,
0
,
0
);
List
<
GeneDetail
>
times
=
new
ArrayList
<>();
List
<
GeneDetail
>
oldTimes
=
new
ArrayList
<>();
while
(
remainingTime
>
0
)
{
TimeSegment
shift
=
GetCurrentOrNextShift
(
machine
,
currentTime
,
prevtime
,
checkprevtime
);
if
(
shift
==
null
)
break
;
LocalDateTime
shiftStart
=
shift
.
getStart
();
LocalDateTime
shiftEnd
=
shift
.
getEnd
();
// 检查班次间冲突
if
(!
prevEnd
.
isEqual
(
LocalDateTime
.
of
(
2000
,
1
,
1
,
0
,
0
,
0
)))
{
if
(
prevEnd
.
isBefore
(
currentTime
))
{
// 检查班次间任务
LocalDateTime
finalPrevEnd
=
prevEnd
;
boolean
hasTask
=
machineTasks
.
stream
()
.
anyMatch
(
t
->
{
LocalDateTime
taskStart
=
baseTime
.
plusMinutes
(
t
.
getStartTime
());
return
taskStart
.
isAfter
(
finalPrevEnd
)
&&
taskStart
.
isBefore
(
shiftStart
);
});
if
(
hasTask
)
{
// 重置状态
currentTime
=
shiftStart
;
st
=
shiftStart
;
remainingTime
=
processingTime
;
prevEnd
=
LocalDateTime
.
of
(
2000
,
1
,
1
,
0
,
0
,
0
);
oldTimes
.
addAll
(
deepCopyGeneDetailList
(
times
));
times
.
clear
();
continue
;
}
// 检查班次间维修窗口
if
(
machine
.
getMaintenanceWindows
()
!=
null
)
{
LocalDateTime
finalPrevEnd1
=
prevEnd
;
boolean
hasMaintenance
=
machine
.
getMaintenanceWindows
().
stream
()
.
anyMatch
(
w
->
w
.
getStartTime
().
isAfter
(
finalPrevEnd1
)
&&
w
.
getStartTime
().
isBefore
(
shiftStart
));
if
(
hasMaintenance
)
{
currentTime
=
shiftStart
;
st
=
shiftStart
;
remainingTime
=
processingTime
;
prevEnd
=
LocalDateTime
.
of
(
2000
,
1
,
1
,
0
,
0
,
0
);
times
.
clear
();
continue
;
}
}
}
}
prevEnd
=
shiftEnd
;
// 计算有效时间
LocalDateTime
effectiveStart
=
st
.
isAfter
(
shiftStart
)
?
st
:
shiftStart
;
long
availableMinutes
=
ChronoUnit
.
MINUTES
.
between
(
effectiveStart
,
shiftEnd
);
// 处理当前班次
int
processable
=
Math
.
min
(
remainingTime
,
(
int
)
availableMinutes
);
remainingTime
-=
processable
;
currentTime
=
effectiveStart
.
plusMinutes
(
processable
);
// 添加时间详情
GeneDetail
time
=
new
GeneDetail
();
time
.
setKey
(
shift
.
getKey
());
time
.
setStartTime
((
int
)
ChronoUnit
.
MINUTES
.
between
(
baseTime
,
effectiveStart
));
time
.
setEndTime
((
int
)
ChronoUnit
.
MINUTES
.
between
(
baseTime
,
currentTime
));
times
.
add
(
time
);
RemoveMachineAvailable
(
machine
,
time
);
}
// 还原未使用的时间段
AddMachineAvailable
(
machine
,
oldTimes
);
return
times
;
}
private
TimeSegment
GetCurrentOrNextShift
(
Machine
machine
,
LocalDateTime
time
,
String
prevtime
,
boolean
checkprevtime
)
{
TimeSegment
start
=
null
;
// 查找有效班次
start
=
machine
.
getAvailability
().
stream
()
.
filter
(
slot
->
!
slot
.
isUsed
()
&&
slot
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
slot
->
slot
.
getStart
().
isAfter
(
time
)
||
slot
.
getEnd
().
isAfter
(
time
))
.
findFirst
()
.
orElse
(
null
);
if
(
start
==
null
)
{
// 生成新时间段
List
<
TimeSegment
>
timeSegments
=
_machineScheduler
.
generateTimeSegment
(
machine
,
time
);
machine
.
getAvailability
().
addAll
(
timeSegments
);
// 更新设备时间线
Machine
originalMachine
=
_machines
.
stream
()
.
filter
(
t
->
t
.
getId
()
==
machine
.
getId
())
.
findFirst
()
.
orElse
(
null
);
if
(
originalMachine
!=
null
)
{
MachineTimeline
timeline
=
_machineScheduler
.
getOrCreateTimeline
(
originalMachine
);
}
// 递归查找
return
GetCurrentOrNextShift
(
machine
,
time
,
prevtime
,
checkprevtime
);
}
return
start
;
}
public
void
PrintScheduleSummary
(
ScheduleChromosome
schedule
)
{
System
.
out
.
println
(
"\n=== Schedule Summary ==="
);
// 按订单分组打印
Map
<
Integer
,
List
<
Gene
>>
orderGroups
=
schedule
.
getGenes
().
stream
()
.
collect
(
Collectors
.
groupingBy
(
Gene:
:
getOrderId
));
for
(
Map
.
Entry
<
Integer
,
List
<
Gene
>>
group
:
orderGroups
.
entrySet
())
{
System
.
out
.
printf
(
"\nOrder %d Schedule:%n"
,
group
.
getKey
());
// 按工序排序
List
<
Gene
>
sortedJobs
=
group
.
getValue
().
stream
()
.
sorted
(
Comparator
.
comparingInt
(
Gene:
:
getOperationId
))
.
collect
(
Collectors
.
toList
());
for
(
Gene
job
:
sortedJobs
)
{
Product
product
=
_products
.
stream
()
.
filter
(
p
->
p
.
getId
()
==
job
.
getProductId
())
.
findFirst
()
.
orElse
(
null
);
Operation
operation
=
product
!=
null
?
product
.
getOperations
().
stream
()
.
filter
(
o
->
o
.
getId
()
==
job
.
getOperationId
())
.
findFirst
()
.
orElse
(
null
)
:
null
;
if
(
product
!=
null
&&
operation
!=
null
)
{
String
print
=
String
.
format
(
"[%s-%s] Order %d, Product %s, Machine %d, Operation %d, Batch %d, "
+
"processingTime %d, preTime %d, setupTime %d, teardownTime %d, "
+
"实际加工时间: %d分钟"
,
ConvertTime
(
job
.
getStartTime
()),
ConvertTime
(
job
.
getEndTime
()),
job
.
getOrderId
(),
product
.
getName
(),
job
.
getMachineId
(),
operation
.
getSequence
(),
job
.
getBatchSize
(),
job
.
getProcessingTime
(),
job
.
getPreTime
(),
job
.
getSetupTime
(),
job
.
getTeardownTime
(),
(
job
.
getEndTime
()
-
job
.
getStartTime
())
-
(
job
.
getPreTime
()
+
job
.
getTeardownTime
()
+
job
.
getSetupTime
())
);
System
.
out
.
println
(
print
);
}
}
}
}
public
void
WriteScheduleSummary
(
ScheduleChromosome
schedule
)
{
// 写入日志
FileHelper
.
writeLogFile
(
String
.
format
(
"\n=== Schedule Summary === 适应度: %f"
,
schedule
.
getFitness
()));
FileHelper
.
writeLogFile
(
String
.
format
(
"最大完工时间: %f 分钟"
,
schedule
.
getObjectiveValues
().
get
(
0
)));
FileHelper
.
writeLogFile
(
String
.
format
(
"总延迟时间: %f 小时"
,
schedule
.
getObjectiveValues
().
get
(
1
)));
FileHelper
.
writeLogFile
(
String
.
format
(
"总换型时间: %f 分钟"
,
schedule
.
getObjectiveValues
().
get
(
2
)));
FileHelper
.
writeLogFile
(
String
.
format
(
"总流程时间: %f 分钟"
,
schedule
.
getObjectiveValues
().
get
(
3
)));
FileHelper
.
writeLogFile
(
String
.
format
(
"机器负载均衡: %.2f%%"
,
schedule
.
getObjectiveValues
().
get
(
4
)
*
100
));
FileHelper
.
writeLogFile
(
"-------------------------"
);
// 按订单分组写入
Map
<
Integer
,
List
<
Gene
>>
orderGroups
=
schedule
.
getGenes
().
stream
()
.
collect
(
Collectors
.
groupingBy
(
Gene:
:
getOrderId
));
for
(
Map
.
Entry
<
Integer
,
List
<
Gene
>>
group
:
orderGroups
.
entrySet
())
{
FileHelper
.
writeLogFile
(
String
.
format
(
"\n订单 %d 的调度安排:"
,
group
.
getKey
()));
List
<
Gene
>
sortedJobs
=
group
.
getValue
().
stream
()
.
sorted
(
Comparator
.
comparingInt
(
Gene:
:
getOperationId
))
.
collect
(
Collectors
.
toList
());
for
(
Gene
job
:
sortedJobs
)
{
Product
product
=
_products
.
stream
()
.
filter
(
p
->
p
.
getId
()
==
job
.
getProductId
())
.
findFirst
()
.
orElse
(
null
);
Operation
operation
=
product
!=
null
?
product
.
getOperations
().
stream
()
.
filter
(
o
->
o
.
getId
()
==
job
.
getOperationId
())
.
findFirst
()
.
orElse
(
null
)
:
null
;
if
(
product
!=
null
&&
operation
!=
null
)
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
String
.
format
(
"[%d-%d]:[%s-%s] 订单%d, 产品%s, 设备%d, 工序%d, 批次%d, "
+
"加工%d分钟, 前处理%d分钟, 换型%d分钟, 后处理%d分钟, 总时长%d分钟"
,
job
.
getStartTime
(),
job
.
getEndTime
(),
ConvertTime
(
job
.
getStartTime
()),
ConvertTime
(
job
.
getEndTime
()),
job
.
getOrderId
(),
product
.
getName
(),
job
.
getMachineId
(),
operation
.
getSequence
(),
job
.
getBatchSize
(),
job
.
getProcessingTime
(),
job
.
getPreTime
(),
job
.
getSetupTime
(),
job
.
getTeardownTime
(),
job
.
getEndTime
()
-
job
.
getStartTime
()
));
// 追加基因详情
for
(
GeneDetail
d
:
job
.
getGeneDetails
())
{
sb
.
append
(
String
.
format
(
"\n\t\t\t\t\t\t\t\t\t\t [%d-%d]:[%s-%s] 时长%d分钟"
,
d
.
getStartTime
(),
d
.
getEndTime
(),
ConvertTime
(
d
.
getStartTime
()),
ConvertTime
(
d
.
getEndTime
()),
d
.
getEndTime
()
-
d
.
getStartTime
()
));
}
FileHelper
.
writeLogFile
(
sb
.
toString
());
}
}
FileHelper
.
writeLogFile
(
""
);
}
}
private
String
ConvertTime
(
int
minute
)
{
return
baseTime
.
plusMinutes
(
minute
).
format
(
java
.
time
.
format
.
DateTimeFormatter
.
ofPattern
(
"MM-dd HH:mm"
));
}
// ========================== 深拷贝工具方法 ==========================
private
List
<
Product
>
deepCopyProductList
(
List
<
Product
>
source
)
{
return
source
==
null
?
new
ArrayList
<>()
:
source
.
stream
().
map
(
this
::
deepCopyProduct
).
collect
(
Collectors
.
toList
());
}
private
Product
deepCopyProduct
(
Product
source
)
{
if
(
source
==
null
)
return
null
;
Product
copy
=
new
Product
();
copy
.
setId
(
source
.
getId
());
copy
.
setName
(
source
.
getName
());
copy
.
setOperations
(
deepCopyOperationList
(
source
.
getOperations
()));
return
copy
;
}
private
List
<
Operation
>
deepCopyOperationList
(
List
<
Operation
>
source
)
{
return
source
==
null
?
new
ArrayList
<>()
:
source
.
stream
().
map
(
this
::
deepCopyOperation
).
collect
(
Collectors
.
toList
());
}
private
Operation
deepCopyOperation
(
Operation
source
)
{
if
(
source
==
null
)
return
null
;
Operation
copy
=
new
Operation
();
copy
.
setId
(
source
.
getId
());
copy
.
setProductId
(
source
.
getProductId
());
copy
.
setSequence
(
source
.
getSequence
());
copy
.
setInterrupt
(
source
.
isInterrupt
());
copy
.
setMachineOptions
(
deepCopyMachineOptionList
(
source
.
getMachineOptions
()));
return
copy
;
}
private
List
<
MachineOption
>
deepCopyMachineOptionList
(
List
<
MachineOption
>
source
)
{
return
source
==
null
?
new
ArrayList
<>()
:
source
.
stream
().
map
(
this
::
deepCopyMachineOption
).
collect
(
Collectors
.
toList
());
}
private
MachineOption
deepCopyMachineOption
(
MachineOption
source
)
{
if
(
source
==
null
)
return
null
;
MachineOption
copy
=
new
MachineOption
();
copy
.
setMachineId
(
source
.
getMachineId
());
copy
.
setProcessingTime
(
source
.
getProcessingTime
());
copy
.
setPreTime
(
source
.
getPreTime
());
copy
.
setSetupTime
(
source
.
getSetupTime
());
copy
.
setTeardownTime
(
source
.
getTeardownTime
());
return
copy
;
}
private
List
<
Machine
>
deepCopyMachineList
(
List
<
Machine
>
source
)
{
return
source
==
null
?
new
ArrayList
<>()
:
source
.
stream
().
map
(
this
::
deepCopyMachine
).
collect
(
Collectors
.
toList
());
}
private
Machine
deepCopyMachine
(
Machine
source
)
{
if
(
source
==
null
)
return
null
;
Machine
copy
=
new
Machine
();
copy
.
setId
(
source
.
getId
());
copy
.
setName
(
source
.
getName
());
copy
.
setEarliestTime
(
source
.
getEarliestTime
());
copy
.
setTotalTaskTime
(
source
.
getTotalTaskTime
());
copy
.
setShifts
(
deepCopyShiftList
(
source
.
getShifts
()));
copy
.
setMaintenanceWindows
(
deepCopyMaintenanceWindowList
(
source
.
getMaintenanceWindows
()));
copy
.
setAvailability
(
deepCopyTimeSegmentList
(
source
.
getAvailability
()));
copy
.
setShiftsChanged
(
source
.
getShiftsChanged
());
copy
.
setMaintenanceWindowsChanged
(
source
.
getMaintenanceWindowsChanged
());
return
copy
;
}
private
List
<
Shift
>
deepCopyShiftList
(
List
<
Shift
>
source
)
{
return
source
==
null
?
new
ArrayList
<>()
:
source
.
stream
().
map
(
this
::
deepCopyShift
).
collect
(
Collectors
.
toList
());
}
private
Shift
deepCopyShift
(
Shift
source
)
{
if
(
source
==
null
)
return
null
;
Shift
copy
=
new
Shift
();
copy
.
setStartTime
(
source
.
getStartTime
());
copy
.
setEndTime
(
source
.
getEndTime
());
copy
.
setDays
(
source
.
getDays
()
!=
null
?
new
HashSet
<>(
source
.
getDays
())
:
null
);
copy
.
setShiftDate
(
source
.
getShiftDate
());
copy
.
setTemporaryShift
(
source
.
isTemporaryShift
());
copy
.
setPriority
(
source
.
getPriority
());
copy
.
setStatus
(
source
.
getStatus
());
copy
.
setMachineId
(
source
.
getMachineId
());
copy
.
setMachineName
(
source
.
getMachineName
());
return
copy
;
}
private
List
<
MaintenanceWindow
>
deepCopyMaintenanceWindowList
(
List
<
MaintenanceWindow
>
source
)
{
return
source
==
null
?
new
ArrayList
<>()
:
source
.
stream
().
map
(
this
::
deepCopyMaintenanceWindow
).
collect
(
Collectors
.
toList
());
}
private
MaintenanceWindow
deepCopyMaintenanceWindow
(
MaintenanceWindow
source
)
{
if
(
source
==
null
)
return
null
;
MaintenanceWindow
copy
=
new
MaintenanceWindow
();
copy
.
setStartTime
(
source
.
getStartTime
());
copy
.
setEndTime
(
source
.
getEndTime
());
copy
.
setReason
(
source
.
getReason
());
return
copy
;
}
private
List
<
TimeSegment
>
deepCopyTimeSegmentList
(
List
<
TimeSegment
>
source
)
{
return
source
==
null
?
new
ArrayList
<>()
:
source
.
stream
().
map
(
this
::
deepCopyTimeSegment
).
collect
(
Collectors
.
toList
());
}
private
TimeSegment
deepCopyTimeSegment
(
TimeSegment
source
)
{
if
(
source
==
null
)
return
null
;
TimeSegment
copy
=
new
TimeSegment
();
copy
.
setKey
(
source
.
getKey
());
copy
.
setStart
(
source
.
getStart
());
copy
.
setEnd
(
source
.
getEnd
());
copy
.
setEarliestTime
(
source
.
getEarliestTime
());
copy
.
setTotalTaskTime
(
source
.
getTotalTaskTime
());
copy
.
setType
(
source
.
getType
());
copy
.
setHoliday
(
source
.
isHoliday
());
copy
.
setUsed
(
source
.
isUsed
());
return
copy
;
}
private
List
<
Order
>
deepCopyOrderList
(
List
<
Order
>
source
)
{
return
source
==
null
?
new
ArrayList
<>()
:
source
.
stream
().
map
(
this
::
deepCopyOrder
).
collect
(
Collectors
.
toList
());
}
private
Order
deepCopyOrder
(
Order
source
)
{
if
(
source
==
null
)
return
null
;
Order
copy
=
new
Order
();
copy
.
setId
(
source
.
getId
());
copy
.
setProductId
(
source
.
getProductId
());
copy
.
setQuantity
(
source
.
getQuantity
());
copy
.
setDueDate
(
source
.
getDueDate
());
copy
.
setOrderCompletion
(
source
.
getOrderCompletion
());
copy
.
setTardiness
(
source
.
getTardiness
());
copy
.
setPriority
(
source
.
getPriority
());
copy
.
setCanSplit
(
source
.
isCanSplit
());
copy
.
setCanInterrupt
(
source
.
isCanInterrupt
());
return
copy
;
}
private
List
<
Gene
>
deepCopyGeneList
(
List
<
Gene
>
source
)
{
return
source
==
null
?
new
ArrayList
<>()
:
source
.
stream
().
map
(
this
::
deepCopyGene
).
collect
(
Collectors
.
toList
());
}
private
Gene
deepCopyGene
(
Gene
source
)
{
if
(
source
==
null
)
return
null
;
Gene
copy
=
new
Gene
();
copy
.
setOrderId
(
source
.
getOrderId
());
copy
.
setProductId
(
source
.
getProductId
());
copy
.
setOperationId
(
source
.
getOperationId
());
copy
.
setMachineId
(
source
.
getMachineId
());
copy
.
setBatchSize
(
source
.
getBatchSize
());
copy
.
setStartTime
(
source
.
getStartTime
());
copy
.
setEndTime
(
source
.
getEndTime
());
copy
.
setProcessingTime
(
source
.
getProcessingTime
());
copy
.
setPreTime
(
source
.
getPreTime
());
copy
.
setSetupTime
(
source
.
getSetupTime
());
copy
.
setTeardownTime
(
source
.
getTeardownTime
());
copy
.
setOperationName
(
source
.
getOperationName
());
copy
.
setGeneDetails
(
deepCopyGeneDetailList
(
source
.
getGeneDetails
()));
return
copy
;
}
private
List
<
GeneDetail
>
deepCopyGeneDetailList
(
List
<
GeneDetail
>
source
)
{
return
source
==
null
?
new
ArrayList
<>()
:
source
.
stream
().
map
(
this
::
deepCopyGeneDetail
).
collect
(
Collectors
.
toList
());
}
private
GeneDetail
deepCopyGeneDetail
(
GeneDetail
source
)
{
if
(
source
==
null
)
return
null
;
GeneDetail
copy
=
new
GeneDetail
();
copy
.
setKey
(
source
.
getKey
());
copy
.
setStartTime
(
source
.
getStartTime
());
copy
.
setEndTime
(
source
.
getEndTime
());
return
copy
;
}
private
ScheduleChromosome
deepCopyScheduleChromosome
(
ScheduleChromosome
source
)
{
if
(
source
==
null
)
return
null
;
ScheduleChromosome
copy
=
new
ScheduleChromosome
(
source
.
getBaseTime
());
copy
.
setGenes
(
deepCopyGeneList
(
source
.
getGenes
()));
copy
.
setOrders
(
deepCopyOrderList
(
source
.
getOrders
()));
copy
.
setMachines
(
deepCopyMachineList
(
source
.
getMachines
()));
copy
.
setObjectiveValues
(
new
HashMap
<>(
source
.
getObjectiveValues
()));
copy
.
setFitness
(
source
.
getFitness
());
copy
.
setTardiness
(
source
.
getTardiness
());
return
copy
;
}
// ========================== 辅助类:StringUtils ==========================
private
static
class
StringUtils
{
public
static
boolean
isEmpty
(
String
str
)
{
return
str
==
null
||
str
.
trim
().
isEmpty
();
}
}
/**
* 计算绝对开始时间(考虑设备日历+前处理+换型时间,跨时段自动拆分)
*/
/**
* 计算绝对开始时间(完善边界处理:结束点自动顺延)
*/
/**
* 计算绝对开始时间(前处理+换型时间不跨设备非工作时段,仅占用当前可用时段)
* 核心:前处理时间最多用到当前可用时段结束点,不顺延到下一个时段
*/
private
int
calculateAbsoluteStartTime
(
Gene
gene
,
MachineOption
machineOption
)
{
int
startTime
=
gene
.
getStartTime
();
// 基因开始时间(相对baseTime分钟数)
int
setupTime
=
gene
.
getSetupTime
();
// 换型时间(分钟)
int
preTime
=
gene
.
getPreTime
();
// 前处理时间(分钟)
int
totalPrepTime
=
setupTime
+
preTime
;
if
(
totalPrepTime
<=
0
)
{
// 无准备时间,直接返回基因开始时间(按日历调整边界)
return
adjustTimeByMachineCalendar
(
gene
.
getMachineId
(),
startTime
,
true
);
}
// 获取目标设备(深拷贝,避免污染原状态)
Machine
targetMachine
=
deepCopyMachine
(
_machines
.
stream
()
.
filter
(
m
->
m
.
getId
()
==
gene
.
getMachineId
())
.
findFirst
()
.
orElseThrow
(()
->
new
NoSuchElementException
(
"Machine not found: "
+
gene
.
getMachineId
()))
);
// 步骤1:找到基因开始时间所在的“当前可用时段”
LocalDateTime
geneStartDateTime
=
baseTime
.
plusMinutes
(
startTime
);
TimeSegment
currentAvailableSegment
=
findCurrentAvailableSegment
(
targetMachine
,
geneStartDateTime
);
if
(
currentAvailableSegment
==
null
)
{
// 无当前可用时段,兜底返回原计算值
int
fallbackTime
=
adjustTimeByMachineCalendar
(
gene
.
getMachineId
(),
startTime
,
true
);
System
.
out
.
printf
(
"设备%d:无当前可用时段,兜底绝对开始时间=%d(%s)%n"
,
targetMachine
.
getId
(),
fallbackTime
,
ConvertTime
(
fallbackTime
));
return
fallbackTime
;
}
// 步骤2:计算当前可用时段的“剩余时间”(从基因开始时间到时段结束点)
long
segmentRemainingMinutes
=
ChronoUnit
.
MINUTES
.
between
(
geneStartDateTime
,
currentAvailableSegment
.
getEnd
()
);
int
usablePrepTime
=
(
int
)
Math
.
min
(
totalPrepTime
,
segmentRemainingMinutes
);
// 步骤3:绝对开始时间 = 基因开始时间 + 实际可用的准备时间(不跨时段)
int
absoluteStartTime
=
startTime
+
usablePrepTime
;
// 步骤4:边界校验(确保不超过当前时段结束点)
LocalDateTime
absoluteStartDateTime
=
baseTime
.
plusMinutes
(
absoluteStartTime
);
if
(
absoluteStartDateTime
.
isAfter
(
currentAvailableSegment
.
getEnd
()))
{
absoluteStartTime
=
(
int
)
ChronoUnit
.
MINUTES
.
between
(
baseTime
,
currentAvailableSegment
.
getEnd
());
}
// 步骤5:最终按日历调整(避免落在时段结束点)
absoluteStartTime
=
adjustTimeByMachineCalendar
(
gene
.
getMachineId
(),
absoluteStartTime
,
true
);
return
absoluteStartTime
;
}
/**
* 辅助方法:找到指定时间所在的“当前可用时段”(未占用、非维修、包含该时间)
*/
private
TimeSegment
findCurrentAvailableSegment
(
Machine
machine
,
LocalDateTime
targetTime
)
{
return
machine
.
getAvailability
().
stream
()
.
filter
(
segment
->
!
segment
.
isUsed
())
// 未被占用
.
filter
(
segment
->
segment
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
// 非维修时段
.
filter
(
segment
->
{
// 包含目标时间(且目标时间不是时段结束点)
boolean
isAfterStart
=
!
targetTime
.
isBefore
(
segment
.
getStart
());
boolean
isBeforeEnd
=
targetTime
.
isBefore
(
segment
.
getEnd
());
return
isAfterStart
&&
isBeforeEnd
;
})
.
findFirst
()
.
orElse
(
null
);
}
/**
* 计算绝对结束时间(考虑设备日历+后处理时间)
*/
/**
* 计算绝对结束时间(完善边界处理)
*/
/**
* 计算绝对结束时间(正确逻辑:基因结束时间 - 后处理时间 + 设备日历调整)
* 绝对结束时间 = 加工完成时间(后处理开始前的时间)
*/
private
int
calculateAbsoluteEndTime
(
Gene
gene
,
MachineOption
machineOption
)
{
int
geneEndTime
=
gene
.
getEndTime
();
// 基因的结束时间(含后处理,相对baseTime的分钟数)
int
teardownTime
=
gene
.
getTeardownTime
();
// 后处理时间(分钟,加工完成后执行)
// 无后处理时间:绝对结束时间=基因结束时间(直接按日历调整)
if
(
teardownTime
<=
0
)
{
return
adjustTimeByMachineCalendar
(
gene
.
getMachineId
(),
geneEndTime
,
false
);
}
// 核心:加工完成时间 = 基因结束时间 - 后处理时间(后处理在加工完成后执行)
int
processCompletionTime
=
geneEndTime
-
teardownTime
;
// 校验:加工完成时间不能早于基因开始时间(避免逻辑矛盾)
if
(
processCompletionTime
<
gene
.
getStartTime
())
{
System
.
out
.
printf
(
"设备%d:订单%d工序%d - 后处理时间%d分钟过长,基因结束时间%d < 开始时间%d,兜底为基因开始时间%n"
,
gene
.
getMachineId
(),
gene
.
getOrderId
(),
gene
.
getOperationId
(),
teardownTime
,
geneEndTime
,
gene
.
getStartTime
());
processCompletionTime
=
gene
.
getStartTime
();
}
// 按设备日历调整(处理非工作时段、结束点边界)
int
absoluteEndTime
=
adjustTimeByMachineCalendar
(
gene
.
getMachineId
(),
processCompletionTime
,
false
);
// 最终校验:调整后的绝对结束时间不能早于基因开始时间
absoluteEndTime
=
Math
.
max
(
absoluteEndTime
,
gene
.
getStartTime
());
// 日志输出(清晰展示计算链路)
System
.
out
.
printf
(
"设备%d:基因结束时间=%d(%s),后处理%d分钟 → 加工完成时间=%d(%s)→ 调整后绝对结束时间=%d(%s)%n"
,
gene
.
getMachineId
(),
geneEndTime
,
ConvertTime
(
geneEndTime
),
teardownTime
,
processCompletionTime
,
ConvertTime
(
processCompletionTime
),
absoluteEndTime
,
ConvertTime
(
absoluteEndTime
));
return
absoluteEndTime
;
}
/**
* 根据设备日历调整时间(处理边界情况:时间=时段结束点→顺延到下一个可用时段)
*/
private
int
adjustTimeByMachineCalendar
(
Machine
machine
,
int
proposedTime
,
boolean
isStartTime
)
{
LocalDateTime
proposedDateTime
=
baseTime
.
plusMinutes
(
proposedTime
);
// 查找包含提议时间的可用时段(排除维修时段、已占用时段)
TimeSegment
availableSegment
=
machine
.
getAvailability
().
stream
()
.
filter
(
segment
->
!
segment
.
isUsed
())
.
filter
(
segment
->
segment
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
segment
->
{
// 关键修改:包含「时段内」但排除「时段结束点」(结束点视为不可用)
boolean
isAfterStart
=
!
proposedDateTime
.
isBefore
(
segment
.
getStart
());
boolean
isBeforeEnd
=
proposedDateTime
.
isBefore
(
segment
.
getEnd
());
// 用isBefore替代!isAfter
return
isAfterStart
&&
isBeforeEnd
;
})
.
findFirst
()
.
orElse
(
null
);
if
(
availableSegment
!=
null
)
{
return
proposedTime
;
// 时间在可用时段内(非结束点),无需调整
}
// 情况1:提议时间是开始时间 → 查找下一个可用时段的开始时间
if
(
isStartTime
)
{
TimeSegment
nextSegment
=
machine
.
getAvailability
().
stream
()
.
filter
(
segment
->
!
segment
.
isUsed
())
.
filter
(
segment
->
segment
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
segment
->
segment
.
getStart
().
isAfter
(
proposedDateTime
))
.
min
(
Comparator
.
comparing
(
TimeSegment:
:
getStart
))
.
orElse
(
null
);
if
(
nextSegment
!=
null
)
{
int
adjustedTime
=
(
int
)
ChronoUnit
.
MINUTES
.
between
(
baseTime
,
nextSegment
.
getStart
());
System
.
out
.
printf
(
"设备%d:开始时间%d(%s)是时段结束点/非可用时间,顺延到下一个可用时段开始时间%d(%s)%n"
,
machine
.
getId
(),
proposedTime
,
ConvertTime
(
proposedTime
),
adjustedTime
,
ConvertTime
(
adjustedTime
));
return
adjustedTime
;
}
}
// 情况2:提议时间是结束时间 → 查找前一个可用时段的结束时间(避免跨时段)
else
{
TimeSegment
prevSegment
=
machine
.
getAvailability
().
stream
()
.
filter
(
segment
->
!
segment
.
isUsed
())
.
filter
(
segment
->
segment
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
segment
->
segment
.
getEnd
().
isBefore
(
proposedDateTime
))
.
max
(
Comparator
.
comparing
(
TimeSegment:
:
getEnd
))
.
orElse
(
null
);
if
(
prevSegment
!=
null
)
{
return
(
int
)
ChronoUnit
.
MINUTES
.
between
(
baseTime
,
prevSegment
.
getEnd
());
}
}
// 兜底:无可用时段时返回原时间(理论上不会发生)
return
proposedTime
;
}
/**
* 按设备ID查询可用时间(复用adjustTimeByMachineCalendar逻辑)
*/
private
int
adjustTimeByMachineCalendar
(
int
machineId
,
int
proposedTime
,
boolean
isStartTime
)
{
Machine
machine
=
_machines
.
stream
()
.
filter
(
m
->
m
.
getId
()
==
machineId
)
.
findFirst
()
.
orElse
(
null
);
if
(
machine
==
null
)
{
return
proposedTime
;
}
return
adjustTimeByMachineCalendar
(
machine
,
proposedTime
,
isStartTime
);
}
/**
* 计算绝对准备时间(考虑设备日历,跳过非工作时间)
*/
private
int
calculateAbsolutePreparationTime
(
Gene
gene
,
MachineOption
machineOption
)
{
int
totalPreparationTime
=
gene
.
getPreTime
();
// 总准备时间
if
(
totalPreparationTime
<=
0
)
{
return
0
;
// 没有准备时间
}
// 获取设备
Machine
machine
=
_machines
.
stream
()
.
filter
(
m
->
m
.
getId
()
==
gene
.
getMachineId
())
.
findFirst
()
.
orElse
(
null
);
if
(
machine
==
null
)
{
return
totalPreparationTime
;
// 找不到设备,返回原始时间
}
// 开始时间(基因开始时间)
int
startTime
=
gene
.
getStartTime
();
LocalDateTime
currentTime
=
baseTime
.
plusMinutes
(
startTime
);
// 计算考虑日历的实际准备时间
return
calculateCalendarAdjustedTime
(
machine
,
currentTime
,
totalPreparationTime
);
}
/**
* 计算绝对换型时间(考虑设备日历,跳过非工作时间)
*/
private
int
calculateAbsoluteSetupTime
(
Gene
gene
,
MachineOption
machineOption
)
{
int
totalPreparationTime
=
gene
.
getSetupTime
();
// 总准备时间
if
(
totalPreparationTime
<=
0
)
{
return
0
;
// 没有准备时间
}
// 获取设备
Machine
machine
=
_machines
.
stream
()
.
filter
(
m
->
m
.
getId
()
==
gene
.
getMachineId
())
.
findFirst
()
.
orElse
(
null
);
if
(
machine
==
null
)
{
return
totalPreparationTime
;
// 找不到设备,返回原始时间
}
// 开始时间(基因开始时间)
int
startTime
=
gene
.
getStartTime
();
LocalDateTime
currentTime
=
baseTime
.
plusMinutes
(
startTime
);
// 计算考虑日历的实际准备时间
return
calculateCalendarAdjustedTime
(
machine
,
currentTime
,
totalPreparationTime
);
}
/**
* 计算考虑设备日历的时间(跳过非工作时间)
*/
private
int
calculateCalendarAdjustedTime
(
Machine
machine
,
LocalDateTime
startTime
,
int
requiredMinutes
)
{
LocalDateTime
currentTime
=
startTime
;
int
remainingMinutes
=
requiredMinutes
;
int
totalElapsedMinutes
=
0
;
// 实际经过的分钟数(包含非工作时间)
System
.
out
.
printf
(
" 开始计算: %s, 需要%d分钟%n"
,
startTime
.
format
(
java
.
time
.
format
.
DateTimeFormatter
.
ofPattern
(
"MM-dd HH:mm"
)),
requiredMinutes
);
while
(
remainingMinutes
>
0
)
{
// 查找包含当前时间的可用时间段
TimeSegment
currentSegment
=
findAvailableSegmentAtTime
(
machine
,
currentTime
);
if
(
currentSegment
==
null
)
{
// 没有找到可用时间段,查找下一个可用时间段
TimeSegment
nextSegment
=
findNextAvailableSegment
(
machine
,
currentTime
);
if
(
nextSegment
==
null
)
{
System
.
out
.
printf
(
" 警告: 找不到后续可用时间段,剩余%d分钟无法安排%n"
,
remainingMinutes
);
break
;
// 无法安排剩余时间
}
// 跳到下一个可用时间段的开始
long
jumpMinutes
=
ChronoUnit
.
MINUTES
.
between
(
currentTime
,
nextSegment
.
getStart
());
totalElapsedMinutes
+=
(
int
)
jumpMinutes
;
currentTime
=
nextSegment
.
getStart
();
System
.
out
.
printf
(
" 跳过非工作时间: 跳转%d分钟到 %s%n"
,
jumpMinutes
,
currentTime
.
format
(
java
.
time
.
format
.
DateTimeFormatter
.
ofPattern
(
"MM-dd HH:mm"
)));
continue
;
}
// 计算当前时间段可用的分钟数
long
availableMinutesInSegment
=
ChronoUnit
.
MINUTES
.
between
(
currentTime
,
currentSegment
.
getEnd
());
int
processableMinutes
=
(
int
)
Math
.
min
(
remainingMinutes
,
availableMinutesInSegment
);
// 处理当前时间段
remainingMinutes
-=
processableMinutes
;
totalElapsedMinutes
+=
processableMinutes
;
currentTime
=
currentTime
.
plusMinutes
(
processableMinutes
);
System
.
out
.
printf
(
" 在当前时间段处理%d分钟: %s → %s, 剩余%d分钟%n"
,
processableMinutes
,
currentTime
.
minusMinutes
(
processableMinutes
).
format
(
java
.
time
.
format
.
DateTimeFormatter
.
ofPattern
(
"MM-dd HH:mm"
)),
currentTime
.
format
(
java
.
time
.
format
.
DateTimeFormatter
.
ofPattern
(
"MM-dd HH:mm"
)),
remainingMinutes
);
// 如果当前时间段用完但还有剩余时间,移动到时间段结束点
if
(
remainingMinutes
>
0
&&
currentTime
.
equals
(
currentSegment
.
getEnd
()))
{
// 查找下一个时间段
TimeSegment
nextSegment
=
findNextAvailableSegment
(
machine
,
currentTime
);
if
(
nextSegment
==
null
)
{
System
.
out
.
printf
(
" 警告: 当前时间段用完,但找不到后续时间段,剩余%d分钟无法安排%n"
,
remainingMinutes
);
break
;
}
// 跳到下一个时间段的开始
long
jumpMinutes
=
ChronoUnit
.
MINUTES
.
between
(
currentTime
,
nextSegment
.
getStart
());
totalElapsedMinutes
+=
(
int
)
jumpMinutes
;
currentTime
=
nextSegment
.
getStart
();
System
.
out
.
printf
(
" 时间段结束,跳转到下一个: 跳转%d分钟到 %s%n"
,
jumpMinutes
,
currentTime
.
format
(
java
.
time
.
format
.
DateTimeFormatter
.
ofPattern
(
"MM-dd HH:mm"
)));
}
}
System
.
out
.
printf
(
" 计算完成: 原始需要%d分钟,实际经过%d分钟%n"
,
requiredMinutes
,
totalElapsedMinutes
);
return
totalElapsedMinutes
;
}
/**
* 查找包含指定时间的可用时间段
*/
private
TimeSegment
findAvailableSegmentAtTime
(
Machine
machine
,
LocalDateTime
time
)
{
return
machine
.
getAvailability
().
stream
()
.
filter
(
segment
->
!
segment
.
isUsed
())
.
filter
(
segment
->
segment
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
segment
->
!
segment
.
getStart
().
isAfter
(
time
)
&&
segment
.
getEnd
().
isAfter
(
time
))
.
findFirst
()
.
orElse
(
null
);
}
/**
* 查找下一个可用时间段
*/
private
TimeSegment
findNextAvailableSegment
(
Machine
machine
,
LocalDateTime
afterTime
)
{
return
machine
.
getAvailability
().
stream
()
.
filter
(
segment
->
!
segment
.
isUsed
())
.
filter
(
segment
->
segment
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
segment
->
segment
.
getStart
().
isAfter
(
afterTime
))
.
min
(
Comparator
.
comparing
(
TimeSegment:
:
getStart
))
.
orElse
(
null
);
}
/**
* 反向查找:找到包含指定时间的可用工作时段(用于向前追溯)
*/
private
TimeSegment
findAvailableSegmentAtTimeReverse
(
Machine
machine
,
LocalDateTime
time
)
{
return
machine
.
getAvailability
().
stream
()
.
filter
(
segment
->
!
segment
.
isUsed
())
.
filter
(
segment
->
segment
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
segment
->
!
segment
.
isHoliday
())
.
filter
(
segment
->
{
boolean
isAfterStart
=
!
time
.
isBefore
(
segment
.
getStart
());
boolean
isBeforeEnd
=
time
.
isBefore
(
segment
.
getEnd
());
return
isAfterStart
&&
isBeforeEnd
;
})
.
findFirst
()
.
orElse
(
null
);
}
/**
* 查找前一个可用时间段
*/
private
TimeSegment
findPreviousAvailableSegment
(
Machine
machine
,
LocalDateTime
beforeTime
)
{
return
machine
.
getAvailability
().
stream
()
.
filter
(
segment
->
!
segment
.
isUsed
())
.
filter
(
segment
->
segment
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
segment
->
segment
.
getEnd
().
isBefore
(
beforeTime
))
.
max
(
Comparator
.
comparing
(
TimeSegment:
:
getEnd
))
.
orElse
(
null
);
}
/**
* 计算绝对后处理时间(从结束时间向前计算,考虑设备日历,跳过非工作时间)
* 后处理时间是从加工完成时间向前安排的
*/
private
int
calculateAbsoluteTeardownTime
(
Gene
gene
,
MachineOption
machineOption
)
{
int
teardownTime
=
gene
.
getTeardownTime
();
// 后处理时间
if
(
teardownTime
<=
0
)
{
return
0
;
// 没有后处理时间
}
// 获取设备
Machine
machine
=
_machines
.
stream
()
.
filter
(
m
->
m
.
getId
()
==
gene
.
getMachineId
())
.
findFirst
()
.
orElse
(
null
);
if
(
machine
==
null
)
{
return
teardownTime
;
// 找不到设备,返回原始时间
}
// 后处理结束时间 = 基因结束时间(包含后处理的时间)
int
endTime
=
gene
.
getEndTime
();
LocalDateTime
endDateTime
=
baseTime
.
plusMinutes
(
endTime
);
System
.
out
.
printf
(
"计算绝对后处理时间 - 设备%d: 结束时间=%d (%s), 后处理时间=%d分钟%n"
,
machine
.
getId
(),
endTime
,
ConvertTime
(
endTime
),
teardownTime
);
// 计算考虑日历的实际后处理时间(向前计算)
return
calculateCalendarAdjustedTimeBackward
(
machine
,
endDateTime
,
teardownTime
);
}
/**
* 向后计算考虑设备日历的时间(从结束时间向前回溯,跳过非工作时间)
*/
private
int
calculateCalendarAdjustedTimeBackward
(
Machine
machine
,
LocalDateTime
endTime
,
int
requiredMinutes
)
{
LocalDateTime
currentTime
=
endTime
;
int
remainingMinutes
=
requiredMinutes
;
int
totalElapsedMinutes
=
0
;
// 实际经过的分钟数(包含非工作时间)
System
.
out
.
printf
(
" 开始向后计算后处理时间: 结束时间=%s, 需要%d分钟%n"
,
endTime
.
format
(
java
.
time
.
format
.
DateTimeFormatter
.
ofPattern
(
"MM-dd HH:mm"
)),
requiredMinutes
);
while
(
remainingMinutes
>
0
)
{
// 查找包含当前时间的可用时间段(向前查找)
TimeSegment
currentSegment
=
findAvailableSegmentAtTimeBackward
(
machine
,
currentTime
);
if
(
currentSegment
==
null
)
{
// 没有找到可用时间段,查找前一个可用时间段
TimeSegment
prevSegment
=
findPreviousAvailableSegment
(
machine
,
currentTime
);
if
(
prevSegment
==
null
)
{
System
.
out
.
printf
(
" 警告: 找不到前续可用时间段,剩余%d分钟无法安排%n"
,
remainingMinutes
);
break
;
// 无法安排剩余时间
}
// 跳到前一个可用时间段的结束时间
long
jumpMinutes
=
ChronoUnit
.
MINUTES
.
between
(
prevSegment
.
getEnd
(),
currentTime
);
totalElapsedMinutes
+=
(
int
)
jumpMinutes
;
currentTime
=
prevSegment
.
getEnd
();
System
.
out
.
printf
(
" 跳过非工作时间: 向后跳转%d分钟到 %s%n"
,
jumpMinutes
,
currentTime
.
format
(
java
.
time
.
format
.
DateTimeFormatter
.
ofPattern
(
"MM-dd HH:mm"
)));
continue
;
}
// 计算当前时间段可用的分钟数(向前计算)
long
availableMinutesInSegment
=
ChronoUnit
.
MINUTES
.
between
(
currentSegment
.
getStart
(),
currentTime
);
int
processableMinutes
=
(
int
)
Math
.
min
(
remainingMinutes
,
availableMinutesInSegment
);
// 处理当前时间段(向前移动)
remainingMinutes
-=
processableMinutes
;
totalElapsedMinutes
+=
processableMinutes
;
currentTime
=
currentTime
.
minusMinutes
(
processableMinutes
);
System
.
out
.
printf
(
" 在当前时间段处理%d分钟: %s ← %s, 剩余%d分钟%n"
,
processableMinutes
,
currentTime
.
format
(
java
.
time
.
format
.
DateTimeFormatter
.
ofPattern
(
"MM-dd HH:mm"
)),
currentTime
.
plusMinutes
(
processableMinutes
).
format
(
java
.
time
.
format
.
DateTimeFormatter
.
ofPattern
(
"MM-dd HH:mm"
)),
remainingMinutes
);
// 如果当前时间段用完但还有剩余时间,移动到时间段开始点
if
(
remainingMinutes
>
0
&&
currentTime
.
equals
(
currentSegment
.
getStart
()))
{
// 查找前一个时间段
TimeSegment
prevSegment
=
findPreviousAvailableSegment
(
machine
,
currentTime
);
if
(
prevSegment
==
null
)
{
System
.
out
.
printf
(
" 警告: 当前时间段用完,但找不到前续时间段,剩余%d分钟无法安排%n"
,
remainingMinutes
);
break
;
}
// 跳到前一个时间段的结束时间
long
jumpMinutes
=
ChronoUnit
.
MINUTES
.
between
(
prevSegment
.
getEnd
(),
currentTime
);
totalElapsedMinutes
+=
(
int
)
jumpMinutes
;
currentTime
=
prevSegment
.
getEnd
();
System
.
out
.
printf
(
" 时间段开始,跳转到前一个: 向后跳转%d分钟到 %s%n"
,
jumpMinutes
,
currentTime
.
format
(
java
.
time
.
format
.
DateTimeFormatter
.
ofPattern
(
"MM-dd HH:mm"
)));
}
}
System
.
out
.
printf
(
" 后处理时间计算完成: 原始需要%d分钟,实际经过%d分钟%n"
,
requiredMinutes
,
totalElapsedMinutes
);
return
totalElapsedMinutes
;
}
/**
* 向后查找包含指定时间的可用时间段
*/
private
TimeSegment
findAvailableSegmentAtTimeBackward
(
Machine
machine
,
LocalDateTime
time
)
{
return
machine
.
getAvailability
().
stream
()
.
filter
(
segment
->
!
segment
.
isUsed
())
.
filter
(
segment
->
segment
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
segment
->
{
// 包含目标时间(且目标时间不是时段开始点)
boolean
isAfterStart
=
time
.
isAfter
(
segment
.
getStart
());
boolean
isBeforeEnd
=
!
time
.
isAfter
(
segment
.
getEnd
());
// 用!isAfter替代isBefore
return
isAfterStart
&&
isBeforeEnd
;
})
.
findFirst
()
.
orElse
(
null
);
}
}
\ No newline at end of file
src/main/java/com/aps/service/plan/PlanResultService.java
0 → 100644
View file @
2d7e8ff6
package
com
.
aps
.
service
.
plan
;
import
com.aps.common.util.JsonFileReader
;
import
com.aps.controller.gantt.FileUploadController
;
import
com.aps.entity.Schedule.GenVO
;
import
com.aps.entity.Schedule.MachineVO
;
import
com.aps.entity.basic.*
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
java.io.IOException
;
import
java.math.BigDecimal
;
import
java.time.LocalDateTime
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.stream.Collectors
;
@Service
public
class
PlanResultService
{
// 注入FileUploadController
@Autowired
private
FileUploadController
fileUploadController
;
private
final
LocalDateTime
baseTime
=
LocalDateTime
.
of
(
2025
,
10
,
1
,
0
,
0
,
0
);
public
List
<
ScheduleChromosome
>
execute
()
{
try
{
// 1. 读取数据
List
<
Machine
>
machines
=
loadData
(
"machines.json"
,
Machine
.
class
);
List
<
Product
>
products
=
loadData
(
"products.json"
,
Product
.
class
);
List
<
Order
>
orders
=
loadData
(
"orders.json"
,
Order
.
class
);
// 设置机器信息到班次中
for
(
Machine
machine
:
machines
)
{
if
(
machine
.
getShifts
()
!=
null
)
{
for
(
Shift
shift
:
machine
.
getShifts
())
{
shift
.
setMachineId
(
machine
.
getId
());
shift
.
setMachineName
(
machine
.
getName
());
}
}
// 调试:打印机器和班次信息
System
.
out
.
println
(
"Machine: "
+
machine
.
getId
()
+
", Name: "
+
machine
.
getName
());
if
(
machine
.
getShifts
()
!=
null
)
{
for
(
Shift
shift
:
machine
.
getShifts
())
{
System
.
out
.
println
(
" Shift: "
+
shift
.
getStartTime
()
+
" - "
+
shift
.
getEndTime
()
+
", Status: "
+
shift
.
getStatus
()
+
", MachineId: "
+
shift
.
getMachineId
()
+
", MachineName: "
+
shift
.
getMachineName
());
}
}
}
// 创建节假日
List
<
Holiday
>
holidays
=
Arrays
.
asList
(
new
Holiday
(
LocalDateTime
.
of
(
2025
,
10
,
1
,
0
,
0
),
LocalDateTime
.
of
(
2025
,
10
,
7
,
23
,
59
))
);
// 将节假日添加到所有设备中
addHolidaysToAllMachines
(
machines
,
holidays
);
// 3. 创建调度服务
MachineSchedulerService
machineScheduler
=
new
MachineSchedulerService
(
holidays
,
LocalDateTime
.
of
(
2025
,
10
,
1
,
0
,
0
,
0
));
// 4. 初始化机器时间线
for
(
Machine
machine
:
machines
)
{
MachineTimeline
timeline
=
machineScheduler
.
getOrCreateTimeline
(
machine
);
machine
.
setAvailability
(
timeline
.
getSegments
());
}
// 5. 执行调度算法
AlgorithmScheduler7
scheduler
=
new
AlgorithmScheduler7
(
products
,
machines
,
orders
,
machineScheduler
);
List
<
ScheduleChromosome
>
scheduleChromosomes
=
scheduler
.
RunAll
();
// 对调度结果按照 fitness 由高到低排序
scheduleChromosomes
.
sort
((
c1
,
c2
)
->
Double
.
compare
(
c2
.
getFitness
(),
c1
.
getFitness
()));
// 为每个 ScheduleChromosome 分配场景ID(基于排序后的位置)
for
(
int
i
=
0
;
i
<
scheduleChromosomes
.
size
();
i
++)
{
scheduleChromosomes
.
get
(
i
).
setSceneId
(
i
+
1
);
// 场景ID从1开始
}
return
scheduleChromosomes
;
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"调度执行失败"
,
e
);
}
}
/**
* 加载数据,优先从上传文件夹加载,如果不存在则从resources加载
*
* @param fileName 文件名
* @param clazz 类型
* @param <T> 泛型
* @return 数据列表
* @throws IOException IO异常
*/
private
<
T
>
List
<
T
>
loadData
(
String
fileName
,
Class
<
T
>
clazz
)
throws
IOException
{
// 检查是否有上传的文件
if
(
fileUploadController
.
isFileUploaded
(
fileName
))
{
String
filePath
=
fileUploadController
.
getUploadedFilePath
(
fileName
);
return
JsonFileReader
.
readListFromFile
(
filePath
,
clazz
);
}
else
{
// 使用默认的resources文件
return
JsonFileReader
.
readListFromResources
(
fileName
,
clazz
);
}
}
/**
* 将假期添加到所有设备的维护窗口中
* @param machines 设备列表
* @param holidays 假期列表
*/
private
void
addHolidaysToAllMachines
(
List
<
Machine
>
machines
,
List
<
Holiday
>
holidays
)
{
if
(
machines
==
null
||
holidays
==
null
)
{
return
;
}
for
(
Machine
machine
:
machines
)
{
// 确保维护窗口列表不为null
if
(
machine
.
getMaintenanceWindows
()
==
null
)
{
machine
.
setMaintenanceWindows
(
new
ArrayList
<>());
}
for
(
Holiday
holiday
:
holidays
)
{
// 将假期转换为维护窗口并添加到设备中
MaintenanceWindow
maintenanceWindow
=
new
MaintenanceWindow
(
holiday
,
"Holiday Period"
);
machine
.
addMaintenanceWindow
(
maintenanceWindow
);
}
}
}
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
());
}
public
List
<
MachineVO
>
convertMachineListToVO
(
List
<
Machine
>
machines
)
{
return
machines
.
stream
()
.
map
(
this
::
convertToVO
)
.
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
());
// 注意:tasks 字段需要在其他地方设置,因为 Machine 类中没有任务信息
return
machineVO
;
}
}
\ No newline at end of file
src/main/resources/application.yml
View file @
2d7e8ff6
...
@@ -2,6 +2,10 @@ server:
...
@@ -2,6 +2,10 @@ server:
port
:
8181
# 修改为你想要的端口号
port
:
8181
# 修改为你想要的端口号
spring
:
spring
:
servlet
:
multipart
:
max-file-size
:
10MB
max-request-size
:
10MB
mvc
:
mvc
:
pathmatch
:
pathmatch
:
matching-strategy
:
ant_path_matcher
# Spring Boot 2.6+ 需要这个配置
matching-strategy
:
ant_path_matcher
# Spring Boot 2.6+ 需要这个配置
...
@@ -37,7 +41,11 @@ spring:
...
@@ -37,7 +41,11 @@ spring:
username
:
sa
username
:
sa
password
:
root_mes123456
password
:
root_mes123456
# MyBatis-Plus配置
# 文件上传配置
file
:
upload
:
dir
:
uploads
mybatis-plus
:
mybatis-plus
:
mapper-locations
:
classpath:mapper/**/*.xml
# Mapper XML路径
mapper-locations
:
classpath:mapper/**/*.xml
# Mapper XML路径
type-aliases-package
:
com.aps.entity
# 实体类包路径
type-aliases-package
:
com.aps.entity
# 实体类包路径
...
...
src/main/resources/machines.json
View file @
2d7e8ff6
...
@@ -7,32 +7,37 @@
...
@@ -7,32 +7,37 @@
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"shiftDate"
:
"0001-01-01T00:00:00"
,
"shiftDate"
:
"0001-01-01T00:00:00"
,
"temporaryShift"
:
false
,
"temporaryShift"
:
false
,
"priority"
:
0
"priority"
:
0
,
"status"
:
0
},{
},{
"startTime"
:
"18:00:00"
,
"startTime"
:
"18:00:00"
,
"endTime"
:
"20:00:00"
,
"endTime"
:
"20:00:00"
,
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"shiftDate"
:
"0001-01-01T00:00:00"
,
"shiftDate"
:
"0001-01-01T00:00:00"
,
"temporaryShift"
:
true
,
"temporaryShift"
:
true
,
"priority"
:
0
"priority"
:
0
,
"status"
:
1
},{
},{
"startTime"
:
"08:00:00"
,
"startTime"
:
"08:00:00"
,
"endTime"
:
"18:00:00"
,
"endTime"
:
"18:00:00"
,
"days"
:
[
0
],
"days"
:
[
0
],
"shiftDate"
:
"0001-01-01T00:00:00"
,
"shiftDate"
:
"0001-01-01T00:00:00"
,
"temporaryShift"
:
true
,
"temporaryShift"
:
true
,
"priority"
:
0
"priority"
:
0
,
"status"
:
1
},{
},{
"startTime"
:
"08:00:00"
,
"startTime"
:
"08:00:00"
,
"endTime"
:
"18:00:00"
,
"endTime"
:
"18:00:00"
,
"days"
:
null
,
"days"
:
null
,
"shiftDate"
:
"2025-10-01T00:00:00"
,
"shiftDate"
:
"2025-10-01T00:00:00"
,
"temporaryShift"
:
true
,
"temporaryShift"
:
true
,
"priority"
:
0
"priority"
:
0
,
"status"
:
1
}],
}],
"maintenanceWindows"
:
[{
"maintenanceWindows"
:
[{
"startTime"
:
"2025-10-08T10:00:00"
,
"startTime"
:
"2025-10-08T10:00:00"
,
"endTime"
:
"2025-10-10T12:00:00"
"endTime"
:
"2025-10-10T12:00:00"
,
"status"
:
2
}]
}]
},
{
},
{
"id"
:
2
,
"id"
:
2
,
...
@@ -43,7 +48,8 @@
...
@@ -43,7 +48,8 @@
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"shiftDate"
:
"0001-01-01T00:00:00"
,
"shiftDate"
:
"0001-01-01T00:00:00"
,
"temporaryShift"
:
false
,
"temporaryShift"
:
false
,
"priority"
:
0
"priority"
:
0
,
"status"
:
0
}],
}],
"maintenanceWindows"
:
null
"maintenanceWindows"
:
null
},
{
},
{
...
@@ -55,7 +61,8 @@
...
@@ -55,7 +61,8 @@
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"shiftDate"
:
"0001-01-01T00:00:00"
,
"shiftDate"
:
"0001-01-01T00:00:00"
,
"temporaryShift"
:
false
,
"temporaryShift"
:
false
,
"priority"
:
0
"priority"
:
0
,
"status"
:
0
}],
}],
"maintenanceWindows"
:
null
"maintenanceWindows"
:
null
},
{
},
{
...
@@ -67,7 +74,8 @@
...
@@ -67,7 +74,8 @@
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"shiftDate"
:
"0001-01-01T00:00:00"
,
"shiftDate"
:
"0001-01-01T00:00:00"
,
"temporaryShift"
:
false
,
"temporaryShift"
:
false
,
"priority"
:
0
"priority"
:
0
,
"status"
:
0
}],
}],
"maintenanceWindows"
:
null
"maintenanceWindows"
:
null
},
{
},
{
...
@@ -79,7 +87,8 @@
...
@@ -79,7 +87,8 @@
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"shiftDate"
:
"0001-01-01T00:00:00"
,
"shiftDate"
:
"0001-01-01T00:00:00"
,
"temporaryShift"
:
false
,
"temporaryShift"
:
false
,
"priority"
:
0
"priority"
:
0
,
"status"
:
0
}],
}],
"maintenanceWindows"
:
null
"maintenanceWindows"
:
null
}]
}]
\ No newline at end of file
src/main/resources/products.json
View file @
2d7e8ff6
...
@@ -11,7 +11,9 @@
...
@@ -11,7 +11,9 @@
{
{
"machineId"
:
2
,
"machineId"
:
2
,
"processingTime"
:
11
,
"processingTime"
:
11
,
"setupTime"
:
59
"setupTime"
:
0
,
"teardownTime"
:
0
,
"preTime"
:
0
}
}
],
],
"isInterrupt"
:
false
"isInterrupt"
:
false
...
@@ -24,12 +26,16 @@
...
@@ -24,12 +26,16 @@
{
{
"machineId"
:
3
,
"machineId"
:
3
,
"processingTime"
:
5
,
"processingTime"
:
5
,
"setupTime"
:
45
"setupTime"
:
0
,
"teardownTime"
:
0
,
"preTime"
:
0
},
},
{
{
"machineId"
:
5
,
"machineId"
:
5
,
"processingTime"
:
21
,
"processingTime"
:
21
,
"setupTime"
:
11
"setupTime"
:
0
,
"teardownTime"
:
0
,
"preTime"
:
0
}
}
],
],
"isInterrupt"
:
false
"isInterrupt"
:
false
...
@@ -42,7 +48,9 @@
...
@@ -42,7 +48,9 @@
{
{
"machineId"
:
5
,
"machineId"
:
5
,
"processingTime"
:
6
,
"processingTime"
:
6
,
"setupTime"
:
40
"setupTime"
:
0
,
"teardownTime"
:
0
,
"preTime"
:
0
}
}
],
],
"isInterrupt"
:
false
"isInterrupt"
:
false
...
@@ -55,12 +63,16 @@
...
@@ -55,12 +63,16 @@
{
{
"machineId"
:
1
,
"machineId"
:
1
,
"processingTime"
:
19
,
"processingTime"
:
19
,
"setupTime"
:
48
"setupTime"
:
0
,
"teardownTime"
:
0
,
"preTime"
:
0
},
},
{
{
"machineId"
:
4
,
"machineId"
:
4
,
"processingTime"
:
20
,
"processingTime"
:
20
,
"setupTime"
:
51
"setupTime"
:
0
,
"teardownTime"
:
0
,
"preTime"
:
0
}
}
],
],
"isInterrupt"
:
false
"isInterrupt"
:
false
...
@@ -79,7 +91,9 @@
...
@@ -79,7 +91,9 @@
{
{
"machineId"
:
1
,
"machineId"
:
1
,
"processingTime"
:
5
,
"processingTime"
:
5
,
"setupTime"
:
30
"setupTime"
:
0
,
"teardownTime"
:
0
,
"preTime"
:
0
}
}
],
],
"isInterrupt"
:
false
"isInterrupt"
:
false
...
@@ -92,7 +106,9 @@
...
@@ -92,7 +106,9 @@
{
{
"machineId"
:
2
,
"machineId"
:
2
,
"processingTime"
:
22
,
"processingTime"
:
22
,
"setupTime"
:
47
"setupTime"
:
0
,
"teardownTime"
:
0
,
"preTime"
:
0
}
}
],
],
"isInterrupt"
:
false
"isInterrupt"
:
false
...
@@ -105,12 +121,16 @@
...
@@ -105,12 +121,16 @@
{
{
"machineId"
:
1
,
"machineId"
:
1
,
"processingTime"
:
8
,
"processingTime"
:
8
,
"setupTime"
:
35
"setupTime"
:
0
,
"teardownTime"
:
0
,
"preTime"
:
0
},
},
{
{
"machineId"
:
5
,
"machineId"
:
5
,
"processingTime"
:
28
,
"processingTime"
:
28
,
"setupTime"
:
31
"setupTime"
:
0
,
"teardownTime"
:
0
,
"preTime"
:
0
}
}
],
],
"isInterrupt"
:
false
"isInterrupt"
:
false
...
...
src/test/java/com/aps/demo/MachineSchedulerTest.java
View file @
2d7e8ff6
...
@@ -4,6 +4,7 @@ package com.aps.demo;
...
@@ -4,6 +4,7 @@ package com.aps.demo;
import
com.aps.common.util.JsonFileReader
;
import
com.aps.common.util.JsonFileReader
;
import
com.aps.entity.basic.*
;
import
com.aps.entity.basic.*
;
import
com.aps.service.plan.AlgorithmScheduler6
;
import
com.aps.service.plan.AlgorithmScheduler6
;
import
com.aps.service.plan.AlgorithmScheduler7
;
import
com.aps.service.plan.MachineSchedulerService
;
import
com.aps.service.plan.MachineSchedulerService
;
import
java.time.LocalDateTime
;
import
java.time.LocalDateTime
;
...
@@ -82,7 +83,7 @@ public class MachineSchedulerTest {
...
@@ -82,7 +83,7 @@ public class MachineSchedulerTest {
}
}
AlgorithmScheduler
6
scheduler
=
new
AlgorithmScheduler6
(
products
,
machines
,
orders
,
machineScheduler
);
AlgorithmScheduler
7
scheduler
=
new
AlgorithmScheduler7
(
products
,
machines
,
orders
,
machineScheduler
);
List
<
ScheduleChromosome
>
scheduleChromosomes
=
scheduler
.
RunAll
();
List
<
ScheduleChromosome
>
scheduleChromosomes
=
scheduler
.
RunAll
();
for
(
ScheduleChromosome
run:
scheduleChromosomes
for
(
ScheduleChromosome
run:
scheduleChromosomes
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment