aboutsummaryrefslogtreecommitdiff
path: root/src/qt/rpcconsole.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qt/rpcconsole.cpp')
-rw-r--r--src/qt/rpcconsole.cpp220
1 files changed, 200 insertions, 20 deletions
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index ba5871ae2b..e1f40ddd09 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -7,12 +7,19 @@
#include "clientmodel.h"
#include "guiutil.h"
+#include "peertablemodel.h"
+#include "main.h"
#include "rpcserver.h"
#include "rpcclient.h"
+#include "util.h"
#include "json/json_spirit_value.h"
+#ifdef ENABLE_WALLET
+#include <db_cxx.h>
+#endif
#include <openssl/crypto.h>
+
#include <QKeyEvent>
#include <QScrollBar>
#include <QThread>
@@ -195,6 +202,10 @@ RPCConsole::RPCConsole(QWidget *parent) :
clientModel(0),
historyPtr(0)
{
+ detailNodeStats = CNodeCombinedStats();
+ detailNodeStats.nodestats.nodeid = -1;
+ detailNodeStats.statestats.nMisbehavior = -1;
+
ui->setupUi(this);
GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this);
@@ -209,12 +220,20 @@ RPCConsole::RPCConsole(QWidget *parent) :
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
connect(ui->btnClearTrafficGraph, SIGNAL(clicked()), ui->trafficGraph, SLOT(clear()));
- // set OpenSSL version label
+ // set library version labels
ui->openSSLVersion->setText(SSLeay_version(SSLEAY_VERSION));
+#ifdef ENABLE_WALLET
+ ui->berkeleyDBVersion->setText(DbEnv::version(0, 0, 0));
+#else
+ ui->label_berkeleyDBVersion->hide();
+ ui->berkeleyDBVersion->hide();
+#endif
startExecutor();
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
+ ui->detailWidget->hide();
+
clear();
}
@@ -271,19 +290,34 @@ void RPCConsole::setClientModel(ClientModel *model)
setNumConnections(model->getNumConnections());
connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
- setNumBlocks(model->getNumBlocks(), model->getNumBlocksOfPeers());
- connect(model, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int)));
+ setNumBlocks(model->getNumBlocks());
+ connect(model, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int)));
updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent());
connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64)));
+ // set up peer table
+ ui->peerWidget->setModel(model->getPeerTableModel());
+ ui->peerWidget->verticalHeader()->hide();
+ ui->peerWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
+ ui->peerWidget->setSelectionMode(QAbstractItemView::SingleSelection);
+ ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
+ columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(ui->peerWidget, MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH);
+
+ // connect the peerWidget's selection model to our peerSelected() handler
+ QItemSelectionModel *peerSelectModel = ui->peerWidget->selectionModel();
+ connect(peerSelectModel, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
+ this, SLOT(peerSelected(const QItemSelection &, const QItemSelection &)));
+ connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged()));
+
// Provide initial values
ui->clientVersion->setText(model->formatFullVersion());
ui->clientName->setText(model->clientName());
ui->buildDate->setText(model->formatBuildDate());
ui->startupTime->setText(model->formatClientStartupTime());
- ui->networkName->setText(model->getNetworkName());
+ ui->networkName->setText(QString::fromStdString(Params().NetworkIDString()));
}
}
@@ -366,11 +400,9 @@ void RPCConsole::setNumConnections(int count)
ui->numberOfConnections->setText(connections);
}
-void RPCConsole::setNumBlocks(int count, int countOfPeers)
+void RPCConsole::setNumBlocks(int count)
{
ui->numberOfBlocks->setText(QString::number(count));
- // If there is no current countOfPeers available display N/A instead of 0, which can't ever be true
- ui->totalBlocks->setText(countOfPeers == 0 ? tr("N/A") : QString::number(countOfPeers));
if(clientModel)
ui->lastBlockTime->setText(clientModel->getLastBlockDate().toString());
}
@@ -384,8 +416,8 @@ void RPCConsole::on_lineEdit_returnPressed()
{
message(CMD_REQUEST, cmd);
emit cmdRequest(cmd);
- // Truncate history from current position
- history.erase(history.begin() + historyPtr, history.end());
+ // Remove command, if already in history
+ history.removeOne(cmd);
// Append command to history
history.append(cmd);
// Enforce maximum history size
@@ -476,17 +508,7 @@ QString RPCConsole::FormatBytes(quint64 bytes)
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));
- }
- }
+ ui->lblGraphRange->setText(GUIUtil::formatDurationStr(mins * 60));
}
void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
@@ -494,3 +516,161 @@ void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
ui->lblBytesIn->setText(FormatBytes(totalBytesIn));
ui->lblBytesOut->setText(FormatBytes(totalBytesOut));
}
+
+void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelection &deselected)
+{
+ Q_UNUSED(deselected);
+
+ if (selected.indexes().isEmpty())
+ return;
+
+ // mark the cached banscore as unknown
+ detailNodeStats.statestats.nMisbehavior = -1;
+
+ const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.indexes().first().row());
+
+ if (stats)
+ {
+ detailNodeStats.nodestats.nodeid = stats->nodestats.nodeid;
+ updateNodeDetail(stats);
+ ui->detailWidget->show();
+ ui->detailWidget->setDisabled(false);
+ }
+}
+
+void RPCConsole::peerLayoutChanged()
+{
+ const CNodeCombinedStats *stats = NULL;
+ bool fUnselect = false, fReselect = false, fDisconnected = false;
+
+ if (detailNodeStats.nodestats.nodeid == -1)
+ // no node selected yet
+ return;
+
+ // find the currently selected row
+ int selectedRow;
+ QModelIndexList selectedModelIndex = ui->peerWidget->selectionModel()->selectedIndexes();
+ if (selectedModelIndex.isEmpty())
+ selectedRow = -1;
+ else
+ selectedRow = selectedModelIndex.first().row();
+
+ // check if our detail node has a row in the table (it may not necessarily
+ // be at selectedRow since its position can change after a layout change)
+ int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(detailNodeStats.nodestats.nodeid);
+
+ if (detailNodeRow < 0)
+ {
+ // detail node dissapeared from table (node disconnected)
+ fUnselect = true;
+ fDisconnected = true;
+ detailNodeStats.nodestats.nodeid = 0;
+ }
+ else
+ {
+ if (detailNodeRow != selectedRow)
+ {
+ // detail node moved position
+ fUnselect = true;
+ fReselect = true;
+ }
+
+ // get fresh stats on the detail node.
+ stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
+ }
+
+ if (fUnselect && selectedRow >= 0)
+ {
+ ui->peerWidget->selectionModel()->select(QItemSelection(selectedModelIndex.first(), selectedModelIndex.last()),
+ QItemSelectionModel::Deselect);
+ }
+
+ if (fReselect)
+ {
+ ui->peerWidget->selectRow(detailNodeRow);
+ }
+
+ if (stats)
+ updateNodeDetail(stats);
+
+ if (fDisconnected)
+ {
+ ui->peerHeading->setText(QString(tr("Peer Disconnected")));
+ ui->detailWidget->setDisabled(true);
+ QDateTime dt = QDateTime::fromTime_t(detailNodeStats.nodestats.nLastSend);
+ if (detailNodeStats.nodestats.nLastSend)
+ ui->peerLastSend->setText(dt.toString("yyyy-MM-dd hh:mm:ss"));
+ dt.setTime_t(detailNodeStats.nodestats.nLastRecv);
+ if (detailNodeStats.nodestats.nLastRecv)
+ ui->peerLastRecv->setText(dt.toString("yyyy-MM-dd hh:mm:ss"));
+ dt.setTime_t(detailNodeStats.nodestats.nTimeConnected);
+ ui->peerConnTime->setText(dt.toString("yyyy-MM-dd hh:mm:ss"));
+ }
+}
+
+void RPCConsole::updateNodeDetail(const CNodeCombinedStats *combinedStats)
+{
+ CNodeStats stats = combinedStats->nodestats;
+
+ // keep a copy of timestamps, used to display dates upon disconnect
+ detailNodeStats.nodestats.nLastSend = stats.nLastSend;
+ detailNodeStats.nodestats.nLastRecv = stats.nLastRecv;
+ detailNodeStats.nodestats.nTimeConnected = stats.nTimeConnected;
+
+ // update the detail ui with latest node information
+ ui->peerHeading->setText(QString("<b>%1</b>").arg(tr("Node Detail")));
+ ui->peerAddr->setText(QString(stats.addrName.c_str()));
+ ui->peerServices->setText(GUIUtil::formatServicesStr(stats.nServices));
+ ui->peerLastSend->setText(stats.nLastSend ? GUIUtil::formatDurationStr(GetTime() - stats.nLastSend) : tr("never"));
+ ui->peerLastRecv->setText(stats.nLastRecv ? GUIUtil::formatDurationStr(GetTime() - stats.nLastRecv) : tr("never"));
+ ui->peerBytesSent->setText(FormatBytes(stats.nSendBytes));
+ ui->peerBytesRecv->setText(FormatBytes(stats.nRecvBytes));
+ ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetTime() - stats.nTimeConnected));
+ ui->peerPingTime->setText(stats.dPingTime == 0 ? tr("N/A") : QString(tr("%1 secs")).arg(QString::number(stats.dPingTime, 'f', 3)));
+ ui->peerVersion->setText(QString("%1").arg(stats.nVersion));
+ ui->peerSubversion->setText(QString(stats.cleanSubVer.c_str()));
+ ui->peerDirection->setText(stats.fInbound ? tr("Inbound") : tr("Outbound"));
+ ui->peerHeight->setText(QString("%1").arg(stats.nStartingHeight));
+ ui->peerSyncNode->setText(stats.fSyncNode ? tr("Yes") : tr("No"));
+
+ // if we can, display the peer's ban score
+ CNodeStateStats statestats = combinedStats->statestats;
+ if (statestats.nMisbehavior >= 0)
+ {
+ // we have a new nMisbehavor value - update the cache
+ detailNodeStats.statestats.nMisbehavior = statestats.nMisbehavior;
+ }
+
+ // pull the ban score from cache. -1 means it hasn't been retrieved yet (lock busy).
+ if (detailNodeStats.statestats.nMisbehavior >= 0)
+ ui->peerBanScore->setText(QString("%1").arg(detailNodeStats.statestats.nMisbehavior));
+ else
+ ui->peerBanScore->setText(tr("Fetching..."));
+}
+
+// We override the virtual resizeEvent of the QWidget to adjust tables column
+// sizes as the tables width is proportional to the dialogs width.
+void RPCConsole::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+ columnResizingFixer->stretchColumnWidth(PeerTableModel::Address);
+}
+
+void RPCConsole::showEvent(QShowEvent *event)
+{
+ QWidget::showEvent(event);
+
+ // peerWidget needs a resize in case the dialog has non-default geometry
+ columnResizingFixer->stretchColumnWidth(PeerTableModel::Address);
+
+ // start PeerTableModel auto refresh
+ clientModel->getPeerTableModel()->startAutoRefresh(1000);
+}
+
+void RPCConsole::hideEvent(QHideEvent *event)
+{
+ QWidget::hideEvent(event);
+
+ // stop PeerTableModel auto refresh
+ clientModel->getPeerTableModel()->stopAutoRefresh();
+}