OPC工作记录整理——第七篇(数据订阅)

    数据订阅主要用到了一个接口——IOPCDataCallback,该接口是在opcda.h中定义的,因为它是个抽像类,所以需要实现OnDataChange、OnReadComplete、OnWriteComplete、OnCancelComplete以及QueryInterface、AddRef、Release七个方法,为了避免重写后面三种方法,笔者才用了COM组件的模板类,就只需要解决上面四种方法即可(其实笔者也不太清楚为什么要这样做,但是仿佛使用了模板类以后,程序能够编译通过,要是采用IConnectionPointContainer这种方式,需要把七种方法全部都实现,所以果断就这样写了)。

    数据订阅主要用到了一个接口——IOPCDataCallback,该接口是在opcda.h中定义的,因为它是个抽像类,所以需要实现OnDataChange、OnReadComplete、OnWriteComplete、OnCancelComplete以及QueryInterface、AddRef、Release七个方法,为了避免重写后面三种方法,笔者才用了COM组件的模板类,就只需要解决上面四种方法即可(其实笔者也不太清楚为什么要这样做,但是仿佛使用了模板类以后,程序能够编译通过,要是采用IConnectionPointContainer这种方式,需要把七种方法全部都实现,所以果断就这样写了)。

    笔者重写了一个类,继承自IOPCDataCallback,命名为COPCDataCallback,其头文件为:

#include <atlbase.h>
#include <atlcom.h>
#include "opcda.h"
//
// Callback.h : Declaration of callback class and definition of minor methods
//
//---------------------------------------------------------
// (c) COPYRIGHT 2003,2004 http://www.opc-china.com INC.
// ALL RIGHTS RESERVED
// Original Author:geekCarnegie	
// Original Author Email:geekcarnegie@gmail.com
//---------------------------------------------------------
class COPCDataCallback : public IOPCDataCallback,
	   public CComObjectRootEx<CComSingleThreadModel>
{

public:
	COPCDataCallback() {};
	virtual ~COPCDataCallback() { ; };

	BEGIN_COM_MAP(COPCDataCallback)
		COM_INTERFACE_ENTRY(IOPCDataCallback)
	END_COM_MAP()

	// IOPCDataCallback
	STDMETHODIMP  OnDataChange(
		/* [in] */ DWORD dwTransid,
		/* [in] */ OPCHANDLE hGroup,
		/* [in] */ HRESULT hrMasterquality,
		/* [in] */ HRESULT hrMastererror,
		/* [in] */ DWORD dwCount,
		/* [size_is][in] */ OPCHANDLE __RPC_FAR *phClientItems,
		/* [size_is][in] */ VARIANT __RPC_FAR *pvValues,
		/* [size_is][in] */ WORD __RPC_FAR *pwQualities,
		/* [size_is][in] */ FILETIME __RPC_FAR *pftTimeStamps,
		/* [size_is][in] */ HRESULT __RPC_FAR *pErrors);
	STDMETHODIMP  OnReadComplete(
		/* [in] */ DWORD dwTransid,
		/* [in] */ OPCHANDLE hGroup,
		/* [in] */ HRESULT hrMasterquality,
		/* [in] */ HRESULT hrMastererror,
		/* [in] */ DWORD dwCount,
		/* [size_is][in] */ OPCHANDLE __RPC_FAR *phClientItems,
		/* [size_is][in] */ VARIANT __RPC_FAR *pvValues,
		/* [size_is][in] */ WORD __RPC_FAR *pwQualities,
		/* [size_is][in] */ FILETIME __RPC_FAR *pftTimeStamps,
		/* [size_is][in] */ HRESULT __RPC_FAR *pErrors);
	STDMETHODIMP  OnWriteComplete(
		/* [in] */ DWORD dwTransid,
		/* [in] */ OPCHANDLE hGroup,
		/* [in] */ HRESULT hrMastererr,
		/* [in] */ DWORD dwCount,
		/* [size_is][in] */ OPCHANDLE __RPC_FAR *pClienthandles,
		/* [size_is][in] */ HRESULT __RPC_FAR *pErrors);
	STDMETHODIMP  OnCancelComplete(
		/* [in] */ DWORD dwTransid,
		/* [in] */ OPCHANDLE hGroup)
	{
		return S_OK;
	};
};
    
STDMETHODIMP COPCDataCallback::OnDataChange(  // OnDataChange notifications
	DWORD dwTransID,   // 0 for normal OnDataChange events, non-zero for Refreshes
	OPCHANDLE hGroup,   // client group handle
	HRESULT hrMasterQuality, // S_OK if all qualities are GOOD, otherwise S_FALSE
	HRESULT hrMasterError,  // S_OK if all errors are S_OK, otherwise S_FALSE
	DWORD dwCount,    // number of items in the lists that follow
	OPCHANDLE *phClientItems, // item client handles
	VARIANT *pvValues,   // item data
	WORD *pwQualities,   // item qualities
	FILETIME *pftTimeStamps, // item timestamps
	HRESULT *pErrors)   // item errors 
{
	std::cout << std::endl;
	std::cout << "数据第" << changeFlag << "次变更:" << std::endl;
	DWORD i;
	for (i = 0; i<dwCount; i++)
	{
		value[i] = pvValues[i].fltVal;
		quility[i] = GetQualityText(pwQualities[i]);
		timestamp[i] = COleDateTime(pftTimeStamps[i]).Format();
	}
	std::cout << "数据变更完毕..." << std::endl;
	changeFlag++;
	return S_OK;
};</span>

    其中OnDataChange方法的实现代码为:

STDMETHODIMP COPCDataCallback::OnDataChange(  // OnDataChange notifications
	DWORD dwTransID,   // 0 for normal OnDataChange events, non-zero for Refreshes
	OPCHANDLE hGroup,   // client group handle
	HRESULT hrMasterQuality, // S_OK if all qualities are GOOD, otherwise S_FALSE
	HRESULT hrMasterError,  // S_OK if all errors are S_OK, otherwise S_FALSE
	DWORD dwCount,    // number of items in the lists that follow
	OPCHANDLE *phClientItems, // item client handles
	VARIANT *pvValues,   // item data
	WORD *pwQualities,   // item qualities
	FILETIME *pftTimeStamps, // item timestamps
	HRESULT *pErrors)   // item errors 
{
	std::cout << std::endl;
	std::cout << "数据第" << changeFlag << "次变更:" << std::endl;
	DWORD i;
	for (i = 0; i<dwCount; i++)
	{
		value[i] = pvValues[i].fltVal;
		quility[i] = GetQualityText(pwQualities[i]);
		timestamp[i] = COleDateTime(pftTimeStamps[i]).Format();
	}

	std::cout << "数据变更完毕..." << std::endl;
	changeFlag++;
	return S_OK;
};

    OnReadComplete的代码为:

STDMETHODIMP COPCDataCallback::OnReadComplete( // OnReadComplete notifications
	DWORD dwTransID,   // Transaction ID returned by the server when the read was initiated
	OPCHANDLE hGroup,   // client group handle
	HRESULT hrMasterQuality, // S_OK if all qualities are GOOD, otherwise S_FALSE
	HRESULT hrMasterError,  // S_OK if all errors are S_OK, otherwise S_FALSE
	DWORD dwCount,    // number of items in the lists that follow
	OPCHANDLE *phClientItems, // item client handles
	VARIANT *pvValues,   // item data
	WORD *pwQualities,   // item qualities
	FILETIME *pftTimeStamps, // item timestamps
	HRESULT *pErrors)   // item errors 
{	
	std::cout << std::endl;
	std::cout << "数据获取中..." << std::endl;
	if (pErrors[0] == S_OK)
	{
		DWORD i;
		for (i = 0; i<dwCount; i++)
		{
			readValue[i] = pvValues[i].fltVal;
			readQulity[i] = GetQualityText(pwQualities[i]);
			readTS[i] = COleDateTime(pftTimeStamps[i]).Format();
		}
	}
	else
	{
		CString readQuality = GetQualityText(pErrors[0]);
	}
	std::cout << "数据已全部读完..." << std::endl;
	return S_OK;
};

    在主程序中先获取组状态:    

/*组更新状态*/
hr = pIOPCItemMgt->QueryInterface(IID_IOPCGroupStateMgt, /*OUT*/(void**)&pIOPCGroupStateMgt); //得到第十个指针
ASSERT(pIOPCGroupStateMgt);

if (FAILED(hr))
{
	cout << "获取IOPCGroupStateMgt接口失败..." << endl;
	if (pItemResult) CoTaskMemFree(pItemResult);
	if (pErrors) CoTaskMemFree(pErrors);
	CoTaskMemFree(&hOPCServer1);
	CoTaskMemFree(&hOPCServer2); //第六个内存释放
	CoTaskMemFree(&itemArray); //第五个内存释放
	CoTaskMemFree(&clsid); //第四个内存释放
	CoTaskMemFree(&catID); //第三个内存释放
	CoTaskMemFree(&mqi); //第二个内存释放
	CoTaskMemFree(&si); //第一个内存释放
	if (pIOPCGroupStateMgt) pIOPCGroupStateMgt->Release(); //第十个指针释放
	pIOPCGroupStateMgt = NULL;
	if (pIOPCItemMgt) pIOPCItemMgt->Release(); //第五个指针释放
	pIOPCItemMgt = NULL;
	if (pIServer) pIServer->Release(); //第四个指针释放
	pIServer = NULL;
	if (pIUnknown) pIUnknown->Release(); //第三个指针释放
	pIUnknown = NULL;
	if (pIEnumGUID) pIEnumGUID->Release(); //第二个指针释放
	pIEnumGUID = NULL;
	if (pIServerList) pIServerList->Release(); //第一个指针释放
	pIServerList = NULL;
	return 1;
}
else if (SUCCEEDED(hr)) {
	cout << "已获取到IOPCGroupStateMgt接口..." << endl;
}

DWORD  dwRevUpdateRate = 10;
BOOL bActivateGroup = TRUE;

hr = pIOPCGroupStateMgt->SetState(/*[in] RequestedUpdateRate*/ NULL, /*[out] RevisedUpdateRate */ &dwRevUpdateRate, /*[in] ActiveFlag for Group */ &bActivateGroup, /*[in] TimeBias*/ NULL, /*[in] PercentDeadband*/ NULL, /*[in] LCID*/ NULL, NULL);	</span>

    然后在主程序中调用COPCDataCallback类,设置回调:

CComObject<COPCDataCallback> *pCOPCDataCallback;
CComObject<COPCDataCallback>::CreateInstance(&pCOPCDataCallback);

LPUNKNOWN pCbUnk;
pCbUnk = pCOPCDataCallback->GetUnknown();
DWORD dwCookie;

HRESULT hRes = AtlAdvise(pIOPCGroupStateMgt, pCbUnk, IID_IOPCDataCallback, &dwCookie);

if (FAILED(hRes)) {
	cout << "数据订阅回调设置失败..." << endl;
	if (pItemResult) CoTaskMemFree(pItemResult);
	if (pErrors) CoTaskMemFree(pErrors);
	CoTaskMemFree(&hOPCServer1);
	CoTaskMemFree(&hOPCServer2); //第六个内存释放
	CoTaskMemFree(&itemArray); //第五个内存释放
	CoTaskMemFree(&clsid); //第四个内存释放
	CoTaskMemFree(&catID); //第三个内存释放
	CoTaskMemFree(&mqi); //第二个内存释放
	CoTaskMemFree(&si); //第一个内存释放
	if (pIOPCGroupStateMgt) pIOPCGroupStateMgt->Release(); //第十个指针释放
	pIOPCGroupStateMgt = NULL;
	if (pIOPCItemMgt) pIOPCItemMgt->Release(); //第五个指针释放
	pIOPCItemMgt = NULL;
	if (pIServer) pIServer->Release(); //第四个指针释放
	pIServer = NULL;
	if (pIUnknown) pIUnknown->Release(); //第三个指针释放
	pIUnknown = NULL;
	if (pIEnumGUID) pIEnumGUID->Release(); //第二个指针释放
	pIEnumGUID = NULL;
	if (pIServerList) pIServerList->Release(); //第一个指针释放
	pIServerList = NULL;
	return 1;
}
else if (SUCCEEDED(hRes)) {
	cout << "数据订阅回调设置成功..." << endl;
}

    在主程序的最后,笔者加了系统暂停,以便控制台程序能够在数据变更的时候就能够获取到变更的数据:

system("pause>nul");
    最后释放指针,程序退出,数据订阅完毕。


  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值