先把問題拆開 定義狀態 請求與完成條件
先把這篇當成兩個虛擬模組:Sender送出工作資料,Receiver處理後回報結果。你會以ReqId追蹤同一筆工作,並在每個掃描週期觀察訊號。本文固定規則是Done代表成功、Fail代表失敗,兩者互斥;逾時也必須走Fail,不能用Done掩蓋問題。
| 項目 | 應定義 | 不要混淆 |
|---|---|---|
| Req | 提出工作 | 已被接收 |
| Accept | 等待收件確認 | 完成結果 |
| Busy | 等待處理 | 可覆蓋新資料 |
| Done/Fail | 等待結果 | 同時成立 |
教學先用虛擬狀態與監看欄位,不接實體輸出。每一步都寫前置、操作、預期結果與失敗先查位置;這樣你在工程軟體中才能逐項核對,而不是只看最後一盞燈。
建立流程 先做狀態表 再寫轉移
先建立Req、ReqId、Accept、Busy、Done、Fail、ResultAck及各自的識別碼。Sender寫好資料後保持Req,Receiver在IDLE看見尚未處理的ReqId時鎖存請求,接受時複製快照。Accept保持到Sender清除Req,不用一掃描脈衝跨任務傳遞。Done或Fail保持到ResultAck核對同一ReqId;確認完才回IDLE。
建立 Req、ReqId、Accept、Busy、Done、Fail、DoneId、FailId、ErrorCode。
ReqId=17 保持到 Accept,確認只接受一次。
Busy 期間送 ReqId=18,依規格拒絕,不覆蓋 17。
完成三步後核對 DoneId=17;逾時則 FailId=17。
完成後應看到:狀態、操作請求、資料欄位與虛擬輸出一致。若不同,先查是否有其他程式段改寫狀態、在錯誤分支清除記憶,或取樣時機不一致。
資料接收失敗而尚未接受時,應帶原ReqId回覆拒絕原因;已接受後的執行失敗才使用Fail。識別碼回捲與重啟要有會話編號或等效規則,不能讓舊結果碰巧等於新請求。此握手是本文自訂協議,不是任何模組內建的固定旗標。
具體合成案例 逐掃描核對正常與邊界
案例ReqId=17:S2保持請求,S3接受並鎖定資料,S4送出方看到Accept後清除Req,接收方繼續工作。S5仍處理,S6成功後保持Done與DoneId=17;S7送出方保存結果並回ResultAck=17,S8接收方清除結果回IDLE。Fail採相同確認流程。
| 掃描/條件 | 判斷 | 狀態 | 應看到的結果 |
|---|---|---|---|
| S1 | Req=0 | IDLE | 無待辦 |
| S2 | Req=1 Id=17 | REQUEST | 鎖存待接受請求 |
| S3 | 資料檢查通過 | BUSY | Accept=1 鎖定快照 |
| S4 | Sender清Req | BUSY | 清Accept 繼續處理 |
| S5 | 尚未完成 | BUSY | 保持原工作 |
| S6 | 成功且未逾時 | RESULT | Done=1 Fail=0 Id=17 |
| S7 | ResultAck=17 | RESULT | 確認結果已取走 |
| S8 | 確認完成 | IDLE | 清結果 可接受下一件 |
本例每次只採用一個狀態轉移,故障優先於一般操作;取消、停止與完成的細節依下列固定案例核對。不同設備改用其他政策時,文字、事件表與程式必須一起修改。
把晚到回覆與重複請求分開測試
先從最容易觀察的成功路徑開始。把兩個模組的旗標排在同一監看表,逐次記錄誰改了哪個欄位。送出方提出請求後,資料保持不變;接收方接受時複製自己的工作資料。兩份資料的用途不同,接收方後續計算應只讀工作快照,不能再次讀取可能已被畫面編輯的來源欄位。
再故意延後送出方讀取結果。接收方已完成,但送出方暫時沒有執行,成功旗標和結果識別碼都應保持,不能在一個掃描後消失。送出方恢復後先保存結果,再回覆結果確認。接收方看到相同識別碼的確認,才清除結果;確認其他工作不能清掉這一筆。
第三個練習是逾時之後收到晚到結果。送出方已經回報等待逾時,不代表接收方一定停止了工作。這時把晚到結果放進待核對紀錄,依原識別碼判斷是否已完成,不要直接寫到下一件工作的畫面。若工作有實際副作用,未確認結果之前盲目重試可能做兩次。
同一請求長時間保持,也不應被反覆接受。接收方記錄已接受的識別碼及目前狀態,當工作進行中或結果待確認時拒絕新接受。回到待機後,送出方需完成舊請求解除,再以新識別碼開始下一件。這個回到空閒的過程,是握手的一部分,不是多餘延遲。
最後測試重啟。若只有送出方重新啟動,它可能忘了正在等哪一件;若只有接收方重新啟動,它可能忘了哪些工作已完成。因此要先對帳會話、請求識別碼與結果狀態,再開放新的工作。單靠成功位元仍為一,無法證明那就是這次請求的結果。
失敗先查 適用限制與常見問題
在Q系列中可用步進狀態或等效旗標實作握手;實際裝置位址與保持設定請依專案配置。先用暫存器監看每一掃描:Req、Accept、Busy、Done、Fail、ReqId、DoneId、ErrorCode。看到Done時應確認Fail=0且DoneId等於ReqId;看到Fail時應先記錄錯誤碼再復歸。
三個常見問題
Req一定要脈衝嗎?本例保持到Accept才清除。Done代表成功嗎?本例是,而且要核對DoneId;Fail表示失敗,兩者互斥。忙碌能覆蓋資料嗎?本例拒絕新工作,不排隊、不覆蓋原快照。
送出方的操作順序也要寫進測試:先準備資料、增加 ReqId、保持 Req,再等待 Accept;收到 Accept 後不可任意改寫快照。若等待超過上限,送出方要把此次工作標為未完成,保存逾時原因,不能把 Busy 清掉後立刻送出另一件而讓晚到的 Done 對錯。接收方每次回報都帶 DoneId 或 FailId,並在結果確認後回到IDLE。若事件脈衝可能被另一任務漏讀,可改用事件序號或保持到確認,但要避免確認本身被重複計算。測試時故意讓 Req 在接收方忙碌、完成同掃描送新 Req、以及 Reset 發生在 Busy 中,逐項確認規格結果。
實作時可把協議畫成兩條泳道:Sender 只寫 Req 與資料,Receiver 只寫 Accept、Busy、Done、Fail;回覆欄位由 Receiver 寫、Sender 讀。若同一欄位兩邊都寫,除錯時無法知道誰覆蓋誰。請再加入版本或資料長度欄位,接收方先檢查長度再接受。完成後 Sender 應以 DoneId 比對等待中的 ReqId,對不上就記錄晚到回覆,不要直接更新目前畫面。
故障演練時,在Receiver忙碌中刻意送新Id,應得到獨立RejectId與RejectReason,原工作不受影響;不要用原工作的Fail欄位回報另一件請求。再測完成與逾時同時成立,本例逾時優先,保持Fail與原ReqId。接收方失聯時,送出方記錄未知結果,不能自行清除對方Busy後假裝可重試。
操作驗收與適用限制
驗收時固定記三個結果:成功為Done=1、Fail=0且DoneId=ReqId;失敗為Fail=1、Done=0且FailId=ReqId;忙碌時新Req不得改寫原資料。限制是本文只示範握手概念,實際旗標位址、保持範圍、通訊更新週期仍須依Q系列專案與模組手冊確認。
請用三次測試收尾:正常完成、處理中重送Req、逾時。每次保存ReqId與ErrorCode,重新RUN後確認是否需要保留結果;若專案要求斷電保持,請另行設定保持裝置並檢查初始化是否會覆寫。
適用型號與限制:概念可套用具備位元與狀態資料的 PLC;實際指令、資料型別、計時單位、模式切換與復歸行為必須依目標 CPU、工程軟體和設備規格確認。