1// Copyright (C) 2015 basysKom GmbH, opensource@basyskom.com
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 "qopcuaplugin.h"
5#include "qopcuaprovider.h"
6#include <QtOpcUa/qopcuaclient.h>
7#include <QtOpcUa/qopcuanode.h>
8#include <QtOpcUa/qopcuatype.h>
9#include <QtOpcUa/qopcuaapplicationidentity.h>
10#include <QtOpcUa/qopcuapkiconfiguration.h>
11#include <private/qopcuanodeimpl_p.h>
12#include <QtOpcUa/qopcuaqualifiedname.h>
13#include <QtOpcUa/qopcuarange.h>
14#include <QtOpcUa/qopcuaeuinformation.h>
15#include <QtOpcUa/qopcuacomplexnumber.h>
16#include <QtOpcUa/qopcuadoublecomplexnumber.h>
17#include <QtOpcUa/qopcuaaxisinformation.h>
18#include <QtOpcUa/qopcuaxvalue.h>
19#include <QtOpcUa/qopcuaargument.h>
20#include <QtOpcUa/qopcuaextensionobject.h>
21#include <QtOpcUa/qopcuaendpointdescription.h>
22#include <QtOpcUa/qopcuaexpandednodeid.h>
23#include <QtOpcUa/qopcuarelativepathelement.h>
24#include <QtOpcUa/qopcuabrowsepathtarget.h>
25
26#include <QtCore/qcborarray.h>
27#include <private/qfactoryloader_p.h>
28#include <QtCore/qloggingcategory.h>
29#include <QtCore/qpluginloader.h>
30
31QT_BEGIN_NAMESPACE
32
33Q_LOGGING_CATEGORY(QT_OPCUA, "qt.opcua")
34Q_LOGGING_CATEGORY(QT_OPCUA_SECURITY, "qt.opcua.security")
35
36/*!
37 \class QOpcUaProvider
38 \inmodule QtOpcUa
39
40 \brief QOpcUaProvider creates an instance of QOpcUaClient.
41
42 QOpcUaProvider allows the user to create an instance of QOpcUaClient by
43 loading a QOpcUaPlugin using the Qt plugin system.
44
45 For the available plugins and their capabilities please refer to the
46 \l {Qt OPC UA} {introduction}.
47
48 \section1 Example
49 This code creates a client using the first available backend:
50 \code
51 QOpcUaProvider provider;
52 QStringList available = provider.availableBackends();
53 if (!available.isEmpty()) {
54 QOpcUaClient *client = provider.createClient(available[0]);
55 if (client)
56 qDebug() << "Client successfully created";
57 }
58 \endcode
59*/
60
61Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qOpcualoader,
62 (QOpcUaProviderFactory_iid, QLatin1String("/opcua")))
63
64/*!
65 \fn static QHash<QString, QCborMap> loadPluginMetadata()
66
67 Reads the meta data from the plugins known by the loader.
68*/
69static QHash<QString, QCborMap> loadPluginMetadata()
70{
71 QHash<QString, QCborMap> plugins;
72 const QFactoryLoader *l = qOpcualoader();
73 QList<QPluginParsedMetaData> const meta = l->metaData();
74 for (int i = 0; i < meta.size(); ++i) {
75 QCborMap map = meta.at(i).value(k: QtPluginMetaDataKeys::MetaData).toMap();
76 map.insert(key: QLatin1String("index"), value_: i);
77 plugins.insert(key: map.value(key: QLatin1String("Provider")).toString(), value: map);
78 }
79 return plugins;
80}
81
82/*!
83 \fn static QHash<QString, QCborMap> plugins()
84
85 Returns a \l QHash mapping names to JSON objects containing the meta data of
86 available plugins.
87*/
88static QHash<QString, QCborMap> plugins()
89{
90 static QHash<QString, QCborMap> plugins;
91 static bool alreadyDiscovered = false;
92
93 if (!alreadyDiscovered) {
94 plugins = loadPluginMetadata();
95 alreadyDiscovered = true;
96 }
97 return plugins;
98}
99
100/*!
101 Creates a new OPC UA provider with a given \a parent.
102 */
103QOpcUaProvider::QOpcUaProvider(QObject *parent)
104 : QObject(parent)
105{
106 qRegisterMetaType<QOpcUa::Types>();
107 qRegisterMetaType<QOpcUa::TypedVariant>();
108 qRegisterMetaType<QList<QOpcUa::TypedVariant>>();
109 qRegisterMetaType<QOpcUa::UaStatusCode>();
110 qRegisterMetaType<QOpcUa::NodeClass>();
111 qRegisterMetaType<QOpcUa::NodeClasses>();
112 qRegisterMetaType<QOpcUaQualifiedName>();
113 qRegisterMetaType<QOpcUa::NodeAttribute>();
114 qRegisterMetaType<QOpcUa::NodeAttributes>();
115 qRegisterMetaType<QOpcUaNode::AttributeMap>();
116 qRegisterMetaType<QList<QOpcUaReadResult>>();
117 qRegisterMetaType<QOpcUaClient::ClientState>();
118 qRegisterMetaType<QOpcUaClient::ClientError>();
119 qRegisterMetaType<QOpcUa::ReferenceTypeId>();
120 qRegisterMetaType<QOpcUaMonitoringParameters::SubscriptionType>();
121 qRegisterMetaType<QOpcUaMonitoringParameters::Parameter>();
122 qRegisterMetaType<QOpcUaMonitoringParameters::Parameters>();
123 qRegisterMetaType<QOpcUaMonitoringParameters>();
124 qRegisterMetaType<QOpcUaReferenceDescription>();
125 qRegisterMetaType<QList<QOpcUaReferenceDescription>>();
126 qRegisterMetaType<QOpcUa::ReferenceTypeId>();
127 qRegisterMetaType<QOpcUaRange>();
128 qRegisterMetaType<QOpcUaEUInformation>();
129 qRegisterMetaType<QOpcUaComplexNumber>();
130 qRegisterMetaType<QOpcUaDoubleComplexNumber>();
131 qRegisterMetaType<QOpcUaAxisInformation>();
132 qRegisterMetaType<QOpcUaXValue>();
133 qRegisterMetaType<QOpcUaExpandedNodeId>();
134 qRegisterMetaType<QOpcUaRelativePathElement>();
135 qRegisterMetaType<QList<QOpcUaRelativePathElement>>();
136 qRegisterMetaType<QOpcUaBrowsePathTarget>();
137 qRegisterMetaType<QList<QOpcUaBrowsePathTarget>>();
138 qRegisterMetaType<QOpcUaEndpointDescription>();
139 qRegisterMetaType<QList<QOpcUaEndpointDescription>>();
140 qRegisterMetaType<QOpcUaArgument>();
141 qRegisterMetaType<QOpcUaExtensionObject>();
142 qRegisterMetaType<QOpcUaBrowseRequest>();
143 qRegisterMetaType<QOpcUaReadItem>();
144 qRegisterMetaType<QOpcUaReadResult>();
145 qRegisterMetaType<QList<QOpcUaReadItem>>();
146 qRegisterMetaType<QList<QOpcUaReadResult>>();
147 qRegisterMetaType<QOpcUaWriteItem>();
148 qRegisterMetaType<QOpcUaWriteResult>();
149 qRegisterMetaType<QList<QOpcUaWriteItem>>();
150 qRegisterMetaType<QList<QOpcUaWriteResult>>();
151 qRegisterMetaType<QOpcUaNodeCreationAttributes>();
152 qRegisterMetaType<QOpcUaAddNodeItem>();
153 qRegisterMetaType<QOpcUaAddReferenceItem>();
154 qRegisterMetaType<QOpcUaDeleteReferenceItem>();
155 qRegisterMetaType<QList<QOpcUaApplicationDescription>>();
156 qRegisterMetaType<QOpcUaApplicationIdentity>();
157 qRegisterMetaType<QOpcUaPkiConfiguration>();
158}
159
160QOpcUaProvider::~QOpcUaProvider()
161{
162 qDeleteAll(c: m_plugins);
163}
164
165static QOpcUaPlugin *loadPlugin(const QString &key)
166{
167 const int index = qOpcualoader()->indexOf(needle: key);
168 if (index != -1) {
169 QObject *factoryObject = qOpcualoader()->instance(index);
170 if (QOpcUaPlugin *plugin = qobject_cast<QOpcUaPlugin *>(object: factoryObject)) {
171 return plugin;
172 }
173 }
174 return nullptr;
175}
176
177/*!
178 Returns a pointer to a QOpcUaClient object by loading the selected \a backend
179 as a plugin and creating a client object.
180 If the plugin loading fails, \c nullptr is returned instead.
181
182 The user is responsible for deleting the returned \l QOpcUaClient object
183 when it is no longer needed.
184
185 The optional argument \a backendProperties can be used to pass custom backend specific settings as key value pairs.
186 Those settings are specific to the backend being instantiated.
187
188 Available settings are
189 \table
190 \header
191 \li Setting string
192 \li Backend
193 \li Description
194 \row
195 \li disableEncryptedPasswordCheck
196 \li Unified Automation
197 \li By default, the backend refuses to connect to endpoints without encryption to avoid
198 sending passwords in clear text. This parameter allows to disable this feature.
199 \row
200 \li enableVerboseDebugOutput
201 \li Unified Automation
202 \li Tells the backend to print additional output to the terminal. The backend specific logging
203 level is set to \c OPCUA_TRACE_OUTPUT_LEVEL_ALL.
204 \row
205 \li minimumClientIterateIntervalMs
206 \li open62541
207 \li This parameter is no longer evaluated by the backend and has been replaced by \c clientIterateIntervalMs.
208 \row
209 \li clientIterateIntervalMs
210 \li open62541
211 \li Defines the client iterate interval for the backend. If the client is causing too much CPU load,
212 setting this value higher than the default will reduce the CPU load at the price of an increased
213 response time to service requests and value updates from subscriptions.
214 The default value is 50ms.
215 \row
216 \li asyncRequestTimeoutMs
217 \li open62541
218 \li Defines the timeout for asynchronous requests to an OPC UA server. If the server doesn't reply to
219 a service request before the timeout occurs, the service call fails and the finished signal will
220 contain a \c bad status code. The default value is 15000ms.
221 \endtable
222*/
223QOpcUaClient *QOpcUaProvider::createClient(const QString &backend, const QVariantMap &backendProperties)
224{
225 QOpcUaPlugin *plugin;
226 auto it = m_plugins.find(key: backend);
227 if (it == m_plugins.end()) {
228 plugin = loadPlugin(key: backend);
229 if (!plugin) {
230 qCWarning(QT_OPCUA) << "Failed to load OPC UA plugin:" << backend;
231 qCWarning(QT_OPCUA) << "Available plugins:" << availableBackends();
232 return nullptr;
233 }
234 m_plugins.insert(key: backend, value: plugin);
235 }
236 else {
237 plugin = it.value();
238 }
239 return plugin->createClient(backendProperties);
240}
241
242/*!
243 Returns a QStringList of available plugins.
244*/
245QStringList QOpcUaProvider::availableBackends()
246{
247 return plugins().keys();
248}
249
250QT_END_NAMESPACE
251

source code of qtopcua/src/opcua/core/qopcuaprovider.cpp