Skip to content

Enhanced Traffic Graph Widget with Multi-timeframe Support and Data Persistence #866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/interfaces/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ class Node
//! Get total bytes sent.
virtual int64_t getTotalBytesSent() = 0;

//! Set total bytes recv
virtual void setTotalBytesRecv(uint64_t bytes) = 0;

//! Set total bytes sent
virtual void setTotalBytesSent(uint64_t bytes) = 0;

//! Get mempool size.
virtual size_t getMempoolSize() = 0;

Expand Down
8 changes: 8 additions & 0 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3779,6 +3779,14 @@ uint64_t CConnman::GetTotalBytesSent() const
return nTotalBytesSent;
}

void CConnman::SetTotalBytesRecv(uint64_t bytes) { nTotalBytesRecv.store(bytes); }

void CConnman::SetTotalBytesSent(uint64_t bytes) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex)
{
WAIT_LOCK(m_total_bytes_sent_mutex, lock);
nTotalBytesSent = bytes;
}

ServiceFlags CConnman::GetLocalServices() const
{
return m_local_services;
Expand Down
2 changes: 2 additions & 0 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -1261,6 +1261,8 @@ class CConnman

uint64_t GetTotalBytesRecv() const;
uint64_t GetTotalBytesSent() const EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex);
void SetTotalBytesRecv(uint64_t bytes);
void SetTotalBytesSent(uint64_t bytes);

/** Get a unique deterministic randomizer. */
CSipHasher GetDeterministicRandomizer(uint64_t id) const;
Expand Down
2 changes: 2 additions & 0 deletions src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@ class NodeImpl : public Node
}
int64_t getTotalBytesRecv() override { return m_context->connman ? m_context->connman->GetTotalBytesRecv() : 0; }
int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; }
void setTotalBytesRecv(uint64_t bytes) override { if (m_context->connman) m_context->connman->SetTotalBytesRecv(bytes); }
void setTotalBytesSent(uint64_t bytes) override { if (m_context->connman) m_context->connman->SetTotalBytesSent(bytes); }
size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; }
size_t getMempoolDynamicUsage() override { return m_context->mempool ? m_context->mempool->DynamicMemoryUsage() : 0; }
size_t getMempoolMaxUsage() override { return m_context->mempool ? m_context->mempool->m_opts.max_size_bytes : 0; }
Expand Down
27 changes: 13 additions & 14 deletions src/qt/forms/debugwindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -665,20 +665,29 @@
<item>
<widget class="QSlider" name="sldGraphRange">
<property name="minimum">
<number>1</number>
<number>0</number>
</property>
<property name="maximum">
<number>288</number>
<number>2400</number>
</property>
<property name="singleStep">
<number>200</number>
</property>
<property name="pageStep">
<number>12</number>
<number>200</number>
</property>
<property name="value">
<number>6</number>
<number>0</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>200</number>
</property>
</widget>
</item>
<item>
Expand All @@ -694,16 +703,6 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnClearTrafficGraph">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're deleting this, maybe there should be a way for the user to insert a reference line?

For now, I'd move removal of anything to a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user can effectively remove the data by deleting the .dat file - is there any basis for being able to do this while the client is running?

<property name="text">
<string>&amp;Reset</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
Expand Down
34 changes: 34 additions & 0 deletions src/qt/guiutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,40 @@ QString formatBytes(uint64_t bytes)
return QObject::tr("%1 GB").arg(bytes / 1'000'000'000);
}

QString formatBytesps(float val)
{
if (val < 10)
//: "Bytes per second"
return QObject::tr("%1 B/s").arg(0.01 * int(val * 100 + 0.5));
if (val < 100)
//: "Bytes per second"
return QObject::tr("%1 B/s").arg(0.1 * int(val * 10 + 0.5));
if (val < 1'000)
//: "Bytes per second"
return QObject::tr("%1 B/s").arg(int(val + 0.5));
if (val < 10'000)
//: "Kilobytes per second"
return QObject::tr("%1 kB/s").arg(0.01 * int(val / 10 + 0.5));
if (val < 100'000)
//: "Kilobytes per second"
return QObject::tr("%1 kB/s").arg(0.1 * int(val / 100 + 0.5));
if (val < 1'000'000)
//: "Kilobytes per second"
return QObject::tr("%1 kB/s").arg(int(val / 1'000 + 0.5));
if (val < 10'000'000)
//: "Megabytes per second"
return QObject::tr("%1 MB/s").arg(0.01 * int(val / 10'000 + 0.5));
if (val < 100'000'000)
//: "Megabytes per second"
return QObject::tr("%1 MB/s").arg(0.1 * int(val / 100'000 + 0.5));
if (val < 10'000'000'000)
//: "Megabytes per second"
return QObject::tr("%1 MB/s").arg(long(val / 1'000'000 + 0.5));

//: "Gigabytes per second"
return QObject::tr("%1 GB/s").arg(long(val / 1'000'000'000 + 0.5));
}

qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize, qreal font_size) {
while(font_size >= minPointSize) {
font.setPointSizeF(font_size);
Expand Down
1 change: 1 addition & 0 deletions src/qt/guiutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ namespace GUIUtil
QString formatNiceTimeOffset(qint64 secs);

QString formatBytes(uint64_t bytes);
QString formatBytesps(float bytes);

qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize = 4, qreal startPointSize = 14);

Expand Down
57 changes: 47 additions & 10 deletions src/qt/rpcconsole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
using util::Join;

const int CONSOLE_HISTORY = 50;
const int INITIAL_TRAFFIC_GRAPH_MINS = 30;
const QSize FONT_RANGE(4, 40);
const char fontSizeSettingsKey[] = "consoleFontSize";

Expand Down Expand Up @@ -566,7 +565,6 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
connect(ui->clearButton, &QAbstractButton::clicked, [this] { clear(); });
connect(ui->fontBiggerButton, &QAbstractButton::clicked, this, &RPCConsole::fontBigger);
connect(ui->fontSmallerButton, &QAbstractButton::clicked, this, &RPCConsole::fontSmaller);
connect(ui->btnClearTrafficGraph, &QPushButton::clicked, ui->trafficGraph, &TrafficGraphWidget::clear);

// disable the wallet selector by default
ui->WalletSelector->setVisible(false);
Expand All @@ -578,7 +576,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
// based timer interface
m_node.rpcSetTimerInterfaceIfUnset(rpcTimerInterface);

setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we losing this default constant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because it isn't needed - on startup it "defaults" to the first non-full range

setTrafficGraphRange(1); // 1 is the lowest setting (0 bumps up)
updateDetailWidget();

consoleFontSize = settings.value(fontSizeSettingsKey, QFont().pointSize()).toInt();
Expand Down Expand Up @@ -1164,21 +1162,60 @@ void RPCConsole::scrollToEnd()
scrollbar->setValue(scrollbar->maximum());
}

void RPCConsole::on_sldGraphRange_valueChanged(int value)
void RPCConsole::on_sldGraphRange_valueChanged(int slider_value)
{
const int multiplier = 5; // each position on the slider represents 5 min
int mins = value * multiplier;
setTrafficGraphRange(mins);
static int64_t last_click_time = 0;
static bool last_click_was_up = false;
unsigned int value = (slider_value + 100) / 200 + 1; // minimum of 1, 0 reserve for scale bump
if (!slider_in_use) {
// Avoid accidental boucing of direction
int64_t now = GetTime<std::chrono::milliseconds>().count();
bool this_click_is_up = false;
bool bouncing = false;
if (slider_value > set_slider_value) this_click_is_up = true;
if (now - last_click_time < 250 && this_click_is_up != last_click_was_up) {
bouncing = true;
ui->sldGraphRange->blockSignals(true);
ui->sldGraphRange->setValue(set_slider_value);
ui->sldGraphRange->blockSignals(false);
}
last_click_time = now;
last_click_was_up = this_click_is_up;
set_slider_value = slider_value;
if (bouncing) return;
}
set_slider_value = slider_value;
setTrafficGraphRange(value);
}

void RPCConsole::setTrafficGraphRange(int mins)
void RPCConsole::setTrafficGraphRange(int value)
{
ui->trafficGraph->setGraphRange(std::chrono::minutes{mins});
ui->lblGraphRange->setText(GUIUtil::formatDurationStr(std::chrono::minutes{mins}));
std::chrono::minutes mins = ui->trafficGraph->setGraphRange(value);
if (value)
set_slider_value = (value - 1) * 200;
else {
// When bumping, calculate the proper slider position based on the traffic graph's new value
unsigned int new_graph_value = ui->trafficGraph->getCurrentRangeIndex() + 1; // +1 because the index is 0-based
set_slider_value = (new_graph_value - 1) * 200;
ui->sldGraphRange->blockSignals(true);
ui->sldGraphRange->setValue(set_slider_value);
ui->sldGraphRange->blockSignals(false);
}
ui->lblGraphRange->setText(GUIUtil::formatDurationStr(mins));
}

void RPCConsole::on_sldGraphRange_sliderReleased()
{
ui->sldGraphRange->setValue(set_slider_value);
slider_in_use = false;
}

void RPCConsole::on_sldGraphRange_sliderPressed() { slider_in_use = true; }

void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
{
if (!slider_in_use && ui->trafficGraph->GraphRangeBump())
setTrafficGraphRange(0); // bump it up
ui->lblBytesIn->setText(GUIUtil::formatBytes(totalBytesIn));
ui->lblBytesOut->setText(GUIUtil::formatBytes(totalBytesOut));
}
Expand Down
6 changes: 5 additions & 1 deletion src/qt/rpcconsole.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ private Q_SLOTS:
void on_openDebugLogfileButton_clicked();
/** change the time range of the network traffic graph */
void on_sldGraphRange_valueChanged(int value);
void on_sldGraphRange_sliderReleased();
void on_sldGraphRange_sliderPressed();
/** update traffic statistics */
void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut);
void resizeEvent(QResizeEvent *event) override;
Expand Down Expand Up @@ -146,7 +148,7 @@ public Q_SLOTS:
} const ts;

void startExecutor();
void setTrafficGraphRange(int mins);
void setTrafficGraphRange(int value);

enum ColumnWidths
{
Expand Down Expand Up @@ -177,6 +179,8 @@ public Q_SLOTS:
bool m_is_executing{false};
QByteArray m_peer_widget_header_state;
QByteArray m_banlist_widget_header_state;
bool slider_in_use{false};
int set_slider_value{0};

/** Update UI with latest network info from model. */
void updateNetworkState();
Expand Down
Loading