diff --git a/retroshare-gui/src/gui/statistics/RttStatistics.cpp b/retroshare-gui/src/gui/statistics/RttStatistics.cpp index f4e97948e..0cb88bd95 100644 --- a/retroshare-gui/src/gui/statistics/RttStatistics.cpp +++ b/retroshare-gui/src/gui/statistics/RttStatistics.cpp @@ -18,12 +18,18 @@ * * *******************************************************************************/ +/******************************************************************************* + * gui/statistics/RttStatistics.cpp * + *******************************************************************************/ + #include +#include #include #include - #include #include +#include +#include #include #include @@ -32,13 +38,49 @@ #include "gui/settings/rsharesettings.h" +// --- Custom Item for Sorting (Table) --- +class RttTreeItem : public QTreeWidgetItem { +public: + using QTreeWidgetItem::QTreeWidgetItem; + + bool operator<(const QTreeWidgetItem &other) const { + int sortCol = treeWidget() ? treeWidget()->sortColumn() : 0; + + // RTT Sort + if (sortCol == 1) { + QString txt1 = text(1); + QString txt2 = other.text(1); + long val1 = (txt1 == "?") ? std::numeric_limits::max() : txt1.toLong(); + long val2 = (txt2 == "?") ? std::numeric_limits::max() : txt2.toLong(); + return val1 < val2; + } + // IP Sort + if (sortCol == 3) { + QString s1 = text(3); + QString s2 = other.text(3); + QHostAddress ip1(s1); QHostAddress ip2(s2); + bool isTorI2p1 = s1.contains(".onion") || s1.contains(".i2p"); + bool isTorI2p2 = s2.contains(".onion") || s2.contains(".i2p"); + + if (!ip1.isNull() && !ip2.isNull() && !isTorI2p1 && !isTorI2p2) { + if (ip1.protocol() != ip2.protocol()) return ip1.protocol() < ip2.protocol(); + if (ip1.protocol() == QAbstractSocket::IPv4Protocol) return ip1.toIPv4Address() < ip2.toIPv4Address(); + } + return s1 < s2; + } + return QTreeWidgetItem::operator<(other); + } +}; + +// --- Main Constructor --- RttStatistics::RttStatistics(QWidget * /*parent*/) { setupUi(this) ; - + m_bProcessSettings = false; + // Setup Graph in Tab 1 _tunnel_statistics_F->setWidget( _tst_CW = new RttStatisticsGraph(this) ) ; _tunnel_statistics_F->setWidgetResizable(true); _tunnel_statistics_F->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -46,14 +88,24 @@ RttStatistics::RttStatistics(QWidget * /*parent*/) _tunnel_statistics_F->viewport()->setBackgroundRole(QPalette::NoRole); _tunnel_statistics_F->setFrameStyle(QFrame::NoFrame); _tunnel_statistics_F->setFocusPolicy(Qt::NoFocus); - + + // Setup Table in Tab 2 + treeWidget->setAlternatingRowColors(true); + treeWidget->setSortingEnabled(true); + treeWidget->sortByColumn(1, Qt::AscendingOrder); + + // Timer for Table Update + m_timer = new QTimer(this); + connect(m_timer, SIGNAL(timeout()), this, SLOT(updateRttValues())); + m_timer->start(1000); + // load settings processSettings(true); } RttStatistics::~RttStatistics() { - + if(m_timer) m_timer->stop(); // save settings processSettings(false); } @@ -66,23 +118,75 @@ void RttStatistics::processSettings(bool bLoad) if (bLoad) { // load settings + QByteArray state = Settings->value("TreeState").toByteArray(); + if (!state.isEmpty()) treeWidget->header()->restoreState(state); - // state of splitter - //splitter->restoreState(Settings->value("Splitter").toByteArray()); + tabWidget->setCurrentIndex(Settings->value("ActiveTab", 0).toInt()); } else { // save settings - - // state of splitter - //Settings->setValue("Splitter", splitter->saveState()); - + Settings->setValue("TreeState", treeWidget->header()->saveState()); + Settings->setValue("ActiveTab", tabWidget->currentIndex()); } Settings->endGroup(); - - m_bProcessSettings = false; + m_bProcessSettings = false; } +// --- Table Update Logic (O(N) Optimized) --- +void RttStatistics::updateRttValues() +{ + // Only update if the rtt tab is visible and the table view is selected + if (!isVisible() || tabWidget->currentIndex() != 1) return; + + std::list idList; + if (!rsPeers) return; + rsPeers->getOnlineList(idList); + + QHash existingItems; + for(int i = 0; i < treeWidget->topLevelItemCount(); ++i) { + QTreeWidgetItem* item = treeWidget->topLevelItem(i); + existingItems.insert(item->data(0, Qt::UserRole).toString(), item); + } + + for(std::list::const_iterator it = idList.begin(); it != idList.end(); ++it) + { + std::string peerIdStr = (*it).toStdString(); + QString qPeerId = QString::fromStdString(peerIdStr); + + std::list results; + rsRtt->getPongResults(*it, 1, results); + int rttInMs = -1; + if (!results.empty()) { + float rttSec = results.back().mRTT; + if (rttSec > 0.000001f) rttInMs = (int)(rttSec * 1000.0f); + } + + RsPeerDetails details; + rsPeers->getPeerDetails(*it, details); + QString peerName = QString::fromUtf8(details.name.c_str()); + QString ipAddress = QString::fromStdString(details.extAddr); + + QTreeWidgetItem* item = existingItems.take(qPeerId); + + if (!item) { + item = new RttTreeItem(treeWidget); + item->setData(0, Qt::UserRole, qPeerId); + } + + item->setText(0, peerName); + if (rttInMs != -1) item->setText(1, QString::number(rttInMs)); + else item->setText(1, "?"); + item->setText(2, qPeerId); + item->setText(3, ipAddress); + } + + qDeleteAll(existingItems); +} + + +// --- Graph Source Implementation (Unchanged logic) --- + QString RttGraphSource::unitName() const { return QObject::tr("secs") ; @@ -109,8 +213,9 @@ void RttGraphSource::getValues(std::map& vals) const for(std::list::const_iterator it(idList.begin());it!=idList.end();++it) { rsRtt->getPongResults(*it, 1, results); - - vals[(*it).toStdString()] = results.back().mRTT ; + if(!results.empty()) { + vals[(*it).toStdString()] = results.back().mRTT ; + } } } diff --git a/retroshare-gui/src/gui/statistics/RttStatistics.h b/retroshare-gui/src/gui/statistics/RttStatistics.h index 32c8b4f1b..705f4eaf3 100644 --- a/retroshare-gui/src/gui/statistics/RttStatistics.h +++ b/retroshare-gui/src/gui/statistics/RttStatistics.h @@ -18,9 +18,16 @@ * * *******************************************************************************/ +/******************************************************************************* + * gui/statistics/RttStatistics.h * + *******************************************************************************/ + #pragma once #include +#include +#include +#include #include #include @@ -47,17 +54,19 @@ public: class RttStatistics: public MainPage, public Ui::RttStatistics { + Q_OBJECT + public: RttStatistics(QWidget *parent = NULL) ; ~RttStatistics(); +private slots: + void updateRttValues(); // Slot for table update + private: void processSettings(bool bLoad); bool m_bProcessSettings; RttStatisticsGraph *_tst_CW ; + QTimer *m_timer; // Timer for the table } ; - - - - diff --git a/retroshare-gui/src/gui/statistics/RttStatistics.ui b/retroshare-gui/src/gui/statistics/RttStatistics.ui index fe4e21b7a..855e5d049 100644 --- a/retroshare-gui/src/gui/statistics/RttStatistics.ui +++ b/retroshare-gui/src/gui/statistics/RttStatistics.ui @@ -6,44 +6,97 @@ 0 0 - 611 - 408 + 700 + 500 RTT Statistics - - - :/images/logo/logo_16.png:/images/logo/logo_16.png - - - - - - Qt::Vertical + + + + + 0 - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - true - - - - - 0 - 0 - 593 - 390 - - - + + + + Graph View + + + + + + Qt::Vertical + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 100 + 100 + + + + + + + + + + + Table View + + + + + + false + + + true + + + true + + + + Peer Name + + + + + RTT (ms) + + + + + Node ID + + + + + IP Address + + + + + + +