SLikeNet 0.1.3

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
StatisticsHistory.cpp
Go to the documentation of this file.
1 /*
2 * Original work: Copyright (c) 2014, Oculus VR, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * RakNet License.txt file in the licenses directory of this source tree. An additional grant
7 * of patent rights can be found in the RakNet Patents.txt file in the same directory.
8 *
9 *
10 * Modified work: Copyright (c) 2017, SLikeSoft UG (haftungsbeschränkt)
11 *
12 * This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
13 * license found in the license.txt file in the root directory of this source tree.
14 */
15
16 #include "slikenet/NativeFeatureIncludes.h"
17 #if _RAKNET_SUPPORT_StatisticsHistory==1
18
19 #include "slikenet/StatisticsHistory.h"
20 #include "slikenet/GetTime.h"
21 #include "slikenet/statistics.h"
22 #include "slikenet/peerinterface.h"
23
24 using namespace SLNet;
25
26 STATIC_FACTORY_DEFINITIONS(StatisticsHistory,StatisticsHistory);
27 STATIC_FACTORY_DEFINITIONS(StatisticsHistoryPlugin,StatisticsHistoryPlugin);
28
29 int StatisticsHistory::TrackedObjectComp( const uint64_t &key, StatisticsHistory::TrackedObject* const &data )
30 {
31 if (key < data->trackedObjectData.objectId)
32 return -1;
33 if (key == data->trackedObjectData.objectId)
34 return 0;
35 return 1;
36 }
37
38 int TimeAndValueQueueCompAsc( StatisticsHistory::TimeAndValueQueue* const &key, StatisticsHistory::TimeAndValueQueue* const &data )
39 {
40 if (key->sortValue < data->sortValue)
41 return -1;
42 if (key->sortValue > data->sortValue)
43 return 1;
44 if (key->key < data->key)
45 return -1;
46 if (key->key > data->key)
47 return 1;
48 return 0;
49 }
50
51 int TimeAndValueQueueCompDesc( StatisticsHistory::TimeAndValueQueue* const &key, StatisticsHistory::TimeAndValueQueue* const &data )
52 {
53 if (key->sortValue > data->sortValue)
54 return -1;
55 if (key->sortValue < data->sortValue)
56 return 1;
57 if (key->key > data->key)
58 return -1;
59 if (key->key < data->key)
60 return 1;
61 return 0;
62 }
63 StatisticsHistory::TrackedObjectData::TrackedObjectData() {}
64 StatisticsHistory::TrackedObjectData::TrackedObjectData(uint64_t _objectId, int _objectType, void *_userData)
65 {
66 objectId=_objectId;
67 objectType=_objectType;
68 userData=_userData;
69 }
70 StatisticsHistory::StatisticsHistory() {timeToTrack = 30000;}
71 StatisticsHistory::~StatisticsHistory()
72 {
73 Clear();
74 }
75 void StatisticsHistory::SetDefaultTimeToTrack(Time defaultTimeToTrack) {timeToTrack = defaultTimeToTrack;}
76 Time StatisticsHistory::GetDefaultTimeToTrack(void) const {return timeToTrack;}
77 bool StatisticsHistory::AddObject(TrackedObjectData tod)
78 {
79 bool objectExists;
80 unsigned int idx = objects.GetIndexFromKey(tod.objectId, &objectExists);
81 if (objectExists)
82 return false;
83 TrackedObject *to = SLNet::OP_NEW<TrackedObject>(_FILE_AND_LINE_);
84 to->trackedObjectData=tod;
85 objects.InsertAtIndex(to,idx,_FILE_AND_LINE_);
86 return true;
87 }
88 bool StatisticsHistory::RemoveObject(uint64_t objectId, void **userData)
89 {
90 unsigned int idx = GetObjectIndex(objectId);
91 if (idx == (unsigned int) -1)
92 return false;
93 if (userData)
94 *userData = objects[idx]->trackedObjectData.userData;
95 RemoveObjectAtIndex(idx);
96 return true;
97 }
98 void StatisticsHistory::RemoveObjectAtIndex(unsigned int index)
99 {
100 TrackedObject *to = objects[index];
101 objects.RemoveAtIndex(index);
102 SLNet::OP_DELETE(to, _FILE_AND_LINE_);
103 }
104 void StatisticsHistory::Clear(void)
105 {
106 for (unsigned int idx=0; idx < objects.Size(); idx++)
107 {
108 SLNet::OP_DELETE(objects[idx], _FILE_AND_LINE_);
109 }
110 objects.Clear(false, _FILE_AND_LINE_);
111 }
112 unsigned int StatisticsHistory::GetObjectCount(void) const {return objects.Size();}
113 StatisticsHistory::TrackedObjectData * StatisticsHistory::GetObjectAtIndex(unsigned int index) const {return &objects[index]->trackedObjectData;}
114 bool StatisticsHistory::AddValueByObjectID(uint64_t objectId, RakString key, SHValueType val, Time curTime, bool combineEqualTimes)
115 {
116 unsigned int idx = GetObjectIndex(objectId);
117 if (idx == (unsigned int) -1)
118 return false;
119 AddValueByIndex(idx, key, val, curTime, combineEqualTimes);
120 return true;
121 }
122 void StatisticsHistory::AddValueByIndex(unsigned int index, RakString key, SHValueType val, Time curTime, bool combineEqualTimes)
123 {
124 TimeAndValueQueue *queue;
125 TrackedObject *to = objects[index];
126 DataStructures::HashIndex hi = to->dataQueues.GetIndexOf(key);
127 if (hi.IsInvalid())
128 {
129 queue = SLNet::OP_NEW<TimeAndValueQueue>(_FILE_AND_LINE_);
130 queue->key=key;
131 queue->timeToTrackValues = timeToTrack;
132 to->dataQueues.Push(key, queue, _FILE_AND_LINE_);
133 }
134 else
135 {
136 queue = to->dataQueues.ItemAtIndex(hi);
137 }
138
139 TimeAndValue tav;
140 if (combineEqualTimes==true && queue->values.Size()>0 && queue->values.PeekTail().time==curTime)
141 {
142 tav = queue->values.PopTail();
143
144 queue->recentSum -= tav.val;
145 queue->recentSumOfSquares -= tav.val * tav.val;
146 queue->longTermSum -= tav.val;
147 queue->longTermCount = queue->longTermCount - 1;
148 }
149 else
150 {
151 tav.val=0.0;
152 tav.time=curTime;
153 }
154
155 tav.val+=val;
156 queue->values.Push(tav, _FILE_AND_LINE_);
157
158 queue->recentSum += tav.val;
159 queue->recentSumOfSquares += tav.val * tav.val;
160 queue->longTermSum += tav.val;
161 queue->longTermCount = queue->longTermCount + 1;
162 if (queue->longTermLowest > tav.val)
163 queue->longTermLowest = tav.val;
164 if (queue->longTermHighest < tav.val)
165 queue->longTermHighest = tav.val;
166 }
167 StatisticsHistory::SHErrorCode StatisticsHistory::GetHistoryForKey(uint64_t objectId, RakString key, StatisticsHistory::TimeAndValueQueue **values, Time curTime) const
168 {
169 if (values == 0)
170 return SH_INVALID_PARAMETER;
171
172 unsigned int idx = GetObjectIndex(objectId);
173 if (idx == (unsigned int) -1)
174 return SH_UKNOWN_OBJECT;
175 TrackedObject *to = objects[idx];
176 DataStructures::HashIndex hi = to->dataQueues.GetIndexOf(key);
177 if (hi.IsInvalid())
178 return SH_UKNOWN_KEY;
179 *values = to->dataQueues.ItemAtIndex(hi);
180 (*values)->CullExpiredValues(curTime);
181 return SH_OK;
182 }
183 bool StatisticsHistory::GetHistorySorted(uint64_t objectId, SHSortOperation sortType, DataStructures::List<StatisticsHistory::TimeAndValueQueue *> &values) const
184 {
185 unsigned int idx = GetObjectIndex(objectId);
186 if (idx == (unsigned int) -1)
187 return false;
188 TrackedObject *to = objects[idx];
189 DataStructures::List<TimeAndValueQueue*> itemList;
190 DataStructures::List<RakString> keyList;
191 to->dataQueues.GetAsList(itemList,keyList,_FILE_AND_LINE_);
192 Time curTime = GetTime();
193
194 DataStructures::OrderedList<TimeAndValueQueue*, TimeAndValueQueue*,TimeAndValueQueueCompAsc> sortedQueues;
195 for (unsigned int i=0; i < itemList.Size(); i++)
196 {
197 TimeAndValueQueue *tavq = itemList[i];
198 tavq->CullExpiredValues(curTime);
199
200 if (sortType == SH_SORT_BY_RECENT_SUM_ASCENDING || sortType == SH_SORT_BY_RECENT_SUM_DESCENDING)
201 tavq->sortValue = tavq->GetRecentSum();
202 else if (sortType == SH_SORT_BY_LONG_TERM_SUM_ASCENDING || sortType == SH_SORT_BY_LONG_TERM_SUM_DESCENDING)
203 tavq->sortValue = tavq->GetLongTermSum();
204 else if (sortType == SH_SORT_BY_RECENT_SUM_OF_SQUARES_ASCENDING || sortType == SH_SORT_BY_RECENT_SUM_OF_SQUARES_DESCENDING)
205 tavq->sortValue = tavq->GetRecentSumOfSquares();
206 else if (sortType == SH_SORT_BY_RECENT_AVERAGE_ASCENDING || sortType == SH_SORT_BY_RECENT_AVERAGE_DESCENDING)
207 tavq->sortValue = tavq->GetRecentAverage();
208 else if (sortType == SH_SORT_BY_LONG_TERM_AVERAGE_ASCENDING || sortType == SH_SORT_BY_LONG_TERM_AVERAGE_DESCENDING)
209 tavq->sortValue = tavq->GetLongTermAverage();
210 else if (sortType == SH_SORT_BY_RECENT_HIGHEST_ASCENDING || sortType == SH_SORT_BY_RECENT_HIGHEST_DESCENDING)
211 tavq->sortValue = tavq->GetRecentHighest();
212 else if (sortType == SH_SORT_BY_RECENT_LOWEST_ASCENDING || sortType == SH_SORT_BY_RECENT_LOWEST_DESCENDING)
213 tavq->sortValue = tavq->GetRecentLowest();
214 else if (sortType == SH_SORT_BY_LONG_TERM_HIGHEST_ASCENDING || sortType == SH_SORT_BY_LONG_TERM_HIGHEST_DESCENDING)
215 tavq->sortValue = tavq->GetLongTermHighest();
216 else
217 tavq->sortValue = tavq->GetLongTermLowest();
218
219 if (
220 sortType == SH_SORT_BY_RECENT_SUM_ASCENDING ||
221 sortType == SH_SORT_BY_LONG_TERM_SUM_ASCENDING ||
222 sortType == SH_SORT_BY_RECENT_SUM_OF_SQUARES_ASCENDING ||
223 sortType == SH_SORT_BY_RECENT_AVERAGE_ASCENDING ||
224 sortType == SH_SORT_BY_LONG_TERM_AVERAGE_ASCENDING ||
225 sortType == SH_SORT_BY_RECENT_HIGHEST_ASCENDING ||
226 sortType == SH_SORT_BY_RECENT_LOWEST_ASCENDING ||
227 sortType == SH_SORT_BY_LONG_TERM_HIGHEST_ASCENDING ||
228 sortType == SH_SORT_BY_LONG_TERM_LOWEST_ASCENDING)
229 sortedQueues.Insert(tavq, tavq, false, _FILE_AND_LINE_, TimeAndValueQueueCompAsc);
230 else
231 sortedQueues.Insert(tavq, tavq, false, _FILE_AND_LINE_, TimeAndValueQueueCompDesc);
232 }
233
234 for (unsigned int i=0; i < sortedQueues.Size(); i++)
235 values.Push(sortedQueues[i], _FILE_AND_LINE_);
236 return true;
237 }
238 void StatisticsHistory::MergeAllObjectsOnKey(RakString key, TimeAndValueQueue *tavqOutput, SHDataCategory dataCategory) const
239 {
240 tavqOutput->Clear();
241
242 Time curTime = GetTime();
243
244 // Find every object with this key
245 for (unsigned int idx=0; idx < objects.Size(); idx++)
246 {
247 TrackedObject *to = objects[idx];
248 DataStructures::HashIndex hi = to->dataQueues.GetIndexOf(key);
249 if (hi.IsInvalid()==false)
250 {
251 TimeAndValueQueue *tavqInput = to->dataQueues.ItemAtIndex(hi);
252 tavqInput->CullExpiredValues(curTime);
253 TimeAndValueQueue::MergeSets(tavqOutput, dataCategory, tavqInput, dataCategory, tavqOutput);
254 }
255 }
256 }
257 void StatisticsHistory::GetUniqueKeyList(DataStructures::List<RakString> &keys)
258 {
259 keys.Clear(true, _FILE_AND_LINE_);
260
261 for (unsigned int idx=0; idx < objects.Size(); idx++)
262 {
263 TrackedObject *to = objects[idx];
264 DataStructures::List<TimeAndValueQueue*> itemList;
265 DataStructures::List<SLNet::RakString> keyList;
266 to->dataQueues.GetAsList(itemList, keyList, _FILE_AND_LINE_);
267 for (unsigned int k=0; k < keyList.Size(); k++)
268 {
269 bool hasKey=false;
270 for (unsigned int j=0; j < keys.Size(); j++)
271 {
272 if (keys[j]==keyList[k])
273 {
274 hasKey=true;
275 break;
276 }
277 }
278
279 if (hasKey==false)
280 keys.Push(keyList[k], _FILE_AND_LINE_);
281 }
282 }
283 }
284 StatisticsHistory::TimeAndValueQueue::TimeAndValueQueue()
285 {
286 Clear();
287 }
288 StatisticsHistory::TimeAndValueQueue::~TimeAndValueQueue(){}
289 void StatisticsHistory::TimeAndValueQueue::SetTimeToTrackValues(Time t)
290 {
291 timeToTrackValues = t;
292 }
293 Time StatisticsHistory::TimeAndValueQueue::GetTimeToTrackValues(void) const {return timeToTrackValues;}
294 SHValueType StatisticsHistory::TimeAndValueQueue::GetRecentSum(void) const {return recentSum;}
295 SHValueType StatisticsHistory::TimeAndValueQueue::GetRecentSumOfSquares(void) const {return recentSumOfSquares;}
296 SHValueType StatisticsHistory::TimeAndValueQueue::GetLongTermSum(void) const {return longTermSum;}
297 SHValueType StatisticsHistory::TimeAndValueQueue::GetRecentAverage(void) const
298 {
299 if (values.Size() > 0)
300 return recentSum / (SHValueType) values.Size();
301 else
302 return 0;
303 }
304 SHValueType StatisticsHistory::TimeAndValueQueue::GetRecentLowest(void) const
305 {
306 SHValueType out = SH_TYPE_MAX;
307 for (unsigned int idx=0; idx < values.Size(); idx++)
308 {
309 if (values[idx].val < out)
310 out = values[idx].val;
311 }
312 return out;
313 }
314 SHValueType StatisticsHistory::TimeAndValueQueue::GetRecentHighest(void) const
315 {
316 SHValueType out = -SH_TYPE_MAX;
317 for (unsigned int idx=0; idx < values.Size(); idx++)
318 {
319 if (values[idx].val > out)
320 out = values[idx].val;
321 }
322 return out;
323 }
324 SHValueType StatisticsHistory::TimeAndValueQueue::GetRecentStandardDeviation(void) const
325 {
326 if (values.Size()==0)
327 return 0;
328
329 SHValueType recentMean= GetRecentAverage();
330 SHValueType squareOfMean = recentMean * recentMean;
331 SHValueType meanOfSquares = GetRecentSumOfSquares() / (SHValueType) values.Size();
332 return meanOfSquares - squareOfMean;
333 }
334 SHValueType StatisticsHistory::TimeAndValueQueue::GetLongTermAverage(void) const
335 {
336 if (longTermCount == 0)
337 return 0;
338 return longTermSum / longTermCount;
339 }
340 SHValueType StatisticsHistory::TimeAndValueQueue::GetLongTermLowest(void) const {return longTermLowest;}
341 SHValueType StatisticsHistory::TimeAndValueQueue::GetLongTermHighest(void) const {return longTermHighest;}
342 Time StatisticsHistory::TimeAndValueQueue::GetTimeRange(void) const
343 {
344 if (values.Size()<2)
345 return 0;
346 return values[values.Size()-1].time - values[0].time;
347 }
348 SHValueType StatisticsHistory::TimeAndValueQueue::GetSumSinceTime(Time t) const
349 {
350 SHValueType sum = 0;
351 for (int i=values.Size(); i > 0; --i)
352 {
353 if (values[i-1].time>=t)
354 sum+=values[i-1].val;
355 }
356 return sum;
357 }
358 void StatisticsHistory::TimeAndValueQueue::MergeSets( const TimeAndValueQueue *lhs, SHDataCategory lhsDataCategory, const TimeAndValueQueue *rhs, SHDataCategory rhsDataCategory, TimeAndValueQueue *output )
359 {
360 // Two ways to merge:
361 // 1. Treat rhs as just more data points.
362 // 1A. Sums are just added. If two values have the same time, just put in queue twice
363 // 1B. longTermLowest and longTermHighest are the lowest and highest of the two sets
364 //
365 // 2. Add by time. If time for the other set is missing, calculate slope to extrapolate
366 // 2A. Have to recalculate recentSum, recentSumOfSquares.
367 // 2B. longTermSum, longTermCount, longTermLowest, longTermHighest are unknown
368
369 if (lhs!=output)
370 {
371 output->key = lhs->key;
372 output->timeToTrackValues = lhs->timeToTrackValues;
373 }
374 else
375 {
376 output->key = rhs->key;
377 output->timeToTrackValues = rhs->timeToTrackValues;
378 }
379
380 unsigned int lhsIndex, rhsIndex;
381 lhsIndex=0;
382 rhsIndex=0;
383
384 // I use local valuesOutput in case lhs==output || rhs==output
385 DataStructures::Queue<TimeAndValue> valuesOutput;
386
387 if (lhsDataCategory==StatisticsHistory::DC_DISCRETE && rhsDataCategory==StatisticsHistory::DC_DISCRETE)
388 {
389 while (rhsIndex < rhs->values.Size() && lhsIndex < lhs->values.Size())
390 {
391 if (rhs->values[rhsIndex].time < lhs->values[lhsIndex].time)
392 {
393 valuesOutput.Push(rhs->values[rhsIndex], _FILE_AND_LINE_ );
394 rhsIndex++;
395 }
396 else if (rhs->values[rhsIndex].time > lhs->values[lhsIndex].time)
397 {
398 valuesOutput.Push(lhs->values[rhsIndex], _FILE_AND_LINE_ );
399 lhsIndex++;
400 }
401 else
402 {
403 valuesOutput.Push(rhs->values[rhsIndex], _FILE_AND_LINE_ );
404 rhsIndex++;
405 valuesOutput.Push(lhs->values[rhsIndex], _FILE_AND_LINE_ );
406 lhsIndex++;
407 }
408 }
409
410 while (rhsIndex < rhs->values.Size())
411 {
412 valuesOutput.Push(rhs->values[rhsIndex], _FILE_AND_LINE_ );
413 rhsIndex++;
414 }
415 while (lhsIndex < lhs->values.Size())
416 {
417 valuesOutput.Push(lhs->values[lhsIndex], _FILE_AND_LINE_ );
418 lhsIndex++;
419 }
420
421 output->recentSum = lhs->recentSum + rhs->recentSum;
422 output->recentSumOfSquares = lhs->recentSumOfSquares + rhs->recentSumOfSquares;
423 output->longTermSum = lhs->longTermSum + rhs->longTermSum;
424 output->longTermCount = lhs->longTermCount + rhs->longTermCount;
425 if (lhs->longTermLowest < rhs->longTermLowest)
426 output->longTermLowest = lhs->longTermLowest;
427 else
428 output->longTermLowest = rhs->longTermLowest;
429 if (lhs->longTermHighest > rhs->longTermHighest)
430 output->longTermHighest = lhs->longTermHighest;
431 else
432 output->longTermHighest = rhs->longTermHighest;
433 }
434 else
435 {
436 TimeAndValue lastTimeAndValueLhs, lastTimeAndValueRhs;
437 lastTimeAndValueLhs.time=0;
438 lastTimeAndValueLhs.val=0;
439 lastTimeAndValueRhs.time=0;
440 lastTimeAndValueRhs.val=0;
441 SHValueType lastSlopeLhs=0;
442 SHValueType lastSlopeRhs=0;
443 Time timeSinceOppositeValue;
444
445 TimeAndValue newTimeAndValue;
446
447 while (rhsIndex < rhs->values.Size() && lhsIndex < lhs->values.Size())
448 {
449 if (rhs->values[rhsIndex].time < lhs->values[lhsIndex].time)
450 {
451 timeSinceOppositeValue = rhs->values[rhsIndex].time - lastTimeAndValueLhs.time;
452 newTimeAndValue.val = rhs->values[rhsIndex].val + lastTimeAndValueLhs.val + lastSlopeLhs * timeSinceOppositeValue;
453 newTimeAndValue.time = rhs->values[rhsIndex].time;
454 lastTimeAndValueRhs = rhs->values[rhsIndex];
455 if (rhsIndex>0 && rhs->values[rhsIndex].time != rhs->values[rhsIndex-1].time && rhsDataCategory==StatisticsHistory::DC_CONTINUOUS)
456 lastSlopeRhs = (rhs->values[rhsIndex].val - rhs->values[rhsIndex-1].val) / (SHValueType) (rhs->values[rhsIndex].time - rhs->values[rhsIndex-1].time);
457 rhsIndex++;
458 }
459 else if (lhs->values[lhsIndex].time < rhs->values[rhsIndex].time)
460 {
461 timeSinceOppositeValue = lhs->values[lhsIndex].time - lastTimeAndValueRhs.time;
462 newTimeAndValue.val = lhs->values[lhsIndex].val + lastTimeAndValueRhs.val + lastSlopeRhs * timeSinceOppositeValue;
463 newTimeAndValue.time = lhs->values[lhsIndex].time;
464 lastTimeAndValueLhs = lhs->values[lhsIndex];
465 if (lhsIndex>0 && lhs->values[lhsIndex].time != lhs->values[lhsIndex-1].time && lhsDataCategory==StatisticsHistory::DC_CONTINUOUS)
466 lastSlopeLhs = (lhs->values[lhsIndex].val - lhs->values[lhsIndex-1].val) / (SHValueType) (lhs->values[lhsIndex].time - lhs->values[lhsIndex-1].time);
467 lhsIndex++;
468 }
469 else
470 {
471 newTimeAndValue.val = lhs->values[lhsIndex].val + rhs->values[rhsIndex].val;
472 newTimeAndValue.time = lhs->values[lhsIndex].time;
473 lastTimeAndValueRhs = rhs->values[rhsIndex];
474 lastTimeAndValueLhs = lhs->values[lhsIndex];
475 if (rhsIndex>0 && rhs->values[rhsIndex].time != rhs->values[rhsIndex-1].time && rhsDataCategory==StatisticsHistory::DC_CONTINUOUS)
476 lastSlopeRhs = (rhs->values[rhsIndex].val - rhs->values[rhsIndex-1].val) / (SHValueType) (rhs->values[rhsIndex].time - rhs->values[rhsIndex-1].time);
477 if (lhsIndex>0 && lhs->values[lhsIndex].time != lhs->values[lhsIndex-1].time && lhsDataCategory==StatisticsHistory::DC_CONTINUOUS)
478 lastSlopeLhs = (lhs->values[lhsIndex].val - lhs->values[lhsIndex-1].val) / (SHValueType) (lhs->values[lhsIndex].time - lhs->values[lhsIndex-1].time);
479 lhsIndex++;
480 rhsIndex++;
481 }
482
483 valuesOutput.Push(newTimeAndValue, _FILE_AND_LINE_ );
484 }
485
486 while (rhsIndex < rhs->values.Size())
487 {
488 timeSinceOppositeValue = rhs->values[rhsIndex].time - lastTimeAndValueLhs.time;
489 newTimeAndValue.val = rhs->values[rhsIndex].val + lastTimeAndValueLhs.val + lastSlopeLhs * timeSinceOppositeValue;
490 newTimeAndValue.time = rhs->values[rhsIndex].time;
491 valuesOutput.Push(newTimeAndValue, _FILE_AND_LINE_ );
492 rhsIndex++;
493 }
494 while (lhsIndex < lhs->values.Size())
495 {
496 timeSinceOppositeValue = lhs->values[lhsIndex].time - lastTimeAndValueRhs.time;
497 newTimeAndValue.val = lhs->values[lhsIndex].val + lastTimeAndValueRhs.val + lastSlopeRhs * timeSinceOppositeValue;
498 newTimeAndValue.time = lhs->values[lhsIndex].time;
499 valuesOutput.Push(newTimeAndValue, _FILE_AND_LINE_ );
500 lhsIndex++;
501 }
502
503 output->recentSum = 0;
504 output->recentSumOfSquares = 0;
505 for (unsigned int i=0; i < valuesOutput.Size(); i++)
506 {
507 output->recentSum += valuesOutput[i].val;
508 output->recentSumOfSquares += valuesOutput[i].val * valuesOutput[i].val;
509 }
510 }
511
512 output->values = valuesOutput;
513 }
514 void StatisticsHistory::TimeAndValueQueue::ResizeSampleSet( int maxSamples, DataStructures::Queue<StatisticsHistory::TimeAndValue> &histogram, SHDataCategory dataCategory, Time timeClipStart, Time timeClipEnd )
515 {
516 histogram.Clear(_FILE_AND_LINE_);
517 if (maxSamples==0)
518 return;
519 Time timeRange = GetTimeRange();
520 if (timeRange==0)
521 return;
522 if (maxSamples==1)
523 {
524 StatisticsHistory::TimeAndValue tav;
525 tav.time = timeRange;
526 tav.val = GetRecentSum();
527 histogram.Push(tav, _FILE_AND_LINE_);
528 return;
529 }
530 Time interval = timeRange / maxSamples;
531 if (interval==0)
532 interval=1;
533 unsigned int dataIndex;
534 Time timeBoundary;
535 StatisticsHistory::TimeAndValue currentSum;
536 Time currentTime;
537 SHValueType numSamples;
538 Time endTime;
539
540 numSamples=0;
541 endTime = values[values.Size()-1].time;
542 dataIndex=0;
543 currentTime=values[0].time;
544 currentSum.val=0;
545 currentSum.time=values[0].time + interval / 2;
546 timeBoundary = values[0].time + interval;
547 while (timeBoundary <= endTime)
548 {
549 while (dataIndex < values.Size() && values[dataIndex].time <= timeBoundary)
550 {
551 currentSum.val += values[dataIndex].val;
552 dataIndex++;
553 numSamples++;
554 }
555
556 if (dataCategory==DC_CONTINUOUS)
557 {
558 if (dataIndex > 0 &&
559 dataIndex < values.Size() &&
560 values[dataIndex-1].time < timeBoundary &&
561 values[dataIndex].time > timeBoundary)
562 {
563 SHValueType interpolatedValue = Interpolate(values[dataIndex-1], values[dataIndex], timeBoundary);
564 currentSum.val+=interpolatedValue;
565 numSamples++;
566 }
567
568 if (numSamples > 1)
569 {
570 currentSum.val /= numSamples;
571 }
572 }
573
574 histogram.Push(currentSum, _FILE_AND_LINE_);
575 currentSum.time=timeBoundary + interval / 2;
576 timeBoundary += interval;
577 currentSum.val=0;
578 numSamples=0;
579 }
580
581
582 if ( timeClipStart!=0 && histogram.Size()>=1)
583 {
584 timeClipStart = histogram.Peek().time+timeClipStart;
585 if (histogram.PeekTail().time < timeClipStart)
586 {
587 histogram.Clear(_FILE_AND_LINE_);
588 }
589 else if (histogram.Size()>=2 && histogram.Peek().time < timeClipStart)
590 {
591 StatisticsHistory::TimeAndValue tav;
592
593 do
594 {
595 tav = histogram.Pop();
596
597 if (histogram.Peek().time == timeClipStart)
598 {
599 break;
600 }
601 else if (histogram.Peek().time > timeClipStart)
602 {
603 StatisticsHistory::TimeAndValue tav2;
604 tav2.val = StatisticsHistory::TimeAndValueQueue::Interpolate(tav, histogram.Peek(), timeClipStart);
605 tav2.time=timeClipStart;
606 histogram.PushAtHead(tav2, 0, _FILE_AND_LINE_);
607 break;
608 }
609 } while (histogram.Size()>=2);
610 }
611 }
612
613 if ( timeClipEnd!=0 && histogram.Size()>=1)
614 {
615 timeClipEnd = histogram.PeekTail().time-timeClipEnd;
616 if (histogram.Peek().time > timeClipEnd)
617 {
618 histogram.Clear(_FILE_AND_LINE_);
619 }
620 else if (histogram.Size()>=2 && histogram.PeekTail().time > timeClipEnd)
621 {
622 StatisticsHistory::TimeAndValue tav;
623
624 do
625 {
626 tav = histogram.PopTail();
627
628 if (histogram.PeekTail().time == timeClipEnd)
629 {
630 break;
631 }
632 else if (histogram.PeekTail().time < timeClipEnd)
633 {
634 StatisticsHistory::TimeAndValue tav2;
635 tav2.val = StatisticsHistory::TimeAndValueQueue::Interpolate(tav, histogram.PeekTail(), timeClipEnd);
636 tav2.time=timeClipEnd;
637 histogram.Push(tav2, _FILE_AND_LINE_);
638 break;
639 }
640 } while (histogram.Size()>=2);
641 }
642 }
643 }
644 void StatisticsHistory::TimeAndValueQueue::CullExpiredValues(Time curTime)
645 {
646 while (values.Size())
647 {
648 StatisticsHistory::TimeAndValue tav = values.Peek();
649 if (curTime - tav.time > timeToTrackValues)
650 {
651 recentSum -= tav.val;
652 recentSumOfSquares -= tav.val * tav.val;
653 values.Pop();
654 }
655 else
656 {
657 break;
658 }
659 }
660 }
661 SHValueType StatisticsHistory::TimeAndValueQueue::Interpolate(StatisticsHistory::TimeAndValue t1, StatisticsHistory::TimeAndValue t2, Time time)
662 {
663 if (t2.time==t1.time)
664 return (t1.val + t2.val) / 2;
665 // if (t2.time > t1.time)
666 // {
667 SHValueType slope = (t2.val - t1.val) / ((SHValueType) t2.time - (SHValueType) t1.time);
668 return t1.val + slope * ((SHValueType) time - (SHValueType) t1.time);
669 // }
670 // else
671 // {
672 // SHValueType slope = (t1.val - t2.val) / (SHValueType) (t1.time - t2.time);
673 // return t2.val + slope * (SHValueType) (time - t2.time);
674 // }
675 }
676 void StatisticsHistory::TimeAndValueQueue::Clear(void)
677 {
678 recentSum = 0;
679 recentSumOfSquares = 0;
680 longTermSum = 0;
681 longTermCount = 0;
682 longTermLowest = SH_TYPE_MAX;
683 longTermHighest = -SH_TYPE_MAX;
684 values.Clear(_FILE_AND_LINE_);
685 }
686 StatisticsHistory::TimeAndValueQueue& StatisticsHistory::TimeAndValueQueue::operator = ( const TimeAndValueQueue& input )
687 {
688 values=input.values;
689 timeToTrackValues=input.timeToTrackValues;
690 key=input.key;
691 recentSum=input.recentSum;
692 recentSumOfSquares=input.recentSumOfSquares;
693 longTermSum=input.longTermSum;
694 longTermCount=input.longTermCount;
695 longTermLowest=input.longTermLowest;
696 longTermHighest=input.longTermHighest;
697 return *this;
698 }
699 StatisticsHistory::TrackedObject::TrackedObject() {}
700 StatisticsHistory::TrackedObject::~TrackedObject()
701 {
702 DataStructures::List<StatisticsHistory::TimeAndValueQueue*> itemList;
703 DataStructures::List<RakString> keyList;
704 for (unsigned int idx=0; idx < itemList.Size(); idx++)
705 SLNet::OP_DELETE(itemList[idx], _FILE_AND_LINE_);
706 }
707 unsigned int StatisticsHistory::GetObjectIndex(uint64_t objectId) const
708 {
709 bool objectExists;
710 unsigned int idx = objects.GetIndexFromKey(objectId, &objectExists);
711 if (objectExists)
712 return idx;
713 return (unsigned int) -1;
714 }
715 StatisticsHistoryPlugin::StatisticsHistoryPlugin()
716 {
717 addNewConnections = true;
718 removeLostConnections = true;
719 newConnectionsObjectType = 0;
720 }
721 StatisticsHistoryPlugin::~StatisticsHistoryPlugin()
722 {
723 }
724 void StatisticsHistoryPlugin::SetTrackConnections(bool _addNewConnections, int _newConnectionsObjectType, bool _removeLostConnections)
725 {
726 addNewConnections = _addNewConnections;
727 removeLostConnections = _removeLostConnections;
728 newConnectionsObjectType = _newConnectionsObjectType;
729 }
730 void StatisticsHistoryPlugin::Update(void)
731 {
732 DataStructures::List<SystemAddress> addresses;
733 DataStructures::List<RakNetGUID> guids;
734 DataStructures::List<RakNetStatistics> stats;
735 rakPeerInterface->GetStatisticsList(addresses, guids, stats);
736
737 Time curTime = GetTime();
738 for (unsigned int idx = 0; idx < guids.Size(); idx++)
739 {
740 unsigned int objectIndex = statistics.GetObjectIndex(guids[idx].g);
741 if (objectIndex!=(unsigned int)-1)
742 {
743 statistics.AddValueByIndex(objectIndex,
744 "RN_ACTUAL_BYTES_SENT",
745 (SHValueType) stats[idx].valueOverLastSecond[ACTUAL_BYTES_SENT],
746 curTime, false);
747
748 statistics.AddValueByIndex(objectIndex,
749 "RN_USER_MESSAGE_BYTES_RESENT",
750 (SHValueType) stats[idx].valueOverLastSecond[USER_MESSAGE_BYTES_RESENT],
751 curTime, false);
752
753 statistics.AddValueByIndex(objectIndex,
754 "RN_ACTUAL_BYTES_RECEIVED",
755 (SHValueType) stats[idx].valueOverLastSecond[ACTUAL_BYTES_RECEIVED],
756 curTime, false);
757
758 statistics.AddValueByIndex(objectIndex,
759 "RN_USER_MESSAGE_BYTES_PUSHED",
760 (SHValueType) stats[idx].valueOverLastSecond[USER_MESSAGE_BYTES_PUSHED],
761 curTime, false);
762
763 statistics.AddValueByIndex(objectIndex,
764 "RN_USER_MESSAGE_BYTES_RECEIVED_PROCESSED",
765 (SHValueType) stats[idx].valueOverLastSecond[USER_MESSAGE_BYTES_RECEIVED_PROCESSED],
766 curTime, false);
767
768 statistics.AddValueByIndex(objectIndex,
769 "RN_lastPing",
770 (SHValueType) rakPeerInterface->GetLastPing(guids[idx]),
771 curTime, false);
772
773 statistics.AddValueByIndex(objectIndex,
774 "RN_bytesInResendBuffer",
775 (SHValueType) stats[idx].bytesInResendBuffer,
776 curTime, false);
777
778 statistics.AddValueByIndex(objectIndex,
779 "RN_packetlossLastSecond",
780 (SHValueType) stats[idx].packetlossLastSecond,
781 curTime, false);
782 }
783
784 }
785
786 /*
787 RakNetStatistics rns;
788 DataStructures::List<SystemAddress> addresses;
789 DataStructures::List<RakNetGUID> guids;
790 rakPeerInterface->GetSystemList(addresses, guids);
791 for (unsigned int idx = 0; idx < guids.Size(); idx++)
792 {
793 rakPeerInterface->GetStatistics(remoteSystems[idx], &rns);
794 statistics.AddValue();
795
796 bool AddValue(uint64_t objectId, RakString key, SHValueType val, Time curTime);
797
798 }
799 */
800 }
801 /*
802 void StatisticsHistoryPlugin::OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress)
803 {
804 // Would have to use GetGuidFromSystemAddress for every send
805 }
806 void StatisticsHistoryPlugin::OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress)
807 {
808 }
809 */
810 void StatisticsHistoryPlugin::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
811 {
812 (void) lostConnectionReason;
813 (void) systemAddress;
814
815 if (removeLostConnections)
816 {
817 statistics.RemoveObject(rakNetGUID.g, 0);
818 }
819 }
820 void StatisticsHistoryPlugin::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming)
821 {
822 (void) systemAddress;
823 (void) isIncoming;
824
825 if (addNewConnections)
826 {
827 statistics.AddObject(StatisticsHistory::TrackedObjectData(rakNetGUID.g, newConnectionsObjectType, 0));
828 }
829 }
830 // --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
831
832 #endif // _RAKNET_SUPPORT_StatisticsHistory==1