先固定Schema版本與驗證契約
批次配方不是一串可以直接寫入控制器的數字,而是有版本、欄位、型別、單位與相互關係的資料。先在輸入邊界宣告schema_version,例如recipe-v3,再依這個版本解讀欄位;不可讓缺版本的資料靠猜測套用最新規則。版本、來源識別、原始文字與接收時間都應留下,方便日後重現同一次驗證。
本篇示範欄位recipe_id、temp、speed、low、high:recipe_id是非空字串,temp與speed是可解析的數值,low與high是數值且low不得大於high。實際設備的單位、精度、上下限仍要由專案規格定義;這些名稱只是資料模型示例,不是任何PLC裝置或寄存器配置。
錯誤清單採固定順序:先檢查schema版本,再檢查required欄位,接著檢查type,然後檢查range,最後檢查cross-field關係。每筆錯誤至少包含路徑、規則、代碼、原始值或缺值標記與可讀說明。路徑如/steps/2/speed要保留原樣,不能只報一個模糊的配方錯誤。
缺值不等於零。temp缺漏代表資料沒有提供,必須回報required;只有輸入明確為0,且0在該欄位範圍內,才可視為合法零。字串"0"也不能未經型別規格就當成數字0。解析失敗時保留原字串,避免錯誤訊息把abc改寫成0而失去現場證據。
按層次收集而不是遇錯即停
第一輪只確認版本與結構,第二輪掃描所有required欄位,因此一個配方可同時列出多個缺漏。第三輪對已存在欄位做型別檢查:speed='abc'是type錯誤,不能先轉成預設速度。若文字內含空白、單位後綴或超長數字,須依明確解析契約處理,不能用語言或PLC的隱式轉換猜答案。
range檢查只對型別已成立的值進行。temp有值但超出工程上限時報range;speed若根本不是數字,就不要再拿它和上下限比較,否則會衍生一個沒有意義的超界錯誤。cross-field也一樣:low缺失時不要宣稱low大於high,因為比較前提不存在。
以下策略可兼顧完整性與可讀性:同一欄位可保留一個主要型別錯誤,避免無限重複;不同欄位和不同層級的獨立錯誤照實列出。錯誤清單必須穩定排序,先按驗證階段,再按資料路徑;這讓測試、畫面與事件紀錄不會因走訪順序改變而漂移。
未知欄位要有版本政策。嚴格模式將未列於schema的欄位報unknown;相容模式可保留並標記ignored,但不得讓未知值影響候選版本,也不得悄悄丟掉原文。若欄位疑似拼錯,例如speeed,嚴格模式能及早阻止它被當成有效speed。
本例以缺少鍵才報required,已存在但空字串的數值欄報type;不把空字串當成不存在。若schema版本不支援或頂層結構無法解析,停止依欄位規格的後續檢查,回報版本或結構錯誤,不猜測欄位規則。
完整案例與避免錯誤爆量
輸入示例:schema_version=recipe-v3、recipe_id=R7、temp缺失、speed='abc'、low=20、high=10。第一項是/temp的required錯誤;第二項是/speed的type錯誤,原字串abc原封不動保留;第三項是/high與/low的cross-field錯誤,因為20大於10。這三項互相獨立,應形成三筆清單,而不是只回傳第一個缺漏。
本例沒有把temp缺失當成temp=0,也沒有對speed做range比較,更沒有再產生speed不能和low或high比較的衍生錯誤。low與high本身都是有效數值,所以low>high的關係檢查仍可執行。這種依賴前提的判斷,能讓操作員修正真正資料,而不是清理驗證器製造的噪音。
若同一欄位同時違反多個規則,要先定義錯誤優先級。例如speed是空字串時可報type或required,但不能一會兒當缺值、一會兒當零;版本文件應固定選擇。對陣列索引、巢狀物件和重複recipe_id,也要使用穩定路徑與明確規則,讓前端能精準定位修正位置。
防止惡意或意外爆量時設定上限:檔案大小、陣列筆數、每筆字串長度與最多錯誤數都要有明確限制。達到錯誤上限後可追加一筆ErrorLimitReached,標示清單不完整並保留截斷位置;不可宣稱配方只有前幾筆錯誤。限制值屬系統規格,不能由本文任意替設備決定。
驗證器輸出應分成原始輸入、正規化檢視、錯誤清單和驗證摘要。正規化只可在規格允許時產生,不能覆蓋原資料。來源檔案簽章、版本或接收序號若失敗,應另列來源錯誤,與欄位內容錯誤分開,方便判斷是傳輸問題還是配方作者輸入問題。
只讓整組有效資料進入候選流程
任何required、type、range或cross-field錯誤存在時,整組配方維持invalid,不形成可供選擇的候選版本。所有欄位都通過後,才可依schema版本與內容雜湊產生候選識別,供人員審核、比對或另行部署。候選版本是資料流程狀態,不代表已寫入PLC,也不代表設備已接受。
正常結果可包含valid=true、candidate_id、schema_version與每個欄位的單位;失敗結果則包含valid=false、三筆或更多錯誤、原始路徑與摘要。不要在失敗時以0、上一次成功配方或自動夾限值取代欄位,因為這會把「資料錯誤」變成「看似可執行的新配方」。
驗收向量至少涵蓋全欄位有效、單一缺欄位、多欄位同時缺漏、字串型別錯誤、上下限相等、low大於high、未知欄位、超長輸入與錯誤數上限。逐筆核對路徑、原字串、錯誤順序和valid狀態。本文描述資料設計。
若現場要求部分欄位沿用舊值,應把那個行為寫成另一份明確的合併規格,包含來源版本與每欄位決策紀錄;它不是缺值自動補零。任何將候選送入控制的步驟,都還需要依目標平台做權限、通訊、互鎖與人工核准設計。
錯誤清單也應能安全重播:相同schema、相同原始輸入與相同規則版本,應得到相同排序和代碼。規則更新時增加版本,不要悄悄改寫舊事件的解釋。
常見問題
問:缺少temp可以先當零讓流程繼續嗎?答:不可以直接這樣推定;缺值與明確零是兩個不同狀態,除非專案另有寫明的補值流程。
問:speed='abc'要同時報速度超出範圍嗎?答:先報type,因為沒有可供range比較的數值;避免衍生誤報。
問:未知欄位一定要拒絕嗎?答:由schema版本的嚴格或相容政策決定;相容時也要保留並標記,不可靜默遺失。
問:錯誤清單完整後可以直接寫PLC嗎?答:本文的valid只代表資料驗證通過,候選仍須經專案規定的審核、通訊與安全流程。
參考:JSON Schema官方規範:required、type與數值限制。low與high的跨欄位大小比較由本文應用層實作,不能宣稱標準Schema原生支援任意兩欄相互比較。
參考:Python json官方文件,說明JSON資料解碼與資料型態邊界;本文保留原字串與路徑的策略仍需由專案介面規格落實。