1// Copyright (C) 2017 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 "qopen62541backend.h"
5#include "qopen62541client.h"
6#include "qopen62541node.h"
7#include "qopen62541subscription.h"
8#include "qopen62541utils.h"
9#include "qopen62541valueconverter.h"
10#include <private/qopcuaclient_p.h>
11#include <private/qopcuahistoryreadresponseimpl_p.h>
12
13#include <QtCore/qloggingcategory.h>
14#include <QtCore/qstringlist.h>
15#include <QtCore/qthread.h>
16#include <QtCore/qurl.h>
17
18QT_BEGIN_NAMESPACE
19
20Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_OPEN62541)
21
22QOpen62541Client::QOpen62541Client(const QVariantMap &backendProperties)
23 : QOpcUaClientImpl()
24 , m_backend(new Open62541AsyncBackend(this))
25{
26#ifdef UA_ENABLE_ENCRYPTION
27 m_hasSha1SignatureSupport = Open62541Utils::checkSha1SignatureSupport();
28
29 if (!m_hasSha1SignatureSupport)
30 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "SHA-1 signatures are not supported by OpenSSL"
31 << "The security policies Basic128Rsa15 and Basic256 will not be available";
32#endif
33
34 bool ok = false;
35 const quint32 clientIterateInterval = backendProperties.value(QStringLiteral("clientIterateIntervalMs"), defaultValue: 50)
36 .toUInt(ok: &ok);
37
38 if (ok)
39 m_backend->m_clientIterateInterval = clientIterateInterval;
40
41 const quint32 asyncRequestTimeout = backendProperties.value(QStringLiteral("asyncRequestTimeoutMs"), defaultValue: 15000)
42 .toUInt(ok: &ok);
43
44 if (ok)
45 m_backend->m_asyncRequestTimeout = asyncRequestTimeout;
46
47 m_thread = new QThread();
48 m_thread->setObjectName("QOpen62541Client");
49 connectBackendWithClient(backend: m_backend);
50 m_backend->moveToThread(thread: m_thread);
51 connect(sender: m_thread, signal: &QThread::finished, context: m_thread, slot: &QObject::deleteLater);
52 connect(sender: m_thread, signal: &QThread::finished, context: m_backend, slot: &QObject::deleteLater);
53 m_thread->start();
54}
55
56QOpen62541Client::~QOpen62541Client()
57{
58 // The connectError() and passwordForPrivateKeyRequired() signals use a blocking queued connection.
59 // They must be disconnected before waiting for the thread to avoid a deadlock.
60 QObject::disconnect(sender: m_backend, signal: &Open62541AsyncBackend::connectError, receiver: this, slot: &QOpcUaClientImpl::connectError);
61 QObject::disconnect(sender: m_backend, signal: &Open62541AsyncBackend::passwordForPrivateKeyRequired,
62 receiver: this, slot: &QOpcUaClientImpl::passwordForPrivateKeyRequired);
63
64 if (m_thread->isRunning())
65 m_thread->quit();
66
67 m_thread->wait();
68}
69
70void QOpen62541Client::connectToEndpoint(const QOpcUaEndpointDescription &endpoint)
71{
72 QMetaObject::invokeMethod(obj: m_backend, member: "connectToEndpoint", c: Qt::QueuedConnection,
73 Q_ARG(QOpcUaEndpointDescription, endpoint));
74}
75
76void QOpen62541Client::disconnectFromEndpoint()
77{
78 QMetaObject::invokeMethod(obj: m_backend, member: "disconnectFromEndpoint", c: Qt::QueuedConnection);
79}
80
81QOpcUaNode *QOpen62541Client::node(const QString &nodeId)
82{
83 UA_NodeId uaNodeId = Open62541Utils::nodeIdFromQString(name: nodeId);
84 if (UA_NodeId_isNull(p: &uaNodeId))
85 return nullptr;
86
87 auto tempNode = new QOpen62541Node(uaNodeId, this, nodeId);
88 if (!tempNode->registered()) {
89 qCDebug(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to register node with backend, maximum number of nodes reached.";
90 delete tempNode;
91 return nullptr;
92 }
93 return new QOpcUaNode(tempNode, m_client);
94}
95
96QString QOpen62541Client::backend() const
97{
98 return QStringLiteral("open62541");
99}
100
101bool QOpen62541Client::requestEndpoints(const QUrl &url)
102{
103 return QMetaObject::invokeMethod(obj: m_backend, member: "requestEndpoints", c: Qt::QueuedConnection, Q_ARG(QUrl, url));
104}
105
106bool QOpen62541Client::findServers(const QUrl &url, const QStringList &localeIds, const QStringList &serverUris)
107{
108 return QMetaObject::invokeMethod(obj: m_backend, member: "findServers", c: Qt::QueuedConnection,
109 Q_ARG(QUrl, url),
110 Q_ARG(QStringList, localeIds),
111 Q_ARG(QStringList, serverUris));
112}
113
114bool QOpen62541Client::readNodeAttributes(const QList<QOpcUaReadItem> &nodesToRead)
115{
116 return QMetaObject::invokeMethod(obj: m_backend, member: "readNodeAttributes", c: Qt::QueuedConnection,
117 Q_ARG(QList<QOpcUaReadItem>, nodesToRead));
118}
119
120bool QOpen62541Client::writeNodeAttributes(const QList<QOpcUaWriteItem> &nodesToWrite)
121{
122 return QMetaObject::invokeMethod(obj: m_backend, member: "writeNodeAttributes", c: Qt::QueuedConnection,
123 Q_ARG(QList<QOpcUaWriteItem>, nodesToWrite));
124}
125
126QOpcUaHistoryReadResponse *QOpen62541Client::readHistoryData(const QOpcUaHistoryReadRawRequest &request)
127{
128 if (!m_client)
129 return nullptr;
130
131 auto impl = new QOpcUaHistoryReadResponseImpl(request);
132 auto result = new QOpcUaHistoryReadResponse(impl);
133
134 // Connect signals
135 QObject::connect(sender: m_backend, signal: &QOpcUaBackend::historyDataAvailable, context: impl, slot: &QOpcUaHistoryReadResponseImpl::handleDataAvailable);
136 QObject::connect(sender: impl, signal: &QOpcUaHistoryReadResponseImpl::historyReadRawRequested, context: this, slot: &QOpen62541Client::handleHistoryReadRawRequested);
137 QObject::connect(sender: this, signal: &QOpen62541Client::historyReadRequestError, context: impl, slot: &QOpcUaHistoryReadResponseImpl::handleRequestError);
138
139 auto success = handleHistoryReadRawRequested(request, continuationPoints: {}, releaseContinuationPoints: false, handle: impl->handle());
140
141 if (!success) {
142 delete result;
143 return nullptr;
144 }
145
146 return result;
147}
148
149QOpcUaHistoryReadResponse *QOpen62541Client::readHistoryEvents(const QOpcUaHistoryReadEventRequest &request)
150{
151 if (!m_client)
152 return nullptr;
153
154 auto impl = new QOpcUaHistoryReadResponseImpl(request);
155 const auto result = new QOpcUaHistoryReadResponse(impl);
156
157 // Connect signals
158 QObject::connect(sender: m_backend, signal: &QOpcUaBackend::historyEventsAvailable, context: impl, slot: &QOpcUaHistoryReadResponseImpl::handleEventsAvailable);
159 QObject::connect(sender: impl, signal: &QOpcUaHistoryReadResponseImpl::historyReadEventsRequested, context: this, slot: &QOpen62541Client::handleHistoryReadEventsRequested);
160 QObject::connect(sender: this, signal: &QOpen62541Client::historyReadRequestError, context: impl, slot: &QOpcUaHistoryReadResponseImpl::handleRequestError);
161
162 const auto success = handleHistoryReadEventsRequested(request, continuationPoints: {}, releaseContinuationPoints: false, handle: impl->handle());
163
164 if (!success) {
165 delete result;
166 return nullptr;
167 }
168
169 return result;
170}
171
172bool QOpen62541Client::addNode(const QOpcUaAddNodeItem &nodeToAdd)
173{
174 return QMetaObject::invokeMethod(obj: m_backend, member: "addNode", c: Qt::QueuedConnection,
175 Q_ARG(QOpcUaAddNodeItem, nodeToAdd));
176}
177
178bool QOpen62541Client::deleteNode(const QString &nodeId, bool deleteTargetReferences)
179{
180 return QMetaObject::invokeMethod(obj: m_backend, member: "deleteNode", c: Qt::QueuedConnection,
181 Q_ARG(QString, nodeId),
182 Q_ARG(bool, deleteTargetReferences));
183}
184
185bool QOpen62541Client::addReference(const QOpcUaAddReferenceItem &referenceToAdd)
186{
187 return QMetaObject::invokeMethod(obj: m_backend, member: "addReference", c: Qt::QueuedConnection,
188 Q_ARG(QOpcUaAddReferenceItem, referenceToAdd));
189}
190
191bool QOpen62541Client::deleteReference(const QOpcUaDeleteReferenceItem &referenceToDelete)
192{
193 return QMetaObject::invokeMethod(obj: m_backend, member: "deleteReference", c: Qt::QueuedConnection,
194 Q_ARG(QOpcUaDeleteReferenceItem, referenceToDelete));
195}
196
197QStringList QOpen62541Client::supportedSecurityPolicies() const
198{
199 auto result = QStringList {
200 "http://opcfoundation.org/UA/SecurityPolicy#None"
201 };
202#ifdef UA_ENABLE_ENCRYPTION
203 if (m_hasSha1SignatureSupport) {
204 result.append(t: "http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
205 result.append(t: "http://opcfoundation.org/UA/SecurityPolicy#Basic256");
206 }
207
208 result.append(t: "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
209 result.append(t: "http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep");
210#endif
211
212 return result;
213}
214
215QList<QOpcUaUserTokenPolicy::TokenType> QOpen62541Client::supportedUserTokenTypes() const
216{
217 return QList<QOpcUaUserTokenPolicy::TokenType> {
218 QOpcUaUserTokenPolicy::TokenType::Anonymous,
219 QOpcUaUserTokenPolicy::TokenType::Username
220 };
221}
222
223bool QOpen62541Client::handleHistoryReadRawRequested(const QOpcUaHistoryReadRawRequest &request, const QList<QByteArray> &continuationPoints,
224 bool releaseContinuationPoints, quint64 handle)
225{
226 const auto success = QMetaObject::invokeMethod(obj: m_backend, member: "readHistoryRaw",
227 c: Qt::QueuedConnection,
228 Q_ARG(QOpcUaHistoryReadRawRequest, request),
229 Q_ARG(QList<QByteArray>, continuationPoints),
230 Q_ARG(bool, releaseContinuationPoints),
231 Q_ARG(quint64, handle));
232
233 if (!success)
234 emit historyReadRequestError(handle);
235
236 return success;
237}
238
239bool QOpen62541Client::registerNodes(const QStringList &nodesToRegister)
240{
241 return QMetaObject::invokeMethod(obj: m_backend, member: "registerNodes",
242 c: Qt::QueuedConnection,
243 Q_ARG(QStringList, nodesToRegister));
244}
245
246bool QOpen62541Client::unregisterNodes(const QStringList &nodesToUnregister)
247{
248 return QMetaObject::invokeMethod(obj: m_backend, member: "unregisterNodes",
249 c: Qt::QueuedConnection,
250 Q_ARG(QStringList, nodesToUnregister));
251}
252
253bool QOpen62541Client::handleHistoryReadEventsRequested(const QOpcUaHistoryReadEventRequest &request, const QList<QByteArray> &continuationPoints,
254 bool releaseContinuationPoints, quint64 handle)
255{
256 const auto success = QMetaObject::invokeMethod(obj: m_backend, member: "readHistoryEvents",
257 c: Qt::QueuedConnection,
258 Q_ARG(QOpcUaHistoryReadEventRequest, request),
259 Q_ARG(QList<QByteArray>, continuationPoints),
260 Q_ARG(bool, releaseContinuationPoints),
261 Q_ARG(quint64, handle));
262
263 if (!success)
264 emit historyReadRequestError(handle);
265
266 return success;
267}
268
269QT_END_NAMESPACE
270

source code of qtopcua/src/plugins/opcua/open62541/qopen62541client.cpp