2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 // Copyright (c) 2005-2008 Dévai Tamás ( gonosztopi@amule.org )
8 // Any parts of this program derived from the xMule, lMule or eMule project,
9 // or contributed by third-party developers are copyrighted by their
10 // respective authors.
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "Statistics.h" // Interface declarations
29 #include <protocol/ed2k/ClientSoftware.h>
31 #include <ec/cpp/ECTag.h> // Needed for CECTag
35 #include <common/Format.h> // Needed for CFormat
37 #include "DataToText.h" // Needed for GetSoftName()
38 #include "Preferences.h" // Needed for thePrefs
39 #include "amule.h" // Needed for theApp
40 #include "ListenSocket.h" // (tree, GetAverageConnections)
41 #include "ServerList.h" // Needed for CServerList (tree)
42 #include <cmath> // Needed for std::floor
43 #include "updownclient.h" // Needed for CUpDownClient
44 #include "DownloadQueue.h" // Needed for theApp->downloadqueue
46 #include "Preferences.h"
47 #include <ec/cpp/RemoteConnect.h> // Needed for CRemoteConnect
56 #define round(x) floor(x+0.5)
62 /*----- CPreciseRateCounter -----*/
64 void CPreciseRateCounter::CalculateRate(uint64_t now
)
66 wxMutexLocker
lock(m_mutex
);
69 m_byte_history
.push_back(m_tmp_sum
);
70 m_tick_history
.push_back(now
);
73 uint64_t timespan
= now
- m_tick_history
.front();
75 // Checking maximal timespan, but make sure not to remove
76 // the extra node in m_tick_history.
77 while (timespan
> m_timespan
&& m_byte_history
.size() > 0) {
78 m_total
-= m_byte_history
.front();
79 m_byte_history
.pop_front();
80 m_tick_history
.pop_front();
81 timespan
= now
- m_tick_history
.front();
85 if (m_count_average
) {
86 if (m_byte_history
.size() > 0) {
87 m_rate
= m_total
/ (double)m_byte_history
.size();
91 m_rate
= m_total
/ (timespan
/ 1000.0);
95 if (m_rate
> m_max_rate
) {
101 /*----- CStatTreeItemRateCounter -----*/
104 wxString
CStatTreeItemRateCounter::GetDisplayString() const
106 return CFormat(wxGetTranslation(m_label
)) % CastItoSpeed(m_show_maxrate
? (uint32
)m_max_rate
: (uint32
)m_rate
);
110 void CStatTreeItemRateCounter::AddECValues(CECTag
* tag
) const
112 CECTag
value(EC_TAG_STAT_NODE_VALUE
, m_show_maxrate
? (uint32
)m_max_rate
: (uint32
)m_rate
);
113 value
.AddTag(CECTag(EC_TAG_STAT_VALUE_TYPE
, (uint8
)EC_VALUE_SPEED
));
118 /*----- CStatTreeItemPeakConnections -----*/
121 wxString
CStatTreeItemPeakConnections::GetDisplayString() const
123 return wxString::Format(wxGetTranslation(m_label
), theStats::GetPeakConnections());
127 void CStatTreeItemPeakConnections::AddECValues(CECTag
* tag
) const
129 tag
->AddTag(CECTag(EC_TAG_STAT_NODE_VALUE
, (uint64
)theStats::GetPeakConnections()));
133 /*----- CStatistics -----*/
138 CPreciseRateCounter
* CStatistics::s_upOverheadRate
;
139 CPreciseRateCounter
* CStatistics::s_downOverheadRate
;
140 CStatTreeItemRateCounter
* CStatistics::s_uploadrate
;
141 CStatTreeItemRateCounter
* CStatistics::s_downloadrate
;
143 #else /* EC_REMOTE */
145 uint64
CStatistics::s_start_time
;
146 uint64
CStatistics::s_statData
[sdTotalItems
];
148 #endif /* !EC_REMOTE / EC_REMOTE */
151 CStatTreeItemBase
* CStatistics::s_statTree
;
155 CStatTreeItemTimer
* CStatistics::s_uptime
;
158 CStatTreeItemUlDlCounter
* CStatistics::s_sessionUpload
;
159 CStatTreeItemPacketTotals
* CStatistics::s_totalUpOverhead
;
160 CStatTreeItemPackets
* CStatistics::s_fileReqUpOverhead
;
161 CStatTreeItemPackets
* CStatistics::s_sourceXchgUpOverhead
;
162 CStatTreeItemPackets
* CStatistics::s_serverUpOverhead
;
163 CStatTreeItemPackets
* CStatistics::s_kadUpOverhead
;
164 CStatTreeItemCounter
* CStatistics::s_cryptUpOverhead
;
165 CStatTreeItemNativeCounter
* CStatistics::s_activeUploads
;
166 CStatTreeItemNativeCounter
* CStatistics::s_waitingUploads
;
167 CStatTreeItemCounter
* CStatistics::s_totalSuccUploads
;
168 CStatTreeItemCounter
* CStatistics::s_totalFailedUploads
;
169 CStatTreeItemCounter
* CStatistics::s_totalUploadTime
;
172 CStatTreeItemUlDlCounter
* CStatistics::s_sessionDownload
;
173 CStatTreeItemPacketTotals
* CStatistics::s_totalDownOverhead
;
174 CStatTreeItemPackets
* CStatistics::s_fileReqDownOverhead
;
175 CStatTreeItemPackets
* CStatistics::s_sourceXchgDownOverhead
;
176 CStatTreeItemPackets
* CStatistics::s_serverDownOverhead
;
177 CStatTreeItemPackets
* CStatistics::s_kadDownOverhead
;
178 CStatTreeItemCounter
* CStatistics::s_cryptDownOverhead
;
179 CStatTreeItemNativeCounter
* CStatistics::s_foundSources
;
180 CStatTreeItemNativeCounter
* CStatistics::s_activeDownloads
;
181 float CStatistics::s_downloadRateAdjust
= 1.0;
184 CStatTreeItemReconnects
* CStatistics::s_reconnects
;
185 CStatTreeItemTimer
* CStatistics::s_sinceFirstTransfer
;
186 CStatTreeItemTimer
* CStatistics::s_sinceConnected
;
187 CStatTreeItemCounterMax
* CStatistics::s_activeConnections
;
188 CStatTreeItemMaxConnLimitReached
* CStatistics::s_limitReached
;
189 CStatTreeItemSimple
* CStatistics::s_avgConnections
;
192 CStatTreeItemHiddenCounter
* CStatistics::s_clients
;
193 CStatTreeItemCounter
* CStatistics::s_unknown
;
194 //CStatTreeItem CStatistics::s_lowID;
195 //CStatTreeItem CStatistics::s_secIdentOnOff;
197 CStatTreeItemNativeCounter
* CStatistics::s_hasSocket
;
199 CStatTreeItemNativeCounter
* CStatistics::s_filtered
;
200 CStatTreeItemNativeCounter
* CStatistics::s_banned
;
203 CStatTreeItemSimple
* CStatistics::s_workingServers
;
204 CStatTreeItemSimple
* CStatistics::s_failedServers
;
205 CStatTreeItemNativeCounter
* CStatistics::s_totalServers
;
206 CStatTreeItemNativeCounter
* CStatistics::s_deletedServers
;
207 CStatTreeItemNativeCounter
* CStatistics::s_filteredServers
;
208 CStatTreeItemSimple
* CStatistics::s_usersOnWorking
;
209 CStatTreeItemSimple
* CStatistics::s_filesOnWorking
;
210 CStatTreeItemSimple
* CStatistics::s_totalUsers
;
211 CStatTreeItemSimple
* CStatistics::s_totalFiles
;
212 CStatTreeItemSimple
* CStatistics::s_serverOccupation
;
215 CStatTreeItemCounter
* CStatistics::s_numberOfShared
;
216 CStatTreeItemCounter
* CStatistics::s_sizeOfShare
;
219 uint64
CStatistics::s_kadNodesTotal
;
220 uint16
CStatistics::s_kadNodesCur
;
223 CStatistics::CStatistics()
224 : m_graphRunningAvgDown(thePrefs::GetStatsAverageMinutes() * 60 * 1000, true),
225 m_graphRunningAvgUp(thePrefs::GetStatsAverageMinutes() * 60 * 1000, true),
226 m_graphRunningAvgKad(thePrefs::GetStatsAverageMinutes() * 60 * 1000, true)
228 uint64 start_time
= GetTickCount64();
232 average_minutes
= thePrefs::GetStatsAverageMinutes();
234 HR hr
= {0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0};
236 nHistRanges
= 7; // =ceil(log(max_update_delay)/log(2))
237 nPointsPerRange
= GetPointsPerRange();
238 bitsHistClockMask
= (1 << (nHistRanges
-1)) - 1;
239 aposRecycle
= new listPOS
[nHistRanges
];
240 listPOS
*ppos
= aposRecycle
+nHistRanges
-1;
241 for (int i
=nHistRanges
; i
>0; --i
) { // permanently allocated history list
242 listHR
.push_back(hr
);
243 *ppos
-- = --listHR
.end();
244 for (int j
=nPointsPerRange
; j
>1; --j
)
245 listHR
.push_back(hr
);
248 // Init rate counters outside the tree
250 s_upOverheadRate
= new CPreciseRateCounter(5000);
251 s_downOverheadRate
= new CPreciseRateCounter(5000);
256 s_uptime
->SetStartTime(start_time
);
260 CStatistics::~CStatistics()
262 // clearing listHR frees the memory occupied by the nodes
264 delete [] aposRecycle
;
268 // delete items not in the tree
269 delete s_totalUploadTime
;
271 // delete rate counters outside the tree
272 delete s_upOverheadRate
;
273 delete s_downOverheadRate
;
277 void CStatistics::CalculateRates()
279 uint64_t now
= GetTickCount64();
280 s_downOverheadRate
->CalculateRate(now
);
281 s_upOverheadRate
->CalculateRate(now
);
282 s_downloadrate
->CalculateRate(now
);
283 s_uploadrate
->CalculateRate(now
);
284 // calculate rate adjustment, so that the sum of all file download rates
285 // matches the total transfer rate
286 float dlspeedfiles
= theApp
->downloadqueue
->GetDownloadingFileRate();
287 float dlspeedall
= s_downloadrate
->GetRate();
288 s_downloadRateAdjust
= (dlspeedfiles
< 1.0 || dlspeedall
< 1.0) ? 1.0 : dlspeedall
/dlspeedfiles
;
292 /* ------------------------------- GRAPHS ---------------------------- */
297 The basic idea here is that we want to keep as much history as we can without paying
298 a high price in terms of memory space. Because we keep the history for display purposes,
299 we can take advantage of the fact that when the period shown in the graphs is long
300 then each pixel represents a long period. So as the most recent history we keep one
301 window full of points at a resolution of 1 second, the next window full at 2 seconds,
302 the next at 4 seconds and so on, up to the maximum desired. This way there is always
303 at least one sample point per pixel for any update delay set by the user, and the
304 memory required grows with the *log* of the total time period covered.
305 The history is kept in a doubly-linked list, with the most recent snapshot at the tail.
306 The number of nodes in the list is fixed, and there are no calls to RemoveHead() and
307 AddTail() which would add overhead and contribute to memory fragmentation. Instead,
308 every second when a new point gets recorded, one of the existing nodes is recycled;
309 it is disjoined from its present place, put at the tail of the list, and then gets
310 filled with new data. [Emilio Sandoz]
311 This unfortunately does not work with stl classes, as none of them supports moving
312 a node to another place, so we have to erase and re-add nodes.
315 void CStatistics::RecordHistory()
316 { // First we query and compute some values, then we store them in the history list
318 // A few comments about the use of double and float in computations:
319 // Even on a hi-res screen our graphs will have 10 bits of resolution at most,
320 // so the 24 bits resolution of a float on 32 bit Intel processors is more than
321 // enough for all displayed values. Rate computations however, and especially
322 // running average computations, use differences (delta bytes/ delta time), and
323 // for long uptimes the difference between two timestamps can lose too much
324 // accuracy because the large mantissa causes less significant bits to be dropped
325 // (same for the difference between two cumulative byte counts). [We don't store
326 // these values as integers because they will be used in floating point calculations,
327 // and we want to perform the conversion only once). Therefore timestamps and
328 // Kbyte counts are stored in the history as doubles, while computed values use
329 // float (to save space and execution time).
332 Store values; first determine the node to be recycled (using the bits in iClock)
334 oldest records ----------------- listHR ------------------ youngest records
336 O-[Range 2^n sec]-O- ... -O-[Range 4 sec]-O-[Range 2 sec]-O-[Range 1 sec]-O
337 | | | | > every 2 secs -^
338 | | ... | >--------------- every 4 secs -^
339 | | >------------------------ recycle every 8 secs -^
341 | >-the node at this position is recycled every 2^n secs -^
342 >-------------------(ditto for the oldest node at the head of the list) --^
344 aposRecycle[nHistRanges-1] ... aposRecycle[0] Tail
348 int iClockPrev
= iClock
++;
349 int bits
= (iClockPrev
^iClock
) & iClock
; // identify the highest changed bit
350 if (bits
<= bitsHistClockMask
) {
352 while ((bits
/= 2) != 0) // count to the highest bit that was just toggled to 1
354 // recycle one node and jump over the next to move it to the next higher range
355 listHR
.push_back(**ppos
);
356 *ppos
= ++listHR
.erase(*ppos
);
358 ppos
= aposRecycle
+nHistRanges
-1;
359 // recycle the node at the head; there is no higher range to move nodes into
360 listHR
.push_back(**ppos
);
361 *ppos
= listHR
.erase(*ppos
);
364 // now save the latest data point in this node
365 listPOS phr
= --listHR
.end();
366 phr
->kBytesSent
= GetSessionSentBytes() / 1024.0;
367 phr
->kBytesReceived
= GetSessionReceivedBytes() / 1024.0;
368 phr
->kBpsUpCur
= GetUploadRate() / 1024.0;
369 phr
->kBpsDownCur
= GetDownloadRate() / 1024.0;
370 phr
->cntUploads
= GetActiveUploadsCount();
371 phr
->cntConnections
= GetActiveConnections();
372 phr
->cntDownloads
= GetDownloadingSources();
373 phr
->sTimestamp
= GetUptimeMillis() / 1000.0;
375 s_kadNodesTotal
+= s_kadNodesCur
;
376 phr
->kadNodesTotal
= s_kadNodesTotal
;
377 phr
->kadNodesCur
= s_kadNodesCur
;
381 unsigned CStatistics::GetHistory( // Assemble arrays of sample points for a graph
382 unsigned cntPoints
, // number of sample points to assemble
383 double sStep
, // time difference between sample points
384 double sFinal
, // latest allowed timestamp
385 const std::vector
<float *> &ppf
,// an array of pointers to arrays of floats for the result
386 StatsGraphType which_graph
) // the graph which will receive the points
388 if (sStep
==0.0 || cntPoints
==0) {
395 unsigned cntFilled
= 0;
396 listRPOS pos
= listHR
.rbegin();
398 // start of list should be an integer multiple of the sampling period for samples
399 // to be consistent when the graphs are resized horizontally
404 sTarget
= sStep
==1.0 ?
406 std::floor(pos
->sTimestamp
/sStep
) * sStep
;
409 HR
**ahr
= NULL
, **pphr
= NULL
;
410 bool bRateGraph
= (which_graph
!= GRAPH_CONN
); // rate graph or connections graph?
412 ahr
= new HR
* [cntPoints
];
416 while (pos
!= listHR
.rend()) {
417 if (pos
->sTimestamp
> sTarget
) {
421 if (bRateGraph
) { // assemble an array of pointers for ComputeAverages
423 } else { // or build the arrays if possible
424 *pf1
++ = (float)pos
->cntUploads
;
425 *pf2
++ = (float)pos
->cntConnections
;
426 *pf3
++ = (float)pos
->cntDownloads
;
428 if (++cntFilled
== cntPoints
) { // enough points
431 if (pos
->sTimestamp
== 0.0) { // reached beginning of uptime
434 if ((sTarget
-= sStep
) <= 0.0) { // don't overshoot the beginning
438 *pf1
++ = *pf2
++ = *pf3
++ = 0.0;
447 ComputeAverages(pphr
, pos
, cntFilled
, sStep
, ppf
, which_graph
);
456 unsigned CStatistics::GetHistoryForWeb( // Assemble arrays of sample points for the webserver
457 unsigned cntPoints
, // maximum number of sample points to assemble
458 double sStep
, // time difference between sample points
459 double *sStart
, // earliest allowed timestamp
460 uint32
**graphData
) // a pointer to a pointer that will point to the graph data array
465 if (sStep
==0.0 || cntPoints
==0)
467 unsigned cntFilled
= 0;
468 listRPOS pos
= listHR
.rbegin();
469 double LastTimeStamp
= pos
->sTimestamp
;
470 double sTarget
= LastTimeStamp
;
472 HR
**pphr
= new HR
*[cntPoints
];
474 while (pos
!= listHR
.rend()) {
475 if (pos
->sTimestamp
> sTarget
) {
476 ++pos
; // find next history record
479 pphr
[cntFilled
] = &(*pos
);
480 if (++cntFilled
== cntPoints
) // enough points
482 if (pos
->sTimestamp
<= *sStart
) // reached beginning of requested time
484 if ((sTarget
-= sStep
) <= 0.0) { // don't overshoot the beginning
485 pphr
[cntFilled
++] = NULL
;
491 *graphData
= new uint32
[4 * cntFilled
];
493 for (unsigned int i
= 0; i
< cntFilled
; i
++) {
494 HR
*phr
= pphr
[cntFilled
- i
- 1];
496 (*graphData
)[4 * i
] = ENDIAN_HTONL((uint32
)(phr
->kBpsDownCur
* 1024.0));
497 (*graphData
)[4 * i
+ 1] = ENDIAN_HTONL((uint32
)(phr
->kBpsUpCur
* 1024.0));
498 (*graphData
)[4 * i
+ 2] = ENDIAN_HTONL((uint32
)phr
->cntConnections
);
499 (*graphData
)[4 * i
+ 3] = ENDIAN_HTONL((uint32
)phr
->kadNodesCur
);
501 (*graphData
)[4 * i
] = (*graphData
)[4 * i
+ 1] = 0;
502 (*graphData
)[4 * i
+ 2] = (*graphData
)[4 * i
+ 3] = 0;
512 *sStart
= LastTimeStamp
;
518 void CStatistics::ComputeAverages(
519 HR
**pphr
, // pointer to (end of) array of assembled history records
520 listRPOS pos
, // position in history list from which to backtrack
521 unsigned cntFilled
, // number of points in the sample data
522 double sStep
, // time difference between two samples
523 const std::vector
<float *> &ppf
,// an array of pointers to arrays of floats with sample data
524 StatsGraphType which_graph
) // the graph which will receive the points
526 double sTarget
, kValueRun
;
527 uint64 avgTime
= average_minutes
* 60;
528 unsigned nBtPoints
= (unsigned)(avgTime
/ sStep
);
530 CPreciseRateCounter
* runningAvg
= NULL
;
531 switch (which_graph
) {
532 case GRAPH_DOWN
: runningAvg
= &m_graphRunningAvgDown
; break;
533 case GRAPH_UP
: runningAvg
= &m_graphRunningAvgUp
; break;
534 case GRAPH_KAD
: runningAvg
= &m_graphRunningAvgKad
; break;
536 wxCHECK_RET(false, wxT("ComputeAverages called with unsupported graph type."));
539 runningAvg
->m_timespan
= avgTime
* 1000;
540 runningAvg
->m_tick_history
.clear();
541 runningAvg
->m_byte_history
.clear();
542 runningAvg
->m_total
= 0;
543 runningAvg
->m_tmp_sum
= 0;
545 if (pos
== listHR
.rend()) {
548 sTarget
= std::max(0.0, pos
->sTimestamp
- sStep
);
551 while (nBtPoints
--) {
552 while (pos
!= listHR
.rend() && pos
->sTimestamp
> sTarget
) ++pos
; // find next history record
553 if (pos
!= listHR
.rend()) {
554 runningAvg
->m_tick_history
.push_front((uint64
)(pos
->sTimestamp
* 1000.0));
557 switch (which_graph
) {
559 value
= (uint32
)(pos
->kBpsDownCur
* 1024.0);
562 value
= (uint32
)(pos
->kBpsUpCur
* 1024.0);
565 value
= (uint32
)(pos
->kadNodesCur
* 1024.0);
568 wxCHECK_RET(false, wxT("ComputeAverages called with unsupported graph type."));
571 runningAvg
->m_byte_history
.push_front(value
);
572 runningAvg
->m_total
+= value
;
576 if ((sTarget
-= sStep
) < 0.0) {
581 // now compute averages in returned arrays, starting with the earliest values
582 float *pf1
= ppf
[0] + cntFilled
- 1; // holds session avg
583 float *pf2
= ppf
[1] + cntFilled
- 1; // holds running avg
584 float *pf3
= ppf
[2] + cntFilled
- 1; // holds current rate
586 for (int cnt
=cntFilled
; cnt
>0; cnt
--, pf1
--, pf2
--, pf3
--) {
588 if (which_graph
== GRAPH_DOWN
) {
589 kValueRun
= phr
->kBytesReceived
;
590 *pf3
= phr
->kBpsDownCur
;
591 } else if (which_graph
== GRAPH_UP
) {
592 kValueRun
= phr
->kBytesSent
;
593 *pf3
= phr
->kBpsUpCur
;
595 kValueRun
= phr
->kadNodesTotal
;
596 *pf3
= phr
->kadNodesCur
;
599 *pf1
= kValueRun
/ phr
->sTimestamp
;
600 (*runningAvg
) += (uint32
)(*pf3
* 1024.0);
601 runningAvg
->CalculateRate((uint64
)(phr
->sTimestamp
* 1000.0));
602 *pf2
= (float)(runningAvg
->GetRate() / 1024.0);
607 GraphUpdateInfo
CStatistics::GetPointsForUpdate()
609 GraphUpdateInfo update
;
610 listPOS phr
= --listHR
.end();
611 update
.timestamp
= (double) phr
->sTimestamp
;
613 m_graphRunningAvgDown
+= (uint32
)(phr
->kBpsDownCur
* 1024.0);
614 m_graphRunningAvgUp
+= (uint32
)(phr
->kBpsUpCur
* 1024.0);
615 // Note: kadNodesCur is multiplied by 1024 since the value is done
616 // in other places, so we simply follow suit here to avoid trouble.
617 m_graphRunningAvgKad
+= (uint32
)(phr
->kadNodesCur
* 1024.0);
618 m_graphRunningAvgDown
.CalculateRate((uint64
)(phr
->sTimestamp
* 1000.0));
619 m_graphRunningAvgUp
.CalculateRate((uint64
)(phr
->sTimestamp
* 1000.0));
620 m_graphRunningAvgKad
.CalculateRate((uint64
)(phr
->sTimestamp
* 1000.0));
622 update
.downloads
[0] = phr
->kBytesReceived
/ phr
->sTimestamp
;
623 update
.downloads
[1] = m_graphRunningAvgDown
.GetRate() / 1024.0;
624 update
.downloads
[2] = phr
->kBpsDownCur
;
626 update
.uploads
[0] = phr
->kBytesSent
/ phr
->sTimestamp
;
627 update
.uploads
[1] = m_graphRunningAvgUp
.GetRate() / 1024.0;
628 update
.uploads
[2] = phr
->kBpsUpCur
;
630 update
.connections
[0] = (float)phr
->cntUploads
;
631 update
.connections
[1] = (float)phr
->cntConnections
;
632 update
.connections
[2] = (float)phr
->cntDownloads
;
634 update
.kadnodes
[0] = phr
->kadNodesTotal
/ phr
->sTimestamp
;
635 update
.kadnodes
[1] = m_graphRunningAvgKad
.GetRate() / 1024.0;
636 update
.kadnodes
[2] = phr
->kadNodesCur
;
642 /* ------------------------------- TREE ---------------------------- */
644 void CStatistics::InitStatsTree()
646 s_statTree
= new CStatTreeItemBase(wxTRANSLATE("Statistics"));
648 CStatTreeItemBase
* tmpRoot1
;
649 CStatTreeItemBase
* tmpRoot2
;
651 s_uptime
= (CStatTreeItemTimer
*)s_statTree
->AddChild(new CStatTreeItemTimer(wxTRANSLATE("Uptime: %s")));
653 tmpRoot1
= s_statTree
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Transfer"), stSortChildren
));
655 tmpRoot2
= tmpRoot1
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Uploads")), 2);
656 s_sessionUpload
= (CStatTreeItemUlDlCounter
*)tmpRoot2
->AddChild(new CStatTreeItemUlDlCounter(wxTRANSLATE("Uploaded Data (Session (Total)): %s"), thePrefs::GetTotalUploaded
, stSortChildren
| stSortByValue
));
657 // Children will be added on-the-fly
658 s_totalUpOverhead
= (CStatTreeItemPacketTotals
*)tmpRoot2
->AddChild(new CStatTreeItemPacketTotals(wxTRANSLATE("Total Overhead (Packets): %s")));
659 s_fileReqUpOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("File Request Overhead (Packets): %s")));
660 s_totalUpOverhead
->AddPacketCounter(s_fileReqUpOverhead
);
661 s_sourceXchgUpOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Source Exchange Overhead (Packets): %s")));
662 s_totalUpOverhead
->AddPacketCounter(s_sourceXchgUpOverhead
);
663 s_serverUpOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Server Overhead (Packets): %s")));
664 s_totalUpOverhead
->AddPacketCounter(s_serverUpOverhead
);
665 s_kadUpOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Kad Overhead (Packets): %s")));
666 s_totalUpOverhead
->AddPacketCounter(s_kadUpOverhead
);
667 s_cryptUpOverhead
= (CStatTreeItemCounter
*)tmpRoot2
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Crypt overhead (UDP): %s")));
668 s_cryptUpOverhead
->SetDisplayMode(dmBytes
);
669 s_activeUploads
= (CStatTreeItemNativeCounter
*)tmpRoot2
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Active Uploads: %s")));
670 s_waitingUploads
= (CStatTreeItemNativeCounter
*)tmpRoot2
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Waiting Uploads: %s")));
671 s_totalSuccUploads
= (CStatTreeItemCounter
*)tmpRoot2
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Total successful upload sessions: %s")));
672 s_totalFailedUploads
= (CStatTreeItemCounter
*)tmpRoot2
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Total failed upload sessions: %s")));
673 s_totalUploadTime
= new CStatTreeItemCounter(wxEmptyString
);
674 tmpRoot2
->AddChild(new CStatTreeItemAverage(wxTRANSLATE("Average upload time: %s"), s_totalUploadTime
, s_totalSuccUploads
, dmTime
));
676 tmpRoot2
= tmpRoot1
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Downloads")), 1);
677 s_sessionDownload
= (CStatTreeItemUlDlCounter
*)tmpRoot2
->AddChild(new CStatTreeItemUlDlCounter(wxTRANSLATE("Downloaded Data (Session (Total)): %s"), thePrefs::GetTotalDownloaded
, stSortChildren
| stSortByValue
));
678 // Children will be added on-the-fly
679 s_totalDownOverhead
= (CStatTreeItemPacketTotals
*)tmpRoot2
->AddChild(new CStatTreeItemPacketTotals(wxTRANSLATE("Total Overhead (Packets): %s")));
680 s_fileReqDownOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("File Request Overhead (Packets): %s")));
681 s_totalDownOverhead
->AddPacketCounter(s_fileReqDownOverhead
);
682 s_sourceXchgDownOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Source Exchange Overhead (Packets): %s")));
683 s_totalDownOverhead
->AddPacketCounter(s_sourceXchgDownOverhead
);
684 s_serverDownOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Server Overhead (Packets): %s")));
685 s_totalDownOverhead
->AddPacketCounter(s_serverDownOverhead
);
686 s_kadDownOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Kad Overhead (Packets): %s")));
687 s_totalDownOverhead
->AddPacketCounter(s_kadDownOverhead
);
688 s_cryptDownOverhead
= (CStatTreeItemCounter
*)tmpRoot2
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Crypt overhead (UDP): %s")));
689 s_cryptDownOverhead
->SetDisplayMode(dmBytes
);
690 s_foundSources
= (CStatTreeItemNativeCounter
*)tmpRoot2
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Found Sources: %s"), stSortChildren
| stSortByValue
));
691 s_activeDownloads
= (CStatTreeItemNativeCounter
*)tmpRoot2
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Active Downloads (chunks): %s")));
693 tmpRoot1
->AddChild(new CStatTreeItemRatio(wxTRANSLATE("Session UL:DL Ratio (Total): %s"), s_sessionUpload
, s_sessionDownload
), 3);
695 tmpRoot1
= s_statTree
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Connection")));
696 tmpRoot1
->AddChild(new CStatTreeItemAverageSpeed(wxTRANSLATE("Average download rate (Session): %s"), s_sessionDownload
, s_uptime
));
697 tmpRoot1
->AddChild(new CStatTreeItemAverageSpeed(wxTRANSLATE("Average upload rate (Session): %s"), s_sessionUpload
, s_uptime
));
698 s_downloadrate
= (CStatTreeItemRateCounter
*)tmpRoot1
->AddChild(new CStatTreeItemRateCounter(wxTRANSLATE("Max download rate (Session): %s"), true, 30000));
699 s_uploadrate
= (CStatTreeItemRateCounter
*)tmpRoot1
->AddChild(new CStatTreeItemRateCounter(wxTRANSLATE("Max upload rate (Session): %s"), true, 30000));
700 s_reconnects
= (CStatTreeItemReconnects
*)tmpRoot1
->AddChild(new CStatTreeItemReconnects(wxTRANSLATE("Reconnects: %i")));
701 s_sinceFirstTransfer
= (CStatTreeItemTimer
*)tmpRoot1
->AddChild(new CStatTreeItemTimer(wxTRANSLATE("Time Since First Transfer: %s"), stHideIfZero
));
702 s_sinceConnected
= (CStatTreeItemTimer
*)tmpRoot1
->AddChild(new CStatTreeItemTimer(wxTRANSLATE("Connected To Server Since: %s")));
703 s_activeConnections
= (CStatTreeItemCounterMax
*)tmpRoot1
->AddChild(new CStatTreeItemCounterMax(wxTRANSLATE("Active Connections (estimate): %i")));
704 s_limitReached
= (CStatTreeItemMaxConnLimitReached
*)tmpRoot1
->AddChild(new CStatTreeItemMaxConnLimitReached(wxTRANSLATE("Max Connection Limit Reached: %s")));
705 s_avgConnections
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Average Connections (estimate): %g")));
706 s_avgConnections
->SetValue(0.0);
707 tmpRoot1
->AddChild(new CStatTreeItemPeakConnections(wxTRANSLATE("Peak Connections (estimate): %i")));
709 s_clients
= (CStatTreeItemHiddenCounter
*)s_statTree
->AddChild(new CStatTreeItemHiddenCounter(wxTRANSLATE("Clients"), stSortChildren
| stSortByValue
));
710 s_unknown
= (CStatTreeItemCounter
*)s_clients
->AddChild(new CStatTreeItemCounter(wxString(wxTRANSLATE("Unknown")) + wxT(": %s")), 6);
711 //s_lowID = (CStatTreeItem*)s_clients->AddChild(new CStatTreeItem(wxTRANSLATE("LowID: %u (%.2f%% Total %.2f%% Known)")), 5);
712 //s_secIdentOnOff = (CStatTreeItem*)s_clients->AddChild(new CStatTreeItem(wxTRANSLATE("SecIdent On/Off: %u (%.2f%%) : %u (%.2f%%)")), 4);
714 s_hasSocket
= (CStatTreeItemNativeCounter
*)s_clients
->AddChild(new CStatTreeItemNativeCounter(wxT("HasSocket: %s")), 3);
716 s_filtered
= (CStatTreeItemNativeCounter
*)s_clients
->AddChild(new CStatTreeItemNativeCounter(wxString(wxTRANSLATE("Filtered")) + wxT(": %s")), 2);
717 s_banned
= (CStatTreeItemNativeCounter
*)s_clients
->AddChild(new CStatTreeItemNativeCounter(wxString(wxTRANSLATE("Banned")) + wxT(": %s")), 1);
718 s_clients
->AddChild(new CStatTreeItemTotalClients(wxTRANSLATE("Total: %i Known: %i"), s_clients
, s_unknown
), 0x80000000);
720 // TODO: Use counters?
721 tmpRoot1
= s_statTree
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Servers")));
722 s_workingServers
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Working Servers: %i")));
723 s_failedServers
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Failed Servers: %i")));
724 s_totalServers
= (CStatTreeItemNativeCounter
*)tmpRoot1
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Total: %s")));
725 s_deletedServers
= (CStatTreeItemNativeCounter
*)tmpRoot1
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Deleted Servers: %s")));
726 s_filteredServers
= (CStatTreeItemNativeCounter
*)tmpRoot1
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Filtered Servers: %s")));
727 s_usersOnWorking
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Users on Working Servers: %llu")));
728 s_filesOnWorking
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Files on Working Servers: %llu")));
729 s_totalUsers
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Total Users: %llu")));
730 s_totalFiles
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Total Files: %llu")));
731 s_serverOccupation
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Server Occupation: %.2f%%")));
732 s_serverOccupation
->SetValue(0.0);
734 tmpRoot1
= s_statTree
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Shared Files")));
735 s_numberOfShared
= (CStatTreeItemCounter
*)tmpRoot1
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Number of Shared Files: %s")));
736 s_sizeOfShare
= (CStatTreeItemCounter
*)tmpRoot1
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Total size of Shared Files: %s")));
737 s_sizeOfShare
->SetDisplayMode(dmBytes
);
738 tmpRoot1
->AddChild(new CStatTreeItemAverage(wxTRANSLATE("Average file size: %s"), s_sizeOfShare
, s_numberOfShared
, dmBytes
));
742 void CStatistics::UpdateStatsTree()
744 // get sort orders right
745 s_sessionUpload
->ReSortChildren();
746 s_sessionDownload
->ReSortChildren();
747 s_clients
->ReSortChildren();
748 s_foundSources
->ReSortChildren();
749 // TODO: sort OS_Info subtrees.
751 s_avgConnections
->SetValue(theApp
->listensocket
->GetAverageConnections());
754 // TODO: make these realtime, too
761 theApp
->serverlist
->GetStatus(servfail
, servuser
, servfile
, servtuser
, servtfile
, servocc
);
762 s_workingServers
->SetValue((uint64
)((*s_totalServers
)-servfail
));
763 s_failedServers
->SetValue((uint64
)servfail
);
764 s_usersOnWorking
->SetValue((uint64
)servuser
);
765 s_filesOnWorking
->SetValue((uint64
)servfile
);
766 s_totalUsers
->SetValue((uint64
)servtuser
);
767 s_totalFiles
->SetValue((uint64
)servtfile
);
768 s_serverOccupation
->SetValue(servocc
);
772 void CStatistics::AddSourceOrigin(unsigned origin
)
774 CStatTreeItemNativeCounter
* counter
= (CStatTreeItemNativeCounter
*)s_foundSources
->GetChildById(0x0100 + origin
);
778 counter
= new CStatTreeItemNativeCounter(OriginToText(origin
) + wxT(": %s"), stHideIfZero
| stShowPercent
);
780 s_foundSources
->AddChild(counter
, 0x0100 + origin
);
784 void CStatistics::RemoveSourceOrigin(unsigned origin
)
786 CStatTreeItemNativeCounter
* counter
= (CStatTreeItemNativeCounter
*)s_foundSources
->GetChildById(0x0100 + origin
);
791 uint32
GetSoftID(uint8 SoftType
)
793 // prevent appearing multiple tree entries with the same name
794 // this should be kept in sync with GetSoftName().
797 return 0x0100 + SO_EMULE
;
798 case SO_NEW_SHAREAZA
:
799 case SO_NEW2_SHAREAZA
:
800 return 0x0100 + SO_SHAREAZA
;
801 case SO_NEW2_MLDONKEY
:
802 return 0x0100 + SO_NEW_MLDONKEY
;
804 return 0x0100 + SoftType
;
808 void CStatistics::AddDownloadFromSoft(uint8 SoftType
, uint32 bytes
)
810 AddReceivedBytes(bytes
);
812 uint32 id
= GetSoftID(SoftType
);
814 if (s_sessionDownload
->HasChildWithId(id
)) {
815 (*((CStatTreeItemCounter
*)s_sessionDownload
->GetChildById(id
))) += bytes
;
817 CStatTreeItemCounter
* tmp
= new CStatTreeItemCounter(GetSoftName(SoftType
) + wxT(": %s"));
818 tmp
->SetDisplayMode(dmBytes
);
820 s_sessionDownload
->AddChild(tmp
, id
);
824 void CStatistics::AddUploadToSoft(uint8 SoftType
, uint32 bytes
)
826 uint32 id
= GetSoftID(SoftType
);
828 if (s_sessionUpload
->HasChildWithId(id
)) {
829 (*((CStatTreeItemCounter
*)s_sessionUpload
->GetChildById(id
))) += bytes
;
831 CStatTreeItemCounter
* tmp
= new CStatTreeItemCounter(GetSoftName(SoftType
) + wxT(": %s"));
832 tmp
->SetDisplayMode(dmBytes
);
834 s_sessionUpload
->AddChild(tmp
, id
);
838 inline bool SupportsOSInfo(unsigned clientSoft
)
840 return (clientSoft
== SO_AMULE
) || (clientSoft
== SO_HYDRANODE
) || (clientSoft
== SO_NEW2_MLDONKEY
);
843 // Do some random black magic to strings to get a relatively unique number for them.
844 uint32
GetIdFromString(const wxString
& str
)
847 for (unsigned i
= 0; i
< str
.Length(); ++i
) {
848 unsigned old_id
= id
;
849 id
+= (uint32
)str
.GetChar(i
);
854 return ((id
>> 1) + id
| 0x00000100) & 0x7fffffff;
857 void CStatistics::AddKnownClient(CUpDownClient
*pClient
)
861 uint32 clientSoft
= pClient
->GetClientSoft();
862 uint32 id
= GetSoftID(clientSoft
);
864 CStatTreeItemCounter
*client
;
866 if (s_clients
->HasChildWithId(id
)) {
867 client
= (CStatTreeItemCounter
*)s_clients
->GetChildById(id
);
870 uint32 flags
= stSortChildren
| stShowPercent
| stHideIfZero
;
871 if (!SupportsOSInfo(clientSoft
)) {
872 flags
|= stCapChildren
;
874 client
= new CStatTreeItemCounter(GetSoftName(clientSoft
) + wxT(": %s"), flags
);
876 s_clients
->AddChild(client
, id
);
877 if (SupportsOSInfo(clientSoft
)) {
878 client
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Version"), stSortChildren
| stCapChildren
), 2);
879 client
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Operating System"), stSortChildren
| stSortByValue
), 1);
883 CStatTreeItemBase
*versionRoot
= SupportsOSInfo(clientSoft
) ? client
->GetChildById(2) : client
;
884 uint32 clientVersion
= pClient
->GetVersion();
886 if (versionRoot
->HasChildWithId(clientVersion
)) {
887 CStatTreeItemCounter
*version
= (CStatTreeItemCounter
*)versionRoot
->GetChildById(clientVersion
);
890 const wxString
& versionStr
= pClient
->GetVersionString();
891 CStatTreeItemCounter
*version
= new CStatTreeItemCounter((versionStr
.IsEmpty() ? wxString(wxTRANSLATE("Unknown")) : versionStr
) + wxT(": %s"), stShowPercent
| stHideIfZero
);
893 versionRoot
->AddChild(version
, clientVersion
, SupportsOSInfo(clientSoft
));
896 if (SupportsOSInfo(clientSoft
)) {
897 const wxString
& OSInfo
= pClient
->GetClientOSInfo();
898 uint32 OS_ID
= OSInfo
.IsEmpty() ? 0 : GetIdFromString(OSInfo
);
899 CStatTreeItemBase
* OSRoot
= client
->GetChildById(1);
900 CStatTreeItemCounter
* OSNode
= (CStatTreeItemCounter
*)OSRoot
->GetChildById(OS_ID
);
904 OSNode
= new CStatTreeItemCounter((OS_ID
? OSInfo
: wxString(wxTRANSLATE("Not Received"))) + wxT(": %s"), stShowPercent
| stHideIfZero
);
906 OSRoot
->AddChild(OSNode
, OS_ID
, true);
911 void CStatistics::RemoveKnownClient(uint32 clientSoft
, uint32 clientVersion
, const wxString
& OSInfo
)
915 uint32 id
= GetSoftID(clientSoft
);
917 CStatTreeItemCounter
*client
= (CStatTreeItemCounter
*)s_clients
->GetChildById(id
);
921 CStatTreeItemBase
*versionRoot
= SupportsOSInfo(clientSoft
) ? client
->GetChildById(2) : client
;
923 CStatTreeItemCounter
*version
= (CStatTreeItemCounter
*)versionRoot
->GetChildById(clientVersion
);
927 if (SupportsOSInfo(clientSoft
)) {
928 uint32 OS_ID
= OSInfo
.IsEmpty() ? 0 : GetIdFromString(OSInfo
);
929 CStatTreeItemCounter
* OSNode
= (CStatTreeItemCounter
*)client
->GetChildById(1)->GetChildById(OS_ID
);
935 #else /* EC_REMOTE (CLIENT_GUI) */
937 CStatistics::CStatistics(CRemoteConnect
&conn
)
941 s_start_time
= GetTickCount64();
944 s_statTree
= new CStatTreeItemBase(_("Statistics"), 0);
946 // Clear stat data container
947 for (int i
= 0; i
< sdTotalItems
; ++i
) {
953 CStatistics::~CStatistics()
959 void CStatistics::UpdateStats(const CECPacket
* stats
)
961 s_statData
[sdUpload
] = stats
->GetTagByNameSafe(EC_TAG_STATS_UL_SPEED
)->GetInt();
962 s_statData
[sdUpOverhead
] = stats
->GetTagByNameSafe(EC_TAG_STATS_UP_OVERHEAD
)->GetInt();
963 s_statData
[sdDownload
] = stats
->GetTagByNameSafe(EC_TAG_STATS_DL_SPEED
)->GetInt();
964 s_statData
[sdDownOverhead
] = stats
->GetTagByNameSafe(EC_TAG_STATS_DOWN_OVERHEAD
)->GetInt();
965 s_statData
[sdWaitingClients
] = stats
->GetTagByNameSafe(EC_TAG_STATS_UL_QUEUE_LEN
)->GetInt();
966 s_statData
[sdBannedClients
] = stats
->GetTagByNameSafe(EC_TAG_STATS_BANNED_COUNT
)->GetInt();
967 s_statData
[sdED2KUsers
] = stats
->GetTagByNameSafe(EC_TAG_STATS_ED2K_USERS
)->GetInt();
968 s_statData
[sdKadUsers
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_USERS
)->GetInt();
969 s_statData
[sdED2KFiles
] = stats
->GetTagByNameSafe(EC_TAG_STATS_ED2K_FILES
)->GetInt();
970 s_statData
[sdKadFiles
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_FILES
)->GetInt();
974 void CStatistics::UpdateStatsTree()
976 CECPacket
request(EC_OP_GET_STATSTREE
);
977 if (thePrefs::GetMaxClientVersions() != 0) {
978 request
.AddTag(CECTag(EC_TAG_STATTREE_CAPPING
, (uint8
)thePrefs::GetMaxClientVersions()));
980 const CECPacket
* reply
= m_conn
.SendRecvPacket(&request
);
982 const CECTag
* treeRoot
= reply
->GetTagByName(EC_TAG_STATTREE_NODE
);
985 s_statTree
= new CStatTreeItemBase(treeRoot
);
991 #endif /* !EC_REMOTE */
993 // File_checked_for_headers