1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the examples of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
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 https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include "satellitemodel.h" |
52 | #include <QTimer> |
53 | #include <QDebug> |
54 | |
55 | SatelliteModel::SatelliteModel(QObject *parent) : |
56 | QAbstractListModel(parent), source(0), m_componentCompleted(false), m_running(false), |
57 | m_runningRequested(false), demo(false), isSingle(false), singleRequestServed(false) |
58 | { |
59 | source = QGeoSatelliteInfoSource::createDefaultSource(parent: this); |
60 | if (!demo && !source) { |
61 | qWarning() << "No satellite data source found. Changing to demo mode." ; |
62 | demo = true; |
63 | } |
64 | if (!demo) { |
65 | source->setUpdateInterval(3000); |
66 | connect(sender: source, SIGNAL(satellitesInViewUpdated(QList<QGeoSatelliteInfo>)), |
67 | receiver: this, SLOT(satellitesInViewUpdated(QList<QGeoSatelliteInfo>))); |
68 | connect(sender: source, SIGNAL(satellitesInUseUpdated(QList<QGeoSatelliteInfo>)), |
69 | receiver: this, SLOT(satellitesInUseUpdated(QList<QGeoSatelliteInfo>))); |
70 | connect(sender: source, SIGNAL(error(QGeoSatelliteInfoSource::Error)), |
71 | receiver: this, SLOT(error(QGeoSatelliteInfoSource::Error))); |
72 | } |
73 | |
74 | if (demo) { |
75 | timer = new QTimer(this); |
76 | connect(sender: timer, SIGNAL(timeout()), receiver: this, SLOT(updateDemoData())); |
77 | timer->start(msec: 3000); |
78 | } |
79 | } |
80 | |
81 | int SatelliteModel::rowCount(const QModelIndex &parent) const |
82 | { |
83 | Q_UNUSED(parent); |
84 | if (!source && !demo) |
85 | return 0; |
86 | |
87 | return knownSatellites.count(); |
88 | } |
89 | |
90 | QVariant SatelliteModel::data(const QModelIndex &index, int role) const |
91 | { |
92 | if (!demo && !source) |
93 | return QVariant(); |
94 | |
95 | if (!index.isValid() || index.row() < 0) |
96 | return QVariant(); |
97 | |
98 | if (index.row() >= knownSatellites.count()) { |
99 | qWarning() << "SatelliteModel: Index out of bound" ; |
100 | return QVariant(); |
101 | } |
102 | |
103 | const QGeoSatelliteInfo &info = knownSatellites.at(i: index.row()); |
104 | switch (role) { |
105 | case IdentifierRole: |
106 | return info.satelliteIdentifier(); |
107 | case InUseRole: |
108 | return satellitesInUse.contains(value: info.satelliteIdentifier()); |
109 | case SignalStrengthRole: |
110 | return info.signalStrength(); |
111 | case ElevationRole: |
112 | if (!info.hasAttribute(attribute: QGeoSatelliteInfo::Elevation)) |
113 | return QVariant(); |
114 | return info.attribute(attribute: QGeoSatelliteInfo::Elevation); |
115 | case AzimuthRole: |
116 | if (!info.hasAttribute(attribute: QGeoSatelliteInfo::Azimuth)) |
117 | return QVariant(); |
118 | return info.attribute(attribute: QGeoSatelliteInfo::Azimuth); |
119 | default: |
120 | break; |
121 | |
122 | } |
123 | |
124 | return QVariant(); |
125 | } |
126 | |
127 | QHash<int, QByteArray> SatelliteModel::roleNames() const |
128 | { |
129 | QHash<int, QByteArray> roleNames; |
130 | roleNames.insert(akey: IdentifierRole, avalue: "satelliteIdentifier" ); |
131 | roleNames.insert(akey: InUseRole, avalue: "isInUse" ); |
132 | roleNames.insert(akey: SignalStrengthRole, avalue: "signalStrength" ); |
133 | roleNames.insert(akey: ElevationRole, avalue: "elevation" ); |
134 | roleNames.insert(akey: AzimuthRole, avalue: "azimuth" ); |
135 | return roleNames; |
136 | } |
137 | |
138 | void SatelliteModel::componentComplete() |
139 | { |
140 | m_componentCompleted = true; |
141 | if (m_runningRequested) |
142 | setRunning(true); |
143 | } |
144 | |
145 | bool SatelliteModel::running() const |
146 | { |
147 | return m_running; |
148 | } |
149 | |
150 | bool SatelliteModel::isSingleRequest() const |
151 | { |
152 | return isSingle; |
153 | } |
154 | |
155 | void SatelliteModel::setSingleRequest(bool single) |
156 | { |
157 | if (running()) { |
158 | qWarning() << "Cannot change single request mode while running" ; |
159 | return; |
160 | } |
161 | |
162 | if (single != isSingle) { //flag changed |
163 | isSingle = single; |
164 | emit singleRequestChanged(); |
165 | } |
166 | } |
167 | |
168 | void SatelliteModel::setRunning(bool isActive) |
169 | { |
170 | if (!source && !demo) |
171 | return; |
172 | |
173 | if (!m_componentCompleted) { |
174 | m_runningRequested = isActive; |
175 | return; |
176 | } |
177 | |
178 | if (m_running == isActive) |
179 | return; |
180 | |
181 | m_running = isActive; |
182 | |
183 | if (m_running) { |
184 | clearModel(); |
185 | if (demo) |
186 | timer->start(msec: 2000); |
187 | else if (isSingleRequest()) |
188 | source->requestUpdate(timeout: 10000); |
189 | else |
190 | source->startUpdates(); |
191 | |
192 | if (demo) |
193 | singleRequestServed = false; |
194 | } else { |
195 | if (demo) |
196 | timer->stop(); |
197 | else if (!isSingleRequest()) |
198 | source->stopUpdates(); |
199 | } |
200 | |
201 | |
202 | Q_EMIT runningChanged(); |
203 | } |
204 | |
205 | int SatelliteModel::entryCount() const |
206 | { |
207 | return knownSatellites.count(); |
208 | } |
209 | |
210 | bool SatelliteModel::canProvideSatelliteInfo() const |
211 | { |
212 | return !demo; |
213 | } |
214 | |
215 | void SatelliteModel::clearModel() |
216 | { |
217 | beginResetModel(); |
218 | knownSatelliteIds.clear(); |
219 | knownSatellites.clear(); |
220 | satellitesInUse.clear(); |
221 | endResetModel(); |
222 | } |
223 | |
224 | void SatelliteModel::updateDemoData() |
225 | { |
226 | static bool flag = true; |
227 | QList<QGeoSatelliteInfo> satellites; |
228 | if (flag) { |
229 | for (int i = 0; i<5; i++) { |
230 | QGeoSatelliteInfo info; |
231 | info.setSatelliteIdentifier(i); |
232 | info.setSignalStrength(20 + 20*i); |
233 | satellites.append(t: info); |
234 | } |
235 | } else { |
236 | for (int i = 0; i<9; i++) { |
237 | QGeoSatelliteInfo info; |
238 | info.setSatelliteIdentifier(i*2); |
239 | info.setSignalStrength(20 + 10*i); |
240 | satellites.append(t: info); |
241 | } |
242 | } |
243 | |
244 | |
245 | satellitesInViewUpdated(infos: satellites); |
246 | flag ? satellitesInUseUpdated(infos: QList<QGeoSatelliteInfo>() << satellites.at(i: 2)) |
247 | : satellitesInUseUpdated(infos: QList<QGeoSatelliteInfo>() << satellites.at(i: 3)); |
248 | flag = !flag; |
249 | |
250 | emit errorFound(code: flag); |
251 | |
252 | if (isSingleRequest() && !singleRequestServed) { |
253 | singleRequestServed = true; |
254 | setRunning(false); |
255 | } |
256 | } |
257 | |
258 | void SatelliteModel::error(QGeoSatelliteInfoSource::Error error) |
259 | { |
260 | emit errorFound(code: (int)error); |
261 | } |
262 | |
263 | QT_BEGIN_NAMESPACE |
264 | inline bool operator<(const QGeoSatelliteInfo& a, const QGeoSatelliteInfo& b) |
265 | { |
266 | return a.satelliteIdentifier() < b.satelliteIdentifier(); |
267 | } |
268 | QT_END_NAMESPACE |
269 | |
270 | void SatelliteModel::satellitesInViewUpdated(const QList<QGeoSatelliteInfo> &infos) |
271 | { |
272 | if (!running()) |
273 | return; |
274 | |
275 | int oldEntryCount = knownSatellites.count(); |
276 | |
277 | |
278 | QSet<int> satelliteIdsInUpdate; |
279 | foreach (const QGeoSatelliteInfo &info, infos) |
280 | satelliteIdsInUpdate.insert(value: info.satelliteIdentifier()); |
281 | |
282 | QSet<int> toBeRemoved = knownSatelliteIds - satelliteIdsInUpdate; |
283 | |
284 | //We reset the model as in reality just about all entry values change |
285 | //and there are generally a lot of inserts and removals each time |
286 | //Hence we don't bother with complex model update logic beyond resetModel() |
287 | beginResetModel(); |
288 | |
289 | knownSatellites = infos; |
290 | |
291 | //sort them for presentation purposes |
292 | std::sort(first: knownSatellites.begin(), last: knownSatellites.end()); |
293 | |
294 | //remove old "InUse" data |
295 | //new satellites are by default not in "InUse" |
296 | //existing satellites keep their "inUse" state |
297 | satellitesInUse -= toBeRemoved; |
298 | |
299 | knownSatelliteIds = satelliteIdsInUpdate; |
300 | endResetModel(); |
301 | |
302 | if (oldEntryCount != knownSatellites.count()) |
303 | emit entryCountChanged(); |
304 | } |
305 | |
306 | void SatelliteModel::satellitesInUseUpdated(const QList<QGeoSatelliteInfo> &infos) |
307 | { |
308 | if (!running()) |
309 | return; |
310 | |
311 | beginResetModel(); |
312 | |
313 | satellitesInUse.clear(); |
314 | foreach (const QGeoSatelliteInfo &info, infos) |
315 | satellitesInUse.insert(value: info.satelliteIdentifier()); |
316 | |
317 | endResetModel(); |
318 | } |
319 | |
320 | |