歡迎光臨
每天分享高質量文章

工業通訊的開源專案 HslCommunication 介紹

前言:


本專案的孵化說來也是機緣巧合的事,本人於13年大學畢業後去了一家大型的國企工作,慢慢的走上了工業軟體,上位機軟體開發的道路。於14年正式開發基於windows的軟體,當時可選的技術棧就是MFC和C#的winform,後來就發現C#的更為簡單一些,那就直接乾,先做再說。工廠的基本主要需求就是工藝引數的歷史追述,附帶一些實時監控裝置的功能,那麼第一道攔路虎就是如何將三菱的PLC(邏輯控制器,通常作為裝置的核心控制單元)的資料給拿到我的軟體中來呢?這真是一個棘手的問題啊,首先就是百度,搜尋到了MX component元件,初步試了試,真的比較麻煩。然後就去看看有沒有其他的方式實現,後來就搜尋到了三菱的乙太網模組,下載到了支援的通訊說明,長篇大論。邊測試邊開發,勉勉強強讀到了我想要讀的資料(當然,這時候的程式碼基本都是寫死的),在接下來的兩三年裡,接觸並開發了好幾個類似的專案,通常工業軟體的需求是採集,分析,存資料庫,顯示。後來對通訊的理解深入,由單機軟體發展成了CS架構的軟體。後來在17年趁著換工作和考駕照的間隙,梳理了上份工作積累的經驗,和實際的需求,整理成了HslCommunication,並將之開源出來,初步的功能是三菱PLC的資料讀寫,C#軟體之間的資料通訊。後來又集成了modbus協議,西門子,歐姆龍,ab plc,三菱串列埠等等,都是後話的事了。

做這個專案的標的和開源的初衷是方便廣大的像我這種的在工廠一線的軟體工程師,我一直覺得我們不應該把自己看做是程式員,程式員的角色更像是碼農,主要工作就是敲程式碼,而軟體工程師應該是更大的定義,設計軟體的整體架構和開發的。這幾年大多數工作都開始意識到工業軟體,上位機軟體,資料追述系統,SCADA軟體,MES軟體開發的重要性,所以像我這樣的有通訊需求的人應該不在少數,況且開源有助於別人來一起改進,和程式碼測試。所以在開源之後,在部落格園就陸陸續續的寫了一些文章,比如如何使用C#和三菱PLC通訊,C#和西門子通訊等等。從部落格園的點選量來看,確實有大量的工廠的程式員有這方便的需求,而直接採用socket來開發,比較晦澀難懂,坑又比較多,事實上確實有很多人來報告了bug。幫助我修複了這個元件,提高了穩定性。

HslCommunication 能幹什麼?


相比大多數人比較關心這個問題,簡單的說,這個元件主要是用於工業通訊的,也有兩個程式之間的通訊,還有其他雜七雜八的功能,更像是我的工具外掛。各種小功能,擴充套件的小功能等等。直接上圖:

這是這個開源專案的demo程式,基本上將80%的功能列舉出來了,當然還有一些小功能沒有列舉。大多數支援的裝置都在上面進行顯示了,可以方便的進行測試,看看是不是可以實現讀寫的操作(對現場實際在生產的裝置應當註意寫入不正確的資料會導致意外事故發生)。比如我們來看看三菱的PLC的demo程式:

其他的截圖畫面就不一一舉例了,都是類似或是基本類似的。可以方便的使用demo進行測試。

特別註意,本元件實現的所有的通訊都是基於socket直接實現的,通訊部分不依賴任何第三方通訊庫或是元件安裝,也就是說,你拿個dll可以直接和PLC通訊,這對於部署,開發除錯,升級都是非常方便的。

通訊核心說明


整個網路類的核心在於 NetworkBase類

這個類實現了基礎的位元組收發功能和連線斷開功能。接下來就是 NetworkDoubleBase 類的實現,實現了長短連線的操作,在我們實際讀寫裝置的過程中,網路狀況往往是差別很大,所以本專案的初衷就是同時支援長連線和短連線。

實現了長短的連線後,還要實現裝置的BCL型別的讀寫,本質是基於byte陣列和C#基礎型別的轉換,但是這裡有個問題,不同的PLC,modbus協議對於轉換的格式不是固定的,有可能是一樣的,有可能不是一樣的,所以又抽象出來一個 IByteTransform 介面

這個介面整合到了下麵的裝置互動的基類 NetworkDeviceBase 裡,這個基類實現了一些基礎的型別的資料讀寫。

所以到這裡可以看到,從NetworkDeviceBase類繼承出去的裝置類(大部分的裝置通訊協議都是從這個繼承出去的),其基本的讀寫程式碼都是一致的,關於解析協議,通訊的底層都是封裝完畢。

通訊舉例說明


先舉例說明三菱PLC的讀寫操作:

// 實體化物件,指定PLC的ip地址和埠號
MelsecMcNet melsecMc = new MelsecMcNet( “192.168.1.110”, 6000 );
// 連線物件
OperateResult connect = melsecMc.ConnectServer( );
if (!connect.IsSuccess)
{
    Console.WriteLine( “connect failed:” + connect.Message );
    return;
}
// 舉例讀取D100的值
short D100 = melsecMc.ReadInt16( “D100” ).Content;
melsecMc.ConnectClose( );

經過層層封裝後,讀寫的邏輯精簡為,實體化,連線,讀寫,關閉。無論是三菱的PLC,還是西門子的PLC,都是一致的,因為基類的模型都是一致的。

// 實體化物件,指定PLC的ip地址和埠號
SiemensS7Net siemens = new SiemensS7Net( SiemensPLCS.S1200, ” 192.168.1.110″ );
// 連線物件
OperateResult connect = siemens.ConnectServer( );
if (!connect.IsSuccess)
{
    Console.WriteLine( “connect failed:” + connect.Message );
    return;
}
// 舉例讀取M100的值
short M100 = siemens.ReadInt16( “M100” ).Content;
siemens.ConnectClose( );

當然,支援大多數的C#型別資料讀寫

MelsecMcNet melsec_net = new MelsecMcNet( “192.168.0.100”, 6000 );
// 此處以D暫存器作為示例
short short_D1000 = melsec_net.ReadInt16( “D1000” ).Content;         // 讀取D1000的short值
ushort ushort_D1000 = melsec_net.ReadUInt16( “D1000” ).Content;      // 讀取D1000的ushort值
int int_D1000 = melsec_net.ReadInt32( “D1000” ).Content;             // 讀取D1000-D1001組成的int資料
uint uint_D1000 = melsec_net.ReadUInt32( “D1000” ).Content;          // 讀取D1000-D1001組成的uint資料
float float_D1000 = melsec_net.ReadFloat( “D1000” ).Content;         // 讀取D1000-D1001組成的float資料
long long_D1000 = melsec_net.ReadInt64( “D1000” ).Content;           // 讀取D1000-D1003組成的long資料
ulong ulong_D1000 = melsec_net.ReadUInt64( “D1000” ).Content;        // 讀取D1000-D1003組成的long資料
double double_D1000 = melsec_net.ReadDouble( “D1000” ).Content;      // 讀取D1000-D1003組成的double資料
string str_D1000 = melsec_net.ReadString( “D1000”, 10 ).Content;     // 讀取D1000-D1009組成的條碼資料
// 讀取陣列
short[] short_D1000_array = melsec_net.ReadInt16( “D1000”, 10 ).Content;         // 讀取D1000的short值
ushort[] ushort_D1000_array = melsec_net.ReadUInt16( “D1000”, 10 ).Content;      // 讀取D1000的ushort值
int[] int_D1000_array = melsec_net.ReadInt32( “D1000”, 10 ).Content;             // 讀取D1000-D1001組成的int資料
uint[] uint_D1000_array = melsec_net.ReadUInt32( “D1000”, 10 ).Content;          // 讀取D1000-D1001組成的uint資料
float[] float_D1000_array = melsec_net.ReadFloat( “D1000”, 10 ).Content;         // 讀取D1000-D1001組成的float資料
long[] long_D1000_array = melsec_net.ReadInt64( “D1000”, 10 ).Content;           // 讀取D1000-D1003組成的long資料
ulong[] ulong_D1000_array = melsec_net.ReadUInt64( “D1000”, 10 ).Content;        // 讀取D1000-D1003組成的long資料
double[] double_D1000_array = melsec_net.ReadDouble( “D1000”, 10 ).Content;      // 讀取D1000-D1003組成的double資料

寫入的操作:

MelsecMcNet melsec_net = new MelsecMcNet( “192.168.0.100”, 6000 );
// 此處以D暫存器作為示例
melsec_net.Write( “D1000”, (short)1234 );                // 寫入D1000  short值  ,W3C0,R3C0 效果是一樣的
melsec_net.Write( “D1000”, (ushort)45678 );              // 寫入D1000  ushort值
melsec_net.Write( “D1000”, 1234566 );                    // 寫入D1000  int值
melsec_net.Write( “D1000”, (uint)1234566 );               // 寫入D1000  uint值
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”, 523456661235123534UL );          // 寫入D1000  ulong值
melsec_net.Write( “D1000”, “K123456789” );                // 寫入D1000  string值
// 讀取陣列
melsec_net.Write( “D1000”, new short[] { 123, 3566, -123 } );                // 寫入D1000  short值  ,W3C0,R3C0 效果是一樣的
melsec_net.Write( “D1000”, new ushort[] { 12242, 42321, 12323 } );              // 寫入D1000  ushort值
melsec_net.Write( “D1000”, new int[] { 1234312312, 12312312, -1237213 } );                    // 寫入D1000  int值
melsec_net.Write( “D1000”, new uint[] { 523123212, 213,13123 } );               // 寫入D1000  uint值
melsec_net.Write( “D1000”, new float[] { 123.456f, 35.3f, -675.2f } );                    // 寫入D1000  float值
melsec_net.Write( “D1000”, new double[] { 12343.542312d, 213123.123d, -231232.53432d } );                    // 寫入D1000  double值
melsec_net.Write( “D1000”, new long[] { 1231231242312,34312312323214,-1283862312631823 } );          // 寫入D1000  long值
melsec_net.Write( “D1000”, new ulong[] { 1231231242312, 34312312323214, 9731283862312631823 } );          // 寫入D1000  ulong值

這裡舉例了三菱的PLC,實際上各種PLC的操作都是類似的。

 Redis實現


除了上述的基本的裝置通訊,還實現了redis資料庫讀寫操作,分了兩個類實現,下圖為一般的通訊功能

同時demo中實現了一個瀏覽redis伺服器的介面功能

最後的總結


本通訊庫實現了.net 3.5 和 .net 4.5的框架,還附帶了一些簡單的控制元件,此外還實現了.net standard版本,已在linux測試成功,由於官方在.net core2.2中還未實現串列埠類,所以暫時沒有實現串列埠相關的。

未來的方向,希望繼續最佳化程式碼,架構,整合實現更多裝置通訊,方便廣大的網友直接開發測試。

開源地址:https://github.com/dathlin/HslCommunication

更多詳細的內容請檢視原始碼的readme檔案。

 

原文地址:https://www.cnblogs.com/dathlin/p/10390311.html


.NET社群新聞,深度好文,歡迎訪問公眾號文章彙總 http://www.csharpkit.com


 

    閱讀原文

    贊(0)

    分享創造快樂