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 | |
7 | QT_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 | |
102 | OpcUaEndpointDiscovery::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 | |
109 | OpcUaEndpointDiscovery::~OpcUaEndpointDiscovery() = default; |
110 | |
111 | const QString &OpcUaEndpointDiscovery::serverUrl() const |
112 | { |
113 | return m_serverUrl; |
114 | } |
115 | |
116 | void 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 | |
125 | int 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 | |
152 | QOpcUaEndpointDescription OpcUaEndpointDiscovery::at(int row) const |
153 | { |
154 | if (row >= m_endpoints.size()) |
155 | return QOpcUaEndpointDescription(); |
156 | return m_endpoints.at(i: row); |
157 | } |
158 | |
159 | const OpcUaStatus &OpcUaEndpointDiscovery::status() const |
160 | { |
161 | return m_status; |
162 | } |
163 | |
164 | void 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 | |
173 | void 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 | |
191 | void 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 | |
223 | void 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 | |
238 | OpcUaConnection *OpcUaEndpointDiscovery::connection() |
239 | { |
240 | if (!m_connection) |
241 | setConnection(OpcUaConnection::defaultConnection()); |
242 | |
243 | return m_connection; |
244 | } |
245 | |
246 | void OpcUaEndpointDiscovery::classBegin() |
247 | { |
248 | } |
249 | |
250 | void OpcUaEndpointDiscovery::componentComplete() |
251 | { |
252 | m_componentCompleted = true; |
253 | startRequestEndpoints(); |
254 | } |
255 | |
256 | QT_END_NAMESPACE |
257 | |