refs #288, added FSD servers to VATSIM reader

* Voice and FSD servers can be obtained via context
* using QSCopedPointer for network reply
* minor fixes / renaming
This commit is contained in:
Klaus Basan
2014-11-08 16:41:15 +01:00
committed by Roland Winklmeier
parent adba17da0f
commit a401ce439d
5 changed files with 109 additions and 60 deletions

View File

@@ -10,6 +10,7 @@
#include "blackmisc/sequence.h" #include "blackmisc/sequence.h"
#include "blackmisc/avatcstation.h" #include "blackmisc/avatcstation.h"
#include "blackmisc/nwuser.h" #include "blackmisc/nwuser.h"
#include "blackmisc/logmessage.h"
#include "vatsimbookingreader.h" #include "vatsimbookingreader.h"
#include <QtXml/QDomElement> #include <QtXml/QDomElement>
@@ -56,12 +57,17 @@ namespace BlackCore
/* /*
* Parse bookings * Parse bookings
*/ */
void CVatsimBookingReader::parseBookings(QNetworkReply *nwReply) void CVatsimBookingReader::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);
// Worker thread, make sure to write no members here! // Worker thread, make sure to write no members here!
if (this->isStopped()) if (this->isStopped())
{ {
qDebug() << "terminated" << Q_FUNC_INFO; CLogMessage(this).debug() << Q_FUNC_INFO;
CLogMessage(this).info("terminated booking parsing process"); // for users
return; // stop, terminate straight away, ending thread return; // stop, terminate straight away, ending thread
} }
@@ -69,6 +75,7 @@ namespace BlackCore
{ {
static const QString timestampFormat("yyyy-MM-dd HH:mm:ss"); static const QString timestampFormat("yyyy-MM-dd HH:mm:ss");
QString xmlData = nwReply->readAll(); QString xmlData = nwReply->readAll();
nwReply->close(); // close asap
QDomDocument doc; QDomDocument doc;
QDateTime updateTimestamp = QDateTime::currentDateTimeUtc(); QDateTime updateTimestamp = QDateTime::currentDateTimeUtc();
@@ -95,7 +102,8 @@ namespace BlackCore
{ {
if (this->isStopped()) if (this->isStopped())
{ {
qDebug() << "terminated" << Q_FUNC_INFO; CLogMessage(this).debug() << Q_FUNC_INFO;
CLogMessage(this).info("terminated booking parsing process"); // for users
return; // stop, terminate straight away, ending thread return; // stop, terminate straight away, ending thread
} }
@@ -148,11 +156,10 @@ namespace BlackCore
this->setUpdateTimestamp(updateTimestamp); // thread safe update this->setUpdateTimestamp(updateTimestamp); // thread safe update
emit this->dataRead(bookedStations); emit this->dataRead(bookedStations);
} // node } // node
} // content } else {
// with errors
nwReply->close(); nwReply->abort();
nwReply->deleteLater(); }
} // method } // method
} // namespace } // namespace

View File

@@ -46,7 +46,7 @@ namespace BlackCore
//! Parse received bookings //! Parse received bookings
//! \threadsafe //! \threadsafe
void parseBookings(QNetworkReply *nwReply); void parseBookings(QNetworkReply *nwReplyPtr);
signals: signals:
//! Bookings have been read and converted to BlackMisc::Aviation::CAtcStationList //! Bookings have been read and converted to BlackMisc::Aviation::CAtcStationList

View File

@@ -11,6 +11,7 @@
#include "blackmisc/avatcstation.h" #include "blackmisc/avatcstation.h"
#include "blackmisc/nwuser.h" #include "blackmisc/nwuser.h"
#include "blackmisc/nwserver.h" #include "blackmisc/nwserver.h"
#include "blackmisc/logmessage.h"
#include "vatsimdatafilereader.h" #include "vatsimdatafilereader.h"
#include <QRegularExpression> #include <QRegularExpression>
@@ -69,6 +70,12 @@ namespace BlackCore
return this->m_voiceServers; return this->m_voiceServers;
} }
CServerList CVatsimDataFileReader::getFsdServers() const
{
QReadLocker rl(&this->m_lock);
return this->m_fsdServers;
}
CUserList CVatsimDataFileReader::getPilotsForCallsigns(const CCallsignList &callsigns) CUserList CVatsimDataFileReader::getPilotsForCallsigns(const CCallsignList &callsigns)
{ {
return this->getAircrafts().findByCallsigns(callsigns).transform(Predicates::MemberTransform(&CAircraft::getPilot)); return this->getAircrafts().findByCallsigns(callsigns).transform(Predicates::MemberTransform(&CAircraft::getPilot));
@@ -153,12 +160,17 @@ namespace BlackCore
* Data file read from XML * Data file read from XML
* Example: http://info.vroute.net/vatsim-data.txt * Example: http://info.vroute.net/vatsim-data.txt
*/ */
void CVatsimDataFileReader::parseVatsimFileInBackground(QNetworkReply *nwReply) void CVatsimDataFileReader::parseVatsimFileInBackground(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 only synced here! // Worker thread, make sure to write only synced here!
if (this->isStopped()) if (this->isStopped())
{ {
qDebug() << "terminated" << Q_FUNC_INFO; CLogMessage(this).debug() << Q_FUNC_INFO;
CLogMessage(this).info("terminated VATSIM file parsing process"); // for users
return; // stop, terminate straight away, ending thread return; // stop, terminate straight away, ending thread
} }
@@ -166,12 +178,15 @@ namespace BlackCore
if (nwReply->error() == QNetworkReply::NoError) if (nwReply->error() == QNetworkReply::NoError)
{ {
const QString dataFileData = nwReply->readAll(); const QString dataFileData = nwReply->readAll();
nwReply->close(); // close asap
if (dataFileData.isEmpty()) return; if (dataFileData.isEmpty()) return;
QStringList lines = dataFileData.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); QStringList lines = dataFileData.split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
if (lines.isEmpty()) return; if (lines.isEmpty()) return;
// build on local vars for thread safety // build on local vars for thread safety
CServerList voiceServers; CServerList voiceServers;
CServerList fsdServers;
CAtcStationList atcStations; CAtcStationList atcStations;
CAircraftList aircrafts; CAircraftList aircrafts;
QMap<CCallsign, CVoiceCapabilities> voiceCapabilities; QMap<CCallsign, CVoiceCapabilities> voiceCapabilities;
@@ -183,7 +198,8 @@ namespace BlackCore
{ {
if (this->isStopped()) if (this->isStopped())
{ {
qDebug() << "terminated" << Q_FUNC_INFO; CLogMessage(this).debug() << Q_FUNC_INFO;
CLogMessage(this).info("terminated booking parsing process"); // for users
return; // stop, terminate straight away, ending thread return; // stop, terminate straight away, ending thread
} }
@@ -204,24 +220,10 @@ namespace BlackCore
} }
else if (currentLine.startsWith("!")) else if (currentLine.startsWith("!"))
{ {
if (currentLine.contains("GENERAL", Qt::CaseInsensitive)) section = currentLineToSection(currentLine);
{
section = SectionGeneral;
}
else if (currentLine.contains("VOICE SERVERS", Qt::CaseInsensitive))
{
section = SectionVoiceServer;
}
else if (currentLine.contains("CLIENTS", Qt::CaseInsensitive))
{
section = SectionClients;
}
else
{
section = SectionNone;
}
continue; continue;
} }
switch (section) switch (section)
{ {
case SectionClients: case SectionClients:
@@ -305,42 +307,61 @@ namespace BlackCore
bool alreadyRead = (updateTimestampFromFile == this->getUpdateTimestamp()); bool alreadyRead = (updateTimestampFromFile == this->getUpdateTimestamp());
if (alreadyRead) { return; }// still same data, terminate if (alreadyRead) { return; }// still same data, terminate
} }
break;
case SectionVoiceServer:
{
QStringList voiceServerParts = currentLine.split(':');
if (voiceServerParts.size() < 3) continue;
BlackMisc::Network::CServer voiceServer(voiceServerParts.at(1), voiceServerParts.at(2), voiceServerParts.at(0), -1, CUser());
voiceServers.push_back(voiceServer);
}
break;
case SectionNone:
default:
break;
} }
} // for each break;
case SectionFsdServers:
{
// ident:hostname_or_IP:location:name:clients_connection_allowed:
QStringList fsdServerParts = currentLine.split(':');
if (fsdServerParts.size() < 4) continue;
if (!fsdServerParts.at(3).trimmed().contains('1')) continue; // allowed?
BlackMisc::Network::CServer fsdServer(fsdServerParts.at(0), fsdServerParts.at(2), 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:
QStringList voiceServerParts = currentLine.split(':');
if (voiceServerParts.size() < 3) continue;
if (!voiceServerParts.at(3).trimmed().contains('1')) continue; // 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
// this part needs to be synchronized // this part needs to be synchronized
{ {
QWriteLocker wl(&this->m_lock); QWriteLocker wl(&this->m_lock);
this->m_updateTimestamp = updateTimestampFromFile; this->setUpdateTimestamp(updateTimestampFromFile);
this->m_aircrafts = aircrafts; this->m_aircrafts = aircrafts;
this->m_atcStations = atcStations; this->m_atcStations = atcStations;
this->m_voiceServers = voiceServers; this->m_voiceServers = voiceServers;
this->m_fsdServers = fsdServers;
this->m_voiceCapabilities = voiceCapabilities; this->m_voiceCapabilities = voiceCapabilities;
} }
} // read success } // for each line
nwReply->close();
nwReply->deleteLater(); // we are responsible for deleting this
emit this->dataRead();
// warnings, if required // warnings, if required
if (!illegalIcaoCodes.isEmpty()) if (!illegalIcaoCodes.isEmpty())
{ {
qWarning() << "Illegal ICAO code(s) in VATSIM data file:" << illegalIcaoCodes.join(", "); CLogMessage(this).info("Illegal / ignored ICAO code(s) in VATSIM data file: %1") << illegalIcaoCodes.join(", ");
} }
// data read finished
emit this->dataRead();
} }
else
{
// network error
nwReply->abort();
}
} }
const QMap<QString, QString> CVatsimDataFileReader::clientPartsToMap(const QString &currentLine, const QStringList &clientSectionAttributes) const QMap<QString, QString> CVatsimDataFileReader::clientPartsToMap(const QString &currentLine, const QStringList &clientSectionAttributes)
@@ -355,4 +376,13 @@ namespace BlackCore
} }
return parts; return parts;
} }
CVatsimDataFileReader::Section CVatsimDataFileReader::currentLineToSection(const QString &currentLine)
{
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;
}
} // namespace } // namespace

View File

@@ -53,6 +53,10 @@ namespace BlackCore
//! \threadsafe //! \threadsafe
BlackMisc::Network::CServerList getVoiceServers() const; BlackMisc::Network::CServerList getVoiceServers() const;
//! Get all FSD servers
//! \threadsafe
BlackMisc::Network::CServerList getFsdServers() const;
//! Users for callsign(s) //! Users for callsign(s)
//! \threadsafe //! \threadsafe
BlackMisc::Network::CUserList getUsersForCallsigns(const BlackMisc::Aviation::CCallsignList &callsigns); BlackMisc::Network::CUserList getUsersForCallsigns(const BlackMisc::Aviation::CCallsignList &callsigns);
@@ -98,6 +102,7 @@ namespace BlackCore
int m_currentUrlIndex; int m_currentUrlIndex;
QNetworkAccessManager *m_networkManager; QNetworkAccessManager *m_networkManager;
BlackMisc::Network::CServerList m_voiceServers; BlackMisc::Network::CServerList m_voiceServers;
BlackMisc::Network::CServerList m_fsdServers;
BlackMisc::Aviation::CAtcStationList m_atcStations; BlackMisc::Aviation::CAtcStationList m_atcStations;
BlackMisc::Aviation::CAircraftList m_aircrafts; BlackMisc::Aviation::CAircraftList m_aircrafts;
QMap<BlackMisc::Aviation::CCallsign, BlackMisc::Network::CVoiceCapabilities> m_voiceCapabilities; QMap<BlackMisc::Aviation::CCallsign, BlackMisc::Network::CVoiceCapabilities> m_voiceCapabilities;
@@ -109,13 +114,17 @@ namespace BlackCore
enum Section enum Section
{ {
SectionNone, SectionNone,
SectionVoiceServer, SectionFsdServers,
SectionVoiceServers,
SectionClients, SectionClients,
SectionGeneral SectionGeneral
}; };
//! Get current section
static Section currentLineToSection(const QString &currentLine);
//! Parse the VATSIM data file in backgroun //! Parse the VATSIM data file in backgroun
void parseVatsimFileInBackground(QNetworkReply *nwReply); void parseVatsimFileInBackground(QNetworkReply *nwReplyPtr);
signals: signals:
//! Data have been read //! Data have been read

View File

@@ -34,8 +34,8 @@ namespace BlackMisc
//! Destructor //! Destructor
virtual ~CThreadedReader() virtual ~CThreadedReader()
{ {
delete m_updateTimer;
this->stop(); this->stop();
delete m_updateTimer;
} }
//! Thread safe, set update timestamp //! Thread safe, set update timestamp
@@ -59,10 +59,13 @@ namespace BlackMisc
virtual void stop() virtual void stop()
{ {
if (this->isStopped()) return; if (this->isStopped()) return;
// thread safe stopping timer and mark as stopped
this->setStopFlag(); this->setStopFlag();
this->setInterval(0); this->setInterval(0);
// shutdown pending // shutdown pending
QWriteLocker(&this->m_lock);
if (this->m_pendingFuture.isRunning()) if (this->m_pendingFuture.isRunning())
{ {
// cancel does not work with all futures, especially not with QConcurrent::run // cancel does not work with all futures, especially not with QConcurrent::run
@@ -78,7 +81,6 @@ namespace BlackMisc
// cancel or stop flag above should terminate QFuture // cancel or stop flag above should terminate QFuture
this->m_pendingFuture.waitForFinished(); this->m_pendingFuture.waitForFinished();
} }
//! Thread safe, is in state stopped? //! Thread safe, is in state stopped?
@@ -114,8 +116,7 @@ namespace BlackMisc
protected: protected:
//! Constructor //! Constructor
CThreadedReader() : CThreadedReader() : m_lock(QReadWriteLock::Recursive)
m_updateTimer(nullptr), m_stopped(false), m_pendingNetworkReply(nullptr), m_lock(QReadWriteLock::Recursive)
{ {
this->m_updateTimer = new QTimer(); this->m_updateTimer = new QTimer();
} }
@@ -144,12 +145,14 @@ namespace BlackMisc
this->m_stopped = true; this->m_stopped = true;
} }
QDateTime m_updateTimestamp; //!< when was file / resource read QTimer *m_updateTimer = nullptr; //!< update timer
QTimer *m_updateTimer; //!< update times mutable QReadWriteLock m_lock; //!< lock
bool m_stopped; //!< mark as stopped, threads should terminate
QFuture<FutureRet> m_pendingFuture; //!< optional future to be stopped private:
QNetworkReply *m_pendingNetworkReply; //!< optional future to be stopped QDateTime m_updateTimestamp; //!< when was file / resource read
mutable QReadWriteLock m_lock; //!< lock bool m_stopped = false; //!< mark as stopped, threads should terminate
QFuture<FutureRet> m_pendingFuture; //!< optional future to be stopped
QNetworkReply *m_pendingNetworkReply = nullptr; //!< optional network reply to be stopped
}; };
} // namespace } // namespace