diff options
author | Martin Mathieson <martin.r.mathieson@googlemail.com> | 2015-10-10 15:53:45 -0700 |
---|---|---|
committer | Martin Mathieson <martin.r.mathieson@googlemail.com> | 2015-10-11 21:59:45 +0000 |
commit | 3221dbf542217cea5acd9ec764cf4779edaf65e8 (patch) | |
tree | 8999d3a38ee9a687737e35cdd39bde1ea7d18a2e /ui/qt | |
parent | a6673b3fde1cac904a405cb1125d547d064d3aa7 (diff) |
LTE RLC graphs - initial version
Change-Id: Ic5f2c353ae1f787ac19cb575a938cb093ff5f6dc
Reviewed-on: https://code.wireshark.org/review/10930
Petri-Dish: Martin Mathieson <martin.r.mathieson@googlemail.com>
Reviewed-by: Martin Mathieson <martin.r.mathieson@googlemail.com>
Diffstat (limited to 'ui/qt')
-rw-r--r-- | ui/qt/CMakeLists.txt | 3 | ||||
-rw-r--r-- | ui/qt/Makefile.am | 2 | ||||
-rw-r--r-- | ui/qt/Makefile.common | 4 | ||||
-rw-r--r-- | ui/qt/Wireshark.pro | 3 | ||||
-rw-r--r-- | ui/qt/lte_rlc_graph_dialog.cpp | 643 | ||||
-rw-r--r-- | ui/qt/lte_rlc_graph_dialog.h | 118 | ||||
-rw-r--r-- | ui/qt/lte_rlc_graph_dialog.ui | 275 | ||||
-rw-r--r-- | ui/qt/main_window.cpp | 1 | ||||
-rw-r--r-- | ui/qt/main_window.h | 1 | ||||
-rw-r--r-- | ui/qt/main_window.ui | 11 | ||||
-rw-r--r-- | ui/qt/main_window_slots.cpp | 7 |
11 files changed, 1067 insertions, 1 deletions
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index d214b4826f..376ad38b87 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -81,6 +81,7 @@ set(WIRESHARK_QT_HEADERS lbm_lbtru_transport_dialog.h lbm_stream_dialog.h lbm_uimflow_dialog.h + lte_rlc_graph_dialog.h lte_mac_statistics_dialog.h lte_rlc_statistics_dialog.h main_status_bar.h @@ -224,6 +225,7 @@ set(WIRESHARK_QT_SRC lbm_lbtru_transport_dialog.cpp lbm_stream_dialog.cpp lbm_uimflow_dialog.cpp + lte_rlc_graph_dialog.cpp lte_mac_statistics_dialog.cpp lte_rlc_statistics_dialog.cpp main_status_bar.cpp @@ -365,6 +367,7 @@ set(WIRESHARK_QT_UI lbm_lbtru_transport_dialog.ui lbm_stream_dialog.ui lbm_uimflow_dialog.ui + lte_rlc_graph_dialog.ui main_welcome.ui main_window.ui main_window_preferences_frame.ui diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am index 13bd0d2db7..fb7f2f592f 100644 --- a/ui/qt/Makefile.am +++ b/ui/qt/Makefile.am @@ -202,6 +202,8 @@ lbm_stream_dialog.$(OBJEXT): ui_lbm_stream_dialog.h lbm_uimflow_dialog.$(OBJEXT): ui_lbm_uimflow_dialog.h +lte_rlc_graph_dialog.$(OBJEXT): ui_lte_rlc_graph_dialog.h + main_welcome.$(OBJEXT): ui_main_welcome.h main_window.$(OBJEXT): ui_main_window.h diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common index 1af30ecbfb..409dca819c 100644 --- a/ui/qt/Makefile.common +++ b/ui/qt/Makefile.common @@ -66,6 +66,7 @@ NODIST_GENERATED_HEADER_FILES = \ ui_lbm_lbtru_transport_dialog.h \ ui_lbm_stream_dialog.h \ ui_lbm_uimflow_dialog.h \ + ui_lte_rlc_graph_dialog.h \ ui_main_welcome.h \ ui_main_window.h \ ui_main_window_preferences_frame.h \ @@ -202,6 +203,7 @@ MOC_HDRS = \ lbm_stream_dialog.h \ lbm_uimflow_dialog.h \ lte_mac_statistics_dialog.h \ + lte_rlc_graph_dialog.h \ lte_rlc_statistics_dialog.h \ main_status_bar.h \ main_welcome.h \ @@ -310,6 +312,7 @@ UI_FILES = \ lbm_lbtru_transport_dialog.ui \ lbm_stream_dialog.ui \ lbm_uimflow_dialog.ui \ + lte_rlc_graph_dialog.ui \ main_welcome.ui \ main_window.ui \ main_window_preferences_frame.ui \ @@ -456,6 +459,7 @@ WIRESHARK_QT_SRC = \ lbm_stream_dialog.cpp \ lbm_uimflow_dialog.cpp \ lte_mac_statistics_dialog.cpp \ + lte_rlc_graph_dialog.cpp \ lte_rlc_statistics_dialog.cpp \ main_status_bar.cpp \ main_welcome.cpp \ diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro index a056d5b6b9..a2361c9124 100644 --- a/ui/qt/Wireshark.pro +++ b/ui/qt/Wireshark.pro @@ -245,6 +245,7 @@ FORMS += \ lbm_lbtru_transport_dialog.ui \ lbm_stream_dialog.ui \ lbm_uimflow_dialog.ui \ + lte_rlc_graph_dialog.ui \ main_welcome.ui \ main_window.ui \ main_window_preferences_frame.ui \ @@ -329,6 +330,7 @@ HEADERS += $$HEADERS_WS_C \ lbm_stream_dialog.h \ lbm_uimflow_dialog.h \ lte_mac_statistics_dialog.h \ + lte_rlc_graph_dialog.h \ lte_rlc_statistics_dialog.h \ main_window_preferences_frame.h \ manage_interfaces_dialog.h \ @@ -722,6 +724,7 @@ SOURCES += \ lbm_stream_dialog.cpp \ lbm_uimflow_dialog.cpp \ lte_mac_statistics_dialog.cpp \ + lte_rlc_graph_dialog.cpp \ lte_rlc_statistics_dialog.cpp \ main_status_bar.cpp \ main_welcome.cpp \ diff --git a/ui/qt/lte_rlc_graph_dialog.cpp b/ui/qt/lte_rlc_graph_dialog.cpp new file mode 100644 index 0000000000..8b60df7e22 --- /dev/null +++ b/ui/qt/lte_rlc_graph_dialog.cpp @@ -0,0 +1,643 @@ +/* lte_rlc_graph_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "lte_rlc_graph_dialog.h" +#include <ui_lte_rlc_graph_dialog.h> + +#include <epan/epan.h> +#include <epan/epan_dissect.h> +#include <epan/tap.h> +#include <epan/stat_tap_ui.h> + +#include <epan/tvbuff-int.h> +#include <epan/tvbuff.h> +#include <frame_tvbuff.h> + +#include "tango_colors.h" + +#include <QMenu> +#include <QRubberBand> + +#include "qt_ui_utils.h" +#include "wireshark_application.h" +#include "simple_dialog.h" + +#include "globals.h" + +#include <epan/dissectors/packet-rlc-lte.h> + +#include <ui/tap-rlc-graph.h> + +// TODO: +// - better handling of zooming (select area like TCP and/or Jim's patch for 1 dimension at a time) +// - get launched from RLC stats for a pre-known channel +// - how to avoid panning or zooming out to -ve (x or y axis) +// - goto packet functionality when click on segments + +const QRgb graph_color_seq = 0x000000; // Black. +const QRgb graph_color_ack = tango_sky_blue_4; // Blue for ACK lines +const QRgb graph_color_nack = tango_scarlet_red_3; // Red for NACKs +const QRgb graph_color_resegmented = 0x888888; // Grey for resegmentations + +// Size of selectable packet points in the base graph +const double pkt_point_size_ = 3.0; + + +// Constructor. +LteRlcGraphDialog::LteRlcGraphDialog(QWidget &parent, CaptureFile &cf) : + WiresharkDialog(parent, cf), + ui(new Ui::LteRlcGraphDialog), + mouse_drags_(true), + rubber_band_(NULL) +{ + ui->setupUi(this); + + // XXX Use recent settings instead + resize(parent.width() * 4 / 5, parent.height() * 3 / 4); + + QCustomPlot *rp = ui->rlcPlot; + rp->xAxis->setLabel(tr("Time")); + rp->yAxis->setLabel(tr("Sequence Number")); + + // TODO: don't want all of these... + ctx_menu_ = new QMenu(this); + ctx_menu_->addAction(ui->actionZoomIn); + ctx_menu_->addAction(ui->actionZoomOut); + ctx_menu_->addAction(ui->actionReset); + ctx_menu_->addSeparator(); + ctx_menu_->addAction(ui->actionMoveRight10); + ctx_menu_->addAction(ui->actionMoveLeft10); + ctx_menu_->addAction(ui->actionMoveUp10); + ctx_menu_->addAction(ui->actionMoveUp100); + ctx_menu_->addAction(ui->actionMoveDown10); + ctx_menu_->addAction(ui->actionMoveDown100); + ctx_menu_->addAction(ui->actionMoveRight1); + ctx_menu_->addAction(ui->actionMoveLeft1); + ctx_menu_->addAction(ui->actionMoveUp1); + ctx_menu_->addAction(ui->actionMoveDown1); +// ctx_menu_.addSeparator(); +// ctx_menu_->addAction(ui->actionGoToPacket); + ctx_menu_->addSeparator(); + ctx_menu_->addAction(ui->actionDragZoom); +// ctx_menu_->addAction(ui->actionToggleTimeOrigin); + ctx_menu_->addAction(ui->actionCrosshairs); + + // Zero out this struct. + memset(&graph_, 0, sizeof(graph_)); + + // If no channel chosen already, try to use currently selected frame. + findChannel(); + + // Set window title here. + if (graph_.channelSet) { + QString dlg_title = tr("LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5)") + .arg(graph_.ueid) + .arg((graph_.channelType == CHANNEL_TYPE_SRB) ? "SRB" : "DRB") + .arg(graph_.channelId) + .arg((graph_.direction == DIRECTION_UPLINK) ? "UL" : "DL") + .arg((graph_.rlcMode == RLC_UM_MODE) ? "UM" : "AM"); + setWindowTitle(dlg_title); + } + else { + setWindowTitle(tr("LTE RLC Graph - no channel selected")); + } + + // Set colours/styles for each of the traces on the graph. + QCustomPlot *sp = ui->rlcPlot; + base_graph_ = sp->addGraph(); // All: Selectable segments + base_graph_->setPen(QPen(QBrush(graph_color_seq), 0.25)); + + reseg_graph_ = sp->addGraph(); + reseg_graph_->setPen(QPen(QBrush(graph_color_resegmented), 0.25)); + + acks_graph_ = sp->addGraph(); + acks_graph_->setPen(QPen(QBrush(graph_color_ack), 1.0)); + + nacks_graph_ = sp->addGraph(); + nacks_graph_->setPen(QPen(QBrush(graph_color_nack), 0.25)); + + connect(rp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*))); + connect(rp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); + connect(rp, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*))); + + // Extract the data that the graph can use. + fillGraph(); +} + +// Destructor +LteRlcGraphDialog::~LteRlcGraphDialog() +{ + delete ui; +} + +// See if the given segment matches the channel this graph is plotting. +bool LteRlcGraphDialog::compareHeaders(rlc_segment *seg) +{ + return compare_rlc_headers(graph_.ueid, graph_.channelType, + graph_.channelId, graph_.rlcMode, graph_.direction, + seg->ueid, seg->channelType, + seg->channelId, seg->rlcMode, seg->direction, + seg->isControlPDU); +} + +// Look for channel to plot based upon currently selected frame. +void LteRlcGraphDialog::findChannel() +{ + char *err_string = NULL; + gboolean free_err_string = FALSE; + rlc_graph_segment_list_free(&graph_); + if (!rlc_graph_segment_list_get(cap_file_.capFile(), &graph_, graph_.channelSet, + &err_string, &free_err_string)) { + // Pop up an error box to report error. + simple_error_message_box("%s", err_string); + if (free_err_string) { + g_free(err_string); + } + } +} + +// Fill in graph data based upon what was read into the rlc_graph struct. +void LteRlcGraphDialog::fillGraph() +{ + QCustomPlot *sp = ui->rlcPlot; + + // We should always have 4 graphs, but cover case if no channel was chosen. + if (sp->graphCount() < 1) { + return; + } + + base_graph_->setLineStyle(QCPGraph::lsNone); // dot + reseg_graph_->setLineStyle(QCPGraph::lsNone); // dot + acks_graph_->setLineStyle(QCPGraph::lsStepLeft); // to get step effect... + nacks_graph_->setLineStyle(QCPGraph::lsNone); // dot, but bigger. + + // Will show all graphs with data we find. + for (int i = 0; i < sp->graphCount(); i++) { + sp->graph(i)->clearData(); + sp->graph(i)->setVisible(true); + } + + // NACKs are shown bigger than others. + base_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_)); + reseg_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_)); + acks_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_)); + nacks_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_*2)); + + + ts_offset_ = 0; + seq_offset_ = 0; // TODO: needed? + bool first = true; + + // Map timestamps -> segments in first pass. + time_stamp_map_.clear(); + for (struct rlc_segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + if (!compareHeaders(seg)) { + continue; + } + double ts = seg->rel_secs + seg->rel_usecs / 1000000.0; + if (first) { + // Take note of first sequence number seen. + if (seq_origin_zero_) { + seq_offset_ = seg->SN; + } + first = false; + } + time_stamp_map_.insertMulti(ts - ts_offset_, seg); + } + + // Now sequence numbers. + QVector<double> seq_time, seq, + reseg_seq_time, reseg_seq, + acks_time, acks, + nacks_time, nacks; + for (struct rlc_segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + double ts = (seg->rel_secs + seg->rel_usecs / 1000000.0) - ts_offset_; + if (compareHeaders(seg)) { + if (!seg->isControlPDU) { + // Data + if (seg->isResegmented) { + reseg_seq_time.append(ts); + reseg_seq.append(seg->SN - seq_offset_); + } + else { + seq_time.append(ts); + seq.append(seg->SN - seq_offset_); + } + } + else { + // Status (ACKs/NACKs) + acks_time.append(ts); + acks.append(seg->ACKNo-1); + for (int n=0; n < seg->noOfNACKs; n++) { + nacks_time.append(ts); + nacks.append(seg->NACKs[n]); + } + } + } + } + + // Add the data from the graphs. + base_graph_->setData(seq_time, seq); + reseg_graph_->setData(reseg_seq_time, reseg_seq); + acks_graph_->setData(acks_time, acks); + nacks_graph_->setData(nacks_time, nacks); + + sp->setEnabled(true); + + // Auto-size... + mouseMoved(NULL); + resetAxes(); + + // XXX QCustomPlot doesn't seem to draw any sort of focus indicator. + sp->setFocus(); +} + +// Copied from TCP graphs, seems like a kludge to get the graph resized immediately after it is built... +void LteRlcGraphDialog::showEvent(QShowEvent *) +{ + resetAxes(); +} + +// Respond to a key press. +void LteRlcGraphDialog::keyPressEvent(QKeyEvent *event) +{ + int pan_pixels = event->modifiers() & Qt::ShiftModifier ? 1 : 10; + + switch(event->key()) { + case Qt::Key_Minus: + case Qt::Key_Underscore: // Shifted minus on U.S. keyboards + case Qt::Key_O: // GTK+ + case Qt::Key_R: + zoomAxes(false); + break; + case Qt::Key_Plus: + case Qt::Key_Equal: // Unshifted plus on U.S. keyboards + case Qt::Key_I: // GTK+ + zoomAxes(true); + break; + + case Qt::Key_Right: + case Qt::Key_L: + panAxes(pan_pixels, 0); + break; + case Qt::Key_Left: + case Qt::Key_H: + panAxes(-1 * pan_pixels, 0); + break; + case Qt::Key_Up: + case Qt::Key_K: + panAxes(0, pan_pixels); + break; + case Qt::Key_Down: + case Qt::Key_J: + panAxes(0, -1 * pan_pixels); + break; + + case Qt::Key_PageUp: + panAxes(0, 20 * pan_pixels); + break; + case Qt::Key_PageDown: + panAxes(0, -20 * pan_pixels); + break; + + case Qt::Key_Space: +// toggleTracerStyle(); + break; + + case Qt::Key_0: + case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards + case Qt::Key_Home: + resetAxes(); + break; + + case Qt::Key_G: +// on_actionGoToPacket_triggered(); + break; + case Qt::Key_T: +// on_actionToggleTimeOrigin_triggered(); + break; + case Qt::Key_Z: + on_actionDragZoom_triggered(); + break; + } + + WiresharkDialog::keyPressEvent(event); +} + +void LteRlcGraphDialog::zoomAxes(bool in) +{ + QCustomPlot *rp = ui->rlcPlot; + double h_factor = rp->axisRect()->rangeZoomFactor(Qt::Horizontal); + double v_factor = rp->axisRect()->rangeZoomFactor(Qt::Vertical); + + if (!in) { + h_factor = pow(h_factor, -1); + v_factor = pow(v_factor, -1); + } + + rp->xAxis->scaleRange(h_factor, rp->xAxis->range().center()); + rp->yAxis->scaleRange(v_factor, rp->yAxis->range().center()); + rp->replot(); +} + +void LteRlcGraphDialog::panAxes(int x_pixels, int y_pixels) +{ + QCustomPlot *rp = ui->rlcPlot; + double h_pan = 0.0; + double v_pan = 0.0; + + h_pan = rp->xAxis->range().size() * x_pixels / rp->xAxis->axisRect()->width(); + v_pan = rp->yAxis->range().size() * y_pixels / rp->yAxis->axisRect()->height(); + // The GTK+ version won't pan unless we're zoomed. Should we do the same here? + if (h_pan) { + rp->xAxis->moveRange(h_pan); + rp->replot(); + } + if (v_pan) { + rp->yAxis->moveRange(v_pan); + rp->replot(); + } +} + +// Don't accidentally zoom into a 1x1 rect if you happen to click on the graph +// in zoom mode. +const int min_zoom_pixels_ = 20; +QRectF LteRlcGraphDialog::getZoomRanges(QRect zoom_rect) +{ + QRectF zoom_ranges = QRectF(); + + if (zoom_rect.width() < min_zoom_pixels_ && zoom_rect.height() < min_zoom_pixels_) { + return zoom_ranges; + } + + QCustomPlot *rp = ui->rlcPlot; + QRect zr = zoom_rect.normalized(); + QRect ar = rp->axisRect()->rect(); + if (ar.intersects(zr)) { + QRect zsr = ar.intersected(zr); + zoom_ranges.setX(rp->xAxis->range().lower + + rp->xAxis->range().size() * (zsr.left() - ar.left()) / ar.width()); + zoom_ranges.setWidth(rp->xAxis->range().size() * zsr.width() / ar.width()); + + // QRects grow down + zoom_ranges.setY(rp->yAxis->range().lower + + rp->yAxis->range().size() * (ar.bottom() - zsr.bottom()) / ar.height()); + zoom_ranges.setHeight(rp->yAxis->range().size() * zsr.height() / ar.height()); + } + return zoom_ranges; +} + +void LteRlcGraphDialog::graphClicked(QMouseEvent *event) +{ + QCustomPlot *rp = ui->rlcPlot; + + if (event->button() == Qt::RightButton) { + // XXX We should find some way to get rlcPlot to handle a + // contextMenuEvent instead. + ctx_menu_->exec(event->globalPos()); + } else if (mouse_drags_) { + if (rp->axisRect()->rect().contains(event->pos())) { + rp->setCursor(QCursor(Qt::ClosedHandCursor)); + } +// on_actionGoToPacket_triggered(); + } else { + if (!rubber_band_) { + rubber_band_ = new QRubberBand(QRubberBand::Rectangle, rp); + } + rb_origin_ = event->pos(); + rubber_band_->setGeometry(QRect(rb_origin_, QSize())); + rubber_band_->show(); + } + rp->setFocus(); +} + +void LteRlcGraphDialog::mouseMoved(QMouseEvent *event) +{ + QCustomPlot *rp = ui->rlcPlot; + QString hint; + Qt::CursorShape shape = Qt::ArrowCursor; + + if (event) { + if (event->buttons().testFlag(Qt::LeftButton)) { + if (mouse_drags_) { + shape = Qt::ClosedHandCursor; + } else { + shape = Qt::CrossCursor; + } + } else if (rp->axisRect()->rect().contains(event->pos())) { + if (mouse_drags_) { + shape = Qt::OpenHandCursor; + } else { + shape = Qt::CrossCursor; + } + } + rp->setCursor(QCursor(shape)); + } + + if (mouse_drags_) { +// double ts = 0; +// packet_num_ = 0; +// int interval_packet = -1; + +// if (event && tracer_->graph()) { +// tracer_->setGraphKey(rp->xAxis->pixelToCoord(event->pos().x())); +// ts = tracer_->position->key(); + +// QTreeWidgetItem *ti = ui->graphTreeWidget->topLevelItem(0); +// IOGraph *iog = NULL; +// if (ti) { +// iog = ti->data(name_col_, Qt::UserRole).value<IOGraph *>(); +// interval_packet = iog->packetFromTime(ts); +// } +// } + +// if (interval_packet < 0) { +// hint += tr("Hover over the graph for details."); +// } else { +// QString msg = tr("No packets in interval"); +// QString val; +// if (interval_packet > 0) { +// packet_num_ = (guint32) interval_packet; +// msg = tr("%1 %2") +// .arg(!file_closed_ ? tr("Click to select packet") : tr("Packet")) +// .arg(packet_num_); +// val = " = " + QString::number(tracer_->position->value(), 'g', 4); +// } +// hint += tr("%1 (%2s%3).") +// .arg(msg) +// .arg(QString::number(ts, 'g', 4)) +// .arg(val); +// } + rp->replot(); + } else { + if (event && rubber_band_ && rubber_band_->isVisible()) { + rubber_band_->setGeometry(QRect(rb_origin_, event->pos()).normalized()); + QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos())); + if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) { + hint += tr("Release to zoom, x = %1 to %2, y = %3 to %4") + .arg(zoom_ranges.x()) + .arg(zoom_ranges.x() + zoom_ranges.width()) + .arg(zoom_ranges.y()) + .arg(zoom_ranges.y() + zoom_ranges.height()); + } else { + hint += tr("Unable to select range."); + } + } else { + hint += tr("Click to select a portion of the graph."); + } + } + + hint.prepend("<small><i>"); + hint.append("</i></small>"); + ui->hintLabel->setText(hint); +} + +void LteRlcGraphDialog::mouseReleased(QMouseEvent *event) +{ + QCustomPlot *rp = ui->rlcPlot; + if (rubber_band_) { + rubber_band_->hide(); + if (!mouse_drags_) { + QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos())); + if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) { + rp->xAxis->setRangeLower(zoom_ranges.x()); + rp->xAxis->setRangeUpper(zoom_ranges.x() + zoom_ranges.width()); + rp->yAxis->setRangeLower(zoom_ranges.y()); + rp->yAxis->setRangeUpper(zoom_ranges.y() + zoom_ranges.height()); + rp->replot(); + } + } + } else if (rp->cursor().shape() == Qt::ClosedHandCursor) { + rp->setCursor(QCursor(Qt::OpenHandCursor)); + } +} + +void LteRlcGraphDialog::resetAxes() +{ + QCustomPlot *rp = ui->rlcPlot; + + QCPRange x_range = rp->xAxis->scaleType() == QCPAxis::stLogarithmic ? + rp->xAxis->range().sanitizedForLogScale() : rp->xAxis->range(); + + double pixel_pad = 10.0; // per side + + rp->rescaleAxes(true); + base_graph_->rescaleValueAxis(false, true); + + double axis_pixels = rp->xAxis->axisRect()->width(); + rp->xAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, x_range.center()); + + axis_pixels = rp->yAxis->axisRect()->height(); + rp->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, rp->yAxis->range().center()); + + rp->replot(); +} + +void LteRlcGraphDialog::on_actionReset_triggered() +{ + resetAxes(); +} + +void LteRlcGraphDialog::on_actionZoomIn_triggered() +{ + zoomAxes(true); +} + +void LteRlcGraphDialog::on_actionZoomOut_triggered() +{ + zoomAxes(false); +} + +void LteRlcGraphDialog::on_actionMoveUp10_triggered() +{ + panAxes(0, 10); +} + +void LteRlcGraphDialog::on_actionMoveUp100_triggered() +{ + panAxes(0, 100); +} + +void LteRlcGraphDialog::on_actionMoveLeft10_triggered() +{ + panAxes(-10, 0); +} + +void LteRlcGraphDialog::on_actionMoveRight10_triggered() +{ + panAxes(10, 0); +} + +void LteRlcGraphDialog::on_actionMoveDown10_triggered() +{ + panAxes(0, -10); +} + +void LteRlcGraphDialog::on_actionMoveDown100_triggered() +{ + panAxes(0, -100); +} + +void LteRlcGraphDialog::on_actionMoveUp1_triggered() +{ + panAxes(0, 1); +} + +void LteRlcGraphDialog::on_actionMoveLeft1_triggered() +{ + panAxes(-1, 0); +} + +void LteRlcGraphDialog::on_actionMoveRight1_triggered() +{ + panAxes(1, 0); +} + +void LteRlcGraphDialog::on_actionMoveDown1_triggered() +{ + panAxes(0, -1); +} + +void LteRlcGraphDialog::on_actionDragZoom_triggered() +{ +// if (mouse_drags_) { +// ui->zoomRadioButton->toggle(); +// } else { +// ui->dragRadioButton->toggle(); +// } +} + + +// No need to register tap listeners here. This is done +// in calls to the common functions in ui/tap-rlc-graph.c + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/lte_rlc_graph_dialog.h b/ui/qt/lte_rlc_graph_dialog.h new file mode 100644 index 0000000000..eaadfc4b85 --- /dev/null +++ b/ui/qt/lte_rlc_graph_dialog.h @@ -0,0 +1,118 @@ +/* lte_rlc_graph_dialog.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef LTE_RLC_GRAPH_DIALOG_H +#define LTE_RLC_GRAPH_DIALOG_H + +#include "wireshark_dialog.h" +#include <ui/tap-rlc-graph.h> + +#include "qcustomplot.h" + +class QMenu; +class QRubberBand; + +namespace Ui { +class LteRlcGraphDialog; +} + +class LteRlcGraphDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + // TODO: will need to add another constructor option to give channel explicitly, + // rather than find in currently selected packet, for when launch graph from + // RLC statistics dialog. + explicit LteRlcGraphDialog(QWidget &parent, CaptureFile &cf); + ~LteRlcGraphDialog(); + +protected: + void showEvent(QShowEvent *event); + void keyPressEvent(QKeyEvent *event); + +private: + Ui::LteRlcGraphDialog *ui; + bool mouse_drags_; + QRubberBand *rubber_band_; + QPoint rb_origin_; + QMenu *ctx_menu_; + + // Data gleaned directly from tapping packets (shared with gtk impl) + struct rlc_graph graph_; + + // Data + QMap<double, struct rlc_segment *> time_stamp_map_; + double ts_offset_; + QMap<double, struct rlc_segment *> sequence_num_map_; + double seq_offset_; + bool seq_origin_zero_; + + QCPGraph *base_graph_; // Clickable packets + QCPGraph *reseg_graph_; + QCPGraph *acks_graph_; + QCPGraph *nacks_graph_; + + bool compareHeaders(rlc_segment *seg); + + void findChannel(); + void fillGraph(); + + void zoomAxes(bool in); + void panAxes(int x_pixels, int y_pixels); + QRectF getZoomRanges(QRect zoom_rect); + +private slots: + void graphClicked(QMouseEvent *event); + void mouseMoved(QMouseEvent *event); + void mouseReleased(QMouseEvent *event); + void resetAxes(); + + void on_actionReset_triggered(); + void on_actionZoomIn_triggered(); + void on_actionZoomOut_triggered(); + void on_actionMoveUp10_triggered(); + void on_actionMoveLeft10_triggered(); + void on_actionMoveRight10_triggered(); + void on_actionMoveDown10_triggered(); + void on_actionMoveUp1_triggered(); + void on_actionMoveLeft1_triggered(); + void on_actionMoveRight1_triggered(); + void on_actionMoveDown1_triggered(); + void on_actionDragZoom_triggered(); + void on_actionMoveUp100_triggered(); + void on_actionMoveDown100_triggered(); +}; + +#endif // LTE_RLC_GRAPH_DIALOG_H + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/lte_rlc_graph_dialog.ui b/ui/qt/lte_rlc_graph_dialog.ui new file mode 100644 index 0000000000..827a1bb62b --- /dev/null +++ b/ui/qt/lte_rlc_graph_dialog.ui @@ -0,0 +1,275 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>LteRlcGraphDialog</class> + <widget class="QDialog" name="LteRlcGraphDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>660</width> + <height>447</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0"> + <item> + <widget class="QCustomPlot" name="rlcPlot" native="true"/> + </item> + <item> + <widget class="QLabel" name="hintLabel"> + <property name="toolTip"> + <string><html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html></string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Save</set> + </property> + </widget> + </item> + </layout> + <action name="actionReset"> + <property name="text"> + <string>Reset Graph</string> + </property> + <property name="toolTip"> + <string>Reset the graph to its initial state.</string> + </property> + <property name="shortcut"> + <string>0</string> + </property> + </action> + <action name="actionZoomIn"> + <property name="text"> + <string>Zoom In</string> + </property> + <property name="toolTip"> + <string>Zoom In</string> + </property> + <property name="shortcut"> + <string>+</string> + </property> + </action> + <action name="actionZoomOut"> + <property name="text"> + <string>Zoom Out</string> + </property> + <property name="toolTip"> + <string>Zoom Out</string> + </property> + <property name="shortcut"> + <string>-</string> + </property> + </action> + <action name="actionMoveUp10"> + <property name="text"> + <string>Move Up 10 Pixels</string> + </property> + <property name="toolTip"> + <string>Move Up 10 Pixels</string> + </property> + <property name="shortcut"> + <string>Up</string> + </property> + </action> + <action name="actionMoveLeft10"> + <property name="text"> + <string>Move Left 10 Pixels</string> + </property> + <property name="toolTip"> + <string>Move Left 10 Pixels</string> + </property> + <property name="shortcut"> + <string>Left</string> + </property> + </action> + <action name="actionMoveRight10"> + <property name="text"> + <string>Move Right 10 Pixels</string> + </property> + <property name="toolTip"> + <string>Move Right 10 Pixels</string> + </property> + <property name="shortcut"> + <string>Right</string> + </property> + </action> + <action name="actionMoveDown10"> + <property name="text"> + <string>Move Down 10 Pixels</string> + </property> + <property name="toolTip"> + <string>Move Down 10 Pixels</string> + </property> + <property name="shortcut"> + <string>Down</string> + </property> + </action> + <action name="actionMoveUp1"> + <property name="text"> + <string>Move Up 1 Pixel</string> + </property> + <property name="toolTip"> + <string>Move Up 1 Pixel</string> + </property> + <property name="shortcut"> + <string>Shift+Up</string> + </property> + </action> + <action name="actionMoveLeft1"> + <property name="text"> + <string>Move Left 1 Pixel</string> + </property> + <property name="toolTip"> + <string>Move Left 1 Pixel</string> + </property> + <property name="shortcut"> + <string>Shift+Left</string> + </property> + </action> + <action name="actionMoveRight1"> + <property name="text"> + <string>Move Right 1 Pixel</string> + </property> + <property name="toolTip"> + <string>Move Right 1 Pixel</string> + </property> + <property name="shortcut"> + <string>Shift+Right</string> + </property> + </action> + <action name="actionMoveDown1"> + <property name="text"> + <string>Move Down 1 Pixel</string> + </property> + <property name="toolTip"> + <string>Move down 1 Pixel</string> + </property> + <property name="shortcut"> + <string>Shift+Down</string> + </property> + </action> + <action name="actionDragZoom"> + <property name="text"> + <string>Drag / Zoom</string> + </property> + <property name="toolTip"> + <string>Toggle mouse drag / zoom behavior</string> + </property> + <property name="shortcut"> + <string>Z</string> + </property> + </action> + <action name="actionCrosshairs"> + <property name="text"> + <string>Crosshairs</string> + </property> + <property name="toolTip"> + <string>Toggle crosshairs</string> + </property> + <property name="shortcut"> + <string>Space</string> + </property> + </action> + <action name="actionMoveUp100"> + <property name="text"> + <string>Move Up 100 Pixels</string> + </property> + <property name="toolTip"> + <string>Move Up 100 Pixels</string> + </property> + <property name="shortcut"> + <string>PgUp</string> + </property> + </action> + <action name="actionMoveDown100"> + <property name="text"> + <string>Move Up 100 Pixels</string> + </property> + <property name="toolTip"> + <string>Move Up 100 Pixels</string> + </property> + <property name="shortcut"> + <string>PgDown</string> + </property> + </action> + </widget> + <customwidgets> + <customwidget> + <class>QCustomPlot</class> + <extends>QWidget</extends> + <header>qcustomplot.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>LteRlcGraphDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>LteRlcGraphDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp index b053c0c1cf..7c2d2c05bd 100644 --- a/ui/qt/main_window.cpp +++ b/ui/qt/main_window.cpp @@ -2156,6 +2156,7 @@ void MainWindow::addDynamicMenus() wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary); wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics); wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics); + wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph); wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary); // Fill in each menu diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index 8f25f1e334..dd883ffcac 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -540,6 +540,7 @@ private slots: void on_actionTelephonyLteRlcStatistics_triggered(); void statCommandLteRlcStatistics(const char *arg, void *); void on_actionTelephonyLteMacStatistics_triggered(); + void on_actionTelephonyLteRlcGraph_triggered(); void on_actionTelephonyIax2StreamAnalysis_triggered(); void on_actionTelephonyISUPMessages_triggered(); void on_actionTelephonyMtp3Summary_triggered(); diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index 72fc47ea43..b9baa1d738 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -2449,7 +2449,16 @@ <property name="toolTip"> <string>LTE RLC statistics</string> </property> - </action> <action name="actionTelephonyMtp3Summary"> + </action> + <action name="actionTelephonyLteRlcGraph"> + <property name="text"> + <string>RLC Graph</string> + </property> + <property name="toolTip"> + <string>LTE RLC graph</string> + </property> + </action> + <action name="actionTelephonyMtp3Summary"> <property name="text"> <string>MTP3 Summary</string> </property> diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index 2579f02169..395df9faf7 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -114,6 +114,7 @@ #include "lbm_lbtru_transport_dialog.h" #include "lte_mac_statistics_dialog.h" #include "lte_rlc_statistics_dialog.h" +#include "lte_rlc_graph_dialog.h" #include "mtp3_summary_dialog.h" #include "multicast_statistics_dialog.h" #include "packet_comment_dialog.h" @@ -3025,6 +3026,12 @@ void MainWindow::on_actionTelephonyLteRlcStatistics_triggered() statCommandLteRlcStatistics(NULL, NULL); } +void MainWindow::on_actionTelephonyLteRlcGraph_triggered() +{ + LteRlcGraphDialog *lrg_dialog = new LteRlcGraphDialog(*this, capture_file_); + lrg_dialog->show(); +} + void MainWindow::on_actionTelephonyMtp3Summary_triggered() { Mtp3SummaryDialog *mtp3s_dialog = new Mtp3SummaryDialog(*this, capture_file_); |