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