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
e45bc27d
Commit
e45bc27d
authored
Nov 26, 2025
by
DESKTOP-VKRD9QF\Administration
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/master'
parents
acfb5bcd
a15bc091
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
2561 additions
and
1829078 deletions
+2561
-1829078
pom.xml
pom.xml
+1
-0
schedule_log.txt
schedule_log.txt
+1055
-1829018
ResourceGanttController.java
...ava/com/aps/controller/gantt/ResourceGanttController.java
+11
-0
Chromosome.java
src/main/java/com/aps/entity/Algorithm/Chromosome.java
+28
-2
GAScheduleResult.java
src/main/java/com/aps/entity/Algorithm/GAScheduleResult.java
+6
-1
OpMachine.java
src/main/java/com/aps/entity/Algorithm/OpMachine.java
+29
-0
Pair.java
src/main/java/com/aps/entity/Algorithm/Pair.java
+23
-0
ScheduleParams.java
src/main/java/com/aps/entity/Algorithm/ScheduleParams.java
+3
-5
MachineOption.java
src/main/java/com/aps/entity/basic/MachineOption.java
+1
-1
Order.java
src/main/java/com/aps/entity/basic/Order.java
+2
-0
AdaptiveGaParams.java
...main/java/com/aps/service/Algorithm/AdaptiveGaParams.java
+149
-0
FitnessCalculator.java
...ain/java/com/aps/service/Algorithm/FitnessCalculator.java
+31
-0
GeneticAlgorithm.java
...main/java/com/aps/service/Algorithm/GeneticAlgorithm.java
+149
-2
GeneticDecoder.java
src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
+376
-0
GeneticOperations.java
...ain/java/com/aps/service/Algorithm/GeneticOperations.java
+243
-0
Initialization.java
src/main/java/com/aps/service/Algorithm/Initialization.java
+32
-21
MachineCalculator.java
...ain/java/com/aps/service/Algorithm/MachineCalculator.java
+301
-0
PlanResultService.java
src/main/java/com/aps/service/plan/PlanResultService.java
+107
-16
application.yml
src/main/resources/application.yml
+12
-12
machines.json
src/main/resources/machines.json
+2
-0
No files found.
pom.xml
View file @
e45bc27d
...
...
@@ -91,6 +91,7 @@
<artifactId>
spring-boot-starter-test
</artifactId>
<scope>
test
</scope>
</dependency>
</dependencies>
<build>
...
...
schedule_log.txt
View file @
e45bc27d
This source diff could not be displayed because it is too large. You can
view the blob
instead.
src/main/java/com/aps/controller/gantt/ResourceGanttController.java
View file @
e45bc27d
package
com
.
aps
.
controller
.
gantt
;
import
com.aps.entity.Algorithm.Chromosome
;
import
com.aps.entity.basic.ScheduleChromosome
;
import
com.aps.entity.Gantt.ProductGanttVO
;
import
com.aps.entity.Gantt.ResourceGanttVO
;
...
...
@@ -103,6 +104,16 @@ public class ResourceGanttController {
.
collect
(
Collectors
.
toList
());
}
@GetMapping
(
"/getScene"
)
@Operation
(
summary
=
"获取所有场景ID"
,
description
=
"获取所有场景ID"
)
public
List
<
Chromosome
>
getScene
()
{
// 调用 PlanResultService 获取 ScheduleChromosome 列表
List
<
Chromosome
>
scheduleChromosomes
=
planResultService
.
execute1
();
// 提取所有场景ID
return
scheduleChromosomes
;
}
/**
* 将 ScheduleChromosome 转换为 ResourceGanttVO 列表
* @param scheduleChromosome 调度结果
...
...
src/main/java/com/aps/entity/Algorithm/Chromosome.java
View file @
e45bc27d
package
com
.
aps
.
entity
.
Algorithm
;
import
com.aps.entity.Algorithm.ScheduleResult
;
import
com.aps.entity.basic.Machine
;
import
lombok.Data
;
import
java.util.List
;
import
java.util.UUID
;
import
java.util.stream.Collectors
;
/**
* 作者:佟礼
...
...
@@ -12,16 +13,41 @@ import java.util.List;
*/
@Data
public
class
Chromosome
{
private
String
ID
=
UUID
.
randomUUID
().
toString
();
/// <summary>
/// 机器选择部分(可选机器集中的顺序号)
/// </summary>
private
List
<
Integer
>
MachineSelection
;
/// <summary>
/// 工序排序部分(工件/订单ID)
/// </summary>
private
String
MachineStr
;
public
String
getMachineStr
()
{
return
MachineSelection
.
stream
()
.
map
(
String:
:
valueOf
)
// 将每个 Integer 转换为 String
.
collect
(
Collectors
.
joining
(
","
));
};
/// <summary>
/// 工序排序部分(工件/订单ID)
/// </summary>
private
List
<
Integer
>
OperationSequencing
;
/// <summary>
/// 工序排序部分(工件/订单ID)
/// </summary>
private
String
OperationStr
;
public
String
getOperationStr
()
{
return
OperationSequencing
.
stream
()
.
map
(
String:
:
valueOf
)
// 将每个 Integer 转换为 String
.
collect
(
Collectors
.
joining
(
"|"
));
};
/// <summary>
/// 适应度值
/// </summary>
...
...
@@ -35,7 +61,7 @@ public class Chromosome {
/// <summary>
/// 解码后的调度结果
/// </summary>
private
List
<
ScheduleResult
>
Result
;
private
List
<
GA
ScheduleResult
>
Result
;
/// <summary>
/// 最早完工时间
...
...
src/main/java/com/aps/entity/Algorithm/ScheduleResult.java
→
src/main/java/com/aps/entity/Algorithm/
GA
ScheduleResult.java
View file @
e45bc27d
...
...
@@ -9,7 +9,7 @@ import java.util.List;
* 时间:2025-11-21
*/
@Data
public
class
ScheduleResult
{
public
class
GA
ScheduleResult
{
private
int
GroupId
;
private
int
ProductId
;
private
int
OperationId
;
...
...
@@ -21,4 +21,9 @@ public class ScheduleResult {
private
int
OneTime
;
// 单件工时
private
int
ProcessingTime
;
// 绝对处理时间(分钟)
private
int
ChangeoverTime
;
public
int
getFlowTime
()
{
return
EndTime
-
StartTime
;
};
}
src/main/java/com/aps/entity/Algorithm/OpMachine.java
0 → 100644
View file @
e45bc27d
package
com
.
aps
.
entity
.
Algorithm
;
import
lombok.Data
;
/**
* 作者:佟礼
* 时间:2025-11-25
*/
@Data
public
class
OpMachine
{
/**
* 所属组ID
*/
private
int
GroupId
;
/**
* 订单内工序顺序号
*/
private
int
Sequence
;
/**
* 设备ID
*/
private
int
machineId
;
/**
* 单件工时
*/
private
int
processingTime
;
// 加工时间
}
src/main/java/com/aps/entity/Algorithm/Pair.java
0 → 100644
View file @
e45bc27d
package
com
.
aps
.
entity
.
Algorithm
;
/**
* 作者:佟礼
* 时间:2025-11-24
*/
public
class
Pair
<
A
,
B
>
{
private
final
A
first
;
private
final
B
second
;
public
Pair
(
A
first
,
B
second
)
{
this
.
first
=
first
;
this
.
second
=
second
;
}
public
A
getFirst
()
{
return
first
;
}
public
B
getSecond
()
{
return
second
;
}
}
src/main/java/com/aps/entity/Algorithm/ScheduleParams.java
View file @
e45bc27d
...
...
@@ -3,6 +3,7 @@ package com.aps.entity.Algorithm;
import
com.aps.entity.basic.Entry
;
import
lombok.Data
;
import
java.time.LocalDateTime
;
import
java.util.Date
;
/**
...
...
@@ -11,12 +12,9 @@ import java.util.Date;
*/
@Data
public
class
ScheduleParams
{
private
Date
BaseTime
=
new
Date
();
// 当前基准时间
private
LocalDateTime
BaseTime
;
// 当前基准时间
/// <summary>
/// 设备总数
/// </summary>
private
int
MachineCount
;
// 设备总数
/// <summary>
/// 交叉概率
...
...
src/main/java/com/aps/entity/basic/MachineOption.java
View file @
e45bc27d
...
...
@@ -8,7 +8,7 @@ import lombok.Data;
@Data
public
class
MachineOption
{
private
int
machineId
;
private
int
processingTime
;
// 加工时间
private
int
processingTime
;
// 加工时间
(秒)
private
int
setupTime
;
// 换型时间(如果与前一个产品不同)
private
int
teardownTime
;
// 收尾时间(后处理时间)
private
int
contantTime
;
// 常数时间
...
...
src/main/java/com/aps/entity/basic/Order.java
View file @
e45bc27d
...
...
@@ -13,6 +13,8 @@ public class Order {
private
int
id
;
private
int
productId
;
private
int
quantity
=
100
;
// 100个
private
int
sYQuantity
;
private
OffsetDateTime
dueDate
;
private
LocalDateTime
orderCompletion
;
private
double
tardiness
;
...
...
src/main/java/com/aps/service/Algorithm/AdaptiveGaParams.java
0 → 100644
View file @
e45bc27d
package
com
.
aps
.
service
.
Algorithm
;
import
lombok.Data
;
import
java.text.DecimalFormat
;
/**
* 作者:佟礼
* 时间:2025-11-26
*/
@Data
public
class
AdaptiveGaParams
{
// 基础参数(自适应调整的基准值)
private
static
final
int
MIN_POPULATION_SIZE
=
50
;
private
static
final
int
MAX_POPULATION_SIZE
=
500
;
private
static
final
int
MIN_MAX_ITERATIONS
=
50
;
private
static
final
int
MAX_MAX_ITERATIONS
=
200
;
private
static
final
float
MIN_CROSSOVER_PROB
=
0.6f
;
private
static
final
float
MAX_CROSSOVER_PROB
=
0.9f
;
private
static
final
float
MIN_MUTATION_PROB
=
0.05f
;
private
static
final
float
MAX_MUTATION_PROB
=
0.2f
;
private
static
final
int
MIN_TOURNAMENT_SIZE
=
3
;
private
static
final
int
MAX_TOURNAMENT_SIZE
=
7
;
private
static
final
int
MIN_LOW_PRIORITY_THRESHOLD
=
3
;
private
static
final
int
MAX_LOW_PRIORITY_THRESHOLD
=
6
;
private
static
final
int
DELIVERY_DEADLINE_BASE
=
1000
;
// 交付期基准值
// 自适应调整后的参数(公开可访问)
private
int
populationSize
;
private
int
maxIterations
;
private
float
crossoverProb
;
private
float
mutationProb
;
private
int
tournamentSize
;
private
int
lowPriorityThreshold
;
private
int
deliveryDeadline
;
// 自适应调整系数(可根据实际场景微调)
private
float
populationSizeCoeff
=
0.1f
;
// 种群大小对工序数的敏感系数
private
float
maxIterationsCoeff
=
0.05f
;
// 迭代次数对工序数的敏感系数
private
float
crossoverProbCoeff
=
0.0001f
;
// 交叉概率对工序数的敏感系数
private
float
mutationProbCoeff
=
0.00005f
;
// 变异概率对工序数的敏感系数
private
float
tournamentSizeCoeff
=
200
f
;
// 锦标赛规模对工序数的敏感系数(分母越大越不敏感)
private
float
lowPriorityThresholdCoeff
=
300
f
;
// 低优先级阈值对工序数的敏感系数(分母越大越不敏感)
private
float
deliveryDeadlineCoeff
=
0.5f
;
// 交付期对工序数的敏感系数
/**
* 根据总工序数初始化自适应参数(一次性调整)
*
* @param totalOps 总工序数
*/
public
void
initAdaptiveParams
(
int
totalOps
)
{
// 1. 种群大小:50 ~ 500,随工序数线性增加
double
populationSizeTemp
=
MIN_POPULATION_SIZE
+
totalOps
*
populationSizeCoeff
;
populationSize
=
(
int
)
clamp
(
populationSizeTemp
,
MIN_POPULATION_SIZE
,
MAX_POPULATION_SIZE
);
// 确保偶数(方便交叉)
if
(
populationSize
%
2
!=
0
)
{
populationSize
+=
1
;
}
// 2. 最大迭代次数:50 ~ 200,随工序数线性减少
double
maxIterationsTemp
=
MAX_MAX_ITERATIONS
-
totalOps
*
maxIterationsCoeff
;
maxIterations
=
(
int
)
clamp
(
maxIterationsTemp
,
MIN_MAX_ITERATIONS
,
MAX_MAX_ITERATIONS
);
// 3. 交叉概率:0.6 ~ 0.9,随工序数轻微减少
double
crossoverProbTemp
=
MAX_CROSSOVER_PROB
-
totalOps
*
crossoverProbCoeff
;
crossoverProb
=
(
float
)
clamp
(
crossoverProbTemp
,
MIN_CROSSOVER_PROB
,
MAX_CROSSOVER_PROB
);
// 4. 变异概率:0.05 ~ 0.2,随工序数轻微增加
double
mutationProbTemp
=
MIN_MUTATION_PROB
+
totalOps
*
mutationProbCoeff
;
mutationProb
=
(
float
)
clamp
(
mutationProbTemp
,
MIN_MUTATION_PROB
,
MAX_MUTATION_PROB
);
// 5. 锦标赛规模:3 ~ 7,随工序数阶梯式增加
double
tournamentSizeTemp
=
MIN_TOURNAMENT_SIZE
+
totalOps
/
tournamentSizeCoeff
;
tournamentSize
=
(
int
)
clamp
(
tournamentSizeTemp
,
MIN_TOURNAMENT_SIZE
,
MAX_TOURNAMENT_SIZE
);
// 6. 低优先级阈值:3 ~ 6,随工序数阶梯式减少
double
lowPriorityThresholdTemp
=
MAX_LOW_PRIORITY_THRESHOLD
-
totalOps
/
lowPriorityThresholdCoeff
;
lowPriorityThreshold
=
(
int
)
clamp
(
lowPriorityThresholdTemp
,
MIN_LOW_PRIORITY_THRESHOLD
,
MAX_LOW_PRIORITY_THRESHOLD
);
// 7. 交付期:基准1000 + 工序数*0.5(随工序数增加)
deliveryDeadline
=
(
int
)
(
DELIVERY_DEADLINE_BASE
+
totalOps
*
deliveryDeadlineCoeff
);
// 输出自适应参数日志
DecimalFormat
df
=
new
DecimalFormat
(
"#.###"
);
System
.
out
.
println
(
"=== 自适应参数初始化完成(总工序数:"
+
totalOps
+
")==="
);
System
.
out
.
println
(
"种群大小:"
+
populationSize
);
System
.
out
.
println
(
"最大迭代次数:"
+
maxIterations
);
System
.
out
.
println
(
"交叉概率:"
+
df
.
format
(
crossoverProb
));
System
.
out
.
println
(
"变异概率:"
+
df
.
format
(
mutationProb
));
System
.
out
.
println
(
"锦标赛规模:"
+
tournamentSize
);
System
.
out
.
println
(
"低优先级阈值:"
+
lowPriorityThreshold
);
System
.
out
.
println
(
"交付期:"
+
deliveryDeadline
);
System
.
out
.
println
(
"============================================\n"
);
}
/**
* 迭代中动态微调参数(根据种群收敛情况)
*
* @param currentIter 当前迭代次数
* @param fitnessStd 当前种群适应度标准差(越小收敛越好)
*/
public
void
fineTuneParams
(
int
currentIter
,
float
fitnessStd
)
{
// 每10代微调一次,避免频繁调整
if
(
currentIter
%
10
!=
0
)
{
return
;
}
// 适应度标准差阈值(可调整):小于0.05视为收敛过慢,大于0.2视为收敛过快
final
float
LOW_STD_THRESHOLD
=
0.05f
;
final
float
HIGH_STD_THRESHOLD
=
0.2f
;
final
float
ADJUST_STEP
=
0.05f
;
// 调整步长
DecimalFormat
df
=
new
DecimalFormat
(
"#.###"
);
// 1. 收敛过慢(适应度方差小):增加变异概率,降低交叉概率
if
(
fitnessStd
<
LOW_STD_THRESHOLD
)
{
mutationProb
=
(
float
)
clamp
(
mutationProb
+
ADJUST_STEP
*
0.5f
,
MIN_MUTATION_PROB
,
MAX_MUTATION_PROB
);
crossoverProb
=
(
float
)
clamp
(
crossoverProb
-
ADJUST_STEP
*
0.3f
,
MIN_CROSSOVER_PROB
,
MAX_CROSSOVER_PROB
);
System
.
out
.
println
(
"迭代"
+
currentIter
+
":收敛过慢,微调参数→变异概率:"
+
df
.
format
(
mutationProb
)
+
",交叉概率:"
+
df
.
format
(
crossoverProb
));
}
// 2. 收敛过快(适应度方差大):降低变异概率,增加交叉概率
else
if
(
fitnessStd
>
HIGH_STD_THRESHOLD
)
{
mutationProb
=
(
float
)
clamp
(
mutationProb
-
ADJUST_STEP
*
0.5f
,
MIN_MUTATION_PROB
,
MAX_MUTATION_PROB
);
crossoverProb
=
(
float
)
clamp
(
crossoverProb
+
ADJUST_STEP
*
0.3f
,
MIN_CROSSOVER_PROB
,
MAX_CROSSOVER_PROB
);
System
.
out
.
println
(
"迭代"
+
currentIter
+
":收敛过快,微调参数→变异概率:"
+
df
.
format
(
mutationProb
)
+
",交叉概率:"
+
df
.
format
(
crossoverProb
));
}
// 3. 后期迭代(超过80%迭代次数):降低变异概率,稳定最优解
if
(
currentIter
>
maxIterations
*
0.8f
)
{
mutationProb
=
(
float
)
clamp
(
mutationProb
*
0.8f
,
MIN_MUTATION_PROB
,
MAX_MUTATION_PROB
);
System
.
out
.
println
(
"迭代"
+
currentIter
+
":后期迭代,微调参数→变异概率:"
+
df
.
format
(
mutationProb
));
}
}
/**
*
*
* @param value 需要限制的值
* @param min 最小值
* @param max 最大值
* @return 限制后的值
*/
private
double
clamp
(
double
value
,
double
min
,
double
max
)
{
return
Math
.
max
(
min
,
Math
.
min
(
max
,
value
));
}
}
src/main/java/com/aps/service/Algorithm/FitnessCalculator.java
0 → 100644
View file @
e45bc27d
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.entity.Algorithm.Chromosome
;
/**
* 作者:佟礼
* 时间:2025-11-24
*/
public
class
FitnessCalculator
{
/**
* 多目标适应度计算(加权求和)
*/
public
double
calculateFitness
(
Chromosome
chromosome
)
{
// 权重可根据实际需求调整
double
w1
=
0.3
;
// 最早完工时间(越小越好,归一化后取反)
double
w2
=
0.2
;
// 总流程时间(越小越好)
double
w3
=
0.15
;
// 总换型时间(越小越好)
double
w4
=
0.2
;
// 机器负载均衡(标准差越小越好)
double
w5
=
0.15
;
// 交付期延迟(越小越好)
// 归一化(假设最大可能值,实际应根据问题规模调整)
double
normMakespan
=
1
/
(
1
+(
double
)
chromosome
.
getMakespan
());
double
normFlowTime
=
1
/
(
1
+
(
double
)
chromosome
.
getTotalFlowTime
()
);
double
normChangeover
=
1
/
(
1
+
(
double
)
chromosome
.
getTotalChangeoverTime
());
double
normLoadStd
=
chromosome
.
getMachineLoadStd
();
double
normDelay
=
1
/
(
1
+
(
double
)
chromosome
.
getDelayTime
()
);
// 适应度值(越大越好)
return
w1
*
normMakespan
+
w2
*
normFlowTime
+
w3
*
normChangeover
+
w4
*
normLoadStd
+
w5
*
normDelay
;
}
}
src/main/java/com/aps/service/Algorithm/GeneticAlgorithm.java
View file @
e45bc27d
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.common.util.DeepCopyUtil
;
import
com.aps.common.util.ProductionDeepCopyUtil
;
import
com.aps.entity.Algorithm.Chromosome
;
import
com.aps.entity.Algorithm.GlobalOperationInfo
;
import
com.aps.entity.Algorithm.Pair
;
import
com.aps.entity.Algorithm.ScheduleParams
;
import
com.aps.entity.basic.Entry
;
import
com.aps.entity.basic.Machine
;
import
com.aps.entity.basic.Material
;
import
com.aps.entity.basic.Order
;
import
com.aps.service.plan.MachineSchedulerService
;
import
java.util.*
;
import
java.util.stream.Collectors
;
/**
* 作者:佟礼
* 时间:2025-11-21
*/
public
class
GeneticAlgorithm
{
public
void
Run
()
{
System
.
out
.
println
(
""
);
private
final
Random
rnd
=
new
Random
();
private
final
List
<
Machine
>
machines
;
private
final
MachineSchedulerService
machineScheduler
;
private
final
List
<
Order
>
orders
;
private
final
List
<
Material
>
materials
;
public
GeneticAlgorithm
(
List
<
Machine
>
machines
,
List
<
Order
>
orders
,
List
<
Material
>
materials
,
MachineSchedulerService
machineScheduler
)
{
this
.
machines
=
machines
;
this
.
orders
=
orders
;
this
.
materials
=
materials
;
this
.
machineScheduler
=
machineScheduler
;
}
public
List
<
Chromosome
>
Run
(
ScheduleParams
param
,
List
<
Entry
>
allOperations
)
{
System
.
out
.
println
(
"开始"
);
Initialization
initialization
=
new
Initialization
(
allOperations
);
GeneticOperations
geneticOps
=
new
GeneticOperations
(
allOperations
);
// 预生成全局工序列表(所有初始化方法共享同一顺序)
List
<
GlobalOperationInfo
>
globalOpList
=
initialization
.
generateGlobalOpList
();
// 步骤1:初始化种群
List
<
Chromosome
>
population
=
initialization
.
generateInitialPopulation
(
param
,
globalOpList
);
Chromosomedecode
(
param
,
allOperations
,
globalOpList
,
population
);
int
ordercount
=
globalOpList
.
stream
()
.
mapToInt
(
GlobalOperationInfo:
:
getGroupId
)
.
max
()
.
orElse
(
0
);
Chromosome
best
=
new
Chromosome
();
double
bestFitness
=
0
;
int
Iteration
=
0
;
// 步骤2:迭代进化
for
(
int
iter
=
0
;
iter
<
param
.
getMaxIterations
();
iter
++)
{
// 解码并计算适应度
// 检查终止条件(此处简化为迭代次数)
if
(
iter
==
param
.
getMaxIterations
()
-
1
)
{
break
;
}
// 选择操作
List
<
Chromosome
>
selected
=
geneticOps
.
tournamentSelection
(
population
);
// 交叉操作
List
<
Chromosome
>
nextPopulation
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
selected
.
size
();
i
+=
2
)
{
if
(
i
+
1
>=
selected
.
size
())
{
nextPopulation
.
add
(
selected
.
get
(
i
));
break
;
}
Chromosome
parent1
=
selected
.
get
(
i
);
Chromosome
parent2
=
selected
.
get
(
i
+
1
);
if
(
rnd
.
nextDouble
()
<
param
.
getCrossoverProb
())
{
// 假设PoxCrossover返回包含两个子染色体的数组
Pair
<
Chromosome
,
Chromosome
>
children
=
geneticOps
.
poxCrossover
(
parent1
,
parent2
,
ordercount
);
nextPopulation
.
add
(
children
.
getFirst
());
nextPopulation
.
add
(
children
.
getSecond
());
}
else
{
nextPopulation
.
add
(
parent1
);
nextPopulation
.
add
(
parent2
);
}
}
// 变异操作
for
(
Chromosome
chromosome
:
nextPopulation
)
{
if
(
rnd
.
nextDouble
()
<
param
.
getMutationProb
())
{
geneticOps
.
mutate
(
chromosome
,
globalOpList
);
}
}
// 精英保留
List
<
Chromosome
>
population1
=
population
.
stream
()
.
sorted
((
c1
,
c2
)
->
Double
.
compare
(
c2
.
getFitness
(),
c1
.
getFitness
()))
.
collect
(
Collectors
.
toList
());
// 降序排序
List
<
Chromosome
>
elites
=
population1
.
subList
(
0
,
param
.
getElitismCount
());
List
<
Chromosome
>
newPopulation
=
new
ArrayList
<>();
newPopulation
.
addAll
(
elites
);
newPopulation
.
addAll
(
nextPopulation
);
// 更新种群
population
=
newPopulation
.
stream
()
.
limit
(
param
.
getPopulationSize
()
)
.
collect
(
Collectors
.
toList
());
Chromosomedecode
(
param
,
allOperations
,
globalOpList
,
population
);
best
=
population
.
stream
()
.
max
(
Comparator
.
comparingDouble
(
Chromosome:
:
getFitness
))
.
orElse
(
null
);
if
(
bestFitness
<
best
.
getFitness
())
{
bestFitness
=
best
.
getFitness
();
}
else
{
Iteration
++;
}
if
(
Iteration
>
20
)
{
break
;
}
}
// 步骤3:返回最优解
return
population
.
stream
()
.
sorted
((
c1
,
c2
)
->
Double
.
compare
(
c2
.
getFitness
(),
c1
.
getFitness
()))
.
limit
(
10
)
.
collect
(
Collectors
.
toList
());
}
private
void
Chromosomedecode
(
ScheduleParams
param
,
List
<
Entry
>
allOperations
,
List
<
GlobalOperationInfo
>
globalOpList
,
List
<
Chromosome
>
population
)
{
GeneticDecoder
decoder
=
new
GeneticDecoder
(
param
.
getBaseTime
(),
machines
,
orders
,
materials
,
machineScheduler
);
FitnessCalculator
fitnessCalc
=
new
FitnessCalculator
();
population
.
parallelStream
().
forEach
(
chromosome
->
{
chromosome
.
setResult
(
new
ArrayList
<>());
// 假设Machine类有拷贝方法,或使用MapStruct等工具进行映射
chromosome
.
setMachines
(
ProductionDeepCopyUtil
.
deepCopyList
(
machines
));
// 简单拷贝,实际可能需要深拷贝
decoder
.
decodeChromosomeWithCache
(
chromosome
,
globalOpList
,
allOperations
);
chromosome
.
setFitness
(
fitnessCalc
.
calculateFitness
(
chromosome
));
});
}
}
src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
0 → 100644
View file @
e45bc27d
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.common.util.ProductionDeepCopyUtil
;
import
com.aps.entity.Algorithm.*
;
import
com.aps.entity.basic.*
;
import
com.aps.service.plan.MachineSchedulerService
;
import
java.time.LocalDateTime
;
import
java.time.temporal.ChronoUnit
;
import
java.util.*
;
import
java.util.stream.Collectors
;
import
java.util.concurrent.locks.ReentrantLock
;
/**
* 作者:佟礼
* 时间:2025-11-24
*/
public
class
GeneticDecoder
{
private
final
Map
<
String
,
Chromosome
>
decodingCache
=
new
HashMap
<>();
// 缓存大小限制
private
static
final
int
MAX_CACHE_SIZE
=
1000
;
// 线程安全锁:避免多线程下缓存操作冲突(可选,若单线程可移除)
private
final
ReentrantLock
cacheLock
=
new
ReentrantLock
();
private
LocalDateTime
baseTime
;
private
final
List
<
Machine
>
machines
;
private
final
MachineSchedulerService
machineScheduler
;
private
final
List
<
Order
>
orders
;
private
int
orderMaxID
;
private
final
List
<
Material
>
materials
;
public
GeneticDecoder
(
LocalDateTime
baseTime
,
List
<
Machine
>
machines
,
List
<
Order
>
orders
,
List
<
Material
>
materials
,
MachineSchedulerService
machineScheduler
)
{
this
.
baseTime
=
baseTime
;
this
.
machines
=
machines
;
this
.
orders
=
orders
;
this
.
orders
.
forEach
(
t
->
t
.
setSYQuantity
(
t
.
getQuantity
()));
this
.
materials
=
materials
;
this
.
machineScheduler
=
machineScheduler
;
this
.
orderMaxID
=
orders
.
stream
().
mapToInt
(
Order:
:
getId
).
max
().
orElse
(
0
);
}
public
void
decodeChromosomeWithCache
(
Chromosome
chromosome
,
List
<
GlobalOperationInfo
>
globalOpList
,
List
<
Entry
>
allOperations
)
{
// 1. 创建缓存键
String
cacheKey
=
createCacheKey
(
chromosome
);
// 2. 尝试从缓存获取(加锁保证线程安全)
cacheLock
.
lock
();
try
{
if
(
decodingCache
.
containsKey
(
cacheKey
))
{
// 缓存命中:复用缓存结果
Chromosome
cachedResult
=
decodingCache
.
get
(
cacheKey
);
// 赋值给染色体
String
ID
=
chromosome
.
getID
();
chromosome
=
ProductionDeepCopyUtil
.
deepCopy
(
cachedResult
);
chromosome
.
setID
(
ID
);
return
;
}
}
finally
{
cacheLock
.
unlock
();
// 确保锁释放
}
// 3. 缓存未命中:执行解码逻辑
decode
(
chromosome
,
globalOpList
,
allOperations
);
// 4. 将解码结果存入缓存(加锁保证线程安全)
cacheLock
.
lock
();
try
{
// 存储缓存:封装为 CacheValue 对象
decodingCache
.
put
(
cacheKey
,
chromosome
);
// 5. 限制缓存大小(超过 1000 移除最早插入的键)
if
(
decodingCache
.
size
()
>
MAX_CACHE_SIZE
)
{
Iterator
<
String
>
keyIterator
=
decodingCache
.
keySet
().
iterator
();
if
(
keyIterator
.
hasNext
())
{
keyIterator
.
next
();
// 获取首个键
keyIterator
.
remove
();
// 移除首个键(对应 C# _decodingCache.Keys.First())
}
}
}
finally
{
cacheLock
.
unlock
();
}
}
/**
* 染色体解码为调度方案
*/
public
void
decode
(
Chromosome
chromosome
,
List
<
GlobalOperationInfo
>
globalOpList
,
List
<
Entry
>
allOperations
)
{
Map
<
Integer
,
GlobalOperationInfo
>
globalOpMap
=
globalOpList
.
stream
()
.
collect
(
Collectors
.
toMap
(
GlobalOperationInfo:
:
getGlobalOpId
,
g
->
g
));
// 验证:MachineSelection长度必须与全局工序数一致
if
(
chromosome
.
getMachineSelection
().
size
()
!=
globalOpList
.
size
())
{
throw
new
IllegalArgumentException
(
String
.
format
(
"MachineSelection长度(%d)与全局工序数(%d)不匹配!"
,
chromosome
.
getMachineSelection
().
size
(),
globalOpList
.
size
()));
}
// 步骤1:生成“工序→机器/加工时间”映射
List
<
OpMachine
>
opMachineMap
=
new
ArrayList
<>();
for
(
GlobalOperationInfo
globalOp
:
globalOpList
)
{
int
globalOpId
=
globalOp
.
getGlobalOpId
();
Entry
op
=
globalOp
.
getOp
();
int
groupId
=
globalOp
.
getGroupId
();
int
sequence
=
globalOp
.
getSequence
();
// 从MachineSelection中获取当前工序的机器选择顺序号
int
machineSeq
=
chromosome
.
getMachineSelection
().
get
(
globalOpId
);
List
<
MachineOption
>
optionalMachines
=
op
.
getMachineOptions
();
// 验证:机器顺序号必须在可选范围内
if
(
machineSeq
<
1
||
machineSeq
>
optionalMachines
.
size
())
{
throw
new
IllegalStateException
(
String
.
format
(
"全局工序%d(订单%d工序%d)的机器顺序号%d超出范围(1-%d)"
,
globalOpId
,
groupId
,
sequence
,
machineSeq
,
optionalMachines
.
size
()));
}
// 获取选择的设备ID和加工时间
MachineOption
selectedMachine
=
optionalMachines
.
get
(
machineSeq
-
1
);
OpMachine
opMachine
=
new
OpMachine
();
opMachine
.
setGroupId
(
groupId
);
opMachine
.
setSequence
(
sequence
);
opMachine
.
setMachineId
(
selectedMachine
.
getMachineId
());
opMachine
.
setProcessingTime
(
selectedMachine
.
getProcessingTime
());
opMachineMap
.
add
(
opMachine
);
}
// 步骤2:按OperationSequencing顺序调度工序
Map
<
Integer
,
Integer
>
orderProcessCounter
=
allOperations
.
stream
()
.
collect
(
Collectors
.
groupingBy
(
Entry:
:
getGroupId
,
Collectors
.
collectingAndThen
(
Collectors
.
counting
(),
Long:
:
intValue
)))
.
entrySet
().
stream
()
.
collect
(
Collectors
.
toMap
(
Map
.
Entry
::
getKey
,
e
->
0
));
Map
<
Integer
,
Integer
>
orderLastEndTime
=
allOperations
.
stream
()
.
collect
(
Collectors
.
groupingBy
(
Entry:
:
getGroupId
))
.
keySet
().
stream
()
.
collect
(
Collectors
.
toMap
(
k
->
k
,
k
->
0
));
Map
<
Integer
,
String
>
machineState
=
chromosome
.
getMachines
().
stream
()
.
collect
(
Collectors
.
toMap
(
Machine:
:
getId
,
m
->
""
));
List
<
Entry
>
allScheduledOps
=
new
ArrayList
<>();
for
(
int
groupId
:
chromosome
.
getOperationSequencing
())
{
int
scheduledCount
=
orderProcessCounter
.
get
(
groupId
);
List
<
Entry
>
orderOps
=
allOperations
.
stream
()
.
filter
(
t
->
t
.
getGroupId
()
==
groupId
)
.
collect
(
Collectors
.
toList
());
if
(
scheduledCount
>=
orderOps
.
size
())
{
throw
new
IllegalStateException
(
String
.
format
(
"订单%d的工序已全部调度(共%d道),无需重复处理!"
,
groupId
,
orderOps
.
size
()));
}
Entry
currentOp
=
orderOps
.
get
(
scheduledCount
);
int
opSequence
=
currentOp
.
getSequence
();
// 从映射表中获取机器和加工时间
OpMachine
machineInfo
=
opMachineMap
.
stream
()
.
filter
(
m
->
m
.
getGroupId
()
==
groupId
&&
m
.
getSequence
()==
opSequence
)
.
findFirst
()
.
orElse
(
null
);
int
machineId
=
machineInfo
.
getMachineId
();
int
processTime
=
machineInfo
.
getProcessingTime
();
Machine
targetMachine
=
chromosome
.
getMachines
().
stream
()
.
filter
(
m
->
m
.
getId
()
==
machineId
)
.
findFirst
()
.
orElse
(
null
);
int
prevtime
=
0
;
if
(!
currentOp
.
getPrevEntryIds
().
isEmpty
())
{
// 处理多个前工序
for
(
int
opid
:
currentOp
.
getPrevEntryIds
())
{
List
<
GAScheduleResult
>
prevOperations
=
chromosome
.
getResult
().
stream
()
.
filter
(
t
->
t
.
getGroupId
()
==
groupId
&&
t
.
getOperationId
()
==
opid
)
.
collect
(
Collectors
.
toList
());
for
(
GAScheduleResult
prevOp
:
prevOperations
)
{
prevtime
=
Math
.
max
(
prevtime
,
prevOp
.
getEndTime
());
}
}
}
// 上个离散参数
String
lastDiscreteParameter
=
machineState
.
get
(
machineId
);
int
bomtime
=
0
;
prevtime
=
Math
.
max
(
prevtime
,
bomtime
);
Machine
machine
=
chromosome
.
getMachines
().
stream
()
.
filter
(
m
->
m
.
getId
()
==
machineId
)
.
findFirst
()
.
orElse
(
null
);
int
changeoverTime
=
0
;
//(lastDiscreteParameter.isEmpty() ||
// lastDiscreteParameter.equals(currentOp.getDiscreteParameter())) ? 0 : 0;
int
actualEndTime
=
processWithSingleMachine
(
currentOp
,
machine
,
processTime
,
prevtime
,
chromosome
);
orderProcessCounter
.
put
(
groupId
,
orderProcessCounter
.
get
(
groupId
)
+
1
);
orderLastEndTime
.
put
(
groupId
,
actualEndTime
);
machineState
.
put
(
machineId
,
currentOp
.
getDiscreteParameter
());
}
// 步骤4:计算调度指标
calculateScheduleResult
(
chromosome
);
}
private
int
processWithSingleMachine
(
Entry
operation
,
Machine
machine
,
int
processingTime
,
int
prevtime
,
Chromosome
chromosome
)
{
int
processingTimeTotal
=
processingTime
*
operation
.
getQuantity
();
MachineCalculator
machineCalculator
=
new
MachineCalculator
(
baseTime
,
machines
,
machineScheduler
);
List
<
ScheduleResultDetail
>
geneDetails
=
machineCalculator
.
getNextAvailableTime
(
machine
,
prevtime
,
-
1
,
processingTimeTotal
,
chromosome
.
getResult
(),
false
,
true
,
true
);
int
startTime
=
geneDetails
.
stream
()
.
mapToInt
(
ScheduleResultDetail:
:
getStartTime
)
.
min
()
.
orElse
(
0
);
int
endTime
=
geneDetails
.
stream
()
.
mapToInt
(
ScheduleResultDetail:
:
getEndTime
)
.
max
()
.
orElse
(
0
);
GAScheduleResult
result
=
new
GAScheduleResult
();
result
.
setGroupId
(
operation
.
getGroupId
());
result
.
setOperationId
(
operation
.
getId
());
result
.
setMachineId
(
machine
.
getId
());
result
.
setQuantity
(
operation
.
getQuantity
());
result
.
setStartTime
(
startTime
);
result
.
setEndTime
(
endTime
);
result
.
setOneTime
(
processingTime
);
result
.
setProcessingTime
(
processingTimeTotal
);
result
.
setGeneDetails
(
geneDetails
);
chromosome
.
getResult
().
add
(
result
);
return
endTime
;
}
private
void
calculateScheduleResult
(
Chromosome
chromosome
)
{
// 1. 最早完工时间(最小化)
double
makespan
=
chromosome
.
getResult
().
stream
()
.
mapToInt
(
GAScheduleResult:
:
getEndTime
)
.
max
()
.
orElse
(
0
);
// 2. 交付期满足情况(最小化延迟)
double
tardiness
=
0
;
Map
<
Integer
,
List
<
GAScheduleResult
>>
orderGroups
=
chromosome
.
getResult
().
stream
()
.
collect
(
Collectors
.
groupingBy
(
GAScheduleResult:
:
getGroupId
));
for
(
Map
.
Entry
<
Integer
,
List
<
GAScheduleResult
>>
group
:
orderGroups
.
entrySet
())
{
int
groupId
=
group
.
getKey
();
int
orderCompletion
=
group
.
getValue
().
stream
()
.
mapToInt
(
GAScheduleResult:
:
getEndTime
)
.
max
()
.
orElse
(
0
);
Order
order
=
orders
.
stream
()
.
filter
(
t
->
t
.
getId
()
==
groupId
)
.
findFirst
()
.
orElse
(
null
);
LocalDateTime
completionTime
=
baseTime
.
plusMinutes
(
orderCompletion
);
// 修复:正确处理OffsetDateTime到LocalDateTime的转换
LocalDateTime
dueDateTime
=
order
.
getDueDate
().
toLocalDateTime
();
if
(
completionTime
.
isAfter
(
dueDateTime
))
{
// 计算延迟小时数(修复时间计算)
long
hours
=
ChronoUnit
.
HOURS
.
between
(
dueDateTime
,
completionTime
);
long
minutes
=
ChronoUnit
.
MINUTES
.
between
(
dueDateTime
,
completionTime
)
%
60
;
tardiness
+=
hours
*
60
+
(
double
)
minutes
;
}
}
// 3. 最小总换型时间
double
totalSetupTime
=
calculateTotalSetupTime
(
chromosome
);
// 4. 最小化总流程时间
double
totalFlowTime
=
calculateTotalFlowTime
(
chromosome
);
// 5. 机器负载均衡
double
machineLoadBalance
=
calculateMachineLoadBalance
(
chromosome
);
// 存储各目标值
chromosome
.
setMakespan
(
makespan
);
chromosome
.
setTotalFlowTime
(
totalFlowTime
);
chromosome
.
setTotalChangeoverTime
(
totalSetupTime
);
chromosome
.
setMachineLoadStd
(
machineLoadBalance
);
chromosome
.
setDelayTime
(
tardiness
);
// 加权求和作为适应度(权重可根据需求调整)
// 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
(
Chromosome
chromosome
)
{
return
chromosome
.
getResult
().
stream
()
.
mapToDouble
(
GAScheduleResult:
:
getFlowTime
)
.
sum
();
}
// 计算总换型时间
private
double
calculateTotalSetupTime
(
Chromosome
chromosome
)
{
return
chromosome
.
getResult
().
stream
()
.
mapToDouble
(
GAScheduleResult:
:
getChangeoverTime
)
.
sum
();
}
// 计算机器负载均衡指标
private
double
calculateMachineLoadBalance
(
Chromosome
chromosome
)
{
Map
<
Integer
,
Double
>
machineUtilization
=
new
HashMap
<>();
int
maxEndTime
=
chromosome
.
getResult
().
stream
()
.
mapToInt
(
GAScheduleResult:
:
getEndTime
)
.
max
()
.
orElse
(
0
);
if
(
maxEndTime
==
0
)
return
0
;
for
(
Machine
machine
:
chromosome
.
getMachines
())
{
List
<
GAScheduleResult
>
machineGenes
=
chromosome
.
getResult
().
stream
()
.
filter
(
g
->
g
.
getMachineId
()
==
machine
.
getId
())
.
collect
(
Collectors
.
toList
());
double
busyTime
=
machineGenes
.
stream
()
.
mapToInt
(
g
->
g
.
getEndTime
()
-
g
.
getStartTime
())
.
sum
();
machineUtilization
.
put
(
machine
.
getId
(),
busyTime
/
maxEndTime
);
}
double
avgUtilization
=
machineUtilization
.
values
().
stream
()
.
mapToDouble
(
Double:
:
doubleValue
)
.
average
()
.
orElse
(
0
);
double
variance
=
machineUtilization
.
values
().
stream
()
.
mapToDouble
(
u
->
Math
.
pow
(
u
-
avgUtilization
,
2
))
.
sum
()
/
chromosome
.
getMachines
().
size
();
// 方差越小,负载越均衡
return
1.0
/
(
1
+
variance
);
}
/**
* 补全:创建缓存键(核心逻辑,需与原 C# CreateCacheKey 一致)
* 思路:将染色体的核心特征(机器选择+工序排序)拼接为字符串,确保相同染色体生成相同键
*/
private
String
createCacheKey
(
Chromosome
chromosome
)
{
// 拼接机器选择:用 "," 分隔(例:1,3,2,4)
String
machineStr
=
chromosome
.
getMachineStr
();
// 拼接工序排序:用 "|" 分隔(例:2|1|3|4)
String
operationStr
=
chromosome
.
getOperationStr
();
// 组合最终键(用 "_" 分隔两部分,避免冲突)
return
machineStr
+
"_"
+
operationStr
;
}
}
src/main/java/com/aps/service/Algorithm/GeneticOperations.java
0 → 100644
View file @
e45bc27d
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.common.util.ProductionDeepCopyUtil
;
import
com.aps.entity.Algorithm.Chromosome
;
import
com.aps.entity.Algorithm.GlobalOperationInfo
;
import
com.aps.entity.Algorithm.OperationSequencingWeight
;
import
com.aps.entity.Algorithm.Pair
;
import
com.aps.entity.basic.Entry
;
import
com.aps.entity.basic.GlobalParam
;
import
com.aps.entity.basic.MachineOption
;
import
java.util.*
;
import
java.util.stream.Collectors
;
/**
* 作者:佟礼
* 时间:2025-11-24
*/
public
class
GeneticOperations
{
private
final
Random
rnd
=
new
Random
();
private
static
List
<
Entry
>
allOperations
;
public
GeneticOperations
(
List
<
Entry
>
allOperations
)
{
GeneticOperations
.
allOperations
=
allOperations
;
}
/**
* 锦标赛选择
*/
public
List
<
Chromosome
>
tournamentSelection
(
List
<
Chromosome
>
population
,
int
tournamentSize
)
{
List
<
Chromosome
>
selected
=
new
ArrayList
<>();
while
(
selected
.
size
()
<
population
.
size
())
{
// 随机选择k个个体
// List<Chromosome> candidates = population.stream()
// .sorted((a, b) -> rnd.nextInt())
// .limit(tournamentSize)
// .collect(Collectors.toList());
// 步骤1: 创建一个临时列表用于存放候选者
List
<
Chromosome
>
candidates
=
new
ArrayList
<>(
population
);
// 步骤2: 随机打乱候选者列表
Collections
.
shuffle
(
candidates
,
rnd
);
// 步骤3: 截取前 tournamentSize 个元素作为本次锦标赛的参与者
if
(
tournamentSize
<
candidates
.
size
())
{
candidates
=
candidates
.
subList
(
0
,
tournamentSize
);
}
// 步骤4: 从参与者中选择适应度最高的个体
Chromosome
best
=
candidates
.
stream
()
.
max
(
Comparator
.
comparingDouble
(
Chromosome:
:
getFitness
))
.
orElseThrow
(()
->
new
IllegalStateException
(
"候选列表为空,无法选择最佳个体。"
));
// 复制个体(假设Chromosome有复制方法或通过构造函数复制)
selected
.
add
(
ProductionDeepCopyUtil
.
deepCopy
(
best
));
}
return
selected
;
}
// 重载,使用默认锦标赛大小3
public
List
<
Chromosome
>
tournamentSelection
(
List
<
Chromosome
>
population
)
{
return
tournamentSelection
(
population
,
3
);
}
/**
* 改进POX交叉(工序排序部分)
*/
public
Pair
<
Chromosome
,
Chromosome
>
poxCrossover
(
Chromosome
parent1
,
Chromosome
parent2
,
int
orderCount
)
{
// 步骤1:随机划分工件集
List
<
Integer
>
jobset1
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
orderCount
;
i
++)
{
jobset1
.
add
(
i
);
}
Collections
.
shuffle
(
jobset1
,
rnd
);
jobset1
=
jobset1
.
subList
(
0
,
orderCount
/
2
);
Set
<
Integer
>
jobset1Set
=
new
HashSet
<>(
jobset1
);
List
<
Integer
>
jobset2
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
orderCount
;
i
++)
{
if
(!
jobset1Set
.
contains
(
i
))
{
jobset2
.
add
(
i
);
}
}
// 步骤2:生成子代1
List
<
Integer
>
child1Os
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
parent1
.
getOperationSequencing
().
size
();
i
++)
{
int
op1
=
parent1
.
getOperationSequencing
().
get
(
i
);
int
op2
=
parent2
.
getOperationSequencing
().
get
(
i
);
if
(
jobset1Set
.
contains
(
op1
)
||
jobset1Set
.
contains
(
op2
))
{
child1Os
.
add
(
op1
);
}
else
{
child1Os
.
add
(
op2
);
}
}
// 步骤3:生成子代2
List
<
Integer
>
child2Os
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
parent2
.
getOperationSequencing
().
size
();
i
++)
{
int
op1
=
parent1
.
getOperationSequencing
().
get
(
i
);
int
op2
=
parent2
.
getOperationSequencing
().
get
(
i
);
if
(
jobset1Set
.
contains
(
op2
)
||
jobset1Set
.
contains
(
op1
))
{
child2Os
.
add
(
op2
);
}
else
{
child2Os
.
add
(
op1
);
}
}
// 机器选择部分交叉
List
<
Integer
>
child1Ms
=
new
ArrayList
<>();
List
<
Integer
>
child2Ms
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
parent1
.
getMachineSelection
().
size
();
i
++)
{
int
m1
=
parent1
.
getMachineSelection
().
get
(
i
);
int
m2
=
parent2
.
getMachineSelection
().
get
(
i
);
if
(
rnd
.
nextDouble
()
<
0.5
)
{
child1Ms
.
add
(
m1
);
child2Ms
.
add
(
m2
);
}
else
{
child1Ms
.
add
(
m2
);
child2Ms
.
add
(
m1
);
}
}
Chromosome
child1
=
new
Chromosome
();
child1
.
setMachineSelection
(
child1Ms
);
child1
.
setOperationSequencing
(
child1Os
);
Chromosome
child2
=
new
Chromosome
();
child2
.
setMachineSelection
(
child2Ms
);
child2
.
setOperationSequencing
(
child2Os
);
return
new
Pair
<>(
child1
,
child2
);
}
/**
* 变异操作
*/
public
void
mutate
(
Chromosome
chromosome
,
List
<
GlobalOperationInfo
>
globalOpList
,
double
baseMutationProb
)
{
// 1. 机器选择部分变异
mutateMachineSelection
(
chromosome
,
globalOpList
,
baseMutationProb
);
// 2. 工序排序部分变异
mutateOperationSequencing
(
chromosome
);
}
// 重载,使用默认变异概率
public
void
mutate
(
Chromosome
chromosome
,
List
<
GlobalOperationInfo
>
globalOpList
)
{
mutate
(
chromosome
,
globalOpList
,
0.1
);
}
private
void
mutateMachineSelection
(
Chromosome
chromosome
,
List
<
GlobalOperationInfo
>
globalOpList
,
double
baseMutationProb
)
{
// 计算变异位置数量
int
r
=
Math
.
max
(
1
,
(
int
)
(
chromosome
.
getMachineSelection
().
size
()
*
baseMutationProb
));
int
i
=
0
;
while
(
i
<
r
)
{
int
pos
=
rnd
.
nextInt
(
chromosome
.
getMachineSelection
().
size
());
// 获取对应工序
GlobalOperationInfo
opInfo
=
globalOpList
.
get
(
pos
);
Entry
operation
=
opInfo
.
getOp
();
int
machineSeq
=
chromosome
.
getMachineSelection
().
get
(
pos
);
if
(
operation
.
getMachineOptions
().
size
()
==
1
)
{
continue
;
}
else
{
// 选择当前所选设备外最短加工时间的机器
List
<
MachineOption
>
optionalMachines
=
operation
.
getMachineOptions
();
// 找到当前选中的机器
MachineOption
currentMachine
=
optionalMachines
.
get
(
machineSeq
-
1
);
// 筛选其他机器并找到加工时间最短的
MachineOption
minLoadMachine
=
optionalMachines
.
stream
()
.
filter
(
m
->
m
.
getMachineId
()
!=
currentMachine
.
getMachineId
())
.
min
(
Comparator
.
comparingInt
(
m
->
m
.
getProcessingTime
()))
.
orElse
(
currentMachine
);
// 如果没有其他机器,保持当前
machineSeq
=
optionalMachines
.
indexOf
(
minLoadMachine
)
+
1
;
chromosome
.
getMachineSelection
().
set
(
pos
,
machineSeq
);
i
++;
}
}
}
/**
* 低优先级优先的工序排序变异
*/
private
void
mutateOperationSequencing
(
Chromosome
chromosome
)
{
Random
rnd
=
new
Random
();
// 选择变异方式(交换或反转)
if
(
rnd
.
nextDouble
()
<
0.5
)
{
// 交换:优先选择高优先级工序的索引
OperationSequencingWeight
os1
=
selectHighPriorityIndex
(
chromosome
.
getOperationSequencing
(),
0
);
int
idx1
=
os1
.
getIndex
();
OperationSequencingWeight
os2
=
selectHighPriorityIndex
(
chromosome
.
getOperationSequencing
(),
os1
.
getWeight
());
int
idx2
=
os2
.
getIndex
();
// 交换位置
List
<
Integer
>
os
=
chromosome
.
getOperationSequencing
();
Collections
.
swap
(
os
,
idx1
,
idx2
);
}
else
{
// 反转:仅对高优先级工序集中的子序列反转
OperationSequencingWeight
osStart
=
selectHighPriorityIndex
(
chromosome
.
getOperationSequencing
(),
0
);
int
start
=
osStart
.
getIndex
();
OperationSequencingWeight
osEnd
=
selectHighPriorityIndex
(
chromosome
.
getOperationSequencing
(),
osStart
.
getWeight
());
int
end
=
osEnd
.
getIndex
();
if
(
start
>
end
)
{
int
temp
=
start
;
start
=
end
;
end
=
temp
;
}
// 执行反转
List
<
Integer
>
os
=
chromosome
.
getOperationSequencing
();
for
(
int
i
=
0
;
i
<
(
end
-
start
+
1
)
/
2
;
i
++)
{
int
pos1
=
start
+
i
;
int
pos2
=
end
-
i
;
Collections
.
swap
(
os
,
pos1
,
pos2
);
}
}
}
/**
* 优先选择高优先级工序的索引(用于变异)
*/
private
OperationSequencingWeight
selectHighPriorityIndex
(
List
<
Integer
>
os
,
int
weight
)
{
Random
rnd
=
new
Random
();
List
<
OperationSequencingWeight
>
indexWeights
=
CommonCalculator
.
getOsw
(
os
,
allOperations
);
if
(!
GlobalParam
.
IsBreakPriority
)
{
if
(
weight
==
0
)
{
return
indexWeights
.
get
(
rnd
.
nextInt
(
indexWeights
.
size
()));
}
else
{
List
<
OperationSequencingWeight
>
filtered
=
indexWeights
.
stream
()
.
filter
(
t
->
t
.
getWeight
()
==
weight
)
.
collect
(
Collectors
.
toList
());
return
filtered
.
get
(
rnd
.
nextInt
(
filtered
.
size
()));
}
}
return
indexWeights
.
get
(
CommonCalculator
.
getOsIndex
(
indexWeights
));
}
}
src/main/java/com/aps/service/Algorithm/Initialization.java
View file @
e45bc27d
...
...
@@ -43,33 +43,42 @@ public class Initialization {
*/
public
List
<
Chromosome
>
generateInitialPopulation
(
ScheduleParams
param
,
List
<
GlobalOperationInfo
>
globalOpList
)
{
List
<
Chromosome
>
population
=
new
ArrayList
<>();
// 按比例生成不同类型个体(GS:40%, LS:40%, RS:20%)
int
gsCount
=
(
int
)
(
param
.
getPopulationSize
()
*
param
.
getGsRatio
());
int
lsCount
=
(
int
)
(
param
.
getPopulationSize
()
*
param
.
getLsRatio
());
int
lsCount
=
gsCount
+
(
int
)
(
param
.
getPopulationSize
()
*
param
.
getLsRatio
());
int
rsCount
=
param
.
getPopulationSize
()
-
gsCount
-
lsCount
;
int
populationSize
=
param
.
getPopulationSize
();
// 并行循环:对应 Parallel.For(0, PopulationSize, i => { ... })
IntStream
.
range
(
0
,
populationSize
)
.
parallel
()
// 开启并行
.
forEach
(
i
->
{
Chromosome
chromo
=
new
Chromosome
();
// 初始化染色体
// 全局选择(GS):按GlobalOpId顺序生成MachineSelection
for
(
int
i
=
0
;
i
<
gsCount
;
i
++)
{
population
.
add
(
generateGSChromosome
(
globalOpList
,
param
.
getMachineCount
()));
}
if
(
i
<
gsCount
)
{
generateGSChromosome
(
chromo
,
globalOpList
);
// 对应 C# GenerateGSChromosome
}
else
if
(
i
<
lsCount
)
{
// 局部选择(LS):按GlobalOpId顺序生成MachineSelection(仅负载计算范围不同)
for
(
int
i
=
0
;
i
<
lsCount
;
i
++)
{
population
.
add
(
generateLSChromosome
(
globalOpList
,
param
.
getMachineCount
()));
}
generateLSChromosome
(
chromo
,
globalOpList
);
}
else
{
// 随机选择(RS):按GlobalOpId顺序生成MachineSelection(仅机器选择随机)
for
(
int
i
=
0
;
i
<
rsCount
;
i
++)
{
population
.
add
(
generateRSChromosome
(
globalOpList
,
param
.
getMachineCount
()));
generateRSChromosome
(
chromo
,
globalOpList
);
}
population
.
add
(
chromo
);
// 赋值到数组,线程安全(数组索引唯一)
});
return
population
;
}
/**
* 全局选择(GS)生成染色体(按GlobalOpId顺序生成MachineSelection)
*/
private
Chromosome
generateGSChromosome
(
List
<
GlobalOperationInfo
>
globalOpList
,
int
machineCount
)
{
Chromosome
chromosome
=
new
Chromosome
();
private
Chromosome
generateGSChromosome
(
Chromosome
chromosome
,
List
<
GlobalOperationInfo
>
globalOpList
)
{
Map
<
Integer
,
Integer
>
machineLoad
=
new
HashMap
<>();
// int[] machineLoad = new int[machineCount + 1]; // 设备负载(1-based,设备ID从1开始)
List
<
Integer
>
ms
=
new
ArrayList
<>();
// MachineSelection(顺序=GlobalOpId顺序)
...
...
@@ -106,7 +115,7 @@ public class Initialization {
.
findFirst
();
// 计算该机器在“可选机器列表”中的顺序号(1-based)
int
machineSeq
=
index
.
orElse
(
0
)
;
//findIndex(optionalMachines, minLoadMachine.getMachineId()) + 1;
int
machineSeq
=
index
.
orElse
(
0
)
+
1
;
//findIndex(optionalMachines, minLoadMachine.getMachineId()) + 1;
ms
.
add
(
machineSeq
);
// 更新设备负载
...
...
@@ -132,8 +141,9 @@ public class Initialization {
/**
* 局部选择(LS)生成染色体(按GlobalOpId顺序,每个订单重新初始化负载)
*/
private
Chromosome
generateLSChromosome
(
List
<
GlobalOperationInfo
>
globalOpList
,
int
machineCount
)
{
Chromosome
chromosome
=
new
Chromosome
();
private
Chromosome
generateLSChromosome
(
Chromosome
chromosome
,
List
<
GlobalOperationInfo
>
globalOpList
)
{
List
<
Integer
>
ms
=
new
ArrayList
<>();
List
<
Integer
>
os
=
new
ArrayList
<>();
Random
rnd
=
new
Random
();
...
...
@@ -199,8 +209,9 @@ public class Initialization {
/**
* 随机选择(RS)生成染色体(按GlobalOpId顺序,随机选择机器)
*/
private
Chromosome
generateRSChromosome
(
List
<
GlobalOperationInfo
>
globalOpList
,
int
machineCount
)
{
Chromosome
chromosome
=
new
Chromosome
();
private
Chromosome
generateRSChromosome
(
Chromosome
chromosome
,
List
<
GlobalOperationInfo
>
globalOpList
)
{
List
<
Integer
>
ms
=
new
ArrayList
<>();
List
<
Integer
>
os
=
new
ArrayList
<>();
Random
rnd
=
new
Random
();
...
...
src/main/java/com/aps/service/Algorithm/MachineCalculator.java
0 → 100644
View file @
e45bc27d
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.common.util.ProductionDeepCopyUtil
;
import
com.aps.entity.Algorithm.GAScheduleResult
;
import
com.aps.entity.Algorithm.ScheduleResultDetail
;
import
com.aps.entity.basic.*
;
import
com.aps.service.plan.MachineSchedulerService
;
import
com.baomidou.mybatisplus.core.toolkit.StringUtils
;
import
java.time.LocalDateTime
;
import
java.time.format.DateTimeFormatter
;
import
java.time.temporal.ChronoUnit
;
import
java.util.*
;
import
java.util.stream.Collectors
;
/**
* 作者:佟礼
* 时间:2025-11-24
*/
public
class
MachineCalculator
{
private
LocalDateTime
baseTime
;
private
final
List
<
Machine
>
machines
;
private
final
MachineSchedulerService
machineScheduler
;
public
MachineCalculator
(
LocalDateTime
baseTime
,
List
<
Machine
>
machines
,
MachineSchedulerService
machineScheduler
)
{
this
.
baseTime
=
baseTime
;
this
.
machines
=
machines
;
this
.
machineScheduler
=
machineScheduler
;
}
/**
* 获取机器下一个可用时间窗口(考虑班次约束)
*/
public
List
<
ScheduleResultDetail
>
getNextAvailableTime
(
Machine
machine
,
int
proposedStartTime
,
int
prevtime
,
double
processingTime
,
List
<
GAScheduleResult
>
existingTasks
,
boolean
isInterrupt
,
boolean
istask
,
boolean
islockMachineTime
)
{
LocalDateTime
startTime
=
baseTime
.
plus
(
proposedStartTime
,
ChronoUnit
.
MINUTES
);
String
prevtimestr
=
""
;
if
(
prevtime
>
-
1
)
{
prevtimestr
=
baseTime
.
plus
(
prevtime
,
ChronoUnit
.
MINUTES
)
.
format
(
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd HH:mm:ss"
));
}
// 查找合适的班次窗口
return
findEarliestStart
(
machine
,
processingTime
,
startTime
,
prevtimestr
,
existingTasks
,
istask
,
islockMachineTime
);
}
// 查找最早可用开始时间
private
List
<
ScheduleResultDetail
>
findEarliestStart
(
Machine
machine
,
double
processingTime
,
LocalDateTime
currentTime
,
String
prevtime
,
List
<
GAScheduleResult
>
existingTasks
,
boolean
checkprevtime
,
boolean
islockMachineTime
)
{
// 获取设备上已有任务
List
<
GAScheduleResult
>
machineTasks
=
existingTasks
.
stream
()
.
filter
(
t
->
t
.
getMachineId
()
==
machine
.
getId
())
.
sorted
(
Comparator
.
comparingInt
(
GAScheduleResult:
:
getStartTime
))
.
collect
(
Collectors
.
toList
());
List
<
ScheduleResultDetail
>
times
=
new
ArrayList
<>();
TimeSegment
slot
=
GetCurrentOrNextShift
(
machine
,
currentTime
,
prevtime
,
checkprevtime
);
LocalDateTime
startCandidate
=
slot
.
getStart
().
isAfter
(
(
prevtime
.
isEmpty
()
?
currentTime
:
LocalDateTime
.
parse
(
prevtime
,
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd HH:mm:ss"
)))
)
?
slot
.
getStart
()
:
(
prevtime
.
isEmpty
()
?
currentTime
:
LocalDateTime
.
parse
(
prevtime
,
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd HH:mm:ss"
)));
LocalDateTime
endCandidate
=
startCandidate
.
plus
((
long
)
processingTime
,
ChronoUnit
.
MINUTES
);
// 检查是否在可用时间段内
if
(
endCandidate
.
isAfter
(
slot
.
getEnd
()))
{
// 放不下,继续寻找后续可用时间
return
CaldEarliestStart
(
machine
,
processingTime
,
currentTime
,
prevtime
,
machineTasks
,
checkprevtime
,
islockMachineTime
);
}
else
{
ScheduleResultDetail
time
=
new
ScheduleResultDetail
();
time
.
setKey
(
slot
.
getKey
());
time
.
setStartTime
((
int
)
ChronoUnit
.
MINUTES
.
between
(
baseTime
,
startCandidate
));
time
.
setEndTime
((
int
)
ChronoUnit
.
MINUTES
.
between
(
baseTime
,
endCandidate
));
times
.
add
(
time
);
if
(
islockMachineTime
)
{
RemoveMachineAvailable
(
machine
,
time
);
}
return
times
;
}
}
private
List
<
ScheduleResultDetail
>
CaldEarliestStart
(
Machine
machine
,
double
processingTime
,
LocalDateTime
currentTime
,
String
prevtime
,
List
<
GAScheduleResult
>
machineTasks
,
boolean
checkprevtime
,
boolean
islockMachineTime
)
{
double
remainingTime
=
processingTime
;
LocalDateTime
st
=
StringUtils
.
isEmpty
(
prevtime
)
?
currentTime
:
LocalDateTime
.
parse
(
prevtime
);
LocalDateTime
prevEnd
=
LocalDateTime
.
of
(
2000
,
1
,
1
,
0
,
0
,
0
);
List
<
ScheduleResultDetail
>
times
=
new
ArrayList
<>();
List
<
ScheduleResultDetail
>
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
(
ProductionDeepCopyUtil
.
deepCopyList
(
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
);
// 处理当前班次
double
processable
=
Math
.
min
(
remainingTime
,
(
int
)
availableMinutes
);
remainingTime
-=
processable
;
currentTime
=
effectiveStart
.
plusMinutes
((
long
)
processable
);
// 添加时间详情
ScheduleResultDetail
time
=
new
ScheduleResultDetail
();
time
.
setKey
(
shift
.
getKey
());
time
.
setStartTime
((
int
)
ChronoUnit
.
MINUTES
.
between
(
baseTime
,
effectiveStart
));
time
.
setEndTime
((
int
)
ChronoUnit
.
MINUTES
.
between
(
baseTime
,
currentTime
));
times
.
add
(
time
);
if
(
islockMachineTime
)
{
RemoveMachineAvailable
(
machine
,
time
);
}
}
if
(
islockMachineTime
)
{
// 还原未使用的时间段
AddMachineAvailable
(
machine
,
oldTimes
);
}
return
times
;
}
/**
* 获取设备当前或下一个有效班次
*/
private
TimeSegment
GetCurrentOrNextShift
(
Machine
machine
,
LocalDateTime
time
,
String
prevtime
,
boolean
checkprevtime
)
{
TimeSegment
start
=
null
;
// 查找有效班次
start
=
machine
.
getAvailability
().
stream
()
.
filter
(
slot
->
!
slot
.
isUsed
()
&&
slot
.
getType
()
!=
SegmentType
.
MAINTENANCE
)
.
filter
(
slot
->
slot
.
getStart
().
isAfter
(
time
)
||
slot
.
getEnd
().
isAfter
(
time
))
.
findFirst
()
.
orElse
(
null
);
if
(
start
==
null
)
{
// 生成新时间段
List
<
TimeSegment
>
timeSegments
=
machineScheduler
.
generateTimeSegment
(
machine
,
time
);
machine
.
getAvailability
().
addAll
(
timeSegments
);
// 更新设备时间线
Machine
originalMachine
=
machines
.
stream
()
.
filter
(
t
->
t
.
getId
()
==
machine
.
getId
())
.
findFirst
()
.
orElse
(
null
);
if
(
originalMachine
!=
null
)
{
MachineTimeline
timeline
=
machineScheduler
.
getOrCreateTimeline
(
originalMachine
);
}
// 递归查找
return
GetCurrentOrNextShift
(
machine
,
time
,
prevtime
,
checkprevtime
);
}
return
start
;
}
private
void
RemoveMachineAvailable
(
Machine
machine
,
ScheduleResultDetail
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
<
ScheduleResultDetail
>
geneDetails
)
{
if
(
geneDetails
==
null
||
geneDetails
.
isEmpty
())
return
;
for
(
ScheduleResultDetail
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
;
}
}
src/main/java/com/aps/service/plan/PlanResultService.java
View file @
e45bc27d
package
com
.
aps
.
service
.
plan
;
import
com.aps.common.util.FileHelper
;
import
com.aps.common.util.JsonFileReader
;
import
com.aps.controller.gantt.FileUploadController
;
import
com.aps.entity.Algorithm.Chromosome
;
import
com.aps.entity.Algorithm.GAScheduleResult
;
import
com.aps.entity.Algorithm.ScheduleParams
;
import
com.aps.entity.Algorithm.ScheduleResultDetail
;
import
com.aps.entity.basic.ScheduleChromosome
;
import
com.aps.entity.Schedule.GenVO
;
import
com.aps.entity.Schedule.MachineVO
;
...
...
@@ -13,9 +18,7 @@ import org.springframework.stereotype.Service;
import
java.io.IOException
;
import
java.math.BigDecimal
;
import
java.time.LocalDateTime
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.*
;
import
java.util.stream.Collectors
;
@Service
...
...
@@ -92,10 +95,9 @@ public class PlanResultService {
}
}
public
List
<
ScheduleChromosome
>
execute1
()
{
public
List
<
Chromosome
>
execute1
()
{
try
{
Entry
entry
=
new
Entry
();
// 1. 读取数据
List
<
Machine
>
machines
=
loadData
(
"machines.json"
,
Machine
.
class
);
...
...
@@ -122,6 +124,11 @@ public class PlanResultService {
}
}
}
ScheduleParams
param
=
new
ScheduleParams
();
param
.
setBaseTime
(
LocalDateTime
.
of
(
2025
,
10
,
1
,
0
,
0
,
0
));
param
.
setPopulationSize
(
50
);
param
.
setMaxIterations
(
100
);
// 创建节假日
List
<
Holiday
>
holidays
=
Arrays
.
asList
(
...
...
@@ -134,34 +141,118 @@ public class PlanResultService {
// 3. 创建调度服务
MachineSchedulerService
machineScheduler
=
new
MachineSchedulerService
(
holidays
,
LocalDateTime
.
of
(
2025
,
10
,
1
,
0
,
0
,
0
));
holidays
,
param
.
getBaseTime
(
));
// 4. 初始化机器时间线
for
(
Machine
machine
:
machines
)
{
MachineTimeline
timeline
=
machineScheduler
.
getOrCreateTimeline
(
machine
);
machine
.
setAvailability
(
timeline
.
getSegments
());
}
// 3. 构建订单-工序数据
List
<
Entry
>
allOperations
=
new
ArrayList
<>();
Random
rnd
=
new
Random
();
// 注意:此处变量声明但未使用,可根据实际需求保留或移除
int
id
=
1
;
for
(
Order
order
:
orders
)
{
// 假设products是一个List<Product>,根据Product的Id查找对应的产品
Product
product
=
products
.
stream
()
.
filter
(
p
->
p
.
getId
()
==
order
.
getProductId
())
.
findFirst
()
.
orElseThrow
(()
->
new
IllegalArgumentException
(
"未找到对应产品: "
+
order
.
getProductId
()));
int
sequence
=
1
;
for
(
Operation
o
:
product
.
getOperations
())
{
// 假设Product类有getOperations()方法返回工序列表
Entry
entry
=
new
Entry
();
entry
.
setId
(
id
);
entry
.
setGroupId
(
order
.
getId
());
entry
.
setSequence
(
sequence
);
entry
.
setMachineOptions
(
o
.
getMachineOptions
());
// 假设Operation类有获取机器选项的方法
entry
.
setPriority
(
order
.
getPriority
());
entry
.
setQuantity
(
order
.
getQuantity
());
// entry.setMaterialRequirements(o.getMaterialRequirements()); // 假设Operation类有获取物料需求的方法
if
(
sequence
!=
1
)
{
entry
.
getPrevEntryIds
().
add
(
id
-
1
);
// 假设Entry类有getPrevEntryIds()返回List<Integer>
}
allOperations
.
add
(
entry
);
sequence
++;
id
++;
}
}
// 5. 执行调度算法
GeneticAlgorithm
scheduler
=
new
GeneticAlgorithm
();
//new GeneticAlgorithm(products, machines, orders, machineScheduler);
List
<
ScheduleChromosome
>
scheduleChromosomes
=
null
;
scheduler
.
Run
();
GeneticAlgorithm
scheduler
=
new
GeneticAlgorithm
(
machines
,
orders
,
null
,
machineScheduler
);
//new GeneticAlgorithm(products, machines, orders, machineScheduler);
List
<
Chromosome
>
Chromosomes
=
scheduler
.
Run
(
param
,
allOperations
);
// 对调度结果按照 fitness 由高到低排序
scheduleChromosomes
.
sort
((
c1
,
c2
)
->
Double
.
compare
(
c2
.
getFitness
(),
c1
.
getFitness
()));
Chromosomes
.
forEach
(
this
::
WriteScheduleSummary
);
// 为每个 ScheduleChromosome 分配场景ID(基于排序后的位置)
for
(
int
i
=
0
;
i
<
scheduleChromosomes
.
size
();
i
++)
{
scheduleChromosomes
.
get
(
i
).
setSceneId
(
i
+
1
);
// 场景ID从1开始
}
return
scheduleChromosomes
;
return
Chromosomes
;
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"调度执行失败"
,
e
);
}
}
public
void
WriteScheduleSummary
(
Chromosome
schedule
)
{
// 写入日志
FileHelper
.
writeLogFile
(
String
.
format
(
"\n=== Schedule Summary === %f"
,
schedule
.
getFitness
()));
FileHelper
.
writeLogFile
(
String
.
format
(
"Operation: %s"
,
schedule
.
getOperationStr
()));
FileHelper
.
writeLogFile
(
String
.
format
(
"Makespan: %f minutes"
,
schedule
.
getMakespan
()));
FileHelper
.
writeLogFile
(
String
.
format
(
"Total Tardiness: %f hours"
,
schedule
.
getDelayTime
()));
FileHelper
.
writeLogFile
(
String
.
format
(
"Setup Time: %f minutes"
,
schedule
.
getTotalChangeoverTime
()));
FileHelper
.
writeLogFile
(
String
.
format
(
"Flow Time: %f minutes"
,
schedule
.
getTotalFlowTime
()));
FileHelper
.
writeLogFile
(
String
.
format
(
"Machine Load Balance: %.2f%%"
,
schedule
.
getMachineLoadStd
()
*
100
));
FileHelper
.
writeLogFile
(
"-------------------------"
);
// 按订单分组写入
Map
<
Integer
,
List
<
GAScheduleResult
>>
orderGroups
=
schedule
.
getResult
().
stream
()
.
collect
(
Collectors
.
groupingBy
(
GAScheduleResult:
:
getGroupId
));
for
(
Map
.
Entry
<
Integer
,
List
<
GAScheduleResult
>>
group
:
orderGroups
.
entrySet
())
{
List
<
GAScheduleResult
>
sortedJobs
=
group
.
getValue
().
stream
()
.
sorted
(
Comparator
.
comparingInt
(
GAScheduleResult:
:
getOperationId
))
.
collect
(
Collectors
.
toList
());
for
(
GAScheduleResult
job
:
sortedJobs
)
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
String
.
format
(
"[%d-%d]:[%s-%s] Order %d, Machine %d, Operation %d, Batch %d, processingTime %d"
,
job
.
getStartTime
(),
job
.
getEndTime
(),
ConvertTime
(
job
.
getStartTime
()),
ConvertTime
(
job
.
getEndTime
()),
job
.
getGroupId
(),
job
.
getMachineId
(),
job
.
getOperationId
(),
job
.
getQuantity
(),
job
.
getProcessingTime
()
));
// 追加基因详情
for
(
ScheduleResultDetail
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"
));
}
/**
* 加载数据,优先从上传文件夹加载,如果不存在则从resources加载
...
...
src/main/resources/application.yml
View file @
e45bc27d
...
...
@@ -23,23 +23,23 @@ spring:
# 默认数据源
strict
:
false
# 关闭严格模式
datasource
:
# MySQL数据源
mysql
:
driver-class-name
:
com.mysql.cj.jdbc.Driver
url
:
jdbc:mysql://192.168.0.181:3310/mes?useSSL=false&serverTimezone=UTC
username
:
root
# 替换为你的MySQL用户名
password
:
root_mes@123456~
# 替换为你的MySQL密码
#
# MySQL数据源
#
mysql:
#
driver-class-name: com.mysql.cj.jdbc.Driver
#
url: jdbc:mysql://192.168.0.181:3310/mes?useSSL=false&serverTimezone=UTC
#
username: root # 替换为你的MySQL用户名
#
password: root_mes@123456~ # 替换为你的MySQL密码
# Oracle数据源
oracle
:
driver-class-name
:
oracle.jdbc.OracleDriver
url
:
jdbc:oracle:thin:@//
192.168.0.181:152
2/ORCLPDB1
# ORCL为你的Oracle实例名
url
:
jdbc:oracle:thin:@//
39.100.78.207:700
2/ORCLPDB1
# ORCL为你的Oracle实例名
username
:
mes
# 替换为你的Oracle用户名
password
:
root_mes123456
# 替换为你的Oracle密码
sqlserver
:
driver-class-name
:
com.microsoft.sqlserver.jdbc.SQLServerDriver
url
:
jdbc:sqlserver://192.168.0.181:1434;databaseName=mes;encrypt=false
username
:
sa
password
:
root_mes123456
#
sqlserver:
#
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
#
url: jdbc:sqlserver://192.168.0.181:1434;databaseName=mes;encrypt=false
#
username: sa
#
password: root_mes123456
# 文件上传配置
...
...
src/main/resources/machines.json
View file @
e45bc27d
...
...
@@ -5,6 +5,8 @@
"startTime"
:
"08:00:00"
,
"endTime"
:
"18:00:00"
,
"days"
:
[
1
,
2
,
3
,
4
,
5
],
"startDate"
:
"2025-10-10T00:00:00"
,
"endDate"
:
"2025-11-10T00:00:00"
,
"shiftDate"
:
"0001-01-01T00:00:00"
,
"temporaryShift"
:
false
,
"priority"
:
0
,
...
...
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