From c64fca09e1fd8af7eb277da0c690b2bc7f62d29f Mon Sep 17 00:00:00 2001 From: ssk Date: Thu, 3 Sep 2026 19:18:42 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E6=95=B4=E7=90=86=E9=80=9A=E7=94=A8=20Opti?= =?UTF-8?q?mize=20=E6=A8=A1=E6=8B=9F=E6=95=B0=E6=8D=AE=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/round-86-generic-world-pack-plan.md | 19 ++++++ .../test_optimize_simulation_world_pack.py | 64 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 docs/round-86-generic-world-pack-plan.md create mode 100644 tests/golden/test_optimize_simulation_world_pack.py diff --git a/docs/round-86-generic-world-pack-plan.md b/docs/round-86-generic-world-pack-plan.md new file mode 100644 index 0000000..bc92fca --- /dev/null +++ b/docs/round-86-generic-world-pack-plan.md @@ -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` 开发,合并前单独确认。 diff --git a/tests/golden/test_optimize_simulation_world_pack.py b/tests/golden/test_optimize_simulation_world_pack.py new file mode 100644 index 0000000..40f936e --- /dev/null +++ b/tests/golden/test_optimize_simulation_world_pack.py @@ -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 From ac6026089e57a783569a1c3b062e6f5fcbdcaf54 Mon Sep 17 00:00:00 2001 From: ssk Date: Thu, 3 Sep 2026 19:18:54 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E5=8A=A0=E5=85=A5=20Optimize=20=E6=A8=A1?= =?UTF-8?q?=E6=8B=9F=20world=20pack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/data/packs/optimize-simulation-v1.json | 2230 +++++++++++++++++ 1 file changed, 2230 insertions(+) create mode 100644 server/data/packs/optimize-simulation-v1.json diff --git a/server/data/packs/optimize-simulation-v1.json b/server/data/packs/optimize-simulation-v1.json new file mode 100644 index 0000000..2f933ca --- /dev/null +++ b/server/data/packs/optimize-simulation-v1.json @@ -0,0 +1,2230 @@ +{ + "packVersion": 1, + "name": "Optimize synthetic debug V1", + "description": "Generic APS world pack: 10 synthetic orders, 72 operations, and 6 virtual resources for Optimize development debugging only.", + "baseDate": "2026-09-03", + "world": { + "factories": [], + "workshops": [], + "lines": [], + "workstations": [], + "operations": [], + "routings": [], + "routingSteps": [], + "materials": [ + { + "id": 1, + "code": "28200003654300", + "name": "28200003654300", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 2, + "code": "28200003676900", + "name": "28200003676900", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 3, + "code": "M1201001290100", + "name": "M1201001290100", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 4, + "code": "M1201001300100", + "name": "M1201001300100", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 5, + "code": "M2290001250100", + "name": "M2290001250100", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 6, + "code": "0802300207", + "name": "0802300207", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 7, + "code": "M1201002610100", + "name": "M1201002610100", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 8, + "code": "0802300203", + "name": "0802300203", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 9, + "code": "0802300208", + "name": "0802300208", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 10, + "code": "0802300202", + "name": "0802300202", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + } + ], + "boms": [], + "bomItems": [], + "lineProducts": [], + "workstationOperations": [], + "equipment": [], + "teams": [], + "shifts": [], + "shiftCalendar": [], + "maintenance": [], + "salesOrders": [ + { + "id": 1, + "orderNo": "102285668", + "status": "APPROVED", + "deliveryDate": "2026-08-08", + "priority": 1, + "source": "SYNTHETIC", + "items": [ + { + "id": 1, + "productId": 1, + "productCode": "28200003654300", + "productName": "28200003654300", + "quantity": 1.0, + "unit": "PCS", + "status": "OPEN" + } + ] + }, + { + "id": 2, + "orderNo": "102289293", + "status": "APPROVED", + "deliveryDate": "2026-07-21", + "priority": 1, + "source": "SYNTHETIC", + "items": [ + { + "id": 2, + "productId": 2, + "productCode": "28200003676900", + "productName": "28200003676900", + "quantity": 1.0, + "unit": "PCS", + "status": "OPEN" + } + ] + }, + { + "id": 3, + "orderNo": "102342090", + "status": "APPROVED", + "deliveryDate": "2026-01-15", + "priority": 1, + "source": "SYNTHETIC", + "items": [ + { + "id": 3, + "productId": 3, + "productCode": "M1201001290100", + "productName": "M1201001290100", + "quantity": 2.0, + "unit": "PCS", + "status": "OPEN" + } + ] + }, + { + "id": 4, + "orderNo": "102342085", + "status": "APPROVED", + "deliveryDate": "2026-01-15", + "priority": 1, + "source": "SYNTHETIC", + "items": [ + { + "id": 4, + "productId": 4, + "productCode": "M1201001300100", + "productName": "M1201001300100", + "quantity": 2.0, + "unit": "PCS", + "status": "OPEN" + } + ] + }, + { + "id": 5, + "orderNo": "102342092", + "status": "APPROVED", + "deliveryDate": "2026-01-15", + "priority": 1, + "source": "SYNTHETIC", + "items": [ + { + "id": 5, + "productId": 5, + "productCode": "M2290001250100", + "productName": "M2290001250100", + "quantity": 4.0, + "unit": "PCS", + "status": "OPEN" + } + ] + }, + { + "id": 6, + "orderNo": "102352579", + "status": "APPROVED", + "deliveryDate": "2026-04-03", + "priority": 1, + "source": "SYNTHETIC", + "items": [ + { + "id": 6, + "productId": 6, + "productCode": "0802300207", + "productName": "0802300207", + "quantity": 1.0, + "unit": "PCS", + "status": "OPEN" + } + ] + }, + { + "id": 7, + "orderNo": "102314266", + "status": "APPROVED", + "deliveryDate": "2026-03-16", + "priority": 1, + "source": "SYNTHETIC", + "items": [ + { + "id": 7, + "productId": 7, + "productCode": "M1201002610100", + "productName": "M1201002610100", + "quantity": 1.0, + "unit": "PCS", + "status": "OPEN" + } + ] + }, + { + "id": 8, + "orderNo": "102375367", + "status": "APPROVED", + "deliveryDate": "2026-04-03", + "priority": 1, + "source": "SYNTHETIC", + "items": [ + { + "id": 8, + "productId": 8, + "productCode": "0802300203", + "productName": "0802300203", + "quantity": 1.0, + "unit": "PCS", + "status": "OPEN" + } + ] + }, + { + "id": 9, + "orderNo": "102375368", + "status": "APPROVED", + "deliveryDate": "2026-04-03", + "priority": 1, + "source": "SYNTHETIC", + "items": [ + { + "id": 9, + "productId": 9, + "productCode": "0802300208", + "productName": "0802300208", + "quantity": 1.0, + "unit": "PCS", + "status": "OPEN" + } + ] + }, + { + "id": 10, + "orderNo": "102378688", + "status": "APPROVED", + "deliveryDate": "2026-04-03", + "priority": 1, + "source": "SYNTHETIC", + "items": [ + { + "id": 10, + "productId": 10, + "productCode": "0802300202", + "productName": "0802300202", + "quantity": 1.0, + "unit": "PCS", + "status": "OPEN" + } + ] + } + ], + "productionOrders": [], + "workOrders": [], + "purchaseOrders": [], + "outsourceOrders": [], + "forecastOrders": [], + "changeoverMatrix": [], + "scheduleVersions": [], + "conflicts": [], + "logs": [], + "auditEvents": [], + "scheduleParams": { + "defaultEngine": "RULE", + "planningHorizonDays": 14, + "timeGranularityMinutes": 15, + "deliveryBufferRatio": 0.95, + "weights": { + "tardiness": 0.4, + "cost": 0.3, + "utilization": 0.2, + "balance": 0.1 + }, + "customerLevelWeights": { + "VIP": 3.0, + "A": 2.0, + "B": 1.0, + "C": 1.0 + }, + "freezeWindowHours": 24 + }, + "constraintProfile": { + "profileId": "default", + "name": "\u9ed8\u8ba4\u7ea6\u675f\u5256\u9762", + "constraints": {} + }, + "flexZones": [ + { + "id": 1, + "code": "assembly", + "name": "assembly" + }, + { + "id": 2, + "code": "highspeed", + "name": "highspeed" + }, + { + "id": 3, + "code": "preassembly", + "name": "preassembly" + } + ], + "flexOperations": [ + { + "id": 1, + "code": "0503727Z1M", + "name": "\u5e72\u7ebf\u673a\u6784\u90e8\u88c5", + "type": "INTERNAL", + "standardTime": 27, + "isBottleneck": false, + "sourceStandardTime": 27.0 + }, + { + "id": 2, + "code": "0503722Z1M", + "name": "\u5e72\u7ebf\u673a\u6784\u603b\u88c51", + "type": "INTERNAL", + "standardTime": 5, + "isBottleneck": false, + "sourceStandardTime": 5.0 + }, + { + "id": 3, + "code": "0503723Z1M", + "name": "\u5e72\u7ebf\u673a\u6784\u603b\u88c52", + "type": "INTERNAL", + "standardTime": 5, + "isBottleneck": false, + "sourceStandardTime": 5.0 + }, + { + "id": 4, + "code": "0503724Z1M", + "name": "\u5e72\u7ebf\u673a\u6784\u603b\u88c53", + "type": "INTERNAL", + "standardTime": 5, + "isBottleneck": false, + "sourceStandardTime": 5.0 + }, + { + "id": 5, + "code": "0503725Z1M", + "name": "\u5e72\u7ebf\u673a\u6784\u603b\u88c54", + "type": "INTERNAL", + "standardTime": 5, + "isBottleneck": false, + "sourceStandardTime": 5.0 + }, + { + "id": 6, + "code": "0503726Z1M", + "name": "\u5e72\u7ebf\u673a\u6784\u603b\u88c55", + "type": "INTERNAL", + "standardTime": 5, + "isBottleneck": false, + "sourceStandardTime": 5.0 + }, + { + "id": 7, + "code": "0503006Z1M", + "name": "\u5e72\u7ebf\u673a\u6784\u603b\u88c56", + "type": "INTERNAL", + "standardTime": 5, + "isBottleneck": false, + "sourceStandardTime": 5.0 + }, + { + "id": 8, + "code": "0503007Z1M", + "name": "\u5e72\u7ebf\u673a\u6784\u603b\u88c57", + "type": "INTERNAL", + "standardTime": 6, + "isBottleneck": false, + "sourceStandardTime": 6.0 + }, + { + "id": 9, + "code": "0503002Z1M", + "name": "\u5e72\u7ebf\u673a\u6784\u603b\u88c58", + "type": "INTERNAL", + "standardTime": 5, + "isBottleneck": false, + "sourceStandardTime": 5.0 + }, + { + "id": 10, + "code": "0503003Z1M", + "name": "\u5e72\u7ebf\u673a\u6784\u603b\u88c59", + "type": "INTERNAL", + "standardTime": 5, + "isBottleneck": false, + "sourceStandardTime": 5.0 + }, + { + "id": 11, + "code": "0604001Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c51", + "type": "INTERNAL", + "standardTime": 40, + "isBottleneck": false, + "sourceStandardTime": 40.0 + }, + { + "id": 12, + "code": "0604007Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c51", + "type": "INTERNAL", + "standardTime": 40, + "isBottleneck": false, + "sourceStandardTime": 40.0 + }, + { + "id": 13, + "code": "0604217Z1M", + "name": "\u9501\u88c5\u914d", + "type": "INTERNAL", + "standardTime": 7, + "isBottleneck": false, + "sourceStandardTime": 6.667 + }, + { + "id": 14, + "code": "0604002Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c52", + "type": "INTERNAL", + "standardTime": 11, + "isBottleneck": false, + "sourceStandardTime": 11.0 + }, + { + "id": 15, + "code": "0604003Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c53", + "type": "INTERNAL", + "standardTime": 11, + "isBottleneck": false, + "sourceStandardTime": 11.0 + }, + { + "id": 16, + "code": "0604004Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c54", + "type": "INTERNAL", + "standardTime": 11, + "isBottleneck": false, + "sourceStandardTime": 11.0 + }, + { + "id": 17, + "code": "0604005Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c55", + "type": "INTERNAL", + "standardTime": 11, + "isBottleneck": false, + "sourceStandardTime": 11.0 + }, + { + "id": 18, + "code": "0604006Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c56", + "type": "INTERNAL", + "standardTime": 10, + "isBottleneck": false, + "sourceStandardTime": 10.0 + }, + { + "id": 19, + "code": "0604008Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c52", + "type": "INTERNAL", + "standardTime": 6, + "isBottleneck": false, + "sourceStandardTime": 6.0 + }, + { + "id": 20, + "code": "0604009Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c53", + "type": "INTERNAL", + "standardTime": 6, + "isBottleneck": false, + "sourceStandardTime": 6.0 + }, + { + "id": 21, + "code": "0604010Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c54", + "type": "INTERNAL", + "standardTime": 12, + "isBottleneck": false, + "sourceStandardTime": 12.0 + }, + { + "id": 22, + "code": "0604011Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c55", + "type": "INTERNAL", + "standardTime": 6, + "isBottleneck": false, + "sourceStandardTime": 6.0 + }, + { + "id": 23, + "code": "0604012Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c56", + "type": "INTERNAL", + "standardTime": 6, + "isBottleneck": false, + "sourceStandardTime": 6.0 + }, + { + "id": 24, + "code": "0604013Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c57", + "type": "INTERNAL", + "standardTime": 6, + "isBottleneck": false, + "sourceStandardTime": 6.0 + }, + { + "id": 25, + "code": "0604014Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c58", + "type": "INTERNAL", + "standardTime": 6, + "isBottleneck": false, + "sourceStandardTime": 6.0 + }, + { + "id": 26, + "code": "0604015Z1M", + "name": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c59", + "type": "INTERNAL", + "standardTime": 6, + "isBottleneck": false, + "sourceStandardTime": 6.0 + } + ], + "flexEquipment": [ + { + "id": 1, + "code": "V_PREASSY_01", + "name": "V_PREASSY_01", + "status": "RUNNING", + "capabilities": [ + "0503727Z1M", + "0604001Z1M", + "0604002Z1M", + "0604003Z1M", + "0604004Z1M", + "0604005Z1M", + "0604006Z1M" + ], + "opStdTime": { + "0503727Z1M": 35.0, + "0604001Z1M": 40.0, + "0604002Z1M": 11.0, + "0604003Z1M": 11.0, + "0604004Z1M": 11.0, + "0604005Z1M": 11.0, + "0604006Z1M": 10.0 + }, + "zone": "preassembly", + "movable": false, + "moveTimeMin": 0.0, + "availabilityRate": 1.0 + }, + { + "id": 2, + "code": "V_PREASSY_02", + "name": "V_PREASSY_02", + "status": "RUNNING", + "capabilities": [ + "0503727Z1M", + "0604001Z1M", + "0604002Z1M", + "0604003Z1M", + "0604004Z1M", + "0604005Z1M", + "0604006Z1M" + ], + "opStdTime": { + "0503727Z1M": 35.0, + "0604001Z1M": 40.0, + "0604002Z1M": 11.0, + "0604003Z1M": 11.0, + "0604004Z1M": 11.0, + "0604005Z1M": 11.0, + "0604006Z1M": 10.0 + }, + "zone": "preassembly", + "movable": false, + "moveTimeMin": 0.0, + "availabilityRate": 1.0 + }, + { + "id": 3, + "code": "V_ASSY_01", + "name": "V_ASSY_01", + "status": "RUNNING", + "capabilities": [ + "0503722Z1M", + "0503723Z1M", + "0503724Z1M", + "0503725Z1M", + "0503726Z1M", + "0503006Z1M", + "0503007Z1M", + "0503002Z1M", + "0503003Z1M", + "0604217Z1M", + "0604014Z1M", + "0604015Z1M" + ], + "opStdTime": { + "0503722Z1M": 5.0, + "0503723Z1M": 5.0, + "0503724Z1M": 5.0, + "0503725Z1M": 5.0, + "0503726Z1M": 5.0, + "0503006Z1M": 5.0, + "0503007Z1M": 6.0, + "0503002Z1M": 5.0, + "0503003Z1M": 5.0, + "0604217Z1M": 6.667, + "0604014Z1M": 6.0, + "0604015Z1M": 6.0 + }, + "zone": "assembly", + "movable": false, + "moveTimeMin": 0.0, + "availabilityRate": 1.0 + }, + { + "id": 4, + "code": "V_ASSY_02", + "name": "V_ASSY_02", + "status": "RUNNING", + "capabilities": [ + "0503722Z1M", + "0503723Z1M", + "0503724Z1M", + "0503725Z1M", + "0503726Z1M", + "0503006Z1M", + "0503007Z1M", + "0503002Z1M", + "0503003Z1M", + "0604217Z1M", + "0604014Z1M", + "0604015Z1M" + ], + "opStdTime": { + "0503722Z1M": 5.0, + "0503723Z1M": 5.0, + "0503724Z1M": 5.0, + "0503725Z1M": 5.0, + "0503726Z1M": 5.0, + "0503006Z1M": 5.0, + "0503007Z1M": 6.0, + "0503002Z1M": 5.0, + "0503003Z1M": 5.0, + "0604217Z1M": 6.667, + "0604014Z1M": 6.0, + "0604015Z1M": 6.0 + }, + "zone": "assembly", + "movable": false, + "moveTimeMin": 0.0, + "availabilityRate": 1.0 + }, + { + "id": 5, + "code": "V_ASSY_03", + "name": "V_ASSY_03", + "status": "RUNNING", + "capabilities": [ + "0503722Z1M", + "0503723Z1M", + "0503724Z1M", + "0503725Z1M", + "0503726Z1M", + "0503006Z1M", + "0503007Z1M", + "0503002Z1M", + "0503003Z1M", + "0604217Z1M", + "0604014Z1M", + "0604015Z1M" + ], + "opStdTime": { + "0503722Z1M": 5.0, + "0503723Z1M": 5.0, + "0503724Z1M": 5.0, + "0503725Z1M": 5.0, + "0503726Z1M": 5.0, + "0503006Z1M": 5.0, + "0503007Z1M": 6.0, + "0503002Z1M": 5.0, + "0503003Z1M": 5.0, + "0604217Z1M": 6.667, + "0604014Z1M": 6.0, + "0604015Z1M": 6.0 + }, + "zone": "assembly", + "movable": false, + "moveTimeMin": 0.0, + "availabilityRate": 1.0 + }, + { + "id": 6, + "code": "V_HSASSY_01", + "name": "V_HSASSY_01", + "status": "RUNNING", + "capabilities": [ + "0604007Z1M", + "0604008Z1M", + "0604009Z1M", + "0604010Z1M", + "0604011Z1M", + "0604012Z1M", + "0604013Z1M" + ], + "opStdTime": { + "0604007Z1M": 40.0, + "0604008Z1M": 6.0, + "0604009Z1M": 6.0, + "0604010Z1M": 12.0, + "0604011Z1M": 6.0, + "0604012Z1M": 6.0, + "0604013Z1M": 6.0 + }, + "zone": "highspeed", + "movable": false, + "moveTimeMin": 0.0, + "availabilityRate": 1.0 + } + ], + "flexMolds": [], + "flexMaterials": [ + { + "id": 1, + "code": "28200003654300", + "name": "28200003654300", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 2, + "code": "28200003676900", + "name": "28200003676900", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 3, + "code": "M1201001290100", + "name": "M1201001290100", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 4, + "code": "M1201001300100", + "name": "M1201001300100", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 5, + "code": "M2290001250100", + "name": "M2290001250100", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 6, + "code": "0802300207", + "name": "0802300207", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 7, + "code": "M1201002610100", + "name": "M1201002610100", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 8, + "code": "0802300203", + "name": "0802300203", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 9, + "code": "0802300208", + "name": "0802300208", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + }, + { + "id": 10, + "code": "0802300202", + "name": "0802300202", + "type": "FINISHED_PRODUCT", + "unit": "PCS", + "stock": 0, + "inTransit": 0, + "sourcingType": "MAKE", + "sourcingTypeSource": "EXPLICIT", + "dataGrade": "synthetic" + } + ], + "flexRoutings": [ + { + "id": 1, + "routingId": "SIM-28200003654300", + "routingVersion": "SIM-V1", + "productCode": "28200003654300", + "productName": "28200003654300", + "seq": 1, + "operationCode": "0503727Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u90e8\u88c5", + "stdTimePerUnit": 27, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 27.0 + }, + { + "id": 2, + "routingId": "SIM-28200003654300", + "routingVersion": "SIM-V1", + "productCode": "28200003654300", + "productName": "28200003654300", + "seq": 2, + "operationCode": "0503722Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c51", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 3, + "routingId": "SIM-28200003654300", + "routingVersion": "SIM-V1", + "productCode": "28200003654300", + "productName": "28200003654300", + "seq": 3, + "operationCode": "0503723Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c52", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 4, + "routingId": "SIM-28200003654300", + "routingVersion": "SIM-V1", + "productCode": "28200003654300", + "productName": "28200003654300", + "seq": 4, + "operationCode": "0503724Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c53", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 5, + "routingId": "SIM-28200003654300", + "routingVersion": "SIM-V1", + "productCode": "28200003654300", + "productName": "28200003654300", + "seq": 5, + "operationCode": "0503725Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c54", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 6, + "routingId": "SIM-28200003654300", + "routingVersion": "SIM-V1", + "productCode": "28200003654300", + "productName": "28200003654300", + "seq": 6, + "operationCode": "0503726Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c55", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 7, + "routingId": "SIM-28200003654300", + "routingVersion": "SIM-V1", + "productCode": "28200003654300", + "productName": "28200003654300", + "seq": 7, + "operationCode": "0503006Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c56", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 8, + "routingId": "SIM-28200003654300", + "routingVersion": "SIM-V1", + "productCode": "28200003654300", + "productName": "28200003654300", + "seq": 8, + "operationCode": "0503007Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c57", + "stdTimePerUnit": 6, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 6.0 + }, + { + "id": 9, + "routingId": "SIM-28200003654300", + "routingVersion": "SIM-V1", + "productCode": "28200003654300", + "productName": "28200003654300", + "seq": 9, + "operationCode": "0503002Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c58", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 10, + "routingId": "SIM-28200003654300", + "routingVersion": "SIM-V1", + "productCode": "28200003654300", + "productName": "28200003654300", + "seq": 10, + "operationCode": "0503003Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c59", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 11, + "routingId": "SIM-28200003676900", + "routingVersion": "SIM-V1", + "productCode": "28200003676900", + "productName": "28200003676900", + "seq": 1, + "operationCode": "0503727Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u90e8\u88c5", + "stdTimePerUnit": 27, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 27.0 + }, + { + "id": 12, + "routingId": "SIM-28200003676900", + "routingVersion": "SIM-V1", + "productCode": "28200003676900", + "productName": "28200003676900", + "seq": 2, + "operationCode": "0503722Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c51", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 13, + "routingId": "SIM-28200003676900", + "routingVersion": "SIM-V1", + "productCode": "28200003676900", + "productName": "28200003676900", + "seq": 3, + "operationCode": "0503723Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c52", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 14, + "routingId": "SIM-28200003676900", + "routingVersion": "SIM-V1", + "productCode": "28200003676900", + "productName": "28200003676900", + "seq": 4, + "operationCode": "0503724Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c53", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 15, + "routingId": "SIM-28200003676900", + "routingVersion": "SIM-V1", + "productCode": "28200003676900", + "productName": "28200003676900", + "seq": 5, + "operationCode": "0503725Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c54", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 16, + "routingId": "SIM-28200003676900", + "routingVersion": "SIM-V1", + "productCode": "28200003676900", + "productName": "28200003676900", + "seq": 6, + "operationCode": "0503726Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c55", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 17, + "routingId": "SIM-28200003676900", + "routingVersion": "SIM-V1", + "productCode": "28200003676900", + "productName": "28200003676900", + "seq": 7, + "operationCode": "0503006Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c56", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 18, + "routingId": "SIM-28200003676900", + "routingVersion": "SIM-V1", + "productCode": "28200003676900", + "productName": "28200003676900", + "seq": 8, + "operationCode": "0503007Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c57", + "stdTimePerUnit": 6, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 6.0 + }, + { + "id": 19, + "routingId": "SIM-28200003676900", + "routingVersion": "SIM-V1", + "productCode": "28200003676900", + "productName": "28200003676900", + "seq": 9, + "operationCode": "0503002Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c58", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 20, + "routingId": "SIM-28200003676900", + "routingVersion": "SIM-V1", + "productCode": "28200003676900", + "productName": "28200003676900", + "seq": 10, + "operationCode": "0503003Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c59", + "stdTimePerUnit": 5, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 5.0 + }, + { + "id": 21, + "routingId": "SIM-M1201001290100", + "routingVersion": "SIM-V1", + "productCode": "M1201001290100", + "productName": "M1201001290100", + "seq": 1, + "operationCode": "0604001Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c51", + "stdTimePerUnit": 40, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 40.0 + }, + { + "id": 22, + "routingId": "SIM-M1201001290100", + "routingVersion": "SIM-V1", + "productCode": "M1201001290100", + "productName": "M1201001290100", + "seq": 2, + "operationCode": "0604007Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c51", + "stdTimePerUnit": 40, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 40.0 + }, + { + "id": 23, + "routingId": "SIM-M1201001300100", + "routingVersion": "SIM-V1", + "productCode": "M1201001300100", + "productName": "M1201001300100", + "seq": 1, + "operationCode": "0604001Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c51", + "stdTimePerUnit": 40, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 40.0 + }, + { + "id": 24, + "routingId": "SIM-M1201001300100", + "routingVersion": "SIM-V1", + "productCode": "M1201001300100", + "productName": "M1201001300100", + "seq": 2, + "operationCode": "0604007Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c51", + "stdTimePerUnit": 40, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 40.0 + }, + { + "id": 25, + "routingId": "SIM-M2290001250100", + "routingVersion": "SIM-V1", + "productCode": "M2290001250100", + "productName": "M2290001250100", + "seq": 1, + "operationCode": "0604217Z1M", + "operationName": "\u9501\u88c5\u914d", + "stdTimePerUnit": 7, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 6.667 + }, + { + "id": 26, + "routingId": "SIM-0802300207", + "routingVersion": "SIM-V1", + "productCode": "0802300207", + "productName": "0802300207", + "seq": 1, + "operationCode": "0503727Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u90e8\u88c5", + "stdTimePerUnit": 35, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 35.0 + }, + { + "id": 27, + "routingId": "SIM-0802300207", + "routingVersion": "SIM-V1", + "productCode": "0802300207", + "productName": "0802300207", + "seq": 2, + "operationCode": "0503722Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c51", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 28, + "routingId": "SIM-0802300207", + "routingVersion": "SIM-V1", + "productCode": "0802300207", + "productName": "0802300207", + "seq": 3, + "operationCode": "0503723Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c52", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 29, + "routingId": "SIM-0802300207", + "routingVersion": "SIM-V1", + "productCode": "0802300207", + "productName": "0802300207", + "seq": 4, + "operationCode": "0503724Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c53", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 30, + "routingId": "SIM-0802300207", + "routingVersion": "SIM-V1", + "productCode": "0802300207", + "productName": "0802300207", + "seq": 5, + "operationCode": "0503725Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c54", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 31, + "routingId": "SIM-0802300207", + "routingVersion": "SIM-V1", + "productCode": "0802300207", + "productName": "0802300207", + "seq": 6, + "operationCode": "0503726Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c55", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 32, + "routingId": "SIM-0802300207", + "routingVersion": "SIM-V1", + "productCode": "0802300207", + "productName": "0802300207", + "seq": 7, + "operationCode": "0503006Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c56", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 33, + "routingId": "SIM-0802300207", + "routingVersion": "SIM-V1", + "productCode": "0802300207", + "productName": "0802300207", + "seq": 8, + "operationCode": "0503007Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c57", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 34, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 1, + "operationCode": "0604001Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c51", + "stdTimePerUnit": 11, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 11.0 + }, + { + "id": 35, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 2, + "operationCode": "0604002Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c52", + "stdTimePerUnit": 11, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 11.0 + }, + { + "id": 36, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 3, + "operationCode": "0604003Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c53", + "stdTimePerUnit": 11, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 11.0 + }, + { + "id": 37, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 4, + "operationCode": "0604004Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c54", + "stdTimePerUnit": 11, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 11.0 + }, + { + "id": 38, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 5, + "operationCode": "0604005Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c55", + "stdTimePerUnit": 11, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 11.0 + }, + { + "id": 39, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 6, + "operationCode": "0604006Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u90e8\u88c56", + "stdTimePerUnit": 10, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 10.0 + }, + { + "id": 40, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 7, + "operationCode": "0604007Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c51", + "stdTimePerUnit": 6, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 6.0 + }, + { + "id": 41, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 8, + "operationCode": "0604008Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c52", + "stdTimePerUnit": 6, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 6.0 + }, + { + "id": 42, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 9, + "operationCode": "0604009Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c53", + "stdTimePerUnit": 6, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 6.0 + }, + { + "id": 43, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 10, + "operationCode": "0604010Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c54", + "stdTimePerUnit": 12, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 12.0 + }, + { + "id": 44, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 11, + "operationCode": "0604011Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c55", + "stdTimePerUnit": 6, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 6.0 + }, + { + "id": 45, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 12, + "operationCode": "0604012Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c56", + "stdTimePerUnit": 6, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 6.0 + }, + { + "id": 46, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 13, + "operationCode": "0604013Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c57", + "stdTimePerUnit": 6, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 6.0 + }, + { + "id": 47, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 14, + "operationCode": "0604014Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c58", + "stdTimePerUnit": 6, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 6.0 + }, + { + "id": 48, + "routingId": "SIM-M1201002610100", + "routingVersion": "SIM-V1", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "seq": 15, + "operationCode": "0604015Z1M", + "operationName": "\u9ad8\u901f\u8f66\u673a\u6784\u603b\u88c59", + "stdTimePerUnit": 6, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 6.0 + }, + { + "id": 49, + "routingId": "SIM-0802300203", + "routingVersion": "SIM-V1", + "productCode": "0802300203", + "productName": "0802300203", + "seq": 1, + "operationCode": "0503727Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u90e8\u88c5", + "stdTimePerUnit": 30, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 30.0 + }, + { + "id": 50, + "routingId": "SIM-0802300203", + "routingVersion": "SIM-V1", + "productCode": "0802300203", + "productName": "0802300203", + "seq": 2, + "operationCode": "0503722Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c51", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 51, + "routingId": "SIM-0802300203", + "routingVersion": "SIM-V1", + "productCode": "0802300203", + "productName": "0802300203", + "seq": 3, + "operationCode": "0503723Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c52", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 52, + "routingId": "SIM-0802300203", + "routingVersion": "SIM-V1", + "productCode": "0802300203", + "productName": "0802300203", + "seq": 4, + "operationCode": "0503724Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c53", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 53, + "routingId": "SIM-0802300203", + "routingVersion": "SIM-V1", + "productCode": "0802300203", + "productName": "0802300203", + "seq": 5, + "operationCode": "0503725Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c54", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 54, + "routingId": "SIM-0802300203", + "routingVersion": "SIM-V1", + "productCode": "0802300203", + "productName": "0802300203", + "seq": 6, + "operationCode": "0503726Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c55", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 55, + "routingId": "SIM-0802300203", + "routingVersion": "SIM-V1", + "productCode": "0802300203", + "productName": "0802300203", + "seq": 7, + "operationCode": "0503006Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c56", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 56, + "routingId": "SIM-0802300203", + "routingVersion": "SIM-V1", + "productCode": "0802300203", + "productName": "0802300203", + "seq": 8, + "operationCode": "0503007Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c57", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 57, + "routingId": "SIM-0802300208", + "routingVersion": "SIM-V1", + "productCode": "0802300208", + "productName": "0802300208", + "seq": 1, + "operationCode": "0503727Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u90e8\u88c5", + "stdTimePerUnit": 35, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 35.0 + }, + { + "id": 58, + "routingId": "SIM-0802300208", + "routingVersion": "SIM-V1", + "productCode": "0802300208", + "productName": "0802300208", + "seq": 2, + "operationCode": "0503722Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c51", + "stdTimePerUnit": 3, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 3.0 + }, + { + "id": 59, + "routingId": "SIM-0802300208", + "routingVersion": "SIM-V1", + "productCode": "0802300208", + "productName": "0802300208", + "seq": 3, + "operationCode": "0503723Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c52", + "stdTimePerUnit": 3, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 3.0 + }, + { + "id": 60, + "routingId": "SIM-0802300208", + "routingVersion": "SIM-V1", + "productCode": "0802300208", + "productName": "0802300208", + "seq": 4, + "operationCode": "0503724Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c53", + "stdTimePerUnit": 3, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 3.0 + }, + { + "id": 61, + "routingId": "SIM-0802300208", + "routingVersion": "SIM-V1", + "productCode": "0802300208", + "productName": "0802300208", + "seq": 5, + "operationCode": "0503725Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c54", + "stdTimePerUnit": 3, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 3.0 + }, + { + "id": 62, + "routingId": "SIM-0802300208", + "routingVersion": "SIM-V1", + "productCode": "0802300208", + "productName": "0802300208", + "seq": 6, + "operationCode": "0503726Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c55", + "stdTimePerUnit": 3, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 3.0 + }, + { + "id": 63, + "routingId": "SIM-0802300208", + "routingVersion": "SIM-V1", + "productCode": "0802300208", + "productName": "0802300208", + "seq": 7, + "operationCode": "0503006Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c56", + "stdTimePerUnit": 3, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 3.0 + }, + { + "id": 64, + "routingId": "SIM-0802300208", + "routingVersion": "SIM-V1", + "productCode": "0802300208", + "productName": "0802300208", + "seq": 8, + "operationCode": "0503007Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c57", + "stdTimePerUnit": 3, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 3.0 + }, + { + "id": 65, + "routingId": "SIM-0802300202", + "routingVersion": "SIM-V1", + "productCode": "0802300202", + "productName": "0802300202", + "seq": 1, + "operationCode": "0503727Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u90e8\u88c5", + "stdTimePerUnit": 30, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 30.0 + }, + { + "id": 66, + "routingId": "SIM-0802300202", + "routingVersion": "SIM-V1", + "productCode": "0802300202", + "productName": "0802300202", + "seq": 2, + "operationCode": "0503722Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c51", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 67, + "routingId": "SIM-0802300202", + "routingVersion": "SIM-V1", + "productCode": "0802300202", + "productName": "0802300202", + "seq": 3, + "operationCode": "0503723Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c52", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 68, + "routingId": "SIM-0802300202", + "routingVersion": "SIM-V1", + "productCode": "0802300202", + "productName": "0802300202", + "seq": 4, + "operationCode": "0503724Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c53", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 69, + "routingId": "SIM-0802300202", + "routingVersion": "SIM-V1", + "productCode": "0802300202", + "productName": "0802300202", + "seq": 5, + "operationCode": "0503725Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c54", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 70, + "routingId": "SIM-0802300202", + "routingVersion": "SIM-V1", + "productCode": "0802300202", + "productName": "0802300202", + "seq": 6, + "operationCode": "0503726Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c55", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 71, + "routingId": "SIM-0802300202", + "routingVersion": "SIM-V1", + "productCode": "0802300202", + "productName": "0802300202", + "seq": 7, + "operationCode": "0503006Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c56", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + }, + { + "id": 72, + "routingId": "SIM-0802300202", + "routingVersion": "SIM-V1", + "productCode": "0802300202", + "productName": "0802300202", + "seq": 8, + "operationCode": "0503007Z1M", + "operationName": "\u5e72\u7ebf\u673a\u6784\u603b\u88c57", + "stdTimePerUnit": 4, + "stdTimeSource": "SIMULATION", + "sourcingType": "MAKE", + "isExternal": false, + "sourceStdTimePerUnit": 4.0 + } + ], + "flexBom": [], + "flexOrders": [ + { + "id": 1, + "orderNo": "102285668", + "productCode": "28200003654300", + "productName": "28200003654300", + "quantity": 1.0, + "unit": "PCS", + "dueDate": "2026-08-08", + "releaseDate": "2026-06-25", + "priority": 1, + "status": "RELEASED", + "kitStatus": "READY", + "source": "SYNTHETIC" + }, + { + "id": 2, + "orderNo": "102289293", + "productCode": "28200003676900", + "productName": "28200003676900", + "quantity": 1.0, + "unit": "PCS", + "dueDate": "2026-07-21", + "releaseDate": "2026-07-06", + "priority": 1, + "status": "RELEASED", + "kitStatus": "READY", + "source": "SYNTHETIC" + }, + { + "id": 3, + "orderNo": "102342090", + "productCode": "M1201001290100", + "productName": "M1201001290100", + "quantity": 2.0, + "unit": "PCS", + "dueDate": "2026-01-15", + "releaseDate": "2026-01-15", + "priority": 1, + "status": "RELEASED", + "kitStatus": "READY", + "source": "SYNTHETIC" + }, + { + "id": 4, + "orderNo": "102342085", + "productCode": "M1201001300100", + "productName": "M1201001300100", + "quantity": 2.0, + "unit": "PCS", + "dueDate": "2026-01-15", + "releaseDate": "2026-01-15", + "priority": 1, + "status": "RELEASED", + "kitStatus": "READY", + "source": "SYNTHETIC" + }, + { + "id": 5, + "orderNo": "102342092", + "productCode": "M2290001250100", + "productName": "M2290001250100", + "quantity": 4.0, + "unit": "PCS", + "dueDate": "2026-01-15", + "releaseDate": "2026-01-15", + "priority": 1, + "status": "RELEASED", + "kitStatus": "READY", + "source": "SYNTHETIC" + }, + { + "id": 6, + "orderNo": "102352579", + "productCode": "0802300207", + "productName": "0802300207", + "quantity": 1.0, + "unit": "PCS", + "dueDate": "2026-04-03", + "releaseDate": "2026-04-03", + "priority": 1, + "status": "RELEASED", + "kitStatus": "READY", + "source": "SYNTHETIC" + }, + { + "id": 7, + "orderNo": "102314266", + "productCode": "M1201002610100", + "productName": "M1201002610100", + "quantity": 1.0, + "unit": "PCS", + "dueDate": "2026-03-16", + "releaseDate": "2026-03-16", + "priority": 1, + "status": "RELEASED", + "kitStatus": "READY", + "source": "SYNTHETIC" + }, + { + "id": 8, + "orderNo": "102375367", + "productCode": "0802300203", + "productName": "0802300203", + "quantity": 1.0, + "unit": "PCS", + "dueDate": "2026-04-03", + "releaseDate": "2026-04-03", + "priority": 1, + "status": "RELEASED", + "kitStatus": "READY", + "source": "SYNTHETIC" + }, + { + "id": 9, + "orderNo": "102375368", + "productCode": "0802300208", + "productName": "0802300208", + "quantity": 1.0, + "unit": "PCS", + "dueDate": "2026-04-03", + "releaseDate": "2026-04-03", + "priority": 1, + "status": "RELEASED", + "kitStatus": "READY", + "source": "SYNTHETIC" + }, + { + "id": 10, + "orderNo": "102378688", + "productCode": "0802300202", + "productName": "0802300202", + "quantity": 1.0, + "unit": "PCS", + "dueDate": "2026-04-03", + "releaseDate": "2026-04-03", + "priority": 1, + "status": "RELEASED", + "kitStatus": "READY", + "source": "SYNTHETIC" + } + ], + "flexTeams": [], + "flexCalendar": [ + { + "shiftCode": "SIM-DEBUG", + "startTime": "00:00", + "endTime": "23:59", + "breaks": [], + "workdays": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ] + } + ], + "flexParams": { + "sortMode": "BOTTLENECK", + "beforeDays": 0, + "afterDays": 30, + "rollingWindows": { + "realtime": "60m", + "long": "7d", + "mid": "2d", + "short": "2h" + }, + "mrpControllerDays": 3, + "weights": { + "tardiness": 0.4, + "cost": 0.3, + "utilization": 0.2, + "balance": 0.1 + }, + "demoDataCleared": true, + "siteProfile": "synthetic-world-pack", + "dataGrade": "synthetic", + "sourcePackage": "kangni-simulation-package-v1", + "sourceCalendar": { + "startTime": "08:00", + "endTime": "17:00", + "breakMinutes": 60, + "workdays": [ + 1, + 2, + 3, + 4, + 5 + ], + "mode": "source-simulation" + } + }, + "flexScheduleVersions": [], + "flexVirtualLines": [], + "flexWorkOrders": [], + "flexConflicts": [] + } +} From 6478e8c5129d33e93400a3290a2260bb2f476aba Mon Sep 17 00:00:00 2001 From: ssk Date: Thu, 3 Sep 2026 19:27:39 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=A8=A1=E6=8B=9F?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E7=9A=84=E9=80=9A=E7=94=A8=E6=8A=95=E5=BD=B1?= =?UTF-8?q?=E6=9D=A5=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/data/packs/optimize-simulation-v1.json | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/server/data/packs/optimize-simulation-v1.json b/server/data/packs/optimize-simulation-v1.json index 2f933ca..cd09a1b 100644 --- a/server/data/packs/optimize-simulation-v1.json +++ b/server/data/packs/optimize-simulation-v1.json @@ -149,7 +149,7 @@ "status": "APPROVED", "deliveryDate": "2026-08-08", "priority": 1, - "source": "SYNTHETIC", + "source": "FLEX", "items": [ { "id": 1, @@ -160,7 +160,8 @@ "unit": "PCS", "status": "OPEN" } - ] + ], + "dataGrade": "synthetic" }, { "id": 2, @@ -168,7 +169,7 @@ "status": "APPROVED", "deliveryDate": "2026-07-21", "priority": 1, - "source": "SYNTHETIC", + "source": "FLEX", "items": [ { "id": 2, @@ -179,7 +180,8 @@ "unit": "PCS", "status": "OPEN" } - ] + ], + "dataGrade": "synthetic" }, { "id": 3, @@ -187,7 +189,7 @@ "status": "APPROVED", "deliveryDate": "2026-01-15", "priority": 1, - "source": "SYNTHETIC", + "source": "FLEX", "items": [ { "id": 3, @@ -198,7 +200,8 @@ "unit": "PCS", "status": "OPEN" } - ] + ], + "dataGrade": "synthetic" }, { "id": 4, @@ -206,7 +209,7 @@ "status": "APPROVED", "deliveryDate": "2026-01-15", "priority": 1, - "source": "SYNTHETIC", + "source": "FLEX", "items": [ { "id": 4, @@ -217,7 +220,8 @@ "unit": "PCS", "status": "OPEN" } - ] + ], + "dataGrade": "synthetic" }, { "id": 5, @@ -225,7 +229,7 @@ "status": "APPROVED", "deliveryDate": "2026-01-15", "priority": 1, - "source": "SYNTHETIC", + "source": "FLEX", "items": [ { "id": 5, @@ -236,7 +240,8 @@ "unit": "PCS", "status": "OPEN" } - ] + ], + "dataGrade": "synthetic" }, { "id": 6, @@ -244,7 +249,7 @@ "status": "APPROVED", "deliveryDate": "2026-04-03", "priority": 1, - "source": "SYNTHETIC", + "source": "FLEX", "items": [ { "id": 6, @@ -255,7 +260,8 @@ "unit": "PCS", "status": "OPEN" } - ] + ], + "dataGrade": "synthetic" }, { "id": 7, @@ -263,7 +269,7 @@ "status": "APPROVED", "deliveryDate": "2026-03-16", "priority": 1, - "source": "SYNTHETIC", + "source": "FLEX", "items": [ { "id": 7, @@ -274,7 +280,8 @@ "unit": "PCS", "status": "OPEN" } - ] + ], + "dataGrade": "synthetic" }, { "id": 8, @@ -282,7 +289,7 @@ "status": "APPROVED", "deliveryDate": "2026-04-03", "priority": 1, - "source": "SYNTHETIC", + "source": "FLEX", "items": [ { "id": 8, @@ -293,7 +300,8 @@ "unit": "PCS", "status": "OPEN" } - ] + ], + "dataGrade": "synthetic" }, { "id": 9, @@ -301,7 +309,7 @@ "status": "APPROVED", "deliveryDate": "2026-04-03", "priority": 1, - "source": "SYNTHETIC", + "source": "FLEX", "items": [ { "id": 9, @@ -312,7 +320,8 @@ "unit": "PCS", "status": "OPEN" } - ] + ], + "dataGrade": "synthetic" }, { "id": 10, @@ -320,7 +329,7 @@ "status": "APPROVED", "deliveryDate": "2026-04-03", "priority": 1, - "source": "SYNTHETIC", + "source": "FLEX", "items": [ { "id": 10, @@ -331,7 +340,8 @@ "unit": "PCS", "status": "OPEN" } - ] + ], + "dataGrade": "synthetic" } ], "productionOrders": [], From 5485f6de6278ed14907a0e065a831782ae70abc8 Mon Sep 17 00:00:00 2001 From: ssk Date: Thu, 3 Sep 2026 21:04:23 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=20Optimize=20=E6=9F=94=E6=80=A7=E6=8E=92?= =?UTF-8?q?=E4=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/agent_core/intent.py | 4 +++ server/aps_domain/workflow.py | 5 +++- tests/golden/test_optimize_chat_trigger.py | 30 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/golden/test_optimize_chat_trigger.py diff --git a/server/agent_core/intent.py b/server/agent_core/intent.py index 7658bb5..e88358e 100644 --- a/server/agent_core/intent.py +++ b/server/agent_core/intent.py @@ -707,6 +707,10 @@ def parse_fast(text: str, world: World) -> IntentResult | None: params["window"] = win elif re.search(r"滚动", t): params["window"] = "short" + # Explicit Optimize wording selects the integrated Optimize V2 adapter; + # ordinary flexible scheduling keeps the CLOSED_LOOP default. + if re.search(r"\boptimize\b|优化引擎|优化算法", t, re.I): + params["engine"] = "OPTIMIZE" return hit("flex.schedule", params) # 瓶颈产能评估(瓶颈产能法) if re.search(r"瓶颈产能|产能池|产能评估|各?工序产能|瓶颈在哪|产能瓶颈|能做多少|日产能", t): diff --git a/server/aps_domain/workflow.py b/server/aps_domain/workflow.py index c5827c9..0adf23b 100644 --- a/server/aps_domain/workflow.py +++ b/server/aps_domain/workflow.py @@ -1785,9 +1785,12 @@ def _run_flex(store: WorldStore, intent: IntentResult, actor: str) -> AgentReply store.save() else: result = run_flex_schedule(store, sort_mode=mode, order_ids=order_ids or None, - actor=actor, window=window) # P1 + actor=actor, window=window, + engine_type=engine or None) # P1 mode_key = str(result.get("sortMode") or result.get("engineType") or mode or "CLOSED_LOOP") mode_cn = _FLEX_MODE_CN.get(mode_key, mode_key) + if engine == "OPTIMIZE": + mode_cn = f"Optimize · {mode_cn}" solve_status = str(result.get("solveStatus") or "FEASIBLE").upper() if result.get("sortMode") == "EXTERNAL" or use_ext: mode_cn = f"外部算法({result.get('skillId') or 'skill'})" diff --git a/tests/golden/test_optimize_chat_trigger.py b/tests/golden/test_optimize_chat_trigger.py new file mode 100644 index 0000000..1aa9833 --- /dev/null +++ b/tests/golden/test_optimize_chat_trigger.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from pathlib import Path + +from server.agent_core.intent import parse_fast +from server.state.packs import load_pack + + +PACK_PATH = Path(__file__).resolve().parents[2] / "server" / "data" / "packs" / "optimize-simulation-v1.json" + + +def test_explicit_optimize_language_selects_optimize_flex_engine(): + world = load_pack(str(PACK_PATH)) + + result = parse_fast("用 Optimize 算法生成一版柔性排产", world) + + assert result is not None + assert result.intent == "flex.schedule" + assert result.params["engine"] == "OPTIMIZE" + assert result.params["sortMode"] == "BOTTLENECK" + + +def test_plain_flexible_schedule_keeps_closed_loop_default(): + world = load_pack(str(PACK_PATH)) + + result = parse_fast("生成一版柔性排产方案", world) + + assert result is not None + assert result.intent == "flex.schedule" + assert "engine" not in result.params From 1e64e9c0aeb7801c4344dbab4218c6224fc51c8b Mon Sep 17 00:00:00 2001 From: ssk Date: Thu, 3 Sep 2026 21:11:32 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E8=A7=84=E5=88=92=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E6=95=B0=E6=8D=AE=E5=8C=85=E4=B8=8E=E7=AE=97?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增本轮对话式数据包与 Optimize 算法选择计划,明确通用注册表解析、闭环调用、验收命令和合并前风险边界。关键洞察是把数据与算法作为同一条指令的两个独立槽位,复用现有柔性排产管线。 --- ...d-88-chat-data-algorithm-selection-plan.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 docs/round-88-chat-data-algorithm-selection-plan.md diff --git a/docs/round-88-chat-data-algorithm-selection-plan.md b/docs/round-88-chat-data-algorithm-selection-plan.md new file mode 100644 index 0000000..19dfc7e --- /dev/null +++ b/docs/round-88-chat-data-algorithm-selection-plan.md @@ -0,0 +1,55 @@ +# Round 88:对话指定数据包与排产算法 + +## 目标 + +让用户可以用一条自然语言指令同时指定数据包和算法,例如: + +> 使用 Optimize synthetic debug V1 数据,用 EDD 算法生成排产方案 + +系统应解析出 `dataPackRef`、`engine=OPTIMIZE` 和 `sortMode=EDD`,从已登记的数据包加载完整世界,再通过现有柔性闭环流程生成方案。 + +## 设计 + +1. 数据包使用 `server/state/packs.py` 的通用注册表解析,不增加某个客户或某个文件的专用 loader。支持文件名、名称和包内 aliases;找不到或匹配不唯一时不执行排产,直接提示可用数据包。 +2. 对话解析器识别 Optimize 算法及 EDD/SPT/PRIORITY/FIFO/LPT/CR/ATC 等排产规则,并保留原有未点名算法时的默认行为。 +3. `_run_flex` 在明确指定数据包时替换当前会话世界、重置 ID 计数器并记录所选包;随后把算法交给现有 `run_flex_schedule(..., engine_type="OPTIMIZE")`,不复制调度逻辑。 +4. 结果块增加数据包和算法证据,文本明确显示“使用哪个数据包、哪个算法、生成多少虚拟产线/工单”。 + +## 实施范围 + +- `server/state/packs.py`:包别名和通用引用解析。 +- `server/agent_core/intent.py`:数据包引用、算法槽位和自然语言参数。 +- `server/aps_domain/workflow.py`:加载包、重置计数器、结果证据和未找到包的错误提示。 +- `server/data/packs/optimize-simulation-v1.json`:补充可读 aliases(仅包元数据)。 +- `tests/golden/test_optimize_chat_data_selection.py`:解析和端到端柔性排产验证。 + +## 验收标准 + +- 指令 `使用 Optimize synthetic debug V1 数据,用 EDD 算法生成排产方案` 命中 `flex.schedule`,参数包含 `dataPackRef`、`engine=OPTIMIZE`、`sortMode=EDD`。 +- 在干净会话中执行后得到 `FEASIBLE`、10 条虚拟产线、72 条工单,结果块能看到数据包名称和算法。 +- 未知数据包不会创建排产版本,并返回可用数据包提示。 +- 普通“生成一版柔性排产方案”仍走原来的 CLOSED_LOOP 默认路径。 + +## 验证命令 + +```text +pytest -q tests/golden/test_optimize_chat_data_selection.py tests/golden/test_optimize_chat_trigger.py tests/golden/test_optimize_simulation_world_pack.py tests/golden/test_optimize_engine.py +``` + +再使用独立 `APS_HOME` 启动后端和前端,通过 `/api/chat` 发送上述完整中文指令,检查 SSE 中的 intent 参数、`FEASIBLE` 结果及 10/72 统计。 + +## 风险与边界 + +- 数据包选择是显式的会话世界替换,只允许来自注册表的路径;本轮不开放任意路径加载。 +- 规则名由 Optimize 适配器支持的 dispatch rule 白名单约束,未知算法不会被静默当成 EDD。 +- 当前轮次只在 `round/88-chat-data-algorithm-selection` 验证;完成后停在合并前,等待用户确认是否合并到 `codex/integrate-optimize`。 + +## 计划审计 + +PLAN AUDIT: PASS + +Blocking issues: none + +Clarification needed: none + +Non-blocking improvements: 后续可再把算法白名单从代码抽到能力描述接口。 From 1790a23a2d6b7ce8492517308cc0dfa186973d06 Mon Sep 17 00:00:00 2001 From: ssk Date: Thu, 3 Sep 2026 21:28:19 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E6=95=B0=E6=8D=AE=E5=8C=85=E4=B8=8E=E6=8E=92?= =?UTF-8?q?=E4=BA=A7=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为柔性排产增加数据包引用和 Optimize dispatch rule 解析,使用注册表安全加载世界数据并在结果卡片展示数据与算法证据。关键洞察是把数据源和算法作为两个独立槽位复用现有闭环管线,避免客户专用 loader 和重复调度逻辑。已验证 12 个定向测试、19 个闭环回归、前端构建及独立 HTTP SSE 聊天链路,实测产出 10 条虚拟产线和 72 个工单。 --- apps/web/src/chat/ChatPanel.tsx | 8 +++ apps/web/src/styles.css | 1 + server/agent_core/intent.py | 37 ++++++++-- server/aps_domain/workflow.py | 39 ++++++++++- server/data/packs/optimize-simulation-v1.json | 1 + server/state/packs.py | 33 +++++++++ .../test_optimize_chat_data_selection.py | 68 +++++++++++++++++++ 7 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 tests/golden/test_optimize_chat_data_selection.py diff --git a/apps/web/src/chat/ChatPanel.tsx b/apps/web/src/chat/ChatPanel.tsx index b885025..92f09a6 100644 --- a/apps/web/src/chat/ChatPanel.tsx +++ b/apps/web/src/chat/ChatPanel.tsx @@ -1346,6 +1346,8 @@ const CONFLICT_CN: Record = { function FlexScheduleBlock(props: { block: UIBlock }) { const p = props.block.props as { versionNo: string; modeCn: string; + dataPack?: { file?: string; name?: string; reference?: string } | null; + algorithm?: { engine?: string; rule?: string; algorithmId?: string | null } | null; stats: { vlCount: number; woCount: number; makespan: string | null; onTimeCount: number; conflictCount: number; utilization: number }; bottleneck: { name: string; count: number }[]; lines: FlexLine[]; @@ -1378,6 +1380,12 @@ function FlexScheduleBlock(props: { block: UIBlock }) { + {(p.dataPack || p.algorithm) && ( +
+ {p.dataPack && 数据:{p.dataPack.name || p.dataPack.file}} + {p.algorithm && 算法:{p.algorithm.engine || 'CLOSED_LOOP'}{p.algorithm.rule ? ` · ${p.algorithm.rule}` : ''}} +
+ )}
{s.vlCount}虚拟产线
{s.woCount}工单
diff --git a/apps/web/src/styles.css b/apps/web/src/styles.css index 770391d..fa2303d 100644 --- a/apps/web/src/styles.css +++ b/apps/web/src/styles.css @@ -2001,6 +2001,7 @@ html.aps-desktop .app-menubar-spacer, .flex-mode-badge { margin-left: auto; flex: 0 0 auto; font-size: 11px; font-weight: 600; color: var(--accent-deep); background: var(--accent-soft); border-radius: 999px; padding: 2px 10px; } .flex-mode-badge.warn { color: var(--warn); background: var(--warn-soft); } +.flex-block-meta { display: flex; gap: 14px; flex-wrap: wrap; margin: -2px 0 10px; color: var(--muted); font-size: 11.5px; } .flex-bottleneck { font-size: 11.5px; color: var(--muted); margin-bottom: 10px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .flex-bn-chip { font-size: 11px; font-weight: 600; color: var(--warn); background: var(--warn-soft); diff --git a/server/agent_core/intent.py b/server/agent_core/intent.py index e88358e..fae0b79 100644 --- a/server/agent_core/intent.py +++ b/server/agent_core/intent.py @@ -485,7 +485,13 @@ def parse_fast(text: str, world: World) -> IntentResult | None: if re.search(r"多方案|多策略|方案对比|(对比|比较).{0,8}(策略|方案|排法)|对比一下|三种策略|沙盒对比", t): return hit("scenario.compare") # 报告生成(M3 §9.10):日报 / 版本对比 / 排产方案 - if re.search(r"排产方案报告|排产报告|方案报告|下载排产|导出排产|生成排产方案|下载方案报告", t): + explicit_data_algorithm_schedule = bool( + re.search(r"(?:使用|用).+?数据(?:包)?", t, re.I) + and re.search(r"(?:算法|\b(?:EDD|SPT|PRIORITY|FIFO|LPT|CR|ATC)\b|\boptimize\b)", t, re.I) + and re.search(r"排产", t) + ) + if re.search(r"排产方案报告|排产报告|方案报告|下载排产|导出排产|生成排产方案|下载方案报告", t) \ + and not explicit_data_algorithm_schedule: from server.aps_domain.master_query import extract_code params: dict = {"reportType": "plan"} code = extract_code(t) @@ -690,9 +696,30 @@ def parse_fast(text: str, world: World) -> IntentResult | None: # 仅固定轨时走规则试排 return hit("schedule.run", params) # 柔性排产(能力池 + 虚拟产线):须在固定产线 schedule.run 之前判定 - if re.search(r"柔性排产|柔性排一?版|能力池排产|按能力池|虚拟产线|多品种小批量" + if explicit_data_algorithm_schedule or re.search(r"柔性排产|柔性排一?版|能力池排产|按能力池|虚拟产线|多品种小批量" r"|全流程排产|走一?个?全流程|正排|顺排|倒排|逆排|瓶颈锚|滚动排产|短窗排产|中窗排产" r"|实时排产|分钟级排产", t): + # Data pack and dispatch rule are independent slots in one command. + # Keep the reference unresolved here so the parser remains pure; the + # workflow resolves it against the registered pack catalog. + data_pack_ref = None + pack_match = re.search(r"(?:使用|用)\s*(.+?)\s*数据(?:包)?(?=\s*(?:[,,。]|用|使用|$))", t, re.I) + if pack_match: + data_pack_ref = pack_match.group(1).strip(" \t,,。") + + algorithm_rules = ( + ("EDD", r"\bEDD\b|最早交期|交期优先"), + ("SPT", r"\bSPT\b|最短工时|短工时优先"), + ("PRIORITY", r"\bPRIORITY\b|优先级(?:优先)?"), + ("FIFO", r"\bFIFO\b|先进先出"), + ("LPT", r"\bLPT\b|最长工时|长工时优先"), + ("CR", r"\bCR\b|临界比"), + ("ATC", r"\bATC\b|逾期成本"), + ) + selected_algorithm = next( + (rule for rule, pattern in algorithm_rules if re.search(pattern, t, re.I)), + None, + ) mode = ("ASC" if re.search(r"正排|顺排", t) else "DESC" if re.search(r"倒排|逆排", t) else "BOTTLENECK") @@ -702,14 +729,16 @@ def parse_fast(text: str, world: World) -> IntentResult | None: else "long" if re.search(r"长窗|周窗|7\s*天", t) else "full" if re.search(r"全量|全窗", t) else None) - params: dict = {"sortMode": mode} + params: dict = {"sortMode": selected_algorithm or mode} + if data_pack_ref: + params["dataPackRef"] = data_pack_ref if win: params["window"] = win elif re.search(r"滚动", t): params["window"] = "short" # Explicit Optimize wording selects the integrated Optimize V2 adapter; # ordinary flexible scheduling keeps the CLOSED_LOOP default. - if re.search(r"\boptimize\b|优化引擎|优化算法", t, re.I): + if selected_algorithm or re.search(r"\boptimize\b|优化引擎|优化算法", t, re.I): params["engine"] = "OPTIMIZE" return hit("flex.schedule", params) # 瓶颈产能评估(瓶颈产能法) diff --git a/server/aps_domain/workflow.py b/server/aps_domain/workflow.py index 0adf23b..d2813cf 100644 --- a/server/aps_domain/workflow.py +++ b/server/aps_domain/workflow.py @@ -1755,6 +1755,27 @@ def _run_flex(store: WorldStore, intent: IntentResult, actor: str) -> AgentReply from server.timeutil import parse_dt mode = intent.params.get("sortMode") or "BOTTLENECK" window = intent.params.get("window") + data_pack = None + data_pack_ref = str(intent.params.get("dataPackRef") or "").strip() + if data_pack_ref: + from server.state.packs import load_pack, resolve_pack_reference + data_pack = resolve_pack_reference(data_pack_ref) + if not data_pack: + from server.state.packs import list_packs + available = "、".join(str(item.get("name") or item.get("file")) for item in list_packs()) + return AgentReply( + text=(f"找不到数据包「{data_pack_ref}」,没有执行排产。" + f"可用数据包:{available or '暂无'}。"), + ) + # An explicit pack reference intentionally starts this run from that + # registered world, making repeated debug commands deterministic. + store.data = load_pack(data_pack["path"]) + store._reset_counters() + store.data.setdefault("flexParams", {})["selectedPack"] = { + "file": data_pack.get("file"), + "name": data_pack.get("name"), + "reference": data_pack_ref, + } order_ids = intent.params.get("orderIds") if isinstance(order_ids, list): order_ids = [int(x) for x in order_ids if str(x).isdigit() or isinstance(x, int)] @@ -1787,10 +1808,14 @@ def _run_flex(store: WorldStore, intent: IntentResult, actor: str) -> AgentReply result = run_flex_schedule(store, sort_mode=mode, order_ids=order_ids or None, actor=actor, window=window, engine_type=engine or None) # P1 - mode_key = str(result.get("sortMode") or result.get("engineType") or mode or "CLOSED_LOOP") + mode_key = str(result.get("dispatchRule") or result.get("sortMode") or result.get("engineType") or mode or "CLOSED_LOOP") mode_cn = _FLEX_MODE_CN.get(mode_key, mode_key) if engine == "OPTIMIZE": - mode_cn = f"Optimize · {mode_cn}" + optimize_rule_cn = { + "EDD": "EDD 最早交期", "SPT": "SPT 最短工时", "PRIORITY": "PRIORITY 优先级", + "FIFO": "FIFO 先进先出", "LPT": "LPT 最长工时", "CR": "CR 临界比", "ATC": "ATC 逾期成本", + } + mode_cn = f"Optimize · {optimize_rule_cn.get(mode_key.upper(), mode_cn)}" solve_status = str(result.get("solveStatus") or "FEASIBLE").upper() if result.get("sortMode") == "EXTERNAL" or use_ext: mode_cn = f"外部算法({result.get('skillId') or 'skill'})" @@ -1861,6 +1886,13 @@ def _run_flex(store: WorldStore, intent: IntentResult, actor: str) -> AgentReply blockId=f"flex-sched-{result['versionId']}", type="flex-schedule", props={ "versionNo": result["versionNo"], "modeCn": mode_cn, + "algorithm": { + "engine": result.get("engineType") or engine or "CLOSED_LOOP", + "rule": result.get("dispatchRule") or mode, + "algorithmId": result.get("algorithmId"), + }, + "dataPack": ({"file": data_pack.get("file"), "name": data_pack.get("name"), + "reference": data_pack_ref} if data_pack else None), "stats": {"vlCount": result["vlCount"], "woCount": result["woCount"], "makespan": result.get("makespan"), "onTimeCount": result.get("onTimeCount", 0), "conflictCount": result["conflictCount"], @@ -1911,8 +1943,9 @@ def _run_flex(store: WorldStore, intent: IntentResult, actor: str) -> AgentReply + ("\n\n别担心,我把还缺的东西列在下面,你按顺序补就行。" if guide_block else "") ) else: + pack_hint = f"数据包「{data_pack.get('name')}」· " if data_pack else "" text = ( - f"柔性排产完成。{focus_hint}版本 {result['versionNo']}({mode_cn}·{win_cn}):" + f"柔性排产完成。{pack_hint}{focus_hint}版本 {result['versionNo']}({mode_cn}·{win_cn}):" f"{result['vlCount']} 条虚拟产线 / {result['woCount']} 个工单,冲突 {result['conflictCount']} 项" + (f",窗外延期 {deferred}" if deferred else "") + f"。{dl_hint}" diff --git a/server/data/packs/optimize-simulation-v1.json b/server/data/packs/optimize-simulation-v1.json index cd09a1b..0098f22 100644 --- a/server/data/packs/optimize-simulation-v1.json +++ b/server/data/packs/optimize-simulation-v1.json @@ -1,6 +1,7 @@ { "packVersion": 1, "name": "Optimize synthetic debug V1", + "aliases": ["Optimize 模拟数据", "Optimize synthetic data", "simulation"], "description": "Generic APS world pack: 10 synthetic orders, 72 operations, and 6 virtual resources for Optimize development debugging only.", "baseDate": "2026-09-03", "world": { diff --git a/server/state/packs.py b/server/state/packs.py index f9b940c..09eb06f 100644 --- a/server/state/packs.py +++ b/server/state/packs.py @@ -80,7 +80,40 @@ def list_packs() -> list[dict[str, Any]]: head = json.load(f) out.append({"file": fn, "name": head.get("name") or fn, "description": head.get("description") or "", + "aliases": list(head.get("aliases") or []), "baseDate": head.get("baseDate") or "", "path": path}) except (json.JSONDecodeError, OSError): continue return out + + +def _pack_ref_key(value: str) -> str: + """Normalize a user-facing pack reference without imposing a customer name.""" + value = str(value or "").strip().lower() + value = re.sub(r"(?:数据包?|data\s*pack|数据)$", "", value).strip() + return re.sub(r"[^0-9a-z\u4e00-\u9fff]+", "", value) + + +def resolve_pack_reference(reference: str) -> dict[str, Any] | None: + """Resolve a natural-language pack reference against the registered pack catalog. + + Matching is deliberately limited to metadata (file/name/aliases) so a chat + command can never turn an arbitrary path into a data load operation. + """ + key = _pack_ref_key(reference) + if not key: + return None + packs = list_packs() + exact: list[dict[str, Any]] = [] + loose: list[dict[str, Any]] = [] + for pack in packs: + labels = [pack.get("file"), os.path.splitext(str(pack.get("file") or ""))[0], + pack.get("name"), *(pack.get("aliases") or [])] + keys = {_pack_ref_key(label) for label in labels if label} + if key in keys: + exact.append(pack) + continue + if any(key in label_key or label_key in key for label_key in keys if label_key): + loose.append(pack) + matches = exact if exact else loose + return matches[0] if len(matches) == 1 else None diff --git a/tests/golden/test_optimize_chat_data_selection.py b/tests/golden/test_optimize_chat_data_selection.py new file mode 100644 index 0000000..dfa0f4d --- /dev/null +++ b/tests/golden/test_optimize_chat_data_selection.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +import asyncio +from pathlib import Path + +from server.agent_core.intent import parse_fast +from server.aps_domain.workflow import handle_intent +from server.state.packs import load_pack, resolve_pack_reference +from server.state.store import WorldStore + + +PACK_PATH = Path(__file__).resolve().parents[2] / "server" / "data" / "packs" / "optimize-simulation-v1.json" + + +def test_chat_command_parses_data_pack_and_algorithm_slots(): + world = load_pack(str(PACK_PATH)) + + result = parse_fast( + "使用 Optimize synthetic debug V1 数据用 EDD 算法生成排产方案", + world, + ) + + assert result is not None + assert result.intent == "flex.schedule" + assert result.params["dataPackRef"] == "Optimize synthetic debug V1" + assert result.params["engine"] == "OPTIMIZE" + assert result.params["sortMode"] == "EDD" + + +def test_pack_reference_resolves_registered_aliases(): + pack = resolve_pack_reference("Optimize 模拟数据") + + assert pack is not None + assert pack["file"] == "optimize-simulation-v1.json" + + +def test_chat_command_loads_pack_and_materializes_optimize_result(tmp_path, monkeypatch): + monkeypatch.setenv("APS_DB_DISABLED", "1") + store = WorldStore(path=str(tmp_path / "world.json")) + intent = parse_fast( + "使用 Optimize synthetic debug V1 数据用 EDD 算法生成排产方案", + store.data, + ) + assert intent is not None + + reply = asyncio.run(handle_intent(store, "round88", intent, actor="test")) + + assert "Optimize" in reply.text + assert "Optimize synthetic debug V1" in reply.text + block = next(block for block in reply.blocks if block.type == "flex-schedule") + assert block.props["dataPack"]["file"] == "optimize-simulation-v1.json" + assert block.props["algorithm"]["rule"] == "EDD" + assert block.props["stats"]["vlCount"] == 10 + assert block.props["stats"]["woCount"] == 72 + assert store.data["flexScheduleVersions"][-1]["engineType"] == "OPTIMIZE" + assert store.data["flexScheduleVersions"][-1]["algorithmId"] == "optimize.edd" + + +def test_unknown_pack_fails_closed_without_creating_schedule(tmp_path, monkeypatch): + monkeypatch.setenv("APS_DB_DISABLED", "1") + store = WorldStore(path=str(tmp_path / "world.json")) + intent = parse_fast("使用不存在的数据,用 EDD 算法生成排产方案", store.data) + assert intent is not None + + reply = asyncio.run(handle_intent(store, "round88", intent, actor="test")) + + assert "找不到数据包" in reply.text + assert not store.data.get("flexScheduleVersions") From f03596b134d8b999ee8dd1062ba104dcf4f9dc72 Mon Sep 17 00:00:00 2001 From: ssk Date: Thu, 3 Sep 2026 22:24:48 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=8C=87=E5=AE=9A=E7=AE=97=E6=B3=95=E6=8E=92?= =?UTF-8?q?=E4=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 扩展对话解析以识别目录、文件和算法三个槽位,复用项目深度分析读取 Excel/CSV/SQL,并在路径不存在时阻断旧数据回退。关键洞察是显式文件输入必须先做路径校验,再进入现有 Optimize 闭环。已验证 29 个回归测试通过,并用 HTTP SSE 实测不存在路径会直接返回错误。 --- ...d-88-chat-data-algorithm-selection-plan.md | 12 +++-- server/agent_core/intent.py | 48 +++++++++++++++---- server/aps_domain/workflow.py | 44 +++++++++++++++-- .../test_optimize_chat_data_selection.py | 30 ++++++++++++ 4 files changed, 120 insertions(+), 14 deletions(-) diff --git a/docs/round-88-chat-data-algorithm-selection-plan.md b/docs/round-88-chat-data-algorithm-selection-plan.md index 19dfc7e..7ae72d9 100644 --- a/docs/round-88-chat-data-algorithm-selection-plan.md +++ b/docs/round-88-chat-data-algorithm-selection-plan.md @@ -2,15 +2,19 @@ ## 目标 -让用户可以用一条自然语言指令同时指定数据包和算法,例如: +让用户可以用一条自然语言指令同时指定数据来源和算法,例如: > 使用 Optimize synthetic debug V1 数据,用 EDD 算法生成排产方案 +也支持指定本机目录里的文件: + +> 使用 D:\\aps-data 目录下的 orders.xlsx 数据用 EDD 算法生成排产方案 + 系统应解析出 `dataPackRef`、`engine=OPTIMIZE` 和 `sortMode=EDD`,从已登记的数据包加载完整世界,再通过现有柔性闭环流程生成方案。 ## 设计 -1. 数据包使用 `server/state/packs.py` 的通用注册表解析,不增加某个客户或某个文件的专用 loader。支持文件名、名称和包内 aliases;找不到或匹配不唯一时不执行排产,直接提示可用数据包。 +1. 已登记数据包使用 `server/state/packs.py` 的通用注册表解析,不增加某个客户或某个文件的专用 loader。目录/文件指令复用 `analyze_project_deep` 的通用 Excel/CSV/SQL 读取器;路径不存在时不回退到旧世界。 2. 对话解析器识别 Optimize 算法及 EDD/SPT/PRIORITY/FIFO/LPT/CR/ATC 等排产规则,并保留原有未点名算法时的默认行为。 3. `_run_flex` 在明确指定数据包时替换当前会话世界、重置 ID 计数器并记录所选包;随后把算法交给现有 `run_flex_schedule(..., engine_type="OPTIMIZE")`,不复制调度逻辑。 4. 结果块增加数据包和算法证据,文本明确显示“使用哪个数据包、哪个算法、生成多少虚拟产线/工单”。 @@ -26,8 +30,10 @@ ## 验收标准 - 指令 `使用 Optimize synthetic debug V1 数据,用 EDD 算法生成排产方案` 命中 `flex.schedule`,参数包含 `dataPackRef`、`engine=OPTIMIZE`、`sortMode=EDD`。 +- 指令 `使用 D:\\aps-data 目录下的 orders.xlsx 数据用 EDD 算法生成排产方案` 命中 `flex.schedule`,参数包含 `dataDir`、`dataFile`、`dataPath`、`engine=OPTIMIZE`、`sortMode=EDD`。 - 在干净会话中执行后得到 `FEASIBLE`、10 条虚拟产线、72 条工单,结果块能看到数据包名称和算法。 - 未知数据包不会创建排产版本,并返回可用数据包提示。 +- 不存在的目录或文件不会使用当前旧数据排产,而是直接返回路径错误。 - 普通“生成一版柔性排产方案”仍走原来的 CLOSED_LOOP 默认路径。 ## 验证命令 @@ -52,4 +58,4 @@ Blocking issues: none Clarification needed: none -Non-blocking improvements: 后续可再把算法白名单从代码抽到能力描述接口。 +Non-blocking improvements: 后续可再把算法白名单从代码抽到能力描述接口;目录导入仍沿用现有 P2 确认流程的审计策略。 diff --git a/server/agent_core/intent.py b/server/agent_core/intent.py index fae0b79..cd2e46c 100644 --- a/server/agent_core/intent.py +++ b/server/agent_core/intent.py @@ -5,6 +5,7 @@ # ============================================================ from __future__ import annotations # 前向类型引用 +import os # 路径解析 import re # 正则快路 from typing import Any # 类型标注 @@ -206,6 +207,33 @@ def _is_imperative_schedule(text: str) -> bool: )) +def _extract_explicit_data_source(text: str) -> dict[str, str]: + """Extract an absolute data directory/file pair from a chat command.""" + t = str(text or "") + ext = r"(?:xlsx|xlsm|csv|txt|sql)" + file_match = re.search(rf"([A-Za-z]:[\\/][^\r\n\"'<>|,,。;;]*?\.{ext})", t, re.I) + dir_match = re.search(r"([A-Za-z]:[\\/][^\r\n\"'<>|,,。;;]+?)\s*目录下", t, re.I) + data_dir = "" + data_file = "" + data_path = "" + if file_match: + data_path = file_match.group(1).strip().rstrip("。,,;;") + data_dir = re.sub(r"[\\/][^\\/]+$", "", data_path) + data_file = re.split(r"[\\/]", data_path)[-1] + if dir_match: + data_dir = dir_match.group(1).strip().rstrip("\\/") + file_name_match = re.search( + rf"目录下(?:的)?\s*[`\"“”']?([^\s`\"“”',,。;;]+\.{ext})", + t, re.I, + ) + if file_name_match: + data_file = file_name_match.group(1).strip() + data_path = f"{data_dir}{os.sep}{data_file}" + if not data_dir and not data_path: + return {} + return {"dataDir": data_dir, "dataFile": data_file, "dataPath": data_path} + + # ---------------- 一级:规则快路(移植 POC parseNL,P0 纯解析) ---------------- def parse_fast(text: str, world: World) -> IntentResult | None: """正则词表快路:命中返回高置信意图;未命中返回 None(交给 LLM)。""" @@ -234,8 +262,14 @@ def parse_fast(text: str, world: World) -> IntentResult | None: return hit("data.analyze", params) # 带真实表格/SQL 路径的口令优先按数据处理,不能被句尾「知识库」误吞。 has_data_path = bool(re.search(r"[A-Za-z]:[\\/].*\.(xlsx|xlsm|csv|txt|sql)\b", t, re.I)) + explicit_data_algorithm_schedule = bool( + re.search(r"(?:使用|用).+?数据(?:包)?", t, re.I) + and re.search(r"(?:算法|\b(?:EDD|SPT|PRIORITY|FIFO|LPT|CR|ATC)\b|\boptimize\b)", t, re.I) + and re.search(r"排产", t) + ) if has_data_path and re.search(r"排产|试排|开排", t): - return hit("folder.schedule", {"query": t}) + if not explicit_data_algorithm_schedule: + return hit("folder.schedule", {"query": t}) if has_data_path and re.search(r"解析|分析|读取|识别|导入|入库|主数据|知识库", t): return hit("data.analyze", {"query": t}) # 工程目录:深度解析 / 按目录数据排产(靠前) @@ -485,11 +519,6 @@ def parse_fast(text: str, world: World) -> IntentResult | None: if re.search(r"多方案|多策略|方案对比|(对比|比较).{0,8}(策略|方案|排法)|对比一下|三种策略|沙盒对比", t): return hit("scenario.compare") # 报告生成(M3 §9.10):日报 / 版本对比 / 排产方案 - explicit_data_algorithm_schedule = bool( - re.search(r"(?:使用|用).+?数据(?:包)?", t, re.I) - and re.search(r"(?:算法|\b(?:EDD|SPT|PRIORITY|FIFO|LPT|CR|ATC)\b|\boptimize\b)", t, re.I) - and re.search(r"排产", t) - ) if re.search(r"排产方案报告|排产报告|方案报告|下载排产|导出排产|生成排产方案|下载方案报告", t) \ and not explicit_data_algorithm_schedule: from server.aps_domain.master_query import extract_code @@ -703,8 +732,9 @@ def parse_fast(text: str, world: World) -> IntentResult | None: # Keep the reference unresolved here so the parser remains pure; the # workflow resolves it against the registered pack catalog. data_pack_ref = None + source_slots = _extract_explicit_data_source(t) pack_match = re.search(r"(?:使用|用)\s*(.+?)\s*数据(?:包)?(?=\s*(?:[,,。]|用|使用|$))", t, re.I) - if pack_match: + if pack_match and not source_slots: data_pack_ref = pack_match.group(1).strip(" \t,,。") algorithm_rules = ( @@ -730,7 +760,9 @@ def parse_fast(text: str, world: World) -> IntentResult | None: else "full" if re.search(r"全量|全窗", t) else None) params: dict = {"sortMode": selected_algorithm or mode} - if data_pack_ref: + if source_slots: + params.update(source_slots) + elif data_pack_ref: params["dataPackRef"] = data_pack_ref if win: params["window"] = win diff --git a/server/aps_domain/workflow.py b/server/aps_domain/workflow.py index d2813cf..286cb31 100644 --- a/server/aps_domain/workflow.py +++ b/server/aps_domain/workflow.py @@ -1749,13 +1749,48 @@ def _external_audit_evidence(summary: dict, trace: dict) -> list[str]: return refs -def _run_flex(store: WorldStore, intent: IntentResult, actor: str) -> AgentReply: +def _run_flex(store: WorldStore, intent: IntentResult, actor: str, + session_id: str | None = None) -> AgentReply: """触发柔性排产并回执(短文案 + 结构化 flex-schedule 块:KPI + 虚拟产线表 + 瓶颈)。""" from server.aps_domain.flex import run_flex_schedule from server.timeutil import parse_dt mode = intent.params.get("sortMode") or "BOTTLENECK" window = intent.params.get("window") data_pack = None + data_source = None + data_path = str(intent.params.get("dataPath") or "").strip() + data_dir = str(intent.params.get("dataDir") or "").strip() + data_file = str(intent.params.get("dataFile") or "").strip() + if data_path or data_dir: + from server.aps_domain.project_analyze import analyze_project_deep + source_query = data_path or (os.path.join(data_dir, data_file) if data_file else data_dir) + if data_file and not os.path.isfile(source_query): + return AgentReply(text=f"数据文件不存在:{source_query},没有执行排产。") + if not data_file and data_dir and not os.path.isdir(data_dir): + return AgentReply(text=f"数据目录不存在:{data_dir},没有执行排产。") + if data_path and not data_file and not os.path.isfile(data_path) and not os.path.isdir(data_path): + return AgentReply(text=f"数据路径不存在:{data_path},没有执行排产。") + try: + source_report = analyze_project_deep( + store.data, session_id, apply_sql=True, + query=source_query, next_id=store.next_id, + ) + except (OSError, PermissionError, ValueError) as exc: + return AgentReply(text=f"数据文件读取失败:{exc}") + if not source_report.get("ok"): + return AgentReply(text=f"数据目录分析失败:{source_report.get('error') or '目录不可用'}") + if not source_report.get("canSchedule"): + missing = (source_report.get("plan") or [])[:3] + detail = ";".join(str(item) for item in missing) + return AgentReply( + text=(f"数据已读取,但暂时不能排产:缺少可排所需的订单、工艺或设备信息。" + + (f"\n{detail}" if detail else "")), + ) + data_source = { + "directory": source_report.get("workDir") or data_dir, + "file": data_file or (os.path.basename(data_path) if data_path else None), + "paths": source_report.get("sourcePaths") or [], + } data_pack_ref = str(intent.params.get("dataPackRef") or "").strip() if data_pack_ref: from server.state.packs import load_pack, resolve_pack_reference @@ -1893,6 +1928,7 @@ def _run_flex(store: WorldStore, intent: IntentResult, actor: str) -> AgentReply }, "dataPack": ({"file": data_pack.get("file"), "name": data_pack.get("name"), "reference": data_pack_ref} if data_pack else None), + "dataSource": data_source, "stats": {"vlCount": result["vlCount"], "woCount": result["woCount"], "makespan": result.get("makespan"), "onTimeCount": result.get("onTimeCount", 0), "conflictCount": result["conflictCount"], @@ -1943,7 +1979,9 @@ def _run_flex(store: WorldStore, intent: IntentResult, actor: str) -> AgentReply + ("\n\n别担心,我把还缺的东西列在下面,你按顺序补就行。" if guide_block else "") ) else: - pack_hint = f"数据包「{data_pack.get('name')}」· " if data_pack else "" + pack_hint = (f"数据包「{data_pack.get('name')}」· " if data_pack else + f"数据文件「{data_source.get('file') or data_source.get('directory')}」· " + if data_source else "") text = ( f"柔性排产完成。{pack_hint}{focus_hint}版本 {result['versionNo']}({mode_cn}·{win_cn}):" f"{result['vlCount']} 条虚拟产线 / {result['woCount']} 个工单,冲突 {result['conflictCount']} 项" @@ -2942,7 +2980,7 @@ async def handle_intent(store: WorldStore, session_id: str, intent: IntentResult return AgentReply(text=f"{title} 属于 P2 写操作,需要你确认。", blocks=[block]) # ---- 柔性排产(P1:能力池动态组虚拟产线,写草稿版本 §M5) ---- if name == "flex.schedule": - return _run_flex(store, intent, actor) + return _run_flex(store, intent, actor, session_id=session_id) if name == "flex.reschedule": return _flex_reschedule(store, intent, session_id, actor) if name == "flex.swap": diff --git a/tests/golden/test_optimize_chat_data_selection.py b/tests/golden/test_optimize_chat_data_selection.py index dfa0f4d..f14b4e5 100644 --- a/tests/golden/test_optimize_chat_data_selection.py +++ b/tests/golden/test_optimize_chat_data_selection.py @@ -34,6 +34,21 @@ def test_pack_reference_resolves_registered_aliases(): assert pack["file"] == "optimize-simulation-v1.json" +def test_chat_command_parses_directory_file_and_algorithm_slots(): + result = parse_fast( + r"使用 D:\aps-data 目录下的 orders.xlsx 数据用 EDD 算法生成排产方案", + {}, + ) + + assert result is not None + assert result.intent == "flex.schedule" + assert result.params["dataDir"] == r"D:\aps-data" + assert result.params["dataFile"] == "orders.xlsx" + assert result.params["dataPath"].endswith(r"aps-data\orders.xlsx") + assert result.params["sortMode"] == "EDD" + assert result.params["engine"] == "OPTIMIZE" + + def test_chat_command_loads_pack_and_materializes_optimize_result(tmp_path, monkeypatch): monkeypatch.setenv("APS_DB_DISABLED", "1") store = WorldStore(path=str(tmp_path / "world.json")) @@ -66,3 +81,18 @@ def test_unknown_pack_fails_closed_without_creating_schedule(tmp_path, monkeypat assert "找不到数据包" in reply.text assert not store.data.get("flexScheduleVersions") + + +def test_unreadable_directory_fails_closed_without_scheduling(tmp_path, monkeypatch): + monkeypatch.setenv("APS_DB_DISABLED", "1") + store = WorldStore(path=str(tmp_path / "world.json")) + intent = parse_fast( + r"使用 D:\missing-aps-data 目录下的 orders.xlsx 数据用 EDD 算法生成排产方案", + store.data, + ) + assert intent is not None + + reply = asyncio.run(handle_intent(store, "round88", intent, actor="test")) + + assert "数据文件不存在" in reply.text or "数据目录不存在" in reply.text + assert not store.data.get("flexScheduleVersions")