ostinato/client/portstatsmodel.cpp
Srivats P f756ead433 Add Tx/Rx bps to GUI stats window
bps stats are derived from the Bps stats and represent line rate taking
into account the per packet overhead on the line with the following
assumptions -
* Link type is Ethernet
* Bps (and total Bytes) stats don't include the 4-byte FCS

Fixes #241
2018-01-31 16:03:34 +05:30

352 lines
9.7 KiB
C++

/*
Copyright (C) 2010 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include "portstatsmodel.h"
#include "portgrouplist.h"
#include <QTimer>
enum {
// XXX: The byte stats don't include FCS so include it in the overhead
kPerPacketByteOverhead = 24 // 1(SFD)+7(Preamble)+12(IPG)+4(FCS)
};
PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent)
: QAbstractTableModel(parent)
{
pgl = p;
timer = new QTimer();
connect(timer, SIGNAL(timeout()), this, SLOT(updateStats()));
timer->start(1000);
}
PortStatsModel::~PortStatsModel()
{
timer->stop();
delete timer;
}
int PortStatsModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
if (numPorts.isEmpty())
return 0;
if (numPorts.last() == 0)
return 0;
return (int) e_STAT_MAX;
}
int PortStatsModel::columnCount(const QModelIndex &parent ) const
{
if (parent.isValid())
return 0;
else
if (numPorts.isEmpty())
return 0;
else
return numPorts.last();
}
void PortStatsModel::getDomainIndexes(const QModelIndex &index,
uint &portGroupIdx, uint &portIdx) const
{
int portNum;
// TODO(LOW): Optimize using binary search: see qLowerBound()
portNum = index.column() + 1;
for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++)
if (portNum <= numPorts.at(portGroupIdx))
break;
if (portGroupIdx)
{
if (numPorts.at(portGroupIdx -1))
portIdx = (portNum - 1) - numPorts.at(portGroupIdx - 1);
else
portIdx = portNum - 1;
}
else
portIdx = portNum - 1;
//qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx);
}
QVariant PortStatsModel::data(const QModelIndex &index, int role) const
{
uint pgidx, pidx;
int row;
// Check for a valid index
if (!index.isValid())
return QVariant();
// Check for row/column limits
row = index.row();
if (row >= e_STAT_MAX)
return QVariant();
if (numPorts.isEmpty())
return QVariant();
if (index.column() >= (numPorts.last()))
return QVariant();
getDomainIndexes(index, pgidx, pidx);
// Check role
if (role == Qt::DisplayRole)
{
OstProto::PortStats stats;
stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats();
switch(row)
{
// Info
case e_INFO_USER:
return pgl->mPortGroups.at(pgidx)->mPorts[pidx]->userName();
// States
case e_LINK_STATE:
return LinkStateName.at(stats.state().link_state());
case e_TRANSMIT_STATE:
return BoolStateName.at(stats.state().is_transmit_on());
case e_CAPTURE_STATE:
return BoolStateName.at(stats.state().is_capture_on());
// Statistics
case e_STAT_FRAMES_RCVD:
return QString("%L1").arg(quint64(stats.rx_pkts()));
case e_STAT_FRAMES_SENT:
return QString("%L1").arg(quint64(stats.tx_pkts()));
case e_STAT_FRAME_SEND_RATE:
return QString("%L1").arg(quint64(stats.tx_pps()));
case e_STAT_FRAME_RECV_RATE:
return QString("%L1").arg(quint64(stats.rx_pps()));
case e_STAT_BYTES_RCVD:
return QString("%L1").arg(quint64(stats.rx_bytes()));
case e_STAT_BYTES_SENT:
return QString("%L1").arg(quint64(stats.tx_bytes()));
case e_STAT_BYTE_SEND_RATE:
return QString("%L1").arg(quint64(stats.tx_bps()));
case e_STAT_BYTE_RECV_RATE:
return QString("%L1").arg(quint64(stats.rx_bps()));
case e_STAT_BIT_SEND_RATE:
return QString("%L1").arg(quint64(
stats.tx_bps()
+ stats.tx_pps()*kPerPacketByteOverhead)*8);
case e_STAT_BIT_RECV_RATE:
return QString("%L1").arg(quint64(
stats.rx_bps()
+ stats.rx_pps()*kPerPacketByteOverhead)*8);
#if 0
case e_STAT_FRAMES_RCVD_NIC:
return stats.rx_pkts_nic();
case e_STAT_FRAMES_SENT_NIC:
return stats.tx_pkts_nic();
case e_STAT_BYTES_RCVD_NIC:
return stats.rx_bytes_nic();
case e_STAT_BYTES_SENT_NIC:
return stats.tx_bytes_nic();
#endif
case e_STAT_RX_DROPS:
return QString("%L1").arg(quint64(stats.rx_drops()));
case e_STAT_RX_ERRORS:
return QString("%L1").arg(quint64(stats.rx_errors()));
case e_STAT_RX_FIFO_ERRORS:
return QString("%L1").arg(quint64(stats.rx_fifo_errors()));
case e_STAT_RX_FRAME_ERRORS:
return QString("%L1").arg(quint64(stats.rx_frame_errors()));
default:
qWarning("%s: Unhandled stats id %d\n", __FUNCTION__,
index.row());
return 0;
}
}
else if (role == Qt::TextAlignmentRole)
{
if (row >= e_STATISTICS_START && row <= e_STATISTICS_END)
return Qt::AlignRight; // right-align numbers
else
return Qt::AlignHCenter; // center-align everything else
}
else
return QVariant();
}
QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::ToolTipRole)
{
if (orientation == Qt::Horizontal)
{
QString notes;
uint portGroupIdx, portIdx;
if (numPorts.isEmpty() || section >= numPorts.last())
return QVariant();
getDomainIndexes(index(0, section), portGroupIdx, portIdx);
notes = pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes();
if (!notes.isEmpty())
return notes;
else
return QVariant();
}
else
return QVariant();
}
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal)
{
uint portGroupIdx, portIdx;
QString portName;
if (numPorts.isEmpty() || section >= numPorts.last())
return QVariant();
getDomainIndexes(index(0, section), portGroupIdx, portIdx);
portName = QString("Port %1-%2")
.arg(pgl->mPortGroups.at(portGroupIdx)->id())
.arg(pgl->mPortGroups.at(portGroupIdx)->mPorts.at(portIdx)->id());
if (portGroupIdx < (uint) pgl->mPortGroups.size()
&& portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size())
{
if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes()
.isEmpty())
portName += " *";
}
return portName;
}
else
return PortStatName.at(section);
}
void PortStatsModel::portListFromIndex(QModelIndexList indices,
QList<PortGroupAndPortList> &portList)
{
int i, j;
QModelIndexList selectedCols(indices);
portList.clear();
//selectedCols = indices.selectedColumns();
for (i = 0; i < selectedCols.size(); i++)
{
uint portGroupIdx, portIdx;
getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx);
for (j = 0; j < portList.size(); j++)
{
if (portList[j].portGroupId == portGroupIdx)
break;
}
if (j >= portList.size())
{
// PortGroup Not found
PortGroupAndPortList p;
p.portGroupId = portGroupIdx;
p.portList.append(portIdx);
portList.append(p);
}
else
{
// PortGroup found
portList[j].portList.append(portIdx);
}
}
}
//
// Slots
//
void PortStatsModel::when_portListChanged()
{
int i, count = 0;
// recalc numPorts
while (numPorts.size())
numPorts.removeFirst();
for (i = 0; i < pgl->mPortGroups.size(); i++)
{
count += pgl->mPortGroups.at(i)->numPorts();
numPorts.append(count);
}
reset();
}
// FIXME: unused? if used, the index calculation row/column needs to be swapped
#if 0
void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/)
{
QModelIndex topLeft = index(port, 0, QModelIndex());
QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex());
emit dataChanged(topLeft, bottomRight);
}
#endif
void PortStatsModel::updateStats()
{
// Request each portgroup to fetch updated stats - the port group
// raises a signal once updated stats are available
for (int i = 0; i < pgl->mPortGroups.size(); i++)
pgl->mPortGroups[i]->getPortStats();
}
void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/)
{
// FIXME(MED): update only the changed ports, not all
QModelIndex topLeft = index(0, 0, QModelIndex());
QModelIndex bottomRight = index(rowCount()-1, columnCount()-1, QModelIndex());
emit dataChanged(topLeft, bottomRight);
}