OPC客户端开发的简单整理

简要

本文是对前段时间因公司需要而实现的opc客户端的一个总结,主要是opcda的客户端,opcua客户端做了一个建模比较简单的(涉及到建模,后面因公司决定采用其他方式对接,后续没做深究)。关于opc协议,网上的资料很多,这里主要是便于以后自己的温习,结合开发过程中踩过的坑做一个简单的整理,可能有些内容和别的博客有相似,如果有觉得不妥的,请及时联系我,谢谢大家!

OPC协议简介

1、协议理解
OPC(OLE for Process Control)就是为了不同的供应商的device和应用程序之间的接口标准化,使他们的数据变得更加的简单化而提出的一种标准或者说是规范。

实际运用中以OPCDA、OPCUA这两种类型为主。笔者因公司要做行业集成,主要也是接触这两种协议。下面简单介绍一下这两种协议。

OPCDA是在windows的com/dcom技术上定义的接口定义,在TCP IP七层模型的最高层应用层,决定了它必须在windows平台,不能够跨平台,灵活性和安全性不如OPC UA,因为OPC DA的会话层和表示层用户是有权利来决定的。

为了应对标准化和跨平台的趋势,推出了新的标准OPC UA,该接口协议包含了之前的A&E,DA,OPC XMLDA or HDA,只使用一个地址空间就能访问之前的所有对象,而且不受windows平台的限制,具有跨平台性。一个通用接口集成了之前所有OPC的特性和信息。
OPC UA不再是基于分布式组件对象模型DCOM,而是以面向服务的架构SOA为基础

2、两种类型的区别
a)OPC虽然通过配置COM\DOM来提供数据加密和签名功能,配置防火墙,用户权限来让数据访问变得更加安全,但是会增加额外的工作量,而对于OPC UA,这些都是默认的功能;
b)另外基于DOM的OPC使用的是动态端口分配,端口不固定,让防火墙难以确定,但是对于OPC UA的端口都是唯一的;
c)核心的区别是因为OPC和OPC UA协议使用的TCP层不一样,OPC是基于DOM\COM上,应用层最顶层,OPC UA是基于TCP IPsocket传输层

COM技术

1、COM简介
因为OPC是基于COM组件进行通信的,因此对COM有个简单的了解有利于后续对COM接口的使用,下面是百度百科对COM的解释:

COM(Component Object Model,组件对象模型),是由微软推出的一套接口规范,通过设定不同组件之间需要遵守的标准与协议,主要用来跨语言、跨进程之间的模块通信

这里推荐一个很好的博客,有助于对COM有个更好的了解。用VC进行COM编程所必须掌握的理论知识

2、OPC和COM之间的关系
OPC是建立在COM技术基础之上的,COM技术的出现为简单的实现控制设备和控制管理系统之间的数据交换提供了技术基础,但是如果不提供一个工业标准化的com接口,各个控制设备厂家开发的com组件之间的相互连接任然是不可能的。

客户程序和组件程序通过接口进行相互之间的通信,组件程序就是通过接口暴露它的功能给客户程序的,而com客户程序是可能看见组件对象本身的,仅有接口时可见的,它告诉客户程序能利用组件干什么,如何利用它的功能。

OPCDA客户端开发流程

开发客户端之前,要先确认opc的服务端采用的是哪个版本的协议库,客户端和服务端的协议库版本必须保持一致,因为每个版本的接口可能不太一样。(如果需要2.0版本和3.0版本的接口标准说明书,如有需要可以联系我)
下面简单介绍一下流程,因为网上能找到代码,这里不再做过多的代码体现。

1.注册OPC接口组件

	hr = CoInitializeEx(NULL, COINITBASE_MULTITHREADED);    //注册DCOM组件,获取结果
	HRESULT hr_sec = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

2.通过COM接口创建OPCServerlist接口指针,CoCreateInstance只能遍历本机,CoCreateInstanceEx可以遍历远程机器OPC

	si.pwszName = A2W(strNode);
	mqi[0].hr = S_OK;
	mqi[0].pIID = &IID_IOPCServerList;
	mqi[0].pItf = NULL;
	hr = CoCreateInstanceEx(CLSID_OpcServerList, NULL, CLSCTX_REMOTE_SERVER, &si, 1, mqi);

3.枚举OPC服务器列表。通过IOPCServerlist接口EnumClassesOfCategories方法

	do
	{
		hr_1 = pIEnumGUID->Next(20, clsid, &nCount);
		for (ULONG i = 0; i < nCount; i++)
		{
			LPOLESTR szProID;
			LPOLESTR szUserType;
			HRESULT hr_2 = pIServerList->GetClassDetails(clsid[i], &szProID, &szUserType);
			poco_assert(hr_2 == S_OK);
			USES_CONVERSION;
			char* ID = OLE2A((LPOLESTR)szProID);
			LOG_INFO("port%d:%s", i + 1, &ID);
			CoTaskMemFree(szProID);
			CoTaskMemFree(szUserType);
		}
	} while (hr_1 == S_OK);

4.连接opc服务器

	mqi[0].hr = S_OK;
	mqi[0].pIID = &IID_IOPCServer;
	mqi[0].pItf = NULL;

	hr = CoCreateInstanceEx(clsid_citect, NULL, CLSCTX_REMOTE_SERVER, &si, 1, mqi);

5.添加group。这步如果没有做,默认分配一个group名。

	hr = m_pOPCServer->AddGroup(L"Group", TRUE, 10, 1235, &lTimeBias, &fTem, 1033, &m_hOPCServer1, &dwActualRate, IID_IOPCItemMgt, (LPUNKNOWN*)&m_pIOPCItemMgt);

6.遍历服务器上的Item项。这里需要考虑到节点的结构,属于flat、leaf还是branch的结构,依情况处理。


	HRESULT hr;
	hr = pBSAS->ChangeBrowsePosition(OPC_BROWSE_TO, lpcw);       //to root
	IEnumString *pEnum = 0;
	hr = pBSAS->BrowseOPCItemIDs(OPC_BRANCH, L"", VT_EMPTY, 0, &pEnum);
	LPOLESTR pStr = NULL;
	ULONG actual = 0;
	char bufName[256] = { 0 };   
	while ((hr = pEnum->Next(1, &pStr, &actual)) == S_OK)
	{
		WideCharToMultiByte(CP_ACP, 0, pStr, -1, bufName, 256, NULL, NULL);
		m_pItemName.push_back(bufName);
		m_size = m_pItemName.size();
		TryBrowseBranch(pBSAS, pStr);
    }

8.读取数据。这里可以选择同步读取,也可以采用订阅式异步读取。

	HRESULT hres = AtlAdvise(pIOPCGroupStateMgt, this, IID_IOPCDataCallback, &dwCookie);

笔者之前采用的是订阅式的方式去读取的,采用的这个接口:

STDMETHODIMP COPCClient::OnDataChange(DWORD dwTransID,
	OPCHANDLE hGroup,       //Group的句柄
	HRESULT hrMasterQuality,  //当所有的Items的qualitied是good,该值为s_ok,否则是s_false
	HRESULT hrMasterError,   //当所有的Items的errors为s_ok,该值为s_ok,否则是s_false
	DWORD dwCount,           //item的个数
	OPCHANDLE *phClientItems,  //item的句柄
	VARIANT *pvValues,         //item的值
	WORD *pwQualities,    //
	FILETIME *pftTimeStamps,
	HRESULT *pErrors)  //item的hresults信息

opc dcom配置常见的问题

dcom配置的配置,因为受COM组件的限制,如果采用opcda协议对接的话,就需要在客户端和服务端都进行dcom配置,如果有一个没有配好就会导致出错。配置方法网上能够找到,如果找不到,可以联系我我邮件发给大家。 这里整理了几个常见opc dcom配置过程中的问题:

1)Opcclient无法遍历远程计算机上的opcserver列表,日志显示get target opc serverlist failed
排错思路: 检测网络是否畅通,防火墙是否关闭,Dcom配置未配置

2)opcclient能够遍历远程计算机上的opcserverlist列表,日志显示Not connect opcserver which you want ,此时代码的返回码一般都是general access denied error
排错思路:能够遍历远程计算机的opc列表,说明远程计算机整体的dcom配置已经ok,连接不到目标opcserver,一定是目标opcserver的配置或者opcserver本身的问题,如连接数,未授权等等

3)找不到opcEnum.exe ?
解决方法:将opc服务器文件夹中的四个动态库以及exe文件分别在windows/system32或者是syswow64下注册,注册之后刷新就会出现opcEnum.exe

4)能够遍历远程计算机上的opcserver,也可以连接指定的opcserver,但是无法增加组。
排错思路:opcclient所在的计算机的opc配置不正确导致。

5)配置opcenum时,如果交互式用户选项置灰?
解决方法: 找到C:\Windows\syswow64\opcenum.exe将它拖到cmd控制台,重新注册,C:Windows\syswow64\opcenum.exe /regserver ,然后重新打开opcenum属性,32位的系统在C:windows\system32下

6)配置opcenum属性时,“在此计算机上运行为灰色”?
解决方法:cmd->mmc comexp.msc /32 .

待解决的问题

在行业集成过程中,因为集成了多个厂家支持opcda的设备,遇到了个别厂家提供的opc服务器和opc的客户端必须在一台服务器上才能够遍历到item的情况,如果在不同的PC端,调试代码会发现在遍历item的时候会出现“枚举值越界”的问题,笔者找了几个开源的opcclient去和连接也是出现同样的问题(两台pc端对接成功过其他支持opcda协议的设备,因此排除dcom未配置成功的原因)。目前还未找到原因,如果大家有知道的或者遇到过的,愿不吝赐教,谢谢!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章