Qt-OPCUA客户端

简介

1.为了方便与plc设备地址进行交互,通过KepServer连接plc设备,使用Qt制作OpcUa客户端读写KepServer数据。
2.代码依赖于open62541.c/.h, 和pthread,libwsock32,libws2_32库。LIBS += -lpthread libwsock32 libws2_32

核心代码

#ifndef OPCUA_H
#define OPCUA_H

#include 
#include 
#include 
#include 

#include "open62541.h"

struct OpcUaData {
    QString clientUrl;
    int namespaceIndex;
    QString identifier_String; //my.plc.M101
    bool isListening;
    int listeningTimeMS;  //
    const UA_DataType *type; // &UA_TYPES[UA_TYPES_INT32]
    OpcUaData ()
        : clientUrl("opc.tcp://127.0.0.1:49320")
        , namespaceIndex(2)
        , identifier_String("")
        , isListening(false)
        , listeningTimeMS(1000)
    {}
};

class OpcUA : public QObject
{
    Q_OBJECT
public:
    explicit OpcUA(OpcUaData opcData, QObject *parent = 0);
    ~OpcUA();
    void setIdentifierString(QString item);
    OpcUaData getOpcSetting();

signals:
    void signalSendOpcItemData(QString itemId, const QVariant& value);
public slots:
    void init();
    void onListeningTimerTimeout();
    bool onSetItem(QString itemId, const QVariant& value);
private:
    void wait(int ms);
    UA_Client* creationClient();
    QVariant dataConvert(UA_Variant* variant);
private:
    OpcUaData m_OpcUaData;
    QTimer* m_ListeningTimer;
};

#endif // OPCUA_H

#include "opcua.h"

#include 
#include 
#include 

#include 

static QVariant m_VarDataOld;

OpcUA::OpcUA(OpcUaData opcData, QObject *parent)
    : QObject(parent)
    , m_OpcUaData(opcData)
{
    QThread* t = new QThread();
    this->moveToThread(t);
    connect(t, SIGNAL(started()), this, SLOT(init()));
    t->start();
}

OpcUA::~OpcUA()
{

}

void OpcUA::setIdentifierString(QString item)
{
    m_OpcUaData.identifier_String = item;
}

OpcUaData OpcUA::getOpcSetting()
{
    return m_OpcUaData;
}

void OpcUA::init()
{
    m_ListeningTimer = new QTimer();
    connect(m_ListeningTimer, SIGNAL(timeout()), this, SLOT(onListeningTimerTimeout()));
    m_ListeningTimer->setInterval(m_OpcUaData.listeningTimeMS);

    if (m_OpcUaData.isListening) {
        m_ListeningTimer->start();
    }
}

void OpcUA::onListeningTimerTimeout()
{
    UA_Variant* variant = UA_Variant_new();
    char* data;
    QByteArray ba = m_OpcUaData.identifier_String.toUtf8();
    data = ba.data();

    UA_Client* client = creationClient();
    if (client == NULL) {
        qDebug() << "onListeningTimerTimeout连接失败";
        return;
    }
    UA_StatusCode retval = UA_Client_readValueAttribute(client, UA_NODEID_STRING(m_OpcUaData.namespaceIndex, data), variant);
    if (retval == UA_STATUSCODE_GOOD && UA_Variant_isScalar(variant)) {
        QVariant var = dataConvert(variant);
        if (var != m_VarDataOld) {
            m_VarDataOld = var;
            emit signalSendOpcItemData(m_OpcUaData.identifier_String, var);
        }
    }
}

bool OpcUA::onSetItem(QString itemId, const QVariant &value)
{
    if (itemId != m_OpcUaData.identifier_String) {
        return false;
    }

    UA_Variant *myVariant = UA_Variant_new();

    if (m_OpcUaData.type == &UA_TYPES[UA_TYPES_STRING]) {
        std::string TestString = value.toString().toStdString();
        unsigned char* DataChar = const_cast<unsigned char *>((const unsigned char*)TestString.c_str());

        UA_String data;
        data.data = DataChar;
        data.length = value.toString().length();

        UA_Variant_setScalarCopy(myVariant, &data, m_OpcUaData.type);
    }
    else {
        UA_Variant_setScalarCopy(myVariant, &value, m_OpcUaData.type);
    }

    char *isting;
    QByteArray ba = m_OpcUaData.identifier_String.toUtf8();
    isting = ba.data();

    UA_Client* client = creationClient();
    if (client == NULL) {
        return false;
    }

    UA_StatusCode retval = UA_Client_writeValueAttribute(client, UA_NODEID_STRING(m_OpcUaData.namespaceIndex, isting), myVariant);
    UA_Client_delete(client);
    if (retval != UA_STATUSCODE_GOOD) {
        qDebug() << "写入失败状态";
        return false;
    }
    else {
        qDebug() << "写入成功";
        return true;
    }
    return false;
}

void OpcUA::wait(int ms)
{
    QElapsedTimer t;
    t.start();
    while (t.elapsed() < ms)
        QCoreApplication::processEvents();
}

UA_Client *OpcUA::creationClient()
{
    UA_Client* client = UA_Client_new(UA_ClientConfig_default);
    UA_StatusCode retval = UA_Client_connect(client, m_OpcUaData.clientUrl.toUtf8().data());

    if (retval != UA_STATUSCODE_GOOD) {
        UA_Client_delete(client);
        //连接失败进行多次重连
        for (int i = 0; i < 10; i++) {
            wait(100);
            client = UA_Client_new(UA_ClientConfig_default);
            retval = UA_Client_connect(client, m_OpcUaData.clientUrl.toUtf8().data());
            if (retval == UA_STATUSCODE_GOOD) {
                return client;
            }
        }
        return NULL;
    }
    return client;
}

QVariant OpcUA::dataConvert(UA_Variant *variant)
{
    if (m_OpcUaData.type == &UA_TYPES[UA_TYPES_BOOLEAN]) {
        bool value = *(bool*)variant->data;
        return QVariant(value);
    }
    else if(m_OpcUaData.type == &UA_TYPES[UA_TYPES_STRING]) {
        UA_String value = *(UA_String*)variant->data;
        QString str;
        for (unsigned int i = 0; i < value.length; i++) {
            str.append(QChar(value.data[i]));
        }
        return QVariant(str);
    }
    else if (m_OpcUaData.type == &UA_TYPES[UA_TYPES_INT32]) {
        UA_UInt32 value = *(UA_UInt32*)variant->data;
        return QVariant(value);
    }
    else if (m_OpcUaData.type == &UA_TYPES[UA_TYPES_INT64]) {
        UA_UInt64 value = *(UA_UInt64*)variant->data;
        return QVariant(value);
    }
    else if (m_OpcUaData.type == &UA_TYPES[UA_TYPES_DOUBLE]) {
        double value = *(double*)variant->data;
        return QVariant(value);
    }

    return QVariant();
}

总结

1.使用OpcUa客户端高频读写KepServer数据会导致服务经常挂掉,但是会立马恢复,所以才编写了重连机制,一直无法解决服务挂掉问题。错误代码0x80340000,0x80ae0000,0x80020000。如有解决方案,评论区请教大佬。
2.核心代码读写数据类型必须要匹配,否则会出现数据错误等问题。

你可能感兴趣的:(Qt-功能分享,qt,开发语言)