使用OpcDaNet.dll实现Opc Server的访问
老板只给了一句话:通过Opc将数据传到服务器中。一头雾水,赶紧百度,终于有点眉目,记录下来,供需要的人参考,希望能够对你有所帮助,如果文章中有什么不对的地方,请不吝赐教,如果是你需要的文章就接着往下看吧。
思路
我需要提供开发一个客户端去访问Opc服务器,传递数据给服务器,或者从服务器中获取数据。但是接到项目时,客户还没有提供服务器给我,我就从网上下载了一个测试服务器,用的是Matrikon OPC,其他也看到有网友用KepServer,接下来只是介绍C#开发的客户端如何与Matrikon OPC服务器通信,以及如何知道你的通信是否成功。
Opc测试服务器
下载安装好后如下图所示。
- MatrikonOPC Server for Simulation是服务器,打开之后可以新建Group、Item,然后一个简单的OPC服务器就配置好了,我们开发的客户端就是要访问它,下图是我配置的一个测试服务器。
- 右上角的黄色标签点开后可以打开MatrikonOPC自带的客户端,如下图所示,结合自带客户端可以轻松实现自己开发的客户端的测试。可以在客户端中添加需要的Item。
简单的OPC服务器配置已经完成。接下来就需要开发我们自己的客户端。
使用OpcDaNet.dll开发OPC客户端
- 下载OpcDaNet.dll,它是实现C#与OPC通信的SDK,地址:https://advosol.com/product/1/opcda-net#ProductInfo/
网站中介绍了该dll如何使用,如下图,其中介绍了每个函数的具体功能,参数设置,内容很多,我只用到了其中几个函数,其他的我没用到也没有看其中的功能介绍。
- 在项目中添加了dll,我需要在界面上展示服务器的Group和Item,然后在选择需要的Item进行读写,所有我的做法是列出来Group、Item,收集起来,显示在界面上,然后选择需要的Item进行读写。
下面是具体用法。
(1)新建一个项目,添加OpcDaNet.dll的引用;
(2)在项目中新建OPCClient类;
(3)添加函数OPCServerConnect(),连接至服务器:
OpcServerBrowser opcServerBrower;
string[] str;
public bool OPCServerConnect(OpcServer _opcserver)
{
try
{
Guid[] ClsIDs;
opcServerBrower = new OpcServerBrowser();
opcServerBrower.GetServerList(out str, out ClsIDs);//遍历所有服务器
int re = 1;
for (int i = 0; i < str.Length; i++)
{
//Matrikon.OPC.Simulation
re = _opcserver.Connect(str[i]);//本地上只有个服务器,连接上
}
return re == 0 ? true : false;
}
catch
{
return false;
}
}
(4)获取服务器上所有的Group:
/// <summary>
/// 获取服务器中所有的Group
/// </summary>
/// <param name="_opcserver"></param>
/// <param name="_treeNodes"></param>
/// <returns></returns>
public void ShowServerGroups(OpcServer _opcserver,out TreeNode[] _treeNodes)
{
TreeView treeView = new TreeView();
ShowBrowseTree show = _opcserver.ShowBrowseTree(treeView);
show.Show();//必须要加,否则treeNodes为空
_treeNodes = show.ItemTree;// show all available Tags
}
(5)获取服务器上所有的Item:
/// <summary>
/// 获取服务器中所有的Items
/// </summary>
/// <param name="_opcserver"></param>
public void ShowServerItems(OpcServer _opcserver,out List<string> _Items)
{
string[] nodes;//遍历了Server中所有的Items
_opcserver.BrowseOPCItemIDs(OPCBROWSETYPE.OPC_FLAT,null, null, OPCACCESSRIGHTS.OPC_READWRITEABLE,out nodes);
_Items = new List<string>();
for (int i = 0; i < nodes.Length; i++)
{
if (nodes[i].Equals("#MonitorACLFile"))
{
break;//过滤不需要的Item
}
else
{
_Items.Add(nodes[i]);
}
}
string[] str = new string[_Items.Count];
for (int i = 0; i < _Items.Count; i++)
{
str[i] = _Items[i];
}
}
(6)读Item、RemoveItems和RemoveGroup:
/// <summary>
/// 通过OpcServer读取服务器Item的值
/// </summary>
/// <param name="_opcserver">客户服务器</param>
/// <param name="_Items">ShowServerItems的返回值</param>
/// <param name="_read">Access Path和DataType</param>
/// <param name="_read">Read后得到的值</param>
/// <returns></returns>
public void ServerReadItems(OpcServer _opcserver, List<string> _Items, out List<AcPathDP> AcPathDPList, out ItemValue[] _read)
{
AcPathDPList = new List<AcPathDP>();
try
{
string[] str = new string[_Items.Count];
for (int i = 0; i < _Items.Count; i++)
{
str[i] = _Items[i];
#region Get DataType and Access Path
AcPathDP AcPathDP = new AcPathDP();
OpcGroup oGrp = _opcserver.AddGroup(_Items[i], true, 0, 1);
OPCItemDef ReadItem = new OPCItemDef(str[i], true, 1, typeof(void));
OPCItemDef[] opcReadItemsArray = new OPCItemDef[1];
opcReadItemsArray[0] = ReadItem;
oGrp.AddItems(opcReadItemsArray, out OPCItemResult[] itemAddRslt);
int[] arrHSrv = new int[1];
arrHSrv[0] = itemAddRslt[0].HandleServer;
oGrp.RemoveItems(arrHSrv, out int[] error);
oGrp.Remove(true);
AcPathDP.DataType = itemAddRslt[0].CanonicalType.Name;
AcPathDP.AccessPath = ReadItem.AccessPath;
AcPathDPList.Add(AcPathDP);
#endregion
}
_read = _opcserver.Read(str, _Items.Count);
}
catch
{
_read = null;
}
}
(7)写入Item:
/// <summary>
/// 写数据到Server的Items中
/// </summary>
/// <param name="_opcserver">服务器</param>
/// <param name="item">将要写入的ItemID</param>
/// <param name="WriteValue">写入的数据</param>
public bool WriteItems(OpcServer _opcserver, string item, object WriteValue)
{
OPCItemDef WriteItem = new OPCItemDef(item, true, 1, typeof(void));
OPCItemDef[] opcWriteItemsArray = new OPCItemDef[1];
opcWriteItemsArray[0] = WriteItem;
OPCItemResult[] itemAddRslt;
OpcGroup oGrp = _opcserver.AddGroup(item, true, 0, 1);
oGrp.AddItems(opcWriteItemsArray, out itemAddRslt);
int[] ItemHandlesToWrite = new int[itemAddRslt.Length];
bool WriteOperationFailed = false;
for (int index = 0; index < itemAddRslt.Length; index++)
{
ItemHandlesToWrite[index] = itemAddRslt[index].HandleServer;//服务器句柄
if (HRESULTS.Failed(itemAddRslt[index].Error))
{
WriteOperationFailed = true;
}
}
if (!WriteOperationFailed)
{
object[] val = new object[ItemHandlesToWrite.Length];
val[0] = WriteValue;
oGrp.Write(ItemHandlesToWrite, val, out int[] results);
#region Remove Group
int[] aSrvHnd = new int[1];//服务器句柄
aSrvHnd[0] = itemAddRslt[0].HandleServer;
oGrp.RemoveItems(aSrvHnd, out int[] error);
int err = oGrp.Remove(true);
#endregion
}
return WriteOperationFailed;
}
(8)断开与服务器的连接:
OPCServer.Disconnect();
- 如何测试:
使用2中的函数,使用2中的(1),连接到服务器后如下图:
使用2中的(2)~(7),如果服务器自带的Client和我们开发的Client可以相互改值的话就成功了;
最后使用2中的(8)断开与服务器的连接即可。