OPC工作记录整理——第一篇(序)
https://blog.youkuaiyun.com/u014801367/article/details/43057109
OPC工作记录整理——第二篇(OPC客户端运行环境配置)
https://blog.youkuaiyun.com/u014801367/article/details/43058483
OPC工作记录整理——第三篇(开发环境的配置)
https://blog.youkuaiyun.com/u014801367/article/details/43064535
OPC工作记录整理——第四篇(OPC客户端开发之OPC服务器的枚举和连接)
https://blog.youkuaiyun.com/u014801367/article/details/43309231
OPC工作记录整理——第五篇(添加组和添加项)
https://blog.youkuaiyun.com/u014801367/article/details/43314281
OPC工作记录整理——第六篇(同步读取和异步读取)
https://blog.youkuaiyun.com/u014801367/article/details/43314907
OPC工作记录整理——第七篇(数据订阅)
https://blog.youkuaiyun.com/u014801367/article/details/43316633
qt实现 opc客户端代码:
//OPCClient.h
#ifndef OPCCLIENT_H
#define OPCCLIENT_H
#include <iostream>
#include "opcerror.h"
#include "opccomn.h"
#include "OpcEnum.h"
#include "Opcda.h"
#include "copcdatacallback.h"
#include "wldef.h"
#include <QObject>
#include <QDebug>
#include <QTreeWidget>
#include <QListWidget>
#include "windows.h"
WCHAR * WSTRFromSBCS(const CHAR *buf, IMalloc * pmem);
std::string ComErrMsg(HRESULT hr);
class COPCDataCallback;
class ItemDef
{
public:
QString name;
DWORD accessRights;
VARIANT value;
WORD quality;
OPCHANDLE hServerHandle;
OPCHANDLE hClientHandle;
FILETIME time;
VARTYPE type;
ItemDef()
{
type = VT_EMPTY;
quality = 0;
hServerHandle = 0;
hClientHandle = 0;
}
ItemDef( const ItemDef& it )
{
name = it.name;
value = it.value;
quality = it.quality;
hServerHandle = it.hServerHandle;
hClientHandle = it.hClientHandle;
time = it.time;
type = it.type;
}
};
struct ItemInfo
{
QString m_name;
QString m_val;
QString m_time;
QString m_quality;
};
class OPCClient:public QObject
{
Q_OBJECT
public:
explicit OPCClient();
OPCClient(QString s);
~OPCClient();
bool Init();
void UnInit();
bool Connect(QString serverName, QString ip);
bool isConnected(); // 此接口如果连上服务器后再断开是无法检测到服务器断开的,还需通过读取接口来判断服务器是否断开
HRESULT Connect(QString s);
void DisConnect();
void TryBrowseBranch(IOPCBrowseServerAddressSpace *pBSAS, LPCWSTR lpprestr,LPCWSTR lpcw);
void BrowseAllItem();
bool browseSubGroup(QTreeWidget *groupWidget, QString groupName);
bool browseItem(QTreeWidget *itemWidget, QString groupName);
void browseGroup(QTreeWidget *groupWidget);
HRESULT AddGroup(QString n, DWORD update_rate , int async=0); // async=1表示同步方式是异步,0为同步
QStringList AddItems(QStringList names);
bool ReadItem(ItemDef *item);
void browseServerList(QString ip, QListWidget *listWidget);
bool WriteValue(QString &name, QString &value );
bool WriteValue( DWORD cHandle, FILETIME &time, VARIANT &value, WORD Quality );
HRESULT WriteValue_Async(ItemDef * item);
HRESULT WriteValue_Sync (ItemDef * item);
HRESULT ReadAllValue_Sync(std::vector<ItemInfo> &changeVec);
HRESULT ReadAllValue_Async();
HRESULT RemoveItems(QStringList inames);
HRESULT RemoveAllItems();
void dataCallback();
QString VariantString(VARIANT &pValue);
QString QualityString(UINT qnr);
QString ValueTypeString(const VARTYPE& v);
void StringVariant(ItemDef* item, QString v);
QString AccessTypeString(qint16 accessIdent);
QString TimeString(FILETIME t);
void ClearOPCITEMDEF( OPCITEMDEF *idef, int count = 1 );
ItemDef* getItemByName(QString s);
ItemDef* getItemByHandle(DWORD h);
WString qstr2Wstr(QString s);
COPCTransaction *m_COPCTransaction = NULL;
bool m_bInit = false;
bool m_bConnect = false;
bool isWriteAble;
QList<ItemDef*> *items;
std::vector<ItemInfo> m_itemInfoList;
QStringList m_readItemNameLs;
HRESULT hResult;
HRESULT *pErrors;
WString m_ServerName;
WString m_GroupName;
DWORD m_UpdateRate;
CLSID m_clsid;
DWORD m_Cookie;
bool m_Async;
IConnectionPointContainer* _pIConnectionPointContainer;
IConnectionPoint* _pIConnectionPoint;
IOPCServer *_pIOPCServer;
IOPCBrowseServerAddressSpace *_pIOpcNamespace;
IOPCItemProperties *_pIOpcProperties;
IOPCGroupStateMgt *_pIOPCGroupStateMgt;
IOPCItemMgt *_pIOPCItemMgt;
IOPCSyncIO * _pIOPCSyncIO;
IOPCAsyncIO2 * _pIOPCAsyncIO2;
IOPCDataCallback * _pIOPCDataCallback;
COPCDataCallback *m_OPCDataCallback;
OPCHANDLE m_GroupHandle;
OPCHANDLE m_ServerHandle;
};
#endif //OPCCLIENT_H
// OPCClient.cpp
#include "OPCClient.h"
#include "copcdatacallback.h"
#include "mlog.h"
#include <QDateTime>
#include <QTextCodec>
#include <QMessageBox>
#include <QCoreApplication>
#include <comdef.h> // _com_error 所在头文件
#define _WIN32_DCOM
WCHAR * WSTRFromSBCS(const CHAR *buf, IMalloc * pmem)
{
int length, i;
WCHAR *temp;
length = strlen(buf) + 1;
if(pmem) temp = (WCHAR*)pmem->Alloc(sizeof(WCHAR) * (strlen(buf) + 1));
else temp = new WCHAR[strlen(buf) + 1];
if(temp)
{
for(i=0; i<length; i++) temp[i] = (WCHAR) buf[i];
}
return temp;
}
std::string ComErrMsg(HRESULT hr)
{
return QString::fromWCharArray(_com_error(hr).ErrorMessage()).toLocal8Bit().data();
}
OPCClient::OPCClient(QString s):
m_ServerName(qstr2Wstr(s))
{
Init();
}
OPCClient::OPCClient()
{
_pIConnectionPointContainer = NULL;
_pIConnectionPoint = NULL;
_pIOPCServer = NULL;
_pIOPCDataCallback = NULL;
_pIOpcNamespace = NULL;
_pIOpcProperties = NULL;
_pIOPCGroupStateMgt = NULL;
_pIOPCItemMgt = NULL;
_pIOPCSyncIO = NULL;
_pIOPCAsyncIO2 = NULL;
m_GroupHandle = 0;
m_ServerHandle = 0;
m_OPCDataCallback = NULL;
m_COPCTransaction = NULL;
m_Cookie = 0;
pErrors = NULL;
//state
isWriteAble = NULL;
items = new QList<ItemDef*>();
}
OPCClient::~OPCClient()
{
DisConnect();
UnInit();
}
bool OPCClient::Init()
{
// 为当前线程初始化COM库并设置并发模式。基于线程的并发模式一旦设置,将不能再改变。一个线程上调用CoInitializeEx如果与原来调用设置的并发模式不一致,将会失败并返回RPC_E_CHANGED_MODE。
//hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); //注册DCOM组件,获取结果
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr) )
{
WLog(LOG_ERR,"CoInitialize fail,hr[%0x] errMsg[%s]",hr,ComErrMsg(hr).c_str());
//qDebug() << "未开启COM组件注册...";
//QMessageBox::warning(0, tr("连接反馈"),tr("未开启COM组件注册"),QMessageBox::Ok);
return false;
}
WLog(LOG_INFO,"CoInitialize succ.");
//整个进程安全可以通过CoInitializeSecurity和相应参数设置。每个进程调用CoInitializeSercurity一次,再次调用会失败,并返回RPC_E_TOO_LATE
hr = CoInitializeSecurity(NULL, -1, NULL, NULL,RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);
if(hr != RPC_E_TOO_LATE && hr != S_OK) // 说明失败
{
WLog(LOG_ERR,"CoInitializeSecurity fail");
return false;
}
m_bInit = true;
return true;
}
void OPCClient::UnInit()
{
m_bInit = false;
CoUninitialize();
}
/**
* 建立OPC连接
* @brief OPCClient::Connect
* @param s
* @return
*/
HRESULT OPCClient::Connect(QString s)
{
this->m_ServerName = qstr2Wstr(s);
hResult = CoInitialize(0);
if (FAILED(hResult))
{
if (hResult == S_FALSE)
{
WLog(LOG_ERR,"Error CoInitialize():COM Library already initialized,hr[%0x],errMsg[%s]",hResult,ComErrMsg(hResult).c_str());
}
else
{
WLog(LOG_ERR,"Error CoInitialize():Initialisation of COM Library failed. hr=%0x,errMsg[%s]",hResult,ComErrMsg(hResult).c_str());
_pIOPCServer = 0;
CoUninitialize();
return hResult;
}
}
hResult = CLSIDFromProgID(this->m_ServerName, &this->m_clsid);
if (FAILED(hResult))
{
WLog(LOG_ERR,"Error CLSIDFromProgID():Retrival of CLSID failed,opcServerName[%s],hr[%0x] errMsg[%s]",
QString::fromWCharArray(this->m_ServerName).toLocal8Bit().data(),hResult,ComErrMsg(hResult).c_str());
CoUninitialize();
return hResult;
}
hResult = CoCreateInstance (this->m_clsid, NULL, CLSCTX_LOCAL_SERVER ,IID_IOPCServer, (void**)&_pIOPCServer);
if (FAILED(hResult))
{
WLog(LOG_ERR,"Error CoCreateInstance():Creation of IOPCServer-Object failed,hr[%d] errMsg[%s]",hResult,ComErrMsg(hResult).c_str());
_pIOPCServer = 0;
CoTaskMemFree(&this->m_clsid);
CoUninitialize();
return hResult;
}
hResult = _pIOPCServer->QueryInterface(IID_IOPCBrowseServerAddressSpace, (void**)&_pIOpcNamespace);
if (FAILED(hResult))
{
WLog(LOG_ERR,"Error QueryInterface of IID_IOPCBrowseServerAddressSpace-Object failed,hr[%d] errMsg[%s]",hResult,ComErrMsg(hResult).c_str());
CoUninitialize();
return hResult;
}
hResult = _pIOPCServer->QueryInterface(IID_IOPCItemProperties, (void**)&_pIOpcProperties);
if (FAILED(hResult))
{
WLog(LOG_ERR,"Error QueryInterface of IID_IOPCItemProperties-Object failed,hr[%d] errMsg[%s]",hResult,ComErrMsg(hResult).c_str());
CoUninitialize();
return hResult;
}
CoUninitialize();
return hResult;
}
bool OPCClient::Connect(QString serverName, QString ip)
{
WLog(LOG_INFO,"Connect to server[%s][%s] start",serverName.toLocal8Bit().data(),ip.toLocal8Bit().data());
if(!m_bInit)
{
Init();
}
this->m_ServerName = qstr2Wstr(serverName);
if(ip.isEmpty())
ip = "127.0.0.1";
IOPCServerList *pIServerList = NULL; //第一个接口指针,指向OPC服务器列表
IEnumGUID *pIEnumGUID = NULL; //第二个接口指针,指向OPC服务器列表枚举
IUnknown *pIUnknown = NULL; //第三个指针,服务器接口指针
//IOPCServer *_pIOPCServer = NULL; //第四个指针,指向OPC服务器的接口指针
COSERVERINFO si;
MULTI_QI mqi[1]; // 用于接收查询到的接口,可以为数组,以接收多个接口。
CATID catID[2];
CLSID clsid[20];
ZeroMemory(&si, sizeof(si)); //内存1 存放服务器信息
ZeroMemory(&mqi, sizeof(mqi)); //内存2 存放MULTI_QI用以获取OPC服务器列表
ZeroMemory(&catID, 2 * sizeof(CATID)); //内存3 存放OPC标准接口1.0和2.0
ZeroMemory(&clsid, 20 * sizeof(CLSID)); //内存4 存放枚举出的OPC服务器的CLSID
si.pwszName = (LPWSTR)(reinterpret_cast<const wchar_t*>(ip.utf16())); // 指定远程计算机名
mqi[0].hr = S_OK;
mqi[0].pIID = &IID_IOPCServerList;
mqi[0].pItf = NULL;
// CoCreateInstanceEx常用于创建远程组件,因为考虑到远程访问时要在网络上来回传递信息,所以此函数允许一次询多个接口。
HRESULT hr = CoCreateInstanceEx(CLSID_OpcServerList, NULL, CLSCTX_ALL, &si, 1, mqi); //1为指明要查询的接口个数
if (FAILED(hr)) //获取目标服务器端OPC服务器列表失败
{
WLog(LOG_ERR,"get CLSID_OpcServerList fail,hr[%0x] err[%s]",hr,ComErrMsg(hr).c_str());
return false;
}
// 获取目标服务器端OPC服务器列表成功
WLog(LOG_INFO,"get CLSID_OpcServerList succ.");
bool bConnect = false;
do
{
pIServerList = (IOPCServerList*)mqi[0].pItf; //得到第一个指针
if(pIServerList == NULL)
{
WLog(LOG_ERR,"mqi[0].pItf == NULL");
break;
}
// 进行具有OPC1.0和OPC2.0接口标准的OPC服务器的枚举
catID[0] = CATID_OPCDAServer10;
catID[1] = CATID_OPCDAServer20;
WLog(LOG_DEBUG,"Enum OPCDASERVER1.0 & OPCDASERVER2");
hr = pIServerList->EnumClassesOfCategories(2, catID, 2, catID, &pIEnumGUID); //Enum OPCDASERVER1.0 & OPCDASERVER2到第二个指针
if(pIEnumGUID == NULL || FAILED(hr))
{
WLog(LOG_ERR,"can't get OPCDAServer1.0和OPCDAServer2.0 interface,hr[%0x],err[%s]",hr,ComErrMsg(hr).c_str());
break;
}
hr = pIServerList->CLSIDFromProgID(m_ServerName, &this->m_clsid); //获取OPC服务器的CLSID
if (FAILED(hr))
{
WLog(LOG_ERR,"can't find the opc server,hr[%0x],err[%s]",hr,ComErrMsg(hr).c_str());
break;
}
WLog(LOG_INFO,"get the opc server clsid succ");
mqi[0].hr = S_OK;
mqi[0].pIID = &IID_IOPCServer;
mqi[0].pItf = NULL;
hr = CoCreateInstanceEx(this->m_clsid, NULL, CLSCTX_ALL, &si, 1, mqi); // 未连接上需要连接的OPC服务器
if (FAILED(hr))
{
WLog(LOG_ERR,"can't connect to opc server,hr[%0x],err[%s]",hr,ComErrMsg(hr).c_str());
break;
}
WLog(LOG_INFO,"connect to opc server succ ");
pIUnknown = (IUnknown*)mqi[0].pItf; //得到第三个指针
if(pIUnknown == NULL)
{
WLog(LOG_ERR,"mqi[0].pItf == NUL");
break;
}
hr = pIUnknown->QueryInterface(IID_IOPCServer,/*OUT*/(void**)&_pIOPCServer); //得到第四个指针
if(_pIOPCServer == NULL || FAILED(hr)) // 未能获取IOPCServer接口
{
WLog(LOG_ERR,"can't get the IID_IOPCServer interface,hr[%0x],errMsg[%s]",hr,ComErrMsg(hr).c_str());
break;
}
// 已获取IOPCServer接口
WLog(LOG_INFO,"get the IID_IOPCServer interface succ");
WLog(LOG_INFO,"Connect to server succ");
bConnect = true;
}while(0);
if(!bConnect)
{
if (_pIOPCServer != NULL)
{
_pIOPCServer->Release(); //第四个内存释放
_pIOPCServer = NULL;
}
if (pIUnknown != NULL)
{
pIUnknown->Release(); //第三个内存释放
pIUnknown = NULL;
}
if (pIEnumGUID != NULL) //第二个指针释放
{
pIEnumGUID->Release();
pIEnumGUID = NULL;
}
if(pIServerList != NULL) //第一个指针释放
{
pIServerList->Release();
pIServerList = NULL;
}
}
m_bConnect = bConnect;
return bConnect;
}
/**
* 添加分组
* @brief OPCClient::AddGroup
* @param n
* @param update_rate
* @return
*/
HRESULT OPCClient::AddGroup(QString n, DWORD update_rate, int async)
{
m_Async = async;
m_GroupName = qstr2Wstr(n);
m_ServerHandle = 1;
m_UpdateRate = 100;
long TimeBias = 0;
DWORD LanguageCode = 0x416, RevisedUpdateRate;
float DeadBand = 0.0; // 不敏感区域
hResult=_pIOPCServer->AddGroup(m_GroupName, // [in] group name
TRUE, // [in] active
update_rate, // [in] request this Update Rate from Server
m_ServerHandle, // [in] Client handle
&TimeBias, // [in] no time interval to system UTC time
&DeadBand, // [in] no deadband, so all data changes are reported
LanguageCode, // [in] Server uses English language for text values
&m_GroupHandle, // [out] Server handle to identify this group in later calls
&RevisedUpdateRate, // [out] the answer form the Server to the requested update rate
IID_IOPCGroupStateMgt, // [in] requested interface type of the group object
(LPUNKNOWN*)&this->_pIOPCGroupStateMgt); // [out] pointer to the requested interface
WLog(LOG_DEBUG,"AddGroup, result hr[%0x],errmsg[%s]",hResult,ComErrMsg(hResult).c_str());
if( hResult ==OPC_E_DUPLICATENAME ) //分组名称已存在
{
WLog(LOG_DEBUG,"1001:OPC_E_DUPLICATENAME");
}
if( hResult ==E_NOINTERFACE )
{
WLog(LOG_DEBUG, "1002:IOPCServer::AddGroup returned E_NOINTERFACE (IID_IOPCGroupStateMgt)");
}
if( update_rate != m_UpdateRate )
{
WLog(LOG_DEBUG,"1003: OPC server rejected data refresh interval. Setting it to [%d]ms,",update_rate);
}
if (hResult == OPC_S_UNSUPPORTEDRATE) // 请求的刷新速率与实际的刷新速率不一致
{
WLog(LOG_DEBUG,"OPC_S_UNSUPPORTEDRATE:%0x",OPC_S_UNSUPPORTEDRATE);
}
if( FAILED(hResult) || _pIOPCGroupStateMgt == NULL)
{
WLog(LOG_DEBUG,"AddGroup fail");
DisConnect();
return hResult;
}
hResult = _pIOPCGroupStateMgt->QueryInterface(IID_IOPCItemMgt,(void**)&this->_pIOPCItemMgt);
if (FAILED(hResult))
{
WLog(LOG_DEBUG,"_pIOPCGroupStateMgt->QueryInterface IID_IOPCItemMgt fail,hr[%0x],errmsg[%s]",hResult,ComErrMsg(hResult).c_str());
DisConnect();
return hResult;
}
//查询 group 对象的同步接口
if(m_Async) // 异步方式
{
hResult = _pIOPCItemMgt->QueryInterface(IID_IOPCAsyncIO2, (void**)&this->_pIOPCAsyncIO2);
if (FAILED(hResult))
{
WLog(LOG_DEBUG,"1008: _pIOPCItemMgt->QueryInterface IID_IOPCAsyncIO2 fail,hr[%0x],errmsg[%s]",hResult,ComErrMsg(hResult).c_str());
DisConnect();
return hResult;
}
//m_COPCTransaction = new COPCTransaction;
hResult = _pIOPCItemMgt->QueryInterface(IID_IConnectionPointContainer, (void**)&this->_pIConnectionPointContainer);
//hResult = _pIOPCItemMgt->QueryInterface(&_pIConnectionPointContainer);
if (FAILED(hResult))
{
WLog(LOG_DEBUG,"1009: IConnectionPointContainer query fail!hr[%0x],errmsg[%s]",hResult,ComErrMsg(hResult).c_str());
DisConnect();
return hResult;
}
hResult = _pIConnectionPointContainer->FindConnectionPoint( IID_IOPCDataCallback, &_pIConnectionPoint );
if( FAILED(hResult) || _pIConnectionPoint == NULL)
{
WLog(LOG_DEBUG,"1010: A group of OPC with no IOPCDataCallback. OPC server appears to not conform to the OPC DA 2.0 standard.hr[%0x],errmsg[%s]",
hResult,ComErrMsg(hResult).c_str());
DisConnect();
return hResult;
}
if( m_OPCDataCallback == NULL )
{
if(m_COPCTransaction == NULL)
{
m_COPCTransaction = new COPCTransaction;
}
m_OPCDataCallback = new COPCDataCallback(m_COPCTransaction);
m_OPCDataCallback->AddRef();
/*CComModule _Module;
_pAtlModule = &_Module;
WLog(LOG_DEBUG, "CreateInstance before");
//hResult = CComObject<COPCDataCallback>::CreateInstance(&m_OPCDataCallback);
m_OPCDataCallback->CreateInstance(&m_OPCDataCallback);
m_OPCDataCallback->AddRef();
m_OPCDataCallback->m_COPCTransaction = m_COPCTransaction;*/
}
hResult = _pIConnectionPoint->Advise(m_OPCDataCallback, &m_Cookie); // 设置回调,进行数据订阅
if(FAILED(hResult))
{
WLog(LOG_ERR, "1011: OPCDataCallback set faild,m_Cookie[%d],hr[%0x],err[%s].",m_Cookie,hResult,ComErrMsg(hResult).c_str());
_pIConnectionPointContainer->Release();
DisConnect();
return hResult;
}
WLog(LOG_INFO,"set callback succ,Advise succ");
DWORD dwRevUpdateRate = 10;
BOOL bActivateGroup = TRUE;
hResult = _pIOPCGroupStateMgt->SetState(/*[in] RequestedUpdateRate*/ NULL, /*[out] RevisedUpdateRate */ &dwRevUpdateRate,
/*[in] ActiveFlag for Group */ &bActivateGroup, /*[in] TimeBias*/ NULL,
/*[in] PercentDeadband*/ NULL, /*[in] LCID*/ NULL, NULL);
if(FAILED(hResult))
{
WLog(LOG_ERR,"_pIOPCGroupStateMgt->SetState hr[%0x] ,errMsg[%s]",hResult,ComErrMsg(hResult).c_str());
}
isWriteAble = true;
}
else // 同步方式
{
hResult = _pIOPCItemMgt->QueryInterface(IID_IOPCSyncIO, (void**)&this->_pIOPCSyncIO);
if (FAILED(hResult))
{
WLog(LOG_ERR,"1007: IOPCSyncIO QueryInterface fail!,hr[%0x],err[%s].",m_Cookie,hResult,ComErrMsg(hResult).c_str());
DisConnect();
return hResult;
}
isWriteAble = true;
}
WLog(LOG_INFO,"isWriteAble[%d] async[%d]",isWriteAble,async);
return hResult;
}
/**
* 添加监视项
* @brief OPCClient::AddItems
* @param inames
* @return
*/
QStringList OPCClient::AddItems(QStringList inames)
{
QStringList r_inames;
if(!_pIOPCItemMgt) return r_inames;
pErrors = NULL;
DWORD iCount=items->count();
OPCITEMRESULT * pResults = NULL;
WLog(LOG_DEBUG,"AddItems begin,inames.count[%d]",inames.count());
for(int i=0;i<inames.count();i++)
{
QString iname = inames[i];
if(getItemByName(iname))
{
WLog(LOG_DEBUG,"%s already exsits.",iname.toLocal8Bit().data());
continue;
}
r_inames.append(iname);
}
int nCount=r_inames.count();
if(nCount>0)
{
OPCITEMDEF *defVec = (OPCITEMDEF*)CoTaskMemAlloc(sizeof(OPCITEMDEF)*nCount);
for(int i=0;i<nCount;i++)
{
QString iname = r_inames[i];
defVec[i].szAccessPath= SysAllocString(L""); // path SysAllocString(qstr2Wstr(name))
defVec[i].szItemID = SysAllocString(qstr2Wstr(iname)); // name
defVec[i].bActive = TRUE;
defVec[i].hClient = iCount+i;
defVec[i].dwBlobSize = 0;
defVec[i].pBlob = NULL;
defVec[i].vtRequestedDataType = 0;
}
//此处添加的所有项必须在服务器端都有才能添加成功,否则将返回服务器端异常或者函数不正确 错误信息
hResult = _pIOPCItemMgt->AddItems(nCount, // [in] add items
defVec, // [in] see above
&pResults, // [out] array with additional information about the item
&pErrors); // [out] tells which of the items was successfully added.
WLog(LOG_DEBUG,"AddItems hr[%0x] errMsg[%s]",hResult,ComErrMsg(hResult).c_str());
if(hResult==S_OK)
{
for(int i=0;i < nCount; i++)
{
QString iname = r_inames[i];
DWORD cItemHandle = iCount+i;
ItemDef *item = new ItemDef;
item->name = iname;
item->accessRights = pResults[i].dwAccessRights;
item->hClientHandle = cItemHandle;
item->type = pResults[i].vtCanonicalDataType;
item->hServerHandle = pResults[i].hServer;
items->append(item);
// 检测 Item 的可读写性
if (pResults[i].dwAccessRights != (OPC_READABLE + OPC_WRITEABLE)) // 不可读,也不可写,请检查服务器配置
{
// WLog(LOG_DEBUG,"Item[%d][%s] dwAccessRights[%0x]: !OPC_READABLE[%0x] && !OPC_WRITEABLE[%0x]",i,iname.toLocal8Bit().data(),
// pResults[i].dwAccessRights,OPC_READABLE,OPC_WRITEABLE);
}
}
}
if(pResults) CoTaskMemFree( pResults );
if(pErrors) CoTaskMemFree( pErrors );
ClearOPCITEMDEF(defVec, nCount);
}
WLog(LOG_DEBUG,"AddItems end, add count[%d],items.size[%d]",nCount,items->size());
return r_inames;
}
void OPCClient::browseServerList(QString ip, QListWidget *listWidget)
{
listWidget->clear();
IOPCServerList *pIServerList = NULL; //第一个接口指针,指向OPC服务器列表
IEnumGUID *pIEnumGUID = NULL; //第二个接口指针,指向OPC服务器列表枚举
IUnknown *pIUnknown = NULL; //第三个指针,服务器接口指针
IOPCServer *pIServer = NULL; //第四个指针,指向OPC服务器的接口指针
HRESULT hr;
COSERVERINFO si;
MULTI_QI mqi[1];
CATID catID[2];
CLSID clsid[20];
ZeroMemory(&si, sizeof(si)); //内存1 存放服务器信息
ZeroMemory(&mqi, sizeof(mqi)); //内存2 存放MULTI_QI用以获取OPC服务器列表
ZeroMemory(&catID, 2 * sizeof(CATID)); //内存3 存放OPC标准接口1.0和2.0
ZeroMemory(&clsid, 20 * sizeof(CLSID)); //内存4 存放枚举出的OPC服务器的CLSID
//hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); //注册DCOM组件,获取结果
hr = CoInitialize(0);
if (FAILED(hr))
{
WLog(LOG_ERR,"注册COM组件失败,CoInitialize fail,hr[%0x] errMsg[%s]",hr,ComErrMsg(hr).c_str());
return;
}
else if (SUCCEEDED(hr))
{
WLog(LOG_INFO,"已注册COM组件...");
}
HRESULT hr_sec = CoInitializeSecurity(NULL, -1, NULL, NULL,RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);
if(hr_sec == S_OK || RPC_E_TOO_LATE == hr_sec)
{
//si.pwszName = (LPWSTR)(ipstr); //Covert CString to LPWSTR 把服务器名转为宽字节字符串
si.pwszName = (LPWSTR)(reinterpret_cast<const wchar_t*>(ip.utf16()));
mqi[0].hr = S_OK;
mqi[0].pIID = &IID_IOPCServerList;
mqi[0].pItf = NULL;
hr = CoCreateInstanceEx(CLSID_OpcServerList, NULL, CLSCTX_ALL, &si, 1, mqi); //Connect to the t
if (FAILED(hr))
{
WLog(LOG_ERR,"获取目标服务器端OPC服务器列表失败,hr[%0x],errMsg[%s]",hr,ComErrMsg(hr).c_str());
}
else if (SUCCEEDED(hr))
{
qDebug() << "获取目标服务器端OPC服务器列表成功...";
}
pIServerList = (IOPCServerList*)mqi[0].pItf; //得到第一个指针
if(pIServerList)
{
catID[0] = CATID_OPCDAServer10;
catID[1] = CATID_OPCDAServer20;
hr = pIServerList->EnumClassesOfCategories(2, catID, 2, catID, &pIEnumGUID); //Enum OPCDASERVER1.0 & OPCDASERVER2到第二个指针
if(pIEnumGUID)
{
if (FAILED(hr))
{
qDebug() << "未获取到OPCDAServer1.0和OPCDAServer2.0接口";
CoTaskMemFree(&catID); //第三个内存释放
CoTaskMemFree(&mqi); //第二个内存释放
CoTaskMemFree(&si); //第一个内存释放
if (pIEnumGUID) pIEnumGUID->Release(); //第二个指针释放
pIEnumGUID = NULL;
if (pIServerList) pIServerList->Release(); //第一个指针释放
pIServerList = NULL;
return;
}
else if (SUCCEEDED(hr))
{
qDebug() << "已获取到OPCDAServer1.0和OPCDAServer2.0接口";
}
ULONG nCount;
HRESULT hr_1;
qDebug() << "搜索到的接口列表:" << endl;
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);
if(hr_2 == S_OK)
{
qDebug()<< szProID;
listWidget->addItem(QString::fromStdWString(szProID));
CoTaskMemFree(szProID);
CoTaskMemFree(szUserType);
}
}
} while (hr_1 == S_OK);
qDebug() << "枚举完毕...";
}
}
}
CoTaskMemFree(&catID); //第三个内存释放
CoTaskMemFree(&mqi); //第二个内存释放
CoTaskMemFree(&si); //第一个内存释放
if (pIEnumGUID) pIEnumGUID->Release(); //第二个指针释放
pIEnumGUID = NULL;
if (pIServerList) pIServerList->Release(); //第一个指针释放
pIServerList = NULL;
}
/**
* 同步读取指定项
* @brief OPCClient::ReadItem
* @param item
* @return
*/
bool OPCClient::ReadItem(ItemDef *item)
{
bool result = false;
OPCITEMSTATE *pItemValue;
hResult = _pIOPCSyncIO->Read(OPC_DS_DEVICE, 1, &item->hServerHandle, &pItemValue, &pErrors);
if(SUCCEEDED(hResult))
{
item->value = pItemValue[0].vDataValue;
item->quality = pItemValue[0].wQuality;
item->time = pItemValue[0].ftTimeStamp;
item->hClientHandle = pItemValue[0].hClient;
result = true;
VariantClear(&pItemValue->vDataValue);
WLog(LOG_DEBUG,"ReadItem succ,name[%s] val[%s] quality[%l] time[%s]",
item->name.toLocal8Bit().data(),VariantString(item->value).toLocal8Bit().data(),item->quality,TimeString(item->time).toLocal8Bit().data());
}
else
{
WLog(LOG_ERR,"同步读取项目失败,hr[%d] errMsg[%s]",hResult,ComErrMsg(hResult).c_str());
}
CoTaskMemFree(pErrors);
CoTaskMemFree(pItemValue);
return result;
}
HRESULT OPCClient::ReadAllValue_Sync(std::vector<ItemInfo> &changeVec)
{
if (_pIOPCSyncIO == NULL)
{
WLog(LOG_ERR, "Failed to ReadAllValue_Sync,_pIOPCSyncIO == NULL");
return E_FAIL;
}
if(items->size() == 0)
{
WLog(LOG_ERR, "It had not add item,items.size[%d]",items->size());
return E_FAIL;
}
OPCITEMSTATE *pItemValue;
HRESULT *pErrors = NULL;
std::vector<OPCHANDLE> phServerVec;
for (ItemDef* item : *items)
{
phServerVec.push_back(item->hServerHandle);
}
HRESULT hr = _pIOPCSyncIO->Read(OPC_DS_DEVICE,phServerVec.size(), phServerVec.data(), &pItemValue, &pErrors);
if (FAILED(hr))
{
WLog(LOG_ERR, "_pIOPCSyncIO->Read err,hr[%0x] errMsg[%s],item.size[%d]", hr, ComErrMsg(hr).c_str(), phServerVec.size());
if(hr == 0x800706ba) // 远程过程调用失败 或 RPC 服务器不可用。 说明与opc服务器断开连接了
{
m_bConnect = false;
}
if (pErrors) CoTaskMemFree(pErrors);
CoTaskMemFree(pItemValue);
return hr;
}
int count = items->size();
for (int i = 0; i < count; i++)
{
ItemDef *pItem = getItemByHandle(pItemValue[i].hClient);
if(pItem == nullptr)
{
continue;
}
bool bValChange = false;
if (pItemValue[i].vDataValue.vt != VT_EMPTY && pItemValue[i].wQuality == OPC_QUALITY_GOOD) // 必须是质量最好的才是,否则不能要
{
pItem->value = pItemValue[i].vDataValue; // 更新对应的值和时间
pItem->quality = pItemValue[i].wQuality;
pItem->time = pItemValue[i].ftTimeStamp;
ItemInfo info;
info.m_name = pItem->name;
info.m_val = VariantString(pItem->value);
info.m_time = TimeString(pItem->time);
info.m_quality = QualityString(pItem->quality);
auto it = std::find_if(m_itemInfoList.begin(),m_itemInfoList.end(),[=](ItemInfo &tinfo){
return tinfo.m_name == info.m_name;
});
if(it == m_itemInfoList.end()) // 列表中没找到,则加入列表
{
m_itemInfoList.push_back(info);
changeVec.push_back(info);
bValChange = true;
}
else
{
if(it->m_val != info.m_val) // 值有变化,添加到vec中,如无变化,则不理会
{
it->m_val = info.m_val; // 值变化,更新到列表,防止下次再查询到相同的值又更新
changeVec.push_back(info);
bValChange = true;
}
}
if(bValChange) // 有变化才打印
WLog(LOG_DEBUG, "readSync name[%s] val[%s] time[%s] quality[%s]", pItem->name.toLocal8Bit().data(), info.m_val.toLocal8Bit().data(),
info.m_time.toLocal8Bit().data(), info.m_quality.toLocal8Bit().data());
}
}
if (pErrors) CoTaskMemFree(pErrors);
CoTaskMemFree(pItemValue);
if(changeVec.size() > 0) WLog(LOG_DEBUG, "_pIOPCSyncIO->Read succ,changeVec.size[%d]", changeVec.size());
return hr;
}
HRESULT OPCClient::ReadAllValue_Async()
{
if( _pIOPCAsyncIO2 == NULL )
{
WLog(LOG_ERR,"Failed to ReadAllValue_Async,_pIOPCAsyncIO2 == NULL");
return E_FAIL;
}
DWORD cancelid = 0;
HRESULT *pErrors = NULL;
std::vector<OPCHANDLE> phServerVec;
for(ItemDef* item : *items)
{
phServerVec.push_back(item->hServerHandle);
}
HRESULT hr = _pIOPCAsyncIO2->Read(phServerVec.size(), phServerVec.data(), 1, &cancelid, &pErrors);
if(SUCCEEDED(hr))
{
WLog(LOG_DEBUG,"_pIOPCAsyncIO2->Read succ,item.size[%d]",phServerVec.size());
return hr;
}
WLog(LOG_ERR,"_pIOPCAsyncIO2->Read err,hr[%0x] errMsg[%s],item.size[%d]",hr,ComErrMsg(hr).c_str(),phServerVec.size());
return hr;
}
/**
* 写入值
* @brief OPCClient::WriteValue
* @param cHandle
* @param time
* @param value
* @param Quality
* @return
*/
bool OPCClient::WriteValue( DWORD cHandle, FILETIME &time, VARIANT &value, WORD Quality )
{
ItemDef * item = getItemByHandle( cHandle );
if( !item )
return false;
item->quality = Quality;
item->value = value;
item->time = time;
if( m_Async )
return SUCCEEDED( WriteValue_Async(item) );
else
return SUCCEEDED( WriteValue_Sync( item ) );
}
bool OPCClient::WriteValue(QString &n, QString &v )
{
ItemDef * item = getItemByName(n);
if(!item )
return false;
StringVariant(item, v);
if( m_Async )
return SUCCEEDED( WriteValue_Async(item) );
else
return SUCCEEDED( WriteValue_Sync( item ) );
}
HRESULT OPCClient::WriteValue_Async(ItemDef * item)
{
QString str;
pErrors = NULL;
if( m_COPCTransaction == NULL )
{
WLog(LOG_ERR,"m_COPCTransaction == NULL");
return E_FAIL;
}
if( !isConnected() )
{
WLog(LOG_ERR,"Un connected");
return E_FAIL;
}
// m_Group->QueryInterface( &AsyncIO );
if( _pIOPCAsyncIO2 == NULL )
{
WLog(LOG_ERR,"Failed to get interface IOPCAsyncIO2,_pIOPCAsyncIO2 == NULL");
return E_FAIL;
}
DWORD cancelID = 0;
hResult = _pIOPCAsyncIO2->Write( 1, &item->hServerHandle, &item->value, rand(), &cancelID, &pErrors);
if( FAILED(hResult) /*|| FAILED(pErrors[0])*/)
{
WLog(LOG_ERR,"Parameter[%s] is not passed,hResult[%0x],errMsg[%s]",item->name.toLocal8Bit().data(),hResult,ComErrMsg(hResult).c_str());
hResult = E_FAIL;
}
if( pErrors )
{
switch( pErrors[0] )
{
case OPC_S_CLAMP:
WLog(LOG_DEBUG,"AsyncIO->Write(%s) -> [OPC_S_CLAMP] The value was accepted but was clamped.",item->name.toLocal8Bit().data());
break;
case OPC_E_RANGE: WLog(LOG_DEBUG,"AsyncIO->Write(%s) -> [OPC_E_RANGE] The value was out of range.",item->name.toLocal8Bit().data());
break;
case OPC_E_BADTYPE:
str=QString("AsyncIO->Write(%1) -> [OPC_E_BADTYPE] The passed data type (%2) cannot be accepted for this item.").arg(item->name).arg(item->value.vt);
WLog(LOG_DEBUG,"%s",str.toLocal8Bit().data());
break;
case OPC_E_BADRIGHTS:
WLog(LOG_DEBUG,"AsyncIO->Write(%s)->[OPC_E_BADRIGHTS] The item is not writeable.",item->name.toLocal8Bit().data());
break;
case OPC_E_INVALIDHANDLE:
WLog(LOG_DEBUG,"AsyncIO->Write(%s)->[OPC_E_INVALIDHANDLE] The passed item handle was invalid.",item->name.toLocal8Bit().data());
break;
case OPC_E_UNKNOWNITEMID:
WLog(LOG_DEBUG,"AsyncIO->Write(%s)->[OPC_E_UNKNOWNITEMID] The item is no longer available in the server address space.",item->name.toLocal8Bit().data());
break;
}
}
if( pErrors )CoTaskMemFree( pErrors );
pErrors = NULL;
return hResult;
}
HRESULT OPCClient::WriteValue_Sync( ItemDef * item )
{
pErrors = NULL;
if( m_COPCTransaction == NULL )
{
WLog(LOG_ERR,"m_COPCTransaction == NULL");
return E_FAIL;
}
if( !isConnected() )
{
WLog(LOG_ERR,"Un connected");
return E_FAIL;
}
WLog(LOG_DEBUG,"Sync Write start (hdl=%d value=%s)",item->hServerHandle,VariantString(item->value).toLocal8Bit().data());
hResult = _pIOPCSyncIO->Write(1, &item->hServerHandle, &item->value, &pErrors);
WLog(LOG_DEBUG,"Sync Write finished.hr[%0x],errMsg[%s]",hResult,ComErrMsg(hResult).c_str());
if( FAILED(hResult) /*|| FAILED(pErrors[0])*/)
{
WLog(LOG_ERR,"Parameter [%s] is not passed",item->name.toLocal8Bit().data());
hResult = E_FAIL;
if( pErrors != NULL)
{
switch( pErrors[0] )
{
case OPC_S_CLAMP:
WLog(LOG_DEBUG,"SyncIO->Write(%s)->[OPC_S_CLAMP]. The value was accepted but was clamped.",item->name.toLocal8Bit().data()) ;
break;
case OPC_E_RANGE:
WLog(LOG_DEBUG,"SyncIO->Write(%s)->[OPC_E_RANGE]. The value was out of range.",item->name.toLocal8Bit().data()) ;
break;
case OPC_E_BADTYPE:
WLog(LOG_DEBUG,"SyncIO->Write(%s)->[OPC_E_BADTYPE]. The passed data type cannot be accepted for this item.",item->name.toLocal8Bit().data());
break;
case OPC_E_BADRIGHTS:
WLog(LOG_DEBUG,"SyncIO->Write(%s)->[OPC_E_BADRIGHTS] The item is not writeable.",item->name.toLocal8Bit().data());
break;
case OPC_E_INVALIDHANDLE:
WLog(LOG_DEBUG,"SyncIO->Write(%s)->[OPC_E_INVALIDHANDLE] The passed item handle was invalid.",item->name.toLocal8Bit().data());
break;
case OPC_E_UNKNOWNITEMID:
WLog(LOG_DEBUG,"SyncIO->Write(%s)->[OPC_E_UNKNOWNITEMID] The item is no longer available in the server address space.",item->name.toLocal8Bit().data());
break;
}
}
}
if( pErrors )
CoTaskMemFree( pErrors );
pErrors = NULL;
return hResult;
}
/**
* 移除所有项目
* @brief OPCClient::RemoveItems
* @param _items
* @return
*/
HRESULT OPCClient::RemoveItems(QStringList inames)
{
int _iCount = inames.count();
if( _iCount==0) return -1;
std::vector<OPCHANDLE> _hpSvr;
_hpSvr.resize(_iCount);
//OPCHANDLE _hpSvr[_iCount];
for(int i=0;i<_iCount ;i++)
{
if(getItemByName(inames.value(i)))
{
_hpSvr[i] = getItemByName(inames.at(i))->hServerHandle;
}
}
hResult = _pIOPCItemMgt->RemoveItems(_iCount, _hpSvr.data(), &pErrors);
if(SUCCEEDED(hResult))
{
for(int i=0;i<_iCount ;i++)
{
items->removeAll(getItemByName(inames.at(i)));;
}
}
return hResult;
}
/**
* @brief OPCClient::RemoveAllItems
* @return
*/
HRESULT OPCClient::RemoveAllItems()
{
if(items->size() == 0)
{
return S_OK;
}
int _iCount = items->count();
std::vector<OPCHANDLE> _hpSvr;
_hpSvr.resize(_iCount);
for(int i=0;i<_iCount ;i++)
{
// qDebug() <<" next one->"<< items->at(i)->hServerHandle << items->at(i)->name;
_hpSvr[i] = items->at(i)->hServerHandle;
}
hResult = _pIOPCItemMgt->RemoveItems(_iCount, _hpSvr.data(), &pErrors);
if(FAILED(hResult))
{
WLog(LOG_DEBUG,"RemoveAllItems fail,hResult[%0x],errMsg[%s]",hResult,ComErrMsg(hResult).c_str());
}
auto it = items->begin();
for(; it!= items->end(); )
{
ItemDef *itdef = (*it);
delete itdef;
itdef = nullptr;
items->erase(it++);
}
items->clear();
return hResult;
}
/**
* 断开连接
* @brief OPCClient::DisConnect
*/
void OPCClient::DisConnect()
{
if(_pIOPCSyncIO)
{
_pIOPCSyncIO->Release();
_pIOPCSyncIO=0;
}
if (_pIConnectionPoint)
{
_pIConnectionPoint->Unadvise(m_Cookie);
_pIConnectionPoint->Release();
_pIConnectionPoint = 0;
}
if(_pIOPCItemMgt)
{
_pIOPCItemMgt->Release();
_pIOPCItemMgt = 0;
}
if(_pIOPCGroupStateMgt)
{
_pIOPCGroupStateMgt->Release();
_pIOPCGroupStateMgt = 0;
}
if(_pIOPCServer)
{
_pIOPCServer->RemoveGroup(m_GroupHandle, TRUE);
_pIOPCServer->Release();
_pIOPCServer=0;
}
if(m_OPCDataCallback)
{
m_OPCDataCallback->Release();
m_OPCDataCallback = 0;
}
if(m_COPCTransaction)
{
m_COPCTransaction->deleteLater();
m_COPCTransaction = 0;
}
m_GroupHandle = 0;
m_ServerHandle = 0;
RemoveAllItems();
delete items;
items = nullptr;
}
void OPCClient::TryBrowseBranch(IOPCBrowseServerAddressSpace *pBSAS, LPCWSTR lpprestr, LPCWSTR lpcw)
{
HRESULT r1;
OPCNAMESPACETYPE onst;
LPWSTR pItemID;
WCHAR s,*lp;
IEnumString *pEnum = 0;
IMalloc *pIMalloc;
char szBuf[40];
long nPos = 1;
HRESULT r2 = S_OK;
LPOLESTR pStr;
ULONG actual;
//lp = WSTRFromSBCS("%ls,%ls",pIMalloc);
// 向lpcw节点的枝叶改变位置
pBSAS->ChangeBrowsePosition(OPC_BROWSE_DOWN,lpcw);
r1 = pBSAS->BrowseOPCItemIDs(OPC_FLAT,&s,VT_EMPTY,0,&pEnum);
pBSAS->QueryOrganization(&onst);
// 提取该节点下的所有枝叶
while((r2 = pEnum->Next(1, &pStr, &actual)) == S_OK)
{
// WLog(LOG_DEBUG,"TryBrowseBranch[%s][%s][%s]",QString::fromWCharArray(lpprestr).toLocal8Bit().data(),QString::fromWCharArray(lpcw).toLocal8Bit().data(),
// QString::fromWCharArray(pStr).toLocal8Bit().data());
if(onst == OPC_BRANCH)
TryBrowseBranch(pBSAS,lpprestr,pStr);
// browseItem(nullptr,QString::fromWCharArray(pStr)); // 添加测试
}
// pBSAS->ChangeBrowsePosition(OPC_BROWSE_DOWN,
// printf("OPC_NS_HIERARCHIAL 含有枝干");
// get firset item
// pIMalloc->Free(lp);
//pIMalloc->Free(pStr);
pEnum->Release();
// 回到名叫lpcw的主干位置
pBSAS->ChangeBrowsePosition(OPC_BROWSE_UP,lpcw);
}
void OPCClient::BrowseAllItem()
{
if (_pIOPCServer == nullptr)
{
return;
}
HRESULT hr;
IOPCBrowseServerAddressSpace *pIOPCBrowseServerAddressSpace=NULL;
_pIOPCServer->QueryInterface(IID_IOPCBrowseServerAddressSpace, (void **)&pIOPCBrowseServerAddressSpace);
LPCWSTR lpprestr;
LPCWSTR lpcw;
TryBrowseBranch(pIOPCBrowseServerAddressSpace,lpprestr,lpcw);
}
bool OPCClient::browseSubGroup(QTreeWidget *groupWidget, QString groupName)
{
bool find = false;
LPCWSTR name = groupName.toStdWString().c_str();
HRESULT hr;
IOPCBrowseServerAddressSpace *pIOPCBrowseServerAddressSpace=NULL;
_pIOPCServer->QueryInterface(IID_IOPCBrowseServerAddressSpace, (void **)&pIOPCBrowseServerAddressSpace);
LPENUMSTRING pPENUMSTRING;
hr = pIOPCBrowseServerAddressSpace->ChangeBrowsePosition(OPC_BROWSE_DOWN,name);
if(hr == S_OK)
WLog(LOG_DEBUG,"ChangeBrowsePosition success");
else
{
WLog(LOG_ERR,"ChangeBrowsePosition fail,hr[%0x] errMsg[%s]",hr,ComErrMsg(hr).c_str());
return find;
}
hr = pIOPCBrowseServerAddressSpace->BrowseOPCItemIDs(OPC_BRANCH, L"", VT_EMPTY, 0, &pPENUMSTRING);
if(hr == S_OK)
WLog(LOG_DEBUG,"BrowseOPCItemIDs");
else
WLog(LOG_ERR,"BrowseOPCItemIDs fail,hr[%0x],errMsg[%s]",hr,ComErrMsg(hr).c_str());
IOPCItemProperties *pIOPCItemProperties=NULL;
_pIOPCServer->QueryInterface(IID_IOPCItemProperties, (void **)&pIOPCItemProperties);
ULONG count=0;
LPOLESTR pItemName, pItemID;
while(pPENUMSTRING->Next(1, &pItemName, &count) == S_OK)
{
find = true;
char bufName[256]={0};
WideCharToMultiByte(CP_ACP, 0, pItemName, -1, bufName, 256, NULL, NULL);
QTextCodec *codec = QTextCodec::codecForName("GBK");//指定QString的编码方式
QString itemname = codec->toUnicode(bufName,strlen(bufName));
qDebug()<<itemname;
WLog(LOG_DEBUG,"browseSubGroup[%s]",itemname.toLocal8Bit().data());
if(groupWidget != Q_NULLPTR)
{
QTreeWidgetItem *item = new QTreeWidgetItem(QStringList(itemname));
QTreeWidgetItem *parentItem = groupWidget->currentItem();
parentItem->addChild(item);
}
pIOPCBrowseServerAddressSpace->GetItemID(pItemName, &pItemID);
WideCharToMultiByte(CP_ACP, 0, pItemID, -1, bufName, 256, NULL, NULL);
//printf("ITEM_ID: %s\n", bufName);
itemname = codec->toUnicode(bufName,strlen(bufName));
qDebug()<<itemname;
DWORD dwCount;
DWORD *dwPropertyIDs;
LPWSTR *ppDescriptions;
VARTYPE *ppvtDataTypes;
VARIANT *ppvData;
HRESULT *ppErrors;
pIOPCItemProperties->QueryAvailableProperties(pItemID, &dwCount, &dwPropertyIDs, &ppDescriptions, &ppvtDataTypes);
pIOPCItemProperties->GetItemProperties(pItemID, dwCount, dwPropertyIDs, &ppvData, &ppErrors);
QCoreApplication::processEvents();
}
return find;
}
bool OPCClient::browseItem(QTreeWidget *itemWidget, QString groupName)
{
bool find = false;
LPCWSTR name = groupName.toStdWString().c_str();
HRESULT hr;
IOPCBrowseServerAddressSpace *pIOPCBrowseServerAddressSpace=NULL;
_pIOPCServer->QueryInterface(IID_IOPCBrowseServerAddressSpace, (void **)&pIOPCBrowseServerAddressSpace);
LPENUMSTRING pPENUMSTRING;
hr = pIOPCBrowseServerAddressSpace->ChangeBrowsePosition(OPC_BROWSE_DOWN,name);
if(hr == S_OK)
WLog(LOG_DEBUG,"ChangeBrowsePosition success");
else
{
WLog(LOG_ERR,"ChangeBrowsePosition fail,hr[%0x] errMsg[%s]",hr,ComErrMsg(hr).c_str());
return find;
}
hr = pIOPCBrowseServerAddressSpace->BrowseOPCItemIDs(OPC_FLAT, L"", VT_EMPTY, 0, &pPENUMSTRING);
if(hr == S_OK)
WLog(LOG_DEBUG,"BrowseOPCItemIDs");
else
{
WLog(LOG_ERR,"BrowseOPCItemIDs fail,change the option,hr[%0x],errMsg[%s]",hr,ComErrMsg(hr).c_str());
hr = pIOPCBrowseServerAddressSpace->BrowseOPCItemIDs(OPC_FLAT, L"", VT_EMPTY, 0, &pPENUMSTRING);
}
IOPCItemProperties *pIOPCItemProperties=NULL;
_pIOPCServer->QueryInterface(IID_IOPCItemProperties, (void **)&pIOPCItemProperties);
ULONG count=0;
LPOLESTR pItemName, pItemID;
while(pPENUMSTRING->Next(1, &pItemName, &count) == S_OK)
{
find = true ;
char bufName[256]={0};
WideCharToMultiByte(CP_ACP, 0, pItemName, -1, bufName, 256, NULL, NULL);
QTextCodec *codec = QTextCodec::codecForName("GBK");//指定QString的编码方式
QString itemname = codec->toUnicode(bufName,strlen(bufName));
// if(itemname == groupName)
// break;
//qDebug()<<itemname;
//WLog(LOG_DEBUG,"browseItem[%s]",itemname.toLocal8Bit().data());
m_readItemNameLs.push_back(itemname);
if(itemWidget != Q_NULLPTR)
{
QTreeWidgetItem *item = new QTreeWidgetItem(QStringList(itemname));
QTreeWidgetItem *parentItem = itemWidget->currentItem();
parentItem->addChild(item);
}
pIOPCBrowseServerAddressSpace->GetItemID(pItemName, &pItemID);
pIOPCBrowseServerAddressSpace->GetItemID(pItemName, &pItemID);
WideCharToMultiByte(CP_ACP, 0, pItemID, -1, bufName, 256, NULL, NULL);
//WLog(LOG_DEBUG,"ITEM_ID[%s]", bufName);
DWORD dwCount;
DWORD *dwPropertyIDs;
LPWSTR *ppDescriptions;
VARTYPE *ppvtDataTypes;
VARIANT *ppvData;
HRESULT *ppErrors;
pIOPCItemProperties->QueryAvailableProperties(pItemID, &dwCount, &dwPropertyIDs, &ppDescriptions, &ppvtDataTypes);
pIOPCItemProperties->GetItemProperties(pItemID, dwCount, dwPropertyIDs, &ppvData, &ppErrors);
QCoreApplication::processEvents();
}
pIOPCBrowseServerAddressSpace->ChangeBrowsePosition(OPC_BROWSE_UP,name);
return find;
}
void OPCClient::browseGroup(QTreeWidget *groupWidget)
{
/*if(groupWidget == NULL)
return;
groupWidget->clear();*/
HRESULT hr;
IOPCBrowseServerAddressSpace *pIOPCBrowseServerAddressSpace=NULL;
_pIOPCServer->QueryInterface(IID_IOPCBrowseServerAddressSpace, (void **)&pIOPCBrowseServerAddressSpace);
LPENUMSTRING pPENUMSTRING;
hr = pIOPCBrowseServerAddressSpace->BrowseOPCItemIDs(OPC_BRANCH, L"", VT_EMPTY, 0, &pPENUMSTRING);
if(hr == S_OK)
WLog(LOG_DEBUG,"BrowseOPCItemIDs");
else
WLog(LOG_ERR,"BrowseOPCItemIDs fail,hr[%0x] errMsg[%s]",hr,ComErrMsg(hr).c_str());
IOPCItemProperties *pIOPCItemProperties=NULL;
_pIOPCServer->QueryInterface(IID_IOPCItemProperties, (void **)&pIOPCItemProperties);
ULONG count=0;
LPOLESTR pItemName, pItemID;
while(pPENUMSTRING->Next(1, &pItemName, &count) == S_OK)
{
char bufName[256]={0};
WideCharToMultiByte(CP_ACP, 0, pItemName, -1, bufName, 256, NULL, NULL);
QTextCodec *codec = QTextCodec::codecForName("GBK");//指定QString的编码方式
QString itemname = codec->toUnicode(bufName,strlen(bufName));
WLog(LOG_DEBUG,"browseGroup:[%s]",itemname.toLocal8Bit().data());
//qDebug()<<itemname;
if(groupWidget != Q_NULLPTR)
{
QTreeWidgetItem *item = new QTreeWidgetItem(groupWidget,QStringList(itemname));
groupWidget->addTopLevelItem(item);
}
pIOPCBrowseServerAddressSpace->GetItemID(pItemName, &pItemID);
WideCharToMultiByte(CP_ACP, 0, pItemID, -1, bufName, 256, NULL, NULL);
//printf("ITEM_ID: %s\n", bufName);
itemname = codec->toUnicode(bufName,strlen(bufName));
qDebug()<<itemname;
DWORD dwCount;
DWORD *dwPropertyIDs;
LPWSTR *ppDescriptions;
VARTYPE *ppvtDataTypes;
VARIANT *ppvData;
HRESULT *ppErrors;
pIOPCItemProperties->QueryAvailableProperties(pItemID, &dwCount, &dwPropertyIDs, &ppDescriptions, &ppvtDataTypes);
pIOPCItemProperties->GetItemProperties(pItemID, dwCount, dwPropertyIDs, &ppvData, &ppErrors);
}
}
void OPCClient::dataCallback()
{
// // 建立异步回调
// CComObject<COPCDataCallback>* pCOPCDataCallback;
// // 回调对象的指针
// // 通过 ATL 模板创建回调对象的实例
// CComObject<COPCDataCallback>::CreateInstance(&pCOPCDataCallback);
// // 查询 IUnknown 接口
// LPUNKNOWN pCbUnk;
// pCbUnk = pCOPCDataCallback->GetUnknown();
// // 建立一个服务器的连接点与客户程序接收器之间的连接
// HRESULT hRes = AtlAdvise( m_IOPCGroupStateMgt, // [in] //连接点的 IUnknown 接口
// pCbUnk, // [in] 回调对象的 IUnknown 接口
// IID_IOPCDataCallback,// [in] 连接点 ID
// &m_dwAdvise // [out] 唯一的标识符
// );
// if (hRes != S_OK) {
// AfxMessageBox("Advise 失败!");
// }
}
/**
* 连接状态
* @brief OPCClient::isConnected
* @return
*/
bool OPCClient::isConnected()
{
return m_bConnect;
/*if(_pIOPCServer == NULL || _pIOPCGroupStateMgt == NULL)
{
return false;
}
return true;*/
}
void OPCClient::ClearOPCITEMDEF( OPCITEMDEF *idef, int count )
{
if( idef != NULL)
{
for(int i=0;i<count;i++)
{
if( idef[i].szItemID != NULL )
SysFreeString( idef[i].szItemID );
if( idef[i].szAccessPath != NULL )
SysFreeString( idef[i].szAccessPath );
}
CoTaskMemFree(idef);
idef = NULL;
}
}
/**
* 获取ItemDef by name
* @brief OPCClient::getItemByName
* @param s
* @return
*/
ItemDef* OPCClient::getItemByName(QString s)
{
int c = items->count();
for(int i=0; i<c; i++)
{
if(items->at(i)->name == s)
{
return items->at(i);
}
}
return 0;
}
/**
* 获取ItemDef by Handle
* @brief OPCClient::getItemByHandle
* @param s
* @return
*/
ItemDef* OPCClient::getItemByHandle(DWORD h)
{
auto it = std::find_if(items->begin(),items->end(),[=](ItemDef *pItem){
return pItem->hClientHandle == h;
});
if(it != items->end())
{
return *it;
}
return nullptr;
/*int c = items->count();
for(int i=0; i<c; i++)
{
if(items->at(i)->hClientHandle == h)
{
return items->at(i);
}
}
return 0;*/
}
//////////////////////////////////////////////////
/**
* QString 转换为 const wchar*
* @brief OPCClient::qstr2Wstr
* @param s
* @return
*/
WString OPCClient::qstr2Wstr(QString s)
{
return reinterpret_cast<const wchar_t*>(s.utf16());
//wchar_t* tempWide = const_cast< wchar_t* >( tempConstWide );
}
/**
* 输出项目值字符串
* @brief OPCClient::VariantString
* @param pValue
* @return
*/
QString OPCClient::VariantString(VARIANT &pValue)
{
QString valueString = QString("unknown type:%1").arg(pValue.vt);
//return valueString; //测试
//qDebug()<< QString(" Format type:%1 ").arg(pValue.vt);
switch(pValue.vt)
{
case VT_I1:
case VT_UI1: // BYTE
{
int i = pValue.iVal;
valueString= QString::number(i);
break;
}
case VT_I2: // SHORT
valueString= QString::number(pValue.iVal);
break;
case VT_UI2: // UNSIGNED SHORT
valueString= QString::number(pValue.uiVal);
break;
case VT_I4: // LONG
valueString= QString::number(pValue.lVal);
break;
case VT_UI4: // UNSIGNED LONG
valueString= QString::number(pValue.ulVal);
break;
case VT_INT: // INTEGER
valueString= QString::number(pValue.intVal);
break;
case VT_UINT: // UNSIGNED INTEGER
valueString= QString::number(pValue.uintVal);
break;
case VT_R4: // FLOAT
//sprintf (buf, "%5.2f ", pValue.fltVal );
valueString= QString::number(pValue.fltVal);
break;
case VT_R8: // DOUBLE
//sprintf (buf, "%9.4f ", pValue.dblVal );
valueString= QString::number(pValue.dblVal);
break;
case VT_BSTR: //BSTR
{
//sprintf (buf, "%ls ", pValue.bstrVal );
/*BSTR bstr_str = pValue.bstrVal;
QString q_str((QChar*)bstr_str, wcslen(bstr_str));
valueString = q_str;*/
valueString = QString::fromWCharArray(pValue.bstrVal,wcslen(pValue.bstrVal));
break;
}
case VT_BOOL:
{
if (pValue.boolVal)
valueString = "TRUE";
else
valueString = "FALSE";
break;
}
case VT_DATE:
{
QDateTime dt;
//qDebug()<< pValue.date;
dt.fromMSecsSinceEpoch(pValue.date);
valueString = dt.toString("yyyy-MM-dd hh:mm:ss");
break;
}
default:
valueString = QString("unknown type:%1").arg(pValue.vt);
break;
}
return valueString;
}
/**
* 输出项目值质量字符串
* @brief OPCClient::VariantString
* @param pValue
* @return
*/
QString OPCClient::QualityString(UINT qnr)
{
QString result;
switch(qnr)
{
case OPC_QUALITY_BAD:
result = "BAD";
break;
case OPC_QUALITY_UNCERTAIN:
result= "UNCERTAIN";
break;
case OPC_QUALITY_GOOD:
result = "GOOD";
break;
case OPC_QUALITY_NOT_CONNECTED:
result = "NOT_CONNECTED";
break;
case OPC_QUALITY_DEVICE_FAILURE:
result = "DEVICE_FAILURE";
break;
case OPC_QUALITY_SENSOR_FAILURE:
result = "SENSOR_FAILURE";
break;
case OPC_QUALITY_LAST_KNOWN:
result = "LAST_KNOWN";
break;
case OPC_QUALITY_COMM_FAILURE:
result = "COMM_FAILURE";
break;
case OPC_QUALITY_OUT_OF_SERVICE:
result = "OUT_OF_SERVICE";
break;
case OPC_QUALITY_LAST_USABLE:
result = "LAST_USABLE";
break;
case OPC_QUALITY_SENSOR_CAL:
result = "SENSOR_CAL";
break;
case OPC_QUALITY_EGU_EXCEEDED:
result = "EGU_EXCEEDED";
break; case OPC_QUALITY_SUB_NORMAL:
result = "SUB_NORMAL";
break;
case OPC_QUALITY_LOCAL_OVERRIDE:
result = "LOCAL_OVERRIDE";
break;
default:
result = QString("UNKNOWN ERROR:%1").arg(qnr,0,16);
}
return result;
}
/**
* 输出值类型字符串
* @brief OPCClient::ValueTypeString
* @param v
* @return
*/
QString OPCClient::ValueTypeString(const VARTYPE& v)
{
QString str;
switch(v)
{
case VT_BSTR:
str = QString("VT_BSTR");
break;
case VT_I1:
str = QString("VT_I1");
break;
case VT_I2:
str = QString("VT_I2");
break;
case VT_I4:
str = QString("VT_I4");
break;
case VT_I8:
str = QString("VT_I8");
break;
case VT_R4:
str = QString("VT_R4");
break;
case VT_R8:
str = QString("VT_R8");
break;
case VT_UI1:
str = QString("VT_UI1");
break;
case VT_UI2:
str = QString("VT_UI2");
break;
case VT_UI4:
str = QString("VT_UI4");
break;
case VT_UI8:
str = QString("VT_UI8");
break;
case VT_INT:
str = QString("VT_INT");
break;
case VT_UINT:
str = QString("VT_UINT");
break;
case VT_BOOL:
str = QString("VT_BOOL");
break;
case VT_DATE:
str = QString("VT_DATE");
break;
case VT_LPSTR:
str = QString("VT_LPSTR");
break;
default:
str = QString("unknown type:%1").arg(v);
break;
};
return str;
}
/**
* 赋值
* @brief OPCClient::StringVariant
* @param item
* @param v
* @return
*/
void OPCClient::StringVariant(ItemDef* item, QString v)
{
switch (item->value.vt)
{
case VT_UI1:
case VT_I1:
//cbRead = sizeof(BYTE);
break;
case VT_I2:
case VT_UI2:
case VT_BOOL:
item->value.iVal = v.toShort();
break;
case VT_I4:
case VT_UI4:
case VT_INT:
case VT_UINT:
case VT_ERROR:
item->value.lVal = v.toLong();
break;
case VT_R4:
item->value.fltVal = v.toFloat();
break;
case VT_I8:
case VT_UI8:
item->value.llVal = v.toLongLong();
break;
case VT_R8:
case VT_CY:
case VT_DATE:
item->value.dblVal = v.toDouble();
break;
case VT_BSTR:
{
//Value.bstrVal = SysAllocString(v.utf16());
//Value.bstrVal = qstringToBstr(v);
wchar_t *pW = new wchar_t[v.size()+1];
v.toWCharArray(pW);
//dialog.m_valueValue.bstrVal = SysAllocString(dialog.m_value.toStdWString().c_str());
break;
}
default:
break;
}
return;
}
/**
* 输出权限字符串
* @brief OPCClient::AccessTypeString
* @param accessIdent
* @return
*/
QString OPCClient::AccessTypeString(qint16 accessIdent)
{
QString s;
switch(accessIdent)
{
case OPC_READABLE:
s = "Read";
break;
case OPC_WRITEABLE:
s = "Write";
break;
default:
s = "Read/Write";
}
return s;
}
QString OPCClient::TimeString(FILETIME t)
{
SYSTEMTIME sytime;
FileTimeToSystemTime(&t, &sytime);
return QString("%1-%2-%3 %4:%5:%6").arg(sytime.wYear).arg(sytime.wMonth).arg(sytime.wDay).arg(sytime.wHour).arg(sytime.wMinute).arg(sytime.wSecond);
// return QString::number(t.dwLowDateTime);
}
调用opc客户端代码:
// .h
#ifndef FLIGHTLINKAGE_H
#define FLIGHTLINKAGE_H
#include <QObject>
#include <QTimer>
#include "opcda/OPCClient.h"
enum OPC_READ_DATA_TYPE
{
DATA_SUBSCRIPTION = 0, //数据订阅
SYNC_READ = 1, //同步读取
ASYNC_READ = 2 // 异步读取,暂未实现
};
class FlightLinkage : public QObject
{
Q_OBJECT
public:
FlightLinkage(QObject *parent = nullptr);
~FlightLinkage();
void connectToOpcServer();
void startOPCClient();
void loadOPCItem();
private slots:
void OnDataChange(OPCHANDLE hGroup, DWORD dwCount, OPCHANDLE *phClientItems, VARIANT *pvValues, WORD *pwQualities, FILETIME *pftTimeStamps, HRESULT *pErrors);
void OnReadComplete(OPCHANDLE hGroup, DWORD dwCount, OPCHANDLE *phClientItems, VARIANT *pvValues, WORD *pwQualities, FILETIME *pftTimeStamps, HRESULT *pErrors);
void SendReadAllItemsToOpcServer();
private:
//void OPCServerList();
void startOPC();
void OpcChangeDataUpdateFlightDb(const std::vector<ItemInfo> &vec);
void GetInfoFromDb();
private:
OPCClient *opc = nullptr;
//void showData();
int index;
QString SQL;
QString indexSQL;
QList<QVariantMap> dateList;
QString selectID;
bool finish;
QTimer *m_readAllItems = nullptr;
QString m_opcServerName;
QString m_opcServerIp;
int m_opcReadDataType = SYNC_READ;
QString m_forbiddenStartTime = "08:00";
QString m_forbiddenEndTime = "18:00";
};
#endif // FLIGHTLINKAGE_H
//.cpp
#include "FlightLinkage.h"
#include <QMessageBox>
#include <QUuid>
#include <thread>
QString GetUUID()
{
QString uuid = QUuid::createUuid().toString();
QString String = uuid.mid(1,uuid.length()-2);
return String;
}
FlightLinkage::FlightLinkage(QObject *parent) : QObject(parent)
{
m_readAllItems = new QTimer(this);
connect(m_readAllItems,&QTimer::timeout,this,&FlightLinkage::SendReadAllItemsToOpcServer); // 重连服务器放到SendReadAllItemsToOpcServer里面重连
}
FlightLinkage::~FlightLinkage()
{
if(opc != nullptr)
{
opc->DisConnect();
opc->deleteLater();
}
if(m_readAllItems!=nullptr && m_readAllItems->isActive())
{
m_readAllItems->stop();
delete m_readAllItems;
m_readAllItems = nullptr;
}
}
void FlightLinkage::connectToOpcServer()
{
if(m_opcServerName.isEmpty() || m_opcServerIp.isEmpty())
{
WLog(LOG_ERR,"opcServerName.isEmpty()[%d] IP.isEmpty()[%d]",m_opcServerName.isEmpty(),m_opcServerIp.isEmpty());
return;
}
if(opc == nullptr)
{
opc = new OPCClient();
}
if(opc->Connect(m_opcServerName,m_opcServerIp))
{
QString groupName = "FlightLinkage";
int async = (m_opcReadDataType == SYNC_READ ? 0 : 1); // 1为异步,0为同步
if(opc->AddGroup(groupName,1000,async)==S_OK)
{
WLog(LOG_INFO,"AddGroup succ");
connect(opc->m_COPCTransaction,&COPCTransaction::sg_OnDataChange,this,&FlightLinkage::OnDataChange);
//connect(opc->m_COPCTransaction,&COPCTransaction::sg_OnReadComplete,this,&FlightLinkage::OnReadComplete);
// 加载items
loadOPCItem();
}
else
{
WLog(LOG_ERR,"AddGroup fail");
}
}
}
void FlightLinkage::startOPCClient()
{
connectToOpcServer();
if(m_opcReadDataType == SYNC_READ)
{
SendReadAllItemsToOpcServer(); // 软件启动先运行一次
}
int time = 30*1000; // 30s读一次
m_readAllItems->start(time);
}
// 此函数除了同步读取功能外,还有重连服务器功能。通过同步读取返回的错误码检查是否与服务器断开来进行重连
void FlightLinkage::SendReadAllItemsToOpcServer()
{
if(opc == nullptr)
{
return;
}
//opc->ReadAllValue_Async();
if(m_opcReadDataType == SYNC_READ)
{
std::vector<ItemInfo> vec;
opc->ReadAllValue_Sync(vec); // // 同步读取方式,如果opc服务器连接断开可以检测到,如果用订阅方式则无法检测得到服务器断开
OpcChangeDataUpdateFlightDb(vec);
}
if(!opc->isConnected())
{
connectToOpcServer(); // 重连服务器
}
}
void FlightLinkage::loadOPCItem()
{
if(opc == nullptr)
{
WLog(LOG_ERR,"opc == nullptr");
return;
}
/*if (opc->isConnected())
{
opc->BrowseAllItem();//添加测试用
opc->browseGroup(nullptr); //添加测试用
opc->AddItems(opc->m_readItemNameLs); // 将读取回来的项全部添加进去
}*/
opc->RemoveAllItems();
QList<QVariantMap> itemList = Singleton<SqliteHelper>::getInstance().queryDataSet(querySql);
if(!itemList.isEmpty())
{
QStringList inames;
for(QVariantMap &vmap : itemList)
{
QString plName = vmap.value("pointLocationName").toString();
inames.push_back(plName);
}
QStringList r_inames= opc->AddItems(inames); // 数据库中所有的项必须服务器端都有才能添加成功,否则将返回服务器端异常或者函数不正确 错误信息
if(r_inames.size() == inames.size())
{
WLog(LOG_INFO,"AddItems all succ, size:%d",inames.size());
}
else if (r_inames.size() < inames.size() && r_inames.size() != 0)
{
WLog(LOG_ERR,"AddItems size[%d],succ size[%d]",inames.size(),r_inames.size());
}
else
{
WLog(LOG_ERR,"AddItems fail,size[%d]",inames.size());
}
}
else
WLog(LOG_ERR,"There is no item to add.");
}
void FlightLinkage::OnDataChange(OPCHANDLE hGroup, DWORD dwCount, OPCHANDLE *phClientItems, VARIANT *pvValues, WORD *pwQualities, FILETIME *pftTimeStamps, HRESULT *pErrors)
{
std::vector<ItemInfo> fVec;
WLog(LOG_DEBUG,"OnDataChange: hGroup[%d],dwCount[%d]",hGroup,dwCount);
for(DWORD i = 0; i< dwCount; i++)
{
ItemDef *pItem = opc->getItemByHandle(phClientItems[i]);
if(pItem == nullptr)// 没有找到对应的项,则继续
{
continue;
}
ItemInfo info;
info.m_name = pItem->name;
info.m_val = opc->VariantString(pvValues[i]);
WLog(LOG_DEBUG,"OnDataChange:itemName[%s] phClientItems[%d] newVal[%s] pwQualities[%s] dwLowDateTime[%s] err[%d] errMsg[%s]",
info.m_name.toLocal8Bit().data(),phClientItems[i], info.m_val.toLocal8Bit().data(),
opc->QualityString(pwQualities[i]).toLocal8Bit().data(),opc->TimeString(pftTimeStamps[i]).toLocal8Bit().data(),pErrors[i],ComErrMsg(pErrors[i]).c_str());
if(!info.m_val.isEmpty())
{
fVec.push_back(info);
}
}
OpcChangeDataUpdateFlightDb(fVec);
}
void FlightLinkage::OnReadComplete(OPCHANDLE hGroup, DWORD dwCount, OPCHANDLE *phClientItems, VARIANT *pvValues, WORD *pwQualities, FILETIME *pftTimeStamps, HRESULT *pErrors)
{
// 处理读出来的数据
for (DWORD i = 0; i < dwCount; i++)
{
WLog(LOG_DEBUG,"get the OnReadComplete:hGroup[%d] dwCount[%d] phClientItems[%d][%d] pvValues[%s] pwQualities[%s] dwLowDateTime[%s] err[%d]",
hGroup,dwCount,i,phClientItems[i], opc->VariantString(pvValues[i]).toLocal8Bit().data(),
opc->QualityString(pwQualities[i]).toLocal8Bit().data(),QString::number(pftTimeStamps[i].dwLowDateTime).toLocal8Bit().data(),pErrors[i]);
}
}
void FlightLinkage::OpcChangeDataUpdateFlightDb(const std::vector<ItemInfo> &vec)
{
}