#include "syncmanager.h" Q_LOGGING_CATEGORY(syncmanager, "sync.manager"); SyncManager::SyncManager(bool dryMode) :mDryMode(dryMode) { } bool SyncManager::init() { database = std::make_shared(&dbMan, mDryMode); if (database->open()){ mlastSync.set_timestamp(database->getLastSync()); colnod = std::make_shared(&reqMan, mlastSync.timestamp(), mDryMode); // -1 --> some error with getting data from DB if (mlastSync.timestamp() >= 0){ return true; } } return false; } void SyncManager::run() { qint64 syncBegin = database->getNow(); qInfo(syncmanager) << equals; qInfo(syncmanager) << "SYNCHRONIZATION BEGIN"; database->initNewTokens(); downloadData(); connect(this, &SyncManager::dataReady, [=](){ prepareData(); connect(this, &SyncManager::dataProcessFinished, [=](){ qInfo(syncmanager) << "PROCESSING DATA -> FINISHED"; qInfo(syncmanager) << equals; updateLastUpdate(syncBegin); connect(this, &SyncManager::syncFinished, [=](){ database->setLastSync(syncBegin); qInfo(syncmanager) << "Update last synchronization time to time of this sync (" << QDateTime::fromMSecsSinceEpoch(syncBegin).toString("yyyy-MM-dd hh:mm:ss.zzz") << ")"; qInfo(syncmanager) << equals; qInfo(syncmanager) << "SYNCHRONIZATION FINISHED"; qInfo(syncmanager) << equals; QCoreApplication::exit(); }); }); processData(); }); } void SyncManager::downloadData() { qInfo(syncmanager) << equals; qInfo(syncmanager) << "DOWNLOADING DATA -> BEGIN"; qInfo(syncmanager) << dash; qInfo(syncmanager) << "Modified data since last synchronization (" << mlastSync.toDateString() << "):"; qInfo(syncmanager) << dash; connect(colnod.get(), &ColnodAPI::downloadDataFinished, this, &SyncManager::saveColnodData); connect(database.get(), &DatabaseAPI::downloadDataFinished, this, &SyncManager::saveDatabaseData); database->downloadData(); colnod->downloadData(); } // finds conflicts -> changes on both sides (colnod and DB), apply newer change void SyncManager::prepareData() { qInfo(syncmanager) << equals; qInfo(syncmanager) << "PREPARING DATA -> BEGIN"; qInfo(syncmanager) << dash; qInfo(syncmanager) << "Resolve conflicts ( changes on same item on both Colnod and DB ):"; qInfo(syncmanager) << dash; colnodDataToProcess = std::make_shared(*colnodData); databaseDataToProcess = std::make_shared(*databaseData); QList conflicts; QList alreadySynced; for (const auto &colnodToken : colnodDataToProcess->updatedTokens) { if (databaseDataToProcess->updatedTokens.contains(colnodToken->id())) { if (databaseDataToProcess->updatedTokens.value(colnodToken->id()) > colnodToken) { conflicts.append(colnodToken->id()); } continue; } auto token = database->getLastUpdate(colnodToken->id()); if ( token ) if (token == colnodToken) alreadySynced.append(colnodToken->id()); } for (const auto &id : conflicts) colnodDataToProcess->updatedTokens.remove(id); for (const auto &id : alreadySynced) colnodDataToProcess->updatedTokens.remove(id); qInfo(syncmanager) << tab << "-> COLNOD DATA - CHANGES:"; qInfo(syncmanager) << tab << tab << conflicts.size() << "TOKENS WERE MODIFIED IN DATABASE"; qInfo(syncmanager) << tab << tab << alreadySynced.size() << "TOKENS WERE ALREADY SYNCED"; qInfo(syncmanager) << dash; conflicts.clear(); for (const auto &databaseToken : databaseData->updatedTokens) { if (colnodDataToProcess->updatedTokens.contains(databaseToken->id())) { if (databaseToken > colnodDataToProcess->updatedTokens.value(databaseToken->id())) conflicts.append(databaseToken->id()); } else { if (databaseToken->description() != "mobile") { qWarning(syncmanager) << "Token:" << databaseToken->id() << "doesn't have 'mobile' in description and won't be pushed to Colnod"; conflicts.append(databaseToken->id()); } } } for (const auto &id : conflicts) colnodDataToProcess->updatedTokens.remove(id); qInfo(syncmanager) << tab << "-> DATABASE DATA - CHANGES:"; qInfo(syncmanager) << tab << tab << conflicts.size() << "TOKENS WERE MODIFIED IN COLNOD"; qInfo(syncmanager) << dash; qInfo(syncmanager) << "PREPARING DATA -> FINISHED"; qInfo(syncmanager) << equals; } void SyncManager::processData() { qInfo(syncmanager) << equals; qInfo(syncmanager) << "PROCESSING DATA -> BEGIN"; qInfo(syncmanager) << dash; qInfo(syncmanager) << "Push changes to both Colnod and Database:"; qInfo(syncmanager) << dash; database->processData(colnodDataToProcess); connect(colnod.get(), &ColnodAPI::dataProcessFinished, this, &SyncManager::dataProcessFinished); colnod->processData(databaseDataToProcess); } void SyncManager::updateLastUpdate(qint64 syncBegin) { Timestamp time; time.set_timestamp(syncBegin); colnod->getTokensUpdated(time); connect(colnod.get(), &ColnodAPI::partOfDataReady, [=](const std::shared_ptr &data){ database->setLastUpdate(data->updatedTokens); emit syncFinished(); }); } void SyncManager::areDataReady() { if (++dataAmount == 2) { qInfo(syncmanager) << "DOWNLOADING DATA -> FINISHED"; qInfo(syncmanager) << equals; emit dataReady(); } } void SyncManager::saveColnodData(const std::shared_ptr &data) { colnodData = data; areDataReady(); } void SyncManager::saveDatabaseData(const std::shared_ptr &data) { databaseData = data; areDataReady(); }