// Copyright (c) 2011-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include // private implementation class PeerTablePriv { public: /** Local cache of peer information */ QList cachedNodeStats; /** Pull a full list of peers from vNodes into our cache */ void refreshPeers(interfaces::Node& node) { cachedNodeStats.clear(); interfaces::Node::NodesStats nodes_stats; node.getNodesStats(nodes_stats); cachedNodeStats.reserve(nodes_stats.size()); for (const auto& node_stats : nodes_stats) { CNodeCombinedStats stats; stats.nodeStats = std::get<0>(node_stats); stats.fNodeStateStatsAvailable = std::get<1>(node_stats); stats.nodeStateStats = std::get<2>(node_stats); cachedNodeStats.append(stats); } } int size() const { return cachedNodeStats.size(); } CNodeCombinedStats *index(int idx) { if (idx >= 0 && idx < cachedNodeStats.size()) return &cachedNodeStats[idx]; return nullptr; } }; PeerTableModel::PeerTableModel(interfaces::Node& node, QObject* parent) : QAbstractTableModel(parent), m_node(node), timer(nullptr) { priv.reset(new PeerTablePriv()); // set up timer for auto refresh timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &PeerTableModel::refresh); timer->setInterval(MODEL_UPDATE_DELAY); // load initial data refresh(); } PeerTableModel::~PeerTableModel() { // Intentionally left empty } void PeerTableModel::startAutoRefresh() { timer->start(); } void PeerTableModel::stopAutoRefresh() { timer->stop(); } int PeerTableModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return priv->size(); } int PeerTableModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return columns.length(); } QVariant PeerTableModel::data(const QModelIndex &index, int role) const { if(!index.isValid()) return QVariant(); CNodeCombinedStats *rec = static_cast(index.internalPointer()); const auto column = static_cast(index.column()); if (role == Qt::DisplayRole) { switch (column) { case NetNodeId: return (qint64)rec->nodeStats.nodeid; case Address: // prepend to peer address down-arrow symbol for inbound connection and up-arrow for outbound connection return QString(rec->nodeStats.fInbound ? "↓ " : "↑ ") + QString::fromStdString(rec->nodeStats.addrName); case ConnectionType: return GUIUtil::ConnectionTypeToQString(rec->nodeStats.m_conn_type, /* prepend_direction */ false); case Network: return GUIUtil::NetworkToQString(rec->nodeStats.m_network); case Ping: return GUIUtil::formatPingTime(rec->nodeStats.m_min_ping_time); case Sent: return GUIUtil::formatBytes(rec->nodeStats.nSendBytes); case Received: return GUIUtil::formatBytes(rec->nodeStats.nRecvBytes); case Subversion: return QString::fromStdString(rec->nodeStats.cleanSubVer); } // no default case, so the compiler can warn about missing cases assert(false); } else if (role == Qt::TextAlignmentRole) { switch (column) { case NetNodeId: case Address: return {}; case ConnectionType: case Network: return QVariant(Qt::AlignCenter); case Ping: case Sent: case Received: return QVariant(Qt::AlignRight | Qt::AlignVCenter); case Subversion: return {}; } // no default case, so the compiler can warn about missing cases assert(false); } else if (role == StatsRole) { return QVariant::fromValue(rec); } return QVariant(); } QVariant PeerTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if(orientation == Qt::Horizontal) { if(role == Qt::DisplayRole && section < columns.size()) { return columns[section]; } } return QVariant(); } Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; return retval; } QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); CNodeCombinedStats *data = priv->index(row); if (data) return createIndex(row, column, data); return QModelIndex(); } void PeerTableModel::refresh() { Q_EMIT layoutAboutToBeChanged(); priv->refreshPeers(m_node); Q_EMIT layoutChanged(); }