Add a network reachability monitor class

This commit is contained in:
Michael Zanetti 2022-09-05 21:56:54 +02:00
parent 76f52acb33
commit 2633bc4e40
5 changed files with 159 additions and 102 deletions

View File

@ -0,0 +1,105 @@
#include "networkreachabilitymonitor.h"
#include <QGuiApplication>
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(dcNymeaConnection)
NetworkReachabilityMonitor::NetworkReachabilityMonitor(QObject *parent)
: QObject{parent}
{
m_networkConfigManager = new QNetworkConfigurationManager(this);
QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationAdded, this, [this](const QNetworkConfiguration &config){
Q_UNUSED(config)
qCDebug(dcNymeaConnection()) << "Network configuration added:" << config.name() << config.bearerTypeName() << config.purpose();
updateActiveBearers();
});
QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationRemoved, this, [this](const QNetworkConfiguration &config){
Q_UNUSED(config)
qCDebug(dcNymeaConnection()) << "Network configuration removed:" << config.name() << config.bearerTypeName() << config.purpose();
updateActiveBearers();
});
QGuiApplication *app = static_cast<QGuiApplication*>(QGuiApplication::instance());
QObject::connect(app, &QGuiApplication::applicationStateChanged, this, [app, this](Qt::ApplicationState state) {
qCDebug(dcNymeaConnection()) << "Application state changed to:" << state;
updateActiveBearers();
});
updateActiveBearers();
}
NymeaConnection::BearerTypes NetworkReachabilityMonitor::availableBearerTypes() const
{
return m_availableBearerTypes;
}
void NetworkReachabilityMonitor::updateActiveBearers()
{
NymeaConnection::BearerTypes availableBearerTypes;
QList<QNetworkConfiguration> configs = m_networkConfigManager->allConfigurations(QNetworkConfiguration::Active);
qCDebug(dcNymeaConnection()) << "Network configuations:" << configs.count();
foreach (const QNetworkConfiguration &config, configs) {
qCDebug(dcNymeaConnection()) << "Active network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName();
// NOTE: iOS doesn't correctly report bearer types. It'll be Unknown all the time. Let's hardcode it to WiFi for that...
#if defined(Q_OS_IOS)
availableBearerTypes.setFlag(NymeaConnection::BearerTypeWiFi);
#else
availableBearerTypes.setFlag(qBearerTypeToNymeaBearerType(config.bearerType()));
#endif
}
if (availableBearerTypes == NymeaConnection::BearerTypeNone) {
// This is just debug info... On some platform bearer management seems a bit broken, so let's get some infos right away...
qCDebug(dcNymeaConnection()) << "No active bearer available. Inactive bearers are:";
QList<QNetworkConfiguration> configs = m_networkConfigManager->allConfigurations();
foreach (const QNetworkConfiguration &config, configs) {
qCDebug(dcNymeaConnection()) << "Inactive network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName();
}
qCDebug(dcNymeaConnection()) << "Updating network manager";
m_networkConfigManager->updateConfigurations();
}
if (m_availableBearerTypes != availableBearerTypes) {
qCInfo(dcNymeaConnection()) << "Available Bearer Types changed to:" << availableBearerTypes;
m_availableBearerTypes = availableBearerTypes;
emit availableBearerTypesChanged();
} else {
qCDebug(dcNymeaConnection()) << "Available Bearer Types:" << availableBearerTypes;
}
emit availableBearerTypesUpdated();
}
NymeaConnection::BearerType NetworkReachabilityMonitor::qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const
{
switch (type) {
case QNetworkConfiguration::BearerUnknown:
// Unable to determine the connection type. Assume it's something we can establish any connection type on
return NymeaConnection::BearerTypeAll;
case QNetworkConfiguration::BearerEthernet:
return NymeaConnection::BearerTypeEthernet;
case QNetworkConfiguration::BearerWLAN:
return NymeaConnection::BearerTypeWiFi;
case QNetworkConfiguration::Bearer2G:
case QNetworkConfiguration::BearerCDMA2000:
case QNetworkConfiguration::BearerWCDMA:
case QNetworkConfiguration::BearerHSPA:
case QNetworkConfiguration::BearerWiMAX:
case QNetworkConfiguration::BearerEVDO:
case QNetworkConfiguration::BearerLTE:
case QNetworkConfiguration::Bearer3G:
case QNetworkConfiguration::Bearer4G:
return NymeaConnection::BearerTypeMobileData;
case QNetworkConfiguration::BearerBluetooth:
// Note: Do not confuse this with the Bluetooth transport... For Qt, this means IP over BT, not RFCOMM as we do it.
return NymeaConnection::BearerTypeNone;
}
return NymeaConnection::BearerTypeAll;
}

View File

@ -0,0 +1,34 @@
#ifndef NETWORKREACHABILITYMONITOR_H
#define NETWORKREACHABILITYMONITOR_H
#include <QObject>
#include <QNetworkConfigurationManager>
#include "nymeaconnection.h"
class NetworkReachabilityMonitor : public QObject
{
Q_OBJECT
Q_PROPERTY(NymeaConnection::BearerTypes availableBearerTypes READ availableBearerTypes NOTIFY availableBearerTypesChanged)
public:
explicit NetworkReachabilityMonitor(QObject *parent = nullptr);
NymeaConnection::BearerTypes availableBearerTypes() const;
signals:
void availableBearerTypesChanged();
void availableBearerTypesUpdated(); // Does not necessarily mean they changed, but they're reasonably up to date now.
private slots:
void updateActiveBearers();
private:
NymeaConnection::BearerType qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const;
private:
QNetworkConfigurationManager *m_networkConfigManager = nullptr;
NymeaConnection::BearerTypes m_availableBearerTypes = NymeaConnection::BearerTypeNone;
};
#endif // NETWORKREACHABILITYMONITOR_H

View File

@ -43,6 +43,7 @@
#include <QTimer> #include <QTimer>
#include <QGuiApplication> #include <QGuiApplication>
#include "networkreachabilitymonitor.h"
#include "nymeatransportinterface.h" #include "nymeatransportinterface.h"
#include "logging.h" #include "logging.h"
@ -50,18 +51,9 @@ NYMEA_LOGGING_CATEGORY(dcNymeaConnection, "NymeaConnection")
NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent) NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent)
{ {
m_networkConfigManager = new QNetworkConfigurationManager(this); m_networkReachabilityMonitor = new NetworkReachabilityMonitor(this);
connect(m_networkReachabilityMonitor, &NetworkReachabilityMonitor::availableBearerTypesChanged, this, &NymeaConnection::availableBearerTypesChanged);
QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationAdded, this, [this](const QNetworkConfiguration &config){ connect(m_networkReachabilityMonitor, &NetworkReachabilityMonitor::availableBearerTypesUpdated, this, &NymeaConnection::onAvailableBearerTypesUpdated);
Q_UNUSED(config)
qCDebug(dcNymeaConnection()) << "Network configuration added:" << config.name() << config.bearerTypeName() << config.purpose();
updateActiveBearers();
});
QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationRemoved, this, [this](const QNetworkConfiguration &config){
Q_UNUSED(config)
qCDebug(dcNymeaConnection()) << "Network configuration removed:" << config.name() << config.bearerTypeName() << config.purpose();
updateActiveBearers();
});
QGuiApplication *app = static_cast<QGuiApplication*>(QGuiApplication::instance()); QGuiApplication *app = static_cast<QGuiApplication*>(QGuiApplication::instance());
QObject::connect(app, &QGuiApplication::applicationStateChanged, this, [app, this](Qt::ApplicationState state) { QObject::connect(app, &QGuiApplication::applicationStateChanged, this, [app, this](Qt::ApplicationState state) {
@ -79,12 +71,8 @@ NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent)
} }
} }
} }
updateActiveBearers();
}); });
updateActiveBearers();
m_reconnectTimer.setInterval(500); m_reconnectTimer.setInterval(500);
m_reconnectTimer.setSingleShot(true); m_reconnectTimer.setSingleShot(true);
connect(&m_reconnectTimer, &QTimer::timeout, this, [this](){ connect(&m_reconnectTimer, &QTimer::timeout, this, [this](){
@ -108,7 +96,7 @@ NymeaConnection::~NymeaConnection()
NymeaConnection::BearerTypes NymeaConnection::availableBearerTypes() const NymeaConnection::BearerTypes NymeaConnection::availableBearerTypes() const
{ {
return m_availableBearerTypes; return m_networkReachabilityMonitor->availableBearerTypes();
} }
bool NymeaConnection::connected() bool NymeaConnection::connected()
@ -391,41 +379,8 @@ void NymeaConnection::onDataAvailable(const QByteArray &data)
} }
} }
void NymeaConnection::updateActiveBearers() void NymeaConnection::onAvailableBearerTypesUpdated()
{ {
NymeaConnection::BearerTypes availableBearerTypes;
QList<QNetworkConfiguration> configs = m_networkConfigManager->allConfigurations(QNetworkConfiguration::Active);
qCDebug(dcNymeaConnection()) << "Network configuations:" << configs.count();
foreach (const QNetworkConfiguration &config, configs) {
qCDebug(dcNymeaConnection()) << "Active network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName();
// NOTE: iOS doesn't correctly report bearer types. It'll be Unknown all the time. Let's hardcode it to WiFi for that...
#if defined(Q_OS_IOS)
availableBearerTypes.setFlag(NymeaConnection::BearerTypeWiFi);
#else
availableBearerTypes.setFlag(qBearerTypeToNymeaBearerType(config.bearerType()));
#endif
}
if (availableBearerTypes == NymeaConnection::BearerTypeNone) {
// This is just debug info... On some platform bearer management seems a bit broken, so let's get some infos right away...
qCDebug(dcNymeaConnection()) << "No active bearer available. Inactive bearers are:";
QList<QNetworkConfiguration> configs = m_networkConfigManager->allConfigurations();
foreach (const QNetworkConfiguration &config, configs) {
qCDebug(dcNymeaConnection()) << "Inactive network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName();
}
qCDebug(dcNymeaConnection()) << "Updating network manager";
m_networkConfigManager->updateConfigurations();
}
if (m_availableBearerTypes != availableBearerTypes) {
qCInfo(dcNymeaConnection()) << "Available Bearer Types changed to:" << availableBearerTypes;
m_availableBearerTypes = availableBearerTypes;
emit availableBearerTypesChanged();
} else {
qCDebug(dcNymeaConnection()) << "Available Bearer Types:" << availableBearerTypes;
}
if (!m_currentHost) { if (!m_currentHost) {
// No host set... Nothing to do... // No host set... Nothing to do...
qCInfo(dcNymeaConnection()) << "No current host... Nothing to do..."; qCInfo(dcNymeaConnection()) << "No current host... Nothing to do...";
@ -490,8 +445,8 @@ void NymeaConnection::connectInternal(NymeaHost *host)
qCDebug(dcNymeaConnection()) << "Best candidate Loopback connection:" << loopbackConnection->url(); qCDebug(dcNymeaConnection()) << "Best candidate Loopback connection:" << loopbackConnection->url();
connectInternal(loopbackConnection); connectInternal(loopbackConnection);
} else if (m_availableBearerTypes.testFlag(NymeaConnection::BearerTypeWiFi) } else if (m_networkReachabilityMonitor->availableBearerTypes().testFlag(NymeaConnection::BearerTypeWiFi)
|| m_availableBearerTypes.testFlag(NymeaConnection::BearerTypeEthernet)) { || m_networkReachabilityMonitor->availableBearerTypes().testFlag(NymeaConnection::BearerTypeEthernet)) {
Connection* lanConnection = host->connections()->bestMatch(Connection::BearerTypeLan | Connection::BearerTypeWan); Connection* lanConnection = host->connections()->bestMatch(Connection::BearerTypeLan | Connection::BearerTypeWan);
if (lanConnection) { if (lanConnection) {
qCDebug(dcNymeaConnection()) << "Best candidate LAN/WAN connection:" << lanConnection->url(); qCDebug(dcNymeaConnection()) << "Best candidate LAN/WAN connection:" << lanConnection->url();
@ -500,7 +455,7 @@ void NymeaConnection::connectInternal(NymeaHost *host)
qCDebug(dcNymeaConnection()) << "No available LAN/WAN connection to" << host->name(); qCDebug(dcNymeaConnection()) << "No available LAN/WAN connection to" << host->name();
} }
} else if (m_availableBearerTypes.testFlag(NymeaConnection::BearerTypeMobileData)) { } else if (m_networkReachabilityMonitor->availableBearerTypes().testFlag(NymeaConnection::BearerTypeMobileData)) {
Connection* wanConnection = host->connections()->bestMatch(Connection::BearerTypeWan); Connection* wanConnection = host->connections()->bestMatch(Connection::BearerTypeWan);
if (wanConnection) { if (wanConnection) {
qCDebug(dcNymeaConnection()) << "Best candidate WAN connection:" << wanConnection->url(); qCDebug(dcNymeaConnection()) << "Best candidate WAN connection:" << wanConnection->url();
@ -547,61 +502,24 @@ bool NymeaConnection::connectInternal(Connection *connection)
QObject::connect(newTransport, &NymeaTransportInterface::disconnected, this, &NymeaConnection::onDisconnected); QObject::connect(newTransport, &NymeaTransportInterface::disconnected, this, &NymeaConnection::onDisconnected);
QObject::connect(newTransport, &NymeaTransportInterface::dataReady, this, &NymeaConnection::onDataAvailable, Qt::QueuedConnection); QObject::connect(newTransport, &NymeaTransportInterface::dataReady, this, &NymeaConnection::onDataAvailable, Qt::QueuedConnection);
// // Load any certificate we might have for this url
// QByteArray pem;
// if (loadPem(connection->url(), pem)) {
// qDebug() << "Loaded SSL certificate for" << connection->url().host();
// QList<QSslError> expectedSslErrors;
// expectedSslErrors.append(QSslError::HostNameMismatch);
// expectedSslErrors.append(QSslError(QSslError::SelfSignedCertificate, QSslCertificate(pem)));
// newTransport->ignoreSslErrors(expectedSslErrors);
// }
m_transportCandidates.insert(newTransport, connection); m_transportCandidates.insert(newTransport, connection);
qCInfo(dcNymeaConnection()) << "Connecting to:" << connection->url() << newTransport << m_transportCandidates.value(newTransport); qCInfo(dcNymeaConnection()) << "Connecting to:" << connection->url() << newTransport << m_transportCandidates.value(newTransport);
return newTransport->connect(connection->url()); return newTransport->connect(connection->url());
} }
NymeaConnection::BearerType NymeaConnection::qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const
{
switch (type) {
case QNetworkConfiguration::BearerUnknown:
// Unable to determine the connection type. Assume it's something we can establish any connection type on
return BearerTypeAll;
case QNetworkConfiguration::BearerEthernet:
return BearerTypeEthernet;
case QNetworkConfiguration::BearerWLAN:
return BearerTypeWiFi;
case QNetworkConfiguration::Bearer2G:
case QNetworkConfiguration::BearerCDMA2000:
case QNetworkConfiguration::BearerWCDMA:
case QNetworkConfiguration::BearerHSPA:
case QNetworkConfiguration::BearerWiMAX:
case QNetworkConfiguration::BearerEVDO:
case QNetworkConfiguration::BearerLTE:
case QNetworkConfiguration::Bearer3G:
case QNetworkConfiguration::Bearer4G:
return BearerTypeMobileData;
case QNetworkConfiguration::BearerBluetooth:
// Note: Do not confuse this with the Bluetooth transport... For Qt, this means IP over BT, not RFCOMM as we do it.
return BearerTypeNone;
}
return BearerTypeAll;
}
bool NymeaConnection::isConnectionBearerAvailable(Connection::BearerType connectionBearerType) const bool NymeaConnection::isConnectionBearerAvailable(Connection::BearerType connectionBearerType) const
{ {
switch (connectionBearerType) { switch (connectionBearerType) {
case Connection::BearerTypeLan: case Connection::BearerTypeLan:
return m_availableBearerTypes.testFlag(BearerTypeEthernet) return availableBearerTypes().testFlag(BearerTypeEthernet)
|| m_availableBearerTypes.testFlag(BearerTypeWiFi); || availableBearerTypes().testFlag(BearerTypeWiFi);
case Connection::BearerTypeWan: case Connection::BearerTypeWan:
case Connection::BearerTypeCloud: case Connection::BearerTypeCloud:
return m_availableBearerTypes.testFlag(BearerTypeEthernet) return availableBearerTypes().testFlag(BearerTypeEthernet)
|| m_availableBearerTypes.testFlag(BearerTypeWiFi) || availableBearerTypes().testFlag(BearerTypeWiFi)
|| m_availableBearerTypes.testFlag(BearerTypeMobileData); || availableBearerTypes().testFlag(BearerTypeMobileData);
case Connection::BearerTypeBluetooth: case Connection::BearerTypeBluetooth:
return m_availableBearerTypes.testFlag(BearerTypeBluetooth); return availableBearerTypes().testFlag(BearerTypeBluetooth);
case Connection::BearerTypeUnknown: case Connection::BearerTypeUnknown:
return true; return true;
case Connection::BearerTypeNone: case Connection::BearerTypeNone:

View File

@ -43,6 +43,7 @@
class NymeaTransportInterface; class NymeaTransportInterface;
class NymeaTransportInterfaceFactory; class NymeaTransportInterfaceFactory;
class NetworkReachabilityMonitor;
class NymeaConnection : public QObject class NymeaConnection : public QObject
{ {
@ -121,20 +122,17 @@ private slots:
void onDisconnected(); void onDisconnected();
void onDataAvailable(const QByteArray &data); void onDataAvailable(const QByteArray &data);
void updateActiveBearers(); void onAvailableBearerTypesUpdated();
void hostConnectionsUpdated(); void hostConnectionsUpdated();
private: private:
void connectInternal(NymeaHost *host); void connectInternal(NymeaHost *host);
bool connectInternal(Connection *connection); bool connectInternal(Connection *connection);
NymeaConnection::BearerType qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const;
bool isConnectionBearerAvailable(Connection::BearerType connectionBearerType) const; bool isConnectionBearerAvailable(Connection::BearerType connectionBearerType) const;
private: private:
ConnectionStatus m_connectionStatus = ConnectionStatusUnconnected; ConnectionStatus m_connectionStatus = ConnectionStatusUnconnected;
QNetworkConfigurationManager *m_networkConfigManager = nullptr; NetworkReachabilityMonitor *m_networkReachabilityMonitor = nullptr;
NymeaConnection::BearerTypes m_availableBearerTypes = BearerTypeNone;
QHash<QString, NymeaTransportInterfaceFactory*> m_transportFactories; QHash<QString, NymeaTransportInterfaceFactory*> m_transportFactories;
QHash<NymeaTransportInterface*, Connection*> m_transportCandidates; QHash<NymeaTransportInterface*, Connection*> m_transportCandidates;

View File

@ -21,6 +21,7 @@ INCLUDEPATH += \
SOURCES += \ SOURCES += \
$$PWD/appdata.cpp \ $$PWD/appdata.cpp \
$$PWD/connection/networkreachabilitymonitor.cpp \
$$PWD/energy/energylogs.cpp \ $$PWD/energy/energylogs.cpp \
$$PWD/energy/energymanager.cpp \ $$PWD/energy/energymanager.cpp \
$$PWD/energy/powerbalancelogs.cpp \ $$PWD/energy/powerbalancelogs.cpp \
@ -186,6 +187,7 @@ SOURCES += \
HEADERS += \ HEADERS += \
$$PWD/appdata.h \ $$PWD/appdata.h \
$$PWD/connection/networkreachabilitymonitor.h \
$$PWD/energy/energylogs.h \ $$PWD/energy/energylogs.h \
$$PWD/energy/energymanager.h \ $$PWD/energy/energymanager.h \
$$PWD/energy/powerbalancelogs.h \ $$PWD/energy/powerbalancelogs.h \