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
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
if (this._opcServer != null)
{
this._opcServer.Disconnect();
this._opcServer = null;
}
3) 添加Group
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类型:
public delegate void DataChangedEventHandler(object subscriptionHandle, object requestHandle, ItemValueResult[] values);
private void OnDataChange(object subscriptionHandle, object requestHandle, ItemValueResult[] values);
4) 删除Group
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
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
// 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
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用来接收异步读取的数据结果,其定义为:
private void OnReadComplete(object clientHandle, ItemValueResult[] values);
8) 异步写Items
// 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 包装组件下载:
源代码下载: