Files
pilotclient/src/blackcore/vatsim/vatsimmetarreader.cpp
Lars Toenning b4cbed107b refactor: Remove CNetworkWatchdog
The watchdog was used in a few places as a shortcut to skip reading
data. Further, it was used in some places in the UI to display
connectivity. But it also introduced quite some complexity. In some
cases it can be fragile: network accessibilty cannot be looked up on all
platforms/hardware constellations. The connectivity could change
between the last watchdog call and the real call. Hence all readers must
still handle the case where the connection fails.
To simplify swift and further reduce the dependency onto the project
infrastructure (pings etc.), this removes the watchdog.
This also removes the QNetworkConfigurationManager, which is deprecated
and not available with Qt6.
2024-04-15 22:02:11 +02:00

156 lines
5.0 KiB
C++

// SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
#include "blackcore/vatsim/vatsimmetarreader.h"
#include "blackcore/application.h"
#include "blackmisc/mixin/mixincompare.h"
#include "blackmisc/logmessage.h"
#include "blackmisc/network/entityflags.h"
#include "blackmisc/network/url.h"
#include "blackmisc/network/urllist.h"
#include "blackmisc/statusmessage.h"
#include <QByteArray>
#include <QMetaObject>
#include <QNetworkReply>
#include <QReadLocker>
#include <QScopedPointer>
#include <QScopedPointerDeleteLater>
#include <QString>
#include <QTextStream>
#include <QTimer>
#include <QUrl>
#include <QWriteLocker>
#include <QtGlobal>
using namespace BlackMisc;
using namespace BlackMisc::Aviation;
using namespace BlackMisc::Network;
using namespace BlackMisc::Weather;
using namespace BlackCore::Data;
namespace BlackCore::Vatsim
{
CVatsimMetarReader::CVatsimMetarReader(QObject *owner) : CThreadedReader(owner, "CVatsimMetarReader")
{
this->reloadSettings();
}
void CVatsimMetarReader::readInBackgroundThread()
{
QPointer<CVatsimMetarReader> myself(this);
QTimer::singleShot(0, this, [=] {
if (!myself) { return; }
myself->read();
});
}
CMetarList CVatsimMetarReader::getMetars() const
{
QReadLocker l(&m_lock);
return m_metars;
}
CMetar CVatsimMetarReader::getMetarForAirport(const CAirportIcaoCode &icao) const
{
QReadLocker l(&m_lock);
return m_metars.getMetarForAirport(icao);
}
void CVatsimMetarReader::doWorkImpl()
{
this->read();
}
void CVatsimMetarReader::read()
{
this->threadAssertCheck();
if (!this->doWorkCheck()) { return; }
CFailoverUrlList urls(sApp->getVatsimMetarUrls());
const CUrl url(urls.obtainNextWorkingUrl(true));
if (url.isEmpty()) { return; }
Q_ASSERT_X(sApp, Q_FUNC_INFO, "No Application");
this->getFromNetworkAndLog(url.withAppendedQuery("id=all"), { this, &CVatsimMetarReader::decodeMetars });
}
void CVatsimMetarReader::decodeMetars(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);
// Worker thread, make sure to write thread safe!
this->threadAssertCheck();
if (!this->doWorkCheck())
{
CLogMessage(this).info(u"Terminated METAR decoding process"); // for users
return; // stop, terminate straight away, ending thread
}
this->logNetworkReplyReceived(nwReplyPtr);
const QUrl url = nwReply->url();
const QString metarUrl = url.toString();
if (nwReply->error() == QNetworkReply::NoError)
{
QString metarData = nwReply->readAll();
nwReply->close(); // close asap
if (metarData.isEmpty()) // Quick check by hash
{
CLogMessage(this).warning(u"No METAR data from '%1', skipped") << metarUrl;
return;
}
if (!this->didContentChange(metarData)) // Quick check by hash
{
CLogMessage(this).info(u"METAR file from '%1' has same content, skipped") << metarUrl;
return;
}
CMetarList metars;
int invalidLines = 0;
QTextStream lineReader(&metarData);
while (!lineReader.atEnd())
{
if (!this->doWorkCheck()) { return; }
const QString line = lineReader.readLine();
// some check for obvious errors
if (line.contains("<html")) { continue; }
const CMetar metar = m_metarDecoder.decode(line);
if (metar != CMetar())
{
metars.push_back(metar);
}
else
{
invalidLines++;
}
}
CLogMessage(this).info(u"METARs: %1 Metars (invalid %2) from '%3'") << metars.size() << invalidLines << metarUrl;
{
QWriteLocker l(&m_lock);
m_metars = metars;
}
emit metarsRead(metars);
emit dataRead(CEntityFlags::MetarEntity, CEntityFlags::ReadFinished, metars.size(), url);
}
else
{
// network error
CLogMessage(this).warning(u"Reading METARs failed '%1' for '%2'") << nwReply->errorString() << metarUrl;
nwReply->abort();
emit this->dataRead(CEntityFlags::MetarEntity, CEntityFlags::ReadFailed, 0, url);
}
} // method
void CVatsimMetarReader::reloadSettings()
{
const CReaderSettings s = m_settings.get();
this->setInitialAndPeriodicTime(s.getInitialTime().toMs(), s.getPeriodicTime().toMs());
}
} // ns