1818 * *
1919 *******************************************************************************/
2020
21+ /* ******************************************************************************
22+ * gui/statistics/RttStatistics.cpp *
23+ *******************************************************************************/
24+
2125#include < iostream>
26+ #include < limits>
2227#include < QTimer>
2328#include < QObject>
24-
2529#include < QPainter>
2630#include < QStylePainter>
31+ #include < QHostAddress>
32+ #include < QHash>
2733
2834#include < retroshare/rsrtt.h>
2935#include < retroshare/rspeers.h>
3238
3339#include " gui/settings/rsharesettings.h"
3440
41+ // --- Custom Item for Sorting (Table) ---
42+ class RttTreeItem : public QTreeWidgetItem {
43+ public:
44+ using QTreeWidgetItem::QTreeWidgetItem;
45+
46+ bool operator <(const QTreeWidgetItem &other) const {
47+ int sortCol = treeWidget () ? treeWidget ()->sortColumn () : 0 ;
48+
49+ if (sortCol == RttStatistics::COL_RTT ) {
50+ QString txt1 = text (RttStatistics::COL_RTT );
51+ QString txt2 = other.text (RttStatistics::COL_RTT );
52+ long val1 = (txt1 == " ?" ) ? std::numeric_limits<long >::max () : txt1.toLong ();
53+ long val2 = (txt2 == " ?" ) ? std::numeric_limits<long >::max () : txt2.toLong ();
54+ return val1 < val2;
55+ }
56+
57+ if (sortCol == RttStatistics::COL_IP_ADDRESS ) {
58+ QString s1 = text (RttStatistics::COL_IP_ADDRESS );
59+ QString s2 = other.text (RttStatistics::COL_IP_ADDRESS );
60+ QHostAddress ip1 (s1); QHostAddress ip2 (s2);
61+
62+ bool isTorI2p1 = s1.contains (" .onion" ) || s1.contains (" .i2p" );
63+ bool isTorI2p2 = s2.contains (" .onion" ) || s2.contains (" .i2p" );
64+
65+ if (!ip1.isNull () && !ip2.isNull () && !isTorI2p1 && !isTorI2p2) {
66+ if (ip1.protocol () != ip2.protocol ()) return ip1.protocol () < ip2.protocol ();
67+ if (ip1.protocol () == QAbstractSocket::IPv4Protocol) return ip1.toIPv4Address () < ip2.toIPv4Address ();
68+ }
69+ return s1 < s2;
70+ }
71+ return QTreeWidgetItem::operator <(other);
72+ }
73+ };
74+
75+ // --- Main Constructor ---
3576
3677RttStatistics::RttStatistics (QWidget * /* parent*/ )
3778{
3879 setupUi (this ) ;
39-
80+
4081 m_bProcessSettings = false ;
4182
83+ // Setup Graph in Tab 1
4284 _tunnel_statistics_F->setWidget ( _tst_CW = new RttStatisticsGraph (this ) ) ;
4385 _tunnel_statistics_F->setWidgetResizable (true );
4486 _tunnel_statistics_F->setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
4587 _tunnel_statistics_F->setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
4688 _tunnel_statistics_F->viewport ()->setBackgroundRole (QPalette::NoRole);
4789 _tunnel_statistics_F->setFrameStyle (QFrame::NoFrame);
4890 _tunnel_statistics_F->setFocusPolicy (Qt::NoFocus);
49-
91+
92+ // Setup Table in Tab 2
93+ treeWidget->setAlternatingRowColors (true );
94+ treeWidget->setSortingEnabled (true );
95+ treeWidget->sortByColumn (1 , Qt::AscendingOrder);
96+
97+ // Timer for Table Update
98+ m_timer = new QTimer (this );
99+ connect (m_timer, SIGNAL (timeout ()), this , SLOT (updateRttValues ()));
100+ m_timer->start (1000 );
101+
50102 // load settings
51103 processSettings (true );
52104}
53105
54106RttStatistics::~RttStatistics ()
55107{
56-
108+ if (m_timer) {
109+ m_timer->stop ();
110+ delete m_timer; // Explicitly delete the timer as requested
111+ m_timer = nullptr ;
112+ }
57113 // save settings
58114 processSettings (false );
59115}
@@ -66,23 +122,82 @@ void RttStatistics::processSettings(bool bLoad)
66122
67123 if (bLoad) {
68124 // load settings
125+ QByteArray state = Settings->value (" TreeState" ).toByteArray ();
126+ if (!state.isEmpty ()) treeWidget->header ()->restoreState (state);
69127
70- // state of splitter
71- // splitter->restoreState(Settings->value("Splitter").toByteArray());
128+ tabWidget->setCurrentIndex (Settings->value (" ActiveTab" , 0 ).toInt ());
72129 } else {
73130 // save settings
74-
75- // state of splitter
76- // Settings->setValue("Splitter", splitter->saveState());
77-
131+ Settings->setValue (" TreeState" , treeWidget->header ()->saveState ());
132+ Settings->setValue (" ActiveTab" , tabWidget->currentIndex ());
78133 }
79134
80135 Settings->endGroup ();
81-
136+
82137 m_bProcessSettings = false ;
138+ }
139+
140+ // --- Table Update Logic (O(N) Optimized) ---
141+ void RttStatistics::updateRttValues ()
142+ {
143+ // Only update if the RTT tab is visible and the table view is selected
144+ if (!isVisible () || tabWidget->currentIndex () != 1 ) return ;
145+
146+ std::list<RsPeerId> idList;
147+ if (!rsPeers) return ;
148+ rsPeers->getOnlineList (idList);
149+
150+ // 1. Collect all current items into a hash map to track them.
151+ // We use the Peer ID (stored in UserRole) as the key.
152+ QHash<QString, QTreeWidgetItem*> existingItems;
153+ for (int i = 0 ; i < treeWidget->topLevelItemCount (); ++i) {
154+ QTreeWidgetItem* item = treeWidget->topLevelItem (i);
155+ existingItems.insert (item->data (0 , Qt::UserRole).toString (), item);
156+ }
157+
158+ for (std::list<RsPeerId>::const_iterator it = idList.begin (); it != idList.end (); ++it)
159+ {
160+ std::string peerIdStr = (*it).toStdString ();
161+ QString qPeerId = QString::fromStdString (peerIdStr);
162+
163+ // Fetch RTT and peer details
164+ std::list<RsRttPongResult> results;
165+ int rttInMs = -1 ;
83166
167+ if (rsRtt->getPongResults (*it, 1 , results) > 0 && !results.empty ()) {
168+ float rttSec = results.back ().mRTT ;
169+ if (rttSec > 0 .000001f ) rttInMs = (int )(rttSec * 1000 .0f );
170+ }
171+
172+ RsPeerDetails details;
173+ rsPeers->getPeerDetails (*it, details);
174+ QString peerName = QString::fromUtf8 (details.name .c_str ());
175+ QString ipAddress = QString::fromStdString (details.extAddr );
176+
177+ // 2. Take the item from the map.
178+ // This removes it from the 'existingItems' hash so it won't be deleted later.
179+ QTreeWidgetItem* item = existingItems.take (qPeerId);
180+
181+ if (!item) {
182+ // If the peer is new, create a new item
183+ item = new RttTreeItem (treeWidget);
184+ item->setData (0 , Qt::UserRole, qPeerId);
185+ }
186+
187+ // Update the row values using the enums defined in the header
188+ item->setText (COL_PEER_NAME , peerName);
189+ item->setText (COL_RTT , (rttInMs != -1 ) ? QString::number (rttInMs) : " ?" );
190+ item->setText (COL_NODE_ID , qPeerId);
191+ item->setText (COL_IP_ADDRESS , ipAddress);
192+ }
193+
194+ // 3. Delete any items remaining in the hash map.
195+ // These correspond to peers that are no longer online or have been removed.
196+ qDeleteAll (existingItems);
84197}
85198
199+ // --- Graph Source Implementation (Unchanged logic) ---
200+
86201QString RttGraphSource::unitName () const
87202{
88203 return QObject::tr (" secs" ) ;
@@ -104,9 +219,10 @@ void RttGraphSource::getValues(std::map<std::string,float>& vals) const
104219 rsPeers->getOnlineList (idList);
105220
106221 vals.clear () ;
222+ std::list<RsRttPongResult> results ;
223+
107224 for (std::list<RsPeerId>::const_iterator it (idList.begin ());it!=idList.end ();++it)
108225 {
109- std::list<RsRttPongResult> results ;
110226 if (rsRtt->getPongResults (*it, 1 , results) > 0 && !results.empty ()) {
111227 vals[(*it).toStdString ()] = results.back ().mRTT ;
112228 }
0 commit comments