refs #712, improved readers

* only trigger read when network interface is available
* do not trigger all reads at same time but slightly shifted
* renamed to hasReceivedOkReply()
This commit is contained in:
Klaus Basan
2016-07-24 01:36:09 +02:00
parent 31b03fe2e1
commit 0357dbde8f
9 changed files with 102 additions and 42 deletions

View File

@@ -109,6 +109,12 @@ namespace BlackCore
{ {
// ps_read is implemented in the derived classes // ps_read is implemented in the derived classes
if (entities == CEntityFlags::NoEntity) { return; } if (entities == CEntityFlags::NoEntity) { return; }
if (!this->isNetworkAvailable())
{
CLogMessage(this).warning("No network, will not read %1") << CEntityFlags::flagToString(entities);
return;
}
const bool s = QMetaObject::invokeMethod(this, "ps_read", const bool s = QMetaObject::invokeMethod(this, "ps_read",
Q_ARG(BlackMisc::Network::CEntityFlags::Entity, entities), Q_ARG(BlackMisc::Network::CEntityFlags::Entity, entities),
Q_ARG(QDateTime, newerThan)); Q_ARG(QDateTime, newerThan));
@@ -169,7 +175,7 @@ namespace BlackCore
CDatabaseReader::JsonDatastoreResponse CDatabaseReader::setStatusAndTransformReplyIntoDatastoreResponse(QNetworkReply *nwReply) CDatabaseReader::JsonDatastoreResponse CDatabaseReader::setStatusAndTransformReplyIntoDatastoreResponse(QNetworkReply *nwReply)
{ {
this->setConnectionStatus(nwReply); this->setReplyStatus(nwReply);
return this->transformReplyIntoDatastoreResponse(nwReply); return this->transformReplyIntoDatastoreResponse(nwReply);
} }
@@ -233,17 +239,23 @@ namespace BlackCore
return oldS != currentS; return oldS != currentS;
} }
bool CDatabaseReader::canConnect() const bool CDatabaseReader::hasReceivedOkReply() const
{ {
QReadLocker rl(&this->m_statusLock); QReadLocker rl(&this->m_statusLock);
return m_canConnect; return m_1stReplyReceived && m_1stReplyStatus == QNetworkReply::NoError;
} }
bool CDatabaseReader::canConnect(QString &message) const bool CDatabaseReader::hasReceivedOkReply(QString &message) const
{ {
QReadLocker rl(&this->m_statusLock); QReadLocker rl(&this->m_statusLock);
message = m_statusMessage; message = m_statusMessage;
return m_canConnect; return m_1stReplyReceived && m_1stReplyStatus == QNetworkReply::NoError;
}
bool CDatabaseReader::hasReceivedFirstReply() const
{
QReadLocker rl(&this->m_statusLock);
return m_1stReplyReceived;
} }
const QString &CDatabaseReader::getStatusMessage() const const QString &CDatabaseReader::getStatusMessage() const
@@ -251,28 +263,20 @@ namespace BlackCore
return this->m_statusMessage; return this->m_statusMessage;
} }
void CDatabaseReader::setConnectionStatus(bool ok, const QString &message) void CDatabaseReader::setReplyStatus(QNetworkReply::NetworkError status, const QString &message)
{
{ {
QWriteLocker wl(&this->m_statusLock); QWriteLocker wl(&this->m_statusLock);
this->m_statusMessage = message; this->m_statusMessage = message;
this->m_canConnect = ok; this->m_1stReplyStatus = status;
} this->m_1stReplyReceived = true;
} }
void CDatabaseReader::setConnectionStatus(QNetworkReply *nwReply) void CDatabaseReader::setReplyStatus(QNetworkReply *nwReply)
{ {
Q_ASSERT_X(nwReply, Q_FUNC_INFO, "Missing network reply"); Q_ASSERT_X(nwReply, Q_FUNC_INFO, "Missing network reply");
if (nwReply->isFinished()) if (nwReply && nwReply->isFinished())
{ {
if (nwReply->error() == QNetworkReply::NoError) this->setReplyStatus(nwReply->error(), nwReply->errorString());
{
setConnectionStatus(true);
}
else
{
setConnectionStatus(false, nwReply->errorString());
}
} }
} }

View File

@@ -29,6 +29,7 @@
#include <QString> #include <QString>
#include <QTimer> #include <QTimer>
#include <QtGlobal> #include <QtGlobal>
#include <QNetworkReply>
class QNetworkReply; class QNetworkReply;
namespace BlackMisc { class CLogCategoryList; } namespace BlackMisc { class CLogCategoryList; }
@@ -98,14 +99,18 @@ namespace BlackCore
//! Start reading in own thread (without config/caching) //! Start reading in own thread (without config/caching)
void startReadFromDbInBackgroundThread(BlackMisc::Network::CEntityFlags::Entity entities, const QDateTime &newerThan); void startReadFromDbInBackgroundThread(BlackMisc::Network::CEntityFlags::Entity entities, const QDateTime &newerThan);
//! Can connect to DB //! Has received Ok response from server at least once?
//! \threadsafe //! \threadsafe
bool canConnect() const; bool hasReceivedOkReply() const;
//! Can connect to server? //! Has received Ok response from server at least once?
//! \return message why connect failed //! A message why connect failed can be obtained.
//! \threadsafe //! \threadsafe
bool canConnect(QString &message) const; bool hasReceivedOkReply(QString &message) const;
//! Has received 1st reply?
//! \threadsafe
bool hasReceivedFirstReply() const;
//! Get cache timestamp //! Get cache timestamp
virtual QDateTime getCacheTimestamp(BlackMisc::Network::CEntityFlags::Entity entities) const = 0; virtual QDateTime getCacheTimestamp(BlackMisc::Network::CEntityFlags::Entity entities) const = 0;
@@ -146,7 +151,8 @@ namespace BlackCore
protected: protected:
CDatabaseReaderConfigList m_config; //!< DB reder configuration CDatabaseReaderConfigList m_config; //!< DB reder configuration
QString m_statusMessage; //!< Returned status message from watchdog QString m_statusMessage; //!< Returned status message from watchdog
bool m_canConnect = false; //!< Successful connection? bool m_1stReplyReceived = false; //!< Successful connection?
QNetworkReply::NetworkError m_1stReplyStatus = QNetworkReply::UnknownServerError; //!< Successful connection?
mutable QReadWriteLock m_statusLock; //!< Lock mutable QReadWriteLock m_statusLock; //!< Lock
//! Constructor //! Constructor
@@ -192,11 +198,11 @@ namespace BlackCore
//! Feedback about connection status //! Feedback about connection status
//! \threadsafe //! \threadsafe
void setConnectionStatus(bool ok, const QString &message = ""); void setReplyStatus(QNetworkReply::NetworkError status, const QString &message = "");
//! Feedback about connection status //! Feedback about connection status
//! \threadsafe //! \threadsafe
void setConnectionStatus(QNetworkReply *nwReply); void setReplyStatus(QNetworkReply *nwReply);
}; };
} // ns } // ns
} // ns } // ns

View File

@@ -197,6 +197,15 @@ namespace BlackCore
CStatusMessageList CSetupReader::triggerReadSetup() CStatusMessageList CSetupReader::triggerReadSetup()
{ {
if (m_shutdown) { return CStatusMessage(this, CStatusMessage::SeverityError, "shutdown"); } if (m_shutdown) { return CStatusMessage(this, CStatusMessage::SeverityError, "shutdown"); }
if (!CNetworkUtils::hasConnectedInterface())
{
const CStatusMessage m(this, CStatusMessage::SeverityError,
"No network, cancelled reading of setup");
CStatusMessageList msgs(m);
msgs.push_back(this->manageSetupAvailability(false, false));
return msgs;
}
const CUrl url(this->m_bootstrapUrls.obtainNextWorkingUrl()); const CUrl url(this->m_bootstrapUrls.obtainNextWorkingUrl());
if (url.isEmpty()) if (url.isEmpty())
{ {
@@ -456,7 +465,7 @@ namespace BlackCore
} }
else else
{ {
bool cacheAvailable = this->m_setup.getThreadLocal().wasLoaded(); bool cacheAvailable = this->m_setup.get().wasLoaded();
available = cacheAvailable && this->m_bootstrapMode != Explicit; available = cacheAvailable && this->m_bootstrapMode != Explicit;
} }

View File

@@ -73,6 +73,12 @@ namespace BlackCore
this->initialize(); this->initialize();
} }
bool CThreadedReader::isNetworkAvailable() const
{
static const bool nw = CNetworkUtils::hasConnectedInterface();
return nw;
}
void CThreadedReader::gracefulShutdown() void CThreadedReader::gracefulShutdown()
{ {
// if not in main thread stop, otherwise it makes no sense to abandon // if not in main thread stop, otherwise it makes no sense to abandon

View File

@@ -55,6 +55,9 @@ namespace BlackCore
//! \note override as required, default is to call initialize() //! \note override as required, default is to call initialize()
virtual void requestReload(); virtual void requestReload();
//! Network available
bool isNetworkAvailable() const;
//! Get the timer interval (ms) //! Get the timer interval (ms)
//! \threadsafe //! \threadsafe
int interval() const; int interval() const;

View File

@@ -67,6 +67,13 @@ namespace BlackCore
void CVatsimBookingReader::ps_read() void CVatsimBookingReader::ps_read()
{ {
if (!this->isNetworkAvailable())
{
CLogMessage(this).warning("No network, cancel bookings reader");
this->m_updateTimer->stop();
return;
}
this->threadAssertCheck(); this->threadAssertCheck();
this->restartTimer(true); // when timer active, restart so we cause no undesired reads this->restartTimer(true); // when timer active, restart so we cause no undesired reads

View File

@@ -186,6 +186,13 @@ namespace BlackCore
void CVatsimDataFileReader::ps_read() void CVatsimDataFileReader::ps_read()
{ {
if (!this->isNetworkAvailable())
{
CLogMessage(this).warning("No network, cancel data file reader");
this->m_updateTimer->stop();
return;
}
this->threadAssertCheck(); this->threadAssertCheck();
this->restartTimer(true); // when timer active, restart so we cause no undesired reads this->restartTimer(true); // when timer active, restart so we cause no undesired reads

View File

@@ -80,6 +80,12 @@ namespace BlackCore
void CVatsimMetarReader::readMetars() void CVatsimMetarReader::readMetars()
{ {
if (this->isAbandoned()) { return; } if (this->isAbandoned()) { return; }
if (!this->isNetworkAvailable())
{
CLogMessage(this).warning("No network, cancel METAR reader");
this->m_updateTimer->stop();
return;
}
this->threadAssertCheck(); this->threadAssertCheck();
this->restartTimer(true); // when timer active, restart so we cause no undesired reads this->restartTimer(true); // when timer active, restart so we cause no undesired reads

View File

@@ -83,7 +83,17 @@ namespace BlackCore
entities &= ~CEntityFlags::InfoObjectEntity; // triggered in init readers entities &= ~CEntityFlags::InfoObjectEntity; // triggered in init readers
entities &= ~CEntityFlags::VatsimStatusFile; // triggered in init readers entities &= ~CEntityFlags::VatsimStatusFile; // triggered in init readers
entities &= ~this->m_entitiesPeriodicallyRead; // will be triggered by timers entities &= ~this->m_entitiesPeriodicallyRead; // will be triggered by timers
this->readDeferredInBackground(entities, 1000);
// trigger reading
// but do not start all at the same time
const CEntityFlags::Entity icaoPart = entities & CEntityFlags::AllIcaoAndCountries;
const CEntityFlags::Entity modelPart = entities & CEntityFlags::DistributorLiveryModel;
this->readDeferredInBackground(icaoPart, 1000);
this->readDeferredInBackground(modelPart, 2000);
CEntityFlags::Entity restEntities = entities & ~icaoPart;
restEntities &= ~modelPart;
this->readDeferredInBackground(modelPart, 3000);
} }
CServerList CWebDataServices::getVatsimFsdServers() const CServerList CWebDataServices::getVatsimFsdServers() const
@@ -146,15 +156,15 @@ namespace BlackCore
// use the first one to test // use the first one to test
if (m_infoDataReader) if (m_infoDataReader)
{ {
return m_infoDataReader->canConnect(); return m_infoDataReader->hasReceivedOkReply();
} }
else if (m_icaoDataReader) else if (m_icaoDataReader)
{ {
return m_icaoDataReader->canConnect(); return m_icaoDataReader->hasReceivedOkReply();
} }
else if (m_modelDataReader) else if (m_modelDataReader)
{ {
return m_modelDataReader->canConnect(); return m_modelDataReader->hasReceivedOkReply();
} }
return false; return false;
} }
@@ -701,6 +711,7 @@ namespace BlackCore
void CWebDataServices::readDeferredInBackground(CEntityFlags::Entity entities, int delayMs) void CWebDataServices::readDeferredInBackground(CEntityFlags::Entity entities, int delayMs)
{ {
if (entities == CEntityFlags::NoEntity) { return; }
QTimer::singleShot(delayMs, [ = ]() QTimer::singleShot(delayMs, [ = ]()
{ {
this->readInBackground(entities); this->readInBackground(entities);
@@ -712,7 +723,7 @@ namespace BlackCore
m_initialRead = true; // read started m_initialRead = true; // read started
const int waitForInfoObjects = 1000; // ms const int waitForInfoObjects = 1000; // ms
const int maxWaitCycles = 6; const int maxWaitCycles = 10;
// with info objects wait until info objects are loaded // with info objects wait until info objects are loaded
Q_ASSERT_X(!entities.testFlag(CEntityFlags::InfoObjectEntity), Q_FUNC_INFO, "Info object must be read upfront"); Q_ASSERT_X(!entities.testFlag(CEntityFlags::InfoObjectEntity), Q_FUNC_INFO, "Info object must be read upfront");
@@ -724,14 +735,15 @@ namespace BlackCore
CLogMessage(this).error("Cannot read info objects for %1 from %2") CLogMessage(this).error("Cannot read info objects for %1 from %2")
<< CEntityFlags::flagToString(entities) << CEntityFlags::flagToString(entities)
<< this->m_infoDataReader->getInfoObjectsUrl().toQString(); << this->m_infoDataReader->getInfoObjectsUrl().toQString();
// continue here and read data // continue here and read data without info objects
} }
else if (this->m_infoDataReader->canConnect()) else if (this->m_infoDataReader->hasReceivedFirstReply())
{ {
if (this->m_infoDataReader->areAllDataRead()) if (this->m_infoDataReader->areAllDataRead())
{ {
CLogMessage(this).info("Info objects for %1 loaded from %2") CLogMessage(this).info("Info objects for %1 loaded (trial %2) from %3")
<< CEntityFlags::flagToString(entities) << CEntityFlags::flagToString(entities)
<< this->m_infoObjectTrials
<< this->m_infoDataReader->getInfoObjectsUrl().toQString(); << this->m_infoDataReader->getInfoObjectsUrl().toQString();
// continue here and read data // continue here and read data
} }
@@ -747,7 +759,7 @@ namespace BlackCore
} }
else else
{ {
// can not connect right now, postpone and try again // wait for 1st reply
this->m_infoObjectTrials++; this->m_infoObjectTrials++;
this->readDeferredInBackground(entities, waitForInfoObjects); this->readDeferredInBackground(entities, waitForInfoObjects);
return; return;