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
85fa9adb
Commit
85fa9adb
authored
May 27, 2026
by
DESKTOP-VKRD9QF\Administration
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
http://39.100.78.207:1213/tongli/hyh.apsj
parents
59ee6455
8cd195f9
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
1217 additions
and
11 deletions
+1217
-11
pom.xml
pom.xml
+6
-0
ScheduleParams.java
src/main/java/com/aps/entity/Algorithm/ScheduleParams.java
+7
-0
Entry.java
src/main/java/com/aps/entity/basic/Entry.java
+5
-0
CpSatFjspModel.java
src/main/java/com/aps/service/Algorithm/CpSatFjspModel.java
+417
-0
CpSatInitializer.java
...main/java/com/aps/service/Algorithm/CpSatInitializer.java
+268
-0
GeneticDecoder.java
src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
+15
-6
HybridAlgorithm.java
src/main/java/com/aps/service/Algorithm/HybridAlgorithm.java
+3
-1
Initialization.java
src/main/java/com/aps/service/Algorithm/Initialization.java
+162
-0
OperationSplitService.java
...java/com/aps/service/Algorithm/OperationSplitService.java
+329
-0
RoutingDataService.java
...in/java/com/aps/service/Algorithm/RoutingDataService.java
+1
-0
PlanResultService.java
src/main/java/com/aps/service/plan/PlanResultService.java
+3
-3
PlanResultServiceTest.java
src/test/java/com/aps/demo/PlanResultServiceTest.java
+1
-1
No files found.
pom.xml
View file @
85fa9adb
...
@@ -111,6 +111,12 @@
...
@@ -111,6 +111,12 @@
<artifactId>
hutool-all
</artifactId>
<artifactId>
hutool-all
</artifactId>
<version>
5.8.16
</version>
<version>
5.8.16
</version>
</dependency>
</dependency>
<!-- OR-Tools CP-SAT 约束规划求解器 -->
<dependency>
<groupId>
com.google.ortools
</groupId>
<artifactId>
ortools-java
</artifactId>
<version>
9.7.2996
</version>
</dependency>
</dependencies>
</dependencies>
<build>
<build>
...
...
src/main/java/com/aps/entity/Algorithm/ScheduleParams.java
View file @
85fa9adb
...
@@ -74,6 +74,13 @@ public class ScheduleParams {
...
@@ -74,6 +74,13 @@ public class ScheduleParams {
return
populationSize
;
return
populationSize
;
}
}
/// <summary>
/// 种群规模
/// </summary>
public
void
setPopulationSize
(
int
populationSize
)
{
populationSize
=
populationSize
;
}
/// <summary>
/// <summary>
/// 最大迭代次数
/// 最大迭代次数
/// </summary>
/// </summary>
...
...
src/main/java/com/aps/entity/basic/Entry.java
View file @
85fa9adb
...
@@ -64,6 +64,11 @@ public class Entry {
...
@@ -64,6 +64,11 @@ public class Entry {
*/
*/
private
String
mainId
;
private
String
mainId
;
/**
* 拆分来源ID:当state=1时,记录原始工序的id,用于识别同一原始工序的拆分子工序
*/
private
transient
Integer
splitSourceId
;
/**
/**
* 离散参数
* 离散参数
*/
*/
...
...
src/main/java/com/aps/service/Algorithm/CpSatFjspModel.java
0 → 100644
View file @
85fa9adb
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.entity.Algorithm.Chromosome
;
import
com.aps.entity.Algorithm.GlobalOperationInfo
;
import
com.aps.entity.basic.Entry
;
import
com.aps.entity.basic.Machine
;
import
com.aps.entity.basic.MachineOption
;
import
com.google.ortools.Loader
;
import
com.google.ortools.sat.CpModel
;
import
com.google.ortools.sat.CpSolver
;
import
com.google.ortools.sat.CpSolverStatus
;
import
com.google.ortools.sat.IntVar
;
import
com.google.ortools.sat.IntervalVar
;
import
com.google.ortools.sat.LinearArgument
;
import
com.google.ortools.sat.LinearExpr
;
import
com.google.ortools.sat.Literal
;
import
java.time.LocalDateTime
;
import
java.util.*
;
/**
* 基于 OR-Tools CP-SAT 的柔性作业车间调度(FJSP)建模器
* 用于生成高质量的初始染色体编码(machineSelection + operationSequencing)
*
* 建模方式:每个工序使用 Literal (BoolVar) 表示选择哪台机器,
* 通过 optional IntervalVar + NoOverlap 约束保证机器容量。
*
* 作者:佟礼
* 时间:2026-05-21
*/
public
class
CpSatFjspModel
{
private
static
boolean
nativeLibAvailable
=
false
;
static
{
try
{
Loader
.
loadNativeLibraries
();
nativeLibAvailable
=
true
;
}
catch
(
Exception
ignored
)
{
System
.
err
.
println
(
"[CP-SAT] 原生库加载失败,将回退到启发式初始化: "
+
ignored
.
getMessage
());
}
}
public
static
boolean
isAvailable
()
{
return
nativeLibAvailable
;
}
private
final
List
<
Machine
>
machines
;
private
final
List
<
GlobalOperationInfo
>
globalOpList
;
private
final
List
<
Entry
>
allOperations
;
private
final
int
operationCount
;
private
CpModel
model
;
private
final
List
<
IntVar
>
startVars
=
new
ArrayList
<>();
private
final
List
<
IntVar
>
endVars
=
new
ArrayList
<>();
private
final
List
<
Literal
[]>
presenceMatrix
=
new
ArrayList
<>();
private
final
Map
<
Long
,
List
<
IntervalVar
>>
machineToIntervals
=
new
LinkedHashMap
<>();
private
IntVar
makespanVar
;
private
int
horizonSeconds
;
public
CpSatFjspModel
(
List
<
GlobalOperationInfo
>
globalOpList
,
List
<
Entry
>
allOperations
,
List
<
Machine
>
machines
,
LocalDateTime
baseTime
)
{
this
.
globalOpList
=
globalOpList
;
this
.
allOperations
=
allOperations
;
this
.
machines
=
machines
;
this
.
operationCount
=
globalOpList
.
size
();
}
private
int
estimateHorizon
()
{
double
totalProcessing
=
0
;
for
(
GlobalOperationInfo
gop
:
globalOpList
)
{
Entry
op
=
gop
.
getOp
();
if
(
op
.
getMachineOptions
()
!=
null
&&
!
op
.
getMachineOptions
().
isEmpty
())
{
double
avgTime
=
op
.
getMachineOptions
().
stream
()
.
mapToDouble
(
MachineOption:
:
getProcessingTime
)
.
average
()
.
orElse
(
0
);
totalProcessing
+=
avgTime
;
}
}
int
machineCount
=
Math
.
max
(
machines
.
size
(),
1
);
return
Math
.
max
((
int
)
Math
.
ceil
(
totalProcessing
*
4.0
/
machineCount
)
+
86400
,
3600
);
}
/**
* 构建 FJSP 的 CP-SAT 模型(创建新模型实例)
*/
public
CpModel
build
()
{
model
=
new
CpModel
();
startVars
.
clear
();
endVars
.
clear
();
presenceMatrix
.
clear
();
machineToIntervals
.
clear
();
buildModelOnModel
(
model
);
return
model
;
}
/**
* 在指定 model 实例上构建全部变量和约束
*/
private
void
buildModelOnModel
(
CpModel
targetModel
)
{
this
.
model
=
targetModel
;
horizonSeconds
=
estimateHorizon
();
for
(
Machine
m
:
machines
)
{
machineToIntervals
.
put
(
m
.
getId
(),
new
ArrayList
<>());
}
// 1. 工序变量创建 + machineIndex → duration 映射
// 2. 同订单工序顺序(groupId + sequence)
// 3. Priority 硬约束
// ├── 按 distinct Priority 值分组(TreeMap 自动升序)
// ├── Priority=1 全部完成 → Priority=3 才能开始
// ├── Priority=3 全部完成 → Priority=5 才能开始
// └── ... 只需 (distinct数 - 1) 条约束
// 4. 机器容量 no_overlap
// 5. 目标函数:makespan + Priority 加权完工时间
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
Entry
op
=
globalOpList
.
get
(
i
).
getOp
();
List
<
MachineOption
>
options
=
op
.
getMachineOptions
();
if
(
options
==
null
||
options
.
isEmpty
())
{
throw
new
RuntimeException
(
"工序没有设置设备: "
+
op
.
getOrderCode
()
+
":"
+
op
.
getSequence
());
}
int
maxDuration
=
0
;
for
(
MachineOption
mo
:
options
)
{
maxDuration
=
Math
.
max
(
maxDuration
,
(
int
)
mo
.
getProcessingTime
());
}
IntVar
startVar
=
model
.
newIntVar
(
0
,
horizonSeconds
,
"s_"
+
i
);
IntVar
endVar
=
model
.
newIntVar
(
0
,
horizonSeconds
,
"e_"
+
i
);
IntervalVar
mainInterval
=
model
.
newIntervalVar
(
startVar
,
model
.
newConstant
(
maxDuration
),
endVar
,
"iv_"
+
i
);
Literal
[]
presences
=
new
Literal
[
options
.
size
()];
for
(
int
j
=
0
;
j
<
options
.
size
();
j
++)
{
MachineOption
mo
=
options
.
get
(
j
);
int
procTime
=
(
int
)
mo
.
getProcessingTime
();
Long
machineId
=
mo
.
getMachineId
();
presences
[
j
]
=
model
.
newBoolVar
(
"p_"
+
i
+
"_"
+
j
);
IntervalVar
optInterval
=
model
.
newOptionalIntervalVar
(
startVar
,
model
.
newConstant
(
procTime
),
endVar
,
presences
[
j
],
"opt_"
+
i
+
"_m"
+
machineId
);
List
<
IntervalVar
>
list
=
machineToIntervals
.
computeIfAbsent
(
machineId
,
k
->
new
ArrayList
<>());
list
.
add
(
optInterval
);
}
model
.
addBoolOr
(
presences
);
for
(
int
j
=
0
;
j
<
presences
.
length
;
j
++)
{
for
(
int
k
=
j
+
1
;
k
<
presences
.
length
;
k
++)
{
model
.
addBoolOr
(
new
Literal
[]{
presences
[
j
].
not
(),
presences
[
k
].
not
()});
}
}
startVars
.
add
(
startVar
);
endVars
.
add
(
endVar
);
presenceMatrix
.
add
(
presences
);
}
Map
<
Integer
,
List
<
Integer
>>
orderOpsMap
=
new
LinkedHashMap
<>();
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
orderOpsMap
.
computeIfAbsent
(
globalOpList
.
get
(
i
).
getGroupId
(),
k
->
new
ArrayList
<>()).
add
(
i
);
}
for
(
List
<
Integer
>
ops
:
orderOpsMap
.
values
())
{
ops
.
sort
(
Comparator
.
comparingInt
(
idx
->
globalOpList
.
get
(
idx
).
getSequence
()));
for
(
int
k
=
0
;
k
<
ops
.
size
()
-
1
;
k
++)
{
model
.
addLessOrEqual
(
endVars
.
get
(
ops
.
get
(
k
)),
startVars
.
get
(
ops
.
get
(
k
+
1
)));
}
}
Map
<
Double
,
List
<
Integer
>>
priorityGroups
=
new
TreeMap
<>();
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
double
p
=
globalOpList
.
get
(
i
).
getOp
().
getPriority
();
if
(
p
>
0
)
{
priorityGroups
.
computeIfAbsent
(
p
,
k
->
new
ArrayList
<>()).
add
(
i
);
}
}
List
<
Double
>
sortedPriorities
=
new
ArrayList
<>(
priorityGroups
.
keySet
());
for
(
int
k
=
0
;
k
<
sortedPriorities
.
size
()
-
1
;
k
++)
{
List
<
Integer
>
hiPriOps
=
priorityGroups
.
get
(
sortedPriorities
.
get
(
k
));
List
<
Integer
>
loPriOps
=
priorityGroups
.
get
(
sortedPriorities
.
get
(
k
+
1
));
IntVar
hiMaxEnd
=
model
.
newIntVar
(
0
,
horizonSeconds
,
"hiEnd_"
+
k
);
IntVar
loMinStart
=
model
.
newIntVar
(
0
,
horizonSeconds
,
"loStart_"
+
k
);
for
(
int
idx
:
hiPriOps
)
{
model
.
addLessOrEqual
(
endVars
.
get
(
idx
),
hiMaxEnd
);
}
for
(
int
idx
:
loPriOps
)
{
model
.
addGreaterOrEqual
(
startVars
.
get
(
idx
),
loMinStart
);
}
model
.
addLessOrEqual
(
hiMaxEnd
,
loMinStart
);
}
for
(
Map
.
Entry
<
Long
,
List
<
IntervalVar
>>
entry
:
machineToIntervals
.
entrySet
())
{
List
<
IntervalVar
>
intervals
=
entry
.
getValue
();
if
(
intervals
.
size
()
>
1
)
{
model
.
addNoOverlap
(
intervals
.
toArray
(
new
IntervalVar
[
0
]));
}
}
makespanVar
=
model
.
newIntVar
(
0
,
horizonSeconds
,
"makespan"
);
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
model
.
addLessOrEqual
(
endVars
.
get
(
i
),
makespanVar
);
}
double
maxPriority
=
0
;
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
maxPriority
=
Math
.
max
(
maxPriority
,
globalOpList
.
get
(
i
).
getOp
().
getPriority
());
}
int
priorityTerms
=
0
;
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
if
(
globalOpList
.
get
(
i
).
getOp
().
getPriority
()
>
0
)
priorityTerms
++;
}
LinearArgument
[]
objVars
=
new
LinearArgument
[
1
+
priorityTerms
];
long
[]
objCoeffs
=
new
long
[
1
+
priorityTerms
];
objVars
[
0
]
=
makespanVar
;
objCoeffs
[
0
]
=
100
;
int
t
=
1
;
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
double
priority
=
globalOpList
.
get
(
i
).
getOp
().
getPriority
();
if
(
priority
>
0
)
{
objVars
[
t
]
=
endVars
.
get
(
i
);
objCoeffs
[
t
]
=
(
long
)(
maxPriority
-
priority
+
1
);
t
++;
}
}
model
.
minimize
(
LinearExpr
.
weightedSum
(
objVars
,
objCoeffs
));
}
/**
* 单次求解,返回一个 Chromosome
*/
public
Chromosome
solveOnce
(
int
timeLimitSeconds
)
{
if
(
model
==
null
)
{
build
();
}
CpSolver
solver
=
new
CpSolver
();
solver
.
getParameters
().
setMaxTimeInSeconds
(
timeLimitSeconds
);
solver
.
getParameters
().
setLogSearchProgress
(
false
);
solver
.
getParameters
().
setNumSearchWorkers
(
4
);
CpSolverStatus
status
=
solver
.
solve
(
model
);
if
(
status
==
CpSolverStatus
.
OPTIMAL
||
status
==
CpSolverStatus
.
FEASIBLE
)
{
return
extractChromosome
(
solver
);
}
return
null
;
}
/**
* 生成多个多样性解(通过随机种子 + 不同目标权重)
*/
public
List
<
Chromosome
>
generateDiverseSolutions
(
int
targetCount
,
int
timeBudgetSec
,
boolean
randomPerturb
)
{
List
<
Chromosome
>
results
=
new
ArrayList
<>();
if
(
targetCount
<=
0
)
return
results
;
Random
rnd
=
new
Random
();
int
perSolveTime
=
Math
.
max
(
timeBudgetSec
/
Math
.
min
(
targetCount
,
5
),
3
);
int
maxRounds
=
Math
.
min
(
targetCount
*
2
,
15
);
for
(
int
round
=
0
;
round
<
maxRounds
&&
results
.
size
()
<
targetCount
;
round
++)
{
model
=
new
CpModel
();
startVars
.
clear
();
endVars
.
clear
();
presenceMatrix
.
clear
();
machineToIntervals
.
clear
();
buildModelOnModel
(
model
);
if
(
round
%
3
==
1
&&
operationCount
>
0
)
{
double
maxPriority
=
0
;
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
maxPriority
=
Math
.
max
(
maxPriority
,
globalOpList
.
get
(
i
).
getOp
().
getPriority
());
}
int
priorityCount
=
0
;
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
if
(
globalOpList
.
get
(
i
).
getOp
().
getPriority
()
>
0
)
priorityCount
++;
}
int
totalTerms
=
1
+
operationCount
+
priorityCount
;
LinearArgument
[]
objVars
=
new
LinearArgument
[
totalTerms
];
long
[]
objCoeffs
=
new
long
[
totalTerms
];
objVars
[
0
]
=
makespanVar
;
objCoeffs
[
0
]
=
100
;
int
t
=
1
;
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
objVars
[
t
]
=
endVars
.
get
(
i
);
objCoeffs
[
t
]
=
1
;
t
++;
}
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
double
priority
=
globalOpList
.
get
(
i
).
getOp
().
getPriority
();
if
(
priority
>
0
)
{
objVars
[
t
]
=
endVars
.
get
(
i
);
objCoeffs
[
t
]
=
(
long
)(
maxPriority
-
priority
+
1
);
t
++;
}
}
model
.
minimize
(
LinearExpr
.
weightedSum
(
objVars
,
objCoeffs
));
}
CpSolver
solver
=
new
CpSolver
();
solver
.
getParameters
().
setMaxTimeInSeconds
(
perSolveTime
);
solver
.
getParameters
().
setLogSearchProgress
(
false
);
solver
.
getParameters
().
setNumSearchWorkers
(
4
);
solver
.
getParameters
().
setRandomSeed
((
int
)
(
round
*
1000L
+
rnd
.
nextInt
(
1000
)));
CpSolverStatus
status
=
solver
.
solve
(
model
);
if
(
status
==
CpSolverStatus
.
OPTIMAL
||
status
==
CpSolverStatus
.
FEASIBLE
)
{
Chromosome
chromo
=
extractChromosome
(
solver
);
if
(
chromo
!=
null
&&
!
containsDuplicate
(
results
,
chromo
))
{
chromo
.
setGsOrls
(
4
);
chromo
.
setGenerateType
(
"CP-SAT"
);
results
.
add
(
chromo
);
}
}
}
return
results
;
}
private
Chromosome
extractChromosome
(
CpSolver
solver
)
{
Chromosome
chromo
=
new
Chromosome
();
chromo
.
setGsOrls
(
4
);
chromo
.
setGenerateType
(
"CP-SAT"
);
List
<
Integer
>
ms
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
Literal
[]
pres
=
presenceMatrix
.
get
(
i
);
int
selectedIdx
=
0
;
for
(
int
j
=
0
;
j
<
pres
.
length
;
j
++)
{
if
(
solver
.
booleanValue
(
pres
[
j
]))
{
selectedIdx
=
j
;
break
;
}
}
ms
.
add
(
selectedIdx
+
1
);
}
chromo
.
setMachineSelection
(
ms
);
List
<
OpTimeRecord
>
records
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
GlobalOperationInfo
gop
=
globalOpList
.
get
(
i
);
int
machineIdx
=
ms
.
get
(
i
)
-
1
;
records
.
add
(
new
OpTimeRecord
(
gop
.
getGroupId
(),
gop
.
getSequence
(),
(
int
)
solver
.
value
(
startVars
.
get
(
i
)),
machineIdx
,
i
));
}
records
.
sort
(
Comparator
.
comparingInt
(
OpTimeRecord:
:
getStartTime
)
.
thenComparingInt
(
OpTimeRecord:
:
getMachineIdx
));
List
<
Integer
>
os
=
new
ArrayList
<>();
for
(
OpTimeRecord
rec
:
records
)
{
os
.
add
(
rec
.
getGroupId
());
}
chromo
.
setOperationSequencing
(
os
);
chromo
.
setMakespan
(
solver
.
value
(
makespanVar
));
return
chromo
;
}
private
boolean
containsDuplicate
(
List
<
Chromosome
>
list
,
Chromosome
candidate
)
{
String
candidateKey
=
candidate
.
getGeneStr
();
for
(
Chromosome
c
:
list
)
{
if
(
c
.
getGeneStr
().
equals
(
candidateKey
))
{
return
true
;
}
}
return
false
;
}
private
static
class
OpTimeRecord
{
private
final
int
groupId
;
private
final
int
sequence
;
private
final
int
startTime
;
private
final
int
machineIdx
;
private
final
int
globalIdx
;
OpTimeRecord
(
int
groupId
,
int
sequence
,
int
startTime
,
int
machineIdx
,
int
globalIdx
)
{
this
.
groupId
=
groupId
;
this
.
sequence
=
sequence
;
this
.
startTime
=
startTime
;
this
.
machineIdx
=
machineIdx
;
this
.
globalIdx
=
globalIdx
;
}
int
getGroupId
()
{
return
groupId
;
}
int
getSequence
()
{
return
sequence
;
}
int
getStartTime
()
{
return
startTime
;
}
int
getMachineIdx
()
{
return
machineIdx
;
}
}
}
\ No newline at end of file
src/main/java/com/aps/service/Algorithm/CpSatInitializer.java
0 → 100644
View file @
85fa9adb
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.common.util.FileHelper
;
import
com.aps.entity.Algorithm.Chromosome
;
import
com.aps.entity.Algorithm.GlobalOperationInfo
;
import
com.aps.entity.basic.Entry
;
import
com.aps.entity.basic.Machine
;
import
com.aps.entity.basic.MachineOption
;
import
com.aps.entity.basic.Order
;
import
java.time.LocalDateTime
;
import
java.util.*
;
import
java.util.stream.Collectors
;
/**
* CP-SAT 自适应初始种群生成器
* 根据工序规模自动选择策略,确保不阻塞主流程
*
* 策略分级:
* ≤100道 :CP-SAT 全力求解,生成多个高质量解
* 100~500道:CP-SAT 限时截断,配合启发式补充
* 500~800道:瓶颈分解 + 分组 CP-SAT
* >800道 :纯启发式 fallback
*
* 作者:佟礼
* 时间:2026-05-21
*/
public
class
CpSatInitializer
{
private
static
final
int
SMALL_SCALE_THRESHOLD
=
100
;
private
static
final
int
MEDIUM_SCALE_THRESHOLD
=
500
;
private
static
final
int
LARGE_SCALE_THRESHOLD
=
800
;
private
final
List
<
Entry
>
allOperations
;
private
final
List
<
Machine
>
machines
;
private
final
List
<
Order
>
orders
;
private
final
LocalDateTime
baseTime
;
public
CpSatInitializer
(
List
<
Entry
>
allOperations
,
List
<
Machine
>
machines
,
List
<
Order
>
orders
,
LocalDateTime
baseTime
)
{
this
.
allOperations
=
allOperations
;
this
.
machines
=
machines
;
this
.
orders
=
orders
;
this
.
baseTime
=
baseTime
;
}
/**
* 自适应生成初始种群(带 fallback)
*
* @param globalOpList 全局工序列表
* @param targetCount 目标 CP-SAT 解的数量
* @param timeBudgetSec 总时间预算(秒)
* @return CP-SAT 生成的 Chromosome 列表,失败时返回空列表
*/
public
List
<
Chromosome
>
generate
(
List
<
GlobalOperationInfo
>
globalOpList
,
int
targetCount
,
int
timeBudgetSec
)
{
if
(!
CpSatFjspModel
.
isAvailable
())
{
FileHelper
.
writeLogFile
(
"[CP-SAT] 原生库不可用,跳过"
);
return
Collections
.
emptyList
();
}
int
opCount
=
globalOpList
.
size
();
FileHelper
.
writeLogFile
(
String
.
format
(
"[CP-SAT] 工序数=%d, 目标解数=%d, 时间预算=%ds"
,
opCount
,
targetCount
,
timeBudgetSec
));
try
{
if
(
opCount
<=
SMALL_SCALE_THRESHOLD
)
{
return
smallScaleSolve
(
globalOpList
,
targetCount
,
timeBudgetSec
);
}
else
if
(
opCount
<=
MEDIUM_SCALE_THRESHOLD
)
{
return
mediumScaleSolve
(
globalOpList
,
targetCount
,
timeBudgetSec
);
}
else
if
(
opCount
<=
LARGE_SCALE_THRESHOLD
)
{
return
largeScaleDecomposed
(
globalOpList
,
targetCount
,
timeBudgetSec
);
}
else
{
FileHelper
.
writeLogFile
(
"[CP-SAT] 工序数超过阈值,跳过CP-SAT,回退启发式"
);
return
Collections
.
emptyList
();
}
}
catch
(
Exception
e
)
{
FileHelper
.
writeLogFile
(
"[CP-SAT] 求解异常,回退启发式: "
+
e
.
getMessage
());
return
Collections
.
emptyList
();
}
}
/**
* 小规模:全力求解多个高质量解
*/
private
List
<
Chromosome
>
smallScaleSolve
(
List
<
GlobalOperationInfo
>
globalOpList
,
int
targetCount
,
int
timeBudgetSec
)
{
CpSatFjspModel
model
=
new
CpSatFjspModel
(
globalOpList
,
allOperations
,
machines
,
baseTime
);
return
model
.
generateDiverseSolutions
(
Math
.
min
(
targetCount
,
8
),
timeBudgetSec
,
true
);
}
/**
* 中规模:限时截断,少生成几个解
*/
private
List
<
Chromosome
>
mediumScaleSolve
(
List
<
GlobalOperationInfo
>
globalOpList
,
int
targetCount
,
int
timeBudgetSec
)
{
CpSatFjspModel
model
=
new
CpSatFjspModel
(
globalOpList
,
allOperations
,
machines
,
baseTime
);
int
effectiveTarget
=
Math
.
min
(
targetCount
,
5
);
int
perSolveTime
=
Math
.
max
(
timeBudgetSec
,
15
);
return
model
.
generateDiverseSolutions
(
effectiveTarget
,
perSolveTime
,
true
);
}
/**
* 大规模:瓶颈分解策略
* 只对瓶颈机器上的工序使用 CP-SAT,其余用贪心分配
*/
private
List
<
Chromosome
>
largeScaleDecomposed
(
List
<
GlobalOperationInfo
>
globalOpList
,
int
targetCount
,
int
timeBudgetSec
)
{
List
<
Chromosome
>
results
=
new
ArrayList
<>();
int
effectiveTarget
=
Math
.
min
(
targetCount
,
3
);
// 1. 识别瓶颈机器:总加工负荷最高的机器
List
<
Long
>
bottleneckMachineIds
=
findBottleneckMachineIds
(
globalOpList
,
3
);
// 2. 分离瓶颈工序 vs 非瓶颈工序
List
<
GlobalOperationInfo
>
bottleneckOps
=
new
ArrayList
<>();
List
<
GlobalOperationInfo
>
nonBottleneckOps
=
new
ArrayList
<>();
for
(
GlobalOperationInfo
gop
:
globalOpList
)
{
Entry
op
=
gop
.
getOp
();
if
(
op
.
getMachineOptions
()
!=
null
)
{
boolean
isOnBottleneck
=
op
.
getMachineOptions
().
stream
()
.
anyMatch
(
mo
->
bottleneckMachineIds
.
contains
(
mo
.
getMachineId
()));
if
(
isOnBottleneck
)
{
bottleneckOps
.
add
(
gop
);
}
else
{
nonBottleneckOps
.
add
(
gop
);
}
}
else
{
nonBottleneckOps
.
add
(
gop
);
}
}
if
(
bottleneckOps
.
isEmpty
())
{
FileHelper
.
writeLogFile
(
"[CP-SAT] 未识别到瓶颈,回退中规模求解"
);
return
mediumScaleSolve
(
globalOpList
,
effectiveTarget
,
timeBudgetSec
);
}
FileHelper
.
writeLogFile
(
String
.
format
(
"[CP-SAT] 瓶颈分解: 瓶颈工序=%d, 非瓶颈工序=%d"
,
bottleneckOps
.
size
(),
nonBottleneckOps
.
size
()));
// 3. 对瓶颈工序用 CP-SAT
List
<
Machine
>
bottleneckMachines
=
machines
.
stream
()
.
filter
(
m
->
bottleneckMachineIds
.
contains
(
m
.
getId
()))
.
collect
(
Collectors
.
toList
());
if
(
bottleneckMachines
.
isEmpty
())
{
bottleneckMachines
=
machines
;
}
CpSatFjspModel
model
=
new
CpSatFjspModel
(
bottleneckOps
,
allOperations
,
bottleneckMachines
,
baseTime
);
List
<
Chromosome
>
cpSatResults
=
model
.
generateDiverseSolutions
(
effectiveTarget
,
timeBudgetSec
,
true
);
// 4. 对非瓶颈工序填入贪心分配
for
(
Chromosome
chromo
:
cpSatResults
)
{
fillGreedyForNonBottleneck
(
chromo
,
bottleneckOps
,
nonBottleneckOps
,
globalOpList
);
results
.
add
(
chromo
);
}
return
results
;
}
/**
* 找出负载最高的前 N 台瓶颈机器
*/
private
List
<
Long
>
findBottleneckMachineIds
(
List
<
GlobalOperationInfo
>
globalOpList
,
int
topN
)
{
Map
<
Long
,
Double
>
machineTotalLoad
=
new
LinkedHashMap
<>();
for
(
GlobalOperationInfo
gop
:
globalOpList
)
{
Entry
op
=
gop
.
getOp
();
if
(
op
.
getMachineOptions
()
!=
null
)
{
for
(
MachineOption
mo
:
op
.
getMachineOptions
())
{
machineTotalLoad
.
put
(
mo
.
getMachineId
(),
machineTotalLoad
.
getOrDefault
(
mo
.
getMachineId
(),
0.0
)
+
mo
.
getProcessingTime
());
}
}
}
return
machineTotalLoad
.
entrySet
().
stream
()
.
sorted
(
Map
.
Entry
.<
Long
,
Double
>
comparingByValue
().
reversed
())
.
limit
(
topN
)
.
map
(
Map
.
Entry
::
getKey
)
.
collect
(
Collectors
.
toList
());
}
/**
* 为非瓶颈工序填入贪心 machineSelection 和 operationSequencing
*/
private
void
fillGreedyForNonBottleneck
(
Chromosome
chromo
,
List
<
GlobalOperationInfo
>
bottleneckOps
,
List
<
GlobalOperationInfo
>
nonBottleneckOps
,
List
<
GlobalOperationInfo
>
fullGlobalOpList
)
{
if
(
nonBottleneckOps
.
isEmpty
())
return
;
Map
<
Long
,
Double
>
machineLoad
=
new
LinkedHashMap
<>();
Random
rnd
=
new
Random
();
// 按全局顺序收集所有工序的 MS 和 OS
List
<
Integer
>
fullMs
=
new
ArrayList
<>();
List
<
Integer
>
fullOs
=
new
ArrayList
<>();
int
bottleneckIdx
=
0
;
int
nonBottleneckIdx
=
0
;
List
<
Integer
>
cpSatMs
=
chromo
.
getMachineSelection
();
List
<
Integer
>
cpSatOs
=
chromo
.
getOperationSequencing
();
// 重建完整的 machineSelection(保持全局顺序)
for
(
GlobalOperationInfo
gop
:
fullGlobalOpList
)
{
boolean
isBottleneck
=
bottleneckOps
.
contains
(
gop
);
boolean
isNonBottleneck
=
nonBottleneckOps
.
contains
(
gop
);
if
(
isBottleneck
&&
bottleneckIdx
<
cpSatMs
.
size
())
{
fullMs
.
add
(
cpSatMs
.
get
(
bottleneckIdx
));
if
(
bottleneckIdx
<
cpSatOs
.
size
())
{
fullOs
.
add
(
cpSatOs
.
get
(
bottleneckIdx
));
}
bottleneckIdx
++;
}
else
if
(
isNonBottleneck
)
{
Entry
op
=
gop
.
getOp
();
List
<
MachineOption
>
options
=
op
.
getMachineOptions
();
int
selectedIdx
=
selectMinLoadMachine
(
options
,
machineLoad
,
rnd
);
fullMs
.
add
(
selectedIdx
+
1
);
MachineOption
selected
=
options
.
get
(
selectedIdx
);
machineLoad
.
put
(
selected
.
getMachineId
(),
machineLoad
.
getOrDefault
(
selected
.
getMachineId
(),
0.0
)
+
selected
.
getProcessingTime
());
fullOs
.
add
(
gop
.
getGroupId
());
nonBottleneckIdx
++;
}
}
chromo
.
setMachineSelection
(
fullMs
);
chromo
.
setOperationSequencing
(
fullOs
);
}
private
int
selectMinLoadMachine
(
List
<
MachineOption
>
options
,
Map
<
Long
,
Double
>
machineLoad
,
Random
rnd
)
{
double
minLoad
=
options
.
stream
()
.
mapToDouble
(
m
->
machineLoad
.
getOrDefault
(
m
.
getMachineId
(),
0.0
)
+
m
.
getProcessingTime
())
.
min
()
.
orElse
(
Double
.
MAX_VALUE
);
List
<
Integer
>
candidates
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
options
.
size
();
i
++)
{
MachineOption
mo
=
options
.
get
(
i
);
double
candidateLoad
=
machineLoad
.
getOrDefault
(
mo
.
getMachineId
(),
0.0
)
+
mo
.
getProcessingTime
();
if
(
Math
.
abs
(
candidateLoad
-
minLoad
)
<
0.01
)
{
candidates
.
add
(
i
);
}
}
return
candidates
.
get
(
rnd
.
nextInt
(
candidates
.
size
()));
}
}
\ No newline at end of file
src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
View file @
85fa9adb
...
@@ -597,7 +597,7 @@ public class GeneticDecoder {
...
@@ -597,7 +597,7 @@ public class GeneticDecoder {
//存储锚点时间
//存储锚点时间
if
(
isJit
)
if
(
isJit
)
{
{
orderSchedulingInfo
.
put
(
order
.
getId
(),
new
AbstractMap
.
SimpleEntry
<>(
fals
e
,
end
));
orderSchedulingInfo
.
put
(
order
.
getId
(),
new
AbstractMap
.
SimpleEntry
<>(
tru
e
,
end
));
}
else
{
}
else
{
orderSchedulingInfo
.
put
(
order
.
getId
(),
new
AbstractMap
.
SimpleEntry
<>(
false
,
0
));
orderSchedulingInfo
.
put
(
order
.
getId
(),
new
AbstractMap
.
SimpleEntry
<>(
false
,
0
));
...
@@ -1408,11 +1408,11 @@ public class GeneticDecoder {
...
@@ -1408,11 +1408,11 @@ public class GeneticDecoder {
int
bomtime
=
0
;
int
bomtime
=
0
;
if
(
needMaterialCheck
&&
islockMachineTime
)
{
if
(
needMaterialCheck
&&
islockMachineTime
)
{
if
(!
isJit
)
{
bomtime
=
getOperationBOMTime
(
operation
,
chromosome
,
earliestStartTime
,
2
);
bomtime
=
getOperationBOMTime
(
operation
,
chromosome
,
earliestStartTime
,
2
);
earliestStartTime
=
Math
.
max
(
earliestStartTime
,
bomtime
);
earliestStartTime
=
Math
.
max
(
earliestStartTime
,
bomtime
);
}
}
}
// 正式落排前,再取一次当前机台最后一道工序,保证换型计算基于最新排程结果。
// 正式落排前,再取一次当前机台最后一道工序,保证换型计算基于最新排程结果。
...
@@ -1524,7 +1524,16 @@ public class GeneticDecoder {
...
@@ -1524,7 +1524,16 @@ public class GeneticDecoder {
// }
// }
//扣库存
//扣库存
if
(
needMaterialCheck
&&
islockMachineTime
)
{
if
(
needMaterialCheck
&&
islockMachineTime
)
{
if
(!
isJit
)
{
EditOperationBOMTime
(
operation
,
chromosome
,
startTime
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
);
EditOperationBOMTime
(
operation
,
chromosome
,
startTime
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
);
}
else
{
int
bomtime1
=
getOperationBOMTime
(
operation
,
chromosome
,
startTime
,
2
);
if
(
bomtime1
<=
startTime
)
{
EditOperationBOMTime
(
operation
,
chromosome
,
startTime
,
machineTasksCache
,
entryIndexById
,
scheduleIndexById
);
}
}
}
}
//换型时间是否占用设备加工时间
//换型时间是否占用设备加工时间
//10:00 开始上班 前面的任务:24:00 结束 开始换型 休息时间 10小时
//10:00 开始上班 前面的任务:24:00 结束 开始换型 休息时间 10小时
...
...
src/main/java/com/aps/service/Algorithm/HybridAlgorithm.java
View file @
85fa9adb
...
@@ -117,7 +117,9 @@ public class HybridAlgorithm {
...
@@ -117,7 +117,9 @@ public class HybridAlgorithm {
// 步骤1:使用构造启发式算法生成初始种群
// 步骤1:使用构造启发式算法生成初始种群
FileHelper
.
writeLogFile
(
"构造启发式初始化-----------开始-------"
);
FileHelper
.
writeLogFile
(
"构造启发式初始化-----------开始-------"
);
List
<
Chromosome
>
population
=
initialization
.
generateHeuristicInitialPopulation
(
param
);
// List<Chromosome> population = initialization.generateHeuristicInitialPopulation(param);
List
<
Chromosome
>
population
=
initialization
.
generateHybridInitialPopulation
(
param
);
FileHelper
.
writeLogFile
(
"构造启发式初始化-----------结束-------"
);
FileHelper
.
writeLogFile
(
"构造启发式初始化-----------结束-------"
);
...
...
src/main/java/com/aps/service/Algorithm/Initialization.java
View file @
85fa9adb
package
com
.
aps
.
service
.
Algorithm
;
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.common.util.FileHelper
;
import
com.aps.common.util.ProductionDeepCopyUtil
;
import
com.aps.common.util.ProductionDeepCopyUtil
;
import
com.aps.entity.Algorithm.Chromosome
;
import
com.aps.entity.Algorithm.Chromosome
;
import
com.aps.entity.Algorithm.GlobalOperationInfo
;
import
com.aps.entity.Algorithm.GlobalOperationInfo
;
...
@@ -1071,6 +1072,167 @@ public class Initialization {
...
@@ -1071,6 +1072,167 @@ public class Initialization {
return
shuffled
;
return
shuffled
;
}
}
/**
* HybridAlgorithm 专用混合初始种群
* 先用 CP-SAT 生成高质量种子,剩余用原有构造启发式补足
*/
public
List
<
Chromosome
>
generateHybridInitialPopulation
(
ScheduleParams
param
)
{
int
populationSize
=
param
.
getPopulationSize
();
this
.
baseTime
=
param
.
getBaseTime
();
List
<
GlobalOperationInfo
>
sharedGlobalOpList
=
generateGlobalOpList
();
List
<
Chromosome
>
population
=
new
ArrayList
<>(
populationSize
);
int
cpSatTarget
=
Math
.
min
(
Math
.
max
(
populationSize
/
5
,
2
),
6
);
int
timeBudgetSec
=
Math
.
max
(
populationSize
*
2
,
15
);
CpSatInitializer
cpSatInit
=
new
CpSatInitializer
(
allOperations
,
machines
,
orders
,
param
.
getBaseTime
());
List
<
Chromosome
>
cpSatResults
=
cpSatInit
.
generate
(
sharedGlobalOpList
,
cpSatTarget
,
timeBudgetSec
);
if
(!
cpSatResults
.
isEmpty
())
{
for
(
Chromosome
chromo
:
cpSatResults
)
{
chromo
.
setOrders
(
new
CopyOnWriteArrayList
<>(
orders
));
chromo
.
setGlobalOpList
(
deepCopyGlobalOpList
(
sharedGlobalOpList
));
PriorityCheckResult
r
=
Initialization
.
checkPriorityOrder
(
chromo
);
// if (r.isPerfect()) {
// population.add(chromo);
//
// }
}
population
.
addAll
(
cpSatResults
);
FileHelper
.
writeLogFile
(
"[Hybrid初始化] CP-SAT贡献 "
+
cpSatResults
.
size
()
+
" 个高质量种子"
);
}
int
remaining
=
populationSize
-
population
.
size
();
if
(
remaining
>
0
)
{
ScheduleParams
subParam
=
new
ScheduleParams
();
subParam
.
setPopulationSize
(
remaining
);
subParam
.
setBaseTime
(
param
.
getBaseTime
());
List
<
Chromosome
>
heuristicPopulation
=
generateHeuristicInitialPopulation
(
subParam
);
for
(
Chromosome
chromo
:
heuristicPopulation
)
{
chromo
.
setOrders
(
new
CopyOnWriteArrayList
<>(
orders
));
}
population
.
addAll
(
heuristicPopulation
);
}
long
cpSatCount
=
population
.
stream
()
.
filter
(
c
->
c
.
getGsOrls
()
==
4
).
count
();
FileHelper
.
writeLogFile
(
String
.
format
(
"[Hybrid初始化] 总种群=%d, CP-SAT=%d, 启发式=%d"
,
population
.
size
(),
cpSatCount
,
population
.
size
()
-
cpSatCount
));
return
population
;
}
private
List
<
GlobalOperationInfo
>
deepCopyGlobalOpList
(
List
<
GlobalOperationInfo
>
source
)
{
List
<
GlobalOperationInfo
>
copy
=
new
ArrayList
<>(
source
.
size
());
for
(
GlobalOperationInfo
info
:
source
)
{
GlobalOperationInfo
newInfo
=
new
GlobalOperationInfo
();
newInfo
.
setGlobalOpId
(
info
.
getGlobalOpId
());
newInfo
.
setGroupId
(
info
.
getGroupId
());
newInfo
.
setSequence
(
info
.
getSequence
());
newInfo
.
setOp
(
info
.
getOp
());
copy
.
add
(
newInfo
);
}
return
copy
;
}
/**
* 检查染色体的operationSequencing是否遵守Priority排序
* Priority值越小优先级越高(1最先排,99最后排)
*
* @param chromo 待检查的染色体
* @return PriorityCheckResult 包含违反次数、严重度、详细报告
*/
public
static
PriorityCheckResult
checkPriorityOrder
(
Chromosome
chromo
)
{
List
<
GlobalOperationInfo
>
globalOpList
=
chromo
.
getGlobalOpList
();
List
<
Integer
>
os
=
chromo
.
getOperationSequencing
();
if
(
os
==
null
||
os
.
isEmpty
()
||
globalOpList
==
null
||
globalOpList
.
isEmpty
())
{
return
new
PriorityCheckResult
(
0
,
0
,
0
,
"无数据"
);
}
Map
<
Integer
,
Double
>
groupPriority
=
new
HashMap
<>();
Map
<
Integer
,
List
<
Entry
>>
groupOps
=
new
HashMap
<>();
for
(
GlobalOperationInfo
info
:
globalOpList
)
{
int
gid
=
info
.
getGroupId
();
groupPriority
.
putIfAbsent
(
gid
,
info
.
getOp
().
getPriority
());
groupOps
.
computeIfAbsent
(
gid
,
k
->
new
ArrayList
<>()).
add
(
info
.
getOp
());
}
int
violationCount
=
0
;
int
totalSeverity
=
0
;
double
maxSingleSeverity
=
0
;
StringBuilder
detail
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
os
.
size
()
-
1
;
i
++)
{
int
gidA
=
os
.
get
(
i
);
int
gidB
=
os
.
get
(
i
+
1
);
if
(
gidA
==
gidB
)
continue
;
double
priA
=
groupPriority
.
getOrDefault
(
gidA
,
Double
.
MAX_VALUE
);
double
priB
=
groupPriority
.
getOrDefault
(
gidB
,
Double
.
MAX_VALUE
);
if
(
priA
>
priB
)
{
double
severity
=
priA
-
priB
;
violationCount
++;
totalSeverity
+=
severity
;
maxSingleSeverity
=
Math
.
max
(
maxSingleSeverity
,
severity
);
if
(
detail
.
length
()
<
800
)
{
detail
.
append
(
String
.
format
(
" [%d→%d] groupId=%d(优先级%d) 排在 groupId=%d(优先级%d) 之前,差距=%d\n"
,
i
,
i
+
1
,
gidA
,
priA
,
gidB
,
priB
,
severity
));
}
}
}
String
report
=
String
.
format
(
"Priority检查: 违反次数=%d, 总严重度=%d, 最大单次差距=%f, "
+
"工序组数=%d, 不同优先级组数=%d\n%s"
,
violationCount
,
totalSeverity
,
maxSingleSeverity
,
groupPriority
.
size
(),
new
HashSet
<>(
groupPriority
.
values
()).
size
(),
detail
.
toString
());
return
new
PriorityCheckResult
(
violationCount
,
totalSeverity
,
maxSingleSeverity
,
report
);
}
/**
* Priority检查结果
*/
public
static
class
PriorityCheckResult
{
public
final
int
violationCount
;
public
final
int
totalSeverity
;
public
final
double
maxSingleSeverity
;
public
final
String
report
;
PriorityCheckResult
(
int
violationCount
,
int
totalSeverity
,
double
maxSingleSeverity
,
String
report
)
{
this
.
violationCount
=
violationCount
;
this
.
totalSeverity
=
totalSeverity
;
this
.
maxSingleSeverity
=
maxSingleSeverity
;
this
.
report
=
report
;
}
public
boolean
isPerfect
()
{
return
violationCount
==
0
;
}
@Override
public
String
toString
()
{
return
report
;
}
}
/**
/**
* 辅助类:用于权重排序
* 辅助类:用于权重排序
*/
*/
...
...
src/main/java/com/aps/service/Algorithm/OperationSplitService.java
0 → 100644
View file @
85fa9adb
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.common.util.FileHelper
;
import
com.aps.common.util.ProductionDeepCopyUtil
;
import
com.aps.entity.Algorithm.OperationDependency
;
import
com.aps.entity.basic.Entry
;
import
com.aps.entity.basic.GlobalParam
;
import
com.aps.entity.basic.Machine
;
import
com.aps.entity.basic.MachineOption
;
import
java.util.*
;
import
java.util.stream.Collectors
;
/**
* 多设备工序拆分服务
* 当 GlobalParam.IsMultipleMachine = true 时,
* 将拥有多个可选设备的工序按策略拆分为多个子工序,每道子工序分配一台设备,可并行加工。
*
* 数据对齐 ScheduleOperationService.SpiltOperation:
* - mainId:同源拆分子工序共享的 UUID
* - state=1(拆分/原始)、state=2(新建)
* - newCreate:新建标记
* - execId:第一个保留原始,其余新生成 UUID
*
* 作者:佟礼
* 时间:2026-05-21
*/
public
class
OperationSplitService
{
/**
* 对工序列表进行多设备拆分(预处理入口,结合设备负载)
*
* @param allOperations 原始工序列表
* @param globalParam 全局参数
* @param machines 设备列表(用于计算各设备总预期负载)
* @return 拆分后的工序列表
*/
public
static
List
<
Entry
>
splitMultiMachineOperations
(
List
<
Entry
>
allOperations
,
GlobalParam
globalParam
,
List
<
Machine
>
machines
)
{
if
(
allOperations
==
null
||
allOperations
.
isEmpty
())
{
return
allOperations
;
}
if
(!
globalParam
.
isIsMultipleMachine
())
{
return
allOperations
;
}
boolean
hasMultiMachine
=
allOperations
.
stream
()
.
anyMatch
(
op
->
op
.
getMachineOptions
()
!=
null
&&
op
.
getMachineOptions
().
size
()
>
1
);
if
(!
hasMultiMachine
)
{
FileHelper
.
writeLogFile
(
"[工序拆分] 没有多设备工序,跳过拆分"
);
return
allOperations
;
}
FileHelper
.
writeLogFile
(
"[工序拆分] 开始多设备工序拆分(负载感知),原始工序数="
+
allOperations
.
size
());
Map
<
Long
,
Double
>
machineTotalLoad
=
calcMachineTotalLoad
(
allOperations
);
int
maxId
=
allOperations
.
stream
().
mapToInt
(
Entry:
:
getId
).
max
().
orElse
(
0
);
int
nextId
=
maxId
+
1
;
Map
<
Integer
,
List
<
Integer
>>
splitMapping
=
new
LinkedHashMap
<>();
List
<
Entry
>
result
=
new
ArrayList
<>();
Map
<
Integer
,
List
<
Entry
>>
groupMap
=
new
LinkedHashMap
<>();
for
(
Entry
op
:
allOperations
)
{
groupMap
.
computeIfAbsent
(
op
.
getGroupId
(),
k
->
new
ArrayList
<>()).
add
(
op
);
}
int
totalSplitCount
=
0
;
int
totalSplitOps
=
0
;
for
(
Map
.
Entry
<
Integer
,
List
<
Entry
>>
groupEntry
:
groupMap
.
entrySet
())
{
List
<
Entry
>
groupOps
=
groupEntry
.
getValue
();
groupOps
.
sort
(
Comparator
.
comparingInt
(
Entry:
:
getSequence
));
List
<
Entry
>
newGroupOps
=
new
ArrayList
<>();
for
(
Entry
op
:
groupOps
)
{
List
<
MachineOption
>
options
=
op
.
getMachineOptions
();
if
(
options
!=
null
&&
options
.
size
()
>
1
)
{
List
<
Entry
>
subOps
=
splitEntryByLoad
(
op
,
options
,
nextId
,
machineTotalLoad
);
nextId
+=
subOps
.
size
();
newGroupOps
.
addAll
(
subOps
);
splitMapping
.
put
(
op
.
getId
(),
subOps
.
stream
().
map
(
Entry:
:
getId
).
collect
(
Collectors
.
toList
()));
totalSplitOps
++;
totalSplitCount
+=
subOps
.
size
();
}
else
{
newGroupOps
.
add
(
op
);
}
}
for
(
int
i
=
0
;
i
<
newGroupOps
.
size
();
i
++)
{
newGroupOps
.
get
(
i
).
setSequence
(
i
+
1
);
}
result
.
addAll
(
newGroupOps
);
}
updateAllDependencies
(
result
,
splitMapping
);
FileHelper
.
writeLogFile
(
String
.
format
(
"[工序拆分] 拆分了%d道多设备工序 → 产生%d道子工序,最终总工序数=%d"
,
totalSplitOps
,
totalSplitCount
,
result
.
size
()));
return
result
;
}
/**
* 按设备总负载比例拆分单道工序(预处理用)
* 设备负载越高,分到的数量越少
*/
public
static
List
<
Entry
>
splitEntryByLoad
(
Entry
original
,
List
<
MachineOption
>
options
,
int
startId
,
Map
<
Long
,
Double
>
machineTotalLoad
)
{
return
doSplit
(
original
,
options
,
startId
,
machineTotalLoad
,
null
,
null
);
}
/**
* 按设备即时负载比例拆分单道工序(初始化 GS/LS 用)
* 设备当前累计负载越高,分到的数量越少
*/
public
static
List
<
Entry
>
splitEntryByRuntimeLoad
(
Entry
original
,
List
<
MachineOption
>
options
,
int
startId
,
Map
<
Long
,
Double
>
machineRuntimeLoad
)
{
return
doSplit
(
original
,
options
,
startId
,
machineRuntimeLoad
,
null
,
null
);
}
/**
* 随机比例拆分单道工序(随机初始化 / 启发式用)
*/
public
static
List
<
Entry
>
splitEntryRandom
(
Entry
original
,
List
<
MachineOption
>
options
,
int
startId
,
Random
rnd
)
{
return
doSplit
(
original
,
options
,
startId
,
null
,
null
,
rnd
);
}
/**
* 随机选择部分设备拆分(概率随机的部分拆分)
*/
public
static
List
<
Entry
>
splitEntryRandomPartial
(
Entry
original
,
List
<
MachineOption
>
options
,
int
startId
,
Random
rnd
)
{
List
<
MachineOption
>
selected
=
new
ArrayList
<>();
for
(
MachineOption
mo
:
options
)
{
if
(
rnd
.
nextBoolean
())
{
selected
.
add
(
mo
);
}
}
if
(
selected
.
size
()
<
2
)
{
selected
=
options
;
}
return
doSplit
(
original
,
selected
,
startId
,
null
,
null
,
rnd
);
}
private
static
List
<
Entry
>
doSplit
(
Entry
original
,
List
<
MachineOption
>
options
,
int
startId
,
Map
<
Long
,
Double
>
loadMap
,
Map
<
Long
,
Double
>
percentMap
,
Random
rnd
)
{
List
<
Entry
>
subOps
=
new
ArrayList
<>();
int
n
=
options
.
size
();
String
mainId
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
);
double
[]
weights
=
new
double
[
n
];
double
totalWeight
=
0
;
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
MachineOption
mo
=
options
.
get
(
i
);
if
(
rnd
!=
null
)
{
weights
[
i
]
=
rnd
.
nextDouble
()
+
0.5
;
}
else
if
(
loadMap
!=
null
)
{
double
load
=
loadMap
.
getOrDefault
(
mo
.
getMachineId
(),
0.0
);
weights
[
i
]
=
1.0
/
Math
.
max
(
load
+
mo
.
getProcessingTime
(),
0.001
);
}
else
{
weights
[
i
]
=
1.0
/
Math
.
max
(
mo
.
getProcessingTime
(),
0.001
);
}
totalWeight
+=
weights
[
i
];
}
double
remainingQty
=
original
.
getQuantity
();
int
id
=
startId
;
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
MachineOption
mo
=
options
.
get
(
i
);
Entry
sub
=
ProductionDeepCopyUtil
.
deepCopy
(
original
,
Entry
.
class
);
sub
.
setId
(
id
);
sub
.
setSplitSourceId
(
original
.
getId
());
sub
.
setMainId
(
mainId
);
sub
.
setSelectMachineID
(
mo
.
getMachineId
());
sub
.
setMachineOptions
(
Collections
.
singletonList
(
mo
));
if
(
i
==
0
)
{
sub
.
setState
(
1
);
sub
.
setNewCreate
(
false
);
}
else
{
sub
.
setState
(
2
);
sub
.
setNewCreate
(
true
);
sub
.
setExecId
(
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
));
}
double
proportion
=
(
i
==
n
-
1
)
?
1.0
:
weights
[
i
]
/
totalWeight
;
double
splitQty
=
Math
.
max
(
1
,
Math
.
round
(
original
.
getQuantity
()
*
proportion
*
100.0
)
/
100.0
);
if
(
i
==
n
-
1
)
{
splitQty
=
remainingQty
;
}
splitQty
=
Math
.
min
(
splitQty
,
remainingQty
);
remainingQty
-=
splitQty
;
sub
.
setQuantity
(
splitQty
);
fixDependencyReferences
(
sub
.
getPrevEntryIds
(),
original
.
getId
(),
id
,
true
);
fixDependencyReferences
(
sub
.
getNextEntryIds
(),
original
.
getId
(),
id
,
false
);
subOps
.
add
(
sub
);
id
++;
}
return
subOps
;
}
/**
* 对启发式初始化用的工序列表进行随机拆分(直接修改列表,返回拆分后的新列表)
* 拆分后每个 group 内重新编号 sequence
*/
public
static
List
<
Entry
>
splitOpsForHeuristic
(
List
<
Entry
>
ops
,
Random
rnd
)
{
Map
<
Integer
,
List
<
Entry
>>
groupMap
=
new
LinkedHashMap
<>();
for
(
Entry
op
:
ops
)
{
groupMap
.
computeIfAbsent
(
op
.
getGroupId
(),
k
->
new
ArrayList
<>()).
add
(
op
);
}
int
maxId
=
ops
.
stream
().
mapToInt
(
Entry:
:
getId
).
max
().
orElse
(
0
);
int
nextId
=
maxId
+
1
;
List
<
Entry
>
result
=
new
ArrayList
<>();
for
(
Map
.
Entry
<
Integer
,
List
<
Entry
>>
groupEntry
:
groupMap
.
entrySet
())
{
List
<
Entry
>
expanded
=
new
ArrayList
<>();
for
(
Entry
op
:
groupEntry
.
getValue
())
{
List
<
MachineOption
>
options
=
op
.
getMachineOptions
();
if
(
op
.
getSplitSourceId
()
==
null
&&
options
!=
null
&&
options
.
size
()
>
1
)
{
List
<
Entry
>
subOps
=
splitEntryRandomPartial
(
op
,
options
,
nextId
,
rnd
);
nextId
+=
subOps
.
size
();
expanded
.
addAll
(
subOps
);
}
else
{
expanded
.
add
(
op
);
}
}
for
(
int
i
=
0
;
i
<
expanded
.
size
();
i
++)
{
expanded
.
get
(
i
).
setSequence
(
i
+
1
);
}
result
.
addAll
(
expanded
);
}
return
result
;
}
private
static
Map
<
Long
,
Double
>
calcMachineTotalLoad
(
List
<
Entry
>
allOperations
)
{
Map
<
Long
,
Double
>
load
=
new
LinkedHashMap
<>();
for
(
Entry
op
:
allOperations
)
{
if
(
op
.
getMachineOptions
()
!=
null
)
{
for
(
MachineOption
mo
:
op
.
getMachineOptions
())
{
load
.
put
(
mo
.
getMachineId
(),
load
.
getOrDefault
(
mo
.
getMachineId
(),
0.0
)
+
mo
.
getProcessingTime
()
*
op
.
getQuantity
());
}
}
}
return
load
;
}
private
static
void
fixDependencyReferences
(
List
<
OperationDependency
>
deps
,
int
originalId
,
int
newId
,
boolean
isPrev
)
{
if
(
deps
==
null
)
return
;
for
(
OperationDependency
dep
:
deps
)
{
if
(
isPrev
)
{
if
(
dep
.
getNextOperationId
()
==
originalId
)
{
dep
.
setNextOperationId
(
newId
);
}
}
else
{
if
(
dep
.
getPrevOperationId
()
==
originalId
)
{
dep
.
setPrevOperationId
(
newId
);
}
}
}
}
private
static
void
updateAllDependencies
(
List
<
Entry
>
allOps
,
Map
<
Integer
,
List
<
Integer
>>
splitMapping
)
{
if
(
splitMapping
.
isEmpty
())
return
;
for
(
Entry
op
:
allOps
)
{
if
(
op
.
getPrevEntryIds
()
!=
null
&&
!
op
.
getPrevEntryIds
().
isEmpty
())
{
List
<
OperationDependency
>
expanded
=
new
ArrayList
<>();
for
(
OperationDependency
dep
:
op
.
getPrevEntryIds
())
{
List
<
Integer
>
splitIds
=
splitMapping
.
get
(
dep
.
getPrevOperationId
());
if
(
splitIds
!=
null
)
{
for
(
int
splitId
:
splitIds
)
{
OperationDependency
newDep
=
new
OperationDependency
();
newDep
.
setPrevOperationId
(
splitId
);
newDep
.
setNextOperationId
(
dep
.
getNextOperationId
());
newDep
.
setDependencyType
(
dep
.
getDependencyType
());
expanded
.
add
(
newDep
);
}
}
else
{
expanded
.
add
(
dep
);
}
}
op
.
setPrevEntryIds
(
expanded
);
}
if
(
op
.
getNextEntryIds
()
!=
null
&&
!
op
.
getNextEntryIds
().
isEmpty
())
{
List
<
OperationDependency
>
expanded
=
new
ArrayList
<>();
for
(
OperationDependency
dep
:
op
.
getNextEntryIds
())
{
List
<
Integer
>
splitIds
=
splitMapping
.
get
(
dep
.
getNextOperationId
());
if
(
splitIds
!=
null
)
{
for
(
int
splitId
:
splitIds
)
{
OperationDependency
newDep
=
new
OperationDependency
();
newDep
.
setPrevOperationId
(
dep
.
getPrevOperationId
());
newDep
.
setNextOperationId
(
splitId
);
newDep
.
setDependencyType
(
dep
.
getDependencyType
());
expanded
.
add
(
newDep
);
}
}
else
{
expanded
.
add
(
dep
);
}
}
op
.
setNextEntryIds
(
expanded
);
}
}
FileHelper
.
writeLogFile
(
String
.
format
(
"[工序拆分] 更新了%d个拆分的依赖引用"
,
splitMapping
.
size
()));
}
}
\ No newline at end of file
src/main/java/com/aps/service/Algorithm/RoutingDataService.java
View file @
85fa9adb
...
@@ -409,6 +409,7 @@ if(entry.getMachineOptions()!=null)
...
@@ -409,6 +409,7 @@ if(entry.getMachineOptions()!=null)
if
(
machine
.
getCapacityTypeName
()
==
null
)
{
if
(
machine
.
getCapacityTypeName
()
==
null
)
{
machine
.
setCapacityTypeName
(
equipinfo
.
getCapacityTypeName
());
machine
.
setCapacityTypeName
(
equipinfo
.
getCapacityTypeName
());
}
}
}
else
{
}
else
{
machine
.
setCode
(
PlanResource
.
getReferenceCode
());
machine
.
setCode
(
PlanResource
.
getReferenceCode
());
machine
.
setName
(
PlanResource
.
getTitle
());
machine
.
setName
(
PlanResource
.
getTitle
());
...
...
src/main/java/com/aps/service/plan/PlanResultService.java
View file @
85fa9adb
...
@@ -177,7 +177,7 @@ public class PlanResultService {
...
@@ -177,7 +177,7 @@ public class PlanResultService {
* 后续会按场景创建人自动回退到可用的策略配置。
* 后续会按场景创建人自动回退到可用的策略配置。
*/
*/
public
Chromosome
execute2
(
String
SceneId
)
{
public
Chromosome
execute2
(
String
SceneId
)
{
return
execute2
(
SceneId
,
null
,
nul
l
,
null
);
return
execute2
(
SceneId
,
2361
l
,
241
l
,
null
);
}
}
/**
/**
...
@@ -188,7 +188,7 @@ public class PlanResultService {
...
@@ -188,7 +188,7 @@ public class PlanResultService {
try
{
try
{
ScheduleParams
param
=
InitScheduleParams
();
ScheduleParams
param
=
InitScheduleParams
();
// param.setBaseTime(LocalDateTime.of(2026, 1, 1, 0, 0, 0));
this
.
baseTime
=
param
.
getBaseTime
();
this
.
baseTime
=
param
.
getBaseTime
();
// 策略读取入口:优先使用前端传入的 userId;没传时用 sceneId 查场景创建人。
// 策略读取入口:优先使用前端传入的 userId;没传时用 sceneId 查场景创建人。
Long
effectiveUserId
=
scheduleStrategyService
.
resolveScheduleUserId
(
SceneId
,
userId
);
Long
effectiveUserId
=
scheduleStrategyService
.
resolveScheduleUserId
(
SceneId
,
userId
);
...
@@ -2246,7 +2246,7 @@ if(job.getGeneDetails()!=null)
...
@@ -2246,7 +2246,7 @@ if(job.getGeneDetails()!=null)
ApsTimeConfig
apsTimeConfig
=
apsTimeConfigMapper
.
selectOne
(
queryWrapper
);
ApsTimeConfig
apsTimeConfig
=
apsTimeConfigMapper
.
selectOne
(
queryWrapper
);
ScheduleParams
param
=
new
ScheduleParams
();
ScheduleParams
param
=
new
ScheduleParams
();
param
.
setBaseTime
(
LocalDateTime
.
of
(
202
5
,
1
1
,
1
,
0
,
0
,
0
));
param
.
setBaseTime
(
LocalDateTime
.
of
(
202
6
,
1
,
1
,
0
,
0
,
0
));
if
(
apsTimeConfig
!=
null
)
{
if
(
apsTimeConfig
!=
null
)
{
if
(
apsTimeConfig
.
getBaseTime
()!=
null
)
if
(
apsTimeConfig
.
getBaseTime
()!=
null
)
{
{
...
...
src/test/java/com/aps/demo/PlanResultServiceTest.java
View file @
85fa9adb
...
@@ -42,7 +42,7 @@ public class PlanResultServiceTest {
...
@@ -42,7 +42,7 @@ public class PlanResultServiceTest {
// nsgaiiUtils.Test();
// nsgaiiUtils.Test();
// planResultService.execute2("64E64F6B68094AF38CEDC418630C3CC2");//2000
// planResultService.execute2("64E64F6B68094AF38CEDC418630C3CC2");//2000
planResultService
.
execute2
(
"
72744D094BAB45948F5172E84C78B260
"
);
//2000
planResultService
.
execute2
(
"
E1448B3C9C8743DEAB39708F2CFE348A
"
);
//2000
// planResultService.execute2("15210B13B88A453F8B84AAC7F16C7541");//2000
// planResultService.execute2("15210B13B88A453F8B84AAC7F16C7541");//2000
...
...
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