Back to home page

Bitcoin sources

 
 

    


0001 // Copyright (c) 2011-2013 The Bitcoin developers
0002 // Distributed under the MIT/X11 software license, see the accompanying
0003 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
0004 
0005 #include "peertablemodel.h"
0006 
0007 #include "clientmodel.h"
0008 #include "guiconstants.h"
0009 #include "guiutil.h"
0010 
0011 #include "net.h"
0012 #include "sync.h"
0013 
0014 #include <QDebug>
0015 #include <QList>
0016 #include <QTimer>
0017 
0018 bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const
0019 {
0020     const CNodeStats *pLeft = &(left.nodeStats);
0021     const CNodeStats *pRight = &(right.nodeStats);
0022 
0023     if (order == Qt::DescendingOrder)
0024         std::swap(pLeft, pRight);
0025 
0026     switch(column)
0027     {
0028     case PeerTableModel::Address:
0029         return pLeft->addrName.compare(pRight->addrName) < 0;
0030     case PeerTableModel::Subversion:
0031         return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
0032     case PeerTableModel::Ping:
0033         return pLeft->dPingTime < pRight->dPingTime;
0034     }
0035 
0036     return false;
0037 }
0038 
0039 // private implementation
0040 class PeerTablePriv
0041 {
0042 public:
0043     /** Local cache of peer information */
0044     QList<CNodeCombinedStats> cachedNodeStats;
0045     /** Column to sort nodes by */
0046     int sortColumn;
0047     /** Order (ascending or descending) to sort nodes by */
0048     Qt::SortOrder sortOrder;
0049     /** Index of rows by node ID */
0050     std::map<NodeId, int> mapNodeRows;
0051 
0052     /** Pull a full list of peers from vNodes into our cache */
0053     void refreshPeers()
0054     {
0055         {
0056             TRY_LOCK(cs_vNodes, lockNodes);
0057             if (!lockNodes)
0058             {
0059                 // skip the refresh if we can't immediately get the lock
0060                 return;
0061             }
0062             cachedNodeStats.clear();
0063 #if QT_VERSION >= 0x040700
0064             cachedNodeStats.reserve(vNodes.size());
0065 #endif
0066             BOOST_FOREACH(CNode* pnode, vNodes)
0067             {
0068                 CNodeCombinedStats stats;
0069                 stats.nodeStateStats.nMisbehavior = 0;
0070                 stats.nodeStateStats.nSyncHeight = -1;
0071                 stats.fNodeStateStatsAvailable = false;
0072                 pnode->copyStats(stats.nodeStats);
0073                 cachedNodeStats.append(stats);
0074             }
0075         }
0076 
0077         // Try to retrieve the CNodeStateStats for each node.
0078         {
0079             TRY_LOCK(cs_main, lockMain);
0080             if (lockMain)
0081             {
0082                 BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
0083                     stats.fNodeStateStatsAvailable = GetNodeStateStats(stats.nodeStats.nodeid, stats.nodeStateStats);
0084             }
0085         }
0086 
0087         if (sortColumn >= 0)
0088             // sort cacheNodeStats (use stable sort to prevent rows jumping around unneceesarily)
0089             qStableSort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder));
0090 
0091         // build index map
0092         mapNodeRows.clear();
0093         int row = 0;
0094         BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
0095             mapNodeRows.insert(std::pair<NodeId, int>(stats.nodeStats.nodeid, row++));
0096     }
0097 
0098     int size()
0099     {
0100         return cachedNodeStats.size();
0101     }
0102 
0103     CNodeCombinedStats *index(int idx)
0104     {
0105         if(idx >= 0 && idx < cachedNodeStats.size()) {
0106             return &cachedNodeStats[idx];
0107         } else {
0108             return 0;
0109         }
0110     }
0111 };
0112 
0113 PeerTableModel::PeerTableModel(ClientModel *parent) :
0114     QAbstractTableModel(parent),
0115     clientModel(parent),
0116     timer(0)
0117 {
0118     columns << tr("Address/Hostname") << tr("User Agent") << tr("Ping Time");
0119     priv = new PeerTablePriv();
0120     // default to unsorted
0121     priv->sortColumn = -1;
0122 
0123     // set up timer for auto refresh
0124     timer = new QTimer();
0125     connect(timer, SIGNAL(timeout()), SLOT(refresh()));
0126     timer->setInterval(MODEL_UPDATE_DELAY);
0127 
0128     // load initial data
0129     refresh();
0130 }
0131 
0132 void PeerTableModel::startAutoRefresh()
0133 {
0134     timer->start();
0135 }
0136 
0137 void PeerTableModel::stopAutoRefresh()
0138 {
0139     timer->stop();
0140 }
0141 
0142 int PeerTableModel::rowCount(const QModelIndex &parent) const
0143 {
0144     Q_UNUSED(parent);
0145     return priv->size();
0146 }
0147 
0148 int PeerTableModel::columnCount(const QModelIndex &parent) const
0149 {
0150     Q_UNUSED(parent);
0151     return columns.length();;
0152 }
0153 
0154 QVariant PeerTableModel::data(const QModelIndex &index, int role) const
0155 {
0156     if(!index.isValid())
0157         return QVariant();
0158 
0159     CNodeCombinedStats *rec = static_cast<CNodeCombinedStats*>(index.internalPointer());
0160 
0161     if (role == Qt::DisplayRole) {
0162         switch(index.column())
0163         {
0164         case Address:
0165             return QString::fromStdString(rec->nodeStats.addrName);
0166         case Subversion:
0167             return QString::fromStdString(rec->nodeStats.cleanSubVer);
0168         case Ping:
0169             return GUIUtil::formatPingTime(rec->nodeStats.dPingTime);
0170         }
0171     } else if (role == Qt::TextAlignmentRole) {
0172         if (index.column() == Ping)
0173             return (int)(Qt::AlignRight | Qt::AlignVCenter);
0174     }
0175 
0176     return QVariant();
0177 }
0178 
0179 QVariant PeerTableModel::headerData(int section, Qt::Orientation orientation, int role) const
0180 {
0181     if(orientation == Qt::Horizontal)
0182     {
0183         if(role == Qt::DisplayRole && section < columns.size())
0184         {
0185             return columns[section];
0186         }
0187     }
0188     return QVariant();
0189 }
0190 
0191 Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const
0192 {
0193     if(!index.isValid())
0194         return 0;
0195 
0196     Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
0197     return retval;
0198 }
0199 
0200 QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const
0201 {
0202     Q_UNUSED(parent);
0203     CNodeCombinedStats *data = priv->index(row);
0204 
0205     if (data)
0206     {
0207         return createIndex(row, column, data);
0208     }
0209     else
0210     {
0211         return QModelIndex();
0212     }
0213 }
0214 
0215 const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx)
0216 {
0217     return priv->index(idx);
0218 }
0219 
0220 void PeerTableModel::refresh()
0221 {
0222     emit layoutAboutToBeChanged();
0223     priv->refreshPeers();
0224     emit layoutChanged();
0225 }
0226 
0227 int PeerTableModel::getRowByNodeId(NodeId nodeid)
0228 {
0229     std::map<NodeId, int>::iterator it = priv->mapNodeRows.find(nodeid);
0230     if (it == priv->mapNodeRows.end())
0231         return -1;
0232 
0233     return it->second;
0234 }
0235 
0236 void PeerTableModel::sort(int column, Qt::SortOrder order)
0237 {
0238     priv->sortColumn = column;
0239     priv->sortOrder = order;
0240     refresh();
0241 }