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
e3a2cdde
Commit
e3a2cdde
authored
Aug 20, 2026
by
Tong Li
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MP
parent
8dc6c378
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
5150 additions
and
711 deletions
+5150
-711
log.txt
mp/log/log.txt
+244
-236
model.lp
mp/lp/model.lp
+4702
-366
optimization_result_1.json
mp/result/optimization_result_1.json
+1
-1
MacroPlannerOptimizer.java
...main/java/com/aps/macroplanner/MacroPlannerOptimizer.java
+25
-16
InventorySpecConstraint.java
.../aps/macroplanner/constraint/InventorySpecConstraint.java
+1
-1
MacroPlannerDataConverter.java
.../com/aps/macroplanner/data/MacroPlannerDataConverter.java
+63
-13
ObjectiveBuilder.java
...java/com/aps/macroplanner/objective/ObjectiveBuilder.java
+17
-3
SolutionPrinter.java
...ain/java/com/aps/macroplanner/output/SolutionPrinter.java
+89
-75
POAOrToolsModel.java
src/main/java/com/aps/service/Algorithm/POAOrToolsModel.java
+8
-0
No files found.
mp/log/log.txt
View file @
e3a2cdde
This diff is collapsed.
Click to expand it.
mp/lp/model.lp
View file @
e3a2cdde
This diff is collapsed.
Click to expand it.
mp/result/optimization_result_1.json
View file @
e3a2cdde
This source diff could not be displayed because it is too large. You can
view the blob
instead.
src/main/java/com/aps/macroplanner/MacroPlannerOptimizer.java
View file @
e3a2cdde
package
com
.
aps
.
macroplanner
;
import
com.aps.common.util.FileHelper
;
import
com.google.ortools.Loader
;
import
com.google.ortools.linearsolver.MPSolver
;
import
com.aps.macroplanner.constraint.ConstraintFactory
;
...
...
@@ -89,7 +90,7 @@ public class MacroPlannerOptimizer {
private
static
final
String
LP_FILE_PATH
=
LOG_DIR
+
"/lp/model.lp"
;
/** 运行日志文件路径 */
private
static
final
String
LOG_FILE_PATH
=
LOG_DIR
+
"/log/
log.txt
"
;
private
static
final
String
LOG_FILE_PATH
=
LOG_DIR
+
"/log/"
;
/** 获取模型容器 (供 ResultWriter 等外部组件使用)。 */
public
MacroPlannerModel
getModel
()
{
return
model
;
}
...
...
@@ -148,6 +149,11 @@ public class MacroPlannerOptimizer {
rootLogger
.
addHandler
(
handler
);
}
private
void
writeLog
(
String
msg
)
{
FileHelper
.
writeFile
(
msg
,
LOG_FILE_PATH
,
"log.txt"
);
}
// ==================== 模型构建流程 ====================
/**
...
...
@@ -163,32 +169,32 @@ public class MacroPlannerOptimizer {
// 设为 FINE 可输出每个周期的折算详情
configureLogging
();
System
.
out
.
println
(
"=== 开始构建 MacroPlanner 优化模型 ===\n"
);
writeLog
(
"=== 开始构建 MacroPlanner 优化模型 ===\n"
);
// 0. 数据完整性检查 (在构建模型前验证)
DataValidator
validator
=
new
DataValidator
(
data
);
if
(!
validator
.
validate
())
{
System
.
out
.
println
(
" ⚠️ 数据检查发现错误, 求解结果可能不可靠\n"
);
writeLog
(
" ⚠️ 数据检查发现错误, 求解结果可能不可靠\n"
);
}
else
{
System
.
out
.
println
(
" [OK] 数据检查通过\n"
);
writeLog
(
" [OK] 数据检查通过\n"
);
}
// 1. 决策变量
VariableFactory
.
createAll
(
model
,
data
);
System
.
out
.
println
(
" [OK] 决策变量创建完成"
);
writeLog
(
" [OK] 决策变量创建完成"
);
// 2. 约束 + KPI 汇总 (由 ConstraintFactory 统一调度)
ConstraintFactory
.
buildAll
(
model
,
data
);
System
.
out
.
println
(
" [OK] 约束与KPI汇总创建完成"
);
writeLog
(
" [OK] 约束与KPI汇总创建完成"
);
// 3. 目标函数 (加权求和)
ObjectiveBuilder
.
build
(
model
,
data
);
System
.
out
.
println
(
" [OK] 目标函数创建完成"
);
writeLog
(
" [OK] 目标函数创建完成"
);
// 4. 导出 LP 模型文件
exportLpModel
();
System
.
out
.
println
(
"\n模型统计: 变量="
+
model
.
getSolver
().
numVariables
()
writeLog
(
"\n模型统计: 变量="
+
model
.
getSolver
().
numVariables
()
+
", 约束="
+
model
.
getSolver
().
numConstraints
()
+
"\n"
);
}
...
...
@@ -211,9 +217,9 @@ public class MacroPlannerOptimizer {
String
lpContent
=
model
.
getSolver
().
exportModelAsLpFormat
();
Path
lpPath
=
Paths
.
get
(
LP_FILE_PATH
).
toAbsolutePath
();
Files
.
write
(
lpPath
,
lpContent
.
getBytes
(
StandardCharsets
.
UTF_8
));
System
.
out
.
println
(
" [OK] LP模型文件已导出: "
+
lpPath
);
writeLog
(
" [OK] LP模型文件已导出: "
+
lpPath
);
}
catch
(
Exception
e
)
{
System
.
err
.
println
(
" [WARN] LP模型导出失败: "
+
e
.
getMessage
());
writeLog
(
" [WARN] LP模型导出失败: "
+
e
.
getMessage
());
}
}
...
...
@@ -255,7 +261,7 @@ public class MacroPlannerOptimizer {
* 每个层级独立求解, 上层最优值作为下层约束, 确保严格优先级顺序。</p>
*/
public
void
solve
()
{
System
.
out
.
println
(
"=== 开始分层求解 ===\n"
);
writeLog
(
"=== 开始分层求解 ===\n"
);
startTimeMs
=
System
.
currentTimeMillis
();
KPIWeights
w
=
data
.
getKpiWeights
();
...
...
@@ -300,17 +306,20 @@ public class MacroPlannerOptimizer {
if
(
i
<
levels
.
size
()
-
1
&&
level
.
getRelativeGoalSlack
()
>=
0.0
)
{
ObjectiveBuilder
.
addLevelBoundConstraint
(
model
,
level
,
optimalValue
);
System
.
out
.
printf
(
" 已添加边界约束: 上层目标 ≤ %.2f%n"
,
optimalValue
*
(
1.0
+
level
.
getRelativeGoalSlack
()));
ObjectiveBuilder
.
computeUpperBound
(
optimalValue
,
level
.
getRelativeGoalSlack
()));
}
System
.
out
.
println
(
);
writeLog
(
"-----------------------------------"
);
}
// 输出最终结果
MPSolver
.
ResultStatus
finalStatus
=
model
.
getSolver
().
solve
();
if
(
finalStatus
==
MPSolver
.
ResultStatus
.
OPTIMAL
||
finalStatus
==
MPSolver
.
ResultStatus
.
FEASIBLE
)
{
SolutionPrinter
printer
=
new
SolutionPrinter
(
model
,
data
,
startTimeMs
);
SolutionPrinter
printer
=
new
SolutionPrinter
(
model
,
data
,
startTimeMs
,
LOG_FILE_PATH
);
printer
.
printAll
();
// 构建层级最优值列表
List
<
Double
>
levelObjValues
=
new
ArrayList
<>();
...
...
@@ -323,10 +332,10 @@ public class MacroPlannerOptimizer {
ResultWriter
rw
=
new
ResultWriter
(
model
,
data
,
startTimeMs
);
boolean
jsonPath
=
rw
.
saveResultToFile
(
"1"
);
if
(
jsonPath
)
{
System
.
out
.
println
(
"\n[OK] 优化结果JSON已导出: "
+
jsonPath
);
writeLog
(
"\n[OK] 优化结果JSON已导出: "
+
jsonPath
);
}
}
else
{
System
.
out
.
println
(
"求解失败! 状态: "
+
finalStatus
);
writeLog
(
"求解失败! 状态: "
+
finalStatus
);
}
}
...
...
src/main/java/com/aps/macroplanner/constraint/InventorySpecConstraint.java
View file @
e3a2cdde
...
...
@@ -135,7 +135,7 @@ public class InventorySpecConstraint {
}
else
{
double
rhs
=
spec
.
getTargetLevel
();
MPConstraint
c
=
model
.
getSolver
().
makeConstraint
(
rhs
,
rhs
,
"TargetInv_"
+
invKey
);
rhs
,
MPSolver
.
infinity
()
,
"TargetInv_"
+
invKey
);
c
.
setCoefficient
(
invVar
,
1.0
);
MPVariable
u
=
targetUnderVars
.
get
(
invKey
);
if
(
u
!=
null
)
c
.
setCoefficient
(
u
,
1.0
);
...
...
src/main/java/com/aps/macroplanner/data/MacroPlannerDataConverter.java
View file @
e3a2cdde
...
...
@@ -5,6 +5,7 @@ import com.aps.entity.*;
import
com.aps.entity.basic.Material
;
import
com.aps.entity.basic.MaterialSupply
;
import
com.aps.mapper.EquipShiftCapacityMapper
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
com.fasterxml.jackson.core.type.TypeReference
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.aps.mapper.EquipinfoMapper
;
...
...
@@ -190,6 +191,16 @@ public class MacroPlannerDataConverter {
new
LambdaQueryWrapper
<
ApsDemandOrder
>()
.
ge
(
ApsDemandOrder:
:
getDeliverytime
,
ctx
.
baseTime
)
.
lt
(
ApsDemandOrder:
:
getDeliverytime
,
horizonEndDateTime
));
// ctx.apsDemandOrders = apsDemandOrderMapper.selectList(
//
// new LambdaQueryWrapper<ApsDemandOrder>()
// .ge(ApsDemandOrder::getDeliverytime, ctx.baseTime)
// .eq(ApsDemandOrder::getCode,"XQDD_20260812_6")
// .lt(ApsDemandOrder::getDeliverytime, horizonEndDateTime)
// );
log
.
info
(
"加载需求订单: {} 条 (时间范围过滤)"
,
ctx
.
apsDemandOrders
.
size
());
// 2. 收集 materialIds (从 ApsDemandOrder.mmid)
...
...
@@ -215,6 +226,40 @@ public class MacroPlannerDataConverter {
// 4. 工序 (通过 LanuchService 批量查询)
if
(!
routingIds
.
isEmpty
())
{
List
<
Integer
>
routingIds1
=
routingIds
;
// while (routingIds1!=null&&routingIds1.size()>0) {
// List<Routingsupporting> rss = routingsupportingMapper.selectList(
// new LambdaQueryWrapper<Routingsupporting>()
// .in(Routingsupporting::getRoutingHeaderId, routingIds)
// .eq(Routingsupporting::getIsdeleted, 0));
// if (rss != null && rss.size() > 0) {
// Set<String> materialIdrss = rss.stream()
// .map(Routingsupporting::getMaterialId)
// .filter(Objects::nonNull)
// .distinct()
// .collect(Collectors.toSet());
// materialIds.addAll(materialIdrss);
// ctx.routingsupportings.addAll(rss);
//
// List<RoutingHeader> rhs = routingHeaderMapper.selectList(
// new LambdaQueryWrapper<RoutingHeader>()
// .in(RoutingHeader::getMaterialId, materialIdrss));
//
// if (rhs != null && rhs.size() > 0) {
// routingIds1 = rhs.stream()
// .map(RoutingHeader::getId)
// .filter(Objects::nonNull)
// .distinct()
// .collect(Collectors.toList());
// routingIds.addAll(routingIds1);
// ctx.routingHeaders.addAll(rhs);
// } else {
// routingIds1 = null;
// }
// } else {
// routingIds1 = null;
// }
// }
List
<
Long
>
routingIdsLong
=
routingIds
.
stream
()
.
map
(
Long:
:
valueOf
)
.
collect
(
Collectors
.
toList
());
...
...
@@ -237,9 +282,12 @@ public class MacroPlannerDataConverter {
// 6. 收集所有 materialId (订单 + 工艺物料消耗 + 工艺路线头表)
ctx
.
routingsupportings
.
forEach
(
rs
->
{
if
(
rs
.
getMaterialId
()
!=
null
)
materialIds
.
add
(
rs
.
getMaterialId
());
});
ctx
.
routingsupportings
.
forEach
(
rs
->
{
if
(
rs
.
getMaterialId
()
!=
null
)
materialIds
.
add
(
rs
.
getMaterialId
());
});
// 7. 物料主数据
...
...
@@ -641,7 +689,7 @@ public class MacroPlannerDataConverter {
// 3.1 为每个 Material 创建 Product
for
(
Material
m
:
ctx
.
materialByMaterialId
.
values
())
{
String
name
=
pickName
(
m
.
getName
(),
m
.
getCode
(),
m
.
getId
());
Product
p
=
new
Product
(
m
.
get
Id
(),
name
);
Product
p
=
new
Product
(
m
.
get
Code
(),
name
);
products
.
add
(
p
);
ctx
.
productByMaterialId
.
put
(
m
.
getId
(),
p
);
}
...
...
@@ -892,10 +940,10 @@ public class MacroPlannerDataConverter {
if
(
inputMaterial
==
null
)
{
continue
;
}
if
(!
"MP"
.
equals
(
inputMaterial
.
getMaterialTypeName
()))
{
// 半成品/成品不作为投料, 由各自 Routing 产出
continue
;
}
//
if (!"MP".equals(inputMaterial.getMaterialTypeName())) {
//
// 半成品/成品不作为投料, 由各自 Routing 产出
//
continue;
//
}
if
(
rs
.
getMainQty
()
==
null
||
rs
.
getMainQty
().
compareTo
(
BigDecimal
.
ZERO
)
==
0
)
{
log
.
warn
(
"跳过主量为0的BOM项: routingDetailId={}, materialId={}"
,
...
...
@@ -919,9 +967,11 @@ public class MacroPlannerDataConverter {
// - 有 MATERIAL_PURCHASE → 每供应商一个 Operation, leadTimeDays = purchaseCycle
// - 无 MATERIAL_PURCHASE → 通用供应商 unit, relativeDuration=1, leadTimeDays=0, UnitPeriod 无限
for
(
Material
m
:
ctx
.
materialByMaterialId
.
values
())
{
if
(!
"MP"
.
equals
(
m
.
getMaterialTypeName
()))
{
continue
;
}
long
headercount
=
ctx
.
routingHeaders
.
stream
().
filter
(
t
->
t
.
getMaterialId
()==
m
.
getId
()).
count
();
if
(
headercount
>
0
)
{
continue
;
}
Product
p
=
ctx
.
productByMaterialId
.
get
(
m
.
getId
());
if
(
p
==
null
)
{
continue
;
...
...
@@ -1287,7 +1337,7 @@ public class MacroPlannerDataConverter {
String
key
=
m
.
getProduct
().
getId
()
+
"_"
+
m
.
getStockingPoint
().
getId
()
+
"_"
+
p
.
getIndex
();
if
(
invSpecKeys
.
add
(
key
))
{
inventorySpecs
.
add
(
new
InventorySpec
(
m
.
getProduct
(),
m
.
getStockingPoint
(),
p
,
0.0
,
0.0
,
LOOSE_MAX
,
tru
e
,
true
,
true
));
0.0
,
0.0
,
LOOSE_MAX
,
fals
e
,
true
,
true
));
}
}
}
...
...
@@ -1305,7 +1355,7 @@ public class MacroPlannerDataConverter {
for
(
Operation
op
:
operations
)
{
if
(
op
.
getId
().
startsWith
(
"OP_PROCURE_"
))
{
supplySpecs
.
add
(
new
SupplySpec
(
"Supply-"
+
op
.
getId
(),
0.0
,
0.0
,
LOOSE_MAX
,
fals
e
,
Collections
.
singletonList
(
op
)));
0.0
,
0.0
,
LOOSE_MAX
,
tru
e
,
Collections
.
singletonList
(
op
)));
}
}
log
.
info
(
"构建 InventorySpec: {}, SupplySpec: {}"
,
inventorySpecs
.
size
(),
supplySpecs
.
size
());
...
...
src/main/java/com/aps/macroplanner/objective/ObjectiveBuilder.java
View file @
e3a2cdde
...
...
@@ -112,7 +112,21 @@ public class ObjectiveBuilder {
// 设置为最小化目标
objective
.
setMinimization
();
}
/**
* 计算层级边界约束的上界。
*
* <p>松弛比例应允许上层目标值<b>变差</b>(对最小化而言即数值增大)。
* 由于目标值可能为负(负系数 KPI 导致),直接使用
* {@code optimalValue × (1 + slack)} 会在负值时反向收紧约束,
* 因此改用 {@code optimalValue + |optimalValue| × slack}。</p>
*
* @param optimalValue 上层最优目标值
* @param relativeGoalSlack 松弛比例 (>= 0)
* @return 放宽后的上界
*/
public
static
double
computeUpperBound
(
double
optimalValue
,
double
relativeGoalSlack
)
{
return
optimalValue
+
Math
.
abs
(
optimalValue
)
*
relativeGoalSlack
;
}
// ==================== 分层优化方法 ====================
/**
...
...
@@ -165,7 +179,7 @@ public class ObjectiveBuilder {
*
* <h3>数学公式</h3>
* <pre>
* Σ (effectiveCoeff × KPI_variable) ≤ optimalValue
× (1 + relativeGoalSlack)
* Σ (effectiveCoeff × KPI_variable) ≤ optimalValue
+ |optimalValue| × relativeGoalSlack
* </pre>
*
* @param model 模型容器
...
...
@@ -178,7 +192,7 @@ public class ObjectiveBuilder {
if
(
level
.
getRelativeGoalSlack
()
<
0.0
)
return
;
// 负松弛表示不约束
MPSolver
solver
=
model
.
getSolver
();
double
upperBound
=
optimalValue
*
(
1.0
+
level
.
getRelativeGoalSlack
());
double
upperBound
=
computeUpperBound
(
optimalValue
,
level
.
getRelativeGoalSlack
());
MPConstraint
bound
=
solver
.
makeConstraint
(
-
MPSolver
.
infinity
(),
upperBound
,
...
...
src/main/java/com/aps/macroplanner/output/SolutionPrinter.java
View file @
e3a2cdde
This diff is collapsed.
Click to expand it.
src/main/java/com/aps/service/Algorithm/POAOrToolsModel.java
0 → 100644
View file @
e3a2cdde
package
com
.
aps
.
service
.
Algorithm
;
/**
* 作者:佟礼
* 时间:2026-08-18
*/
public
class
POAOrToolsModel
{
}
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