mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-30 05:51:23 +08:00
refs #287 Thread safety, fix "memory access violation"
* some improved comments and information * QMetaObject::invokeMethod in tool.cpp as thread safe invocation * common base class for threaded readers * removed event class, using QMetaObject::invoke instead for forcing calls in main event loop * stop methods for readers, as used for graceful shutdown (preparing for thread safe destruction of objects) * graceful shutdown for network context * calls in tool now via inkoke for thread safety (only thread safe methods called directly)
This commit is contained in:
@@ -45,7 +45,9 @@ int main(int argc, char *argv[])
|
|||||||
// configure DBus server
|
// configure DBus server
|
||||||
QString dBusAddress = BlackCore::CDBusServer::sessionDBusServer();
|
QString dBusAddress = BlackCore::CDBusServer::sessionDBusServer();
|
||||||
if (input.startsWith("2"))
|
if (input.startsWith("2"))
|
||||||
|
{
|
||||||
dBusAddress = BlackCore::CDBusServer::systemDBusServer();
|
dBusAddress = BlackCore::CDBusServer::systemDBusServer();
|
||||||
|
}
|
||||||
else if (input.startsWith("3"))
|
else if (input.startsWith("3"))
|
||||||
{
|
{
|
||||||
qDebug() << "found: " << BlackMisc::CNetworkUtils::getKnownIpAddresses();
|
qDebug() << "found: " << BlackMisc::CNetworkUtils::getKnownIpAddresses();
|
||||||
@@ -63,8 +65,10 @@ int main(int argc, char *argv[])
|
|||||||
BlackCore::CRuntime *core = remoteAudio ?
|
BlackCore::CRuntime *core = remoteAudio ?
|
||||||
new BlackCore::CRuntime(BlackCore::CRuntimeConfig::forCoreAllLocalInDBusNoAudio(dBusAddress), &a) :
|
new BlackCore::CRuntime(BlackCore::CRuntimeConfig::forCoreAllLocalInDBusNoAudio(dBusAddress), &a) :
|
||||||
new BlackCore::CRuntime(BlackCore::CRuntimeConfig::forCoreAllLocalInDBus(dBusAddress), &a);
|
new BlackCore::CRuntime(BlackCore::CRuntimeConfig::forCoreAllLocalInDBus(dBusAddress), &a);
|
||||||
|
|
||||||
|
// tool to allow input indepent from event loop
|
||||||
QtConcurrent::run(BlackMiscTest::Tool::serverLoop, core); // QFuture<void> future
|
QtConcurrent::run(BlackMiscTest::Tool::serverLoop, core); // QFuture<void> future
|
||||||
qDebug() << "Server event loop, pid:" << BlackMiscTest::Tool::getPid();
|
qDebug() << "Server event loop, pid:" << BlackMiscTest::Tool::getPid() << "Thread id:" << QThread::currentThreadId();
|
||||||
|
|
||||||
// end
|
// end
|
||||||
return a.exec();
|
return a.exec();
|
||||||
|
|||||||
@@ -8,11 +8,15 @@
|
|||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
|
#include <QMetaObject>
|
||||||
|
#include <QGenericReturnArgument>
|
||||||
|
#include <QGenericArgument>
|
||||||
#include <QtConcurrent/QtConcurrent>
|
#include <QtConcurrent/QtConcurrent>
|
||||||
|
|
||||||
using namespace BlackCore;
|
using namespace BlackCore;
|
||||||
using namespace BlackMisc::PhysicalQuantities;
|
using namespace BlackMisc::PhysicalQuantities;
|
||||||
using namespace BlackMisc::Aviation;
|
using namespace BlackMisc::Aviation;
|
||||||
|
using namespace BlackMisc::Audio;
|
||||||
|
|
||||||
namespace BlackMiscTest
|
namespace BlackMiscTest
|
||||||
{
|
{
|
||||||
@@ -33,8 +37,8 @@ namespace BlackMiscTest
|
|||||||
void Tool::serverLoop(BlackCore::CRuntime *runtime)
|
void Tool::serverLoop(BlackCore::CRuntime *runtime)
|
||||||
{
|
{
|
||||||
Q_ASSERT(runtime);
|
Q_ASSERT(runtime);
|
||||||
QThread::sleep(3); // let the client connect
|
QThread::sleep(3); // let the DBus server startup
|
||||||
qDebug() << "Running on server here" << Tool::getPid();
|
qDebug() << "Running on server here" << Tool::getPid() << "thread:" << QThread::currentThreadId();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Server loop
|
// Server loop
|
||||||
@@ -43,15 +47,16 @@ namespace BlackMiscTest
|
|||||||
QString line;
|
QString line;
|
||||||
while (line != "x" && runtime)
|
while (line != "x" && runtime)
|
||||||
{
|
{
|
||||||
const BlackCore::IContextNetwork *networkContext = runtime->getIContextNetwork();
|
BlackCore::IContextNetwork *networkContext = runtime->getIContextNetwork();
|
||||||
const BlackCore::IContextAudio *audioContext = runtime->getIContextAudio();
|
BlackCore::IContextAudio *audioContext = runtime->getIContextAudio();
|
||||||
const BlackCore::IContextSettings *settingsContext = runtime->getIContextSettings();
|
BlackCore::IContextSettings *settingsContext = runtime->getIContextSettings();
|
||||||
const BlackCore::IContextOwnAircraft *ownAircraftContext = runtime->getIContextOwnAircraft();
|
BlackCore::IContextOwnAircraft *ownAircraftContext = runtime->getIContextOwnAircraft();
|
||||||
|
|
||||||
BlackCore::IContextApplication *applicationContext = runtime->getIContextApplication();
|
BlackCore::IContextApplication *applicationContext = runtime->getIContextApplication();
|
||||||
|
|
||||||
qDebug() << "-------------";
|
qDebug() << "-------------";
|
||||||
qDebug() << "Connected with network: " << networkContext->isConnected();
|
qDebug() << "Connected with network: " << networkContext->isConnected();
|
||||||
|
qDebug() << "Thread id:" << QThread::currentThreadId();
|
||||||
qDebug() << "-------------";
|
qDebug() << "-------------";
|
||||||
qDebug() << "x .. to exit 0 .. settings";
|
qDebug() << "x .. to exit 0 .. settings";
|
||||||
qDebug() << "1 .. ATC booked 2 .. ATC online";
|
qDebug() << "1 .. ATC booked 2 .. ATC online";
|
||||||
@@ -71,44 +76,79 @@ namespace BlackMiscTest
|
|||||||
if (line.startsWith("0"))
|
if (line.startsWith("0"))
|
||||||
{
|
{
|
||||||
qDebug() << "-------------";
|
qDebug() << "-------------";
|
||||||
qDebug() << "Settings:" << settingsContext->getSettingsFileName();
|
QString ret1;
|
||||||
qDebug() << settingsContext->getSettingsAsJsonString();
|
QMetaObject::invokeMethod(settingsContext, "getSettingsFileName",
|
||||||
|
Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(QString, ret1));
|
||||||
|
qDebug() << "Settings:" << ret1;
|
||||||
|
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(settingsContext, "getSettingsAsJsonString",
|
||||||
|
Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(QString, ret1));
|
||||||
|
qDebug() << "JSON" << ret1;
|
||||||
}
|
}
|
||||||
else if (line.startsWith("1"))
|
else if (line.startsWith("1"))
|
||||||
{
|
{
|
||||||
|
// remarks: use fully qualified name in Q_RETURN_ARG
|
||||||
qDebug() << "-------------";
|
qDebug() << "-------------";
|
||||||
qDebug() << "ATC booked";
|
qDebug() << "ATC booked";
|
||||||
qDebug() << networkContext->getAtcStationsBooked().toFormattedQString();
|
CAtcStationList stations;
|
||||||
|
QMetaObject::invokeMethod(networkContext, "getAtcStationsBooked",
|
||||||
|
Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(BlackMisc::Aviation::CAtcStationList, stations));
|
||||||
|
qDebug() << stations;
|
||||||
}
|
}
|
||||||
else if (line.startsWith("2"))
|
else if (line.startsWith("2"))
|
||||||
{
|
{
|
||||||
qDebug() << "-------------";
|
qDebug() << "-------------";
|
||||||
qDebug() << "ATC online";
|
qDebug() << "ATC online";
|
||||||
qDebug() << networkContext->getAtcStationsOnline().toFormattedQString();
|
CAtcStationList stations;
|
||||||
|
QMetaObject::invokeMethod(networkContext, "getAtcStationsOnline",
|
||||||
|
Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(BlackMisc::Aviation::CAtcStationList, stations));
|
||||||
|
qDebug() << stations;
|
||||||
}
|
}
|
||||||
else if (line.startsWith("3"))
|
else if (line.startsWith("3"))
|
||||||
{
|
{
|
||||||
qDebug() << "-------------";
|
qDebug() << "-------------";
|
||||||
qDebug() << "aircrafts in range";
|
qDebug() << "aircrafts in range";
|
||||||
qDebug() << networkContext->getAircraftsInRange().toFormattedQString();
|
CAircraftList aircrafts;
|
||||||
|
QMetaObject::invokeMethod(networkContext, "getAircraftsInRange",
|
||||||
|
Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(BlackMisc::Aviation::CAircraftList, aircrafts));
|
||||||
|
qDebug() << aircrafts;
|
||||||
}
|
}
|
||||||
else if (line.startsWith("4"))
|
else if (line.startsWith("4"))
|
||||||
{
|
{
|
||||||
qDebug() << "-------------";
|
qDebug() << "-------------";
|
||||||
qDebug() << "my aircraft";
|
qDebug() << "my aircraft";
|
||||||
qDebug() << ownAircraftContext->getOwnAircraft();
|
CAircraft aircraft;
|
||||||
|
QMetaObject::invokeMethod(ownAircraftContext, "getOwnAircraft",
|
||||||
|
Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(BlackMisc::Aviation::CAircraft, aircraft));
|
||||||
|
qDebug() << aircraft;
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (line.startsWith("5"))
|
else if (line.startsWith("5"))
|
||||||
{
|
{
|
||||||
qDebug() << "-------------";
|
qDebug() << "-------------";
|
||||||
qDebug() << "voice rooms";
|
qDebug() << "voice rooms";
|
||||||
qDebug() << audioContext->getComVoiceRooms();
|
CVoiceRoomList voiceRooms;
|
||||||
|
QMetaObject::invokeMethod(audioContext, "getComVoiceRooms",
|
||||||
|
Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(BlackMisc::Audio::CVoiceRoomList, voiceRooms));
|
||||||
|
qDebug() << voiceRooms;
|
||||||
}
|
}
|
||||||
else if (line.startsWith("6"))
|
else if (line.startsWith("6"))
|
||||||
{
|
{
|
||||||
qDebug() << "-------------";
|
qDebug() << "-------------";
|
||||||
qDebug() << "vatlib audio devices";
|
qDebug() << "vatlib audio devices";
|
||||||
qDebug() << audioContext->getAudioDevices();
|
CAudioDeviceList devices;
|
||||||
|
QMetaObject::invokeMethod(audioContext, "getAudioDevices",
|
||||||
|
Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(BlackMisc::Audio::CAudioDeviceList, devices));
|
||||||
|
qDebug() << devices;
|
||||||
}
|
}
|
||||||
else if (line.startsWith("7"))
|
else if (line.startsWith("7"))
|
||||||
{
|
{
|
||||||
@@ -116,7 +156,6 @@ namespace BlackMiscTest
|
|||||||
qDebug() << "Qt audio devices";
|
qDebug() << "Qt audio devices";
|
||||||
BlackSound::CSoundGenerator::printAllQtSoundDevices();
|
BlackSound::CSoundGenerator::printAllQtSoundDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (line.startsWith("oe"))
|
else if (line.startsWith("oe"))
|
||||||
{
|
{
|
||||||
applicationContext->setOutputRedirectionLevel(IContextApplication::RedirectAllOutput);
|
applicationContext->setOutputRedirectionLevel(IContextApplication::RedirectAllOutput);
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
#include "blackmisc/project.h"
|
#include "blackmisc/project.h"
|
||||||
#include "blackmisc/indexvariantmap.h"
|
#include "blackmisc/indexvariantmap.h"
|
||||||
|
|
||||||
|
// KB_REMOVE with debug log message
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
namespace BlackCore
|
namespace BlackCore
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -304,6 +307,9 @@ namespace BlackCore
|
|||||||
|
|
||||||
void CAirspaceMonitor::receivedBookings(const CAtcStationList &bookedStations)
|
void CAirspaceMonitor::receivedBookings(const CAtcStationList &bookedStations)
|
||||||
{
|
{
|
||||||
|
// KB_REMOVE qDebug
|
||||||
|
qDebug() << Q_FUNC_INFO << QThread::currentThreadId();
|
||||||
|
|
||||||
this->m_atcStationsBooked.clear();
|
this->m_atcStationsBooked.clear();
|
||||||
foreach(CAtcStation bookedStation, bookedStations)
|
foreach(CAtcStation bookedStation, bookedStations)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "blackcore/context_application.h"
|
#include "blackcore/context_application.h"
|
||||||
#include "blackcore/context_application_impl.h"
|
#include "blackcore/context_application_impl.h"
|
||||||
#include "blackcore/context_application_proxy.h"
|
#include "blackcore/context_application_proxy.h"
|
||||||
#include "blackcore/context_application_event.h"
|
|
||||||
#include "blackmisc/statusmessage.h"
|
#include "blackmisc/statusmessage.h"
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
@@ -24,11 +23,29 @@ namespace BlackCore
|
|||||||
case CRuntimeConfig::Remote:
|
case CRuntimeConfig::Remote:
|
||||||
return new BlackCore::CContextApplicationProxy(BlackCore::CDBusServer::ServiceName, conn, mode, parent);
|
return new BlackCore::CContextApplicationProxy(BlackCore::CDBusServer::ServiceName, conn, mode, parent);
|
||||||
default:
|
default:
|
||||||
qFatal("Always initialize an application context");
|
qFatal("Always initialize an application context!");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IContextApplication::RedirectionLevel IContextApplication::getOutputRedirectionLevel() const
|
||||||
|
{
|
||||||
|
QReadLocker(&this->m_lock);
|
||||||
|
return this->m_outputRedirectionLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IContextApplication::setOutputRedirectionLevel(IContextApplication::RedirectionLevel redirectionLevel)
|
||||||
|
{
|
||||||
|
QWriteLocker(&this->m_lock);
|
||||||
|
this->m_outputRedirectionLevel = redirectionLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
IContextApplication::RedirectionLevel IContextApplication::getStreamingForRedirectedOutputLevel() const
|
||||||
|
{
|
||||||
|
QReadLocker(&this->m_lock);
|
||||||
|
return this->m_redirectedOutputRedirectionLevel;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
@@ -36,7 +53,9 @@ namespace BlackCore
|
|||||||
CContext(mode, runtime), m_outputRedirectionLevel(IContextApplication::RedirectNone), m_redirectedOutputRedirectionLevel(IContextApplication::RedirectNone)
|
CContext(mode, runtime), m_outputRedirectionLevel(IContextApplication::RedirectNone), m_redirectedOutputRedirectionLevel(IContextApplication::RedirectNone)
|
||||||
{
|
{
|
||||||
if (IContextApplication::s_contexts.isEmpty())
|
if (IContextApplication::s_contexts.isEmpty())
|
||||||
|
{
|
||||||
IContextApplication::s_oldHandler = qInstallMessageHandler(IContextApplication::messageHandlerDispatch);
|
IContextApplication::s_oldHandler = qInstallMessageHandler(IContextApplication::messageHandlerDispatch);
|
||||||
|
}
|
||||||
IContextApplication::s_contexts.append(this);
|
IContextApplication::s_contexts.append(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,24 +64,13 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
void IContextApplication::setStreamingForRedirectedOutputLevel(RedirectionLevel redirectionLevel)
|
void IContextApplication::setStreamingForRedirectedOutputLevel(RedirectionLevel redirectionLevel)
|
||||||
{
|
{
|
||||||
|
QWriteLocker(&this->m_lock);
|
||||||
disconnect(this, &IContextApplication::redirectedOutput, this, &IContextApplication::streamRedirectedOutput);
|
disconnect(this, &IContextApplication::redirectedOutput, this, &IContextApplication::streamRedirectedOutput);
|
||||||
if (redirectionLevel != RedirectNone)
|
if (redirectionLevel != RedirectNone)
|
||||||
connect(this, &IContextApplication::redirectedOutput, this, &IContextApplication::streamRedirectedOutput);
|
|
||||||
this->m_redirectedOutputRedirectionLevel = redirectionLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Process event in object's thread, used to emit signal from other thread
|
|
||||||
*/
|
|
||||||
bool IContextApplication::event(QEvent *event)
|
|
||||||
{
|
|
||||||
if (event->type() == CApplicationEvent::eventType())
|
|
||||||
{
|
{
|
||||||
CApplicationEvent *e = static_cast<CApplicationEvent *>(event);
|
connect(this, &IContextApplication::redirectedOutput, this, &IContextApplication::streamRedirectedOutput);
|
||||||
emit this->redirectedOutput(e->m_message, this->getUniqueId());
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return CContext::event(event);
|
this->m_redirectedOutputRedirectionLevel = redirectionLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -93,24 +101,25 @@ namespace BlackCore
|
|||||||
void IContextApplication::messageHandler(QtMsgType type, const QMessageLogContext &messageContext, const QString &message)
|
void IContextApplication::messageHandler(QtMsgType type, const QMessageLogContext &messageContext, const QString &message)
|
||||||
{
|
{
|
||||||
Q_UNUSED(messageContext);
|
Q_UNUSED(messageContext);
|
||||||
if (this->m_outputRedirectionLevel == RedirectNone) return;
|
RedirectionLevel outputRedirectionLevel = this->getOutputRedirectionLevel(); // local copy, thready safety
|
||||||
|
if (outputRedirectionLevel == RedirectNone) return;
|
||||||
CStatusMessage m(CStatusMessage::TypeStdoutRedirect, CStatusMessage::SeverityInfo, message);
|
CStatusMessage m(CStatusMessage::TypeStdoutRedirect, CStatusMessage::SeverityInfo, message);
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case QtDebugMsg:
|
case QtDebugMsg:
|
||||||
if (this->m_outputRedirectionLevel != RedirectAllOutput) return;
|
if (outputRedirectionLevel != RedirectAllOutput) return;
|
||||||
break;
|
break;
|
||||||
case QtWarningMsg:
|
case QtWarningMsg:
|
||||||
if (this->m_outputRedirectionLevel == RedirectAllOutput) return;
|
if (outputRedirectionLevel == RedirectAllOutput) return;
|
||||||
if (this->m_outputRedirectionLevel == RedirectError) return;
|
if (outputRedirectionLevel == RedirectError) return;
|
||||||
m.setSeverity(CStatusMessage::SeverityWarning);
|
m.setSeverity(CStatusMessage::SeverityWarning);
|
||||||
break;
|
break;
|
||||||
case QtCriticalMsg:
|
case QtCriticalMsg:
|
||||||
if (this->m_outputRedirectionLevel != RedirectError) return;
|
if (outputRedirectionLevel != RedirectError) return;
|
||||||
m.setSeverity(CStatusMessage::SeverityError);
|
m.setSeverity(CStatusMessage::SeverityError);
|
||||||
break;
|
break;
|
||||||
case QtFatalMsg:
|
case QtFatalMsg:
|
||||||
if (this->m_outputRedirectionLevel != RedirectError) return;
|
if (m_outputRedirectionLevel != RedirectError) return;
|
||||||
m.setSeverity(CStatusMessage::SeverityError);
|
m.setSeverity(CStatusMessage::SeverityError);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -125,11 +134,9 @@ namespace BlackCore
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// different threads, use event.
|
// Different threads, use invoke so that is called in "main / object's thread"
|
||||||
// in this event the same redirect as above will emitted, only that this is then
|
// Otherwise for DBus: QtDBus: cannot relay signals from parent BlackCore::CContextApplication(0x4b4358 "") unless they are emitted in the object's thread QThread(0x4740b0 ""). Current thread is QThread(0x4b5530 "Thread (pooled)")
|
||||||
// done in the same thread as the parent object
|
QMetaObject::invokeMethod(this, "redirectedOutput", Q_ARG(BlackMisc::CStatusMessage, m), Q_ARG(qint64, this->getUniqueId()));
|
||||||
CApplicationEvent *e = new CApplicationEvent(m, this->getUniqueId());
|
|
||||||
QCoreApplication::postEvent(this, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,22 +146,22 @@ namespace BlackCore
|
|||||||
void IContextApplication::streamRedirectedOutput(const CStatusMessage &message, qint64 contextId)
|
void IContextApplication::streamRedirectedOutput(const CStatusMessage &message, qint64 contextId)
|
||||||
{
|
{
|
||||||
if (this->getUniqueId() == contextId) return; // avoid infinite output
|
if (this->getUniqueId() == contextId) return; // avoid infinite output
|
||||||
if (this->m_redirectedOutputRedirectionLevel == RedirectNone) return;
|
RedirectionLevel redirectedOutputRedirectionLevel = this->getStreamingForRedirectedOutputLevel(); // local copy
|
||||||
|
|
||||||
if (message.isEmpty()) return;
|
if (message.isEmpty()) return;
|
||||||
switch (message.getSeverity())
|
switch (message.getSeverity())
|
||||||
{
|
{
|
||||||
case CStatusMessage::SeverityInfo:
|
case CStatusMessage::SeverityInfo:
|
||||||
if (this->m_redirectedOutputRedirectionLevel != RedirectAllOutput) return;
|
if (redirectedOutputRedirectionLevel != RedirectAllOutput) return;
|
||||||
qDebug() << message.getMessage();
|
qDebug() << message.getMessage();
|
||||||
break;
|
break;
|
||||||
case CStatusMessage::SeverityWarning:
|
case CStatusMessage::SeverityWarning:
|
||||||
if (this->m_redirectedOutputRedirectionLevel == RedirectAllOutput) return;
|
if (redirectedOutputRedirectionLevel == RedirectAllOutput) return;
|
||||||
if (this->m_redirectedOutputRedirectionLevel == RedirectError) return;
|
if (redirectedOutputRedirectionLevel == RedirectError) return;
|
||||||
qWarning() << message.getMessage();
|
qWarning() << message.getMessage();
|
||||||
break;
|
break;
|
||||||
case CStatusMessage::SeverityError:
|
case CStatusMessage::SeverityError:
|
||||||
if (this->m_redirectedOutputRedirectionLevel != RedirectError) return;
|
if (redirectedOutputRedirectionLevel != RedirectError) return;
|
||||||
qCritical() << message.getMessage();
|
qCritical() << message.getMessage();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "blackcore/context.h"
|
#include "blackcore/context.h"
|
||||||
#include "blackmisc/statusmessagelist.h"
|
#include "blackmisc/statusmessagelist.h"
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
#define BLACKCORE_CONTEXTAPPLICATION_INTERFACENAME "net.vatsim.PilotClient.BlackCore.ContextApplication"
|
#define BLACKCORE_CONTEXTAPPLICATION_INTERFACENAME "net.vatsim.PilotClient.BlackCore.ContextApplication"
|
||||||
#define BLACKCORE_CONTEXTAPPLICATION_OBJECTPATH "/Application"
|
#define BLACKCORE_CONTEXTAPPLICATION_OBJECTPATH "/Application"
|
||||||
@@ -16,7 +17,7 @@
|
|||||||
namespace BlackCore
|
namespace BlackCore
|
||||||
{
|
{
|
||||||
/*!
|
/*!
|
||||||
* \brief Application context interface
|
* Application context interface
|
||||||
*/
|
*/
|
||||||
class IContextApplication : public CContext
|
class IContextApplication : public CContext
|
||||||
{
|
{
|
||||||
@@ -76,20 +77,21 @@ namespace BlackCore
|
|||||||
virtual ~IContextApplication() {}
|
virtual ~IContextApplication() {}
|
||||||
|
|
||||||
//! Output redirection (redirect my output)
|
//! Output redirection (redirect my output)
|
||||||
RedirectionLevel getOutputRedirectionLevel() const { return this->m_outputRedirectionLevel; }
|
//! \threadsafe
|
||||||
|
RedirectionLevel getOutputRedirectionLevel() const;
|
||||||
|
|
||||||
//! Output redirection (redirect my output)
|
//! Output redirection (redirect my output)
|
||||||
void setOutputRedirectionLevel(RedirectionLevel redirectionLevel) { this->m_outputRedirectionLevel = redirectionLevel; }
|
//! \threadsafe
|
||||||
|
void setOutputRedirectionLevel(RedirectionLevel redirectionLevel);
|
||||||
|
|
||||||
//! Redirected output generated by others
|
//! Redirected output generated by others
|
||||||
RedirectionLevel getStreamingForRedirectedOutputLevel() const { return this->m_redirectedOutputRedirectionLevel; }
|
//! \threadsafe
|
||||||
|
RedirectionLevel getStreamingForRedirectedOutputLevel() const;
|
||||||
|
|
||||||
//! Redirected output generated by others
|
//! Redirected output generated by others
|
||||||
|
//! \threadsafe
|
||||||
void setStreamingForRedirectedOutputLevel(RedirectionLevel redirectionLevel) ;
|
void setStreamingForRedirectedOutputLevel(RedirectionLevel redirectionLevel) ;
|
||||||
|
|
||||||
//! Process event, cross thread messages
|
|
||||||
bool event(QEvent *event) override;
|
|
||||||
|
|
||||||
//! Reset output redirection
|
//! Reset output redirection
|
||||||
static void resetOutputRedirection();
|
static void resetOutputRedirection();
|
||||||
|
|
||||||
@@ -144,13 +146,16 @@ namespace BlackCore
|
|||||||
static QtMessageHandler s_oldHandler;
|
static QtMessageHandler s_oldHandler;
|
||||||
|
|
||||||
//! Message handler, handles one individual context
|
//! Message handler, handles one individual context
|
||||||
|
//! \threadsafe
|
||||||
void messageHandler(QtMsgType type, const QMessageLogContext &messageContext, const QString &messsage);
|
void messageHandler(QtMsgType type, const QMessageLogContext &messageContext, const QString &messsage);
|
||||||
|
|
||||||
//! Handle output dispatch, handles all contexts
|
//! Handle output dispatch, handles all contexts
|
||||||
|
//! \remarks Can be called in thread, has to be thread safe
|
||||||
static void messageHandlerDispatch(QtMsgType type, const QMessageLogContext &messageContext, const QString &message);
|
static void messageHandlerDispatch(QtMsgType type, const QMessageLogContext &messageContext, const QString &message);
|
||||||
|
|
||||||
RedirectionLevel m_outputRedirectionLevel; //!< enable / disable my output
|
RedirectionLevel m_outputRedirectionLevel; //!< enable / disable my output
|
||||||
RedirectionLevel m_redirectedOutputRedirectionLevel; //!< enable / disable others output
|
RedirectionLevel m_redirectedOutputRedirectionLevel; //!< enable / disable others' output
|
||||||
|
mutable QReadWriteLock m_lock; //!< thread safety
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
//! Re-stream the redirected output
|
//! Re-stream the redirected output
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
#ifndef BLACKCORE_CONTEXT_APPLICATION_EVENT_H
|
|
||||||
#define BLACKCORE_CONTEXT_APPLICATION_EVENT_H
|
|
||||||
|
|
||||||
#include "blackcore/context_application.h"
|
|
||||||
#include <QEvent>
|
|
||||||
|
|
||||||
namespace BlackCore
|
|
||||||
{
|
|
||||||
/*!
|
|
||||||
* \brief Event to allow cross thread output redirection
|
|
||||||
*/
|
|
||||||
class CApplicationEvent : public QEvent
|
|
||||||
{
|
|
||||||
friend class IContextApplication;
|
|
||||||
|
|
||||||
public:
|
|
||||||
//! Constructor
|
|
||||||
CApplicationEvent(const BlackMisc::CStatusMessage &msg, qint64 contextId) :
|
|
||||||
QEvent(eventType()), m_message(msg), m_contextId(contextId) {}
|
|
||||||
//! Destructor
|
|
||||||
virtual ~CApplicationEvent() {}
|
|
||||||
//! Event type
|
|
||||||
static const QEvent::Type &eventType()
|
|
||||||
{
|
|
||||||
const static QEvent::Type t = static_cast<QEvent::Type>(QEvent::registerEventType());
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
BlackMisc::CStatusMessage m_message;
|
|
||||||
qint64 m_contextId;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
#endif // guard
|
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
#ifndef BLACKCORE_CONTEXTNETWORK_H
|
#ifndef BLACKCORE_CONTEXTNETWORK_H
|
||||||
#define BLACKCORE_CONTEXTNETWORK_H
|
#define BLACKCORE_CONTEXTNETWORK_H
|
||||||
|
|
||||||
|
//! \file
|
||||||
|
|
||||||
#include "blackcore/context.h"
|
#include "blackcore/context.h"
|
||||||
#include "blackmisc/avallclasses.h"
|
#include "blackmisc/avallclasses.h"
|
||||||
#include "blackmisc/statusmessage.h"
|
#include "blackmisc/statusmessage.h"
|
||||||
@@ -22,7 +24,7 @@
|
|||||||
namespace BlackCore
|
namespace BlackCore
|
||||||
{
|
{
|
||||||
|
|
||||||
//! \brief Network context proxy
|
//! Network context proxy
|
||||||
class IContextNetwork : public CContext
|
class IContextNetwork : public CContext
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|||||||
@@ -84,6 +84,16 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
CContextNetwork::~CContextNetwork()
|
CContextNetwork::~CContextNetwork()
|
||||||
{
|
{
|
||||||
|
this->gracefulShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop, going down
|
||||||
|
*/
|
||||||
|
void CContextNetwork::gracefulShutdown()
|
||||||
|
{
|
||||||
|
if (this->m_vatsimBookingReader) this->m_vatsimBookingReader->stop();
|
||||||
|
if (this->m_vatsimDataFileReader) this->m_vatsimDataFileReader->stop();
|
||||||
if (this->isConnected()) this->disconnectFromNetwork();
|
if (this->isConnected()) this->disconnectFromNetwork();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#ifndef BLACKCORE_CONTEXTNETWORK_IMPL_H
|
#ifndef BLACKCORE_CONTEXTNETWORK_IMPL_H
|
||||||
#define BLACKCORE_CONTEXTNETWORK_IMPL_H
|
#define BLACKCORE_CONTEXTNETWORK_IMPL_H
|
||||||
|
|
||||||
|
//! \file
|
||||||
|
|
||||||
#include "blackcore/context_network.h"
|
#include "blackcore/context_network.h"
|
||||||
#include "blackcore/context_settings.h"
|
#include "blackcore/context_settings.h"
|
||||||
#include "blackcore/context_runtime.h"
|
#include "blackcore/context_runtime.h"
|
||||||
@@ -118,6 +120,9 @@ namespace BlackCore
|
|||||||
//! \copydoc IContextNetwork::requestAtisUpdates
|
//! \copydoc IContextNetwork::requestAtisUpdates
|
||||||
virtual void requestAtisUpdates() override;
|
virtual void requestAtisUpdates() override;
|
||||||
|
|
||||||
|
//! Gracefully shut down, e.g. for thread safety
|
||||||
|
void gracefulShutdown();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Constructor, with link to runtime
|
//! Constructor, with link to runtime
|
||||||
CContextNetwork(CRuntimeConfig::ContextMode, CRuntime *runtime);
|
CContextNetwork(CRuntimeConfig::ContextMode, CRuntime *runtime);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#include "blackcore/context_runtime.h"
|
|
||||||
#include "blackcore/context_all_impl.h"
|
#include "blackcore/context_all_impl.h"
|
||||||
#include "blackcore/context_all_proxies.h"
|
#include "blackcore/context_all_proxies.h"
|
||||||
#include "blackcore/blackcorefreefunctions.h"
|
#include "blackcore/blackcorefreefunctions.h"
|
||||||
@@ -9,6 +8,7 @@
|
|||||||
#include "blackmisc/statusmessagelist.h"
|
#include "blackmisc/statusmessagelist.h"
|
||||||
#include "blackmisc/avaircraft.h"
|
#include "blackmisc/avaircraft.h"
|
||||||
#include "blackmisc/blackmiscfreefunctions.h"
|
#include "blackmisc/blackmiscfreefunctions.h"
|
||||||
|
#include "blackcore/context_runtime.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
@@ -58,6 +58,7 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
bool CRuntime::signalLogForApplication(bool enabled)
|
bool CRuntime::signalLogForApplication(bool enabled)
|
||||||
{
|
{
|
||||||
|
QWriteLocker wl(&m_lock);
|
||||||
if (enabled == this->m_signalLogApplication) return enabled;
|
if (enabled == this->m_signalLogApplication) return enabled;
|
||||||
if (!this->getIContextApplication())
|
if (!this->getIContextApplication())
|
||||||
{
|
{
|
||||||
@@ -88,6 +89,7 @@ namespace BlackCore
|
|||||||
|
|
||||||
bool CRuntime::signalLogForAudio(bool enabled)
|
bool CRuntime::signalLogForAudio(bool enabled)
|
||||||
{
|
{
|
||||||
|
QWriteLocker wl(&m_lock);
|
||||||
if (enabled == this->m_signalLogAudio) return enabled;
|
if (enabled == this->m_signalLogAudio) return enabled;
|
||||||
if (!this->getIContextNetwork())
|
if (!this->getIContextNetwork())
|
||||||
{
|
{
|
||||||
@@ -114,6 +116,7 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
bool CRuntime::signalLogForNetwork(bool enabled)
|
bool CRuntime::signalLogForNetwork(bool enabled)
|
||||||
{
|
{
|
||||||
|
QWriteLocker wl(&m_lock);
|
||||||
if (enabled == this->m_signalLogNetwork) return enabled;
|
if (enabled == this->m_signalLogNetwork) return enabled;
|
||||||
if (!this->getIContextNetwork())
|
if (!this->getIContextNetwork())
|
||||||
{
|
{
|
||||||
@@ -152,6 +155,7 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
bool CRuntime::signalLogForOwnAircraft(bool enabled)
|
bool CRuntime::signalLogForOwnAircraft(bool enabled)
|
||||||
{
|
{
|
||||||
|
QWriteLocker wl(&m_lock);
|
||||||
if (enabled == this->m_signalLogOwnAircraft) return enabled;
|
if (enabled == this->m_signalLogOwnAircraft) return enabled;
|
||||||
if (!this->getIContextOwnAircraft())
|
if (!this->getIContextOwnAircraft())
|
||||||
{
|
{
|
||||||
@@ -188,6 +192,7 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
bool CRuntime::signalLogForSettings(bool enabled)
|
bool CRuntime::signalLogForSettings(bool enabled)
|
||||||
{
|
{
|
||||||
|
QWriteLocker wl(&m_lock);
|
||||||
if (enabled == this->m_signalLogSettings) return enabled;
|
if (enabled == this->m_signalLogSettings) return enabled;
|
||||||
if (!this->getIContextSettings())
|
if (!this->getIContextSettings())
|
||||||
{
|
{
|
||||||
@@ -214,6 +219,7 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
bool CRuntime::signalLogForSimulator(bool enabled)
|
bool CRuntime::signalLogForSimulator(bool enabled)
|
||||||
{
|
{
|
||||||
|
QWriteLocker wl(&m_lock);
|
||||||
if (enabled == this->m_signalLogSimulator) return enabled;
|
if (enabled == this->m_signalLogSimulator) return enabled;
|
||||||
if (!this->getIContextSimulator())
|
if (!this->getIContextSimulator())
|
||||||
{
|
{
|
||||||
@@ -285,12 +291,12 @@ namespace BlackCore
|
|||||||
switch (context)
|
switch (context)
|
||||||
{
|
{
|
||||||
default: return true;
|
default: return true;
|
||||||
case LogForApplication: return this->m_slotLogApplication;
|
case LogForApplication: return this->isSlotLogForApplicationEnabled();
|
||||||
case LogForAudio: return this->m_slotLogAudio;
|
case LogForAudio: return this->isSlotLogForAudioEnabled();
|
||||||
case LogForNetwork: return this->m_slotLogNetwork;
|
case LogForNetwork: return this->isSlotLogForNetworkEnabled();
|
||||||
case LogForOwnAircraft: return this->m_slotLogOwnAircraft;
|
case LogForOwnAircraft: return this->isSlotLogForOwnAircraftEnabled();
|
||||||
case LogForSettings: return this->m_slotLogSettings;
|
case LogForSettings: return this->isSlotLogForSettingsEnabled();
|
||||||
case LogForSimulator: return this->m_slotLogSimulator;
|
case LogForSimulator: return this->isSlotLogForSimulatorEnabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,6 +502,10 @@ namespace BlackCore
|
|||||||
{
|
{
|
||||||
disconnect(this->getIContextNetwork());
|
disconnect(this->getIContextNetwork());
|
||||||
this->getIContextNetwork()->disconnectFromNetwork();
|
this->getIContextNetwork()->disconnectFromNetwork();
|
||||||
|
if (this->m_contextNetwork->usingLocalObjects())
|
||||||
|
{
|
||||||
|
this->getCContextNetwork()->gracefulShutdown(); // for threads
|
||||||
|
}
|
||||||
this->getIContextNetwork()->deleteLater();
|
this->getIContextNetwork()->deleteLater();
|
||||||
this->m_contextNetwork = nullptr;
|
this->m_contextNetwork = nullptr;
|
||||||
}
|
}
|
||||||
@@ -663,8 +673,6 @@ namespace BlackCore
|
|||||||
return static_cast<CContextNetwork *>(this->m_contextNetwork);
|
return static_cast<CContextNetwork *>(this->m_contextNetwork);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CContextOwnAircraft *CRuntime::getCContextOwnAircraft()
|
CContextOwnAircraft *CRuntime::getCContextOwnAircraft()
|
||||||
{
|
{
|
||||||
Q_ASSERT_X(!this->m_contextOwnAircraft || this->m_contextOwnAircraft->usingLocalObjects(), "CCoreRuntime", "Cannot downcast to local object");
|
Q_ASSERT_X(!this->m_contextOwnAircraft || this->m_contextOwnAircraft->usingLocalObjects(), "CCoreRuntime", "Cannot downcast to local object");
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <QDBusConnection>
|
#include <QDBusConnection>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QMultiMap>
|
#include <QMultiMap>
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
using namespace BlackMisc;
|
using namespace BlackMisc;
|
||||||
|
|
||||||
@@ -58,66 +59,135 @@ namespace BlackCore
|
|||||||
const QDBusConnection &getDBusConnection() const { return this->m_dbusConnection; }
|
const QDBusConnection &getDBusConnection() const { return this->m_dbusConnection; }
|
||||||
|
|
||||||
//! Enable / disable all logging
|
//! Enable / disable all logging
|
||||||
|
//! \threadsafe
|
||||||
void signalLog(bool enabled);
|
void signalLog(bool enabled);
|
||||||
|
|
||||||
//! Signal logging for application context
|
//! Signal logging for application context
|
||||||
|
//! \threadsafe
|
||||||
bool signalLogForApplication(bool enabled);
|
bool signalLogForApplication(bool enabled);
|
||||||
|
|
||||||
//! Signal logging for audio context
|
//! Signal logging for audio context
|
||||||
|
//! \threadsafe
|
||||||
bool signalLogForAudio(bool enabled);
|
bool signalLogForAudio(bool enabled);
|
||||||
|
|
||||||
//! Signal logging for network context
|
//! Signal logging for network context
|
||||||
|
//! \threadsafe
|
||||||
bool signalLogForNetwork(bool enabled);
|
bool signalLogForNetwork(bool enabled);
|
||||||
|
|
||||||
//! Signal logging for own aircraft context
|
//! Signal logging for own aircraft context
|
||||||
|
//! \threadsafe
|
||||||
bool signalLogForOwnAircraft(bool enabled);
|
bool signalLogForOwnAircraft(bool enabled);
|
||||||
|
|
||||||
//! Signal logging for settings context
|
//! Signal logging for settings context
|
||||||
|
//! \threadsafe
|
||||||
bool signalLogForSettings(bool enabled);
|
bool signalLogForSettings(bool enabled);
|
||||||
|
|
||||||
//! Signal logging for simulator context
|
//! Signal logging for simulator context
|
||||||
|
//! \threadsafe
|
||||||
bool signalLogForSimulator(bool enabled);
|
bool signalLogForSimulator(bool enabled);
|
||||||
|
|
||||||
//! Enable / disable all logging
|
//! Enable / disable all logging
|
||||||
|
//! \threadsafe
|
||||||
void slotLog(bool enabled);
|
void slotLog(bool enabled);
|
||||||
|
|
||||||
//! Slot logging for application context
|
//! Slot logging for application context
|
||||||
void slotLogForApplication(bool enabled) { this->m_slotLogApplication = enabled; }
|
//! \threadsafe
|
||||||
|
void slotLogForApplication(bool enabled)
|
||||||
|
{
|
||||||
|
QWriteLocker wl(&m_lock);
|
||||||
|
this->m_slotLogApplication = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
//! Slot logging for audio context
|
//! Slot logging for audio context
|
||||||
void slotLogForAudio(bool enabled) { this->m_slotLogAudio = enabled; }
|
//! \threadsafe
|
||||||
|
void slotLogForAudio(bool enabled)
|
||||||
|
{
|
||||||
|
QWriteLocker wl(&m_lock);
|
||||||
|
this->m_slotLogAudio = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
//! Slot logging for network context
|
//! Slot logging for network context
|
||||||
void slotLogForNetwork(bool enabled) { this->m_slotLogNetwork = enabled; }
|
//! \threadsafe
|
||||||
|
void slotLogForNetwork(bool enabled)
|
||||||
|
{
|
||||||
|
QWriteLocker wl(&m_lock);
|
||||||
|
this->m_slotLogNetwork = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
//! Slot logging for own aircraft context
|
//! Slot logging for own aircraft context
|
||||||
void slotLogForOwnAircraft(bool enabled) { this->m_slotLogOwnAircraft = enabled; }
|
//! \threadsafe
|
||||||
|
void slotLogForOwnAircraft(bool enabled)
|
||||||
|
{
|
||||||
|
QWriteLocker wl(&m_lock);
|
||||||
|
this->m_slotLogOwnAircraft = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
//! Slot logging for settings context
|
//! Slot logging for settings context
|
||||||
void slotLogForSettings(bool enabled) { this->m_slotLogSettings = enabled; }
|
//! \threadsafe
|
||||||
|
void slotLogForSettings(bool enabled)
|
||||||
|
{
|
||||||
|
QWriteLocker wl(&m_lock);
|
||||||
|
this->m_slotLogSettings = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
//! Slot logging for simulator context
|
//! Slot logging for simulator context
|
||||||
void slotLogForSimulator(bool enabled) { this->m_slotLogSimulator = enabled; }
|
//! \threadsafe
|
||||||
|
void slotLogForSimulator(bool enabled)
|
||||||
|
{
|
||||||
|
QWriteLocker wl(&m_lock);
|
||||||
|
this->m_slotLogSimulator = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
//! Slot logging for application context
|
//! Slot logging for application context
|
||||||
bool isSlotLogForApplicationEnabled() const { return this->m_slotLogApplication; }
|
//! \threadsafe
|
||||||
|
bool isSlotLogForApplicationEnabled() const
|
||||||
|
{
|
||||||
|
QReadLocker rl(&m_lock);
|
||||||
|
return this->m_slotLogApplication;
|
||||||
|
}
|
||||||
|
|
||||||
//! Slot logging for audio context
|
//! Slot logging for audio context
|
||||||
bool isSlotLogForAudioEnabled() const { return this->m_slotLogAudio; }
|
//! \threadsafe
|
||||||
|
bool isSlotLogForAudioEnabled() const
|
||||||
|
{
|
||||||
|
QReadLocker rl(&m_lock);
|
||||||
|
return this->m_slotLogAudio;
|
||||||
|
}
|
||||||
|
|
||||||
//! Slot logging for network context
|
//! Slot logging for network context
|
||||||
bool isSlotLogForNetworkEnabled() const { return this->m_slotLogNetwork; }
|
//! \threadsafe
|
||||||
|
bool isSlotLogForNetworkEnabled() const
|
||||||
|
{
|
||||||
|
QReadLocker rl(&m_lock);
|
||||||
|
return this->m_slotLogNetwork;
|
||||||
|
}
|
||||||
|
|
||||||
//! Slot log for own aircraft
|
//! Slot log for own aircraft
|
||||||
bool isSlotLogForOwnAircraftEnabled() const { return this->m_slotLogOwnAircraft; }
|
//! \threadsafe
|
||||||
|
bool isSlotLogForOwnAircraftEnabled() const
|
||||||
|
{
|
||||||
|
QReadLocker rl(&m_lock);
|
||||||
|
return this->m_slotLogOwnAircraft;
|
||||||
|
}
|
||||||
|
|
||||||
//! Slot logging for settings context
|
//! Slot logging for settings context
|
||||||
bool isSlotLogForSettingsEnabled() const { return this->m_slotLogSettings; }
|
//! \threadsafe
|
||||||
|
bool isSlotLogForSettingsEnabled() const
|
||||||
|
{
|
||||||
|
QReadLocker rl(&m_lock);
|
||||||
|
return this->m_slotLogSettings;
|
||||||
|
}
|
||||||
|
|
||||||
//! Slot logging for simulator context
|
//! Slot logging for simulator context
|
||||||
bool isSlotLogForSimulatorEnabled() const { return this->m_slotLogSimulator; }
|
//! \threadsafe
|
||||||
|
bool isSlotLogForSimulatorEnabled() const
|
||||||
|
{
|
||||||
|
QReadLocker rl(&m_lock);
|
||||||
|
return this->m_slotLogSimulator;
|
||||||
|
}
|
||||||
|
|
||||||
//! Slot logging for specified context
|
//! Slot logging for specified context
|
||||||
|
//! \threadsafe
|
||||||
bool isSlotLogEnabledFor(LogContext context) const;
|
bool isSlotLogEnabledFor(LogContext context) const;
|
||||||
|
|
||||||
//! Slot logging
|
//! Slot logging
|
||||||
@@ -266,6 +336,7 @@ namespace BlackCore
|
|||||||
IContextSettings *m_contextSettings;
|
IContextSettings *m_contextSettings;
|
||||||
IContextSimulator *m_contextSimulator;
|
IContextSimulator *m_contextSimulator;
|
||||||
QMultiMap<QString, QMetaObject::Connection> m_logSignalConnections;
|
QMultiMap<QString, QMetaObject::Connection> m_logSignalConnections;
|
||||||
|
mutable QReadWriteLock m_lock;
|
||||||
|
|
||||||
//! initialization of DBus connection (where applicable)
|
//! initialization of DBus connection (where applicable)
|
||||||
void initDBusConnection(const QString &address);
|
void initDBusConnection(const QString &address);
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ using namespace BlackMisc::Network;
|
|||||||
|
|
||||||
namespace BlackCore
|
namespace BlackCore
|
||||||
{
|
{
|
||||||
|
CVatsimBookingReader::CVatsimBookingReader(const QString &url, QObject *parent) :
|
||||||
CVatsimBookingReader::CVatsimBookingReader(const QString &url, QObject *parent) : QObject(parent), m_serviceUrl(url), m_networkManager(nullptr), m_updateTimer(nullptr)
|
QObject(parent), CThreadedReader(),
|
||||||
|
m_serviceUrl(url), m_networkManager(nullptr)
|
||||||
{
|
{
|
||||||
this->m_networkManager = new QNetworkAccessManager(this);
|
this->m_networkManager = new QNetworkAccessManager(this);
|
||||||
this->m_updateTimer = new QTimer(this);
|
|
||||||
this->connect(this->m_networkManager, &QNetworkAccessManager::finished, this, &CVatsimBookingReader::loadFinished);
|
this->connect(this->m_networkManager, &QNetworkAccessManager::finished, this, &CVatsimBookingReader::loadFinished);
|
||||||
this->connect(this->m_updateTimer, &QTimer::timeout, this, &CVatsimBookingReader::read);
|
this->connect(this->m_updateTimer, &QTimer::timeout, this, &CVatsimBookingReader::read);
|
||||||
}
|
}
|
||||||
@@ -27,16 +27,8 @@ namespace BlackCore
|
|||||||
if (url.isEmpty()) return;
|
if (url.isEmpty()) return;
|
||||||
Q_ASSERT(this->m_networkManager);
|
Q_ASSERT(this->m_networkManager);
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
this->m_networkManager->get(request);
|
QNetworkReply *reply = this->m_networkManager->get(request);
|
||||||
}
|
this->setPendingNetworkReply(reply);
|
||||||
|
|
||||||
void CVatsimBookingReader::setInterval(int updatePeriodMs)
|
|
||||||
{
|
|
||||||
Q_ASSERT(this->m_updateTimer);
|
|
||||||
if (updatePeriodMs < 1)
|
|
||||||
this->m_updateTimer->stop();
|
|
||||||
else
|
|
||||||
this->m_updateTimer->start(updatePeriodMs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -44,7 +36,12 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
void CVatsimBookingReader::loadFinished(QNetworkReply *nwReply)
|
void CVatsimBookingReader::loadFinished(QNetworkReply *nwReply)
|
||||||
{
|
{
|
||||||
QtConcurrent::run(this, &CVatsimBookingReader::parseBookings, nwReply);
|
this->setPendingNetworkReply(nullptr);
|
||||||
|
if (!this->isStopped())
|
||||||
|
{
|
||||||
|
QFuture<void> f = QtConcurrent::run(this, &CVatsimBookingReader::parseBookings, nwReply);
|
||||||
|
this->setPendingFuture(f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -52,30 +49,33 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
void CVatsimBookingReader::parseBookings(QNetworkReply *nwReply)
|
void CVatsimBookingReader::parseBookings(QNetworkReply *nwReply)
|
||||||
{
|
{
|
||||||
|
// Worker thread, make sure to write no members here!
|
||||||
|
if (this->isStopped())
|
||||||
|
{
|
||||||
|
qDebug() << "terminated" << Q_FUNC_INFO;
|
||||||
|
return; // stop, terminate straight away, ending thread
|
||||||
|
}
|
||||||
|
|
||||||
if (nwReply->error() == QNetworkReply::NoError)
|
if (nwReply->error() == QNetworkReply::NoError)
|
||||||
{
|
{
|
||||||
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();
|
||||||
QDomDocument doc;
|
QDomDocument doc;
|
||||||
|
QDateTime updateTimestamp = QDateTime::currentDateTimeUtc();
|
||||||
|
|
||||||
if (doc.setContent(xmlData))
|
if (doc.setContent(xmlData))
|
||||||
{
|
{
|
||||||
QDomNode timestamp = doc.elementsByTagName("timestamp").at(0);
|
QDomNode timestamp = doc.elementsByTagName("timestamp").at(0);
|
||||||
QString ts = timestamp.toElement().text().trimmed();
|
QString ts = timestamp.toElement().text().trimmed();
|
||||||
Q_ASSERT(!ts.isEmpty());
|
Q_ASSERT(!ts.isEmpty());
|
||||||
if (ts.isEmpty())
|
|
||||||
{
|
if (!ts.isEmpty())
|
||||||
// fallback
|
|
||||||
m_updateTimestamp = QDateTime::currentDateTimeUtc();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// normally the timestamp is always updated from backend
|
// normally the timestamp is always updated from backend
|
||||||
// if this changes in the future we're prepared
|
// if this changes in the future we're prepared
|
||||||
QDateTime fileTimestamp = QDateTime::fromString(ts, timestampFormat);
|
updateTimestamp = QDateTime::fromString(ts, timestampFormat);
|
||||||
fileTimestamp.setTimeSpec(Qt::UTC);
|
updateTimestamp.setTimeSpec(Qt::UTC);
|
||||||
if (this->m_updateTimestamp == fileTimestamp) return; // nothing to do
|
if (this->getUpdateTimestamp() == updateTimestamp) return; // nothing to do
|
||||||
this->m_updateTimestamp = fileTimestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QDomNode atc = doc.elementsByTagName("atcs").at(0);
|
QDomNode atc = doc.elementsByTagName("atcs").at(0);
|
||||||
@@ -84,6 +84,13 @@ namespace BlackCore
|
|||||||
CAtcStationList bookedStations;
|
CAtcStationList bookedStations;
|
||||||
for (int i = 0; i < size; i++)
|
for (int i = 0; i < size; i++)
|
||||||
{
|
{
|
||||||
|
if (this->isStopped())
|
||||||
|
{
|
||||||
|
qDebug() << "terminated" << Q_FUNC_INFO;
|
||||||
|
return; // stop, terminate straight away, ending thread
|
||||||
|
}
|
||||||
|
|
||||||
|
// pase nodes
|
||||||
QDomNode bookingNode = bookingNodes.at(i);
|
QDomNode bookingNode = bookingNodes.at(i);
|
||||||
QDomNodeList bookingNodeValues = bookingNode.childNodes();
|
QDomNodeList bookingNodeValues = bookingNode.childNodes();
|
||||||
CAtcStation bookedStation;
|
CAtcStation bookedStation;
|
||||||
@@ -129,8 +136,8 @@ namespace BlackCore
|
|||||||
bookedStation.setController(user);
|
bookedStation.setController(user);
|
||||||
bookedStations.push_back(bookedStation);
|
bookedStations.push_back(bookedStation);
|
||||||
}
|
}
|
||||||
|
this->setUpdateTimestamp(updateTimestamp); // thread safe update
|
||||||
emit this->dataRead(bookedStations);
|
emit this->dataRead(bookedStations);
|
||||||
|
|
||||||
} // node
|
} // node
|
||||||
} // content
|
} // content
|
||||||
|
|
||||||
@@ -138,4 +145,5 @@ namespace BlackCore
|
|||||||
nwReply->deleteLater();
|
nwReply->deleteLater();
|
||||||
|
|
||||||
} // method
|
} // method
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -6,50 +6,42 @@
|
|||||||
#ifndef BLACKCORE_VATSIMBOOKINGREADER_H
|
#ifndef BLACKCORE_VATSIMBOOKINGREADER_H
|
||||||
#define BLACKCORE_VATSIMBOOKINGREADER_H
|
#define BLACKCORE_VATSIMBOOKINGREADER_H
|
||||||
|
|
||||||
|
//! \file
|
||||||
|
|
||||||
|
#include "blackmisc/threadedreader.h"
|
||||||
#include "blackmisc/avatcstationlist.h"
|
#include "blackmisc/avatcstationlist.h"
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
namespace BlackCore
|
namespace BlackCore
|
||||||
{
|
{
|
||||||
/*!
|
/*!
|
||||||
* \brief Read bookings from VATSIM
|
* Read bookings from VATSIM
|
||||||
*/
|
*/
|
||||||
class CVatsimBookingReader : public QObject
|
class CVatsimBookingReader : public QObject, public BlackMisc::CThreadedReader<void>
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//! \brief Constructor
|
//! Constructor
|
||||||
explicit CVatsimBookingReader(const QString &url, QObject *parent = nullptr);
|
explicit CVatsimBookingReader(const QString &url, QObject *parent = nullptr);
|
||||||
|
|
||||||
//! \brief Update timestamp
|
//! Read / re-read bookings
|
||||||
QDateTime getUpdateTimestamp() const { return this->m_updateTimestamp; }
|
|
||||||
|
|
||||||
//! \brief Read / re-read bookings
|
|
||||||
void read();
|
void read();
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Set the update time
|
|
||||||
* \param updatePeriodMs 0 stops the timer
|
|
||||||
*/
|
|
||||||
void setInterval(int updatePeriodMs);
|
|
||||||
|
|
||||||
//! \brief Get the timer interval (ms)
|
|
||||||
int interval() const { return this->m_updateTimer->interval();}
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
//! \brief Bookings have been read
|
//! Bookings have been read
|
||||||
void loadFinished(QNetworkReply *nwReply);
|
void loadFinished(QNetworkReply *nwReply);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_serviceUrl; /*!< URL of the service */
|
QString m_serviceUrl; /*!< URL of the service */
|
||||||
QNetworkAccessManager *m_networkManager;
|
QNetworkAccessManager *m_networkManager;
|
||||||
QDateTime m_updateTimestamp;
|
|
||||||
QTimer *m_updateTimer;
|
|
||||||
|
|
||||||
//! Parse received bookings
|
//! Parse received bookings
|
||||||
|
//! \threadsafe
|
||||||
void parseBookings(QNetworkReply *nwReply);
|
void parseBookings(QNetworkReply *nwReply);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|||||||
@@ -13,14 +13,13 @@ using namespace BlackMisc::Network;
|
|||||||
using namespace BlackMisc::Geo;
|
using namespace BlackMisc::Geo;
|
||||||
using namespace BlackMisc::PhysicalQuantities;
|
using namespace BlackMisc::PhysicalQuantities;
|
||||||
|
|
||||||
|
|
||||||
namespace BlackCore
|
namespace BlackCore
|
||||||
{
|
{
|
||||||
|
CVatsimDataFileReader::CVatsimDataFileReader(const QStringList &urls, QObject *parent) :
|
||||||
CVatsimDataFileReader::CVatsimDataFileReader(const QStringList &urls, QObject *parent) : QObject(parent), m_serviceUrls(urls), m_currentUrlIndex(0), m_networkManager(nullptr), m_updateTimer(nullptr)
|
QObject(parent), CThreadedReader(),
|
||||||
|
m_serviceUrls(urls), m_currentUrlIndex(0), m_networkManager(nullptr)
|
||||||
{
|
{
|
||||||
this->m_networkManager = new QNetworkAccessManager(this);
|
this->m_networkManager = new QNetworkAccessManager(this);
|
||||||
this->m_updateTimer = new QTimer(this);
|
|
||||||
this->connect(this->m_networkManager, &QNetworkAccessManager::finished, this, &CVatsimDataFileReader::loadFinished);
|
this->connect(this->m_networkManager, &QNetworkAccessManager::finished, this, &CVatsimDataFileReader::loadFinished);
|
||||||
this->connect(this->m_updateTimer, &QTimer::timeout, this, &CVatsimDataFileReader::read);
|
this->connect(this->m_updateTimer, &QTimer::timeout, this, &CVatsimDataFileReader::read);
|
||||||
}
|
}
|
||||||
@@ -39,16 +38,26 @@ namespace BlackCore
|
|||||||
if (url.isEmpty()) return;
|
if (url.isEmpty()) return;
|
||||||
Q_ASSERT(this->m_networkManager);
|
Q_ASSERT(this->m_networkManager);
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
this->m_networkManager->get(request);
|
QNetworkReply *r = this->m_networkManager->get(request);
|
||||||
|
this->setPendingNetworkReply(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVatsimDataFileReader::setInterval(int updatePeriodMs)
|
const CAircraftList &CVatsimDataFileReader::getAircrafts()
|
||||||
{
|
{
|
||||||
Q_ASSERT(this->m_updateTimer);
|
QReadLocker rl(&this->m_lock);
|
||||||
if (updatePeriodMs < 1)
|
return this->m_aircrafts;
|
||||||
this->m_updateTimer->stop();
|
}
|
||||||
else
|
|
||||||
this->m_updateTimer->start(updatePeriodMs);
|
const CAtcStationList &CVatsimDataFileReader::getAtcStations()
|
||||||
|
{
|
||||||
|
QReadLocker rl(&this->m_lock);
|
||||||
|
return this->m_atcStations;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CServerList &CVatsimDataFileReader::getVoiceServers()
|
||||||
|
{
|
||||||
|
QReadLocker rl(&this->m_lock);
|
||||||
|
return this->m_voiceServers;
|
||||||
}
|
}
|
||||||
|
|
||||||
CUserList CVatsimDataFileReader::getPilotsForCallsigns(const CCallsignList &callsigns)
|
CUserList CVatsimDataFileReader::getPilotsForCallsigns(const CCallsignList &callsigns)
|
||||||
@@ -57,7 +66,7 @@ namespace BlackCore
|
|||||||
if (callsigns.isEmpty()) return users;
|
if (callsigns.isEmpty()) return users;
|
||||||
foreach(CCallsign callsign, callsigns)
|
foreach(CCallsign callsign, callsigns)
|
||||||
{
|
{
|
||||||
users.push_back(this->m_aircrafts.findByCallsign(callsign).getPilots());
|
users.push_back(this->getAircrafts().findByCallsign(callsign).getPilots());
|
||||||
}
|
}
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
@@ -71,7 +80,7 @@ namespace BlackCore
|
|||||||
|
|
||||||
CAircraftIcao CVatsimDataFileReader::getIcaoInfo(const CCallsign &callsign)
|
CAircraftIcao CVatsimDataFileReader::getIcaoInfo(const CCallsign &callsign)
|
||||||
{
|
{
|
||||||
CAircraft aircraft = this->m_aircrafts.findFirstByCallsign(callsign);
|
CAircraft aircraft = this->getAircrafts().findFirstByCallsign(callsign);
|
||||||
return aircraft.getIcaoInfo();
|
return aircraft.getIcaoInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +104,7 @@ namespace BlackCore
|
|||||||
if (callsigns.isEmpty()) return users;
|
if (callsigns.isEmpty()) return users;
|
||||||
foreach(CCallsign callsign, callsigns)
|
foreach(CCallsign callsign, callsigns)
|
||||||
{
|
{
|
||||||
users.push_back(this->m_atcStations.findByCallsign(callsign).getControllers());
|
users.push_back(this->getAtcStations().findByCallsign(callsign).getControllers());
|
||||||
}
|
}
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
@@ -117,7 +126,12 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
void CVatsimDataFileReader::loadFinished(QNetworkReply *nwReply)
|
void CVatsimDataFileReader::loadFinished(QNetworkReply *nwReply)
|
||||||
{
|
{
|
||||||
QtConcurrent::run(this, &CVatsimDataFileReader::parseVatsimFileInBackground, nwReply);
|
this->setPendingNetworkReply(nullptr);
|
||||||
|
if (!this->isStopped())
|
||||||
|
{
|
||||||
|
QFuture<void> f = QtConcurrent::run(this, &CVatsimDataFileReader::parseVatsimFileInBackground, nwReply);
|
||||||
|
this->setPendingFuture(f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -126,8 +140,14 @@ namespace BlackCore
|
|||||||
*/
|
*/
|
||||||
void CVatsimDataFileReader::parseVatsimFileInBackground(QNetworkReply *nwReply)
|
void CVatsimDataFileReader::parseVatsimFileInBackground(QNetworkReply *nwReply)
|
||||||
{
|
{
|
||||||
QStringList illegalIcaoCodes;
|
// Worker thread, make sure to write only synced here!
|
||||||
|
if (this->isStopped())
|
||||||
|
{
|
||||||
|
qDebug() << "terminated" << Q_FUNC_INFO;
|
||||||
|
return; // stop, terminate straight away, ending thread
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList illegalIcaoCodes;
|
||||||
if (nwReply->error() == QNetworkReply::NoError)
|
if (nwReply->error() == QNetworkReply::NoError)
|
||||||
{
|
{
|
||||||
const QString dataFileData = nwReply->readAll();
|
const QString dataFileData = nwReply->readAll();
|
||||||
@@ -135,10 +155,23 @@ namespace BlackCore
|
|||||||
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
|
||||||
|
CServerList voiceServers;
|
||||||
|
CAtcStationList atcStations;
|
||||||
|
CAircraftList aircrafts;
|
||||||
|
QDateTime updateTimestampFromFile;
|
||||||
|
|
||||||
QStringList clientSectionAttributes;
|
QStringList clientSectionAttributes;
|
||||||
Section section = SectionNone;
|
Section section = SectionNone;
|
||||||
foreach(QString currentLine, lines)
|
foreach(QString currentLine, lines)
|
||||||
{
|
{
|
||||||
|
if (this->isStopped())
|
||||||
|
{
|
||||||
|
qDebug() << "terminated" << Q_FUNC_INFO;
|
||||||
|
return; // stop, terminate straight away, ending thread
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse lines
|
||||||
currentLine = currentLine.trimmed();
|
currentLine = currentLine.trimmed();
|
||||||
if (currentLine.isEmpty()) continue;
|
if (currentLine.isEmpty()) continue;
|
||||||
if (currentLine.startsWith(";"))
|
if (currentLine.startsWith(";"))
|
||||||
@@ -162,13 +195,10 @@ namespace BlackCore
|
|||||||
else if (currentLine.contains("VOICE SERVERS", Qt::CaseInsensitive))
|
else if (currentLine.contains("VOICE SERVERS", Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
section = SectionVoiceServer;
|
section = SectionVoiceServer;
|
||||||
this->m_voiceServers.clear();
|
|
||||||
}
|
}
|
||||||
else if (currentLine.contains("CLIENTS", Qt::CaseInsensitive))
|
else if (currentLine.contains("CLIENTS", Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
section = SectionClients;
|
section = SectionClients;
|
||||||
this->m_aircrafts.clear();
|
|
||||||
this->m_atcStations.clear();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -217,16 +247,16 @@ namespace BlackCore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->m_aircrafts.push_back(aircraft);
|
aircrafts.push_back(aircraft);
|
||||||
}
|
}
|
||||||
else if (clientType.startsWith('a'))
|
else if (clientType.startsWith('a'))
|
||||||
{
|
{
|
||||||
// ATC section
|
// ATC section
|
||||||
CLength range;
|
CLength range;
|
||||||
position.setHeight(altitude); // the altitude is elevation for a station
|
position.setGeodeticHeight(altitude); // the altitude is elevation for a station
|
||||||
CAtcStation station(user.getCallsign().getStringAsSet(), user, frequency, position, range);
|
CAtcStation station(user.getCallsign().getStringAsSet(), user, frequency, position, range);
|
||||||
station.setOnline(true);
|
station.setOnline(true);
|
||||||
this->m_atcStations.push_back(station);
|
atcStations.push_back(station);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -241,40 +271,45 @@ namespace BlackCore
|
|||||||
QStringList updateParts = currentLine.replace(" ", "").split('=');
|
QStringList updateParts = currentLine.replace(" ", "").split('=');
|
||||||
if (updateParts.length() < 2) continue;
|
if (updateParts.length() < 2) continue;
|
||||||
QString dts = updateParts.at(1).trimmed();
|
QString dts = updateParts.at(1).trimmed();
|
||||||
QDateTime dt = QDateTime::fromString(dts, "yyyyMMddHHmmss");
|
updateTimestampFromFile = QDateTime::fromString(dts, "yyyyMMddHHmmss");
|
||||||
dt.setOffsetFromUtc(0);
|
updateTimestampFromFile.setOffsetFromUtc(0);
|
||||||
if (dt == this->m_updateTimestamp)
|
bool alreadyRead = (updateTimestampFromFile == this->getUpdateTimestamp());
|
||||||
{
|
if (alreadyRead) { return; }// still same data, terminate
|
||||||
return; // still same data, terminate
|
|
||||||
}
|
|
||||||
this->m_updateTimestamp = dt;
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
break;
|
} // for each
|
||||||
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());
|
|
||||||
this->m_voiceServers.push_back(voiceServer);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SectionNone:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} // for each
|
|
||||||
}
|
|
||||||
|
|
||||||
nwReply->close();
|
// this part needs to be synchronized
|
||||||
nwReply->deleteLater(); // we are responsible for reading this
|
this->m_lock.lockForWrite();
|
||||||
emit this->dataRead();
|
this->m_updateTimestamp = updateTimestampFromFile;
|
||||||
|
this->m_aircrafts = aircrafts;
|
||||||
|
this->m_atcStations = atcStations;
|
||||||
|
this->m_voiceServers = voiceServers;
|
||||||
|
this->m_lock.unlock();
|
||||||
|
} // read success
|
||||||
|
|
||||||
// warnings, if required
|
nwReply->close();
|
||||||
if (!illegalIcaoCodes.isEmpty())
|
nwReply->deleteLater(); // we are responsible for reading this
|
||||||
{
|
|
||||||
const QString w = QString("Illegal ICAO code(s) in VATSIM data file: %1").arg(illegalIcaoCodes.join(", "));
|
emit this->dataRead();
|
||||||
qWarning(w.toLatin1());
|
// warnings, if required
|
||||||
|
if (!illegalIcaoCodes.isEmpty())
|
||||||
|
{
|
||||||
|
const QString w = QString("Illegal ICAO code(s) in VATSIM data file: %1").arg(illegalIcaoCodes.join(", "));
|
||||||
|
qWarning(w.toLatin1());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
#ifndef BLACKCORE_VATSIMDATAFILEREADER_H
|
#ifndef BLACKCORE_VATSIMDATAFILEREADER_H
|
||||||
#define BLACKCORE_VATSIMDATAFILEREADER_H
|
#define BLACKCORE_VATSIMDATAFILEREADER_H
|
||||||
|
|
||||||
|
//! \file
|
||||||
|
|
||||||
|
#include "blackmisc/threadedreader.h"
|
||||||
#include "blackmisc/avatcstationlist.h"
|
#include "blackmisc/avatcstationlist.h"
|
||||||
#include "blackmisc/avaircraftlist.h"
|
#include "blackmisc/avaircraftlist.h"
|
||||||
#include "blackmisc/nwserverlist.h"
|
#include "blackmisc/nwserverlist.h"
|
||||||
@@ -15,13 +18,14 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
namespace BlackCore
|
namespace BlackCore
|
||||||
{
|
{
|
||||||
/*!
|
/*!
|
||||||
* Read bookings from VATSIM
|
* Read bookings from VATSIM
|
||||||
*/
|
*/
|
||||||
class CVatsimDataFileReader : public QObject
|
class CVatsimDataFileReader : public QObject, public BlackMisc::CThreadedReader<void>
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -29,29 +33,17 @@ namespace BlackCore
|
|||||||
//! Constructor
|
//! Constructor
|
||||||
explicit CVatsimDataFileReader(const QStringList &urls, QObject *parent = nullptr);
|
explicit CVatsimDataFileReader(const QStringList &urls, QObject *parent = nullptr);
|
||||||
|
|
||||||
//! Update timestamp
|
|
||||||
QDateTime getUpdateTimestamp() const { return this->m_updateTimestamp; }
|
|
||||||
|
|
||||||
//! Read / re-read bookings
|
//! Read / re-read bookings
|
||||||
void read();
|
void read();
|
||||||
|
|
||||||
/*!
|
//! Get aircrafts
|
||||||
* \brief Set the update time
|
const BlackMisc::Aviation::CAircraftList &getAircrafts();
|
||||||
* \param updatePeriodMs 0 stops the timer
|
|
||||||
*/
|
|
||||||
void setInterval(int updatePeriodMs);
|
|
||||||
|
|
||||||
//! Get the timer interval (ms)
|
|
||||||
int interval() const { return this->m_updateTimer->interval();}
|
|
||||||
|
|
||||||
//! Get aircrafts
|
//! Get aircrafts
|
||||||
const BlackMisc::Aviation::CAircraftList &getAircrafts() { return this->m_aircrafts; }
|
const BlackMisc::Aviation::CAtcStationList &getAtcStations();
|
||||||
|
|
||||||
//! Get aircrafts
|
|
||||||
const BlackMisc::Aviation::CAtcStationList &getAtcStations() { return this->m_atcStations; }
|
|
||||||
|
|
||||||
//! Get all voice servers
|
//! Get all voice servers
|
||||||
const BlackMisc::Network::CServerList &getVoiceServers() { return this->m_voiceServers; }
|
const BlackMisc::Network::CServerList &getVoiceServers();
|
||||||
|
|
||||||
//! Users for callsign(s)
|
//! Users for callsign(s)
|
||||||
BlackMisc::Network::CUserList getUsersForCallsigns(const BlackMisc::Aviation::CCallsignList &callsigns);
|
BlackMisc::Network::CUserList getUsersForCallsigns(const BlackMisc::Aviation::CCallsignList &callsigns);
|
||||||
@@ -85,8 +77,6 @@ namespace BlackCore
|
|||||||
QStringList m_serviceUrls; /*!< URL of the service */
|
QStringList m_serviceUrls; /*!< URL of the service */
|
||||||
int m_currentUrlIndex;
|
int m_currentUrlIndex;
|
||||||
QNetworkAccessManager *m_networkManager;
|
QNetworkAccessManager *m_networkManager;
|
||||||
QDateTime m_updateTimestamp;
|
|
||||||
QTimer *m_updateTimer;
|
|
||||||
BlackMisc::Network::CServerList m_voiceServers;
|
BlackMisc::Network::CServerList m_voiceServers;
|
||||||
BlackMisc::Aviation::CAtcStationList m_atcStations;
|
BlackMisc::Aviation::CAtcStationList m_atcStations;
|
||||||
BlackMisc::Aviation::CAircraftList m_aircrafts;
|
BlackMisc::Aviation::CAircraftList m_aircrafts;
|
||||||
@@ -107,6 +97,7 @@ namespace BlackCore
|
|||||||
signals:
|
signals:
|
||||||
//! Data have been read
|
//! Data have been read
|
||||||
void dataRead();
|
void dataRead();
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
146
src/blackmisc/threadedreader.h
Normal file
146
src/blackmisc/threadedreader.h
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
#ifndef BLACKMISC_THREADED_READER_H
|
||||||
|
#define BLACKMISC_THREADED_READER_H
|
||||||
|
|
||||||
|
//! \file
|
||||||
|
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
// Header only class, to avoid orward instantiation across subprojects
|
||||||
|
|
||||||
|
namespace BlackMisc
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* Support for threaded based reading and parsing tasks such
|
||||||
|
* as data files via http, or file system and parsing (such as FSX models)
|
||||||
|
*/
|
||||||
|
template <class FutureRet = void> class CThreadedReader
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Destructor
|
||||||
|
virtual ~CThreadedReader()
|
||||||
|
{
|
||||||
|
delete m_updateTimer;
|
||||||
|
this->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Thread safe, set update timestamp
|
||||||
|
//! \threadsafe
|
||||||
|
QDateTime getUpdateTimestamp() const
|
||||||
|
{
|
||||||
|
QReadLocker(&this->m_lock);
|
||||||
|
return this->m_updateTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Thread safe, set update timestamp
|
||||||
|
//! \threadsafe
|
||||||
|
void setUpdateTimestamp(const QDateTime &updateTimestamp)
|
||||||
|
{
|
||||||
|
QWriteLocker(&this->m_lock);
|
||||||
|
this->m_updateTimestamp = updateTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Thread safe, mark as stopped
|
||||||
|
virtual void stop()
|
||||||
|
{
|
||||||
|
if (this->isStopped()) return;
|
||||||
|
this->setStopFlag();
|
||||||
|
this->setInterval(0);
|
||||||
|
|
||||||
|
// shutdown pending
|
||||||
|
if (this->m_pendingFuture.isRunning())
|
||||||
|
{
|
||||||
|
// cancel does not work with all futures, especially not with QConcurrent::run
|
||||||
|
// the stop flag should the job
|
||||||
|
// but I will cancel anyway
|
||||||
|
this->m_pendingFuture.cancel();
|
||||||
|
}
|
||||||
|
if (this->m_pendingNetworkReply && this->m_pendingNetworkReply->isRunning())
|
||||||
|
{
|
||||||
|
this->m_pendingNetworkReply->abort();
|
||||||
|
QCoreApplication::processEvents(QEventLoop::AllEvents, 250); // allow the abort to be called
|
||||||
|
}
|
||||||
|
|
||||||
|
// cancel or stop flag above should terminate QFuture
|
||||||
|
this->m_pendingFuture.waitForFinished();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Thread safe, is in state stopped?
|
||||||
|
//! \threadsafe
|
||||||
|
bool isStopped() const
|
||||||
|
{
|
||||||
|
QReadLocker rl(&this->m_lock);
|
||||||
|
return this->m_stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set the update time
|
||||||
|
* \param updatePeriodMs <=0 stops the timer
|
||||||
|
* \threadsafe
|
||||||
|
*/
|
||||||
|
void setInterval(int updatePeriodMs)
|
||||||
|
{
|
||||||
|
Q_ASSERT(this->m_updateTimer);
|
||||||
|
QWriteLocker(&this->m_lock);
|
||||||
|
if (updatePeriodMs < 1)
|
||||||
|
this->m_updateTimer->stop();
|
||||||
|
else
|
||||||
|
this->m_updateTimer->start(updatePeriodMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Get the timer interval (ms)
|
||||||
|
//! \threadsafe
|
||||||
|
int interval() const
|
||||||
|
{
|
||||||
|
QReadLocker rl(&this->m_lock);
|
||||||
|
return this->m_updateTimer->interval();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//! Constructor
|
||||||
|
CThreadedReader() :
|
||||||
|
m_updateTimer(nullptr), m_stopped(false), m_pendingNetworkReply(nullptr), m_lock(QReadWriteLock::Recursive)
|
||||||
|
{
|
||||||
|
this->m_updateTimer = new QTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Has pending network replay
|
||||||
|
//! \threadsafe
|
||||||
|
void setPendingNetworkReply(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
QWriteLocker(&this->m_lock);
|
||||||
|
this->m_pendingNetworkReply = reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Has pending operation
|
||||||
|
//! \threadsafe
|
||||||
|
void setPendingFuture(QFuture<FutureRet> future)
|
||||||
|
{
|
||||||
|
QWriteLocker(&this->m_lock);
|
||||||
|
this->m_pendingFuture = future;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Thread safe, mark as to be stopped
|
||||||
|
//! \threadsafe
|
||||||
|
void setStopFlag()
|
||||||
|
{
|
||||||
|
QWriteLocker wl(&this->m_lock);
|
||||||
|
this->m_stopped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime m_updateTimestamp;
|
||||||
|
QTimer *m_updateTimer;
|
||||||
|
bool m_stopped;
|
||||||
|
QFuture<FutureRet> m_pendingFuture; //!< optional future to be stopped
|
||||||
|
QNetworkReply *m_pendingNetworkReply; //!< optional future to be stopped
|
||||||
|
mutable QReadWriteLock m_lock;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif // guard
|
||||||
Reference in New Issue
Block a user