aboutsummaryrefslogtreecommitdiffstats
path: root/ui/qt
diff options
context:
space:
mode:
authorMartin Mathieson <martin.r.mathieson@googlemail.com>2015-10-10 15:53:45 -0700
committerMartin Mathieson <martin.r.mathieson@googlemail.com>2015-10-11 21:59:45 +0000
commit3221dbf542217cea5acd9ec764cf4779edaf65e8 (patch)
tree8999d3a38ee9a687737e35cdd39bde1ea7d18a2e /ui/qt
parenta6673b3fde1cac904a405cb1125d547d064d3aa7 (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.txt3
-rw-r--r--ui/qt/Makefile.am2
-rw-r--r--ui/qt/Makefile.common4
-rw-r--r--ui/qt/Wireshark.pro3
-rw-r--r--ui/qt/lte_rlc_graph_dialog.cpp643
-rw-r--r--ui/qt/lte_rlc_graph_dialog.h118
-rw-r--r--ui/qt/lte_rlc_graph_dialog.ui275
-rw-r--r--ui/qt/main_window.cpp1
-rw-r--r--ui/qt/main_window.h1
-rw-r--r--ui/qt/main_window.ui11
-rw-r--r--ui/qt/main_window_slots.cpp7
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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;
+
+&lt;h3&gt;Valuable and amazing time-saving keyboard shortcuts&lt;/h3&gt;
+&lt;table&gt;&lt;tbody&gt;
+
+&lt;tr&gt;&lt;th&gt;+&lt;/th&gt;&lt;td&gt;Zoom in&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;-&lt;/th&gt;&lt;td&gt;Zoom out&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;0&lt;/th&gt;&lt;td&gt;Reset graph to its initial state&lt;/td&gt;&lt;/th&gt;
+
+&lt;tr&gt;&lt;th&gt;→&lt;/th&gt;&lt;td&gt;Move right 10 pixels&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;←&lt;/th&gt;&lt;td&gt;Move left 10 pixels&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;↑&lt;/th&gt;&lt;td&gt;Move up 10 pixels&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;↓&lt;/th&gt;&lt;td&gt;Move down 10 pixels&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;→&lt;/th&gt;&lt;td&gt;Move right 1 pixel&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;←&lt;/th&gt;&lt;td&gt;Move left 1 pixel&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;↑&lt;/th&gt;&lt;td&gt;Move up 1 pixel&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;↓&lt;/th&gt;&lt;td&gt;Move down 1 pixel&lt;/td&gt;&lt;/th&gt;
+
+&lt;tr&gt;&lt;th&gt;g&lt;/th&gt;&lt;td&gt;Go to packet under cursor&lt;/td&gt;&lt;/th&gt;
+
+&lt;tr&gt;&lt;th&gt;z&lt;/th&gt;&lt;td&gt;Toggle mouse drag / zoom&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;t&lt;/th&gt;&lt;td&gt;Toggle capture / session time origin&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;Space&lt;/th&gt;&lt;td&gt;Toggle crosshairs&lt;/td&gt;&lt;/th&gt;
+
+&lt;/tbody&gt;&lt;/table&gt;
+&lt;/body&gt;&lt;/html&gt;</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_);