From e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 22:34:10 +0200 Subject: Adding upstream version 4.2.2. Signed-off-by: Daniel Baumann --- ui/qt/lte_mac_statistics_dialog.cpp | 931 ++++++++++++++++++++++++++++++++++++ 1 file changed, 931 insertions(+) create mode 100644 ui/qt/lte_mac_statistics_dialog.cpp (limited to 'ui/qt/lte_mac_statistics_dialog.cpp') diff --git a/ui/qt/lte_mac_statistics_dialog.cpp b/ui/qt/lte_mac_statistics_dialog.cpp new file mode 100644 index 00000000..691128ea --- /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 + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "lte_mac_statistics_dialog.h" + +#include +#include +#include + +#include + +#include +#include + +#include +#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 rowData() const + { + return QList(); + } + +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(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(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(&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 rowData() const + { + QList 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(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(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 = "\n"; + stats_tables += QString("\n"); + stats_tables += QString("").arg(commonStats_.max_ul_ues_in_tti); + stats_tables += QString("\n").arg(commonStats_.max_dl_ues_in_tti); + + stats_tables += QString("").arg(commonStats_.mib_frames); + stats_tables += QString("\n").arg(commonStats_.sib_frames).arg(commonStats_.sib_bytes); + + stats_tables += QString("\n"). + arg(commonStats_.rar_frames). + arg(commonStats_.rar_entries); + + stats_tables += QString("\n"). + arg(commonStats_.pch_frames). + arg(commonStats_.pch_bytes). + arg(commonStats_.pch_paging_ids); + + stats_tables += QString("
System Max UL UEs/TTI=%1Max DL UEs/TTI=%1
System broadcastMIBs=%1SIBs=%1 (%2 bytes)
RACHRARs=%1 frames (%2 RARs)
PagingPCH=%1 (%2 bytes, %3 IDs)
\n"); + stats_tables += "\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(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(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(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(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(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(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(ti); + filter_expr = mac_ue_ti->filterExpression(showSRFilterCheckBox_->checkState() > Qt::Unchecked, + showRACHFilterCheckBox_->checkState() > Qt::Unchecked); + } else { + MacULDLTreeWidgetItem *mac_channels_ti = static_cast(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 LteMacStatisticsDialog::treeItemData(QTreeWidgetItem *item) const +{ + // Cast up to our type. + MacULDLTreeWidgetItem *channel_item = dynamic_cast(item); + if (channel_item) { + return channel_item->rowData(); + } + MacUETreeWidgetItem *ue_item = dynamic_cast(item); + if (ue_item) { + return ue_item->rowData(); + } + + // Need to return something.. + return QList(); +} + + +// 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(<e_mac_statistics_ui, NULL); +} + +} -- cgit v1.2.3