整理通用 Optimize 模拟数据包
This commit is contained in:
parent
e1fd636790
commit
c64fca09e1
|
|
@ -0,0 +1,19 @@
|
|||
# Round 86: 通用 Optimize 模拟数据包
|
||||
|
||||
## 目标
|
||||
|
||||
把 `optimize/deliverables/kangni-simulation-package-v1` 的 10 个模拟订单、72 条工序转换成 APS 已有的通用 `world pack`,用于对话式 Optimize 调试和回归测试。
|
||||
|
||||
## 设计约束
|
||||
|
||||
- 不在 APS 生产代码中增加康尼专用 loader,也不让运行时识别客户名或外部包目录。
|
||||
- 数据包只填充现有 `flexOrders`、`flexRoutings`、`flexOperations`、`flexEquipment`、`flexMaterials`、`salesOrders` 和 `flexCalendar` 契约。
|
||||
- `flexParams.dataGrade=synthetic`,并保留来源包名;该数据只用于开发调试,不作为生产排产输入。
|
||||
- 原始数据的 8:00-17:00 班次保存在 `flexParams.sourceCalendar`。由于当前 PoolEngine 按分钟落盘且不拆跨班次工序,调试日历使用全天可排,保证严格 V2 校验能验证结果。
|
||||
- 小数分钟工时向上取整到分钟,原值写入 `sourceStdTimePerUnit`/`sourceStandardTime`。
|
||||
|
||||
## 验收
|
||||
|
||||
- `load_pack` 能加载数据包并出现在 `list_packs()`。
|
||||
- 7 个 Optimize 调度规则(EDD、SPT、PRIORITY、FIFO、LPT、CR、ATC)均产生 10 条虚拟产线、72 条工单,且 V2 严格校验通过。
|
||||
- 目标分支仍保持为 `codex/integrate-optimize`;本轮只在 `round/86-generic-world-pack` 开发,合并前单独确认。
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from server.aps_domain.closed_loop_runtime import run_closed_loop_candidate
|
||||
from server.state.packs import load_pack, list_packs
|
||||
|
||||
|
||||
PACK_PATH = Path(__file__).resolve().parents[2] / "server" / "data" / "packs" / "optimize-simulation-v1.json"
|
||||
RULES = ("EDD", "SPT", "PRIORITY", "FIFO", "LPT", "CR", "ATC")
|
||||
|
||||
|
||||
def _next_id_factory():
|
||||
counters: dict[str, int] = {}
|
||||
|
||||
def next_id(kind: str) -> int:
|
||||
counters[kind] = counters.get(kind, 0) + 1
|
||||
return counters[kind]
|
||||
|
||||
return next_id
|
||||
|
||||
|
||||
def test_simulation_world_pack_is_a_generic_aps_fixture():
|
||||
pack = json.loads(PACK_PATH.read_text(encoding="utf-8"))
|
||||
world = load_pack(str(PACK_PATH))
|
||||
|
||||
assert pack["packVersion"] == 1
|
||||
assert pack["name"] == "Optimize synthetic debug V1"
|
||||
assert world["flexParams"]["dataGrade"] == "synthetic"
|
||||
assert len(world["flexOrders"]) == 10
|
||||
assert len(world["salesOrders"]) == 10
|
||||
assert len(world["flexRoutings"]) == 72
|
||||
assert len(world["flexEquipment"]) == 6
|
||||
assert all(row["source"] == "SYNTHETIC" for row in world["flexOrders"])
|
||||
assert {row["productCode"] for row in world["flexRoutings"]} == {
|
||||
row["productCode"] for row in world["flexOrders"]
|
||||
}
|
||||
assert any(item["file"] == PACK_PATH.name for item in list_packs())
|
||||
|
||||
|
||||
def test_simulation_world_pack_runs_all_optimize_dispatch_rules():
|
||||
source_world = load_pack(str(PACK_PATH))
|
||||
|
||||
for rule in RULES:
|
||||
world = copy.deepcopy(source_world)
|
||||
result = run_closed_loop_candidate(
|
||||
world,
|
||||
_next_id_factory(),
|
||||
business_date="2026-08-02",
|
||||
engine_type="OPTIMIZE",
|
||||
sort_mode=rule,
|
||||
strict=True,
|
||||
)
|
||||
|
||||
assert result["solveStatus"] == "FEASIBLE", rule
|
||||
assert result["engineType"] == "OPTIMIZE", rule
|
||||
assert result["algorithmId"] == f"optimize.{rule.lower()}", rule
|
||||
assert result["orderCount"] == 10, rule
|
||||
assert result["vlCount"] == 10, rule
|
||||
assert result["woCount"] == 72, rule
|
||||
assert result["validation"]["valid"] is True, rule
|
||||
assert result["validation"]["hardViolations"] == [], rule
|
||||
Loading…
Reference in New Issue