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
fd3b9e49
Commit
fd3b9e49
authored
Sep 22, 2026
by
Tong Li
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MP-将kpi设置接入,优化器
parent
2f9e7b36
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
361 additions
and
148 deletions
+361
-148
MacroPlannerResultController.java
...java/com/aps/controller/MacroPlannerResultController.java
+4
-3
MacroPlannerDataConverterRunner.java
...com/aps/macroplanner/MacroPlannerDataConverterRunner.java
+2
-2
MacroPlannerOptimizer.java
...main/java/com/aps/macroplanner/MacroPlannerOptimizer.java
+55
-16
MacroPlannerDataConverter.java
.../com/aps/macroplanner/data/MacroPlannerDataConverter.java
+33
-21
TestDataBuilder.java
src/main/java/com/aps/macroplanner/data/TestDataBuilder.java
+25
-16
MacroPlannerModel.java
...in/java/com/aps/macroplanner/model/MacroPlannerModel.java
+43
-0
KpiLib.java
src/main/java/com/aps/macroplanner/objective/KpiLib.java
+58
-0
KpiSetting.java
src/main/java/com/aps/macroplanner/objective/KpiSetting.java
+36
-0
ObjectiveBuilder.java
...java/com/aps/macroplanner/objective/ObjectiveBuilder.java
+52
-52
StrategyLevel.java
...in/java/com/aps/macroplanner/objective/StrategyLevel.java
+9
-5
ResultWriter.java
src/main/java/com/aps/macroplanner/output/ResultWriter.java
+1
-1
SolutionPrinter.java
...ain/java/com/aps/macroplanner/output/SolutionPrinter.java
+43
-32
No files found.
src/main/java/com/aps/controller/MacroPlannerResultController.java
View file @
fd3b9e49
...
@@ -532,7 +532,8 @@ public class MacroPlannerResultController {
...
@@ -532,7 +532,8 @@ public class MacroPlannerResultController {
description
=
"执行数据库转换→数据验证→MIP建模→分层求解→JSON结果保存的完整流程。"
description
=
"执行数据库转换→数据验证→MIP建模→分层求解→JSON结果保存的完整流程。"
+
"耗时通常数秒到数分钟, 取决于数据规模。"
)
+
"耗时通常数秒到数分钟, 取决于数据规模。"
)
public
R
<
Map
<
String
,
Object
>>
runOptimization
(
public
R
<
Map
<
String
,
Object
>>
runOptimization
(
@RequestParam
(
"sceneId"
)
@Parameter
(
description
=
"场景ID"
,
required
=
true
)
String
sceneId
)
{
@RequestParam
(
"sceneId"
)
@Parameter
(
description
=
"场景ID"
,
required
=
true
)
String
sceneId
,
@RequestParam
(
"kpiSetting"
)
@Parameter
(
description
=
"kpi设置"
,
required
=
true
)
Integer
kpiSetting
)
{
if
(
sceneId
==
null
||
sceneId
.
trim
().
isEmpty
())
{
if
(
sceneId
==
null
||
sceneId
.
trim
().
isEmpty
())
{
return
R
.
failed
(
"sceneId不能为空"
);
return
R
.
failed
(
"sceneId不能为空"
);
}
}
...
@@ -543,7 +544,7 @@ public class MacroPlannerResultController {
...
@@ -543,7 +544,7 @@ public class MacroPlannerResultController {
try
{
try
{
// 1. 数据转换: 数据库工艺物料类 → macroplanner 实体
// 1. 数据转换: 数据库工艺物料类 → macroplanner 实体
TestDataBuilder
data
=
macroPlannerDataConverter
.
convert
(
sid
);
TestDataBuilder
data
=
macroPlannerDataConverter
.
convert
(
sid
,
kpiSetting
);
result
.
put
(
"dataSummary"
,
buildDataSummary
(
data
));
result
.
put
(
"dataSummary"
,
buildDataSummary
(
data
));
// 2. 数据验证
// 2. 数据验证
...
@@ -560,7 +561,7 @@ public class MacroPlannerResultController {
...
@@ -560,7 +561,7 @@ public class MacroPlannerResultController {
long
solveStart
=
System
.
currentTimeMillis
();
long
solveStart
=
System
.
currentTimeMillis
();
MacroPlannerOptimizer
optimizer
=
new
MacroPlannerOptimizer
(
data
);
MacroPlannerOptimizer
optimizer
=
new
MacroPlannerOptimizer
(
data
);
optimizer
.
buildModel
();
optimizer
.
buildModel
();
optimizer
.
solve
();
optimizer
.
solve
(
sid
);
long
solveEnd
=
System
.
currentTimeMillis
();
long
solveEnd
=
System
.
currentTimeMillis
();
// 4. 保存结果到 JSON 文件
// 4. 保存结果到 JSON 文件
...
...
src/main/java/com/aps/macroplanner/MacroPlannerDataConverterRunner.java
View file @
fd3b9e49
...
@@ -30,7 +30,7 @@ import org.springframework.context.ApplicationContext;
...
@@ -30,7 +30,7 @@ import org.springframework.context.ApplicationContext;
public
class
MacroPlannerDataConverterRunner
{
public
class
MacroPlannerDataConverterRunner
{
public
static
void
main
(
String
[]
args
)
{
public
static
void
main
(
String
[]
args
)
{
String
sceneId
=
(
args
.
length
>
0
)
?
args
[
0
]
:
"
ff8a2f2b-e134-4ec2-8e2e-b77e830c9e64
"
;
String
sceneId
=
(
args
.
length
>
0
)
?
args
[
0
]
:
"
15b4cf5b-1224-418e-aa6f-1c643ea78d3c
"
;
System
.
out
.
println
(
"===== MACROPLANNER DATA CONVERTER RUNNER START ====="
);
System
.
out
.
println
(
"===== MACROPLANNER DATA CONVERTER RUNNER START ====="
);
System
.
out
.
println
(
"SceneId: "
+
sceneId
);
System
.
out
.
println
(
"SceneId: "
+
sceneId
);
...
@@ -40,7 +40,7 @@ public class MacroPlannerDataConverterRunner {
...
@@ -40,7 +40,7 @@ public class MacroPlannerDataConverterRunner {
MacroPlannerDataConverter
converter
=
ctx
.
getBean
(
MacroPlannerDataConverter
.
class
);
MacroPlannerDataConverter
converter
=
ctx
.
getBean
(
MacroPlannerDataConverter
.
class
);
// 1. 转换数据: 数据库工艺物料类 → macroplanner 实体
// 1. 转换数据: 数据库工艺物料类 → macroplanner 实体
TestDataBuilder
data
=
converter
.
convert
(
sceneId
);
TestDataBuilder
data
=
converter
.
convert
(
sceneId
,
8
);
// 2. 打印转换结果规模
// 2. 打印转换结果规模
System
.
out
.
println
(
"Data converted:"
);
System
.
out
.
println
(
"Data converted:"
);
...
...
src/main/java/com/aps/macroplanner/MacroPlannerOptimizer.java
View file @
fd3b9e49
package
com
.
aps
.
macroplanner
;
package
com
.
aps
.
macroplanner
;
import
com.aps.common.util.FileHelper
;
import
com.aps.common.util.FileHelper
;
import
com.aps.macroplanner.objective.KpiLib
;
import
com.aps.macroplanner.objective.KpiSetting
;
import
com.aps.macroplanner.output.dto.OptimizationResult
;
import
com.aps.macroplanner.output.dto.OptimizationResult
;
import
com.aps.service.MpPispipResultPersistenceService
;
import
com.aps.service.MpPispipResultPersistenceService
;
import
com.google.ortools.Loader
;
import
com.google.ortools.Loader
;
...
@@ -25,9 +27,12 @@ import java.time.LocalDateTime;
...
@@ -25,9 +27,12 @@ import java.time.LocalDateTime;
import
java.time.format.DateTimeFormatter
;
import
java.time.format.DateTimeFormatter
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.TreeMap
;
import
java.util.logging.ConsoleHandler
;
import
java.util.logging.ConsoleHandler
;
import
java.util.logging.Level
;
import
java.util.logging.Level
;
import
java.util.logging.Logger
;
import
java.util.logging.Logger
;
import
java.util.stream.Collectors
;
/**
/**
* MacroPlanner 核心优化器 — 供应链产能规划混合整数规划(MIP)模型
* MacroPlanner 核心优化器 — 供应链产能规划混合整数规划(MIP)模型
...
@@ -197,11 +202,11 @@ public class MacroPlannerOptimizer {
...
@@ -197,11 +202,11 @@ public class MacroPlannerOptimizer {
writeLog
(
" [OK] 约束与KPI汇总创建完成"
);
writeLog
(
" [OK] 约束与KPI汇总创建完成"
);
// 3. 目标函数 (加权求和)
// 3. 目标函数 (加权求和)
ObjectiveBuilder
.
build
(
model
,
data
);
//
ObjectiveBuilder.build(model, data);
writeLog
(
" [OK] 目标函数创建完成"
);
writeLog
(
" [OK] 目标函数创建完成"
);
// 4. 导出 LP 模型文件
// 4. 导出 LP 模型文件
exportLpModel
();
//
exportLpModel();
writeLog
(
"\n模型统计: 变量="
+
model
.
getSolver
().
numVariables
()
writeLog
(
"\n模型统计: 变量="
+
model
.
getSolver
().
numVariables
()
+
", 约束="
+
model
.
getSolver
().
numConstraints
()
+
"\n"
);
+
", 约束="
+
model
.
getSolver
().
numConstraints
()
+
"\n"
);
...
@@ -279,12 +284,13 @@ public class MacroPlannerOptimizer {
...
@@ -279,12 +284,13 @@ public class MacroPlannerOptimizer {
final
long
startTimeMs
=
System
.
currentTimeMillis
();
final
long
startTimeMs
=
System
.
currentTimeMillis
();
// ========== 1. 前置参数校验 ==========
// ========== 1. 前置参数校验 ==========
KPIWeights
w
=
data
.
getKpiWeights
();
List
<
KpiSetting
>
w
=
data
.
getKpiSettings
();
List
<
StrategyLevel
>
levels
=
new
ArrayList
<>();
if
(
w
==
null
)
{
if
(
w
==
null
)
{
writeLog
(
"[ERROR] KPI权重配置为空,终止求解"
);
levels
=
defineLevels
(
data
.
getKpiWeights
());
return
;
}
else
{
levels
=
getKpiLevels
(
w
);
}
}
List
<
StrategyLevel
>
levels
=
defineLevels
(
w
);
if
(
levels
==
null
||
levels
.
isEmpty
())
{
if
(
levels
==
null
||
levels
.
isEmpty
())
{
writeLog
(
"[ERROR] 分层策略为空,终止求解"
);
writeLog
(
"[ERROR] 分层策略为空,终止求解"
);
return
;
return
;
...
@@ -427,31 +433,64 @@ public class MacroPlannerOptimizer {
...
@@ -427,31 +433,64 @@ public class MacroPlannerOptimizer {
// === Level 1: 需求满足 (最高优先级, 严格分层, slack=0%) ===
// === Level 1: 需求满足 (最高优先级, 严格分层, slack=0%) ===
StrategyLevel
l1
=
new
StrategyLevel
(
1
,
"需求满足"
,
0.0
);
StrategyLevel
l1
=
new
StrategyLevel
(
1
,
"需求满足"
,
0.0
);
l1
.
addKPI
(
"需求缺口"
,
model
.
getTotalFulfillment
(),
w
.
getFulfillmentWeight
());
l1
.
addKPI
(
KpiLib
.
Fulfillment
.
getEn
(),
KpiLib
.
Fulfillment
.
getCn
()
,
model
.
getTotalFulfillment
(),
w
.
getFulfillmentWeight
());
levels
.
add
(
l1
);
levels
.
add
(
l1
);
// === Level 2: 产能约束 (物理硬约束, 严格分层, slack=0%) ===
// === Level 2: 产能约束 (物理硬约束, 严格分层, slack=0%) ===
StrategyLevel
l2
=
new
StrategyLevel
(
2
,
"产能约束"
,
0.0
);
StrategyLevel
l2
=
new
StrategyLevel
(
2
,
"产能约束"
,
0.0
);
l2
.
addKPI
(
"产能超载"
,
model
.
getTotalUnitCapacity
(),
w
.
getUnitCapacityWeight
());
l2
.
addKPI
(
KpiLib
.
UnitCapacity
.
getEn
(),
KpiLib
.
Fulfillment
.
getCn
()
,
model
.
getTotalUnitCapacity
(),
w
.
getUnitCapacityWeight
());
levels
.
add
(
l2
);
levels
.
add
(
l2
);
// === Level 3: 业务KPI (允许 5% 退化, slack=5%) ===
// === Level 3: 业务KPI (允许 5% 退化, slack=5%) ===
StrategyLevel
l3
=
new
StrategyLevel
(
3
,
"业务KPI"
,
0.05
);
StrategyLevel
l3
=
new
StrategyLevel
(
3
,
"业务KPI"
,
0.05
);
l3
.
addKPI
(
"批次偏差"
,
model
.
getTotalLotSize
(),
w
.
getLotSizeWeight
());
l3
.
addKPI
(
KpiLib
.
LotSize
.
getEn
(),
KpiLib
.
LotSize
.
getCn
()
,
model
.
getTotalLotSize
(),
w
.
getLotSizeWeight
());
l3
.
addKPI
(
"目标库存偏差"
,
model
.
getTotalTargetInvLevel
(),
w
.
getTargetInventoryLevelWeight
());
l3
.
addKPI
(
KpiLib
.
TargetInvLevel
.
getEn
(),
KpiLib
.
TargetInvLevel
.
getCn
()
,
model
.
getTotalTargetInvLevel
(),
w
.
getTargetInventoryLevelWeight
());
l3
.
addKPI
(
"供应目标偏差"
,
model
.
getTotalSupplyTarget
(),
w
.
getSupplyTargetWeight
());
l3
.
addKPI
(
KpiLib
.
SupplyTarget
.
getEn
(),
KpiLib
.
SupplyTarget
.
getCn
()
,
model
.
getTotalSupplyTarget
(),
w
.
getSupplyTargetWeight
());
l3
.
addKPI
(
"销售优先级"
,
model
.
getTotalSalesDemandPriority
(),
l3
.
addKPI
(
KpiLib
.
SalesDemandPriority
.
getEn
(),
KpiLib
.
SalesDemandPriority
.
getCn
()
,
model
.
getTotalSalesDemandPriority
(),
w
.
getSalesDemandPriorityWeight
(),
true
);
// 负系数 = 最大化
w
.
getSalesDemandPriorityWeight
(),
true
);
// 负系数 = 最大化
levels
.
add
(
l3
);
levels
.
add
(
l3
);
// === Level 4: 软约束 (允许 10% 退化, slack=10%) ===
// === Level 4: 软约束 (允许 10% 退化, slack=10%) ===
StrategyLevel
l4
=
new
StrategyLevel
(
4
,
"软约束"
,
0.10
);
StrategyLevel
l4
=
new
StrategyLevel
(
4
,
"软约束"
,
0.10
);
l4
.
addKPI
(
"超库存"
,
model
.
getTotalMaxInventoryLevel
(),
w
.
getMaxInventoryLevelWeight
());
l4
.
addKPI
(
KpiLib
.
MaxInventoryLevel
.
getEn
(),
KpiLib
.
MaxInventoryLevel
.
getCn
()
,
model
.
getTotalMaxInventoryLevel
(),
w
.
getMaxInventoryLevelWeight
());
l4
.
addKPI
(
"欠库存"
,
model
.
getTotalMinInventoryLevel
(),
w
.
getMinInventoryLevelWeight
());
l4
.
addKPI
(
KpiLib
.
MinInventoryLevel
.
getEn
(),
KpiLib
.
MinInventoryLevel
.
getCn
()
,
model
.
getTotalMinInventoryLevel
(),
w
.
getMinInventoryLevelWeight
());
l4
.
addKPI
(
"最小供应不足"
,
model
.
getTotalMinSupply
(),
w
.
getMinSupplyWeight
());
l4
.
addKPI
(
KpiLib
.
MinSupply
.
getEn
(),
KpiLib
.
MinSupply
.
getCn
()
,
model
.
getTotalMinSupply
(),
w
.
getMinSupplyWeight
());
l4
.
addKPI
(
"最大供应超出"
,
model
.
getTotalMaxSupply
(),
w
.
getMaxSupplyWeight
());
l4
.
addKPI
(
KpiLib
.
MaxSupply
.
getEn
(),
KpiLib
.
MaxSupply
.
getCn
()
,
model
.
getTotalMaxSupply
(),
w
.
getMaxSupplyWeight
());
levels
.
add
(
l4
);
levels
.
add
(
l4
);
return
levels
;
}
private
List
<
StrategyLevel
>
getKpiLevels
(
List
<
KpiSetting
>
kpis
)
{
List
<
StrategyLevel
>
levels
=
new
ArrayList
<>();
Map
<
Integer
,
List
<
KpiSetting
>>
groupByLevelMap
=
kpis
.
stream
()
.
collect
(
Collectors
.
groupingBy
(
KpiSetting:
:
getLevel
,
//如果改成getter
TreeMap:
:
new
,
Collectors
.
toList
()
));
for
(
Map
.
Entry
<
Integer
,
List
<
KpiSetting
>>
entry
:
groupByLevelMap
.
entrySet
())
{
Integer
currentLevel
=
entry
.
getKey
();
List
<
KpiSetting
>
kpiGroup
=
entry
.
getValue
();
StrategyLevel
level
=
new
StrategyLevel
(
currentLevel
,
""
,
0.05
);
// 遍历当前分组里面每一个KPI
for
(
KpiSetting
kpi
:
kpiGroup
)
{
level
.
addKPI
(
kpi
.
code
,
kpi
.
name
,
model
.
getKpi
(
kpi
.
code
),
kpi
.
weight
);
}
levels
.
add
(
level
);
}
return
levels
;
return
levels
;
}
}
...
...
src/main/java/com/aps/macroplanner/data/MacroPlannerDataConverter.java
View file @
fd3b9e49
...
@@ -2,10 +2,12 @@ package com.aps.macroplanner.data;
...
@@ -2,10 +2,12 @@ package com.aps.macroplanner.data;
import
com.aps.common.util.ParamValidator
;
import
com.aps.common.util.ParamValidator
;
import
com.aps.entity.*
;
import
com.aps.entity.*
;
import
com.aps.macroplanner.objective.KpiSetting
;
import
com.aps.macroplanner.scene.MacroSceneContext
;
import
com.aps.macroplanner.scene.MacroSceneContext
;
import
com.aps.entity.basic.Material
;
import
com.aps.entity.basic.Material
;
import
com.aps.entity.basic.MaterialSupply
;
import
com.aps.entity.basic.MaterialSupply
;
import
com.aps.mapper.EquipShiftCapacityMapper
;
import
com.aps.mapper.EquipShiftCapacityMapper
;
import
com.aps.service.KpiCategoryItemService
;
import
com.aps.service.PlanPeriodService
;
import
com.aps.service.PlanPeriodService
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
com.fasterxml.jackson.core.type.TypeReference
;
import
com.fasterxml.jackson.core.type.TypeReference
;
...
@@ -131,6 +133,9 @@ public class MacroPlannerDataConverter {
...
@@ -131,6 +133,9 @@ public class MacroPlannerDataConverter {
@Autowired
@Autowired
private
PlanPeriodService
planPeriodService
;
private
PlanPeriodService
planPeriodService
;
@Autowired
private
KpiCategoryItemService
kpiCategoryItemService
;
// ==================== 公开入口 ====================
// ==================== 公开入口 ====================
/**
/**
...
@@ -139,16 +144,16 @@ public class MacroPlannerDataConverter {
...
@@ -139,16 +144,16 @@ public class MacroPlannerDataConverter {
* @param sceneId 场景ID
* @param sceneId 场景ID
* @return 填充好的 TestDataBuilder, 可直接传给 MacroPlannerOptimizer
* @return 填充好的 TestDataBuilder, 可直接传给 MacroPlannerOptimizer
*/
*/
public
TestDataBuilder
convert
(
String
sceneId
)
{
public
TestDataBuilder
convert
(
String
sceneId
,
Integer
kpiSetting
)
{
return
MacroSceneContext
.
execute
(
sceneId
,
()
->
convertScoped
(
sceneId
));
return
MacroSceneContext
.
execute
(
sceneId
,
()
->
convertScoped
(
sceneId
,
kpiSetting
));
}
}
private
TestDataBuilder
convertScoped
(
String
sceneId
)
{
private
TestDataBuilder
convertScoped
(
String
sceneId
,
Integer
kpiSetting
)
{
log
.
info
(
"开始转换场景数据到 macroplanner: sceneId={}"
,
sceneId
);
log
.
info
(
"开始转换场景数据到 macroplanner: sceneId={}"
,
sceneId
);
ConvertContext
ctx
=
loadRawData
(
sceneId
);
ConvertContext
ctx
=
loadRawData
(
sceneId
,
kpiSetting
);
buildMaterials
(
ctx
);
buildMaterials
(
ctx
);
buildDailyCapacityByUnitId
(
ctx
);
buildDailyCapacityByUnitId
(
ctx
);
...
@@ -167,7 +172,7 @@ public class MacroPlannerDataConverter {
...
@@ -167,7 +172,7 @@ public class MacroPlannerDataConverter {
// ==================== 步骤1: 数据加载 ====================
// ==================== 步骤1: 数据加载 ====================
private
ConvertContext
loadRawData
(
String
sceneId
)
{
private
ConvertContext
loadRawData
(
String
sceneId
,
Integer
kpiSetting
)
{
ConvertContext
ctx
=
new
ConvertContext
();
ConvertContext
ctx
=
new
ConvertContext
();
// 0. 读取时间配置, 计算 horizonEnd
// 0. 读取时间配置, 计算 horizonEnd
...
@@ -395,6 +400,14 @@ public class MacroPlannerDataConverter {
...
@@ -395,6 +400,14 @@ public class MacroPlannerDataConverter {
}
}
log
.
info
(
"加载设备产能日历(EquipShiftCapacity): {} 条 (时间范围过滤)"
,
ctx
.
equipShiftCapacities
.
size
());
log
.
info
(
"加载设备产能日历(EquipShiftCapacity): {} 条 (时间范围过滤)"
,
ctx
.
equipShiftCapacities
.
size
());
ctx
.
kpiCategoryItems
=
kpiCategoryItemService
.
list
(
new
LambdaQueryWrapper
<
KpiCategoryItem
>()
.
eq
(
KpiCategoryItem:
:
getIsDeleted
,
0
)
.
eq
(
KpiCategoryItem:
:
getCategoryId
,
kpiSetting
));
return
ctx
;
return
ctx
;
}
}
...
@@ -720,7 +733,7 @@ public class MacroPlannerDataConverter {
...
@@ -720,7 +733,7 @@ public class MacroPlannerDataConverter {
fillPeriodsAndUnitPeriods
(
ctx
);
fillPeriodsAndUnitPeriods
(
ctx
);
fillSalesDemands
(
ctx
);
fillSalesDemands
(
ctx
);
fillInventorySpecsAndSupplySpecs
(
ctx
);
fillInventorySpecsAndSupplySpecs
(
ctx
);
fillKpiWeights
();
fillKpiWeights
(
ctx
);
}
}
// ---------- 步骤3: Product + StockingPoint ----------
// ---------- 步骤3: Product + StockingPoint ----------
...
@@ -1499,21 +1512,15 @@ public class MacroPlannerDataConverter {
...
@@ -1499,21 +1512,15 @@ public class MacroPlannerDataConverter {
// ---------- 步骤10: KPIWeights (默认值, 复用 RoutingTestDataBuilder) ----------
// ---------- 步骤10: KPIWeights (默认值, 复用 RoutingTestDataBuilder) ----------
private
void
fillKpiWeights
()
{
private
void
fillKpiWeights
(
ConvertContext
ctx
)
{
kpiWeights
=
new
KPIWeights
(
100.0
,
// fulfillmentWeight
List
<
KpiSetting
>
kpis
=
new
ArrayList
<>();
10.0
,
// lotSizeWeight
for
(
KpiCategoryItem
item:
ctx
.
kpiCategoryItems
)
{
5.0
,
// maxInventoryLevelWeight
KpiSetting
kpi
=
new
KpiSetting
(
item
.
getKpiName
(),
item
.
getKpiCode
(),
item
.
getWeight
().
doubleValue
(),
item
.
getSort
().
intValue
());
5.0
,
// minInventoryLevelWeight
kpis
.
add
(
kpi
);
8.0
,
// targetInventoryLevelWeight
}
3.0
,
// unitCapacityWeight
8.0
,
// supplyTargetWeight
kpiSettings
=
kpis
;
5.0
,
// minSupplyWeight
5.0
,
// maxSupplyWeight
1.0
,
// salesDemandPriorityWeight
20.0
,
// postponementPenaltyWeight
5.0
// processMaxQuantityWeight
);
}
}
// ---------- 工具方法 ----------
// ---------- 工具方法 ----------
...
@@ -1549,6 +1556,11 @@ public class MacroPlannerDataConverter {
...
@@ -1549,6 +1556,11 @@ public class MacroPlannerDataConverter {
List
<
Equipinfo
>
equipinfos
=
new
ArrayList
<>();
List
<
Equipinfo
>
equipinfos
=
new
ArrayList
<>();
List
<
PlanResource
>
planResources
=
new
ArrayList
<>();
List
<
PlanResource
>
planResources
=
new
ArrayList
<>();
List
<
EquipShiftCapacity
>
equipShiftCapacities
=
new
ArrayList
<>();
List
<
EquipShiftCapacity
>
equipShiftCapacities
=
new
ArrayList
<>();
List
<
KpiCategoryItem
>
kpiCategoryItems
=
new
ArrayList
<>();
LocalDateTime
baseTime
;
LocalDateTime
baseTime
;
/** 计划截止日期 (不包含), 由 baseTime + periodDimension + periodCount 计算 */
/** 计划截止日期 (不包含), 由 baseTime + periodDimension + periodCount 计算 */
LocalDate
horizonEnd
;
LocalDate
horizonEnd
;
...
...
src/main/java/com/aps/macroplanner/data/TestDataBuilder.java
View file @
fd3b9e49
package
com
.
aps
.
macroplanner
.
data
;
package
com
.
aps
.
macroplanner
.
data
;
import
com.aps.macroplanner.objective.KpiSetting
;
import
java.util.*
;
import
java.util.*
;
import
java.time.LocalDate
;
import
java.time.LocalDate
;
...
@@ -57,7 +59,12 @@ public class TestDataBuilder {
...
@@ -57,7 +59,12 @@ public class TestDataBuilder {
/** 在途供应列表 (供应商已发货, 将在未来周期到货的固定量) */
/** 在途供应列表 (供应商已发货, 将在未来周期到货的固定量) */
protected
final
List
<
InTransitSupply
>
inTransitSupplies
=
new
ArrayList
<>();
protected
final
List
<
InTransitSupply
>
inTransitSupplies
=
new
ArrayList
<>();
/** KPI 权重 (子类可访问) */
/** KPI 权重 (子类可访问) */
protected
KPIWeights
kpiWeights
;
protected
KPIWeights
kpiWeights
;
protected
List
<
KpiSetting
>
kpiSettings
=
new
ArrayList
<>();
/** 初始库存: key = productId_spId, value = 初始库存量 (子类可访问) */
/** 初始库存: key = productId_spId, value = 初始库存量 (子类可访问) */
/** 初始库存列表 (子类可访问) */
/** 初始库存列表 (子类可访问) */
...
@@ -278,21 +285,20 @@ public class TestDataBuilder {
...
@@ -278,21 +285,20 @@ public class TestDataBuilder {
supplySpecs
.
add
(
new
SupplySpec
(
"Supply-R2-Alt"
,
40.0
,
20.0
,
80.0
,
supplySpecs
.
add
(
new
SupplySpec
(
"Supply-R2-Alt"
,
40.0
,
20.0
,
80.0
,
false
,
Arrays
.
asList
(
opR2Alt
)));
false
,
Arrays
.
asList
(
opR2Alt
)));
// === KPI 权重 ===
List
<
KpiSetting
>
kpis
=
new
ArrayList
<>();
kpiWeights
=
new
KPIWeights
(
kpis
.
add
(
new
KpiSetting
(
"需求满足"
,
"Fulfillment"
,
100
,
1
));
100.0
,
// fulfillmentWeight
kpis
.
add
(
new
KpiSetting
(
"产能"
,
"UnitCapacity"
,
20
,
1
));
10.0
,
// lotSizeWeight
kpis
.
add
(
new
KpiSetting
(
"批次"
,
"LotSize"
,
10
,
1
));
5.0
,
// maxInventoryLevelWeight
kpis
.
add
(
new
KpiSetting
(
"目标库存"
,
"TargetInvLevel"
,
8
,
1
));
5.0
,
// minInventoryLevelWeight
kpis
.
add
(
new
KpiSetting
(
"供应目标"
,
"SupplyTarget"
,
8
,
1
));
8.0
,
// targetInventoryLevelWeight
kpis
.
add
(
new
KpiSetting
(
"销售优先级"
,
"SalesDemandPriority"
,
1
,
1
));
20.0
,
// unitCapacityWeight
kpis
.
add
(
new
KpiSetting
(
"最大库存"
,
"MaxInventoryLevel"
,
5
,
1
));
8.0
,
// supplyTargetWeight
kpis
.
add
(
new
KpiSetting
(
"最小库存"
,
"MinInventoryLevel"
,
5
,
1
));
5.0
,
// minSupplyWeight
kpis
.
add
(
new
KpiSetting
(
"最小供应"
,
"MinSupply"
,
5
,
1
));
5.0
,
// maxSupplyWeight
kpis
.
add
(
new
KpiSetting
(
"最大供应"
,
"MaxSupply"
,
5
,
1
));
1.0
,
// salesDemandPriorityWeight
kpiSettings
=
kpis
;
20.0
,
// postponementPenaltyWeight
5.0
// processMaxQuantityWeight
);
}
}
// ==================== Getters ====================
// ==================== Getters ====================
...
@@ -395,8 +401,11 @@ public class TestDataBuilder {
...
@@ -395,8 +401,11 @@ public class TestDataBuilder {
public
List
<
InitialInventory
>
getInitialInventories
()
{
return
initialInventories
;
}
public
List
<
InitialInventory
>
getInitialInventories
()
{
return
initialInventories
;
}
public
List
<
KpiSetting
>
getKpiSettings
()
{
return
kpiSettings
;
}
public
KPIWeights
getKpiWeights
()
{
return
kpiWeights
;
}
public
KPIWeights
getKpiWeights
()
{
return
kpiWeights
;
}
/**
/**
* 获取初始库存量
* 获取初始库存量
* @param productId 产品ID
* @param productId 产品ID
...
...
src/main/java/com/aps/macroplanner/model/MacroPlannerModel.java
View file @
fd3b9e49
package
com
.
aps
.
macroplanner
.
model
;
package
com
.
aps
.
macroplanner
.
model
;
import
com.aps.macroplanner.objective.KpiLib
;
import
com.google.ortools.linearsolver.MPSolver
;
import
com.google.ortools.linearsolver.MPSolver
;
import
com.google.ortools.linearsolver.MPVariable
;
import
com.google.ortools.linearsolver.MPVariable
;
...
@@ -170,4 +171,46 @@ public class MacroPlannerModel {
...
@@ -170,4 +171,46 @@ public class MacroPlannerModel {
public
MPVariable
getTotalSalesDemandPriority
()
{
return
totalSalesDemandPriority
;
}
public
MPVariable
getTotalSalesDemandPriority
()
{
return
totalSalesDemandPriority
;
}
public
MPVariable
getTotalPostponementPenalty
()
{
return
totalPostponementPenalty
;
}
public
MPVariable
getTotalPostponementPenalty
()
{
return
totalPostponementPenalty
;
}
public
MPVariable
getTotalProcessMaxQuantity
()
{
return
totalProcessMaxQuantity
;
}
public
MPVariable
getTotalProcessMaxQuantity
()
{
return
totalProcessMaxQuantity
;
}
public
MPVariable
getKpi
(
String
code
)
{
MPVariable
kpivar
=
null
;
KpiLib
kpi
=
KpiLib
.
ofEn
(
code
);
switch
(
kpi
)
{
case
Fulfillment:
kpivar
=
totalFulfillment
;
break
;
case
UnitCapacity:
kpivar
=
totalUnitCapacity
;
break
;
case
LotSize:
kpivar
=
totalLotSize
;
break
;
case
TargetInvLevel:
kpivar
=
totalTargetInvLevel
;
break
;
case
SupplyTarget:
kpivar
=
totalSupplyTarget
;
break
;
case
SalesDemandPriority:
kpivar
=
totalSalesDemandPriority
;
break
;
case
MaxInventoryLevel:
kpivar
=
totalMaxInventoryLevel
;
break
;
case
MinInventoryLevel:
kpivar
=
totalMinInventoryLevel
;
break
;
case
MinSupply:
kpivar
=
totalMinSupply
;
break
;
case
MaxSupply:
kpivar
=
totalMaxSupply
;
break
;
}
return
kpivar
;
}
}
}
\ No newline at end of file
src/main/java/com/aps/macroplanner/objective/KpiLib.java
0 → 100644
View file @
fd3b9e49
package
com
.
aps
.
macroplanner
.
objective
;
import
java.util.Arrays
;
import
java.util.Map
;
import
java.util.stream.Collectors
;
/**
* 作者:佟礼
* 时间:2026-09-22
*/
public
enum
KpiLib
{
Fulfillment
(
"需求满足"
),
UnitCapacity
(
"产能"
),
LotSize
(
"批次"
),
TargetInvLevel
(
"目标库存"
),
SupplyTarget
(
"供应目标"
),
SalesDemandPriority
(
"销售优先级"
),
MaxInventoryLevel
(
"最大库存"
),
MinInventoryLevel
(
"最小库存"
),
MinSupply
(
"最小供应"
),
MaxSupply
(
"最大供应"
);
private
final
String
en
;
private
final
String
cn
;
KpiLib
(
String
cn
)
{
this
.
en
=
this
.
name
();
this
.
cn
=
cn
;
}
public
String
getEn
()
{
return
en
;
}
public
String
getCn
()
{
return
cn
;
}
/**
* 转为 Map<英文编码,中文名称>,用于批量翻译、表头转换
*/
public
static
Map
<
String
,
String
>
toEnCnMap
()
{
return
Arrays
.
stream
(
KpiLib
.
values
())
.
collect
(
Collectors
.
toMap
(
KpiLib:
:
getEn
,
KpiLib:
:
getCn
));
}
/**
* 根据英文编码获取枚举,返回null则未匹配
*/
public
static
KpiLib
ofEn
(
String
enName
)
{
for
(
KpiLib
dict
:
KpiLib
.
values
())
{
if
(
dict
.
getEn
().
equals
(
enName
))
{
return
dict
;
}
}
return
null
;
}
}
src/main/java/com/aps/macroplanner/objective/KpiSetting.java
0 → 100644
View file @
fd3b9e49
package
com
.
aps
.
macroplanner
.
objective
;
import
com.google.ortools.linearsolver.MPVariable
;
import
lombok.Data
;
/**
* 作者:佟礼
* 时间:2026-09-21
*/
@Data
public
class
KpiSetting
{
public
final
String
code
;
/**
* 该 KPI 在当前层级内的权重
*/
public
final
double
weight
;
public
final
Integer
level
;
/**
* KPI 名称
*/
public
final
String
name
;
public
KpiSetting
(
String
name
,
String
code
,
double
weight
,
Integer
level
)
{
this
.
name
=
name
;
this
.
code
=
code
;
this
.
weight
=
weight
;
this
.
level
=
level
;
}
}
src/main/java/com/aps/macroplanner/objective/ObjectiveBuilder.java
View file @
fd3b9e49
...
@@ -54,63 +54,63 @@ public class ObjectiveBuilder {
...
@@ -54,63 +54,63 @@ public class ObjectiveBuilder {
*/
*/
public
static
void
build
(
MacroPlannerModel
model
,
TestDataBuilder
data
)
{
public
static
void
build
(
MacroPlannerModel
model
,
TestDataBuilder
data
)
{
MPObjective
objective
=
model
.
getSolver
().
objective
();
MPObjective
objective
=
model
.
getSolver
().
objective
();
KPIWeights
w
=
data
.
getKpiWeights
();
//
KPIWeights w = data.getKpiWeights();
// ===== 惩罚项 (最小化: 正系数) =====
// ===== 惩罚项 (最小化: 正系数) =====
// Fulfillment: 需求松弛惩罚, 权重 100.0 (最高优先级)
// Fulfillment: 需求松弛惩罚, 权重 100.0 (最高优先级)
// 含义: 所有周期所有产品的需求松弛总和。值越小表示需求满足越好。
// 含义: 所有周期所有产品的需求松弛总和。值越小表示需求满足越好。
objective
.
setCoefficient
(
model
.
getTotalFulfillment
(),
w
.
getFulfillmentWeight
());
//
objective.setCoefficient(model.getTotalFulfillment(), w.getFulfillmentWeight());
//
// LotSize: 批次偏差惩罚, 权重 10.0
//
// LotSize: 批次偏差惩罚, 权重 10.0
// 含义: 所有操作所有周期的批次大小偏差总和。惩罚不按批次生产的量。
//
// 含义: 所有操作所有周期的批次大小偏差总和。惩罚不按批次生产的量。
objective
.
setCoefficient
(
model
.
getTotalLotSize
(),
w
.
getLotSizeWeight
());
//
objective.setCoefficient(model.getTotalLotSize(), w.getLotSizeWeight());
//
// MaxInventoryLevel: 超库存惩罚, 权重 5.0
//
// MaxInventoryLevel: 超库存惩罚, 权重 5.0
// 含义: 所有超标库存的总和。惩罚库存超过最大水平。
//
// 含义: 所有超标库存的总和。惩罚库存超过最大水平。
objective
.
setCoefficient
(
model
.
getTotalMaxInventoryLevel
(),
w
.
getMaxInventoryLevelWeight
());
//
objective.setCoefficient(model.getTotalMaxInventoryLevel(), w.getMaxInventoryLevelWeight());
//
// MinInventoryLevel: 欠库存(安全库存)惩罚, 权重 5.0
//
// MinInventoryLevel: 欠库存(安全库存)惩罚, 权重 5.0
// 含义: 所有低于最小库存的总和。惩罚库存低于安全水平。
//
// 含义: 所有低于最小库存的总和。惩罚库存低于安全水平。
objective
.
setCoefficient
(
model
.
getTotalMinInventoryLevel
(),
w
.
getMinInventoryLevelWeight
());
//
objective.setCoefficient(model.getTotalMinInventoryLevel(), w.getMinInventoryLevelWeight());
//
// TargetInventoryLevel: 目标库存偏差惩罚, 权重 8.0
//
// TargetInventoryLevel: 目标库存偏差惩罚, 权重 8.0
// 含义: 所有低于目标库存的总和。惩罚库存偏离目标水平。
//
// 含义: 所有低于目标库存的总和。惩罚库存偏离目标水平。
objective
.
setCoefficient
(
model
.
getTotalTargetInvLevel
(),
w
.
getTargetInventoryLevelWeight
());
//
objective.setCoefficient(model.getTotalTargetInvLevel(), w.getTargetInventoryLevelWeight());
//
// UnitCapacity: 产能超载惩罚, 权重 3.0
//
// UnitCapacity: 产能超载惩罚, 权重 3.0
// 含义: 所有设备所有周期的产能超载总和。惩罚超负荷生产。
//
// 含义: 所有设备所有周期的产能超载总和。惩罚超负荷生产。
objective
.
setCoefficient
(
model
.
getTotalUnitCapacity
(),
w
.
getUnitCapacityWeight
());
//
objective.setCoefficient(model.getTotalUnitCapacity(), w.getUnitCapacityWeight());
//
// SupplyTarget: 供应目标偏差惩罚, 权重 8.0
//
// SupplyTarget: 供应目标偏差惩罚, 权重 8.0
// 含义: 供应低于目标的总缺口。惩罚供应不足。
//
// 含义: 供应低于目标的总缺口。惩罚供应不足。
objective
.
setCoefficient
(
model
.
getTotalSupplyTarget
(),
w
.
getSupplyTargetWeight
());
//
objective.setCoefficient(model.getTotalSupplyTarget(), w.getSupplyTargetWeight());
//
// MinSupply: 最小供应不足惩罚, 权重 5.0
//
// MinSupply: 最小供应不足惩罚, 权重 5.0
// 含义: 供应低于最小值的总缺口。惩罚供应严重不足。
//
// 含义: 供应低于最小值的总缺口。惩罚供应严重不足。
objective
.
setCoefficient
(
model
.
getTotalMinSupply
(),
w
.
getMinSupplyWeight
());
//
objective.setCoefficient(model.getTotalMinSupply(), w.getMinSupplyWeight());
//
// MaxSupply: 最大供应超出惩罚, 权重 5.0
//
// MaxSupply: 最大供应超出惩罚, 权重 5.0
// 含义: 供应超出最大值的总量。惩罚过度供应。
//
// 含义: 供应超出最大值的总量。惩罚过度供应。
objective
.
setCoefficient
(
model
.
getTotalMaxSupply
(),
w
.
getMaxSupplyWeight
());
//
objective.setCoefficient(model.getTotalMaxSupply(), w.getMaxSupplyWeight());
//
// ===== 收益项 (最大化: 负系数, 因为 OR-Tools 默认最小化) =====
//
// ===== 收益项 (最大化: 负系数, 因为 OR-Tools 默认最小化) =====
//
// SalesDemandPriority: 销售需求优先级, 权重 1.0, 负系数 = 最大化
//
// SalesDemandPriority: 销售需求优先级, 权重 1.0, 负系数 = 最大化
// 含义: 按优先级加权的销售需求满足量。值越大表示满足的需求越多越好。
//
// 含义: 按优先级加权的销售需求满足量。值越大表示满足的需求越多越好。
// 因目标函数是最小化, 此项使用负系数 (最大化 = 加负号后最小化)。
//
// 因目标函数是最小化, 此项使用负系数 (最大化 = 加负号后最小化)。
objective
.
setCoefficient
(
model
.
getTotalSalesDemandPriority
(),
-
w
.
getSalesDemandPriorityWeight
());
//
objective.setCoefficient(model.getTotalSalesDemandPriority(), -w.getSalesDemandPriorityWeight());
//
// PostponementPenalty: 推迟惩罚, 权重 20.0 (简化版固定为 0)
//
// PostponementPenalty: 推迟惩罚, 权重 20.0 (简化版固定为 0)
// 含义: 对应原模型中的延迟销售需求惩罚。当前版本未实现推迟逻辑。
//
// 含义: 对应原模型中的延迟销售需求惩罚。当前版本未实现推迟逻辑。
objective
.
setCoefficient
(
model
.
getTotalPostponementPenalty
(),
w
.
getPostponementPenaltyWeight
());
//
objective.setCoefficient(model.getTotalPostponementPenalty(), w.getPostponementPenaltyWeight());
//
// ProcessMaxQuantity: 过程最大量惩罚, 权重 5.0 (简化版固定为 0)
//
// ProcessMaxQuantity: 过程最大量惩罚, 权重 5.0 (简化版固定为 0)
// 含义: 对应原模型中超出最大过程量的惩罚。当前版本未实现。
//
// 含义: 对应原模型中超出最大过程量的惩罚。当前版本未实现。
objective
.
setCoefficient
(
model
.
getTotalProcessMaxQuantity
(),
w
.
getProcessMaxQuantityWeight
());
//
objective.setCoefficient(model.getTotalProcessMaxQuantity(), w.getProcessMaxQuantityWeight());
//
// 设置为最小化目标
//
// 设置为最小化目标
objective
.
setMinimization
();
//
objective.setMinimization();
}
}
/**
/**
* 计算层级边界约束的上界。
* 计算层级边界约束的上界。
...
...
src/main/java/com/aps/macroplanner/objective/StrategyLevel.java
View file @
fd3b9e49
...
@@ -63,10 +63,14 @@ public class StrategyLevel {
...
@@ -63,10 +63,14 @@ public class StrategyLevel {
/** 是否为负向 KPI (越小越好 = 正常惩罚项; false = 正常惩罚项) */
/** 是否为负向 KPI (越小越好 = 正常惩罚项; false = 正常惩罚项) */
public
final
boolean
isNegative
;
public
final
boolean
isNegative
;
/** 编号 */
public
final
String
code
;
/** KPI 名称 (用于日志) */
/** KPI 名称 (用于日志) */
public
final
String
name
;
public
final
String
name
;
public
KPIEntry
(
String
name
,
MPVariable
variable
,
double
weight
,
boolean
isNegative
)
{
public
KPIEntry
(
String
code
,
String
name
,
MPVariable
variable
,
double
weight
,
boolean
isNegative
)
{
this
.
code
=
code
;
this
.
name
=
name
;
this
.
name
=
name
;
this
.
variable
=
variable
;
this
.
variable
=
variable
;
this
.
weight
=
weight
;
this
.
weight
=
weight
;
...
@@ -102,8 +106,8 @@ public class StrategyLevel {
...
@@ -102,8 +106,8 @@ public class StrategyLevel {
* @param variable KPI 汇总变量
* @param variable KPI 汇总变量
* @param weight 权重 (0 = 跳过)
* @param weight 权重 (0 = 跳过)
*/
*/
public
void
addKPI
(
String
name
,
MPVariable
variable
,
double
weight
)
{
public
void
addKPI
(
String
code
,
String
name
,
MPVariable
variable
,
double
weight
)
{
addKPI
(
name
,
variable
,
weight
,
false
);
addKPI
(
code
,
name
,
variable
,
weight
,
false
);
}
}
/**
/**
...
@@ -114,9 +118,9 @@ public class StrategyLevel {
...
@@ -114,9 +118,9 @@ public class StrategyLevel {
* @param weight 权重 (0 = 跳过)
* @param weight 权重 (0 = 跳过)
* @param isNegative 是否为负向 KPI (true = 最大化, 使用负系数)
* @param isNegative 是否为负向 KPI (true = 最大化, 使用负系数)
*/
*/
public
void
addKPI
(
String
name
,
MPVariable
variable
,
double
weight
,
boolean
isNegative
)
{
public
void
addKPI
(
String
code
,
String
name
,
MPVariable
variable
,
double
weight
,
boolean
isNegative
)
{
if
(
weight
>
0.0
&&
variable
!=
null
)
{
if
(
weight
>
0.0
&&
variable
!=
null
)
{
kpis
.
add
(
new
KPIEntry
(
name
,
variable
,
weight
,
isNegative
));
kpis
.
add
(
new
KPIEntry
(
code
,
name
,
variable
,
weight
,
isNegative
));
}
}
}
}
...
...
src/main/java/com/aps/macroplanner/output/ResultWriter.java
View file @
fd3b9e49
...
@@ -535,7 +535,7 @@ public class ResultWriter {
...
@@ -535,7 +535,7 @@ public class ResultWriter {
// KPI + 统计
// KPI + 统计
writeLog
(
"Kpi"
);
writeLog
(
"Kpi"
);
result
.
setKpis
(
buildKpis
());
//
result.setKpis(buildKpis());
// result.setStatistics(buildStatistics());
// result.setStatistics(buildStatistics());
return
result
;
return
result
;
...
...
src/main/java/com/aps/macroplanner/output/SolutionPrinter.java
View file @
fd3b9e49
...
@@ -3,6 +3,7 @@ package com.aps.macroplanner.output;
...
@@ -3,6 +3,7 @@ package com.aps.macroplanner.output;
import
com.aps.common.util.FileHelper
;
import
com.aps.common.util.FileHelper
;
import
com.aps.macroplanner.data.*
;
import
com.aps.macroplanner.data.*
;
import
com.aps.macroplanner.model.MacroPlannerModel
;
import
com.aps.macroplanner.model.MacroPlannerModel
;
import
com.aps.macroplanner.objective.KpiLib
;
import
com.aps.macroplanner.objective.StrategyLevel
;
import
com.aps.macroplanner.objective.StrategyLevel
;
import
com.google.ortools.linearsolver.MPSolver
;
import
com.google.ortools.linearsolver.MPSolver
;
...
@@ -670,7 +671,7 @@ public class SolutionPrinter {
...
@@ -670,7 +671,7 @@ public class SolutionPrinter {
double
totalWeightedPenalty
=
0.0
;
double
totalWeightedPenalty
=
0.0
;
KPIWeights
w
=
data
.
getKpiWeights
();
KPIWeights
w
=
data
.
getKpiWeights
();
Map
<
String
,
Double
>
weights
=
new
HashMap
<>();
for
(
int
i
=
0
;
i
<
levels
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
levels
.
size
();
i
++)
{
StrategyLevel
level
=
levels
.
get
(
i
);
StrategyLevel
level
=
levels
.
get
(
i
);
double
objValue
=
(
i
<
levelObjValues
.
size
())
?
levelObjValues
.
get
(
i
)
:
0.0
;
double
objValue
=
(
i
<
levelObjValues
.
size
())
?
levelObjValues
.
get
(
i
)
:
0.0
;
...
@@ -679,6 +680,7 @@ public class SolutionPrinter {
...
@@ -679,6 +680,7 @@ public class SolutionPrinter {
List
<
String
>
kpiNames
=
new
ArrayList
<>();
List
<
String
>
kpiNames
=
new
ArrayList
<>();
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
kpiNames
.
add
(
kpi
.
name
);
kpiNames
.
add
(
kpi
.
name
);
weights
.
put
(
kpi
.
code
,
kpi
.
weight
);
}
}
writeLog
(
"%-6d %-14s %-12s %-12.2f %s"
,
writeLog
(
"%-6d %-14s %-12s %-12.2f %s"
,
...
@@ -703,39 +705,48 @@ public class SolutionPrinter {
...
@@ -703,39 +705,48 @@ public class SolutionPrinter {
double
maxSupply
=
model
.
getTotalMaxSupply
().
solutionValue
();
double
maxSupply
=
model
.
getTotalMaxSupply
().
solutionValue
();
double
salesPriority
=
model
.
getTotalSalesDemandPriority
().
solutionValue
();
double
salesPriority
=
model
.
getTotalSalesDemandPriority
().
solutionValue
();
double
totalPenalty
=
fulfillment
*
w
.
getFulfillmentWeight
(
)
double
totalPenalty
=
fulfillment
*
w
eights
.
getOrDefault
(
KpiLib
.
Fulfillment
.
getEn
(),
0
d
)
+
lotSize
*
w
.
getLotSizeWeight
(
)
+
lotSize
*
w
eights
.
getOrDefault
(
KpiLib
.
LotSize
.
getEn
(),
0
d
)
+
maxInv
*
w
.
getMaxInventoryLevelWeight
(
)
+
maxInv
*
w
eights
.
getOrDefault
(
KpiLib
.
MinInventoryLevel
.
getEn
(),
0
d
)
+
minInv
*
w
.
getMinInventoryLevelWeight
(
)
+
minInv
*
w
eights
.
getOrDefault
(
KpiLib
.
MinInventoryLevel
.
getEn
(),
0
d
)
+
targetInv
*
w
.
getTargetInventoryLevelWeight
(
)
+
targetInv
*
w
eights
.
getOrDefault
(
KpiLib
.
TargetInvLevel
.
getEn
(),
0
d
)
+
capacity
*
w
.
getUnitCapacityWeight
(
)
+
capacity
*
w
eights
.
getOrDefault
(
KpiLib
.
UnitCapacity
.
getEn
(),
0
d
)
+
supplyTarget
*
w
.
getSupplyTargetWeight
(
)
+
supplyTarget
*
w
eights
.
getOrDefault
(
KpiLib
.
SupplyTarget
.
getEn
(),
0
d
)
+
minSupply
*
w
.
getMinSupplyWeight
(
)
+
minSupply
*
w
eights
.
getOrDefault
(
KpiLib
.
MinSupply
.
getEn
(),
0
d
)
+
maxSupply
*
w
.
getMaxSupplyWeight
(
)
+
maxSupply
*
w
eights
.
getOrDefault
(
KpiLib
.
MaxSupply
.
getEn
(),
0
d
)
-
salesPriority
*
w
.
getSalesDemandPriorityWeight
(
);
-
salesPriority
*
w
eights
.
getOrDefault
(
KpiLib
.
SalesDemandPriority
.
getEn
(),
0
d
);
writeLog
(
" 加权总惩罚: %.2f"
,
totalPenalty
);
writeLog
(
" 加权总惩罚: %.2f"
,
totalPenalty
);
writeLog
(
" 需求缺口: %.2f (权重%.0f × %.2f)"
,
fulfillment
*
w
.
getFulfillmentWeight
(),
w
.
getFulfillmentWeight
(),
fulfillment
);
for
(
Map
.
Entry
<
String
,
Double
>
entry
:
weights
.
entrySet
())
{
writeLog
(
" 产能超载: %.2f (权重%.0f × %.2f)"
,
String
key
=
entry
.
getKey
();
capacity
*
w
.
getUnitCapacityWeight
(),
w
.
getUnitCapacityWeight
(),
capacity
);
Double
weight
=
entry
.
getValue
();
writeLog
(
" 批次偏差: %.2f (权重%.0f × %.2f)"
,
KpiLib
kpi
=
KpiLib
.
ofEn
(
key
);
lotSize
*
w
.
getLotSizeWeight
(),
w
.
getLotSizeWeight
(),
lotSize
);
double
val
=
model
.
getKpi
(
key
).
solutionValue
();
writeLog
(
" 目标库存偏差: %.2f (权重%.0f × %.2f)"
,
writeLog
(
" %s: %.2f (权重%.0f × %.2f)"
,
targetInv
*
w
.
getTargetInventoryLevelWeight
(),
w
.
getTargetInventoryLevelWeight
(),
targetInv
);
kpi
.
getCn
(),
val
*
weight
,
val
,
val
);
writeLog
(
" 供应目标偏差: %.2f (权重%.0f × %.2f)"
,
}
supplyTarget
*
w
.
getSupplyTargetWeight
(),
w
.
getSupplyTargetWeight
(),
supplyTarget
);
writeLog
(
" 超库存: %.2f (权重%.0f × %.2f)"
,
maxInv
*
w
.
getMaxInventoryLevelWeight
(),
w
.
getMaxInventoryLevelWeight
(),
maxInv
);
// writeLog(" 产能超载: %.2f (权重%.0f × %.2f)",
writeLog
(
" 欠库存: %.2f (权重%.0f × %.2f)"
,
// capacity * weights.get(KpiLib.Fulfillment.getEn()), w.getUnitCapacityWeight(), capacity);
minInv
*
w
.
getMinInventoryLevelWeight
(),
w
.
getMinInventoryLevelWeight
(),
minInv
);
// writeLog(" 批次偏差: %.2f (权重%.0f × %.2f)",
writeLog
(
" 最小供应不足: %.2f (权重%.0f × %.2f)"
,
// lotSize * weights.get(KpiLib.Fulfillment.getEn()), w.getLotSizeWeight(), lotSize);
minSupply
*
w
.
getMinSupplyWeight
(),
w
.
getMinSupplyWeight
(),
minSupply
);
// writeLog(" 目标库存偏差: %.2f (权重%.0f × %.2f)",
writeLog
(
" 最大供应超出: %.2f (权重%.0f × %.2f)"
,
// targetInv * w.getTargetInventoryLevelWeight(), w.getTargetInventoryLevelWeight(), targetInv);
maxSupply
*
w
.
getMaxSupplyWeight
(),
w
.
getMaxSupplyWeight
(),
maxSupply
);
// writeLog(" 供应目标偏差: %.2f (权重%.0f × %.2f)",
writeLog
(
" 销售优先级(负): %.2f (权重%.0f × %.2f)"
,
// supplyTarget * w.getSupplyTargetWeight(), w.getSupplyTargetWeight(), supplyTarget);
salesPriority
*
w
.
getSalesDemandPriorityWeight
(),
// writeLog(" 超库存: %.2f (权重%.0f × %.2f)",
w
.
getSalesDemandPriorityWeight
(),
salesPriority
);
// maxInv * w.getMaxInventoryLevelWeight(), w.getMaxInventoryLevelWeight(), maxInv);
// writeLog(" 欠库存: %.2f (权重%.0f × %.2f)",
// minInv * w.getMinInventoryLevelWeight(), w.getMinInventoryLevelWeight(), minInv);
// writeLog(" 最小供应不足: %.2f (权重%.0f × %.2f)",
// minSupply * w.getMinSupplyWeight(), w.getMinSupplyWeight(), minSupply);
// writeLog(" 最大供应超出: %.2f (权重%.0f × %.2f)",
// maxSupply * w.getMaxSupplyWeight(), w.getMaxSupplyWeight(), maxSupply);
// writeLog(" 销售优先级(负): %.2f (权重%.0f × %.2f)",
// salesPriority * w.getSalesDemandPriorityWeight(),
// w.getSalesDemandPriorityWeight(), salesPriority);
}
}
// ==================== 辅助方法 ====================
// ==================== 辅助方法 ====================
...
...
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