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 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
45 #include "Preferences.h"
46 #include <ec/cpp/RemoteConnect.h> // Needed for CRemoteConnect
55 #define round(x) floor(x+0.5)
61 /*----- CPreciseRateCounter -----*/
63 void CPreciseRateCounter::CalculateRate(uint64_t now
)
65 wxMutexLocker
lock(m_mutex
);
68 m_byte_history
.push_back(m_tmp_sum
);
69 m_tick_history
.push_back(now
);
72 uint64_t timespan
= now
- m_tick_history
.front();
74 // Checking maximal timespan, but make sure not to remove
75 // the extra node in m_tick_history.
76 while (timespan
> m_timespan
&& m_byte_history
.size() > 0) {
77 m_total
-= m_byte_history
.front();
78 m_byte_history
.pop_front();
79 m_tick_history
.pop_front();
80 timespan
= now
- m_tick_history
.front();
84 if (m_count_average
) {
85 if (m_byte_history
.size() > 0) {
86 m_rate
= m_total
/ (double)m_byte_history
.size();
90 m_rate
= m_total
/ (timespan
/ 1000.0);
94 if (m_rate
> m_max_rate
) {
100 /*----- CStatTreeItemRateCounter -----*/
103 wxString
CStatTreeItemRateCounter::GetDisplayString() const
105 return CFormat(wxGetTranslation(m_label
)) % CastItoSpeed(m_show_maxrate
? (uint32
)m_max_rate
: (uint32
)m_rate
);
109 void CStatTreeItemRateCounter::AddECValues(CECTag
* tag
) const
111 CECTag
value(EC_TAG_STAT_NODE_VALUE
, m_show_maxrate
? (uint32
)m_max_rate
: (uint32
)m_rate
);
112 value
.AddTag(CECTag(EC_TAG_STAT_VALUE_TYPE
, (uint8
)EC_VALUE_SPEED
));
117 /*----- CStatTreeItemPeakConnections -----*/
120 wxString
CStatTreeItemPeakConnections::GetDisplayString() const
122 return wxString::Format(wxGetTranslation(m_label
), theStats::GetPeakConnections());
126 void CStatTreeItemPeakConnections::AddECValues(CECTag
* tag
) const
128 tag
->AddTag(CECTag(EC_TAG_STAT_NODE_VALUE
, (uint64
)theStats::GetPeakConnections()));
132 /*----- CStatistics -----*/
137 CPreciseRateCounter
* CStatistics::s_upOverheadRate
;
138 CPreciseRateCounter
* CStatistics::s_downOverheadRate
;
139 CStatTreeItemRateCounter
* CStatistics::s_uploadrate
;
140 CStatTreeItemRateCounter
* CStatistics::s_downloadrate
;
142 #else /* EC_REMOTE */
144 uint64
CStatistics::s_start_time
;
145 uint64
CStatistics::s_statData
[sdTotalItems
];
147 #endif /* !EC_REMOTE / EC_REMOTE */
150 CStatTreeItemBase
* CStatistics::s_statTree
;
154 CStatTreeItemTimer
* CStatistics::s_uptime
;
157 CStatTreeItemUlDlCounter
* CStatistics::s_sessionUpload
;
158 CStatTreeItemPacketTotals
* CStatistics::s_totalUpOverhead
;
159 CStatTreeItemPackets
* CStatistics::s_fileReqUpOverhead
;
160 CStatTreeItemPackets
* CStatistics::s_sourceXchgUpOverhead
;
161 CStatTreeItemPackets
* CStatistics::s_serverUpOverhead
;
162 CStatTreeItemPackets
* CStatistics::s_kadUpOverhead
;
163 CStatTreeItemNativeCounter
* CStatistics::s_activeUploads
;
164 CStatTreeItemNativeCounter
* CStatistics::s_waitingUploads
;
165 CStatTreeItemCounter
* CStatistics::s_totalSuccUploads
;
166 CStatTreeItemCounter
* CStatistics::s_totalFailedUploads
;
167 CStatTreeItemCounter
* CStatistics::s_totalUploadTime
;
170 CStatTreeItemUlDlCounter
* CStatistics::s_sessionDownload
;
171 CStatTreeItemPacketTotals
* CStatistics::s_totalDownOverhead
;
172 CStatTreeItemPackets
* CStatistics::s_fileReqDownOverhead
;
173 CStatTreeItemPackets
* CStatistics::s_sourceXchgDownOverhead
;
174 CStatTreeItemPackets
* CStatistics::s_serverDownOverhead
;
175 CStatTreeItemPackets
* CStatistics::s_kadDownOverhead
;
176 CStatTreeItemNativeCounter
* CStatistics::s_foundSources
;
177 CStatTreeItemNativeCounter
* CStatistics::s_activeDownloads
;
180 CStatTreeItemReconnects
* CStatistics::s_reconnects
;
181 CStatTreeItemTimer
* CStatistics::s_sinceFirstTransfer
;
182 CStatTreeItemTimer
* CStatistics::s_sinceConnected
;
183 CStatTreeItemCounterMax
* CStatistics::s_activeConnections
;
184 CStatTreeItemMaxConnLimitReached
* CStatistics::s_limitReached
;
185 CStatTreeItemSimple
* CStatistics::s_avgConnections
;
188 CStatTreeItemHiddenCounter
* CStatistics::s_clients
;
189 CStatTreeItemCounter
* CStatistics::s_unknown
;
190 //CStatTreeItem CStatistics::s_lowID;
191 //CStatTreeItem CStatistics::s_secIdentOnOff;
193 CStatTreeItemNativeCounter
* CStatistics::s_hasSocket
;
195 CStatTreeItemNativeCounter
* CStatistics::s_filtered
;
196 CStatTreeItemNativeCounter
* CStatistics::s_banned
;
199 CStatTreeItemSimple
* CStatistics::s_workingServers
;
200 CStatTreeItemSimple
* CStatistics::s_failedServers
;
201 CStatTreeItemNativeCounter
* CStatistics::s_totalServers
;
202 CStatTreeItemNativeCounter
* CStatistics::s_deletedServers
;
203 CStatTreeItemNativeCounter
* CStatistics::s_filteredServers
;
204 CStatTreeItemSimple
* CStatistics::s_usersOnWorking
;
205 CStatTreeItemSimple
* CStatistics::s_filesOnWorking
;
206 CStatTreeItemSimple
* CStatistics::s_totalUsers
;
207 CStatTreeItemSimple
* CStatistics::s_totalFiles
;
208 CStatTreeItemSimple
* CStatistics::s_serverOccupation
;
211 CStatTreeItemCounter
* CStatistics::s_numberOfShared
;
212 CStatTreeItemCounter
* CStatistics::s_sizeOfShare
;
215 uint64
CStatistics::s_kadNodesTotal
;
216 uint16
CStatistics::s_kadNodesCur
;
219 CStatistics::CStatistics()
220 : m_graphRunningAvgDown(thePrefs::GetStatsAverageMinutes() * 60 * 1000, true),
221 m_graphRunningAvgUp(thePrefs::GetStatsAverageMinutes() * 60 * 1000, true),
222 m_graphRunningAvgKad(thePrefs::GetStatsAverageMinutes() * 60 * 1000, true)
224 uint64 start_time
= GetTickCount64();
228 average_minutes
= thePrefs::GetStatsAverageMinutes();
230 HR hr
= {0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0};
232 nHistRanges
= 7; // =ceil(log(max_update_delay)/log(2))
233 nPointsPerRange
= GetPointsPerRange();
234 bitsHistClockMask
= (1 << (nHistRanges
-1)) - 1;
235 aposRecycle
= new listPOS
[nHistRanges
];
236 listPOS
*ppos
= aposRecycle
+nHistRanges
-1;
237 for (int i
=nHistRanges
; i
>0; --i
) { // permanently allocated history list
238 listHR
.push_back(hr
);
239 *ppos
-- = --listHR
.end();
240 for (int j
=nPointsPerRange
; j
>1; --j
)
241 listHR
.push_back(hr
);
244 // Init rate counters outside the tree
246 s_upOverheadRate
= new CPreciseRateCounter(5000);
247 s_downOverheadRate
= new CPreciseRateCounter(5000);
252 s_uptime
->SetStartTime(start_time
);
256 CStatistics::~CStatistics()
258 // clearing listHR frees the memory occupied by the nodes
260 delete [] aposRecycle
;
264 // delete items not in the tree
265 delete s_totalUploadTime
;
267 // delete rate counters outside the tree
268 delete s_upOverheadRate
;
269 delete s_downOverheadRate
;
273 void CStatistics::CalculateRates()
275 uint64_t now
= GetTickCount64();
276 s_downOverheadRate
->CalculateRate(now
);
277 s_upOverheadRate
->CalculateRate(now
);
278 s_downloadrate
->CalculateRate(now
);
279 s_uploadrate
->CalculateRate(now
);
283 /* ------------------------------- GRAPHS ---------------------------- */
288 The basic idea here is that we want to keep as much history as we can without paying
289 a high price in terms of memory space. Because we keep the history for display purposes,
290 we can take advantage of the fact that when the period shown in the graphs is long
291 then each pixel represents a long period. So as the most recent history we keep one
292 window full of points at a resolution of 1 second, the next window full at 2 seconds,
293 the next at 4 seconds and so on, up to the maximum desired. This way there is always
294 at least one sample point per pixel for any update delay set by the user, and the
295 memory required grows with the *log* of the total time period covered.
296 The history is kept in a doubly-linked list, with the most recent snapshot at the tail.
297 The number of nodes in the list is fixed, and there are no calls to RemoveHead() and
298 AddTail() which would add overhead and contribute to memory fragmentation. Instead,
299 every second when a new point gets recorded, one of the existing nodes is recycled;
300 it is disjoined from its present place, put at the tail of the list, and then gets
301 filled with new data. [Emilio Sandoz]
302 This unfortunately does not work with stl classes, as none of them supports moving
303 a node to another place, so we have to erase and re-add nodes.
306 void CStatistics::RecordHistory()
307 { // First we query and compute some values, then we store them in the history list
309 // A few comments about the use of double and float in computations:
310 // Even on a hi-res screen our graphs will have 10 bits of resolution at most,
311 // so the 24 bits resolution of a float on 32 bit Intel processors is more than
312 // enough for all displayed values. Rate computations however, and especially
313 // running average computations, use differences (delta bytes/ delta time), and
314 // for long uptimes the difference between two timestamps can lose too much
315 // accuracy because the large mantissa causes less significant bits to be dropped
316 // (same for the difference between two cumulative byte counts). [We don't store
317 // these values as integers because they will be used in floating point calculations,
318 // and we want to perform the conversion only once). Therefore timestamps and
319 // Kbyte counts are stored in the history as doubles, while computed values use
320 // float (to save space and execution time).
323 Store values; first determine the node to be recycled (using the bits in iClock)
325 oldest records ----------------- listHR ------------------ youngest records
327 O-[Range 2^n sec]-O- ... -O-[Range 4 sec]-O-[Range 2 sec]-O-[Range 1 sec]-O
328 | | | | > every 2 secs -^
329 | | ... | >--------------- every 4 secs -^
330 | | >------------------------ recycle every 8 secs -^
332 | >-the node at this position is recycled every 2^n secs -^
333 >-------------------(ditto for the oldest node at the head of the list) --^
335 aposRecycle[nHistRanges-1] ... aposRecycle[0] Tail
339 int iClockPrev
= iClock
++;
340 int bits
= (iClockPrev
^iClock
) & iClock
; // identify the highest changed bit
341 if (bits
<= bitsHistClockMask
) {
343 while ((bits
/= 2) != 0) // count to the highest bit that was just toggled to 1
345 // recycle one node and jump over the next to move it to the next higher range
346 listHR
.push_back(**ppos
);
347 *ppos
= ++listHR
.erase(*ppos
);
349 ppos
= aposRecycle
+nHistRanges
-1;
350 // recycle the node at the head; there is no higher range to move nodes into
351 listHR
.push_back(**ppos
);
352 *ppos
= listHR
.erase(*ppos
);
355 // now save the latest data point in this node
356 listPOS phr
= --listHR
.end();
357 phr
->kBytesSent
= GetSessionSentBytes() / 1024.0;
358 phr
->kBytesReceived
= GetSessionReceivedBytes() / 1024.0;
359 phr
->kBpsUpCur
= GetUploadRate() / 1024.0;
360 phr
->kBpsDownCur
= GetDownloadRate() / 1024.0;
361 phr
->cntUploads
= GetActiveUploadsCount();
362 phr
->cntConnections
= GetActiveConnections();
363 phr
->cntDownloads
= GetDownloadingSources();
364 phr
->sTimestamp
= GetUptimeMillis() / 1000.0;
366 s_kadNodesTotal
+= s_kadNodesCur
;
367 phr
->kadNodesTotal
= s_kadNodesTotal
;
368 phr
->kadNodesCur
= s_kadNodesCur
;
372 unsigned CStatistics::GetHistory( // Assemble arrays of sample points for a graph
373 unsigned cntPoints
, // number of sample points to assemble
374 double sStep
, // time difference between sample points
375 double sFinal
, // latest allowed timestamp
376 const std::vector
<float *> &ppf
,// an array of pointers to arrays of floats for the result
377 StatsGraphType which_graph
) // the graph which will receive the points
379 if (sStep
==0.0 || cntPoints
==0) {
386 unsigned cntFilled
= 0;
387 listRPOS pos
= listHR
.rbegin();
389 // start of list should be an integer multiple of the sampling period for samples
390 // to be consistent when the graphs are resized horizontally
395 sTarget
= sStep
==1.0 ?
397 std::floor(pos
->sTimestamp
/sStep
) * sStep
;
400 HR
**ahr
= NULL
, **pphr
= NULL
;
401 bool bRateGraph
= (which_graph
!= GRAPH_CONN
); // rate graph or connections graph?
403 ahr
= new HR
* [cntPoints
];
407 while (pos
!= listHR
.rend()) {
408 if (pos
->sTimestamp
> sTarget
) {
412 if (bRateGraph
) { // assemble an array of pointers for ComputeAverages
414 } else { // or build the arrays if possible
415 *pf1
++ = (float)pos
->cntUploads
;
416 *pf2
++ = (float)pos
->cntConnections
;
417 *pf3
++ = (float)pos
->cntDownloads
;
419 if (++cntFilled
== cntPoints
) { // enough points
422 if (pos
->sTimestamp
== 0.0) { // reached beginning of uptime
425 if ((sTarget
-= sStep
) <= 0.0) { // don't overshoot the beginning
429 *pf1
++ = *pf2
++ = *pf3
++ = 0.0;
438 ComputeAverages(pphr
, pos
, cntFilled
, sStep
, ppf
, which_graph
);
447 unsigned CStatistics::GetHistoryForWeb( // Assemble arrays of sample points for the webserver
448 unsigned cntPoints
, // maximum number of sample points to assemble
449 double sStep
, // time difference between sample points
450 double *sStart
, // earliest allowed timestamp
451 uint32
**graphData
) // a pointer to a pointer that will point to the graph data array
456 if (sStep
==0.0 || cntPoints
==0)
458 unsigned cntFilled
= 0;
459 listRPOS pos
= listHR
.rbegin();
460 double LastTimeStamp
= pos
->sTimestamp
;
461 double sTarget
= LastTimeStamp
;
463 HR
**pphr
= new HR
*[cntPoints
];
465 while (pos
!= listHR
.rend()) {
466 if (pos
->sTimestamp
> sTarget
) {
467 ++pos
; // find next history record
470 pphr
[cntFilled
] = &(*pos
);
471 if (++cntFilled
== cntPoints
) // enough points
473 if (pos
->sTimestamp
<= *sStart
) // reached beginning of requested time
475 if ((sTarget
-= sStep
) <= 0.0) { // don't overshoot the beginning
476 pphr
[cntFilled
++] = NULL
;
482 *graphData
= new uint32
[4 * cntFilled
];
484 for (unsigned int i
= 0; i
< cntFilled
; i
++) {
485 HR
*phr
= pphr
[cntFilled
- i
- 1];
487 (*graphData
)[4 * i
] = ENDIAN_HTONL((uint32
)(phr
->kBpsDownCur
* 1024.0));
488 (*graphData
)[4 * i
+ 1] = ENDIAN_HTONL((uint32
)(phr
->kBpsUpCur
* 1024.0));
489 (*graphData
)[4 * i
+ 2] = ENDIAN_HTONL((uint32
)phr
->cntConnections
);
490 (*graphData
)[4 * i
+ 3] = ENDIAN_HTONL((uint32
)phr
->kadNodesCur
);
492 (*graphData
)[4 * i
] = (*graphData
)[4 * i
+ 1] = 0;
493 (*graphData
)[4 * i
+ 2] = (*graphData
)[4 * i
+ 3] = 0;
503 *sStart
= LastTimeStamp
;
509 void CStatistics::ComputeAverages(
510 HR
**pphr
, // pointer to (end of) array of assembled history records
511 listRPOS pos
, // position in history list from which to backtrack
512 unsigned cntFilled
, // number of points in the sample data
513 double sStep
, // time difference between two samples
514 const std::vector
<float *> &ppf
,// an array of pointers to arrays of floats with sample data
515 StatsGraphType which_graph
) // the graph which will receive the points
517 double sTarget
, kValueRun
;
518 uint64 avgTime
= average_minutes
* 60;
519 unsigned nBtPoints
= (unsigned)(avgTime
/ sStep
);
521 CPreciseRateCounter
* runningAvg
= NULL
;
522 switch (which_graph
) {
523 case GRAPH_DOWN
: runningAvg
= &m_graphRunningAvgDown
; break;
524 case GRAPH_UP
: runningAvg
= &m_graphRunningAvgUp
; break;
525 case GRAPH_KAD
: runningAvg
= &m_graphRunningAvgKad
; break;
527 wxCHECK_RET(false, wxT("ComputeAverages called with unsupported graph type."));
530 runningAvg
->m_timespan
= avgTime
* 1000;
531 runningAvg
->m_tick_history
.clear();
532 runningAvg
->m_byte_history
.clear();
533 runningAvg
->m_total
= 0;
534 runningAvg
->m_tmp_sum
= 0;
536 if (pos
== listHR
.rend()) {
539 sTarget
= std::max(0.0, pos
->sTimestamp
- sStep
);
542 while (nBtPoints
--) {
543 while (pos
!= listHR
.rend() && pos
->sTimestamp
> sTarget
) ++pos
; // find next history record
544 if (pos
!= listHR
.rend()) {
545 runningAvg
->m_tick_history
.push_front((uint64
)(pos
->sTimestamp
* 1000.0));
548 switch (which_graph
) {
550 value
= (uint32
)(pos
->kBpsDownCur
* 1024.0);
553 value
= (uint32
)(pos
->kBpsUpCur
* 1024.0);
556 value
= (uint32
)(pos
->kadNodesCur
* 1024.0);
559 wxCHECK_RET(false, wxT("ComputeAverages called with unsupported graph type."));
562 runningAvg
->m_byte_history
.push_front(value
);
563 runningAvg
->m_total
+= value
;
567 if ((sTarget
-= sStep
) < 0.0) {
572 // now compute averages in returned arrays, starting with the earliest values
573 float *pf1
= ppf
[0] + cntFilled
- 1; // holds session avg
574 float *pf2
= ppf
[1] + cntFilled
- 1; // holds running avg
575 float *pf3
= ppf
[2] + cntFilled
- 1; // holds current rate
577 for (int cnt
=cntFilled
; cnt
>0; cnt
--, pf1
--, pf2
--, pf3
--) {
579 if (which_graph
== GRAPH_DOWN
) {
580 kValueRun
= phr
->kBytesReceived
;
581 *pf3
= phr
->kBpsDownCur
;
582 } else if (which_graph
== GRAPH_UP
) {
583 kValueRun
= phr
->kBytesSent
;
584 *pf3
= phr
->kBpsUpCur
;
586 kValueRun
= phr
->kadNodesTotal
;
587 *pf3
= phr
->kadNodesCur
;
590 *pf1
= kValueRun
/ phr
->sTimestamp
;
591 (*runningAvg
) += (uint32
)(*pf3
* 1024.0);
592 runningAvg
->CalculateRate((uint64
)(phr
->sTimestamp
* 1000.0));
593 *pf2
= (float)(runningAvg
->GetRate() / 1024.0);
598 GraphUpdateInfo
CStatistics::GetPointsForUpdate()
600 GraphUpdateInfo update
;
601 listPOS phr
= --listHR
.end();
602 update
.timestamp
= (double) phr
->sTimestamp
;
604 m_graphRunningAvgDown
+= (uint32
)(phr
->kBpsDownCur
* 1024.0);
605 m_graphRunningAvgUp
+= (uint32
)(phr
->kBpsUpCur
* 1024.0);
606 // Note: kadNodesCur is multiplied by 1024 since the value is done
607 // in other places, so we simply follow suit here to avoid trouble.
608 m_graphRunningAvgKad
+= (uint32
)(phr
->kadNodesCur
* 1024.0);
609 m_graphRunningAvgDown
.CalculateRate((uint64
)(phr
->sTimestamp
* 1000.0));
610 m_graphRunningAvgUp
.CalculateRate((uint64
)(phr
->sTimestamp
* 1000.0));
611 m_graphRunningAvgKad
.CalculateRate((uint64
)(phr
->sTimestamp
* 1000.0));
613 update
.downloads
[0] = phr
->kBytesReceived
/ phr
->sTimestamp
;
614 update
.downloads
[1] = m_graphRunningAvgDown
.GetRate() / 1024.0;
615 update
.downloads
[2] = phr
->kBpsDownCur
;
617 update
.uploads
[0] = phr
->kBytesSent
/ phr
->sTimestamp
;
618 update
.uploads
[1] = m_graphRunningAvgUp
.GetRate() / 1024.0;
619 update
.uploads
[2] = phr
->kBpsUpCur
;
621 update
.connections
[0] = (float)phr
->cntUploads
;
622 update
.connections
[1] = (float)phr
->cntConnections
;
623 update
.connections
[2] = (float)phr
->cntDownloads
;
625 update
.kadnodes
[0] = phr
->kadNodesTotal
/ phr
->sTimestamp
;
626 update
.kadnodes
[1] = m_graphRunningAvgKad
.GetRate() / 1024.0;
627 update
.kadnodes
[2] = phr
->kadNodesCur
;
633 /* ------------------------------- TREE ---------------------------- */
635 void CStatistics::InitStatsTree()
637 s_statTree
= new CStatTreeItemBase(wxTRANSLATE("Statistics"));
639 CStatTreeItemBase
* tmpRoot1
;
640 CStatTreeItemBase
* tmpRoot2
;
642 s_uptime
= (CStatTreeItemTimer
*)s_statTree
->AddChild(new CStatTreeItemTimer(wxTRANSLATE("Uptime: %s")));
644 tmpRoot1
= s_statTree
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Transfer"), stSortChildren
));
646 tmpRoot2
= tmpRoot1
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Uploads")), 2);
647 s_sessionUpload
= (CStatTreeItemUlDlCounter
*)tmpRoot2
->AddChild(new CStatTreeItemUlDlCounter(wxTRANSLATE("Uploaded Data (Session (Total)): %s"), thePrefs::GetTotalUploaded
, stSortChildren
| stSortByValue
));
648 // Children will be added on-the-fly
649 s_totalUpOverhead
= (CStatTreeItemPacketTotals
*)tmpRoot2
->AddChild(new CStatTreeItemPacketTotals(wxTRANSLATE("Total Overhead (Packets): %s")));
650 s_fileReqUpOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("File Request Overhead (Packets): %s")));
651 s_totalUpOverhead
->AddPacketCounter(s_fileReqUpOverhead
);
652 s_sourceXchgUpOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Source Exchange Overhead (Packets): %s")));
653 s_totalUpOverhead
->AddPacketCounter(s_sourceXchgUpOverhead
);
654 s_serverUpOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Server Overhead (Packets): %s")));
655 s_totalUpOverhead
->AddPacketCounter(s_serverUpOverhead
);
656 s_kadUpOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Kad Overhead (Packets): %s")));
657 s_totalUpOverhead
->AddPacketCounter(s_kadUpOverhead
);
658 s_activeUploads
= (CStatTreeItemNativeCounter
*)tmpRoot2
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Active Uploads: %s")));
659 s_waitingUploads
= (CStatTreeItemNativeCounter
*)tmpRoot2
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Waiting Uploads: %s")));
660 s_totalSuccUploads
= (CStatTreeItemCounter
*)tmpRoot2
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Total successful upload sessions: %s")));
661 s_totalFailedUploads
= (CStatTreeItemCounter
*)tmpRoot2
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Total failed upload sessions: %s")));
662 s_totalUploadTime
= new CStatTreeItemCounter(wxEmptyString
);
663 tmpRoot2
->AddChild(new CStatTreeItemAverage(wxTRANSLATE("Average upload time: %s"), s_totalUploadTime
, s_totalSuccUploads
, dmTime
));
665 tmpRoot2
= tmpRoot1
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Downloads")), 1);
666 s_sessionDownload
= (CStatTreeItemUlDlCounter
*)tmpRoot2
->AddChild(new CStatTreeItemUlDlCounter(wxTRANSLATE("Downloaded Data (Session (Total)): %s"), thePrefs::GetTotalDownloaded
, stSortChildren
| stSortByValue
));
667 // Children will be added on-the-fly
668 s_totalDownOverhead
= (CStatTreeItemPacketTotals
*)tmpRoot2
->AddChild(new CStatTreeItemPacketTotals(wxTRANSLATE("Total Overhead (Packets): %s")));
669 s_fileReqDownOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("File Request Overhead (Packets): %s")));
670 s_totalDownOverhead
->AddPacketCounter(s_fileReqDownOverhead
);
671 s_sourceXchgDownOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Source Exchange Overhead (Packets): %s")));
672 s_totalDownOverhead
->AddPacketCounter(s_sourceXchgDownOverhead
);
673 s_serverDownOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Server Overhead (Packets): %s")));
674 s_totalDownOverhead
->AddPacketCounter(s_serverDownOverhead
);
675 s_kadDownOverhead
= (CStatTreeItemPackets
*)tmpRoot2
->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Kad Overhead (Packets): %s")));
676 s_totalDownOverhead
->AddPacketCounter(s_kadDownOverhead
);
677 s_foundSources
= (CStatTreeItemNativeCounter
*)tmpRoot2
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Found Sources: %s"), stSortChildren
| stSortByValue
));
678 s_activeDownloads
= (CStatTreeItemNativeCounter
*)tmpRoot2
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Active Downloads (chunks): %s")));
680 tmpRoot1
->AddChild(new CStatTreeItemRatio(wxTRANSLATE("Session UL:DL Ratio (Total): %s"), s_sessionUpload
, s_sessionDownload
), 3);
682 tmpRoot1
= s_statTree
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Connection")));
683 tmpRoot1
->AddChild(new CStatTreeItemAverageSpeed(wxTRANSLATE("Average Downloadrate (Session): %s"), s_sessionDownload
, s_uptime
));
684 tmpRoot1
->AddChild(new CStatTreeItemAverageSpeed(wxTRANSLATE("Average Uploadrate (Session): %s"), s_sessionUpload
, s_uptime
));
685 s_downloadrate
= (CStatTreeItemRateCounter
*)tmpRoot1
->AddChild(new CStatTreeItemRateCounter(wxTRANSLATE("Max Downloadrate (Session): %s"), true, 30000));
686 s_uploadrate
= (CStatTreeItemRateCounter
*)tmpRoot1
->AddChild(new CStatTreeItemRateCounter(wxTRANSLATE("Max Uploadrate (Session): %s"), true, 30000));
687 s_reconnects
= (CStatTreeItemReconnects
*)tmpRoot1
->AddChild(new CStatTreeItemReconnects(wxTRANSLATE("Reconnects: %i")));
688 s_sinceFirstTransfer
= (CStatTreeItemTimer
*)tmpRoot1
->AddChild(new CStatTreeItemTimer(wxTRANSLATE("Time Since First Transfer: %s"), stHideIfZero
));
689 s_sinceConnected
= (CStatTreeItemTimer
*)tmpRoot1
->AddChild(new CStatTreeItemTimer(wxTRANSLATE("Connected To Server Since: %s")));
690 s_activeConnections
= (CStatTreeItemCounterMax
*)tmpRoot1
->AddChild(new CStatTreeItemCounterMax(wxTRANSLATE("Active Connections (estimate): %i")));
691 s_limitReached
= (CStatTreeItemMaxConnLimitReached
*)tmpRoot1
->AddChild(new CStatTreeItemMaxConnLimitReached(wxTRANSLATE("Max Connection Limit Reached: %s")));
692 s_avgConnections
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Average Connections (estimate): %g")));
693 s_avgConnections
->SetValue(0.0);
694 tmpRoot1
->AddChild(new CStatTreeItemPeakConnections(wxTRANSLATE("Peak Connections (estimate): %i")));
696 s_clients
= (CStatTreeItemHiddenCounter
*)s_statTree
->AddChild(new CStatTreeItemHiddenCounter(wxTRANSLATE("Clients"), stSortChildren
| stSortByValue
));
697 s_unknown
= (CStatTreeItemCounter
*)s_clients
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Unknown") wxT(": %s")), 6);
698 //s_lowID = (CStatTreeItem*)s_clients->AddChild(new CStatTreeItem(wxTRANSLATE("LowID: %u (%.2f%% Total %.2f%% Known)")), 5);
699 //s_secIdentOnOff = (CStatTreeItem*)s_clients->AddChild(new CStatTreeItem(wxTRANSLATE("SecIdent On/Off: %u (%.2f%%) : %u (%.2f%%)")), 4);
701 s_hasSocket
= (CStatTreeItemNativeCounter
*)s_clients
->AddChild(new CStatTreeItemNativeCounter(wxT("HasSocket: %s")), 3);
703 s_filtered
= (CStatTreeItemNativeCounter
*)s_clients
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Filtered") wxT(": %s")), 2);
704 s_banned
= (CStatTreeItemNativeCounter
*)s_clients
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Banned") wxT(": %s")), 1);
705 s_clients
->AddChild(new CStatTreeItemTotalClients(wxTRANSLATE("Total: %i Known: %i"), s_clients
, s_unknown
), 0x80000000);
707 // TODO: Use counters?
708 tmpRoot1
= s_statTree
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Servers")));
709 s_workingServers
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Working Servers: %i")));
710 s_failedServers
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Failed Servers: %i")));
711 s_totalServers
= (CStatTreeItemNativeCounter
*)tmpRoot1
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Total: %s")));
712 s_deletedServers
= (CStatTreeItemNativeCounter
*)tmpRoot1
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Deleted Servers: %s")));
713 s_filteredServers
= (CStatTreeItemNativeCounter
*)tmpRoot1
->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Filtered Servers: %s")));
714 s_usersOnWorking
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Users on Working Servers: %llu")));
715 s_filesOnWorking
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Files on Working Servers: %llu")));
716 s_totalUsers
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Total Users: %llu")));
717 s_totalFiles
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Total Files: %llu")));
718 s_serverOccupation
= (CStatTreeItemSimple
*)tmpRoot1
->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Server Occupation: %.2f%%")));
719 s_serverOccupation
->SetValue(0.0);
721 tmpRoot1
= s_statTree
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Shared Files")));
722 s_numberOfShared
= (CStatTreeItemCounter
*)tmpRoot1
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Number of Shared Files: %s")));
723 s_sizeOfShare
= (CStatTreeItemCounter
*)tmpRoot1
->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Total size of Shared Files: %s")));
724 s_sizeOfShare
->SetDisplayMode(dmBytes
);
725 tmpRoot1
->AddChild(new CStatTreeItemAverage(wxTRANSLATE("Average filesize: %s"), s_sizeOfShare
, s_numberOfShared
, dmBytes
));
729 void CStatistics::UpdateStatsTree()
731 // get sort orders right
732 s_sessionUpload
->ReSortChildren();
733 s_sessionDownload
->ReSortChildren();
734 s_clients
->ReSortChildren();
735 s_foundSources
->ReSortChildren();
736 // TODO: sort OS_Info subtrees.
738 s_avgConnections
->SetValue(theApp
->listensocket
->GetAverageConnections());
741 (*cli13
) = wxString::Format(_("LowID: %u (%.2f%% Total %.2f%% Known)"),#lowid , (#total>0)?((double)100*#lowid/#total):0, (double)100*#knownLowID/#known);
742 (*cli14
) = wxString::Format(_("SecIdent On/Off: %u (%.2f%%) : %u (%.2f%%)"), #secOn , ((#eMule+#aMule)>0)?((double)100*#secOn / (#eMule+#aMule)):0, #secOff, ((#eMule+#aMule)>0)?((double)100*#secOff /(#eMule+#aMule) ):0);
746 // TODO: make these realtime, too
753 theApp
->serverlist
->GetStatus(servfail
, servuser
, servfile
, servtuser
, servtfile
, servocc
);
754 s_workingServers
->SetValue((uint64
)((*s_totalServers
)-servfail
));
755 s_failedServers
->SetValue((uint64
)servfail
);
756 s_usersOnWorking
->SetValue((uint64
)servuser
);
757 s_filesOnWorking
->SetValue((uint64
)servfile
);
758 s_totalUsers
->SetValue((uint64
)servtuser
);
759 s_totalFiles
->SetValue((uint64
)servtfile
);
760 s_serverOccupation
->SetValue(servocc
);
764 void CStatistics::AddSourceOrigin(unsigned origin
)
766 CStatTreeItemNativeCounter
* counter
= (CStatTreeItemNativeCounter
*)s_foundSources
->GetChildById(0x0100 + origin
);
770 counter
= new CStatTreeItemNativeCounter(OriginToText(origin
) + wxT(": %s"), stHideIfZero
| stShowPercent
);
772 s_foundSources
->AddChild(counter
, 0x0100 + origin
);
776 void CStatistics::RemoveSourceOrigin(unsigned origin
)
778 CStatTreeItemNativeCounter
* counter
= (CStatTreeItemNativeCounter
*)s_foundSources
->GetChildById(0x0100 + origin
);
783 uint32
GetSoftID(uint8 SoftType
)
785 // prevent appearing multiple tree entries with the same name
786 // this should be kept in sync with GetSoftName().
789 return 0x0100 + SO_EMULE
;
790 case SO_NEW_SHAREAZA
:
791 case SO_NEW2_SHAREAZA
:
792 return 0x0100 + SO_SHAREAZA
;
793 case SO_NEW2_MLDONKEY
:
794 return 0x0100 + SO_NEW_MLDONKEY
;
796 return 0x0100 + SoftType
;
800 void CStatistics::AddDownloadFromSoft(uint8 SoftType
, uint32 bytes
)
802 AddReceivedBytes(bytes
);
804 uint32 id
= GetSoftID(SoftType
);
806 if (s_sessionDownload
->HasChildWithId(id
)) {
807 (*((CStatTreeItemCounter
*)s_sessionDownload
->GetChildById(id
))) += bytes
;
809 CStatTreeItemCounter
* tmp
= new CStatTreeItemCounter(GetSoftName(SoftType
) + wxT(": %s"));
810 tmp
->SetDisplayMode(dmBytes
);
812 s_sessionDownload
->AddChild(tmp
, id
);
816 void CStatistics::AddUploadToSoft(uint8 SoftType
, uint32 bytes
)
818 uint32 id
= GetSoftID(SoftType
);
820 if (s_sessionUpload
->HasChildWithId(id
)) {
821 (*((CStatTreeItemCounter
*)s_sessionUpload
->GetChildById(id
))) += bytes
;
823 CStatTreeItemCounter
* tmp
= new CStatTreeItemCounter(GetSoftName(SoftType
) + wxT(": %s"));
824 tmp
->SetDisplayMode(dmBytes
);
826 s_sessionUpload
->AddChild(tmp
, id
);
830 inline bool SupportsOSInfo(unsigned clientSoft
)
832 return (clientSoft
== SO_AMULE
) || (clientSoft
== SO_HYDRANODE
) || (clientSoft
== SO_NEW2_MLDONKEY
);
835 // Do some random black magic to strings to get a relatively unique number for them.
836 uint32
GetIdFromString(const wxString
& str
)
839 for (unsigned i
= 0; i
< str
.Length(); ++i
) {
840 unsigned old_id
= id
;
841 id
+= (uint32
)str
.GetChar(i
);
846 return ((id
>> 1) + id
| 0x00000100) & 0x7fffffff;
849 void CStatistics::AddKnownClient(CUpDownClient
*pClient
)
853 uint32 clientSoft
= pClient
->GetClientSoft();
854 uint32 id
= GetSoftID(clientSoft
);
856 CStatTreeItemCounter
*client
;
858 if (s_clients
->HasChildWithId(id
)) {
859 client
= (CStatTreeItemCounter
*)s_clients
->GetChildById(id
);
862 uint32 flags
= stSortChildren
| stShowPercent
| stHideIfZero
;
863 if (!SupportsOSInfo(clientSoft
)) {
864 flags
|= stCapChildren
;
866 client
= new CStatTreeItemCounter(GetSoftName(clientSoft
) + wxT(": %s"), flags
);
868 s_clients
->AddChild(client
, id
);
869 if (SupportsOSInfo(clientSoft
)) {
870 client
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Version"), stSortChildren
| stCapChildren
), 2);
871 client
->AddChild(new CStatTreeItemBase(wxTRANSLATE("Operating System"), stSortChildren
| stSortByValue
), 1);
875 CStatTreeItemBase
*versionRoot
= SupportsOSInfo(clientSoft
) ? client
->GetChildById(2) : client
;
876 uint32 clientVersion
= pClient
->GetVersion();
878 if (versionRoot
->HasChildWithId(clientVersion
)) {
879 CStatTreeItemCounter
*version
= (CStatTreeItemCounter
*)versionRoot
->GetChildById(clientVersion
);
882 const wxString
& versionStr
= pClient
->GetVersionString();
883 CStatTreeItemCounter
*version
= new CStatTreeItemCounter((versionStr
.IsEmpty() ? wxString(wxTRANSLATE("Unknown")) : versionStr
) + wxT(": %s"), stShowPercent
| stHideIfZero
);
885 versionRoot
->AddChild(version
, clientVersion
, SupportsOSInfo(clientSoft
));
888 if (SupportsOSInfo(clientSoft
)) {
889 const wxString
& OSInfo
= pClient
->GetClientOSInfo();
890 uint32 OS_ID
= OSInfo
.IsEmpty() ? 0 : GetIdFromString(OSInfo
);
891 CStatTreeItemBase
* OSRoot
= client
->GetChildById(1);
892 CStatTreeItemCounter
* OSNode
= (CStatTreeItemCounter
*)OSRoot
->GetChildById(OS_ID
);
896 OSNode
= new CStatTreeItemCounter((OS_ID
? OSInfo
: wxString(wxTRANSLATE("Not Received"))) + wxT(": %s"), stShowPercent
| stHideIfZero
);
898 OSRoot
->AddChild(OSNode
, OS_ID
, true);
903 void CStatistics::RemoveKnownClient(uint32 clientSoft
, uint32 clientVersion
, const wxString
& OSInfo
)
907 uint32 id
= GetSoftID(clientSoft
);
909 CStatTreeItemCounter
*client
= (CStatTreeItemCounter
*)s_clients
->GetChildById(id
);
913 CStatTreeItemBase
*versionRoot
= SupportsOSInfo(clientSoft
) ? client
->GetChildById(2) : client
;
915 CStatTreeItemCounter
*version
= (CStatTreeItemCounter
*)versionRoot
->GetChildById(clientVersion
);
919 if (SupportsOSInfo(clientSoft
)) {
920 uint32 OS_ID
= OSInfo
.IsEmpty() ? 0 : GetIdFromString(OSInfo
);
921 CStatTreeItemCounter
* OSNode
= (CStatTreeItemCounter
*)client
->GetChildById(1)->GetChildById(OS_ID
);
927 #else /* EC_REMOTE (CLIENT_GUI) */
929 CStatistics::CStatistics(CRemoteConnect
&conn
)
933 s_start_time
= GetTickCount64();
936 s_statTree
= new CStatTreeItemBase(_("Statistics"), 0);
938 // Clear stat data container
939 for (int i
= 0; i
< sdTotalItems
; ++i
) {
945 CStatistics::~CStatistics()
951 void CStatistics::UpdateStats(const CECPacket
* stats
)
953 s_statData
[sdUpload
] = stats
->GetTagByNameSafe(EC_TAG_STATS_UL_SPEED
)->GetInt();
954 s_statData
[sdUpOverhead
] = stats
->GetTagByNameSafe(EC_TAG_STATS_UP_OVERHEAD
)->GetInt();
955 s_statData
[sdDownload
] = stats
->GetTagByNameSafe(EC_TAG_STATS_DL_SPEED
)->GetInt();
956 s_statData
[sdDownOverhead
] = stats
->GetTagByNameSafe(EC_TAG_STATS_DOWN_OVERHEAD
)->GetInt();
957 s_statData
[sdWaitingClients
] = stats
->GetTagByNameSafe(EC_TAG_STATS_UL_QUEUE_LEN
)->GetInt();
958 s_statData
[sdBannedClients
] = stats
->GetTagByNameSafe(EC_TAG_STATS_BANNED_COUNT
)->GetInt();
959 s_statData
[sdED2KUsers
] = stats
->GetTagByNameSafe(EC_TAG_STATS_ED2K_USERS
)->GetInt();
960 s_statData
[sdKadUsers
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_USERS
)->GetInt();
961 s_statData
[sdED2KFiles
] = stats
->GetTagByNameSafe(EC_TAG_STATS_ED2K_FILES
)->GetInt();
962 s_statData
[sdKadFiles
] = stats
->GetTagByNameSafe(EC_TAG_STATS_KAD_FILES
)->GetInt();
966 void CStatistics::UpdateStatsTree()
968 CECPacket
request(EC_OP_GET_STATSTREE
);
969 if (thePrefs::GetMaxClientVersions() != 0) {
970 request
.AddTag(CECTag(EC_TAG_STATTREE_CAPPING
, (uint8
)thePrefs::GetMaxClientVersions()));
972 const CECPacket
* reply
= m_conn
.SendRecvPacket(&request
);
974 const CECTag
* treeRoot
= reply
->GetTagByName(EC_TAG_STATTREE_NODE
);
977 s_statTree
= new CStatTreeItemBase(treeRoot
);
983 #endif /* !EC_REMOTE */
985 // File_checked_for_headers