← 所有文章

大量迴圈拖慢 PLC 掃描 如何分批處理工作

· 站長

以Capacity=1000、每批25筆的千筆案例,處理索引邊界、無號下溢、來源快照、批次取消與整批發布。

先固定容量與索引契約

本例有Capacity=1000,合法索引是0到999,Total=1000代表要檢查全部一千筆。工作開始時NextIndex=0,每掃描最多處理25筆,因此需要40批完成。先把容量、總數與索引寫成契約,避免把陣列長度、有效資料數和下一筆位置混為一談。

名稱 本例值 含義
Capacity 1000 陣列可容納筆數
Total 1000 本次有效資料筆數
NextIndex 0 下一批起點
BatchSize 25 每批上限
批次數 40 1000÷25

每次執行前先檢查0≤NextIndex≤Total≤Capacity。若Total=1001而Capacity仍是1000,結果必須是CAPACITY_ERROR,不能把第1000筆寫入陣列,也不能用『最後一批只有26筆』掩蓋超界。若需求真的要處理1001筆,先把容量明確改成至少1001,再重新建立來源快照與測試。

這個工作只做離線資料加總,沒有寫入致動器。先把輸入凍結成固定版本,初始化WorkSum為0、WorkCount為0、Busy為TRUE、ResultValid為FALSE。PublishedSum保留上一次完整工作的數字供顯示,但目前有效旗標為假。不要在開始新工作時把舊結果和新的進度混在同一個顯示欄,否則使用者會把未完成的部分值當成正式結果。

計算每批數量避免無號下溢

合法範圍通過後,Remaining=Total-NextIndex,n取BatchSize與Remaining的較小值。當NextIndex=975、Total=1000時,Remaining=25,n=25;處理完才把NextIndex推到1000。當NextIndex=1000時,Remaining=0,工作已完成,絕不可執行FOR 0 TO n-1。若n是無號型別,n-1在n=0時可能下溢成很大的數。

NextIndex Total Remaining n 動作
0 1000 1000 25 處理0..24
25 1000 975 25 處理25..49
950 1000 50 25 處理950..974
975 1000 25 25 處理975..999
1000 1000 0 0 新空工作完成 已完成工作不再重發Done

安全的教學偽碼(不可直接編譯)是:先驗證範圍;若NextIndex=Total則進入完成分支;否則n=MIN(25,Total-NextIndex),只有n>0才進入迴圈。索引使用NextIndex+k,且每次存取前仍可檢查小於Capacity。

控制迴圈的變數與總和應分別選型。索引需要表示0到1000這個完成哨兵值,總和需要表示499500;十六位元有號整數無法容納總和,即使每一筆Raw最大只有999。計算範圍應包含中間批和與累計值。本例整數相加可用足夠寬的型別精確驗算,實作前仍要查目標PLC的整數寬度與溢位行為。

用Raw[i]=i檢算每批結果

為了讓結果可手算,設定Raw[i]=i,i從0到999,檢查工作計算所有資料的總和。完整預期值是0+1+…+999=499500。第一批索引0到24的和是(0+24)×25÷2=300;第二批25到49的和是(25+49)×25÷2=925。這兩筆具體數值可抓出起點偏移或少算一筆。

批次 索引範圍 筆數 批和 累計
1 0..24 25 300 300
2 25..49 25 925 1225
39 950..974 25 24050 474825
40 975..999 25 24675 499500

第39批的批和為(950+974)×25÷2=24050;第40批為(975+999)×25÷2=24675。前38批累計加上這兩批後,最終必須是499500。結果先寫到WorkSum與WorkCount,每個小批成功後只更新工作進度;直到全部1000筆完成,才把WorkSum一次發布為PublishedSum並令ResultValid為TRUE。

第39批提交後,WorkCount為975,WorkSum為0到974的總和474825;再加最後25筆的24675,才得到499500。若某批驗證失敗,先前已完成的WorkSum保留供診斷,但ResultValid仍為假。重新開始採新工作,不從錯誤索引續跑;本例也不允許Busy期間的新請求覆蓋目前來源版本。

來源快照也要算進時間預算

分批檢查只能限制每次迴圈的工作量,不能自動解決來源不一致或快照搬移成本。若在工作開始時複製1000筆,複製本身可能超過掃描預算,必須把搬移時間、記憶體占用與其他任務一併估算。若資料來源可固定為不可變版本,保存SourceVersion並讓每批讀取同一版本;若來源會變動,版本改變時不能把舊NextIndex套到新排列。

策略 一致性 時間成本 本例處置
完整快照 一次搬1000筆 納入預算再決定
不可變來源 不搬整表 保存Version
每批直接讀取 可能混合 較低 來源變更即錯誤
雙份來源切換 依發布協定 需兩份容量 待平台驗證

合成單筆20微秒時,25筆約500微秒,1000筆理論約20毫秒;這只是估算,不是硬體測量。實際還要加入索引、判斷、來源讀取、結果寫入、任務切換和通訊成本。應用目標CPU量測最長批次時間,再決定25是否適合。

分批會增加牆鐘完成時間,不能只看計算量變少。若理想任務每10毫秒啟動一次,第一批在0毫秒開始,第40批約390毫秒開始,之後還要加最後一批的實際執行時間。這不是20毫秒內完成一千筆的同一種服務。需求若同時要求短掃描與很短總完成時間,就要重新評估硬體、算法或可接受的資料筆數。

Cancel與整批結果發布

本例固定規則:Cancel只在每批開始取樣。掃描進入批次時若Cancel=1,工作轉CANCELLED,不執行該批;若批次執行中才收到Cancel,當批仍完成,下一批開始前才取樣;若已是最後一批,則完成先於尚未取樣的取消,此次結果仍為DONE。這避免在單筆中途停止造成WorkSum與WorkCount半批發布。CancelRequested、CancelSampled與Cancelled結果分開記錄。

時刻 NextIndex Cancel 結果
批次3開始 50 0 執行50..74
批次3中途 仍50 1 本批完成,不立即停
批次4開始 75 1 取樣後CANCELLED
取消後 75 1 Done=false,保留已完成50..74
重新開始 依命令 0 新工作重新快照

每批結果先放暫存區,包含批次起點、筆數、批和、失敗筆數與SourceVersion。n筆全部完成才提交;任何一筆檢查失敗,依本例策略回ERROR且不發布該批部分結果。Done只有全部資料完成、最後批已提交,且本次批首沒有接受Cancel、過程沒有ERROR時才為真;未取樣的取消不追溯推翻已完成結果。

CancelRequested應保持到工作者確認或終態,不能只送一個比任務週期短的脈衝。以第三批中途取消為例,第三批全部完成後NextIndex為75,WorkSum為0到74的和2775;第四批起始接受取消,因此PublishedSum不更新、ResultValid為假。這組數字同時檢查取消延遲與沒有發布半成品兩個要求。

邊界排查 FAQ與來源

完成後應看到40次小批提交、WorkCount為1000、WorkSum與PublishedSum皆為499500,ResultValid為真。Done只在進入完成終態的那次呼叫為真,後續呼叫清回假且不再存取陣列。Total為0的新工作則回傳有效總和0並完成一次;若只是已完成工作再次被呼叫,不能把它當成另一個空工作重發完成事件。

排查先看Capacity、Total、NextIndex是否通過不等式,再看n=0是否避開迴圈,接著核對每批起訖索引與WorkCount。若總和不是499500,先比對第一批300與第二批925,再查最後批是否完整包含999。若1001筆被接受,立即查容量驗證;這是錯誤而非正常延伸。

問:每批25筆一定不會超時嗎?不一定,20微秒只是合成估算,仍需量測最長批次。問:Cancel一到就停可以嗎?本例固定在批次起始取樣,批內取消下批才接受。問:PartialSum能先顯示嗎?可以顯示工作進度,但不能把它標成完成結果。問:1001筆如何處理?先把Capacity改到至少1001並重新驗證,Capacity=1000時必須回CAPACITY_ERROR。

本文偽碼不可直接編譯;需依目標PLC的陣列界限、無號整數、任務週期、看門狗與資料快照功能改寫。案例為離線手算。

參考:CODESYS FOR 迴圈

參考:CODESYS array declaration and bounds

參考:CODESYS 任務設定

延伸閱讀


使用 PLC 工具箱 →