粗排引擎 Functional Spec ⇄ 舊版 ScheduleEngine_wo 比對報告
產出日期:2026-06-08 比對對象 A(基準・預計更新版):
functional-spec/rough-cut-scheduler.md(狀態:已簽核;2026-06-04 修正上游 JIT 拉動、廢棄 §RCS-FS-10) 比對對象 B(既有實作):Lean-Scheduling/src/main/java/com/thuai/smb/lean/module/ScheduleEngine_wo.java(2350 行) 輔助佐證:LatestStartCalculator.java、LatestStartResult.java、VSMUtil.java、StdScheduleKanbanSorter.java性質:一次性比對作業(與 workflow 五階段鏈無關),不修改任何程式碼、不裁定。
0. 一句話結論
舊版 ScheduleEngine_wo 是一支 「細排+粗排合一」的工單級排程引擎(以 scheduleParameter.isRoughSchedule() 切換模式);新 spec 描述的是一支 「純試算、投產單級、只產日期」的獨立粗排引擎。兩者定位不同:
- 方向一(spec 是否涵蓋舊版功能):spec 涵蓋了粗排該有的核心(LSD 反推、LeanPlay 節拍點錨定+有限產能堆疊、上游拉動/下游推動、空批次、交期早於今日),但有 2 處語意刻意改變(LeanPlay 改正推 ASAP 錨定、不再交期反推;引擎不判 overdue)與 1 處 spec 更嚴謹、舊版未實作(§RCS-FS-1 無節拍點 fallback)。
- 方向二(舊版是否多出未定義功能):舊版多出大量功能。其中一部分 spec 已明確聲明「不做/留下一期」(屬設計性排除,非缺口);另一部分 spec 完全未提及(多為「工單級/細排」機制),需團隊裁定要「捨棄、或標記為未來範圍」。
最該注意的 3 件事(詳見 §4):
- ❌ 舊版會判逾期並產生 ScheduleAlarm/拋例外,與 spec §RCS-FS-7「引擎不判 overdue」直接相反。
- ❌ 舊版無 §RCS-FS-1 fallback:VSM 找不到節拍點時回傳
null,後續 NPE。 - ⚠️ 「投產順序」兩邊定義不同:spec=LeanPlay 完工日全域排序;舊版=可插拔 sorter 依「MTO/MTS→開工日→manualSeq」排。
1. 兩邊根本定位差異(先對齊框架,再逐項比)
| 面向 | 新 Functional Spec | 舊版 ScheduleEngine_wo |
|---|---|---|
| 引擎範圍 | 獨立粗排引擎(細排的 input 之一) | 細排+粗排合一,isRoughSchedule() 切換 |
| 處理顆粒 | 投產單({poNo, product, qty, dueDate, prioritySeq}) | **工單(WorkOrder)**級,支援單投產單對多工單迭代 |
| 輸出模型 | 每個 VSM 節點掛 {LSD, LeanPlay 交期, 投產順序}(純日期標記) | KanBanProduce 生產看板 + KanBanMaterial 物料看板清單 |
| 產能語意 | 純試算、不寫回/不鎖定 | 實際消耗並 commit 產能表;非粗排模式另 saveSnapshot 落地 |
| 顆粒度 | 到「天」 | 到時間(時/分,含班制 WorkShift、時區位移) |
| 到料/庫存 | 不考慮(§RCS-FS-5,只保留「需要哪些料」) | 完整 WIP/庫存供需、未搓合工單搓合 |
因為定位不同,「舊版多很多功能」是預期內的——關鍵在於分辨哪些是 spec 刻意排除、哪些是 spec 漏掉而需補進範圍說明。
2. 方向一:Spec 是否涵蓋舊版(粗排相關)功能?
逐項對照 spec 的 Use Case/邊界條件 vs 舊版實作。狀態:✅ 已涵蓋且一致/⚠️ 涵蓋但語意有差/❌ 未涵蓋或相反。
| # | Spec 項目 | 舊版對應實作(檔:行) | 狀態 | 說明 |
|---|---|---|---|---|
| 1 | LSD 反推(§RCS-FS-13:自交期反推、純 leadtime、不看產能) | LatestStartCalculator.calculateNodeRecursive(LatestStartCalculator.java:91-153),由 calculateLatestStart() 在排程後呼叫(ScheduleEngine_wo.java:2142-2174) | ⚠️ | 核心一致(交期為錨、往前反推、不消耗產能)。兩點差異:(a) 逾期時 LSD clamp 到 nowDt(LatestStartCalculator.java:117-118),spec §RCS-FS-3 則是「保留過去值、視為無意義」;(b) leadtime 會除以開機數(LatestStartCalculator.java:161-165),spec toy 假設單一資源、未涵蓋資源數對 LSD 的影響。 |
| 2 | LeanPlay:節拍點為錨(§RCS-FS-11 正推定錨求最早可開工日 → 有限產能堆疊 → 節拍點往前 JIT 拉動上游、往後推動下游) | schedule() 節拍點起排日計算(ScheduleEngine_wo.java:450-475)、有限產能 arrangeTimeWithProduceCapacity(:534)、pullScheduleProduceKanban(:850)、pushScheduleProduceKanban(:1435) | ⚠️ | 結構對應(節拍點定錨→拉動/推動)。語意差異:spec 2026-06-04 已廢棄交期反推(§RCS-FS-10)改為自起排日正推 ASAP;舊版粗排模式仍保留「交期 JIT 反推」分支(:456-459,STRATEGY_TAKT_STARTDATE_JIT)與「nowDt 正推 standardLeadTime」分支(:461-463)。即舊版的節拍點錨點來源與新 spec 不一致,需以 spec 為準重做。 |
| 3 | 有限產能堆疊、不足往後一天(§RCS-FS-2 排擠) | arrangeTimeWithProduceCapacity 失敗→節拍點 +1 天(ScheduleEngine_wo.java:538-547) | ✅ | 機制一致。 |
| 4 | 上游往前拉動 N-x(JIT、緊鄰、不留閒置) | getPullNodeDt(最晚開工反推,:1371-1402)+ pullScheduleProduceKanban(:850) | ✅ | 拉動以「緊鄰後站開工」反推,與 JIT 不留閒置一致。 |
| 5 | 下游往後推動 N+y | getPushNodeDt(:1328-1369)+ pushScheduleProduceKanban(:1435) | ✅ | 機制一致。 |
| 6 | 節拍點隨排擠後移、上游整段隨之後移(§RCS-FS-15) | 節拍點 +1 天後重排上游(:656-664 整段重排) | ✅ | 行為一致(上游隨節拍點重新拉動)。 |
| 7 | 無節拍點→以最後一站為節拍點(§RCS-FS-1) | VSMUtil.getTaktPoint 找不到回傳 null(VSMUtil.java:43-50);ScheduleEngine_wo.java:398 取得後無 fallback,後續 taktPoint.dataBox/getArrangeKanBan 會 NPE | ❌ | 舊版未實作 §RCS-FS-1。spec 此處比舊版嚴謹,屬新增需求。 |
| 8 | 投產順序=LeanPlay 完工日全域排序、連續編號 1..N | processAfterSchedule() 用可插拔 IScheduleKanbanSorter(ScheduleEngine_wo.java:841-845);預設 StdScheduleKanbanSorter 依「MTO/MTS → 開工日 expectStartTime → manualSeq → 成品階 → processSeq」排(StdScheduleKanbanSorter.java:20-35) | ⚠️ | 定義不同:spec 依「完工日」全域排序;舊版依「開工日 +業務分群(MTO/MTS、manualSeq)」。需釐清採哪個定義。 |
| 9 | 空批次 → 回傳空、不報錯(§RCS-FS-8) | schedule() 對空 productionOrderList 自然產生空輸出(:357 for-loop);但 inputCheck() 對缺 VSM/產能等會 inputCheckOK=false 直接不跑(:189-243) | ⚠️ | 「空投產單批次」可達成空輸出;但舊版的 inputCheck 是另一層前置擋門,對「空輸入」的容忍邊界與 spec B8「合法空輸入」略有出入,需確認。 |
| 10 | 交期早於今日 → LeanPlay 落未來、LSD 落過去、引擎不判 overdue(§RCS-FS-3) | LeanPlay:nowDt 夾住、不排到過去(:419-421, 469-474)✅;LSD:clamp 到 nowDt(LatestStartCalculator.java:117-118)⚠️;overdue:見下列第 11 點 ❌ | ⚠️ | LeanPlay 不落過去一致;LSD 落過去的處理不同(見第 1 點)。 |
| 11 | 引擎只輸出日期、不比對交期、不判 overdue(§RCS-FS-7) | 舊版多處判逾期::530-531 log error、:688-690 setOverdue、:772-776 拋「逾期」例外並 createScheduleAlarm、:786-790 互動模式逾期停止 | ❌ | 與 spec 相反。舊版主動判逾期、產警報、甚至中斷;spec 明定引擎不判、交由下游。 |
| 12 | 純試算、不寫回/鎖定產能(UC A1 預期結果) | 粗排模式跳過 saveSnapshot(:765-767);但 arrangeTimeWithProduceCapacity 仍會消耗並 commitFinalProductivityCalculation(:700, :316-319)改寫記憶體產能表 | ⚠️ | 不落 DB 與「不寫回」精神相符;但引擎內部確實會佔用/commit 產能(為了多單排擠 §RCS-FS-2),是否算「鎖定」需與 spec 語意對齊。 |
方向一小結:粗排核心骨架(節拍點錨定+有限產能堆疊+拉動/推動+LSD 反推)在舊版都找得到對應,spec 沒有「漏掉舊版既有的粗排能力」。但有 3 處需要對齊:§RCS-FS-1 fallback(舊版缺)、§RCS-FS-7 不判 overdue(舊版相反)、LeanPlay 錨點來源(舊版仍用交期反推,spec 已改正推 ASAP),以及「投產順序」定義差異。
3. 方向二:舊版多出哪些 Spec 未定義的功能?
分三類。A 類=spec 明確排除(非缺口);B 類=spec 完全未提及(潛在缺口,需裁定);C 類=spec 有但舊版無(補記,與 §2 呼應)。
A 類:Spec 已明確聲明「不做/留下一期」——屬設計性排除
| 舊版功能 | 證據(檔:行) | Spec 對應排除依據 |
|---|---|---|
| 產銷協調互動入口/互動式模式(逾期單停止) | isTurnOnInteractionMode、:772-790 | 概述「產銷協調互動入口明確不做、留下一期」;需求一覆蓋對照=「明確不做」 |
| 逾期判定/排程警報(ScheduleAlarm) | AlarmMgr.createScheduleAlarm、ALARM_CODE_PO_SCHEDULE_OVERDUE、:772-790 | §RCS-FS-7(引擎不判 overdue) |
| 寫回/鎖定產能(Snapshot 落地) | SnapshotDelegate.saveSnapshot、:32-36, 765-767 | UC A1 預期結果「純試算、不寫回/鎖定」(§Q4 待裁定是否落地) |
| 到料/WIP/庫存供需協調 | checkSupplyDemand(:2021)、updateOrderRequestQty(:1954)、getWIPSupply/getInventorySupply(:975-984) | §RCS-FS-5(不考慮到料,只保留「需要哪些料」) |
| 集批控制 | getArrangeKanBan 已被移除、恆回傳空(:2072-2074);getTaktTolerance 拆批(:548-571) | §RCS-FS-14(集批不支援;LSD 給單一日期) |
A 類重點:這些舊版有、spec 沒有,是故意的。報告只是把它們點名,確認「捨棄/延後」是有意識的決定。其中「集批」在舊版其實也已被拔成空殼(
getArrangeKanBan恆空),與 spec B14 方向一致。
B 類:Spec 完全未提及——潛在缺口,需團隊裁定(捨棄 or 標記未來範圍)
| # | 舊版功能 | 證據(檔:行) | 為何值得裁定 |
|---|---|---|---|
| B1 | 工單(WorkOrder)級處理:單投產單對多工單迭代、isCheckSupplyPlan、FixedFinishDatetime 固定完工日 | :491-508, 590-606, 718-740, 937-947 | spec 只到「投產單+qty」。粗排是否需感知工單/固定完工日? |
| B2 | 計畫性訂單/共用件合併:未搓合工單搓合、WorkOrderSupply、SOURCE_PLAN | partUnfinishedWoMap(:90-91, 949-1007)、:716-754, 1819-1900 | 跨投產單共用件邏輯,spec 未涵蓋。 |
| B3 | 採購件直接出貨 | isDirectSale、createShipMaterialKanbanForBuy(:386-393) | 採購件不走製程樹,spec 未提。 |
| B4 | 物料看板生成(pull/store/ship/buy material kanban) | KanbanEngine_new 系列(:578, 1028, 1100-1101, 1607-1684) | spec §RCS-FS-5 只要求保留「需要哪些料」,未要求產生各類物料看板。輸出模型差異核心。 |
| B5 | 移轉批量/拆批:transferQmax、splitProduceKanban、splitMaterialKanban、DateTolerance/TaktTolerance 容許天數 | getPushNodeDt(:1334-1357)、getPullNodeDt(:1379-1395)、:548-571, 1068-1073, 1242-1247 | 比 spec 顆粒更細的批量控制,spec 未定義。 |
| B6 | Lot 拆分:assignLotsToKanbans、isEnableLotSplit、lotId、TRYMOLD 跳過 | :797-824, 2198-2348 | 批次級拆分,spec 未涵蓋(spec 到「天」、到「節點」)。 |
| B7 | 瓶頸統計分析建議 | BottleneckStatEngine(:123, 179-181, 484-536) | 排單統計/瓶頸建議,spec 未提。 |
| B8 | 產能 Gap 掃描 | buildPoRecordGap/traverseAndGetGap/ScheduleGap(:1690-1775) | 不足量回報,spec 未提(spec 只輸出日期)。 |
| B9 | 加班/特殊加班 | overTimeMap、salesOrderOvertimeMap、SalesOrderOvertimeConsume(:73-76, 296-298) | spec §RCS-FS-9 只說 Freeze 在引擎外、引擎讀「可用產能」;加班設定屬產能組成細節,spec 未展開。 |
| B10 | 資源數計算策略/模治具限制 | ResourcePlanCalculator、ResourcePlanStrategy、maxResourceQtyMap、moldFixtureConstraintMap(:99-110, 2151-2159) | 開機數會影響 leadtime(連帶影響 LSD);spec toy 假設單一資源、未定義資源數規劃。 |
| B11 | 齊料拉動(forerunner / kit pull) | expectCompleteKitDtLong、oidForerunnerFinishDt、isForerunner(:367-384, 442-448, 712-714) | 跨單齊料同步,spec 未涵蓋。 |
| B12 | 中斷機制/進度回報 | interrupt(:246-248, 360-363)、getProgress(:250-255) | 執行控制,spec 未提(屬非功能性)。 |
| B13 | 時區位移 | zoneOffsetHours(processAfterSchedule :826-834) | spec 到「天」、未談時區;若粗排也需跨時區,需補。 |
B 類重點:B1~B6、B11 多屬**細排/工單級機制,落在粗排「投產單級、純試算」定位之外——可能本就該排除,但 spec 目前沒有白紙黑字說明**,建議在 spec 的「範圍說明/待裁定」補一句,避免日後誤判為「漏做」。B9/B10 與 spec 的「可用產能/leadtime」間接相關,最需要釐清是否納入。
C 類:Spec 有、舊版無(與 §2 呼應,反向補記)
| 項目 | 說明 |
|---|---|
| §RCS-FS-1 無節拍點 fallback(以最後一站為節拍點) | 舊版 getTaktPoint 回 null、無 fallback(會 NPE)。spec 較嚴謹。 |
| §RCS-FS-7 明確「引擎不判 overdue」 | 舊版相反(會判逾期+產警報)。spec 為刻意收斂。 |
| LeanPlay 自起排日正推 ASAP 錨定(§RCS-FS-11,廢棄交期反推 §RCS-FS-10) | 舊版粗排仍用「交期 JIT 反推」或「nowDt 正推 standardLeadTime」分支,與 spec 新定義不一致。 |
4. 給人裁定的重點清單(依優先序)
- §RCS-FS-7 vs 舊版逾期判定(衝突):spec 要引擎「只給日期、不判 overdue」,舊版會判逾期、產
ScheduleAlarm、互動模式還會中斷。實作新粗排時須移除逾期判定與警報,逾期比對交下游。 - §RCS-FS-1 fallback(舊版缺):新引擎須補「無節拍點→以最後一站為節拍點」,避免舊版的 null/NPE 路徑。
- LeanPlay 錨點來源(語意已改):採 spec §RCS-FS-11 正推 ASAP 定錨,不可沿用舊版的交期反推(
STRATEGY_TAKT_STARTDATE_JIT)。 - 「投產順序」定義(不一致):確認採 spec 的「LeanPlay 完工日全域排序」,而非舊版 sorter 的「開工日+MTO/MTS+manualSeq」。
- LSD 細節對齊:(a) 逾期時 LSD 要「保留過去值」(spec §RCS-FS-3) 還是「clamp 到 nowDt」(舊版)?(b) LSD 的 leadtime 是否要納入「開機數除 CT/CO」(舊版有、spec toy 無)?
- B 類功能範圍宣告:對 B1~B13 各項,明確標記「本期捨棄」或「未來範圍」,補進 spec 的範圍/待裁定段,使「不做」是有意識的決定而非遺漏。其中 B9(加班)、B10(資源數/模治具) 與「可用產能、leadtime」直接相關,最需先決定是否納入。
附錄:關鍵證據索引
- LSD(最晚開工日)計算:
LatestStartCalculator.java:74-202;逾期 clamp:117-118;資源數除 CT/CO:161-165;填回看板:207-218 - LeanPlay 節拍點起排日策略:
ScheduleEngine_wo.java:450-475(rough+JIT 交期反推:456-459;nowDt 正推:461-463) - 有限產能堆疊/排擠 +1 天:
ScheduleEngine_wo.java:534-547 - 上游拉動:
pullScheduleProduceKanban :850-1326;getPullNodeDt :1371-1402 - 下游推動:
pushScheduleProduceKanban :1435-1688;getPushNodeDt :1328-1369 - 逾期判定/警報:
:530-531, 688-690, 769-790 - Snapshot 寫回(粗排跳過):
:32-36, 765-767 - WIP/庫存供需:
checkSupplyDemand :2021-2040;updateOrderRequestQty :1954-2009 - 集批已拔成空殼:
getArrangeKanBan :2072-2074 - Lot 拆分:
assignLotsToKanbans :2198-2348 - 投產順序 sorter:
ScheduleEngine_wo.java:841-845;StdScheduleKanbanSorter.java:20-35 - 節拍點查找(無 fallback):
VSMUtil.java:43-50;呼叫點ScheduleEngine_wo.java:398