From 704068d29902e9f35d791add50936c158038dd4d Mon Sep 17 00:00:00 2001 From: Roland Winklmeier Date: Mon, 22 Jan 2018 14:18:50 +0100 Subject: [PATCH] Add methods to receive and write raw FSD messages This commit adds methods to register for live FSD message reception. The amount of traffic can be quite high, therefore no normal signal is used - which would be available via DBus. Instead one has to connect manually by passing a functor. This guarantees that we communicate only in-process. If someone tries to connect on the proxy side, the connection will fail. This needs to be handled properly in client code. This commit also adds a method to write the FSD message to a selected file. Maniphest Tasks: T222 --- src/blackcore/context/contextnetwork.h | 9 ++ src/blackcore/context/contextnetworkempty.h | 10 +++ src/blackcore/context/contextnetworkimpl.cpp | 11 +++ src/blackcore/context/contextnetworkimpl.h | 4 + src/blackcore/context/contextnetworkproxy.cpp | 8 ++ src/blackcore/context/contextnetworkproxy.h | 4 + src/blackcore/network.h | 6 ++ src/blackcore/registermetadata.cpp | 1 + src/blackcore/vatsim/networkvatlib.cpp | 48 +++++++++++ src/blackcore/vatsim/networkvatlib.h | 9 +- src/blackcore/vatsim/vatsimsettings.cpp | 42 +++++++++ src/blackcore/vatsim/vatsimsettings.h | 85 +++++++++++++++++++ src/blackmisc/propertyindex.h | 1 + 13 files changed, 236 insertions(+), 2 deletions(-) diff --git a/src/blackcore/context/contextnetwork.h b/src/blackcore/context/contextnetwork.h index 16ac01b5c..210870ace 100644 --- a/src/blackcore/context/contextnetwork.h +++ b/src/blackcore/context/contextnetwork.h @@ -40,6 +40,8 @@ #include "blackmisc/statusmessage.h" #include "blackmisc/weather/metar.h" +#include + //! \addtogroup dbus //! @{ @@ -302,6 +304,13 @@ namespace BlackCore //! Request parts for callsign (from another client) virtual void testRequestAircraftConfig(const BlackMisc::Aviation::CCallsign &callsign) = 0; + public: + //! Raw FSD message receiver functor + using RawFsdMessageReceivedSlot = std::function; + + //! Connect to receive raw fsd messages + virtual QMetaObject::Connection connectRawFsdMessageSignal(QObject *receiver, RawFsdMessageReceivedSlot rawFsdMessageReceivedSlot) = 0; + protected: //! Constructor IContextNetwork(CCoreFacadeConfig::ContextMode mode, CCoreFacade *runtime) : CContext(mode, runtime) {} diff --git a/src/blackcore/context/contextnetworkempty.h b/src/blackcore/context/contextnetworkempty.h index b132404cf..dba702b91 100644 --- a/src/blackcore/context/contextnetworkempty.h +++ b/src/blackcore/context/contextnetworkempty.h @@ -385,6 +385,16 @@ namespace BlackCore logEmptyContextWarning(Q_FUNC_INFO); Q_UNUSED(enabled); } + + public: + //! \copydoc IContextNetwork::connectRawFsdMessageSignal + virtual QMetaObject::Connection connectRawFsdMessageSignal(QObject *receiver, RawFsdMessageReceivedSlot rawFsdMessageReceivedSlot) override + { + logEmptyContextWarning(Q_FUNC_INFO); + Q_UNUSED(receiver); + Q_UNUSED(rawFsdMessageReceivedSlot); + return {}; + } }; } // namespace } // namespace diff --git a/src/blackcore/context/contextnetworkimpl.cpp b/src/blackcore/context/contextnetworkimpl.cpp index add268343..119950098 100644 --- a/src/blackcore/context/contextnetworkimpl.cpp +++ b/src/blackcore/context/contextnetworkimpl.cpp @@ -751,5 +751,16 @@ namespace BlackCore rooms.push_back(s2.getVoiceRoom()); return rooms; } + + QMetaObject::Connection CContextNetwork::connectRawFsdMessageSignal(QObject *receiver, RawFsdMessageReceivedSlot rawFsdMessageReceivedSlot) + { + Q_ASSERT_X(receiver, Q_FUNC_INFO, "Missing receiver"); + + // bind does not allow to define connection type, so we use receiver as workaround + const QMetaObject::Connection uc; // unconnected + const QMetaObject::Connection c = rawFsdMessageReceivedSlot ? connect(m_network, &INetwork::rawFsdMessageReceived, receiver, rawFsdMessageReceivedSlot) : uc; + Q_ASSERT_X(c || !rawFsdMessageReceivedSlot, Q_FUNC_INFO, "connect failed"); + return c; + } } // namespace } // namespace diff --git a/src/blackcore/context/contextnetworkimpl.h b/src/blackcore/context/contextnetworkimpl.h index ad7677fcd..0211df185 100644 --- a/src/blackcore/context/contextnetworkimpl.h +++ b/src/blackcore/context/contextnetworkimpl.h @@ -191,6 +191,10 @@ namespace BlackCore //! Gracefully shut down, e.g. for thread safety void gracefulShutdown(); + public: + //! \copydoc IContextNetwork::connectRawFsdMessageSignal + virtual QMetaObject::Connection connectRawFsdMessageSignal(QObject *receiver, RawFsdMessageReceivedSlot rawFsdMessageReceivedSlot) override; + protected: //! Constructor, with link to runtime CContextNetwork(CCoreFacadeConfig::ContextMode, CCoreFacade *runtime); diff --git a/src/blackcore/context/contextnetworkproxy.cpp b/src/blackcore/context/contextnetworkproxy.cpp index 2c7136842..095224fe0 100644 --- a/src/blackcore/context/contextnetworkproxy.cpp +++ b/src/blackcore/context/contextnetworkproxy.cpp @@ -93,6 +93,7 @@ namespace BlackCore s = connection.connect(serviceName, IContextNetwork::ObjectPath(), IContextNetwork::InterfaceName(), "removedAircraft", this, SIGNAL(removedAircraft(BlackMisc::Aviation::CCallsign))); Q_ASSERT(s); + Q_UNUSED(s); this->relayBaseClassSignals(serviceName, connection, IContextNetwork::ObjectPath(), IContextNetwork::InterfaceName()); } @@ -331,5 +332,12 @@ namespace BlackCore { return m_dBusInterface->callDBusRet(QLatin1String("getMetarForAirport"), airportIcaoCode); } + + QMetaObject::Connection CContextNetworkProxy::connectRawFsdMessageSignal(QObject *receiver, RawFsdMessageReceivedSlot rawFsdMessageReceivedSlot) + { + Q_UNUSED(receiver); + Q_UNUSED(rawFsdMessageReceivedSlot); + return {}; + } } // ns } // ns diff --git a/src/blackcore/context/contextnetworkproxy.h b/src/blackcore/context/contextnetworkproxy.h index 54dfe95e7..895c01abf 100644 --- a/src/blackcore/context/contextnetworkproxy.h +++ b/src/blackcore/context/contextnetworkproxy.h @@ -124,6 +124,10 @@ namespace BlackCore virtual void testRequestAircraftConfig(const BlackMisc::Aviation::CCallsign &callsign) override; //! @} + public: + //! \copydoc IContextNetwork::connectRawFsdMessageSignal + virtual QMetaObject::Connection connectRawFsdMessageSignal(QObject *receiver, RawFsdMessageReceivedSlot rawFsdMessageReceivedSlot) override; + private: BlackMisc::CGenericDBusInterface *m_dBusInterface; /*!< DBus interface */ diff --git a/src/blackcore/network.h b/src/blackcore/network.h index ed5511171..51fe25978 100644 --- a/src/blackcore/network.h +++ b/src/blackcore/network.h @@ -16,6 +16,7 @@ #include "blackmisc/simulation/simulatorplugininfo.h" #include "blackmisc/simulation/simulatedaircraft.h" #include "blackmisc/geo/coordinategeodetic.h" +#include "blackmisc/network/rawfsdmessage.h" #include "blackmisc/network/serverlist.h" #include "blackmisc/network/textmessagelist.h" #include "blackmisc/aviation/informationmessage.h" @@ -526,6 +527,11 @@ namespace BlackCore */ void aircraftConfigPacketReceived(const BlackMisc::Aviation::CCallsign &callsign, const QJsonObject &incremental, bool isFull); + /*! + * We received a raw message for debugging purposes + */ + void rawFsdMessageReceived(const BlackMisc::Network::CRawFsdMessage &rawFsdMessage); + //! @} //////////////////////////////////////////////////////////////// //! \name Weather signals diff --git a/src/blackcore/registermetadata.cpp b/src/blackcore/registermetadata.cpp index 94f7ca7d5..4a3cc389a 100644 --- a/src/blackcore/registermetadata.cpp +++ b/src/blackcore/registermetadata.cpp @@ -49,5 +49,6 @@ namespace BlackCore Data::CVatsimSetup::registerMetadata(); Data::CLauncherSetup::registerMetadata(); Vatsim::CReaderSettings::registerMetadata(); + Vatsim::CRawFsdMessageSettings::registerMetadata(); } } // namespace diff --git a/src/blackcore/vatsim/networkvatlib.cpp b/src/blackcore/vatsim/networkvatlib.cpp index d94b94bc0..7d2bd4012 100644 --- a/src/blackcore/vatsim/networkvatlib.cpp +++ b/src/blackcore/vatsim/networkvatlib.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -158,6 +159,7 @@ namespace BlackCore Vat_SetAircraftInfoHandler(m_net.data(), onPilotInfoReceived, this); Vat_SetCustomPilotPacketHandler(m_net.data(), onCustomPacketReceived, this); Vat_SetAircraftConfigHandler(m_net.data(), onAircraftConfigReceived, this); + Vat_SetFsdMessageHandler(m_net.data(), onRawFsdMessage, this); } CNetworkVatlib::~CNetworkVatlib() @@ -983,6 +985,11 @@ namespace BlackCore cbvar_cast(cbvar)->customPacketDispatcher(cbvar_cast(cbvar)->fromFSD(callsign), cbvar_cast(cbvar)->fromFSD(packetId), cbvar_cast(cbvar)->fromFSD(data, dataSize)); } + void CNetworkVatlib::onRawFsdMessage(VatFsdClient *, const char *message, void *cbvar) + { + cbvar_cast(cbvar)->handleRawFsdMessage(cbvar_cast(cbvar)->fromFSD(message)); + } + void CNetworkVatlib::customPacketDispatcher(const CCallsign &callsign, const QString &packetId, const QStringList &data) { if (packetId.compare("FSIPI", Qt::CaseInsensitive) == 0) @@ -1022,6 +1029,47 @@ namespace BlackCore } } + void CNetworkVatlib::handleRawFsdMessage(const QString &fsdMessage) + { + CRawFsdMessage rawFsdMessage(fsdMessage); + if (m_rawFsdMessageLogFile.isOpen()) + { + QTextStream stream(&m_rawFsdMessageLogFile); + stream << rawFsdMessage.toQString() << endl; + } + emit rawFsdMessageReceived(rawFsdMessage); + } + + void CNetworkVatlib::fsdMessageSettingsChanged() + { + if (m_rawFsdMessageLogFile.isOpen()) { m_rawFsdMessageLogFile.close(); } + const CRawFsdMessageSettings setting = m_fsdMessageSetting.get(); + if (!setting.isFileWritingEnabled() || setting.getFileDir().isEmpty()) { return; } + + if (setting.getFileWriteMode() == CRawFsdMessageSettings::Truncate) + { + QString filePath = CFileUtils::appendFilePaths(setting.getFileDir(), "rawfsdmessages.log"); + m_rawFsdMessageLogFile.setFileName(filePath); + m_rawFsdMessageLogFile.open(QIODevice::Text | QIODevice::WriteOnly); + } + else if (setting.getFileWriteMode() == CRawFsdMessageSettings::Append) + { + QString filePath = CFileUtils::appendFilePaths(setting.getFileDir(), "rawfsdmessages.log"); + m_rawFsdMessageLogFile.setFileName(filePath); + m_rawFsdMessageLogFile.open(QIODevice::Text | QIODevice::WriteOnly | QIODevice::Append); + } + else if (setting.getFileWriteMode() == CRawFsdMessageSettings::Timestamped) + { + QString filename("rawfsdmessages"); + filename += QLatin1String("_"); + filename += QDateTime::currentDateTime().toString(QStringLiteral("yyMMddhhmmss")); + filename += QLatin1String(".log"); + QString filePath = CFileUtils::appendFilePaths(setting.getFileDir(), filename); + m_rawFsdMessageLogFile.setFileName(filePath); + m_rawFsdMessageLogFile.open(QIODevice::Text | QIODevice::WriteOnly); + } + } + void CNetworkVatlib::consolidateTextMessage(const CTextMessage &textMessage) { if (textMessage.isSupervisorMessage()) diff --git a/src/blackcore/vatsim/networkvatlib.h b/src/blackcore/vatsim/networkvatlib.h index 28faae5ec..69dcb7202 100644 --- a/src/blackcore/vatsim/networkvatlib.h +++ b/src/blackcore/vatsim/networkvatlib.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -165,6 +166,7 @@ namespace BlackCore static void onPilotPositionUpdate(VatFsdClient *, const char *callsign, const VatPilotPosition *position, void *cbvar); static void onAircraftConfigReceived(VatFsdClient *, const char *callsign, const char *aircraftConfig, void *cbvar); static void onCustomPacketReceived(VatFsdClient *, const char *callsign, const char *packetId, const char **data, int dataSize, void *cbvar); + static void onRawFsdMessage(VatFsdClient *, const char *message, void *cbvar); //! @} QByteArray toFSD(const QString &qstr) const; @@ -198,6 +200,8 @@ namespace BlackCore void sendPositionUpdate(); void sendInterimPositions(); void customPacketDispatcher(const BlackMisc::Aviation::CCallsign &callsign, const QString &packetId, const QStringList &data); + void handleRawFsdMessage(const QString &fsdMessage); + void fsdMessageSettingsChanged(); signals: void terminate(); //!< \private @@ -251,10 +255,11 @@ namespace BlackCore { QDateTime m_queryTime = QDateTime::currentDateTimeUtc(); QStringList m_atisMessage; - }; - QHash m_pendingAtisQueries; + + BlackMisc::CSettingReadOnly m_fsdMessageSetting { this, &CNetworkVatlib::fsdMessageSettingsChanged }; + QFile m_rawFsdMessageLogFile; }; } //namespace } //namespace diff --git a/src/blackcore/vatsim/vatsimsettings.cpp b/src/blackcore/vatsim/vatsimsettings.cpp index 05f88a3be..0f7fa6eff 100644 --- a/src/blackcore/vatsim/vatsimsettings.cpp +++ b/src/blackcore/vatsim/vatsimsettings.cpp @@ -74,5 +74,47 @@ namespace BlackCore break; } } + + CRawFsdMessageSettings::CRawFsdMessageSettings() + { } + + CRawFsdMessageSettings::CRawFsdMessageSettings(bool enabled, const QString &FileDir) : + m_fileWritingEnabled(enabled), m_FileDir(FileDir) + { } + + QString CRawFsdMessageSettings::convertToQString(bool i18n) const + { + Q_UNUSED(i18n); + QString s("CRawFsdMessageSettings"); + s.append(" enabled: ").append(boolToYesNo(m_fileWritingEnabled)); + s.append(" dir: ").append(m_FileDir); + return s; + } + + CVariant CRawFsdMessageSettings::propertyByIndex(const BlackMisc::CPropertyIndex &index) const + { + if (index.isMyself()) { return CVariant::from(*this); } + ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexWriteEnabled: return CVariant::fromValue(this->m_fileWritingEnabled); + case IndexFileDir: return CVariant::fromValue(this->m_FileDir); + case IndexFileWriteMode: return CVariant::fromValue(this->m_fileWriteMode); + default: return CValueObject::propertyByIndex(index); + } + } + + void CRawFsdMessageSettings::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant) + { + if (index.isMyself()) { (*this) = variant.to(); return; } + ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexWriteEnabled: this->m_fileWritingEnabled = variant.toBool(); break; + case IndexFileDir: this->m_FileDir = variant.toQString(); break; + case IndexFileWriteMode: this->m_fileWriteMode = variant.to(); break; + default: CValueObject::setPropertyByIndex(index, variant); break; + } + } } // ns } // ns diff --git a/src/blackcore/vatsim/vatsimsettings.h b/src/blackcore/vatsim/vatsimsettings.h index 2461649ba..6cbc8cc50 100644 --- a/src/blackcore/vatsim/vatsimsettings.h +++ b/src/blackcore/vatsim/vatsimsettings.h @@ -17,6 +17,8 @@ #include "blackmisc/valueobject.h" #include "blackmisc/pq/time.h" #include "blackmisc/network/serverlist.h" +#include "blackmisc/directoryutils.h" +#include "blackmisc/fileutils.h" namespace BlackCore { @@ -164,11 +166,94 @@ namespace BlackCore return reader; } }; + + //! FSD Message settings + class BLACKCORE_EXPORT CRawFsdMessageSettings : public BlackMisc::CValueObject + { + public: + //! File writing mode + enum FileWriteMode + { + Truncate, + Append, + Timestamped + }; + + //! Properties by index + enum ColumnIndex + { + IndexWriteEnabled = BlackMisc::CPropertyIndex::GlobalIndexRawFsdMessageSettings, + IndexFileDir, + IndexFileWriteMode + }; + + //! Default constructor. + CRawFsdMessageSettings(); + + //! Simplified constructor + CRawFsdMessageSettings(bool enabled, const QString &fileDir); + + //! Is file writing enabled? + bool isFileWritingEnabled() const { return m_fileWritingEnabled; } + + //! Get file directory + QString getFileDir() const { return m_FileDir; } + + //! Get file write mode + FileWriteMode getFileWriteMode () const { return m_fileWriteMode; } + + //! \copydoc BlackMisc::Mixin::Index::propertyByIndex + BlackMisc::CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; + + //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex + void setPropertyByIndex(const BlackMisc::CPropertyIndex &index, const BlackMisc::CVariant &variant); + + //! \copydoc BlackMisc::Mixin::String::toQString + QString convertToQString(bool i18n = false) const; + + private: + bool m_fileWritingEnabled = false; + QString m_FileDir; + FileWriteMode m_fileWriteMode = Truncate; + + BLACK_METACLASS( + CRawFsdMessageSettings, + BLACK_METAMEMBER(fileWritingEnabled), + BLACK_METAMEMBER(FileDir), + BLACK_METAMEMBER(fileWriteMode) + ); + }; + + //! Raw FSD message settings + struct TRawFsdMessageSetting : public BlackMisc::TSettingTrait + { + //! \copydoc BlackCore::TSettingTrait::key + static const char *key() { return "network/rawfsdmessagelog"; } + + //! \copydoc BlackCore::TSettingTrait::humanReadable + static const QString &humanReadable() { static const QString name("FSD message Logging"); return name; } + + //! \copydoc BlackCore::TSettingTrait::isValid + static bool isValid(const CRawFsdMessageSettings &setting) + { + if (setting.isFileWritingEnabled()) { return !setting.getFileDir().isEmpty(); } + return true; + } + + //! \copydoc BlackCore::TSettingTrait::defaultValue + static const CRawFsdMessageSettings &defaultValue() + { + static const CRawFsdMessageSettings setting { false, BlackMisc::CDirectoryUtils::logDirectory() }; + return setting; + } + }; } // ns } // ns Q_DECLARE_METATYPE(BlackCore::Vatsim::CReaderSettings) Q_DECLARE_METATYPE(BlackMisc::CCollection) Q_DECLARE_METATYPE(BlackMisc::CSequence) +Q_DECLARE_METATYPE(BlackCore::Vatsim::CRawFsdMessageSettings) +Q_DECLARE_METATYPE(BlackCore::Vatsim::CRawFsdMessageSettings::FileWriteMode) #endif diff --git a/src/blackmisc/propertyindex.h b/src/blackmisc/propertyindex.h index c1832b579..9353c7322 100644 --- a/src/blackmisc/propertyindex.h +++ b/src/blackmisc/propertyindex.h @@ -158,6 +158,7 @@ namespace BlackMisc GlobalIndexCGeneralGuiSettings = 14600, GlobalIndexCTextMessageSettings = 14700, GlobalIndexCAtcStationsSettings = 14800, + GlobalIndexRawFsdMessageSettings = 14900, GlobalIndexCInterpolatioRenderingSetup = 16000, GlobalIndexCInterpolationHints = 16100, GlobalIndexSwiftPilotClient = 17000,