From 19040af65406f7299d00f460b7bc03b5704b281c Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Fri, 8 Jul 2016 12:09:38 +0200 Subject: [PATCH] refs #704, moved VATSIM specific stuff to ns VATSIM/subdir --- samples/cliclient/client.cpp | 6 +- src/blackcore/application.cpp | 3 +- src/blackcore/audiodevicevatlib.cpp | 140 --- src/blackcore/audiodevicevatlib.h | 131 --- src/blackcore/audiomixervatlib.cpp | 34 - src/blackcore/audiomixervatlib.h | 71 -- src/blackcore/context/contextaudioimpl.cpp | 3 +- src/blackcore/context/contextnetworkimpl.cpp | 3 +- src/blackcore/networkvatlib.cpp | 1109 ----------------- src/blackcore/networkvatlib.h | 226 ---- src/blackcore/vatsim/audiodevicevatlib.cpp | 142 +++ src/blackcore/vatsim/audiodevicevatlib.h | 134 +++ src/blackcore/vatsim/audiomixervatlib.cpp | 37 + src/blackcore/vatsim/audiomixervatlib.h | 72 ++ src/blackcore/vatsim/networkvatlib.cpp | 1110 ++++++++++++++++++ src/blackcore/vatsim/networkvatlib.h | 228 ++++ src/blackcore/vatsim/voicechannelvatlib.cpp | 218 ++++ src/blackcore/vatsim/voicechannelvatlib.h | 111 ++ src/blackcore/vatsim/voicevatlib.cpp | 142 +++ src/blackcore/vatsim/voicevatlib.h | 124 ++ src/blackcore/voicechannelvatlib.cpp | 216 ---- src/blackcore/voicechannelvatlib.h | 108 -- src/blackcore/voicevatlib.cpp | 140 --- src/blackcore/voicevatlib.h | 123 -- tests/blackcore/testnetwork.h | 4 +- 25 files changed, 2329 insertions(+), 2306 deletions(-) delete mode 100644 src/blackcore/audiodevicevatlib.cpp delete mode 100644 src/blackcore/audiodevicevatlib.h delete mode 100644 src/blackcore/audiomixervatlib.cpp delete mode 100644 src/blackcore/audiomixervatlib.h delete mode 100644 src/blackcore/networkvatlib.cpp delete mode 100644 src/blackcore/networkvatlib.h create mode 100644 src/blackcore/vatsim/audiodevicevatlib.cpp create mode 100644 src/blackcore/vatsim/audiodevicevatlib.h create mode 100644 src/blackcore/vatsim/audiomixervatlib.cpp create mode 100644 src/blackcore/vatsim/audiomixervatlib.h create mode 100644 src/blackcore/vatsim/networkvatlib.cpp create mode 100644 src/blackcore/vatsim/networkvatlib.h create mode 100644 src/blackcore/vatsim/voicechannelvatlib.cpp create mode 100644 src/blackcore/vatsim/voicechannelvatlib.h create mode 100644 src/blackcore/vatsim/voicevatlib.cpp create mode 100644 src/blackcore/vatsim/voicevatlib.h delete mode 100644 src/blackcore/voicechannelvatlib.cpp delete mode 100644 src/blackcore/voicechannelvatlib.h delete mode 100644 src/blackcore/voicevatlib.cpp delete mode 100644 src/blackcore/voicevatlib.h diff --git a/samples/cliclient/client.cpp b/samples/cliclient/client.cpp index fc8aa0d40..a3226f9e4 100644 --- a/samples/cliclient/client.cpp +++ b/samples/cliclient/client.cpp @@ -11,7 +11,7 @@ //! \ingroup samplecliclient #include "client.h" -#include "blackcore/networkvatlib.h" +#include "blackcore/vatsim/networkvatlib.h" #include "blackmisc/aviation/aircrafticaocode.h" #include "blackmisc/aviation/aircraftsituation.h" #include "blackmisc/aviation/airlineicaocode.h" @@ -42,6 +42,7 @@ #include using namespace BlackCore; +using namespace BlackCore::Vatsim; using namespace BlackMisc; using namespace BlackMisc::Simulation; using namespace BlackMisc::Aviation; @@ -55,7 +56,7 @@ namespace BlackSample : QObject(parent), COwnAircraftAware(COwnAircraftProviderDummy::instance()), CIdentifiable("samples:cmdClient"), - m_net(new BlackCore::CNetworkVatlib(COwnAircraftProviderDummy::instance(), this)) + m_net(new CNetworkVatlib(COwnAircraftProviderDummy::instance(), this)) { connect(m_net, &INetwork::atcPositionUpdate, this, &Client::atcPositionUpdate); connect(m_net, &INetwork::atcDisconnected, this, &Client::atcDisconnected); @@ -578,5 +579,4 @@ namespace BlackSample } std::cout << std::endl; } - } // ns diff --git a/src/blackcore/application.cpp b/src/blackcore/application.cpp index c77ca1679..adb0a1451 100644 --- a/src/blackcore/application.cpp +++ b/src/blackcore/application.cpp @@ -12,7 +12,7 @@ #include "blackcore/context/contextapplication.h" #include "blackcore/cookiemanager.h" #include "blackcore/corefacade.h" -#include "blackcore/networkvatlib.h" +#include "blackcore/vatsim/networkvatlib.h" #include "blackcore/registermetadata.h" #include "blackcore/setupreader.h" #include "blackcore/webdataservices.h" @@ -62,6 +62,7 @@ using namespace BlackMisc::Simulation; using namespace BlackMisc::Weather; using namespace BlackCore; using namespace BlackCore::Context; +using namespace BlackCore::Vatsim; using namespace BlackCore::Data; using namespace BlackCore::Db; diff --git a/src/blackcore/audiodevicevatlib.cpp b/src/blackcore/audiodevicevatlib.cpp deleted file mode 100644 index e772e1abc..000000000 --- a/src/blackcore/audiodevicevatlib.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* Copyright (C) 2014 - * swift project community / contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \cond PRIVATE - -#include "blackcore/audiodevicevatlib.h" -#include "blackmisc/logmessage.h" - -#include -#include - -using namespace BlackMisc; -using namespace BlackMisc::Audio; -using namespace BlackMisc::Aviation; - -namespace BlackCore -{ - //! Cast void* to a pointer of CAudioInputDeviceVatlib - CAudioInputDeviceVatlib *cbvar_cast_inputDevice(void *cbvar) - { - return static_cast(cbvar); - } - - //! Cast void* to a pointer of CAudioOutputDeviceVatlib - CAudioOutputDeviceVatlib *cbvar_cast_outputDevice(void *cbvar) - { - return static_cast(cbvar); - } - - CAudioInputDeviceVatlib::CAudioInputDeviceVatlib(VatAudioService audioService, QObject *parent) - : IAudioInputDevice(parent), - m_audioService(audioService) - { - m_inputCodec.reset(Vat_CreateLocalInputCodec(audioService, vatCodecLegacy)); - Vat_GetInputDeviceInfo(m_inputCodec.data(), onInputHardwareDeviceReceived, this, nullptr); - m_currentDevice = getDefaultInputDevice(); - } - - const BlackMisc::Audio::CAudioDeviceInfoList &CAudioInputDeviceVatlib::getInputDevices() const - { - return m_devices; - } - - const BlackMisc::Audio::CAudioDeviceInfo &CAudioInputDeviceVatlib::getDefaultInputDevice() const - { - static BlackMisc::Audio::CAudioDeviceInfo info(BlackMisc::Audio::CAudioDeviceInfo::InputDevice, BlackMisc::Audio::CAudioDeviceInfo::defaultDeviceIndex(), "default"); - return info; - } - - const BlackMisc::Audio::CAudioDeviceInfo &CAudioInputDeviceVatlib::getCurrentInputDevice() const - { - return m_currentDevice; - } - - void CAudioInputDeviceVatlib::setInputDevice(const BlackMisc::Audio::CAudioDeviceInfo &device) - { - Q_ASSERT_X(m_inputCodec, "CAudioInputDeviceVatlib", "VatLocalCodec is invalid!"); - if (!device.isValid()) - { - CLogMessage(this).warning("Cannot set invalid input device!"); - return; - } - - if (!Vat_SetAudioInputDevice(m_inputCodec.data(), device.getIndex())) - { - CLogMessage(this).warning("Setting input device failed"); - } - m_currentDevice = device; - } - - void CAudioInputDeviceVatlib::onInputHardwareDeviceReceived(int deviceIndex, const char *hardwareName, void *cbVar) - { - BlackMisc::Audio::CAudioDeviceInfo inputDevice(BlackMisc::Audio::CAudioDeviceInfo::InputDevice, deviceIndex, QString(hardwareName)); - cbvar_cast_inputDevice(cbVar)->m_devices.push_back(inputDevice); - } - - CAudioOutputDeviceVatlib::CAudioOutputDeviceVatlib(VatAudioService audioService, QObject *parent) - : IAudioOutputDevice(parent), - m_audioService(audioService) - { - m_outputCodec.reset(Vat_CreateLocalOutputCodec(m_audioService, vatCodecLegacy)); - Vat_GetOutputDeviceInfo(m_outputCodec.data(), onOutputHardwareDeviceReceived, this, nullptr); - m_currentDevice = getDefaultOutputDevice(); - } - - const BlackMisc::Audio::CAudioDeviceInfoList &CAudioOutputDeviceVatlib::getOutputDevices() const - { - return m_devices; - } - - const BlackMisc::Audio::CAudioDeviceInfo &CAudioOutputDeviceVatlib::getDefaultOutputDevice() const - { - static BlackMisc::Audio::CAudioDeviceInfo info(BlackMisc::Audio::CAudioDeviceInfo::OutputDevice, BlackMisc::Audio::CAudioDeviceInfo::defaultDeviceIndex(), "default"); - return info; - } - - const BlackMisc::Audio::CAudioDeviceInfo &CAudioOutputDeviceVatlib::getCurrentOutputDevice() const - { - return m_currentDevice; - } - - void CAudioOutputDeviceVatlib::setOutputDevice(const BlackMisc::Audio::CAudioDeviceInfo &device) - { - Q_ASSERT_X(m_outputCodec, "CAudioOutputDeviceVatlib", "VatLocalCodec is invalid!"); - if (!device.isValid()) - { - CLogMessage(this).warning("Cannot set invalid output device!"); - return; - } - - Vat_SetAudioOutputDevice(m_outputCodec.data(), device.getIndex()); - this->m_currentDevice = device; - } - - void CAudioOutputDeviceVatlib::setOutputVolume(int volume) - { - Q_ASSERT(m_outputCodec); - Vat_SetOutputVolume(m_outputCodec.data(), volume); - } - - int CAudioOutputDeviceVatlib::getOutputVolume() const - { - Q_ASSERT(m_outputCodec); - return Vat_GetOutputVolume(m_outputCodec.data()); - } - - void CAudioOutputDeviceVatlib::onOutputHardwareDeviceReceived(int deviceIndex, const char *hardwareName, void *cbVar) - { - BlackMisc::Audio::CAudioDeviceInfo outputDevice(BlackMisc::Audio::CAudioDeviceInfo::OutputDevice, deviceIndex, QString(hardwareName)); - cbvar_cast_outputDevice(cbVar)->m_devices.push_back(outputDevice); - } -} - -//! \endcond diff --git a/src/blackcore/audiodevicevatlib.h b/src/blackcore/audiodevicevatlib.h deleted file mode 100644 index 671d0438f..000000000 --- a/src/blackcore/audiodevicevatlib.h +++ /dev/null @@ -1,131 +0,0 @@ -/* Copyright (C) 2014 - * swift project community / contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \file - -#ifndef BLACKCORE_AUDIODEVICEVATLIB_H -#define BLACKCORE_AUDIODEVICEVATLIB_H - -#include "blackcore/audiodevice.h" -#include "blackcore/blackcoreexport.h" -#include "blackmisc/audio/audiodeviceinfo.h" -#include "blackmisc/audio/audiodeviceinfolist.h" -#include "vatlib/vatlib.h" - -#include -#include - -struct VatConsumer_tag; -struct VatProducer_tag; - -namespace BlackCore -{ - //! Audio Input Device - class BLACKCORE_EXPORT CAudioInputDeviceVatlib : public IAudioInputDevice - { - Q_OBJECT - - public: - - //! Constructor - CAudioInputDeviceVatlib(VatAudioService audioService, QObject *parent = nullptr); - - //! Destructor - virtual ~CAudioInputDeviceVatlib() = default; - - //! \copydoc IAudioInputDevice::getInputDevices - virtual const BlackMisc::Audio::CAudioDeviceInfoList &getInputDevices() const override; - - //! \copydoc IAudioInputDevice::getDefaultInputDevice - virtual const BlackMisc::Audio::CAudioDeviceInfo &getDefaultInputDevice() const override; - - //! \copydoc IAudioInputDevice::getCurrentInputDevice - virtual const BlackMisc::Audio::CAudioDeviceInfo &getCurrentInputDevice() const override; - - //! \copydoc IAudioInputDevice::setInputDevice - virtual void setInputDevice(const BlackMisc::Audio::CAudioDeviceInfo &device) override; - - //! Get vatlib input device pointer - VatLocalInputCodec getVatLocalInputCodec() { return m_inputCodec.data(); } - - private: - - struct VatLocalInputCodecDeleter - { - static inline void cleanup(VatProducer_tag *obj) - { - if (obj) Vat_DestroyLocalInputCodec(obj); - } - }; - - static void onInputHardwareDeviceReceived(int deviceIndex, const char *hardwareName, void *cbVar); - - BlackMisc::Audio::CAudioDeviceInfoList m_devices; /*!< in and output devices */ - BlackMisc::Audio::CAudioDeviceInfo m_currentDevice; - - VatAudioService m_audioService; - QScopedPointer m_inputCodec; - }; - - //! Audio Output Device - class CAudioOutputDeviceVatlib : public IAudioOutputDevice - { - Q_OBJECT - - public: - - //! Constructor - CAudioOutputDeviceVatlib(VatAudioService audioService, QObject *parent = nullptr); - - //! Destructor - virtual ~CAudioOutputDeviceVatlib() = default; - - //! \copydoc IAudioOutputDevice::getOutputDevices - virtual const BlackMisc::Audio::CAudioDeviceInfoList &getOutputDevices() const override; - - //! \copydoc IAudioOutputDevice::getDefaultOutputDevice - virtual const BlackMisc::Audio::CAudioDeviceInfo &getDefaultOutputDevice() const override; - - //! \copydoc IAudioOutputDevice::getCurrentOutputDevice - virtual const BlackMisc::Audio::CAudioDeviceInfo &getCurrentOutputDevice() const override; - - //! \copydoc IAudioOutputDevice::setOutputDevice - virtual void setOutputDevice(const BlackMisc::Audio::CAudioDeviceInfo &device) override; - - //! \copydoc IAudioOutputDevice::setOutputVolume - virtual void setOutputVolume(int volume) override; - - //! \copydoc IAudioOutputDevice::getOutputVolume - virtual int getOutputVolume() const override; - - //! Get vatlib output device pointer - VatLocalOutputCodec getVatLocalOutputCodec() { return m_outputCodec.data(); } - - private: - - struct VatLocalOutputCodecDeleter - { - static inline void cleanup(VatConsumer_tag *obj) - { - if (obj) Vat_DestroyLocalOutputCodec(obj); - } - }; - - static void onOutputHardwareDeviceReceived(int deviceIndex, const char *hardwareName, void *cbVar); - - - BlackMisc::Audio::CAudioDeviceInfoList m_devices; /*!< in and output devices */ - BlackMisc::Audio::CAudioDeviceInfo m_currentDevice; - - VatAudioService m_audioService; - QScopedPointer m_outputCodec; - }; -} - -#endif // guard diff --git a/src/blackcore/audiomixervatlib.cpp b/src/blackcore/audiomixervatlib.cpp deleted file mode 100644 index 002ebfd2b..000000000 --- a/src/blackcore/audiomixervatlib.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* Copyright (C) 2014 - * swift project community / contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -#include "blackcore/audiomixervatlib.h" - -namespace BlackCore -{ - CAudioMixerVatlib::CAudioMixerVatlib(QObject *parent) - : IAudioMixer(parent) - { - m_audioMixer.reset(Vat_CreateAudioMixer()); - } - - void CAudioMixerVatlib::makeMixerConnection(InputPort inputPort, OutputPort outputPort) - { - Vat_MakeMixerConnection(m_audioMixer.data(), inputPort, outputPort, true); - } - - void CAudioMixerVatlib::removeMixerConnection(InputPort inputPort, OutputPort outputPort) - { - Vat_MakeMixerConnection(m_audioMixer.data(), inputPort, outputPort, false); - } - - bool CAudioMixerVatlib::hasMixerConnection(InputPort inputPort, OutputPort outputPort) - { - return Vat_HasMixerConnection(m_audioMixer.data(), inputPort, outputPort); - } -} diff --git a/src/blackcore/audiomixervatlib.h b/src/blackcore/audiomixervatlib.h deleted file mode 100644 index 6473a05bc..000000000 --- a/src/blackcore/audiomixervatlib.h +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright (C) 2014 - * swift project community / contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \file - -#ifndef BLACKCORE_AUDIOMIXERVATLIB_H -#define BLACKCORE_AUDIOMIXERVATLIB_H - -#include "blackcore/audiomixer.h" -#include "blackcore/blackcoreexport.h" -#include "vatlib/vatlib.h" - -#include -#include -#include - -struct VatProducerConsumer_tag; - -namespace BlackCore -{ - //! Interface to an audio mixer - class BLACKCORE_EXPORT CAudioMixerVatlib : public IAudioMixer - { - Q_OBJECT - - public: - - /*! - * \brief Default constructor with parent - * \param parent - */ - CAudioMixerVatlib(QObject *parent = nullptr); - - //! Virtual destructor. - virtual ~CAudioMixerVatlib() {} - - //! \copydoc IAudioMixer::makeMixerConnection - virtual void makeMixerConnection(InputPort inputPort, OutputPort outputPort) override; - - //! \copydoc IAudioMixer::removeMixerConnection - virtual void removeMixerConnection(InputPort inputPort, OutputPort outputPort) override; - - //! \copydoc IAudioMixer::hasMixerConnection - virtual bool hasMixerConnection(InputPort inputPort, OutputPort outputPort) override; - - //! Return the pointer to vatlib audio mixer - VatAudioMixer getVatAudioMixer() { return m_audioMixer.data(); } - - private: - - struct VatAudioMixerDeleter - { - static inline void cleanup(VatProducerConsumer_tag *obj) - { - if (obj) Vat_DestroyAudioMixer(obj); - } - }; - - QScopedPointer m_audioMixer; - - }; - -} // namespace BlackCore - -#endif // guard diff --git a/src/blackcore/context/contextaudioimpl.cpp b/src/blackcore/context/contextaudioimpl.cpp index 8dd9d509d..e7be20fc8 100644 --- a/src/blackcore/context/contextaudioimpl.cpp +++ b/src/blackcore/context/contextaudioimpl.cpp @@ -14,7 +14,7 @@ #include "blackcore/context/contextownaircraft.h" #include "blackcore/corefacade.h" #include "blackcore/voice.h" -#include "blackcore/voicevatlib.h" +#include "blackcore/vatsim/voicevatlib.h" #include "blackmisc/audio/audiodeviceinfo.h" #include "blackmisc/audio/notificationsounds.h" #include "blackmisc/audio/settings/settingsaudio.h" @@ -41,6 +41,7 @@ using namespace BlackMisc::Audio; using namespace BlackMisc::Input; using namespace BlackMisc::Audio; using namespace BlackSound; +using namespace BlackCore::Vatsim; namespace BlackCore { diff --git a/src/blackcore/context/contextnetworkimpl.cpp b/src/blackcore/context/contextnetworkimpl.cpp index 9346f687b..14bc863aa 100644 --- a/src/blackcore/context/contextnetworkimpl.cpp +++ b/src/blackcore/context/contextnetworkimpl.cpp @@ -15,7 +15,7 @@ #include "blackcore/context/contextownaircraftimpl.h" #include "blackcore/context/contextsimulator.h" #include "blackcore/corefacade.h" -#include "blackcore/networkvatlib.h" +#include "blackcore/vatsim/networkvatlib.h" #include "blackcore/webdataservices.h" #include "blackmisc/aviation/aircrafticaocode.h" #include "blackmisc/aviation/aircraftparts.h" @@ -48,6 +48,7 @@ using namespace BlackMisc::Geo; using namespace BlackMisc::Audio; using namespace BlackMisc::Simulation; using namespace BlackMisc::Weather; +using namespace BlackCore::Vatsim; namespace BlackCore { diff --git a/src/blackcore/networkvatlib.cpp b/src/blackcore/networkvatlib.cpp deleted file mode 100644 index 3a80439b8..000000000 --- a/src/blackcore/networkvatlib.cpp +++ /dev/null @@ -1,1109 +0,0 @@ -/* Copyright (C) 2013 - * swift Project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \cond PRIVATE - -#include "blackconfig/buildconfig.h" -#include "blackcore/application.h" -#include "blackcore/networkvatlib.h" -#include "blackmisc/aviation/aircraftsituation.h" -#include "blackmisc/aviation/altitude.h" -#include "blackmisc/aviation/comsystem.h" -#include "blackmisc/aviation/flightplan.h" -#include "blackmisc/aviation/heading.h" -#include "blackmisc/aviation/informationmessage.h" -#include "blackmisc/aviation/livery.h" -#include "blackmisc/aviation/transponder.h" -#include "blackmisc/compare.h" -#include "blackmisc/geo/coordinategeodetic.h" -#include "blackmisc/geo/latitude.h" -#include "blackmisc/geo/longitude.h" -#include "blackmisc/json.h" -#include "blackmisc/logmessage.h" -#include "blackmisc/network/network.h" -#include "blackmisc/network/textmessage.h" -#include "blackmisc/network/user.h" -#include "blackmisc/pq/angle.h" -#include "blackmisc/pq/frequency.h" -#include "blackmisc/pq/length.h" -#include "blackmisc/pq/speed.h" -#include "blackmisc/pq/time.h" -#include "blackmisc/pq/units.h" -#include "blackmisc/simulation/aircraftmodel.h" -#include "blackmisc/simulation/simulatedaircraft.h" -#include "blackmisc/statusmessage.h" -#include "vatlib/vatlib.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static_assert(! std::is_abstract::value, "Must implement all pure virtuals"); -static_assert(VAT_LIBVATLIB_VERSION == 905, "Wrong vatlib header installed"); - -using namespace BlackConfig; -using namespace BlackMisc; -using namespace BlackMisc::Aviation; -using namespace BlackMisc::Geo; -using namespace BlackMisc::Json; -using namespace BlackMisc::Network; -using namespace BlackMisc::PhysicalQuantities; -using namespace BlackMisc::Simulation; - -namespace BlackCore -{ - CNetworkVatlib::CNetworkVatlib(IOwnAircraftProvider *ownAircraft, QObject *parent) - : INetwork(parent), COwnAircraftAware(ownAircraft), - m_loginMode(LoginNormal), - m_status(vatStatusIdle), - m_tokenBucket(10, CTime(5, CTimeUnit::s()), 1) - { - connect(this, &CNetworkVatlib::terminate, this, &INetwork::terminateConnection, Qt::QueuedConnection); - connect(this, &INetwork::customPacketReceived, this, &CNetworkVatlib::customPacketDispatcher); - - Q_ASSERT_X(Vat_GetVersion() == VAT_LIBVATLIB_VERSION, "swift.network", "Wrong vatlib shared library installed"); - - Vat_SetNetworkLogHandler(SeverityLevel::SeverityError, CNetworkVatlib::networkLogHandler); - - m_processingTimer.setObjectName(this->objectName().append(":m_processingTimer")); - m_positionUpdateTimer.setObjectName(this->objectName().append(":m_positionUpdateTimer")); - m_interimPositionUpdateTimer.setObjectName(this->objectName().append(":m_interimPositionUpdateTimer")); - - connect(&m_processingTimer, &QTimer::timeout, this, &CNetworkVatlib::process); - connect(&m_positionUpdateTimer, &QTimer::timeout, this, &CNetworkVatlib::sendPositionUpdate); - connect(&m_interimPositionUpdateTimer, &QTimer::timeout, this, &CNetworkVatlib::sendInterimPositions); - connect(&this->m_scheduledConfigUpdate, &QTimer::timeout, this, &CNetworkVatlib::sendIncrementalAircraftConfig); - m_scheduledConfigUpdate.setSingleShot(true); - - m_processingTimer.start(c_processingIntervalMsec); - } - - void CNetworkVatlib::initializeSession() - { - Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "attempted to reinitialize session while still connected"); - - int clientCapabilities = vatCapsModelDesc | vatCapsInterminPos | vatCapsAtcInfo | vatCapsAircraftConfig; - if (m_loginMode == LoginStealth) - { - clientCapabilities |= vatCapsStealth; - } - - static const QByteArray pk(this->getCmdLineFsdKey().isEmpty() ? CBuildConfig::vatsimPrivateKey().toLocal8Bit() : this->getCmdLineFsdKey().toLocal8Bit()); - m_net.reset(Vat_CreateNetworkSession(vatServerLegacyFsd, sApp->swiftVersionChar(), - CVersion::versionMajor(), CVersion::versionMinor(), - "None", CBuildConfig::vatsimClientId(), pk.constData(), - clientCapabilities)); - - Vat_SetStateChangeHandler(m_net.data(), onConnectionStatusChanged, this); - Vat_SetTextMessageHandler(m_net.data(), onTextMessageReceived, this); - Vat_SetRadioMessageHandler(m_net.data(), onRadioMessageReceived, this); - Vat_SetDeletePilotHandler(m_net.data(), onPilotDisconnected, this); - Vat_SetDeleteAtcHandler(m_net.data(), onControllerDisconnected, this); - Vat_SetPilotPositionHandler(m_net.data(), onPilotPositionUpdate, this); - Vat_SetInterimPilotPositionHandler(m_net.data(), onInterimPilotPositionUpdate, this); - Vat_SetAtcPositionHandler(m_net.data(), onAtcPositionUpdate, this); - Vat_SetKillHandler(m_net.data(), onKicked, this); - Vat_SetPongHandler(m_net.data(), onPong, this); - Vat_SetMetarResponseHandler(m_net.data(), onMetarReceived, this); - Vat_SetClientQueryHandler(m_net.data(), onInfoQueryRequestReceived, this); - Vat_SetClientQueryResponseHandler(m_net.data(), onInfoQueryReplyReceived, this); - Vat_SetInfoCAPSReplyHandler(m_net.data(), onCapabilitiesReplyReceived, this); - Vat_SetControllerAtisHandler(m_net.data(), onAtisReplyReceived, this); - Vat_SetFlightPlanHandler(m_net.data(), onFlightPlanReceived, this); - Vat_SetServerErrorHandler(m_net.data(), onErrorReceived, this); - Vat_SetAircraftInfoRequestHandler(m_net.data(), onPilotInfoRequestReceived, this); - Vat_SetAircraftInfoHandler(m_net.data(), onPilotInfoReceived, this); - Vat_SetCustomPilotPacketHandler(m_net.data(), onCustomPacketReceived, this); - Vat_SetAircraftConfigHandler(m_net.data(), onAircraftConfigReceived, this); - } - - CNetworkVatlib::~CNetworkVatlib() - { - Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "CNetworkVatlib destroyed while still connected."); - } - - void CNetworkVatlib::process() - { - if (!m_net) { return; } - sendIncrementalAircraftConfig(); - Vat_ExecuteNetworkTasks(m_net.data()); - } - - void CNetworkVatlib::sendPositionUpdate() - { - if (!m_net) { return; } - - if (isConnected()) - { - CSimulatedAircraft myAircraft(getOwnAircraft()); - if (this->m_loginMode == LoginAsObserver) - { - // Observer - VatAtcPosition pos; - pos.facility = vatFacilityTypeUnknown; - pos.visibleRange = 10; // NM - pos.latitude = myAircraft.latitude().value(CAngleUnit::deg()); - pos.longitude = myAircraft.longitude().value(CAngleUnit::deg()); - pos.elevation = 0; - pos.rating = vatAtcRatingObserver; - pos.frequency = 199998; - Vat_SendATCUpdate(m_net.data(), &pos); - } - else - { - // Normal / Stealth mode - VatPilotPosition pos; - // TODO: we need to distinguish true and pressure altitude - pos.altitudePressure = myAircraft.getAltitude().value(CLengthUnit::ft()); - pos.altitudeTrue = myAircraft.getAltitude().value(CLengthUnit::ft()); - pos.heading = myAircraft.getHeading().value(CAngleUnit::deg()); - pos.pitch = myAircraft.getPitch().value(CAngleUnit::deg()); - pos.bank = myAircraft.getBank().value(CAngleUnit::deg()); - pos.latitude = myAircraft.latitude().value(CAngleUnit::deg()); - pos.longitude = myAircraft.longitude().value(CAngleUnit::deg()); - pos.groundSpeed = myAircraft.getGroundSpeed().value(CSpeedUnit::kts()); - pos.rating = vatPilotRatingUnknown; - pos.transponderCode = static_cast(myAircraft.getTransponderCode()); - pos.transponderMode = vatTransponderModeStandby; - switch (myAircraft.getTransponderMode()) - { - case CTransponder::ModeC: pos.transponderMode = vatTransponderModeCharlie; break; - case CTransponder::StateIdent: pos.transponderMode = vatTransponderModeIdent; break; - default: pos.transponderMode = vatTransponderModeStandby; break; - } - Vat_SendPilotUpdate(m_net.data(), &pos); - } - } - } - - void CNetworkVatlib::sendInterimPositions() - { - if (!m_net) { return; } - - if (isConnected()) - { - CSimulatedAircraft myAircraft(getOwnAircraft()); - if (this->m_loginMode == LoginNormal) - { - VatInterimPilotPosition pos; - pos.altitudeTrue = myAircraft.getAltitude().value(CLengthUnit::ft()); - pos.heading = myAircraft.getHeading().value(CAngleUnit::deg()); - pos.pitch = myAircraft.getPitch().value(CAngleUnit::deg()); - pos.bank = myAircraft.getBank().value(CAngleUnit::deg()); - pos.latitude = myAircraft.latitude().value(CAngleUnit::deg()); - pos.longitude = myAircraft.longitude().value(CAngleUnit::deg()); - - for (const auto &receiver : as_const(m_interimPositionReceivers)) - { - Vat_SendInterimPilotUpdate(m_net.data(), toFSD(receiver), &pos); - } - } - } - } - - //! Convert vatlib status code to INetwork::ConnectionStatus - INetwork::ConnectionStatus convertConnectionStatus(VatConnectionStatus status) - { - switch (status) - { - case vatStatusIdle: return INetwork::Disconnected; - case vatStatusConnecting: return INetwork::Connecting; - case vatStatusConnected: return INetwork::Connected; - case vatStatusDisconnected: return INetwork::Disconnected; - case vatStatusDisconnecting: return INetwork::Disconnecting; - case vatStatusError: return INetwork::DisconnectedError; - } - qFatal("unrecognised connection status"); - return INetwork::DisconnectedError; - } - - void CNetworkVatlib::changeConnectionStatus(VatConnectionStatus status) - { - if (m_status != status) - { - qSwap(m_status, status); - emit connectionStatusChanged(convertConnectionStatus(status), convertConnectionStatus(m_status)); - - if (isDisconnected()) - { - stopPositionTimers(); - } - } - } - - QByteArray CNetworkVatlib::toFSD(QString qstr) const - { - Q_ASSERT_X(m_fsdTextCodec, Q_FUNC_INFO, "Missing codec"); - return m_fsdTextCodec->fromUnicode(qstr); - } - - QByteArray CNetworkVatlib::toFSD(const BlackMisc::Aviation::CCallsign &callsign) const - { - return toFSD(callsign.getStringAsSet()); - } - - std::function CNetworkVatlib::toFSD(QStringList qstrList) const - { - QVector bytesVec; - for (auto i = qstrList.cbegin(); i != qstrList.cend(); ++i) - { - bytesVec.push_back(toFSD(*i)); - } - - return [ cstrVec = QVector(), bytesVec = std::move(bytesVec) ]() mutable - { - Q_ASSERT(cstrVec.isEmpty()); - for (auto i = bytesVec.cbegin(); i != bytesVec.cend(); ++i) - { - cstrVec.push_back(i->constData()); - } - return const_cast(cstrVec.constData()); - }; - } - - QString CNetworkVatlib::fromFSD(const char *cstr) const - { - Q_ASSERT_X(m_fsdTextCodec, Q_FUNC_INFO, "Missing codec"); - return m_fsdTextCodec->toUnicode(cstr); - } - - QStringList CNetworkVatlib::fromFSD(const char **cstrArray, int size) const - { - QStringList qstrList; - for (int i = 0; i < size; ++i) - { - qstrList.push_back(fromFSD(cstrArray[i])); - } - return qstrList; - } - - bool CNetworkVatlib::isInterimPositionUpdateEnabledForServer() const - { - const CFsdSetup::SendReceiveDetails d = m_server.getFsdSetup().getSendReceiveDetails(); - return (d & CFsdSetup::SendIterimPositions) || (d & CFsdSetup::ReceiveInterimPositions); - } - - void CNetworkVatlib::startPositionTimers() - { - m_positionUpdateTimer.start(c_updatePostionIntervalMsec); - if (isInterimPositionUpdateEnabledForServer()) { m_interimPositionUpdateTimer.start(c_updateInterimPostionIntervalMsec); } - } - - void CNetworkVatlib::stopPositionTimers() - { - m_positionUpdateTimer.stop(); - m_interimPositionUpdateTimer.stop(); - } - - QString CNetworkVatlib::convertToUnicodeEscaped(const QString &str) - { - QString escaped; - for (const auto &ch : str) - { - ushort code = ch.unicode(); - if (code < 0x80) - { - escaped += ch; - } - else - { - escaped += "\\u"; - escaped += QString::number(code, 16).rightJustified(4, '0'); - } - } - return escaped; - } - - VatSimType CNetworkVatlib::convertToSimType(CSimulatorPluginInfo &simInfo) - { - /* TODO Define recognized simulators somewhere */ - if (simInfo.getSimulator() == "fs9" || simInfo.getSimulator() == "fsx") - { - return vatSimTypeMSCFS; - } - else if (simInfo.getSimulator() == "xplane") - { - return vatSimTypeXPLANE; - } - else - { - return vatSimTypeUnknown; - } - } - - /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ - /********************************** INetwork functions ************************************/ - /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ - - void CNetworkVatlib::presetServer(const CServer &server) - { - Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "Can't change server details while still connected"); - m_server = server; - const QString codecName(server.getFsdSetup().getTextCodec()); - Q_ASSERT_X(!codecName.isEmpty(), Q_FUNC_INFO, "Missing code name"); - this->m_fsdTextCodec = QTextCodec::codecForName(codecName.toLocal8Bit()); - if (!this->m_fsdTextCodec) { this->m_fsdTextCodec = QTextCodec::codecForName("latin1"); } - } - - void CNetworkVatlib::presetSimulatorInfo(const CSimulatorPluginInfo &simInfo) - { - Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "Can't change server details while still connected"); - m_simulatorInfo = simInfo; - } - - void CNetworkVatlib::presetCallsign(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "Can't change callsign while still connected"); - m_ownCallsign = callsign; - updateOwnCallsign(callsign); - } - - void CNetworkVatlib::presetIcaoCodes(const BlackMisc::Simulation::CSimulatedAircraft &ownAircraft) - { - Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "Can't change ICAO codes while still connected"); - m_ownAircraftIcaoCode = ownAircraft.getAircraftIcaoCode(); - m_ownAirlineIcaoCode = ownAircraft.getAirlineIcaoCode(); - m_ownLiveryDescription = ownAircraft.getLivery().getDescription(); - updateOwnIcaoCodes(m_ownAircraftIcaoCode, m_ownAirlineIcaoCode); // \todo livery? - } - - void CNetworkVatlib::presetLoginMode(LoginMode mode) - { - Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "Can't change login mode while still connected"); - m_loginMode = mode; - m_net.reset(nullptr); - } - - void CNetworkVatlib::initiateConnection() - { - Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "Can't connect while still connected"); - if (!m_net) { initializeSession(); } - QByteArray callsign = toFSD(m_loginMode == LoginAsObserver ? - m_ownCallsign.getAsObserverCallsignString() : - m_ownCallsign.asString()); - QByteArray name = toFSD(m_server.getUser().getRealName()); - - if (this->m_loginMode == LoginAsObserver) - { - // Observer mode - VatAtcConnection info; - info.name = name.data(); - info.rating = vatAtcRatingObserver; - info.callsign = callsign.data(); - Vat_SpecifyATCLogon(m_net.data(), toFSD(m_server.getAddress()), m_server.getPort(), - toFSD(m_server.getUser().getId()), - toFSD(m_server.getUser().getPassword()), - &info); - } - else - { - // normal scenario, also used in STEALTH - VatPilotConnection info; - info.callsign = callsign.data(); - info.name = name.data(); - info.rating = vatPilotRatingStudent; //TODO - info.simType = convertToSimType(m_simulatorInfo); - Vat_SpecifyPilotLogon(m_net.data(), toFSD(m_server.getAddress()), m_server.getPort(), - toFSD(m_server.getUser().getId()), - toFSD(m_server.getUser().getPassword()), - &info); - } - - Vat_Logon(m_net.data()); - - startPositionTimers(); - } - - void CNetworkVatlib::terminateConnection() - { - stopPositionTimers(); - if (m_net && isConnected()) - { - Vat_Logoff(m_net.data()); - } - } - - void CNetworkVatlib::sendTextMessages(const BlackMisc::Network::CTextMessageList &messages) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - - if (messages.isEmpty()) { return; } - CTextMessageList privateMessages = messages.getPrivateMessages(); - privateMessages.markAsSent(); - for (const auto &message : privateMessages) - { - if (message.getRecipientCallsign().isEmpty()) { continue; } - Vat_SendTextMessage(m_net.data(), toFSD(message.getRecipientCallsign()), toFSD(message.getMessage())); - emit textMessageSent(message); - } - - CTextMessageList radioMessages = messages.getRadioMessages(); - radioMessages.markAsSent(); - for (const auto &message : radioMessages) - { - // I could send the same message to n frequencies in one step - // if this is really required, I need to group by message - // currently I send individual messages - QVector freqsVec; - freqsVec.push_back(message.getFrequency().valueRounded(CFrequencyUnit::kHz(), 0)); - Vat_SendRadioMessage(m_net.data(), freqsVec.data(), freqsVec.size(), toFSD(message.getMessage())); - emit textMessageSent(message); - } - } - - void CNetworkVatlib::sendCustomPacket(const BlackMisc::Aviation::CCallsign &callsign, const QString &packetId, const QStringList &data) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_SendCustomPilotPacket(m_net.data(), toFSD(callsign), toFSD(packetId), toFSD(data)(), data.size()); - } - - void CNetworkVatlib::sendIpQuery() - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_SendClientQuery(m_net.data(), vatClientQueryIP, nullptr); - } - - void CNetworkVatlib::sendFrequencyQuery(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_SendClientQuery(m_net.data(), vatClientQueryFreq, toFSD(callsign)); - } - - void CNetworkVatlib::sendUserInfoQuery(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_SendClientQuery(m_net.data(), vatClientQueryInfo, toFSD(callsign)); - } - - void CNetworkVatlib::setInterimPositionReceivers(const CCallsignSet &receivers) - { - m_interimPositionReceivers = receivers; - } - - void CNetworkVatlib::sendServerQuery(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_SendClientQuery(m_net.data(), vatClientQueryServer, toFSD(callsign)); - } - - void CNetworkVatlib::sendAtcQuery(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_SendClientQuery(m_net.data(), vatClientQueryAtc, toFSD(callsign)); - } - - void CNetworkVatlib::sendAtisQuery(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_SendClientQuery(m_net.data(), vatClientQueryAtis, toFSD(callsign)); - } - - void CNetworkVatlib::sendFlightPlan(const CFlightPlan &flightPlan) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - - VatFlightPlan vatlibFP; - QString route = QString(flightPlan.getRoute()).replace(" ", "."); - QString remarks = QString(flightPlan.getRemarks()).replace(":", ";").trimmed(); - QString alt = flightPlan.getCruiseAltitude().isFlightLevel() ? - flightPlan.getCruiseAltitude().toQString() : - flightPlan.getCruiseAltitude().valueRoundedWithUnit(0); - alt = alt.remove('.').remove(','); // remove any separators - - QByteArray acTypeTemp, altAptTemp, cruiseAltTemp, depAptTemp, destAptTemp, routeTemp, remarksTemp; - vatlibFP.aircraftType = acTypeTemp = toFSD(flightPlan.getEquipmentIcao()); - vatlibFP.alternateAirport = altAptTemp = toFSD(flightPlan.getAlternateAirportIcao().asString()); - vatlibFP.cruiseAltitude = cruiseAltTemp = toFSD(alt); - vatlibFP.departAirport = depAptTemp = toFSD(flightPlan.getOriginAirportIcao().asString()); - vatlibFP.departTimeActual = flightPlan.getTakeoffTimeActual().toUTC().toString("hhmm").toInt(); - vatlibFP.departTime = flightPlan.getTakeoffTimePlanned().toUTC().toString("hhmm").toInt(); - vatlibFP.destAirport = destAptTemp = toFSD(flightPlan.getDestinationAirportIcao().asString()); - - QList timeParts = flightPlan.getEnrouteTime().getHrsMinSecParts(); - vatlibFP.enrouteHrs = timeParts[CTime::Hours]; - vatlibFP.enrouteMins = timeParts[CTime::Minutes]; - - timeParts = flightPlan.getFuelTime().getHrsMinSecParts(); - vatlibFP.fuelHrs = timeParts[CTime::Hours]; - vatlibFP.fuelMins = timeParts[CTime::Minutes]; - vatlibFP.remarks = remarksTemp = toFSD(remarks); - vatlibFP.route = routeTemp = toFSD(route); - vatlibFP.trueCruisingSpeed = flightPlan.getCruiseTrueAirspeed().valueRounded(CSpeedUnit::kts()); - switch (flightPlan.getFlightRules()) - { - default: - case CFlightPlan::IFR: vatlibFP.flightType = vatFlightTypeIFR; break; - case CFlightPlan::VFR: vatlibFP.flightType = vatFlightTypeVFR; break; - case CFlightPlan::SVFR: vatlibFP.flightType = vatFlightTypeSVFR; break; - case CFlightPlan::DVFR: vatlibFP.flightType = vatFlightTypeDVFR; break; - } - Vat_SendFlightPlan(m_net.data(), &vatlibFP); - } - - void CNetworkVatlib::sendFlightPlanQuery(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_SendClientQuery(m_net.data(), vatClientQueryFP, toFSD(callsign)); - } - - void CNetworkVatlib::sendRealNameQuery(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_SendClientQuery(m_net.data(), vatClientQueryName, toFSD(callsign)); - } - - void CNetworkVatlib::sendCapabilitiesQuery(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_SendClientQuery(m_net.data(), vatClientQueryCaps, toFSD(callsign)); - } - - void CNetworkVatlib::replyToFrequencyQuery(const BlackMisc::Aviation::CCallsign &callsign) // private - { - QStringList response { QString::number(getOwnAircraft().getCom1System().getFrequencyActive().value(CFrequencyUnit::MHz()), 'f', 3)}; - Vat_SendClientQueryResponse(m_net.data(), vatClientQueryFreq, toFSD(callsign), toFSD(response)(), response.size()); - } - - void CNetworkVatlib::replyToNameQuery(const BlackMisc::Aviation::CCallsign &callsign) // private - { - QStringList response { m_server.getUser().getRealName(), "" }; - Vat_SendClientQueryResponse(m_net.data(), vatClientQueryName, toFSD(callsign), toFSD(response)(), response.size()); - } - - void CNetworkVatlib::replyToConfigQuery(const CCallsign &callsign) - { - QJsonObject config = getOwnAircraftParts().toJson(); - config.insert("is_full_data", true); - QString data = QJsonDocument(QJsonObject { { "config", config } }).toJson(QJsonDocument::Compact); - data = convertToUnicodeEscaped(data); - Vat_SendAircraftConfig(m_net.data(), toFSD(callsign), toFSD(data)); - } - - void CNetworkVatlib::sendIcaoCodesQuery(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_RequestAircraftInfo(m_net.data(), toFSD(callsign)); - } - - void CNetworkVatlib::sendAircraftInfo(const BlackMisc::Aviation::CCallsign &callsign) // private - { - const QByteArray acTypeICAObytes = toFSD(m_ownAircraftIcaoCode.getDesignator()); - const QByteArray airlineICAObytes = toFSD(m_ownAirlineIcaoCode.getDesignator()); - const QByteArray liverybytes = toFSD(m_ownLiveryDescription); - - VatAircraftInfo aircraftInfo {acTypeICAObytes, airlineICAObytes, liverybytes}; - Vat_SendModernPlaneInfo(m_net.data(), toFSD(callsign), &aircraftInfo); - } - - void CNetworkVatlib::sendIncrementalAircraftConfig() - { - if (!isConnected()) { return; } - CAircraftParts currentParts(getOwnAircraftParts()); - - // If it hasn't changed, return - if (m_sentAircraftConfig == currentParts) { return; } - - if (!m_tokenBucket.tryConsume()) - { - // If timer is not yet active, start it - if (!m_scheduledConfigUpdate.isActive()) m_scheduledConfigUpdate.start(1000); - return; - } - - // Method could have been triggered by another change in aircraft config - // so a previous update might still be scheduled. Stop it. - if (m_scheduledConfigUpdate.isActive()) m_scheduledConfigUpdate.stop(); - QJsonObject previousConfig = m_sentAircraftConfig.toJson(); - QJsonObject currentConfig = currentParts.toJson(); - QJsonObject incrementalConfig = getIncrementalObject(previousConfig, currentConfig); - broadcastAircraftConfig(incrementalConfig); - m_sentAircraftConfig = currentParts; - } - - void CNetworkVatlib::sendPing(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_SendPing(m_net.data(), toFSD(callsign)); - } - - void CNetworkVatlib::sendMetarQuery(const BlackMisc::Aviation::CAirportIcaoCode &airportIcao) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - Vat_RequestMetar(m_net.data(), toFSD(airportIcao.asString())); - } - - const QList &CNetworkVatlib::getCmdLineOptions() - { - static const QList e; - static const QList opts - { - QCommandLineOption({ "key", "fsdkey" }, - QCoreApplication::translate("application", "Key for FSD"), - "fsdkey") - }; - - // only in not officially shipped versions - return (CBuildConfig::isShippedVersion() && !CBuildConfig::isBetaTest()) ? e : opts; - } - - QString CNetworkVatlib::getCmdLineFsdKey() const - { - return sApp->getParserValue("fsdkey").toLower(); - } - - void CNetworkVatlib::sendCustomFsinnQuery(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - CSimulatedAircraft myAircraft(getOwnAircraft()); - QString modelString = myAircraft.getModel().getModelString(); - if (modelString.isEmpty()) { modelString = defaultModelString(); } - - QStringList data { { "0" }, - myAircraft.getAirlineIcaoCodeDesignator(), - myAircraft.getAircraftIcaoCodeDesignator(), - { "" }, { "" }, { "" }, { "" }, - myAircraft.getAircraftIcaoCombinedType(), - modelString - }; - sendCustomPacket(callsign, "FSIPIR", data); - } - - void CNetworkVatlib::sendCustomFsinnReponse(const BlackMisc::Aviation::CCallsign &callsign) - { - Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); - CSimulatedAircraft myAircraft(getOwnAircraft()); - QString modelString = myAircraft.getModel().getModelString(); - if (modelString.isEmpty()) { modelString = defaultModelString(); } - - QStringList data { { "0" }, - myAircraft.getAirlineIcaoCodeDesignator(), - myAircraft.getAircraftIcaoCodeDesignator(), - { "" }, { "" }, { "" }, { "" }, - myAircraft.getAircraftIcaoCombinedType(), - modelString - }; - sendCustomPacket(callsign, "FSIPI", data); - } - - void CNetworkVatlib::broadcastAircraftConfig(const QJsonObject &config) - { - QString data = QJsonDocument(QJsonObject { { "config", config } }).toJson(QJsonDocument::Compact); - data = convertToUnicodeEscaped(data); - Vat_SendAircraftConfigBroadcast(m_net.data(), toFSD(data)); - } - - void CNetworkVatlib::sendAircraftConfigQuery(const CCallsign &callsign) - { - QJsonDocument doc(JsonPackets::aircraftConfigRequest()); - QString data { doc.toJson(QJsonDocument::Compact) }; - Vat_SendAircraftConfig(m_net.data(), toFSD(callsign), toFSD(data)); - } - - /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ - /********************************** shimlib callbacks ************************************/ - /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ - -//! Cast void* to a pointer of CNetworkVatlib - CNetworkVatlib *cbvar_cast(void *cbvar) - { - return static_cast(cbvar); - } - - void CNetworkVatlib::onConnectionStatusChanged(VatSessionID, VatConnectionStatus, VatConnectionStatus newStatus, void *cbvar) - { - if (newStatus == vatStatusError) - { - cbvar_cast(cbvar)->changeConnectionStatus(newStatus); - } - else - { - cbvar_cast(cbvar)->changeConnectionStatus(newStatus); - } - } - - void CNetworkVatlib::onTextMessageReceived(VatSessionID, const char *from, const char *to, const char *msg, void *cbvar) - { - BlackMisc::Network::CTextMessageList messages(cbvar_cast(cbvar)->fromFSD(msg), CCallsign(cbvar_cast(cbvar)->fromFSD(from)), CCallsign(cbvar_cast(cbvar)->fromFSD(to))); - emit cbvar_cast(cbvar)->textMessagesReceived(messages); - } - - void CNetworkVatlib::onRadioMessageReceived(VatSessionID, const char *from, int numFreq, int *freqList, const char *msg, void *cbvar) - { - int com1 = cbvar_cast(cbvar)->getOwnAircraft().getCom1System().getFrequencyActive().valueInteger(CFrequencyUnit::kHz()); - int com2 = cbvar_cast(cbvar)->getOwnAircraft().getCom2System().getFrequencyActive().valueInteger(CFrequencyUnit::kHz()); - QList frequencies; - for (int i = 0; i < numFreq; ++i) - { - if (freqList[i] == com1 || freqList[i] == com2) - { - frequencies.push_back(CFrequency(freqList[i], CFrequencyUnit::kHz())); - } - } - if (frequencies.isEmpty()) { return; } - BlackMisc::Network::CTextMessageList messages(cbvar_cast(cbvar)->fromFSD(msg), frequencies, CCallsign(cbvar_cast(cbvar)->fromFSD(from))); - emit cbvar_cast(cbvar)->textMessagesReceived(messages); - } - - void CNetworkVatlib::onPilotDisconnected(VatSessionID, const char *callsign, void *cbvar) - { - emit cbvar_cast(cbvar)->pilotDisconnected(CCallsign(cbvar_cast(cbvar)->fromFSD(callsign), CCallsign::Aircraft)); - } - - void CNetworkVatlib::onControllerDisconnected(VatSessionID, const char *callsign, void *cbvar) - { - emit cbvar_cast(cbvar)->atcDisconnected(CCallsign(cbvar_cast(cbvar)->fromFSD(callsign), CCallsign::Atc)); - } - - void CNetworkVatlib::onPilotPositionUpdate(VatSessionID, const char *callsignChar , const VatPilotPosition *position, void *cbvar) - { - const CCallsign callsign(callsignChar, CCallsign::Aircraft); - CAircraftSituation situation( - callsign, - CCoordinateGeodetic(position->latitude, position->longitude, 0.0), - CAltitude(position->altitudeTrue, CAltitude::MeanSeaLevel, CLengthUnit::ft()), - CHeading(position->heading, CHeading::True, CAngleUnit::deg()), - CAngle(position->pitch, CAngleUnit::deg()), - CAngle(position->bank, CAngleUnit::deg()), - CSpeed(position->groundSpeed, CSpeedUnit::kts()) - ); - situation.setCurrentUtcTime(); - situation.setTimeOffsetMs(6000); - - QString transponderName("transponder "); - transponderName.append(callsign.asString()); - CTransponder::TransponderMode mode = CTransponder::StateStandby; - switch (position->transponderMode) - { - case vatTransponderModeCharlie: - mode = CTransponder::ModeC; - break; - case vatTransponderModeStandby: - mode = CTransponder::StateStandby; - break; - case vatTransponderModeIdent: - mode = CTransponder::StateIdent; - break; - default: - mode = CTransponder::StateStandby; - break; - } - - // I did have a situation where I got wrong transponder codes (KB) - // So I now check for a valid code in order to detect such codes - CTransponder transponder; - if (CTransponder::isValidTransponderCode(position->transponderCode)) - { - transponder = CTransponder(position->transponderCode, mode); - } - else - { - CLogMessage(static_cast(nullptr)).warning("Wrong transponder code %1 for %2") << position->transponderCode << callsign; - - // default - transponder = CTransponder(7000, mode); - } - emit cbvar_cast(cbvar)->aircraftPositionUpdate(situation, transponder); - } - - void CNetworkVatlib::onAircraftConfigReceived(VatSessionID, const char *callsign, const char *aircraftConfig, void *cbvar) - { - QByteArray json = cbvar_cast(cbvar)->fromFSD(aircraftConfig).toUtf8(); - QJsonParseError parserError; - QJsonDocument doc = QJsonDocument::fromJson(json, &parserError); - CCallsign cs(callsign, CCallsign::Aircraft); - - if (parserError.error != QJsonParseError::NoError) - { - CLogMessage(static_cast(nullptr)).warning("Failed to parse aircraft config packet: %1") << parserError.errorString(); - } - - QJsonObject packet = doc.object(); - - if (packet == JsonPackets::aircraftConfigRequest()) - { - cbvar_cast(cbvar)->replyToConfigQuery(cbvar_cast(cbvar)->fromFSD(callsign)); - return; - } - - QJsonObject config = doc.object().value("config").toObject(); - if (config.empty()) return; - - bool isFull = config.take("is_full_data").toBool(false); - emit cbvar_cast(cbvar)->aircraftConfigPacketReceived(cs, config, isFull); - } - - void CNetworkVatlib::onInterimPilotPositionUpdate(VatSessionID, const char *sender, const VatInterimPilotPosition *position, void *cbvar) - { - CAircraftSituation situation( - CCallsign(sender), - CCoordinateGeodetic(position->latitude, position->longitude, 0.0), - CAltitude(position->altitudeTrue, CAltitude::MeanSeaLevel, CLengthUnit::ft()), - CHeading(position->heading, CHeading::True, CAngleUnit::deg()), - CAngle(position->pitch, CAngleUnit::deg()), - CAngle(position->bank, CAngleUnit::deg()), - // There is no speed information in a interim packet - CSpeed(0.0, CSpeedUnit::kts()) - ); - situation.setCurrentUtcTime(); - situation.setTimeOffsetMs(2000); - situation.setInterimFlag(true); - - emit cbvar_cast(cbvar)->aircraftInterimPositionUpdate(situation); - } - - void CNetworkVatlib::onAtcPositionUpdate(VatSessionID, const char *callsign, const VatAtcPosition *pos, void *cbvar) - { - int frequencyKHz = pos->frequency; - CFrequency freq(frequencyKHz, CFrequencyUnit::kHz()); - freq.switchUnit(CFrequencyUnit::MHz()); // we would not need to bother, but this makes it easier to identify - emit cbvar_cast(cbvar)->atcPositionUpdate( - CCallsign(cbvar_cast(cbvar)->fromFSD(callsign), CCallsign::Atc), freq, - CCoordinateGeodetic(pos->latitude, pos->longitude, 0), CLength(pos->visibleRange, CLengthUnit::NM())); - } - - void CNetworkVatlib::onKicked(VatSessionID, const char *reason, void *cbvar) - { - emit cbvar_cast(cbvar)->kicked(cbvar_cast(cbvar)->fromFSD(reason)); - } - - void CNetworkVatlib::onPong(VatSessionID, const char *sender, double elapsedTime, void *cbvar) - { - emit cbvar_cast(cbvar)->pongReceived(cbvar_cast(cbvar)->fromFSD(sender), CTime(elapsedTime, CTimeUnit::ms())); - } - - void CNetworkVatlib::onCustomPacketReceived(VatSessionID, const char *callsign, const char *packetId, const char **data, int dataSize, void *cbvar) - { - emit cbvar_cast(cbvar)->customPacketReceived(cbvar_cast(cbvar)->fromFSD(callsign), cbvar_cast(cbvar)->fromFSD(packetId), cbvar_cast(cbvar)->fromFSD(data, dataSize)); - } - - void CNetworkVatlib::customPacketDispatcher(const BlackMisc::Aviation::CCallsign &callsign, const QString &packetId, const QStringList &data) - { - if (packetId.compare("FSIPI", Qt::CaseInsensitive) == 0) - { - if (data.size() < 9) - { - CLogMessage(this).warning("Malformed FSIPI packet"); - } - else - { - // It doesn't matter whether it was a query or response. The information - // is the same for both. - emit customFSinnPacketReceived(callsign, data[1], data[2], data[7], data[8]); - } - } - else if (packetId.compare("FSIPIR", Qt::CaseInsensitive) == 0) - { - if (data.size() < 9) - { - CLogMessage(this).warning("Malformed FSIPIR packet"); - } - else - { - sendCustomFsinnReponse(callsign); - // It doesn't matter whether it was a query or response. The information - // is the same for both. - emit customFSinnPacketReceived(callsign, data[1], data[2], data[7], data[8]); - } - } - } - - void CNetworkVatlib::onMetarReceived(VatSessionID, const char *data, void *cbvar) - { - emit cbvar_cast(cbvar)->metarReplyReceived(cbvar_cast(cbvar)->fromFSD(data)); - } - - void CNetworkVatlib::onInfoQueryRequestReceived(VatSessionID, const char *callsignString, VatClientQueryType type, const char *, void *cbvar) - { - auto timer = new QTimer(cbvar_cast(cbvar)); - timer->setSingleShot(true); - timer->start(0); - - BlackMisc::Aviation::CCallsign callsign(callsignString); - switch (type) - { - case vatClientQueryFreq: - connect(timer, &QTimer::timeout, [ = ]() { cbvar_cast(cbvar)->replyToFrequencyQuery(callsign); }); - break; - case vatClientQueryName: - connect(timer, &QTimer::timeout, [ = ]() { cbvar_cast(cbvar)->replyToNameQuery(callsign); }); - break; - default: - break; - } - } - - void CNetworkVatlib::onInfoQueryReplyReceived(VatSessionID, const char *callsign, VatClientQueryType type, const char *data, const char *data2, void *cbvar) - { - switch (type) - { - case vatClientQueryFreq: emit cbvar_cast(cbvar)->frequencyReplyReceived(cbvar_cast(cbvar)->fromFSD(callsign), CFrequency(cbvar_cast(cbvar)->fromFSD(data).toFloat(), CFrequencyUnit::MHz())); break; - case vatClientQueryServer: emit cbvar_cast(cbvar)->serverReplyReceived(cbvar_cast(cbvar)->fromFSD(callsign), cbvar_cast(cbvar)->fromFSD(data)); break; - case vatClientQueryAtc: emit cbvar_cast(cbvar)->atcReplyReceived(CCallsign(cbvar_cast(cbvar)->fromFSD(data2), CCallsign::Atc), *data == 'Y'); break; - case vatClientQueryName: emit cbvar_cast(cbvar)->realNameReplyReceived(cbvar_cast(cbvar)->fromFSD(callsign), cbvar_cast(cbvar)->fromFSD(data)); break; - case vatClientQueryIP: emit cbvar_cast(cbvar)->ipReplyReceived(cbvar_cast(cbvar)->fromFSD(data)); break; - default: break; - } - } - - void CNetworkVatlib::onCapabilitiesReplyReceived(VatSessionID, const char *callsign, int capabilityFlags, void *cbvar) - { - int flags = 0; - if (capabilityFlags & vatCapsAtcInfo) { flags |= AcceptsAtisResponses; } - if (capabilityFlags & vatCapsInterminPos) { flags |= SupportsInterimPosUpdates; } - if (capabilityFlags & vatCapsModelDesc) { flags |= SupportsIcaoCodes; } - if (capabilityFlags & vatCapsAircraftConfig) { flags |= SupportsAircraftConfigs; } - emit cbvar_cast(cbvar)->capabilitiesReplyReceived(cbvar_cast(cbvar)->fromFSD(callsign), flags); - } - - void CNetworkVatlib::onAtisReplyReceived(VatSessionID, const char *callsign, const VatControllerAtis *atis, void *cbvar) - { - emit cbvar_cast(cbvar)->atisVoiceRoomReplyReceived(cbvar_cast(cbvar)->fromFSD(callsign), cbvar_cast(cbvar)->fromFSD(atis->voiceRoom)); - emit cbvar_cast(cbvar)->atisLogoffTimeReplyReceived(cbvar_cast(cbvar)->fromFSD(callsign), cbvar_cast(cbvar)->fromFSD(atis->zuluLogoff)); - - CInformationMessage atisMessage; - atisMessage.setType(CInformationMessage::ATIS); - for (int i = 0; i < atis->textLineCount; ++i) - { - const QString fixed = cbvar_cast(cbvar)->fromFSD(atis->textLines[i]).trimmed(); - if (! fixed.isEmpty()) - { - // detect the stupid z1, z2, z3 placeholders - // TODO: Anything better as this stupid code here? - const QString test = fixed.toLower().remove(QRegExp("[\\n\\t\\r]")); - if (test == "z") return; - if (test.startsWith("z") && test.length() == 2) return; // z1, z2, .. - if (test.length() == 1) return; // sometimes just z - - // append - if (!atisMessage.isEmpty()) atisMessage.appendMessage("\n"); - atisMessage.appendMessage(fixed); - } - } - - emit cbvar_cast(cbvar)->atisReplyReceived(CCallsign(cbvar_cast(cbvar)->fromFSD(callsign), CCallsign::Atc), atisMessage); - } - - void CNetworkVatlib::onFlightPlanReceived(VatSessionID, const char *callsign, const VatFlightPlan *fp, void *cbvar) - { - BlackMisc::Aviation::CFlightPlan::FlightRules rules = BlackMisc::Aviation::CFlightPlan::VFR; - switch (fp->flightType) - { - default: - case vatFlightTypeVFR: rules = BlackMisc::Aviation::CFlightPlan::VFR; break; - case vatFlightTypeIFR: rules = BlackMisc::Aviation::CFlightPlan::IFR; break; - // case Cvatlib_Network::fpRuleType_SVFR: rules = BlackMisc::Aviation::CFlightPlan::SVFR; break; - } - - auto cruiseAltString = cbvar_cast(cbvar)->fromFSD(fp->cruiseAltitude); - static const QRegExp withUnit("\\D+"); - if (!cruiseAltString.isEmpty() && withUnit.indexIn(cruiseAltString) < 0) - { - cruiseAltString += "ft"; - } - BlackMisc::Aviation::CAltitude cruiseAlt; - cruiseAlt.parseFromString(cruiseAltString); - - QString depTimePlanned = QString("0000").append(QString::number(fp->departTime)).right(4); - QString depTimeActual = QString("0000").append(QString::number(fp->departTimeActual)).right(4); - - BlackMisc::Aviation::CFlightPlan flightPlan( - cbvar_cast(cbvar)->fromFSD(fp->aircraftType), - cbvar_cast(cbvar)->fromFSD(fp->departAirport), - cbvar_cast(cbvar)->fromFSD(fp->destAirport), - cbvar_cast(cbvar)->fromFSD(fp->alternateAirport), - QDateTime::fromString(depTimePlanned, "hhmm"), - QDateTime::fromString(depTimeActual, "hhmm"), - BlackMisc::PhysicalQuantities::CTime(fp->enrouteHrs * 60 + fp->enrouteMins, BlackMisc::PhysicalQuantities::CTimeUnit::min()), - BlackMisc::PhysicalQuantities::CTime(fp->fuelHrs * 60 + fp->fuelMins, BlackMisc::PhysicalQuantities::CTimeUnit::min()), - cruiseAlt, - BlackMisc::PhysicalQuantities::CSpeed(fp->trueCruisingSpeed, BlackMisc::PhysicalQuantities::CSpeedUnit::kts()), - rules, - cbvar_cast(cbvar)->fromFSD(fp->route), - cbvar_cast(cbvar)->fromFSD(fp->remarks) - ); - - emit cbvar_cast(cbvar)->flightPlanReplyReceived(CCallsign(callsign, CCallsign::Atc), flightPlan); - } - - void CNetworkVatlib::onErrorReceived(VatSessionID, VatServerError error, const char *msg, const char *data, void *cbvar) - { - auto *self = cbvar_cast(cbvar); - switch (error) - { - case vatServerErrorCsInUs: CLogMessage(self).error("The requested callsign is already taken"); emit self->terminate(); break; - case vatServerErrorCallsignInvalid: CLogMessage(self).error("The requested callsign is not valid"); emit self->terminate(); break; - case vatServerErrorCidInvalid: CLogMessage(self).error("Wrong user ID or password"); emit self->terminate(); break; - case vatServerErrorRevision: CLogMessage(self).error("This server does not support our protocol version"); emit self->terminate(); break; - case vatServerErrorLevel: CLogMessage(self).error("You are not authorized to use the requested pilot rating"); emit self->terminate(); break; - case vatServerErrorServFull: CLogMessage(self).error("The server is full"); emit self->terminate(); break; - case vatServerErrorCsSuspended: CLogMessage(self).error("Your user account is suspended"); emit self->terminate(); break; - case vatServerErrorInvPos: CLogMessage(self).error("You are not authorized to use the requested rating"); emit self->terminate(); break; - case vatServerErrorUnAuth: CLogMessage(self).error("This software is not authorized for use on this network"); emit self->terminate(); break; - - case vatServerErrorNone: CLogMessage(self).info("OK"); break; - case vatServerErrorSyntax: CLogMessage(self).info("Malformed packet: Syntax error: %1") << self->fromFSD(data); break; - case vatServerErrorSrcInvalid: CLogMessage(self).info("Server: source invalid %1") << self->fromFSD(data); break; - case vatServerErrorNoSuchCs: CLogMessage(self).info("Shim lib: %1 (%2)") << self->fromFSD(msg) << self->fromFSD(data); break; - case vatServerErrorNoFP: CLogMessage(self).info("Server: no flight plan"); break; - case vatServerErrorNoWeather: CLogMessage(self).info("Server: requested weather profile does not exist"); break; - - // we have no idea what these mean - case vatServerErrorRegistered: - case vatServerErrorInvalidCtrl: CLogMessage(self).info("Server: ") << self->fromFSD(msg); break; - - default: qFatal("vatlib: %s (error %d)", msg, error); emit self->terminate(); - } - } - - void CNetworkVatlib::onPilotInfoRequestReceived(VatSessionID, const char *callsignString, void *cbvar) - { - auto timer = new QTimer(cbvar_cast(cbvar)); - timer->setSingleShot(true); - timer->start(0); - - BlackMisc::Aviation::CCallsign callsign(callsignString); - connect(timer, &QTimer::timeout, [ = ]() { cbvar_cast(cbvar)->sendAircraftInfo(callsign); }); - } - - void CNetworkVatlib::onPilotInfoReceived(VatSessionID, const char *callsign, const VatAircraftInfo *aircraftInfo, void *cbvar) - { - emit cbvar_cast(cbvar)->icaoCodesReplyReceived( - cbvar_cast(cbvar)->fromFSD(callsign), - cbvar_cast(cbvar)->fromFSD(aircraftInfo->aircraftType).trimmed().toUpper(), - cbvar_cast(cbvar)->fromFSD(aircraftInfo->airline).trimmed().toUpper(), - cbvar_cast(cbvar)->fromFSD(aircraftInfo->livery).trimmed().toUpper() - ); - } - - void CNetworkVatlib::networkLogHandler(SeverityLevel /** severity **/, const char *message) - { - CLogMessage(static_cast(nullptr)).error(message); - } - - QJsonObject CNetworkVatlib::JsonPackets::aircraftConfigRequest() - { - return { { "request", "full" } }; - } - -} // namespace - -//! \endcond diff --git a/src/blackcore/networkvatlib.h b/src/blackcore/networkvatlib.h deleted file mode 100644 index 1acb9a6e5..000000000 --- a/src/blackcore/networkvatlib.h +++ /dev/null @@ -1,226 +0,0 @@ -/* Copyright (C) 2013 - * swift Project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \file - -#ifndef BLACKCORE_NETWORK_VATLIB_H -#define BLACKCORE_NETWORK_VATLIB_H - -#include "blackcore/blackcoreexport.h" -#include "blackcore/network.h" -#include "blackcore/settings/network.h" -#include "blackcore/tokenbucket.h" -#include "blackmisc/aviation/aircrafticaocode.h" -#include "blackmisc/aviation/aircraftparts.h" -#include "blackmisc/aviation/airlineicaocode.h" -#include "blackmisc/aviation/airporticaocode.h" -#include "blackmisc/aviation/callsign.h" -#include "blackmisc/aviation/callsignset.h" -#include "blackmisc/network/server.h" -#include "blackmisc/network/textmessagelist.h" -#include "blackmisc/settingscache.h" -#include "blackmisc/simulation/ownaircraftprovider.h" -#include "blackmisc/simulation/simulatorplugininfo.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class QCommandLineOption; - -namespace BlackMisc -{ - namespace Aviation { class CFlightPlan; } - namespace Simulation { class CSimulatedAircraft; } -} - -namespace BlackCore -{ - //! Implementation of INetwork using the vatlib shim - class BLACKCORE_EXPORT CNetworkVatlib : - public INetwork, - public BlackMisc::Simulation::COwnAircraftAware // network vatlib consumes own aircraft data and sets ICAO/callsign data - { - Q_OBJECT - - public: - //! Constructor - CNetworkVatlib(BlackMisc::Simulation::IOwnAircraftProvider *ownAircraft, QObject *parent = nullptr); - - //! Destructor - virtual ~CNetworkVatlib(); - - //! \name Network functions - //! @{ - virtual bool isConnected() const override { return m_status == vatStatusConnected; } - virtual BlackMisc::Network::CServer getPresetServer() const override { return m_server; } - virtual bool isPendingConnection() const override { return m_status == vatStatusConnecting; } - virtual void presetLoginMode(LoginMode mode) override; - virtual void presetServer(const BlackMisc::Network::CServer &server) override; - virtual void presetCallsign(const BlackMisc::Aviation::CCallsign &callsign) override; - virtual void presetIcaoCodes(const BlackMisc::Simulation::CSimulatedAircraft &ownAircraft) override; - virtual void presetSimulatorInfo(const BlackMisc::Simulation::CSimulatorPluginInfo &simInfo) override; - virtual void initiateConnection() override; - virtual void terminateConnection() override; - virtual void sendPing(const BlackMisc::Aviation::CCallsign &callsign) override; - virtual void sendRealNameQuery(const BlackMisc::Aviation::CCallsign &callsign) override; - virtual void sendIpQuery() override; - virtual void sendServerQuery(const BlackMisc::Aviation::CCallsign &callsign) override; - virtual void sendCustomPacket(const BlackMisc::Aviation::CCallsign &callsign, const QString &packetId, const QStringList &data) override; - virtual void sendCustomFsinnQuery(const BlackMisc::Aviation::CCallsign &callsign) override; - virtual void sendCustomFsinnReponse(const BlackMisc::Aviation::CCallsign &callsign) override; - - virtual void broadcastAircraftConfig(const QJsonObject &config) override; - virtual void sendAircraftConfigQuery(const BlackMisc::Aviation::CCallsign &callsign) override; - //! @} - - //! \name Text message functions - //! @{ - virtual void sendTextMessages(const BlackMisc::Network::CTextMessageList &messages) override; - //! @} - - //! \name ATC functions - //! @{ - virtual void sendAtcQuery(const BlackMisc::Aviation::CCallsign &callsign) override; - virtual void sendAtisQuery(const BlackMisc::Aviation::CCallsign &callsign) override; - virtual void sendFlightPlan(const BlackMisc::Aviation::CFlightPlan &flightPlan) override; - virtual void sendFlightPlanQuery(const BlackMisc::Aviation::CCallsign &callsign) override; - //! @} - - //! \name Aircraft functions - //! @{ - virtual void sendCapabilitiesQuery(const BlackMisc::Aviation::CCallsign &callsign) override; - virtual void sendIcaoCodesQuery(const BlackMisc::Aviation::CCallsign &callsign) override; - virtual void sendFrequencyQuery(const BlackMisc::Aviation::CCallsign &callsign) override; - virtual void sendUserInfoQuery(const BlackMisc::Aviation::CCallsign &callsign) override; - virtual void setInterimPositionReceivers(const BlackMisc::Aviation::CCallsignSet &receivers) override; - //! @} - - //! \name Weather functions - //! @{ - virtual void sendMetarQuery(const BlackMisc::Aviation::CAirportIcaoCode &airportIcao) override; - //! @} - - //! Cmd.line options this library can handle - static const QList &getCmdLineOptions(); - - //! Key if any from cmd.line arguments - QString getCmdLineFsdKey() const; - - private slots: - void replyToFrequencyQuery(const BlackMisc::Aviation::CCallsign &callsign); - void replyToNameQuery(const BlackMisc::Aviation::CCallsign &callsign); - void replyToConfigQuery(const BlackMisc::Aviation::CCallsign &callsign); - void sendAircraftInfo(const BlackMisc::Aviation::CCallsign &callsign); - void sendIncrementalAircraftConfig(); - - private: - //! \name Shimlib callbacks - //! @{ - static void onConnectionStatusChanged(VatSessionID, VatConnectionStatus oldStatus, VatConnectionStatus newStatus, void *cbvar); - static void onTextMessageReceived(VatSessionID, const char *from, const char *to, const char *msg, void *cbvar); - static void onRadioMessageReceived(VatSessionID, const char *from, int freqCount, int *freqList, const char *message, void *cbvar); - static void onControllerDisconnected(VatSessionID, const char *callsign, void *cbvar); - static void onInterimPilotPositionUpdate(VatSessionID, const char *sender, const VatInterimPilotPosition *position, void *cbvar); - static void onAtcPositionUpdate(VatSessionID, const char *callsign, const VatAtcPosition *pos, void *cbvar); - static void onKicked(VatSessionID, const char *reason, void *cbvar); - static void onPong(VatSessionID, const char *sender, double elapsedTime, void *cbvar); - static void onMetarReceived(VatSessionID, const char *data, void *cbvar); - static void onInfoQueryRequestReceived(VatSessionID, const char *callsign, VatClientQueryType type, const char *data, void *cbvar); - static void onInfoQueryReplyReceived(VatSessionID, const char *callsign, VatClientQueryType type, const char *data, const char *data2, void *cbvar); - static void onCapabilitiesReplyReceived(VatSessionID, const char *callsign, int capabilityFlags, void *cbvar); - static void onAtisReplyReceived(VatSessionID, const char *callsign, const VatControllerAtis *atis, void *cbvar); - static void onFlightPlanReceived(VatSessionID, const char *callsign, const VatFlightPlan *fp, void *cbvar); - static void onErrorReceived(VatSessionID, VatServerError error, const char *msg, const char *data, void *cbvar); - static void onPilotDisconnected(VatSessionID, const char *callsign, void *cbvar); - static void onPilotInfoRequestReceived(VatSessionID, const char *callsign, void *cbvar); - static void onPilotInfoReceived(VatSessionID, const char *callsign, const VatAircraftInfo *aircraftInfo, void *cbvar); - static void onPilotPositionUpdate(VatSessionID, const char *callsign, const VatPilotPosition *position, void *cbvar); - static void onAircraftConfigReceived(VatSessionID, const char *callsign, const char *aircraftConfig, void *cbvar); - static void onCustomPacketReceived(VatSessionID, const char *callsign, const char *packetId, const char **data, int dataSize, void *cbvar); - //! @} - - QByteArray toFSD(QString qstr) const; - QByteArray toFSD(const BlackMisc::Aviation::CCallsign &callsign) const; - std::function toFSD(QStringList qstrList) const; - QString fromFSD(const char *cstr) const; - QStringList fromFSD(const char **cstrArray, int size) const; - bool isInterimPositionUpdateEnabledForServer() const; - void startPositionTimers(); - void stopPositionTimers(); - void initializeSession(); - void changeConnectionStatus(VatConnectionStatus newStatus); - bool isDisconnected() const { return m_status != vatStatusConnecting && m_status != vatStatusConnected; } - static QString convertToUnicodeEscaped(const QString &str); - static VatSimType convertToSimType(BlackMisc::Simulation::CSimulatorPluginInfo &simInfo); - static void networkLogHandler(SeverityLevel severity, const char *message); - - inline QString defaultModelString() - { - return QStringLiteral("Cessna Skyhawk 172SP"); - } - - struct JsonPackets - { - static QJsonObject aircraftConfigRequest(); - }; - - private slots: - void process(); - void sendPositionUpdate(); - void sendInterimPositions(); - void customPacketDispatcher(const BlackMisc::Aviation::CCallsign &callsign, const QString &packetId, const QStringList &data); - - signals: - void terminate(); //!< \private - - private: - //! Deletion policy for QScopedPointer - struct VatlibQScopedPointerDeleter - { - //! Called by QScopedPointer destructor - static void cleanup(VatSessionID session) { if (session) Vat_DestroyNetworkSession(session); } - }; - - QScopedPointer m_net; - LoginMode m_loginMode; - VatConnectionStatus m_status; - BlackMisc::Network::CServer m_server; - QTextCodec *m_fsdTextCodec = nullptr; - BlackMisc::Simulation::CSimulatorPluginInfo m_simulatorInfo; - BlackMisc::Aviation::CCallsign m_ownCallsign; //!< "buffered callsign", as this must not change when connected - BlackMisc::Aviation::CAircraftIcaoCode m_ownAircraftIcaoCode; //!< "buffered icao", as this must not change when connected - BlackMisc::Aviation::CAirlineIcaoCode m_ownAirlineIcaoCode; //!< "buffered icao", as this must not change when connected - QString m_ownLiveryDescription; //!< "buffered livery", as this must not change when connected - BlackMisc::Aviation::CCallsignSet m_interimPositionReceivers; - BlackMisc::Aviation::CAircraftParts m_sentAircraftConfig; - CTokenBucket m_tokenBucket; - - QTimer m_scheduledConfigUpdate; - QTimer m_processingTimer; - QTimer m_positionUpdateTimer; - QTimer m_interimPositionUpdateTimer; - - static int const c_processingIntervalMsec = 100; - static int const c_updatePostionIntervalMsec = 5000; - static int const c_updateInterimPostionIntervalMsec = 1000; - static int const c_logoffTimeoutSec = 5; - }; -} //namespace - -#endif // guard diff --git a/src/blackcore/vatsim/audiodevicevatlib.cpp b/src/blackcore/vatsim/audiodevicevatlib.cpp new file mode 100644 index 000000000..efd998d32 --- /dev/null +++ b/src/blackcore/vatsim/audiodevicevatlib.cpp @@ -0,0 +1,142 @@ +/* Copyright (C) 2014 + * swift project community / contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \cond PRIVATE + +#include "blackcore/vatsim/audiodevicevatlib.h" +#include "blackmisc/logmessage.h" + +#include +#include + +using namespace BlackMisc; +using namespace BlackMisc::Audio; +using namespace BlackMisc::Aviation; + +namespace BlackCore +{ + namespace Vatsim + { + //! Cast void* to a pointer of CAudioInputDeviceVatlib + CAudioInputDeviceVatlib *cbvar_cast_inputDevice(void *cbvar) + { + return static_cast(cbvar); + } + + //! Cast void* to a pointer of CAudioOutputDeviceVatlib + CAudioOutputDeviceVatlib *cbvar_cast_outputDevice(void *cbvar) + { + return static_cast(cbvar); + } + + CAudioInputDeviceVatlib::CAudioInputDeviceVatlib(VatAudioService audioService, QObject *parent) + : IAudioInputDevice(parent), + m_audioService(audioService) + { + m_inputCodec.reset(Vat_CreateLocalInputCodec(audioService, vatCodecLegacy)); + Vat_GetInputDeviceInfo(m_inputCodec.data(), onInputHardwareDeviceReceived, this, nullptr); + m_currentDevice = getDefaultInputDevice(); + } + + const BlackMisc::Audio::CAudioDeviceInfoList &CAudioInputDeviceVatlib::getInputDevices() const + { + return m_devices; + } + + const BlackMisc::Audio::CAudioDeviceInfo &CAudioInputDeviceVatlib::getDefaultInputDevice() const + { + static BlackMisc::Audio::CAudioDeviceInfo info(BlackMisc::Audio::CAudioDeviceInfo::InputDevice, BlackMisc::Audio::CAudioDeviceInfo::defaultDeviceIndex(), "default"); + return info; + } + + const BlackMisc::Audio::CAudioDeviceInfo &CAudioInputDeviceVatlib::getCurrentInputDevice() const + { + return m_currentDevice; + } + + void CAudioInputDeviceVatlib::setInputDevice(const BlackMisc::Audio::CAudioDeviceInfo &device) + { + Q_ASSERT_X(m_inputCodec, "CAudioInputDeviceVatlib", "VatLocalCodec is invalid!"); + if (!device.isValid()) + { + CLogMessage(this).warning("Cannot set invalid input device!"); + return; + } + + if (!Vat_SetAudioInputDevice(m_inputCodec.data(), device.getIndex())) + { + CLogMessage(this).warning("Setting input device failed"); + } + m_currentDevice = device; + } + + void CAudioInputDeviceVatlib::onInputHardwareDeviceReceived(int deviceIndex, const char *hardwareName, void *cbVar) + { + BlackMisc::Audio::CAudioDeviceInfo inputDevice(BlackMisc::Audio::CAudioDeviceInfo::InputDevice, deviceIndex, QString(hardwareName)); + cbvar_cast_inputDevice(cbVar)->m_devices.push_back(inputDevice); + } + + CAudioOutputDeviceVatlib::CAudioOutputDeviceVatlib(VatAudioService audioService, QObject *parent) + : IAudioOutputDevice(parent), + m_audioService(audioService) + { + m_outputCodec.reset(Vat_CreateLocalOutputCodec(m_audioService, vatCodecLegacy)); + Vat_GetOutputDeviceInfo(m_outputCodec.data(), onOutputHardwareDeviceReceived, this, nullptr); + m_currentDevice = getDefaultOutputDevice(); + } + + const BlackMisc::Audio::CAudioDeviceInfoList &CAudioOutputDeviceVatlib::getOutputDevices() const + { + return m_devices; + } + + const BlackMisc::Audio::CAudioDeviceInfo &CAudioOutputDeviceVatlib::getDefaultOutputDevice() const + { + static BlackMisc::Audio::CAudioDeviceInfo info(BlackMisc::Audio::CAudioDeviceInfo::OutputDevice, BlackMisc::Audio::CAudioDeviceInfo::defaultDeviceIndex(), "default"); + return info; + } + + const BlackMisc::Audio::CAudioDeviceInfo &CAudioOutputDeviceVatlib::getCurrentOutputDevice() const + { + return m_currentDevice; + } + + void CAudioOutputDeviceVatlib::setOutputDevice(const BlackMisc::Audio::CAudioDeviceInfo &device) + { + Q_ASSERT_X(m_outputCodec, "CAudioOutputDeviceVatlib", "VatLocalCodec is invalid!"); + if (!device.isValid()) + { + CLogMessage(this).warning("Cannot set invalid output device!"); + return; + } + + Vat_SetAudioOutputDevice(m_outputCodec.data(), device.getIndex()); + this->m_currentDevice = device; + } + + void CAudioOutputDeviceVatlib::setOutputVolume(int volume) + { + Q_ASSERT(m_outputCodec); + Vat_SetOutputVolume(m_outputCodec.data(), volume); + } + + int CAudioOutputDeviceVatlib::getOutputVolume() const + { + Q_ASSERT(m_outputCodec); + return Vat_GetOutputVolume(m_outputCodec.data()); + } + + void CAudioOutputDeviceVatlib::onOutputHardwareDeviceReceived(int deviceIndex, const char *hardwareName, void *cbVar) + { + BlackMisc::Audio::CAudioDeviceInfo outputDevice(BlackMisc::Audio::CAudioDeviceInfo::OutputDevice, deviceIndex, QString(hardwareName)); + cbvar_cast_outputDevice(cbVar)->m_devices.push_back(outputDevice); + } + } // ns +} // ns +//! \endcond diff --git a/src/blackcore/vatsim/audiodevicevatlib.h b/src/blackcore/vatsim/audiodevicevatlib.h new file mode 100644 index 000000000..0790e02d5 --- /dev/null +++ b/src/blackcore/vatsim/audiodevicevatlib.h @@ -0,0 +1,134 @@ +/* Copyright (C) 2014 + * swift project community / contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKCORE_VATSIM_AUDIODEVICEVATLIB_H +#define BLACKCORE_VATSIM_AUDIODEVICEVATLIB_H + +#include "blackcore/audiodevice.h" +#include "blackcore/blackcoreexport.h" +#include "blackmisc/audio/audiodeviceinfo.h" +#include "blackmisc/audio/audiodeviceinfolist.h" +#include "vatlib/vatlib.h" + +#include +#include + +struct VatConsumer_tag; +struct VatProducer_tag; + +namespace BlackCore +{ + namespace Vatsim + { + //! Audio Input Device + class BLACKCORE_EXPORT CAudioInputDeviceVatlib : public IAudioInputDevice + { + Q_OBJECT + + public: + + //! Constructor + CAudioInputDeviceVatlib(VatAudioService audioService, QObject *parent = nullptr); + + //! Destructor + virtual ~CAudioInputDeviceVatlib() = default; + + //! \copydoc IAudioInputDevice::getInputDevices + virtual const BlackMisc::Audio::CAudioDeviceInfoList &getInputDevices() const override; + + //! \copydoc IAudioInputDevice::getDefaultInputDevice + virtual const BlackMisc::Audio::CAudioDeviceInfo &getDefaultInputDevice() const override; + + //! \copydoc IAudioInputDevice::getCurrentInputDevice + virtual const BlackMisc::Audio::CAudioDeviceInfo &getCurrentInputDevice() const override; + + //! \copydoc IAudioInputDevice::setInputDevice + virtual void setInputDevice(const BlackMisc::Audio::CAudioDeviceInfo &device) override; + + //! Get vatlib input device pointer + VatLocalInputCodec getVatLocalInputCodec() { return m_inputCodec.data(); } + + private: + + struct VatLocalInputCodecDeleter + { + static inline void cleanup(VatProducer_tag *obj) + { + if (obj) Vat_DestroyLocalInputCodec(obj); + } + }; + + static void onInputHardwareDeviceReceived(int deviceIndex, const char *hardwareName, void *cbVar); + + BlackMisc::Audio::CAudioDeviceInfoList m_devices; /*!< in and output devices */ + BlackMisc::Audio::CAudioDeviceInfo m_currentDevice; + + VatAudioService m_audioService; + QScopedPointer m_inputCodec; + }; + + //! Audio Output Device + class CAudioOutputDeviceVatlib : public IAudioOutputDevice + { + Q_OBJECT + + public: + + //! Constructor + CAudioOutputDeviceVatlib(VatAudioService audioService, QObject *parent = nullptr); + + //! Destructor + virtual ~CAudioOutputDeviceVatlib() = default; + + //! \copydoc IAudioOutputDevice::getOutputDevices + virtual const BlackMisc::Audio::CAudioDeviceInfoList &getOutputDevices() const override; + + //! \copydoc IAudioOutputDevice::getDefaultOutputDevice + virtual const BlackMisc::Audio::CAudioDeviceInfo &getDefaultOutputDevice() const override; + + //! \copydoc IAudioOutputDevice::getCurrentOutputDevice + virtual const BlackMisc::Audio::CAudioDeviceInfo &getCurrentOutputDevice() const override; + + //! \copydoc IAudioOutputDevice::setOutputDevice + virtual void setOutputDevice(const BlackMisc::Audio::CAudioDeviceInfo &device) override; + + //! \copydoc IAudioOutputDevice::setOutputVolume + virtual void setOutputVolume(int volume) override; + + //! \copydoc IAudioOutputDevice::getOutputVolume + virtual int getOutputVolume() const override; + + //! Get vatlib output device pointer + VatLocalOutputCodec getVatLocalOutputCodec() { return m_outputCodec.data(); } + + private: + + struct VatLocalOutputCodecDeleter + { + static inline void cleanup(VatConsumer_tag *obj) + { + if (obj) Vat_DestroyLocalOutputCodec(obj); + } + }; + + static void onOutputHardwareDeviceReceived(int deviceIndex, const char *hardwareName, void *cbVar); + + + BlackMisc::Audio::CAudioDeviceInfoList m_devices; /*!< in and output devices */ + BlackMisc::Audio::CAudioDeviceInfo m_currentDevice; + + VatAudioService m_audioService; + QScopedPointer m_outputCodec; + }; + } // ns +} // ns + +#endif // guard diff --git a/src/blackcore/vatsim/audiomixervatlib.cpp b/src/blackcore/vatsim/audiomixervatlib.cpp new file mode 100644 index 000000000..dd1cbae64 --- /dev/null +++ b/src/blackcore/vatsim/audiomixervatlib.cpp @@ -0,0 +1,37 @@ +/* Copyright (C) 2014 + * swift project community / contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackcore/vatsim/audiomixervatlib.h" + +namespace BlackCore +{ + namespace Vatsim + { + CAudioMixerVatlib::CAudioMixerVatlib(QObject *parent) + : IAudioMixer(parent) + { + m_audioMixer.reset(Vat_CreateAudioMixer()); + } + + void CAudioMixerVatlib::makeMixerConnection(InputPort inputPort, OutputPort outputPort) + { + Vat_MakeMixerConnection(m_audioMixer.data(), inputPort, outputPort, true); + } + + void CAudioMixerVatlib::removeMixerConnection(InputPort inputPort, OutputPort outputPort) + { + Vat_MakeMixerConnection(m_audioMixer.data(), inputPort, outputPort, false); + } + + bool CAudioMixerVatlib::hasMixerConnection(InputPort inputPort, OutputPort outputPort) + { + return Vat_HasMixerConnection(m_audioMixer.data(), inputPort, outputPort); + } + } // ns +} // ns diff --git a/src/blackcore/vatsim/audiomixervatlib.h b/src/blackcore/vatsim/audiomixervatlib.h new file mode 100644 index 000000000..31c925857 --- /dev/null +++ b/src/blackcore/vatsim/audiomixervatlib.h @@ -0,0 +1,72 @@ +/* Copyright (C) 2014 + * swift project community / contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKCORE_VATSIM_AUDIOMIXERVATLIB_H +#define BLACKCORE_VATSIM_AUDIOMIXERVATLIB_H + +#include "blackcore/audiomixer.h" +#include "blackcore/blackcoreexport.h" +#include "vatlib/vatlib.h" + +#include +#include +#include + +struct VatProducerConsumer_tag; + +namespace BlackCore +{ + namespace Vatsim + { + //! Interface to an audio mixer + class BLACKCORE_EXPORT CAudioMixerVatlib : public IAudioMixer + { + Q_OBJECT + + public: + + /*! + * \brief Default constructor with parent + * \param parent + */ + CAudioMixerVatlib(QObject *parent = nullptr); + + //! Virtual destructor. + virtual ~CAudioMixerVatlib() {} + + //! \copydoc IAudioMixer::makeMixerConnection + virtual void makeMixerConnection(InputPort inputPort, OutputPort outputPort) override; + + //! \copydoc IAudioMixer::removeMixerConnection + virtual void removeMixerConnection(InputPort inputPort, OutputPort outputPort) override; + + //! \copydoc IAudioMixer::hasMixerConnection + virtual bool hasMixerConnection(InputPort inputPort, OutputPort outputPort) override; + + //! Return the pointer to vatlib audio mixer + VatAudioMixer getVatAudioMixer() { return m_audioMixer.data(); } + + private: + + struct VatAudioMixerDeleter + { + static inline void cleanup(VatProducerConsumer_tag *obj) + { + if (obj) Vat_DestroyAudioMixer(obj); + } + }; + + QScopedPointer m_audioMixer; + }; + } // ns +} // ns + +#endif // guard diff --git a/src/blackcore/vatsim/networkvatlib.cpp b/src/blackcore/vatsim/networkvatlib.cpp new file mode 100644 index 000000000..7aff7ad75 --- /dev/null +++ b/src/blackcore/vatsim/networkvatlib.cpp @@ -0,0 +1,1110 @@ +/* Copyright (C) 2013 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \cond PRIVATE + +#include "blackconfig/buildconfig.h" +#include "blackcore/application.h" +#include "blackcore/vatsim/networkvatlib.h" +#include "blackmisc/aviation/aircraftsituation.h" +#include "blackmisc/aviation/altitude.h" +#include "blackmisc/aviation/comsystem.h" +#include "blackmisc/aviation/flightplan.h" +#include "blackmisc/aviation/heading.h" +#include "blackmisc/aviation/informationmessage.h" +#include "blackmisc/aviation/livery.h" +#include "blackmisc/aviation/transponder.h" +#include "blackmisc/compare.h" +#include "blackmisc/geo/coordinategeodetic.h" +#include "blackmisc/geo/latitude.h" +#include "blackmisc/geo/longitude.h" +#include "blackmisc/json.h" +#include "blackmisc/logmessage.h" +#include "blackmisc/network/network.h" +#include "blackmisc/network/textmessage.h" +#include "blackmisc/network/user.h" +#include "blackmisc/pq/angle.h" +#include "blackmisc/pq/frequency.h" +#include "blackmisc/pq/length.h" +#include "blackmisc/pq/speed.h" +#include "blackmisc/pq/time.h" +#include "blackmisc/pq/units.h" +#include "blackmisc/simulation/aircraftmodel.h" +#include "blackmisc/simulation/simulatedaircraft.h" +#include "blackmisc/statusmessage.h" +#include "vatlib/vatlib.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static_assert(! std::is_abstract::value, "Must implement all pure virtuals"); +static_assert(VAT_LIBVATLIB_VERSION == 905, "Wrong vatlib header installed"); + +using namespace BlackConfig; +using namespace BlackMisc; +using namespace BlackMisc::Aviation; +using namespace BlackMisc::Geo; +using namespace BlackMisc::Json; +using namespace BlackMisc::Network; +using namespace BlackMisc::PhysicalQuantities; +using namespace BlackMisc::Simulation; + +namespace BlackCore +{ + namespace Vatsim + { + CNetworkVatlib::CNetworkVatlib(IOwnAircraftProvider *ownAircraft, QObject *parent) + : INetwork(parent), COwnAircraftAware(ownAircraft), + m_loginMode(LoginNormal), + m_status(vatStatusIdle), + m_tokenBucket(10, CTime(5, CTimeUnit::s()), 1) + { + connect(this, &CNetworkVatlib::terminate, this, &INetwork::terminateConnection, Qt::QueuedConnection); + connect(this, &INetwork::customPacketReceived, this, &CNetworkVatlib::customPacketDispatcher); + + Q_ASSERT_X(Vat_GetVersion() == VAT_LIBVATLIB_VERSION, "swift.network", "Wrong vatlib shared library installed"); + + Vat_SetNetworkLogHandler(SeverityLevel::SeverityError, CNetworkVatlib::networkLogHandler); + + m_processingTimer.setObjectName(this->objectName().append(":m_processingTimer")); + m_positionUpdateTimer.setObjectName(this->objectName().append(":m_positionUpdateTimer")); + m_interimPositionUpdateTimer.setObjectName(this->objectName().append(":m_interimPositionUpdateTimer")); + + connect(&m_processingTimer, &QTimer::timeout, this, &CNetworkVatlib::process); + connect(&m_positionUpdateTimer, &QTimer::timeout, this, &CNetworkVatlib::sendPositionUpdate); + connect(&m_interimPositionUpdateTimer, &QTimer::timeout, this, &CNetworkVatlib::sendInterimPositions); + connect(&this->m_scheduledConfigUpdate, &QTimer::timeout, this, &CNetworkVatlib::sendIncrementalAircraftConfig); + m_scheduledConfigUpdate.setSingleShot(true); + + m_processingTimer.start(c_processingIntervalMsec); + } + + void CNetworkVatlib::initializeSession() + { + Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "attempted to reinitialize session while still connected"); + + int clientCapabilities = vatCapsModelDesc | vatCapsInterminPos | vatCapsAtcInfo | vatCapsAircraftConfig; + if (m_loginMode == LoginStealth) + { + clientCapabilities |= vatCapsStealth; + } + + static const QByteArray pk(this->getCmdLineFsdKey().isEmpty() ? CBuildConfig::vatsimPrivateKey().toLocal8Bit() : this->getCmdLineFsdKey().toLocal8Bit()); + m_net.reset(Vat_CreateNetworkSession(vatServerLegacyFsd, sApp->swiftVersionChar(), + CVersion::versionMajor(), CVersion::versionMinor(), + "None", CBuildConfig::vatsimClientId(), pk.constData(), + clientCapabilities)); + + Vat_SetStateChangeHandler(m_net.data(), onConnectionStatusChanged, this); + Vat_SetTextMessageHandler(m_net.data(), onTextMessageReceived, this); + Vat_SetRadioMessageHandler(m_net.data(), onRadioMessageReceived, this); + Vat_SetDeletePilotHandler(m_net.data(), onPilotDisconnected, this); + Vat_SetDeleteAtcHandler(m_net.data(), onControllerDisconnected, this); + Vat_SetPilotPositionHandler(m_net.data(), onPilotPositionUpdate, this); + Vat_SetInterimPilotPositionHandler(m_net.data(), onInterimPilotPositionUpdate, this); + Vat_SetAtcPositionHandler(m_net.data(), onAtcPositionUpdate, this); + Vat_SetKillHandler(m_net.data(), onKicked, this); + Vat_SetPongHandler(m_net.data(), onPong, this); + Vat_SetMetarResponseHandler(m_net.data(), onMetarReceived, this); + Vat_SetClientQueryHandler(m_net.data(), onInfoQueryRequestReceived, this); + Vat_SetClientQueryResponseHandler(m_net.data(), onInfoQueryReplyReceived, this); + Vat_SetInfoCAPSReplyHandler(m_net.data(), onCapabilitiesReplyReceived, this); + Vat_SetControllerAtisHandler(m_net.data(), onAtisReplyReceived, this); + Vat_SetFlightPlanHandler(m_net.data(), onFlightPlanReceived, this); + Vat_SetServerErrorHandler(m_net.data(), onErrorReceived, this); + Vat_SetAircraftInfoRequestHandler(m_net.data(), onPilotInfoRequestReceived, this); + Vat_SetAircraftInfoHandler(m_net.data(), onPilotInfoReceived, this); + Vat_SetCustomPilotPacketHandler(m_net.data(), onCustomPacketReceived, this); + Vat_SetAircraftConfigHandler(m_net.data(), onAircraftConfigReceived, this); + } + + CNetworkVatlib::~CNetworkVatlib() + { + Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "CNetworkVatlib destroyed while still connected."); + } + + void CNetworkVatlib::process() + { + if (!m_net) { return; } + sendIncrementalAircraftConfig(); + Vat_ExecuteNetworkTasks(m_net.data()); + } + + void CNetworkVatlib::sendPositionUpdate() + { + if (!m_net) { return; } + + if (isConnected()) + { + CSimulatedAircraft myAircraft(getOwnAircraft()); + if (this->m_loginMode == LoginAsObserver) + { + // Observer + VatAtcPosition pos; + pos.facility = vatFacilityTypeUnknown; + pos.visibleRange = 10; // NM + pos.latitude = myAircraft.latitude().value(CAngleUnit::deg()); + pos.longitude = myAircraft.longitude().value(CAngleUnit::deg()); + pos.elevation = 0; + pos.rating = vatAtcRatingObserver; + pos.frequency = 199998; + Vat_SendATCUpdate(m_net.data(), &pos); + } + else + { + // Normal / Stealth mode + VatPilotPosition pos; + // TODO: we need to distinguish true and pressure altitude + pos.altitudePressure = myAircraft.getAltitude().value(CLengthUnit::ft()); + pos.altitudeTrue = myAircraft.getAltitude().value(CLengthUnit::ft()); + pos.heading = myAircraft.getHeading().value(CAngleUnit::deg()); + pos.pitch = myAircraft.getPitch().value(CAngleUnit::deg()); + pos.bank = myAircraft.getBank().value(CAngleUnit::deg()); + pos.latitude = myAircraft.latitude().value(CAngleUnit::deg()); + pos.longitude = myAircraft.longitude().value(CAngleUnit::deg()); + pos.groundSpeed = myAircraft.getGroundSpeed().value(CSpeedUnit::kts()); + pos.rating = vatPilotRatingUnknown; + pos.transponderCode = static_cast(myAircraft.getTransponderCode()); + pos.transponderMode = vatTransponderModeStandby; + switch (myAircraft.getTransponderMode()) + { + case CTransponder::ModeC: pos.transponderMode = vatTransponderModeCharlie; break; + case CTransponder::StateIdent: pos.transponderMode = vatTransponderModeIdent; break; + default: pos.transponderMode = vatTransponderModeStandby; break; + } + Vat_SendPilotUpdate(m_net.data(), &pos); + } + } + } + + void CNetworkVatlib::sendInterimPositions() + { + if (!m_net) { return; } + + if (isConnected()) + { + CSimulatedAircraft myAircraft(getOwnAircraft()); + if (this->m_loginMode == LoginNormal) + { + VatInterimPilotPosition pos; + pos.altitudeTrue = myAircraft.getAltitude().value(CLengthUnit::ft()); + pos.heading = myAircraft.getHeading().value(CAngleUnit::deg()); + pos.pitch = myAircraft.getPitch().value(CAngleUnit::deg()); + pos.bank = myAircraft.getBank().value(CAngleUnit::deg()); + pos.latitude = myAircraft.latitude().value(CAngleUnit::deg()); + pos.longitude = myAircraft.longitude().value(CAngleUnit::deg()); + + for (const auto &receiver : as_const(m_interimPositionReceivers)) + { + Vat_SendInterimPilotUpdate(m_net.data(), toFSD(receiver), &pos); + } + } + } + } + + //! Convert vatlib status code to INetwork::ConnectionStatus + INetwork::ConnectionStatus convertConnectionStatus(VatConnectionStatus status) + { + switch (status) + { + case vatStatusIdle: return INetwork::Disconnected; + case vatStatusConnecting: return INetwork::Connecting; + case vatStatusConnected: return INetwork::Connected; + case vatStatusDisconnected: return INetwork::Disconnected; + case vatStatusDisconnecting: return INetwork::Disconnecting; + case vatStatusError: return INetwork::DisconnectedError; + } + qFatal("unrecognised connection status"); + return INetwork::DisconnectedError; + } + + void CNetworkVatlib::changeConnectionStatus(VatConnectionStatus status) + { + if (m_status != status) + { + qSwap(m_status, status); + emit connectionStatusChanged(convertConnectionStatus(status), convertConnectionStatus(m_status)); + + if (isDisconnected()) + { + stopPositionTimers(); + } + } + } + + QByteArray CNetworkVatlib::toFSD(QString qstr) const + { + Q_ASSERT_X(m_fsdTextCodec, Q_FUNC_INFO, "Missing codec"); + return m_fsdTextCodec->fromUnicode(qstr); + } + + QByteArray CNetworkVatlib::toFSD(const BlackMisc::Aviation::CCallsign &callsign) const + { + return toFSD(callsign.getStringAsSet()); + } + + std::function CNetworkVatlib::toFSD(QStringList qstrList) const + { + QVector bytesVec; + for (auto i = qstrList.cbegin(); i != qstrList.cend(); ++i) + { + bytesVec.push_back(toFSD(*i)); + } + + return [ cstrVec = QVector(), bytesVec = std::move(bytesVec) ]() mutable + { + Q_ASSERT(cstrVec.isEmpty()); + for (auto i = bytesVec.cbegin(); i != bytesVec.cend(); ++i) + { + cstrVec.push_back(i->constData()); + } + return const_cast(cstrVec.constData()); + }; + } + + QString CNetworkVatlib::fromFSD(const char *cstr) const + { + Q_ASSERT_X(m_fsdTextCodec, Q_FUNC_INFO, "Missing codec"); + return m_fsdTextCodec->toUnicode(cstr); + } + + QStringList CNetworkVatlib::fromFSD(const char **cstrArray, int size) const + { + QStringList qstrList; + for (int i = 0; i < size; ++i) + { + qstrList.push_back(fromFSD(cstrArray[i])); + } + return qstrList; + } + + bool CNetworkVatlib::isInterimPositionUpdateEnabledForServer() const + { + const CFsdSetup::SendReceiveDetails d = m_server.getFsdSetup().getSendReceiveDetails(); + return (d & CFsdSetup::SendIterimPositions) || (d & CFsdSetup::ReceiveInterimPositions); + } + + void CNetworkVatlib::startPositionTimers() + { + m_positionUpdateTimer.start(c_updatePostionIntervalMsec); + if (isInterimPositionUpdateEnabledForServer()) { m_interimPositionUpdateTimer.start(c_updateInterimPostionIntervalMsec); } + } + + void CNetworkVatlib::stopPositionTimers() + { + m_positionUpdateTimer.stop(); + m_interimPositionUpdateTimer.stop(); + } + + QString CNetworkVatlib::convertToUnicodeEscaped(const QString &str) + { + QString escaped; + for (const auto &ch : str) + { + ushort code = ch.unicode(); + if (code < 0x80) + { + escaped += ch; + } + else + { + escaped += "\\u"; + escaped += QString::number(code, 16).rightJustified(4, '0'); + } + } + return escaped; + } + + VatSimType CNetworkVatlib::convertToSimType(CSimulatorPluginInfo &simInfo) + { + /* TODO Define recognized simulators somewhere */ + if (simInfo.getSimulator() == "fs9" || simInfo.getSimulator() == "fsx") + { + return vatSimTypeMSCFS; + } + else if (simInfo.getSimulator() == "xplane") + { + return vatSimTypeXPLANE; + } + else + { + return vatSimTypeUnknown; + } + } + + /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ + /********************************** INetwork functions ************************************/ + /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ + + void CNetworkVatlib::presetServer(const CServer &server) + { + Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "Can't change server details while still connected"); + m_server = server; + const QString codecName(server.getFsdSetup().getTextCodec()); + Q_ASSERT_X(!codecName.isEmpty(), Q_FUNC_INFO, "Missing code name"); + this->m_fsdTextCodec = QTextCodec::codecForName(codecName.toLocal8Bit()); + if (!this->m_fsdTextCodec) { this->m_fsdTextCodec = QTextCodec::codecForName("latin1"); } + } + + void CNetworkVatlib::presetSimulatorInfo(const CSimulatorPluginInfo &simInfo) + { + Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "Can't change server details while still connected"); + m_simulatorInfo = simInfo; + } + + void CNetworkVatlib::presetCallsign(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "Can't change callsign while still connected"); + m_ownCallsign = callsign; + updateOwnCallsign(callsign); + } + + void CNetworkVatlib::presetIcaoCodes(const BlackMisc::Simulation::CSimulatedAircraft &ownAircraft) + { + Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "Can't change ICAO codes while still connected"); + m_ownAircraftIcaoCode = ownAircraft.getAircraftIcaoCode(); + m_ownAirlineIcaoCode = ownAircraft.getAirlineIcaoCode(); + m_ownLiveryDescription = ownAircraft.getLivery().getDescription(); + updateOwnIcaoCodes(m_ownAircraftIcaoCode, m_ownAirlineIcaoCode); // \todo livery? + } + + void CNetworkVatlib::presetLoginMode(LoginMode mode) + { + Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "Can't change login mode while still connected"); + m_loginMode = mode; + m_net.reset(nullptr); + } + + void CNetworkVatlib::initiateConnection() + { + Q_ASSERT_X(isDisconnected(), Q_FUNC_INFO, "Can't connect while still connected"); + if (!m_net) { initializeSession(); } + QByteArray callsign = toFSD(m_loginMode == LoginAsObserver ? + m_ownCallsign.getAsObserverCallsignString() : + m_ownCallsign.asString()); + QByteArray name = toFSD(m_server.getUser().getRealName()); + + if (this->m_loginMode == LoginAsObserver) + { + // Observer mode + VatAtcConnection info; + info.name = name.data(); + info.rating = vatAtcRatingObserver; + info.callsign = callsign.data(); + Vat_SpecifyATCLogon(m_net.data(), toFSD(m_server.getAddress()), m_server.getPort(), + toFSD(m_server.getUser().getId()), + toFSD(m_server.getUser().getPassword()), + &info); + } + else + { + // normal scenario, also used in STEALTH + VatPilotConnection info; + info.callsign = callsign.data(); + info.name = name.data(); + info.rating = vatPilotRatingStudent; //TODO + info.simType = convertToSimType(m_simulatorInfo); + Vat_SpecifyPilotLogon(m_net.data(), toFSD(m_server.getAddress()), m_server.getPort(), + toFSD(m_server.getUser().getId()), + toFSD(m_server.getUser().getPassword()), + &info); + } + + Vat_Logon(m_net.data()); + + startPositionTimers(); + } + + void CNetworkVatlib::terminateConnection() + { + stopPositionTimers(); + if (m_net && isConnected()) + { + Vat_Logoff(m_net.data()); + } + } + + void CNetworkVatlib::sendTextMessages(const BlackMisc::Network::CTextMessageList &messages) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + + if (messages.isEmpty()) { return; } + CTextMessageList privateMessages = messages.getPrivateMessages(); + privateMessages.markAsSent(); + for (const auto &message : privateMessages) + { + if (message.getRecipientCallsign().isEmpty()) { continue; } + Vat_SendTextMessage(m_net.data(), toFSD(message.getRecipientCallsign()), toFSD(message.getMessage())); + emit textMessageSent(message); + } + + CTextMessageList radioMessages = messages.getRadioMessages(); + radioMessages.markAsSent(); + for (const auto &message : radioMessages) + { + // I could send the same message to n frequencies in one step + // if this is really required, I need to group by message + // currently I send individual messages + QVector freqsVec; + freqsVec.push_back(message.getFrequency().valueRounded(CFrequencyUnit::kHz(), 0)); + Vat_SendRadioMessage(m_net.data(), freqsVec.data(), freqsVec.size(), toFSD(message.getMessage())); + emit textMessageSent(message); + } + } + + void CNetworkVatlib::sendCustomPacket(const BlackMisc::Aviation::CCallsign &callsign, const QString &packetId, const QStringList &data) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_SendCustomPilotPacket(m_net.data(), toFSD(callsign), toFSD(packetId), toFSD(data)(), data.size()); + } + + void CNetworkVatlib::sendIpQuery() + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_SendClientQuery(m_net.data(), vatClientQueryIP, nullptr); + } + + void CNetworkVatlib::sendFrequencyQuery(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_SendClientQuery(m_net.data(), vatClientQueryFreq, toFSD(callsign)); + } + + void CNetworkVatlib::sendUserInfoQuery(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_SendClientQuery(m_net.data(), vatClientQueryInfo, toFSD(callsign)); + } + + void CNetworkVatlib::setInterimPositionReceivers(const CCallsignSet &receivers) + { + m_interimPositionReceivers = receivers; + } + + void CNetworkVatlib::sendServerQuery(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_SendClientQuery(m_net.data(), vatClientQueryServer, toFSD(callsign)); + } + + void CNetworkVatlib::sendAtcQuery(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_SendClientQuery(m_net.data(), vatClientQueryAtc, toFSD(callsign)); + } + + void CNetworkVatlib::sendAtisQuery(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_SendClientQuery(m_net.data(), vatClientQueryAtis, toFSD(callsign)); + } + + void CNetworkVatlib::sendFlightPlan(const CFlightPlan &flightPlan) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + + VatFlightPlan vatlibFP; + QString route = QString(flightPlan.getRoute()).replace(" ", "."); + QString remarks = QString(flightPlan.getRemarks()).replace(":", ";").trimmed(); + QString alt = flightPlan.getCruiseAltitude().isFlightLevel() ? + flightPlan.getCruiseAltitude().toQString() : + flightPlan.getCruiseAltitude().valueRoundedWithUnit(0); + alt = alt.remove('.').remove(','); // remove any separators + + QByteArray acTypeTemp, altAptTemp, cruiseAltTemp, depAptTemp, destAptTemp, routeTemp, remarksTemp; + vatlibFP.aircraftType = acTypeTemp = toFSD(flightPlan.getEquipmentIcao()); + vatlibFP.alternateAirport = altAptTemp = toFSD(flightPlan.getAlternateAirportIcao().asString()); + vatlibFP.cruiseAltitude = cruiseAltTemp = toFSD(alt); + vatlibFP.departAirport = depAptTemp = toFSD(flightPlan.getOriginAirportIcao().asString()); + vatlibFP.departTimeActual = flightPlan.getTakeoffTimeActual().toUTC().toString("hhmm").toInt(); + vatlibFP.departTime = flightPlan.getTakeoffTimePlanned().toUTC().toString("hhmm").toInt(); + vatlibFP.destAirport = destAptTemp = toFSD(flightPlan.getDestinationAirportIcao().asString()); + + QList timeParts = flightPlan.getEnrouteTime().getHrsMinSecParts(); + vatlibFP.enrouteHrs = timeParts[CTime::Hours]; + vatlibFP.enrouteMins = timeParts[CTime::Minutes]; + + timeParts = flightPlan.getFuelTime().getHrsMinSecParts(); + vatlibFP.fuelHrs = timeParts[CTime::Hours]; + vatlibFP.fuelMins = timeParts[CTime::Minutes]; + vatlibFP.remarks = remarksTemp = toFSD(remarks); + vatlibFP.route = routeTemp = toFSD(route); + vatlibFP.trueCruisingSpeed = flightPlan.getCruiseTrueAirspeed().valueRounded(CSpeedUnit::kts()); + switch (flightPlan.getFlightRules()) + { + default: + case CFlightPlan::IFR: vatlibFP.flightType = vatFlightTypeIFR; break; + case CFlightPlan::VFR: vatlibFP.flightType = vatFlightTypeVFR; break; + case CFlightPlan::SVFR: vatlibFP.flightType = vatFlightTypeSVFR; break; + case CFlightPlan::DVFR: vatlibFP.flightType = vatFlightTypeDVFR; break; + } + Vat_SendFlightPlan(m_net.data(), &vatlibFP); + } + + void CNetworkVatlib::sendFlightPlanQuery(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_SendClientQuery(m_net.data(), vatClientQueryFP, toFSD(callsign)); + } + + void CNetworkVatlib::sendRealNameQuery(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_SendClientQuery(m_net.data(), vatClientQueryName, toFSD(callsign)); + } + + void CNetworkVatlib::sendCapabilitiesQuery(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_SendClientQuery(m_net.data(), vatClientQueryCaps, toFSD(callsign)); + } + + void CNetworkVatlib::replyToFrequencyQuery(const BlackMisc::Aviation::CCallsign &callsign) // private + { + QStringList response { QString::number(getOwnAircraft().getCom1System().getFrequencyActive().value(CFrequencyUnit::MHz()), 'f', 3)}; + Vat_SendClientQueryResponse(m_net.data(), vatClientQueryFreq, toFSD(callsign), toFSD(response)(), response.size()); + } + + void CNetworkVatlib::replyToNameQuery(const BlackMisc::Aviation::CCallsign &callsign) // private + { + QStringList response { m_server.getUser().getRealName(), "" }; + Vat_SendClientQueryResponse(m_net.data(), vatClientQueryName, toFSD(callsign), toFSD(response)(), response.size()); + } + + void CNetworkVatlib::replyToConfigQuery(const CCallsign &callsign) + { + QJsonObject config = getOwnAircraftParts().toJson(); + config.insert("is_full_data", true); + QString data = QJsonDocument(QJsonObject { { "config", config } }).toJson(QJsonDocument::Compact); + data = convertToUnicodeEscaped(data); + Vat_SendAircraftConfig(m_net.data(), toFSD(callsign), toFSD(data)); + } + + void CNetworkVatlib::sendIcaoCodesQuery(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_RequestAircraftInfo(m_net.data(), toFSD(callsign)); + } + + void CNetworkVatlib::sendAircraftInfo(const BlackMisc::Aviation::CCallsign &callsign) // private + { + const QByteArray acTypeICAObytes = toFSD(m_ownAircraftIcaoCode.getDesignator()); + const QByteArray airlineICAObytes = toFSD(m_ownAirlineIcaoCode.getDesignator()); + const QByteArray liverybytes = toFSD(m_ownLiveryDescription); + + VatAircraftInfo aircraftInfo {acTypeICAObytes, airlineICAObytes, liverybytes}; + Vat_SendModernPlaneInfo(m_net.data(), toFSD(callsign), &aircraftInfo); + } + + void CNetworkVatlib::sendIncrementalAircraftConfig() + { + if (!isConnected()) { return; } + CAircraftParts currentParts(getOwnAircraftParts()); + + // If it hasn't changed, return + if (m_sentAircraftConfig == currentParts) { return; } + + if (!m_tokenBucket.tryConsume()) + { + // If timer is not yet active, start it + if (!m_scheduledConfigUpdate.isActive()) m_scheduledConfigUpdate.start(1000); + return; + } + + // Method could have been triggered by another change in aircraft config + // so a previous update might still be scheduled. Stop it. + if (m_scheduledConfigUpdate.isActive()) m_scheduledConfigUpdate.stop(); + QJsonObject previousConfig = m_sentAircraftConfig.toJson(); + QJsonObject currentConfig = currentParts.toJson(); + QJsonObject incrementalConfig = getIncrementalObject(previousConfig, currentConfig); + broadcastAircraftConfig(incrementalConfig); + m_sentAircraftConfig = currentParts; + } + + void CNetworkVatlib::sendPing(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_SendPing(m_net.data(), toFSD(callsign)); + } + + void CNetworkVatlib::sendMetarQuery(const BlackMisc::Aviation::CAirportIcaoCode &airportIcao) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + Vat_RequestMetar(m_net.data(), toFSD(airportIcao.asString())); + } + + const QList &CNetworkVatlib::getCmdLineOptions() + { + static const QList e; + static const QList opts + { + QCommandLineOption({ "key", "fsdkey" }, + QCoreApplication::translate("application", "Key for FSD"), + "fsdkey") + }; + + // only in not officially shipped versions + return (CBuildConfig::isShippedVersion() && !CBuildConfig::isBetaTest()) ? e : opts; + } + + QString CNetworkVatlib::getCmdLineFsdKey() const + { + return sApp->getParserValue("fsdkey").toLower(); + } + + void CNetworkVatlib::sendCustomFsinnQuery(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + CSimulatedAircraft myAircraft(getOwnAircraft()); + QString modelString = myAircraft.getModel().getModelString(); + if (modelString.isEmpty()) { modelString = defaultModelString(); } + + QStringList data { { "0" }, + myAircraft.getAirlineIcaoCodeDesignator(), + myAircraft.getAircraftIcaoCodeDesignator(), + { "" }, { "" }, { "" }, { "" }, + myAircraft.getAircraftIcaoCombinedType(), + modelString + }; + sendCustomPacket(callsign, "FSIPIR", data); + } + + void CNetworkVatlib::sendCustomFsinnReponse(const BlackMisc::Aviation::CCallsign &callsign) + { + Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected"); + CSimulatedAircraft myAircraft(getOwnAircraft()); + QString modelString = myAircraft.getModel().getModelString(); + if (modelString.isEmpty()) { modelString = defaultModelString(); } + + QStringList data { { "0" }, + myAircraft.getAirlineIcaoCodeDesignator(), + myAircraft.getAircraftIcaoCodeDesignator(), + { "" }, { "" }, { "" }, { "" }, + myAircraft.getAircraftIcaoCombinedType(), + modelString + }; + sendCustomPacket(callsign, "FSIPI", data); + } + + void CNetworkVatlib::broadcastAircraftConfig(const QJsonObject &config) + { + QString data = QJsonDocument(QJsonObject { { "config", config } }).toJson(QJsonDocument::Compact); + data = convertToUnicodeEscaped(data); + Vat_SendAircraftConfigBroadcast(m_net.data(), toFSD(data)); + } + + void CNetworkVatlib::sendAircraftConfigQuery(const CCallsign &callsign) + { + QJsonDocument doc(JsonPackets::aircraftConfigRequest()); + QString data { doc.toJson(QJsonDocument::Compact) }; + Vat_SendAircraftConfig(m_net.data(), toFSD(callsign), toFSD(data)); + } + + /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ + /********************************** shimlib callbacks ************************************/ + /********************************** * * * * * * * * * * * * * * * * * * * ************************************/ + +//! Cast void* to a pointer of CNetworkVatlib + CNetworkVatlib *cbvar_cast(void *cbvar) + { + return static_cast(cbvar); + } + + void CNetworkVatlib::onConnectionStatusChanged(VatSessionID, VatConnectionStatus, VatConnectionStatus newStatus, void *cbvar) + { + if (newStatus == vatStatusError) + { + cbvar_cast(cbvar)->changeConnectionStatus(newStatus); + } + else + { + cbvar_cast(cbvar)->changeConnectionStatus(newStatus); + } + } + + void CNetworkVatlib::onTextMessageReceived(VatSessionID, const char *from, const char *to, const char *msg, void *cbvar) + { + BlackMisc::Network::CTextMessageList messages(cbvar_cast(cbvar)->fromFSD(msg), CCallsign(cbvar_cast(cbvar)->fromFSD(from)), CCallsign(cbvar_cast(cbvar)->fromFSD(to))); + emit cbvar_cast(cbvar)->textMessagesReceived(messages); + } + + void CNetworkVatlib::onRadioMessageReceived(VatSessionID, const char *from, int numFreq, int *freqList, const char *msg, void *cbvar) + { + int com1 = cbvar_cast(cbvar)->getOwnAircraft().getCom1System().getFrequencyActive().valueInteger(CFrequencyUnit::kHz()); + int com2 = cbvar_cast(cbvar)->getOwnAircraft().getCom2System().getFrequencyActive().valueInteger(CFrequencyUnit::kHz()); + QList frequencies; + for (int i = 0; i < numFreq; ++i) + { + if (freqList[i] == com1 || freqList[i] == com2) + { + frequencies.push_back(CFrequency(freqList[i], CFrequencyUnit::kHz())); + } + } + if (frequencies.isEmpty()) { return; } + BlackMisc::Network::CTextMessageList messages(cbvar_cast(cbvar)->fromFSD(msg), frequencies, CCallsign(cbvar_cast(cbvar)->fromFSD(from))); + emit cbvar_cast(cbvar)->textMessagesReceived(messages); + } + + void CNetworkVatlib::onPilotDisconnected(VatSessionID, const char *callsign, void *cbvar) + { + emit cbvar_cast(cbvar)->pilotDisconnected(CCallsign(cbvar_cast(cbvar)->fromFSD(callsign), CCallsign::Aircraft)); + } + + void CNetworkVatlib::onControllerDisconnected(VatSessionID, const char *callsign, void *cbvar) + { + emit cbvar_cast(cbvar)->atcDisconnected(CCallsign(cbvar_cast(cbvar)->fromFSD(callsign), CCallsign::Atc)); + } + + void CNetworkVatlib::onPilotPositionUpdate(VatSessionID, const char *callsignChar , const VatPilotPosition *position, void *cbvar) + { + const CCallsign callsign(callsignChar, CCallsign::Aircraft); + CAircraftSituation situation( + callsign, + CCoordinateGeodetic(position->latitude, position->longitude, 0.0), + CAltitude(position->altitudeTrue, CAltitude::MeanSeaLevel, CLengthUnit::ft()), + CHeading(position->heading, CHeading::True, CAngleUnit::deg()), + CAngle(position->pitch, CAngleUnit::deg()), + CAngle(position->bank, CAngleUnit::deg()), + CSpeed(position->groundSpeed, CSpeedUnit::kts()) + ); + situation.setCurrentUtcTime(); + situation.setTimeOffsetMs(6000); + + QString transponderName("transponder "); + transponderName.append(callsign.asString()); + CTransponder::TransponderMode mode = CTransponder::StateStandby; + switch (position->transponderMode) + { + case vatTransponderModeCharlie: + mode = CTransponder::ModeC; + break; + case vatTransponderModeStandby: + mode = CTransponder::StateStandby; + break; + case vatTransponderModeIdent: + mode = CTransponder::StateIdent; + break; + default: + mode = CTransponder::StateStandby; + break; + } + + // I did have a situation where I got wrong transponder codes (KB) + // So I now check for a valid code in order to detect such codes + CTransponder transponder; + if (CTransponder::isValidTransponderCode(position->transponderCode)) + { + transponder = CTransponder(position->transponderCode, mode); + } + else + { + CLogMessage(static_cast(nullptr)).warning("Wrong transponder code %1 for %2") << position->transponderCode << callsign; + + // default + transponder = CTransponder(7000, mode); + } + emit cbvar_cast(cbvar)->aircraftPositionUpdate(situation, transponder); + } + + void CNetworkVatlib::onAircraftConfigReceived(VatSessionID, const char *callsign, const char *aircraftConfig, void *cbvar) + { + QByteArray json = cbvar_cast(cbvar)->fromFSD(aircraftConfig).toUtf8(); + QJsonParseError parserError; + QJsonDocument doc = QJsonDocument::fromJson(json, &parserError); + CCallsign cs(callsign, CCallsign::Aircraft); + + if (parserError.error != QJsonParseError::NoError) + { + CLogMessage(static_cast(nullptr)).warning("Failed to parse aircraft config packet: %1") << parserError.errorString(); + } + + QJsonObject packet = doc.object(); + + if (packet == JsonPackets::aircraftConfigRequest()) + { + cbvar_cast(cbvar)->replyToConfigQuery(cbvar_cast(cbvar)->fromFSD(callsign)); + return; + } + + QJsonObject config = doc.object().value("config").toObject(); + if (config.empty()) return; + + bool isFull = config.take("is_full_data").toBool(false); + emit cbvar_cast(cbvar)->aircraftConfigPacketReceived(cs, config, isFull); + } + + void CNetworkVatlib::onInterimPilotPositionUpdate(VatSessionID, const char *sender, const VatInterimPilotPosition *position, void *cbvar) + { + CAircraftSituation situation( + CCallsign(sender), + CCoordinateGeodetic(position->latitude, position->longitude, 0.0), + CAltitude(position->altitudeTrue, CAltitude::MeanSeaLevel, CLengthUnit::ft()), + CHeading(position->heading, CHeading::True, CAngleUnit::deg()), + CAngle(position->pitch, CAngleUnit::deg()), + CAngle(position->bank, CAngleUnit::deg()), + // There is no speed information in a interim packet + CSpeed(0.0, CSpeedUnit::kts()) + ); + situation.setCurrentUtcTime(); + situation.setTimeOffsetMs(2000); + situation.setInterimFlag(true); + + emit cbvar_cast(cbvar)->aircraftInterimPositionUpdate(situation); + } + + void CNetworkVatlib::onAtcPositionUpdate(VatSessionID, const char *callsign, const VatAtcPosition *pos, void *cbvar) + { + int frequencyKHz = pos->frequency; + CFrequency freq(frequencyKHz, CFrequencyUnit::kHz()); + freq.switchUnit(CFrequencyUnit::MHz()); // we would not need to bother, but this makes it easier to identify + emit cbvar_cast(cbvar)->atcPositionUpdate( + CCallsign(cbvar_cast(cbvar)->fromFSD(callsign), CCallsign::Atc), freq, + CCoordinateGeodetic(pos->latitude, pos->longitude, 0), CLength(pos->visibleRange, CLengthUnit::NM())); + } + + void CNetworkVatlib::onKicked(VatSessionID, const char *reason, void *cbvar) + { + emit cbvar_cast(cbvar)->kicked(cbvar_cast(cbvar)->fromFSD(reason)); + } + + void CNetworkVatlib::onPong(VatSessionID, const char *sender, double elapsedTime, void *cbvar) + { + emit cbvar_cast(cbvar)->pongReceived(cbvar_cast(cbvar)->fromFSD(sender), CTime(elapsedTime, CTimeUnit::ms())); + } + + void CNetworkVatlib::onCustomPacketReceived(VatSessionID, const char *callsign, const char *packetId, const char **data, int dataSize, void *cbvar) + { + emit cbvar_cast(cbvar)->customPacketReceived(cbvar_cast(cbvar)->fromFSD(callsign), cbvar_cast(cbvar)->fromFSD(packetId), cbvar_cast(cbvar)->fromFSD(data, dataSize)); + } + + void CNetworkVatlib::customPacketDispatcher(const BlackMisc::Aviation::CCallsign &callsign, const QString &packetId, const QStringList &data) + { + if (packetId.compare("FSIPI", Qt::CaseInsensitive) == 0) + { + if (data.size() < 9) + { + CLogMessage(this).warning("Malformed FSIPI packet"); + } + else + { + // It doesn't matter whether it was a query or response. The information + // is the same for both. + emit customFSinnPacketReceived(callsign, data[1], data[2], data[7], data[8]); + } + } + else if (packetId.compare("FSIPIR", Qt::CaseInsensitive) == 0) + { + if (data.size() < 9) + { + CLogMessage(this).warning("Malformed FSIPIR packet"); + } + else + { + sendCustomFsinnReponse(callsign); + // It doesn't matter whether it was a query or response. The information + // is the same for both. + emit customFSinnPacketReceived(callsign, data[1], data[2], data[7], data[8]); + } + } + } + + void CNetworkVatlib::onMetarReceived(VatSessionID, const char *data, void *cbvar) + { + emit cbvar_cast(cbvar)->metarReplyReceived(cbvar_cast(cbvar)->fromFSD(data)); + } + + void CNetworkVatlib::onInfoQueryRequestReceived(VatSessionID, const char *callsignString, VatClientQueryType type, const char *, void *cbvar) + { + auto timer = new QTimer(cbvar_cast(cbvar)); + timer->setSingleShot(true); + timer->start(0); + + BlackMisc::Aviation::CCallsign callsign(callsignString); + switch (type) + { + case vatClientQueryFreq: + connect(timer, &QTimer::timeout, [ = ]() { cbvar_cast(cbvar)->replyToFrequencyQuery(callsign); }); + break; + case vatClientQueryName: + connect(timer, &QTimer::timeout, [ = ]() { cbvar_cast(cbvar)->replyToNameQuery(callsign); }); + break; + default: + break; + } + } + + void CNetworkVatlib::onInfoQueryReplyReceived(VatSessionID, const char *callsign, VatClientQueryType type, const char *data, const char *data2, void *cbvar) + { + switch (type) + { + case vatClientQueryFreq: emit cbvar_cast(cbvar)->frequencyReplyReceived(cbvar_cast(cbvar)->fromFSD(callsign), CFrequency(cbvar_cast(cbvar)->fromFSD(data).toFloat(), CFrequencyUnit::MHz())); break; + case vatClientQueryServer: emit cbvar_cast(cbvar)->serverReplyReceived(cbvar_cast(cbvar)->fromFSD(callsign), cbvar_cast(cbvar)->fromFSD(data)); break; + case vatClientQueryAtc: emit cbvar_cast(cbvar)->atcReplyReceived(CCallsign(cbvar_cast(cbvar)->fromFSD(data2), CCallsign::Atc), *data == 'Y'); break; + case vatClientQueryName: emit cbvar_cast(cbvar)->realNameReplyReceived(cbvar_cast(cbvar)->fromFSD(callsign), cbvar_cast(cbvar)->fromFSD(data)); break; + case vatClientQueryIP: emit cbvar_cast(cbvar)->ipReplyReceived(cbvar_cast(cbvar)->fromFSD(data)); break; + default: break; + } + } + + void CNetworkVatlib::onCapabilitiesReplyReceived(VatSessionID, const char *callsign, int capabilityFlags, void *cbvar) + { + int flags = 0; + if (capabilityFlags & vatCapsAtcInfo) { flags |= AcceptsAtisResponses; } + if (capabilityFlags & vatCapsInterminPos) { flags |= SupportsInterimPosUpdates; } + if (capabilityFlags & vatCapsModelDesc) { flags |= SupportsIcaoCodes; } + if (capabilityFlags & vatCapsAircraftConfig) { flags |= SupportsAircraftConfigs; } + emit cbvar_cast(cbvar)->capabilitiesReplyReceived(cbvar_cast(cbvar)->fromFSD(callsign), flags); + } + + void CNetworkVatlib::onAtisReplyReceived(VatSessionID, const char *callsign, const VatControllerAtis *atis, void *cbvar) + { + emit cbvar_cast(cbvar)->atisVoiceRoomReplyReceived(cbvar_cast(cbvar)->fromFSD(callsign), cbvar_cast(cbvar)->fromFSD(atis->voiceRoom)); + emit cbvar_cast(cbvar)->atisLogoffTimeReplyReceived(cbvar_cast(cbvar)->fromFSD(callsign), cbvar_cast(cbvar)->fromFSD(atis->zuluLogoff)); + + CInformationMessage atisMessage; + atisMessage.setType(CInformationMessage::ATIS); + for (int i = 0; i < atis->textLineCount; ++i) + { + const QString fixed = cbvar_cast(cbvar)->fromFSD(atis->textLines[i]).trimmed(); + if (! fixed.isEmpty()) + { + // detect the stupid z1, z2, z3 placeholders + // TODO: Anything better as this stupid code here? + const QString test = fixed.toLower().remove(QRegExp("[\\n\\t\\r]")); + if (test == "z") return; + if (test.startsWith("z") && test.length() == 2) return; // z1, z2, .. + if (test.length() == 1) return; // sometimes just z + + // append + if (!atisMessage.isEmpty()) atisMessage.appendMessage("\n"); + atisMessage.appendMessage(fixed); + } + } + + emit cbvar_cast(cbvar)->atisReplyReceived(CCallsign(cbvar_cast(cbvar)->fromFSD(callsign), CCallsign::Atc), atisMessage); + } + + void CNetworkVatlib::onFlightPlanReceived(VatSessionID, const char *callsign, const VatFlightPlan *fp, void *cbvar) + { + BlackMisc::Aviation::CFlightPlan::FlightRules rules = BlackMisc::Aviation::CFlightPlan::VFR; + switch (fp->flightType) + { + default: + case vatFlightTypeVFR: rules = BlackMisc::Aviation::CFlightPlan::VFR; break; + case vatFlightTypeIFR: rules = BlackMisc::Aviation::CFlightPlan::IFR; break; + // case Cvatlib_Network::fpRuleType_SVFR: rules = BlackMisc::Aviation::CFlightPlan::SVFR; break; + } + + auto cruiseAltString = cbvar_cast(cbvar)->fromFSD(fp->cruiseAltitude); + static const QRegExp withUnit("\\D+"); + if (!cruiseAltString.isEmpty() && withUnit.indexIn(cruiseAltString) < 0) + { + cruiseAltString += "ft"; + } + BlackMisc::Aviation::CAltitude cruiseAlt; + cruiseAlt.parseFromString(cruiseAltString); + + QString depTimePlanned = QString("0000").append(QString::number(fp->departTime)).right(4); + QString depTimeActual = QString("0000").append(QString::number(fp->departTimeActual)).right(4); + + BlackMisc::Aviation::CFlightPlan flightPlan( + cbvar_cast(cbvar)->fromFSD(fp->aircraftType), + cbvar_cast(cbvar)->fromFSD(fp->departAirport), + cbvar_cast(cbvar)->fromFSD(fp->destAirport), + cbvar_cast(cbvar)->fromFSD(fp->alternateAirport), + QDateTime::fromString(depTimePlanned, "hhmm"), + QDateTime::fromString(depTimeActual, "hhmm"), + BlackMisc::PhysicalQuantities::CTime(fp->enrouteHrs * 60 + fp->enrouteMins, BlackMisc::PhysicalQuantities::CTimeUnit::min()), + BlackMisc::PhysicalQuantities::CTime(fp->fuelHrs * 60 + fp->fuelMins, BlackMisc::PhysicalQuantities::CTimeUnit::min()), + cruiseAlt, + BlackMisc::PhysicalQuantities::CSpeed(fp->trueCruisingSpeed, BlackMisc::PhysicalQuantities::CSpeedUnit::kts()), + rules, + cbvar_cast(cbvar)->fromFSD(fp->route), + cbvar_cast(cbvar)->fromFSD(fp->remarks) + ); + + emit cbvar_cast(cbvar)->flightPlanReplyReceived(CCallsign(callsign, CCallsign::Atc), flightPlan); + } + + void CNetworkVatlib::onErrorReceived(VatSessionID, VatServerError error, const char *msg, const char *data, void *cbvar) + { + auto *self = cbvar_cast(cbvar); + switch (error) + { + case vatServerErrorCsInUs: CLogMessage(self).error("The requested callsign is already taken"); emit self->terminate(); break; + case vatServerErrorCallsignInvalid: CLogMessage(self).error("The requested callsign is not valid"); emit self->terminate(); break; + case vatServerErrorCidInvalid: CLogMessage(self).error("Wrong user ID or password"); emit self->terminate(); break; + case vatServerErrorRevision: CLogMessage(self).error("This server does not support our protocol version"); emit self->terminate(); break; + case vatServerErrorLevel: CLogMessage(self).error("You are not authorized to use the requested pilot rating"); emit self->terminate(); break; + case vatServerErrorServFull: CLogMessage(self).error("The server is full"); emit self->terminate(); break; + case vatServerErrorCsSuspended: CLogMessage(self).error("Your user account is suspended"); emit self->terminate(); break; + case vatServerErrorInvPos: CLogMessage(self).error("You are not authorized to use the requested rating"); emit self->terminate(); break; + case vatServerErrorUnAuth: CLogMessage(self).error("This software is not authorized for use on this network"); emit self->terminate(); break; + + case vatServerErrorNone: CLogMessage(self).info("OK"); break; + case vatServerErrorSyntax: CLogMessage(self).info("Malformed packet: Syntax error: %1") << self->fromFSD(data); break; + case vatServerErrorSrcInvalid: CLogMessage(self).info("Server: source invalid %1") << self->fromFSD(data); break; + case vatServerErrorNoSuchCs: CLogMessage(self).info("Shim lib: %1 (%2)") << self->fromFSD(msg) << self->fromFSD(data); break; + case vatServerErrorNoFP: CLogMessage(self).info("Server: no flight plan"); break; + case vatServerErrorNoWeather: CLogMessage(self).info("Server: requested weather profile does not exist"); break; + + // we have no idea what these mean + case vatServerErrorRegistered: + case vatServerErrorInvalidCtrl: CLogMessage(self).info("Server: ") << self->fromFSD(msg); break; + + default: qFatal("vatlib: %s (error %d)", msg, error); emit self->terminate(); + } + } + + void CNetworkVatlib::onPilotInfoRequestReceived(VatSessionID, const char *callsignString, void *cbvar) + { + auto timer = new QTimer(cbvar_cast(cbvar)); + timer->setSingleShot(true); + timer->start(0); + + BlackMisc::Aviation::CCallsign callsign(callsignString); + connect(timer, &QTimer::timeout, [ = ]() { cbvar_cast(cbvar)->sendAircraftInfo(callsign); }); + } + + void CNetworkVatlib::onPilotInfoReceived(VatSessionID, const char *callsign, const VatAircraftInfo *aircraftInfo, void *cbvar) + { + emit cbvar_cast(cbvar)->icaoCodesReplyReceived( + cbvar_cast(cbvar)->fromFSD(callsign), + cbvar_cast(cbvar)->fromFSD(aircraftInfo->aircraftType).trimmed().toUpper(), + cbvar_cast(cbvar)->fromFSD(aircraftInfo->airline).trimmed().toUpper(), + cbvar_cast(cbvar)->fromFSD(aircraftInfo->livery).trimmed().toUpper() + ); + } + + void CNetworkVatlib::networkLogHandler(SeverityLevel /** severity **/, const char *message) + { + CLogMessage(static_cast(nullptr)).error(message); + } + + QJsonObject CNetworkVatlib::JsonPackets::aircraftConfigRequest() + { + return { { "request", "full" } }; + } + } // namespace +} // namespace +//! \endcond diff --git a/src/blackcore/vatsim/networkvatlib.h b/src/blackcore/vatsim/networkvatlib.h new file mode 100644 index 000000000..910dbdd7f --- /dev/null +++ b/src/blackcore/vatsim/networkvatlib.h @@ -0,0 +1,228 @@ +/* Copyright (C) 2013 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKCORE_VATSIM_NETWORK_VATLIB_H +#define BLACKCORE_VATSIM_NETWORK_VATLIB_H + +#include "blackcore/blackcoreexport.h" +#include "blackcore/network.h" +#include "blackcore/settings/network.h" +#include "blackcore/tokenbucket.h" +#include "blackmisc/aviation/aircrafticaocode.h" +#include "blackmisc/aviation/aircraftparts.h" +#include "blackmisc/aviation/airlineicaocode.h" +#include "blackmisc/aviation/airporticaocode.h" +#include "blackmisc/aviation/callsign.h" +#include "blackmisc/aviation/callsignset.h" +#include "blackmisc/network/server.h" +#include "blackmisc/network/textmessagelist.h" +#include "blackmisc/settingscache.h" +#include "blackmisc/simulation/ownaircraftprovider.h" +#include "blackmisc/simulation/simulatorplugininfo.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QCommandLineOption; + +namespace BlackMisc +{ + namespace Aviation { class CFlightPlan; } + namespace Simulation { class CSimulatedAircraft; } +} + +namespace BlackCore +{ + namespace Vatsim + { + //! Implementation of INetwork using the vatlib shim + class BLACKCORE_EXPORT CNetworkVatlib : + public INetwork, + public BlackMisc::Simulation::COwnAircraftAware // network vatlib consumes own aircraft data and sets ICAO/callsign data + { + Q_OBJECT + + public: + //! Constructor + CNetworkVatlib(BlackMisc::Simulation::IOwnAircraftProvider *ownAircraft, QObject *parent = nullptr); + + //! Destructor + virtual ~CNetworkVatlib(); + + //! \name Network functions + //! @{ + virtual bool isConnected() const override { return m_status == vatStatusConnected; } + virtual BlackMisc::Network::CServer getPresetServer() const override { return m_server; } + virtual bool isPendingConnection() const override { return m_status == vatStatusConnecting; } + virtual void presetLoginMode(LoginMode mode) override; + virtual void presetServer(const BlackMisc::Network::CServer &server) override; + virtual void presetCallsign(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual void presetIcaoCodes(const BlackMisc::Simulation::CSimulatedAircraft &ownAircraft) override; + virtual void presetSimulatorInfo(const BlackMisc::Simulation::CSimulatorPluginInfo &simInfo) override; + virtual void initiateConnection() override; + virtual void terminateConnection() override; + virtual void sendPing(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual void sendRealNameQuery(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual void sendIpQuery() override; + virtual void sendServerQuery(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual void sendCustomPacket(const BlackMisc::Aviation::CCallsign &callsign, const QString &packetId, const QStringList &data) override; + virtual void sendCustomFsinnQuery(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual void sendCustomFsinnReponse(const BlackMisc::Aviation::CCallsign &callsign) override; + + virtual void broadcastAircraftConfig(const QJsonObject &config) override; + virtual void sendAircraftConfigQuery(const BlackMisc::Aviation::CCallsign &callsign) override; + //! @} + + //! \name Text message functions + //! @{ + virtual void sendTextMessages(const BlackMisc::Network::CTextMessageList &messages) override; + //! @} + + //! \name ATC functions + //! @{ + virtual void sendAtcQuery(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual void sendAtisQuery(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual void sendFlightPlan(const BlackMisc::Aviation::CFlightPlan &flightPlan) override; + virtual void sendFlightPlanQuery(const BlackMisc::Aviation::CCallsign &callsign) override; + //! @} + + //! \name Aircraft functions + //! @{ + virtual void sendCapabilitiesQuery(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual void sendIcaoCodesQuery(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual void sendFrequencyQuery(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual void sendUserInfoQuery(const BlackMisc::Aviation::CCallsign &callsign) override; + virtual void setInterimPositionReceivers(const BlackMisc::Aviation::CCallsignSet &receivers) override; + //! @} + + //! \name Weather functions + //! @{ + virtual void sendMetarQuery(const BlackMisc::Aviation::CAirportIcaoCode &airportIcao) override; + //! @} + + //! Cmd.line options this library can handle + static const QList &getCmdLineOptions(); + + //! Key if any from cmd.line arguments + QString getCmdLineFsdKey() const; + + private slots: + void replyToFrequencyQuery(const BlackMisc::Aviation::CCallsign &callsign); + void replyToNameQuery(const BlackMisc::Aviation::CCallsign &callsign); + void replyToConfigQuery(const BlackMisc::Aviation::CCallsign &callsign); + void sendAircraftInfo(const BlackMisc::Aviation::CCallsign &callsign); + void sendIncrementalAircraftConfig(); + + private: + //! \name Shimlib callbacks + //! @{ + static void onConnectionStatusChanged(VatSessionID, VatConnectionStatus oldStatus, VatConnectionStatus newStatus, void *cbvar); + static void onTextMessageReceived(VatSessionID, const char *from, const char *to, const char *msg, void *cbvar); + static void onRadioMessageReceived(VatSessionID, const char *from, int freqCount, int *freqList, const char *message, void *cbvar); + static void onControllerDisconnected(VatSessionID, const char *callsign, void *cbvar); + static void onInterimPilotPositionUpdate(VatSessionID, const char *sender, const VatInterimPilotPosition *position, void *cbvar); + static void onAtcPositionUpdate(VatSessionID, const char *callsign, const VatAtcPosition *pos, void *cbvar); + static void onKicked(VatSessionID, const char *reason, void *cbvar); + static void onPong(VatSessionID, const char *sender, double elapsedTime, void *cbvar); + static void onMetarReceived(VatSessionID, const char *data, void *cbvar); + static void onInfoQueryRequestReceived(VatSessionID, const char *callsign, VatClientQueryType type, const char *data, void *cbvar); + static void onInfoQueryReplyReceived(VatSessionID, const char *callsign, VatClientQueryType type, const char *data, const char *data2, void *cbvar); + static void onCapabilitiesReplyReceived(VatSessionID, const char *callsign, int capabilityFlags, void *cbvar); + static void onAtisReplyReceived(VatSessionID, const char *callsign, const VatControllerAtis *atis, void *cbvar); + static void onFlightPlanReceived(VatSessionID, const char *callsign, const VatFlightPlan *fp, void *cbvar); + static void onErrorReceived(VatSessionID, VatServerError error, const char *msg, const char *data, void *cbvar); + static void onPilotDisconnected(VatSessionID, const char *callsign, void *cbvar); + static void onPilotInfoRequestReceived(VatSessionID, const char *callsign, void *cbvar); + static void onPilotInfoReceived(VatSessionID, const char *callsign, const VatAircraftInfo *aircraftInfo, void *cbvar); + static void onPilotPositionUpdate(VatSessionID, const char *callsign, const VatPilotPosition *position, void *cbvar); + static void onAircraftConfigReceived(VatSessionID, const char *callsign, const char *aircraftConfig, void *cbvar); + static void onCustomPacketReceived(VatSessionID, const char *callsign, const char *packetId, const char **data, int dataSize, void *cbvar); + //! @} + + QByteArray toFSD(QString qstr) const; + QByteArray toFSD(const BlackMisc::Aviation::CCallsign &callsign) const; + std::function toFSD(QStringList qstrList) const; + QString fromFSD(const char *cstr) const; + QStringList fromFSD(const char **cstrArray, int size) const; + bool isInterimPositionUpdateEnabledForServer() const; + void startPositionTimers(); + void stopPositionTimers(); + void initializeSession(); + void changeConnectionStatus(VatConnectionStatus newStatus); + bool isDisconnected() const { return m_status != vatStatusConnecting && m_status != vatStatusConnected; } + static QString convertToUnicodeEscaped(const QString &str); + static VatSimType convertToSimType(BlackMisc::Simulation::CSimulatorPluginInfo &simInfo); + static void networkLogHandler(SeverityLevel severity, const char *message); + + inline QString defaultModelString() + { + return QStringLiteral("Cessna Skyhawk 172SP"); + } + + struct JsonPackets + { + static QJsonObject aircraftConfigRequest(); + }; + + private slots: + void process(); + void sendPositionUpdate(); + void sendInterimPositions(); + void customPacketDispatcher(const BlackMisc::Aviation::CCallsign &callsign, const QString &packetId, const QStringList &data); + + signals: + void terminate(); //!< \private + + private: + //! Deletion policy for QScopedPointer + struct VatlibQScopedPointerDeleter + { + //! Called by QScopedPointer destructor + static void cleanup(VatSessionID session) { if (session) Vat_DestroyNetworkSession(session); } + }; + + QScopedPointer m_net; + LoginMode m_loginMode; + VatConnectionStatus m_status; + BlackMisc::Network::CServer m_server; + QTextCodec *m_fsdTextCodec = nullptr; + BlackMisc::Simulation::CSimulatorPluginInfo m_simulatorInfo; + BlackMisc::Aviation::CCallsign m_ownCallsign; //!< "buffered callsign", as this must not change when connected + BlackMisc::Aviation::CAircraftIcaoCode m_ownAircraftIcaoCode; //!< "buffered icao", as this must not change when connected + BlackMisc::Aviation::CAirlineIcaoCode m_ownAirlineIcaoCode; //!< "buffered icao", as this must not change when connected + QString m_ownLiveryDescription; //!< "buffered livery", as this must not change when connected + BlackMisc::Aviation::CCallsignSet m_interimPositionReceivers; + BlackMisc::Aviation::CAircraftParts m_sentAircraftConfig; + CTokenBucket m_tokenBucket; + + QTimer m_scheduledConfigUpdate; + QTimer m_processingTimer; + QTimer m_positionUpdateTimer; + QTimer m_interimPositionUpdateTimer; + + static int const c_processingIntervalMsec = 100; + static int const c_updatePostionIntervalMsec = 5000; + static int const c_updateInterimPostionIntervalMsec = 1000; + static int const c_logoffTimeoutSec = 5; + }; + } //namespace +} //namespace +#endif // guard diff --git a/src/blackcore/vatsim/voicechannelvatlib.cpp b/src/blackcore/vatsim/voicechannelvatlib.cpp new file mode 100644 index 000000000..e4bf2a836 --- /dev/null +++ b/src/blackcore/vatsim/voicechannelvatlib.cpp @@ -0,0 +1,218 @@ +/* Copyright (C) 2014 + * swift project community / contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \cond PRIVATE + +#include "blackcore/vatsim/voicechannelvatlib.h" +#include "blackmisc/logmessage.h" +#include "blackmisc/statusmessage.h" + +#include +#include +#include + +using namespace BlackMisc; +using namespace BlackMisc::Audio; +using namespace BlackMisc::Aviation; + +namespace BlackCore +{ + namespace Vatsim + { + CVoiceChannelVatlib::CVoiceChannelVatlib(VatAudioService audioService, VatUDPAudioPort udpPort, QObject *parent) + : IVoiceChannel(parent), + m_audioService(audioService), + m_udpPort(udpPort) + { + m_voiceChannel.reset(Vat_CreateVoiceChannel(m_audioService, "", 3782, "", "", m_udpPort)); + Vat_SetConnectionChangedHandler(m_voiceChannel.data(), roomStatusUpdate, this); + Vat_SetClientJoinedHandler(m_voiceChannel.data(), processUserJoined, this); + Vat_SetClientLeftHandler(m_voiceChannel.data(), processUserLeft, this); + Vat_SetVoiceTransmissionChangedHandler(m_voiceChannel.data(), processTransmissionChange, this); + + } + + CVoiceChannelVatlib::~CVoiceChannelVatlib() + { + Q_ASSERT(m_roomStatus == IVoiceChannel::Disconnected); + } + + void CVoiceChannelVatlib::joinVoiceRoom(const CVoiceRoom &voiceRoom) + { + if (m_roomStatus == IVoiceChannel::Connecting || m_roomStatus == IVoiceChannel::Connected) return; + + // No one else is using this voice room, so prepare to join + m_voiceRoom = voiceRoom; + Vat_SetRoomInfo(m_voiceChannel.data(), qPrintable(voiceRoom.getHostname()), 3782, + qPrintable(voiceRoom.getChannel()), + qPrintable(m_callsign.toQString())); + + CLogMessage(this).debug() << "Joining voice room " << m_voiceRoom.getVoiceRoomUrl(); + Vat_JoinRoom(m_voiceChannel.data()); + } + + // Leave room + void CVoiceChannelVatlib::leaveVoiceRoom() + { + // If this room is not connected, there is nothing to do + if (m_roomStatus == IVoiceChannel::Disconnecting || m_roomStatus == IVoiceChannel::Disconnected) return; + + CLogMessage(this).debug() << "Leaving voice room " << m_voiceRoom.getVoiceRoomUrl(); + Vat_DisconnectFromRoom(m_voiceChannel.data()); + m_voiceRoom = {}; + m_listCallsigns = {}; + } + + CCallsignSet CVoiceChannelVatlib::getVoiceRoomCallsigns() const + { + return m_listCallsigns; + } + + void CVoiceChannelVatlib::setOwnAircraftCallsign(const CCallsign &callsign) + { + m_callsign = callsign; + } + + BlackMisc::Audio::CVoiceRoom CVoiceChannelVatlib::getVoiceRoom() const + { + return m_voiceRoom; + } + + bool CVoiceChannelVatlib::isMuted() const + { + // Remove + return false; + } + + void CVoiceChannelVatlib::setVolume(int /* volume */) + { + // Remove + } + + int CVoiceChannelVatlib::getVolume() const + { + // Remove + return 100; + } + + VatVoiceChannel CVoiceChannelVatlib::getVoiceChannel() const + { + return m_voiceChannel.data(); + } + + CCallsign CVoiceChannelVatlib::extractCallsign(const QString &name) + { + CCallsign callsign; + if (name.isEmpty()) return callsign; + + // callsign might contain: VATSIM id, user name + if (name.contains(" ")) + { + QStringList parts = name.split(" "); + callsign = CCallsign(parts[0]); + // I throw away VATSIM id here, maybe we could use it + } + else + { + callsign = CCallsign(name); + } + + return callsign; + } + + void CVoiceChannelVatlib::userJoinedVoiceRoom(VatVoiceChannel, int /** id **/, const char *name) + { + CCallsign callsign(extractCallsign(name)); + m_listCallsigns.push_back(callsign); + emit userJoinedRoom(callsign); + } + + void CVoiceChannelVatlib::userLeftVoiceRoom(VatVoiceChannel, int /** id **/, const char *name) + { + CCallsign callsign(extractCallsign(name)); + m_listCallsigns.remove(callsign); + emit userLeftRoom(callsign); + } + + void CVoiceChannelVatlib::transmissionChanged(VatVoiceChannel, VatVoiceTransmissionStatus status) + { + if (status == vatVoiceStarted) emit audioStarted(); + else emit audioStopped(); + } + + void CVoiceChannelVatlib::updateRoomStatus(VatVoiceChannel channel, VatConnectionStatus oldVatStatus, VatConnectionStatus newVatStatus) + { + Q_UNUSED(channel); + Q_UNUSED(oldVatStatus); + + IVoiceChannel::ConnectionStatus oldStatus = m_roomStatus; + switch (newVatStatus) + { + case vatStatusConnecting: + { + m_roomStatus = IVoiceChannel::Connecting; + break; + } + case vatStatusConnected: + { + m_voiceRoom.setConnected(true); + m_roomStatus = IVoiceChannel::Connected; + break; + } + case vatStatusDisconnecting: + { + m_roomStatus = IVoiceChannel::Disconnecting; + break; + } + case vatStatusDisconnected: + { + // Clear all internals + m_listCallsigns.clear(); + m_voiceRoom = {}; + m_roomStatus = IVoiceChannel::Disconnected; + break; + } + default: + break; + } + emit connectionStatusChanged(oldStatus, m_roomStatus); + } + + //! Cast void* to a pointer of CVoiceChannelVatlib + CVoiceChannelVatlib *cbvar_cast_voiceChannel(void *cbvar) + { + return static_cast(cbvar); + } + + void CVoiceChannelVatlib::processUserJoined(VatVoiceChannel channel, int id, const char *name, void *cbVar) + { + auto obj = cbvar_cast_voiceChannel(cbVar); + obj->userJoinedVoiceRoom(channel, id, name); + } + + void CVoiceChannelVatlib::processUserLeft(VatVoiceChannel channel, int id, const char *name, void *cbVar) + { + auto obj = cbvar_cast_voiceChannel(cbVar); + obj->userLeftVoiceRoom(channel, id, name); + } + + void CVoiceChannelVatlib::processTransmissionChange(VatVoiceChannel channel, VatVoiceTransmissionStatus status, void *cbVar) + { + auto obj = cbvar_cast_voiceChannel(cbVar); + obj->transmissionChanged(channel, status); + } + + void CVoiceChannelVatlib::roomStatusUpdate(VatVoiceChannel channel, VatConnectionStatus oldStatus, VatConnectionStatus newStatus, void *cbVar) + { + auto obj = cbvar_cast_voiceChannel(cbVar); + obj->updateRoomStatus(channel, oldStatus, newStatus); + } + } // ns +} // ns +//! \endcond diff --git a/src/blackcore/vatsim/voicechannelvatlib.h b/src/blackcore/vatsim/voicechannelvatlib.h new file mode 100644 index 000000000..5dede108a --- /dev/null +++ b/src/blackcore/vatsim/voicechannelvatlib.h @@ -0,0 +1,111 @@ +/* Copyright (C) 2014 + * swift project community / contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKCORE_VATSIM_VOICE_CHANNEL_VATLIB_H +#define BLACKCORE_VATSIM_VOICE_CHANNEL_VATLIB_H + +#include "blackcore/blackcoreexport.h" +#include "blackcore/voicechannel.h" +#include "blackmisc/audio/voiceroom.h" +#include "blackmisc/aviation/callsign.h" +#include "blackmisc/aviation/callsignset.h" +#include "vatlib/vatlib.h" + +#include +#include +#include +#include + +struct VatProducerConsumer_tag; + +namespace BlackCore +{ + namespace Vatsim + { + class CVoiceChannelVatlibPrivate; + + //! Class implementing the voice channel interface + class BLACKCORE_EXPORT CVoiceChannelVatlib : public IVoiceChannel + { + Q_OBJECT + + public: + + //! Default constructor + CVoiceChannelVatlib(VatAudioService audioService, VatUDPAudioPort udpPort, QObject *parent = nullptr); + + //! Destructor + virtual ~CVoiceChannelVatlib(); + + //! \copydoc IVoiceChannel::joinVoiceRoom + virtual void joinVoiceRoom(const BlackMisc::Audio::CVoiceRoom &voiceRoom) override; + + //! \copydoc IVoiceChannel::leaveVoiceRoom + virtual void leaveVoiceRoom() override; + + //! \copydoc IVoiceChannel::getVoiceRoomCallsigns + virtual BlackMisc::Aviation::CCallsignSet getVoiceRoomCallsigns() const override; + + //! \copydoc IVoiceChannel::setOwnAircraftCallsign + virtual void setOwnAircraftCallsign(const BlackMisc::Aviation::CCallsign &callsign) override; + + //! \copydoc IVoiceChannel::getVoiceRoom + virtual BlackMisc::Audio::CVoiceRoom getVoiceRoom() const override; + + //! \copydoc IVoiceChannel::isMuted + virtual bool isMuted() const override; + + //! Set channel volume + virtual void setVolume(int volume) override; + + //! Get channel volume + virtual int getVolume() const override; + + //! Get vatlib channel pointer + VatVoiceChannel getVoiceChannel() const; + + private: + + struct VatVoiceChannelDeleter + { + static inline void cleanup(VatProducerConsumer_tag *obj) + { + if (obj) Vat_DestroyVoiceChannel(obj); + } + }; + + BlackMisc::Aviation::CCallsign extractCallsign(const QString &name); + + void userJoinedVoiceRoom(VatVoiceChannel, int id, const char *name); + void userLeftVoiceRoom(VatVoiceChannel, int id, const char *name); + void transmissionChanged(VatVoiceChannel, VatVoiceTransmissionStatus status); + void updateRoomStatus(VatVoiceChannel channel, VatConnectionStatus oldStatus, VatConnectionStatus newStatus); + + static void processUserJoined(VatVoiceChannel channel, int id, const char *name, void *cbVar); + static void processUserLeft(VatVoiceChannel channel, int id, const char *name, void *cbVar); + static void processTransmissionChange(VatVoiceChannel channel, VatVoiceTransmissionStatus status, void *cbVar); + + static void roomStatusUpdate(VatVoiceChannel channel, VatConnectionStatus oldStatus, VatConnectionStatus newStatus, void *cbVar); + + BlackMisc::Aviation::CCallsign m_callsign; // Own callsign + BlackMisc::Audio::CVoiceRoom m_voiceRoom; // Voice Room + BlackMisc::Aviation::CCallsignSet m_listCallsigns; // Callsigns connected to room + IVoiceChannel::ConnectionStatus m_roomStatus = IVoiceChannel::Disconnected; // Room connection status + + VatAudioService m_audioService; + VatUDPAudioPort m_udpPort; + + QScopedPointer m_voiceChannel; + }; + } // ns +} // ns + +#endif // guard diff --git a/src/blackcore/vatsim/voicevatlib.cpp b/src/blackcore/vatsim/voicevatlib.cpp new file mode 100644 index 000000000..1e910fd60 --- /dev/null +++ b/src/blackcore/vatsim/voicevatlib.cpp @@ -0,0 +1,142 @@ +/* Copyright (C) 2013 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackcore/audiodevice.h" +#include "blackcore/vatsim/audiodevicevatlib.h" +#include "blackcore/vatsim/audiomixervatlib.h" +#include "blackcore/voicechannel.h" +#include "blackcore/vatsim/voicechannelvatlib.h" +#include "blackcore/vatsim/voicevatlib.h" +#include "blackmisc/logmessage.h" +#include "vatlib/vatlib.h" + +#include +#include + +using namespace BlackMisc; +using namespace BlackMisc::Audio; +using namespace BlackMisc::Aviation; + +namespace BlackCore +{ + namespace Vatsim + { + CVoiceVatlib::CVoiceVatlib(QObject *parent) : + IVoice(parent), + m_audioService(Vat_CreateAudioService()), + m_udpPort(Vat_CreateUDPAudioPort(m_audioService.data(), 0)) + { + Vat_SetVoiceLogHandler(SeverityLevel::SeverityError, CVoiceVatlib::voiceLogHandler); + + // do processing + this->startTimer(10); + } + + CVoiceVatlib::~CVoiceVatlib() {} + + QSharedPointer CVoiceVatlib::createVoiceChannel() + { + return QSharedPointer(new CVoiceChannelVatlib(m_audioService.data(), m_udpPort.data(), this)); + } + + std::unique_ptr CVoiceVatlib::createInputDevice() + { + return std::make_unique(m_audioService.data(), this); + } + + std::unique_ptr CVoiceVatlib::createOutputDevice() + { + return std::make_unique(m_audioService.data(), this); + } + + std::unique_ptr CVoiceVatlib::createAudioMixer() + { + return std::make_unique(this); + } + + void CVoiceVatlib::connectVoice(IAudioInputDevice *device, IAudioMixer *mixer, IAudioMixer::InputPort inputPort) + { + auto audioInputVatlib = qobject_cast(device); + Q_ASSERT_X(audioInputVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioInputDeviceVatlib pointer."); + + auto audioMixerVatlib = qobject_cast(mixer); + Q_ASSERT_X(audioMixerVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioMixerVatlib pointer."); + + Vat_ConnectProducerToProducerConsumer(audioInputVatlib->getVatLocalInputCodec(), 0, audioMixerVatlib->getVatAudioMixer(), inputPort); + } + + void CVoiceVatlib::connectVoice(IVoiceChannel *channel, IAudioMixer *mixer, IAudioMixer::InputPort inputPort) + { + auto voiceChannelVatlib = qobject_cast(channel); + Q_ASSERT_X(voiceChannelVatlib, "CVoiceVatlib::connectVoice", "No valid CVoiceChannelVatlib pointer."); + + auto audioMixerVatlib = qobject_cast(mixer); + Q_ASSERT_X(audioMixerVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioMixerVatlib pointer."); + + Vat_ConnectProducerConsumerToProducerConsumer(voiceChannelVatlib->getVoiceChannel(), 0, audioMixerVatlib->getVatAudioMixer(), inputPort); + } + + void CVoiceVatlib::connectVoice(IAudioMixer *mixer, IAudioMixer::OutputPort outputPort, IAudioOutputDevice *device) + { + auto audioMixerVatlib = qobject_cast(mixer); + Q_ASSERT_X(audioMixerVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioMixerVatlib pointer."); + + auto audioDeviceVatlib = qobject_cast(device); + Q_ASSERT_X(audioDeviceVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioOutputDeviceVatlib pointer."); + + Vat_ConnectProducerConsumerToConsumer(audioMixerVatlib->getVatAudioMixer(), outputPort, audioDeviceVatlib->getVatLocalOutputCodec(), 0); + } + + void CVoiceVatlib::connectVoice(IAudioMixer *mixer, IAudioMixer::OutputPort outputPort, IVoiceChannel *channel) + { + auto audioMixerVatlib = qobject_cast(mixer); + Q_ASSERT_X(audioMixerVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioMixerVatlib pointer."); + + auto voiceChannelVatlib = qobject_cast(channel); + Q_ASSERT_X(voiceChannelVatlib, "CVoiceVatlib::connectVoice", "No valid CVoiceChannelVatlib pointer."); + + Vat_ConnectProducerConsumerToProducerConsumer(audioMixerVatlib->getVatAudioMixer(), outputPort, voiceChannelVatlib->getVoiceChannel(), 0); + } + + void CVoiceVatlib::disconnectVoice(IAudioInputDevice *device) + { + auto audioInputVatlib = qobject_cast(device); + Q_ASSERT_X(audioInputVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioInputDeviceVatlib pointer."); + Vat_ConnectProducerToConsumer(audioInputVatlib->getVatLocalInputCodec(), 0, nullptr, 0); + } + + void CVoiceVatlib::disconnectVoice(IVoiceChannel *channel) + { + auto voiceChannelVatlib = qobject_cast(channel); + Q_ASSERT_X(voiceChannelVatlib, "CVoiceVatlib::connectVoice", "No valid CVoiceChannelVatlib pointer."); + Vat_ConnectProducerConsumerToConsumer(voiceChannelVatlib->getVoiceChannel(), 0, nullptr, 0); + } + + void CVoiceVatlib::disconnectVoice(IAudioMixer *mixer, IAudioMixer::OutputPort outputPort) + { + auto audioMixerVatlib = qobject_cast(mixer); + Q_ASSERT_X(audioMixerVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioMixerVatlib pointer."); + Vat_ConnectProducerConsumerToConsumer(audioMixerVatlib->getVatAudioMixer(), outputPort, nullptr, 0); + } + + /* + * Process voice handling + */ + void CVoiceVatlib::timerEvent(QTimerEvent *) + { + Q_ASSERT_X(m_audioService, "CVoiceVatlib", "VatAudioService invalid!"); + Vat_ExecuteTasks(m_audioService.data()); + } + + void CVoiceVatlib::voiceLogHandler(SeverityLevel /** severity **/, const char *message) + { + CLogMessage(static_cast(nullptr)).error(message); + } + } // namespace +} // namespace diff --git a/src/blackcore/vatsim/voicevatlib.h b/src/blackcore/vatsim/voicevatlib.h new file mode 100644 index 000000000..adce13c68 --- /dev/null +++ b/src/blackcore/vatsim/voicevatlib.h @@ -0,0 +1,124 @@ +/* Copyright (C) 2013 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKCORE_VOICE_VATLIB_H +#define BLACKCORE_VOICE_VATLIB_H + +#include "blackcore/audiomixer.h" +#include "blackcore/blackcoreexport.h" +#include "blackcore/voice.h" + +#include +#include +#include +#include +#include + +class QTimerEvent; + +struct VatAudioService_tag; +struct VatUDPAudioPort_tag; +template class QSharedPointer; + +#ifdef Q_OS_WIN +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#endif + +namespace BlackCore +{ + class IAudioInputDevice; + class IAudioOutputDevice; + class IVoiceChannel; + namespace Vatsim + { + //! Vatlib implementation of the IVoice interface. + class BLACKCORE_EXPORT CVoiceVatlib : public IVoice + { + Q_OBJECT + + public: + + //! Default constructor + CVoiceVatlib(QObject *parent = nullptr); + + //! Destructor + virtual ~CVoiceVatlib(); + + //! \copydoc IVoice::createVoiceChannel() + virtual QSharedPointer createVoiceChannel() override; + + //! \copydoc IVoice::createInputDevice() + virtual std::unique_ptr createInputDevice() override; + + //! \copydoc IVoice::createOutputDevice() + virtual std::unique_ptr createOutputDevice() override; + + //! \copydoc IVoice::createAudioMixer() + virtual std::unique_ptr createAudioMixer() override; + + //! \copydoc IVoice::connectVoice() + virtual void connectVoice(IAudioInputDevice *device, IAudioMixer *mixer, IAudioMixer::InputPort inputPort) override; + + //! \copydoc IVoice::connectVoice() + virtual void connectVoice(IVoiceChannel *channel, IAudioMixer *mixer, IAudioMixer::InputPort inputPort) override; + + //! \copydoc IVoice::connectVoice() + virtual void connectVoice(IAudioMixer *mixer, IAudioMixer::OutputPort outputPort, IAudioOutputDevice *device) override; + + //! \copydoc IVoice::connectVoice() + virtual void connectVoice(IAudioMixer *mixer, IAudioMixer::OutputPort outputPort, IVoiceChannel *channel) override; + + //! \copydoc IVoice::disconnectVoice() + void disconnectVoice(IAudioInputDevice *device) override; + + //! \copydoc IVoice::disconnectVoice() + void disconnectVoice(IVoiceChannel *channel) override; + + //! \copydoc IVoice::disconnectVoice() + void disconnectVoice(IAudioMixer *mixer, IAudioMixer::OutputPort outputPort) override; + + protected: // QObject overrides + + //! Process voice lib + virtual void timerEvent(QTimerEvent *) override; + + private: + + // this struct calls "myCustomDeallocator" to delete the pointer + struct VatAudioServiceDeleter + { + static inline void cleanup(VatAudioService_tag *obj) + { + Vat_DestroyAudioService(obj); + } + }; + + struct VatUDPAudioPortDeleter + { + static inline void cleanup(VatUDPAudioPort_tag *obj) + { + Vat_DestroyUDPAudioPort(obj); + } + }; + + static void voiceLogHandler(SeverityLevel severity, const char *message); + + QScopedPointer m_audioService; + QScopedPointer m_udpPort; + + }; + } // namespace +} // namespace + +#endif // guard diff --git a/src/blackcore/voicechannelvatlib.cpp b/src/blackcore/voicechannelvatlib.cpp deleted file mode 100644 index 92dda1b98..000000000 --- a/src/blackcore/voicechannelvatlib.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/* Copyright (C) 2014 - * swift project community / contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \cond PRIVATE - -#include "blackcore/voicechannelvatlib.h" -#include "blackmisc/logmessage.h" -#include "blackmisc/statusmessage.h" - -#include -#include -#include - -using namespace BlackMisc; -using namespace BlackMisc::Audio; -using namespace BlackMisc::Aviation; - -namespace BlackCore -{ - CVoiceChannelVatlib::CVoiceChannelVatlib(VatAudioService audioService, VatUDPAudioPort udpPort, QObject *parent) - : IVoiceChannel(parent), - m_audioService(audioService), - m_udpPort(udpPort) - { - m_voiceChannel.reset(Vat_CreateVoiceChannel(m_audioService, "", 3782, "", "", m_udpPort)); - Vat_SetConnectionChangedHandler(m_voiceChannel.data(), roomStatusUpdate, this); - Vat_SetClientJoinedHandler(m_voiceChannel.data(), processUserJoined, this); - Vat_SetClientLeftHandler(m_voiceChannel.data(), processUserLeft, this); - Vat_SetVoiceTransmissionChangedHandler(m_voiceChannel.data(), processTransmissionChange, this); - - } - - CVoiceChannelVatlib::~CVoiceChannelVatlib() - { - Q_ASSERT(m_roomStatus == IVoiceChannel::Disconnected); - } - - void CVoiceChannelVatlib::joinVoiceRoom(const CVoiceRoom &voiceRoom) - { - if (m_roomStatus == IVoiceChannel::Connecting || m_roomStatus == IVoiceChannel::Connected) return; - - // No one else is using this voice room, so prepare to join - m_voiceRoom = voiceRoom; - Vat_SetRoomInfo(m_voiceChannel.data(), qPrintable(voiceRoom.getHostname()), 3782, - qPrintable(voiceRoom.getChannel()), - qPrintable(m_callsign.toQString())); - - CLogMessage(this).debug() << "Joining voice room " << m_voiceRoom.getVoiceRoomUrl(); - Vat_JoinRoom(m_voiceChannel.data()); - } - - // Leave room - void CVoiceChannelVatlib::leaveVoiceRoom() - { - // If this room is not connected, there is nothing to do - if (m_roomStatus == IVoiceChannel::Disconnecting || m_roomStatus == IVoiceChannel::Disconnected) return; - - CLogMessage(this).debug() << "Leaving voice room " << m_voiceRoom.getVoiceRoomUrl(); - Vat_DisconnectFromRoom(m_voiceChannel.data()); - m_voiceRoom = {}; - m_listCallsigns = {}; - } - - CCallsignSet CVoiceChannelVatlib::getVoiceRoomCallsigns() const - { - return m_listCallsigns; - } - - void CVoiceChannelVatlib::setOwnAircraftCallsign(const CCallsign &callsign) - { - m_callsign = callsign; - } - - BlackMisc::Audio::CVoiceRoom CVoiceChannelVatlib::getVoiceRoom() const - { - return m_voiceRoom; - } - - bool CVoiceChannelVatlib::isMuted() const - { - // Remove - return false; - } - - void CVoiceChannelVatlib::setVolume(int /* volume */) - { - // Remove - } - - int CVoiceChannelVatlib::getVolume() const - { - // Remove - return 100; - } - - VatVoiceChannel CVoiceChannelVatlib::getVoiceChannel() const - { - return m_voiceChannel.data(); - } - - CCallsign CVoiceChannelVatlib::extractCallsign(const QString &name) - { - CCallsign callsign; - if (name.isEmpty()) return callsign; - - // callsign might contain: VATSIM id, user name - if (name.contains(" ")) - { - QStringList parts = name.split(" "); - callsign = CCallsign(parts[0]); - // I throw away VATSIM id here, maybe we could use it - } - else - { - callsign = CCallsign(name); - } - - return callsign; - } - - void CVoiceChannelVatlib::userJoinedVoiceRoom(VatVoiceChannel, int /** id **/, const char *name) - { - CCallsign callsign(extractCallsign(name)); - m_listCallsigns.push_back(callsign); - emit userJoinedRoom(callsign); - } - - void CVoiceChannelVatlib::userLeftVoiceRoom(VatVoiceChannel, int /** id **/, const char *name) - { - CCallsign callsign(extractCallsign(name)); - m_listCallsigns.remove(callsign); - emit userLeftRoom(callsign); - } - - void CVoiceChannelVatlib::transmissionChanged(VatVoiceChannel, VatVoiceTransmissionStatus status) - { - if (status == vatVoiceStarted) emit audioStarted(); - else emit audioStopped(); - } - - void CVoiceChannelVatlib::updateRoomStatus(VatVoiceChannel channel, VatConnectionStatus oldVatStatus, VatConnectionStatus newVatStatus) - { - Q_UNUSED(channel); - Q_UNUSED(oldVatStatus); - - IVoiceChannel::ConnectionStatus oldStatus = m_roomStatus; - switch (newVatStatus) - { - case vatStatusConnecting: - { - m_roomStatus = IVoiceChannel::Connecting; - break; - } - case vatStatusConnected: - { - m_voiceRoom.setConnected(true); - m_roomStatus = IVoiceChannel::Connected; - break; - } - case vatStatusDisconnecting: - { - m_roomStatus = IVoiceChannel::Disconnecting; - break; - } - case vatStatusDisconnected: - { - // Clear all internals - m_listCallsigns.clear(); - m_voiceRoom = {}; - m_roomStatus = IVoiceChannel::Disconnected; - break; - } - default: - break; - } - emit connectionStatusChanged(oldStatus, m_roomStatus); - } - - //! Cast void* to a pointer of CVoiceChannelVatlib - CVoiceChannelVatlib *cbvar_cast_voiceChannel(void *cbvar) - { - return static_cast(cbvar); - } - - void CVoiceChannelVatlib::processUserJoined(VatVoiceChannel channel, int id, const char *name, void *cbVar) - { - auto obj = cbvar_cast_voiceChannel(cbVar); - obj->userJoinedVoiceRoom(channel, id, name); - } - - void CVoiceChannelVatlib::processUserLeft(VatVoiceChannel channel, int id, const char *name, void *cbVar) - { - auto obj = cbvar_cast_voiceChannel(cbVar); - obj->userLeftVoiceRoom(channel, id, name); - } - - void CVoiceChannelVatlib::processTransmissionChange(VatVoiceChannel channel, VatVoiceTransmissionStatus status, void *cbVar) - { - auto obj = cbvar_cast_voiceChannel(cbVar); - obj->transmissionChanged(channel, status); - } - - void CVoiceChannelVatlib::roomStatusUpdate(VatVoiceChannel channel, VatConnectionStatus oldStatus, VatConnectionStatus newStatus, void *cbVar) - { - auto obj = cbvar_cast_voiceChannel(cbVar); - obj->updateRoomStatus(channel, oldStatus, newStatus); - } -} // ns - -//! \endcond diff --git a/src/blackcore/voicechannelvatlib.h b/src/blackcore/voicechannelvatlib.h deleted file mode 100644 index 20117a971..000000000 --- a/src/blackcore/voicechannelvatlib.h +++ /dev/null @@ -1,108 +0,0 @@ -/* Copyright (C) 2014 - * swift project community / contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \file - -#ifndef BLACKCORE_VOICE_CHANNEL_VATLIB_H -#define BLACKCORE_VOICE_CHANNEL_VATLIB_H - -#include "blackcore/blackcoreexport.h" -#include "blackcore/voicechannel.h" -#include "blackmisc/audio/voiceroom.h" -#include "blackmisc/aviation/callsign.h" -#include "blackmisc/aviation/callsignset.h" -#include "vatlib/vatlib.h" - -#include -#include -#include -#include - -struct VatProducerConsumer_tag; - -namespace BlackCore -{ - class CVoiceChannelVatlibPrivate; - - //! Class implementing the voice channel interface - class BLACKCORE_EXPORT CVoiceChannelVatlib : public IVoiceChannel - { - Q_OBJECT - - public: - - //! Default constructor - CVoiceChannelVatlib(VatAudioService audioService, VatUDPAudioPort udpPort, QObject *parent = nullptr); - - //! Destructor - virtual ~CVoiceChannelVatlib(); - - //! \copydoc IVoiceChannel::joinVoiceRoom - virtual void joinVoiceRoom(const BlackMisc::Audio::CVoiceRoom &voiceRoom) override; - - //! \copydoc IVoiceChannel::leaveVoiceRoom - virtual void leaveVoiceRoom() override; - - //! \copydoc IVoiceChannel::getVoiceRoomCallsigns - virtual BlackMisc::Aviation::CCallsignSet getVoiceRoomCallsigns() const override; - - //! \copydoc IVoiceChannel::setOwnAircraftCallsign - virtual void setOwnAircraftCallsign(const BlackMisc::Aviation::CCallsign &callsign) override; - - //! \copydoc IVoiceChannel::getVoiceRoom - virtual BlackMisc::Audio::CVoiceRoom getVoiceRoom() const override; - - //! \copydoc IVoiceChannel::isMuted - virtual bool isMuted() const override; - - //! Set channel volume - virtual void setVolume(int volume) override; - - //! Get channel volume - virtual int getVolume() const override; - - //! Get vatlib channel pointer - VatVoiceChannel getVoiceChannel() const; - - private: - - struct VatVoiceChannelDeleter - { - static inline void cleanup(VatProducerConsumer_tag *obj) - { - if (obj) Vat_DestroyVoiceChannel(obj); - } - }; - - BlackMisc::Aviation::CCallsign extractCallsign(const QString &name); - - void userJoinedVoiceRoom(VatVoiceChannel, int id, const char *name); - void userLeftVoiceRoom(VatVoiceChannel, int id, const char *name); - void transmissionChanged(VatVoiceChannel, VatVoiceTransmissionStatus status); - void updateRoomStatus(VatVoiceChannel channel, VatConnectionStatus oldStatus, VatConnectionStatus newStatus); - - static void processUserJoined(VatVoiceChannel channel, int id, const char *name, void *cbVar); - static void processUserLeft(VatVoiceChannel channel, int id, const char *name, void *cbVar); - static void processTransmissionChange(VatVoiceChannel channel, VatVoiceTransmissionStatus status, void *cbVar); - - static void roomStatusUpdate(VatVoiceChannel channel, VatConnectionStatus oldStatus, VatConnectionStatus newStatus, void *cbVar); - - BlackMisc::Aviation::CCallsign m_callsign; // Own callsign - BlackMisc::Audio::CVoiceRoom m_voiceRoom; // Voice Room - BlackMisc::Aviation::CCallsignSet m_listCallsigns; // Callsigns connected to room - IVoiceChannel::ConnectionStatus m_roomStatus = IVoiceChannel::Disconnected; // Room connection status - - VatAudioService m_audioService; - VatUDPAudioPort m_udpPort; - - QScopedPointer m_voiceChannel; - }; -} - -#endif // guard diff --git a/src/blackcore/voicevatlib.cpp b/src/blackcore/voicevatlib.cpp deleted file mode 100644 index 08a570f1a..000000000 --- a/src/blackcore/voicevatlib.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* Copyright (C) 2013 - * swift Project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -#include "blackcore/audiodevice.h" -#include "blackcore/audiodevicevatlib.h" -#include "blackcore/audiomixervatlib.h" -#include "blackcore/voicechannel.h" -#include "blackcore/voicechannelvatlib.h" -#include "blackcore/voicevatlib.h" -#include "blackmisc/logmessage.h" -#include "vatlib/vatlib.h" - -#include -#include - -using namespace BlackMisc; -using namespace BlackMisc::Audio; -using namespace BlackMisc::Aviation; - -namespace BlackCore -{ - CVoiceVatlib::CVoiceVatlib(QObject *parent) : - IVoice(parent), - m_audioService(Vat_CreateAudioService()), - m_udpPort(Vat_CreateUDPAudioPort(m_audioService.data(), 0)) - { - Vat_SetVoiceLogHandler(SeverityLevel::SeverityError, CVoiceVatlib::voiceLogHandler); - - // do processing - this->startTimer(10); - } - - CVoiceVatlib::~CVoiceVatlib() {} - - QSharedPointer CVoiceVatlib::createVoiceChannel() - { - return QSharedPointer(new CVoiceChannelVatlib(m_audioService.data(), m_udpPort.data(), this)); - } - - std::unique_ptr CVoiceVatlib::createInputDevice() - { - return std::make_unique(m_audioService.data(), this); - } - - std::unique_ptr CVoiceVatlib::createOutputDevice() - { - return std::make_unique(m_audioService.data(), this); - } - - std::unique_ptr CVoiceVatlib::createAudioMixer() - { - return std::make_unique(this); - } - - void CVoiceVatlib::connectVoice(IAudioInputDevice *device, IAudioMixer *mixer, IAudioMixer::InputPort inputPort) - { - auto audioInputVatlib = qobject_cast(device); - Q_ASSERT_X(audioInputVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioInputDeviceVatlib pointer."); - - auto audioMixerVatlib = qobject_cast(mixer); - Q_ASSERT_X(audioMixerVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioMixerVatlib pointer."); - - Vat_ConnectProducerToProducerConsumer(audioInputVatlib->getVatLocalInputCodec(), 0, audioMixerVatlib->getVatAudioMixer(), inputPort); - } - - void CVoiceVatlib::connectVoice(IVoiceChannel *channel, IAudioMixer *mixer, IAudioMixer::InputPort inputPort) - { - auto voiceChannelVatlib = qobject_cast(channel); - Q_ASSERT_X(voiceChannelVatlib, "CVoiceVatlib::connectVoice", "No valid CVoiceChannelVatlib pointer."); - - auto audioMixerVatlib = qobject_cast(mixer); - Q_ASSERT_X(audioMixerVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioMixerVatlib pointer."); - - Vat_ConnectProducerConsumerToProducerConsumer(voiceChannelVatlib->getVoiceChannel(), 0, audioMixerVatlib->getVatAudioMixer(), inputPort); - } - - void CVoiceVatlib::connectVoice(IAudioMixer *mixer, IAudioMixer::OutputPort outputPort, IAudioOutputDevice *device) - { - auto audioMixerVatlib = qobject_cast(mixer); - Q_ASSERT_X(audioMixerVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioMixerVatlib pointer."); - - auto audioDeviceVatlib = qobject_cast(device); - Q_ASSERT_X(audioDeviceVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioOutputDeviceVatlib pointer."); - - Vat_ConnectProducerConsumerToConsumer(audioMixerVatlib->getVatAudioMixer(), outputPort, audioDeviceVatlib->getVatLocalOutputCodec(), 0); - } - - void CVoiceVatlib::connectVoice(IAudioMixer *mixer, IAudioMixer::OutputPort outputPort, IVoiceChannel *channel) - { - auto audioMixerVatlib = qobject_cast(mixer); - Q_ASSERT_X(audioMixerVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioMixerVatlib pointer."); - - auto voiceChannelVatlib = qobject_cast(channel); - Q_ASSERT_X(voiceChannelVatlib, "CVoiceVatlib::connectVoice", "No valid CVoiceChannelVatlib pointer."); - - Vat_ConnectProducerConsumerToProducerConsumer(audioMixerVatlib->getVatAudioMixer(), outputPort, voiceChannelVatlib->getVoiceChannel(), 0); - } - - void CVoiceVatlib::disconnectVoice(IAudioInputDevice *device) - { - auto audioInputVatlib = qobject_cast(device); - Q_ASSERT_X(audioInputVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioInputDeviceVatlib pointer."); - Vat_ConnectProducerToConsumer(audioInputVatlib->getVatLocalInputCodec(), 0, nullptr, 0); - } - - void CVoiceVatlib::disconnectVoice(IVoiceChannel *channel) - { - auto voiceChannelVatlib = qobject_cast(channel); - Q_ASSERT_X(voiceChannelVatlib, "CVoiceVatlib::connectVoice", "No valid CVoiceChannelVatlib pointer."); - Vat_ConnectProducerConsumerToConsumer(voiceChannelVatlib->getVoiceChannel(), 0, nullptr, 0); - } - - void CVoiceVatlib::disconnectVoice(IAudioMixer *mixer, IAudioMixer::OutputPort outputPort) - { - auto audioMixerVatlib = qobject_cast(mixer); - Q_ASSERT_X(audioMixerVatlib, "CVoiceVatlib::connectVoice", "No valid CAudioMixerVatlib pointer."); - Vat_ConnectProducerConsumerToConsumer(audioMixerVatlib->getVatAudioMixer(), outputPort, nullptr, 0); - } - - /* - * Process voice handling - */ - void CVoiceVatlib::timerEvent(QTimerEvent *) - { - Q_ASSERT_X(m_audioService, "CVoiceVatlib", "VatAudioService invalid!"); - Vat_ExecuteTasks(m_audioService.data()); - } - - void CVoiceVatlib::voiceLogHandler(SeverityLevel /** severity **/, const char *message) - { - CLogMessage(static_cast(nullptr)).error(message); - } - -} // namespace diff --git a/src/blackcore/voicevatlib.h b/src/blackcore/voicevatlib.h deleted file mode 100644 index 64d515eb6..000000000 --- a/src/blackcore/voicevatlib.h +++ /dev/null @@ -1,123 +0,0 @@ -/* Copyright (C) 2013 - * swift Project Community / Contributors - * - * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level - * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, - * including this file, may be copied, modified, propagated, or distributed except according to the terms - * contained in the LICENSE file. - */ - -//! \file - -#ifndef BLACKCORE_VOICE_VATLIB_H -#define BLACKCORE_VOICE_VATLIB_H - -#include "blackcore/audiomixer.h" -#include "blackcore/blackcoreexport.h" -#include "blackcore/voice.h" - -#include -#include -#include -#include -#include - -class QTimerEvent; - -struct VatAudioService_tag; -struct VatUDPAudioPort_tag; -template class QSharedPointer; - -#ifdef Q_OS_WIN -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include -#endif - -namespace BlackCore -{ - class IAudioInputDevice; - class IAudioOutputDevice; - class IVoiceChannel; - - //! Vatlib implementation of the IVoice interface. - class BLACKCORE_EXPORT CVoiceVatlib : public IVoice - { - Q_OBJECT - - public: - - //! Default constructor - CVoiceVatlib(QObject *parent = nullptr); - - //! Destructor - virtual ~CVoiceVatlib(); - - //! \copydoc IVoice::createVoiceChannel() - virtual QSharedPointer createVoiceChannel() override; - - //! \copydoc IVoice::createInputDevice() - virtual std::unique_ptr createInputDevice() override; - - //! \copydoc IVoice::createOutputDevice() - virtual std::unique_ptr createOutputDevice() override; - - //! \copydoc IVoice::createAudioMixer() - virtual std::unique_ptr createAudioMixer() override; - - //! \copydoc IVoice::connectVoice() - virtual void connectVoice(IAudioInputDevice *device, IAudioMixer *mixer, IAudioMixer::InputPort inputPort) override; - - //! \copydoc IVoice::connectVoice() - virtual void connectVoice(IVoiceChannel *channel, IAudioMixer *mixer, IAudioMixer::InputPort inputPort) override; - - //! \copydoc IVoice::connectVoice() - virtual void connectVoice(IAudioMixer *mixer, IAudioMixer::OutputPort outputPort, IAudioOutputDevice *device) override; - - //! \copydoc IVoice::connectVoice() - virtual void connectVoice(IAudioMixer *mixer, IAudioMixer::OutputPort outputPort, IVoiceChannel *channel) override; - - //! \copydoc IVoice::disconnectVoice() - void disconnectVoice(IAudioInputDevice *device) override; - - //! \copydoc IVoice::disconnectVoice() - void disconnectVoice(IVoiceChannel *channel) override; - - //! \copydoc IVoice::disconnectVoice() - void disconnectVoice(IAudioMixer *mixer, IAudioMixer::OutputPort outputPort) override; - - protected: // QObject overrides - - //! Process voice lib - virtual void timerEvent(QTimerEvent *) override; - - private: - - // this struct calls "myCustomDeallocator" to delete the pointer - struct VatAudioServiceDeleter - { - static inline void cleanup(VatAudioService_tag *obj) - { - Vat_DestroyAudioService(obj); - } - }; - - struct VatUDPAudioPortDeleter - { - static inline void cleanup(VatUDPAudioPort_tag *obj) - { - Vat_DestroyUDPAudioPort(obj); - } - }; - - static void voiceLogHandler(SeverityLevel severity, const char *message); - - QScopedPointer m_audioService; - QScopedPointer m_udpPort; - - }; - -} // namespace - -#endif // guard diff --git a/tests/blackcore/testnetwork.h b/tests/blackcore/testnetwork.h index f890fd454..3ecb3b21c 100644 --- a/tests/blackcore/testnetwork.h +++ b/tests/blackcore/testnetwork.h @@ -14,7 +14,7 @@ //! \file //! \ingroup testblackcore -#include "blackcore/networkvatlib.h" +#include "blackcore/vatsim/networkvatlib.h" #include "blackmisc/simulation/ownaircraftproviderdummy.h" #include @@ -47,7 +47,7 @@ namespace BlackCoreTest //! Common part used by all tests. void networkTest(BlackCore::INetwork *); - BlackCore::CNetworkVatlib m_networkVatlib ; //!< vatlib instance + BlackCore::Vatsim::CNetworkVatlib m_networkVatlib ; //!< vatlib instance //! Test if server is available static bool pingServer(const BlackMisc::Network::CServer &server);