標籤:java版本 結果 bsp add 項目 adf api 開源 ascii
本文將使用一個Github開源的組件庫技術來讀寫三菱PLC和西門子plc資料,使用的是基於乙太網路的TCP/IP實現,不需要額外的組件,讀取操作只要放到後台線程就不會卡死線程,本組件支援超級方便的高效能讀寫操作
github地址:https://github.com/dathlin/HslCommunication 如果喜歡可以star或是fork,還可以打賞支援,打賞請認準原始碼項目。
本項目目前支援C#語言和java語言,C#語言的功能比較齊全,java版本的庫還在開發及完善中。
點擊下載本組件的jar包。HslCommunication.jar
代碼使用支援的例子可以參照C#版本的,兩者幾乎是一模一樣的,支援的資料類型也是一致的。
支援人員QQ群:592132877 (組件的版本更新細節也將第一時間在群裡發布)最後編輯日期:2018年6月13日 08:28:34
裡面各種小夥伴,為您解答資料互動,編程技巧,如果對本介面提供的API有任何疑問,都可以加群諮詢,如果有更好的建議,歡迎提出。
組件的完整資訊和API介紹參照:http://www.cnblogs.com/dathlin/p/7703805.html 組件的使用限制,更新日誌,都在該頁面裡面。
本文將展示如何配置網路參數及怎樣使用代碼來訪問PLC資料,希望給有需要的人解決一些實際問題。主要對三菱Q系列PLC的X,Y,M,L,B,V,F,S,D,W,R地區的資料讀寫,對西門子PLC的M,Q,I,DB塊的資料讀寫,親測有效。
此處使用了網線直接的方式,如果PLC接進了區域網路,就可以進行遠程讀寫了^_^
此處使用到了2個命名空間:
import HslCommunication.Core.Types.OperateResultExOne;
import HslCommunication.Profinet.Melsec.MelsecMcNet;
隨便聊聊
當我們一個上位機需要讀取100台西門子PLC裝置(此處只是舉個例子,凡是都是使用Modbus tcp的都是一樣的)的時候,你採用伺服器主動去請求100台裝置的機制對效能來說是個極大的考驗,如果開100個線程去輪詢100台裝置,那麼效能損失將是非常大的,更不用說再增加裝置,如果搭建Modbus tcp伺服器,就可以完美的解決效能問題,因為串連的壓力將會平均分攤給每一台PLC,伺服器端只要新增一個時間戳記就可以知道用戶端有沒有串連上。
我們在100台PLC裡都增加發送Modbus tcp方法,將資料發送到伺服器的ip和連接埠上去,伺服器根據站號來區分裝置。這樣就可以搭建一個高效能總站。 本組件支援快速搭建一個高效能的Modbus tcp總站。
http://www.cnblogs.com/dathlin/p/7782315.html
關於兩種模式
在PLC端,包括三菱,西門子,歐姆龍以及Modbus Tcp用戶端的訪問器上,都支援兩種模式,短串連模式和長串連模式,現在就來解釋下什麼原理。
短串連:每次讀寫都是一個單獨的請求,請求完畢也就關閉了,如果伺服器的連接埠僅僅支援單串連,那麼關閉後這個連接埠可以被其他串連複用,但是在頻繁的網路請求下,容易發生異常,會有其他的請求不成功,尤其是多線程的情況下。
長串連:建立一個公用的串連通道,所有的讀寫請求都利用這個通道來完成,這樣的話,讀寫效能更快速,即時多線程調用也不會影響,內部有同步機制。如果伺服器的連接埠僅僅支援單串連,那麼這個連接埠就被佔用了,比如三菱的連接埠機制,西門子的Modbus tcp連接埠機制也是這樣的。以下代碼預設使用長串連,效能更高,還支援多線程同步。
在短串連的模式下,每次請求都是單獨的訪問,所以沒有重連的困擾,在長串連的模式下,如果本次請求失敗了,在下次請求的時候,會自動重新串連伺服器,直到請求成功為止。另外,盡量所有的讀寫都對結果的成功進行判斷。
關於日誌記錄
暫時不支援
PLC的配置
環境1:此處以GX Works3為樣本,fx5u的配置如下:(感謝 山楂 提供的圖片)
環境2:此處以GX Works2為樣本,測試PLC為L02CPU,內建了乙太網路協議
環境3:此處以GX Works2為樣本,添加乙太網路模組,型號為QJ71E71-100,組態裡添加完成後進行乙太網路的參數配置,此處需要注意的是:參數的配置對接下來的代碼中配置參數要一一對應
注意:在PLC的乙太網路模組的配置中,無法設定網路號為0,也無法設定站號為0, 所以此處均設定為1,在C#程式中也使用上述的配置,在代碼中均配置為0,如果您自訂設定為網路2, 站號8,那麼在代碼中就要寫對應的資料。如果仍然通訊失敗,重新測試0,0。
開啟設定:在中的開啟設定選項,進行其他參數的配置,只是舉了一個例子,開通了4個連接埠來支援讀寫操作:
連接埠號碼設定規則:
- 為了不與原先存在的系統發生衝突,您在添加自己的連接埠時盡量使用您自己的連接埠。
- 如果讀寫都需要,儘可能的將讀取連接埠和寫入連接埠區分開來,這樣做比較高效能。
- 如果您的網路狀態不是特別穩定,讀取連接埠使用2個,一個受阻切換另一個讀取可以提升系統的穩定性。
本文檔僅作組件的測試,所以只用了一個連接埠作為讀寫。如果你的程式也使用了一個連接埠,那麼你在讀取資料時候, 剛好也在寫入(非同步作業可能發生這樣的情況),那麼寫入會失敗!)(在長串連模式下沒有這個問題)
三菱PLC的資料主要由兩類資料群組成,位元據和字資料,在位元據中,例如X,Y,M,L都是位元據,字資料例如D,W。 兩類的資料在讀取解碼上存在一點小差別。(事實上也可以先將16個M先賦值給一個D,讀取D資料再進行解析, 在讀取M的數量比較多的時候,這樣操作效率更高)
初始化訪問PLC對象
如果想使用本組件的資料讀取功能,必須先初始化Data Access Objects,根據實際情況進行資料的填入。 下面僅僅是測試中的資料:
MelsecMcNet melsec_net = new MelsecMcNet("192.168.1.192",6001);
然後你可以指定一些參數,網路號,網路站號之類的,通常的情況都是不需要指定的
melsec_net.setNetworkNumber((byte) 0x00); melsec_net.setNetworkStationNumber((byte) 0x00); melsec_net.setConnectTimeOut(1000);
開啟串連
melsec_net.ConnectServer();
如果想知道有沒有串連上去
OperateResult connectResult = melsec_net.ConnectServer(); if(connectResult.IsSuccess){ System.out.print("串連成功"); } else { System.out.print("串連失敗:"+connectResult.Message); }
關於地址的表示方式
使用字串表示,這個組件裡所有的讀寫操作提供字串表示的重載方法,所有的支援訪問的類型對應如下,字串的表示方式存在十進位和十六進位的區別:
- 輸入繼電器:"X100","X1A0" // 字串為十六進位機制
- 輸出繼電器:"Y100" ,"Y1A0" // 字串為十六進位機制
- 內部繼電器:"M100","M200" // 字串為十進位
- 鎖存繼電器:"L100" ,"L200" // 字串為十進位
- 通報器: "F100", "F200" // 字串為十進位
- 邊沿繼電器:"V100" , "V200" // 字串為十進位
- 連結繼電器:"B100" , "B1A0" // 字串為十六進位
- 步進繼電器:"S100" , "S200" // 字串為十進位
- 資料寄存器:"D100", "D200" // 字串為十進位
- 連結寄存器:"W100" ,"W1A0" // 字串為十六進位
- 檔案寄存器:"R100","R200" // 字串為十進位
關於資料分類
以上地址的資料是分為位元據和字資料的,位元據只能調用ReadBool,字資料用Read及其擴充的方法
簡單讀寫的樣本
boolean[] M100 = melsec_net.ReadBool("M100",(short) 1).Content; // 讀取M100是否通,十進位地址 boolean[] X1A0 = melsec_net.ReadBool("X1A0",(short) 1).Content; // 讀取X1A0是否通,十六進位地址 boolean[] Y1A0 = melsec_net.ReadBool("Y1A0",(short) 1).Content; // 讀取Y1A0是否通,十六進位地址 boolean[] B1A0 = melsec_net.ReadBool("B1A0",(short) 1).Content; // 讀取B1A0是否通,十六進位地址 short short_D1000 = melsec_net.ReadInt16("D1000").Content; // 讀取D1000的short值 ,W3C0,R3C0 效果是一樣的 int int_D1000 = melsec_net.ReadInt32("D1000").Content; // 讀取D1000-D1001組成的int資料 float float_D1000 = melsec_net.ReadFloat("D1000").Content; // 讀取D1000-D1001組成的float資料 long long_D1000 = melsec_net.ReadInt64("D1000").Content; // 讀取D1000-D1003組成的long資料 double double_D1000 = melsec_net.ReadDouble("D1000").Content; // 讀取D1000-D1003組成的double資料 String str_D1000 = melsec_net.ReadString("D1000", (short) 10).Content; // 讀取D1000-D1009組成的條碼資料 melsec_net.Write("M100", new boolean[] { true} ); // 寫入M100為通 melsec_net.Write( "Y1A0", new boolean[] { true } ); // 寫入Y1A0為通 melsec_net.Write( "X1A0", new boolean[] { true } ); // 寫入X1A0為通 melsec_net.Write( "B1A0", new boolean[] { true } ); // 寫入B1A0為通 melsec_net.Write( "D1000", (short)1234); // 寫入D1000 short值 ,W3C0,R3C0 效果是一樣的 melsec_net.Write( "D1000", 1234566); // 寫入D1000 int值 melsec_net.Write( "D1000", 123.456f); // 寫入D1000 float值 melsec_net.Write( "D1000", 123.456d); // 寫入D1000 double值 melsec_net.Write( "D1000", 123456661235123534L); // 寫入D1000 long值 melsec_net.Write( "D1000", "K123456789"); // 寫入D1000 string值
X,Y,M,L,F,V,B,S位元據的讀寫說明
- X 輸入繼電器
- Y 輸出繼電器
- M 內部繼電器
- L 鎖存繼電器
- F 通報器
- V 邊沿繼電器
- B 連結繼電器
- S 步進繼電器
本小節將展示八種位元據的讀取,雖然更多的時候只是讀取D資料即可,或者是將位元據批量挪到D資料中, 但是在此處仍然進行介紹單獨的讀取X,Y,M,L,F,V,B,S,由於這八種讀取手法一致,故針對M資料進行介紹,其他的您可以自己測試。
如下方法示範讀取了M200-M209這10個M的值,注意:讀取長度必須為偶數,即時寫了奇數,也會補齊至偶數,讀取和寫入的最大長度為7168,否則報錯。如需實際需求確實大於7168的,請分批次讀取。
傳回值解析:如果讀取正常則共返回10個位元組的資料,以下樣本資料進行批量化的讀取
OperateResultExOne<boolean[]> read = melsec_net.ReadBool("M100",(short)10); if(read.IsSuccess){ boolean m100 = read.Content[0]; boolean m101 = read.Content[1]; boolean m102 = read.Content[2]; boolean m103 = read.Content[3]; boolean m104 = read.Content[4]; boolean m105 = read.Content[5]; boolean m106 = read.Content[6]; boolean m107 = read.Content[7]; boolean m108 = read.Content[8]; boolean m109 = read.Content[9]; } else { System.out.print("讀取失敗:"+read.Message); }
// 寫入測試,M100-M104 寫入測試 此處寫入後M100:通 M101:斷 M102:斷 M103:通 M104:通 boolean[] values = new boolean[]{true,false,false,true,true}; OperateResult write = melsec_net.Write("M100",values); if(write.IsSuccess){ System.out.print("寫入成功"); } else { System.out.print("寫入失敗:"+write.Message); }
D,W,R字資料的讀寫操作
此處讀取針對中間存在整數資料的情況,因為兩者讀取方式相同,故而只示範一種資料讀取, 使用該組件讀取資料,一次最多讀取或寫入960個字,超出則失敗。 如果讀取的長度確實超過限制,請考慮分批讀取。
OperateResultExOne<byte[]> read1 = melsec_net.Read("D100",(short)5); if(read1.IsSuccess){ short D100 = melsec_net.getByteTransform().TransByte(read1.Content,0); short D101 = melsec_net.getByteTransform().TransByte(read1.Content,2); short D102 = melsec_net.getByteTransform().TransByte(read1.Content,4); short D103 = melsec_net.getByteTransform().TransByte(read1.Content,6); short D104 = melsec_net.getByteTransform().TransByte(read1.Content,8); } else { System.out.print("讀取失敗:"+read1.Message); }
// D100為1234,D101為8765,D102為1234,D103為4567,D104為-2563 short[] values2 = new short[]{1335, 8765, 1234, 4567, -2563 }; OperateResult write = melsec_net.Write("M100",values2); if(write.IsSuccess){ System.out.print("寫入成功"); } else { System.out.print("寫入失敗:"+write.Message); }
一個實際中複雜的例子示範
實際中可能碰到的情況會很複雜,一台裝置中需要上傳的資料包含了溫度,壓力,產量,規格等等資訊,在一串資料中 會包含各種各樣的不同的資料,上述的讀取D,讀取M,讀取條碼的方式不太好用,所以此處做一個完整樣本的示範,假設我們需要讀取 D4000-D4009的資料,假設D4000存放了溫度資料,55.1℃在D中為551,D4001存放了壓力資料,1.23MPa在D中存放為123,D4002存放了 裝置狀態,0為停止,1為運行,D4003存放了產量,1000就是指1000個,D4004備用,D4005-D4009存放了規格,以下代碼示範如何去解析資料:
//解析複雜資料 OperateResultExOne<byte[]> read3 = melsec_net.Read("D4000", (short) 10); if (read3.IsSuccess) { double 溫度 = melsec_net.getByteTransform().TransInt16(read3.Content, 0) / 10d;//索引很重要 double 壓力 = melsec_net.getByteTransform().TransInt16(read3.Content, 2) / 100d; boolean IsRun = melsec_net.getByteTransform().TransInt16(read3.Content, 4) == 1; int 產量 =melsec_net.getByteTransform().TransInt32(read3.Content, 6); String 規格 = melsec_net.getByteTransform().TransString(read3.Content, 10, 10,"ascii"); } else { System.out.print("讀取失敗:"+read3.Message); }
更詳細的資訊,可以參照原始碼裡面的測試專案。
java 讀寫三菱PLC 使用TCP/IP 協議