1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the QtSystems module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL21$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 or version 3 as published by the Free |
20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and |
21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the |
22 | ** following information to ensure the GNU Lesser General Public License |
23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and |
24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
25 | ** |
26 | ** As a special exception, The Qt Company gives you certain additional |
27 | ** rights. These rights are described in The Qt Company LGPL Exception |
28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
29 | ** |
30 | ** $QT_END_LICENSE$ |
31 | ** |
32 | ****************************************************************************/ |
33 | |
34 | #include <qvaluespacepublisher.h> |
35 | |
36 | #include "qvaluespace_p.h" |
37 | #include "qvaluespacemanager_p.h" |
38 | |
39 | #include <QtCore/qmetaobject.h> |
40 | |
41 | QT_BEGIN_NAMESPACE |
42 | |
43 | /*! |
44 | \class QValueSpacePublisher |
45 | \brief The QValueSpacePublisher class allows applications to publish values in the Value Space. |
46 | \inmodule QtPublishSubscribe |
47 | \ingroup publishsubscribe |
48 | |
49 | When multiple Value Space layers are available QValueSpacePublisher only publishes values to |
50 | the layer with the highest priority. The layers that QValueSpacePublisher can access can be |
51 | limited by specifying either a \l {QValueSpace::LayerOption}{filter} or a QUuid at construction |
52 | time. |
53 | |
54 | The lifetime of values published in the Value Space is specified by the particular Value Space |
55 | layer that the value is published in. For details on the different behaviors see |
56 | QValueSpace::LayerOption. |
57 | |
58 | Once a value is published all applications in the system will have read access to it via the |
59 | QValueSpaceSubscriber class. |
60 | |
61 | Although, logically, the Value Space is a simple collection of |
62 | hierarchical paths, these paths can conceptually be visualized as a set of |
63 | objects with attributes. For example, rather than viewing the following list |
64 | as 12 distinct Value Space paths: |
65 | |
66 | \code |
67 | /Device/Network/Interfaces/eth0/Name |
68 | /Device/Network/Interfaces/eth0/Type |
69 | /Device/Network/Interfaces/eth0/Status |
70 | /Device/Network/Interfaces/eth0/BytesSent |
71 | /Device/Network/Interfaces/eth0/BytesReceived |
72 | /Device/Network/Interfaces/eth0/Time |
73 | /Device/Network/Interfaces/ppp0/Name |
74 | /Device/Network/Interfaces/ppp0/Type |
75 | /Device/Network/Interfaces/ppp0/Status |
76 | /Device/Network/Interfaces/ppp0/BytesSent |
77 | /Device/Network/Interfaces/ppp0/BytesReceived |
78 | /Device/Network/Interfaces/ppp0/Time |
79 | \endcode |
80 | |
81 | it can be thought of as describing two Value Space objects, |
82 | \c { { /Device/Network/Interfaces/eth0, /Device/Network/Interfaces/ppp0 } }, |
83 | each with the six attributes \c { {Name, Type, Status, BytesSent, |
84 | BytesReceived, Time} }. The QValueSpacePublisher class encapsulates this |
85 | abstraction. |
86 | |
87 | For performance reasons the setting of and removing of attributes is buffered |
88 | internally by the QValueSpacePublisher and applied as a batch sometime later. |
89 | Normally this occurs the next time the application enters the Qt event loop, |
90 | but this behavior should not be relied upon. If an application must |
91 | synchronize with others, the QValueSpacePublisher::sync() and QValueSpacePublisher::syncAll() |
92 | functions can be used to force the application of changes. This call is generally unnecessary, |
93 | and should be used sparingly to prevent unnecessary load on the system. |
94 | |
95 | \sa QValueSpaceSubscriber |
96 | */ |
97 | |
98 | /*! |
99 | \fn void QValueSpacePublisher::interestChanged(const QString &attribute, bool interested) |
100 | |
101 | Signal that is emitted when interest in \a attribute changes. If \a interested is true at |
102 | least on QValueSpaceSubscriber is interested in the value of \a attribute. |
103 | */ |
104 | |
105 | class QValueSpacePublisherPrivate |
106 | { |
107 | public: |
108 | QValueSpacePublisherPrivate(const QString &path, QValueSpace::LayerOptions filter = QValueSpace::UnspecifiedLayer); |
109 | QValueSpacePublisherPrivate(const QString &path, const QUuid &uuid); |
110 | |
111 | QString path; |
112 | QAbstractValueSpaceLayer *layer; |
113 | QAbstractValueSpaceLayer::Handle handle; |
114 | |
115 | bool hasSet; |
116 | bool hasWatch; |
117 | }; |
118 | |
119 | QValueSpacePublisherPrivate::QValueSpacePublisherPrivate(const QString &_path, QValueSpace::LayerOptions filter) |
120 | : layer(0) |
121 | , handle(QAbstractValueSpaceLayer::InvalidHandle) |
122 | , hasSet(false) |
123 | , hasWatch(false) |
124 | { |
125 | path = qCanonicalPath(path: _path); |
126 | |
127 | // check filter for mutually exclusive options |
128 | if ((filter & QValueSpace::PermanentLayer && filter & QValueSpace::TransientLayer) |
129 | || (filter & QValueSpace::WritableLayer && filter & QValueSpace::ReadOnlyLayer)) { |
130 | return; |
131 | } |
132 | |
133 | QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers(); |
134 | |
135 | for (int ii = 0; ii < layers.count(); ++ii) { |
136 | if ((layers.at(i: ii)->layerOptions() & filter) == filter) { |
137 | QAbstractValueSpaceLayer::Handle h = |
138 | layers.at(i: ii)->item(parent: QAbstractValueSpaceLayer::InvalidHandle, subPath: path); |
139 | |
140 | if (h != QAbstractValueSpaceLayer::InvalidHandle) { |
141 | layer = layers.at(i: ii); |
142 | handle = h; |
143 | break; |
144 | } |
145 | } |
146 | } |
147 | } |
148 | |
149 | QValueSpacePublisherPrivate::QValueSpacePublisherPrivate(const QString &_path, const QUuid &uuid) |
150 | : layer(0) |
151 | , handle(QAbstractValueSpaceLayer::InvalidHandle) |
152 | , hasSet(false) |
153 | , hasWatch(false) |
154 | { |
155 | path = qCanonicalPath(path: _path); |
156 | |
157 | QList<QAbstractValueSpaceLayer *> layers = QValueSpaceManager::instance()->getLayers(); |
158 | |
159 | for (int ii = 0; ii < layers.count(); ++ii) { |
160 | if (layers.at(i: ii)->id() == uuid) { |
161 | layer = layers.at(i: ii); |
162 | handle = layer->item(parent: QAbstractValueSpaceLayer::InvalidHandle, subPath: path); |
163 | break; |
164 | } |
165 | } |
166 | } |
167 | |
168 | /*! |
169 | Constructs a QValueSpacePublisher with the specified \a parent that publishes values under |
170 | \a path. |
171 | */ |
172 | QValueSpacePublisher::QValueSpacePublisher(const QString &path, QObject *parent) |
173 | : QObject(parent) |
174 | , d_ptr(new QValueSpacePublisherPrivate(path)) |
175 | { |
176 | } |
177 | |
178 | /*! |
179 | Constructs a QValueSpacePublisher with the specified \a parent that publishes values under |
180 | \a path. The \a filter parameter is used to limit which layer this QValueSpacePublisher will |
181 | access. |
182 | |
183 | The constructed Value Space publisher will access the layer with the highest priority that matches |
184 | \a filter and for which \a path is a valid path. |
185 | |
186 | If no suitable layer is found, the constructed QValueSpacePublisher will be unconnected. |
187 | |
188 | \sa isConnected() |
189 | */ |
190 | QValueSpacePublisher::QValueSpacePublisher(QValueSpace::LayerOptions filter, const QString &path, QObject *parent) |
191 | : QObject(parent) |
192 | , d_ptr(new QValueSpacePublisherPrivate(path, filter)) |
193 | { |
194 | } |
195 | |
196 | /*! |
197 | Constructs a QValueSpacePublisher with the specified \a parent that publishes values under |
198 | \a path. Only the layer identified by \a uuid will be accessed by this publisher. |
199 | |
200 | Use of this constructor is not platform agnostic. If possible use one of the constructors that |
201 | take a QValueSpace::LayerOptions parameter instead. |
202 | |
203 | If a layer with a matching \a uuid is not found, the constructed QValueSpacePublisher will be |
204 | unconnected. |
205 | |
206 | \sa isConnected() |
207 | */ |
208 | QValueSpacePublisher::QValueSpacePublisher(const QUuid &uuid, const QString &path, QObject *parent) |
209 | : QObject(parent) |
210 | , d_ptr(new QValueSpacePublisherPrivate(path, uuid)) |
211 | { |
212 | } |
213 | |
214 | /*! |
215 | Destroys the QValueSpacePublisher. This will remove all values published by this publisher in |
216 | \l {QValueSpace::TransientLayer}{non-permanent} layers. |
217 | */ |
218 | QValueSpacePublisher::~QValueSpacePublisher() |
219 | { |
220 | if (!isConnected()) |
221 | return; |
222 | |
223 | if (d_ptr->hasSet && !(d_ptr->layer->layerOptions() & QValueSpace::PermanentLayer)) |
224 | d_ptr->layer->removeSubTree(creator: this, handle: d_ptr->handle); |
225 | |
226 | if (d_ptr->hasWatch) |
227 | d_ptr->layer->removeWatches(creator: this, parent: d_ptr->handle); |
228 | |
229 | delete d_ptr; |
230 | } |
231 | |
232 | /*! |
233 | Returns the path that this QValueSpacePublisher refers to. |
234 | */ |
235 | QString QValueSpacePublisher::path() const |
236 | { |
237 | return d_ptr->path; |
238 | } |
239 | |
240 | /*! |
241 | Returns true if this QValueSpacePublisher is connected to an available layer; otherwise returns |
242 | false. |
243 | */ |
244 | bool QValueSpacePublisher::isConnected() const |
245 | { |
246 | return (d_ptr->layer && d_ptr->handle != QAbstractValueSpaceLayer::InvalidHandle); |
247 | } |
248 | |
249 | /*! |
250 | Forcibly sync all Value Space publisher using the same layer as this publisher. |
251 | |
252 | For performance reasons attribute changes are batched internally by QValueSpacePublisher |
253 | instances. In cases where the visibility of changes must be synchronized with other processes, |
254 | calling this function will flush these batches. By the time this function returns, all other |
255 | processes in the system will be able to see the attribute changes. |
256 | |
257 | Generally, calling this function is unnecessary. |
258 | */ |
259 | void QValueSpacePublisher::sync() |
260 | { |
261 | if (!isConnected()) { |
262 | qWarning(msg: "sync called on unconnected QValueSpacePublisher." ); |
263 | return; |
264 | } |
265 | |
266 | d_ptr->layer->sync(); |
267 | } |
268 | |
269 | /*! |
270 | Sets the value \a name on the publisher to \a data. If name is empty, this call will set the |
271 | value of this publisher's path. |
272 | |
273 | For example: |
274 | |
275 | \code |
276 | QValueSpacePublisher publisher("/Device"); |
277 | publisher.setValue("State", "Starting"); |
278 | publisher.sync(); |
279 | |
280 | // QValueSpaceSubscriber("/Device/State").value() == QVariant("Starting") |
281 | \endcode |
282 | */ |
283 | void QValueSpacePublisher::setValue(const QString &name, const QVariant &data) |
284 | { |
285 | if (!isConnected()) { |
286 | qWarning(msg: "setValue called on unconnected QValueSpacePublisher." ); |
287 | return; |
288 | } |
289 | |
290 | d_ptr->hasSet = true; |
291 | d_ptr->layer->setValue(creator: this, handle: d_ptr->handle, subPath: qCanonicalPath(path: name), value: data); |
292 | } |
293 | |
294 | /*! |
295 | Removes the value \a name and all sub-attributes from the system. |
296 | |
297 | For example: |
298 | \code |
299 | QValueSpacePublisher publisher("/Device"); |
300 | publisher.setValue("State", "Starting"); |
301 | publisher.setValue("State/Memory", "1000"); |
302 | publisher.sync(); |
303 | // QValueSpaceSubscriber("/Device/State").value() == QVariant("Starting") |
304 | // QValueSpaceSubscriber("/Device/State/Memory").value() == QVariant("1000") |
305 | |
306 | publisher.resetValue("State"); |
307 | publisher.sync(); |
308 | // QValueSpaceSubscriber("/Device/State").value() == QVariant(); |
309 | // QValueSpaceSubscriber("/Device/State/Memory").value() == QVariant(); |
310 | \endcode |
311 | */ |
312 | void QValueSpacePublisher::resetValue(const QString &name) |
313 | { |
314 | if (!isConnected()) { |
315 | qWarning(msg: "resetValue called on unconnected QValueSpacePublisher." ); |
316 | return; |
317 | } |
318 | |
319 | d_ptr->layer->removeValue(creator: this, handle: d_ptr->handle, subPath: qCanonicalPath(path: name)); |
320 | } |
321 | |
322 | /*! |
323 | \reimp |
324 | */ |
325 | void QValueSpacePublisher::connectNotify(const QMetaMethod &signal) |
326 | { |
327 | static const QMetaMethod interestChangedSignal = QMetaMethod::fromSignal(signal: &QValueSpacePublisher::interestChanged); |
328 | if (!d_ptr->hasWatch && isConnected() && signal == interestChangedSignal) { |
329 | d_ptr->layer->addWatch(creator: this, handle: d_ptr->handle); |
330 | d_ptr->hasWatch = true; |
331 | } |
332 | } |
333 | |
334 | QT_END_NAMESPACE |
335 | |