C#讀寫三菱Fx PLC 使用Fx 串口協議 讀寫Fx3U設備


本文將使用一個Github開源的組件庫技術來讀寫三菱 FX PLC,使用的是基於串口的實現,不需要額外的組件,讀取操作只要放到后台線程就不會卡死線程,本組件支持超級方便的高性能讀寫操作

github地址:https://github.com/dathlin/HslCommunication 如果喜歡可以star或是fork,還可以打賞支持,打賞請認准源代碼項目。

官網:http://www.hslcommunication.cn/     官網包含了詳細的API文檔

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

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

 

從NUGET安裝


Install-Package HslCommunication

  

准備


 

本文將展示如何配置網絡參數及怎樣使用代碼來訪問PLC數據,希望給有需要的人解決一些實際問題。主要對三菱Fx系列PLC的X,Y,M,S,C,T,D區域的數據讀寫

此處使用了串口直接的方式

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

using HslCommunication.Profinet.Melsec;
using HslCommunication;

  

隨便聊聊


當我們一個上位機需要讀取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端口機制也是這樣的。以下代碼默認使用長連接,性能更高,還支持多線程同步。

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

 

關於日志記錄


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

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

 

訪問測試項目


下面的一個項目是這個組件的訪問測試項目,您可以進行初步的訪問的測試,免去了您寫測試程序的麻煩,三菱的界面和西門子的界面幾乎是一致的。可以同時參考。該項目位於本篇文章開始處的Gitbub源代碼里面的

下載地址為:HslCommunicationDemo.zip 

 

 

演示項目,和本串口的信息無關


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

https://github.com/dathlin/RemoteMonitor

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

 

初始化訪問PLC對象

如果想使用本組件的數據讀取功能,必須先初始化數據訪問對象,根據實際情況進行數據的填入。 下面僅僅是測試中的數據:

MelsecFxSerial melsecFx = new MelsecFxSerial( );
melsecFx.SerialPortInni( sp =>
  {
     sp.PortName = "COM1";
     sp.BaudRate = 9600;
     sp.DataBits = 7;
     sp.StopBits = System.IO.Ports.StopBits.One;
      sp.Parity = System.IO.Ports.Parity.Even;
   } );
 melsecFx.Open( );

  串口的初始化操作一定要正確

 

關於地址的表示方式

使用字符串表示,這個組件里所有的讀寫操作提供字符串表示的重載方法,所有的支持訪問的類型對應如下,字符串的表示方式存在十進制和八進制的區別:

  • 輸入繼電器:"X10", "X17"               // 字符串為八進制機制
  • 輸出繼電器:"Y10" , "Y17"              // 字符串為八進制機制
  • 內部繼電器:"M100", "M200"          // 字符串為十進制
  • 步進繼電器:"S100" , "S200"          // 字符串為十進制
  • 數據寄存器:"D100", "D200"           // 字符串為十進制
  • 計數寄存器:"C100" , "C200"          // 字符串為十進制
  • 定時寄存器:"T100" , "T200"           // 字符串為十進制

 

展示一些簡單實用基礎數據讀寫,這些數據的讀寫沒有進行嚴格的是否成功判斷(判斷方法參照后面的代碼),一般網絡良好的情況下都會成功,但不排除失敗,以下代碼僅作測試,所有沒有嚴格判斷是否成功:

            // 此處以D寄存器作為示例
            short short_D1000 = melsecFx.ReadInt16( "D100" ).Content;         // 讀取D1000的short值 
            ushort ushort_D1000 = melsecFx.ReadUInt16( "D100" ).Content;      // 讀取D1000的ushort值
            int int_D1000 = melsecFx.ReadInt32( "D100" ).Content;             // 讀取D1000-D1001組成的int數據
            uint uint_D1000 = melsecFx.ReadUInt32( "D100" ).Content;          // 讀取D1000-D1001組成的uint數據
            float float_D1000 = melsecFx.ReadFloat( "D100" ).Content;         // 讀取D1000-D1001組成的float數據
            long long_D1000 = melsecFx.ReadInt64( "D100" ).Content;           // 讀取D1000-D1003組成的long數據
            ulong ulong_D1000 = melsecFx.ReadUInt64( "D100" ).Content;        // 讀取D1000-D1003組成的long數據
            double double_D1000 = melsecFx.ReadDouble( "D100" ).Content;      // 讀取D1000-D1003組成的double數據
            string str_D1000 = melsecFx.ReadString( "D100", 10 ).Content;     // 讀取D1000-D1009組成的條碼數據

            // 讀取數組
            short[] short_D1000_array = melsecFx.ReadInt16( "D100", 10 ).Content;         // 讀取D1000的short值 
            ushort[] ushort_D1000_array = melsecFx.ReadUInt16( "D100", 10 ).Content;      // 讀取D1000的ushort值
            int[] int_D1000_array = melsecFx.ReadInt32( "D100", 10 ).Content;             // 讀取D1000-D1001組成的int數據
            uint[] uint_D1000_array = melsecFx.ReadUInt32( "D100", 10 ).Content;          // 讀取D1000-D1001組成的uint數據
            float[] float_D1000_array = melsecFx.ReadFloat( "D100", 10 ).Content;         // 讀取D1000-D1001組成的float數據
            long[] long_D1000_array = melsecFx.ReadInt64( "D100", 10 ).Content;           // 讀取D1000-D1003組成的long數據
            ulong[] ulong_D1000_array = melsecFx.ReadUInt64( "D100", 10 ).Content;        // 讀取D1000-D1003組成的long數據
            double[] double_D1000_array = melsecFx.ReadDouble( "D100", 10 ).Content;      // 讀取D1000-D1003組成的double數據

  寫入操作

            // 此處以D寄存器作為示例
            melsecFx.Write( "D100", (short)1234 );                // 寫入D1000  short值  ,W3C0,R3C0 效果是一樣的
            melsecFx.Write( "D100", (ushort)45678 );              // 寫入D1000  ushort值
            melsecFx.Write( "D100", 1234566 );                    // 寫入D1000  int值
            melsecFx.Write( "D100", (uint)1234566 );               // 寫入D1000  uint值
            melsecFx.Write( "D100", 123.456f );                    // 寫入D1000  float值
            melsecFx.Write( "D100", 123.456d );                    // 寫入D1000  double值
            melsecFx.Write( "D100", 123456661235123534L );          // 寫入D1000  long值
            melsecFx.Write( "D100", 523456661235123534UL );          // 寫入D1000  ulong值
            melsecFx.Write( "D100", "K123456789" );                // 寫入D1000  string值

            // 讀取數組
            melsecFx.Write( "D100", new short[] { 123, 3566, -123 } );                // 寫入D1000  short值  ,W3C0,R3C0 效果是一樣的
            melsecFx.Write( "D100", new ushort[] { 12242, 42321, 12323 } );              // 寫入D1000  ushort值
            melsecFx.Write( "D100", new int[] { 1234312312, 12312312, -1237213 } );                    // 寫入D1000  int值
            melsecFx.Write( "D100", new uint[] { 523123212, 213, 13123 } );               // 寫入D1000  uint值
            melsecFx.Write( "D100", new float[] { 123.456f, 35.3f, -675.2f } );                    // 寫入D1000  float值
            melsecFx.Write( "D100", new double[] { 12343.542312d, 213123.123d, -231232.53432d } );                    // 寫入D1000  double值
            melsecFx.Write( "D100", new long[] { 1231231242312, 34312312323214, -1283862312631823 } );          // 寫入D1000  long值
            melsecFx.Write( "D100", new ulong[] { 1231231242312, 34312312323214, 9731283862312631823 } );          // 寫入D1000  ulong值

  

讀取Bool示例:

            // 以下是簡單的讀取,沒有仔細校驗的方式
            bool X1 = melsecFx.ReadBool( "X1" ).Content;
            bool[] X1_10 = melsecFx.ReadBool( "X1", 10 ).Content;

            // 如果需要判斷是否讀取成功
            OperateResult<bool> R_X1 = melsecFx.ReadBool( "X1" );
            if (R_X1.IsSuccess)
            {
                // success
                bool value = R_X1.Content;
            }
            else
            {
                // failed
            }


            OperateResult<bool[]> R_X1_10 = melsecFx.ReadBool( "X1", 10 );
            if (R_X1_10.IsSuccess)
            {
                // success
                bool x1 = R_X1_10.Content[0];
                bool x2 = R_X1_10.Content[1];
                bool x3 = R_X1_10.Content[2];
                bool x4 = R_X1_10.Content[3];
                bool x5 = R_X1_10.Content[4];
                bool x6 = R_X1_10.Content[5];
                bool x7 = R_X1_10.Content[6];
                bool x8 = R_X1_10.Content[7];
                bool x9 = R_X1_10.Content[8];
                bool xa = R_X1_10.Content[9];
            }
            else
            {
                // failed
            }

  

寫入單個的bool,不支持批量寫入

            // 以下是簡單的寫入,沒有仔細校驗的方式
            melsecFx.Write( "M100", true );

            // 如果需要判斷是否讀取成功
            OperateResult write1 = melsecFx.Write( "M100", true );
            if (write1.IsSuccess)
            {
                // success
            }
            else
            {
                // failed
            }

  

更多的操作和細節可以參照DEMO項目和源代碼項目。


免責聲明!

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



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