linux/windows下基于opc ua协议使用open62541开发客户端-上
最近公司想把windows下軟件遷移到linux下,在與plc通訊方面西門子只提供windows下的庫,linux下沒有對應庫,幸好發現有open62541這個協議外接庫,對應的plc最低型號為s1200,還必須升級固件才行。官方貌似有實例代碼,但是看起來雜亂無章,現在整理一下。
很想直接上代碼,但是先簡單介紹一下,使用博圖16版本進行配合使用,連接有兩種方式,一種匿名連接,一種有名連接,根據需要自己選擇。
同時我們必須要了解節點,NodeId 是節點的唯一編號,NodeClass 是節點類型,BrowseName用于瀏覽,DisplayName 是節點的名稱,TypeDefinitionId是類型定義的唯一編號。我們所需要的是要知道節點的作用域以及節點編號,這個可以在博圖16中看到,而且最多只能有500個點位,類似于snap7中的DB塊之類的東西,然后響應時間之類的設置自己可以用博圖軟件修改。
下面上實現類代碼的頭文件:
這是對應我們公司所需的,open62541中是直接看節點的,節點類型有Numberic、string、StringAlloc、ByteString、GUID等類型,我司只用到Numberic類型,該類型下就對應int、dint、word、dword、bool、byte等數據類型的讀寫。
下面是對應的初始化客戶端以及兩種連接plc代碼
#include "cxnopc.h" #include <stdio.h> #include <unistd.h> #include <QDateTime>#define print(format, ...) \do \{ \printf(format, ##__VA_ARGS__); \} while (0)CXNOpc::CXNOpc(QObject *parent) : m_bIsInit(false),m_bIsConnect(false),m_bStatus(false),m_time(1),m_qLogFile(NULL),m_qOutLog(NULL) {}CXNOpc::~CXNOpc() {if(m_pClient != NULL && m_bIsConnect){if(m_subId > 0)UA_Client_Subscriptions_deleteSingle(m_pClient, m_subId);UA_Client_disconnect(m_pClient);UA_Client_delete(m_pClient);}if(m_bStatus){terminate();quit();}if(m_qLogFile != NULL){m_qLogFile->close();delete m_qLogFile;}if(m_qOutLog != NULL)delete m_qOutLog; }QString CXNOpc::OutLogTime() {m_qLogTime = QDateTime::currentDateTime();QString qStr = m_qLogTime.toString("yyy-MM-dd hh:mm::ss ddd");return qStr; }bool CXNOpc::InitXNOPC(QString qsUrl, QString qsLogFile, UA_ClientStateCallback stateCallback) {if(m_bIsInit)return m_bIsInit;if( qsUrl.size() == 0 )return m_bIsInit;elsem_qsUrl = qsUrl;m_qLogFile = new QFile(qsLogFile);if (!m_qLogFile->open(QIODevice::QIODevice::WriteOnly | QIODevice::Text))printf("Fail to open logfile\n");elsem_qOutLog = new QTextStream(m_qLogFile);QByteArray ba = qsUrl.toLatin1();char* pchUrl = ba.data();size_t szEndpointArraySize = 0;UA_ClientConfig clentConfig = UA_ClientConfig_default;clentConfig.stateCallback = stateCallback;m_pClient = UA_Client_new(clentConfig);UA_EndpointDescription* pEndpointArray = NULL;//查詢服務器節點,存儲到結構體數組中UA_EndpointDescriptionUA_StatusCode retvalue = UA_Client_getEndpoints(m_pClient, pchUrl,&szEndpointArraySize, &pEndpointArray);if(retvalue != UA_STATUSCODE_GOOD){UA_Array_delete(pEndpointArray, szEndpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);//刪除獲取的節點UA_Client_delete(m_pClient);//刪除客戶端return m_bIsInit;}//打印節點信息printf("%i endpoints found\n", (int)szEndpointArraySize); //1 1 endpoints foundfor(size_t i=0;i<szEndpointArraySize;i++)printf("URL of endpoint %i is %.*s\n", (int)i, (int)pEndpointArray[i].endpointUrl.length,pEndpointArray[i].endpointUrl.data);UA_Array_delete(pEndpointArray,szEndpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);m_bIsInit = true;return m_bIsInit; }bool CXNOpc::ConnectOPCByUserName(QString qsUserName, QString qsPassWord) {if(!m_bIsInit || m_bIsConnect)return m_bIsConnect;printf("%s-%s-%s\n",m_qsUrl.toLatin1().data(), qsUserName.toUtf8().data(), qsPassWord.toStdString().c_str());UA_StatusCode retvalue = UA_Client_connect_username(m_pClient, m_qsUrl.toLatin1().data(), qsUserName.toUtf8().data(), qsPassWord.toStdString().c_str());if(retvalue != UA_STATUSCODE_GOOD){UA_Client_delete(m_pClient);return m_bIsConnect;}//初始化 瀏覽請求UA_BrowseRequest uaBrowReq;UA_BrowseRequest_init(&uaBrowReq);uaBrowReq.requestedMaxReferencesPerNode = 0;uaBrowReq.nodesToBrowse = UA_BrowseDescription_new();uaBrowReq.nodesToBrowseSize = 1;uaBrowReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); /* browse objects folder */uaBrowReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything *///瀏覽指定節點下NODEUA_BrowseResponse uaBrowResp = UA_Client_Service_browse(m_pClient, uaBrowReq);print("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME");for(size_t i = 0; i < uaBrowResp.resultsSize; ++i){for(size_t j = 0; j < uaBrowResp.results[i].referencesSize; ++j){UA_ReferenceDescription *puaReferDescrip = &(uaBrowResp.results[i].references[j]);if(puaReferDescrip->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC)print("%-9d %-16d %-16.*s %-16.*s\n", puaReferDescrip->nodeId.nodeId.namespaceIndex,puaReferDescrip->nodeId.nodeId.identifier.numeric, (int)puaReferDescrip->browseName.name.length,puaReferDescrip->browseName.name.data, (int)puaReferDescrip->displayName.text.length,puaReferDescrip->displayName.text.data);else if(puaReferDescrip->nodeId.nodeId.identifierType == UA_NODEIDTYPE_STRING)print("%-9d %-16.*s %-16.*s %-16.*s\n", puaReferDescrip->nodeId.nodeId.namespaceIndex,(int)puaReferDescrip->nodeId.nodeId.identifier.string.length,puaReferDescrip->nodeId.nodeId.identifier.string.data,(int)puaReferDescrip->browseName.name.length, puaReferDescrip->browseName.name.data,(int)puaReferDescrip->displayName.text.length, puaReferDescrip->displayName.text.data);}}fflush(stdout);UA_BrowseRequest_deleteMembers(&uaBrowReq);UA_BrowseResponse_deleteMembers(&uaBrowResp);m_bIsConnect = true;return m_bIsConnect; }bool CXNOpc::ConnectOPCByVisitor() {if(!m_bIsInit || m_bIsConnect)return m_bIsConnect;UA_StatusCode retvalue = UA_Client_connect(m_pClient, m_qsUrl.toLatin1().data());if(retvalue != UA_STATUSCODE_GOOD){UA_Client_delete(m_pClient);return m_bIsConnect;}//初始化 瀏覽請求UA_BrowseRequest uaBrowReq;UA_BrowseRequest_init(&uaBrowReq);uaBrowReq.requestedMaxReferencesPerNode = 0;uaBrowReq.nodesToBrowse = UA_BrowseDescription_new();uaBrowReq.nodesToBrowseSize = 1;uaBrowReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); /* browse objects folder */uaBrowReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything *///瀏覽指定節點下NODEUA_BrowseResponse uaBrowResp = UA_Client_Service_browse(m_pClient, uaBrowReq);print("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME");for(size_t i = 0; i < uaBrowResp.resultsSize; ++i){for(size_t j = 0; j < uaBrowResp.results[i].referencesSize; ++j){UA_ReferenceDescription *puaReferDescrip = &(uaBrowResp.results[i].references[j]);if(puaReferDescrip->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC)print("%-9d %-16d %-16.*s %-16.*s\n", puaReferDescrip->nodeId.nodeId.namespaceIndex,puaReferDescrip->nodeId.nodeId.identifier.numeric, (int)puaReferDescrip->browseName.name.length,puaReferDescrip->browseName.name.data, (int)puaReferDescrip->displayName.text.length,puaReferDescrip->displayName.text.data);else if(puaReferDescrip->nodeId.nodeId.identifierType == UA_NODEIDTYPE_STRING)print("%-9d %-16.*s %-16.*s %-16.*s\n", puaReferDescrip->nodeId.nodeId.namespaceIndex,(int)puaReferDescrip->nodeId.nodeId.identifier.string.length,puaReferDescrip->nodeId.nodeId.identifier.string.data,(int)puaReferDescrip->browseName.name.length, puaReferDescrip->browseName.name.data,(int)puaReferDescrip->displayName.text.length, puaReferDescrip->displayName.text.data);}}fflush(stdout);UA_BrowseRequest_deleteMembers(&uaBrowReq);UA_BrowseResponse_deleteMembers(&uaBrowResp);m_bIsConnect = true;return m_bIsConnect; }剩下的讀寫點的方式后面再介紹,有需要的伙伴可以qq我 965434757
總結
以上是生活随笔為你收集整理的linux/windows下基于opc ua协议使用open62541开发客户端-上的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: psp c语言编程软件,PSP2000自
- 下一篇: 辗转相除法(python)