網(wǎng)關(guān)掛64臺(tái)儀表:工業(yè)數(shù)據(jù)采集的架構(gòu)設(shè)計(jì)與實(shí)戰(zhàn))
1. 項(xiàng)目緣起與整體架構(gòu)思路一臺(tái)網(wǎng)關(guān)掛 64 臺(tái)儀表這個(gè)標(biāo)題第一次看到的時(shí)候我腦子里蹦出來(lái)的第一個(gè)反應(yīng)是這活兒要么是工業(yè)現(xiàn)場(chǎng)的老手在干要么就是被逼到墻角了。為什么這么說(shuō)因?yàn)榈沧鲞^(guò)設(shè)備接入的人都知道一臺(tái)網(wǎng)關(guān)下面掛幾臺(tái)、十幾臺(tái)設(shè)備是常規(guī)操作掛到 64 臺(tái)那基本就是踩在協(xié)議棧、內(nèi)存、輪詢周期、串口帶寬這幾條線的臨界點(diǎn)上了。先把這個(gè)項(xiàng)目的核心講清楚。所謂網(wǎng)關(guān)在這里指的是一個(gè)中間層設(shè)備或軟件服務(wù)它的職責(zé)是把下層的儀表數(shù)據(jù)采集上來(lái)再往上轉(zhuǎn)發(fā)給平臺(tái)、數(shù)據(jù)庫(kù)或者上位機(jī)。儀表可能是 Modbus RTU 的電表、溫控器、流量計(jì)也可能是 Modbus TCP、DL/T645、CJ/T188 這類協(xié)議的計(jì)量設(shè)備。64 臺(tái)這個(gè)數(shù)字不是隨便定的它往往來(lái)自一個(gè)具體的現(xiàn)場(chǎng)約束一個(gè)配電柜、一個(gè)泵房、一層樓、一個(gè)車間正好裝了這么多表而現(xiàn)場(chǎng)只允許放一臺(tái)網(wǎng)關(guān)。那為什么不讓每臺(tái)儀表自己聯(lián)網(wǎng)上報(bào)原因很現(xiàn)實(shí)。第一很多工業(yè)儀表的通信口是 RS485本身就是一個(gè)總線結(jié)構(gòu)物理上就是一條線串下來(lái)的你沒(méi)法讓每臺(tái)表獨(dú)立走網(wǎng)絡(luò)。第二現(xiàn)場(chǎng)布線成本高拉 64 條網(wǎng)線或者 64 個(gè) 4G 模塊成本直接翻幾十倍。第三運(yùn)維上一臺(tái)網(wǎng)關(guān)出問(wèn)題只需要查一個(gè)點(diǎn)64 臺(tái)設(shè)備各自聯(lián)網(wǎng)出問(wèn)題你要滿現(xiàn)場(chǎng)跑。所以一臺(tái)網(wǎng)關(guān)掛 64 臺(tái)儀表這個(gè)方案本質(zhì)上是用集中采集換布線成本和運(yùn)維復(fù)雜度這是它的核心價(jià)值。但集中采集帶來(lái)的代價(jià)也很明顯。RS485 是半雙工總線同一時(shí)刻只能有一個(gè)設(shè)備說(shuō)話64 臺(tái)表要輪流被問(wèn)一遍輪詢周期就會(huì)被拉長(zhǎng)。假設(shè)每臺(tái)表問(wèn)一次需要 50ms發(fā)送請(qǐng)求 等待響應(yīng) 處理間隔64 臺(tái)就是 3.2 秒一輪。如果現(xiàn)場(chǎng)要求 1 秒刷新一次數(shù)據(jù)那這個(gè)方案直接就不成立。所以整個(gè)項(xiàng)目的設(shè)計(jì)思路第一步不是選網(wǎng)關(guān)而是算清楚時(shí)間賬。我在實(shí)際項(xiàng)目里總結(jié)出一個(gè)粗略的估算公式你可以直接拿去用單輪總耗時(shí) ≈ 設(shè)備數(shù)量 × (請(qǐng)求幀時(shí)間 響應(yīng)幀時(shí)間 設(shè)備處理延時(shí) 主站間隔)其中請(qǐng)求幀時(shí)間和響應(yīng)幀時(shí)間跟波特率、字節(jié)數(shù)有關(guān)。以 9600bps、8N1 為例一個(gè)字節(jié) 10 位傳輸一個(gè)字節(jié)約 1.04ms。Modbus RTU 讀保持寄存器請(qǐng)求幀 8 字節(jié)響應(yīng)幀假設(shè) 10 字節(jié)加起來(lái) 18 字節(jié)光傳輸就約 18.7ms。再加上儀表內(nèi)部處理延時(shí)很多國(guó)產(chǎn)表在 10~30ms以及主站必須留的 3.5 字符靜默間隔9600bps 下約 3.6ms單臺(tái)表一輪實(shí)際耗時(shí)在 35~55ms 之間非常正常。64 臺(tái)按 45ms 算一輪就是 2.88 秒。這個(gè)數(shù)字決定了你后面所有的技術(shù)選型波特率要不要提到 19200 或 38400、輪詢策略要不要分組、網(wǎng)關(guān)的采集周期怎么設(shè)、緩存怎么做。很多人上來(lái)就問(wèn)用哪個(gè)網(wǎng)關(guān)我覺(jué)得順序反了應(yīng)該先算時(shí)間賬再定硬件。架構(gòu)上這個(gè)項(xiàng)目通常分三層現(xiàn)場(chǎng)儀表層64 臺(tái) RS485 設(shè)備、網(wǎng)關(guān)采集層協(xié)議轉(zhuǎn)換 數(shù)據(jù)緩存 上報(bào)、平臺(tái)應(yīng)用層數(shù)據(jù)庫(kù)、組態(tài)、告警。網(wǎng)關(guān)這一層是整個(gè)項(xiàng)目的咽喉它要同時(shí)處理串口收發(fā)、協(xié)議解析、多設(shè)備調(diào)度、數(shù)據(jù)打包上報(bào)任何一環(huán)卡住整條鏈路都會(huì)抖。所以我在設(shè)計(jì)時(shí)會(huì)把網(wǎng)關(guān)的職責(zé)壓到最窄只做采集和轉(zhuǎn)發(fā)不做復(fù)雜計(jì)算不做本地大屏把算力留給輪詢調(diào)度和緩存。還有一個(gè)容易被忽略的點(diǎn)64 臺(tái)儀表不一定都是同一種協(xié)議、同一個(gè)波特率?,F(xiàn)場(chǎng)經(jīng)常是電表走 DL/T645溫控器走 Modbus RTU流量計(jì)走 CJ/T188甚至有的表波特率是 2400有的是 9600。這種情況下一臺(tái)網(wǎng)關(guān)要掛 64 臺(tái)表就必須支持多串口 多協(xié)議 多波特率或者至少支持串口分組。這是選型時(shí)第一個(gè)要確認(rèn)的硬指標(biāo)后面我會(huì)詳細(xì)展開(kāi)。2. 核心細(xì)節(jié)解析與選型要點(diǎn)2.1 網(wǎng)關(guān)硬件選型的四個(gè)硬指標(biāo)選網(wǎng)關(guān)這件事參數(shù)表上寫得天花亂墜但真正決定能不能掛 64 臺(tái)的其實(shí)就四個(gè)指標(biāo)。我把它們按重要性排個(gè)序你在選型時(shí)可以直接拿這個(gè)清單去對(duì)。第一個(gè)是串口數(shù)量與類型。一臺(tái)網(wǎng)關(guān)如果只有一個(gè) RS485 口掛 64 臺(tái)表意味著全部設(shè)備共享一條總線。這條總線的帶寬是固定的波特率越高單臺(tái)響應(yīng)時(shí)間越短但總線負(fù)載也越高。我的經(jīng)驗(yàn)是單條 RS485 總線掛載設(shè)備不要超過(guò) 32 臺(tái)這是 RS485 收發(fā)器標(biāo)準(zhǔn)負(fù)載能力決定的1 個(gè)單位負(fù)載對(duì)應(yīng) 32 個(gè)標(biāo)準(zhǔn)負(fù)載。雖然現(xiàn)在很多芯片支持 1/8 負(fù)載理論上能掛 256 臺(tái)但實(shí)際現(xiàn)場(chǎng)線纜質(zhì)量、干擾、終端電阻匹配都會(huì)打折扣。所以掛 64 臺(tái)穩(wěn)妥的做法是至少兩個(gè) RS485 口每個(gè)口掛 32 臺(tái)或者四個(gè)口每個(gè)掛 16 臺(tái)。串口多了輪詢可以并行總周期直接縮短。第二個(gè)是 CPU 與內(nèi)存。64 臺(tái)設(shè)備的數(shù)據(jù)點(diǎn)假設(shè)每臺(tái)表讀 10 個(gè)寄存器就是 640 個(gè)數(shù)據(jù)點(diǎn)。網(wǎng)關(guān)要維護(hù)每個(gè)點(diǎn)的當(dāng)前值、時(shí)間戳、質(zhì)量碼還要做緩存隊(duì)列。如果內(nèi)存只有幾 MB跑起來(lái)會(huì)非常緊張。我一般建議網(wǎng)關(guān)至少 128MB 內(nèi)存起步CPU 主頻 400MHz 以上。別小看這個(gè)很多低端網(wǎng)關(guān)標(biāo)稱支持 128 臺(tái)設(shè)備實(shí)際掛到 40 臺(tái)就開(kāi)始丟包原因就是內(nèi)存不夠緩存隊(duì)列溢出。第三個(gè)是協(xié)議庫(kù)的完整性。網(wǎng)關(guān)內(nèi)置的協(xié)議庫(kù)決定了你能接哪些表。Modbus RTU/TCP 是基礎(chǔ)DL/T645、CJ/T188、IEC104 這些要看現(xiàn)場(chǎng)。更重要的是協(xié)議庫(kù)要支持自定義寄存器地址映射因?yàn)椴煌瑥S家的表寄存器地址定義千差萬(wàn)別沒(méi)有自定義映射能力你只能干瞪眼。第四個(gè)是上報(bào)通道與斷線緩存。網(wǎng)關(guān)采集上來(lái)的數(shù)據(jù)要往上送通道可能是以太網(wǎng)、4G、WiFi。64 臺(tái)表的數(shù)據(jù)量不大但頻率高。如果上報(bào)通道斷了網(wǎng)關(guān)必須能把數(shù)據(jù)緩存到本地等通道恢復(fù)后補(bǔ)傳。這個(gè)斷線緩存能力是區(qū)分工業(yè)級(jí)網(wǎng)關(guān)和玩具網(wǎng)關(guān)的分水嶺。緩存深度至少要能存 24 小時(shí)的數(shù)據(jù)按 640 個(gè)點(diǎn)、每分鐘存一次算一天就是 92 萬(wàn)條記錄對(duì)存儲(chǔ)和索引是個(gè)考驗(yàn)。下面這張表是我實(shí)際選型時(shí)用的對(duì)比框架你可以參考指標(biāo)最低要求推薦配置不達(dá)標(biāo)的后果串口數(shù)量2 路 RS4854 路 RS485 1 路 RS232單總線負(fù)載過(guò)高丟包率上升內(nèi)存128MB512MB緩存溢出數(shù)據(jù)丟失CPU 主頻400MHz800MHz 雙核輪詢調(diào)度卡頓周期抖動(dòng)協(xié)議庫(kù)Modbus RTU/TCP含 DL/T645、CJ/T188部分儀表無(wú)法接入斷線緩存8 小時(shí)72 小時(shí)以上通道中斷期間數(shù)據(jù)永久丟失工作溫度-20~60℃-40~70℃現(xiàn)場(chǎng)高溫或低溫死機(jī)2.2 輪詢策略為什么不能簡(jiǎn)單輪著問(wèn)很多人寫采集程序第一版都是for 循環(huán)遍歷 64 臺(tái)設(shè)備挨個(gè)問(wèn)一遍。這個(gè)寫法在實(shí)驗(yàn)室能跑到現(xiàn)場(chǎng)必掛。原因有三個(gè)。第一沒(méi)有超時(shí)保護(hù)。如果第 17 臺(tái)表壞了或者線松了主站發(fā)請(qǐng)求后一直等響應(yīng)默認(rèn)超時(shí)可能是 1 秒甚至更長(zhǎng)。這一臺(tái)卡住后面 47 臺(tái)全部排隊(duì)等著整個(gè)輪詢周期被一臺(tái)壞表拖垮。所以必須給每臺(tái)設(shè)備設(shè)獨(dú)立的超時(shí)時(shí)間一般 200~500ms超時(shí)就跳過(guò)標(biāo)記該設(shè)備離線下一輪再試。第二沒(méi)有優(yōu)先級(jí)。64 臺(tái)表里有些是關(guān)鍵設(shè)備比如總電表、主控溫控器有些是輔助設(shè)備。如果一視同仁地輪詢關(guān)鍵數(shù)據(jù)的刷新率上不去。我的做法是把設(shè)備分成三組高頻組10 秒一次、中頻組30 秒一次、低頻組60 秒一次。高頻組設(shè)備少插在每輪輪詢的前面保證優(yōu)先響應(yīng)。第三沒(méi)有失敗重試與退避。設(shè)備偶爾通信失敗是正常的可能是干擾可能是總線沖突。如果失敗后立即重試可能連續(xù)失敗浪費(fèi)總線時(shí)間。合理的做法是失敗后標(biāo)記下一輪再試連續(xù)失敗 3 次才判定離線。這樣既不會(huì)誤判也不會(huì)因?yàn)橹卦囃下w節(jié)奏。我實(shí)際用的一套輪詢調(diào)度邏輯是這樣的主循環(huán)維護(hù)一個(gè)設(shè)備隊(duì)列每輪從隊(duì)列頭取設(shè)備發(fā)送請(qǐng)求等待響應(yīng)。響應(yīng)成功則更新數(shù)據(jù)響應(yīng)失敗則把設(shè)備移到隊(duì)列尾部并增加失敗計(jì)數(shù)。同時(shí)維護(hù)一個(gè)下次采集時(shí)間字段高頻設(shè)備即使被移到尾部也會(huì)因?yàn)闀r(shí)間到期被優(yōu)先插回隊(duì)首。這套邏輯用狀態(tài)機(jī)實(shí)現(xiàn)比簡(jiǎn)單的 for 循環(huán)復(fù)雜但穩(wěn)定性完全不是一個(gè)級(jí)別。2.3 數(shù)據(jù)點(diǎn)表設(shè)計(jì)64 臺(tái)表怎么管64 臺(tái)表每臺(tái)表的數(shù)據(jù)點(diǎn)怎么組織這是項(xiàng)目能不能維護(hù)的關(guān)鍵。我見(jiàn)過(guò)最糟糕的做法是代碼里硬編碼每臺(tái)表的寄存器地址64 臺(tái)表就是 64 段 if-else。這種代碼加一臺(tái)表要改代碼改一個(gè)地址要重新編譯維護(hù)成本極高。正確的做法是數(shù)據(jù)點(diǎn)表驅(qū)動(dòng)。把所有設(shè)備的信息、每個(gè)數(shù)據(jù)點(diǎn)的寄存器地址、數(shù)據(jù)類型、縮放系數(shù)、單位全部配置化。程序啟動(dòng)時(shí)讀取配置動(dòng)態(tài)生成采集任務(wù)。這樣加設(shè)備、改地址只需要改配置文件不用動(dòng)代碼。一個(gè)典型的數(shù)據(jù)點(diǎn)表配置我一般用 JSON 或 CSV 存字段包括設(shè)備 ID、設(shè)備名稱、協(xié)議類型、串口號(hào)、從站地址、波特率、數(shù)據(jù)點(diǎn)名稱、寄存器地址、寄存器數(shù)量、數(shù)據(jù)類型如 uint16、int32、float、字節(jié)序大端/小端、縮放系數(shù)、單位、采集頻率。下面是一個(gè)簡(jiǎn)化示例{ device_id: METER_001, device_name: 1號(hào)進(jìn)線電表, protocol: DLT645, port: /dev/ttyS0, slave_addr: 000000000001, baudrate: 2400, points: [ { name: 有功總電能, addr: 00000000, data_type: bcd, scale: 0.01, unit: kWh, interval: 60 }, { name: A相電壓, addr: 02010100, data_type: bcd, scale: 0.1, unit: V, interval: 10 } ] }這個(gè)配置看起來(lái)簡(jiǎn)單但里面有幾個(gè)坑。字節(jié)序是最容易出錯(cuò)的Modbus 設(shè)備有的用大端有的用小端32 位浮點(diǎn)數(shù)還有 ABCD、CDAB、BADC、DCBA 四種排列。我踩過(guò)的坑是同一批表前 10 臺(tái)是 ABCD后 10 臺(tái)是 CDAB讀出來(lái)的數(shù)全是亂的。解決辦法是在配置里明確寫字節(jié)序并且用已知值驗(yàn)證??s放系數(shù)也要注意很多表讀出來(lái)是整數(shù)需要乘 0.1 或 0.01 才是真實(shí)值配置里不寫清楚平臺(tái)顯示的數(shù)據(jù)會(huì)差 10 倍。2.4 串口參數(shù)與總線匹配RS485 總線能不能穩(wěn)定掛 32 臺(tái)設(shè)備串口參數(shù)和物理層匹配很關(guān)鍵。我列幾個(gè)實(shí)際調(diào)試中必須確認(rèn)的點(diǎn)。波特率9600 是最常見(jiàn)的但掛 64 臺(tái)表時(shí)如果時(shí)間賬算下來(lái)不夠就要考慮提到 19200 或 38400。提波特率的前提是所有設(shè)備都支持而且線纜質(zhì)量要跟得上。線太長(zhǎng)、線徑太細(xì)、屏蔽不好高波特率下誤碼率會(huì)飆升。我的經(jīng)驗(yàn)是總線長(zhǎng)度超過(guò) 500 米波特率不要超過(guò) 9600超過(guò) 1000 米降到 4800 甚至 2400。終端電阻RS485 總線兩端必須接 120Ω 終端電阻中間設(shè)備不接。很多現(xiàn)場(chǎng)忘記接或者每個(gè)設(shè)備都接導(dǎo)致總線負(fù)載異常通信時(shí)好時(shí)壞。這個(gè)細(xì)節(jié)看起來(lái)小但實(shí)際排查時(shí)經(jīng)常是罪魁禍?zhǔn)?。偏置電阻總線空閑時(shí)如果沒(méi)有偏置差分電壓可能處于不確定狀態(tài)導(dǎo)致誤觸發(fā)。一般主站端加一對(duì)偏置電阻上拉和下拉保證空閑時(shí) A 線高于 B 線。接地與屏蔽RS485 用雙絞屏蔽線屏蔽層單端接地不要兩端都接否則形成地環(huán)路干擾更大。這個(gè)在強(qiáng)電環(huán)境里尤其重要我見(jiàn)過(guò)變頻器一啟動(dòng)通信就斷的情況最后查出來(lái)是屏蔽層兩端接地。共地問(wèn)題如果 64 臺(tái)表分布在不同配電柜地電位可能不同。地電位差超過(guò) RS485 收發(fā)器的共模范圍-7V~12V通信就會(huì)出錯(cuò)。解決辦法是用隔離型 RS485 收發(fā)器或者加中繼器分段。這個(gè)坑很深現(xiàn)場(chǎng)表現(xiàn)是白天正常晚上出錯(cuò)或者某幾臺(tái)表時(shí)好時(shí)壞本質(zhì)是地電位漂移。3. 實(shí)操過(guò)程與核心環(huán)節(jié)實(shí)現(xiàn)3.1 現(xiàn)場(chǎng)勘查與時(shí)間賬計(jì)算項(xiàng)目開(kāi)始前我一定會(huì)做一次現(xiàn)場(chǎng)勘查把下面這些信息全部記下來(lái)儀表品牌型號(hào)、通信協(xié)議、從站地址、波特率、數(shù)據(jù)點(diǎn)清單、寄存器地址、安裝位置、線纜走向、配電柜分布、供電情況。這些信息不全后面配置就是盲人摸象??辈橥曛蟮谝患率撬銜r(shí)間賬。我拿一個(gè)真實(shí)項(xiàng)目舉例64 臺(tái) Modbus RTU 電表波特率 9600每臺(tái)讀 8 個(gè)保持寄存器。請(qǐng)求幀 8 字節(jié)響應(yīng)幀 8 字節(jié)數(shù)據(jù) 5 字節(jié)頭尾 13 字節(jié)合計(jì) 21 字節(jié)。9600bps 下21 字節(jié)傳輸時(shí)間約 21.9ms。儀表處理延時(shí)實(shí)測(cè)平均 15ms。主站間隔留 5ms。單臺(tái)耗時(shí)約 42ms。64 臺(tái)一輪 2.69 秒?,F(xiàn)場(chǎng)要求 5 秒刷新一次2.69 秒 5 秒時(shí)間賬通過(guò)。但如果要求 2 秒刷新就不夠了。這時(shí)候有三個(gè)選擇提波特率到 19200單臺(tái)耗時(shí)降到約 26ms一輪 1.66 秒、分兩個(gè)串口并行一輪 1.35 秒、減少單次讀取的寄存器數(shù)量。我一般優(yōu)先選分串口因?yàn)樘岵ㄌ芈适芫€纜限制減寄存器數(shù)量受業(yè)務(wù)限制分串口最穩(wěn)妥。3.2 網(wǎng)關(guān)配置與協(xié)議映射網(wǎng)關(guān)到手后第一步是配置串口參數(shù)。以四串口網(wǎng)關(guān)為例我把 64 臺(tái)表分成四組每組 16 臺(tái)分別接在 ttyS0~ttyS3 上。每組波特率根據(jù)設(shè)備實(shí)際情況設(shè)置如果同組設(shè)備波特率不一致就要再拆組。這里有個(gè)原則同一串口下的設(shè)備協(xié)議和波特率必須一致否則輪詢邏輯會(huì)非常復(fù)雜。配置完串口接下來(lái)是設(shè)備添加。每臺(tái)設(shè)備需要填設(shè)備名稱、從站地址、協(xié)議類型、超時(shí)時(shí)間、重試次數(shù)、采集周期。從站地址不能重復(fù)這是 Modbus 的基本要求。我見(jiàn)過(guò)現(xiàn)場(chǎng)兩臺(tái)表地址都是 1結(jié)果讀出來(lái)的數(shù)據(jù)一直跳查了半天才發(fā)現(xiàn)地址沖突。然后是數(shù)據(jù)點(diǎn)映射。這是最繁瑣的一步64 臺(tái)表每臺(tái) 8~10 個(gè)點(diǎn)就是 500 多個(gè)數(shù)據(jù)點(diǎn)。我的做法是先用表格整理再批量導(dǎo)入。表格里每行一個(gè)點(diǎn)包含設(shè)備 ID、點(diǎn)名稱、寄存器地址、數(shù)據(jù)類型、縮放、單位。整理好后用腳本生成網(wǎng)關(guān)配置文件避免手工一個(gè)個(gè)填。協(xié)議映射里最容易出錯(cuò)的是寄存器地址的偏移。Modbus 協(xié)議里寄存器地址有 0 基和 1 基的區(qū)別。有的設(shè)備文檔寫 40001實(shí)際對(duì)應(yīng)寄存器 0有的寫 0實(shí)際對(duì)應(yīng) 40001。這個(gè)不確認(rèn)清楚讀出來(lái)的數(shù)據(jù)全是錯(cuò)的。我的經(jīng)驗(yàn)是拿一個(gè)已知值去驗(yàn)證比如讀電壓用萬(wàn)用表量一下實(shí)際值對(duì)比讀出來(lái)的數(shù)對(duì)不上就調(diào)偏移。3.3 輪詢調(diào)度實(shí)現(xiàn)與參數(shù)調(diào)優(yōu)輪詢調(diào)度的核心是一個(gè)狀態(tài)機(jī)。我用偽代碼描述一下邏輯class PollingScheduler: def __init__(self, devices): self.devices devices self.queue deque(devices) self.fail_count {d.id: 0 for d in devices} def run(self): while True: device self.queue.popleft() if not self.is_due(device): self.queue.append(device) continue result self.poll(device) if result.success: self.update_data(device, result.data) self.fail_count[device.id] 0 else: self.fail_count[device.id] 1 if self.fail_count[device.id] 3: self.mark_offline(device) self.queue.append(device) time.sleep(0.005) # 主站間隔 def poll(self, device): try: response send_request(device, timeoutdevice.timeout) return parse_response(response) except TimeoutError: return PollResult(successFalse)這個(gè)邏輯看起來(lái)簡(jiǎn)單但參數(shù)調(diào)優(yōu)很講究。超時(shí)時(shí)間設(shè)多少太短正常設(shè)備也會(huì)超時(shí)太長(zhǎng)壞設(shè)備拖慢整體。我的經(jīng)驗(yàn)是超時(shí)時(shí)間設(shè)為正常響應(yīng)時(shí)間的 3 倍。比如正常響應(yīng) 40ms超時(shí)設(shè) 120ms。重試次數(shù)設(shè)多少我一般設(shè) 2 次第一次失敗后立即重試一次還失敗就跳過(guò)下一輪再試。連續(xù) 3 輪失敗才判離線。主站間隔設(shè)多少M(fèi)odbus RTU 要求 3.5 字符靜默9600bps 下約 3.6ms我一般設(shè) 5ms留點(diǎn)余量。調(diào)優(yōu)的時(shí)候我會(huì)用網(wǎng)關(guān)的調(diào)試日志看每臺(tái)設(shè)備的實(shí)際響應(yīng)時(shí)間找出響應(yīng)特別慢的設(shè)備。有些老表響應(yīng)要 100ms 以上這種表如果數(shù)量多就要單獨(dú)分組避免拖慢整體。我還遇到過(guò)一臺(tái)表平時(shí)響應(yīng) 30ms但每隔 10 分鐘會(huì)卡頓 2 秒查出來(lái)是表內(nèi)部在做數(shù)據(jù)存儲(chǔ)這種表只能降低采集頻率或者接受偶爾的超時(shí)。3.4 數(shù)據(jù)上報(bào)與斷線緩存采集上來(lái)的數(shù)據(jù)要往上送。上報(bào)通道我一般用以太網(wǎng)走 MQTT 或 Modbus TCP。MQTT 的好處是支持?jǐn)嗑€重連和 QoS適合不穩(wěn)定網(wǎng)絡(luò)。上報(bào)格式用 JSON每個(gè)點(diǎn)包含設(shè)備 ID、點(diǎn)名稱、值、時(shí)間戳、質(zhì)量碼。斷線緩存是重點(diǎn)。網(wǎng)關(guān)本地要有一個(gè)環(huán)形緩沖區(qū)通道正常時(shí)數(shù)據(jù)直接發(fā)走通道斷了數(shù)據(jù)寫入緩沖區(qū)。緩沖區(qū)滿了覆蓋最舊的數(shù)據(jù)。通道恢復(fù)后按時(shí)間順序補(bǔ)傳。補(bǔ)傳的時(shí)候要注意限速不要一次性把幾萬(wàn)條數(shù)據(jù)全推上去把平臺(tái)打掛。我的做法是每秒補(bǔ)傳 100 條慢慢追。緩存深度怎么定按最壞情況算通道斷 24 小時(shí)640 個(gè)點(diǎn)每分鐘存一次就是 92 萬(wàn)條。每條 JSON 約 200 字節(jié)總共約 184MB。所以網(wǎng)關(guān)存儲(chǔ)至少要 256MB 可用空間。如果存儲(chǔ)不夠就要降低緩存頻率比如斷線時(shí)只存關(guān)鍵點(diǎn)或者只存變化的數(shù)據(jù)。3.5 現(xiàn)場(chǎng)調(diào)試與驗(yàn)證現(xiàn)場(chǎng)調(diào)試是整個(gè)項(xiàng)目最耗時(shí)的環(huán)節(jié)。我的調(diào)試順序是先單臺(tái)設(shè)備調(diào)通再一組設(shè)備調(diào)通最后全部 64 臺(tái)一起跑。單臺(tái)調(diào)試時(shí)用串口調(diào)試工具直接發(fā)請(qǐng)求看響應(yīng)是否正確。這一步確認(rèn)協(xié)議、地址、字節(jié)序、縮放都對(duì)。一組調(diào)試時(shí)把同組設(shè)備接上總線跑輪詢程序看是否有沖突、丟包。全部調(diào)試時(shí)64 臺(tái)一起跑觀察輪詢周期、丟包率、CPU 占用、內(nèi)存占用。驗(yàn)證的時(shí)候我會(huì)做幾個(gè)測(cè)試拔線測(cè)試拔掉一臺(tái)設(shè)備的線看程序是否能正確標(biāo)記離線其他設(shè)備是否不受影響干擾測(cè)試在總線附近啟動(dòng)變頻器看通信是否出錯(cuò)斷電測(cè)試網(wǎng)關(guān)斷電重啟看配置是否保存數(shù)據(jù)是否恢復(fù)壓力測(cè)試把采集頻率提高一倍看網(wǎng)關(guān)是否能扛住。這些測(cè)試做完基本能保證現(xiàn)場(chǎng)穩(wěn)定運(yùn)行。我實(shí)際項(xiàng)目里64 臺(tái)表跑起來(lái)輪詢周期穩(wěn)定在 2.8 秒左右丟包率低于 0.1%CPU 占用 30%內(nèi)存占用 40%。這個(gè)狀態(tài)可以長(zhǎng)期運(yùn)行。4. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄4.1 通信類問(wèn)題速查表現(xiàn)場(chǎng)通信問(wèn)題千奇百怪但歸納起來(lái)就那么幾類。我整理了一張速查表遇到問(wèn)題按這個(gè)順序排查能省很多時(shí)間?,F(xiàn)象可能原因排查方法解決辦法全部設(shè)備通信失敗串口參數(shù)錯(cuò)誤、線接反檢查波特率、數(shù)據(jù)位、停止位、校驗(yàn)位用萬(wàn)用表測(cè) A/B 線改正參數(shù)交換 A/B 線部分設(shè)備通信失敗地址沖突、設(shè)備故障、線松逐個(gè)斷開(kāi)設(shè)備測(cè)試檢查從站地址改地址更換設(shè)備緊固接線通信時(shí)好時(shí)壞干擾、地電位差、終端電阻檢查屏蔽層接地測(cè)量地電位差檢查終端電阻單端接地加隔離器補(bǔ)終端電阻數(shù)據(jù)跳變字節(jié)序錯(cuò)誤、縮放錯(cuò)誤用已知值驗(yàn)證檢查字節(jié)序配置改正字節(jié)序調(diào)整縮放系數(shù)響應(yīng)超時(shí)設(shè)備處理慢、總線負(fù)載高看調(diào)試日志測(cè)單臺(tái)響應(yīng)時(shí)間提高超時(shí)分串口降頻率輪詢周期越來(lái)越長(zhǎng)壞設(shè)備拖累、重試過(guò)多看每臺(tái)設(shè)備響應(yīng)時(shí)間看失敗計(jì)數(shù)隔離壞設(shè)備減少重試這張表是我踩了無(wú)數(shù)坑總結(jié)出來(lái)的尤其是通信時(shí)好時(shí)壞這一條我遇到過(guò)三次每次原因都不一樣。第一次是屏蔽層兩端接地第二次是地電位差第三次是終端電阻沒(méi)接。所以遇到這類問(wèn)題一定要按順序排查不要跳步。4.2 那些文檔里不會(huì)寫的坑坑一從站地址 0 和 255 不能用。Modbus 協(xié)議里地址 0 是廣播地址255 是保留地址實(shí)際設(shè)備不能用。我見(jiàn)過(guò)有人把設(shè)備地址設(shè)成 0結(jié)果所有設(shè)備同時(shí)響應(yīng)總線直接癱瘓。這個(gè)坑在設(shè)備手冊(cè)里往往不寫但實(shí)際調(diào)試時(shí)一定要注意??佣ㄌ芈?2400 下的時(shí)間賬。有些老表只支持 2400bps這個(gè)波特率下一個(gè)字節(jié)傳輸時(shí)間約 4.17ms。讀 8 個(gè)寄存器請(qǐng)求加響應(yīng) 21 字節(jié)光傳輸就 87ms。64 臺(tái)表一輪就是 5.6 秒。如果現(xiàn)場(chǎng)要求 5 秒刷新這個(gè)方案直接不成立。所以選型時(shí)一定要確認(rèn)波特率2400 的設(shè)備數(shù)量不能多。坑三網(wǎng)關(guān)的支持 128 臺(tái)設(shè)備是理論值。很多網(wǎng)關(guān)標(biāo)稱支持 128 臺(tái)甚至 256 臺(tái)但那是理想條件下的數(shù)字。實(shí)際掛載時(shí)受內(nèi)存、CPU、總線負(fù)載限制能穩(wěn)定跑 64 臺(tái)就不錯(cuò)了。我一般按標(biāo)稱值的 50% 來(lái)規(guī)劃標(biāo)稱 128 臺(tái)實(shí)際按 64 臺(tái)設(shè)計(jì)留足余量??铀臄?shù)據(jù)點(diǎn)表的維護(hù)成本被低估。64 臺(tái)表500 多個(gè)點(diǎn)如果配置管理不好后期加一臺(tái)表、改一個(gè)地址都要花半天。我的做法是用 Excel 管理數(shù)據(jù)點(diǎn)表用腳本生成網(wǎng)關(guān)配置用版本控制管理變更。這樣加設(shè)備只需要改 Excel跑一下腳本配置就更新了。坑五現(xiàn)場(chǎng)電磁干擾比實(shí)驗(yàn)室嚴(yán)重得多。實(shí)驗(yàn)室里跑得好好的程序到現(xiàn)場(chǎng)可能頻繁丟包。原因是現(xiàn)場(chǎng)有變頻器、接觸器、大功率電機(jī)電磁干擾強(qiáng)。解決辦法是用屏蔽雙絞線、加磁環(huán)、隔離收發(fā)器、遠(yuǎn)離干擾源。我有個(gè)項(xiàng)目通信一直不穩(wěn)最后發(fā)現(xiàn)是網(wǎng)關(guān)和變頻器共用一個(gè)配電柜把網(wǎng)關(guān)移到另一個(gè)柜子問(wèn)題就解決了。4.3 性能瓶頸的定位方法64 臺(tái)表跑起來(lái)后如果發(fā)現(xiàn)輪詢周期變長(zhǎng)、數(shù)據(jù)延遲大怎么定位瓶頸我的方法是分段計(jì)時(shí)。在輪詢程序里加時(shí)間戳記錄每個(gè)環(huán)節(jié)的耗時(shí)發(fā)送請(qǐng)求耗時(shí)、等待響應(yīng)耗時(shí)、解析響應(yīng)耗時(shí)、更新數(shù)據(jù)耗時(shí)、上報(bào)耗時(shí)。跑一段時(shí)間后統(tǒng)計(jì)各環(huán)節(jié)的平均值和最大值。如果等待響應(yīng)耗時(shí)占比高說(shuō)明是設(shè)備或總線問(wèn)題如果解析耗時(shí)高說(shuō)明是 CPU 問(wèn)題如果上報(bào)耗時(shí)高說(shuō)明是通道問(wèn)題。我實(shí)際遇到過(guò)一次輪詢周期從 2.8 秒慢慢漲到 8 秒查日志發(fā)現(xiàn)是上報(bào)環(huán)節(jié)卡住。原因是 MQTT 通道斷了程序在同步等待重連阻塞了輪詢線程。解決辦法是把上報(bào)改成異步用獨(dú)立線程處理輪詢線程只管采集數(shù)據(jù)寫入隊(duì)列上報(bào)線程從隊(duì)列取數(shù)據(jù)發(fā)送。這樣即使上報(bào)通道斷了輪詢也不受影響。4.4 長(zhǎng)期運(yùn)行的穩(wěn)定性保障64 臺(tái)表的項(xiàng)目不是跑一天兩天而是要跑幾年。長(zhǎng)期運(yùn)行的穩(wěn)定性靠的是幾個(gè)細(xì)節(jié)??撮T狗網(wǎng)關(guān)要有硬件看門狗程序卡死時(shí)自動(dòng)重啟。軟件層面也要有守護(hù)進(jìn)程監(jiān)控采集程序掛了就拉起來(lái)。日志輪轉(zhuǎn)調(diào)試日志不能無(wú)限增長(zhǎng)要按天或按大小輪轉(zhuǎn)保留最近 7 天。日志寫滿磁盤程序會(huì)崩。配置備份網(wǎng)關(guān)配置要定期備份到本地和遠(yuǎn)程。網(wǎng)關(guān)壞了換一臺(tái)導(dǎo)入配置就能恢復(fù)。固件更新網(wǎng)關(guān)固件要定期更新修復(fù)已知問(wèn)題。但更新前要在測(cè)試環(huán)境驗(yàn)證不要直接在生產(chǎn)環(huán)境更新。定期巡檢每月檢查一次網(wǎng)關(guān)狀態(tài)看 CPU、內(nèi)存、磁盤、通信質(zhì)量。發(fā)現(xiàn)問(wèn)題提前處理不要等出事。我有個(gè)項(xiàng)目網(wǎng)關(guān)連續(xù)跑了兩年多中間只重啟過(guò)兩次一次是固件更新一次是機(jī)房斷電。能做到這個(gè)穩(wěn)定性靠的就是上面這些細(xì)節(jié)。尤其是看門狗和日志輪轉(zhuǎn)看起來(lái)不起眼但關(guān)鍵時(shí)刻能救命。4.5 擴(kuò)展性設(shè)計(jì)從 64 臺(tái)到更多項(xiàng)目做完往往會(huì)有擴(kuò)展需求。今天掛 64 臺(tái)明天可能加到 128 臺(tái)。所以設(shè)計(jì)時(shí)就要考慮擴(kuò)展性。串口擴(kuò)展網(wǎng)關(guān)串口不夠可以加串口服務(wù)器把網(wǎng)絡(luò)轉(zhuǎn)成串口。或者用多個(gè)網(wǎng)關(guān)每個(gè)網(wǎng)關(guān)負(fù)責(zé)一片區(qū)域平臺(tái)層做數(shù)據(jù)匯聚。協(xié)議擴(kuò)展新設(shè)備接入如果協(xié)議不同網(wǎng)關(guān)要支持多協(xié)議。選型時(shí)確認(rèn)網(wǎng)關(guān)是否支持協(xié)議插件能否自定義協(xié)議。平臺(tái)擴(kuò)展數(shù)據(jù)量大了平臺(tái)層要能水平擴(kuò)展。數(shù)據(jù)庫(kù)分表、消息隊(duì)列削峰、緩存加速這些都要提前規(guī)劃。我的經(jīng)驗(yàn)是設(shè)計(jì)容量按實(shí)際需求的 1.5 倍來(lái)?,F(xiàn)在 64 臺(tái)就按 96 臺(tái)設(shè)計(jì)。這樣加設(shè)備時(shí)不用重新選型直接加就行。多花的成本不多但省下的麻煩很多。4.6 成本與工期的現(xiàn)實(shí)考量最后說(shuō)點(diǎn)現(xiàn)實(shí)的。一臺(tái)網(wǎng)關(guān)掛 64 臺(tái)儀表成本不只是網(wǎng)關(guān)本身。還有線纜、端子、配電柜、施工、調(diào)試。我算過(guò)一個(gè)賬64 臺(tái)表如果用 4 串口網(wǎng)關(guān)網(wǎng)關(guān)成本約 2000 元線纜按每臺(tái)表 20 米算1280 米雙絞屏蔽線約 3000 元端子、配電柜、施工約 5000 元調(diào)試人工按 5 天算約 5000 元??傆?jì)約 1.5 萬(wàn)元。如果改成每臺(tái)表獨(dú)立聯(lián)網(wǎng)64 個(gè) 4G 模塊每個(gè) 200 元就是 1.28 萬(wàn)元加上平臺(tái)側(cè) 64 個(gè)連接的管理成本實(shí)際更高。所以集中采集在成本上是有優(yōu)勢(shì)的。工期上現(xiàn)場(chǎng)勘查 1 天網(wǎng)關(guān)配置 1 天現(xiàn)場(chǎng)調(diào)試 3 天總共 5 天左右。如果現(xiàn)場(chǎng)條件復(fù)雜比如線纜要重新敷設(shè)工期會(huì)拉長(zhǎng)到 10 天以上。所以項(xiàng)目排期時(shí)一定要留足調(diào)試時(shí)間不要壓縮。我個(gè)人在實(shí)際操作中的體會(huì)是這個(gè)項(xiàng)目最難的不是技術(shù)而是現(xiàn)場(chǎng)的不確定性。儀表型號(hào)可能和清單不一致線纜可能不通地址可能沖突干擾可能超標(biāo)。所以做這個(gè)項(xiàng)目技術(shù)方案要扎實(shí)但現(xiàn)場(chǎng)應(yīng)變能力更重要。遇到問(wèn)題不要慌按排查表一步步來(lái)總能找到原因。