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

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