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