Line data Source code
1 : // Copyright (c) 2011-2020 The Bitcoin Core developers 2 : // Distributed under the MIT software license, see the accompanying 3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 : 5 : #include <qt/peertablemodel.h> 6 : 7 : #include <qt/guiconstants.h> 8 : #include <qt/guiutil.h> 9 : 10 : #include <interfaces/node.h> 11 : 12 : #include <utility> 13 : 14 : #include <QDebug> 15 : #include <QList> 16 : #include <QTimer> 17 : 18 0 : bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const 19 : { 20 0 : const CNodeStats *pLeft = &(left.nodeStats); 21 0 : const CNodeStats *pRight = &(right.nodeStats); 22 : 23 0 : if (order == Qt::DescendingOrder) 24 0 : std::swap(pLeft, pRight); 25 : 26 0 : switch(column) 27 : { 28 : case PeerTableModel::NetNodeId: 29 0 : return pLeft->nodeid < pRight->nodeid; 30 : case PeerTableModel::Address: 31 0 : return pLeft->addrName.compare(pRight->addrName) < 0; 32 : case PeerTableModel::Subversion: 33 0 : return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; 34 : case PeerTableModel::Ping: 35 0 : return pLeft->m_min_ping_usec < pRight->m_min_ping_usec; 36 : case PeerTableModel::Sent: 37 0 : return pLeft->nSendBytes < pRight->nSendBytes; 38 : case PeerTableModel::Received: 39 0 : return pLeft->nRecvBytes < pRight->nRecvBytes; 40 : } 41 : 42 0 : return false; 43 0 : } 44 : 45 : // private implementation 46 0 : class PeerTablePriv 47 : { 48 : public: 49 : /** Local cache of peer information */ 50 : QList<CNodeCombinedStats> cachedNodeStats; 51 : /** Column to sort nodes by (default to unsorted) */ 52 0 : int sortColumn{-1}; 53 : /** Order (ascending or descending) to sort nodes by */ 54 : Qt::SortOrder sortOrder; 55 : /** Index of rows by node ID */ 56 : std::map<NodeId, int> mapNodeRows; 57 : 58 : /** Pull a full list of peers from vNodes into our cache */ 59 0 : void refreshPeers(interfaces::Node& node) 60 : { 61 : { 62 0 : cachedNodeStats.clear(); 63 : 64 0 : interfaces::Node::NodesStats nodes_stats; 65 0 : node.getNodesStats(nodes_stats); 66 0 : cachedNodeStats.reserve(nodes_stats.size()); 67 0 : for (const auto& node_stats : nodes_stats) 68 : { 69 0 : CNodeCombinedStats stats; 70 0 : stats.nodeStats = std::get<0>(node_stats); 71 0 : stats.fNodeStateStatsAvailable = std::get<1>(node_stats); 72 0 : stats.nodeStateStats = std::get<2>(node_stats); 73 0 : cachedNodeStats.append(stats); 74 0 : } 75 0 : } 76 : 77 0 : if (sortColumn >= 0) 78 : // sort cacheNodeStats (use stable sort to prevent rows jumping around unnecessarily) 79 0 : std::stable_sort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder)); 80 : 81 : // build index map 82 0 : mapNodeRows.clear(); 83 : int row = 0; 84 0 : for (const CNodeCombinedStats& stats : cachedNodeStats) 85 0 : mapNodeRows.insert(std::pair<NodeId, int>(stats.nodeStats.nodeid, row++)); 86 0 : } 87 : 88 0 : int size() const 89 : { 90 0 : return cachedNodeStats.size(); 91 : } 92 : 93 0 : CNodeCombinedStats *index(int idx) 94 : { 95 0 : if (idx >= 0 && idx < cachedNodeStats.size()) 96 0 : return &cachedNodeStats[idx]; 97 : 98 0 : return nullptr; 99 0 : } 100 : }; 101 : 102 0 : PeerTableModel::PeerTableModel(interfaces::Node& node, QObject* parent) : 103 0 : QAbstractTableModel(parent), 104 0 : m_node(node), 105 0 : timer(nullptr) 106 0 : { 107 0 : columns << tr("NodeId") << tr("Node/Service") << tr("Ping") << tr("Sent") << tr("Received") << tr("User Agent"); 108 0 : priv.reset(new PeerTablePriv()); 109 : 110 : // set up timer for auto refresh 111 0 : timer = new QTimer(this); 112 0 : connect(timer, &QTimer::timeout, this, &PeerTableModel::refresh); 113 0 : timer->setInterval(MODEL_UPDATE_DELAY); 114 : 115 : // load initial data 116 0 : refresh(); 117 0 : } 118 : 119 0 : PeerTableModel::~PeerTableModel() 120 0 : { 121 : // Intentionally left empty 122 0 : } 123 : 124 0 : void PeerTableModel::startAutoRefresh() 125 : { 126 0 : timer->start(); 127 0 : } 128 : 129 0 : void PeerTableModel::stopAutoRefresh() 130 : { 131 0 : timer->stop(); 132 0 : } 133 : 134 0 : int PeerTableModel::rowCount(const QModelIndex &parent) const 135 : { 136 : Q_UNUSED(parent); 137 0 : return priv->size(); 138 : } 139 : 140 0 : int PeerTableModel::columnCount(const QModelIndex &parent) const 141 : { 142 : Q_UNUSED(parent); 143 0 : return columns.length(); 144 : } 145 : 146 0 : QVariant PeerTableModel::data(const QModelIndex &index, int role) const 147 : { 148 0 : if(!index.isValid()) 149 0 : return QVariant(); 150 : 151 0 : CNodeCombinedStats *rec = static_cast<CNodeCombinedStats*>(index.internalPointer()); 152 : 153 0 : if (role == Qt::DisplayRole) { 154 0 : switch(index.column()) 155 : { 156 : case NetNodeId: 157 0 : return (qint64)rec->nodeStats.nodeid; 158 : case Address: 159 : // prepend to peer address down-arrow symbol for inbound connection and up-arrow for outbound connection 160 0 : return QString(rec->nodeStats.fInbound ? "↓ " : "↑ ") + QString::fromStdString(rec->nodeStats.addrName); 161 : case Subversion: 162 0 : return QString::fromStdString(rec->nodeStats.cleanSubVer); 163 : case Ping: 164 0 : return GUIUtil::formatPingTime(rec->nodeStats.m_min_ping_usec); 165 : case Sent: 166 0 : return GUIUtil::formatBytes(rec->nodeStats.nSendBytes); 167 : case Received: 168 0 : return GUIUtil::formatBytes(rec->nodeStats.nRecvBytes); 169 : } 170 0 : } else if (role == Qt::TextAlignmentRole) { 171 0 : switch (index.column()) { 172 : case Ping: 173 : case Sent: 174 : case Received: 175 0 : return QVariant(Qt::AlignRight | Qt::AlignVCenter); 176 : default: 177 0 : return QVariant(); 178 : } 179 : } 180 : 181 0 : return QVariant(); 182 0 : } 183 : 184 0 : QVariant PeerTableModel::headerData(int section, Qt::Orientation orientation, int role) const 185 : { 186 0 : if(orientation == Qt::Horizontal) 187 : { 188 0 : if(role == Qt::DisplayRole && section < columns.size()) 189 : { 190 0 : return columns[section]; 191 : } 192 : } 193 0 : return QVariant(); 194 0 : } 195 : 196 0 : Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const 197 : { 198 0 : if (!index.isValid()) return Qt::NoItemFlags; 199 : 200 0 : Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; 201 0 : return retval; 202 0 : } 203 : 204 0 : QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const 205 : { 206 : Q_UNUSED(parent); 207 0 : CNodeCombinedStats *data = priv->index(row); 208 : 209 0 : if (data) 210 0 : return createIndex(row, column, data); 211 0 : return QModelIndex(); 212 0 : } 213 : 214 0 : const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx) 215 : { 216 0 : return priv->index(idx); 217 : } 218 : 219 0 : void PeerTableModel::refresh() 220 : { 221 0 : Q_EMIT layoutAboutToBeChanged(); 222 0 : priv->refreshPeers(m_node); 223 0 : Q_EMIT layoutChanged(); 224 0 : } 225 : 226 0 : int PeerTableModel::getRowByNodeId(NodeId nodeid) 227 : { 228 0 : std::map<NodeId, int>::iterator it = priv->mapNodeRows.find(nodeid); 229 0 : if (it == priv->mapNodeRows.end()) 230 0 : return -1; 231 : 232 0 : return it->second; 233 0 : } 234 : 235 0 : void PeerTableModel::sort(int column, Qt::SortOrder order) 236 : { 237 0 : priv->sortColumn = column; 238 0 : priv->sortOrder = order; 239 0 : refresh(); 240 0 : }