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
35f0e8bb
Commit
35f0e8bb
authored
Jan 14, 2026
by
DESKTOP-VKRD9QF\Administration
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
行程并发修改
parent
a3e69687
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
150 additions
and
4313 deletions
+150
-4313
ProductionDeepCopyUtil.java
...main/java/com/aps/common/util/ProductionDeepCopyUtil.java
+18
-0
Machine.java
src/main/java/com/aps/entity/basic/Machine.java
+1
-1
MachineTimeline.java
src/main/java/com/aps/entity/basic/MachineTimeline.java
+3
-3
GeneticAlgorithm.java
...main/java/com/aps/service/Algorithm/GeneticAlgorithm.java
+6
-0
MachineCalculator.java
...ain/java/com/aps/service/Algorithm/MachineCalculator.java
+24
-21
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
+18
-18
PlanResultService.java
src/main/java/com/aps/service/plan/PlanResultService.java
+1
-63
PlanSchedulerService.java
src/main/java/com/aps/service/plan/PlanSchedulerService.java
+76
-74
application.yml
src/main/resources/application.yml
+3
-3
MachineSchedulerTest.java
src/test/java/com/aps/demo/MachineSchedulerTest.java
+0
-104
No files found.
src/main/java/com/aps/common/util/ProductionDeepCopyUtil.java
View file @
35f0e8bb
...
@@ -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/entity/basic/Machine.java
View file @
35f0e8bb
...
@@ -16,7 +16,7 @@ public class Machine {
...
@@ -16,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
=
new
CopyOnWriteArrayList
()
;
private
CopyOnWriteArrayList
<
TimeSegment
>
availability
;
private
String
code
;
private
String
code
;
private
List
<
Holiday
>
holidays
;
private
List
<
Holiday
>
holidays
;
...
...
src/main/java/com/aps/entity/basic/MachineTimeline.java
View file @
35f0e8bb
...
@@ -8,7 +8,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
...
@@ -8,7 +8,7 @@ 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
;
...
@@ -27,11 +27,11 @@ public class MachineTimeline {
...
@@ -27,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 @
35f0e8bb
...
@@ -352,9 +352,15 @@ public class GeneticAlgorithm {
...
@@ -352,9 +352,15 @@ 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
)
{
if
(
chromosome
==
null
){
System
.
out
.
println
(
"chromosome==null"
);
}
chromosome
.
setResult
(
new
CopyOnWriteArrayList
<>());
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
);
// 简单拷贝,实际可能需要深拷贝
...
...
src/main/java/com/aps/service/Algorithm/MachineCalculator.java
View file @
35f0e8bb
...
@@ -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
;
...
@@ -308,10 +308,10 @@ public class MachineCalculator {
...
@@ -308,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
)
{
...
@@ -548,8 +548,8 @@ public class MachineCalculator {
...
@@ -548,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
;
...
@@ -700,7 +700,7 @@ public class MachineCalculator {
...
@@ -700,7 +700,7 @@ public class MachineCalculator {
}
}
machine
.
getAvailability
().
addAll
(
newSegments
);
machine
.
getAvailability
().
addAll
(
newSegments
);
List
<
TimeSegment
>
mergedSegments
=
MergeSegments
(
machine
.
getAvailability
());
CopyOnWriteArray
List
<
TimeSegment
>
mergedSegments
=
MergeSegments
(
machine
.
getAvailability
());
// synchronized (machine.getAvailability()) {
// synchronized (machine.getAvailability()) {
// 合并片段(去重+排序),
// 合并片段(去重+排序),
...
@@ -1018,7 +1018,7 @@ public class MachineCalculator {
...
@@ -1018,7 +1018,7 @@ public class MachineCalculator {
private
void
RemoveMachineAvailable1
(
Machine
machine
,
ScheduleResultDetail
geneDetails
,
TimeSegment
targetSegment1
)
{
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
()
...
@@ -1072,9 +1072,12 @@ if(keys1!=null&&keys1.size()>0) {
...
@@ -1072,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
));
...
@@ -1082,7 +1085,7 @@ if(keys1!=null&&keys1.size()>0) {
...
@@ -1082,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
)
...
@@ -1143,18 +1146,18 @@ if(keys1!=null&&keys1.size()>0) {
...
@@ -1143,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
));
...
@@ -1169,7 +1172,7 @@ if(keys1!=null&&keys1.size()>0) {
...
@@ -1169,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/plan/AlgorithmScheduler6.java
deleted
100644 → 0
View file @
a3e69687
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 @
a3e69687
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 @
a3e69687
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 @
35f0e8bb
...
@@ -67,14 +67,14 @@ public class MachineSchedulerService {
...
@@ -67,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
CopyOnWriteArrayList
<>();
CopyOnWriteArray
List
<
TimeSegment
>
allSegments
=
new
CopyOnWriteArrayList
<>();
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
);
// 跳到下一天
// 跳到下一天
...
@@ -116,7 +116,7 @@ public class MachineSchedulerService {
...
@@ -116,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
;
...
@@ -147,9 +147,9 @@ try {
...
@@ -147,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
{
...
@@ -164,7 +164,7 @@ try {
...
@@ -164,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
CopyOnWriteArrayList
<>();
CopyOnWriteArray
List
<
TimeSegment
>
segments
=
new
CopyOnWriteArrayList
<>();
while
(!
currentDate
.
isAfter
(
endDate
))
{
while
(!
currentDate
.
isAfter
(
endDate
))
{
// 检查是否在假期内
// 检查是否在假期内
...
@@ -236,7 +236,7 @@ try {
...
@@ -236,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
);
}
}
...
@@ -260,8 +260,8 @@ try {
...
@@ -260,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
CopyOnWriteArrayList
<>();
CopyOnWriteArray
List
<
TimeSegment
>
segments
=
new
CopyOnWriteArrayList
<>();
if
(
isHoliday
)
{
if
(
isHoliday
)
{
// 假期:只处理特定日期的班次
// 假期:只处理特定日期的班次
...
@@ -359,29 +359,29 @@ if(shifts==null||shifts.size()==0) {
...
@@ -359,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
));
...
@@ -400,7 +400,7 @@ if(shifts==null||shifts.size()==0) {
...
@@ -400,7 +400,7 @@ if(shifts==null||shifts.size()==0) {
}
}
}
}
List
<
TimeSegment
>
result
=
new
CopyOnWriteArrayList
<>(
merged
);
CopyOnWriteArray
List
<
TimeSegment
>
result
=
new
CopyOnWriteArrayList
<>(
merged
);
result
.
addAll
(
maintenanceSegments
);
result
.
addAll
(
maintenanceSegments
);
// 按开始时间排序
// 按开始时间排序
...
...
src/main/java/com/aps/service/plan/PlanResultService.java
View file @
35f0e8bb
...
@@ -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
);
}
}
}
...
...
src/main/java/com/aps/service/plan/PlanSchedulerService.java
View file @
35f0e8bb
...
@@ -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
View file @
35f0e8bb
...
@@ -10,8 +10,8 @@ spring:
...
@@ -10,8 +10,8 @@ spring:
pathmatch
:
pathmatch
:
matching-strategy
:
ant_path_matcher
# Spring Boot 2.6+ 需要这个配置
matching-strategy
:
ant_path_matcher
# Spring Boot 2.6+ 需要这个配置
redis
:
redis
:
host
:
39.100.88.40
host
:
192.168.0.181
port
:
63
79
port
:
63
80
timeout
:
120000
timeout
:
120000
database
:
10
database
:
10
password
:
redis@228!
password
:
redis@228!
...
@@ -37,7 +37,7 @@ spring:
...
@@ -37,7 +37,7 @@ spring:
# Oracle数据源
# Oracle数据源
oracle
:
oracle
:
driver-class-name
:
oracle.jdbc.OracleDriver
driver-class-name
:
oracle.jdbc.OracleDriver
url
:
jdbc:oracle:thin:@//
39.100.78.207:700
2/ORCLPDB1
# ORCL为你的Oracle实例名
url
:
jdbc:oracle:thin:@//
192.168.0.181:152
2/ORCLPDB1
# ORCL为你的Oracle实例名
username
:
mes
# 替换为你的Oracle用户名
username
:
mes
# 替换为你的Oracle用户名
password
:
root_mes123456
# 替换为你的Oracle密码
password
:
root_mes123456
# 替换为你的Oracle密码
# sqlserver:
# sqlserver:
...
...
src/test/java/com/aps/demo/MachineSchedulerTest.java
deleted
100644 → 0
View file @
a3e69687
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