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

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