Bitcoin Core  0.19.99
P2P Digital Currency
peertablemodel.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2019 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 bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const
19 {
20  const CNodeStats *pLeft = &(left.nodeStats);
21  const CNodeStats *pRight = &(right.nodeStats);
22 
23  if (order == Qt::DescendingOrder)
24  std::swap(pLeft, pRight);
25 
26  switch(column)
27  {
29  return pLeft->nodeid < pRight->nodeid;
31  return pLeft->addrName.compare(pRight->addrName) < 0;
33  return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
35  return pLeft->m_min_ping_usec < pRight->m_min_ping_usec;
37  return pLeft->nSendBytes < pRight->nSendBytes;
39  return pLeft->nRecvBytes < pRight->nRecvBytes;
40  }
41 
42  return false;
43 }
44 
45 // private implementation
47 {
48 public:
50  QList<CNodeCombinedStats> cachedNodeStats;
52  int sortColumn{-1};
54  Qt::SortOrder sortOrder;
56  std::map<NodeId, int> mapNodeRows;
57 
60  {
61  {
62  cachedNodeStats.clear();
63 
64  interfaces::Node::NodesStats nodes_stats;
65  node.getNodesStats(nodes_stats);
66  cachedNodeStats.reserve(nodes_stats.size());
67  for (const auto& node_stats : nodes_stats)
68  {
69  CNodeCombinedStats stats;
70  stats.nodeStats = std::get<0>(node_stats);
71  stats.fNodeStateStatsAvailable = std::get<1>(node_stats);
72  stats.nodeStateStats = std::get<2>(node_stats);
73  cachedNodeStats.append(stats);
74  }
75  }
76 
77  if (sortColumn >= 0)
78  // sort cacheNodeStats (use stable sort to prevent rows jumping around unnecessarily)
79  std::stable_sort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder));
80 
81  // build index map
82  mapNodeRows.clear();
83  int row = 0;
84  for (const CNodeCombinedStats& stats : cachedNodeStats)
85  mapNodeRows.insert(std::pair<NodeId, int>(stats.nodeStats.nodeid, row++));
86  }
87 
88  int size() const
89  {
90  return cachedNodeStats.size();
91  }
92 
94  {
95  if (idx >= 0 && idx < cachedNodeStats.size())
96  return &cachedNodeStats[idx];
97 
98  return nullptr;
99  }
100 };
101 
103  QAbstractTableModel(parent),
104  m_node(node),
105  timer(nullptr)
106 {
107  columns << tr("NodeId") << tr("Node/Service") << tr("Ping") << tr("Sent") << tr("Received") << tr("User Agent");
108  priv.reset(new PeerTablePriv());
109 
110  // set up timer for auto refresh
111  timer = new QTimer(this);
112  connect(timer, &QTimer::timeout, this, &PeerTableModel::refresh);
113  timer->setInterval(MODEL_UPDATE_DELAY);
114 
115  // load initial data
116  refresh();
117 }
118 
120 {
121  // Intentionally left empty
122 }
123 
125 {
126  timer->start();
127 }
128 
130 {
131  timer->stop();
132 }
133 
134 int PeerTableModel::rowCount(const QModelIndex &parent) const
135 {
136  Q_UNUSED(parent);
137  return priv->size();
138 }
139 
140 int PeerTableModel::columnCount(const QModelIndex &parent) const
141 {
142  Q_UNUSED(parent);
143  return columns.length();
144 }
145 
146 QVariant PeerTableModel::data(const QModelIndex &index, int role) const
147 {
148  if(!index.isValid())
149  return QVariant();
150 
151  CNodeCombinedStats *rec = static_cast<CNodeCombinedStats*>(index.internalPointer());
152 
153  if (role == Qt::DisplayRole) {
154  switch(index.column())
155  {
156  case NetNodeId:
157  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  return QString(rec->nodeStats.fInbound ? "↓ " : "↑ ") + QString::fromStdString(rec->nodeStats.addrName);
161  case Subversion:
162  return QString::fromStdString(rec->nodeStats.cleanSubVer);
163  case Ping:
165  case Sent:
167  case Received:
169  }
170  } else if (role == Qt::TextAlignmentRole) {
171  switch (index.column()) {
172  case Ping:
173  case Sent:
174  case Received:
175  return QVariant(Qt::AlignRight | Qt::AlignVCenter);
176  default:
177  return QVariant();
178  }
179  }
180 
181  return QVariant();
182 }
183 
184 QVariant PeerTableModel::headerData(int section, Qt::Orientation orientation, int role) const
185 {
186  if(orientation == Qt::Horizontal)
187  {
188  if(role == Qt::DisplayRole && section < columns.size())
189  {
190  return columns[section];
191  }
192  }
193  return QVariant();
194 }
195 
196 Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const
197 {
198  if (!index.isValid()) return Qt::NoItemFlags;
199 
200  Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
201  return retval;
202 }
203 
204 QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const
205 {
206  Q_UNUSED(parent);
207  CNodeCombinedStats *data = priv->index(row);
208 
209  if (data)
210  return createIndex(row, column, data);
211  return QModelIndex();
212 }
213 
215 {
216  return priv->index(idx);
217 }
218 
220 {
221  Q_EMIT layoutAboutToBeChanged();
222  priv->refreshPeers(m_node);
223  Q_EMIT layoutChanged();
224 }
225 
227 {
228  std::map<NodeId, int>::iterator it = priv->mapNodeRows.find(nodeid);
229  if (it == priv->mapNodeRows.end())
230  return -1;
231 
232  return it->second;
233 }
234 
235 void PeerTableModel::sort(int column, Qt::SortOrder order)
236 {
237  priv->sortColumn = column;
238  priv->sortOrder = order;
239  refresh();
240 }
void refreshPeers(interfaces::Node &node)
Pull a full list of peers from vNodes into our cache.
QVariant data(const QModelIndex &index, int role) const
std::vector< std::tuple< CNodeStats, bool, CNodeStateStats > > NodesStats
Get stats for connected nodes.
Definition: node.h:114
int getRowByNodeId(NodeId nodeid)
bool operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const
CNodeStateStats nodeStateStats
NodeContext & m_node
Definition: chain.cpp:382
QStringList columns
QString formatBytes(uint64_t bytes)
Definition: guiutil.cpp:823
CNodeCombinedStats * index(int idx)
std::string cleanSubVer
Definition: net.h:587
NodeLessThan(int nColumn, Qt::SortOrder fOrder)
Qt::SortOrder order
Qt::ItemFlags flags(const QModelIndex &index) const
CNodeStats nodeStats
Qt::SortOrder sortOrder
Order (ascending or descending) to sort nodes by.
int columnCount(const QModelIndex &parent) const
interfaces::Node & m_node
QString formatPingTime(int64_t ping_usec)
Definition: guiutil.cpp:776
QList< CNodeCombinedStats > cachedNodeStats
Local cache of peer information.
bool fInbound
Definition: net.h:588
std::unique_ptr< PeerTablePriv > priv
int size() const
uint64_t nRecvBytes
Definition: net.h:593
std::string addrName
Definition: net.h:585
int64_t NodeId
Definition: net.h:93
const CNodeCombinedStats * getNodeStats(int idx)
uint64_t nSendBytes
Definition: net.h:591
QModelIndex index(int row, int column, const QModelIndex &parent) const
static const int MODEL_UPDATE_DELAY
Definition: guiconstants.h:11
std::map< NodeId, int > mapNodeRows
Index of rows by node ID.
virtual bool getNodesStats(NodesStats &stats)=0
void sort(int column, Qt::SortOrder order)
PeerTableModel(interfaces::Node &node, QObject *parent)
auto it
Definition: validation.cpp:361
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:39
int rowCount(const QModelIndex &parent) const
int64_t m_min_ping_usec
Definition: net.h:599
QVariant headerData(int section, Qt::Orientation orientation, int role) const
NodeId nodeid
Definition: net.h:578