1// Copyright (C) 2016 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 <private/qopcuaclient_p.h>
5
6#include <QtCore/qloggingcategory.h>
7#include <QtOpcUa/qopcuaendpointdescription.h>
8
9#include "qopcuaerrorstate.h"
10
11QT_BEGIN_NAMESPACE
12
13Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA)
14
15QOpcUaClientPrivate::QOpcUaClientPrivate(QOpcUaClientImpl *impl)
16 : QObjectPrivate()
17 , m_impl(impl)
18 , m_state(QOpcUaClient::Disconnected)
19 , m_error(QOpcUaClient::NoError)
20 , m_enableNamespaceArrayAutoupdate(false)
21 , m_namespaceArrayAutoupdateEnabled(false)
22 , m_namespaceArrayUpdateInterval(1000)
23{
24}
25
26QOpcUaClientPrivate::~QOpcUaClientPrivate()
27{
28}
29
30void QOpcUaClientPrivate::connectToEndpoint(const QOpcUaEndpointDescription &endpoint)
31{
32 // Some pre-connection checks
33 if (QOpcUa::isSecurePolicy(securityPolicy: endpoint.securityPolicy())) {
34 // We are going to connect to a secure endpoint
35
36 if (!m_pkiConfig.isPkiValid()) {
37 qCWarning(QT_OPCUA) << "Can not connect to a secure endpoint without a valid PKI setup.";
38 setStateAndError(state: m_state, error: QOpcUaClient::AccessDenied);
39 return;
40 }
41
42 if (!m_pkiConfig.isKeyAndCertificateFileSet()) {
43 qCWarning(QT_OPCUA) << "Can not connect to a secure endpoint without a client certificate.";
44 setStateAndError(state: m_state, error: QOpcUaClient::AccessDenied);
45 return;
46 }
47 }
48
49 m_endpoint = endpoint;
50 m_impl->connectToEndpoint(endpoint);
51}
52
53void QOpcUaClientPrivate::disconnectFromEndpoint()
54{
55 if (m_state != QOpcUaClient::Connected) {
56 qCWarning(QT_OPCUA) << "Closing a connection without being connected";
57 return;
58 }
59
60 setStateAndError(state: QOpcUaClient::Closing);
61 m_impl->disconnectFromEndpoint();
62}
63
64void QOpcUaClientPrivate::setStateAndError(QOpcUaClient::ClientState state,
65 QOpcUaClient::ClientError error)
66{
67 Q_Q(QOpcUaClient);
68
69 // ensure that state and error transition are atomic before signal emission
70 bool stateChanged = false;
71 bool errorOccurred = false;
72
73 if (m_state != state) {
74 m_state = state;
75 stateChanged = true;
76 }
77 if (error != QOpcUaClient::NoError && m_error != error) {
78 errorOccurred = true;
79 }
80 m_error = error;
81
82 if (errorOccurred)
83 emit q->errorChanged(error: m_error);
84 if (stateChanged) {
85 emit q->stateChanged(state: m_state);
86
87 if (m_state == QOpcUaClient::Connected)
88 emit q->connected();
89 else if (m_state == QOpcUaClient::Disconnected)
90 emit q->disconnected();
91 }
92
93 // According to UPC-UA part 5, page 23, the server is allowed to change entries of the namespace
94 // array if there is no active session. This could invalidate the cached namespaces table.
95 if (state == QOpcUaClient::Disconnected) {
96 m_namespaceArray.clear();
97 }
98}
99
100bool QOpcUaClientPrivate::updateNamespaceArray()
101{
102 if (m_state != QOpcUaClient::ClientState::Connected)
103 return false;
104
105 if (!m_namespaceArrayNode) {
106 m_namespaceArrayNode.reset(other: m_impl->node(QStringLiteral("ns=0;i=2255")));
107 if (!m_namespaceArrayNode)
108 return false;
109 QObjectPrivate::connect(sender: m_namespaceArrayNode.data(), signal: &QOpcUaNode::attributeRead, receiverPrivate: this, slot: &QOpcUaClientPrivate::namespaceArrayUpdated);
110 }
111
112 return m_namespaceArrayNode->readAttributes(attributes: QOpcUa::NodeAttribute::Value);
113}
114
115QStringList QOpcUaClientPrivate::namespaceArray() const
116{
117 return m_namespaceArray;
118}
119
120void QOpcUaClientPrivate::namespaceArrayUpdated(QOpcUa::NodeAttributes attr)
121{
122 Q_Q(QOpcUaClient);
123
124 const QVariant value = m_namespaceArrayNode->attribute(attribute: QOpcUa::NodeAttribute::Value);
125
126 if (!(attr & QOpcUa::NodeAttribute::Value) || value.metaType().id() != QMetaType::QVariantList) {
127 m_namespaceArray.clear();
128 emit q->namespaceArrayUpdated(namespaces: QStringList());
129 return;
130 }
131
132 QStringList updatedNamespaceArray(value.toList().size());
133 int index = 0;
134 for (const auto &it : value.toList())
135 updatedNamespaceArray[index++] = (it.toString());
136
137 if (updatedNamespaceArray != m_namespaceArray) {
138 m_namespaceArray = updatedNamespaceArray;
139 emit q->namespaceArrayChanged(namespaces: m_namespaceArray);
140 }
141 emit q->namespaceArrayUpdated(namespaces: m_namespaceArray);
142}
143
144void QOpcUaClientPrivate::setupNamespaceArrayMonitoring()
145{
146 Q_Q(QOpcUaClient);
147
148 if (!m_namespaceArrayNode || m_state != QOpcUaClient::ClientState::Connected)
149 return;
150
151 if (!m_enableNamespaceArrayAutoupdate && m_namespaceArrayAutoupdateEnabled) {
152 m_namespaceArrayNode->disableMonitoring(attr: QOpcUa::NodeAttribute::Value);
153 m_namespaceArrayAutoupdateEnabled = false;
154 return;
155 }
156
157 if (m_enableNamespaceArrayAutoupdate && !m_namespaceArrayAutoupdateEnabled) {
158 QOpcUaMonitoringParameters options;
159 options.setSubscriptionType(QOpcUaMonitoringParameters::SubscriptionType::Exclusive);
160 options.setMaxKeepAliveCount((std::numeric_limits<quint32>::max)() - 1);
161 options.setPublishingInterval(m_namespaceArrayUpdateInterval);
162 m_namespaceArrayAutoupdateEnabled = true;
163
164 QObject::connect(sender: m_namespaceArrayNode.data(), signal: &QOpcUaNode::enableMonitoringFinished, context: q,
165 slot: [&] (QOpcUa::NodeAttribute, QOpcUa::UaStatusCode statusCode) {
166 if (statusCode == QOpcUa::Good) {
167 // Update interval member to the revised value from the server
168 m_namespaceArrayUpdateInterval = m_namespaceArrayNode->monitoringStatus(attr: QOpcUa::NodeAttribute::Value).publishingInterval();
169 } else {
170 m_namespaceArrayAutoupdateEnabled = m_enableNamespaceArrayAutoupdate = false;
171 }
172 }
173 );
174 QObject::connect(sender: m_namespaceArrayNode.data(), signal: &QOpcUaNode::attributeUpdated, context: q,
175 slot: [&] (QOpcUa::NodeAttribute attr, QVariant /*value*/) {
176 namespaceArrayUpdated(attr);
177 }
178 );
179
180 m_namespaceArrayNode->enableMonitoring(attr: QOpcUa::NodeAttribute::Value, settings: options);
181 }
182}
183
184void QOpcUaClientPrivate::setApplicationIdentity(const QOpcUaApplicationIdentity &identity)
185{
186 m_applicationIdentity = identity;
187}
188
189QOpcUaApplicationIdentity QOpcUaClientPrivate::applicationIdentity() const
190{
191 return m_applicationIdentity;
192}
193
194void QOpcUaClientPrivate::setPkiConfiguration(const QOpcUaPkiConfiguration &config)
195{
196 m_pkiConfig = config;
197}
198
199QOpcUaPkiConfiguration QOpcUaClientPrivate::pkiConfiguration() const
200{
201 return m_pkiConfig;
202}
203
204QT_END_NAMESPACE
205

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtopcua/src/opcua/client/qopcuaclientprivate.cpp