summaryrefslogtreecommitdiffstats
path: root/ui/qt/lte_mac_statistics_dialog.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /ui/qt/lte_mac_statistics_dialog.cpp
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ui/qt/lte_mac_statistics_dialog.cpp')
-rw-r--r--ui/qt/lte_mac_statistics_dialog.cpp931
1 files changed, 931 insertions, 0 deletions
diff --git a/ui/qt/lte_mac_statistics_dialog.cpp b/ui/qt/lte_mac_statistics_dialog.cpp
new file mode 100644
index 0000000..691128e
--- /dev/null
+++ b/ui/qt/lte_mac_statistics_dialog.cpp
@@ -0,0 +1,931 @@
+/* lte_mac_statistics_dialog.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "lte_mac_statistics_dialog.h"
+
+#include <epan/packet.h>
+#include <epan/strutil.h>
+#include <epan/tap.h>
+
+#include <epan/dissectors/packet-mac-lte.h>
+
+#include <QFormLayout>
+#include <QTreeWidgetItem>
+
+#include <ui/qt/models/percent_bar_delegate.h>
+#include "main_application.h"
+
+// TODO: have never tested in a live capture.
+
+// Whole-UE headings.
+enum {
+ col_rnti_,
+ col_type_,
+ col_ueid_,
+ // UL-specific
+ col_ul_frames_,
+ col_ul_bytes_,
+ col_ul_mb_s_,
+ col_ul_padding_percent_,
+ /* col_ul_crc_failed_, */
+ col_ul_retx_,
+ // DL-specific
+ col_dl_frames_,
+ col_dl_bytes_,
+ col_dl_mb_s_,
+ col_dl_padding_percent_,
+ col_dl_crc_failed_,
+ col_dl_retx_
+};
+
+
+// Type of tree item, so can set column headings properly.
+enum {
+ mac_whole_ue_row_type_ = 1000,
+ mac_ulsch_packet_count_row_type,
+ mac_ulsch_byte_count_row_type,
+ mac_dlsch_packet_count_row_type,
+ mac_dlsch_byte_count_row_type
+};
+
+// Calculate and return a bandwidth figure, in Mbs
+static double calculate_bw(const nstime_t *start_time, const nstime_t *stop_time,
+ guint32 bytes)
+{
+ // Can only calculate bandwidth if have time delta
+ if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) {
+ double elapsed_ms = (((double)stop_time->secs - start_time->secs) * 1000) +
+ (((double)stop_time->nsecs - start_time->nsecs) / 1000000);
+
+ // Only really meaningful if have a few frames spread over time...
+ // For now at least avoid dividing by something very close to 0.0
+ if (elapsed_ms < 2.0) {
+ return 0.0f;
+ }
+
+ // N.B. very small values will display as scientific notation, but rather that than show 0
+ // when there is some traffic..
+ return ((bytes * 8) / elapsed_ms) / 1000;
+ }
+ else {
+ return 0.0f;
+ }
+}
+
+
+// Channels (by LCID) data node. Used for UL/DL frames/bytes.
+class MacULDLTreeWidgetItem : public QTreeWidgetItem
+{
+public:
+ MacULDLTreeWidgetItem(QTreeWidgetItem *parent, unsigned ueid, unsigned rnti, int row_type) :
+ QTreeWidgetItem (parent, row_type),
+ ueid_(ueid),
+ rnti_(rnti)
+ {
+ // Init values held for all lcids to 0.
+ for (int n=0; n < MAC_LTE_DATA_LCID_COUNT_MAX; n++) {
+ lcids[n] = 0;
+ }
+
+ // Set first column to show what counts in this row mean.
+ switch (row_type) {
+ case mac_ulsch_packet_count_row_type:
+ setText(col_rnti_, "UL Packets");
+ break;
+ case mac_ulsch_byte_count_row_type:
+ setText(col_rnti_, "UL Bytes");
+ break;
+ case mac_dlsch_packet_count_row_type:
+ setText(col_rnti_, "DL Packets");
+ break;
+ case mac_dlsch_byte_count_row_type:
+ setText(col_rnti_, "DL Bytes");
+ break;
+ default:
+ // Should never get here...
+ break;
+ }
+ }
+
+ bool operator< (const QTreeWidgetItem &other) const
+ {
+ // We want rows with a UE to appear in the order they appear in the row_type enum.
+ return type() < other.type();
+ }
+
+ void draw()
+ {
+ // Show current value of counter for each LCID.
+ // N.B. fields that are set as % using percent_bar_delegate.h
+ // for UE headings don't display here...
+ for (int n=0; n < MAC_LTE_DATA_LCID_COUNT_MAX; n++) {
+ setText(col_type_+n, QString::number((uint)lcids[n]));
+ }
+ }
+
+ // Increase value held for lcid by given value.
+ void updateLCID(guint8 lcid, guint value)
+ {
+ lcids[lcid] += value;
+ }
+
+ // Generate expression for this UE and direction, also filter for SRs and RACH if indicated.
+ const QString filterExpression(bool showSR, bool showRACH) {
+ int direction = (type() == mac_dlsch_packet_count_row_type) ||
+ (type() == mac_dlsch_byte_count_row_type);
+
+ QString filter_expr;
+
+ if (showSR) {
+ filter_expr = QString("(mac-lte.sr-req and mac-lte.ueid == %1) or (").arg(ueid_);
+ }
+
+ if (showRACH) {
+ filter_expr += QString("(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or (").arg(ueid_);
+ }
+
+ // Main expression matching this UE and direction
+ filter_expr += QString("mac-lte.ueid==%1 && mac-lte.rnti==%2 && mac-lte.direction==%3").
+ arg(ueid_).arg(rnti_).arg(direction);
+
+ // Close () if open because of SR
+ if (showSR) {
+ filter_expr += QString(")");
+ }
+ // Close () if open because of RACH
+ if (showRACH) {
+ filter_expr += QString(")");
+ }
+
+ return filter_expr;
+ }
+
+ // Not showing anything for individual channels. Headings are different than from UEs, and
+ // trying to show both would be too confusing.
+ QList<QVariant> rowData() const
+ {
+ return QList<QVariant>();
+ }
+
+private:
+ unsigned ueid_;
+ unsigned rnti_;
+ int lcids[MAC_LTE_DATA_LCID_COUNT_MAX]; /* 0 to 10 and 32 to 38 */
+};
+
+
+
+// Whole UE tree item
+class MacUETreeWidgetItem : public QTreeWidgetItem
+{
+public:
+ MacUETreeWidgetItem(QTreeWidget *parent, const mac_lte_tap_info *mlt_info) :
+ QTreeWidgetItem (parent, mac_whole_ue_row_type_),
+ rnti_(0),
+ type_(0),
+ ueid_(0),
+ ul_frames_(0),
+ ul_bytes_(0),
+ ul_raw_bytes_(0),
+ ul_padding_bytes_(0),
+ ul_retx_(0),
+ dl_frames_(0),
+ dl_bytes_(0),
+ dl_raw_bytes_(0),
+ dl_padding_bytes_(0),
+ dl_crc_failed_(0),
+ dl_retx_(0)
+ {
+ // Set fixed fields.
+ rnti_ = mlt_info->rnti;
+ type_ = mlt_info->rntiType;
+ ueid_ = mlt_info->ueid;
+ setText(col_rnti_, QString::number(rnti_));
+ setText(col_type_, type_ == C_RNTI ? QObject::tr("C-RNTI") : QObject::tr("SPS-RNTI"));
+ setText(col_ueid_, QString::number(ueid_));
+
+ // Add UL/DL packet/byte count subitems.
+ addDetails();
+ }
+
+ // Does this tap-info match this existing UE item?
+ bool isMatch(const mac_lte_tap_info *mlt_info) {
+ return ((rnti_ == mlt_info->rnti) &&
+ (type_ == mlt_info->rntiType) &&
+ (ueid_ == mlt_info->ueid));
+ }
+
+ // Update this UE according to the tap info
+ void update(const mac_lte_tap_info *mlt_info) {
+
+ // Uplink.
+ if (mlt_info->direction == DIRECTION_UPLINK) {
+ if (mlt_info->isPHYRetx) {
+ ul_retx_++;
+ return;
+ }
+
+ if (mlt_info->crcStatusValid && (mlt_info->crcStatus != crc_success)) {
+ // TODO: there is not a column for this...
+ //ul_crc_errors_++;
+ return;
+ }
+
+ // Update time range
+ if (ul_frames_ == 0) {
+ ul_time_start_ = mlt_info->mac_lte_time;
+ }
+ ul_time_stop_ = mlt_info->mac_lte_time;
+
+ ul_frames_++;
+
+ // These values needed for padding % calculation.
+ ul_raw_bytes_ += mlt_info->raw_length;
+ ul_padding_bytes_ += mlt_info->padding_bytes;
+
+ // N.B. Not going to support predefined data in Qt version..
+ if (!mlt_info->isPredefinedData) {
+ for (int n=0; n < MAC_LTE_DATA_LCID_COUNT_MAX; n++) {
+ // Update UL child items
+ ul_frames_item_->updateLCID(n, mlt_info->sdus_for_lcid[n]);
+ ul_bytes_item_->updateLCID(n, mlt_info->bytes_for_lcid[n]);
+
+ ul_bytes_ += mlt_info->bytes_for_lcid[n];
+ }
+ }
+ }
+
+ // Downlink
+ else {
+ if (mlt_info->isPHYRetx) {
+ dl_retx_++;
+ return;
+ }
+
+ if (mlt_info->crcStatusValid && (mlt_info->crcStatus != crc_success)) {
+ switch (mlt_info->crcStatus) {
+ case crc_fail:
+ dl_crc_failed_++;
+ break;
+
+ default:
+ // Not a reason we currently care about.
+ break;
+ }
+ return;
+ }
+
+ // Update time range
+ if (dl_frames_ == 0) {
+ dl_time_start_ = mlt_info->mac_lte_time;
+ }
+ dl_time_stop_ = mlt_info->mac_lte_time;
+
+ dl_frames_++;
+
+ // These values needed for padding % calculation.
+ dl_raw_bytes_ += mlt_info->raw_length;
+ dl_padding_bytes_ += mlt_info->padding_bytes;
+
+ // N.B. Not going to support predefined data in Qt version..
+ if (!mlt_info->isPredefinedData) {
+ for (int n=0; n < MAC_LTE_DATA_LCID_COUNT_MAX; n++) {
+ // Update DL child items
+ dl_frames_item_->updateLCID(n, mlt_info->sdus_for_lcid[n]);
+ dl_bytes_item_->updateLCID(n, mlt_info->bytes_for_lcid[n]);
+
+ dl_bytes_ += mlt_info->bytes_for_lcid[n];
+ }
+ }
+ }
+ }
+
+ void addDetails() {
+ // Add UL/DL packet and byte counts.
+ ul_frames_item_ = new MacULDLTreeWidgetItem(this, ueid_, rnti_, mac_ulsch_packet_count_row_type);
+ ul_bytes_item_ = new MacULDLTreeWidgetItem(this, ueid_, rnti_, mac_ulsch_byte_count_row_type);
+ dl_frames_item_ = new MacULDLTreeWidgetItem(this, ueid_, rnti_, mac_dlsch_packet_count_row_type);
+ dl_bytes_item_ = new MacULDLTreeWidgetItem(this, ueid_, rnti_, mac_dlsch_byte_count_row_type);
+
+ setExpanded(false);
+ }
+
+ // Draw this UE.
+ void draw() {
+ // Fixed fields (rnti, type, ueid) won't change during lifetime of UE entry.
+
+ // Calculate bw now.
+ double UL_bw = calculate_bw(&ul_time_start_,
+ &ul_time_stop_,
+ ul_bytes_);
+ double DL_bw = calculate_bw(&dl_time_start_,
+ &dl_time_stop_,
+ dl_bytes_);
+
+ // Set columns with current values.
+ setText(col_ul_frames_, QString::number(ul_frames_));
+ setText(col_ul_bytes_, QString::number(ul_bytes_));
+ setText(col_ul_mb_s_, QString::number(UL_bw));
+ setData(col_ul_padding_percent_, Qt::UserRole,
+ QVariant::fromValue<double>(ul_raw_bytes_ ?
+ (((double)ul_padding_bytes_ / (double)ul_raw_bytes_) * 100.0) :
+ 0.0));
+ setText(col_ul_retx_, QString::number(ul_retx_));
+
+ setText(col_dl_frames_, QString::number(dl_frames_));
+ setText(col_dl_bytes_, QString::number(dl_bytes_));
+ setText(col_dl_mb_s_, QString::number(DL_bw));
+
+ setData(col_dl_padding_percent_, Qt::UserRole,
+ QVariant::fromValue<double>(dl_raw_bytes_ ?
+ (((double)dl_padding_bytes_ / (double)dl_raw_bytes_) * 100.0) :
+ 0.0));
+ setText(col_dl_crc_failed_, QString::number(dl_crc_failed_));
+ setText(col_dl_retx_, QString::number(dl_retx_));
+
+ // Draw child items with per-channel counts.
+ ul_frames_item_->draw();
+ ul_bytes_item_->draw();
+ dl_frames_item_->draw();
+ dl_bytes_item_->draw();
+ }
+
+ // < operator. Compare this item with another item, using the column we are currently sorting on.
+ bool operator< (const QTreeWidgetItem &other) const
+ {
+ if (other.type() != mac_whole_ue_row_type_) return QTreeWidgetItem::operator< (other);
+ const MacUETreeWidgetItem *other_row = static_cast<const MacUETreeWidgetItem *>(&other);
+
+ switch (treeWidget()->sortColumn()) {
+ case col_rnti_:
+ return rnti_ < other_row->rnti_;
+ case col_type_:
+ return type_ < other_row->type_;
+ case col_ueid_:
+ return ueid_ < other_row->ueid_;
+ // TODO: other fields?
+ default:
+ break;
+ }
+
+ return QTreeWidgetItem::operator< (other);
+ }
+
+ // Generate expression for this UE, also filter for SRs and RACH if indicated.
+ const QString filterExpression(bool showSR, bool showRACH) {
+ QString filter_expr;
+
+ if (showSR) {
+ filter_expr = QString("(mac-lte.sr-req and mac-lte.ueid == %1) or (").arg(ueid_);
+ }
+
+ if (showRACH) {
+ filter_expr += QString("(mac-lte.rar or (mac-lte.preamble-sent and mac-lte.ueid == %1)) or (").arg(ueid_);
+ }
+
+ // Main expression matching this UE
+ filter_expr += QString("mac-lte.ueid==%1 && mac-lte.rnti==%2").arg(ueid_).arg(rnti_);
+
+ // Close () if open because of SR
+ if (showSR) {
+ filter_expr += QString(")");
+ }
+ // Close () if open because of RACH
+ if (showRACH) {
+ filter_expr += QString(")");
+ }
+
+ return filter_expr;
+ }
+
+ // Return the UE-specific fields.
+ QList<QVariant> rowData() const
+ {
+ QList<QVariant> row_data;
+
+ // Key fields
+ row_data << rnti_ << (type_ == C_RNTI ? QObject::tr("C-RNTI") : QObject::tr("SPS-RNTI")) << ueid_;
+
+ // UL
+ row_data << ul_frames_ << ul_bytes_
+ << calculate_bw(&ul_time_start_, &ul_time_stop_, ul_bytes_)
+ << QVariant::fromValue<double>(ul_raw_bytes_ ?
+ (((double)ul_padding_bytes_ / (double)ul_raw_bytes_) * 100.0) :
+ 0.0)
+ << ul_retx_;
+
+ // DL
+ row_data << dl_frames_ << dl_bytes_
+ << calculate_bw(&dl_time_start_, &dl_time_stop_, dl_bytes_)
+ << QVariant::fromValue<double>(dl_raw_bytes_ ?
+ (((double)dl_padding_bytes_ / (double)dl_raw_bytes_) * 100.0) :
+ 0.0)
+ << dl_crc_failed_ << dl_retx_;
+ return row_data;
+ }
+
+private:
+ // Unchanging (key) fields.
+ unsigned rnti_;
+ unsigned type_;
+ unsigned ueid_;
+
+ // UL-specific.
+ unsigned ul_frames_;
+ unsigned ul_bytes_;
+ unsigned ul_raw_bytes_;
+ unsigned ul_padding_bytes_;
+ nstime_t ul_time_start_;
+ nstime_t ul_time_stop_;
+ unsigned ul_retx_;
+
+ // DL-specific.
+ unsigned dl_frames_;
+ unsigned dl_bytes_;
+ unsigned dl_raw_bytes_;
+ unsigned dl_padding_bytes_;
+ nstime_t dl_time_start_;
+ nstime_t dl_time_stop_;
+ unsigned dl_crc_failed_;
+ unsigned dl_retx_;
+
+ // Child nodes storing per-lcid counts.
+ MacULDLTreeWidgetItem *ul_frames_item_;
+ MacULDLTreeWidgetItem *ul_bytes_item_;
+ MacULDLTreeWidgetItem *dl_frames_item_;
+ MacULDLTreeWidgetItem *dl_bytes_item_;
+};
+
+
+
+
+// Label headings. Show according to which type of tree item is currently selected.
+static const QStringList mac_whole_ue_row_labels = QStringList()
+ << QObject::tr("RNTI") << QObject::tr("Type") << QObject::tr("UEId")
+ << QObject::tr("UL Frames") << QObject::tr("UL Bytes") << QObject::tr("UL MB/s")
+ << QObject::tr("UL Padding %") << QObject::tr("UL Re TX")
+ << QObject::tr("DL Frames") << QObject::tr("DL Bytes") << QObject::tr("DL MB/s")
+ << QObject::tr("DL Padding %") << QObject::tr("DL CRC Failed")
+ << QObject::tr("DL ReTX")
+ // 'Blank out' Channel-level fields
+ << QObject::tr("") << QObject::tr("") << QObject::tr("") << QObject::tr("") << QObject::tr("");
+
+static const QStringList mac_channel_counts_labels = QStringList()
+ << QObject::tr("") << QObject::tr("CCCH")
+ << QObject::tr("LCID 1") << QObject::tr("LCID 2") << QObject::tr("LCID 3")
+ << QObject::tr("LCID 4") << QObject::tr("LCID 5") << QObject::tr("LCID 6")
+ << QObject::tr("LCID 7") << QObject::tr("LCID 8") << QObject::tr("LCID 9")
+ << QObject::tr("LCID 10") << QObject::tr("LCID 32") << QObject::tr("LCID 33")
+ << QObject::tr("LCID 34") << QObject::tr("LCID 35") << QObject::tr("LCID 36")
+ << QObject::tr("LCID 37") << QObject::tr("LCID 38");
+
+
+
+//------------------------------------------------------------------------------------------
+// Dialog
+
+// Constructor.
+LteMacStatisticsDialog::LteMacStatisticsDialog(QWidget &parent, CaptureFile &cf, const char *filter) :
+ TapParameterDialog(parent, cf, HELP_STATS_LTE_MAC_TRAFFIC_DIALOG),
+ commonStatsCurrent_(false)
+{
+ setWindowSubtitle(tr("LTE Mac Statistics"));
+ loadGeometry(parent.width() * 1, parent.height() * 3 / 4, "LTEMacStatisticsDialog");
+
+ clearCommonStats();
+
+ // Create common_stats_grid to appear just above the filter area.
+ int statstree_layout_idx = verticalLayout()->indexOf(filterLayout()->widget());
+ QGridLayout *common_stats_grid = new QGridLayout();
+ // Insert into the vertical layout
+ verticalLayout()->insertLayout(statstree_layout_idx, common_stats_grid);
+ int one_em = fontMetrics().height();
+ common_stats_grid->setColumnMinimumWidth(2, one_em * 2);
+ common_stats_grid->setColumnStretch(2, 1);
+ common_stats_grid->setColumnMinimumWidth(5, one_em * 2);
+ common_stats_grid->setColumnStretch(5, 1);
+
+ // Create statistics label.
+ commonStatsLabel_ = new QLabel(this);
+ commonStatsLabel_->setObjectName("statisticsLabel");
+ commonStatsLabel_->setTextFormat(Qt::RichText);
+ commonStatsLabel_->setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse);
+ common_stats_grid->addWidget(commonStatsLabel_);
+
+
+ // Create a grid for filtering-related widgetsto also appear in layout.
+ int filter_controls_layout_idx = verticalLayout()->indexOf(filterLayout()->widget());
+ QGridLayout *filter_controls_grid = new QGridLayout();
+ // Insert into the vertical layout
+ verticalLayout()->insertLayout(filter_controls_layout_idx, filter_controls_grid);
+ filter_controls_grid->setColumnMinimumWidth(2, one_em * 2);
+ filter_controls_grid->setColumnStretch(2, 1);
+ filter_controls_grid->setColumnMinimumWidth(5, one_em * 2);
+ filter_controls_grid->setColumnStretch(5, 1);
+
+ // Add individual controls into the grid
+ showSRFilterCheckBox_ = new QCheckBox(tr("Include SR frames in filter"));
+ filter_controls_grid->addWidget(showSRFilterCheckBox_);
+ showRACHFilterCheckBox_ = new QCheckBox(tr("Include RACH frames in filter"));
+ filter_controls_grid->addWidget(showRACHFilterCheckBox_);
+
+ // Will set whole-UE headings originally.
+ updateHeaderLabels();
+
+ ul_delegate_ = new PercentBarDelegate();
+ statsTreeWidget()->setItemDelegateForColumn(col_ul_padding_percent_, ul_delegate_);
+ dl_delegate_ = new PercentBarDelegate();
+ statsTreeWidget()->setItemDelegateForColumn(col_dl_padding_percent_, dl_delegate_);
+
+ statsTreeWidget()->sortByColumn(col_rnti_, Qt::AscendingOrder);
+
+ // Set up column widths.
+ // resizeColumnToContents doesn't work well here, so set sizes manually.
+ for (int col = 0; col < statsTreeWidget()->columnCount() - 1; col++) {
+ switch (col) {
+ case col_rnti_:
+ statsTreeWidget()->setColumnWidth(col, one_em * 8);
+ break;
+ case col_ul_frames_:
+ statsTreeWidget()->setColumnWidth(col, one_em * 5);
+ break;
+ case col_ul_bytes_:
+ statsTreeWidget()->setColumnWidth(col, one_em * 5);
+ break;
+ case col_ul_mb_s_:
+ statsTreeWidget()->setColumnWidth(col, one_em * 4);
+ break;
+ case col_ul_padding_percent_:
+ statsTreeWidget()->setColumnWidth(col, one_em * 6);
+ break;
+ case col_ul_retx_:
+ statsTreeWidget()->setColumnWidth(col, one_em * 6);
+ break;
+ case col_dl_frames_:
+ statsTreeWidget()->setColumnWidth(col, one_em * 5);
+ break;
+ case col_dl_bytes_:
+ statsTreeWidget()->setColumnWidth(col, one_em * 5);
+ break;
+ case col_dl_mb_s_:
+ statsTreeWidget()->setColumnWidth(col, one_em * 4);
+ break;
+ case col_dl_padding_percent_:
+ statsTreeWidget()->setColumnWidth(col, one_em * 6);
+ break;
+ case col_dl_crc_failed_:
+ statsTreeWidget()->setColumnWidth(col, one_em * 6);
+ break;
+ case col_dl_retx_:
+ statsTreeWidget()->setColumnWidth(col, one_em * 6);
+ break;
+
+ default:
+ // The rest are numeric
+ statsTreeWidget()->setColumnWidth(col, one_em * 4);
+ statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight);
+ break;
+ }
+ }
+
+ addFilterActions();
+
+ if (filter) {
+ setDisplayFilter(filter);
+ }
+
+ // Set handler for when the tree item changes to set the appropriate labels.
+ connect(statsTreeWidget(), &QTreeWidget::itemSelectionChanged,
+ this, &LteMacStatisticsDialog::updateHeaderLabels);
+
+ // Set handler for when display filter string is changed.
+ connect(this, &LteMacStatisticsDialog::updateFilter,
+ this, &LteMacStatisticsDialog::filterUpdated);
+}
+
+// Destructor.
+LteMacStatisticsDialog::~LteMacStatisticsDialog()
+{
+ delete ul_delegate_;
+ delete dl_delegate_;
+}
+
+// Update system/common counters, and redraw if changed.
+void LteMacStatisticsDialog::updateCommonStats(const mac_lte_tap_info *tap_info)
+{
+ commonStats_.all_frames++;
+
+ // For common channels, just update global counters
+ switch (tap_info->rntiType) {
+ case P_RNTI:
+ commonStats_.pch_frames++;
+ commonStats_.pch_bytes += tap_info->single_number_of_bytes;
+ commonStats_.pch_paging_ids += tap_info->number_of_paging_ids;
+ commonStatsCurrent_ = false;
+ break;
+ case SI_RNTI:
+ commonStats_.sib_frames++;
+ commonStats_.sib_bytes += tap_info->single_number_of_bytes;
+ commonStatsCurrent_ = false;
+ break;
+ case NO_RNTI:
+ commonStats_.mib_frames++;
+ commonStatsCurrent_ = false;
+ break;
+ case RA_RNTI:
+ commonStats_.rar_frames++;
+ commonStats_.rar_entries += tap_info->number_of_rars;
+ commonStatsCurrent_ = false;
+ break;
+ case C_RNTI:
+ case SPS_RNTI:
+ // UE-specific.
+ break;
+
+ default:
+ // Error...
+ return;
+ }
+
+ // Check max UEs/tti counter
+ switch (tap_info->direction) {
+ case DIRECTION_UPLINK:
+ if (tap_info->ueInTTI > commonStats_.max_ul_ues_in_tti) {
+ commonStats_.max_ul_ues_in_tti = tap_info->ueInTTI;
+ commonStatsCurrent_ = false;
+ }
+ break;
+ case DIRECTION_DOWNLINK:
+ if (tap_info->ueInTTI > commonStats_.max_dl_ues_in_tti) {
+ commonStats_.max_dl_ues_in_tti = tap_info->ueInTTI;
+ commonStatsCurrent_ = false;
+ }
+ break;
+ }
+}
+
+// Draw current common statistics by regenerating label with current values.
+void LteMacStatisticsDialog::drawCommonStats()
+{
+ if (!commonStatsCurrent_) {
+ QString stats_tables = "<html><head></head><body>\n";
+ stats_tables += QString("<table>\n");
+ stats_tables += QString("<tr><th align=\"left\">System</th> <td align=\"left\"> Max UL UEs/TTI=%1</td>").arg(commonStats_.max_ul_ues_in_tti);
+ stats_tables += QString("<td align=\"left\">Max DL UEs/TTI=%1</td></tr>\n").arg(commonStats_.max_dl_ues_in_tti);
+
+ stats_tables += QString("<tr><th align=\"left\">System broadcast</th><td align=\"left\">MIBs=%1</td>").arg(commonStats_.mib_frames);
+ stats_tables += QString("<td align=\"left\">SIBs=%1 (%2 bytes)</td></tr>\n").arg(commonStats_.sib_frames).arg(commonStats_.sib_bytes);
+
+ stats_tables += QString("<tr><th align=\"left\">RACH</th><td align=\"left\">RARs=%1 frames (%2 RARs)</td></tr>\n").
+ arg(commonStats_.rar_frames).
+ arg(commonStats_.rar_entries);
+
+ stats_tables += QString("<tr><th align=\"left\">Paging</th><td align=\"left\">PCH=%1 (%2 bytes, %3 IDs)</td></tr>\n").
+ arg(commonStats_.pch_frames).
+ arg(commonStats_.pch_bytes).
+ arg(commonStats_.pch_paging_ids);
+
+ stats_tables += QString("</table>\n");
+ stats_tables += "</body>\n";
+
+ commonStatsLabel_->setText(stats_tables);
+
+ commonStatsCurrent_ = true;
+ }
+}
+
+void LteMacStatisticsDialog::clearCommonStats()
+{
+ memset(&commonStats_, 0, sizeof(commonStats_));
+}
+
+void LteMacStatisticsDialog::tapReset(void *ws_dlg_ptr)
+{
+ LteMacStatisticsDialog *ws_dlg = static_cast<LteMacStatisticsDialog *>(ws_dlg_ptr);
+ if (!ws_dlg) {
+ return;
+ }
+
+ ws_dlg->statsTreeWidget()->clear();
+ ws_dlg->clearCommonStats();
+}
+
+//---------------------------------------------------------------------------------------
+// Process tap info from a new packet.
+// Returns TAP_PACKET_REDRAW if a redraw is needed, TAP_PACKET_DONT_REDRAW otherwise.
+tap_packet_status LteMacStatisticsDialog::tapPacket(void *ws_dlg_ptr, struct _packet_info *, epan_dissect *, const void *mac_lte_tap_info_ptr, tap_flags_t)
+{
+ // Look up dialog and tap info.
+ LteMacStatisticsDialog *ws_dlg = static_cast<LteMacStatisticsDialog *>(ws_dlg_ptr);
+ const mac_lte_tap_info *mlt_info = (const mac_lte_tap_info *) mac_lte_tap_info_ptr;
+ if (!ws_dlg || !mlt_info) {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+
+ // Update common stats.
+ ws_dlg->updateCommonStats(mlt_info);
+
+ // Nothing more to do if tap entry isn't for a UE.
+ if ((mlt_info->rntiType != C_RNTI) && (mlt_info->rntiType != SPS_RNTI)) {
+ return TAP_PACKET_DONT_REDRAW;
+ }
+
+ // Look for an existing UE to match this tap info.
+ MacUETreeWidgetItem *mac_ue_ti = NULL;
+ for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) {
+ QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i);
+ // Make sure we're looking at a UE entry
+ if (ti->type() != mac_whole_ue_row_type_) {
+ continue;
+ }
+
+ // See if current item matches tap.
+ MacUETreeWidgetItem *cur_muds_ti = static_cast<MacUETreeWidgetItem*>(ti);
+ if (cur_muds_ti->isMatch(mlt_info)) {
+ mac_ue_ti = cur_muds_ti;
+ break;
+ }
+ }
+
+ // If don't find matching UE, create a new one.
+ if (!mac_ue_ti) {
+ mac_ue_ti = new MacUETreeWidgetItem(ws_dlg->statsTreeWidget(), mlt_info);
+ for (int col = 0; col < ws_dlg->statsTreeWidget()->columnCount(); col++) {
+ // int QTreeWidgetItem::textAlignment(int column) const
+ // Returns the text alignment for the label in the given column.
+ // Note: This function returns an int for historical reasons. It will be corrected to return Qt::Alignment in Qt 7.
+ mac_ue_ti->setTextAlignment(col, static_cast<Qt::Alignment>(ws_dlg->statsTreeWidget()->headerItem()->textAlignment(col)));
+ }
+ }
+
+ // Update the UE item with info from tap!
+ mac_ue_ti->update(mlt_info);
+ return TAP_PACKET_REDRAW;
+}
+
+// Return total number of frames tapped.
+unsigned LteMacStatisticsDialog::getFrameCount()
+{
+ return commonStats_.all_frames;
+}
+
+void LteMacStatisticsDialog::tapDraw(void *ws_dlg_ptr)
+{
+ // Look up dialog.
+ LteMacStatisticsDialog *ws_dlg = static_cast<LteMacStatisticsDialog *>(ws_dlg_ptr);
+ if (!ws_dlg) {
+ return;
+ }
+
+ // Go over all of the top-level items.
+ for (int i = 0; i < ws_dlg->statsTreeWidget()->topLevelItemCount(); i++) {
+ // Get item, make sure its of the whole-UE type.
+ QTreeWidgetItem *ti = ws_dlg->statsTreeWidget()->topLevelItem(i);
+ if (ti->type() != mac_whole_ue_row_type_) {
+ continue;
+ }
+
+ // Tell the UE item to draw itself.
+ MacUETreeWidgetItem *mac_ue_ti = static_cast<MacUETreeWidgetItem*>(ti);
+ mac_ue_ti->draw();
+ }
+
+ ws_dlg->drawCommonStats();
+
+ // Update title
+ ws_dlg->setWindowSubtitle(QString("LTE Mac Statistics (%1 UEs, %2 frames)").
+ arg(ws_dlg->statsTreeWidget()->topLevelItemCount()).arg(ws_dlg->getFrameCount()));
+}
+
+const QString LteMacStatisticsDialog::filterExpression()
+{
+ QString filter_expr;
+
+ if (statsTreeWidget()->selectedItems().count() > 0) {
+ QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0];
+
+ if (ti->type() == mac_whole_ue_row_type_) {
+ MacUETreeWidgetItem *mac_ue_ti = static_cast<MacUETreeWidgetItem*>(ti);
+ filter_expr = mac_ue_ti->filterExpression(showSRFilterCheckBox_->checkState() > Qt::Unchecked,
+ showRACHFilterCheckBox_->checkState() > Qt::Unchecked);
+ } else {
+ MacULDLTreeWidgetItem *mac_channels_ti = static_cast<MacULDLTreeWidgetItem*>(ti);
+ filter_expr = mac_channels_ti->filterExpression(showSRFilterCheckBox_->checkState() > Qt::Unchecked,
+ showRACHFilterCheckBox_->checkState() > Qt::Unchecked);
+ }
+ }
+ return filter_expr;
+}
+
+void LteMacStatisticsDialog::fillTree()
+{
+ if (!registerTapListener("mac-lte",
+ this,
+ displayFilter_.toLatin1().data(),
+ TL_REQUIRES_NOTHING,
+ tapReset,
+ tapPacket,
+ tapDraw)) {
+ reject();
+ return;
+ }
+
+ cap_file_.retapPackets();
+ tapDraw(this);
+ removeTapListeners();
+}
+
+void LteMacStatisticsDialog::updateHeaderLabels()
+{
+ if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == mac_whole_ue_row_type_) {
+ // Whole-UE labels
+ statsTreeWidget()->setHeaderLabels(mac_whole_ue_row_labels);
+ } else if (statsTreeWidget()->selectedItems().count() > 0) {
+ // ULDL labels
+ switch (statsTreeWidget()->selectedItems()[0]->type()) {
+ case mac_ulsch_packet_count_row_type:
+ case mac_ulsch_byte_count_row_type:
+ case mac_dlsch_packet_count_row_type:
+ case mac_dlsch_byte_count_row_type:
+ statsTreeWidget()->setHeaderLabels(mac_channel_counts_labels);
+ break;
+
+ default:
+ break;
+ }
+ }
+ else {
+ // Nothing selected yet, but set whole-UE labels.
+ statsTreeWidget()->setHeaderLabels(mac_whole_ue_row_labels);
+ }
+}
+
+void LteMacStatisticsDialog::captureFileClosing()
+{
+ remove_tap_listener(this);
+
+ WiresharkDialog::captureFileClosing();
+}
+
+// Store filter from signal.
+void LteMacStatisticsDialog::filterUpdated(QString filter)
+{
+ displayFilter_ = filter;
+}
+
+// Get the item for the row, depending upon the type of tree item.
+QList<QVariant> LteMacStatisticsDialog::treeItemData(QTreeWidgetItem *item) const
+{
+ // Cast up to our type.
+ MacULDLTreeWidgetItem *channel_item = dynamic_cast<MacULDLTreeWidgetItem*>(item);
+ if (channel_item) {
+ return channel_item->rowData();
+ }
+ MacUETreeWidgetItem *ue_item = dynamic_cast<MacUETreeWidgetItem*>(item);
+ if (ue_item) {
+ return ue_item->rowData();
+ }
+
+ // Need to return something..
+ return QList<QVariant>();
+}
+
+
+// Stat command + args
+
+static void
+lte_mac_statistics_init(const char *args, void*) {
+ QStringList args_l = QString(args).split(',');
+ QByteArray filter;
+ if (args_l.length() > 2) {
+ filter = QStringList(args_l.mid(2)).join(",").toUtf8();
+ }
+ mainApp->emitStatCommandSignal("LteMacStatistics", filter.constData(), NULL);
+}
+
+static stat_tap_ui lte_mac_statistics_ui = {
+ REGISTER_STAT_GROUP_TELEPHONY_LTE,
+ QT_TRANSLATE_NOOP("LteMacStatisticsDialog", "MAC Statistics"),
+ "mac-lte,stat",
+ lte_mac_statistics_init,
+ 0,
+ NULL
+};
+
+extern "C" {
+
+void register_tap_listener_qt_lte_mac_statistics(void);
+
+void
+register_tap_listener_qt_lte_mac_statistics(void)
+{
+ register_stat_tap_ui(&lte_mac_statistics_ui, NULL);
+}
+
+}