diff options
Diffstat (limited to 'src/qt/rpcconsole.cpp')
-rw-r--r-- | src/qt/rpcconsole.cpp | 220 |
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(); +} |