戰(zhàn):UDT+FB重構(gòu)泵控程序,效率提升看得見)
接了個(gè)水泵站項(xiàng)目四個(gè)泵兩個(gè)變頻兩個(gè)工頻現(xiàn)場(chǎng)控制邏輯大同小異。我剛?cè)胄心菚?huì)兒圖省事每個(gè)泵都單獨(dú)寫一段梯形圖一個(gè)子程序能堆到三四百行。改個(gè)延時(shí)時(shí)間得翻半天程序甲方說(shuō)再加一臺(tái)泵我整個(gè)人是崩潰的。后來(lái)第二期項(xiàng)目我咬著牙把所有泵的邏輯重構(gòu)成結(jié)構(gòu)體UDT加功能塊FB的寫法PLC編程效率提升是肉眼可見的后期維護(hù)更是省心不止一點(diǎn)。這篇文章就圍繞“UDTFB”這套模塊化做法展開聊聊為什么PLC程序會(huì)“越寫越亂”、UDT和FB到底解決什么問(wèn)題、兩者怎么配合落地以及我在實(shí)際項(xiàng)目中踩過(guò)的坑。適合那些已經(jīng)能獨(dú)立寫梯形圖或SCL但程序還停留在“一份代碼復(fù)制N遍”階段的工程師也適合剛轉(zhuǎn)行做PLC編程、想從一開始就建立良好編程習(xí)慣的朋友。目錄邏輯我會(huì)按“為什么要模塊化、UDT怎么用、FB怎么用、UDTFB組合實(shí)戰(zhàn)、常見問(wèn)題排查”這樣走盡量把原理、代碼、排錯(cuò)都串起來(lái)讓你看完能直接上手改自己的程序。1. 為什么PLC程序需要模塊化很多初學(xué)者會(huì)有個(gè)錯(cuò)覺PLC程序嘛變量多、邏輯雜能跑起來(lái)就行。確實(shí)單機(jī)設(shè)備、幾十個(gè)I/O點(diǎn)的情況下怎么寫都行。但一旦進(jìn)入多工位、多設(shè)備的產(chǎn)線級(jí)項(xiàng)目程序的可讀性、可維護(hù)性、可擴(kuò)展性就會(huì)決定項(xiàng)目成敗。1.1 復(fù)制粘貼式編程的災(zāi)難我見過(guò)最夸張的一個(gè)項(xiàng)目12臺(tái)相同的風(fēng)機(jī)工程師把風(fēng)機(jī)啟停程序復(fù)制了12份每個(gè)程序段里變量名就是FAN1_START、FAN2_START、FAN3_START……一直到FAN12_START。改一個(gè)保護(hù)邏輯就得同時(shí)修改12份程序漏改一個(gè)就會(huì)出問(wèn)題。這種“復(fù)制、粘貼、改變量”的編程方式短期開發(fā)看似快后期維護(hù)成本是成倍增長(zhǎng)的。更麻煩的是這種程序占用的存儲(chǔ)空間很大掃描周期也會(huì)被拉長(zhǎng)。在S7-1200這類中低端CPU上程序段多了在線監(jiān)控都會(huì)卡頓。而且團(tuán)隊(duì)成員之間協(xié)作時(shí)別人拿了你的程序光搞清楚FAN7和FAN8之間的細(xì)微差別就要花大力氣。1.2 模塊化的核心收益模塊化的本質(zhì)是把“數(shù)據(jù)”和“邏輯”都封裝成可復(fù)用的單元。數(shù)據(jù)上用結(jié)構(gòu)體UDT統(tǒng)一格式邏輯上用功能塊FB統(tǒng)一行為。這樣帶來(lái)三個(gè)直接好處復(fù)用性一個(gè)泵控FB寫好100臺(tái)泵就是100個(gè)實(shí)例不用重復(fù)寫邏輯??删S護(hù)性修改FB內(nèi)部邏輯所有實(shí)例同步更新不用逐個(gè)程序段去改??蓞f(xié)作性程序結(jié)構(gòu)清楚團(tuán)隊(duì)分工明確你寫電機(jī)FB、我寫閥門FB最后拼裝即可。這里要區(qū)分一下PLC的模塊化與高級(jí)語(yǔ)言里的“函數(shù)庫(kù)”思想是一致的。你寫C語(yǔ)言不會(huì)把同一個(gè)函數(shù)復(fù)制100份而是定義函數(shù)再調(diào)用。PLC里的UDT和FB就是PLC世界的“結(jié)構(gòu)體定義”和“類方法”。2. 結(jié)構(gòu)體UDT把松散的數(shù)據(jù)綁在一起UDTUser Defined Type在西門子TIA Portal里叫“PLC數(shù)據(jù)類型”在三菱GX Works3里叫“結(jié)構(gòu)體”在歐姆龍Sysmac Studio里也叫“結(jié)構(gòu)體”。名字不同意思一樣把一組不同類型但邏輯相關(guān)的數(shù)據(jù)打包成一個(gè)整體。2.1 UDT到底是什么打個(gè)比方你要管理一個(gè)倉(cāng)庫(kù)最原始的方式是拿一個(gè)本子把所有貨物的名稱、數(shù)量、位置、入庫(kù)日期都記在流水賬里。找一樣?xùn)|西要翻半天。UDT相當(dāng)于你把倉(cāng)庫(kù)劃分成若干貨架每個(gè)貨架上有固定的格子每種貨物放在自己專屬的位置上。你只要知道貨架編號(hào)就能直接找到所有相關(guān)信息。在PLC里定義一個(gè)電機(jī)相關(guān)的UDT大概包含這些成員Motor_UDT ├── CMD命令區(qū) │ ├── StartCmd : Bool │ ├── StopCmd : Bool │ ├── ResetCmd : Bool ├── ST狀態(tài)區(qū) │ ├── Running : Bool │ ├── Fault : Bool │ ├── Ready : Bool ├── PARAM參數(shù)區(qū) │ ├── StartDelay : Time │ ├── RatedCurrent : Real │ └── OverloadThreshold : Real └── STAT統(tǒng)計(jì)區(qū) └── TotalRunTime : Real有了這樣一個(gè)UDT你在創(chuàng)建電機(jī)變量時(shí)不需要再單獨(dú)建立十幾個(gè)獨(dú)立的Bool、Real變量而是直接聲明一個(gè)Motor1 : Motor_UDT。電機(jī)1的所有信息都通過(guò)Motor1.CMD.StartCmd、Motor1.ST.Running這種層級(jí)方式訪問(wèn)清爽又直觀。2.2 在TIA Portal里定義UDT以西門子S7-1200/1500為例三菱、歐姆龍的操作思路完全一致定義步驟是在項(xiàng)目樹中找到“PLC數(shù)據(jù)類型”右鍵→“添加新數(shù)據(jù)類型”取個(gè)名字比如Pump_UDT。然后在打開的編輯表中輸入成員名稱選擇數(shù)據(jù)類型填初始值和注釋。這里有個(gè)關(guān)鍵點(diǎn)成員順序會(huì)直接影響數(shù)據(jù)的存儲(chǔ)布局。雖然對(duì)運(yùn)行邏輯影響不大但對(duì)在線監(jiān)控時(shí)的可讀性影響很大建議按“命令→狀態(tài)→參數(shù)→預(yù)留”的順序排列和現(xiàn)場(chǎng)端子排布保持一致的對(duì)應(yīng)關(guān)系。2.3 UDT的進(jìn)階玩法嵌套與數(shù)組UDT之間可以互相嵌套。比如你定義了一個(gè)Motor_UDT然后要做一個(gè)多電機(jī)的泵站可以再定義PumpStation_UDT ├── Motor1 : Motor_UDT ├── Motor2 : Motor_UDT ├── Motor3 : Motor_UDT └── InletValve : Valve_UDT這樣整個(gè)泵站就是一個(gè)變量所有子設(shè)備的數(shù)據(jù)都在其中。HMI組態(tài)時(shí)變量表結(jié)構(gòu)也能保持同樣的層級(jí)關(guān)系觸摸屏那邊的變量連接效率也更高。UDT數(shù)組也極其實(shí)用。比如有4臺(tái)泵你直接定義Pump : Array[1..4] of Pump_UDT然后程序里可以用FOR循環(huán)遍歷查找當(dāng)前運(yùn)行狀態(tài)統(tǒng)計(jì)運(yùn)行中泵數(shù)量 FOR i : 1 TO 4 DO IF Pump[i].ST.Running THEN RunningCount : RunningCount 1; END_IF; END_FOR;用數(shù)組的思路配合后文要講的FB是實(shí)現(xiàn)設(shè)備輪換、組啟動(dòng)、組停止的利器。2.4 UDT定義的注意事項(xiàng)UDT看似簡(jiǎn)單但實(shí)際使用中很容易踩坑。我總結(jié)幾條經(jīng)驗(yàn)預(yù)留擴(kuò)展區(qū)在UDT末尾加一段Reserve : Array[0..15] of Byte。項(xiàng)目后期要增加通道、增加參數(shù)時(shí)直接使用預(yù)留區(qū)可以避免修改UDT定義導(dǎo)致CPU停機(jī)或背景DB結(jié)構(gòu)變化。命名要有區(qū)分度“CMD”和“ST”這兩個(gè)區(qū)組名很好用一看就知道是命令還是狀態(tài)。不要取“DATA1、DATA2、DATA3”這種序號(hào)式命名維護(hù)時(shí)猜猜猜太痛苦。注釋寫單位Real類型成員必須寫上單位比如RatedCurrent : Real注釋寫成“額定電流單位A”。否則幾個(gè)月后你自己看著這個(gè)變量都不知道該填什么數(shù)。3. 功能塊FB有記憶的程序塊FB是Function Block翻譯為“函數(shù)塊”或“功能塊”。它和FCFunction最大的區(qū)別在于FB擁有自己獨(dú)立的數(shù)據(jù)存儲(chǔ)區(qū)也就是背景數(shù)據(jù)塊Instance DB。簡(jiǎn)單說(shuō)FB有“記憶”FC沒(méi)有“記憶”。3.1 FB和FC怎么選入門工程師最容易搞混的就是FB和FC。我習(xí)慣用一個(gè)“儲(chǔ)物柜”的比喻FC就像一個(gè)沒(méi)有儲(chǔ)物柜的臨時(shí)工你給他一個(gè)輸入他現(xiàn)場(chǎng)算完給你一個(gè)輸出干完就走什么都不留。FB則自己帶一個(gè)專屬儲(chǔ)物柜干完活會(huì)把中間變量、狀態(tài)值工整地放進(jìn)柜子里存好下次再進(jìn)來(lái)能接著上次的狀態(tài)繼續(xù)干活。實(shí)際項(xiàng)目中設(shè)備級(jí)控制邏輯——如電機(jī)啟停、閥門動(dòng)作、伺服定位——一律用FB因?yàn)樵O(shè)備有“當(dāng)前狀態(tài)”數(shù)學(xué)計(jì)算、數(shù)據(jù)類型轉(zhuǎn)換、簡(jiǎn)單比較——用FC就夠因?yàn)檫@類功能不依賴歷史狀態(tài)。三菱PLC里也有類似的區(qū)分FB還是叫FB和西門子的FB一個(gè)概念但三菱沒(méi)有嚴(yán)格對(duì)應(yīng)FC的“無(wú)狀態(tài)”函數(shù)概念早期多用子程序和宏歐姆龍則既支持功能塊也支持無(wú)記憶的功能。各大品牌思路一致只是叫法略有不同。3.2 FB的接口設(shè)計(jì)先想好“外立面”FB的接口就是它的“外立面”是別人調(diào)用時(shí)能看到的窗口。設(shè)計(jì)FB接口時(shí)分這么幾類VAR_INPUT輸入由外部傳入如啟動(dòng)命令、停止命令、使能信號(hào)。VAR_OUTPUT輸出由FB內(nèi)部計(jì)算后輸出給外部如運(yùn)行指示、故障指示、輸出到接觸器的指令。VAR_IN_OUT輸入輸出兼有適合傳結(jié)構(gòu)體。這個(gè)很關(guān)鍵——你可以把整個(gè)Pump_UDT作為IN_OUT傳給FBFB內(nèi)部可以直接讀寫它的成員。VAR_STATFB內(nèi)部的靜態(tài)變量保存在背景DB中掉電保持或掃描間保持。VAR_TEMP臨時(shí)變量執(zhí)行完本周期就清零不保存。接口設(shè)計(jì)的核心原則是FB要盡量“自包含”。也就是說(shuō)外部只負(fù)責(zé)給命令和讀狀態(tài)具體內(nèi)部怎么判斷、怎么延時(shí)、怎么互鎖都封裝在FB里。一個(gè)標(biāo)準(zhǔn)的泵控FB接口大概長(zhǎng)這樣FUNCTION_BLOCK FB_PumpControl VAR_INPUT Enable : Bool; // 總使能 StartCmd : Bool; // 啟動(dòng)命令 StopCmd : Bool; // 停止命令 AutoMode : Bool; // 自動(dòng)模式 RunFb : Bool; // 運(yùn)行反饋 FaultSig : Bool; // 故障信號(hào) END_VAR VAR_OUTPUT RunCmd : Bool; // 運(yùn)行指令輸出 FaultOut : Bool; // 故障輸出 END_VAR VAR_IN_OUT PumpIO : Pump_UDT; // 泵的數(shù)據(jù)結(jié)構(gòu) END_VAR VAR_STAT step : Int; // 狀態(tài)機(jī)當(dāng)前步 tDelay : TON; // 延時(shí)定時(shí)器 END_VAR3.3 FB內(nèi)部邏輯狀態(tài)機(jī)寫法讓邏輯徹底清晰FB內(nèi)部邏輯組織我非常推薦“狀態(tài)機(jī)”寫法這也是搜索結(jié)果中“PLC編程狀態(tài)機(jī)寫法”這個(gè)熱詞對(duì)應(yīng)的核心內(nèi)容。狀態(tài)機(jī)的思想其實(shí)很簡(jiǎn)單設(shè)備任何時(shí)刻都處在有限個(gè)狀態(tài)之一每個(gè)狀態(tài)只處理自己的事情條件滿足就切換狀態(tài)。用泵控來(lái)舉例狀態(tài)可以分成空閑IDLE→ 啟動(dòng)延時(shí)START_DELAY→ 運(yùn)行RUNNING→ 停止延時(shí)STOP_DELAY→ 故障FAULT。梯形圖寫這種狀態(tài)機(jī)會(huì)很啰嗦SCL會(huì)優(yōu)雅很多核心就是CASE語(yǔ)句CASE step OF 0: // IDLE IF StartCmd AND NOT RunFb AND NOT FaultSig THEN step : 1; tDelay(IN : FALSE); END_IF; 1: // START_DELAY tDelay(IN : TRUE, PT : PumpIO.PARAM.StartDelay); IF tDelay.Q THEN RunCmd : TRUE; step : 2; END_IF; IF StopCmd OR FaultSig THEN step : 0; END_IF; 2: // RUNNING IF FaultSig THEN RunCmd : FALSE; step : 3; ELSIF StopCmd OR NOT RunFb THEN RunCmd : FALSE; step : 0; END_IF; 3: // FAULT IF PumpIO.CMD.ResetCmd THEN step : 0; END_IF; END_CASE;特別提醒狀態(tài)機(jī)的變量只在狀態(tài)切換的瞬間賦值其他情況下不要反復(fù)寫否則一個(gè)掃描周期內(nèi)可能出現(xiàn)“狀態(tài)還沒(méi)啟動(dòng)延時(shí)延時(shí)定時(shí)器就被Clear”的古怪問(wèn)題。這也是為什么靜態(tài)變量step要單獨(dú)聲明——它代表了FB的“記憶”。4. 實(shí)戰(zhàn)UDTFB組合完成一臺(tái)泵的模塊化控制理論講完下面用一套完整的三泵聯(lián)動(dòng)項(xiàng)目案例把UDT和FB的組合應(yīng)用串起來(lái)。這臺(tái)泵的需求不完全一樣兩用一備手動(dòng)/自動(dòng)切換故障自動(dòng)切泵運(yùn)行時(shí)間累計(jì)。4.1 第一步定義泵的數(shù)據(jù)結(jié)構(gòu)在TIA Portal的“PLC數(shù)據(jù)類型”里新建Pump_UDT。包含以下成員Pump_UDT ├── CMD命令區(qū) │ ├── StartRemote : Bool; // 遠(yuǎn)程啟動(dòng) │ ├── StopRemote : Bool; // 遠(yuǎn)程停止 │ ├── ResetFault : Bool; // 復(fù)位故障 │ ├── AutoMode : Bool; // 自動(dòng)模式1自動(dòng)/0手動(dòng) ├── ST狀態(tài)區(qū) │ ├── RunFb : Bool; // 運(yùn)行反饋 │ ├── FaultSig : Bool; // 故障信號(hào) │ ├── Running : Bool; // 運(yùn)行狀態(tài) │ ├── Fault : Bool; // 故障狀態(tài) ├── PARAM參數(shù)區(qū) │ ├── StartDelay : Time; // 啟動(dòng)延時(shí) │ ├── StopDelay : Time; // 停止延時(shí) │ ├── MinRunTime : Time; // 最小運(yùn)行時(shí)間 ├── STAT統(tǒng)計(jì)區(qū) │ └── TotalRunTime : Real; // 累計(jì)運(yùn)行時(shí)間小時(shí) └── Reserve預(yù)留區(qū) └── Reserve : Array[0..7] of Byte;注意這里我把“狀態(tài)區(qū)”和“統(tǒng)計(jì)區(qū)”分開了。為什么因?yàn)闋顟B(tài)區(qū)是實(shí)時(shí)刷新、掉電重置的統(tǒng)計(jì)區(qū)則需要保持后續(xù)編程時(shí)對(duì)掉電保持區(qū)域的規(guī)劃會(huì)更清晰。4.2 第二步封裝泵控FB新建FB命名FB_PumpControl。接口按3.2節(jié)設(shè)計(jì)內(nèi)部邏輯用狀態(tài)機(jī)。下面補(bǔ)充兩個(gè)關(guān)鍵環(huán)節(jié)。手動(dòng)/自動(dòng)切換AutoMode為TRUE時(shí)FB只接收外部聯(lián)鎖邏輯送來(lái)的StartRemote/StopRemoteAutoMode為FALSE時(shí)FB只接收就地按鈕信號(hào)。這里不建議手動(dòng)信號(hào)和自動(dòng)信號(hào)混用否則容易出現(xiàn)“自動(dòng)狀態(tài)下手動(dòng)按鈕也能啟泵”的安全隱患。累計(jì)運(yùn)行時(shí)間在RUNNING狀態(tài)下每個(gè)掃描周期做一次累加。如果直接每次加1單位是掃描周期非常不直觀。更穩(wěn)的做法是調(diào)用系統(tǒng)時(shí)鐘計(jì)算兩次掃描之間的時(shí)間差// 使用系統(tǒng)時(shí)間差累計(jì) nowTime : DINT_TO_LREAL( TIME_TO_DINT( T_PLC_MS() ) ); IF step 2 THEN PumpIO.STAT.TotalRunTime : PumpIO.STAT.TotalRunTime (nowTime - lastTime) / 3600000.0; END_IF; lastTime : nowTime;T_PLC_MS()是讀取CPU運(yùn)行毫秒數(shù)的系統(tǒng)函數(shù)不同的PLC品牌有各自的系統(tǒng)時(shí)鐘讀取方式但原理一樣。這個(gè)寫法比每周期加常數(shù)要精確得多也不會(huì)因?yàn)镃PU掃描周期波動(dòng)導(dǎo)致累計(jì)時(shí)間偏差。4.3 第三步實(shí)例化FB并建立泵組調(diào)度在主程序OB1中創(chuàng)建3臺(tái)泵的數(shù)據(jù)結(jié)構(gòu)和FB實(shí)例。以下是我在FC調(diào)用邏輯里的寫法VAR PumpData : Array[1..3] of Pump_UDT; fbPump1 : FB_PumpControl; fbPump2 : FB_PumpControl; fbPump3 : FB_PumpControl; END_VAR然后分別在網(wǎng)絡(luò)段里調(diào)用// 泵1 fbPump1( Enable : TRUE, StartCmd : PumpData[1].CMD.StartRemote, StopCmd : PumpData[1].CMD.StopRemote, AutoMode : PumpData[1].CMD.AutoMode, RunFb : PumpData[1].ST.RunFb, FaultSig : PumpData[1].ST.FaultSig, RunCmd PumpData[1].ST.Running, FaultOut PumpData[1].ST.Fault, PumpIO : PumpData[1] );3臺(tái)泵就是3個(gè)FB實(shí)例程序段幾乎一模一樣。后續(xù)如果要加第4臺(tái)泵復(fù)制一個(gè)調(diào)用加上對(duì)應(yīng)數(shù)據(jù)和變量5分鐘搞定。輪換邏輯放在另一個(gè)FC里做。核心思路是自動(dòng)模式下如果當(dāng)前運(yùn)行泵故障在泵組中查找“累計(jì)運(yùn)行時(shí)間最短”且“無(wú)故障”的泵作為下一臺(tái)啟動(dòng)對(duì)象。這個(gè)過(guò)程本質(zhì)上就是個(gè)數(shù)組遍歷加比較幾行SCL就搞定。模塊化的好處在這里體現(xiàn)得淋漓盡致輪換調(diào)度只關(guān)心泵的UDT.ST和UDT.STAT根本不用管每一臺(tái)泵具體是怎么啟動(dòng)的。4.4 與HMI聯(lián)動(dòng)變量表也模塊化用了UDT之后HMI組態(tài)的變量連接也會(huì)輕松很多。比如觸摸屏要顯示1號(hào)泵的運(yùn)行狀態(tài)變量地址就是PumpData[1].ST.Running。在WinCC或第三方HMI中可以直接導(dǎo)入PLC變量層級(jí)結(jié)構(gòu)保持和UDT一致。這意味著HMI畫面的開發(fā)也可以模板化建一個(gè)“泵操作面板”的畫面模板綁定PumpData[i]即可復(fù)制3份對(duì)應(yīng)3臺(tái)泵。HMI上最常見的操作——“復(fù)位故障”在FB內(nèi)部是這么處理的按下復(fù)位按鈕ResetFault置位FB在FAULT狀態(tài)下檢測(cè)到之后把故障狀態(tài)清掉、回到IDLE。這里要多說(shuō)一句復(fù)位按鈕最好用“上升沿觸發(fā)”而不是電平觸發(fā)否則按鈕一直按著故障一出現(xiàn)就被瞬間復(fù)掉可能掩蓋真實(shí)故障。5. 常見問(wèn)題與排查技巧實(shí)錄模塊化實(shí)踐不像書上寫的那么順風(fēng)順?biāo)畬?shí)際項(xiàng)目中總會(huì)遇到各種幺蛾子。下面這幾個(gè)問(wèn)題是我和身邊同行交流后總結(jié)出的高頻坑。5.1 背景數(shù)據(jù)塊太多項(xiàng)目樹爆炸每調(diào)用一個(gè)FB就會(huì)生成一個(gè)背景DB。如果一個(gè)設(shè)備有幾十個(gè)FB實(shí)例項(xiàng)目樹里就會(huì)出現(xiàn)幾十個(gè)背景DB看著就腦仁疼。這也是很多工程師嫌棄FB的原因——“變量不好找嘛”。其實(shí)解決思路有兩個(gè)。一是使用多重背景在某個(gè)FB的靜態(tài)變量區(qū)聲明其他FB的實(shí)例例如在FB_PumpSystem的靜態(tài)區(qū)中聲明fbPump1 : FB_PumpControl這樣所有泵控FB的背景DB只有一份作為FB_PumpSystem的背景DB的子區(qū)域。二是把某些內(nèi)部狀態(tài)不重要的FB改用FC減少背景DB數(shù)量。但后者會(huì)犧牲“記憶”能力很多設(shè)備邏輯復(fù)用不了。我的建議是設(shè)備級(jí)FB可以考慮多重背景過(guò)程級(jí)調(diào)度邏輯用FC這種“FB管設(shè)備、FC管調(diào)度”的分工在實(shí)際項(xiàng)目中很順手。5.2 在線修改UDT后CPU直接STOP這個(gè)問(wèn)題最容易讓人冷汗直冒。項(xiàng)目調(diào)試中你覺得UDT里缺一個(gè)參數(shù)于是在線修改了UDT定義然后點(diǎn)擊“下載”結(jié)果CPU從RUN切換到STOP現(xiàn)場(chǎng)設(shè)備全部停機(jī)。原因是UDT結(jié)構(gòu)變化會(huì)導(dǎo)致所有引用它的數(shù)據(jù)塊、背景DB的存儲(chǔ)布局全部變化CPU無(wú)法在不停止的情況下重新分配內(nèi)存。處理方式很明確項(xiàng)目上線前把UDT定義凍結(jié)禁止追加或刪除成員只允許改初始值或注釋。UDT尾部一定要加預(yù)留字節(jié)就是前面說(shuō)的Reserve : Array[0..7] of Byte項(xiàng)目后期需要加數(shù)據(jù)時(shí)用完這8個(gè)字節(jié)日后再加能避免絕大多數(shù)的停機(jī)風(fēng)險(xiǎn)。如果要修改的UDT已經(jīng)在生產(chǎn)線上運(yùn)行務(wù)無(wú)比對(duì)好影響范圍停機(jī)窗口內(nèi)快速下載不熟練的情況下可以用“復(fù)制UDT為另一個(gè)新類型”的方式做增量邏輯驗(yàn)證驗(yàn)證通過(guò)再切換。5.3 狀態(tài)機(jī)執(zhí)行異常為什么設(shè)備偶爾卡在一個(gè)狀態(tài)狀態(tài)機(jī)的坑多半出在“邊沿處理”和“多周期競(jìng)爭(zhēng)”上。比如START_DELAY狀態(tài)里tDelay.Q條件滿足了但同時(shí)StopCmd也來(lái)了這時(shí)候到底進(jìn)RUNNING還是回IDLECASE語(yǔ)句的判斷順序決定了結(jié)果。如果IF StopCmd OR FaultSig寫在IF tDelay.Q前面那就會(huì)優(yōu)先退回IDLE不會(huì)進(jìn)入RUNNING。這不是bug是邏輯順序問(wèn)題但很容易讓后來(lái)接手的工程師困惑。排查方法很簡(jiǎn)單把step變量加到監(jiān)控表里在線觀察設(shè)備卡在哪個(gè)狀態(tài)值。如果一直卡在某個(gè)狀態(tài)不跳多半是跳出條件不滿足——比如反饋信號(hào)一直沒(méi)到位如果狀態(tài)在反復(fù)橫跳多半是多個(gè)條件同時(shí)滿足、而CASE的優(yōu)先級(jí)和你預(yù)期的不一致。5.4 在線監(jiān)控看不到UDT數(shù)組里的成員S7-1200/1500默認(rèn)使用“優(yōu)化的塊訪問(wèn)”在這種模式下監(jiān)控表里雖然能展開UDT數(shù)組但某些版本的TIA Portal在監(jiān)控時(shí)會(huì)顯示為“下標(biāo)不可用”。我見過(guò)不少工程師以為程序?qū)戝e(cuò)了急得團(tuán)團(tuán)轉(zhuǎn)。解決辦法一是把該數(shù)據(jù)塊或FB的“優(yōu)化的塊訪問(wèn)”屬性取消改為非優(yōu)化訪問(wèn)二是直接把要監(jiān)控的成員單獨(dú)建一個(gè)監(jiān)控DB用AT覆蓋或賦值的方式把關(guān)鍵狀態(tài)引出來(lái)三是充分利用TIA Portal的“監(jiān)控表格”功能把表達(dá)式替換成PumpData[1].ST.Running這種完整路徑。不同品牌PLC三菱、歐姆龍的在線監(jiān)控方式各有差異但核心思路一致——監(jiān)控路徑要完整、訪問(wèn)屬性要匹配。5.5 命名規(guī)范一人一個(gè)風(fēng)格等于沒(méi)有規(guī)范模塊化程序最怕命名混亂。UDT叫MotorInfoFB叫FB_MOTOR變量名一會(huì)兒Pump1_State一會(huì)兒boPumpState這種程序即使結(jié)構(gòu)清晰讀起來(lái)依然費(fèi)勁。推薦的做法是建立一份項(xiàng)目級(jí)命名規(guī)范包含以下規(guī)則UDT命名用名詞Pump_UDT、Valve_UDT、Analog_UDT。FB命名用動(dòng)詞名詞FB_PumpControl、FB_ValveControl。變量名采用“設(shè)備名_信號(hào)含義”的格式比如Pump1_StartCmd、Pump1_FaultSig。注釋必須寫明功能狀態(tài)位還要標(biāo)注有效電平高電平有效/低電平有效模擬量要標(biāo)注工程量和單位。我見過(guò)一個(gè)泵站項(xiàng)目由于前人不寫注釋后面人接手后只能對(duì)著地址表猜變量含義平均每次故障排查要花三倍時(shí)間。程序是寫給后來(lái)的工程師包括三個(gè)月后的你自己看的不只是寫給PLC執(zhí)行的。結(jié)尾模塊化實(shí)踐這件事確實(shí)不是一蹴而就的。我個(gè)人的經(jīng)驗(yàn)是不要等到項(xiàng)目做大了才想起來(lái)重構(gòu)而是從下一個(gè)項(xiàng)目開始找一個(gè)最常出現(xiàn)的設(shè)備類型泵、閥門、電機(jī)都行先定義UDT再封裝FB哪怕一開始只有一兩個(gè)實(shí)例也要用調(diào)用的方式寫。你會(huì)發(fā)現(xiàn)前幾次封裝可能比直接寫梯形圖還慢因?yàn)橐伎冀涌?、狀態(tài)劃分、異常處理但一旦封裝完成后面就是復(fù)制-改參-實(shí)例化效率完全是另一回事。最后再分享一個(gè)小技巧把封裝好的UDT和FB放進(jìn)TIA Portal的“全局庫(kù)”中下次項(xiàng)目直接以“庫(kù)元素”的方式拖拽復(fù)用。這樣不僅能跨項(xiàng)目保持程序風(fēng)格一致還能把你自己沉淀下來(lái)的時(shí)間滯后邏輯、保護(hù)互鎖規(guī)則這些“項(xiàng)目魂”帶過(guò)去。用得越多庫(kù)越厚代碼質(zhì)量自然水漲船高。