Qt: bug fixes and enhancement to traffic graph widget (#1429)
* clear trafficgraph on clear button click * set default sample height set default sample height so after clearing traffic graph have some scale * reduce available traffic graph ranges, add optimized graph data storage reduce available traffic graph ranges to 10 (5m,10m,15m,30m,1h,2h,3h,6h,12h,24h), store graph data so range change is possible, data storage contains only necessary data to create graphs for all supported ranges eg. for 10m range storage only half of 10m samples - the second half is calculated from 5m range samples, encapsulate all traffic graph related data into one class * code formatting corrections
This commit is contained in:
parent
28a1d0ecc1
commit
18c83f58e3
@ -149,6 +149,7 @@ BITCOIN_QT_H = \
|
||||
qt/sendcoinsentry.h \
|
||||
qt/signverifymessagedialog.h \
|
||||
qt/splashscreen.h \
|
||||
qt/trafficgraphdata.h \
|
||||
qt/trafficgraphwidget.h \
|
||||
qt/transactiondesc.h \
|
||||
qt/transactiondescdialog.h \
|
||||
@ -379,6 +380,7 @@ BITCOIN_QT_CPP = \
|
||||
qt/qvaluecombobox.cpp \
|
||||
qt/rpcconsole.cpp \
|
||||
qt/splashscreen.cpp \
|
||||
qt/trafficgraphdata.cpp \
|
||||
qt/trafficgraphwidget.cpp \
|
||||
qt/utilitydialog.cpp
|
||||
|
||||
|
@ -3,7 +3,8 @@ TESTS += qt/test/test_dash-qt
|
||||
|
||||
TEST_QT_MOC_CPP = \
|
||||
qt/test/moc_compattests.cpp \
|
||||
qt/test/moc_uritests.cpp
|
||||
qt/test/moc_trafficgraphdatatests.cpp \
|
||||
qt/test/moc_uritests.cpp
|
||||
|
||||
if ENABLE_WALLET
|
||||
TEST_QT_MOC_CPP += qt/test/moc_paymentservertests.cpp
|
||||
@ -13,7 +14,8 @@ TEST_QT_H = \
|
||||
qt/test/compattests.h \
|
||||
qt/test/uritests.h \
|
||||
qt/test/paymentrequestdata.h \
|
||||
qt/test/paymentservertests.h
|
||||
qt/test/paymentservertests.h \
|
||||
qt/test/trafficgraphdatatests.h
|
||||
|
||||
qt_test_test_dash_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \
|
||||
$(QT_INCLUDES) $(QT_TEST_INCLUDES) $(PROTOBUF_CFLAGS)
|
||||
@ -22,6 +24,7 @@ qt_test_test_dash_qt_SOURCES = \
|
||||
qt/test/compattests.cpp \
|
||||
qt/test/test_main.cpp \
|
||||
qt/test/uritests.cpp \
|
||||
qt/test/trafficgraphdatatests.cpp \
|
||||
$(TEST_QT_H)
|
||||
if ENABLE_WALLET
|
||||
qt_test_test_dash_qt_SOURCES += \
|
||||
|
@ -524,16 +524,16 @@
|
||||
<item>
|
||||
<widget class="QSlider" name="sldGraphRange">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>288</number>
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>12</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>6</number>
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
|
@ -45,7 +45,7 @@
|
||||
const int CONSOLE_HISTORY = 50;
|
||||
const QSize ICON_SIZE(24, 24);
|
||||
|
||||
const int INITIAL_TRAFFIC_GRAPH_MINS = 30;
|
||||
const TrafficGraphData::GraphRange INITIAL_TRAFFIC_GRAPH_SETTING = TrafficGraphData::Range_30m;
|
||||
|
||||
// Repair parameters
|
||||
const QString SALVAGEWALLET("-salvagewallet");
|
||||
@ -298,7 +298,7 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) :
|
||||
RPCRegisterTimerInterface(rpcTimerInterface);
|
||||
|
||||
startExecutor();
|
||||
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
|
||||
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_SETTING);
|
||||
|
||||
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
|
||||
|
||||
@ -732,9 +732,7 @@ void RPCConsole::scrollToEnd()
|
||||
|
||||
void RPCConsole::on_sldGraphRange_valueChanged(int value)
|
||||
{
|
||||
const int multiplier = 5; // each position on the slider represents 5 min
|
||||
int mins = value * multiplier;
|
||||
setTrafficGraphRange(mins);
|
||||
setTrafficGraphRange(static_cast<TrafficGraphData::GraphRange>(value));
|
||||
}
|
||||
|
||||
QString RPCConsole::FormatBytes(quint64 bytes)
|
||||
@ -749,10 +747,10 @@ QString RPCConsole::FormatBytes(quint64 bytes)
|
||||
return QString(tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024);
|
||||
}
|
||||
|
||||
void RPCConsole::setTrafficGraphRange(int mins)
|
||||
void RPCConsole::setTrafficGraphRange(TrafficGraphData::GraphRange range)
|
||||
{
|
||||
ui->trafficGraph->setGraphRangeMins(mins);
|
||||
ui->lblGraphRange->setText(GUIUtil::formatDurationStr(mins * 60));
|
||||
ui->trafficGraph->setGraphRangeMins(range);
|
||||
ui->lblGraphRange->setText(GUIUtil::formatDurationStr(TrafficGraphData::RangeMinutes[range] * 60));
|
||||
}
|
||||
|
||||
void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "guiutil.h"
|
||||
#include "peertablemodel.h"
|
||||
#include "trafficgraphdata.h"
|
||||
|
||||
#include "net.h"
|
||||
|
||||
@ -126,7 +127,7 @@ Q_SIGNALS:
|
||||
private:
|
||||
static QString FormatBytes(quint64 bytes);
|
||||
void startExecutor();
|
||||
void setTrafficGraphRange(int mins);
|
||||
void setTrafficGraphRange(TrafficGraphData::GraphRange range);
|
||||
/** Build parameter list for restart */
|
||||
void buildParameterlist(QString arg);
|
||||
/** show detailed information on ui about selected node */
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "util.h"
|
||||
#include "uritests.h"
|
||||
#include "compattests.h"
|
||||
#include "trafficgraphdatatests.h"
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
#include "paymentservertests.h"
|
||||
@ -54,5 +55,9 @@ int main(int argc, char *argv[])
|
||||
if (QTest::qExec(&test4) != 0)
|
||||
fInvalid = true;
|
||||
|
||||
TrafficGraphDataTests test5;
|
||||
if (QTest::qExec(&test5) != 0)
|
||||
fInvalid = true;
|
||||
|
||||
return fInvalid;
|
||||
}
|
||||
|
174
src/qt/test/trafficgraphdatatests.cpp
Normal file
174
src/qt/test/trafficgraphdatatests.cpp
Normal file
@ -0,0 +1,174 @@
|
||||
#include "trafficgraphdatatests.h"
|
||||
#include "../trafficgraphdata.h"
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <QTime>
|
||||
|
||||
void TrafficGraphDataTests::simpleCurrentSampleQueueTests()
|
||||
{
|
||||
TrafficGraphData trafficGraphData(TrafficGraphData::Range_5m);
|
||||
for (int i = 0; i < TrafficGraphData::DESIRED_DATA_SAMPLES; i++)
|
||||
QVERIFY(trafficGraphData.update(TrafficSample(i, i)));
|
||||
|
||||
TrafficGraphData::SampleQueue queue = trafficGraphData.getCurrentRangeQueue();
|
||||
QCOMPARE(queue.size(), TrafficGraphData::DESIRED_DATA_SAMPLES);
|
||||
for (int i = 0; i < TrafficGraphData::DESIRED_DATA_SAMPLES; i++){
|
||||
QCOMPARE((int)queue.at(i).in, TrafficGraphData::DESIRED_DATA_SAMPLES - i - 1);
|
||||
QCOMPARE((int)queue.at(i).out, TrafficGraphData::DESIRED_DATA_SAMPLES - i - 1);
|
||||
}
|
||||
|
||||
QVERIFY(trafficGraphData.update(TrafficSample(0, 0)));
|
||||
queue = trafficGraphData.getCurrentRangeQueue();
|
||||
QCOMPARE(queue.size(), TrafficGraphData::DESIRED_DATA_SAMPLES);
|
||||
QCOMPARE((int)queue.at(0).in, 0);
|
||||
QCOMPARE((int)queue.at(0).out, 0);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void checkQueue(const TrafficGraphData::SampleQueue& queue, int value)
|
||||
{
|
||||
for (int i = 0; i < queue.size(); i++){
|
||||
std::ostringstream oss;
|
||||
oss<< "i:" << i << " value:" << value << " actual:" << queue.at(i).in;
|
||||
QVERIFY2(value == (int)queue.at(i).in, oss.str().c_str());
|
||||
QVERIFY2(value == (int)queue.at(i).out, oss.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void testQueueFill(TrafficGraphData::GraphRange range, int multiplier)
|
||||
{
|
||||
int size = 10000;
|
||||
TrafficGraphData trafficGraphData(range);
|
||||
for (int i = 1; i <= size; i++){
|
||||
bool result = trafficGraphData.update(TrafficSample(1, 1));
|
||||
std::ostringstream oss;
|
||||
oss<< "result:" << result << " multiplier:" << multiplier << " i:" << i << " range:" << range;
|
||||
if (i == 1){
|
||||
if (range == TrafficGraphData::Range_5m)
|
||||
QVERIFY2(result, oss.str().c_str());
|
||||
else
|
||||
QVERIFY2(!result, oss.str().c_str());
|
||||
}
|
||||
else if (i % multiplier == 0){
|
||||
QVERIFY2(result, oss.str().c_str());
|
||||
}
|
||||
else {
|
||||
QVERIFY2(!result, oss.str().c_str());
|
||||
}
|
||||
}
|
||||
TrafficGraphData::SampleQueue queue = trafficGraphData.getCurrentRangeQueue();
|
||||
QCOMPARE(queue.size(), std::min(size / multiplier, TrafficGraphData::DESIRED_DATA_SAMPLES));
|
||||
checkQueue(queue, multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
void TrafficGraphDataTests::accumulationCurrentSampleQueueTests()
|
||||
{
|
||||
testQueueFill(TrafficGraphData::Range_10m, 2);
|
||||
testQueueFill(TrafficGraphData::Range_15m, 3);
|
||||
testQueueFill(TrafficGraphData::Range_30m, 6);
|
||||
testQueueFill(TrafficGraphData::Range_1h, 12);
|
||||
testQueueFill(TrafficGraphData::Range_2h, 24);
|
||||
testQueueFill(TrafficGraphData::Range_3h, 36);
|
||||
testQueueFill(TrafficGraphData::Range_6h, 72);
|
||||
testQueueFill(TrafficGraphData::Range_12h, 144);
|
||||
testQueueFill(TrafficGraphData::Range_24h, 288);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void checkRange(TrafficGraphData& trafficGraphData, int size, TrafficGraphData::GraphRange toRange, int multiplier)
|
||||
{
|
||||
TrafficGraphData::SampleQueue queue = trafficGraphData.getRangeQueue(toRange);
|
||||
QCOMPARE(queue.size(), std::min(size / multiplier, TrafficGraphData::DESIRED_DATA_SAMPLES));
|
||||
checkQueue(queue,multiplier);
|
||||
}
|
||||
|
||||
void testQueueFillAndCheckRangesForSize(int size)
|
||||
{
|
||||
TrafficGraphData trafficGraphData(TrafficGraphData::Range_5m);
|
||||
for (int i = 1; i <= size; i++){
|
||||
trafficGraphData.update(TrafficSample(1, 1));
|
||||
}
|
||||
|
||||
checkRange(trafficGraphData, size, TrafficGraphData::Range_10m, 2);
|
||||
checkRange(trafficGraphData, size, TrafficGraphData::Range_15m, 3);
|
||||
checkRange(trafficGraphData, size, TrafficGraphData::Range_30m, 6);
|
||||
checkRange(trafficGraphData, size, TrafficGraphData::Range_1h, 12);
|
||||
checkRange(trafficGraphData, size, TrafficGraphData::Range_2h, 24);
|
||||
checkRange(trafficGraphData, size, TrafficGraphData::Range_3h, 36);
|
||||
checkRange(trafficGraphData, size, TrafficGraphData::Range_6h, 72);
|
||||
checkRange(trafficGraphData, size, TrafficGraphData::Range_12h, 144);
|
||||
checkRange(trafficGraphData, size, TrafficGraphData::Range_24h, 288);
|
||||
}
|
||||
}
|
||||
|
||||
void TrafficGraphDataTests::getRangeTests()
|
||||
{
|
||||
testQueueFillAndCheckRangesForSize(TrafficGraphData::DESIRED_DATA_SAMPLES);
|
||||
testQueueFillAndCheckRangesForSize(TrafficGraphData::DESIRED_DATA_SAMPLES * 2);
|
||||
testQueueFillAndCheckRangesForSize(TrafficGraphData::DESIRED_DATA_SAMPLES * 3);
|
||||
testQueueFillAndCheckRangesForSize(TrafficGraphData::DESIRED_DATA_SAMPLES * 6);
|
||||
testQueueFillAndCheckRangesForSize(TrafficGraphData::DESIRED_DATA_SAMPLES * 12);
|
||||
testQueueFillAndCheckRangesForSize(TrafficGraphData::DESIRED_DATA_SAMPLES * 24);
|
||||
testQueueFillAndCheckRangesForSize(TrafficGraphData::DESIRED_DATA_SAMPLES * 36);
|
||||
testQueueFillAndCheckRangesForSize(TrafficGraphData::DESIRED_DATA_SAMPLES * 72);
|
||||
testQueueFillAndCheckRangesForSize(TrafficGraphData::DESIRED_DATA_SAMPLES * 144);
|
||||
testQueueFillAndCheckRangesForSize(TrafficGraphData::DESIRED_DATA_SAMPLES * 288);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void compareQueues(const TrafficGraphData::SampleQueue& expected, const TrafficGraphData::SampleQueue& actual)
|
||||
{
|
||||
QCOMPARE(expected.size(), actual.size());
|
||||
for (int i = 0; i < expected.size(); i++){
|
||||
std::ostringstream oss;
|
||||
oss<< "i:" << i << " expected:" << expected.at(i).in << " actual:" << actual.at(i).in;
|
||||
QVERIFY2((int)expected.at(i).in == (int)actual.at(i).in, oss.str().c_str());
|
||||
QVERIFY2((int)expected.at(i).out == (int)actual.at(i).out, oss.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void testRangeSwitch(TrafficGraphData::GraphRange baseRange, TrafficGraphData::GraphRange toRange,int size)
|
||||
{
|
||||
QTime time = QTime::currentTime();
|
||||
qsrand((uint)time.msec());
|
||||
TrafficGraphData trafficGraphDataBase(baseRange);
|
||||
TrafficGraphData trafficGraphData(toRange);
|
||||
for (int i = 1; i <= size; i++){
|
||||
int in = qrand() % 1000;
|
||||
int out = qrand() % 1000;
|
||||
trafficGraphData.update(TrafficSample(in, out));
|
||||
trafficGraphDataBase.update(TrafficSample(in, out));
|
||||
}
|
||||
trafficGraphDataBase.switchRange(toRange);
|
||||
compareQueues(trafficGraphData.getCurrentRangeQueue(),trafficGraphDataBase.getCurrentRangeQueue());
|
||||
}
|
||||
}
|
||||
|
||||
void TrafficGraphDataTests::switchRangeTests()
|
||||
{
|
||||
testRangeSwitch(TrafficGraphData::Range_5m, TrafficGraphData::Range_10m, 10000);
|
||||
testRangeSwitch(TrafficGraphData::Range_5m, TrafficGraphData::Range_30m, 20000);
|
||||
testRangeSwitch(TrafficGraphData::Range_5m, TrafficGraphData::Range_15m, 8000 * 2 - 1);
|
||||
testRangeSwitch(TrafficGraphData::Range_5m, TrafficGraphData::Range_24h, 8000 * 288 - 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TrafficGraphDataTests::clearTests()
|
||||
{
|
||||
TrafficGraphData trafficGraphData(TrafficGraphData::Range_5m);
|
||||
for (int i = 1; i <= TrafficGraphData::DESIRED_DATA_SAMPLES; i++){
|
||||
trafficGraphData.update(TrafficSample(1, 1));
|
||||
}
|
||||
QCOMPARE(trafficGraphData.getCurrentRangeQueue().size(),TrafficGraphData::DESIRED_DATA_SAMPLES);
|
||||
trafficGraphData.clear();
|
||||
QCOMPARE(trafficGraphData.getCurrentRangeQueue().size(), 0);
|
||||
for (int i = 1; i <= TrafficGraphData::DESIRED_DATA_SAMPLES; i++){
|
||||
trafficGraphData.update(TrafficSample(1, 1));
|
||||
}
|
||||
QCOMPARE(trafficGraphData.getCurrentRangeQueue().size(), TrafficGraphData::DESIRED_DATA_SAMPLES);
|
||||
}
|
20
src/qt/test/trafficgraphdatatests.h
Normal file
20
src/qt/test/trafficgraphdatatests.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef TRAFFICGRAPHDATATESTS_H
|
||||
#define TRAFFICGRAPHDATATESTS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
|
||||
class TrafficGraphDataTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void simpleCurrentSampleQueueTests();
|
||||
void accumulationCurrentSampleQueueTests();
|
||||
void getRangeTests();
|
||||
void switchRangeTests();
|
||||
void clearTests();
|
||||
};
|
||||
|
||||
|
||||
#endif // TRAFFICGRAPHDATATESTS_H
|
248
src/qt/trafficgraphdata.cpp
Normal file
248
src/qt/trafficgraphdata.cpp
Normal file
@ -0,0 +1,248 @@
|
||||
#include <trafficgraphdata.h>
|
||||
|
||||
const int TrafficGraphData::RangeMinutes[] = {5,10,15,30,60,120,180,360,720,1440};
|
||||
const int TrafficGraphData::DESIRED_DATA_SAMPLES = 800;
|
||||
const int TrafficGraphData::DesiredQueueSizes[] = {
|
||||
TrafficGraphData::DESIRED_DATA_SAMPLES, //Range_5m
|
||||
TrafficGraphData::DESIRED_DATA_SAMPLES/2, //Range_10m
|
||||
TrafficGraphData::DESIRED_DATA_SAMPLES*2/3, //Range_15m
|
||||
TrafficGraphData::DESIRED_DATA_SAMPLES/2, //Range_30m
|
||||
TrafficGraphData::DESIRED_DATA_SAMPLES/2, //Range_1h
|
||||
TrafficGraphData::DESIRED_DATA_SAMPLES/2, //Range_2h
|
||||
TrafficGraphData::DESIRED_DATA_SAMPLES*2/3, //Range_3h
|
||||
TrafficGraphData::DESIRED_DATA_SAMPLES/2, //Range_6h
|
||||
TrafficGraphData::DESIRED_DATA_SAMPLES/2, //Range_12h
|
||||
TrafficGraphData::DESIRED_DATA_SAMPLES/2, //Range_24h
|
||||
} ;
|
||||
|
||||
const int TrafficGraphData::SMALLEST_SAMPLE_PERIOD =
|
||||
TrafficGraphData::RangeMinutes[TrafficGraphData::Range_5m] * 60 * 1000 / TrafficGraphData::DESIRED_DATA_SAMPLES;
|
||||
|
||||
TrafficGraphData::TrafficGraphData(GraphRange range)
|
||||
:currentGraphRange(range),
|
||||
currentSampleCounter(0),
|
||||
nLastBytesIn(0),
|
||||
nLastBytesOut(0)
|
||||
{
|
||||
}
|
||||
|
||||
void TrafficGraphData::tryAddingSampleToStash(GraphRange range)
|
||||
{
|
||||
SampleQueue& queue = sampleMap[range];
|
||||
if (queue.size() > DesiredQueueSizes[range]){
|
||||
sampleStash[range].push_front(queue.at(DesiredQueueSizes[range]));
|
||||
}
|
||||
}
|
||||
|
||||
void TrafficGraphData::tryUpdateNextWithLast2Samples(GraphRange range, GraphRange nextRange)
|
||||
{
|
||||
SampleQueue& queue = sampleMap[range];
|
||||
if (queue.size() == DesiredQueueSizes[range] + 2){
|
||||
update(nextRange, queue.takeLast() + queue.takeLast());
|
||||
}
|
||||
}
|
||||
|
||||
void TrafficGraphData::tryUpdateNextWithLast3Samples(GraphRange range, GraphRange nextRange)
|
||||
{
|
||||
SampleQueue& stashQueue = sampleStash[range];
|
||||
if (stashQueue.size() == 3){
|
||||
update(nextRange, stashQueue.takeLast() + stashQueue.takeLast() + stashQueue.takeLast());
|
||||
}
|
||||
}
|
||||
|
||||
void TrafficGraphData::setLastBytes(quint64 nLastBytesIn, quint64 nLastBytesOut)
|
||||
{
|
||||
this->nLastBytesIn = nLastBytesIn;
|
||||
this->nLastBytesOut = nLastBytesOut;
|
||||
}
|
||||
|
||||
bool TrafficGraphData::update(quint64 totalBytesRecv, quint64 totalBytesSent)
|
||||
{
|
||||
float inRate = (totalBytesRecv - nLastBytesIn) / 1024.0f * 1000 / SMALLEST_SAMPLE_PERIOD;
|
||||
float outRate = (totalBytesSent - nLastBytesOut) / 1024.0f * 1000 / SMALLEST_SAMPLE_PERIOD;
|
||||
nLastBytesIn = totalBytesRecv;
|
||||
nLastBytesOut = totalBytesSent;
|
||||
return update(TrafficSample(inRate,outRate));
|
||||
}
|
||||
|
||||
bool TrafficGraphData::update(const TrafficSample& trafficSample)
|
||||
{
|
||||
update(Range_5m, trafficSample);
|
||||
|
||||
currentSampleCounter++;
|
||||
|
||||
if (RangeMinutes[currentGraphRange]/RangeMinutes[Range_5m] == currentSampleCounter){
|
||||
currentSampleCounter = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TrafficGraphData::update(GraphRange range, const TrafficSample& trafficSample)
|
||||
{
|
||||
SampleQueue& queue = sampleMap[range];
|
||||
queue.push_front(trafficSample);
|
||||
|
||||
switch(range){
|
||||
case Range_5m:
|
||||
tryAddingSampleToStash(Range_5m);
|
||||
tryUpdateNextWithLast2Samples(Range_5m, Range_10m);
|
||||
tryUpdateNextWithLast3Samples(Range_5m, Range_15m);
|
||||
return;
|
||||
case Range_15m:
|
||||
tryUpdateNextWithLast2Samples(Range_15m, Range_30m);
|
||||
return;
|
||||
case Range_30m:
|
||||
tryUpdateNextWithLast2Samples(Range_30m, Range_1h);
|
||||
return;
|
||||
case Range_1h:
|
||||
tryAddingSampleToStash(Range_1h);
|
||||
tryUpdateNextWithLast2Samples(Range_1h, Range_2h);
|
||||
tryUpdateNextWithLast3Samples(Range_1h, Range_3h);
|
||||
return;
|
||||
case Range_3h:
|
||||
tryUpdateNextWithLast2Samples(Range_3h, Range_6h);
|
||||
return;
|
||||
case Range_6h:
|
||||
tryUpdateNextWithLast2Samples( Range_6h, Range_12h);
|
||||
return;
|
||||
case Range_12h:
|
||||
tryUpdateNextWithLast2Samples(Range_12h, Range_24h);
|
||||
return;
|
||||
default:
|
||||
if (queue.size() > DesiredQueueSizes[range])
|
||||
queue.removeLast();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TrafficGraphData::switchRange(GraphRange newRange)
|
||||
{
|
||||
currentGraphRange = newRange;
|
||||
currentSampleCounter = 0;
|
||||
}
|
||||
|
||||
TrafficGraphData::SampleQueue TrafficGraphData::sumEach2Samples(const SampleQueue& rangeQueue)
|
||||
{
|
||||
SampleQueue result;
|
||||
int i = rangeQueue.size() - 1;
|
||||
|
||||
while (i - 1 >= 0){
|
||||
result.push_front(rangeQueue.at(i)+ rangeQueue.at(i-1));
|
||||
i -= 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TrafficGraphData::SampleQueue TrafficGraphData::sumEach3Samples(const SampleQueue& rangeQueue, GraphRange range)
|
||||
{
|
||||
SampleQueue result;
|
||||
int lastUnusedSample = std::min(rangeQueue.size()-1 ,DESIRED_DATA_SAMPLES-1);
|
||||
|
||||
// use stash first
|
||||
SampleQueue& stashQueue = sampleStash[range];
|
||||
TrafficSample sum(0,0);
|
||||
for (int i = 0; i < stashQueue.size(); i++){
|
||||
sum+=stashQueue.at(i);
|
||||
}
|
||||
int toFullSample = 3 - stashQueue.size();
|
||||
if (toFullSample > rangeQueue.size())
|
||||
return result;
|
||||
|
||||
// append to stash data to create whole sample
|
||||
for (int i = 0; i < toFullSample; i++){
|
||||
sum+=rangeQueue.at(lastUnusedSample);
|
||||
lastUnusedSample--;
|
||||
}
|
||||
result.push_front(sum);
|
||||
|
||||
while (lastUnusedSample - 2 >= 0){
|
||||
result.push_front(rangeQueue.at(lastUnusedSample) + rangeQueue.at(lastUnusedSample-1) + rangeQueue.at(lastUnusedSample-2));
|
||||
lastUnusedSample -= 3;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TrafficGraphData::SampleQueue TrafficGraphData::getRangeQueue(GraphRange range)
|
||||
{
|
||||
switch(range){
|
||||
case Range_5m:
|
||||
return sampleMap[Range_5m];
|
||||
case Range_10m:
|
||||
{
|
||||
SampleQueue queue = sumEach2Samples(getRangeQueue(Range_5m));
|
||||
queue.append(sampleMap[Range_10m]);
|
||||
return queue;
|
||||
}
|
||||
case Range_15m:
|
||||
{
|
||||
SampleQueue queue = sumEach3Samples(getRangeQueue(Range_5m), Range_5m);
|
||||
queue.append(sampleMap[Range_15m]);
|
||||
return queue;
|
||||
}
|
||||
case Range_30m:
|
||||
{
|
||||
SampleQueue queue = sumEach2Samples(getRangeQueue(Range_15m));
|
||||
queue.append(sampleMap[Range_30m]);
|
||||
return queue;
|
||||
}
|
||||
case Range_1h:
|
||||
{
|
||||
SampleQueue queue = sumEach2Samples(getRangeQueue(Range_30m));
|
||||
queue.append(sampleMap[Range_1h]);
|
||||
return queue;
|
||||
}
|
||||
case Range_2h:
|
||||
{
|
||||
SampleQueue queue = sumEach2Samples(getRangeQueue(Range_1h));
|
||||
queue.append(sampleMap[Range_2h]);
|
||||
return queue;
|
||||
}
|
||||
case Range_3h:
|
||||
{
|
||||
SampleQueue queue = sumEach3Samples(getRangeQueue(Range_1h),Range_1h);
|
||||
queue.append(sampleMap[Range_3h]);
|
||||
return queue;
|
||||
}
|
||||
case Range_6h:
|
||||
{
|
||||
SampleQueue queue = sumEach2Samples(getRangeQueue(Range_3h));
|
||||
queue.append(sampleMap[Range_6h]);
|
||||
return queue;
|
||||
}
|
||||
case Range_12h:
|
||||
{
|
||||
SampleQueue queue = sumEach2Samples(getRangeQueue(Range_6h));
|
||||
queue.append(sampleMap[Range_12h]);
|
||||
return queue;
|
||||
}
|
||||
case Range_24h:
|
||||
{
|
||||
SampleQueue queue = sumEach2Samples(getRangeQueue(Range_12h));
|
||||
queue.append(sampleMap[Range_24h]);
|
||||
return queue;
|
||||
};
|
||||
default:
|
||||
return SampleQueue();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
TrafficGraphData::SampleQueue TrafficGraphData::getCurrentRangeQueue()
|
||||
{
|
||||
SampleQueue newQueue;
|
||||
getRangeQueue(currentGraphRange).mid(0,DESIRED_DATA_SAMPLES).swap(newQueue);
|
||||
return newQueue;
|
||||
}
|
||||
|
||||
void TrafficGraphData::clear()
|
||||
{
|
||||
sampleMap.clear();
|
||||
sampleMap.clear();
|
||||
currentSampleCounter = 0;
|
||||
nLastBytesIn = 0;
|
||||
nLastBytesOut = 0;
|
||||
}
|
||||
|
||||
|
90
src/qt/trafficgraphdata.h
Normal file
90
src/qt/trafficgraphdata.h
Normal file
@ -0,0 +1,90 @@
|
||||
#ifndef TRAFFICGRAPHDATA_H
|
||||
#define TRAFFICGRAPHDATA_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QQueue>
|
||||
|
||||
struct TrafficSample
|
||||
{
|
||||
float in;
|
||||
float out;
|
||||
|
||||
TrafficSample(float in, float out)
|
||||
{
|
||||
this->in = in;
|
||||
this->out = out;
|
||||
}
|
||||
|
||||
TrafficSample operator+(const TrafficSample& other) const
|
||||
{
|
||||
return TrafficSample(this->in + other.in, this->out + other.out);
|
||||
}
|
||||
|
||||
TrafficSample& operator+=(const TrafficSample& rhs)
|
||||
{
|
||||
this->in += rhs.in;
|
||||
this->out += rhs.out;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class TrafficGraphData
|
||||
{
|
||||
public:
|
||||
enum GraphRange
|
||||
{
|
||||
Range_5m,
|
||||
Range_10m,
|
||||
Range_15m,
|
||||
Range_30m,
|
||||
Range_1h,
|
||||
Range_2h,
|
||||
Range_3h,
|
||||
Range_6h,
|
||||
Range_12h,
|
||||
Range_24h,
|
||||
};
|
||||
|
||||
static const int RangeMinutes[];
|
||||
static const int DESIRED_DATA_SAMPLES;
|
||||
static const int SMALLEST_SAMPLE_PERIOD;
|
||||
|
||||
typedef QQueue<TrafficSample> SampleQueue;
|
||||
typedef QHash<GraphRange,SampleQueue> SampleQueueMap;
|
||||
|
||||
TrafficGraphData(GraphRange range);
|
||||
bool update(const TrafficSample& trafficSample);
|
||||
bool update(quint64 totalBytesRecv, quint64 totalBytesSent);
|
||||
void switchRange(GraphRange newRange);
|
||||
SampleQueue getRangeQueue(GraphRange range);
|
||||
SampleQueue getCurrentRangeQueue();
|
||||
void clear();
|
||||
void setLastBytes(quint64 nLastBytesIn, quint64 nLastBytesOut);
|
||||
|
||||
private:
|
||||
static const int DesiredQueueSizes[];
|
||||
|
||||
SampleQueueMap sampleMap;
|
||||
SampleQueueMap sampleStash;
|
||||
|
||||
GraphRange currentGraphRange;
|
||||
int currentSampleCounter;
|
||||
|
||||
quint64 nLastBytesIn;
|
||||
quint64 nLastBytesOut;
|
||||
|
||||
void update(GraphRange range, const TrafficSample &trafficSample);
|
||||
|
||||
void tryAddingSampleToStash(GraphRange range);
|
||||
void tryUpdateNextWithLast2Samples(GraphRange range, GraphRange nextRange);
|
||||
void tryUpdateNextWithLast3Samples(GraphRange range, GraphRange nextRange);
|
||||
|
||||
SampleQueue sumEach2Samples(const SampleQueue &rangeQueue);
|
||||
SampleQueue sumEach3Samples(const SampleQueue &rangeQueue, GraphRange range);
|
||||
|
||||
|
||||
TrafficGraphData(const TrafficGraphData& that);
|
||||
TrafficGraphData& operator=(TrafficGraphData const&);
|
||||
};
|
||||
|
||||
#endif // TRAFFICGRAPHDATA_H
|
@ -5,38 +5,38 @@
|
||||
#include "trafficgraphwidget.h"
|
||||
#include "clientmodel.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <QPainter>
|
||||
#include <QColor>
|
||||
#include <QTimer>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#define DESIRED_SAMPLES 800
|
||||
|
||||
#define XMARGIN 10
|
||||
#define YMARGIN 10
|
||||
|
||||
#define DEFAULT_SAMPLE_HEIGHT 1.1f
|
||||
|
||||
TrafficGraphWidget::TrafficGraphWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
timer(0),
|
||||
fMax(0.0f),
|
||||
fMax(DEFAULT_SAMPLE_HEIGHT),
|
||||
nMins(0),
|
||||
vSamplesIn(),
|
||||
vSamplesOut(),
|
||||
nLastBytesIn(0),
|
||||
nLastBytesOut(0),
|
||||
clientModel(0)
|
||||
clientModel(0),
|
||||
trafficGraphData(TrafficGraphData::Range_30m)
|
||||
{
|
||||
timer = new QTimer(this);
|
||||
connect(timer, SIGNAL(timeout()), SLOT(updateRates()));
|
||||
timer->setInterval(TrafficGraphData::SMALLEST_SAMPLE_PERIOD);
|
||||
timer->start();
|
||||
}
|
||||
|
||||
void TrafficGraphWidget::setClientModel(ClientModel *model)
|
||||
{
|
||||
clientModel = model;
|
||||
if(model) {
|
||||
nLastBytesIn = model->getTotalBytesRecv();
|
||||
nLastBytesOut = model->getTotalBytesSent();
|
||||
trafficGraphData.setLastBytes(model->getTotalBytesRecv(), model->getTotalBytesSent());
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,21 +45,34 @@ int TrafficGraphWidget::getGraphRangeMins() const
|
||||
return nMins;
|
||||
}
|
||||
|
||||
void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue<float> &samples)
|
||||
|
||||
void TrafficGraphWidget::paintPath(QPainterPath &path, const TrafficGraphData::SampleQueue &queue, SampleChooser chooser)
|
||||
{
|
||||
int h = height() - YMARGIN * 2, w = width() - XMARGIN * 2;
|
||||
int sampleCount = samples.size(), x = XMARGIN + w, y;
|
||||
int sampleCount = queue.size(), x = XMARGIN + w, y;
|
||||
if(sampleCount > 0) {
|
||||
path.moveTo(x, YMARGIN + h);
|
||||
for(int i = 0; i < sampleCount; ++i) {
|
||||
x = XMARGIN + w - w * i / DESIRED_SAMPLES;
|
||||
y = YMARGIN + h - (int)(h * samples.at(i) / fMax);
|
||||
x = XMARGIN + w - w * i / TrafficGraphData::DESIRED_DATA_SAMPLES;
|
||||
y = YMARGIN + h - (int)(h * chooser(queue.at(i)) / fMax);
|
||||
path.lineTo(x, y);
|
||||
}
|
||||
path.lineTo(x, YMARGIN + h);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
float chooseIn(const TrafficSample& sample)
|
||||
{
|
||||
return sample.in;
|
||||
}
|
||||
float chooseOut(const TrafficSample& sample)
|
||||
{
|
||||
return sample.out;
|
||||
}
|
||||
}
|
||||
|
||||
void TrafficGraphWidget::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
@ -102,19 +115,20 @@ void TrafficGraphWidget::paintEvent(QPaintEvent *)
|
||||
}
|
||||
}
|
||||
|
||||
if(!vSamplesIn.empty()) {
|
||||
QPainterPath p;
|
||||
paintPath(p, vSamplesIn);
|
||||
painter.fillPath(p, QColor(0, 255, 0, 128));
|
||||
const TrafficGraphData::SampleQueue& queue = trafficGraphData.getCurrentRangeQueue();
|
||||
|
||||
if(!queue.empty()) {
|
||||
QPainterPath pIn;
|
||||
paintPath(pIn, queue, boost::bind(chooseIn,_1));
|
||||
painter.fillPath(pIn, QColor(0, 255, 0, 128));
|
||||
painter.setPen(Qt::green);
|
||||
painter.drawPath(p);
|
||||
}
|
||||
if(!vSamplesOut.empty()) {
|
||||
QPainterPath p;
|
||||
paintPath(p, vSamplesOut);
|
||||
painter.fillPath(p, QColor(255, 0, 0, 128));
|
||||
painter.drawPath(pIn);
|
||||
|
||||
QPainterPath pOut;
|
||||
paintPath(pOut, queue, boost::bind(chooseOut,_1));
|
||||
painter.fillPath(pOut, QColor(255, 0, 0, 128));
|
||||
painter.setPen(Qt::red);
|
||||
painter.drawPath(p);
|
||||
painter.drawPath(pOut);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,54 +136,31 @@ void TrafficGraphWidget::updateRates()
|
||||
{
|
||||
if(!clientModel) return;
|
||||
|
||||
quint64 bytesIn = clientModel->getTotalBytesRecv(),
|
||||
bytesOut = clientModel->getTotalBytesSent();
|
||||
float inRate = (bytesIn - nLastBytesIn) / 1024.0f * 1000 / timer->interval();
|
||||
float outRate = (bytesOut - nLastBytesOut) / 1024.0f * 1000 / timer->interval();
|
||||
vSamplesIn.push_front(inRate);
|
||||
vSamplesOut.push_front(outRate);
|
||||
nLastBytesIn = bytesIn;
|
||||
nLastBytesOut = bytesOut;
|
||||
bool updated = trafficGraphData.update(clientModel->getTotalBytesRecv(),clientModel->getTotalBytesSent());
|
||||
|
||||
while(vSamplesIn.size() > DESIRED_SAMPLES) {
|
||||
vSamplesIn.pop_back();
|
||||
if (updated){
|
||||
float tmax = DEFAULT_SAMPLE_HEIGHT;
|
||||
Q_FOREACH(const TrafficSample& sample, trafficGraphData.getCurrentRangeQueue()) {
|
||||
if(sample.in > tmax) tmax = sample.in;
|
||||
if(sample.out > tmax) tmax = sample.out;
|
||||
}
|
||||
fMax = tmax;
|
||||
update();
|
||||
}
|
||||
while(vSamplesOut.size() > DESIRED_SAMPLES) {
|
||||
vSamplesOut.pop_back();
|
||||
}
|
||||
|
||||
float tmax = 0.0f;
|
||||
Q_FOREACH(float f, vSamplesIn) {
|
||||
if(f > tmax) tmax = f;
|
||||
}
|
||||
Q_FOREACH(float f, vSamplesOut) {
|
||||
if(f > tmax) tmax = f;
|
||||
}
|
||||
fMax = tmax;
|
||||
update();
|
||||
}
|
||||
|
||||
void TrafficGraphWidget::setGraphRangeMins(int mins)
|
||||
void TrafficGraphWidget::setGraphRangeMins(int value)
|
||||
{
|
||||
nMins = mins;
|
||||
int msecsPerSample = nMins * 60 * 1000 / DESIRED_SAMPLES;
|
||||
timer->stop();
|
||||
timer->setInterval(msecsPerSample);
|
||||
|
||||
clear();
|
||||
trafficGraphData.switchRange(static_cast<TrafficGraphData::GraphRange>(value));
|
||||
update();
|
||||
}
|
||||
|
||||
void TrafficGraphWidget::clear()
|
||||
{
|
||||
timer->stop();
|
||||
|
||||
vSamplesOut.clear();
|
||||
vSamplesIn.clear();
|
||||
fMax = 0.0f;
|
||||
|
||||
trafficGraphData.clear();
|
||||
fMax = DEFAULT_SAMPLE_HEIGHT;
|
||||
if(clientModel) {
|
||||
nLastBytesIn = clientModel->getTotalBytesRecv();
|
||||
nLastBytesOut = clientModel->getTotalBytesSent();
|
||||
trafficGraphData.setLastBytes(clientModel->getTotalBytesRecv(), clientModel->getTotalBytesSent());
|
||||
}
|
||||
timer->start();
|
||||
update();
|
||||
}
|
||||
|
@ -5,6 +5,10 @@
|
||||
#ifndef BITCOIN_QT_TRAFFICGRAPHWIDGET_H
|
||||
#define BITCOIN_QT_TRAFFICGRAPHWIDGET_H
|
||||
|
||||
#include "trafficgraphdata.h"
|
||||
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QQueue>
|
||||
|
||||
@ -29,20 +33,18 @@ protected:
|
||||
|
||||
public Q_SLOTS:
|
||||
void updateRates();
|
||||
void setGraphRangeMins(int mins);
|
||||
void setGraphRangeMins(int value);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
void paintPath(QPainterPath &path, QQueue<float> &samples);
|
||||
typedef boost::function<float(const TrafficSample&)> SampleChooser;
|
||||
void paintPath(QPainterPath &path, const TrafficGraphData::SampleQueue &queue, SampleChooser chooser);
|
||||
|
||||
QTimer *timer;
|
||||
float fMax;
|
||||
int nMins;
|
||||
QQueue<float> vSamplesIn;
|
||||
QQueue<float> vSamplesOut;
|
||||
quint64 nLastBytesIn;
|
||||
quint64 nLastBytesOut;
|
||||
ClientModel *clientModel;
|
||||
TrafficGraphData trafficGraphData;
|
||||
};
|
||||
|
||||
#endif // BITCOIN_QT_TRAFFICGRAPHWIDGET_H
|
||||
|
Loading…
Reference in New Issue
Block a user