Files
pilotclient/src/blackcore/db/airportdatareader.cpp

291 lines
11 KiB
C++

/* Copyright (C) 2016
* 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. 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 "airportdatareader.h"
#include "blackcore/db/databaseutils.h"
#include "blackcore/application.h"
#include "blackmisc/network/networkutils.h"
#include "blackmisc/logmessage.h"
#include <QStringBuilder>
#include <QNetworkReply>
#include <QFileInfo>
#include <QPointer>
using namespace BlackMisc;
using namespace BlackMisc::Aviation;
using namespace BlackMisc::Network;
using namespace BlackMisc::Db;
namespace BlackCore
{
namespace Db
{
CAirportDataReader::CAirportDataReader(QObject *parent, const CDatabaseReaderConfigList &config) :
CDatabaseReader(parent, config, QStringLiteral("CAirportDataReader"))
{
// void
}
BlackMisc::Aviation::CAirportList CAirportDataReader::getAirports() const
{
return m_airportCache.get();
}
CAirport CAirportDataReader::getAirportForIcaoDesignator(const QString &designator) const
{
return this->getAirports().findFirstByIcao(CAirportIcaoCode(designator));
}
CAirport CAirportDataReader::getAirportForNameOrLocation(const QString &nameOrLocation) const
{
return this->getAirports().findFirstByNameOrLocation(nameOrLocation);
}
int CAirportDataReader::getAirportsCount() const
{
return this->getAirports().size();
}
bool CAirportDataReader::readFromJsonFilesInBackground(const QString &dir, CEntityFlags::Entity whatToRead, bool overrideNewerOnly)
{
if (dir.isEmpty() || whatToRead == CEntityFlags::NoEntity) { return false; }
QPointer<CAirportDataReader> myself(this);
QTimer::singleShot(0, this, [ = ]()
{
if (!myself) { return; }
const CStatusMessageList msgs = this->readFromJsonFiles(dir, whatToRead, overrideNewerOnly);
if (msgs.isFailure())
{
CLogMessage::preformatted(msgs);
}
});
return true;
}
CStatusMessageList CAirportDataReader::readFromJsonFiles(const QString &dir, CEntityFlags::Entity whatToRead, bool overrideNewerOnly)
{
const QDir directory(dir);
if (!directory.exists())
{
return CStatusMessage(this).error(u"Missing directory '%1'") << dir;
}
whatToRead &= CEntityFlags::AirportEntity; // can handle these entities
if (whatToRead == CEntityFlags::NoEntity)
{
return CStatusMessage(this).info(u"'%1' No entity for this reader") << CEntityFlags::flagToString(whatToRead);
}
int c = 0;
CEntityFlags::Entity reallyRead = CEntityFlags::NoEntity;
CStatusMessageList msgs;
const QString fileName = CFileUtils::appendFilePaths(dir, "airports.json");
const QFileInfo fi(fileName);
if (!fi.exists())
{
msgs.push_back(CStatusMessage(this).warning(u"File '%1' does not exist") << fileName);
}
else if (!this->overrideCacheFromFile(overrideNewerOnly, fi, CEntityFlags::AirportEntity, msgs))
{
// void
}
else
{
const QJsonObject airportsJson(CDatabaseUtils::readQJsonObjectFromDatabaseFile(fileName));
if (!airportsJson.isEmpty())
{
try
{
const CAirportList airports = CAirportList::fromMultipleJsonFormats(airportsJson);
c = airports.size();
msgs.push_back(m_airportCache.set(airports, fi.created().toUTC().toMSecsSinceEpoch()));
emit dataRead(CEntityFlags::AirportEntity, CEntityFlags::ReadFinished, c);
reallyRead |= CEntityFlags::AirportEntity;
}
catch (const CJsonException &ex)
{
emit dataRead(CEntityFlags::AirportEntity, CEntityFlags::ReadFailed, 0);
return ex.toStatusMessage(this, QStringLiteral("Reading airports from '%1'").arg(fileName));
}
}
}
return msgs;
}
CEntityFlags::Entity CAirportDataReader::getSupportedEntities() const
{
return CEntityFlags::AirportEntity;
}
QDateTime CAirportDataReader::getCacheTimestamp(CEntityFlags::Entity entities) const
{
return entities == CEntityFlags::AirportEntity ? m_airportCache.getAvailableTimestamp() : QDateTime();
}
int CAirportDataReader::getCacheCount(CEntityFlags::Entity entity) const
{
return entity == CEntityFlags::AirportEntity ? m_airportCache.get().size() : 0;
}
CEntityFlags::Entity CAirportDataReader::getEntitiesWithCacheCount() const
{
CEntityFlags::Entity entities = CEntityFlags::NoEntity;
if (this->getCacheCount(CEntityFlags::AirportEntity) > 0) { entities |= CEntityFlags::AirportEntity; }
return entities;
}
CEntityFlags::Entity CAirportDataReader::getEntitiesWithCacheTimestampNewerThan(const QDateTime &threshold) const
{
CEntityFlags::Entity entities = CEntityFlags::NoEntity;
if (this->hasCacheTimestampNewerThan(CEntityFlags::AirportEntity, threshold)) { entities |= CEntityFlags::AirportEntity; }
return entities;
}
void CAirportDataReader::synchronizeCaches(CEntityFlags::Entity entities)
{
if (entities.testFlag(CEntityFlags::AirportEntity)) { if (m_syncedAirportCache) { return; } m_syncedAirportCache = true; m_airportCache.synchronize(); }
}
void CAirportDataReader::admitCaches(CEntityFlags::Entity entities)
{
if (entities.testFlag(CEntityFlags::AirportEntity)) { m_airportCache.admit(); }
}
void CAirportDataReader::invalidateCaches(CEntityFlags::Entity entities)
{
if (entities.testFlag(CEntityFlags::AirportEntity)) { CDataCache::instance()->clearAllValues(m_airportCache.getKey()); }
}
bool CAirportDataReader::hasChangedUrl(CEntityFlags::Entity entity, CUrl &oldUrlInfo, CUrl &newUrlInfo) const
{
Q_UNUSED(entity);
oldUrlInfo = m_readerUrlCache.get();
newUrlInfo = this->getBaseUrl(CDbFlags::DbReading);
return CDatabaseReader::isChangedUrl(oldUrlInfo, newUrlInfo);
}
CUrl CAirportDataReader::getDbServiceBaseUrl() const
{
return sApp->getGlobalSetup().getDbAirportReaderUrl();
}
CUrl CAirportDataReader::getAirportsUrl(CDbFlags::DataRetrievalModeFlag mode) const
{
return this->getBaseUrl(mode).withAppendedPath(fileNameForMode(CEntityFlags::AirportEntity, mode));
}
void CAirportDataReader::parseAirportData(QNetworkReply *nwReplyPtr)
{
// wrap pointer, make sure any exit cleans up reply
// required to use delete later as object is created in a different thread
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
if (!this->doWorkCheck()) { return; }
const CDatabaseReader::JsonDatastoreResponse res = this->setStatusAndTransformReplyIntoDatastoreResponse(nwReplyPtr);
if (res.hasErrorMessage())
{
CLogMessage::preformatted(res.lastWarningOrAbove());
emit this->dataRead(CEntityFlags::AirportEntity, CEntityFlags::ReadFailed, 0);
return;
}
// parsing
emit this->dataRead(CEntityFlags::AircraftIcaoEntity, CEntityFlags::ReadParsing, 0);
CAirportList airports;
CAirportList inconsistent;
if (res.isRestricted())
{
const CAirportList incrementalAirports(CAirportList::fromDatabaseJson(res, &inconsistent));
if (incrementalAirports.isEmpty()) { return; } // currently ignored
airports = this->getAirports();
airports.replaceOrAddObjectsByKey(incrementalAirports);
}
else
{
QTime time;
time.start();
airports = CAirportList::fromDatabaseJson(res, &inconsistent);
this->logParseMessage("airports", airports.size(), time.elapsed(), res);
}
if (!inconsistent.isEmpty())
{
logInconsistentData(
CStatusMessage(this, CStatusMessage::SeverityInfo, u"Inconsistent airports: " % inconsistent.dbKeysAsString(", ")),
Q_FUNC_INFO);
}
if (!this->doWorkCheck()) { return; }
const int size = airports.size();
qint64 latestTimestamp = airports.latestTimestampMsecsSinceEpoch();
if (size > 0 && latestTimestamp < 0)
{
CLogMessage(this).error(u"No timestamp in airport list, setting to last modified value");
latestTimestamp = lastModifiedMsSinceEpoch(nwReply.data());
}
m_airportCache.set(airports, latestTimestamp);
this->updateReaderUrl(getBaseUrl(CDbFlags::DbReading));
this->emitAndLogDataRead(CEntityFlags::AirportEntity, size, res);
}
void CAirportDataReader::read(CEntityFlags::Entity entity, CDbFlags::DataRetrievalModeFlag mode, const QDateTime &newerThan)
{
this->threadAssertCheck();
if (!this->doWorkCheck()) { return; }
entity &= CEntityFlags::AirportEntity;
if (!this->isInternetAccessible())
{
emit this->dataRead(entity, CEntityFlags::ReadSkipped, 0);
return;
}
if (entity.testFlag(CEntityFlags::AirportEntity))
{
CUrl url = this->getAirportsUrl(mode);
if (!url.isEmpty())
{
url.appendQuery(queryLatestTimestamp(newerThan));
this->getFromNetworkAndLog(url, { this, &CAirportDataReader::parseAirportData });
emit dataRead(CEntityFlags::AirportEntity, CEntityFlags::ReadStarted, 0);
}
else
{
this->logNoWorkingUrl(CEntityFlags::AirportEntity);
}
}
}
void CAirportDataReader::airportCacheChanged()
{
this->cacheHasChanged(CEntityFlags::AirportEntity);
}
void CAirportDataReader::baseUrlCacheChanged()
{
// void
}
void CAirportDataReader::updateReaderUrl(const CUrl &url)
{
const CUrl current = m_readerUrlCache.get();
if (current == url) { return; }
const CStatusMessage m = m_readerUrlCache.set(url);
if (m.isFailure())
{
CLogMessage::preformatted(m);
}
}
} // ns
} // ns