1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <private/opcuapathresolver_p.h>
5#include <private/opcuarelativenodeid_p.h>
6#include <private/opcuarelativenodepath_p.h>
7
8#include <QOpcUaClient>
9#include <QMetaEnum>
10#include <QLoggingCategory>
11
12QT_BEGIN_NAMESPACE
13
14/*!
15 \class OpcUaPathResolver
16 \inqmlmodule QtOpcUa
17 \internal
18 \brief This class resolves relative nodes.
19 \deprecated [6.9]
20
21 This class is used to resolve relative node IDs. It will emit \c resolvedNode
22 with the result and delete itself afterwards.
23 In case of errors the resolved node is empty and the error message is set.
24
25 This class is capable of resolving cascaded relative nodes by recursively instantiating
26 further resolvers. The maximum recursion depth is 50.
27
28 \sa RelativeNodeId, Node
29*/
30const int maxRecursionDepth = 50;
31Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_QML)
32
33OpcUaPathResolver::OpcUaPathResolver(OpcUaRelativeNodeId *relativeNode, QOpcUaClient *client, QObject *target)
34 : QObject(target)
35 , m_level(0)
36 , m_relativeNode(relativeNode)
37 , m_target(target)
38 , m_client(client)
39 , m_node(nullptr)
40{
41}
42
43OpcUaPathResolver::OpcUaPathResolver(int level, OpcUaRelativeNodeId *relativeNode, QOpcUaClient *client, QObject *target)
44 : QObject(target)
45 , m_level(level)
46 , m_relativeNode(relativeNode)
47 , m_target(target)
48 , m_client(client)
49 , m_node(nullptr)
50{
51}
52
53OpcUaPathResolver::~OpcUaPathResolver()
54{
55 if (m_node) {
56 m_node->deleteLater();
57 m_node = nullptr;
58 }
59}
60
61void OpcUaPathResolver::startResolving()
62{
63 if (!m_relativeNode || !m_client || !m_target) {
64 emit resolvedNode(node: UniversalNode(), errorMessage: QLatin1String("Member has been deleted"));
65 deleteLater();
66 return;
67 }
68
69 auto startNode = m_relativeNode->startNode();
70 if (!startNode) {
71 emit resolvedNode(node: UniversalNode(), errorMessage: QLatin1String("Aborted resolving because start node not present"));
72 deleteLater();
73 return;
74 }
75
76 if (m_relativeNode->pathCount() == 0) {
77 emit resolvedNode(node: UniversalNode(), errorMessage: QLatin1String("Skipping to resolve relative node with empty path"));
78 deleteLater();
79 return;
80 }
81
82 if (qobject_cast<const OpcUaRelativeNodeId *>(object: startNode)) {
83 // Trigger recursive resolving
84 if (m_level >= maxRecursionDepth) {
85 emit resolvedNode(node: UniversalNode(), errorMessage: QLatin1String("Maximum recursion depth reached during node resolution"));
86 deleteLater();
87 return;
88 }
89 auto node = qobject_cast<OpcUaRelativeNodeId *>(object: startNode);
90 auto resolver = new OpcUaPathResolver(m_level + 1, node, m_client, this);
91 connect(sender: resolver, signal: &OpcUaPathResolver::resolvedNode, context: this, slot: &OpcUaPathResolver::startNodeResolved);
92 resolver->startResolving();
93 return;
94 } else {
95 startNodeResolved(startNode, errorMessage: QString());
96 }
97}
98
99void OpcUaPathResolver::startNodeResolved(UniversalNode startNode, const QString &errorMessage)
100{
101 if (!m_relativeNode || !m_client || !m_target) {
102 emit resolvedNode(node: UniversalNode(), errorMessage: QLatin1String("Member has been deleted"));
103 deleteLater();
104 return;
105 }
106
107 if (sender())
108 sender()->deleteLater();
109
110 if (!errorMessage.isEmpty()) {
111 emit resolvedNode(node: startNode, errorMessage);
112 deleteLater();
113 return;
114 }
115
116 startNode.resolveNamespace(client: m_client);
117 m_node = m_client->node(nodeId: startNode.fullNodeId());
118 if (!m_node) {
119 emit resolvedNode(node: startNode, QStringLiteral("Could not create node from '%1'")
120 .arg(a: startNode.fullNodeId()));
121 deleteLater();
122 return;
123 }
124
125 // construct path vector
126 QList<QOpcUaRelativePathElement> path;
127 for (int i = 0; i < m_relativeNode->pathCount(); ++i)
128 path.append(t: m_relativeNode->path(i)->toRelativePathElement(client: m_client));
129
130 qCDebug(QT_OPCUA_PLUGINS_QML) << "Starting browse on" << m_node->nodeId();
131 connect(sender: m_node, signal: &QOpcUaNode::resolveBrowsePathFinished, context: this, slot: &OpcUaPathResolver::browsePathFinished);
132 if (!m_node->resolveBrowsePath(path)) {
133 emit resolvedNode(node: UniversalNode(), QStringLiteral("Failed to start browse"));
134 deleteLater();
135 return;
136 }
137}
138
139void OpcUaPathResolver::browsePathFinished(QList<QOpcUaBrowsePathTarget> results, QList<QOpcUaRelativePathElement> path, QOpcUa::UaStatusCode status)
140{
141 Q_UNUSED(path);
142 UniversalNode nodeToUse;
143
144 if (status != QOpcUa::Good) {
145 const QString name = QString::fromUtf8(
146 utf8: QMetaEnum::fromType<QOpcUa::UaStatusCode>().valueToKey(value: status));
147 emit resolvedNode(node: UniversalNode(),
148 QStringLiteral("Resolving browse path return error code %1").arg(a: name));
149 deleteLater();
150 return;
151 }
152
153 if (results.size() == 0) {
154 emit resolvedNode(node: UniversalNode(),
155 QStringLiteral("Relative path could not be resolved: Results are empty"));
156 deleteLater();
157 return;
158 } else if (results.size() == 1) {
159 if (results.at(i: 0).targetId().serverIndex() > 0) {
160 emit resolvedNode(node: UniversalNode(),
161 QStringLiteral("Relative path could not be resolved: "
162 "Resulting node is located on a remote server"));
163 deleteLater();
164 return;
165 }
166 nodeToUse.from(browsePathTarget: results.at(i: 0));
167
168 } else { // greater than one
169 UniversalNode tmp;
170 QString message = QStringLiteral("No resolved node found");
171
172 for (const auto &result : results) {
173 if (result.isFullyResolved()) {
174 if (result.targetId().serverIndex() > 0) {
175 message = QStringLiteral("Relative path could not be resolved: "
176 "Resulting node is located on a remote server");
177 continue;
178 }
179 if (!tmp.nodeIdentifier().isEmpty()) {
180 emit resolvedNode(node: UniversalNode(), errorMessage: QLatin1String("There are multiple resolved nodes"));
181 deleteLater();
182 return;
183 }
184 tmp.from(browsePathTarget: result);
185 }
186 }
187
188 if (!tmp.nodeIdentifier().isEmpty()) {
189 nodeToUse = tmp;
190 } else {
191 emit resolvedNode(node: UniversalNode(), errorMessage: message);
192 deleteLater();
193 return;
194 }
195 }
196
197 nodeToUse.resolveNamespace(client: m_client);
198 qCDebug(QT_OPCUA_PLUGINS_QML) << "Relative node fully resolved to:" << nodeToUse.fullNodeId();
199 emit resolvedNode(node: nodeToUse, errorMessage: QString());
200 deleteLater();
201}
202
203QT_END_NAMESPACE
204

source code of qtopcua/src/declarative_opcua/opcuapathresolver.cpp