diff --git a/src/network/device.cpp b/src/network/device.cpp index 7abe971b..ce5cf665 100644 --- a/src/network/device.cpp +++ b/src/network/device.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "../core/logcat.hpp" #include "enums.hpp" @@ -33,6 +34,11 @@ void NetworkDevice::setNmManaged(bool managed) { emit this->requestSetNmManaged(managed); } +void NetworkDevice::setRefreshRate(quint32 refreshRate) { + if (this->bRefreshRate == refreshRate) return; + emit this->requestSetRefreshRate(refreshRate); +} + void NetworkDevice::disconnect() { if (this->bState == ConnectionState::Disconnected) { qCCritical(logNetworkDevice) << "Device" << this << "is already disconnected"; diff --git a/src/network/device.hpp b/src/network/device.hpp index 385d2bad..3aee19c7 100644 --- a/src/network/device.hpp +++ b/src/network/device.hpp @@ -45,6 +45,16 @@ class NetworkDevice: public QObject { Q_PROPERTY(bool nmManaged READ nmManaged WRITE setNmManaged NOTIFY nmManagedChanged) /// True if the device is allowed to autoconnect to a network. Q_PROPERTY(bool autoconnect READ autoconnect WRITE setAutoconnect NOTIFY autoconnectChanged); + /// Refresh rate of the @@rxBytes and @@txBytes in milliseconds. + /// + /// > [!NOTE] @@rxBytes and @@txBytes are guaranteed to be refreshed each @@refreshRate milliseconds in the case the underlying counter has changed too. + /// > If zero, there is no guaranteed refresh rate of the properties. + Q_PROPERTY(quint32 refreshRate READ default WRITE setRefreshRate NOTIFY refreshRateChanged BINDABLE bindableRefreshRate); + /// Number of bytes this device received. + Q_PROPERTY(quint64 rxBytes READ default NOTIFY rxBytesChanged BINDABLE bindableRxBytes); + /// Number of bytes this device transmitted. + Q_PROPERTY(quint64 txBytes READ default NOTIFY txBytesChanged BINDABLE bindableTxBytes); + // clang-format on public: @@ -69,17 +79,25 @@ class NetworkDevice: public QObject { QBindable bindableAutoconnect() { return &this->bAutoconnect; } [[nodiscard]] bool autoconnect() { return this->bAutoconnect; } void setAutoconnect(bool autoconnect); + QBindable bindableRefreshRate() { return &this->bRefreshRate; } + void setRefreshRate(quint32 refreshRate); + QBindable bindableRxBytes() { return &this->bRxBytes; } + QBindable bindableTxBytes() { return &this->bTxBytes; } signals: QSDOC_HIDE void requestDisconnect(); QSDOC_HIDE void requestSetAutoconnect(bool autoconnect); QSDOC_HIDE void requestSetNmManaged(bool managed); + QSDOC_HIDE void requestSetRefreshRate(quint32 refreshRate); void nameChanged(); void addressChanged(); void connectedChanged(); void stateChanged(); void nmManagedChanged(); void autoconnectChanged(); + void refreshRateChanged(); + void rxBytesChanged(); + void txBytesChanged(); protected: ObjectModel mNetworks {this}; @@ -93,6 +111,9 @@ class NetworkDevice: public QObject { Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, ConnectionState::Enum, bState, &NetworkDevice::stateChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, bool, bNmManaged, &NetworkDevice::nmManagedChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, bool, bAutoconnect, &NetworkDevice::autoconnectChanged); + Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, quint32, bRefreshRate, &NetworkDevice::refreshRateChanged); + Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, quint64, bRxBytes, &NetworkDevice::rxBytesChanged); + Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, quint64, bTxBytes, &NetworkDevice::txBytesChanged); // clang-format on }; diff --git a/src/network/nm/CMakeLists.txt b/src/network/nm/CMakeLists.txt index 8a3cac87..88fa31c4 100644 --- a/src/network/nm/CMakeLists.txt +++ b/src/network/nm/CMakeLists.txt @@ -19,6 +19,16 @@ qt_add_dbus_interface(NM_DBUS_INTERFACES dbus_nm_device ) +set_source_files_properties(org.freedesktop.NetworkManager.Device.Statistics.xml PROPERTIES + CLASSNAME DBusNMDeviceStatisticsProxy + NO_NAMESPACE TRUE +) + +qt_add_dbus_interface(NM_DBUS_INTERFACES + org.freedesktop.NetworkManager.Device.Statistics.xml + dbus_nm_device_statistics +) + set_source_files_properties(org.freedesktop.NetworkManager.Device.Wireless.xml PROPERTIES CLASSNAME DBusNMWirelessProxy NO_NAMESPACE TRUE diff --git a/src/network/nm/device.cpp b/src/network/nm/device.cpp index 0a5f91cc..c3a04da7 100644 --- a/src/network/nm/device.cpp +++ b/src/network/nm/device.cpp @@ -18,6 +18,7 @@ #include "../enums.hpp" #include "active_connection.hpp" #include "dbus_nm_device.h" +#include "dbus_nm_device_statistics.h" #include "dbus_types.hpp" #include "enums.hpp" #include "network.hpp" @@ -38,11 +39,26 @@ NMDevice::NMDevice(const QString& path, QObject* parent): QObject(parent) { this ); + this->deviceStatisticsProxy = new DBusNMDeviceStatisticsProxy( + "org.freedesktop.NetworkManager", + path, + QDBusConnection::systemBus(), + this + ); + if (!this->deviceProxy->isValid()) { qCWarning(logNetworkManager) << "Cannot create DBus interface for device at" << path; return; } + // Valid statistics aren't required to register a device. + if (!this->deviceStatisticsProxy->isValid()) { + qCWarning(logNetworkManager) << "Cannot create DBus interface for device statistics at" << path; + } else { + this->deviceStatisticsProperties.setInterface(this->deviceStatisticsProxy); + this->deviceStatisticsProperties.updateAllViaGetAll(); + } + // clang-format off QObject::connect(this, &NMDevice::availableSettingsPathsChanged, this, &NMDevice::onAvailableSettingsPathsChanged); QObject::connect(this, &NMDevice::activeConnectionPathChanged, this, &NMDevice::onActiveConnectionPathChanged); @@ -69,9 +85,13 @@ void NMDevice::bindFrontend(NetworkDevice* frontend) { frontend->bindableState().setBinding(translateState); frontend->bindableAutoconnect().setBinding([this]() { return this->autoconnect(); }); frontend->bindableNmManaged().setBinding([this]() { return this->managed(); }); + frontend->bindableRefreshRate().setBinding([this]() { return this->refreshRate(); }); + frontend->bindableRxBytes().setBinding([this]() { return this->rxBytes(); }); + frontend->bindableTxBytes().setBinding([this]() { return this->txBytes(); }); QObject::connect(frontend, &NetworkDevice::requestDisconnect, this, &NMDevice::disconnect); QObject::connect(frontend, &NetworkDevice::requestSetAutoconnect, this, &NMDevice::setAutoconnect); QObject::connect(frontend, &NetworkDevice::requestSetNmManaged, this, &NMDevice::setManaged); + QObject::connect(frontend, &NetworkDevice::requestSetRefreshRate, this, &NMDevice::setRefreshRate); QObject::connect(this, &NMDevice::networkAdded, frontend, &NetworkDevice::networkAdded); QObject::connect(this, &NMDevice::networkRemoved, frontend, &NetworkDevice::networkRemoved); } @@ -188,6 +208,17 @@ void NMDevice::setManaged(bool managed) { this->pManaged.write(); } +void NMDevice::setRefreshRate(quint32 refreshRate) { + if (refreshRate == this->bRefreshRate) return; + if (!this->deviceStatisticsProxy->isValid()) { + qCWarning(logNetworkManager) << "Cannot set refresh rate: Invalid device statistics interface"; + return; + } + + this->bRefreshRate = refreshRate; + this->pRefreshRate.write(); +} + bool NMDevice::isValid() const { return this->deviceProxy && this->deviceProxy->isValid(); } QString NMDevice::address() const { return this->deviceProxy ? this->deviceProxy->service() : QString(); diff --git a/src/network/nm/device.hpp b/src/network/nm/device.hpp index d6b332b7..dd85e94f 100644 --- a/src/network/nm/device.hpp +++ b/src/network/nm/device.hpp @@ -11,6 +11,7 @@ #include "../device.hpp" #include "active_connection.hpp" #include "dbus_nm_device.h" +#include "dbus_nm_device_statistics.h" #include "enums.hpp" #include "network.hpp" #include "settings.hpp" @@ -56,6 +57,9 @@ class NMDevice: public QObject { [[nodiscard]] NMDeviceInterfaceFlags::Enum interfaceFlags() const { return this->bInterfaceFlags; } + [[nodiscard]] quint32 refreshRate() const { return this->bRefreshRate; } + [[nodiscard]] quint64 rxBytes() const { return this->bRxBytes; } + [[nodiscard]] quint64 txBytes() const { return this->bTxBytes; } [[nodiscard]] bool autoconnect() const { return this->bAutoconnect; } [[nodiscard]] NMActiveConnection* activeConnection() const { return this->mActiveConnection; } [[nodiscard]] virtual NetworkDevice* frontend() = 0; @@ -83,11 +87,15 @@ class NMDevice: public QObject { void lastFailReasonChanged(NMDeviceStateReason::Enum reason); void autoconnectChanged(bool autoconnect); void interfaceFlagsChanged(NMDeviceInterfaceFlags::Enum flags); + void refreshRateChanged(quint32 refreshRate); + void rxBytesChanged(quint64 rxBytes); + void txBytesChanged(quint64 txBytes); public slots: void disconnect(); void setAutoconnect(bool autoconnect); void setManaged(bool managed); + void setRefreshRate(quint32 refreshRate); protected: void bindFrontend(NetworkDevice* frontend); @@ -115,8 +123,11 @@ private slots: Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QList, bAvailableConnections, &NMDevice::availableSettingsPathsChanged); Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QDBusObjectPath, bActiveConnection, &NMDevice::activeConnectionPathChanged); Q_OBJECT_BINDABLE_PROPERTY(NMDevice, NMDeviceInterfaceFlags::Enum, bInterfaceFlags, &NMDevice::interfaceFlagsChanged); + Q_OBJECT_BINDABLE_PROPERTY(NMDevice, quint32, bRefreshRate, &NMDevice::refreshRateChanged); + Q_OBJECT_BINDABLE_PROPERTY(NMDevice, quint64, bRxBytes, &NMDevice::rxBytesChanged); + Q_OBJECT_BINDABLE_PROPERTY(NMDevice, quint64, bTxBytes, &NMDevice::txBytesChanged); - QS_DBUS_BINDABLE_PROPERTY_GROUP(NMDeviceAdapter, deviceProperties); + QS_DBUS_BINDABLE_PROPERTY_GROUP(NMDevice, deviceProperties); QS_DBUS_PROPERTY_BINDING(NMDevice, pName, bInterface, deviceProperties, "Interface"); QS_DBUS_PROPERTY_BINDING(NMDevice, pAddress, bHwAddress, deviceProperties, "HwAddress"); QS_DBUS_PROPERTY_BINDING(NMDevice, pManaged, bManaged, deviceProperties, "Managed"); @@ -125,9 +136,14 @@ private slots: QS_DBUS_PROPERTY_BINDING(NMDevice, pAvailableConnections, bAvailableConnections, deviceProperties, "AvailableConnections"); QS_DBUS_PROPERTY_BINDING(NMDevice, pActiveConnection, bActiveConnection, deviceProperties, "ActiveConnection"); QS_DBUS_PROPERTY_BINDING(NMDevice, pInterfaceFlags, bInterfaceFlags, deviceProperties, "InterfaceFlags"); + QS_DBUS_BINDABLE_PROPERTY_GROUP(NMDevice, deviceStatisticsProperties); + QS_DBUS_PROPERTY_BINDING(NMDevice, pRefreshRate, bRefreshRate, deviceStatisticsProperties, "RefreshRateMs"); + QS_DBUS_PROPERTY_BINDING(NMDevice, pRxBytes, bRxBytes, deviceStatisticsProperties, "RxBytes"); + QS_DBUS_PROPERTY_BINDING(NMDevice, pTxBytes, bTxBytes, deviceStatisticsProperties, "TxBytes"); // clang-format on DBusNMDeviceProxy* deviceProxy = nullptr; + DBusNMDeviceStatisticsProxy* deviceStatisticsProxy = nullptr; }; } // namespace qs::network diff --git a/src/network/nm/org.freedesktop.NetworkManager.Device.Statistics.xml b/src/network/nm/org.freedesktop.NetworkManager.Device.Statistics.xml new file mode 100644 index 00000000..78024f0b --- /dev/null +++ b/src/network/nm/org.freedesktop.NetworkManager.Device.Statistics.xml @@ -0,0 +1,4 @@ + + + + diff --git a/src/network/test/manual/network.qml b/src/network/test/manual/network.qml index 539cbf85..f874f215 100644 --- a/src/network/test/manual/network.qml +++ b/src/network/test/manual/network.qml @@ -76,6 +76,12 @@ Scope { Component { id: deviceDelegate WrapperRectangle { + id: deviceRoot + + function formatMib(bytes) { + return `${(bytes / 1024 / 1024).toFixed(3)} MiB`; + } + width: parent.width color: "transparent" border.color: palette.button @@ -140,6 +146,27 @@ Scope { } } } + RowLayout { + Label { + text: `RX: ${deviceRoot.formatMib(modelData.rxBytes)}` + color: palette.placeholderText + } + Label { + text: `TX: ${deviceRoot.formatMib(modelData.txBytes)}` + color: palette.placeholderText + } + Label { + text: "Refresh rate:" + } + SpinBox { + from: 0 + to: 60000 + stepSize: 250 + value: modelData.refreshRate + onValueModified: modelData.refreshRate = value + textFromValue: value => value === 0 ? "Off" : value + "ms" + } + } Repeater { model: ScriptModel {