1. 背景
OPC Data Access 規範是基於COM/DCOM定義的,因此大多數的OPC DA Server和client都是基於C++開發的,因爲C++對COM/DCOM有最好的支持。現在,隨着微軟的開發平臺漸漸的轉移到.NET框架上,好多OPC Client程序都需要建立在.NET平臺上,用.NET提供的技術開發OPC Client就成爲一種需求。網上很多網友都有提過,.NET開發OPC Client不外乎下面三種方法:
- 使用OPCNetAPI 2.0,需要用到OPCNetAPI.dll,OPCNetAPI.Com.dll;
- 使用自動化接口,需要用到OPCDAAuto.dll;
- 使用自定義接口,需要用到多個.NET Wrapper:OpcRcw.Ae.dll,OpcRcw.Batch.dll,OpcRcw.Comn.dll,OpcRcw.Da.dll,OpcRcw.Dx.dll,OpcRcw.Hda.dll,OpcRcw.Sec.dll;
OPCNetAPI 2.0由OPC foundation提供,只有註冊會員才能得到,是需要付費的。其他的dll不需要付費,很容易得到。網上有網友已經介紹過使用OPCDAAuto.dll開發.NET Client的方法, 這種方法的優點是比較簡單,缺點是不夠靈活。本文使用自定義接口,藉助OpcRcw.Da.dll,開發出一個OPC .NET Client的類庫,可供其他client程序調用。
必要文件:
OpcRcw.Comn.dll --- 包含對IConnectionPointContainer的包裝。
OpcRcw.Da.dll ---.NET 對OPC COM 接口 定義的包裝。
適應版本:
OPC Data Access specification 2.05
說明:
該類庫正在開發中,這是第一個版本,只實現了一些基本功能,好多功能如OPC Browse等還未實現,代碼也未經過測試,存在bug在所難免,感興趣的朋友請繼續關注。。。
2. VS2008工程項目文件
- 基本類庫視圖
下圖是OpcDa.Client組件實現的基本類庫:
3. 類庫實現的基本功能
OpcServer:
Connect | 連接OPC Server。 |
Disconnect | 斷開Server。 |
GetStatus | 獲得Server的當前狀態,返回ServerStatus。 |
AddGroup | 添加group |
RemoveGroup | 刪除group |
FindGroupByName | 通過名字獲取OpcGroup對象 |
Opc Group:
AddItems | 添加Opc Items到組 |
RemoveItems | 刪除items |
AsyncRead | 異步讀取Items,調用IOPCAsyncIO2::Read接口 |
AsyncWrite | 異步寫items,調用IOPCAsyncIO2::Write接口 |
Refresh | 刷新當前group,調用IOPCAsyncIO2::Refresh接口 |
GetState | 獲得當前group狀態,返回GroupState |
SetState | 設置當前group狀態,返回設置後的group狀態 |
DataChanged | 事件,客戶端註冊,可用來接收OnDataChange事件 |
4. 類庫使用方法
1) 連接OPC DA server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | string serverProgID = "OPCSample.OpcDa20Server.1" ; // opc da sample server prog id string hostName = string .Empty; //local server Type tp = Type.GetTypeFromProgID(serverProgID); this ._opcServer = new OpcServer(tp.GUID.ToString(), hostName); try { this ._opcServer.Connect(); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error" , MessageBoxButtons.OK, MessageBoxIcon.Error); } |
2) 斷開OPC Server
1 2 3 4 5 | if ( this ._opcServer != null ) { this ._opcServer.Disconnect(); this ._opcServer = null ; } |
3) 添加Group
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | string groupName = "grp_0" ; // group name int updateRate = 1000; bool active = true ; try { OpcGroup grp = this ._opcServer.AddGroup(groupName, updateRate, active); grp.DataChanged += OnDataChange; //register OnDataChange Event } catch (Exception ex) { MessageBox.Show(ex.Message, "Error" , MessageBoxButtons.OK, MessageBoxIcon.Error); } |
其中OnDataChange 定義爲DataChangedEventHandler類型:
1 2 3 | public delegate void DataChangedEventHandler( object subscriptionHandle, object requestHandle, ItemValueResult[] values); private void OnDataChange( object subscriptionHandle, object requestHandle, ItemValueResult[] values); |
4) 刪除Group
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | try { OpcGroup grp = this ._opcServer.FindGroupByName(groupName ); if (grp != null ) { grp.DataChanged -= OnDataChange; //unregister OnDataChange Event this ._opcServer.RemoveGroup(grp); } } catch (Exception ex) { MessageBox.Show(ex.Message, "Error" , MessageBoxButtons.OK, MessageBoxIcon.Error); } |
5) 添加Items
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 | List< string > items = new List< string >(); Items.Add( "itemname" ); OpcGroup grp = this ._opcServer.FindGroupByName(groupName); if (grp != null ) { try { ItemResult[] results = grp.AddItems(items.ToArray()); foreach (ItemResult result in results) { if (result.ResultID.Failed()) { string message = "Failed to add item \'" + result.ItemName + "\'" + " Error: " + result.ResultID.Name; MessageBox.Show(message); } else { AddItemToList(result); // add item to view list } } } // end try catch (Exception ex) { MessageBox.Show(ex.Message, "Error" , MessageBoxButtons.OK, MessageBoxIcon.Error); } // end catch } // end if |
6) 刪除Items
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 | // remove items // List<object> itemsClientHandle = new List<object>(); // if (itemsClientHandle.Count > 0) { try { // get group OpcGroup grp = this ._opcServer.FindGroupByName(groupName); if (grp != null ) { IdentifiedResult[] results = grp.RemoveItems(itemsClientHandle.ToArray()); for ( int i = 0; i < results.Length; i++) { if (results[i].ResultID.Succeeded()) { // remove opc item from server successfully, remove it from list RemoveItemFromList(results[i]); } else { string message = "Remove item \'" + results[i].ItemName + "\' error: " + results[i].ResultID.ToString(); MessageBox.Show(message); } } } } catch (Exception ex) { MessageBox.Show(ex.Message, "Error" , MessageBoxButtons.OK, MessageBoxIcon.Error); } // end catch } |
7) 異步讀取Items
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 | List< object > clientHandles = new List< object >(); foreach (ListViewItem lvItem in items) { ItemResult item = (ItemResult)lvItem.Tag; if (item != null ) { clientHandles.Add(item.ClientHandle); } } if (clientHandles.Count > 0) { // get group OpcGroup grp = this ._opcServer.FindGroupByName(groupName); if (grp != null ) { try { IdentifiedResult[] results = grp.AsyncRead(clientHandles.ToArray(), ++ this ._handle, new ReadCompleteEventHandler(OnReadComplete), out this ._request); for ( int i = 0; i < results.Length; i++) { if (results[i].ResultID.Failed()) { string message = "Failed to read item \'" + results[i].ItemName + "\' error: " + results[i].ResultID.ToString(); MessageBox.Show(message); } } } catch (Exception ex) { MessageBox.Show(ex.Message); } } } |
其中OnReadComplete用來接收異步讀取的數據結果,其定義爲:
1 | private void OnReadComplete( object clientHandle, ItemValueResult[] values); |
8) 異步寫Items
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // get group OpcGroup grp = this ._opcServer.FindGroupByName(groupName); if (grp != null ) { try { IdentifiedResult[] results = grp.AsyncWrite( new ItemValue[] { itemValue }, ++_handle, new WriteCompleteEventHandler( this .OnWriteComplete), out _request); for ( int i = 0; i < results.Length; i++) { if (results[i].ResultID.Failed()) { string message = "Failed to write item \'" + results[i].ItemName + "\' error: " + results[i].ResultID.ToString(); MessageBox.Show(message); } } } catch (Exception ex) { MessageBox.Show(ex.Message); } } |
其中OnWriteComplete用來接收異步寫的返回結果,其定義爲:
private void OnWriteComplete(object clientHandle, IdentifiedResult[] results);
類庫的具體使用舉例可以參照OpcTestClient程序。
5. OpcTestClient 用戶界面
OPC 接口 .NET 包裝組件下載:
源代碼下載: