我有一个在 Ubuntu 22.04 上运行的 Qt6 控制台应用程序。 该应用程序连接到多个支持 Opcua 的 PLC。目标是监视一些节点值(所有类别
variable
)。
请注意:我尝试向您展示我的代码,当然我无法提供“完整”示例,因为无法模拟实际的远程计算机。当然,请随时询问任何可能有帮助的更多详细信息。
首先是我的结构来处理节点:
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
}
这是我如何连接到远程计算机:
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);
}
}
然后,连接后,我启用节点列表的监控:
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));
}
}
}
我检查了
enableMonitoring
调用的返回值,都是true
。
问题是实际上只有一个节点被定期读取。我知道这一点,因为这是处理程序:
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);
}
}
并且仅打印一个
key
。当然,如果我手动触发读取,我可以全部读取。
使用 OpcUa 查看器,我检查了一些所需的节点:
我没有发现它们之间有任何区别。顺便说一句,我唯一可以成功监控的节点是
udiActualOrderLengthRT
。
我搜索了其他问题,发现了this。但是,老实说,我不确定答案是否也适用于我的框架。
我还阅读了 QOpcUaNode::enableMonitoring 和 QOpcUaMonitoringParameters 文档,如果我理解正确,我可以使用默认的
SubscriptionType::Shared
模式,因为我希望以相同的间隔读取所有数据。
我的代码或方法中是否有任何错误的证据? 什么会导致这种行为?