[AFV] Use heartbeat to re-connect to AFV

* some utility functions
* reset values after disconnect
This commit is contained in:
Klaus Basan
2020-04-30 22:02:13 +02:00
committed by Mat Sutcliffe
parent fccaf1fae1
commit 1e633a5704
7 changed files with 120 additions and 15 deletions

View File

@@ -180,6 +180,7 @@ namespace BlackCore
this->retryConnectTo(cid, password, callsign, client, QStringLiteral("No connection afer 20secs")); this->retryConnectTo(cid, password, callsign, client, QStringLiteral("No connection afer 20secs"));
}); });
} }
// thread safe connect // thread safe connect
{ {
QMutexLocker lock(&m_mutexConnection); QMutexLocker lock(&m_mutexConnection);
@@ -235,6 +236,11 @@ namespace BlackCore
QMutexLocker lock(&m_mutexConnection); QMutexLocker lock(&m_mutexConnection);
m_connection->disconnectFrom(); m_connection->disconnectFrom();
} }
m_heartBeatFailures = 0;
m_retryConnectAttempt = 0;
m_fsdConnectMismatches = 0;
emit connectionStatusChanged(Disconnected); emit connectionStatusChanged(Disconnected);
if (stop) { this->stopAudio(); } if (stop) { this->stopAudio(); }
@@ -840,8 +846,8 @@ namespace BlackCore
if (loopback && transmit) if (loopback && transmit)
{ {
IAudioDto audioData; IAudioDto audioData;
audioData.audio = QByteArray(args.audio.data(), args.audio.size()); audioData.audio = QByteArray(args.audio.data(), args.audio.size());
audioData.callsign = QStringLiteral("loopback"); audioData.callsign = QStringLiteral("loopback");
audioData.lastPacket = false; audioData.lastPacket = false;
audioData.sequenceCounter = 0; audioData.sequenceCounter = 0;
@@ -1023,6 +1029,47 @@ namespace BlackCore
// for AFV sample client // for AFV sample client
this->updateTransceivers(); this->updateTransceivers();
} }
// connection check
this->checkServerHeartbeat();
}
void CAfvClient::checkServerHeartbeat()
{
if (!this->isStarted()) { return; }
if (!this->isConnected()) { return; }
if (this->isVoiceServerAlive())
{
m_heartBeatFailures = 0;
return;
}
// Heartbeat failure
// it can happen that after connect we see an initial timeout
const int failures = ++m_heartBeatFailures;
if (failures < 2) { return; }
QString un, pw, cs, client;
{
QMutexLocker lock(&m_mutexConnection);
un = m_connection->getUserName();
pw = m_connection->getPassword();
cs = m_connection->getCallsign();
client = m_connection->getClient();
}
if (un.isEmpty() || pw.isEmpty()) { return; }
// make sure we are disconnected
if (this->isConnected()) { this->disconnectFrom(false); }
QPointer<CAfvClient> myself(this);
QTimer::singleShot(5 * 1000, this, [ = ]
{
if (!myself) { return; }
const QString reason = QStringLiteral("Heartbeat failed %1 times").arg(failures);
this->retryConnectTo(un, pw, cs, client, reason);
});
} }
void CAfvClient::onSettingsChanged() void CAfvClient::onSettingsChanged()
@@ -1263,6 +1310,21 @@ namespace BlackCore
return roundedFrequencyHz; return roundedFrequencyHz;
} }
bool CAfvClient::isVoiceServerAlive() const
{
QMutexLocker lock(&m_mutexConnection);
return m_connection && m_connection->isVoiceServerAlive();
}
const QString &CAfvClient::getVoiceServerUrl() const
{
QMutexLocker lock(&m_mutexConnection);
static const QString e;
if (!m_connection) { return e; }
return m_connection->getVoiceServerUrl();
}
bool CAfvClient::fuzzyMatchCallsign(const QString &callsign, const QString &compareTo) const bool CAfvClient::fuzzyMatchCallsign(const QString &callsign, const QString &compareTo) const
{ {
if (callsign.isEmpty() || compareTo.isEmpty()) { return false; } // empty callsigns should NOT match if (callsign.isEmpty() || compareTo.isEmpty()) { return false; } // empty callsigns should NOT match

View File

@@ -133,7 +133,7 @@ namespace BlackCore
//! \threadsafe //! \threadsafe
bool isComUnitIntegrated() const { return m_integratedComUnit; } bool isComUnitIntegrated() const { return m_integratedComUnit; }
/* /* NOT used
//! The device's volume 0..1 @{ //! The device's volume 0..1 @{
double getDeviceInputVolume() const; double getDeviceInputVolume() const;
bool setDeviceInputVolume(double volume); bool setDeviceInputVolume(double volume);
@@ -180,7 +180,7 @@ namespace BlackCore
//! Get transceivers //! Get transceivers
//! \threadsafe //! \threadsafe
//! @{ //! @{
QVector<TransceiverDto> getTransceivers() const; QVector<TransceiverDto> getTransceivers() const;
QVector<TxTransceiverDto> getTransmittingTransceivers() const; QVector<TxTransceiverDto> getTransmittingTransceivers() const;
QSet<quint16> getEnabledTransceivers() const; QSet<quint16> getEnabledTransceivers() const;
//! @} //! @}
@@ -352,6 +352,14 @@ namespace BlackCore
//! \threadsafe //! \threadsafe
quint32 getAliasFrequencyHz(quint32 frequencyHz) const; quint32 getAliasFrequencyHz(quint32 frequencyHz) const;
//! Voice server alive
//! \threadsafe
bool isVoiceServerAlive() const;
//! Get voice server URL
//! \threadsafe
const QString &getVoiceServerUrl() const;
bool fuzzyMatchCallsign(const QString &callsign, const QString &compareTo) const; bool fuzzyMatchCallsign(const QString &callsign, const QString &compareTo) const;
void getPrefixSuffix(const QString &callsign, QString &prefix, QString &suffix) const; void getPrefixSuffix(const QString &callsign, QString &prefix, QString &suffix) const;
@@ -385,10 +393,11 @@ namespace BlackCore
static const QVector<quint16> &allTransceiverIds() { static const QVector<quint16> transceiverIds{0, 1}; return transceiverIds; } static const QVector<quint16> &allTransceiverIds() { static const QVector<quint16> transceiverIds{0, 1}; return transceiverIds; }
std::atomic_int m_fsdConnectMismatches { 0 }; //!< FSD no longer connected? std::atomic_int m_fsdConnectMismatches { 0 }; //!< FSD no longer connected?
std::atomic_int m_retryConnectAttempt { 0 }; //!< Try to connect the n-th time std::atomic_int m_retryConnectAttempt { 0 }; //!< try to connect the n-th time
std::atomic_bool m_isStarted { false }; std::atomic_int m_heartBeatFailures { 0 }; //!< voice server heartbeat failures
std::atomic_bool m_loopbackOn { false }; std::atomic_bool m_isStarted { false };
std::atomic_bool m_enableAliased { true }; std::atomic_bool m_loopbackOn { false };
std::atomic_bool m_enableAliased { true };
std::atomic_bool m_winCoInitialized { false }; //!< Windows only CoInitializeEx std::atomic_bool m_winCoInitialized { false }; //!< Windows only CoInitializeEx
std::atomic_bool m_integratedComUnit { false }; //!< is COM unit sychronized, integrated? std::atomic_bool m_integratedComUnit { false }; //!< is COM unit sychronized, integrated?
@@ -405,6 +414,7 @@ namespace BlackCore
Audio::InputVolumeStreamArgs m_inputVolumeStream; Audio::InputVolumeStreamArgs m_inputVolumeStream;
Audio::OutputVolumeStreamArgs m_outputVolumeStream; Audio::OutputVolumeStreamArgs m_outputVolumeStream;
void checkServerHeartbeat();
void deferredInit(); void deferredInit();
void initTransceivers(); void initTransceivers();
void connectWithContexts(); void connectWithContexts();

View File

@@ -80,6 +80,16 @@ namespace BlackCore
//! Set the URL //! Set the URL
bool setUrl(const QString &url); bool setUrl(const QString &url);
//! Get the URL
const QString &getUrl() const { return m_addressUrl; }
//! User data @{
const QString &getUserName() const { return m_username; }
const QString &getPassword() const { return m_password; }
const QString &getClient() const { return m_client; }
const QUuid &getNetworkVersion() const { return m_networkVersion; }
//! @}
private: private:
//! Post to resource //! Post to resource
template<typename TResponse> template<typename TResponse>

View File

@@ -65,6 +65,7 @@ namespace BlackCore
m_connection.setTokens(m_apiServerConnection->addCallsign(callsign)); m_connection.setTokens(m_apiServerConnection->addCallsign(callsign));
m_connection.setTsAuthenticatedToNow(); m_connection.setTsAuthenticatedToNow();
m_connection.createCryptoChannels(); m_connection.createCryptoChannels();
m_connection.setTsHeartbeatToNow();
this->connectToVoiceServer(); this->connectToVoiceServer();
// taskServerConnectionCheck.Start(); // taskServerConnectionCheck.Start();
@@ -125,6 +126,13 @@ namespace BlackCore
return m_apiServerConnection->setUrl(url); return m_apiServerConnection->setUrl(url);
} }
const QString &CClientConnection::getVoiceServerUrl() const
{
static const QString e;
if (!m_apiServerConnection) { return e; }
return m_apiServerConnection->getUrl();
}
void CClientConnection::connectToVoiceServer() void CClientConnection::connectToVoiceServer()
{ {
const QHostAddress localAddress(QHostAddress::AnyIPv4); const QHostAddress localAddress(QHostAddress::AnyIPv4);

View File

@@ -52,9 +52,11 @@ namespace BlackCore
//! Disconnect //! Disconnect
void disconnectFrom(const QString &reason = {}); void disconnectFrom(const QString &reason = {});
//! Is connected //! Is connected?
bool isConnected() const { return m_connection.isConnected(); } bool isConnected() const { return m_connection.isConnected(); }
//! @}
//! Is alive?
bool isVoiceServerAlive() const { return m_connection.isVoiceServerAlive(); }
//! Receiving audio? @{ //! Receiving audio? @{
void setReceiveAudio(bool value) { m_connection.setReceiveAudio(value); } void setReceiveAudio(bool value) { m_connection.setReceiveAudio(value); }
@@ -89,9 +91,20 @@ namespace BlackCore
//! Update the voice server URL //! Update the voice server URL
bool updateVoiceServerUrl(const QString &url); bool updateVoiceServerUrl(const QString &url);
//! Get the voice server URL
const QString &getVoiceServerUrl() const;
//! Authenticated since when //! Authenticated since when
qint64 secondsSinceAuthentication() const { return m_connection.secondsSinceAuthentication(); } qint64 secondsSinceAuthentication() const { return m_connection.secondsSinceAuthentication(); }
//! User data @{
const QString &getUserName() const { return m_connection.getUserName(); }
const QString &getCallsign() const { return m_connection.getCallsign(); }
const QString &getPassword() const { static const QString e; return m_apiServerConnection ? m_apiServerConnection->getPassword() : e; }
const QString &getClient() const { static const QString e; return m_apiServerConnection ? m_apiServerConnection->getClient() : e; }
const QUuid &getNetworkVersion() const { return m_networkVersion; }
//! @}
signals: signals:
//! Audio has been received //! Audio has been received
void audioReceived(const AudioRxOnTransceiversDto &dto); void audioReceived(const AudioRxOnTransceiversDto &dto);

View File

@@ -32,14 +32,16 @@ namespace BlackCore
bool CClientConnectionData::isVoiceServerAlive() const bool CClientConnectionData::isVoiceServerAlive() const
{ {
return m_lastVoiceServerHeartbeatAckUtc.isValid() && if (!m_lastVoiceServerHeartbeatAckUtc.isValid()) { return false; }
m_lastVoiceServerHeartbeatAckUtc.secsTo(QDateTime::currentDateTimeUtc()) > ServerTimeoutSecs; const qint64 d = qAbs(m_lastVoiceServerHeartbeatAckUtc.secsTo(QDateTime::currentDateTimeUtc()));
return d < ServerTimeoutSecs;
} }
bool CClientConnectionData::isDataServerAlive() const bool CClientConnectionData::isDataServerAlive() const
{ {
return m_lastDataServerHeartbeatAckUtc.isValid() && if (!m_lastDataServerHeartbeatAckUtc.isValid()) { return false; }
m_lastDataServerHeartbeatAckUtc.secsTo(QDateTime::currentDateTimeUtc()) > ServerTimeoutSecs; const qint64 d = qAbs(m_lastDataServerHeartbeatAckUtc.secsTo(QDateTime::currentDateTimeUtc()));
return d < ServerTimeoutSecs;
} }
void CClientConnectionData::createCryptoChannels() void CClientConnectionData::createCryptoChannels()

View File

@@ -42,7 +42,7 @@ namespace BlackCore
//! Servers alive @{ //! Servers alive @{
bool isVoiceServerAlive() const; bool isVoiceServerAlive() const;
bool isDataServerAlive() const; bool isDataServerAlive() const;
//! @} //! @}
//! Is connected? @{ //! Is connected? @{