C#讀寫西門子PLC數據


C#讀寫西門子PLC數據,包含S7協議和Fetch/Write協議,s7支持200smart,300PLC,1200PLC,1500PLC

本文將使用一個gitHub開源的組件技術來讀寫西門子plc數據,使用的是基於以太網的TCP/IP實現,不需要額外的組件,讀取操作只要放到后台線程就不會卡死線程,本組件支持超級方便的高性能讀寫操作

官方地址:http://www.hslcommunication.cn/ 打賞請認准官網。

nuget地址:https://www.nuget.org/packages/HslCommunication/       nuget     下載

github地址:https://github.com/dathlin/HslCommunication      fork      star                 如果喜歡可以star或是fork,還可以打賞支持。

聯系作者及加群方式(激活碼在群里發放):http://www.hslcommunication.cn/Cooperation

 

在Visual Studio 中的NuGet管理器中可以下載安裝,也可以直接在NuGet控制台輸入下面的指令安裝

1
Install-Package HslCommunication

 如果需要教程:Nuget安裝教程:http://www.cnblogs.com/dathlin/p/7705014.html

 

組件的完整信息和其他API介紹參照:http://www.cnblogs.com/dathlin/p/7703805.html   組件的授權協議,更新日志,都在該頁面里面。

 

本文將展示如何配置網絡參數及怎樣使用代碼來訪問PLC數據,希望給有需要的人解決一些實際問題。主要對西門子PLC的M,Q,I,DB塊的數據讀寫,親測有效。

此處使用了網線直接的方式,如果PLC接進了局域網,就可以進行遠程讀寫了^_^

此處使用到了2個命名空間:

1
2
using  HslCommunication;
using  HslCommunication.Profinet.Siemens;

 

隨便聊聊


當我們一個上位機需要讀取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

 

關於兩種模式


本組件所提供的所有客戶端類,包括三菱,西門子,歐姆龍,modbus-tcp,以及SimplifyNet都是繼承自雙模式基類,雙模式包含了短連接和長連接,下面就具體介紹下兩個模式的區別

短連接:每次讀寫都是一個單獨的請求,請求完畢也就關閉了,如果服務器的端口僅僅支持單連接,那么關閉后這個端口可以被其他連接復用,但是在頻繁的網絡請求下,容易發生異常,會有其他的請求不成功,尤其是多線程的情況下。

長連接:創建一個公用的連接通道,所有的讀寫請求都利用這個通道來完成,這樣的話,讀寫性能更快速,即時多線程調用也不會影響,內部有同步機制。如果服務器的端口僅僅支持單連接,那么這個端口就被占用了,比如三菱的端口機制,西門子的Modbus tcp端口機制也是這樣的。以下代碼默認使用長連接,性能更高,還支持多線程同步。

在短連接的模式下,每次請求都是單獨的訪問,所以沒有重連的困擾,在長連接的模式下,如果本次請求失敗了,在下次請求的時候,會自動重新連接服務器,直到請求成功為止。另外,盡量所有的讀寫都對結果的成功進行判斷。

 

關於日志記錄


不管是三菱的數據訪問類,還是西門子的,還是Modbus tcp訪問類,都有一個LogNet屬性用來記錄日志,該屬性是一個接口類,ILogNet,凡事繼承該接口的都可以用來記錄日志,該日志會在訪問失敗時,尤其是因為網絡的原因導致訪問失敗時會進行日志記錄(如果你為這個 LogNet 屬性配置了真實的日志記錄器的話):如果你想使用該記錄日志的功能,請參照如下的博客進行實例化:

http://www.cnblogs.com/dathlin/p/7691693.html

舉個例子:

1
siemensTcpNet.LogNet =  new  HslCommunication.LogNet.LogNetSingle(  "log.txt"  );

 

 

關於兩種協議


本組件支持的西門子通信有兩種協議,一種是S7協議,在PLC側幾乎不需要配置參數,另一個協議Fetch/Write協議,相對比較麻煩一點,如果S7不方便讀取的話,可以選擇Fetch/Write,相對而言,S7更加方便點。

這兩個協議除了實例化的類型不一致,讀寫PLC的代碼和連接機制都是一致的,所以FW協議的具體代碼就不粘貼了,詳細參照下面的Demo項目。

 

訪問測試項目


在上述的github源代碼里有個測試項目,HslCommunicationDemo,里面包含了各種客戶端的Demo項目,不需要編寫任何的代碼就可以測試數據的訪問了。

下載地址為:HslCommunicationDemo.zip

 

演示項目


下面的三篇演示了具體如何去訪問PLC的數據,我們在訪問完成后,通常需要進行處理,以下的示例項目就演示了后台從PLC讀取數據后,前台顯示並推送給所有在線客戶端的功能,客戶端並進行圖形化顯示,具有一定的參考意義,項目地址為:

https://github.com/dathlin/RemoteMonitor

下面的圖片示例中的左邊程序就是服務器程序,它應該和PLC直接連接並接入局域網,然后把數據推送給客戶端顯示。注意:一個復雜高級的程序就應該把處理邏輯程序和界面程序分開,比如這里的服務器程序實現數據采集,推送,存儲。讓客戶端程序去實現數據的整理,分析,顯示,這樣即使客戶端程序因為BUG奔潰,服務器端仍然可以正常的工作。

 

 

S7協議下的tcp直接通訊,配置簡單,一般PLC都支持


 

測試通過的PLC:1200系列 本人親測

                          200smart  感謝 無名①終止^^ 的測試

                          300系列  感謝 懂PLC不懂c# 的測試

                          1500系列 感謝 ∮溪風-⊙_⌒ 的測試

 

 報文的格式參考了如下的兩篇文章

http://www.itpub.net/thread-2052649-1-1.html

https://wenku.baidu.com/view/d93b88b06394dd88d0d233d4b14e852459fb3912.html

如果你擅長於網絡通信和組件開發,可以通過報文格式開發出自己的西門子通信庫,我所做的就是基於報文格式進行了二次封裝,隱藏了socket通信的細節,還包含了異常處理,提供了簡單方便的API來讀寫數據。提供了整數數據的讀寫,字符串讀寫,來豐富各種需求,從事實上來說,只要可以讀寫字節,相當於任何數據了。

准備:在西門子PLC上配置好IP地址,就只有一個IP地址就夠了,然后打開電腦的cmd指令,只要能ping通西門子PLC即可。

還需要在PLC側配置打開  GET/SET通訊允許:(感謝網友 OLIFE 提供的圖片) (如果碰到讀取數據時出現長度驗證失敗的信息,請務必檢查下面的勾是否打上)

 

 

實例化:

1
2
3
siemensTcpNet =  new  SiemensS7Net( SiemensPLCS.S1200,  "192.168.0.100"  ) {
                 ConnectTimeOut = 5000
             };

如果你的PLC是其他系列的,就修改上面的枚舉值,本組件支持的西門子型號都在里面。

 

 

連接服務器,也可以放在窗口的Load方法中,一般建議使用長連接,速度更快,又是線程安全的(調用下面的方法就是使用了長連接,如果不連接直接讀取數據,那就是短連接):

1
2
3
4
5
6
7
8
9
OperateResult connect = siemensTcpNet.ConnectServer( );
if  (connect.IsSuccess)
{
     MessageBox.Show(  "連接成功!"  );
}
else
{
     MessageBox.Show(  "連接失敗!"  );
}

 

斷開連接,也就是關閉了長連接,如果再去請求數據,就變成了短連接

1
siemensTcpNet.ConnectClose( );

 

下面就演示一些簡單的數據操作,省去了對結果是否成功的驗證,所有的讀寫結果都是OperateResult類型及派生類型,都有一個IsSuccess屬性來判斷成功與否

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 讀取操作,這里的M100可以替換成I100,Q100,DB20.100效果時一樣的
bool  M100_7 = siemensTcpNet.ReadBool(  "M100.7"  ).Content;   // 讀取M100.7是否通斷,注意M100.0等同於M100
byte  byte_M100 = siemensTcpNet.ReadByte(  "M100"  ).Content;  // 讀取M100的值
short  short_M100 = siemensTcpNet.ReadInt16(  "M100"  ).Content;  // 讀取M100-M101組成的字
ushort  ushort_M100 = siemensTcpNet.ReadUInt16(  "M100"  ).Content;  // 讀取M100-M101組成的無符號的值
int  int_M100 = siemensTcpNet.ReadInt32(  "M100"  ).Content;          // 讀取M100-M103組成的有符號的數據
uint  uint_M100 = siemensTcpNet.ReadUInt32(  "M100"  ).Content;       // 讀取M100-M103組成的無符號的值
float  float_M100 = siemensTcpNet.ReadFloat(  "M100"  ).Content;    // 讀取M100-M103組成的單精度值
long  long_M100 = siemensTcpNet.ReadInt64(  "M100"  ).Content;       // 讀取M100-M107組成的大數據值
ulong  ulong_M100 = siemensTcpNet.ReadUInt64(  "M100"  ).Content;    // 讀取M100-M107組成的無符號大數據
double  double_M100 = siemensTcpNet.ReadDouble(  "M100"  ).Content;  // 讀取M100-M107組成的雙精度值
string  str_M100 = siemensTcpNet.ReadString(  "M100" , 10 ).Content; // 讀取M100-M109組成的ASCII字符串數據
 
// 寫入操作,這里的M100可以替換成I100,Q100,DB20.100效果時一樣的
siemensTcpNet.Write(  "M100.7" true  );                 // 寫位,注意M100.0等同於M100
siemensTcpNet.Write(  "M100" , ( byte )0x33 );             // 寫單個字節
siemensTcpNet.Write(  "M100" , ( short )12345 );           // 寫雙字節有符號
siemensTcpNet.Write(  "M100" , ( ushort )45678 );          // 寫雙字節無符號
siemensTcpNet.Write(  "M100" , 123456789 );              // 寫雙字有符號
siemensTcpNet.Write(  "M100" , ( uint )3456789123 );       // 寫雙字無符號
siemensTcpNet.Write(  "M100" , 123.456f );               // 寫單精度
siemensTcpNet.Write(  "M100" , 1234556434534545L );      // 寫大整數有符號
siemensTcpNet.Write(  "M100" , 523434234234343UL );      // 寫大整數無符號
siemensTcpNet.Write(  "M100" , 123.456d );               // 寫雙精度
siemensTcpNet.Write(  "M100" "K123456789"  ); // 寫ASCII字符串

 

下面說明復雜的數據操作,以及批量化的數據操作,例如讀取M100-M109

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
OperateResult< byte []> read = siemensTcpNet.Read(  "M100" , 10 );
{
     if (read.IsSuccess)
     {
         byte  m100 = read.Content[0];
         byte  m101 = read.Content[1];
         byte  m102 = read.Content[2];
         byte  m103 = read.Content[3];
         byte  m104 = read.Content[4];
         byte  m105 = read.Content[5];
         byte  m106 = read.Content[6];
         byte  m107 = read.Content[7];
         byte  m108 = read.Content[8];
         byte  m109 = read.Content[9];
     }
     else
     {
         // 發生了異常
     }
}

這樣就把所有的字節數據都提取上來了,如果數據比較復雜,還可以根據實際情況處理。當然也支持批量的寫入數據信息

如果想實現自定義的數據類型,需要繼承一個接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public  class  UserType : HslCommunication.IDataTransfer
{
     #region IDataTransfer
 
     private  HslCommunication.Core.IByteTransform ByteTransform =  new  HslCommunication.Core.ReverseBytesTransform( );
 
 
     public  ushort  ReadCount => 20;
 
     public  void  ParseSource(  byte [] Content )
     {
         int  count = ByteTransform.TransInt32( Content, 0 );
         float  temp = ByteTransform.TransSingle( Content, 4 );
         short  name1 = ByteTransform.TransInt16( Content, 8 );
         string  barcode = Encoding.ASCII.GetString( Content, 10, 10 );
     }
 
     public  byte [] ToSource( )
     {
         byte [] buffer =  new  byte [20];
         ByteTransform.TransByte( count ).CopyTo( buffer, 0 );
         ByteTransform.TransByte( temp ).CopyTo( buffer, 4 );
         ByteTransform.TransByte( name1 ).CopyTo( buffer, 8 );
         Encoding.ASCII.GetBytes( barcode ).CopyTo( buffer, 10 );
         return  buffer;
     }
 
 
     #endregion
 
 
     #region Public Data
 
     public  int  count {  get set ; }
 
     public  float  temp {  get set ; }
 
     public  short  name1 {  get set ; }
 
     public  string  barcode {  get set ; }
 
     #endregion
}

  這樣我們就是可以實現特殊數據的讀寫了

1
2
3
4
5
6
7
OperateResult<UserType> read = siemensTcpNet.ReadCustomer<UserType>(  "M100"  );
if  (read.IsSuccess)
{
     UserType value = read.Content;
}
// write value
siemensTcpNet.WriteCustomer(  "M100" new  UserType( ) );

  

支持M,I,Q,DB,T,C數據的讀寫操作

 

究極數據的讀取:

此處提供一個核心的報文讀取機制,你可以自己傳入自己的報文,然后接收服務器的報文,再自己解析操作,可以根據報文格式實現任意的操作,當然,前提是需要報文支持。假設我要實現寫入M100,為0x3B,那么最終的報文為

03 00 00 24 02 F0 80 32 01 00 00 00 01 00 0E 00 05 05 01 12 0A 10 02 00 01 00 00 83 00 03 20 00 04 00 08 3B

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private  void  userButton23_Click_1( object  sender, EventArgs e)
{
     byte [] buffer = HslCommunication.BasicFramework.SoftBasic.HexStringToBytes(
         "03 00 00 24 02 F0 80 32 01 00 00 00 01 00 0E 00 05 05 01 12 0A 10 02 00 01 00 00 83 00 03 20 00 04 00 08 3B" );
     OperateResult< byte []> operate = siemensTcpNet.ReadFromServerCore(buffer);
     if  (operate.IsSuccess)
     {
         // 顯示服務器返回的報文
         TextBoxAppendStringLine(HslCommunication.BasicFramework.SoftBasic.ByteToHexString(operate.Content));
     }
     else
     {
         // 顯示網絡錯誤
         MessageBox.Show(operate.ToMessageShowString());
     }
}

 

更詳細的信息,可以參照源代碼里面的測試項目。

 

如果使用Fetch/Write協議進行讀寫操作,PLC端的配置不一致,實例化的類不一致,其他都是一樣的,不再贅述了,就重點說明下PLC網絡模塊的配置


 

環境:此處使用了STEP 7V5.5 sp4編程軟件作為示例,在添加以太網模塊(6GK7 343-1EX30-0E0 CP343-1)到組態中時,可以設置IP地址及子網掩碼, 此處測試使用,所以不使用路由器,如果您的西門子需要連接到內網中的話,需要配置路由器。目前只支持M,I,Q數據的讀寫。 然后點擊新建,創建一個Ethernet(1)網絡。以太網參數配置如下圖:



將以太網的模塊添加到機架中以后,現在打開網絡組態 ,打開后點擊組態上的PLC模塊。會出現如下界面,在箭頭出進行雙擊操作,可以彈出對話框,並進行一系列操作:

 

 

 



按照上面一套操作下來,創建了一個讀取的端口,端口號為2000,后面有用,需要記住, 按照上述的步驟再創建一個寫入的端口,只有最后一步不一致,如下:



配置完之后的效果圖如下,新建了兩個端口,一個用於讀取數據,一個用於寫入數據。 <strong>注意:設置完成后一定要寫入到PLC才算真的完成。</strong>



如上圖所示,上圖配置錯誤,應該配置一個同時支持讀寫的操作的端口

 

 

實例化的類的時候

1
private  SiemensFetchWriteNet siemensFWNet =  null ;

  

1
2
3
4
5
6
siemensFWNet =  new  SiemensFetchWriteNet( )
             {
                 IpAddress =  "192.168.0.100" ,
                 Port = 2000,
                 ConnectTimeOut = 5000,
             };

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM