前段时间开发Windows下的设备端软件接触到了OPC DA,虽然现在有了更强大的OPC UA,但是为了兼容一些老的设备,不得不硬着头皮去啃这个老掉牙的已经过时了的技术。本来只是想粗略了解一下,简单写一个应付了事,谁知道过程一波三折。
由于OPCDA采用COM技术,而我又不想再去学COM编程以及VC、AFX、MFC,我采取调用 WTClient 的懒人方法,并且只花了几个小时就整了一个出来,简单测试通过,结果经同事验证,Float、long等类型通通无法读取无法写入,并且每隔半小时左右便与Kepsrever断开连接。然后到处找解决方案,最后在官方网站得到答案:免费版本有30分钟时间限制,数据类型估计也顺手给限了,只有BOOL和WORD少数几个类型可以使用,这期间又是浪费好多时间。
经过这番教训,决定用QT从头开始写一个自己的库,于是有了以下内容,提供给需要的人,稍候会传到github(早已传上去了,但是忘记修改本文,需要的去拿吧:https://github.com/cncap/qt-opcda)。
#ifndef OPCCLIENT_H #define OPCCLIENT_H #include <iostream> #include "opcerror.h" #include "opccomn.h" #include "OpcEnum.h" #include "Opcda.h" #include "copctransaction.h" #include "wldef.h" #include <QObject> #include <QDebug> #include "windows.h" /** * Copyright 2016 Chn.Captain (chn.captain@gmail.com) */ 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; //ZeroMemory(&time, sizeof(time)); } 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; } }; class OPCClient:public QObject { Q_OBJECT public: explicit OPCClient(); OPCClient(QString s); ~OPCClient(); COPCTransaction *m_COPCTransaction; bool isWriteAble; bool isConnected(); HRESULT Connect(QString s); void DisConnect(); HRESULT AddGroup(QString n, DWORD update_rate , int async=1); QStringList AddItems(QStringList names); bool ReadItem(ItemDef *item); 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 RemoveItems(QStringList inames); HRESULT RemoveAllItems(); void dataCallback(); QList<ItemDef*> *items; 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); 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; void ClearOPCITEMDEF( OPCITEMDEF *idef, int count = 1 ); ItemDef* getItemByName(QString s); ItemDef* getItemByHandle(DWORD h); WString qstr2Wstr(QString s); }; #endif //OPCCLIENT_H
#include "OPCClient.h" #include "copcdatacallback.h" #include <QDateTime> #include <QMessageBox> /** * Copyright 2016 Chn.Captain (chn.captain@gmail.com) */ OPCClient::OPCClient(QString s): m_ServerName(qstr2Wstr(s)) { } 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*>(); } /** * 建立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) { qDebug()<<"Error CoInitialize():COM Library already initialized"; } else { qDebug()<< "Error CoInitialize():Initialisation of COM Library failed. Error Code= " << hResult; _pIOPCServer = 0; CoUninitialize(); return hResult; } } hResult = CLSIDFromProgID(this->m_ServerName, &this->m_clsid); if (FAILED(hResult)) { qDebug()<< "Error CLSIDFromProgID():Retrival of CLSID failed"; CoUninitialize(); return hResult; } hResult = CoCreateInstance (this->m_clsid, NULL, CLSCTX_LOCAL_SERVER ,IID_IOPCServer, (void**)&_pIOPCServer); if (FAILED(hResult)) { qDebug()<< "Error CoCreateInstance():Creation of IOPCServer-Object failed"; _pIOPCServer = 0; CoTaskMemFree(&this->m_clsid); CoUninitialize(); return hResult; } hResult = _pIOPCServer->QueryInterface(IID_IOPCBrowseServerAddressSpace, (void**)&_pIOpcNamespace); if (FAILED(hResult)) { qDebug()<< "Error CoCreateInstance():Creation of IID_IOPCBrowseServerAddressSpace-Object failed"; CoUninitialize(); return hResult; } hResult = _pIOPCServer->QueryInterface(IID_IOPCItemProperties, (void**)&_pIOpcProperties); if (FAILED(hResult)) { qDebug()<< "Error CoCreateInstance():Creation of IID_IOPCItemProperties-Object failed"; CoUninitialize(); return hResult; } return hResult; } /** * 添加分组 * @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; DWORD LanguageCode = 0x416, RevisedUpdateRate; float DeadBand; //刷新间隔 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 if( hResult ==OPC_E_DUPLICATENAME ) { qDebug()<< "1001:分组名称已存在."; } if( hResult ==E_NOINTERFACE ) { qDebug()<< "1002:IOPCServer::AddGroup returned E_NOINTERFACE (IID_IOPCGroupStateMgt)"; } if( update_rate != m_UpdateRate ){ qDebug()<< "1003: OPC server rejected data refresh interval. Setting it to %1 ms." << update_rate; } if (hResult == OPC_S_UNSUPPORTEDRATE) { qDebug()<< "1004:请求的刷新速率与实际的刷新速率不一致"; } if( FAILED(hResult) || _pIOPCGroupStateMgt == NULL) { qDebug()<< "1005: 创建分组失败"; DisConnect(); } hResult = _pIOPCGroupStateMgt->QueryInterface(IID_IOPCItemMgt,(void**)&this->_pIOPCItemMgt); if (FAILED(hResult)) { qDebug()<<"1006: 项目操作指针(_pIOPCItemMgt)创建失败"; DisConnect(); } //查询 group 对象的同步接口 if(m_Async){ hResult = _pIOPCItemMgt->QueryInterface(IID_IOPCAsyncIO2, (void**)&this->_pIOPCAsyncIO2); if (FAILED(hResult)) { qDebug()<<"1008: IOPCAsyncIO2 没有发现,错误的查询!"; DisConnect(); } m_COPCTransaction = new COPCTransaction; hResult = _pIOPCItemMgt->QueryInterface(IID_IConnectionPointContainer, (void**)&this->_pIConnectionPointContainer); //hResult = _pIOPCItemMgt->QueryInterface(&_pIConnectionPointContainer); if (FAILED(hResult)) { qDebug()<<"1009: IConnectionPointContainer 创建失败!"; DisConnect(); } hResult = _pIConnectionPointContainer->FindConnectionPoint( IID_IOPCDataCallback, &_pIConnectionPoint ); if( FAILED(hResult) || _pIConnectionPoint == NULL) { qDebug()<< "1010: A group of OPC with no IOPCDataCallback. OPC server appears to not conform to the OPC DA 2.0 standard (%s)."; DisConnect(); } if( m_OPCDataCallback == NULL ) { if(!m_COPCTransaction){ m_COPCTransaction = new COPCTransaction; } m_OPCDataCallback = new COPCDataCallback(m_COPCTransaction); m_OPCDataCallback->AddRef(); // m_OPCDataCallback->receiver = m_DataReceiver; } hResult = _pIConnectionPoint->Advise(m_OPCDataCallback, &m_Cookie); qDebug()<< hResult; if(FAILED(hResult)) { qDebug()<< "1011: OPCDataCallback set faild." << m_Cookie; _pIConnectionPointContainer->Release(); DisConnect(); } isWriteAble = true; }else{ hResult = _pIOPCItemMgt->QueryInterface(IID_IOPCSyncIO, (void**)&this->_pIOPCSyncIO); if (FAILED(hResult)) { qDebug()<<"1007: IOPCSyncIO 没有发现,错误的查询!"; DisConnect(); } isWriteAble = true; } 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; qDebug()<<"1--- begin add item -----------"; for(int i=0;i<inames.count();i++) { QString iname = inames[i]; if(getItemByName(iname)){ qDebug() << " already exsits."<< iname; continue; } r_inames.append(iname); } int nCount=r_inames.count(); if(nCount>0){ OPCITEMDEF idef[nCount]; for(int i=0;i<nCount;i++) { QString iname = r_inames[i]; qDebug()<< " build item def"<< iname; idef[i].szAccessPath= SysAllocString(L""); // path SysAllocString(qstr2Wstr(name)) idef[i].szItemID = SysAllocString(qstr2Wstr(iname)); // name idef[i].bActive = TRUE; idef[i].hClient = iCount+i; idef[i].dwBlobSize = 0; idef[i].pBlob = NULL; idef[i].vtRequestedDataType = 0; } qDebug()<<"2--- end of add item-----------"; hResult = _pIOPCItemMgt->AddItems(nCount, // [in] add items idef, // [in] see above &pResults, // [out] array with additional information about the item &pErrors); // [out] tells which of the items was successfully added. if(hResult==S_OK && hResult!=S_FALSE){ 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)) { // qDebug()<<"Item 不可读,也不可写,请检查服务器配置"; // } } } if(pResults) CoTaskMemFree( pResults ); if(pErrors) CoTaskMemFree( pErrors ); ClearOPCITEMDEF(idef, nCount); } return r_inames; } /** * 同步读取指定项 * @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); }else{ qDebug()<< "同步读取项目失败" << hResult; } CoTaskMemFree(pErrors); CoTaskMemFree(pItemValue); return result; } /** * 写入值 * @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 ) return E_FAIL; if( !isConnected() ) return E_FAIL; // m_Group->QueryInterface( &AsyncIO ); if( _pIOPCAsyncIO2 == NULL ) { qDebug()<< "Failed to get interface IOPCAsyncIO2"; return E_FAIL; } DWORD cancelID = 0; hResult = _pIOPCAsyncIO2->Write( 1, &item->hServerHandle, &item->value, rand(), &cancelID, &pErrors); if( FAILED(hResult) /*|| FAILED(pErrors[0])*/) { qDebug()<< "Parameter [%s] is not passed" << item->name; hResult = E_FAIL; } if( pErrors ) { switch( pErrors[0] ) { case OPC_S_CLAMP: qDebug()<< "AsyncIO->Write(%s) -> [OPC_S_CLAMP] The value was accepted but was clamped."<<item->name; break; case OPC_E_RANGE: qDebug()<< "AsyncIO->Write(%s) -> [OPC_E_RANGE] The value was out of range."<<item->name; 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, item->value.vt); qDebug()<< str; break; case OPC_E_BADRIGHTS: qDebug()<< "AsyncIO->Write(%s) -> [OPC_E_BADRIGHTS] The item is not writeable."<<item->name; break; case OPC_E_INVALIDHANDLE: qDebug()<< "AsyncIO->Write(%s) -> [OPC_E_INVALIDHANDLE] The passed item handle was invalid."<<item->name; break; case OPC_E_UNKNOWNITEMID: qDebug()<< "AsyncIO->Write(%s) -> [OPC_E_UNKNOWNITEMID] The item is no longer available in the server address space."<<item->name; break; } } if( pErrors )CoTaskMemFree( pErrors ); return hResult; } HRESULT OPCClient::WriteValue_Sync( ItemDef * item ) { pErrors = NULL; if( m_COPCTransaction == NULL ) return E_FAIL; if( !isConnected() ) return E_FAIL; qDebug() << "Sync Write start (hdl="<< item->hServerHandle << ") value=" << VariantString(item->value); hResult = _pIOPCSyncIO->Write( 1, &item->hServerHandle, &item->value, &pErrors); qDebug() << "Sync Write finished. hr = " << hResult; if( FAILED(hResult) /*|| FAILED(pErrors[0])*/) { qDebug() << "Parameter [%s] is not passed"<<item->name; hResult = E_FAIL; if( pErrors != NULL) { switch( pErrors[0] ) { case OPC_S_CLAMP: qDebug() << "SyncIO->Write(%s) -> [OPC_S_CLAMP] The value was accepted but was clamped." << item->name ; break; case OPC_E_RANGE: qDebug() << "SyncIO->Write(%s) -> [OPC_E_RANGE] The value was out of range."<<item->name ; break; case OPC_E_BADTYPE: qDebug() << "SyncIO->Write(%s) -> [OPC_E_BADTYPE] The passed data type cannot be accepted for this item."<<item->name ; break; case OPC_E_BADRIGHTS: qDebug() << "SyncIO->Write(%s) -> [OPC_E_BADRIGHTS] The item is not writeable."<<item->name ; break; case OPC_E_INVALIDHANDLE: qDebug() << "SyncIO->Write(%s) -> [OPC_E_INVALIDHANDLE] The passed item handle was invalid."<<item->name ; break; case OPC_E_UNKNOWNITEMID: qDebug() << "SyncIO->Write(%s) -> [OPC_E_UNKNOWNITEMID] The item is no longer available in the server address space."<<item->name ; break; } } } if( pErrors ) CoTaskMemFree( pErrors ); return hResult; } /** * 移除所有项目 * @brief OPCClient::RemoveItems * @param _items * @return */ HRESULT OPCClient::RemoveItems(QStringList inames) { int _iCount = inames.count(); if( _iCount==0) return -1; 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, &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() { int _iCount = items->count(); OPCHANDLE _hpSvr[_iCount]; qDebug()<<"3---begin delete-----------"; 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, &pErrors); qDebug() <<"4---end delete-----------" << hResult; if(SUCCEEDED(hResult)){ while(items->count()!=0){ items->removeAt(0); } } 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; CoUninitialize(); delete items; } void OPCClient::dataCallback() { // // 建立异步回调 // CComObject<COPCDataCallback>* pCOPCDataCallback; // // 回调对象的指针 // // 通过 ATL 模板创建回调对象的实例 // CComObject<COPCDataCallback>::CreateInstance(&pCOPCD ataCallback); // // 查询 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 ( _pIOPCServer && _pIOPCGroupStateMgt ); } void OPCClient::ClearOPCITEMDEF( OPCITEMDEF *idef, int count ) { if( idef ) 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 ); } } /** * 获取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) { int c = items->count(); for(int i=0; i<c; i++) { if(items->at(i)->hClientHandle == h){ return items->at(i); } } return 0; } /** * 析构函数 * @brief OPCClient::~OPCClient */ OPCClient::~OPCClient() { DisConnect(); } ////////////////////////////////////////////////// /** * 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; //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; 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 = "UNKNOWN ERROR"; } 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; default: str = QString("unknown"); 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) { return QString::number(t.dwLowDateTime); }