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 包装组件下载:
源代码下载:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2015-02-10 SQL执行效率和性能测试方法