粗排引擎 Functional Spec ⇄ 舊版 ScheduleEngine_wo 比對報告
可讀性重排版:整篇套「白話上層 + 密表深究層」的架構,內容沿用原報告、未重新查證。原檔在 rough-cut-scheduler-spec-vs-legacy.md。
產出日期:2026-06-08。性質:一次性比對,不改任何程式碼、不做裁定。 比對對象 A(基準):新版 functional spec
functional-spec/rough-cut-scheduler.md(已簽核;2026-06-04 修正上游 JIT 拉動、廢棄 §RCS-FS-10)。 比對對象 B(現役實作):Lean-Scheduling/src/main/java/.../ScheduleEngine_wo.java(2350 行);佐證LatestStartCalculator.java、VSMUtil.java、StdScheduleKanbanSorter.java。
0. 大綱
排程分粗細兩層:
這份報告只比粗排,對象是兩支引擎:
- A・新 spec:新訂的粗排引擎規格。定位是「純試算」:只算出每張單該落在哪幾天,不動真的產能、不寫回資料庫,算完就丟。處理單位是**投產單**(一筆「某產品、某數量、某交期」的生產要求)。
- B・舊程式
ScheduleEngine_wo:現役那支 2350 行的引擎。它把粗排、細排寫在一起,靠一個開關切換,而且會真的吃掉產能、把結果寫回去。
總結:兩者不是同一種東西。 新 spec 是「只算日期、不留痕跡」的輕量試算;舊程式是「算完就把產能佔掉」的重型引擎。
報告分兩個方向進行:一是新 spec 有沒有漏掉舊版該有的粗排能力,二是舊版有沒有多出 spec 沒提到的東西。重點是把查到的差異分成兩類:哪些是故意的、需要決議,哪些是真的對不上、得修正。
1. 兩支引擎的根本定位
逐項比之前先對齊框架。新 spec 是「投產單級、純試算、只產日期」的獨立粗排引擎;舊程式是「工單級、會落地、粗細合一」的重型引擎。六個面向並排如下:
| 面向 | 新 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 重做或補齊。
逐項對照如下。狀態欄:✅ 一致、⚠️ 有但語意有差、❌ 沒有或相反。
| # | Spec 項目 | 舊版對應實作(檔:行) | 狀態 | 說明 |
|---|---|---|---|---|
| 1 | LSD 反推(§RCS-FS-13:自交期反推、純 leadtime、不看產能) | LatestStartCalculator.calculateNodeRecursive(LatestStartCalculator.java:91-153),排程後由 calculateLatestStart() 呼叫(ScheduleEngine_wo.java:2142-2174) | ⚠️ | 核心一致(交期為錨、往前反推、不消耗產能)。兩點差異:(a) 逾期時 LSD clamp 到 nowDt(:117-118),spec §RCS-FS-3 是「保留過去值、視為無意義」;(b) leadtime 會除以開機數(:161-165),spec toy 假設單一資源、未涵蓋。 |
| 2 | LeanPlay:節拍點為錨(§RCS-FS-11 正推定錨求最早可開工日 → 有限產能堆疊 → 往前 JIT 拉上游、往後推下游) | 節拍點起排日(ScheduleEngine_wo.java:450-475)、有限產能 arrangeTimeWithProduceCapacity(:534)、pullScheduleProduceKanban(:850)、pushScheduleProduceKanban(:1435) | ⚠️ | 結構對應。語意差異:spec 2026-06-04 已廢棄交期反推(§RCS-FS-10)改為自起排日正推 ASAP;舊版粗排仍保留「交期 JIT 反推」分支(:456-459)與「nowDt 正推 standardLeadTime」分支(:461-463)。錨點來源與新 spec 不一致,需以 spec 為準重做。 |
| 3 | 有限產能堆疊、不足往後一天(§RCS-FS-2 排擠) | arrangeTimeWithProduceCapacity 失敗→節拍點 +1 天(:538-547) | ✅ | 機制一致。 |
| 4 | 上游往前拉動 N-x(JIT、緊鄰、不留閒置) | getPullNodeDt(:1371-1402)+ pullScheduleProduceKanban(:850) | ✅ | 拉動以「緊鄰後站開工」反推,與不留閒置一致。 |
| 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,後續會 NPE | ❌ | 舊版未實作 §RCS-FS-1。spec 此處較嚴謹,屬新增需求。 |
| 8 | 投產順序=LeanPlay 完工日全域排序、連續編號 1..N | processAfterSchedule() 用可插拔 IScheduleKanbanSorter(:841-845);預設 StdScheduleKanbanSorter 依「MTO/MTS → 開工日 → manualSeq → 成品階 → processSeq」排(StdScheduleKanbanSorter.java:20-35) | ⚠️ | 定義不同:spec 依「完工日」全域排;舊版依「開工日+業務分群」。需釐清採哪個。 |
| 9 | 空批次 → 回傳空、不報錯(§RCS-FS-8) | 對空 productionOrderList 自然產生空輸出(:357);但 inputCheck() 對缺 VSM/產能會直接不跑(:189-243) | ⚠️ | 空批次可達成空輸出;但 inputCheck 是另一層擋門,對「空輸入」的容忍邊界與 spec B8 略有出入,需確認。 |
| 10 | 交期早於今日 → LeanPlay 落未來、LSD 落過去、引擎不判 overdue(§RCS-FS-3) | LeanPlay nowDt 夾住、不排到過去(:419-421, 469-474)✅;LSD clamp 到 nowDt(:117-118)⚠️;overdue 見第 11 點 ❌ | ⚠️ | LeanPlay 不落過去一致;LSD 落過去的處理不同(見第 1 點)。 |
| 11 | 引擎只輸出日期、不比對交期、不判 overdue(§RCS-FS-7) | 舊版多處判逾期::530-531 log、:688-690 setOverdue、:772-776 拋例外並 createScheduleAlarm、:786-790 互動模式逾期停止 | ❌ | 與 spec 相反。舊版主動判逾期、產警報、甚至中斷;spec 明定引擎不判、交下游。 |
| 12 | 純試算、不寫回/鎖定產能(UC A1 預期結果) | 粗排模式跳過 saveSnapshot(:765-767);但 arrangeTimeWithProduceCapacity 仍會消耗並 commitFinalProductivityCalculation(:700, :316-319)改寫記憶體產能表 | ⚠️ | 不落 DB 與「不寫回」相符;但引擎內部確實會佔用/commit 產能(為了多單排擠),是否算「鎖定」需對齊。 |
小結: 核心機制舊版都有。要對齊的是這四件:無節拍點的 fallback(舊版缺)、不判逾期(舊版相反)、LeanPlay 的錨點來源(舊版仍用交期反推、spec 已改正推),以及投產順序的排序依據。
3. 方向二:舊版多出哪些 spec 沒有的功能?
舊版多出不少功能,分三類看:
- A 類:spec 已經明講「不做、或留到下一期」。這些是故意排除,不是缺口。
- B 類:spec 完全沒提。這些是潛在缺口,要團隊決定「丟掉」還是「列為未來範圍」。
- C 類:反過來,spec 有、舊版沒有(和 §2 呼應)。
3A. spec 已明講不做的——故意排除
這幾項舊版有、spec 沒有,而且 spec 有白紙黑字的排除依據。報告只是點名,確認「捨棄/延後」是有意識的決定。
| 舊版功能 | 證據(檔:行) | spec 排除依據 |
|---|---|---|
| 產銷協調互動入口/互動式模式(逾期單停止) | isTurnOnInteractionMode、:772-790 | 概述「互動入口明確不做、留下一期」 |
| 逾期判定/排程警報(ScheduleAlarm) | AlarmMgr.createScheduleAlarm、:772-790 | §RCS-FS-7(引擎不判 overdue) |
| 寫回/鎖定產能(Snapshot 落地) | SnapshotDelegate.saveSnapshot、:32-36, 765-767 | UC A1「純試算、不寫回/鎖定」(§Q4 待裁定是否落地) |
| 到料/WIP/庫存供需協調 | checkSupplyDemand(:2021)、getWIPSupply/getInventorySupply(:975-984) | §RCS-FS-5(不考慮到料,只保留「需要哪些料」) |
| 集批控制 | getArrangeKanBan 已移除、恆回空(:2072-2074);getTaktTolerance 拆批(:548-571) | §RCS-FS-14(集批不支援) |
其中「集批」在舊版其實已被拔成空殼(getArrangeKanBan 恆空),與 spec 方向一致。
3B. spec 完全沒提的——潛在缺口,要裁定
這類最需要注意。多數是「工單級/細排」的機制,可能本來就該排除在粗排之外,但 spec 目前沒寫明,容易日後被誤判成「漏做」。
| # | 舊版功能 | 證據(檔:行) | 為何值得裁定 |
|---|---|---|---|
| B1 | 工單級處理:單投產單對多工單迭代、固定完工日 | :491-508, 590-606, 718-740 | spec 只到「投產單+qty」。粗排是否需感知工單/固定完工日? |
| B2 | 計畫性訂單/共用件合併:未搓合工單搓合 | partUnfinishedWoMap(:949-1007)、:1819-1900 | 跨投產單共用件邏輯,spec 未涵蓋。 |
| B3 | 採購件直接出貨 | isDirectSale、createShipMaterialKanbanForBuy(:386-393) | 採購件不走製程樹,spec 未提。 |
| B4 | 物料看板生成(pull/store/ship/buy) | KanbanEngine_new 系列(:578, 1028, 1607-1684) | spec §RCS-FS-5 只要保留「需要哪些料」,未要求產各類物料看板。輸出模型差異核心。 |
| B5 | 移轉批量/拆批:容許天數控制 | :548-571, 1068-1073, 1242-1247 | 比 spec 顆粒更細的批量控制,spec 未定義。 |
| B6 | Lot 拆分 | assignLotsToKanbans(:797-824, 2198-2348) | 批次級拆分,spec 未涵蓋(spec 到「天」、到「節點」)。 |
| B7 | 瓶頸統計分析建議 | BottleneckStatEngine(:484-536) | 排單統計/瓶頸建議,spec 未提。 |
| B8 | 產能 Gap 掃描 | buildPoRecordGap/ScheduleGap(:1690-1775) | 不足量回報,spec 未提。 |
| B9 | 加班/特殊加班 | overTimeMap、salesOrderOvertimeMap(:73-76, 296-298) | §RCS-FS-9 只說 Freeze 在引擎外;加班設定屬產能組成細節,spec 未展開。 |
| B10 | 資源數計算策略/模治具限制 | ResourcePlanCalculator、moldFixtureConstraintMap(:2151-2159) | 開機數會影響 leadtime(連帶 LSD);spec toy 假設單一資源。 |
| B11 | 齊料拉動(forerunner / kit pull) | oidForerunnerFinishDt、isForerunner(:367-384, 712-714) | 跨單齊料同步,spec 未涵蓋。 |
| B12 | 中斷機制/進度回報 | interrupt(:246-248)、getProgress(:250-255) | 執行控制,spec 未提(屬非功能性)。 |
| B13 | 時區位移 | zoneOffsetHours(:826-834) | spec 到「天」、未談時區;若粗排也跨時區,需補。 |
B1~B6、B11 多屬細排/工單級,落在粗排「投產單級、純試算」定位之外,建議在 spec 範圍說明補一句。B9(加班)、B10(資源數/模治具)與「可用產能、leadtime」直接相關,最該先決定要不要納入。
3C. spec 有、舊版沒有——反向補記
| 項目 | 說明 |
|---|---|
| §RCS-FS-1 無節拍點 fallback(以最後一站為節拍點) | 舊版 getTaktPoint 回 null、無 fallback(會 NPE)。spec 較嚴謹。 |
| §RCS-FS-7 明確「引擎不判 overdue」 | 舊版相反(會判逾期+產警報)。spec 為刻意收斂。 |
| LeanPlay 自起排日正推 ASAP 錨定(§RCS-FS-11,廢棄交期反推 §RCS-FS-10) | 舊版粗排仍用「交期 JIT 反推」或「nowDt 正推」分支,與 spec 新定義不一致。 |
4. 要團隊裁定的事項(依優先序)
把前面查到、需要人決定或修正的列出來。前四項是實作新粗排前一定要先定的。
- 逾期由誰判(衝突):spec 要引擎「只給日期、不判 overdue」,舊版會判逾期、產
ScheduleAlarm、互動模式還會中斷。實作新粗排時須移除逾期判定與警報,逾期比對交下游。(§RCS-FS-7) - 無節拍點 fallback(舊版缺):新引擎須補「無節拍點→以最後一站為節拍點」,避開舊版的 null/NPE 路徑。(§RCS-FS-1)
- LeanPlay 錨點來源(語意已改):採 spec 的正推 ASAP 定錨,不可沿用舊版的交期反推。(§RCS-FS-11)
- 投產順序定義(不一致):確認採 spec 的「完工日全域排序」,而非舊版的「開工日+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 天:
: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