Search code examples
qtopc-uaqt6qtopcua

Monitoring of QtOpcUa value works only for one node


I have a Qt6 console application that runs on Ubuntu 22.04. The application connects to several OpcUa-enabled PLCs. The goal is to monitor some node values (all of class variable).

Please note: I try to show you my code, of course I cannot provide a "complete" example since there is no way to simulate the actual remote machine. Of course, feel free to ask any further details that may help.

First my struct to handle the nodes:

typedef struct
{
    QOpcUaNode *node; // the actual OpcUa node
    QVariant value;   // some internal variables,
    bool updated;     // not important for the question
    bool enableRead;
} Node_t;

void MyOpcUa::insertNode(QString key, QString id, bool enableRead)
{
    Node_t node; // my internal struct
    node.node = _opcUaClient->node(id); // retrieve the actual node
    node.value = QVariant();
    node.updated = false;
    node.enableRead = enableRead;

    Q_ASSERT(node.node); // check the node is valid

    _mapOpcNodes.insert(key, node);
    connect(node.node, &QOpcUaNode::attributeRead, this, &MyOpcUa::handleAttributes);
    connect(node.node, &QOpcUaNode::attributeUpdated, this, &MyOpcUa::handleAttributes);
    // when it reads the attributes I expect `handleAttributes` is called
}

Here how I connect to the remote machine:

void MyOpcUa::createClient()
{
    if (_opcUaClient)
    {
        delete _opcUaClient;
        _opcUaClient = nullptr;
    }

    Q_ASSERT(_opcUaClient == nullptr);

    if (_opcUaClient == nullptr)
    {
        _opcUaClient = _opcUaProvider->createClient(OPC_UA_BACKEND);
        if (!_opcUaClient)
        {
            qInfo() << ID << _name << "Connection to server failed:" << _opcUaClient->error();
            return;
        }

        connect(_opcUaClient, &QOpcUaClient::connectError, this, &MyOpcUa::connectError);
        _opcUaClient->setApplicationIdentity(_identity);
        _opcUaClient->setPkiConfiguration(*_pkiConfig);

        if (_opcUaClient->supportedUserTokenTypes().contains(QOpcUaUserTokenPolicy::TokenType::Certificate))
        {
            QOpcUaAuthenticationInformation authInfo;
            authInfo.setCertificateAuthentication();
            _opcUaClient->setAuthenticationInformation(authInfo);
        }

        connect(_opcUaClient, &QOpcUaClient::connected, this, &MyOpcUa::clientConnected);
        connect(_opcUaClient, &QOpcUaClient::disconnected, this, &MyOpcUa::clientDisconnected);
        connect(_opcUaClient, &QOpcUaClient::errorChanged, this, &MyOpcUa::clientError);
        connect(_opcUaClient, &QOpcUaClient::stateChanged, this, &MyOpcUa::clientState);
        connect(_opcUaClient, &QOpcUaClient::endpointsRequestFinished, this, &MyOpcUa::getEndpointsComplete);
        connect(_opcUaClient, &QOpcUaClient::findServersFinished, this, &MyOpcUa::findServersComplete);
    }
}

Then, after the connection, I enable the monitoring of the list of nodes:

void MyOpcUa::enableMonitoring()
{
   QMapIterator<QString, Node_t> i(_mapOpcNodes);
    while (i.hasNext())
    {
        i.next();
        Node_t node = i.value();
        if (node.enableRead)
        {
            qDebug() << node.node->enableMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters(1000));
        }
    }
}

I checked the returned value of enableMonitoring calls and all are true. The problem is only one node is actually read periodically. I know this since this is the handler:

void MyOpcUa::handleAttributes()
{
    QOpcUaNode *node = qobject_cast<QOpcUaNode*>(sender());
    QString key = retrieveNode(node->nodeId());
    if (key.isEmpty())
    {
        qWarning() << ID << "Key not found" << key;
    }

    QVariant value = node->attribute(QOpcUa::NodeAttribute::Value);    
    qDebug() << key; // print which node has been read

    if (_mapOpcNodes.contains(key))
    {
        _mapOpcNodes[key].updated = true;
        _mapOpcNodes[key].value = value;
        emit valueChanged(key, value);
    }
}

and only one key is printed. Of course, if I manually trigger a read I can read them all. Using an OpcUa viewer I inspected some of the desired nodes:

enter image description here

and I didn't find any difference among them. By the way, the only node I can successfully monitoring is udiActualOrderLengthRT.

I searched for other questions and I found this one. But, honestly I'm not sure if the answer is applicable to my framework as well.

I also read the QOpcUaNode::enableMonitoring and QOpcUaMonitoringParameters documentation and if I understand correctly I can live with the default SubscriptionType::Shared mode since I want all the data to be read with the same interval.

Is there any evidence of error in my code or in my approach? What can cause such a behavior?

UPDATE

I also checked the enableMonitoringFinished signal:

void MyOpcUa::enableMonitoringFinished(QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode statusCode)
{
    qDebug() << attr << statusCode;
}

and it is a success:

QOpcUa::NodeAttribute::Value QOpcUa::Good

Solution

  • OPC UA Subscriptions are only update, after the initail value, if the value has changed. Otherwise there would be no advantage of polling the variables.