← 所有文章

陣列索引超出範圍會怎樣 PLC 批次資料的邊界檢查

· 站長

用 Buffer[0..9] 與 ValidCount 0..10 示範空資料、合法邊界、無效資料與越界拒絕。

先把容量 索引與有效筆數分開

本例建立 Buffer[0..9],容量是十筆,但容量不代表十筆都有資料。Index 是要讀取的實體索引,ValidCount 是目前有效筆數,範圍由 0 到 10。空資料時 ValidCount=0,沒有任何索引可讀;只有 ValidCount=10 時,Index=9 才是最後一筆有效資料。三個概念若混在一個變數,邊界錯誤很難追。

測試資料填入 100、110、120、130、140五筆,ValidCount=5。Index=0 讀到 100,Index=4 讀到 140;Index=5 雖然仍在陣列容量內,卻不屬於目前有效資料,應回報 NO_DATA。Index=10 超出陣列上界,Index=-1 則超出下界,兩者都必須在讀取前拒絕。

狀態 ValidCount Index 預期
空資料 0 0 拒絕 EMPTY
五筆資料 5 4 讀 140
五筆資料 5 5 拒絕 NO_DATA
滿資料 10 9 讀第十筆
滿資料 10 10 拒絕 BAD_INDEX

驗收時要保存輸入、ValidCount、Index、錯誤碼與輸出值。發生錯誤時保持上一個合法輸出但ResultValid為FALSE;開機ResultValid為FALSE且尚無合法輸出。不能把錯誤索引自動夾到 0,否則畫面會顯示一筆看似合理但其實不相干的資料。本文的程式描述是教學邏輯,不是可直接貼入特定 PLC 的語法。

先驗證再存取 不能靠 AND 短路

本篇以單一任務管理整個陣列。每次先確認ValidCount為0至10整數,否則回報BAD_COUNT且不存取;再確認Index>=0,再確認 Index<10,再確認 Index<ValidCount,三項都成立後才執行 Buffer[Index]。不可把條件寫成一個假設會短路的 AND,就直接在同一個表達式裡引用 Buffer[Index]。不同 PLC 語言和編譯器對布林運算的評估順序、陣列邊界診斷與例外行為可能不同;即使某次測試沒出錯,也不能把短路當成保護。

  1. 先把輸入 Index 複製到待驗證變數,不在驗證前計算 Buffer[Index]。

  2. 分別檢查下界、容量上界與 ValidCount 上界,為每個失敗分支給明確錯誤碼。

  3. 只有三項通過後才讀取陣列並更新輸出快照。

  4. 把讀取成功與錯誤事件各自計數,避免只看最後一個畫面值。

  5. 測試負值、10、5、4 與空資料,確認每次都在存取前結束。

Index ValidCount 先檢查結果 是否執行讀取
-1 5 下界失敗
10 10 容量上界失敗
5 5 有效筆數失敗
4 5 三項通過
0 0 空資料失敗

當 Index 來自無號型別或通訊欄位,負值可能先被轉成很大的正數。驗證要在轉型後仍能辨認來源格式,必要時先檢查原始字串或有號暫存。清空資料時也要把 ValidCount 設回 0,不能只清除陣列內容而留下舊的有效筆數。

錯誤碼應在同一掃描內先完成,再由 HMI 顯示;不要等下一次讀取才補寫原因,否則維修人員可能看到錯誤索引卻拿到新的合法輸出。

新增 讀取與清空的邊界

新增前先確認ValidCount合法,再檢查ValidCount<10;通過後寫入 Buffer[ValidCount],再把 ValidCount 增加一。當 ValidCount=9 時,寫入索引 9 是合法的第十筆;寫入完成後才變成 10。當 ValidCount=10 再新增,策略必須明定為拒收 FULL、覆寫或移除最舊資料,不能讓程式繼續寫 Buffer[10]。本篇選擇拒收並增加 RejectCount。

讀取最後一筆時,只有 ValidCount>0 才能計算 ValidCount-1。空資料若先計算 -1,再交給無號索引,可能變成極大值。清空動作則先把 ValidCount=0、輸出標成 EMPTY,再視需求清除內容;內容是否清零不應被誤認為資料是否有效。

操作 起始狀態 動作 結果
新增 Count=0 寫 Buffer[0] Count=1
新增 Count=9 寫 Buffer[9] Count=10
新增 Count=10 拒收 RejectCount+1
讀最後 Count=0 拒絕 EMPTY
清空 Count=5 Count=0 輸出無效

若資料由多個欄位組成,資料列的所有欄位必須在同一筆提交中完成。先增加 ValidCount 再寫欄位會讓讀取任務看到半筆資料。單任務內可先寫完整資料列,再更新ValidCount;若跨任務,這個順序仍不足以保證一致性,必須採平台支援的同步或不可變快照。

當輸入來自 HMI 或通訊封包,還要驗證資料型別與更新序號。畫面送來的 10 可能是字串、帶小數的數值或未完成的編輯值;在轉成 Index 前先檢查格式、範圍與提交狀態。若轉換失敗,保持目前有效快照並回報 INVALID_INPUT,不要把失敗值默認成 0。

邊界測試也要涵蓋上電初始化和程式重啟。初始化尚未完成時,ValidCount 應標為無效,不能沿用記憶體中看似完整的舊內容。只有完成資料填充、批次版本與有效筆數提交後,讀取流程才可開放。這個順序能避免上電第一掃描就讀到半套資料。

本例只支援追加、清空與移除最後一筆。移除最後一筆時,先確認ValidCount大於0再減一;若要移除中間資料,單純減一不會刪掉正確元素,必須另定搬移或有效位圖,不能沿用本例的連續有效前綴規則。

用明確案例驗證負值與上下界

建立八組固定測試:空資料 Index=0;五筆資料 Index=4;五筆資料 Index=5;滿資料 Index=9;滿資料 Index=10;任意資料 Index=-1;清空後讀最後一筆;新增第十一筆。每一組都要記錄是否讀取陣列。測試工具若不能表示負值,另用有號暫存建立同等情境,不要默默把 -1 轉成大數。

  1. 先填入 100 至 140 五筆,核對 ValidCount=5。

  2. 依序執行 Index=4、5、10、-1,保存錯誤碼與陣列讀取計數。

  3. 填滿到第十筆,驗證 Index=9 可讀且值正確。

  4. 嘗試第十一筆,確認 Buffer 未變、ValidCount 仍為 10。

  5. 清空後測最後一筆讀取,確認輸出標記 EMPTY 而非舊值。

排查時先看存取計數或診斷標記,再看輸出。若 BAD_INDEX 的計數增加但輸出仍改變,可能有另一段程式覆寫輸出;若存取計數在錯誤輸入時增加,驗證分支位置就不正確。交接報告要分開寫推算結果、模擬結果和尚未在實機確認的項目。

補充驗收:第 1 次讀取 Index=4 成功,第 2 次輸入 Index=5 被拒,第 3 次新增資料讓 ValidCount 從 5 變成 6 後,Index=5 才能讀到新資料。這個時間線證明索引合法性不只由容量決定,也由有效筆數決定。若資料在不同任務交換,還要保存快照序號,避免把上一批的 ValidCount 配上下一批的 Index。

再測清空與重填:清空後 ValidCount=0,Index=0 必須回報 EMPTY;重新寫入 210、220 後,Index=1 才回傳 220。陣列記憶體可能仍留著 110 或 120,但那些內容已不屬於目前批次。驗收表要以 ValidCount 和批次序號判定有效性,而不是以記憶體是否看起來有數字判定。

FAQ 平台限制與來源

問:Index=5、ValidCount=5 但 Buffer[5] 在容量內,可以讀嗎?不可以,它是尚未有效的資料,應回報 NO_DATA。問:能否用 AND 確保越界不會發生?不能依賴短路,必須在獨立驗證完成後才執行存取。問:空資料時最後索引是多少?沒有合法最後索引,不能計算 ValidCount-1 後讀取。問:第十一筆要怎麼辦?本例拒收並保留 Count=10;若需求要覆寫,必須另訂資料結構與驗收。

適用型號與限制:案例使用一般陣列概念與合成數值,未提供可直接編譯的 GX Works 或 Q 系列語法。陣列上下界、負索引處理、例外診斷、資料型別轉換與任務時序須依目標 CPU、工程軟體版本及官方文件核對。

參考:CODESYS Array data type and bounds

參考:CODESYS Programs and POU reference

延伸閱讀


使用 PLC 工具箱 →