1// Copyright (C) 2019 The Qt Company Ltd.
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/opcuaendpointdiscovery_p.h>
5#include <private/opcuaconnection_p.h>
6
7QT_BEGIN_NAMESPACE
8
9/*!
10 \qmltype EndpointDiscovery
11 \inqmlmodule QtOpcUa
12 \brief Provides information about available endpoints on a server.
13 \since QtOpcUa 5.13
14 \deprecated [6.9]
15
16 Allows to fetch and access information about available endpoints on a server.
17
18 \snippet ../../src/declarative_opcua/doc/snippets/basic/basic.qml Basic discovery
19*/
20
21/*!
22 \qmlproperty string EndpointDiscovery::serverUrl
23
24 Discovery URL of the server to fetch the endpoints from.
25 Every time the URL is changed, a request to the given server is started.
26
27 When starting the request, the list of available endpoints is cleared
28 and the status is set to \l {Status::Status}{Status.GoodCompletesAsynchronously}. Once the request is finished,
29 \l status changes.
30
31 Make sure to check \l status before accessing the list of endpoints.
32
33 \sa endpointsChanged
34*/
35
36/*!
37 \qmlproperty int EndpointDiscovery::count
38
39 Current number of endpoints in this element.
40 Before using any data from an endpoint discovery, you should check \l status if the information
41 was successfully retrieved.
42
43 \sa status Status
44*/
45
46/*!
47 \qmlproperty Status EndpointDiscovery::status
48
49 The current status of this element.
50 In case the last retrieval of endpoints was successful, the status
51 is \l {Status::Status}{Status.Good}.
52
53 \code
54 if (endpoints.status.isGood) {
55 // do something
56 } else {
57 // handle error
58 }
59 \endcode
60
61 \sa Status
62*/
63
64/*!
65 \qmlsignal EndpointDiscovery::endpointsChanged()
66
67 Emitted when a retrieval request started, finished or failed.
68 In a called function, you should first check the \l status of the object.
69 If the status is \l {Status::Status}{Status.GoodCompletesAsynchronously},
70 the request is still running.
71 If the status is \l {Status::Status}{Status.Good}, the request has finished
72 and the endpoint descriptions can be read. If the status is not good, an
73 error happened and \l status contains the returned error code.
74
75 \code
76 onEndpointsChanged: {
77 if (endpoints.status.isGood) {
78 if (endpoints.status.status == QtOpcua.Status.GoodCompletesAsynchronusly)
79 return; // wait until finished
80 if (endpoints.count > 0) {
81 var endpointUrl = endpoints.at(0).endpointUrl();
82 console.log(endpointUrl);
83 }
84 } else {
85 // handle error
86 }
87 }
88 \endcode
89
90 \sa status count at Status EndpointDescription
91*/
92
93/*!
94 \qmlproperty Connection EndpointDiscovery::connection
95
96 The connection to be used for requesting information.
97
98 If this property is not set, the default connection will be used, if any.
99
100 \sa Connection, Connection::defaultConnection
101*/
102
103OpcUaEndpointDiscovery::OpcUaEndpointDiscovery(QObject *parent)
104 : QObject(parent)
105{
106 connect(sender: this, signal: &OpcUaEndpointDiscovery::serverUrlChanged, context: this, slot: &OpcUaEndpointDiscovery::startRequestEndpoints);
107 connect(sender: this, signal: &OpcUaEndpointDiscovery::connectionChanged, context: this, slot: &OpcUaEndpointDiscovery::startRequestEndpoints);
108}
109
110OpcUaEndpointDiscovery::~OpcUaEndpointDiscovery() = default;
111
112const QString &OpcUaEndpointDiscovery::serverUrl() const
113{
114 return m_serverUrl;
115}
116
117void OpcUaEndpointDiscovery::setServerUrl(const QString &serverUrl)
118{
119 if (serverUrl == m_serverUrl)
120 return;
121
122 m_serverUrl = serverUrl;
123 emit serverUrlChanged(serverUrl: m_serverUrl);
124}
125
126int OpcUaEndpointDiscovery::count() const
127{
128 return m_endpoints.size();
129}
130
131/*!
132 \qmlmethod EndpointDescription EndpointDiscovery::at(index)
133
134 Returns the endpoint description at given \a index.
135 In case there are no endpoints available or the index is invalid, an invalid
136 endpoint description is returned.
137 Before using any data from this, you should check \l status if retrieval of the
138 information was successful.
139
140 \code
141 if (endpoints.status.isGood) {
142 if (endpoints.count > 0)
143 var endpointUrl = endpoints.at(0).endpointUrl();
144 console.log(endpointUrl);
145 } else {
146 // handle error
147 }
148 \endcode
149
150 \sa count status EndpointDescription
151*/
152
153QOpcUaEndpointDescription OpcUaEndpointDiscovery::at(int row) const
154{
155 if (row >= m_endpoints.size())
156 return QOpcUaEndpointDescription();
157 return m_endpoints.at(i: row);
158}
159
160const OpcUaStatus &OpcUaEndpointDiscovery::status() const
161{
162 return m_status;
163}
164
165void OpcUaEndpointDiscovery::connectSignals()
166{
167 auto conn = connection();
168
169 if (!conn || !conn->m_client)
170 return;
171 connect(sender: conn->m_client, signal: &QOpcUaClient::endpointsRequestFinished, context: this, slot: &OpcUaEndpointDiscovery::handleEndpoints, type: Qt::UniqueConnection);
172}
173
174void OpcUaEndpointDiscovery::handleEndpoints(const QList<QOpcUaEndpointDescription> &endpoints, QOpcUa::UaStatusCode statusCode, const QUrl &requestUrl)
175{
176 if (requestUrl != m_serverUrl)
177 return; // response is not for last request
178
179 m_status = OpcUaStatus(statusCode);
180
181 if (m_status.isBad()) {
182 emit statusChanged();
183 return;
184 }
185
186 m_endpoints = endpoints;
187 emit endpointsChanged();
188 emit countChanged();
189 emit statusChanged();
190}
191
192void OpcUaEndpointDiscovery::startRequestEndpoints()
193{
194 if (!m_componentCompleted)
195 return;
196
197 if (m_serverUrl.isEmpty())
198 return;
199
200 m_endpoints.clear();
201
202 if (!m_connection) {
203 // If there is not connection set, try the default connection
204 // Any connection change will restart this function
205 connection();
206 return;
207 }
208
209 auto conn = connection();
210
211 if (!conn || !conn->m_client) {
212 m_status = OpcUaStatus(QOpcUa::BadNotConnected);
213 } else if (m_serverUrl.isEmpty()) {
214 m_status = OpcUaStatus(QOpcUa::BadInvalidArgument);
215 } else {
216 m_status = OpcUaStatus(QOpcUa::GoodCompletesAsynchronously);
217 conn->m_client->requestEndpoints(url: m_serverUrl);
218 }
219
220 emit endpointsChanged();
221 emit statusChanged();
222}
223
224void OpcUaEndpointDiscovery::setConnection(OpcUaConnection *connection)
225{
226 if (connection == m_connection || !connection)
227 return;
228
229 if (m_connection)
230 disconnect(sender: m_connection, signal: &OpcUaConnection::backendChanged, receiver: this, slot: &OpcUaEndpointDiscovery::connectSignals);
231
232 m_connection = connection;
233
234 connect(sender: m_connection, signal: &OpcUaConnection::backendChanged, context: this, slot: &OpcUaEndpointDiscovery::connectSignals, type: Qt::UniqueConnection);
235 connectSignals();
236 emit connectionChanged(connection);
237}
238
239OpcUaConnection *OpcUaEndpointDiscovery::connection()
240{
241 if (!m_connection)
242 setConnection(OpcUaConnection::defaultConnection());
243
244 return m_connection;
245}
246
247void OpcUaEndpointDiscovery::classBegin()
248{
249}
250
251void OpcUaEndpointDiscovery::componentComplete()
252{
253 m_componentCompleted = true;
254 startRequestEndpoints();
255}
256
257QT_END_NAMESPACE
258

source code of qtopcua/src/declarative_opcua/opcuaendpointdiscovery.cpp