OPC工作记录整理

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)
{
   
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值