mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-12 07:15:34 +08:00
#refs 686, used settings in readers
* moved threaded reader to BlackCore (settings are normally BlackCore aware) * created ns/subfolder VATSIM * prepared settings for the VATSIM readers
This commit is contained in:
190
src/blackcore/vatsim/vatsimbookingreader.cpp
Normal file
190
src/blackcore/vatsim/vatsimbookingreader.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/* Copyright (C) 2013
|
||||
* 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 "blackcore/application.h"
|
||||
#include "blackcore/data/globalsetup.h"
|
||||
#include "blackcore/vatsim/vatsimbookingreader.h"
|
||||
#include "blackmisc/aviation/atcstation.h"
|
||||
#include "blackmisc/aviation/callsign.h"
|
||||
#include "blackmisc/logmessage.h"
|
||||
#include "blackmisc/network/entityflags.h"
|
||||
#include "blackmisc/network/url.h"
|
||||
#include "blackmisc/network/user.h"
|
||||
#include "blackmisc/statusmessage.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QDomDocument>
|
||||
#include <QDomElement>
|
||||
#include <QDomNode>
|
||||
#include <QDomNodeList>
|
||||
#include <QMetaObject>
|
||||
#include <QNetworkReply>
|
||||
#include <QScopedPointer>
|
||||
#include <QScopedPointerDeleteLater>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <Qt>
|
||||
#include <QtGlobal>
|
||||
|
||||
using namespace BlackMisc;
|
||||
using namespace BlackMisc::Aviation;
|
||||
using namespace BlackMisc::Network;
|
||||
|
||||
namespace BlackCore
|
||||
{
|
||||
namespace Vatsim
|
||||
{
|
||||
CVatsimBookingReader::CVatsimBookingReader(QObject *owner) :
|
||||
CThreadedReader(owner, "CVatsimBookingReader")
|
||||
{
|
||||
this->connect(this->m_updateTimer, &QTimer::timeout, this, &CVatsimBookingReader::ps_read);
|
||||
}
|
||||
|
||||
void CVatsimBookingReader::readInBackgroundThread()
|
||||
{
|
||||
bool s = QMetaObject::invokeMethod(this, "ps_read");
|
||||
Q_ASSERT(s);
|
||||
Q_UNUSED(s);
|
||||
}
|
||||
|
||||
void CVatsimBookingReader::cleanup()
|
||||
{
|
||||
// void
|
||||
}
|
||||
|
||||
Settings::CSettingsReader CVatsimBookingReader::getSettings() const
|
||||
{
|
||||
return this->m_settings.get();
|
||||
}
|
||||
|
||||
void CVatsimBookingReader::ps_read()
|
||||
{
|
||||
this->threadAssertCheck();
|
||||
Q_ASSERT_X(sApp, Q_FUNC_INFO, "No application");
|
||||
const QUrl url(sApp->getGlobalSetup().getVatsimBookingsUrl());
|
||||
if (url.isEmpty()) { return; }
|
||||
|
||||
sApp->getFromNetwork(url, { this, &CVatsimBookingReader::ps_parseBookings});
|
||||
}
|
||||
|
||||
void CVatsimBookingReader::ps_parseBookings(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);
|
||||
|
||||
this->threadAssertCheck();
|
||||
|
||||
// Worker thread, make sure to write no members here!
|
||||
if (this->isAbandoned())
|
||||
{
|
||||
CLogMessage(this).debug() << Q_FUNC_INFO;
|
||||
CLogMessage(this).info("terminated booking parsing process"); // for users
|
||||
return; // stop, terminate straight away, ending thread
|
||||
}
|
||||
|
||||
if (nwReply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
static const QString timestampFormat("yyyy-MM-dd HH:mm:ss");
|
||||
QString xmlData = nwReply->readAll();
|
||||
nwReply->close(); // close asap
|
||||
QDomDocument doc;
|
||||
QDateTime updateTimestamp = QDateTime::currentDateTimeUtc();
|
||||
|
||||
if (doc.setContent(xmlData))
|
||||
{
|
||||
QDomNode timestamp = doc.elementsByTagName("timestamp").at(0);
|
||||
QString ts = timestamp.toElement().text().trimmed();
|
||||
Q_ASSERT(!ts.isEmpty());
|
||||
|
||||
if (!ts.isEmpty())
|
||||
{
|
||||
// normally the timestamp is always updated from backend
|
||||
// if this changes in the future we're prepared
|
||||
updateTimestamp = QDateTime::fromString(ts, timestampFormat);
|
||||
updateTimestamp.setTimeSpec(Qt::UTC);
|
||||
if (this->getUpdateTimestamp() == updateTimestamp) return; // nothing to do
|
||||
}
|
||||
|
||||
QDomNode atc = doc.elementsByTagName("atcs").at(0);
|
||||
QDomNodeList bookingNodes = atc.toElement().elementsByTagName("booking");
|
||||
int size = bookingNodes.size();
|
||||
CAtcStationList bookedStations;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (this->isAbandoned())
|
||||
{
|
||||
CLogMessage(this).debug() << Q_FUNC_INFO;
|
||||
CLogMessage(this).info("Terminated booking parsing process"); // for users
|
||||
return; // stop, terminate straight away, ending thread
|
||||
}
|
||||
|
||||
// pase nodes
|
||||
QDomNode bookingNode = bookingNodes.at(i);
|
||||
QDomNodeList bookingNodeValues = bookingNode.childNodes();
|
||||
CAtcStation bookedStation;
|
||||
CUser user;
|
||||
for (int v = 0; v < bookingNodeValues.size(); v++)
|
||||
{
|
||||
QDomNode bookingNodeValue = bookingNodeValues.at(v);
|
||||
QString name = bookingNodeValue.nodeName().toLower();
|
||||
QString value = bookingNodeValue.toElement().text();
|
||||
if (name == "id")
|
||||
{
|
||||
// could be used as unique key
|
||||
}
|
||||
else if (name == "callsign")
|
||||
{
|
||||
bookedStation.setCallsign(CCallsign(value, CCallsign::Atc));
|
||||
}
|
||||
else if (name == "name")
|
||||
{
|
||||
user.setRealName(value);
|
||||
}
|
||||
else if (name == "cid")
|
||||
{
|
||||
user.setId(value);
|
||||
}
|
||||
else if (name == "time_end")
|
||||
{
|
||||
QDateTime t = QDateTime::fromString(value, timestampFormat);
|
||||
t.setTimeSpec(Qt::UTC);
|
||||
bookedStation.setBookedUntilUtc(t);
|
||||
}
|
||||
else if (name == "time_start")
|
||||
{
|
||||
QDateTime t = QDateTime::fromString(value, timestampFormat);
|
||||
t.setTimeSpec(Qt::UTC);
|
||||
bookedStation.setBookedFromUtc(t);
|
||||
}
|
||||
}
|
||||
// time checks
|
||||
QDateTime now = QDateTime::currentDateTimeUtc();
|
||||
if (now.msecsTo(bookedStation.getBookedUntilUtc()) < (1000 * 60 * 15)) { continue; } // until n mins in past
|
||||
if (now.msecsTo(bookedStation.getBookedFromUtc()) > (1000 * 60 * 60 * 24)) { continue; } // to far in the future, n hours
|
||||
bookedStation.setController(user);
|
||||
bookedStations.push_back(bookedStation);
|
||||
}
|
||||
this->setUpdateTimestamp(updateTimestamp); // thread safe update
|
||||
emit this->atcBookingsRead(bookedStations);
|
||||
emit this->dataRead(CEntityFlags::BookingEntity, CEntityFlags::ReadFinished, bookedStations.size());
|
||||
} // node
|
||||
}
|
||||
else
|
||||
{
|
||||
// network error
|
||||
CLogMessage(this).warning("Reading bookings failed %1 %2") << nwReply->errorString() << nwReply->url().toString();
|
||||
nwReply->abort();
|
||||
emit this->dataRead(CEntityFlags::BookingEntity, CEntityFlags::ReadFailed, 0);
|
||||
}
|
||||
} // method
|
||||
} // ns
|
||||
} // ns
|
||||
68
src/blackcore/vatsim/vatsimbookingreader.h
Normal file
68
src/blackcore/vatsim/vatsimbookingreader.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* Copyright (C) 2013
|
||||
* 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_VATSIM_VATSIMBOOKINGREADER_H
|
||||
#define BLACKCORE_VATSIM_VATSIMBOOKINGREADER_H
|
||||
|
||||
#include "blackcore/blackcoreexport.h"
|
||||
#include "blackmisc/aviation/atcstationlist.h"
|
||||
#include "blackmisc/network/entityflags.h"
|
||||
#include "blackcore/threadedreader.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
namespace BlackCore
|
||||
{
|
||||
namespace Vatsim
|
||||
{
|
||||
//! Read bookings from VATSIM
|
||||
class BLACKCORE_EXPORT CVatsimBookingReader : public BlackCore::CThreadedReader
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
//! Constructor
|
||||
explicit CVatsimBookingReader(QObject *owner);
|
||||
|
||||
//! Read / re-read bookings
|
||||
void readInBackgroundThread();
|
||||
|
||||
signals:
|
||||
//! Bookings have been read and converted to BlackMisc::Aviation::CAtcStationList
|
||||
void atcBookingsRead(const BlackMisc::Aviation::CAtcStationList &bookedStations);
|
||||
|
||||
//! Data have been read
|
||||
void dataRead(BlackMisc::Network::CEntityFlags::Entity entity, BlackMisc::Network::CEntityFlags::ReadState state, int number);
|
||||
|
||||
protected:
|
||||
//! \name BlackCore::CThreadedReader overrides
|
||||
//! @{
|
||||
virtual void cleanup() override;
|
||||
virtual BlackCore::Settings::CSettingsReader getSettings() const override;
|
||||
//! @}
|
||||
|
||||
private slots:
|
||||
//! Bookings have been read
|
||||
//! \threadsafe
|
||||
void ps_parseBookings(QNetworkReply *nwReply);
|
||||
|
||||
//! Do reading
|
||||
void ps_read();
|
||||
|
||||
private:
|
||||
BlackMisc::CSettingReadOnly<BlackCore::Settings::SettingsVatsimBookings> m_settings { this };
|
||||
};
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
#endif // guard
|
||||
440
src/blackcore/vatsim/vatsimdatafilereader.cpp
Normal file
440
src/blackcore/vatsim/vatsimdatafilereader.cpp
Normal file
@@ -0,0 +1,440 @@
|
||||
/* Copyright (C) 2013
|
||||
* 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 "blackcore/vatsim/vatsimdatafilereader.h"
|
||||
#include "blackcore/application.h"
|
||||
#include "blackmisc/aviation/aircraftsituation.h"
|
||||
#include "blackmisc/aviation/altitude.h"
|
||||
#include "blackmisc/aviation/atcstation.h"
|
||||
#include "blackmisc/compare.h"
|
||||
#include "blackmisc/geo/coordinategeodetic.h"
|
||||
#include "blackmisc/logmessage.h"
|
||||
#include "blackmisc/network/entityflags.h"
|
||||
#include "blackmisc/network/server.h"
|
||||
#include "blackmisc/network/url.h"
|
||||
#include "blackmisc/network/urllist.h"
|
||||
#include "blackmisc/network/user.h"
|
||||
#include "blackmisc/pq/frequency.h"
|
||||
#include "blackmisc/pq/length.h"
|
||||
#include "blackmisc/pq/speed.h"
|
||||
#include "blackmisc/pq/units.h"
|
||||
#include "blackmisc/predicates.h"
|
||||
#include "blackmisc/range.h"
|
||||
#include "blackmisc/simulation/simulatedaircraft.h"
|
||||
#include "blackmisc/statusmessage.h"
|
||||
#include "blackmisc/verify.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QMetaObject>
|
||||
#include <QNetworkReply>
|
||||
#include <QReadLocker>
|
||||
#include <QRegExp>
|
||||
#include <QRegularExpression>
|
||||
#include <QScopedPointer>
|
||||
#include <QScopedPointerDeleteLater>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QWriteLocker>
|
||||
#include <Qt>
|
||||
#include <QtGlobal>
|
||||
|
||||
using namespace BlackMisc;
|
||||
using namespace BlackMisc::Aviation;
|
||||
using namespace BlackMisc::Network;
|
||||
using namespace BlackMisc::Geo;
|
||||
using namespace BlackMisc::Simulation;
|
||||
using namespace BlackMisc::PhysicalQuantities;
|
||||
using namespace BlackCore::Data;
|
||||
using namespace BlackCore::Settings;
|
||||
|
||||
namespace BlackCore
|
||||
{
|
||||
namespace Vatsim
|
||||
{
|
||||
CVatsimDataFileReader::CVatsimDataFileReader(QObject *owner) :
|
||||
CThreadedReader(owner, "CVatsimDataFileReader")
|
||||
{
|
||||
this->connect(this->m_updateTimer, &QTimer::timeout, this, &CVatsimDataFileReader::ps_read);
|
||||
}
|
||||
|
||||
CSimulatedAircraftList CVatsimDataFileReader::getAircraft() const
|
||||
{
|
||||
QReadLocker rl(&this->m_lock);
|
||||
return this->m_aircraft;
|
||||
}
|
||||
|
||||
CAtcStationList CVatsimDataFileReader::getAtcStations() const
|
||||
{
|
||||
QReadLocker rl(&this->m_lock);
|
||||
return this->m_atcStations;
|
||||
}
|
||||
|
||||
CAtcStationList CVatsimDataFileReader::getAtcStationsForCallsign(const CCallsign &callsign) const
|
||||
{
|
||||
CCallsignSet cs({callsign});
|
||||
return this->getAtcStationsForCallsigns(cs);
|
||||
}
|
||||
|
||||
CAtcStationList CVatsimDataFileReader::getAtcStationsForCallsigns(const CCallsignSet &callsigns) const
|
||||
{
|
||||
return this->getAtcStations().findByCallsigns(callsigns);
|
||||
}
|
||||
|
||||
CServerList CVatsimDataFileReader::getVoiceServers() const
|
||||
{
|
||||
return this->m_lastGoodSetup.get().getVoiceServers();
|
||||
}
|
||||
|
||||
CServerList CVatsimDataFileReader::getFsdServers() const
|
||||
{
|
||||
return this->m_lastGoodSetup.get().getFsdServers();
|
||||
}
|
||||
|
||||
CUserList CVatsimDataFileReader::getPilotsForCallsigns(const CCallsignSet &callsigns)
|
||||
{
|
||||
return this->getAircraft().findByCallsigns(callsigns).transform(Predicates::MemberTransform(&CSimulatedAircraft::getPilot));
|
||||
}
|
||||
|
||||
CUserList CVatsimDataFileReader::getPilotsForCallsign(const CCallsign &callsign)
|
||||
{
|
||||
CCallsignSet callsigns({callsign});
|
||||
return this->getPilotsForCallsigns(callsigns);
|
||||
}
|
||||
|
||||
CAirlineIcaoCode CVatsimDataFileReader::getAirlineIcaoCode(const CCallsign &callsign)
|
||||
{
|
||||
CSimulatedAircraft aircraft = this->getAircraft().findFirstByCallsign(callsign);
|
||||
return aircraft.getAirlineIcaoCode();
|
||||
}
|
||||
|
||||
CAircraftIcaoCode CVatsimDataFileReader::getAircraftIcaoCode(const CCallsign &callsign)
|
||||
{
|
||||
CSimulatedAircraft aircraft = this->getAircraft().findFirstByCallsign(callsign);
|
||||
return aircraft.getAircraftIcaoCode();
|
||||
}
|
||||
|
||||
CVoiceCapabilities CVatsimDataFileReader::getVoiceCapabilityForCallsign(const CCallsign &callsign)
|
||||
{
|
||||
QReadLocker rl(&this->m_lock);
|
||||
if (this->m_voiceCapabilities.contains(callsign))
|
||||
{
|
||||
return m_voiceCapabilities[callsign];
|
||||
}
|
||||
else
|
||||
{
|
||||
return CVoiceCapabilities::fromVoiceCapabilities(CVoiceCapabilities::Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
void CVatsimDataFileReader::updateWithVatsimDataFileData(CSimulatedAircraft &aircraftToBeUdpated) const
|
||||
{
|
||||
this->getAircraft().updateWithVatsimDataFileData(aircraftToBeUdpated);
|
||||
}
|
||||
|
||||
CUserList CVatsimDataFileReader::getControllersForCallsign(const CCallsign &callsign)
|
||||
{
|
||||
CCallsignSet cs({callsign});
|
||||
return this->getControllersForCallsigns(cs);
|
||||
}
|
||||
|
||||
CUserList CVatsimDataFileReader::getControllersForCallsigns(const CCallsignSet &callsigns)
|
||||
{
|
||||
return this->getAtcStations().findByCallsigns(callsigns).transform(Predicates::MemberTransform(&CAtcStation::getController));
|
||||
}
|
||||
|
||||
CUserList CVatsimDataFileReader::getUsersForCallsign(const CCallsign &callsign)
|
||||
{
|
||||
CCallsignSet callsigns({callsign});
|
||||
return this->getUsersForCallsigns(callsigns);
|
||||
}
|
||||
|
||||
CUserList CVatsimDataFileReader::getUsersForCallsigns(const CCallsignSet &callsigns)
|
||||
{
|
||||
CUserList users;
|
||||
if (callsigns.isEmpty()) { return users; }
|
||||
for (const CCallsign &callsign : callsigns)
|
||||
{
|
||||
users.push_back(this->getPilotsForCallsign(callsign));
|
||||
users.push_back(this->getControllersForCallsign(callsign));
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
void CVatsimDataFileReader::readInBackgroundThread()
|
||||
{
|
||||
bool s = QMetaObject::invokeMethod(this, "ps_read");
|
||||
Q_ASSERT_X(s, Q_FUNC_INFO, "Invoke failed");
|
||||
Q_UNUSED(s);
|
||||
}
|
||||
|
||||
void CVatsimDataFileReader::cleanup()
|
||||
{
|
||||
// void
|
||||
}
|
||||
|
||||
CSettingsReader CVatsimDataFileReader::getSettings() const
|
||||
{
|
||||
return this->m_settings.get();
|
||||
}
|
||||
|
||||
void CVatsimDataFileReader::ps_read()
|
||||
{
|
||||
this->threadAssertCheck();
|
||||
|
||||
// round robin for load balancing
|
||||
// remark: Don't use QThread to run network operations in the background
|
||||
// see http://qt-project.org/doc/qt-4.7/qnetworkaccessmanager.html
|
||||
Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing application");
|
||||
CFailoverUrlList urls(sApp->getVatsimDataFileUrls());
|
||||
const QUrl url(urls.obtainNextWorkingUrl(true));
|
||||
if (url.isEmpty()) { return; }
|
||||
sApp->getFromNetwork(url, { this, &CVatsimDataFileReader::ps_parseVatsimFile});
|
||||
|
||||
}
|
||||
|
||||
void CVatsimDataFileReader::ps_parseVatsimFile(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);
|
||||
|
||||
this->threadAssertCheck();
|
||||
|
||||
// Worker thread, make sure to write only synced here!
|
||||
if (this->isAbandoned())
|
||||
{
|
||||
CLogMessage(this).debug() << Q_FUNC_INFO;
|
||||
CLogMessage(this).info("Terminated VATSIM file parsing process"); // for users
|
||||
return; // stop, terminate straight away, ending thread
|
||||
}
|
||||
|
||||
QStringList illegalIcaoCodes;
|
||||
if (nwReply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
const QString dataFileData = nwReply->readAll();
|
||||
nwReply->close(); // close asap
|
||||
|
||||
if (dataFileData.isEmpty()) return;
|
||||
const QStringList lines = dataFileData.split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
|
||||
if (lines.isEmpty()) { return; }
|
||||
|
||||
// build on local vars for thread safety
|
||||
CServerList voiceServers;
|
||||
CServerList fsdServers;
|
||||
CAtcStationList atcStations;
|
||||
CSimulatedAircraftList aircraft;
|
||||
QMap<CCallsign, CVoiceCapabilities> voiceCapabilities;
|
||||
QDateTime updateTimestampFromFile;
|
||||
|
||||
QStringList clientSectionAttributes;
|
||||
Section section = SectionNone;
|
||||
for (const QString &cl : lines)
|
||||
{
|
||||
if (this->isAbandoned())
|
||||
{
|
||||
CLogMessage(this).debug() << Q_FUNC_INFO;
|
||||
CLogMessage(this).info("Terminated booking parsing process"); // for users
|
||||
return; // stop, terminate straight away, ending thread
|
||||
}
|
||||
|
||||
// parse lines
|
||||
QString currentLine(cl.trimmed());
|
||||
if (currentLine.isEmpty()) continue;
|
||||
if (currentLine.startsWith(";"))
|
||||
{
|
||||
if (clientSectionAttributes.isEmpty() && currentLine.contains("!CLIENTS SECTION", Qt::CaseInsensitive))
|
||||
{
|
||||
// ; !CLIENTS section
|
||||
int i = currentLine.lastIndexOf(' ');
|
||||
const QString attributes = currentLine.mid(i).trimmed();
|
||||
clientSectionAttributes = attributes.split(':', QString::SkipEmptyParts);
|
||||
section = SectionNone; // reset
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (currentLine.startsWith("!"))
|
||||
{
|
||||
section = currentLineToSection(currentLine);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case SectionClients:
|
||||
{
|
||||
const QMap<QString, QString> clientPartsMap = clientPartsToMap(currentLine, clientSectionAttributes);
|
||||
const CCallsign callsign = CCallsign(clientPartsMap["callsign"]);
|
||||
if (callsign.isEmpty()) { break; }
|
||||
const BlackMisc::Network::CUser user(clientPartsMap["cid"], clientPartsMap["realname"], callsign);
|
||||
const QString clientType = clientPartsMap["clienttype"].toLower();
|
||||
if (clientType.isEmpty()) { break; } // sometimes type is empty
|
||||
const double lat = clientPartsMap["latitude"].toDouble();
|
||||
const double lng = clientPartsMap["longitude"].toDouble();
|
||||
const double alt = clientPartsMap["altitude"].toDouble();
|
||||
const CFrequency frequency = CFrequency(clientPartsMap["frequency"].toDouble(), CFrequencyUnit::MHz());
|
||||
CCoordinateGeodetic position(lat, lng, -1);
|
||||
CAltitude altitude(alt, CAltitude::MeanSeaLevel, CLengthUnit::ft());
|
||||
QString flightPlanRemarks = clientPartsMap["planned_remarks"];
|
||||
|
||||
// Voice capabilities
|
||||
if (!flightPlanRemarks.isEmpty())
|
||||
{
|
||||
CVoiceCapabilities vc(flightPlanRemarks);
|
||||
if (!vc.isUnknown())
|
||||
{
|
||||
voiceCapabilities.insert(callsign, vc);
|
||||
}
|
||||
}
|
||||
|
||||
// set as per ATC/pilot
|
||||
if (clientType.startsWith('p'))
|
||||
{
|
||||
// Pilot section
|
||||
const double groundspeed = clientPartsMap["groundspeed"].toDouble();
|
||||
CAircraftSituation situation(position, altitude);
|
||||
situation.setGroundSpeed(CSpeed(groundspeed, CSpeedUnit::kts()));
|
||||
CSimulatedAircraft currentAircraft(user.getCallsign().getStringAsSet(), user, situation);
|
||||
|
||||
QString aircraftIcaoCode = clientPartsMap["planned_aircraft"];
|
||||
if (!aircraftIcaoCode.isEmpty())
|
||||
{
|
||||
// http://uk.flightaware.com/about/faq_aircraft_flight_plan_suffix.rvt
|
||||
// we expect something like H/B772/F B773 B773/F
|
||||
static const QRegularExpression reg("/.");
|
||||
aircraftIcaoCode = aircraftIcaoCode.replace(reg, "").trimmed().toUpper();
|
||||
if (CAircraftIcaoCode::isValidDesignator(aircraftIcaoCode))
|
||||
{
|
||||
currentAircraft.setAircraftIcaoDesignator(aircraftIcaoCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
illegalIcaoCodes.append(aircraftIcaoCode);
|
||||
}
|
||||
}
|
||||
|
||||
aircraft.push_back(currentAircraft);
|
||||
}
|
||||
else if (clientType.startsWith('a'))
|
||||
{
|
||||
// ATC section
|
||||
CLength range;
|
||||
position.setGeodeticHeight(altitude); // the altitude is elevation for a station
|
||||
CAtcStation station(user.getCallsign().getStringAsSet(), user, frequency, position, range);
|
||||
station.setOnline(true);
|
||||
atcStations.push_back(station);
|
||||
}
|
||||
else
|
||||
{
|
||||
BLACK_VERIFY_X(false, Q_FUNC_INFO, "Wrong client type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SectionGeneral:
|
||||
{
|
||||
if (currentLine.contains("UPDATE"))
|
||||
{
|
||||
const QStringList updateParts = currentLine.replace(" ", "").split('=');
|
||||
if (updateParts.length() < 2) break;
|
||||
const QString dts = updateParts.at(1).trimmed();
|
||||
updateTimestampFromFile = QDateTime::fromString(dts, "yyyyMMddHHmmss");
|
||||
updateTimestampFromFile.setOffsetFromUtc(0);
|
||||
bool alreadyRead = (updateTimestampFromFile == this->getUpdateTimestamp());
|
||||
if (alreadyRead) { return; }// still same data, terminate
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SectionFsdServers:
|
||||
{
|
||||
// ident:hostname_or_IP:location:name:clients_connection_allowed:
|
||||
const QStringList fsdServerParts = currentLine.split(':');
|
||||
if (fsdServerParts.size() < 5) break;
|
||||
if (!fsdServerParts.at(4).trimmed().contains('1')) break; // allowed?
|
||||
QString description(fsdServerParts.at(2)); // part(3) could be added
|
||||
BlackMisc::Network::CServer fsdServer(fsdServerParts.at(0), description, fsdServerParts.at(1), 6809, CUser("id", "real name", "email", "password"));
|
||||
fsdServers.push_back(fsdServer);
|
||||
}
|
||||
break;
|
||||
case SectionVoiceServers:
|
||||
{
|
||||
// hostname_or_IP:location:name:clients_connection_allowed:type_of_voice_server:
|
||||
const QStringList voiceServerParts = currentLine.split(':');
|
||||
if (voiceServerParts.size() < 3) break;
|
||||
if (!voiceServerParts.at(3).trimmed().contains('1')) break; // allowed?
|
||||
BlackMisc::Network::CServer voiceServer(voiceServerParts.at(1), voiceServerParts.at(2), voiceServerParts.at(0), -1, CUser());
|
||||
voiceServers.push_back(voiceServer);
|
||||
}
|
||||
break;
|
||||
case SectionNone:
|
||||
default:
|
||||
break;
|
||||
|
||||
} // switch section
|
||||
|
||||
} // for each line
|
||||
|
||||
// this part needs to be synchronized
|
||||
{
|
||||
QWriteLocker wl(&this->m_lock);
|
||||
this->setUpdateTimestamp(updateTimestampFromFile);
|
||||
this->m_aircraft = aircraft;
|
||||
this->m_atcStations = atcStations;
|
||||
this->m_voiceCapabilities = voiceCapabilities;
|
||||
CVatsimSetup vs(this->m_lastGoodSetup.getThreadLocal());
|
||||
vs.setVoiceServers(voiceServers);
|
||||
vs.setFsdServers(fsdServers);
|
||||
vs.setUtcTimestamp(updateTimestampFromFile);
|
||||
this->m_lastGoodSetup.set(vs);
|
||||
}
|
||||
|
||||
// warnings, if required
|
||||
if (!illegalIcaoCodes.isEmpty())
|
||||
{
|
||||
CLogMessage(this).info("Illegal / ignored ICAO code(s) in VATSIM data file: %1") << illegalIcaoCodes.join(", ");
|
||||
}
|
||||
|
||||
// data read finished
|
||||
emit this->dataFileRead(lines.count());
|
||||
emit this->dataRead(CEntityFlags::VatsimDataFile, CEntityFlags::ReadFinished, lines.count());
|
||||
}
|
||||
else
|
||||
{
|
||||
// network error
|
||||
CLogMessage(this).warning("Reading VATSIM data file failed %1 %2") << nwReply->errorString() << nwReply->url().toString();
|
||||
nwReply->abort();
|
||||
emit this->dataRead(CEntityFlags::VatsimDataFile, CEntityFlags::ReadFailed, 0);
|
||||
}
|
||||
}
|
||||
|
||||
const QMap<QString, QString> CVatsimDataFileReader::clientPartsToMap(const QString ¤tLine, const QStringList &clientSectionAttributes)
|
||||
{
|
||||
QMap<QString, QString> parts;
|
||||
if (currentLine.isEmpty()) { return parts; }
|
||||
const QStringList clientParts = currentLine.split(':');
|
||||
for (int i = 0; i < clientSectionAttributes.size(); i++)
|
||||
{
|
||||
BLACK_VERIFY_X(i < clientSectionAttributes.size(), Q_FUNC_INFO, "Wrong section attribute size");
|
||||
BLACK_VERIFY_X(i < clientParts.size(), Q_FUNC_INFO, "Wrong parts size");
|
||||
if (i < clientSectionAttributes.size() || i < clientParts.size()) { continue; }
|
||||
parts.insert(clientSectionAttributes.at(i).toLower(), clientParts.at(i));
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
CVatsimDataFileReader::Section CVatsimDataFileReader::currentLineToSection(const QString ¤tLine)
|
||||
{
|
||||
if (currentLine.contains("!GENERAL", Qt::CaseInsensitive)) { return SectionGeneral; }
|
||||
if (currentLine.contains("!VOICE SERVERS", Qt::CaseInsensitive)) { return SectionVoiceServers; }
|
||||
if (currentLine.contains("!SERVERS", Qt::CaseInsensitive)) { return SectionFsdServers; }
|
||||
if (currentLine.contains("!CLIENTS", Qt::CaseInsensitive)) { return SectionClients; }
|
||||
return SectionNone;
|
||||
}
|
||||
} // ns
|
||||
} // ns
|
||||
167
src/blackcore/vatsim/vatsimdatafilereader.h
Normal file
167
src/blackcore/vatsim/vatsimdatafilereader.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/* Copyright (C) 2013
|
||||
* 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_VATSIM_VATSIMDATAFILEREADER_H
|
||||
#define BLACKCORE_VATSIM_VATSIMDATAFILEREADER_H
|
||||
|
||||
#include "blackcore/blackcoreexport.h"
|
||||
#include "blackcore/data/vatsimsetup.h"
|
||||
#include "blackmisc/aviation/aircrafticaocode.h"
|
||||
#include "blackmisc/aviation/airlineicaocode.h"
|
||||
#include "blackmisc/aviation/atcstationlist.h"
|
||||
#include "blackmisc/aviation/callsign.h"
|
||||
#include "blackmisc/aviation/callsignset.h"
|
||||
#include "blackmisc/datacache.h"
|
||||
#include "blackmisc/network/entityflags.h"
|
||||
#include "blackmisc/network/serverlist.h"
|
||||
#include "blackmisc/network/userlist.h"
|
||||
#include "blackmisc/network/voicecapabilities.h"
|
||||
#include "blackmisc/simulation/simulatedaircraftlist.h"
|
||||
#include "blackcore/threadedreader.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
namespace BlackMisc { namespace Simulation { class CSimulatedAircraft; } }
|
||||
|
||||
namespace BlackCore
|
||||
{
|
||||
namespace Vatsim
|
||||
{
|
||||
//! Read vatsim data file
|
||||
//! \sa http://info.vroute.net/vatsim-data.txt
|
||||
class BLACKCORE_EXPORT CVatsimDataFileReader : public BlackCore::CThreadedReader
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
//! Constructor
|
||||
explicit CVatsimDataFileReader(QObject *owner);
|
||||
|
||||
//! Get aircraft
|
||||
//! \threadsafe
|
||||
BlackMisc::Simulation::CSimulatedAircraftList getAircraft() const;
|
||||
|
||||
//! Get ATC station
|
||||
//! \threadsafe
|
||||
BlackMisc::Aviation::CAtcStationList getAtcStations() const;
|
||||
|
||||
//! Get ATC stations for callsign
|
||||
//! \threadsafe
|
||||
BlackMisc::Aviation::CAtcStationList getAtcStationsForCallsign(const BlackMisc::Aviation::CCallsign &callsign) const;
|
||||
|
||||
//! Get ATC stations for callsigns
|
||||
//! \threadsafe
|
||||
BlackMisc::Aviation::CAtcStationList getAtcStationsForCallsigns(const BlackMisc::Aviation::CCallsignSet &callsigns) const;
|
||||
|
||||
//! Get all voice servers
|
||||
//! \threadsafe
|
||||
BlackMisc::Network::CServerList getVoiceServers() const;
|
||||
|
||||
//! Get all VATSIM FSD servers
|
||||
//! \threadsafe
|
||||
BlackMisc::Network::CServerList getFsdServers() const;
|
||||
|
||||
//! Users for callsign(s)
|
||||
//! \threadsafe
|
||||
BlackMisc::Network::CUserList getUsersForCallsigns(const BlackMisc::Aviation::CCallsignSet &callsigns);
|
||||
|
||||
//! User for callsign
|
||||
//! \threadsafe
|
||||
BlackMisc::Network::CUserList getUsersForCallsign(const BlackMisc::Aviation::CCallsign &callsign);
|
||||
|
||||
//! Controllers for callsigns
|
||||
//! \threadsafe
|
||||
BlackMisc::Network::CUserList getControllersForCallsigns(const BlackMisc::Aviation::CCallsignSet &callsigns);
|
||||
|
||||
//! Controllers for callsign
|
||||
//! \threadsafe
|
||||
BlackMisc::Network::CUserList getControllersForCallsign(const BlackMisc::Aviation::CCallsign &callsign);
|
||||
|
||||
//! Users for callsigns
|
||||
//! \threadsafe
|
||||
BlackMisc::Network::CUserList getPilotsForCallsigns(const BlackMisc::Aviation::CCallsignSet &callsigns);
|
||||
|
||||
//! Users for callsign
|
||||
//! \threadsafe
|
||||
BlackMisc::Network::CUserList getPilotsForCallsign(const BlackMisc::Aviation::CCallsign &callsign);
|
||||
|
||||
//! Aircraft ICAO info for callsign
|
||||
//! \threadsafe
|
||||
BlackMisc::Aviation::CAircraftIcaoCode getAircraftIcaoCode(const BlackMisc::Aviation::CCallsign &callsign);
|
||||
|
||||
//! Airline ICAO info for callsign
|
||||
//! \threadsafe
|
||||
BlackMisc::Aviation::CAirlineIcaoCode getAirlineIcaoCode(const BlackMisc::Aviation::CCallsign &callsign);
|
||||
|
||||
//! Voice capability for callsign
|
||||
//! \threadsafe
|
||||
BlackMisc::Network::CVoiceCapabilities getVoiceCapabilityForCallsign(const BlackMisc::Aviation::CCallsign &callsign);
|
||||
|
||||
//! Update aircraft with VATSIM aircraft data from data file
|
||||
//! \threadsafe
|
||||
void updateWithVatsimDataFileData(BlackMisc::Simulation::CSimulatedAircraft &aircraftToBeUdpated) const;
|
||||
|
||||
//! Start reading in own thread
|
||||
void readInBackgroundThread();
|
||||
|
||||
signals:
|
||||
//! Data have been read
|
||||
void dataFileRead(int lines);
|
||||
|
||||
//! Data have been read
|
||||
void dataRead(BlackMisc::Network::CEntityFlags::Entity entity, BlackMisc::Network::CEntityFlags::ReadState state, int number);
|
||||
|
||||
protected:
|
||||
//! \name BlackCore::CThreadedReader overrides
|
||||
//! @{
|
||||
virtual void cleanup() override;
|
||||
virtual BlackCore::Settings::CSettingsReader getSettings() const override;
|
||||
//! @}
|
||||
|
||||
private slots:
|
||||
//! Data have been read, parse VATSIM file
|
||||
void ps_parseVatsimFile(QNetworkReply *nwReply);
|
||||
|
||||
//! Read / re-read data file
|
||||
void ps_read();
|
||||
|
||||
private:
|
||||
BlackMisc::Aviation::CAtcStationList m_atcStations;
|
||||
BlackMisc::Simulation::CSimulatedAircraftList m_aircraft;
|
||||
BlackMisc::CData<BlackCore::Data::VatsimSetup> m_lastGoodSetup { this };
|
||||
BlackMisc::CSettingReadOnly<BlackCore::Settings::SettingsVatsimDataFile> m_settings { this };
|
||||
QMap<BlackMisc::Aviation::CCallsign, BlackMisc::Network::CVoiceCapabilities> m_voiceCapabilities;
|
||||
|
||||
//! Split line and assign values to their corresponding attribute names
|
||||
static const QMap<QString, QString> clientPartsToMap(const QString ¤tLine, const QStringList &clientSectionAttributes);
|
||||
|
||||
//! Section in file
|
||||
enum Section
|
||||
{
|
||||
SectionNone,
|
||||
SectionFsdServers,
|
||||
SectionVoiceServers,
|
||||
SectionClients,
|
||||
SectionGeneral
|
||||
};
|
||||
|
||||
//! Get current section
|
||||
static Section currentLineToSection(const QString ¤tLine);
|
||||
};
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
#endif // guard
|
||||
158
src/blackcore/vatsim/vatsimmetarreader.cpp
Normal file
158
src/blackcore/vatsim/vatsimmetarreader.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
/* Copyright (C) 2015
|
||||
* 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 "blackcore/vatsim/vatsimmetarreader.h"
|
||||
#include "blackcore/application.h"
|
||||
#include "blackmisc/compare.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::Network;
|
||||
using namespace BlackMisc::Weather;
|
||||
using namespace BlackCore::Data;
|
||||
|
||||
namespace BlackCore
|
||||
{
|
||||
namespace Vatsim
|
||||
{
|
||||
CVatsimMetarReader::CVatsimMetarReader(QObject *owner) :
|
||||
CThreadedReader(owner, "CVatsimMetarReader")
|
||||
{
|
||||
this->connect(this->m_updateTimer, &QTimer::timeout, this, &CVatsimMetarReader::ps_readMetars);
|
||||
}
|
||||
|
||||
void CVatsimMetarReader::readInBackgroundThread()
|
||||
{
|
||||
bool s = QMetaObject::invokeMethod(this, "ps_readMetars");
|
||||
Q_ASSERT_X(s, Q_FUNC_INFO, "Cannot invoke");
|
||||
Q_UNUSED(s);
|
||||
}
|
||||
|
||||
CMetarSet CVatsimMetarReader::getMetars() const
|
||||
{
|
||||
QReadLocker l(&m_lock);
|
||||
return m_metars;
|
||||
}
|
||||
|
||||
CMetar CVatsimMetarReader::getMetarForAirport(const Aviation::CAirportIcaoCode &icao) const
|
||||
{
|
||||
QReadLocker l(&m_lock);
|
||||
return m_metars.getMetarForAirport(icao);
|
||||
}
|
||||
|
||||
int CVatsimMetarReader::getMetarsCount() const
|
||||
{
|
||||
QReadLocker l(&m_lock);
|
||||
return m_metars.size();
|
||||
}
|
||||
|
||||
void CVatsimMetarReader::cleanup()
|
||||
{
|
||||
// void
|
||||
}
|
||||
|
||||
Settings::CSettingsReader CVatsimMetarReader::getSettings() const
|
||||
{
|
||||
return m_settings.get();
|
||||
}
|
||||
|
||||
void CVatsimMetarReader::ps_readMetars()
|
||||
{
|
||||
this->threadAssertCheck();
|
||||
CFailoverUrlList urls(sApp->getVatsimMetarUrls());
|
||||
const CUrl url(urls.obtainNextWorkingUrl(true));
|
||||
if (url.isEmpty()) { return; }
|
||||
Q_ASSERT_X(sApp, Q_FUNC_INFO, "No Application");
|
||||
sApp->getFromNetwork(url.withAppendedQuery("id=all"), { this, &CVatsimMetarReader::ps_decodeMetars});
|
||||
}
|
||||
|
||||
void CVatsimMetarReader::ps_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);
|
||||
|
||||
this->threadAssertCheck();
|
||||
|
||||
// Worker thread, make sure to write thread safe!
|
||||
if (this->isAbandoned())
|
||||
{
|
||||
CLogMessage(this).debug() << Q_FUNC_INFO;
|
||||
CLogMessage(this).info("terminated METAR decoding process"); // for users
|
||||
return; // stop, terminate straight away, ending thread
|
||||
}
|
||||
|
||||
if (nwReply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
QString metarData = nwReply->readAll();
|
||||
nwReply->close(); // close asap
|
||||
CMetarSet metars;
|
||||
|
||||
QString invalidMetars;
|
||||
int invalidLineCount = 0;
|
||||
QTextStream lineReader(&metarData);
|
||||
while (!lineReader.atEnd())
|
||||
{
|
||||
if (this->isAbandoned()) { return; }
|
||||
QString line = lineReader.readLine();
|
||||
CMetar metar = m_metarDecoder.decode(line);
|
||||
if (metar != CMetar())
|
||||
{
|
||||
metars.push_back(metar);
|
||||
}
|
||||
else
|
||||
{
|
||||
invalidMetars += line;
|
||||
invalidMetars += "\n";
|
||||
invalidLineCount++;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
QWriteLocker l(&m_lock);
|
||||
m_metars = metars;
|
||||
}
|
||||
|
||||
// I could use those for logging, etc.
|
||||
Q_UNUSED(invalidMetars);
|
||||
if (invalidLineCount > 0)
|
||||
{
|
||||
// Regular issue, log it, but do not show to user
|
||||
CLogMessage(this).debug() << "Reading METARs failed for entries" << invalidLineCount;
|
||||
}
|
||||
emit metarsRead(metars);
|
||||
emit dataRead(CEntityFlags::MetarEntity, CEntityFlags::ReadFinished, metars.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// network error
|
||||
CLogMessage(this).warning("Reading METARs failed %1 %2") << nwReply->errorString() << nwReply->url().toString();
|
||||
nwReply->abort();
|
||||
emit dataRead(CEntityFlags::MetarEntity, CEntityFlags::ReadFailed, 0);
|
||||
}
|
||||
} // method
|
||||
} // ns
|
||||
} // ns
|
||||
84
src/blackcore/vatsim/vatsimmetarreader.h
Normal file
84
src/blackcore/vatsim/vatsimmetarreader.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/* Copyright (C) 2015
|
||||
* 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_VATSIM_VATSIMMETARREADER_H
|
||||
#define BLACKCORE_VATSIM_VATSIMMETARREADER_H
|
||||
|
||||
#include "blackcore/blackcoreexport.h"
|
||||
#include "blackmisc/aviation/airporticaocode.h"
|
||||
#include "blackmisc/network/entityflags.h"
|
||||
#include "blackcore/threadedreader.h"
|
||||
#include "blackmisc/weather/metar.h"
|
||||
#include "blackmisc/weather/metardecoder.h"
|
||||
#include "blackmisc/weather/metarset.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
namespace BlackCore
|
||||
{
|
||||
namespace Vatsim
|
||||
{
|
||||
//! Read bookings from VATSIM
|
||||
class BLACKCORE_EXPORT CVatsimMetarReader : public BlackCore::CThreadedReader
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
//! Constructor
|
||||
explicit CVatsimMetarReader(QObject *owner);
|
||||
|
||||
//! Read / re-read bookings
|
||||
void readInBackgroundThread();
|
||||
|
||||
//! Get METARs
|
||||
//! \threadsafe
|
||||
virtual BlackMisc::Weather::CMetarSet getMetars() const;
|
||||
|
||||
//! Get METAR for airport
|
||||
//! \threadsafe
|
||||
virtual BlackMisc::Weather::CMetar getMetarForAirport(const BlackMisc::Aviation::CAirportIcaoCode &icao) const;
|
||||
|
||||
//! Get METARs count
|
||||
//! \threadsafe
|
||||
virtual int getMetarsCount() const;
|
||||
|
||||
signals:
|
||||
//! METARs have been read and converted to BlackMisc::Weather::CMetarSet
|
||||
void metarsRead(const BlackMisc::Weather::CMetarSet &metars);
|
||||
|
||||
//! Data have been read
|
||||
void dataRead(BlackMisc::Network::CEntityFlags::Entity entity, BlackMisc::Network::CEntityFlags::ReadState state, int number);
|
||||
|
||||
protected:
|
||||
//! \name BlackCore::CThreadedReader overrides
|
||||
//! @{
|
||||
virtual void cleanup() override;
|
||||
virtual BlackCore::Settings::CSettingsReader getSettings() const override;
|
||||
//! @}
|
||||
|
||||
private slots:
|
||||
//! Decode METARs
|
||||
//! \threadsafe
|
||||
void ps_decodeMetars(QNetworkReply *nwReply);
|
||||
|
||||
//! Do reading
|
||||
void ps_readMetars();
|
||||
|
||||
private:
|
||||
BlackMisc::Weather::CMetarDecoder m_metarDecoder;
|
||||
BlackMisc::Weather::CMetarSet m_metars;
|
||||
BlackMisc::CSettingReadOnly<BlackCore::Settings::SettingsVatsimMetars> m_settings { this };
|
||||
};
|
||||
} // ns
|
||||
} // ns
|
||||
#endif // guard
|
||||
181
src/blackcore/vatsim/vatsimstatusfilereader.cpp
Normal file
181
src/blackcore/vatsim/vatsimstatusfilereader.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
/* Copyright (C) 2013
|
||||
* 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 "blackcore/vatsim/vatsimstatusfilereader.h"
|
||||
#include "blackcore/application.h"
|
||||
#include "blackcore/data/globalsetup.h"
|
||||
#include "blackmisc/logmessage.h"
|
||||
#include "blackmisc/network/url.h"
|
||||
#include "blackmisc/network/urllist.h"
|
||||
#include "blackmisc/statusmessage.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QList>
|
||||
#include <QMetaObject>
|
||||
#include <QNetworkReply>
|
||||
#include <QRegExp>
|
||||
#include <QScopedPointer>
|
||||
#include <QScopedPointerDeleteLater>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QtGlobal>
|
||||
|
||||
using namespace BlackMisc;
|
||||
using namespace BlackMisc::Aviation;
|
||||
using namespace BlackMisc::Network;
|
||||
using namespace BlackMisc::Simulation;
|
||||
using namespace BlackMisc::PhysicalQuantities;
|
||||
using namespace BlackCore::Data;
|
||||
using namespace BlackCore::Settings;
|
||||
|
||||
namespace BlackCore
|
||||
{
|
||||
namespace Vatsim
|
||||
{
|
||||
CVatsimStatusFileReader::CVatsimStatusFileReader(QObject *owner) :
|
||||
CThreadedReader(owner, "CVatsimStatusFileReader")
|
||||
{
|
||||
// do not connect with time, will be read once at startup
|
||||
}
|
||||
|
||||
void CVatsimStatusFileReader::readInBackgroundThread()
|
||||
{
|
||||
bool s = QMetaObject::invokeMethod(this, "ps_read");
|
||||
Q_ASSERT_X(s, Q_FUNC_INFO, "Invoke failed");
|
||||
Q_UNUSED(s);
|
||||
}
|
||||
|
||||
CUrlList CVatsimStatusFileReader::getMetarFileUrls() const
|
||||
{
|
||||
return this->m_lastGoodSetup.get().getMetarFileUrls();
|
||||
}
|
||||
|
||||
CUrlList CVatsimStatusFileReader::getDataFileUrls() const
|
||||
{
|
||||
return this->m_lastGoodSetup.get().getDataFileUrls();
|
||||
}
|
||||
|
||||
void CVatsimStatusFileReader::cleanup()
|
||||
{
|
||||
// void
|
||||
}
|
||||
|
||||
void CVatsimStatusFileReader::ps_read()
|
||||
{
|
||||
this->threadAssertCheck();
|
||||
|
||||
Q_ASSERT_X(sApp, Q_FUNC_INFO, "Missing application");
|
||||
CFailoverUrlList urls(sApp->getGlobalSetup().getVatsimStatusFileUrls());
|
||||
const CUrl url(urls.obtainNextWorkingUrl(true)); // random working URL
|
||||
if (url.isEmpty()) { return; }
|
||||
sApp->getFromNetwork(url, { this, &CVatsimStatusFileReader::ps_parseVatsimFile});
|
||||
}
|
||||
|
||||
void CVatsimStatusFileReader::ps_parseVatsimFile(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);
|
||||
|
||||
this->threadAssertCheck();
|
||||
|
||||
// Worker thread, make sure to write only synced here!
|
||||
if (this->isAbandoned())
|
||||
{
|
||||
CLogMessage(this).debug() << Q_FUNC_INFO;
|
||||
CLogMessage(this).info("Terminated VATSIM status file parsing process"); // for users
|
||||
return; // stop, terminate straight away, ending thread
|
||||
}
|
||||
|
||||
QStringList illegalIcaoCodes;
|
||||
if (nwReply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
const QString dataFileData = nwReply->readAll();
|
||||
nwReply->close(); // close asap
|
||||
|
||||
if (dataFileData.isEmpty()) return;
|
||||
const QStringList lines = dataFileData.split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
|
||||
if (lines.isEmpty()) { return; }
|
||||
|
||||
CUrlList dataFiles;
|
||||
CUrlList serverFiles;
|
||||
CUrlList metarFiles;
|
||||
|
||||
for (const QString &cl : lines)
|
||||
{
|
||||
if (this->isAbandoned())
|
||||
{
|
||||
CLogMessage(this).debug() << Q_FUNC_INFO;
|
||||
CLogMessage(this).info("Terminated status parsing process"); // for users
|
||||
return; // stop, terminate straight away, ending thread
|
||||
}
|
||||
|
||||
// parse lines
|
||||
const QString currentLine(cl.trimmed());
|
||||
if (currentLine.isEmpty()) { continue; }
|
||||
if (currentLine.startsWith(";")) { continue; }
|
||||
if (!currentLine.contains("=")) { continue; }
|
||||
|
||||
const QStringList parts(currentLine.split('='));
|
||||
if (parts.length() != 2) { continue; }
|
||||
const QString key(parts[0].trimmed().toLower());
|
||||
const QString value(parts[1].trimmed());
|
||||
const CUrl url(value);
|
||||
if (key.startsWith("url0"))
|
||||
{
|
||||
dataFiles.push_back(url);
|
||||
}
|
||||
else if (key.startsWith("url1"))
|
||||
{
|
||||
serverFiles.push_back(url);
|
||||
}
|
||||
else if (key.startsWith("metar"))
|
||||
{
|
||||
metarFiles.push_back(url);
|
||||
}
|
||||
else if (key.startsWith("atis"))
|
||||
{
|
||||
// not yet used
|
||||
}
|
||||
} // for each line
|
||||
|
||||
// this part needs to be synchronized
|
||||
{
|
||||
// cache itself is thread safe
|
||||
CVatsimSetup vs(this->m_lastGoodSetup.get());
|
||||
vs.setDataFileUrls(dataFiles);
|
||||
vs.setMetarFileUrls(metarFiles);
|
||||
vs.setServerFileUrls(serverFiles);
|
||||
vs.setUtcTimestamp(QDateTime::currentDateTime());
|
||||
this->m_lastGoodSetup.set(vs);
|
||||
}
|
||||
|
||||
// warnings, if required
|
||||
if (!illegalIcaoCodes.isEmpty())
|
||||
{
|
||||
CLogMessage(this).info("Illegal / ignored ICAO code(s) in VATSIM data file: %1") << illegalIcaoCodes.join(", ");
|
||||
}
|
||||
|
||||
// data read finished
|
||||
emit this->dataFileRead(lines.count());
|
||||
emit this->dataRead(CEntityFlags::VatsimStatusFile, CEntityFlags::ReadFinished, lines.count());
|
||||
}
|
||||
else
|
||||
{
|
||||
// network error
|
||||
CLogMessage(this).warning("Reading VATSIM status file failed %1 %2") << nwReply->errorString() << nwReply->url().toString();
|
||||
nwReply->abort();
|
||||
emit this->dataRead(CEntityFlags::VatsimStatusFile, CEntityFlags::ReadFailed, 0);
|
||||
}
|
||||
}
|
||||
} // ns
|
||||
} // ns
|
||||
78
src/blackcore/vatsim/vatsimstatusfilereader.h
Normal file
78
src/blackcore/vatsim/vatsimstatusfilereader.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/* Copyright (C) 2013
|
||||
* 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_VATSIM_VATSIMSTATUSFILEREADER_H
|
||||
#define BLACKCORE_VATSIM_VATSIMSTATUSFILEREADER_H
|
||||
|
||||
#include "blackcore/blackcoreexport.h"
|
||||
#include "blackcore/data/vatsimsetup.h"
|
||||
#include "blackmisc/datacache.h"
|
||||
#include "blackmisc/network/entityflags.h"
|
||||
#include "blackmisc/network/urllist.h"
|
||||
#include "blackcore/threadedreader.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
namespace BlackCore
|
||||
{
|
||||
namespace Vatsim
|
||||
{
|
||||
//! Sole purpose is to read the URLs where VATSIM data can be downloaded
|
||||
//! \sa https://status.vatsim.net/
|
||||
class BLACKCORE_EXPORT CVatsimStatusFileReader : public BlackCore::CThreadedReader
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
//! Constructor
|
||||
explicit CVatsimStatusFileReader(QObject *owner);
|
||||
|
||||
//! METAR URLs
|
||||
//! \threadsafe
|
||||
BlackMisc::Network::CUrlList getMetarFileUrls() const;
|
||||
|
||||
//! Data file URLs
|
||||
//! \threadsafe
|
||||
BlackMisc::Network::CUrlList getDataFileUrls() const;
|
||||
|
||||
public slots:
|
||||
//! Start reading in own thread
|
||||
void readInBackgroundThread();
|
||||
|
||||
signals:
|
||||
//! Data have been read
|
||||
void dataFileRead(int lines);
|
||||
|
||||
//! Data have been read
|
||||
void dataRead(BlackMisc::Network::CEntityFlags::Entity entity, BlackMisc::Network::CEntityFlags::ReadState state, int number);
|
||||
|
||||
protected:
|
||||
//! \name BlackCore::CThreadedReader overrides
|
||||
//! @{
|
||||
virtual void cleanup() override;
|
||||
//! @}
|
||||
|
||||
private slots:
|
||||
//! Data have been read, parse VATSIM file
|
||||
void ps_parseVatsimFile(QNetworkReply *nwReply);
|
||||
|
||||
//! Read / re-read data file
|
||||
void ps_read();
|
||||
|
||||
private:
|
||||
BlackMisc::CData<BlackCore::Data::VatsimSetup> m_lastGoodSetup { this };
|
||||
};
|
||||
} // ns
|
||||
} // ns
|
||||
|
||||
#endif // guard
|
||||
Reference in New Issue
Block a user