From f12b69bb8756ab37a8226893170923a5da881d11 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Wed, 3 May 2017 02:22:37 +0200 Subject: [PATCH] Ref T28, background updater: consolidate DB and model data in background * automatically reload DB data * consolidate models with DB data --- src/blackcore/db/backgrounddataupdater.cpp | 206 +++++++++++++++++++++ src/blackcore/db/backgrounddataupdater.h | 84 +++++++++ src/blackgui/settings/guisettings.h | 19 ++ 3 files changed, 309 insertions(+) create mode 100644 src/blackcore/db/backgrounddataupdater.cpp create mode 100644 src/blackcore/db/backgrounddataupdater.h diff --git a/src/blackcore/db/backgrounddataupdater.cpp b/src/blackcore/db/backgrounddataupdater.cpp new file mode 100644 index 000000000..a41c1673f --- /dev/null +++ b/src/blackcore/db/backgrounddataupdater.cpp @@ -0,0 +1,206 @@ +/* Copyright (C) 2017 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "backgrounddataupdater.h" +#include "blackcore/db/databaseutils.h" +#include "blackcore/application.h" +#include "blackcore/webdataservices.h" +#include "blackmisc/simulation/aircraftmodellist.h" +#include "blackmisc/threadutils.h" +#include "blackmisc/eventloop.h" +#include "blackmisc/logmessage.h" + +using namespace BlackMisc; +using namespace BlackMisc::Network; +using namespace BlackMisc::Simulation; +using namespace BlackMisc::Simulation::Data; +using namespace BlackCore; +using namespace BlackCore::Db; + +namespace BlackCore +{ + namespace Db + { + const CLogCategoryList &CBackgroundDataUpdater::getLogCategories() + { + static const BlackMisc::CLogCategoryList cats { BlackMisc::CLogCategory::worker() }; + return cats; + } + + CBackgroundDataUpdater::CBackgroundDataUpdater(QObject *owner) : + CContinuousWorker(owner, "Background data updater") + { + connect(&m_updateTimer, &QTimer::timeout, this, &CBackgroundDataUpdater::doWork); + m_updateTimer.start(60 * 1000); + } + + CBackgroundDataUpdater::~CBackgroundDataUpdater() + { + gracefulShutdown(); + } + + bool CBackgroundDataUpdater::isShuttingDown() const + { + if (!sApp) { return true; } // sApp object is gone, whole system shutdown + if (this->m_shutdown) { return true; } // marked as shutdown + if (this->isAbandoned()) { return true; } // worker abandoned + return false; + } + + bool CBackgroundDataUpdater::isEnabled() const + { + return m_enabled; + } + + void CBackgroundDataUpdater::gracefulShutdown() + { + if (m_shutdown) { return; } + m_shutdown = true; + m_enabled = false; + if (!CThreadUtils::isCurrentThreadObjectThread(this)) + { + this->abandonAndWait(); + } + else + { + // timer needs to be stopped in its own thread + m_updateTimer.stop(); + } + } + + void CBackgroundDataUpdater::startUpdating(int updateTimeSecs) + { + if (!CThreadUtils::isCurrentThreadObjectThread(this)) + { + QTimer::singleShot(0, this, [this, updateTimeSecs] { this->startUpdating(updateTimeSecs); }); + return; + } + + m_enabled = updateTimeSecs > 0; + if (updateTimeSecs < 0) + { + m_enabled = false; + QTimer::singleShot(0, &m_updateTimer, &QTimer::stop); + } + else + { + m_enabled = true; + m_updateTimer.start(1000 * updateTimeSecs); + } + } + + void CBackgroundDataUpdater::doWork() + { + if (!this->entryCheck()) { return; } + m_inWork = true; + + const int cycle = m_cycle; + switch (cycle) + { + case 0: + // normally redundant, will be read in other places as well + // new metadata for next comparison + this->triggerInfoReads(); + break; + case 1: + this->syncDbEntity(CEntityFlags::ModelEntity); + this->syncDbEntity(CEntityFlags::DistributorEntity); + break; + case 2: + this->syncModelOrModelSetCacheWithDbData(m_modelCaches); + break; + case 3: + this->syncModelOrModelSetCacheWithDbData(m_modelSetCaches); + break; + case 4: + this->syncDbEntity(CEntityFlags::AircraftIcaoEntity); + this->syncDbEntity(CEntityFlags::AirlineIcaoEntity); + break; + default: + break; + } + ++m_cycle %= 5; + m_inWork = false; + } + + void CBackgroundDataUpdater::triggerInfoReads() + { + if (!this->entryCheck()) { return; } + sApp->getWebDataServices()->triggerReadOfDbInfoObjects(); + sApp->getWebDataServices()->triggerReadOfSharedInfoObjects(); + } + + void CBackgroundDataUpdater::syncModelOrModelSetCacheWithDbData(Simulation::Data::IMultiSimulatorModelCaches &cache) + { + if (!this->entryCheck()) { return; } + const QDateTime cacheTs = sApp->getWebDataServices()->getCacheTimestamp(CEntityFlags::ModelEntity); + if (!cacheTs.isValid()) { return; } + + QDateTime dbModelsLatestChange = m_dbModelsLatestChange.value(cache.getDescription()); + if (dbModelsLatestChange.isValid() && dbModelsLatestChange <= cacheTs) + { + return; + } + + m_dbModelsLatestChange[cache.getDescription()] = cacheTs; + const CSimulatorInfo sims = cache.simulatorsWithInitializedCache(); // sims ever used + if (sims.isNoSimulator()) { return; } + + const CAircraftModelList dbModels = sApp->getWebDataServices()->getModels(); + if (dbModels.isEmpty()) { return; } + const QSet simSet = sims.asSingleSimulatorSet(); + for (const CSimulatorInfo &singleInfo : simSet) + { + CAircraftModelList simModels = cache.getSynchronizedCachedModels(singleInfo); + if (simModels.isEmpty()) { continue; } + const CAircraftModelList dbModelsForSim = dbModels.matchesSimulator(singleInfo); + if (dbModelsForSim.isEmpty()) { continue; } + + // time consuming part + const int c = CDatabaseUtils::consolidateModelsWithDbData(dbModelsForSim, simModels, true); + if (c > 0) + { + CLogMessage(this).info("Consolidated %1 models for '%2'") << c << singleInfo.convertToQString(); + const CStatusMessage m = cache.setCachedModels(simModels, singleInfo); + CLogMessage::preformatted(m); + } + else + { + CLogMessage(this).info("Syncronize, no changes for '%1'") << singleInfo.convertToQString(); + } + if (simSet.size() > 1) { CEventLoop::processEventsFor(5000); } // just give the system some time to relax, consolidate is time consuming + } + } + + void CBackgroundDataUpdater::syncDbEntity(CEntityFlags::Entity entity) const + { + if (!this->entryCheck()) { return; } + const QDateTime latestCacheTs = sApp->getWebDataServices()->getCacheTimestamp(entity); + if (!latestCacheTs.isValid()) { return; } + const QDateTime latestDbTs = sApp->getWebDataServices()->getLatestDbEntityTimestamp(entity); + if (!latestDbTs.isValid()) { return; } + if (latestDbTs <= latestCacheTs) + { + CLogMessage(this).info("No auto sync with DB, entity '%1', DB ts: %2 cache ts: %3") << CEntityFlags::flagToString(entity) << latestDbTs.toString(Qt::ISODate) << latestCacheTs.toString(Qt::ISODate); + return; + } + + CLogMessage(this).info("Triggering read of '%1' since '%2'") << CEntityFlags::flagToString(entity) << latestCacheTs.toString(Qt::ISODate); + sApp->getWebDataServices()->triggerLoadingDirectlyFromDb(CEntityFlags::ModelEntity, latestCacheTs); + } + + bool CBackgroundDataUpdater::entryCheck() const + { + if (!sApp || !sApp->hasWebDataServices()) { return false; } + if (isShuttingDown()) { return false; } + if (!m_enabled) { return false; } + return true; + } + } // ns +} // ns diff --git a/src/blackcore/db/backgrounddataupdater.h b/src/blackcore/db/backgrounddataupdater.h new file mode 100644 index 000000000..146e9d82c --- /dev/null +++ b/src/blackcore/db/backgrounddataupdater.h @@ -0,0 +1,84 @@ +/* Copyright (C) 2017 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKCORE_DB_BACKGROUNDDATAUPDATER_H +#define BLACKCORE_DB_BACKGROUNDDATAUPDATER_H + +#include "blackcore/data/dbcaches.h" +#include "blackcore/blackcoreexport.h" +#include "blackmisc/simulation/data/modelcaches.h" +#include "blackmisc/network/entityflags.h" +#include "blackmisc/worker.h" +#include + +namespace BlackCore +{ + namespace Db + { + /** + * Update and consolidation of DB data + */ + class BLACKCORE_EXPORT CBackgroundDataUpdater : public BlackMisc::CContinuousWorker + { + public: + //! Log categories + static const BlackMisc::CLogCategoryList &getLogCategories(); + + //! Constructor + CBackgroundDataUpdater(QObject *owner); + + //! Destructor + virtual ~CBackgroundDataUpdater(); + + //! Is shutting down? + //! \threadsafe + bool isShuttingDown() const; + + //! Enabled (running)? + //! \threadsafe + bool isEnabled() const; + + //! Graceful shutdown + //! \threadsafe + void gracefulShutdown(); + + //! Enable updates + void startUpdating(int updateTimeSecs); + + private: + std::atomic m_shutdown { false }; //!< marker it is shutting down + std::atomic m_enabled { false }; //!< marker it is enabled + int m_cycle = 0; //!< cycle + bool m_inWork = false; //!< indicates a running update + QTimer m_updateTimer { this }; + + BlackMisc::Simulation::Data::CModelCaches m_modelCaches { false, this }; //!< caches + BlackMisc::Simulation::Data::CModelSetCaches m_modelSetCaches { false, this }; //!< caches + QMap m_dbModelsLatestChange; + + //! Do the udpate checks + void doWork(); + + //! Read of new DB data + void triggerInfoReads(); + + //! Sync the model cache + void syncModelOrModelSetCacheWithDbData(BlackMisc::Simulation::Data::IMultiSimulatorModelCaches &cache); + + //! Sync DB entity + void syncDbEntity(BlackMisc::Network::CEntityFlags::Entity entity) const; + + //! Still enabled etc. + bool entryCheck() const; + }; + } // ns +} // ns +#endif // guard diff --git a/src/blackgui/settings/guisettings.h b/src/blackgui/settings/guisettings.h index c00b97cd0..fa3b5989e 100644 --- a/src/blackgui/settings/guisettings.h +++ b/src/blackgui/settings/guisettings.h @@ -86,6 +86,25 @@ namespace BlackGui //! Key in data cache static const char *key() { return "guinownaircraftmodel"; } }; + + //! Settings for last manual entries of own aircraft mode + struct TBackgroundConsolidation : public BlackMisc::TSettingTrait + { + //! Key in data cache + static const char *key() { return "backgroundconsolidation"; } + + //! Validator function. + static bool isValid(const int &valueInSeconds) { return valueInSeconds == -1 || (valueInSeconds >= minSecs() && valueInSeconds <= maxSecs()); } + + //! Default, not consolidating + static const int &defaultValue() { static const int i = -1; return i; } + + //! Minimum + static int minSecs() { return 60; } + + //! Maximum + static int maxSecs() { return 3600; } + }; } // ns } // ns