2868806f3f
At the moment, the Tx duration for a GUID is taken to be the largest of all tx duration for that GUID across all ports. In the future, we may consider changing it to average instead of largest.
297 lines
8.9 KiB
C++
297 lines
8.9 KiB
C++
/*
|
|
Copyright (C) 2016 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 "streamstatsmodel.h"
|
|
|
|
#include "protocol.pb.h"
|
|
#include "xqlocale.h"
|
|
|
|
#include <QApplication>
|
|
#include <QBrush>
|
|
#include <QFont>
|
|
#include <QPalette>
|
|
|
|
// XXX: Keep the enum in sync with it's string
|
|
enum {
|
|
kTxPkts,
|
|
kRxPkts,
|
|
kTxBytes,
|
|
kRxBytes,
|
|
kMaxStreamStats
|
|
};
|
|
static QStringList statTitles = QStringList()
|
|
<< "Tx Pkts"
|
|
<< "Rx Pkts"
|
|
<< "Tx Bytes"
|
|
<< "Rx Bytes";
|
|
|
|
// XXX: Keep the enum in sync with it's string
|
|
enum {
|
|
kAggrTxPkts,
|
|
kAggrRxPkts,
|
|
kAggrPktLoss,
|
|
kTxDuration,
|
|
kAvgTxFrameRate,
|
|
kAvgRxFrameRate,
|
|
kAvgTxBitRate,
|
|
kAvgRxBitRate,
|
|
kMaxAggrStreamStats
|
|
};
|
|
static QStringList aggrStatTitles = QStringList()
|
|
<< "Total\nTx Pkts"
|
|
<< "Total\nRx Pkts"
|
|
<< "Total\nPkt Loss"
|
|
<< "Duration\n(secs)"
|
|
<< "Avg\nTx PktRate"
|
|
<< "Avg\nRx PktRate"
|
|
<< "Avg\nTx BitRate"
|
|
<< "Avg\nRx BitRate";
|
|
|
|
static const uint kAggrGuid = 0xffffffff;
|
|
|
|
StreamStatsModel::StreamStatsModel(QObject *parent)
|
|
: QAbstractTableModel(parent)
|
|
{
|
|
clearStats();
|
|
}
|
|
|
|
int StreamStatsModel::rowCount(const QModelIndex &/*parent*/) const
|
|
{
|
|
return guidList_.size();
|
|
}
|
|
|
|
int StreamStatsModel::columnCount(const QModelIndex &/*parent*/) const
|
|
{
|
|
if (!portList_.size())
|
|
return 0;
|
|
|
|
return kMaxAggrStreamStats + portList_.size() * kMaxStreamStats;
|
|
}
|
|
|
|
QVariant StreamStatsModel::headerData(
|
|
int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (role != Qt::DisplayRole)
|
|
return QVariant();
|
|
|
|
switch (orientation) {
|
|
case Qt::Horizontal: // Column Header
|
|
if (section < kMaxAggrStreamStats)
|
|
return aggrStatTitles.at(section % kMaxAggrStreamStats);
|
|
|
|
section -= kMaxAggrStreamStats;
|
|
return QString("Port %1-%2\n%3")
|
|
.arg(portList_.at(section/kMaxStreamStats).first)
|
|
.arg(portList_.at(section/kMaxStreamStats).second)
|
|
.arg(statTitles.at(section % kMaxStreamStats));
|
|
case Qt::Vertical: // Row Header
|
|
if (section == (guidList_.size() - 1))
|
|
return QString("GUID Total");
|
|
return QString("Stream GUID %1")
|
|
.arg(guidList_.at(section));
|
|
default:
|
|
break;
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
QVariant StreamStatsModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (role == Qt::TextAlignmentRole)
|
|
return Qt::AlignRight;
|
|
|
|
int portColumn = index.column() - kMaxAggrStreamStats;
|
|
|
|
// Stylesheets typically don't use or set palette colors, so if
|
|
// using one, don't use palette colors
|
|
if ((role == Qt::BackgroundRole) && qApp->styleSheet().isEmpty()) {
|
|
QPalette palette = QApplication::palette();
|
|
if (index.row() == (guidList_.size() - 1)) // Aggregate Row
|
|
return palette.dark();
|
|
if (portColumn < 0) // Aggregate Column
|
|
return palette.alternateBase();
|
|
if ((portColumn/kMaxStreamStats) & 1) // Color alternate Ports
|
|
return palette.alternateBase();
|
|
}
|
|
|
|
Guid guid = guidList_.at(index.row());
|
|
if ((role == Qt::ForegroundRole && qApp->styleSheet().isEmpty())) {
|
|
QPalette palette = QApplication::palette();
|
|
if ((index.column() == kAggrPktLoss)
|
|
&& aggrGuidStats_.value(guid).pktLoss)
|
|
return palette.link();
|
|
if (index.row() == (guidList_.size() - 1)) // Aggregate Row
|
|
return palette.brightText();
|
|
}
|
|
|
|
if (role == Qt::FontRole ) {
|
|
if (index.row() == (guidList_.size() - 1)) { // Aggregate Row
|
|
QFont font;
|
|
font.setBold(true);
|
|
return font;
|
|
}
|
|
}
|
|
|
|
if (role != Qt::DisplayRole)
|
|
return QVariant();
|
|
|
|
if (index.column() < kMaxAggrStreamStats) {
|
|
int stat = index.column() % kMaxAggrStreamStats;
|
|
switch (stat) {
|
|
case kAggrRxPkts:
|
|
return QString("%L1").arg(aggrGuidStats_.value(guid).rxPkts);
|
|
case kAggrTxPkts:
|
|
return QString("%L1").arg(aggrGuidStats_.value(guid).txPkts);
|
|
case kAggrPktLoss:
|
|
return QString("%L1").arg(aggrGuidStats_.value(guid).pktLoss);
|
|
case kTxDuration:
|
|
return QString("%L1").arg(aggrGuidStats_.value(guid).txDuration);
|
|
case kAvgTxFrameRate:
|
|
return QString("%L1").arg(
|
|
aggrGuidStats_.value(guid).txPkts
|
|
/ aggrGuidStats_.value(guid).txDuration);
|
|
case kAvgRxFrameRate:
|
|
return QString("%L1").arg(
|
|
aggrGuidStats_.value(guid).rxPkts
|
|
/ aggrGuidStats_.value(guid).txDuration);
|
|
case kAvgTxBitRate:
|
|
return XLocale().toBitRateString(
|
|
(aggrGuidStats_.value(guid).txBytes
|
|
+ 24 * aggrGuidStats_.value(guid).txPkts) * 8
|
|
/ aggrGuidStats_.value(guid).txDuration);
|
|
case kAvgRxBitRate:
|
|
return XLocale().toBitRateString(
|
|
(aggrGuidStats_.value(guid).rxBytes
|
|
+ 24 * aggrGuidStats_.value(guid).rxPkts) * 8
|
|
/ aggrGuidStats_.value(guid).txDuration);
|
|
default:
|
|
break;
|
|
};
|
|
return QVariant();
|
|
}
|
|
|
|
PortGroupPort pgp = portList_.at(portColumn/kMaxStreamStats);
|
|
int stat = portColumn % kMaxStreamStats;
|
|
|
|
switch (stat) {
|
|
case kRxPkts:
|
|
return QString("%L1").arg(streamStats_.value(guid).value(pgp).rxPkts);
|
|
case kTxPkts:
|
|
return QString("%L1").arg(streamStats_.value(guid).value(pgp).txPkts);
|
|
case kRxBytes:
|
|
return QString("%L1").arg(streamStats_.value(guid).value(pgp).rxBytes);
|
|
case kTxBytes:
|
|
return QString("%L1").arg(streamStats_.value(guid).value(pgp).txBytes);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
Qt::DropActions StreamStatsModel::supportedDropActions() const
|
|
{
|
|
return Qt::IgnoreAction; // read-only model, doesn't accept any data
|
|
}
|
|
|
|
// --------------------------------------------- //
|
|
// Slots
|
|
// --------------------------------------------- //
|
|
void StreamStatsModel::clearStats()
|
|
{
|
|
#if QT_VERSION >= 0x040600
|
|
beginResetModel();
|
|
#endif
|
|
|
|
guidList_.clear();
|
|
portList_.clear();
|
|
streamStats_.clear();
|
|
aggrGuidStats_.clear();
|
|
|
|
#if QT_VERSION >= 0x040600
|
|
endResetModel();
|
|
#else
|
|
reset();
|
|
#endif
|
|
}
|
|
|
|
void StreamStatsModel::appendStreamStatsList(
|
|
quint32 portGroupId,
|
|
const OstProto::StreamStatsList *stats)
|
|
{
|
|
int n = stats->stream_stats_size();
|
|
|
|
#if QT_VERSION >= 0x040600
|
|
beginResetModel();
|
|
#endif
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
const OstProto::StreamStats &s = stats->stream_stats(i);
|
|
PortGroupPort pgp = PortGroupPort(portGroupId, s.port_id().id());
|
|
Guid guid = s.stream_guid().id();
|
|
StreamStats &ss = streamStats_[guid][pgp];
|
|
StreamStats &aggrPort = streamStats_[kAggrGuid][pgp];
|
|
AggrGuidStats &aggrGuid = aggrGuidStats_[guid];
|
|
AggrGuidStats &aggrAggr = aggrGuidStats_[kAggrGuid];
|
|
|
|
ss.rxPkts = s.rx_pkts();
|
|
ss.txPkts = s.tx_pkts();
|
|
ss.rxBytes = s.rx_bytes();
|
|
ss.txBytes = s.tx_bytes();
|
|
|
|
aggrPort.rxPkts += ss.rxPkts;
|
|
aggrPort.txPkts += ss.txPkts;
|
|
aggrPort.rxBytes += ss.rxBytes;
|
|
aggrPort.txBytes += ss.txBytes;
|
|
|
|
aggrGuid.rxPkts += ss.rxPkts;
|
|
aggrGuid.txPkts += ss.txPkts;
|
|
aggrGuid.pktLoss += ss.txPkts - ss.rxPkts;
|
|
aggrGuid.rxBytes += ss.rxBytes;
|
|
aggrGuid.txBytes += ss.txBytes;
|
|
if (s.tx_duration() > aggrGuid.txDuration)
|
|
aggrGuid.txDuration = s.tx_duration(); // XXX: use largest or avg?
|
|
|
|
aggrAggr.rxPkts += ss.rxPkts;
|
|
aggrAggr.txPkts += ss.txPkts;
|
|
aggrAggr.pktLoss += ss.txPkts - ss.rxPkts;
|
|
aggrAggr.rxBytes += ss.rxBytes;
|
|
aggrAggr.txBytes += ss.txBytes;
|
|
if (aggrGuid.txDuration > aggrAggr.txDuration)
|
|
aggrAggr.txDuration = aggrGuid.txDuration;
|
|
|
|
if (!portList_.contains(pgp))
|
|
portList_.append(pgp);
|
|
if (!guidList_.contains(guid))
|
|
guidList_.append(guid);
|
|
}
|
|
|
|
if (guidList_.size() && !guidList_.contains(kAggrGuid))
|
|
guidList_.append(kAggrGuid);
|
|
|
|
#if QT_VERSION >= 0x040600
|
|
endResetModel();
|
|
#else
|
|
reset();
|
|
#endif
|
|
|
|
// Prevent receiving any future updates from this sender
|
|
disconnect(sender(), 0, this, 0);
|
|
}
|