aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorScott Ellis <sje397@gmail.com>2013-08-23 02:09:32 +1000
committerScott Ellis <sje397@gmail.com>2013-10-14 19:02:03 +1100
commitce14345a89dfa05992f8d2c7c9fe36315d4a67e6 (patch)
tree997e08222a82558e8720fa0ce0ba82438f161664 /src
parent9269d0e96e621a6e02da8074785ac310ce64db73 (diff)
Add network traffic graph
Diffstat (limited to 'src')
-rw-r--r--src/bitcoinrpc.cpp1
-rw-r--r--src/bitcoinrpc.h1
-rw-r--r--src/net.cpp37
-rw-r--r--src/net.h14
-rw-r--r--src/qt/Makefile.am6
-rw-r--r--src/qt/clientmodel.cpp12
-rw-r--r--src/qt/clientmodel.h4
-rw-r--r--src/qt/forms/rpcconsole.ui261
-rw-r--r--src/qt/rpcconsole.cpp55
-rw-r--r--src/qt/rpcconsole.h9
-rw-r--r--src/qt/trafficgraphwidget.cpp169
-rw-r--r--src/qt/trafficgraphwidget.h44
-rw-r--r--src/rpcnet.cpp14
13 files changed, 618 insertions, 9 deletions
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index b0df9c5e28..2c8db7b53a 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -232,6 +232,7 @@ static const CRPCCommand vRPCCommands[] =
{ "ping", &ping, true, false, false },
{ "addnode", &addnode, true, true, false },
{ "getaddednodeinfo", &getaddednodeinfo, true, true, false },
+ { "getnettotals", &getnettotals, true, true, false },
{ "getdifficulty", &getdifficulty, true, false, false },
{ "getnetworkhashps", &getnetworkhashps, true, false, false },
{ "getgenerate", &getgenerate, true, false, false },
diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h
index f495870b38..1cad12f14a 100644
--- a/src/bitcoinrpc.h
+++ b/src/bitcoinrpc.h
@@ -156,6 +156,7 @@ extern json_spirit::Value getpeerinfo(const json_spirit::Array& params, bool fHe
extern json_spirit::Value ping(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value addnode(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getaddednodeinfo(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getnettotals(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp
extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp);
diff --git a/src/net.cpp b/src/net.cpp
index 5afedb1fd2..e22c56f006 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -426,8 +426,10 @@ void AddressCurrentlyConnected(const CService& addr)
-
-
+uint64 CNode::nTotalBytesRecv = 0;
+uint64 CNode::nTotalBytesSent = 0;
+CCriticalSection CNode::cs_totalBytesRecv;
+CCriticalSection CNode::cs_totalBytesSent;
CNode* FindNode(const CNetAddr& ip)
{
@@ -731,6 +733,7 @@ void SocketSendData(CNode *pnode)
pnode->nLastSend = GetTime();
pnode->nSendBytes += nBytes;
pnode->nSendOffset += nBytes;
+ pnode->RecordBytesSent(nBytes);
if (pnode->nSendOffset == data.size()) {
pnode->nSendOffset = 0;
pnode->nSendSize -= data.size();
@@ -826,10 +829,9 @@ void ThreadSocketHandler()
}
}
}
- if (vNodes.size() != nPrevNodeCount)
- {
+ if(vNodes.size() != nPrevNodeCount) {
nPrevNodeCount = vNodes.size();
- uiInterface.NotifyNumConnectionsChanged(vNodes.size());
+ uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount);
}
@@ -1008,6 +1010,7 @@ void ThreadSocketHandler()
pnode->CloseSocketDisconnect();
pnode->nLastRecv = GetTime();
pnode->nRecvBytes += nBytes;
+ pnode->RecordBytesRecv(nBytes);
}
else if (nBytes == 0)
{
@@ -1859,3 +1862,27 @@ void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataSt
pnode->PushInventory(inv);
}
}
+
+void CNode::RecordBytesRecv(uint64 bytes)
+{
+ LOCK(cs_totalBytesRecv);
+ nTotalBytesRecv += bytes;
+}
+
+void CNode::RecordBytesSent(uint64 bytes)
+{
+ LOCK(cs_totalBytesSent);
+ nTotalBytesSent += bytes;
+}
+
+uint64 CNode::GetTotalBytesRecv()
+{
+ LOCK(cs_totalBytesRecv);
+ return nTotalBytesRecv;
+}
+
+uint64 CNode::GetTotalBytesSent()
+{
+ LOCK(cs_totalBytesSent);
+ return nTotalBytesSent;
+}
diff --git a/src/net.h b/src/net.h
index b57f38fe3a..ab1b69acb7 100644
--- a/src/net.h
+++ b/src/net.h
@@ -298,8 +298,15 @@ public:
}
private:
+ // Network usage totals
+ static CCriticalSection cs_totalBytesRecv;
+ static CCriticalSection cs_totalBytesSent;
+ static uint64 nTotalBytesRecv;
+ static uint64 nTotalBytesSent;
+
CNode(const CNode&);
void operator=(const CNode&);
+
public:
@@ -646,6 +653,13 @@ public:
static bool IsBanned(CNetAddr ip);
bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot
void copyStats(CNodeStats &stats);
+
+ // Network stats
+ static void RecordBytesRecv(uint64 bytes);
+ static void RecordBytesSent(uint64 bytes);
+
+ static uint64 GetTotalBytesRecv();
+ static uint64 GetTotalBytesSent();
};
diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am
index 248dcba0ec..e7ae0a905f 100644
--- a/src/qt/Makefile.am
+++ b/src/qt/Makefile.am
@@ -48,7 +48,7 @@ QT_MOC_CPP = moc_aboutdialog.cpp moc_addressbookpage.cpp \
moc_optionsmodel.cpp moc_overviewpage.cpp moc_paymentserver.cpp \
moc_qrcodedialog.cpp moc_qvalidatedlineedit.cpp moc_qvaluecombobox.cpp \
moc_rpcconsole.cpp moc_sendcoinsdialog.cpp moc_sendcoinsentry.cpp \
- moc_signverifymessagedialog.cpp moc_splashscreen.cpp moc_transactiondesc.cpp \
+ moc_signverifymessagedialog.cpp moc_splashscreen.cpp moc_trafficgraphwidget.cpp moc_transactiondesc.cpp \
moc_transactiondescdialog.cpp moc_transactionfilterproxy.cpp \
moc_transactiontablemodel.cpp moc_transactionview.cpp moc_walletframe.cpp \
moc_walletmodel.cpp moc_walletstack.cpp moc_walletview.cpp
@@ -73,7 +73,7 @@ BITCOIN_QT_H = aboutdialog.h addressbookpage.h addresstablemodel.h \
optionsmodel.h overviewpage.h paymentrequestplus.h paymentserver.h \
qrcodedialog.h qvalidatedlineedit.h qvaluecombobox.h rpcconsole.h \
sendcoinsdialog.h sendcoinsentry.h signverifymessagedialog.h splashscreen.h \
- transactiondescdialog.h transactiondesc.h transactionfilterproxy.h \
+ trafficgraphwidget.h transactiondescdialog.h transactiondesc.h transactionfilterproxy.h \
transactionrecord.h transactiontablemodel.h transactionview.h walletframe.h \
walletmodel.h walletmodeltransaction.h walletstack.h walletview.h
@@ -102,7 +102,7 @@ BITCOIN_QT_CPP = aboutdialog.cpp addressbookpage.cpp \
optionsdialog.cpp optionsmodel.cpp overviewpage.cpp paymentrequestplus.cpp \
paymentserver.cpp qvalidatedlineedit.cpp qvaluecombobox.cpp \
rpcconsole.cpp sendcoinsdialog.cpp sendcoinsentry.cpp \
- signverifymessagedialog.cpp splashscreen.cpp transactiondesc.cpp \
+ signverifymessagedialog.cpp splashscreen.cpp trafficgraphwidget.cpp transactiondesc.cpp \
transactiondescdialog.cpp transactionfilterproxy.cpp transactionrecord.cpp \
transactiontablemodel.cpp transactionview.cpp walletframe.cpp \
walletmodel.cpp walletmodeltransaction.cpp walletstack.cpp walletview.cpp
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index b33f534f7c..f0cf229b36 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -51,6 +51,16 @@ int ClientModel::getNumBlocksAtStartup()
return numBlocksAtStartup;
}
+quint64 ClientModel::getTotalBytesRecv() const
+{
+ return CNode::GetTotalBytesRecv();
+}
+
+quint64 ClientModel::getTotalBytesSent() const
+{
+ return CNode::GetTotalBytesSent();
+}
+
QDateTime ClientModel::getLastBlockDate() const
{
if (pindexBest)
@@ -85,6 +95,8 @@ void ClientModel::updateTimer()
// ensure we return the maximum of newNumBlocksOfPeers and newNumBlocks to not create weird displays in the GUI
emit numBlocksChanged(newNumBlocks, std::max(newNumBlocksOfPeers, newNumBlocks));
}
+
+ emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent());
}
void ClientModel::updateNumConnections(int numConnections)
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index 15074300b4..925f20acd9 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -35,6 +35,9 @@ public:
int getNumBlocks() const;
int getNumBlocksAtStartup();
+ quint64 getTotalBytesRecv() const;
+ quint64 getTotalBytesSent() const;
+
double getVerificationProgress() const;
QDateTime getLastBlockDate() const;
@@ -74,6 +77,7 @@ signals:
void numConnectionsChanged(int count);
void numBlocksChanged(int count, int countOfPeers);
void alertsChanged(const QString &warnings);
+ void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut);
//! Asynchronous message notification
void message(const QString &title, const QString &message, unsigned int style);
diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui
index d1d8ab42a0..54c41ffb67 100644
--- a/src/qt/forms/rpcconsole.ui
+++ b/src/qt/forms/rpcconsole.ui
@@ -445,10 +445,271 @@
</item>
</layout>
</widget>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>&amp;Network Traffic</string>
+ </attribute>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="TrafficGraphWidget" name="trafficGraph" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QSlider" name="sldGraphRange">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>288</number>
+ </property>
+ <property name="pageStep">
+ <number>12</number>
+ </property>
+ <property name="value">
+ <number>6</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="lblGraphRange">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnClearTrafficGraph">
+ <property name="text">
+ <string>&amp;Clear</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Totals</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="Line" name="line">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>10</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <colorrole role="Light">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>255</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ </active>
+ <inactive>
+ <colorrole role="Light">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>255</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ </inactive>
+ <disabled>
+ <colorrole role="Light">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>255</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ </disabled>
+ </palette>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>In:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="lblBytesIn">
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <item>
+ <widget class="Line" name="line_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>10</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <colorrole role="Light">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ </active>
+ <inactive>
+ <colorrole role="Light">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ </inactive>
+ <disabled>
+ <colorrole role="Light">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ </disabled>
+ </palette>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_17">
+ <property name="text">
+ <string>Out:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="lblBytesOut">
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>407</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
</widget>
</item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>TrafficGraphWidget</class>
+ <extends>QWidget</extends>
+ <header>trafficgraphwidget.h</header>
+ <container>1</container>
+ <slots>
+ <slot>clear()</slot>
+ </slots>
+ </customwidget>
+ </customwidgets>
<resources>
<include location="../bitcoin.qrc"/>
</resources>
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 8953c36579..e7dcdf62a1 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -22,6 +22,8 @@
const int CONSOLE_HISTORY = 50;
const QSize ICON_SIZE(24, 24);
+const int INITIAL_TRAFFIC_GRAPH_MINS = 30;
+
const struct {
const char *url;
const char *source;
@@ -204,6 +206,7 @@ RPCConsole::RPCConsole(QWidget *parent) :
ui->openSSLVersion->setText(SSLeay_version(SSLEAY_VERSION));
startExecutor();
+ setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
clear();
}
@@ -253,7 +256,8 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
void RPCConsole::setClientModel(ClientModel *model)
{
- this->clientModel = model;
+ clientModel = model;
+ ui->trafficGraph->setClientModel(model);
if(model)
{
// Keep up to date with client
@@ -263,6 +267,9 @@ void RPCConsole::setClientModel(ClientModel *model)
setNumBlocks(model->getNumBlocks(), model->getNumBlocksOfPeers());
connect(model, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int)));
+ updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent());
+ connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64)));
+
// Provide initial values
ui->clientVersion->setText(model->formatFullVersion());
ui->clientName->setText(model->clientName());
@@ -431,3 +438,49 @@ void RPCConsole::on_showCLOptionsButton_clicked()
GUIUtil::HelpMessageBox help;
help.exec();
}
+
+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);
+}
+
+QString RPCConsole::FormatBytes(quint64 bytes)
+{
+ if(bytes < 1024)
+ return QString(tr("%1 B")).arg(bytes);
+ if(bytes < 1024 * 1024)
+ return QString(tr("%1 KB")).arg(bytes / 1024);
+ if(bytes < 1024 * 1024 * 1024)
+ return QString(tr("%1 MB")).arg(bytes / 1024 / 1024);
+
+ return QString(tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024);
+}
+
+void RPCConsole::setTrafficGraphRange(int mins)
+{
+ ui->trafficGraph->setGraphRangeMins(mins);
+ if(mins < 60) {
+ ui->lblGraphRange->setText(QString(tr("%1 m")).arg(mins));
+ } else {
+ int hours = mins / 60;
+ int minsLeft = mins % 60;
+ if(minsLeft == 0) {
+ ui->lblGraphRange->setText(QString(tr("%1 h")).arg(hours));
+ } else {
+ ui->lblGraphRange->setText(QString(tr("%1 h %2 m")).arg(hours).arg(minsLeft));
+ }
+ }
+}
+
+void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
+{
+ ui->lblBytesIn->setText(FormatBytes(totalBytesIn));
+ ui->lblBytesOut->setText(FormatBytes(totalBytesOut));
+}
+
+void RPCConsole::on_btnClearTrafficGraph_clicked()
+{
+ ui->trafficGraph->clear();
+}
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 3c38b4b8de..af92b55770 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -37,6 +37,12 @@ private slots:
void on_openDebugLogfileButton_clicked();
/** display messagebox with program parameters (same as bitcoin-qt --help) */
void on_showCLOptionsButton_clicked();
+ /** change the time range of the network traffic graph */
+ void on_sldGraphRange_valueChanged(int value);
+ /** update traffic statistics */
+ void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut);
+ /** clear traffic graph */
+ void on_btnClearTrafficGraph_clicked();
public slots:
void clear();
@@ -55,6 +61,9 @@ signals:
void cmdRequest(const QString &command);
private:
+ static QString FormatBytes(quint64 bytes);
+ void setTrafficGraphRange(int mins);
+
Ui::RPCConsole *ui;
ClientModel *clientModel;
QStringList history;
diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp
new file mode 100644
index 0000000000..d49bc31f3e
--- /dev/null
+++ b/src/qt/trafficgraphwidget.cpp
@@ -0,0 +1,169 @@
+#include "trafficgraphwidget.h"
+#include "clientmodel.h"
+
+#include <QPainter>
+#include <QColor>
+#include <QTimer>
+
+#include <cmath>
+
+#define DESIRED_SAMPLES 800
+
+#define XMARGIN 10
+#define YMARGIN 10
+
+TrafficGraphWidget::TrafficGraphWidget(QWidget *parent) :
+ QWidget(parent),
+ timer(0),
+ fMax(0.0f),
+ nMins(0),
+ vSamplesIn(),
+ vSamplesOut(),
+ nLastBytesIn(0),
+ nLastBytesOut(0),
+ clientModel(0)
+{
+ timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), SLOT(updateRates()));
+}
+
+void TrafficGraphWidget::setClientModel(ClientModel *model)
+{
+ clientModel = model;
+ if(model) {
+ nLastBytesIn = model->getTotalBytesRecv();
+ nLastBytesOut = model->getTotalBytesSent();
+ }
+}
+
+int TrafficGraphWidget::getGraphRangeMins() const
+{
+ return nMins;
+}
+
+void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue<float> &samples)
+{
+ int h = height() - YMARGIN * 2, w = width() - XMARGIN * 2;
+ int sampleCount = samples.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);
+ path.lineTo(x, y);
+ }
+ path.lineTo(x, YMARGIN + h);
+ }
+}
+
+void TrafficGraphWidget::paintEvent(QPaintEvent *)
+{
+ QPainter painter(this);
+ painter.fillRect(rect(), Qt::black);
+
+ if(fMax <= 0.0f) return;
+
+ QColor axisCol(Qt::gray);
+ int h = height() - YMARGIN * 2;
+ painter.setPen(axisCol);
+ painter.drawLine(XMARGIN, YMARGIN + h, width() - XMARGIN, YMARGIN + h);
+
+ // decide what order of magnitude we are
+ int base = floor(log10(fMax));
+ float val = pow(10.0f, base);
+
+ const QString units = tr("KB/s");
+ // draw lines
+ painter.setPen(axisCol);
+ painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax, QString("%1 %2").arg(val).arg(units));
+ for(float y = val; y < fMax; y += val) {
+ int yy = YMARGIN + h - h * y / fMax;
+ painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy);
+ }
+ // if we drew 3 or fewer lines, break them up at the next lower order of magnitude
+ if(fMax / val <= 3.0f) {
+ axisCol = axisCol.darker();
+ val = pow(10.0f, base - 1);
+ painter.setPen(axisCol);
+ painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax, QString("%1 %2").arg(val).arg(units));
+ int count = 1;
+ for(float y = val; y < fMax; y += val, count++) {
+ // don't overwrite lines drawn above
+ if(count % 10 == 0)
+ continue;
+ int yy = YMARGIN + h - h * y / fMax;
+ painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy);
+ }
+ }
+
+ if(!vSamplesIn.empty()) {
+ QPainterPath p;
+ paintPath(p, vSamplesIn);
+ painter.fillPath(p, 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.setPen(Qt::red);
+ painter.drawPath(p);
+ }
+}
+
+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;
+
+ while(vSamplesIn.size() > DESIRED_SAMPLES) {
+ vSamplesIn.pop_back();
+ }
+ while(vSamplesOut.size() > DESIRED_SAMPLES) {
+ vSamplesOut.pop_back();
+ }
+
+ float tmax = 0.0f;
+ foreach(float f, vSamplesIn) {
+ if(f > tmax) tmax = f;
+ }
+ foreach(float f, vSamplesOut) {
+ if(f > tmax) tmax = f;
+ }
+ fMax = tmax;
+ update();
+}
+
+void TrafficGraphWidget::setGraphRangeMins(int mins)
+{
+ nMins = mins;
+ int msecsPerSample = nMins * 60 * 1000 / DESIRED_SAMPLES;
+ timer->stop();
+ timer->setInterval(msecsPerSample);
+
+ clear();
+}
+
+void TrafficGraphWidget::clear()
+{
+ timer->stop();
+
+ vSamplesOut.clear();
+ vSamplesIn.clear();
+ fMax = 0.0f;
+
+ if(clientModel) {
+ nLastBytesIn = clientModel->getTotalBytesRecv();
+ nLastBytesOut = clientModel->getTotalBytesSent();
+ }
+ timer->start();
+}
diff --git a/src/qt/trafficgraphwidget.h b/src/qt/trafficgraphwidget.h
new file mode 100644
index 0000000000..b31d1d5b0a
--- /dev/null
+++ b/src/qt/trafficgraphwidget.h
@@ -0,0 +1,44 @@
+#ifndef TRAFFICGRAPHWIDGET_H
+#define TRAFFICGRAPHWIDGET_H
+
+#include <QWidget>
+#include <QQueue>
+
+class ClientModel;
+
+QT_BEGIN_NAMESPACE
+class QPaintEvent;
+class QTimer;
+QT_END_NAMESPACE
+
+class TrafficGraphWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit TrafficGraphWidget(QWidget *parent = 0);
+ void setClientModel(ClientModel *model);
+ int getGraphRangeMins() const;
+
+protected:
+ void paintEvent(QPaintEvent *);
+
+public slots:
+ void updateRates();
+ void setGraphRangeMins(int mins);
+ void clear();
+
+private:
+ void paintPath(QPainterPath &path, QQueue<float> &samples);
+
+ QTimer *timer;
+ float fMax;
+ int nMins;
+ QQueue<float> vSamplesIn;
+ QQueue<float> vSamplesOut;
+ quint64 nLastBytesIn;
+ quint64 nLastBytesOut;
+ ClientModel *clientModel;
+};
+
+#endif // TRAFFICGRAPHWIDGET_H
diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp
index 7df86cc2db..f78f034e32 100644
--- a/src/rpcnet.cpp
+++ b/src/rpcnet.cpp
@@ -223,3 +223,17 @@ Value getaddednodeinfo(const Array& params, bool fHelp)
return ret;
}
+Value getnettotals(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() > 0)
+ throw runtime_error(
+ "getnettotals\n"
+ "Returns information about network traffic, including bytes in, bytes out,\n"
+ "and current time.");
+
+ Object obj;
+ obj.push_back(Pair("totalbytesrecv", static_cast< boost::uint64_t>(CNode::GetTotalBytesRecv())));
+ obj.push_back(Pair("totalbytessent", static_cast<boost::uint64_t>(CNode::GetTotalBytesSent())));
+ obj.push_back(Pair("timemillis", static_cast<boost::int64_t>(GetTimeMillis())));
+ return obj;
+}