Upstream tarball 9408
[amule.git] / src / Statistics.cpp
blob0603b927ea6911993645ae03142ec31befdfe2b9
1 //
2 // This file is part of the aMule Project.
3 //
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 )
7 //
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.
21 //
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
33 #ifndef EC_REMOTE
34 #ifndef AMULE_DAEMON
35 #include <common/Format.h> // Needed for CFormat
36 #endif
37 #include "DataToText.h" // Needed for GetSoftName()
38 #include "Preferences.h" // Needed for thePrefs
39 #include "ListenSocket.h" // (tree, GetAverageConnections)
40 #include "ServerList.h" // Needed for CServerList (tree)
41 #include <cmath> // Needed for std::floor
42 #include "updownclient.h" // Needed for CUpDownClient
43 #else
44 #include "Preferences.h"
45 #include <ec/cpp/RemoteConnect.h> // Needed for CRemoteConnect
46 #endif
47 #include "amule.h" // Needed for theApp
49 #include <wx/intl.h>
51 #ifdef __BSD__
52 // glibc -> bsd libc
53 #define round rint
54 #else
55 #define round(x) floor(x+0.5)
56 #endif /* __BSD__ */
59 #ifndef EC_REMOTE
61 /*----- CPreciseRateCounter -----*/
63 void CPreciseRateCounter::CalculateRate(uint64_t now)
65 wxMutexLocker lock(m_mutex);
67 m_total += m_tmp_sum;
68 m_byte_history.push_back(m_tmp_sum);
69 m_tick_history.push_back(now);
70 m_tmp_sum = 0;
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();
83 // Count rate/average
84 if (m_count_average) {
85 if (m_byte_history.size() > 0) {
86 m_rate = m_total / (double)m_byte_history.size();
88 } else {
89 if (timespan > 0) {
90 m_rate = m_total / (timespan / 1000.0);
94 if (m_rate > m_max_rate) {
95 m_max_rate = m_rate;
100 /*----- CStatTreeItemRateCounter -----*/
102 #ifndef AMULE_DAEMON
103 wxString CStatTreeItemRateCounter::GetDisplayString() const
105 return CFormat(wxGetTranslation(m_label)) % CastItoSpeed(m_show_maxrate ? (uint32)m_max_rate : (uint32)m_rate);
107 #endif
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));
113 tag->AddTag(value);
117 /*----- CStatTreeItemPeakConnections -----*/
119 #ifndef AMULE_DAEMON
120 wxString CStatTreeItemPeakConnections::GetDisplayString() const
122 return wxString::Format(wxGetTranslation(m_label), theStats::GetPeakConnections());
124 #endif
126 void CStatTreeItemPeakConnections::AddECValues(CECTag* tag) const
128 tag->AddTag(CECTag(EC_TAG_STAT_NODE_VALUE, (uint64)theStats::GetPeakConnections()));
132 /*----- CStatistics -----*/
134 // Static variables
136 // Rate counters
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 */
149 // Tree root
150 CStatTreeItemBase* CStatistics::s_statTree;
152 #ifndef EC_REMOTE
153 // Uptime
154 CStatTreeItemTimer* CStatistics::s_uptime;
156 // Upload
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 CStatTreeItemCounter* CStatistics::s_cryptUpOverhead;
164 CStatTreeItemNativeCounter* CStatistics::s_activeUploads;
165 CStatTreeItemNativeCounter* CStatistics::s_waitingUploads;
166 CStatTreeItemCounter* CStatistics::s_totalSuccUploads;
167 CStatTreeItemCounter* CStatistics::s_totalFailedUploads;
168 CStatTreeItemCounter* CStatistics::s_totalUploadTime;
170 // Download
171 CStatTreeItemUlDlCounter* CStatistics::s_sessionDownload;
172 CStatTreeItemPacketTotals* CStatistics::s_totalDownOverhead;
173 CStatTreeItemPackets* CStatistics::s_fileReqDownOverhead;
174 CStatTreeItemPackets* CStatistics::s_sourceXchgDownOverhead;
175 CStatTreeItemPackets* CStatistics::s_serverDownOverhead;
176 CStatTreeItemPackets* CStatistics::s_kadDownOverhead;
177 CStatTreeItemCounter* CStatistics::s_cryptDownOverhead;
178 CStatTreeItemNativeCounter* CStatistics::s_foundSources;
179 CStatTreeItemNativeCounter* CStatistics::s_activeDownloads;
181 // Connection
182 CStatTreeItemReconnects* CStatistics::s_reconnects;
183 CStatTreeItemTimer* CStatistics::s_sinceFirstTransfer;
184 CStatTreeItemTimer* CStatistics::s_sinceConnected;
185 CStatTreeItemCounterMax* CStatistics::s_activeConnections;
186 CStatTreeItemMaxConnLimitReached* CStatistics::s_limitReached;
187 CStatTreeItemSimple* CStatistics::s_avgConnections;
189 // Clients
190 CStatTreeItemHiddenCounter* CStatistics::s_clients;
191 CStatTreeItemCounter* CStatistics::s_unknown;
192 //CStatTreeItem CStatistics::s_lowID;
193 //CStatTreeItem CStatistics::s_secIdentOnOff;
194 #ifdef __DEBUG__
195 CStatTreeItemNativeCounter* CStatistics::s_hasSocket;
196 #endif
197 CStatTreeItemNativeCounter* CStatistics::s_filtered;
198 CStatTreeItemNativeCounter* CStatistics::s_banned;
200 // Servers
201 CStatTreeItemSimple* CStatistics::s_workingServers;
202 CStatTreeItemSimple* CStatistics::s_failedServers;
203 CStatTreeItemNativeCounter* CStatistics::s_totalServers;
204 CStatTreeItemNativeCounter* CStatistics::s_deletedServers;
205 CStatTreeItemNativeCounter* CStatistics::s_filteredServers;
206 CStatTreeItemSimple* CStatistics::s_usersOnWorking;
207 CStatTreeItemSimple* CStatistics::s_filesOnWorking;
208 CStatTreeItemSimple* CStatistics::s_totalUsers;
209 CStatTreeItemSimple* CStatistics::s_totalFiles;
210 CStatTreeItemSimple* CStatistics::s_serverOccupation;
212 // Shared files
213 CStatTreeItemCounter* CStatistics::s_numberOfShared;
214 CStatTreeItemCounter* CStatistics::s_sizeOfShare;
216 // Kad
217 uint64 CStatistics::s_kadNodesTotal;
218 uint16 CStatistics::s_kadNodesCur;
221 CStatistics::CStatistics()
222 : m_graphRunningAvgDown(thePrefs::GetStatsAverageMinutes() * 60 * 1000, true),
223 m_graphRunningAvgUp(thePrefs::GetStatsAverageMinutes() * 60 * 1000, true),
224 m_graphRunningAvgKad(thePrefs::GetStatsAverageMinutes() * 60 * 1000, true)
226 uint64 start_time = GetTickCount64();
228 // Init graphs
230 average_minutes = thePrefs::GetStatsAverageMinutes();
232 HR hr = {0.0, 0.0, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0};
233 hrInit = hr;
234 nHistRanges = 7; // =ceil(log(max_update_delay)/log(2))
235 nPointsPerRange = GetPointsPerRange();
236 bitsHistClockMask = (1 << (nHistRanges-1)) - 1;
237 aposRecycle = new listPOS[nHistRanges];
238 listPOS *ppos = aposRecycle+nHistRanges-1;
239 for (int i=nHistRanges; i>0; --i) { // permanently allocated history list
240 listHR.push_back(hr);
241 *ppos-- = --listHR.end();
242 for (int j=nPointsPerRange; j>1; --j)
243 listHR.push_back(hr);
246 // Init rate counters outside the tree
248 s_upOverheadRate = new CPreciseRateCounter(5000);
249 s_downOverheadRate = new CPreciseRateCounter(5000);
251 // Init Tree
253 InitStatsTree();
254 s_uptime->SetStartTime(start_time);
258 CStatistics::~CStatistics()
260 // clearing listHR frees the memory occupied by the nodes
261 listHR.clear();
262 delete [] aposRecycle;
264 delete s_statTree;
266 // delete items not in the tree
267 delete s_totalUploadTime;
269 // delete rate counters outside the tree
270 delete s_upOverheadRate;
271 delete s_downOverheadRate;
275 void CStatistics::CalculateRates()
277 uint64_t now = GetTickCount64();
278 s_downOverheadRate->CalculateRate(now);
279 s_upOverheadRate->CalculateRate(now);
280 s_downloadrate->CalculateRate(now);
281 s_uploadrate->CalculateRate(now);
285 /* ------------------------------- GRAPHS ---------------------------- */
288 History List
290 The basic idea here is that we want to keep as much history as we can without paying
291 a high price in terms of memory space. Because we keep the history for display purposes,
292 we can take advantage of the fact that when the period shown in the graphs is long
293 then each pixel represents a long period. So as the most recent history we keep one
294 window full of points at a resolution of 1 second, the next window full at 2 seconds,
295 the next at 4 seconds and so on, up to the maximum desired. This way there is always
296 at least one sample point per pixel for any update delay set by the user, and the
297 memory required grows with the *log* of the total time period covered.
298 The history is kept in a doubly-linked list, with the most recent snapshot at the tail.
299 The number of nodes in the list is fixed, and there are no calls to RemoveHead() and
300 AddTail() which would add overhead and contribute to memory fragmentation. Instead,
301 every second when a new point gets recorded, one of the existing nodes is recycled;
302 it is disjoined from its present place, put at the tail of the list, and then gets
303 filled with new data. [Emilio Sandoz]
304 This unfortunately does not work with stl classes, as none of them supports moving
305 a node to another place, so we have to erase and re-add nodes.
308 void CStatistics::RecordHistory()
309 { // First we query and compute some values, then we store them in the history list
311 // A few comments about the use of double and float in computations:
312 // Even on a hi-res screen our graphs will have 10 bits of resolution at most,
313 // so the 24 bits resolution of a float on 32 bit Intel processors is more than
314 // enough for all displayed values. Rate computations however, and especially
315 // running average computations, use differences (delta bytes/ delta time), and
316 // for long uptimes the difference between two timestamps can lose too much
317 // accuracy because the large mantissa causes less significant bits to be dropped
318 // (same for the difference between two cumulative byte counts). [We don't store
319 // these values as integers because they will be used in floating point calculations,
320 // and we want to perform the conversion only once). Therefore timestamps and
321 // Kbyte counts are stored in the history as doubles, while computed values use
322 // float (to save space and execution time).
325 Store values; first determine the node to be recycled (using the bits in iClock)
327 oldest records ----------------- listHR ------------------ youngest records
329 O-[Range 2^n sec]-O- ... -O-[Range 4 sec]-O-[Range 2 sec]-O-[Range 1 sec]-O
330 | | | | > every 2 secs -^
331 | | ... | >--------------- every 4 secs -^
332 | | >------------------------ recycle every 8 secs -^
333 | | ...
334 | >-the node at this position is recycled every 2^n secs -^
335 >-------------------(ditto for the oldest node at the head of the list) --^
337 aposRecycle[nHistRanges-1] ... aposRecycle[0] Tail
339 listPOS *ppos;
340 static int iClock;
341 int iClockPrev = iClock++;
342 int bits = (iClockPrev^iClock) & iClock; // identify the highest changed bit
343 if (bits <= bitsHistClockMask) {
344 ppos = aposRecycle;
345 while ((bits /= 2) != 0) // count to the highest bit that was just toggled to 1
346 ++ppos;
347 // recycle one node and jump over the next to move it to the next higher range
348 listHR.push_back(**ppos);
349 *ppos = ++listHR.erase(*ppos);
350 } else {
351 ppos = aposRecycle+nHistRanges-1;
352 // recycle the node at the head; there is no higher range to move nodes into
353 listHR.push_back(**ppos);
354 *ppos = listHR.erase(*ppos);
357 // now save the latest data point in this node
358 listPOS phr = --listHR.end();
359 phr->kBytesSent = GetSessionSentBytes() / 1024.0;
360 phr->kBytesReceived = GetSessionReceivedBytes() / 1024.0;
361 phr->kBpsUpCur = GetUploadRate() / 1024.0;
362 phr->kBpsDownCur = GetDownloadRate() / 1024.0;
363 phr->cntUploads = GetActiveUploadsCount();
364 phr->cntConnections = GetActiveConnections();
365 phr->cntDownloads = GetDownloadingSources();
366 phr->sTimestamp = GetUptimeMillis() / 1000.0;
368 s_kadNodesTotal += s_kadNodesCur;
369 phr->kadNodesTotal = s_kadNodesTotal;
370 phr->kadNodesCur = s_kadNodesCur;
374 unsigned CStatistics::GetHistory( // Assemble arrays of sample points for a graph
375 unsigned cntPoints, // number of sample points to assemble
376 double sStep, // time difference between sample points
377 double sFinal, // latest allowed timestamp
378 const std::vector<float *> &ppf,// an array of pointers to arrays of floats for the result
379 StatsGraphType which_graph) // the graph which will receive the points
381 if (sStep==0.0 || cntPoints==0) {
382 return(0);
385 float *pf1 = ppf[0];
386 float *pf2 = ppf[1];
387 float *pf3 = ppf[2];
388 unsigned cntFilled = 0;
389 listRPOS pos = listHR.rbegin();
391 // start of list should be an integer multiple of the sampling period for samples
392 // to be consistent when the graphs are resized horizontally
393 double sTarget;
394 if (sFinal >= 0.0) {
395 sTarget = sFinal;
396 } else {
397 sTarget = sStep==1.0 ?
398 pos->sTimestamp :
399 std::floor(pos->sTimestamp/sStep) * sStep;
402 HR **ahr = NULL, **pphr = NULL;
403 bool bRateGraph = (which_graph != GRAPH_CONN); // rate graph or connections graph?
404 if (bRateGraph) {
405 ahr = new HR* [cntPoints];
406 pphr = ahr;
409 while (pos != listHR.rend()) {
410 if (pos->sTimestamp > sTarget) {
411 ++pos;
412 continue;
414 if (bRateGraph) { // assemble an array of pointers for ComputeAverages
415 *pphr++ = &(*pos);
416 } else { // or build the arrays if possible
417 *pf1++ = (float)pos->cntUploads;
418 *pf2++ = (float)pos->cntConnections;
419 *pf3++ = (float)pos->cntDownloads;
421 if (++cntFilled == cntPoints) { // enough points
422 break;
424 if (pos->sTimestamp == 0.0) { // reached beginning of uptime
425 break;
427 if ((sTarget -= sStep) <= 0.0) { // don't overshoot the beginning
428 if (bRateGraph) {
429 *pphr++ = &hrInit;
430 } else {
431 *pf1++ = *pf2++ = *pf3++ = 0.0;
433 ++cntFilled;
434 break;
438 if (bRateGraph) {
439 if (cntFilled > 0) {
440 ComputeAverages(pphr, pos, cntFilled, sStep, ppf, which_graph);
442 delete[] ahr;
445 return cntFilled;
449 unsigned CStatistics::GetHistoryForWeb( // Assemble arrays of sample points for the webserver
450 unsigned cntPoints, // maximum number of sample points to assemble
451 double sStep, // time difference between sample points
452 double *sStart, // earliest allowed timestamp
453 uint32 **graphData) // a pointer to a pointer that will point to the graph data array
455 if (*sStart < 0.0) {
456 *sStart = 0.0;
458 if (sStep==0.0 || cntPoints==0)
459 return(0);
460 unsigned cntFilled = 0;
461 listRPOS pos = listHR.rbegin();
462 double LastTimeStamp = pos->sTimestamp;
463 double sTarget = LastTimeStamp;
465 HR **pphr = new HR *[cntPoints];
467 while (pos != listHR.rend()) {
468 if (pos->sTimestamp > sTarget) {
469 ++pos; // find next history record
470 continue;
472 pphr[cntFilled] = &(*pos);
473 if (++cntFilled == cntPoints) // enough points
474 break;
475 if (pos->sTimestamp <= *sStart) // reached beginning of requested time
476 break;
477 if ((sTarget -= sStep) <= 0.0) { // don't overshoot the beginning
478 pphr[cntFilled++] = NULL;
479 break;
483 if (cntFilled) {
484 *graphData = new uint32 [4 * cntFilled];
485 if (*graphData) {
486 for (unsigned int i = 0; i < cntFilled; i++) {
487 HR *phr = pphr[cntFilled - i - 1];
488 if (phr) {
489 (*graphData)[4 * i ] = ENDIAN_HTONL((uint32)(phr->kBpsDownCur * 1024.0));
490 (*graphData)[4 * i + 1] = ENDIAN_HTONL((uint32)(phr->kBpsUpCur * 1024.0));
491 (*graphData)[4 * i + 2] = ENDIAN_HTONL((uint32)phr->cntConnections);
492 (*graphData)[4 * i + 3] = ENDIAN_HTONL((uint32)phr->kadNodesCur);
493 } else {
494 (*graphData)[4 * i] = (*graphData)[4 * i + 1] = 0;
495 (*graphData)[4 * i + 2] = (*graphData)[4 * i + 3] = 0;
499 } else {
500 *graphData = NULL;
503 delete [] pphr;
505 *sStart = LastTimeStamp;
507 return cntFilled;
511 void CStatistics::ComputeAverages(
512 HR **pphr, // pointer to (end of) array of assembled history records
513 listRPOS pos, // position in history list from which to backtrack
514 unsigned cntFilled, // number of points in the sample data
515 double sStep, // time difference between two samples
516 const std::vector<float *> &ppf,// an array of pointers to arrays of floats with sample data
517 StatsGraphType which_graph) // the graph which will receive the points
519 double sTarget, kValueRun;
520 uint64 avgTime = average_minutes * 60;
521 unsigned nBtPoints = (unsigned)(avgTime / sStep);
523 CPreciseRateCounter* runningAvg = NULL;
524 switch (which_graph) {
525 case GRAPH_DOWN: runningAvg = &m_graphRunningAvgDown; break;
526 case GRAPH_UP: runningAvg = &m_graphRunningAvgUp; break;
527 case GRAPH_KAD: runningAvg = &m_graphRunningAvgKad; break;
528 default:
529 wxCHECK_RET(false, wxT("ComputeAverages called with unsupported graph type."));
532 runningAvg->m_timespan = avgTime * 1000;
533 runningAvg->m_tick_history.clear();
534 runningAvg->m_byte_history.clear();
535 runningAvg->m_total = 0;
536 runningAvg->m_tmp_sum = 0;
538 if (pos == listHR.rend()) {
539 sTarget = 0.0;
540 } else {
541 sTarget = std::max(0.0, pos->sTimestamp - sStep);
544 while (nBtPoints--) {
545 while (pos != listHR.rend() && pos->sTimestamp > sTarget) ++pos; // find next history record
546 if (pos != listHR.rend()) {
547 runningAvg->m_tick_history.push_front((uint64)(pos->sTimestamp * 1000.0));
549 uint32 value = 0;
550 switch (which_graph) {
551 case GRAPH_DOWN:
552 value = (uint32)(pos->kBpsDownCur * 1024.0);
553 break;
554 case GRAPH_UP:
555 value = (uint32)(pos->kBpsUpCur * 1024.0);
556 break;
557 case GRAPH_KAD:
558 value = (uint32)(pos->kadNodesCur * 1024.0);
559 break;
560 default:
561 wxCHECK_RET(false, wxT("ComputeAverages called with unsupported graph type."));
564 runningAvg->m_byte_history.push_front(value);
565 runningAvg->m_total += value;
566 } else {
567 break;
569 if ((sTarget -= sStep) < 0.0) {
570 break;
574 // now compute averages in returned arrays, starting with the earliest values
575 float *pf1 = ppf[0] + cntFilled - 1; // holds session avg
576 float *pf2 = ppf[1] + cntFilled - 1; // holds running avg
577 float *pf3 = ppf[2] + cntFilled - 1; // holds current rate
579 for (int cnt=cntFilled; cnt>0; cnt--, pf1--, pf2--, pf3--) {
580 HR *phr = *(--pphr);
581 if (which_graph == GRAPH_DOWN) {
582 kValueRun = phr->kBytesReceived;
583 *pf3 = phr->kBpsDownCur;
584 } else if (which_graph == GRAPH_UP) {
585 kValueRun = phr->kBytesSent;
586 *pf3 = phr->kBpsUpCur;
587 } else {
588 kValueRun = phr->kadNodesTotal;
589 *pf3 = phr->kadNodesCur;
592 *pf1 = kValueRun / phr->sTimestamp;
593 (*runningAvg) += (uint32)(*pf3 * 1024.0);
594 runningAvg->CalculateRate((uint64)(phr->sTimestamp * 1000.0));
595 *pf2 = (float)(runningAvg->GetRate() / 1024.0);
600 GraphUpdateInfo CStatistics::GetPointsForUpdate()
602 GraphUpdateInfo update;
603 listPOS phr = --listHR.end();
604 update.timestamp = (double) phr->sTimestamp;
606 m_graphRunningAvgDown += (uint32)(phr->kBpsDownCur * 1024.0);
607 m_graphRunningAvgUp += (uint32)(phr->kBpsUpCur * 1024.0);
608 // Note: kadNodesCur is multiplied by 1024 since the value is done
609 // in other places, so we simply follow suit here to avoid trouble.
610 m_graphRunningAvgKad += (uint32)(phr->kadNodesCur * 1024.0);
611 m_graphRunningAvgDown.CalculateRate((uint64)(phr->sTimestamp * 1000.0));
612 m_graphRunningAvgUp.CalculateRate((uint64)(phr->sTimestamp * 1000.0));
613 m_graphRunningAvgKad.CalculateRate((uint64)(phr->sTimestamp * 1000.0));
615 update.downloads[0] = phr->kBytesReceived / phr->sTimestamp;
616 update.downloads[1] = m_graphRunningAvgDown.GetRate() / 1024.0;
617 update.downloads[2] = phr->kBpsDownCur;
619 update.uploads[0] = phr->kBytesSent / phr->sTimestamp;
620 update.uploads[1] = m_graphRunningAvgUp.GetRate() / 1024.0;
621 update.uploads[2] = phr->kBpsUpCur;
623 update.connections[0] = (float)phr->cntUploads;
624 update.connections[1] = (float)phr->cntConnections;
625 update.connections[2] = (float)phr->cntDownloads;
627 update.kadnodes[0] = phr->kadNodesTotal / phr->sTimestamp;
628 update.kadnodes[1] = m_graphRunningAvgKad.GetRate() / 1024.0;
629 update.kadnodes[2] = phr->kadNodesCur;
631 return update;
635 /* ------------------------------- TREE ---------------------------- */
637 void CStatistics::InitStatsTree()
639 s_statTree = new CStatTreeItemBase(wxTRANSLATE("Statistics"));
641 CStatTreeItemBase* tmpRoot1;
642 CStatTreeItemBase* tmpRoot2;
644 s_uptime = (CStatTreeItemTimer*)s_statTree->AddChild(new CStatTreeItemTimer(wxTRANSLATE("Uptime: %s")));
646 tmpRoot1 = s_statTree->AddChild(new CStatTreeItemBase(wxTRANSLATE("Transfer"), stSortChildren));
648 tmpRoot2 = tmpRoot1->AddChild(new CStatTreeItemBase(wxTRANSLATE("Uploads")), 2);
649 s_sessionUpload = (CStatTreeItemUlDlCounter*)tmpRoot2->AddChild(new CStatTreeItemUlDlCounter(wxTRANSLATE("Uploaded Data (Session (Total)): %s"), thePrefs::GetTotalUploaded, stSortChildren | stSortByValue));
650 // Children will be added on-the-fly
651 s_totalUpOverhead = (CStatTreeItemPacketTotals*)tmpRoot2->AddChild(new CStatTreeItemPacketTotals(wxTRANSLATE("Total Overhead (Packets): %s")));
652 s_fileReqUpOverhead = (CStatTreeItemPackets*)tmpRoot2->AddChild(new CStatTreeItemPackets(wxTRANSLATE("File Request Overhead (Packets): %s")));
653 s_totalUpOverhead->AddPacketCounter(s_fileReqUpOverhead);
654 s_sourceXchgUpOverhead = (CStatTreeItemPackets*)tmpRoot2->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Source Exchange Overhead (Packets): %s")));
655 s_totalUpOverhead->AddPacketCounter(s_sourceXchgUpOverhead);
656 s_serverUpOverhead = (CStatTreeItemPackets*)tmpRoot2->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Server Overhead (Packets): %s")));
657 s_totalUpOverhead->AddPacketCounter(s_serverUpOverhead);
658 s_kadUpOverhead = (CStatTreeItemPackets*)tmpRoot2->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Kad Overhead (Packets): %s")));
659 s_totalUpOverhead->AddPacketCounter(s_kadUpOverhead);
660 s_cryptUpOverhead = (CStatTreeItemCounter*)tmpRoot2->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Crypt overhead (UDP): %s")));
661 s_cryptUpOverhead->SetDisplayMode(dmBytes);
662 s_activeUploads = (CStatTreeItemNativeCounter*)tmpRoot2->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Active Uploads: %s")));
663 s_waitingUploads = (CStatTreeItemNativeCounter*)tmpRoot2->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Waiting Uploads: %s")));
664 s_totalSuccUploads = (CStatTreeItemCounter*)tmpRoot2->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Total successful upload sessions: %s")));
665 s_totalFailedUploads = (CStatTreeItemCounter*)tmpRoot2->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Total failed upload sessions: %s")));
666 s_totalUploadTime = new CStatTreeItemCounter(wxEmptyString);
667 tmpRoot2->AddChild(new CStatTreeItemAverage(wxTRANSLATE("Average upload time: %s"), s_totalUploadTime, s_totalSuccUploads, dmTime));
669 tmpRoot2 = tmpRoot1->AddChild(new CStatTreeItemBase(wxTRANSLATE("Downloads")), 1);
670 s_sessionDownload = (CStatTreeItemUlDlCounter*)tmpRoot2->AddChild(new CStatTreeItemUlDlCounter(wxTRANSLATE("Downloaded Data (Session (Total)): %s"), thePrefs::GetTotalDownloaded, stSortChildren | stSortByValue));
671 // Children will be added on-the-fly
672 s_totalDownOverhead = (CStatTreeItemPacketTotals*)tmpRoot2->AddChild(new CStatTreeItemPacketTotals(wxTRANSLATE("Total Overhead (Packets): %s")));
673 s_fileReqDownOverhead = (CStatTreeItemPackets*)tmpRoot2->AddChild(new CStatTreeItemPackets(wxTRANSLATE("File Request Overhead (Packets): %s")));
674 s_totalDownOverhead->AddPacketCounter(s_fileReqDownOverhead);
675 s_sourceXchgDownOverhead = (CStatTreeItemPackets*)tmpRoot2->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Source Exchange Overhead (Packets): %s")));
676 s_totalDownOverhead->AddPacketCounter(s_sourceXchgDownOverhead);
677 s_serverDownOverhead = (CStatTreeItemPackets*)tmpRoot2->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Server Overhead (Packets): %s")));
678 s_totalDownOverhead->AddPacketCounter(s_serverDownOverhead);
679 s_kadDownOverhead = (CStatTreeItemPackets*)tmpRoot2->AddChild(new CStatTreeItemPackets(wxTRANSLATE("Kad Overhead (Packets): %s")));
680 s_totalDownOverhead->AddPacketCounter(s_kadDownOverhead);
681 s_cryptDownOverhead = (CStatTreeItemCounter*)tmpRoot2->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Crypt overhead (UDP): %s")));
682 s_cryptDownOverhead->SetDisplayMode(dmBytes);
683 s_foundSources = (CStatTreeItemNativeCounter*)tmpRoot2->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Found Sources: %s"), stSortChildren | stSortByValue));
684 s_activeDownloads = (CStatTreeItemNativeCounter*)tmpRoot2->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Active Downloads (chunks): %s")));
686 tmpRoot1->AddChild(new CStatTreeItemRatio(wxTRANSLATE("Session UL:DL Ratio (Total): %s"), s_sessionUpload, s_sessionDownload), 3);
688 tmpRoot1 = s_statTree->AddChild(new CStatTreeItemBase(wxTRANSLATE("Connection")));
689 tmpRoot1->AddChild(new CStatTreeItemAverageSpeed(wxTRANSLATE("Average download rate (Session): %s"), s_sessionDownload, s_uptime));
690 tmpRoot1->AddChild(new CStatTreeItemAverageSpeed(wxTRANSLATE("Average upload rate (Session): %s"), s_sessionUpload, s_uptime));
691 s_downloadrate = (CStatTreeItemRateCounter*)tmpRoot1->AddChild(new CStatTreeItemRateCounter(wxTRANSLATE("Max download rate (Session): %s"), true, 30000));
692 s_uploadrate = (CStatTreeItemRateCounter*)tmpRoot1->AddChild(new CStatTreeItemRateCounter(wxTRANSLATE("Max upload rate (Session): %s"), true, 30000));
693 s_reconnects = (CStatTreeItemReconnects*)tmpRoot1->AddChild(new CStatTreeItemReconnects(wxTRANSLATE("Reconnects: %i")));
694 s_sinceFirstTransfer = (CStatTreeItemTimer*)tmpRoot1->AddChild(new CStatTreeItemTimer(wxTRANSLATE("Time Since First Transfer: %s"), stHideIfZero));
695 s_sinceConnected = (CStatTreeItemTimer*)tmpRoot1->AddChild(new CStatTreeItemTimer(wxTRANSLATE("Connected To Server Since: %s")));
696 s_activeConnections = (CStatTreeItemCounterMax*)tmpRoot1->AddChild(new CStatTreeItemCounterMax(wxTRANSLATE("Active Connections (estimate): %i")));
697 s_limitReached = (CStatTreeItemMaxConnLimitReached*)tmpRoot1->AddChild(new CStatTreeItemMaxConnLimitReached(wxTRANSLATE("Max Connection Limit Reached: %s")));
698 s_avgConnections = (CStatTreeItemSimple*)tmpRoot1->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Average Connections (estimate): %g")));
699 s_avgConnections->SetValue(0.0);
700 tmpRoot1->AddChild(new CStatTreeItemPeakConnections(wxTRANSLATE("Peak Connections (estimate): %i")));
702 s_clients = (CStatTreeItemHiddenCounter*)s_statTree->AddChild(new CStatTreeItemHiddenCounter(wxTRANSLATE("Clients"), stSortChildren | stSortByValue));
703 s_unknown = (CStatTreeItemCounter*)s_clients->AddChild(new CStatTreeItemCounter(wxString(wxTRANSLATE("Unknown")) + wxT(": %s")), 6);
704 //s_lowID = (CStatTreeItem*)s_clients->AddChild(new CStatTreeItem(wxTRANSLATE("LowID: %u (%.2f%% Total %.2f%% Known)")), 5);
705 //s_secIdentOnOff = (CStatTreeItem*)s_clients->AddChild(new CStatTreeItem(wxTRANSLATE("SecIdent On/Off: %u (%.2f%%) : %u (%.2f%%)")), 4);
706 #ifdef __DEBUG__
707 s_hasSocket = (CStatTreeItemNativeCounter*)s_clients->AddChild(new CStatTreeItemNativeCounter(wxT("HasSocket: %s")), 3);
708 #endif
709 s_filtered = (CStatTreeItemNativeCounter*)s_clients->AddChild(new CStatTreeItemNativeCounter(wxString(wxTRANSLATE("Filtered")) + wxT(": %s")), 2);
710 s_banned = (CStatTreeItemNativeCounter*)s_clients->AddChild(new CStatTreeItemNativeCounter(wxString(wxTRANSLATE("Banned")) + wxT(": %s")), 1);
711 s_clients->AddChild(new CStatTreeItemTotalClients(wxTRANSLATE("Total: %i Known: %i"), s_clients, s_unknown), 0x80000000);
713 // TODO: Use counters?
714 tmpRoot1 = s_statTree->AddChild(new CStatTreeItemBase(wxTRANSLATE("Servers")));
715 s_workingServers = (CStatTreeItemSimple*)tmpRoot1->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Working Servers: %i")));
716 s_failedServers = (CStatTreeItemSimple*)tmpRoot1->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Failed Servers: %i")));
717 s_totalServers = (CStatTreeItemNativeCounter*)tmpRoot1->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Total: %s")));
718 s_deletedServers = (CStatTreeItemNativeCounter*)tmpRoot1->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Deleted Servers: %s")));
719 s_filteredServers = (CStatTreeItemNativeCounter*)tmpRoot1->AddChild(new CStatTreeItemNativeCounter(wxTRANSLATE("Filtered Servers: %s")));
720 s_usersOnWorking = (CStatTreeItemSimple*)tmpRoot1->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Users on Working Servers: %llu")));
721 s_filesOnWorking = (CStatTreeItemSimple*)tmpRoot1->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Files on Working Servers: %llu")));
722 s_totalUsers = (CStatTreeItemSimple*)tmpRoot1->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Total Users: %llu")));
723 s_totalFiles = (CStatTreeItemSimple*)tmpRoot1->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Total Files: %llu")));
724 s_serverOccupation = (CStatTreeItemSimple*)tmpRoot1->AddChild(new CStatTreeItemSimple(wxTRANSLATE("Server Occupation: %.2f%%")));
725 s_serverOccupation->SetValue(0.0);
727 tmpRoot1 = s_statTree->AddChild(new CStatTreeItemBase(wxTRANSLATE("Shared Files")));
728 s_numberOfShared = (CStatTreeItemCounter*)tmpRoot1->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Number of Shared Files: %s")));
729 s_sizeOfShare = (CStatTreeItemCounter*)tmpRoot1->AddChild(new CStatTreeItemCounter(wxTRANSLATE("Total size of Shared Files: %s")));
730 s_sizeOfShare->SetDisplayMode(dmBytes);
731 tmpRoot1->AddChild(new CStatTreeItemAverage(wxTRANSLATE("Average file size: %s"), s_sizeOfShare, s_numberOfShared, dmBytes));
735 void CStatistics::UpdateStatsTree()
737 // get sort orders right
738 s_sessionUpload->ReSortChildren();
739 s_sessionDownload->ReSortChildren();
740 s_clients->ReSortChildren();
741 s_foundSources->ReSortChildren();
742 // TODO: sort OS_Info subtrees.
744 s_avgConnections->SetValue(theApp->listensocket->GetAverageConnections());
746 // get serverstats
747 // TODO: make these realtime, too
748 uint32 servfail;
749 uint32 servuser;
750 uint32 servfile;
751 uint32 servtuser;
752 uint32 servtfile;
753 float servocc;
754 theApp->serverlist->GetStatus(servfail, servuser, servfile, servtuser, servtfile, servocc);
755 s_workingServers->SetValue((uint64)((*s_totalServers)-servfail));
756 s_failedServers->SetValue((uint64)servfail);
757 s_usersOnWorking->SetValue((uint64)servuser);
758 s_filesOnWorking->SetValue((uint64)servfile);
759 s_totalUsers->SetValue((uint64)servtuser);
760 s_totalFiles->SetValue((uint64)servtfile);
761 s_serverOccupation->SetValue(servocc);
765 void CStatistics::AddSourceOrigin(unsigned origin)
767 CStatTreeItemNativeCounter* counter = (CStatTreeItemNativeCounter*)s_foundSources->GetChildById(0x0100 + origin);
768 if (counter) {
769 ++(*counter);
770 } else {
771 counter = new CStatTreeItemNativeCounter(OriginToText(origin) + wxT(": %s"), stHideIfZero | stShowPercent);
772 ++(*counter);
773 s_foundSources->AddChild(counter, 0x0100 + origin);
777 void CStatistics::RemoveSourceOrigin(unsigned origin)
779 CStatTreeItemNativeCounter* counter = (CStatTreeItemNativeCounter*)s_foundSources->GetChildById(0x0100 + origin);
780 wxASSERT(counter);
781 --(*counter);
784 uint32 GetSoftID(uint8 SoftType)
786 // prevent appearing multiple tree entries with the same name
787 // this should be kept in sync with GetSoftName().
788 switch (SoftType) {
789 case SO_OLDEMULE:
790 return 0x0100 + SO_EMULE;
791 case SO_NEW_SHAREAZA:
792 case SO_NEW2_SHAREAZA:
793 return 0x0100 + SO_SHAREAZA;
794 case SO_NEW2_MLDONKEY:
795 return 0x0100 + SO_NEW_MLDONKEY;
796 default:
797 return 0x0100 + SoftType;
801 void CStatistics::AddDownloadFromSoft(uint8 SoftType, uint32 bytes)
803 AddReceivedBytes(bytes);
805 uint32 id = GetSoftID(SoftType);
807 if (s_sessionDownload->HasChildWithId(id)) {
808 (*((CStatTreeItemCounter*)s_sessionDownload->GetChildById(id))) += bytes;
809 } else {
810 CStatTreeItemCounter* tmp = new CStatTreeItemCounter(GetSoftName(SoftType) + wxT(": %s"));
811 tmp->SetDisplayMode(dmBytes);
812 (*tmp) += bytes;
813 s_sessionDownload->AddChild(tmp, id);
817 void CStatistics::AddUploadToSoft(uint8 SoftType, uint32 bytes)
819 uint32 id = GetSoftID(SoftType);
821 if (s_sessionUpload->HasChildWithId(id)) {
822 (*((CStatTreeItemCounter*)s_sessionUpload->GetChildById(id))) += bytes;
823 } else {
824 CStatTreeItemCounter* tmp = new CStatTreeItemCounter(GetSoftName(SoftType) + wxT(": %s"));
825 tmp->SetDisplayMode(dmBytes);
826 (*tmp) += bytes;
827 s_sessionUpload->AddChild(tmp, id);
831 inline bool SupportsOSInfo(unsigned clientSoft)
833 return (clientSoft == SO_AMULE) || (clientSoft == SO_HYDRANODE) || (clientSoft == SO_NEW2_MLDONKEY);
836 // Do some random black magic to strings to get a relatively unique number for them.
837 uint32 GetIdFromString(const wxString& str)
839 uint32 id = 0;
840 for (unsigned i = 0; i < str.Length(); ++i) {
841 unsigned old_id = id;
842 id += (uint32)str.GetChar(i);
843 id <<= 2;
844 id ^= old_id;
845 id -= old_id;
847 return ((id >> 1) + id | 0x00000100) & 0x7fffffff;
850 void CStatistics::AddKnownClient(CUpDownClient *pClient)
852 ++(*s_clients);
854 uint32 clientSoft = pClient->GetClientSoft();
855 uint32 id = GetSoftID(clientSoft);
857 CStatTreeItemCounter *client;
859 if (s_clients->HasChildWithId(id)) {
860 client = (CStatTreeItemCounter*)s_clients->GetChildById(id);
861 ++(*client);
862 } else {
863 uint32 flags = stSortChildren | stShowPercent | stHideIfZero;
864 if (!SupportsOSInfo(clientSoft)) {
865 flags |= stCapChildren;
867 client = new CStatTreeItemCounter(GetSoftName(clientSoft) + wxT(": %s"), flags);
868 ++(*client);
869 s_clients->AddChild(client, id);
870 if (SupportsOSInfo(clientSoft)) {
871 client->AddChild(new CStatTreeItemBase(wxTRANSLATE("Version"), stSortChildren | stCapChildren), 2);
872 client->AddChild(new CStatTreeItemBase(wxTRANSLATE("Operating System"), stSortChildren | stSortByValue), 1);
876 CStatTreeItemBase *versionRoot = SupportsOSInfo(clientSoft) ? client->GetChildById(2) : client;
877 uint32 clientVersion = pClient->GetVersion();
879 if (versionRoot->HasChildWithId(clientVersion)) {
880 CStatTreeItemCounter *version = (CStatTreeItemCounter*)versionRoot->GetChildById(clientVersion);
881 ++(*version);
882 } else {
883 const wxString& versionStr = pClient->GetVersionString();
884 CStatTreeItemCounter *version = new CStatTreeItemCounter((versionStr.IsEmpty() ? wxString(wxTRANSLATE("Unknown")) : versionStr) + wxT(": %s"), stShowPercent | stHideIfZero);
885 ++(*version);
886 versionRoot->AddChild(version, clientVersion, SupportsOSInfo(clientSoft));
889 if (SupportsOSInfo(clientSoft)) {
890 const wxString& OSInfo = pClient->GetClientOSInfo();
891 uint32 OS_ID = OSInfo.IsEmpty() ? 0 : GetIdFromString(OSInfo);
892 CStatTreeItemBase* OSRoot = client->GetChildById(1);
893 CStatTreeItemCounter* OSNode = (CStatTreeItemCounter*)OSRoot->GetChildById(OS_ID);
894 if (OSNode) {
895 ++(*OSNode);
896 } else {
897 OSNode = new CStatTreeItemCounter((OS_ID ? OSInfo : wxString(wxTRANSLATE("Not Received"))) + wxT(": %s"), stShowPercent | stHideIfZero);
898 ++(*OSNode);
899 OSRoot->AddChild(OSNode, OS_ID, true);
904 void CStatistics::RemoveKnownClient(uint32 clientSoft, uint32 clientVersion, const wxString& OSInfo)
906 --(*s_clients);
908 uint32 id = GetSoftID(clientSoft);
910 CStatTreeItemCounter *client = (CStatTreeItemCounter*)s_clients->GetChildById(id);
911 wxASSERT(client);
912 --(*client);
914 CStatTreeItemBase *versionRoot = SupportsOSInfo(clientSoft) ? client->GetChildById(2) : client;
916 CStatTreeItemCounter *version = (CStatTreeItemCounter*)versionRoot->GetChildById(clientVersion);
917 wxASSERT(version);
918 --(*version);
920 if (SupportsOSInfo(clientSoft)) {
921 uint32 OS_ID = OSInfo.IsEmpty() ? 0 : GetIdFromString(OSInfo);
922 CStatTreeItemCounter* OSNode = (CStatTreeItemCounter*)client->GetChildById(1)->GetChildById(OS_ID);
923 wxASSERT(OSNode);
924 --(*OSNode);
928 #else /* EC_REMOTE (CLIENT_GUI) */
930 CStatistics::CStatistics(CRemoteConnect &conn)
932 m_conn(conn)
934 s_start_time = GetTickCount64();
936 // Init Tree
937 s_statTree = new CStatTreeItemBase(_("Statistics"), 0);
939 // Clear stat data container
940 for (int i = 0; i < sdTotalItems; ++i) {
941 s_statData[i] = 0;
946 CStatistics::~CStatistics()
948 delete s_statTree;
952 void CStatistics::UpdateStats(const CECPacket* stats)
954 s_statData[sdUpload] = stats->GetTagByNameSafe(EC_TAG_STATS_UL_SPEED)->GetInt();
955 s_statData[sdUpOverhead] = stats->GetTagByNameSafe(EC_TAG_STATS_UP_OVERHEAD)->GetInt();
956 s_statData[sdDownload] = stats->GetTagByNameSafe(EC_TAG_STATS_DL_SPEED)->GetInt();
957 s_statData[sdDownOverhead] = stats->GetTagByNameSafe(EC_TAG_STATS_DOWN_OVERHEAD)->GetInt();
958 s_statData[sdWaitingClients] = stats->GetTagByNameSafe(EC_TAG_STATS_UL_QUEUE_LEN)->GetInt();
959 s_statData[sdBannedClients] = stats->GetTagByNameSafe(EC_TAG_STATS_BANNED_COUNT)->GetInt();
960 s_statData[sdED2KUsers] = stats->GetTagByNameSafe(EC_TAG_STATS_ED2K_USERS)->GetInt();
961 s_statData[sdKadUsers] = stats->GetTagByNameSafe(EC_TAG_STATS_KAD_USERS)->GetInt();
962 s_statData[sdED2KFiles] = stats->GetTagByNameSafe(EC_TAG_STATS_ED2K_FILES)->GetInt();
963 s_statData[sdKadFiles] = stats->GetTagByNameSafe(EC_TAG_STATS_KAD_FILES)->GetInt();
964 s_statData[sdKadFirewalledUDP] = stats->GetTagByNameSafe(EC_TAG_STATS_KAD_FIREWALLED_UDP)->GetInt();
965 s_statData[sdKadIndexedSources] = stats->GetTagByNameSafe(EC_TAG_STATS_KAD_INDEXED_SOURCES)->GetInt();
966 s_statData[sdKadIndexedKeywords] = stats->GetTagByNameSafe(EC_TAG_STATS_KAD_INDEXED_KEYWORDS)->GetInt();
967 s_statData[sdKadIndexedNotes] = stats->GetTagByNameSafe(EC_TAG_STATS_KAD_INDEXED_NOTES)->GetInt();
968 s_statData[sdKadIndexedLoad] = stats->GetTagByNameSafe(EC_TAG_STATS_KAD_INDEXED_LOAD)->GetInt();
969 s_statData[sdKadIPAdress] = stats->GetTagByNameSafe(EC_TAG_STATS_KAD_IP_ADRESS)->GetInt();
970 s_statData[sdBuddyStatus] = stats->GetTagByNameSafe(EC_TAG_STATS_BUDDY_STATUS)->GetInt();
971 s_statData[sdBuddyIP] = stats->GetTagByNameSafe(EC_TAG_STATS_BUDDY_IP)->GetInt();
972 s_statData[sdBuddyPort] = stats->GetTagByNameSafe(EC_TAG_STATS_BUDDY_PORT)->GetInt();
974 const CECTag * LoggerTag = stats->GetTagByName(EC_TAG_STATS_LOGGER_MESSAGE);
975 if (LoggerTag) {
976 for (size_t i = 0; i < LoggerTag->GetTagCount(); i++) {
977 theApp->AddRemoteLogLine(LoggerTag->GetTagByIndex(i)->GetStringData());
983 void CStatistics::UpdateStatsTree()
985 CECPacket request(EC_OP_GET_STATSTREE);
986 if (thePrefs::GetMaxClientVersions() != 0) {
987 request.AddTag(CECTag(EC_TAG_STATTREE_CAPPING, (uint8)thePrefs::GetMaxClientVersions()));
989 const CECPacket* reply = m_conn.SendRecvPacket(&request);
990 if (reply) {
991 const CECTag* treeRoot = reply->GetTagByName(EC_TAG_STATTREE_NODE);
992 if (treeRoot) {
993 delete s_statTree;
994 s_statTree = new CStatTreeItemBase(treeRoot);
997 delete reply;
1000 #endif /* !EC_REMOTE */
1002 // File_checked_for_headers