b90927915f
- 将Q_PROPERTY类型从qint64改为double - 将QML属性类型从qint64改为real - 修复插件无法加载的问题
262 lines
6.9 KiB
C++
262 lines
6.9 KiB
C++
// SPDX-FileCopyrightText: 2024 MyCompany
|
|
//
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
#include "networkmonitorapplet.h"
|
|
#include <pluginfactory.h>
|
|
|
|
#include <QFile>
|
|
#include <QTextStream>
|
|
#include <QDebug>
|
|
#include <QDir>
|
|
#include <QRegularExpression>
|
|
|
|
DS_BEGIN_NAMESPACE
|
|
|
|
NetworkMonitorApplet::NetworkMonitorApplet(QObject *parent)
|
|
: DApplet(parent)
|
|
, m_refreshTimer(nullptr)
|
|
, m_lastRxBytes(0)
|
|
, m_lastTxBytes(0)
|
|
, m_downloadSpeed(0)
|
|
, m_uploadSpeed(0)
|
|
, m_totalDownload(0)
|
|
, m_totalUpload(0)
|
|
, m_ready(false)
|
|
, m_firstUpdate(true)
|
|
{
|
|
}
|
|
|
|
NetworkMonitorApplet::~NetworkMonitorApplet()
|
|
{
|
|
}
|
|
|
|
bool NetworkMonitorApplet::load()
|
|
{
|
|
return DApplet::load();
|
|
}
|
|
|
|
bool NetworkMonitorApplet::init()
|
|
{
|
|
detectInterfaces();
|
|
|
|
// 启动定时刷新
|
|
m_refreshTimer = new QTimer(this);
|
|
connect(m_refreshTimer, &QTimer::timeout, this, &NetworkMonitorApplet::refresh);
|
|
m_refreshTimer->start(1000); // 每秒刷新一次
|
|
|
|
// 初始读取
|
|
readNetworkStats();
|
|
|
|
return DApplet::init();
|
|
}
|
|
|
|
double NetworkMonitorApplet::downloadSpeed() const
|
|
{
|
|
return static_cast<double>(m_downloadSpeed);
|
|
}
|
|
|
|
double NetworkMonitorApplet::uploadSpeed() const
|
|
{
|
|
return static_cast<double>(m_uploadSpeed);
|
|
}
|
|
|
|
double NetworkMonitorApplet::totalDownload() const
|
|
{
|
|
return static_cast<double>(m_totalDownload);
|
|
}
|
|
|
|
double NetworkMonitorApplet::totalUpload() const
|
|
{
|
|
return static_cast<double>(m_totalUpload);
|
|
}
|
|
|
|
QStringList NetworkMonitorApplet::networkInterfaces() const
|
|
{
|
|
return m_interfaceList;
|
|
}
|
|
|
|
QStringList NetworkMonitorApplet::interfaceStats() const
|
|
{
|
|
QStringList stats;
|
|
for (const QString &name : m_interfaceList) {
|
|
if (m_interfaces.contains(name)) {
|
|
const NetworkInterface &iface = m_interfaces[name];
|
|
stats << QString("%1|%2|%3|%4|%5")
|
|
.arg(iface.name)
|
|
.arg(iface.rxBytes)
|
|
.arg(iface.txBytes)
|
|
.arg(iface.rxPackets)
|
|
.arg(iface.txPackets);
|
|
}
|
|
}
|
|
return stats;
|
|
}
|
|
|
|
bool NetworkMonitorApplet::ready() const
|
|
{
|
|
return m_ready;
|
|
}
|
|
|
|
QString NetworkMonitorApplet::activeInterface() const
|
|
{
|
|
return m_activeInterface;
|
|
}
|
|
|
|
void NetworkMonitorApplet::refresh()
|
|
{
|
|
readNetworkStats();
|
|
}
|
|
|
|
void NetworkMonitorApplet::setActiveInterface(const QString &interface)
|
|
{
|
|
if (m_activeInterface != interface) {
|
|
m_activeInterface = interface;
|
|
m_firstUpdate = true;
|
|
emit activeInterfaceChanged();
|
|
}
|
|
}
|
|
|
|
void NetworkMonitorApplet::readNetworkStats()
|
|
{
|
|
QFile file("/proc/net/dev");
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
qDebug() << "Failed to open /proc/net/dev";
|
|
return;
|
|
}
|
|
|
|
QTextStream stream(&file);
|
|
QStringList lines = stream.readAll().split('\n', Qt::SkipEmptyParts);
|
|
file.close();
|
|
|
|
QMap<QString, NetworkInterface> newInterfaces;
|
|
|
|
// 跳过前两行标题
|
|
for (int i = 2; i < lines.size(); ++i) {
|
|
QString line = lines[i].trimmed();
|
|
if (line.isEmpty()) continue;
|
|
|
|
// 格式: "interface: rx_bytes rx_packets rx_errors rx_dropped ... tx_bytes tx_packets tx_errors tx_dropped ..."
|
|
QRegularExpression re("^([^:]+):\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+\\d+\\s+\\d+\\s+\\d+\\s+\\d+\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)");
|
|
QRegularExpressionMatch match = re.match(line);
|
|
|
|
if (match.hasMatch()) {
|
|
NetworkInterface iface;
|
|
iface.name = match.captured(1);
|
|
iface.rxBytes = match.captured(2).toLongLong();
|
|
iface.rxPackets = match.captured(3).toLongLong();
|
|
iface.rxErrors = match.captured(4).toLongLong();
|
|
iface.rxDropped = match.captured(5).toLongLong();
|
|
iface.txBytes = match.captured(6).toLongLong();
|
|
iface.txPackets = match.captured(7).toLongLong();
|
|
iface.txErrors = match.captured(8).toLongLong();
|
|
iface.txDropped = match.captured(9).toLongLong();
|
|
|
|
// 过滤掉回环接口和虚拟接口
|
|
if (iface.name != "lo" && !iface.name.startsWith("veth") &&
|
|
!iface.name.startsWith("docker") && !iface.name.startsWith("br-")) {
|
|
newInterfaces[iface.name] = iface;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 检测接口变化
|
|
if (newInterfaces.keys() != m_interfaces.keys()) {
|
|
m_interfaceList = newInterfaces.keys();
|
|
m_interfaces = newInterfaces;
|
|
|
|
// 自动选择活动接口
|
|
if (m_activeInterface.isEmpty() && !m_interfaceList.isEmpty()) {
|
|
// 优先选择有流量的接口
|
|
for (const QString &name : m_interfaceList) {
|
|
const NetworkInterface &iface = m_interfaces[name];
|
|
if (iface.rxBytes > 0 || iface.txBytes > 0) {
|
|
m_activeInterface = name;
|
|
break;
|
|
}
|
|
}
|
|
if (m_activeInterface.isEmpty()) {
|
|
m_activeInterface = m_interfaceList.first();
|
|
}
|
|
emit activeInterfaceChanged();
|
|
}
|
|
|
|
m_ready = !m_interfaceList.isEmpty();
|
|
emit interfacesChanged();
|
|
emit readyChanged();
|
|
} else {
|
|
m_interfaces = newInterfaces;
|
|
}
|
|
|
|
calculateSpeed();
|
|
emit statsChanged();
|
|
}
|
|
|
|
void NetworkMonitorApplet::calculateSpeed()
|
|
{
|
|
qint64 currentRxBytes = getActiveRxBytes();
|
|
qint64 currentTxBytes = getActiveTxBytes();
|
|
|
|
if (m_firstUpdate) {
|
|
m_firstUpdate = false;
|
|
m_lastRxBytes = currentRxBytes;
|
|
m_lastTxBytes = currentTxBytes;
|
|
return;
|
|
}
|
|
|
|
// 计算速度(字节/秒)
|
|
m_downloadSpeed = currentRxBytes - m_lastRxBytes;
|
|
m_uploadSpeed = currentTxBytes - m_lastTxBytes;
|
|
|
|
// 更新总量
|
|
m_totalDownload = currentRxBytes;
|
|
m_totalUpload = currentTxBytes;
|
|
|
|
m_lastRxBytes = currentRxBytes;
|
|
m_lastTxBytes = currentTxBytes;
|
|
|
|
emit speedChanged();
|
|
emit totalChanged();
|
|
}
|
|
|
|
void NetworkMonitorApplet::detectInterfaces()
|
|
{
|
|
QDir sysClass("/sys/class/net");
|
|
if (!sysClass.exists()) return;
|
|
|
|
QStringList entries = sysClass.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
|
m_interfaceList.clear();
|
|
|
|
for (const QString &entry : entries) {
|
|
// 过滤回环接口
|
|
if (entry != "lo") {
|
|
m_interfaceList << entry;
|
|
}
|
|
}
|
|
|
|
m_ready = !m_interfaceList.isEmpty();
|
|
emit interfacesChanged();
|
|
emit readyChanged();
|
|
}
|
|
|
|
qint64 NetworkMonitorApplet::getActiveRxBytes() const
|
|
{
|
|
if (m_activeInterface.isEmpty() || !m_interfaces.contains(m_activeInterface)) {
|
|
return 0;
|
|
}
|
|
return m_interfaces[m_activeInterface].rxBytes;
|
|
}
|
|
|
|
qint64 NetworkMonitorApplet::getActiveTxBytes() const
|
|
{
|
|
if (m_activeInterface.isEmpty() || !m_interfaces.contains(m_activeInterface)) {
|
|
return 0;
|
|
}
|
|
return m_interfaces[m_activeInterface].txBytes;
|
|
}
|
|
|
|
D_APPLET_CLASS(NetworkMonitorApplet)
|
|
|
|
DS_END_NAMESPACE
|
|
|
|
#include "networkmonitorapplet.moc" |