QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGCTileCacheWorker.cpp
Go to the documentation of this file.
3
4#include <QtCore/QCoreApplication>
5#include <QtCore/QSettings>
6
7#include "QGCCacheTile.h"
8#include "QGCCachedTileSet.h"
10#include "QGCMapTasks.h"
11#include "QGCMapUrlEngine.h"
12
13QGC_LOGGING_CATEGORY(QGCTileCacheWorkerLog, "QtLocationPlugin.QGCTileCacheWorker")
14
16 : QThread(parent)
17{
18 qCDebug(QGCTileCacheWorkerLog) << this;
19}
20
22{
23 stop();
24 wait();
25 qCDebug(QGCTileCacheWorkerLog) << this;
26}
27
29{
30 _stopRequested = true;
31 QMutexLocker lock(&_taskQueueMutex);
32 qDeleteAll(_taskQueue);
33 _taskQueue.clear();
34 lock.unlock();
35
36 if (isRunning()) {
37 _waitc.wakeAll();
38 }
39}
40
42{
43 if (!_dbValid && !isRunning() && (task->type() != QGCMapTask::TaskType::taskInit)) {
44 task->setError(tr("Database Not Initialized"));
45 task->deleteLater();
46 return false;
47 }
48
49 QMutexLocker lock(&_taskQueueMutex);
50 _taskQueue.enqueue(task);
51 lock.unlock();
52
53 if (isRunning()) {
54 _waitc.wakeAll();
55 } else {
56 start(QThread::NormalPriority);
57 }
58
59 return true;
60}
61
63{
64 _stopRequested = false;
65 _database = std::make_unique<QGCTileCacheDatabase>(_databasePath);
66
67 if (!_database->init()) {
68 qCWarning(QGCTileCacheWorkerLog) << "Failed To Init Database";
69 _database.reset();
70
71 QMutexLocker lock(&_taskQueueMutex);
72 for (QGCMapTask *orphan : _taskQueue) {
73 orphan->setError(tr("Database Init Failed"));
74 orphan->deleteLater();
75 }
76 _taskQueue.clear();
77 return;
78 }
79
80 if (_database->isValid()) {
81 if (_database->connectDB()) {
82 _database->deleteBingNoTileTiles();
83 }
84 }
85
86 _dbValid = _database->isValid();
87
88 _updateTimer.start();
89
90 QMutexLocker lock(&_taskQueueMutex);
91 while (!_stopRequested) {
92 if (!_taskQueue.isEmpty()) {
93 QGCMapTask* const task = _taskQueue.dequeue();
94 lock.unlock();
95 _runTask(task);
96 lock.relock();
97 task->deleteLater();
98
99 const qsizetype count = _taskQueue.count();
100 if (count > 100) {
101 _updateTimeout = kLongTimeoutMs;
102 } else if (count < 25) {
103 _updateTimeout = kShortTimeoutMs;
104 }
105
106 if ((count == 0) || _updateTimer.hasExpired(_updateTimeout)) {
107 if (_database && _database->isValid()) {
108 lock.unlock();
109 _emitTotals();
110 lock.relock();
111 }
112 }
113 } else {
114 (void) _waitc.wait(lock.mutex(), 5000);
115 }
116 }
117
118 for (QGCMapTask *orphan : _taskQueue) {
119 orphan->setError(tr("Worker shutting down"));
120 orphan->deleteLater();
121 }
122 _taskQueue.clear();
123 lock.unlock();
124
125 _dbValid = false;
126 if (_database) {
127 _database->disconnectDB();
128 _database.reset();
129 }
130}
131
132void QGCCacheWorker::_runTask(QGCMapTask *task)
133{
134 switch (task->type()) {
136 break;
138 _saveTile(task);
139 break;
141 _getTile(task);
142 break;
144 _getTileSets(task);
145 break;
147 _createTileSet(task);
148 break;
150 _getTileDownloadList(task);
151 break;
153 _updateTileDownloadState(task);
154 break;
156 _deleteTileSet(task);
157 break;
159 _renameTileSet(task);
160 break;
162 _pruneCache(task);
163 break;
165 _resetCacheDatabase(task);
166 break;
168 _exportSets(task);
169 break;
171 _importSets(task);
172 break;
173 default:
174 qCWarning(QGCTileCacheWorkerLog) << "given unhandled task type" << task->type();
175 break;
176 }
177}
178
179bool QGCCacheWorker::_testTask(QGCMapTask *mtask)
180{
181 if (!_database || !_database->isValid()) {
182 mtask->setError("No Cache Database");
183 return false;
184 }
185
186 return true;
187}
188
189void QGCCacheWorker::_emitTotals()
190{
191 TotalsResult t = _database->computeTotals();
193 _updateTimer.restart();
194}
195
196void QGCCacheWorker::_saveTile(QGCMapTask *mtask)
197{
198 if (!_testTask(mtask)) {
199 return;
200 }
201
202 QGCSaveTileTask *task = static_cast<QGCSaveTileTask*>(mtask);
203 if (!_database->saveTile(task->tile()->hash, task->tile()->format,
204 task->tile()->img, task->tile()->type, task->tile()->tileSet)) {
205 mtask->setError("Error saving tile to cache");
206 }
207}
208
209void QGCCacheWorker::_getTile(QGCMapTask *mtask)
210{
211 if (!_testTask(mtask)) {
212 return;
213 }
214
215 QGCFetchTileTask *task = static_cast<QGCFetchTileTask*>(mtask);
216 auto tile = _database->getTile(task->hash());
217 if (tile) {
218 task->setTileFetched(tile.release());
219 } else {
220 task->setError("Tile not in cache database");
221 }
222}
223
224void QGCCacheWorker::_getTileSets(QGCMapTask *mtask)
225{
226 if (!_testTask(mtask)) {
227 return;
228 }
229
230 QGCFetchTileSetTask *task = static_cast<QGCFetchTileSetTask*>(mtask);
231 const QList<TileSetRecord> records = _database->getTileSets();
232
233 for (const auto &rec : records) {
234 QGCCachedTileSet *set = new QGCCachedTileSet(rec.name);
235 set->blockSignals(true);
236
237 set->setId(rec.setID);
238 set->setMapTypeStr(rec.mapTypeStr);
239 set->setTopleftLat(rec.topleftLat);
240 set->setTopleftLon(rec.topleftLon);
241 set->setBottomRightLat(rec.bottomRightLat);
242 set->setBottomRightLon(rec.bottomRightLon);
243 set->setMinZoom(rec.minZoom);
244 set->setMaxZoom(rec.maxZoom);
246 set->setTotalTileCount(rec.numTiles);
247 set->setDefaultSet(rec.defaultSet);
248 set->setCreationDate(QDateTime::fromSecsSinceEpoch(rec.date));
249
250 const SetTotalsResult totals = _database->computeSetTotals(rec.setID, rec.defaultSet, rec.numTiles, set->type());
252 set->setSavedTileSize(totals.savedTileSize);
253 set->setTotalTileSize(totals.totalTileSize);
256
257 set->blockSignals(false);
258 (void) set->moveToThread(QCoreApplication::instance()->thread());
259 task->setTileSetFetched(set);
260 }
261}
262
263void QGCCacheWorker::_createTileSet(QGCMapTask *mtask)
264{
265 if (!_testTask(mtask)) {
266 return;
267 }
268
269 QGCCreateTileSetTask *task = static_cast<QGCCreateTileSetTask*>(mtask);
270 const auto setID = _database->createTileSet(
271 task->tileSet()->name(), task->tileSet()->mapTypeStr(),
272 task->tileSet()->topleftLat(), task->tileSet()->topleftLon(),
273 task->tileSet()->bottomRightLat(), task->tileSet()->bottomRightLon(),
274 task->tileSet()->minZoom(), task->tileSet()->maxZoom(),
275 task->tileSet()->type(), task->tileSet()->totalTileCount());
276
277 if (!setID.has_value()) {
278 mtask->setError("Error saving tile set");
279 return;
280 }
281
282 task->tileSet()->blockSignals(true);
283 task->tileSet()->setId(setID.value());
284
285 const SetTotalsResult totals = _database->computeSetTotals(
286 setID.value(), task->tileSet()->defaultSet(),
287 task->tileSet()->totalTileCount(), task->tileSet()->type());
289 task->tileSet()->setSavedTileSize(totals.savedTileSize);
290 task->tileSet()->setTotalTileSize(totals.totalTileSize);
293 task->tileSet()->blockSignals(false);
294
295 task->setTileSetSaved();
296}
297
298void QGCCacheWorker::_getTileDownloadList(QGCMapTask *mtask)
299{
300 if (!_testTask(mtask)) {
301 return;
302 }
303
304 QGCGetTileDownloadListTask *task = static_cast<QGCGetTileDownloadListTask*>(mtask);
305 const QList<QGCTile> tileValues = _database->getTileDownloadList(task->setID(), task->count());
306 QQueue<QGCTile*> tiles;
307 for (const auto &t : tileValues) {
308 tiles.enqueue(new QGCTile(t));
309 }
310 task->setTileListFetched(tiles);
311}
312
313void QGCCacheWorker::_updateTileDownloadState(QGCMapTask *mtask)
314{
315 if (!_testTask(mtask)) {
316 return;
317 }
318
320 bool ok;
321 if (task->hash() == QStringLiteral("*")) {
322 ok = _database->updateAllTileDownloadStates(task->setID(), static_cast<int>(task->state()));
323 } else {
324 ok = _database->updateTileDownloadState(task->setID(), static_cast<int>(task->state()), task->hash());
325 }
326 if (!ok) {
327 mtask->setError("Error updating tile download state");
328 }
329}
330
331void QGCCacheWorker::_pruneCache(QGCMapTask *mtask)
332{
333 if (!_testTask(mtask)) {
334 return;
335 }
336
337 QGCPruneCacheTask *task = static_cast<QGCPruneCacheTask*>(mtask);
338 if (!_database->pruneCache(task->amount())) {
339 mtask->setError("Error pruning cache");
340 return;
341 }
342 task->setPruned();
343}
344
345void QGCCacheWorker::_deleteTileSet(QGCMapTask *mtask)
346{
347 if (!_testTask(mtask)) {
348 return;
349 }
350
351 QGCDeleteTileSetTask *task = static_cast<QGCDeleteTileSetTask*>(mtask);
352 if (!_database->deleteTileSet(task->setID())) {
353 mtask->setError("Error deleting tile set");
354 return;
355 }
356 _emitTotals();
357 task->setTileSetDeleted();
358}
359
360void QGCCacheWorker::_renameTileSet(QGCMapTask *mtask)
361{
362 if (!_testTask(mtask)) {
363 return;
364 }
365
366 QGCRenameTileSetTask *task = static_cast<QGCRenameTileSetTask*>(mtask);
367 if (!_database->renameTileSet(task->setID(), task->newName())) {
368 task->setError("Error renaming tile set");
369 }
370}
371
372void QGCCacheWorker::_resetCacheDatabase(QGCMapTask *mtask)
373{
374 if (!_testTask(mtask)) {
375 return;
376 }
377
378 QGCResetTask *task = static_cast<QGCResetTask*>(mtask);
379 if (!_database->resetDatabase()) {
380 mtask->setError("Error resetting cache database");
381 return;
382 }
383 _dbValid = _database->isValid();
384 task->setResetCompleted();
385}
386
387void QGCCacheWorker::_importSets(QGCMapTask *mtask)
388{
389 if (!_testTask(mtask)) {
390 return;
391 }
392
393 QGCImportTileTask *task = static_cast<QGCImportTileTask*>(mtask);
394 auto progress = [task](int pct) { task->setProgress(pct); };
395
396 DatabaseResult result;
397 if (task->replace()) {
398 result = _database->importSetsReplace(task->path(), progress);
399 } else {
400 result = _database->importSetsMerge(task->path(), progress);
401 }
402
403 _dbValid = _database->isValid();
404
405 if (!result.success) {
406 task->setError(result.errorString);
407 return;
408 }
409
410 if (task->replace() && _database->isValid()) {
411 QSettings settings;
412 settings.remove(QLatin1String(QGCTileCacheDatabase::kBingNoTileDoneKey));
413 _database->deleteBingNoTileTiles();
414 }
415
416 task->setImportCompleted();
417}
418
419void QGCCacheWorker::_exportSets(QGCMapTask *mtask)
420{
421 if (!_testTask(mtask)) {
422 return;
423 }
424
425 QGCExportTileTask *task = static_cast<QGCExportTileTask*>(mtask);
426
427 auto progress = [task](int pct) { task->setProgress(pct); };
428 DatabaseResult result = _database->exportSets(task->sets(), task->path(), progress);
429
430 if (!result.success) {
431 task->setError(result.errorString);
432 return;
433 }
434
435 task->setExportCompleted();
436}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool enqueueTask(QGCMapTask *task)
void updateTotals(quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize)
void setTotalTileCount(quint32 num)
double bottomRightLon() const
double topleftLat() const
void setUniqueTileCount(quint32 num)
void setTotalTileSize(quint64 size)
void setSavedTileCount(quint32 num)
void setMinZoom(int zoom)
void setSavedTileSize(quint64 size)
void setMaxZoom(int zoom)
void setTopleftLat(double lat)
const QString & type() const
double topleftLon() const
void setDefaultSet(bool def)
quint32 totalTileCount() const
const QString & name() const
void setBottomRightLon(double lon)
void setType(const QString &type)
void setId(quint64 id)
const QString & mapTypeStr() const
void setBottomRightLat(double lat)
bool defaultSet() const
void setCreationDate(const QDateTime &date)
void setMapTypeStr(const QString &typeStr)
double bottomRightLat() const
void setUniqueTileSize(quint64 size)
void setTopleftLon(double lon)
QGCCachedTileSet * tileSet()
Definition QGCMapTasks.h:49
quint64 setID() const
void setProgress(int percentage)
QString path() const
void setExportCompleted()
const QList< TileSetRecord > & sets() const
void setTileSetFetched(QGCCachedTileSet *tileSet)
Definition QGCMapTasks.h:26
QString hash() const
Definition QGCMapTasks.h:83
void setTileFetched(QGCCacheTile *tile)
Definition QGCMapTasks.h:78
void setTileListFetched(const QQueue< QGCTile * > &tiles)
void setImportCompleted()
bool replace() const
void setProgress(int percentage)
QString path() const
void setError(const QString &errorString=QString())
TaskType type() const
quint64 amount() const
QString newName() const
quint64 setID() const
void setResetCompleted()
const QGCCacheTile * tile() const
static constexpr const char * kBingNoTileDoneKey
QGCTile::TileState state() const
static QString getProviderTypeFromQtMapId(int qtMapId)
quint64 tileSet
QByteArray img
QString format