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 if (m_thread->isRunning())
59 m_thread->quit();
60}
61
62void QOpen62541Client::connectToEndpoint(const QOpcUaEndpointDescription &endpoint)
63{
64 QMetaObject::invokeMethod(obj: m_backend, member: "connectToEndpoint", c: Qt::QueuedConnection,
65 Q_ARG(QOpcUaEndpointDescription, endpoint));
66}
67
68void QOpen62541Client::disconnectFromEndpoint()
69{
70 QMetaObject::invokeMethod(obj: m_backend, member: "disconnectFromEndpoint", c: Qt::QueuedConnection);
71}
72
73QOpcUaNode *QOpen62541Client::node(const QString &nodeId)
74{
75 UA_NodeId uaNodeId = Open62541Utils::nodeIdFromQString(name: nodeId);
76 if (UA_NodeId_isNull(p: &uaNodeId))
77 return nullptr;
78
79 auto tempNode = new QOpen62541Node(uaNodeId, this, nodeId);
80 if (!tempNode->registered()) {
81 qCDebug(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to register node with backend, maximum number of nodes reached.";
82 delete tempNode;
83 return nullptr;
84 }
85 return new QOpcUaNode(tempNode, m_client);
86}
87
88QString QOpen62541Client::backend() const
89{
90 return QStringLiteral("open62541");
91}
92
93bool QOpen62541Client::requestEndpoints(const QUrl &url)
94{
95 return QMetaObject::invokeMethod(obj: m_backend, member: "requestEndpoints", c: Qt::QueuedConnection, Q_ARG(QUrl, url));
96}
97
98bool QOpen62541Client::findServers(const QUrl &url, const QStringList &localeIds, const QStringList &serverUris)
99{
100 return QMetaObject::invokeMethod(obj: m_backend, member: "findServers", c: Qt::QueuedConnection,
101 Q_ARG(QUrl, url),
102 Q_ARG(QStringList, localeIds),
103 Q_ARG(QStringList, serverUris));
104}
105
106bool QOpen62541Client::readNodeAttributes(const QList<QOpcUaReadItem> &nodesToRead)
107{
108 return QMetaObject::invokeMethod(obj: m_backend, member: "readNodeAttributes", c: Qt::QueuedConnection,
109 Q_ARG(QList<QOpcUaReadItem>, nodesToRead));
110}
111
112bool QOpen62541Client::writeNodeAttributes(const QList<QOpcUaWriteItem> &nodesToWrite)
113{
114 return QMetaObject::invokeMethod(obj: m_backend, member: "writeNodeAttributes", c: Qt::QueuedConnection,
115 Q_ARG(QList<QOpcUaWriteItem>, nodesToWrite));
116}
117
118QOpcUaHistoryReadResponse *QOpen62541Client::readHistoryData(const QOpcUaHistoryReadRawRequest &request)
119{
120 if (!m_client)
121 return nullptr;
122
123 auto impl = new QOpcUaHistoryReadResponseImpl(request);
124 auto result = new QOpcUaHistoryReadResponse(impl);
125
126 // Connect signals
127 QObject::connect(sender: m_backend, signal: &QOpcUaBackend::historyDataAvailable, context: impl, slot: &QOpcUaHistoryReadResponseImpl::handleDataAvailable);
128 QObject::connect(sender: impl, signal: &QOpcUaHistoryReadResponseImpl::historyReadRawRequested, context: this, slot: &QOpen62541Client::handleHistoryReadRawRequested);
129 QObject::connect(sender: this, signal: &QOpen62541Client::historyReadRequestError, context: impl, slot: &QOpcUaHistoryReadResponseImpl::handleRequestError);
130
131 auto success = handleHistoryReadRawRequested(request, continuationPoints: {}, releaseContinuationPoints: false, handle: impl->handle());
132
133 if (!success) {
134 delete result;
135 return nullptr;
136 }
137
138 return result;
139}
140
141bool QOpen62541Client::addNode(const QOpcUaAddNodeItem &nodeToAdd)
142{
143 return QMetaObject::invokeMethod(obj: m_backend, member: "addNode", c: Qt::QueuedConnection,
144 Q_ARG(QOpcUaAddNodeItem, nodeToAdd));
145}
146
147bool QOpen62541Client::deleteNode(const QString &nodeId, bool deleteTargetReferences)
148{
149 return QMetaObject::invokeMethod(obj: m_backend, member: "deleteNode", c: Qt::QueuedConnection,
150 Q_ARG(QString, nodeId),
151 Q_ARG(bool, deleteTargetReferences));
152}
153
154bool QOpen62541Client::addReference(const QOpcUaAddReferenceItem &referenceToAdd)
155{
156 return QMetaObject::invokeMethod(obj: m_backend, member: "addReference", c: Qt::QueuedConnection,
157 Q_ARG(QOpcUaAddReferenceItem, referenceToAdd));
158}
159
160bool QOpen62541Client::deleteReference(const QOpcUaDeleteReferenceItem &referenceToDelete)
161{
162 return QMetaObject::invokeMethod(obj: m_backend, member: "deleteReference", c: Qt::QueuedConnection,
163 Q_ARG(QOpcUaDeleteReferenceItem, referenceToDelete));
164}
165
166QStringList QOpen62541Client::supportedSecurityPolicies() const
167{
168 auto result = QStringList {
169 "http://opcfoundation.org/UA/SecurityPolicy#None"
170 };
171#ifdef UA_ENABLE_ENCRYPTION
172 if (m_hasSha1SignatureSupport) {
173 result.append(t: "http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
174 result.append(t: "http://opcfoundation.org/UA/SecurityPolicy#Basic256");
175 }
176
177 result.append(t: "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
178 result.append(t: "http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep");
179#endif
180
181 return result;
182}
183
184QList<QOpcUaUserTokenPolicy::TokenType> QOpen62541Client::supportedUserTokenTypes() const
185{
186 return QList<QOpcUaUserTokenPolicy::TokenType> {
187 QOpcUaUserTokenPolicy::TokenType::Anonymous,
188 QOpcUaUserTokenPolicy::TokenType::Username
189 };
190}
191
192bool QOpen62541Client::handleHistoryReadRawRequested(const QOpcUaHistoryReadRawRequest &request, const QList<QByteArray> &continuationPoints,
193 bool releaseContinuationPoints, quint64 handle)
194{
195 const auto success = QMetaObject::invokeMethod(obj: m_backend, member: "readHistoryRaw",
196 c: Qt::QueuedConnection,
197 Q_ARG(QOpcUaHistoryReadRawRequest, request),
198 Q_ARG(QList<QByteArray>, continuationPoints),
199 Q_ARG(bool, releaseContinuationPoints),
200 Q_ARG(quint64, handle));
201
202 if (!success)
203 emit historyReadRequestError(handle);
204
205 return success;
206}
207
208QT_END_NAMESPACE
209

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