前三天,我們花了一些時間了解:
MES 是什麼?
MES 到底在管理什麼?
以及:
正式開始生產以前,需要準備哪些基礎資料?
從今天開始,我想換一個角度。
不再只是「看 MES」,而是開始真的站在研發的角度思考:
如果今天要從零開始開發一套 MES,第一個功能到底要寫什麼?
產品?
製程?
途程?
工單?
還是直接開始寫 WIP?
如果是我,我會選擇:
機台(Machine)。
從使用者的角度來看,工單可能才是生產真正開始的地方。
建立工單:
接著開始建立 Lot、生產、進站、出站。
所以看起來,從工單開始開發好像很合理。
但如果真的打開 IDE 準備寫程式,很快就會遇到一個問題:
我的下拉選單資料從哪裡來?
產品還沒做。
客戶還沒做。
途程也還沒做。
途程裡面的製程甚至也還不存在。
當然,我們可以先 Mock。
也可以先寫死:
{
"product": "PRODUCT-001",
"customer": "CUSTOMER-001",
"route": "ROUTE-001"
}
這樣工單頁面確實可以先做。
但是隨著系統慢慢完成,這些暫時寫死的資料還是得一個一個換掉。
所以如果今天的目標是:
從零開始,一步一步建立一套 MES。
我會選擇另外一個方向:
從依賴最少的功能開始往上開發。
我們先把整套 MES 簡化來看。
未來可能會形成這樣的關係:
Machine
↓
Process
↓
Route
↓
Work Order
↓
Lot
↓
WIP
越下面的功能,通常需要知道越多前面的資訊。
相對來說,Machine 本身非常單純。
我們不需要先知道:
它屬於哪張工單?
也不需要先知道:
哪一個 Lot 正在使用?
更不需要為了建立一台機台,先建立一整條途程。
所以它很適合成為第一個練習。
假設今天先從最簡單的需求開始。
一台機台至少需要:
MACHINE_UUID
MACHINE_CODE
MACHINE_NAME
ENABLE
CREATE_TIME
UPDATE_TIME
例如:
MACHINE_UUID : 550e8400...
MACHINE_CODE : MC-001
MACHINE_NAME : 組裝機 01
ENABLE : True
這時我們已經有第一張 MES Table 了。
接下來就是後端工程師非常熟悉的:
CRUD。
機台的 CRUD 分別代表:
Create: 新增機台。
Read: 查詢機台。
Update: 修改機台。
Delete: 刪除機台。
如果做成 API,可能會長得像:
POST /machines
GET /machines
GET /machines/{machine_uuid}
PUT /machines/{machine_uuid}
DELETE /machines/{machine_uuid}
到這裡看起來跟一般後台管理系統沒有太大的差別。
甚至會讓人覺得:
「MES 不就也是 CRUD?」
至少在第一個功能,確實很像。
但是我們很快就會遇到第一個實務問題。
假設使用者先建立:
MACHINE_CODE = MC-001
MACHINE_NAME = 組裝機 01
過幾天另外一個使用者又建立:
MACHINE_CODE = MC-001
MACHINE_NAME = 測試機 01
對系統來說,現在有兩台:
MC-001
那之後 Lot History 顯示:
LOT-001 使用 MC-001 生產。
到底是哪一台?
所以真正 Create 以前,我們可能就需要先做一些 Validation。
概念上可能像:
def create_machine(data):
if not data["machine_code"]:
raise Exception("Machine code is required.")
if machine_exists(data["machine_code"]):
raise Exception("Machine code already exists.")
machine = Machine(**data)
db.add(machine)
db.commit()
這時候 CRUD 已經開始多了一些東西。
不是:
收到資料
↓
INSERT
↓
結束
而是:
收到資料
↓
資料驗證
↓
Business Rule
↓
INSERT
↓
完成
而且現在還只是 Machine。
接下來是我覺得 Master Data 很值得討論的一個問題:
機台真的應該 Delete 嗎?
假設:
MC-001
已經使用半年。
很多 Lot 都曾經使用這台機台生產。
因此 Lot History 裡可能存在:
LOT-001 → MC-001
LOT-002 → MC-001
LOT-003 → MC-001
...
某一天 MC-001 報廢了。
最直覺的操作可能是:
DELETE FROM MACHINE
WHERE MACHINE_CODE = 'MC-001';
但是如果真的刪掉,我們就要開始考慮:
以前的生產紀錄怎麼辦?
Lot History 還能不能知道當時使用的是哪台機台?
其他資料表有沒有 Foreign Key 指向它?
刪除之後會不會造成歷史資料無法追溯?
所以在這類基礎資料中,比起真正把資料刪掉,我們可能更需要的是:
disable。
例如:
MACHINE_CODE = MC-001
ENABLE = False
代表:
MC-001 曾經存在,也保留過去的紀錄,但從現在開始不允許再使用。
因此畫面上的「刪除」與「停用」,在系統設計上其實是完全不同的概念。
當系統加入 ENABLE 之後,就會出現下一個問題。
假設今天製程設定要選擇可使用的機台。
查詢條件可能是:
WHERE ENABLE = 1
因為新的設定不應該再選到已停用的機台。
但是今天查的是:
三個月以前的 Lot History。
這時即使 MC-001 已經停用,我們仍然希望看到:
LOT-001
Machine:MC-001
所以同樣是「查詢 Machine」,不同使用情境需要的資料可能完全不同。
這也是我覺得開發 MES 很有趣的地方。
一開始看起來只是:
Create / Read / Update / Delete
真正開始加入生產需求之後,就會慢慢變成:
CRUD + Business Rule。
因為它夠簡單。
現在我們只需要考慮:
Machine
還沒有:
Machine → Process → Route → Work Order → Lot
也還不用處理複雜的生產狀態。
所以我們可以先利用 Machine,把最基本的系統架構建立起來:
**Database Table** → **Model** → **Service** → **API** → **Frontend**
先讓第一筆真正的 MES 資料,可以從畫面一路走到 Database。
當這條路打通之後,下一個功能就可以沿用相同的架構繼續往下開發。
如果今天一開始就寫 Work Order,我們會同時需要處理很多尚未存在的資料。
但如果從 Machine 開始:
Day 04
Machine
下一步再建立:
Machine
↓
Process
接著:
Machine
↓
Process
↓
Route
資料之間的關係會一層一層長出來。
等到我們真正開始寫 Work Order 時:
產品有了。
客戶有了。
途程有了。
製程有了。
機台也有了。
這時工單需要的資料就不必再靠 Mock 或寫死。
而是可以真正從前面已經完成的功能取得。
所以如果有人問我:
「開發 MES 第一個功能應該做什麼?」
我不一定會回答:
Machine。
因為不同 MES 的資料模型與需求一定不同。
我真正會先做的是:
找出依賴關係最少、又會被其他功能使用的基礎資料。
然後從底層開始,一層一層往上建立。
對我們目前這套 MES 而言:
Machine 就是一個很適合的起點。
今天我們完成的只是一個很普通的 CRUD。
但從下一篇開始,事情就要稍微變複雜了。
因為我們要開始建立:
製程(Process)。
而當 Process 開始跟 Machine 產生關係之後,我們第一次會碰到:
「資料與資料之間,要怎麼建立關聯?」
下一篇:
「從機台到製程:MES 的資料開始產生關聯了。」