先把三種動作分清楚
你在 PLC 裡做狀態機時,最常見的錯誤是把「進入狀態」和「留在狀態」寫在同一個條件。只要目前狀態等於 RUN,清除緩衝區的線圈就會每一掃描重複動作,累加值也可能被清成零。本篇先定義三類動作:進入只做一次、持續動作每掃描做、離開時清理一次。以下以 WAIT、RUN、DONE 三個虛擬狀態說明,程式碼是設計用偽碼,不能直接宣稱可在 Q 系列編譯。
| 動作類型 | 判斷條件 | 範例 |
|---|---|---|
| 進入初始化 | 目前狀態≠前次狀態,且目前=RUN | 清除測試緩衝、載入本次起始值 |
| 持續動作 | 目前=RUN | 每掃描累加取樣、檢查完成條件 |
| 離開清理 | 前次=RUN,目前≠RUN | 關閉允許訊號、保存結果 |
先列出所有狀態與合法轉移,再為每個狀態寫進入、持續、離開三欄。
建立 state_prev 與 state_now;每一掃描結束才把 now 複製到 prev。
決定重新進入 RUN 是否重新清除緩衝;本例答案是「要」,但同一狀態停留不清除。
建立一次進入事件
在一掃描開始,先讀取目前狀態,再計算 entry_run。規則是 state_now=RUN 且 state_prev≠RUN。初始化放在 entry_run 分支;累加和逾時檢查放在 state_now=RUN 分支。掃描末端才更新 state_prev,這樣 RUN 連續十個掃描只會有一個進入脈衝。若上電後兩個變數都被初始化成 WAIT,第一次切到 RUN 也能產生進入事件。
教學偽碼,非指定PLC語法:
掃描開始:state_now := 上一掃描核准的pending_state
entry_run := state_now=RUN 且 state_prev≠RUN
exit_run := state_prev=RUN 且 state_now≠RUN
若entry_run:test_buffer:=0;sample_count:=0
若state_now=RUN且sample_valid:累加sample;sample_count加一
若exit_run:保存本次資料與離開原因
pending_state := 根據state_now及本次輸入選擇一個合法下一狀態
run_enable := state_now=RUN 且沒有停止或故障
掃描末端:state_prev := state_now
停止與故障直接遮罩輸出,不等下一掃描。
| 掃描 | state_prev | state_now | entry_run | 結果 |
|---|---|---|---|---|
| S1 | WAIT | WAIT | 0 | 不清除 |
| S2 | WAIT | RUN | 1 | 清除一次、開始累加 |
| S3 | RUN | RUN | 0 | 只累加 |
| S4 | RUN | DONE | 0 | 執行離開清理 |
| S5 | DONE | RUN | 1 | 重新進入,再清除一次 |
掃描模型的陷阱與測試
若同一掃描內先把 WAIT 改成 RUN,又立刻改成 DONE,掃描末端只看得到 DONE,RUN 的進入事件會漏掉。你必須選一個規則:限制每掃描最多一次狀態轉移,或改用事件佇列保存中間轉移。本篇採前者,所有轉移要求在下一掃描才生效;若設備真的需要同掃描多階段,請另設 transition_event 陣列並逐項消費。
| 測試情境 | 預期 | 不符時先查 |
|---|---|---|
| RUN 停留 5 掃描 | 清除 1 次、累加 5 次 | entry 條件是否誤用 state_now=RUN |
| RUN→DONE | 執行一次離開清理 | prev 是否在掃描末端更新 |
| DONE→RUN | 重新初始化一次 | 是否把 DONE 當成 RUN 的前態 |
| 異常跳 WAIT→DONE | 不可執行 RUN 初始化 | 轉移表是否允許未定義路徑 |
| 重新啟動後 RUN | 依規格選擇恢復或重新初始化 | 保持區與上電初始化規則 |
表格的同一案例設定S2進入RUN時sample_valid=1、sample=12,因此先清零再累加成12;S3加入8,累計20;S4進DONE時保存20,不累加;S5再次進RUN,sample_valid=0,所以清零後保持0。若每掃描都清除,S3只剩8,就能看出錯誤。
實際寫圖時,把每個狀態的進入動作集中在同一網路或同一個功能區,並在旁邊註明觸發來源。不要在不同子程式各自修改 state_prev,否則先後順序會讓 entry_run 不可信。若有中斷或通訊任務會改 state_now,應先把請求寫入 next_state_request,由主循環統一核准;這樣一掃描一次轉移的規則才成立。狀態完成後若仍收到舊的完成脈衝,必須以 job_id 或目前狀態拒絕,避免上一批次的訊號觸發下一批次初始化。測試時也要故意在 entry_run 那一掃描觸發警報,確認初始化資料已清除但虛擬輸出仍被故障條件遮罩。
操作流程與驗收
本例在掃描開始採用上一掃描已核准的狀態,再比較state_prev與state_now;掃描內只決定一個pending_state,下一掃描生效。離開事件以prev=RUN且now≠RUN判斷。上升緣也可用「state=RUN」布林條件實作,但仍需正確保存前次值。
當你把這套規則搬到實際專案,請先用一個不連接設備的虛擬請求做逐掃描驗證。每次只允許主循環在掃描末端提交 next_state,下一掃描才成為 state_now;因此 WAIT 到 RUN、RUN 到 DONE 不會在同一掃描跳過邊界。若確實需要立即跳轉,請把每次轉移包成事件並設定最大事件數,佇列滿時產生溢位警報。對每個進入事件配置 enter_sequence,初始化完成才設為有效;初始化失敗時保留在 INIT_FAULT,不得假裝已進入 RUN。對離開事件也要設定 cleanup_done,防止通訊重送或重複邊沿再次保存結果。這些欄位要放入監看表,並在測試紀錄寫出掃描編號、前狀態、後狀態、事件計數與資料值。如此即使日後修改狀態圖,也能快速找出是哪一條轉移破壞了一次性規則。
畫出狀態轉移圖,標記每一條邊的觸發條件。
在監看表同時放 state_prev、state_now、entry_run、exit_run、test_buffer。
先以強制的虛擬請求做 WAIT→RUN→DONE→RUN,逐掃描記錄表格。
再測試重複請求、非法轉移、逾時及重新啟動;把每個結果寫入測試紀錄。
操作流程與驗收 續
實作時請把狀態轉移寫成一張可逐格驗證的表。假設按下啟動後,WAIT 在 S10 變成 RUN,S11 到 S14 都維持 RUN,S15 因完成條件變成 DONE。S10只能清除一次,若當次樣本有效則在清除後累加;S11~S14 每掃描累加一次;S15 要先保存結果再關閉輸出。若啟動按鈕在 S12 仍保持 ON,不應重新初始化,因為啟動是請求而不是進入事件。你可以在監看表加一個 entry_counter,每次 entry_run=1 才加一,測試五次重入後應為五。
邊界案例一是兩個條件同時要求轉移,例如完成與停止同時成立。規格必須給出優先序,本例採故障最高、停止其次、完成最後;因此同一掃描若 stop 與 done 同時為真,下一狀態是 WAIT,下一掃描辨認離開RUN並清理,本次先撤銷輸出,不執行 DONE 的完成處理。邊界案例二是狀態碼被通訊寫成 99,程式不可把它當成 RUN;應進入 SAFE 或 FAULT,關閉需要互鎖的輸出並留下原始碼。邊界案例三是重新啟動時 state_prev 與 state_now 不一致,先以初始化規則把兩者同步,再決定是否要求操作者重新啟動工作,避免上電瞬間誤發 entry。
完成後應看到什麼結果
RUN 的清除或載入動作只在 entry_run=1 的一個掃描發生;RUN 期間累加值連續增加;離開時只保存一次;再次進入 RUN 才重新建立初值。
失敗時先查哪裡
先看 prev 是否真的保存上一掃描,再看狀態是否被同一掃描內多次改寫,最後查上電初始化、保持裝置與非法轉移的處理。不要先用延時掩蓋狀態邊界錯誤。
適用型號與限制
概念適用於 Q06UDVCPU 等以循環掃描執行順序控制的系統;實際語言、保持範圍、任務或中斷行為須依 CPU 型號、參數與 GX Works 版本核對。
常見問題與來源
FAQ
問:可以用上升緣嗎?可以,對「state=RUN」條件取上升緣等同辨認進入,但前次狀態與初始化仍需正確管理。
問:RUN內能再次清除嗎?只有明訂的子週期重置才可,否則累加資料會遺失。問:同掃描跳過中間狀態怎麼辦?本例限制一次轉移,若應用需要多次轉移,應另設有容量與溢位規則的事件記錄。