器人TCP瞬時(shí)中斷重連實(shí)戰(zhàn):工業(yè)通信可靠性排障指南)
1. 項(xiàng)目背景與核心痛點(diǎn)為什么TCP中斷在泰國(guó)工廠是個(gè)“隱形炸彈”1.1 這個(gè)項(xiàng)目到底解決什么問(wèn)題先別急著談技術(shù)細(xì)節(jié)我們得先把場(chǎng)景擺清楚。我在泰國(guó)某汽車零部件廠做產(chǎn)線集成時(shí)遇到過(guò)這樣一個(gè)情況產(chǎn)線上有一臺(tái)FANUC弧焊機(jī)器人負(fù)責(zé)把車身底板的兩塊鈑金件焊接在一起。機(jī)器人本體和上位機(jī)一個(gè)簡(jiǎn)單的MES數(shù)據(jù)采集系統(tǒng)之間走的是TCP/IP通信上位機(jī)每隔500毫秒向機(jī)器人請(qǐng)求一次當(dāng)前坐標(biāo)、電流、電壓等焊接參數(shù)用于質(zhì)量追溯。問(wèn)題出在突然的瞬間中斷上。生產(chǎn)節(jié)拍是42秒一件每天兩班倒輻射出去就是上千個(gè)焊點(diǎn)。TCP連接時(shí)不時(shí)地?cái)嘁幌轮剡B成功后一切恢復(fù)正常但中間那幾秒鐘的數(shù)據(jù)就丟了。最要命的是這種中斷還不是固定周期出現(xiàn)完全隨機(jī)。排查了一周從交換機(jī)、網(wǎng)線、防火墻一路查到機(jī)器人控制器的通訊板卡和程序邏輯最后才把問(wèn)題定位清楚。這個(gè)標(biāo)題里說(shuō)到的“瞬時(shí)中斷重連”本質(zhì)上就是一個(gè)典型的工業(yè)物聯(lián)網(wǎng)通信可靠性問(wèn)題。它的難點(diǎn)不在于“TCP理論”有多深而在于工業(yè)現(xiàn)場(chǎng)環(huán)境極其復(fù)雜干擾源多、設(shè)備品牌雜、網(wǎng)絡(luò)拓?fù)鋪y稍不注意就會(huì)踩坑。如果你也在搞機(jī)器人聯(lián)網(wǎng)、數(shù)據(jù)采集、設(shè)備互聯(lián)這類項(xiàng)目這篇文章的經(jīng)驗(yàn)應(yīng)該能幫你少走不少?gòu)澛贰?.2 適合誰(shuí)來(lái)參考這篇復(fù)盤先說(shuō)一下這篇內(nèi)容適合哪幾類朋友正在做FANUC機(jī)器人上位機(jī)通信開發(fā)的工程師用的可能是Socket、TCP客戶端這類常規(guī)方案負(fù)責(zé)產(chǎn)線信息化改造MES、SCADA、數(shù)據(jù)追溯的集成商技術(shù)負(fù)責(zé)人維護(hù)老舊產(chǎn)線、機(jī)器人經(jīng)?!暗艟€”又找不到原因的現(xiàn)場(chǎng)設(shè)備工程師剛?cè)腴T工業(yè)通信想了解TCP重連機(jī)制在真實(shí)場(chǎng)景中怎么落地的新人。這篇文章不是教科書式的TCP教程而是一份實(shí)戰(zhàn)記錄。里面涉及代碼思路、參數(shù)設(shè)置、現(xiàn)場(chǎng)排障手段、還有一些只有踩過(guò)坑才知道的細(xì)節(jié)。我會(huì)盡量把“為什么這樣選”講清楚而不是只給結(jié)論。2. 整體方案設(shè)計(jì)先理清鏈路再談重連策略2.1 通信鏈路長(zhǎng)什么樣在動(dòng)手寫代碼之前第一步永遠(yuǎn)是把通信鏈路畫出來(lái)。這個(gè)環(huán)節(jié)看著簡(jiǎn)單但大部分現(xiàn)場(chǎng)問(wèn)題都出在“你以為的鏈路”和“實(shí)際的鏈路”不一樣。我當(dāng)時(shí)這條鏈路的實(shí)際構(gòu)成是FANUC R-30iB Plus控制器內(nèi)置以太網(wǎng)端口機(jī)器人端運(yùn)行TPTeach Pendant程序通過(guò)Socket通信指令主動(dòng)連接上位機(jī)上位機(jī)是一臺(tái)工控機(jī)安裝自研的C#采集服務(wù)中間經(jīng)過(guò)一臺(tái)工業(yè)交換機(jī)還跨了一個(gè)車間級(jí)的環(huán)網(wǎng)網(wǎng)線走的是橋架和380V動(dòng)力電纜在同一個(gè)橋架里走了大概15米。這里有個(gè)關(guān)鍵點(diǎn)FANUC機(jī)器人的Socket通信通常不是上位機(jī)主動(dòng)連機(jī)器人而是機(jī)器人作為客戶端去連上位機(jī)的服務(wù)端。這和很多人一開始的直覺(jué)相反。FANUC的控制程序里通過(guò)TCPCONNECT指令建立連接你需要指定對(duì)方的IP和端口。! 機(jī)器人端連接上位機(jī)示例TP語(yǔ)言 TCPCONNECT 1, 192.168.0.100, 8080注意FANUC的Socket指令是阻塞式的。也就是說(shuō)執(zhí)行到這一步機(jī)器人程序會(huì)停在那里等連接建立成功。如果上位機(jī)服務(wù)沒(méi)啟動(dòng)、IP不通機(jī)器人程序會(huì)直接報(bào)錯(cuò)甚至影響自動(dòng)運(yùn)行。2.2 為什么“自動(dòng)重連”不能簡(jiǎn)單粗暴地寫個(gè)循環(huán)很多工程師的第一個(gè)想法是TCP斷了我讓機(jī)器人循環(huán)去重連不就完事了理論上對(duì)實(shí)際上不行原因有三點(diǎn)。第一FANUC的Socket狀態(tài)管理比較特殊。你需要在程序里維護(hù)一個(gè)連接狀態(tài)標(biāo)志而不是靠系統(tǒng)自動(dòng)恢復(fù)。如果程序已經(jīng)運(yùn)行到后續(xù)指令TCP連接斷了但狀態(tài)標(biāo)志沒(méi)更新數(shù)據(jù)收發(fā)會(huì)繼續(xù)報(bào)錯(cuò)或直接卡死在那條指令上。第二收發(fā)指令的時(shí)序。FANUC的TCPRECV接收數(shù)據(jù)時(shí)如果連接已經(jīng)斷了它會(huì)報(bào)一個(gè)錯(cuò)誤代碼。如果你沒(méi)有處理這個(gè)錯(cuò)誤程序就會(huì)中止而不是自動(dòng)跳過(guò)然后重連。第三工業(yè)現(xiàn)場(chǎng)的“瞬時(shí)中斷”本身就不穩(wěn)定。有時(shí)候是1秒內(nèi)的抖動(dòng)有時(shí)候是幾秒鐘的持續(xù)中斷。如果重連邏輯寫得不夠精細(xì)會(huì)出現(xiàn)頻繁重連、數(shù)據(jù)重復(fù)、狀態(tài)錯(cuò)亂等問(wèn)題。所以我的做法是把“正常數(shù)據(jù)收發(fā)”和“連接管理”分成兩個(gè)邏輯塊連接狀態(tài)單獨(dú)監(jiān)控?cái)嚅_后走獨(dú)立的恢復(fù)流程而不是在主收發(fā)流程里硬套重連代碼。3. 核心代碼實(shí)現(xiàn)FANUC端的TP程序與上位機(jī)C#服務(wù)3.1 上位機(jī)服務(wù)端監(jiān)聽(tīng)、接收、檢測(cè)斷線先寫上位機(jī)這部分因?yàn)樗钦麄€(gè)方案的“錨點(diǎn)”。我用的是C#TcpListener監(jiān)聽(tīng)機(jī)器人連接的端口。// 上位機(jī)TCP服務(wù)核心骨架 TcpListener listener new TcpListener(IPAddress.Any, 8080); listener.Start(); while (true) { TcpClient client await listener.AcceptTcpClientAsync(); _ Task.Run(() HandleClient(client)); } async Task HandleClient(TcpClient client) { using (client) using (NetworkStream stream client.GetStream()) { byte[] buffer new byte[256]; while (true) { try { int read await stream.ReadAsync(buffer, 0, buffer.Length); if (read 0) { // 對(duì)端關(guān)閉連接 break; } // 解析數(shù)據(jù)、寫數(shù)據(jù)庫(kù) } catch (IOException) { // 連接異常中斷 break; } } } // 記錄斷開日志等待機(jī)器人重連 LogDisconnect(); // 通知業(yè)務(wù)系統(tǒng)該工位離線 NotifyOffline(); }這段代碼里有幾個(gè)細(xì)節(jié)值得展開講。ReadAsync返回0表示對(duì)端正常關(guān)閉了連接。而拋出IOException通常意味著網(wǎng)絡(luò)異常或?qū)Χ酥苯訑嚯?、拔線。這兩種情況必須區(qū)分對(duì)待日志里要寫清楚是“正常關(guān)閉”還是“異常中斷”否則后期統(tǒng)計(jì)故障原因時(shí)全是模糊信息。還有一點(diǎn)我特意在斷開后加了NotifyOffline()把離線狀態(tài)推給業(yè)務(wù)系統(tǒng)的看板。這樣現(xiàn)場(chǎng)班組長(zhǎng)能在機(jī)器人“掉線”的第一時(shí)間看到異常提示而不是等幾分鐘后報(bào)表里缺數(shù)據(jù)才發(fā)現(xiàn)。這在量產(chǎn)產(chǎn)線上非常重要因?yàn)閿?shù)據(jù)缺失直接影響質(zhì)量追溯的完整性。3.2 機(jī)器人端TP程序收發(fā)、檢測(cè)、重連三件套機(jī)器人端的程序是整個(gè)方案的重頭戲。FANUC的TP語(yǔ)言沒(méi)有C#那么順手但它有一套自己的Socket指令體系用熟了也很清晰。核心思路是三個(gè)狀態(tài)機(jī)節(jié)點(diǎn)CONNECT_TRY嘗試連接上位機(jī)WORKING正常收發(fā)數(shù)據(jù)RECONNECT_WAIT等待重連。! 機(jī)器人端主循環(huán)偽代碼 LBL[1] IF R[1]0 THEN ! 未連接狀態(tài) TCPCONNECT 1, 192.168.0.100, 8080 IF STATUS[1]1 THEN R[1]1 ENDIF ENDIF IF R[1]1 THEN ! 已連接正常收發(fā) TCPCLEAR 1 TCPWRITE 1, DATA TCPRECV 1, REPLY IF ERROR[1]0 THEN TCPCLOSE 1 R[1]0 ENDIF ENDIF WAIT 0.10 GOTO LBL[1]這里有幾個(gè)FANUC Socket指令的細(xì)節(jié)我踩過(guò)坑一定要提醒TCPCLEAR清空收發(fā)緩沖區(qū)。如果不先清舊數(shù)據(jù)會(huì)干擾判斷導(dǎo)致接收到的數(shù)據(jù)是上次的殘留。TCPWRITE把數(shù)據(jù)寫到指定Socket連接。數(shù)據(jù)格式可以是字符串或字節(jié)數(shù)組取決于上位機(jī)怎么解析。TCPRECV接收數(shù)據(jù)。它有超時(shí)參數(shù)默認(rèn)情況下可能會(huì)一直等下去務(wù)必設(shè)置一個(gè)合理的超時(shí)值。比如500毫秒超時(shí)就往下走避免程序卡死。TCPCLOSE關(guān)閉連接。關(guān)閉之后一定要把狀態(tài)寄存器置0避免后續(xù)邏輯誤判。3.3 重連時(shí)序和退避策略純粹地“斷了就立刻重連”在工業(yè)現(xiàn)場(chǎng)并不可取。如果網(wǎng)絡(luò)還在抖動(dòng)立刻重連大概率失敗然后循環(huán)重試把CPU和通信端口都占滿反而拖慢恢復(fù)。我采用的策略是分級(jí)退避重連第一次斷開后立即重連若失敗等待2秒再試第二次失敗后等待5秒三次以上失敗統(tǒng)一按10秒間隔重試直到恢復(fù)。這么做的好處是瞬時(shí)抖動(dòng)能在1秒內(nèi)恢復(fù)不耽誤生產(chǎn)持續(xù)斷網(wǎng)時(shí)不至于讓機(jī)器人側(cè)的程序陷入高頻率無(wú)效連接浪費(fèi)資源。! 重連等待時(shí)間可以根據(jù)R[2]的值動(dòng)態(tài)調(diào)整 R[2]R[2]1 IF R[2]1 THEN WAIT 0.5 ENDIF IF R[2]2 THEN WAIT 2.0 ENDIF IF R[2]3 THEN WAIT 10.0 ENDIF有一點(diǎn)特別重要重連期間機(jī)器人的工作是繼續(xù)進(jìn)行的不會(huì)因此停線。焊接照焊只是上位機(jī)暫時(shí)收不到數(shù)據(jù)。斷線期間的數(shù)據(jù)有兩種處理方式方式A機(jī)器人本地緩存存在寄存器或D寄存器里恢復(fù)后批量補(bǔ)傳。方式B放棄斷線期間的數(shù)據(jù)恢復(fù)后從當(dāng)前時(shí)刻開始續(xù)傳。方式A看起來(lái)很美但實(shí)際實(shí)現(xiàn)時(shí)要注意FANUC寄存器的容量有限而且數(shù)據(jù)補(bǔ)傳的時(shí)序邏輯比較復(fù)雜如果斷線時(shí)間太長(zhǎng)緩存溢出會(huì)引發(fā)新的問(wèn)題。我當(dāng)時(shí)的產(chǎn)線對(duì)數(shù)據(jù)完整性要求很高最終選擇了方式A但加了“緩存滿則覆蓋最舊數(shù)據(jù)”的策略保證實(shí)時(shí)性優(yōu)先歷史數(shù)據(jù)盡力而為。4. 現(xiàn)場(chǎng)排查實(shí)錄從“懷疑人生”到定位真兇4.1 第一輪排查網(wǎng)線、交換機(jī)和電磁干擾這起故障最讓人頭疼的地方在于問(wèn)題不是一直出現(xiàn)而是“時(shí)不時(shí)來(lái)一下”。一開始我們按照常規(guī)思路去查物理層。懷疑對(duì)象1網(wǎng)線接頭氧化或接觸不良。檢查了機(jī)器人側(cè)的RJ45水晶頭重新壓過(guò)一次無(wú)果。懷疑對(duì)象2工業(yè)交換機(jī)的端口協(xié)商問(wèn)題。我們把機(jī)器人網(wǎng)口和上位機(jī)網(wǎng)口都強(qiáng)制設(shè)置為100M全雙工避免自動(dòng)協(xié)商的誤判但問(wèn)題依舊。懷疑對(duì)象3電磁干擾。車間里有大功率伺服電機(jī)和焊機(jī)我們懷疑橋架里的動(dòng)力電纜對(duì)網(wǎng)線造成了干擾。用了屏蔽網(wǎng)線、加了磁環(huán)情況有改善——注意“改善”這個(gè)詞——但問(wèn)題仍然存在頻率降低了從“每半小時(shí)一次”變成“每?jī)尚r(shí)一次”。4.2 第二輪排查從物理層轉(zhuǎn)向數(shù)據(jù)鏈路層物理層不干凈但還不至于徹底斷連那問(wèn)題往往出在高一點(diǎn)的位置。我們開始在機(jī)器人控制器和上位機(jī)兩側(cè)同時(shí)抓日志。這里有一個(gè)關(guān)鍵動(dòng)作讓上位機(jī)側(cè)記錄每一次TCP連接受影響的系統(tǒng)事件包括重連時(shí)間、斷開前最后一次數(shù)據(jù)的時(shí)間戳、連接的本地端口號(hào)。日志放出來(lái)后規(guī)律顯現(xiàn)了。斷開事件發(fā)生的時(shí)間點(diǎn)幾乎都集中在機(jī)器人執(zhí)行某個(gè)固定動(dòng)作的時(shí)候——具體來(lái)說(shuō)是焊槍擺動(dòng)到某個(gè)特定角度伺服的加減速特別猛。這就把“隨機(jī)故障”變成了“可復(fù)現(xiàn)故障”排查難度一下子降了一半。我們進(jìn)一步確認(rèn)那個(gè)動(dòng)作發(fā)生時(shí)機(jī)器人控制柜內(nèi)部的通信板卡供電電壓有明顯波動(dòng)但控制器本身沒(méi)報(bào)任何硬件故障。這屬于典型的“瞬態(tài)電壓跌落”導(dǎo)致的以太網(wǎng)收發(fā)器短暫復(fù)位。不是網(wǎng)線問(wèn)題也不是交換機(jī)問(wèn)題而是機(jī)器人柜內(nèi)供電質(zhì)量的問(wèn)題。4.3 最終方案硬件軟件雙管齊下定位到根因之后解決路徑就很清晰了。硬件側(cè)做了三件事把機(jī)器人控制柜內(nèi)的通信板卡供電改到獨(dú)立的24V開關(guān)電源回路避開與伺服驅(qū)動(dòng)共用電源在通信板卡的電源輸入端并聯(lián)了一個(gè)470uF的電解電容和0.1uF的陶瓷電容吸收瞬態(tài)跌落把網(wǎng)線走線的橋架段用金屬軟管包裹與動(dòng)力電纜物理隔離。軟件側(cè)就是我們前面講的那套完整重連機(jī)制。硬件解決了“根因”軟件兜底解決“萬(wàn)一再抖一下”的容錯(cuò)問(wèn)題。兩者缺一不可。提示如果只做軟件重連而不處理硬件問(wèn)題你會(huì)發(fā)現(xiàn)重連機(jī)制越寫越復(fù)雜但斷線仍然不斷發(fā)生。治標(biāo)和治本必須同時(shí)上。5. 常見(jiàn)問(wèn)題與排查技巧手把手教你少走彎路5.1 機(jī)器人連接后上位機(jī)收不到任何數(shù)據(jù)這可能是FANUC Socket最容易踩的坑之一。TCPWRITE發(fā)出去了數(shù)據(jù)也顯示發(fā)送成功了但上位機(jī)就是收不到。排查思路確認(rèn)兩邊IP和端口確實(shí)一致確認(rèn)上位機(jī)防火墻是否放行了該端口Windows自帶防火墻經(jīng)常默認(rèn)攔掉確認(rèn)機(jī)器人發(fā)送的數(shù)據(jù)格式是ASCII字符串還是二進(jìn)制字節(jié)流。上位機(jī)如果用ReadLine讀行但機(jī)器人發(fā)的是原始字節(jié)沒(méi)有換行符那ReadAsync永遠(yuǎn)讀不到完整“一行”確認(rèn)TCPCLEAR是不是被放在了TCPWRITE前面但后面又覆蓋了數(shù)據(jù)這個(gè)順序真有人搞反。5.2 TCPRECV超時(shí)后程序卡死不動(dòng)FANUC的TCPRECV如果沒(méi)設(shè)超時(shí)或者超時(shí)設(shè)置得太大一旦對(duì)端沒(méi)回應(yīng)機(jī)器人程序會(huì)一直停在那里。更要命的是FANUC的Socket指令在執(zhí)行期間會(huì)阻塞整個(gè)TP任務(wù)導(dǎo)致機(jī)器人的其他邏輯也無(wú)法執(zhí)行。我的做法是TCPRECV的超時(shí)時(shí)間設(shè)為500毫秒一輪收發(fā)循環(huán)總時(shí)間控制在1秒以內(nèi)。這樣就算上位機(jī)暫時(shí)卡住機(jī)器人也能及時(shí)跳出收發(fā)邏輯進(jìn)入重連判斷。5.3 重連成功后為什么狀態(tài)寄存器沒(méi)有復(fù)位這是個(gè)隱藏很深的坑。TCPCONNECT建立連接后你判斷連接成功的標(biāo)志位可能在舊連接斷開時(shí)沒(méi)有被正確復(fù)位。FANUC的連接狀態(tài)存儲(chǔ)在不同的系統(tǒng)變量里具體來(lái)說(shuō)STATUS[1]表示連接狀態(tài)但斷開后這個(gè)值可能不會(huì)自動(dòng)歸零需要你主動(dòng)清除。解決方法是在執(zhí)行TCPCLOSE之后立刻顯式地把狀態(tài)標(biāo)志變量賦值為0而不是依賴于“理論上它會(huì)變成0”。5.4 機(jī)器人報(bào)“SOCKET ERROR”停機(jī)如何快速恢復(fù)這個(gè)錯(cuò)誤在FANUC控制器上很常見(jiàn)?,F(xiàn)場(chǎng)產(chǎn)線不能停停了就是損失所以一定要有快速恢復(fù)預(yù)案。我的經(jīng)驗(yàn)是在TP程序里增加一個(gè)手動(dòng)復(fù)位邏輯按一下預(yù)留的FANUC輸入信號(hào)自動(dòng)執(zhí)行TCPCLOSE然后重新走連接流程如果實(shí)在不行直接切換手動(dòng)模式在TP上手動(dòng)執(zhí)行一段“一鍵重連”的子程序?qū)τ趪?yán)重異??梢詳嚯娭貑C(jī)器人但這是最后的辦法畢竟會(huì)影響生產(chǎn)節(jié)拍。5.5 上位機(jī)側(cè)如何判斷“機(jī)器人真的離線了”還是“數(shù)據(jù)沒(méi)更新”這個(gè)問(wèn)題經(jīng)常被忽略。TCP重連成功之后機(jī)器人重新發(fā)送數(shù)據(jù)但你會(huì)發(fā)現(xiàn)時(shí)間戳可能和實(shí)際的當(dāng)前時(shí)間對(duì)不上因?yàn)闄C(jī)器人側(cè)的時(shí)間如果沒(méi)做同步本身就和上位機(jī)有偏差。我當(dāng)時(shí)加了一個(gè)“心跳包時(shí)間戳”雙保險(xiǎn)機(jī)制機(jī)器人每500ms發(fā)一組正常數(shù)據(jù)數(shù)據(jù)里包含一個(gè)自增序號(hào)另外每個(gè)5秒發(fā)一個(gè)單獨(dú)的心跳包包含機(jī)器人的當(dāng)前時(shí)鐘。上位機(jī)拿到心跳包后對(duì)比本機(jī)時(shí)間如果偏差超過(guò)5秒就告警提醒現(xiàn)場(chǎng)做時(shí)間同步。這套機(jī)制在后面排查“數(shù)據(jù)對(duì)不上”的問(wèn)題時(shí)幫了大忙。6. 這套方案能不能復(fù)制到其他機(jī)器人品牌6.1 同樣的思路不同的實(shí)現(xiàn)不少朋友聽(tīng)到“FANUC專用”就覺(jué)得學(xué)了沒(méi)用。其實(shí)通信邏輯是通用的換到其他機(jī)器人品牌只是指令和API變了。比如庫(kù)卡機(jī)器人用的是KRL語(yǔ)言它有自己的以太網(wǎng)XML通信接口重連邏輯一樣要寫狀態(tài)機(jī)ABB機(jī)器人走的是RAPID語(yǔ)言Socket通信的指令是SocketConnect、SocketSend、SocketReceive思路一模一樣發(fā)那科以外的國(guó)產(chǎn)品牌不少直接用Modbus TCP或者Profinet重連邏輯則放在PLC側(cè)而非機(jī)器人側(cè)。所以重要的是把“狀態(tài)機(jī)分級(jí)重連狀態(tài)復(fù)位數(shù)據(jù)心跳”這套思路吃透換平臺(tái)只是換語(yǔ)法的事。6.2 什么時(shí)候可以把重連邏輯放在PLC側(cè)如果你的產(chǎn)線上有西門子、三菱或羅克韋爾PLC而且機(jī)器人是通過(guò)現(xiàn)場(chǎng)總線Profinet、EtherNet/IP和PLC通信、再由PLC轉(zhuǎn)發(fā)數(shù)據(jù)到上位機(jī)的那重連邏輯最好放在PLC側(cè)。PLC的程序循環(huán)周期短通信監(jiān)控更實(shí)時(shí)故障處理也更靈活。但要注意PLC側(cè)的TCP重連和機(jī)器人側(cè)完全是兩碼事。PLC一般不做Socket而是走工業(yè)協(xié)議斷線檢測(cè)用“生產(chǎn)者/消費(fèi)者”或“IO掃描”的機(jī)制。它和TCP重連解決的問(wèn)題類似但手段完全不同。別把這套代碼直接搬過(guò)去用。7. 事后復(fù)盤如果再讓我做一次我會(huì)改什么7.1 日志系統(tǒng)應(yīng)該更“鋒利”我這次是用了兩周時(shí)間才定位到根因其中很大一部分時(shí)間浪費(fèi)在“日志不夠精準(zhǔn)”上。如果一開始就做三層日志——物理層網(wǎng)線通斷、交換機(jī)端口狀態(tài)、系統(tǒng)層TCP連接狀態(tài)、Socket錯(cuò)誤碼、應(yīng)用層數(shù)據(jù)序號(hào)連續(xù)性和時(shí)間戳排查速度至少能快一倍。特別是Socket錯(cuò)誤碼FANUC的ERRNUM在斷開時(shí)會(huì)返回具體的錯(cuò)誤編號(hào)。不同的錯(cuò)誤碼對(duì)應(yīng)不同的原因有的是超時(shí)有的是連接被重置有的是緩沖區(qū)溢出。把這些錯(cuò)誤碼列成對(duì)照表寫到排查文檔里能減少很多重復(fù)勞動(dòng)。7.2 上線前要做“拔線測(cè)試”這個(gè)太重要了。我在項(xiàng)目上線后才發(fā)現(xiàn)重連邏輯有問(wèn)題但那時(shí)產(chǎn)線已經(jīng)開跑了每次調(diào)試都心驚膽戰(zhàn)。如果你還在項(xiàng)目期一定要在試運(yùn)行階段做一次暴力測(cè)試直接拔掉網(wǎng)線10秒再插回去把交換機(jī)關(guān)電再上電在上位機(jī)上強(qiáng)制殺掉服務(wù)進(jìn)程再啟動(dòng)在機(jī)器人運(yùn)行到特定動(dòng)作時(shí)人為制造瞬態(tài)電壓跌落用大功率設(shè)備啟停。把這些場(chǎng)景全部測(cè)一遍重連邏輯才算真正可交付。7.3 和MES的數(shù)據(jù)一致性要做好“斷點(diǎn)續(xù)傳”如果現(xiàn)場(chǎng)有MES系統(tǒng)千萬(wàn)別忽略斷線期間的數(shù)據(jù)補(bǔ)傳。TCP重連成功只是第一步斷線期間那一大段缺失的數(shù)據(jù)如何補(bǔ)傳才是真正體現(xiàn)工程水平的地方。我當(dāng)時(shí)采用的是增量補(bǔ)傳斷線期間機(jī)器人本地緩存數(shù)據(jù)恢復(fù)后按“時(shí)間順序”批量發(fā)給上位機(jī)上位機(jī)按主鍵去重后落庫(kù)。這里要特別注意機(jī)器人側(cè)緩存的循環(huán)覆蓋策略以及補(bǔ)傳時(shí)不能影響實(shí)時(shí)數(shù)據(jù)的實(shí)時(shí)性。我會(huì)給補(bǔ)傳設(shè)置一個(gè)單獨(dú)的優(yōu)先級(jí)比如實(shí)時(shí)數(shù)據(jù)優(yōu)先發(fā)送補(bǔ)傳數(shù)據(jù)在實(shí)時(shí)數(shù)據(jù)發(fā)完后用剩余帶寬慢慢補(bǔ)。最后分享一點(diǎn)個(gè)人體會(huì)每次做這類項(xiàng)目我都覺(jué)得最有價(jià)值的不是那段重連代碼本身而是現(xiàn)場(chǎng)排查的過(guò)程。從“物理層什么都是好的”到終于抓到那幾毫秒的電壓跌落中間那種找不到北的感覺(jué)相信每個(gè)搞過(guò)工業(yè)現(xiàn)場(chǎng)通信的人都懂。如果你也正在被“機(jī)器人掉線”折磨不妨先按我的方法做兩件事第一把從機(jī)器人到上位機(jī)的完整鏈路圖畫出來(lái)標(biāo)注每一段網(wǎng)線、每一個(gè)接頭、每一臺(tái)交換機(jī)第二做好三層日志別嫌麻煩。這兩件事做完你的排查效率至少提升一倍。TCP重連只是最后那道防線把通信鏈路捋干凈才是治本之道。希望這篇復(fù)盤能讓你少熬幾個(gè)夜。