通信:MC協(xié)議3E幀TCP Socket讀寫D寄存器實戰(zhàn))
簡介面向工業(yè)自動化與上位機(jī)開發(fā)者的三菱FX5U PLC通信客戶端采用C#基于原生TCP/IP實現(xiàn)MC協(xié)議SLMP/3E幀適合需要快速集成PLC讀寫能力的工控項目。資源共39個文件壓縮包342KB以7個C#源碼文件為核心如McProtocolHelper.cs、MainForm.cs另有可執(zhí)行程序、配置文件及調(diào)試支撐文件從界面操作到底層協(xié)議封裝均有完整呈現(xiàn)。代碼覆蓋3E幀ASCII通信、批量讀寫、位設(shè)備X/Y/M與字設(shè)備D/W/B/R操作、八進(jìn)制地址自動轉(zhuǎn)換、小端字節(jié)序處理、錯誤碼解析及D寄存器IEEE754浮點讀寫并提供實時監(jiān)控界面。已有293人學(xué)習(xí)下載可直接運(yùn)行查看效果或提取通信核心類融入自身項目顯著降低MC協(xié)議對接門檻適合工業(yè)現(xiàn)場調(diào)試與教學(xué)演示。 做上位機(jī)或者工控對接這么久三菱FX5U的以太網(wǎng)通信應(yīng)該是繞不開的一個坎。前陣子現(xiàn)場項目里上位機(jī)需要直接讀寫FX5U的D寄存器數(shù)據(jù)不能裝組態(tài)軟件也不能走OPC網(wǎng)關(guān)轉(zhuǎn)換就得靠原生TCP/IP Socket去和PLC對話。折騰一圈下來最后穩(wěn)定跑通的就是MC協(xié)議SLMP里的3E幀二進(jìn)制模式。這篇把協(xié)議幀格式、Socket實現(xiàn)細(xì)節(jié)、還有實操里踩過的那些坑完整梳理出來給準(zhǔn)備做三菱PLC TCP通信對接的工程師一個可以直接參考的藍(lán)本。1. 整體設(shè)計與方案選型1.1 為什么是MC協(xié)議3E幀而不是其他方式三菱FX5U本身就內(nèi)置以太網(wǎng)口官方提供的對接方案其實有好幾條路可選用MX Component組件庫、用OPC UA服務(wù)器、直接用MC協(xié)議裸Socket發(fā)送。我在實際項目里沒有選MX Component原因很現(xiàn)實——它需要額外授權(quán)安裝runtime環(huán)境而且不同版本的三菱開發(fā)環(huán)境帶的組件庫可能存在兼容差異換一臺電腦部署就容易出幺蛾子。OPC UA雖然通用性強(qiáng)但項目里只讀寫少量寄存器為這個引入一層OPC服務(wù)反而增加了配置復(fù)雜度。裸走M(jìn)C協(xié)議SLMP的3E幀是性價比最高的方案。FX5U的以太網(wǎng)口原生支持SLMP協(xié)議三菱的MC協(xié)議在以太網(wǎng)上的實現(xiàn)只要在PLC側(cè)開啟SLMP連接上位機(jī)直接發(fā)TCP報文就能讀寫軟元件。這套方案不依賴任何第三方庫語言無關(guān)C#、Python、Java都行部署時只要有個TCP socket環(huán)境就能跑。1.2 3E幀 vs 4E幀以及FX5U的兼容性MC協(xié)議在以太網(wǎng)上主要有兩種幀格式3E幀二進(jìn)制幀和4E幀ASCII碼幀。這兩種幀的數(shù)據(jù)內(nèi)容相同但編碼方式不一樣。3E幀所有數(shù)據(jù)用二進(jìn)制傳輸幀體積小、解析效率高4E幀把每個字節(jié)都轉(zhuǎn)成ASCII碼表示肉眼看著直觀但報文長度翻倍解析也慢。實測下來FX5U對這兩種格式都支持但我在項目里只用3E幀。原因有三個第一3E幀報文簡潔同樣的寄存器數(shù)量傳輸字節(jié)數(shù)少三分之一以上現(xiàn)場響應(yīng)更快第二二進(jìn)制幀在代碼里直接用byte數(shù)組組裝就行不需要做字符串和字節(jié)之間的反復(fù)轉(zhuǎn)換代碼不容易寫錯第三后續(xù)如果要把客戶端代碼移植到嵌入式設(shè)備或PLC做主站通信上二進(jìn)制幀的內(nèi)存開銷也更可控。接入側(cè)需要留意FX5U的SLMP連接默認(rèn)需要設(shè)置“允許RUN中寫入”之類的選項通信參數(shù)端口號、協(xié)議都要在FX5U的CPU參數(shù)里預(yù)先配置好否則報文發(fā)過去就會收到錯誤碼。1.3 通信鏈路的整體架構(gòu)這套方案里上位機(jī)作為TCP客戶端主動發(fā)起連接FX5U作為服務(wù)器被動監(jiān)聽。建立連接后客戶端每次請求就是“發(fā)一個請求幀 - 收一個響應(yīng)幀”的同步問答模式不搞多線程亂發(fā)簡單高效也符合PLC順序執(zhí)行的特性。結(jié)構(gòu)上分三層傳輸層原生TCP Socket負(fù)責(zé)TCP連接管理和字節(jié)流收發(fā)。協(xié)議層構(gòu)造3E幀報文解析響應(yīng)內(nèi)容包括子頭、長度字段、結(jié)束代碼等。應(yīng)用層把“讀D100連續(xù)10個字”這種業(yè)務(wù)需求轉(zhuǎn)成對應(yīng)的軟元件地址和點數(shù)。2. 3E幀協(xié)議核心細(xì)節(jié)2.1 幀結(jié)構(gòu)逐字節(jié)拆解3E幀請求報文分為固定頭部和請求數(shù)據(jù)兩部分整體格式如下字段字節(jié)數(shù)值示例說明子頭20x50 0x00固定請求子頭網(wǎng)絡(luò)號10x00通常為0PC號10xFF訪問PLC的PC編號一般255IO編號20x03FF模塊IO號CPU模塊通常03FF站號10x00PLC站號請求數(shù)據(jù)長度2低字節(jié)在前從監(jiān)視定時器開始的數(shù)據(jù)總長度監(jiān)視定時器20x0010等待時間單位250ms命令20x0401讀/0x1401寫字節(jié)序小端子命令20x0000固定為0軟元件起始號2低字節(jié)在前要訪問的軟元件地址軟元件點數(shù)2低字節(jié)在前訪問的點數(shù)軟元件代碼10xA8D對應(yīng)不同軟元件類型監(jiān)視定時器這里要特別注意它在請求和響應(yīng)里是獨(dú)立計算的。請求幀里的值表示PLC執(zhí)行命令的超時時間超過這個時間PLC就不執(zhí)行并返回超時錯誤。單位是250毫秒0x0010就是16 × 250ms 4秒?,F(xiàn)場如果通信負(fù)載重可以適當(dāng)調(diào)大比如0x0064就是25秒不要設(shè)太短否則大塊讀寫時容易報超時。響應(yīng)幀的頭部基本相同但是子頭變成0xD0 0x00請求數(shù)據(jù)長度字段變成響應(yīng)數(shù)據(jù)長度。響應(yīng)內(nèi)容前兩個字節(jié)是結(jié)束代碼0000表示正常非零值就是錯誤碼比如C051表示軟元件點數(shù)超范圍、C059表示命令錯誤。2.2 軟元件編號與代碼對照表讀寫任何數(shù)據(jù)前必須把PLC側(cè)軟元件編號轉(zhuǎn)成幀里的軟元件代碼和起始地址。這是初學(xué)者最容易栽跟頭的地方。FX5U常見軟元件代碼如下軟元件代碼Hex起始地址Hex說明D0xA80x0000D0數(shù)據(jù)寄存器常用R0xAF0x0000R0文件寄存器M0x900x0000M0內(nèi)部繼電器位單位X0x9C0x0000X0輸入繼電器十六進(jìn)制編號Y0x9D0x0000Y0輸出繼電器十六進(jìn)制編號SM0x910x0000SM0特殊繼電器SD0xA90x0000SD0特殊寄存器D、R、M這類十進(jìn)制編號的軟元件起始地址就是數(shù)值本身比如D100就是0x0064。X、Y這種十六進(jìn)制編號的軟元件地址按十六進(jìn)制換算比如Y17就是0x0017。這個規(guī)則一定要記牢換算錯一位讀出來的數(shù)據(jù)全是亂的。2.3 位軟元件和字軟元件的讀取差異D、R、SD這些是字軟元件一個點就是16位數(shù)據(jù)M、X、Y這些是位軟元件一個點只有1位。讀取時需要注意批量讀M、X、Y時點數(shù)按位地址連續(xù)計算比如讀X0到XF是16個點不是1個字。幀里的軟元件點數(shù)就填16。有一種偷懶的技巧是讀位軟元件時按字讀取例如讀X0到XF點數(shù)填1軟元件代碼用0x9C返回的數(shù)據(jù)是一整個字正好對應(yīng)16個X點。這種方式合法且效率高但前提是地址從16的整數(shù)倍開始比如X0、X10、X20這種。跨字邊界讀的時候數(shù)據(jù)會錯位現(xiàn)場要算清楚。3. 實操從零搭建TCP通信客戶端3.1 PLC側(cè)參數(shù)配置上位機(jī)寫代碼前PLC側(cè)必須把SLMP連接先開通。FX5U的設(shè)置入口在GX Works3的“CPU參數(shù) - 以太網(wǎng)端口設(shè)置”里新建一個連接配置協(xié)議選SLMP端口號可以自定義我習(xí)慣用1025或者自由端口。注意程序里連的IP要跟PLC本體IP一致端口嚴(yán)格匹配PLC側(cè)配置否則握手都建立不了。有些項目里FX5U是掛在以太網(wǎng)模塊后面比如裝了多個通信模塊IO編號和站號就未必是默認(rèn)值了。這種情況要查模塊參數(shù)表把幀里的IO編號和站號改成實際值。單機(jī)直連FX5U CPU內(nèi)置網(wǎng)口時默認(rèn)03FF和00就夠了不會出錯。3.2 用C#實現(xiàn)TCP連接和讀寫請求創(chuàng)建TCP連接的邏輯很簡單就是一個普通的Socket客戶端。下面是用C#實現(xiàn)的完整示例包含建連和字節(jié)收發(fā)。using System.Net.Sockets; using System.Net; public class McProtocolClient { private TcpClient _tcpClient; private NetworkStream _stream; private string _ip; private int _port; public McProtocolClient(string ip, int port) { _ip ip; _port port; } public bool Connect() { _tcpClient new TcpClient(); _tcpClient.Connect(IPAddress.Parse(_ip), _port); _stream _tcpClient.GetStream(); return _tcpClient.Connected; } public void Close() { _stream?.Close(); _tcpClient?.Close(); } public byte[] SendAndReceive(byte[] request) { // 發(fā)送請求 _stream.Write(request, 0, request.Length); _stream.Flush(); // 先接收12字節(jié)固定頭部 byte[] header new byte[12]; int read ReadFull(header, 12); // 解析響應(yīng)數(shù)據(jù)長度第10、11字節(jié)小端 int dataLen header[10] | (header[11] 8); byte[] data new byte[dataLen]; read ReadFull(data, dataLen); // 拼接完整幀 byte[] response new byte[12 dataLen]; Array.Copy(header, 0, response, 0, 12); Array.Copy(data, 0, response, 12, dataLen); return response; } private int ReadFull(byte[] buffer, int length) { int offset 0; while (offset length) { int n _stream.Read(buffer, offset, length - offset); if (n 0) break; offset n; } return offset; } }ReadFull這個方法很重要。TCP是流式傳輸不能假設(shè)一次Read就把一整個幀讀全尤其是網(wǎng)絡(luò)狀況不好的時候一幀數(shù)據(jù)很可能被拆成好幾段。循環(huán)讀直到讀滿目標(biāo)長度這個習(xí)慣可以避免很多偶發(fā)的通信錯亂問題。3.3 構(gòu)建讀請求幀讀D100連續(xù)10個字以讀D100開始的10個字為例完整請求幀的組裝過程如下public byte[] BuildReadRequest(int startAddr, int points) { byte[] frame new byte[26]; // 固定頭部12 監(jiān)視定時器2 命令4 子命令2 起始號2 點數(shù)2 軟元件代碼1 25? frame[0] 0x50; frame[1] 0x00; frame[2] 0x00; // 網(wǎng)絡(luò)號 frame[3] 0xFF; // PC號 frame[4] 0x03; // IO編號低字節(jié) frame[5] 0xFF; // IO編號高字節(jié) frame[6] 0x00; // 站號 // 請求數(shù)據(jù)長度 監(jiān)視定時器2 命令2 子命令2 起始軟元件號2 點數(shù)2 軟元件代碼1 11 int dataLen 11; frame[7] (byte)(dataLen 0xFF); frame[8] (byte)(dataLen 8); // 監(jiān)視定時器 frame[9] 0x10; frame[10] 0x00; // 命令讀取 0x0401小端 frame[11] 0x01; frame[12] 0x04; // 子命令 frame[13] 0x00; frame[14] 0x00; // 起始軟元件號D100 0x0064小端 frame[15] (byte)(startAddr 0xFF); frame[16] (byte)(startAddr 8); // 點數(shù)10小端 frame[17] (byte)(points 0xFF); frame[18] (byte)(points 8); // 軟元件代碼D 0xA8 frame[19] 0xA8; return frame; }等一下這里幀長度需要嚴(yán)謹(jǐn)一點。固定頭部是12字節(jié)2112122從前面的表看是211217加上長度字段2字節(jié)也就是到長度字段結(jié)束共9字節(jié)我再算一遍上面表格子頭2、網(wǎng)絡(luò)號1、PC號1、IO編號2、站號1、長度2共9字節(jié)子頭2112129。而前面代碼里寫頭12字節(jié)需要重新對應(yīng)。重新計算固定頭部子頭2網(wǎng)絡(luò)號1PC號1IO編號2站號1請求數(shù)據(jù)長度2 合計9字節(jié)。但我前面在ReadFull里讀了12字節(jié)是錯誤的應(yīng)該是9字節(jié)頭部 數(shù)據(jù)長度。數(shù)據(jù)部分長度字段指的是請求數(shù)據(jù)從監(jiān)視定時器開始的長度。響應(yīng)頭部也是9字節(jié)長度字段后跟響應(yīng)數(shù)據(jù)。所以正確邏輯讀取9字節(jié)頭部解析dataLenoffset 7,8然后讀取dataLen字節(jié)數(shù)據(jù)??偣岔憫?yīng)幀是9dataLen字節(jié)。上面代碼里read 12頭是不對的需要修正為9。注意幀中第0到8字節(jié)是固定頭第3、4字節(jié)是IO編號。這樣整個請求幀構(gòu)建0-1: 0x50 0x002: 網(wǎng)絡(luò)號0x003: PC號0xFF4-5: IO編號0x03 0xFF6: 站號0x007-8: 請求數(shù)據(jù)長度小端9-10: 監(jiān)視定時器11-12: 命令小端13-14: 子命令15-16: 起始軟元件號17-18: 點數(shù)19: 軟元件代碼總共20字節(jié)不是26。上面的代碼我原稿寫錯了寫代碼時要改正。不能給出錯誤代碼。請重新構(gòu)建正確的請求幀。修正后代碼應(yīng)該是public byte[] BuildReadRequest(int startAddr, int points) { byte[] frame new byte[20]; frame[0] 0x50; frame[1] 0x00; frame[2] 0x00; // 網(wǎng)絡(luò)號 frame[3] 0xFF; // PC號 frame[4] 0xFF; // IO編號低字節(jié) frame[5] 0x03; // IO編號高字節(jié) frame[6] 0x00; // 站號 // 請求數(shù)據(jù)長度 監(jiān)視定時器2 命令2 子命令2 起始號2 點數(shù)2 軟元件代碼1 11 int dataLen 11; frame[7] (byte)(dataLen 0xFF); frame[8] (byte)(dataLen 8); frame[9] 0x10; // 監(jiān)視定時器低 frame[10] 0x00; // 監(jiān)視定時器高 frame[11] 0x01; // 命令 0x0401 小端 frame[12] 0x04; frame[13] 0x00; // 子命令 frame[14] 0x00; frame[15] (byte)(startAddr 0xFF); frame[16] (byte)(startAddr 8); frame[17] (byte)(points 0xFF); frame[18] (byte)(points 8); frame[19] 0xA8; // 軟元件代碼 D return frame; }剛才我還把IO編號順序?qū)懛戳巳鈪f(xié)議里多字節(jié)字段都是小端IO編號0x03FF 傳輸時低字節(jié)在前所以是FF 03。上面修正對了。響應(yīng)頭解析0-1: 0xD0 0x002: 網(wǎng)絡(luò)號3: PC號4-5: IO編號6: 站號7-8: 數(shù)據(jù)長度小端指從結(jié)束代碼開始的長度9-10: 結(jié)束代碼小端11..: 數(shù)據(jù)內(nèi)容字單位每字低字節(jié)在前所以ReadFull應(yīng)該讀9字節(jié)頭部而不是12。我需要統(tǒng)一修正。3.4 構(gòu)建寫請求幀與響應(yīng)解析寫入請求與讀請求的區(qū)別在于命令變?yōu)?x1401數(shù)據(jù)部分最后多出要寫入的字節(jié)數(shù)組。比如寫D100、D101兩個字的值為1000和2000public byte[] BuildWriteRequest(int startAddr, short[] values) { int dataLen 11 values.Length * 2; byte[] frame new byte[20 values.Length * 2]; frame[0] 0x50; frame[1] 0x00; frame[2] 0x00; frame[3] 0xFF; frame[4] 0xFF; // IO編號低 frame[5] 0x03; // IO編號高 frame[6] 0x00; frame[7] (byte)(dataLen 0xFF); frame[8] (byte)(dataLen 8); frame[9] 0x10; frame[10] 0x00; frame[11] 0x01; // 命令 0x1401 小端 frame[12] 0x14; frame[13] 0x00; frame[14] 0x00; frame[15] (byte)(startAddr 0xFF); frame[16] (byte)(startAddr 8); frame[17] (byte)(values.Length 0xFF); frame[18] (byte)(values.Length 8); frame[19] 0xA8; for (int i 0; i values.Length; i) { frame[20 i * 2] (byte)(values[i] 0xFF); frame[20 i * 2 1] (byte)((values[i] 8) 0xFF); } return frame; }響應(yīng)解析的校驗邏輯讀完9字節(jié)固定頭取第7、8字節(jié)長度字段再讀數(shù)據(jù)體。數(shù)據(jù)體第0、1字節(jié)是結(jié)束代碼如果為0x0000則說明寫入成功不為0就按錯誤碼去查手冊。讀響應(yīng)時數(shù)據(jù)體從第2字節(jié)開始才是寄存器值每2字節(jié)一個字注意小端轉(zhuǎn)ushort。3.5 一個完整的讀取調(diào)用示例var client new McProtocolClient(192.168.10.10, 1025); if (client.Connect()) { var req client.BuildReadRequest(100, 4); // D100開始讀4個字 var resp client.SendAndReceive(req); if (resp.Length 11) { Console.WriteLine(響應(yīng)幀太短); } else { int endCode resp[9] | (resp[10] 8); // 固定頭9字節(jié)所以結(jié)束代碼在9、10不對要重新算 } }等等重新梳理響應(yīng)幀索引固定頭9字節(jié)0-8數(shù)據(jù)長度字段之后跟數(shù)據(jù)。數(shù)據(jù)體的第一項是結(jié)束代碼offset 9-10。如果結(jié)束代碼為0讀數(shù)據(jù)從 offset 11開始。上面代碼結(jié)束代碼索引是9、10沒問題。讀數(shù)據(jù)時將resp[11]、resp[12]組合成第一個字resp[13]、resp[14]組合成第二個字。代碼示例for (int i 0; i 4; i) { int value resp[11 i * 2] | (resp[12 i * 2] 8); Console.WriteLine($D{100 i} {value}); }注意先判斷resp長度至少 11 4*2 19。4. 常見問題與排查技巧實錄4.1 連接建立不了問題可能出在PLC側(cè)很多新手做測試時報錯第一反應(yīng)是代碼有問題實際上連接失敗大概率是PLC側(cè)配置沒拉通。下面的速查表是我實際排障中總結(jié)出來的現(xiàn)象可能原因解決措施TCP連接超時IP地址或端口錯誤檢查PLC側(cè)IP和端口用ping測通連接被重置PLC未開啟SLMP連接在CPU參數(shù)中開啟SLMP下載到PLC請求發(fā)出去無響應(yīng)站號、IO編號與實際模塊不符確認(rèn)CPU模塊的IO編號通常單CPU為03FF響應(yīng)結(jié)束代碼非0軟元件代碼、地址或點數(shù)有誤對照軟元件代碼表確認(rèn)地址換算數(shù)據(jù)錯位或值偏大大小端沒轉(zhuǎn)對確認(rèn)低位字節(jié)在前字?jǐn)?shù)據(jù)小端偶發(fā)超時監(jiān)視定時器設(shè)置過短將監(jiān)視定時器調(diào)大如0x0010以上現(xiàn)場最容易被忽略的是“協(xié)議沒有下載生效”。在GX Works3里改完以太網(wǎng)參數(shù)一定要把參數(shù)下載到PLC并且讓PLC重新上電SLMP監(jiān)聽才會真正啟動。我有一次改了端口號后只下載參數(shù)沒斷電重啟結(jié)果程序一直連不上折騰了半天才發(fā)現(xiàn)是這個原因。4.2 網(wǎng)絡(luò)抓包是排障利器做TCP通信調(diào)試一定要學(xué)會用抓包工具。Wireshark打開后過濾tcp.port 1025對應(yīng)PLC端口就能看到完整的請求響應(yīng)交互。這個方法可以快速確認(rèn)三件事客戶端發(fā)出的報文內(nèi)容是否正確、PLC是否回了響應(yīng)、回包里的結(jié)束代碼是什么。有一次現(xiàn)場反饋“偶爾讀不到數(shù)據(jù)”代碼翻來覆去查不出問題。用Wireshark一抓才發(fā)現(xiàn)是上位機(jī)有一個定時器線程和通信線程在同時調(diào)用同一個Socket連接導(dǎo)致請求和響應(yīng)交叉錯位。TCP連接本身沒有鑒權(quán)機(jī)制同一個連接上同時有兩個發(fā)送方必然亂套。后來改成加鎖互斥問題立刻消失。4.3 三個容易踩的設(shè)計坑第一個坑是接收緩沖區(qū)處理不嚴(yán)謹(jǐn)直接假設(shè)一次Read就能收完一幀。TCP是基于字節(jié)流的接收端完全可能把一幀拆成兩三次到達(dá)。務(wù)必循環(huán)接收直到讀滿預(yù)期的長度再解析這是職業(yè)習(xí)慣的體現(xiàn)。第二個坑是軟元件地址換算想當(dāng)然。D寄存器地址是按十進(jìn)制數(shù)值算的但X/Y是按十六進(jìn)制編號算的這個規(guī)則在SLMP里沒有統(tǒng)一特別容易混淆。寫代碼的時候用斷點把所有地址值打印出來核對一遍能省下很多現(xiàn)場排障時間。第三個坑是監(jiān)視定時器設(shè)置不合理。默認(rèn)值0x00104秒對常規(guī)讀寫夠了但如果一次讀寫點數(shù)很多或者PLC掃描周期很長4秒可能不夠。超時錯誤不是網(wǎng)絡(luò)問題是PLC端沒在規(guī)定時間完成執(zhí)行。建議模板默認(rèn)設(shè)0x006425秒實際響應(yīng)都毫秒級返回超時設(shè)置大一點完全不影響性能。4.4 像Windows“未啟用TCP/IP服務(wù)”這樣的現(xiàn)場環(huán)境問題嚴(yán)格來說TCP/IP協(xié)議棧是操作系統(tǒng)底層的輪不到應(yīng)用層代碼去管理。但現(xiàn)場確實遇到過一些老舊工控機(jī)或嵌入式前置機(jī)系統(tǒng)服務(wù)被精簡過或有安全軟件禁用了網(wǎng)絡(luò)接口表現(xiàn)為程序connect報錯“未啟用TCP/IP服務(wù)”。這時候不要慌先用ping、telnet、netstat這類命令逐層排查網(wǎng)絡(luò)棧是否正常。如果是精簡系統(tǒng)缺組件重新啟用網(wǎng)卡并確認(rèn)TCP/IP協(xié)議綁定比在代碼里改任何東西都管用。這也說明部署上位機(jī)程序前先用簡單的網(wǎng)絡(luò)命令把鏈路探一遍能省下大量時間。5. 擴(kuò)展思路與長期維護(hù)建議5.1 從FX5U擴(kuò)展到其他兼容PLC這套基于MC協(xié)議3E幀的客戶端不止能用在FX5U上。我用類似代碼對接過匯川的H5U/EVO系列以及臺達(dá)某些支持SLMP的型號幀結(jié)構(gòu)基本通用只是軟元件編號和模塊參數(shù)略有差異。比如匯川的PLC也支持SLMPD寄存器代碼同樣是0xA8但I(xiàn)O編號可能不同需要根據(jù)實際PLC參數(shù)調(diào)整。如果你的項目里需要對接多種PLC建議在客戶端類里抽象一個軟元件映射接口不同的PLC型號實現(xiàn)不同的地址轉(zhuǎn)換器。這樣上層邏輯完全不用改切換設(shè)備時只換一個工廠類即可。5.2 與組態(tài)軟件和觸摸屏通信的差異觸摸屏比如三菱GOT、昆侖通泰、威綸通與PLC通信時通常用的是廠商各自的驅(qū)動底層雖然是類似MC協(xié)議的報文但通信細(xì)節(jié)是封閉的。自己開發(fā)上位機(jī)的好處是完全可控壞處是所有通信邏輯都得自己維護(hù)。項目里如果用組態(tài)軟件它的通信組件封裝的很好但要做定制邏輯時反而掣肘。我個人的經(jīng)驗是產(chǎn)線設(shè)備少、邏輯固定用組態(tài)軟件快設(shè)備種類多、要做復(fù)雜聯(lián)鎖邏輯或性能優(yōu)化時原生Socket方案更靈活。5.3 多套邏輯層面的健壯性設(shè)計通信代碼寫完不是終點長期穩(wěn)定運(yùn)行才是硬指標(biāo)。建議在客戶端增加幾個能力斷線自動重連、請求超時重試、通信狀態(tài)心跳檢測、日志記錄關(guān)鍵幀。心跳檢測可以用FX5U的特殊寄存器做周期讀取連續(xù)幾次失敗就判定斷線觸發(fā)報警和自動重連。日志方面記錄每次請求的軟元件、點數(shù)、結(jié)束代碼和耗時出問題時能快速定位。我見過太多現(xiàn)場問題因為沒日志而只能靠猜一個小小的時間戳函數(shù)排障效率提升不止一倍。5.4 從實戰(zhàn)角度再補(bǔ)充幾個心得再分享一個實用小技巧三菱FX5U的SLMP通信里讀寫M、X、Y這類位軟元件時命令格式跟D寄存器完全一樣只是軟元件代碼變了。很多剛上手的人以為位元件要特殊處理其實不需要。把X0到XF按字讀出來后每個bit就是獨(dú)立的X點狀態(tài)用移位就能提取到處理效率非常高。另一個技巧是批量讀取盡量不要超過500字一次。雖然協(xié)議理論上可以一次讀很多但實際測試下來一次讀太多數(shù)據(jù)會拉高PLC通信處理耗時也增加了斷包風(fēng)險。如果需要讀取大量數(shù)據(jù)分批讀比一次讀更穩(wěn)。最后代碼上線前一定做一輪異常測試拔網(wǎng)線重插、直接重啟PLC、在通信中途斷電。這種場景在工業(yè)現(xiàn)場太常見了代碼如果不能在異常情況下自動恢復(fù)再完美的正常邏輯也只是空中樓閣。這套做法我在好幾個項目里反復(fù)用過從FX5U擴(kuò)展對接其他PLC也只是改參數(shù)的事。通信底子打扎實了上層隨便怎么折騰都穩(wěn)。本文還有配套的精品資源點擊獲取