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

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