From 8efd83ef82a3a6895a956ae5ceafd8f97826b001 Mon Sep 17 00:00:00 2001 From: Martin Mathieson Date: Sat, 17 Oct 2015 08:54:55 -0700 Subject: [PATCH] LTE RLC Graph: add support for going to packet clicked Change-Id: I7e1ada7508c33f7ccea5703a9ea9e2a76ecdb706 Reviewed-on: https://code.wireshark.org/review/11118 Petri-Dish: Martin Mathieson Tested-by: Petri Dish Buildbot Reviewed-by: Martin Mathieson --- ui/qt/lte_rlc_graph_dialog.cpp | 122 ++++++++++++++++++++-------- ui/qt/lte_rlc_graph_dialog.h | 9 ++ ui/qt/lte_rlc_graph_dialog.ui | 11 +++ ui/qt/lte_rlc_statistics_dialog.cpp | 28 +++++-- ui/qt/lte_rlc_statistics_dialog.h | 6 ++ ui/qt/main_window.h | 3 + ui/qt/main_window_slots.cpp | 23 +++++- 7 files changed, 155 insertions(+), 47 deletions(-) diff --git a/ui/qt/lte_rlc_graph_dialog.cpp b/ui/qt/lte_rlc_graph_dialog.cpp index a60a506017..af4356538c 100644 --- a/ui/qt/lte_rlc_graph_dialog.cpp +++ b/ui/qt/lte_rlc_graph_dialog.cpp @@ -48,9 +48,7 @@ // 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_ack = tango_sky_blue_4; // Blue for ACK lines const QRgb graph_color_nack = tango_scarlet_red_3; // Red for NACKs @@ -91,8 +89,8 @@ LteRlcGraphDialog::LteRlcGraphDialog(QWidget &parent, CaptureFile &cf, bool chan 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->actionGoToPacket); ctx_menu_->addSeparator(); ctx_menu_->addAction(ui->actionDragZoom); // ctx_menu_->addAction(ui->actionToggleTimeOrigin); @@ -164,6 +162,12 @@ void LteRlcGraphDialog::completeGraph() nacks_graph_ = sp->addGraph(); nacks_graph_->setPen(QPen(QBrush(graph_color_nack), 0.25)); + // Create tracer + tracer_ = new QCPItemTracer(sp); + sp->addItem(tracer_); + tracer_->setVisible(false); + toggleTracerStyle(true); + 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*))); @@ -185,8 +189,14 @@ bool LteRlcGraphDialog::compareHeaders(rlc_segment *seg) // Look for channel to plot based upon currently selected frame. void LteRlcGraphDialog::findChannel() { + // Temporarily disconnect mouse move signals. + QCustomPlot *rp = ui->rlcPlot; + disconnect(rp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); + char *err_string = NULL; gboolean free_err_string = FALSE; + + // Rescan for channel data. rlc_graph_segment_list_free(&graph_); if (!rlc_graph_segment_list_get(cap_file_.capFile(), &graph_, graph_.channelSet, &err_string, &free_err_string)) { @@ -196,6 +206,9 @@ void LteRlcGraphDialog::findChannel() g_free(err_string); } } + + // Reconnect mouse move signal. + connect(rp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); } // Fill in graph data based upon what was read into the rlc_graph struct. @@ -208,6 +221,8 @@ void LteRlcGraphDialog::fillGraph() return; } + tracer_->setGraph(NULL); + base_graph_->setLineStyle(QCPGraph::lsNone); // dot reseg_graph_->setLineStyle(QCPGraph::lsNone); // dot acks_graph_->setLineStyle(QCPGraph::lsStepLeft); // to get step effect... @@ -279,6 +294,8 @@ void LteRlcGraphDialog::fillGraph() mouseMoved(NULL); resetAxes(); + tracer_->setGraph(base_graph_); + // XXX QCustomPlot doesn't seem to draw any sort of focus indicator. sp->setFocus(); } @@ -342,7 +359,7 @@ void LteRlcGraphDialog::keyPressEvent(QKeyEvent *event) break; case Qt::Key_G: -// on_actionGoToPacket_triggered(); + on_actionGoToPacket_triggered(); break; case Qt::Key_T: // on_actionToggleTimeOrigin_triggered(); @@ -430,7 +447,7 @@ void LteRlcGraphDialog::graphClicked(QMouseEvent *event) if (rp->axisRect()->rect().contains(event->pos())) { rp->setCursor(QCursor(Qt::ClosedHandCursor)); } -// on_actionGoToPacket_triggered(); + on_actionGoToPacket_triggered(); } else { if (!rubber_band_) { rubber_band_ = new QRubberBand(QRubberBand::Rectangle, rp); @@ -448,6 +465,7 @@ void LteRlcGraphDialog::mouseMoved(QMouseEvent *event) QString hint; Qt::CursorShape shape = Qt::ArrowCursor; + // Set the cursor shape. if (event) { if (event->buttons().testFlag(Qt::LeftButton)) { if (mouse_drags_) { @@ -465,41 +483,41 @@ void LteRlcGraphDialog::mouseMoved(QMouseEvent *event) rp->setCursor(QCursor(shape)); } + if (mouse_drags_) { -// double ts = 0; -// packet_num_ = 0; -// int interval_packet = -1; + double tr_key = tracer_->position->key(); + struct rlc_segment *packet_seg = NULL; + packet_num_ = 0; -// if (event && tracer_->graph()) { -// tracer_->setGraphKey(rp->xAxis->pixelToCoord(event->pos().x())); -// ts = tracer_->position->key(); + // XXX If we have multiple packets with the same timestamp tr_key + // may not return the packet we want. It might be possible to fudge + // unique keys using nextafter(). + //printf("testing here (event=%p, tracer_->graph()=%p\n", event, tracer_->graph()); + if (event && tracer_->graph() && tracer_->position->axisRect()->rect().contains(event->pos())) { + packet_seg = time_stamp_map_.value(tr_key, NULL); + } -// QTreeWidgetItem *ti = ui->graphTreeWidget->topLevelItem(0); -// IOGraph *iog = NULL; -// if (ti) { -// iog = ti->data(name_col_, Qt::UserRole).value(); -// interval_packet = iog->packetFromTime(ts); -// } -// } + if (!packet_seg) { + tracer_->setVisible(false); +// hint += "Hover over the graph for details. " + stream_desc_ + ""; +// ui->hintLabel->setText(hint); +// ui->streamPlot->replot(); + return; + } -// 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); -// } + tracer_->setVisible(true); + packet_num_ = packet_seg->num; +// hint += tr("%1 %2 (%3s len %4 seq %5 ack %6 win %7)") +// .arg(cap_file_ ? tr("Click to select packet") : tr("Packet")) +// .arg(packet_num_) +// .arg(QString::number(packet_seg->rel_secs + packet_seg->rel_usecs / 1000000.0, 'g', 4)) +// .arg(packet_seg->th_seglen) +// .arg(packet_seg->th_seq) +// .arg(packet_seg->th_ack) +// .arg(packet_seg->th_win); + tracer_->setGraphKey(ui->rlcPlot->xAxis->pixelToCoord(event->pos().x())); rp->replot(); + } else { if (event && rubber_band_ && rubber_band_->isVisible()) { rubber_band_->setGeometry(QRect(rb_origin_, event->pos()).normalized()); @@ -564,6 +582,38 @@ void LteRlcGraphDialog::resetAxes() rp->replot(); } +void LteRlcGraphDialog::on_actionGoToPacket_triggered() +{ + if (tracer_->visible() && cap_file_.capFile() && (packet_num_ > 0)) { + // Signal to the packetlist which frame we want to show. + emit goToPacket(packet_num_); + } +} + +void LteRlcGraphDialog::toggleTracerStyle(bool force_default) +{ + if (!tracer_->visible() && !force_default) return; + + QPen sp_pen = ui->rlcPlot->graph(0)->pen(); + QCPItemTracer::TracerStyle tstyle = QCPItemTracer::tsCrosshair; + QPen tr_pen = QPen(tracer_->pen()); + QColor tr_color = sp_pen.color(); + + if (force_default || tracer_->style() != QCPItemTracer::tsCircle) { + tstyle = QCPItemTracer::tsCircle; + tr_color.setAlphaF(1.0); + tr_pen.setWidthF(1.5); + } else { + tr_color.setAlphaF(0.5); + tr_pen.setWidthF(1.0); + } + + tracer_->setStyle(tstyle); + tr_pen.setColor(tr_color); + tracer_->setPen(tr_pen); + ui->rlcPlot->replot(); +} + void LteRlcGraphDialog::on_actionReset_triggered() { resetAxes(); diff --git a/ui/qt/lte_rlc_graph_dialog.h b/ui/qt/lte_rlc_graph_dialog.h index ba602f2c4e..92c090e862 100644 --- a/ui/qt/lte_rlc_graph_dialog.h +++ b/ui/qt/lte_rlc_graph_dialog.h @@ -48,6 +48,9 @@ public: void setChannelInfo(guint16 ueid, guint8 rlcMode, guint16 channelType, guint16 channelId, guint8 direction); +signals: + void goToPacket(int packet_num); + protected: void showEvent(QShowEvent *event); void keyPressEvent(QKeyEvent *event); @@ -70,6 +73,8 @@ private: QCPGraph *reseg_graph_; QCPGraph *acks_graph_; QCPGraph *nacks_graph_; + QCPItemTracer *tracer_; + guint32 packet_num_; void completeGraph(); @@ -82,6 +87,8 @@ private: void panAxes(int x_pixels, int y_pixels); QRectF getZoomRanges(QRect zoom_rect); + void toggleTracerStyle(bool force_default); + private slots: void graphClicked(QMouseEvent *event); void mouseMoved(QMouseEvent *event); @@ -102,6 +109,8 @@ private slots: void on_actionDragZoom_triggered(); void on_actionMoveUp100_triggered(); void on_actionMoveDown100_triggered(); + + void on_actionGoToPacket_triggered(); }; #endif // LTE_RLC_GRAPH_DIALOG_H diff --git a/ui/qt/lte_rlc_graph_dialog.ui b/ui/qt/lte_rlc_graph_dialog.ui index 827a1bb62b..65946dc415 100644 --- a/ui/qt/lte_rlc_graph_dialog.ui +++ b/ui/qt/lte_rlc_graph_dialog.ui @@ -228,6 +228,17 @@ PgDown + + + Go To Packet Under Cursor + + + Go to packet currently under the cursor + + + G + + diff --git a/ui/qt/lte_rlc_statistics_dialog.cpp b/ui/qt/lte_rlc_statistics_dialog.cpp index 945c32c850..1378b068a6 100644 --- a/ui/qt/lte_rlc_statistics_dialog.cpp +++ b/ui/qt/lte_rlc_statistics_dialog.cpp @@ -312,13 +312,11 @@ public: return filter_expr; } - // Create an RLC Graph and populate it with this channel's details. - void launchGraph(guint8 direction, CaptureFile &cf) - { - LteRlcGraphDialog *graph_dialog = new LteRlcGraphDialog(dialog_, cf, true); - graph_dialog->setChannelInfo(ueid_, mode_, channelType_, channelId_, direction); - graph_dialog->show(); - } + // Accessors (queried for launching graph) + unsigned get_ueid() { return ueid_; } + unsigned get_channelType() { return channelType_; } + unsigned get_channelId() { return channelId_; } + unsigned get_mode() { return mode_; } private: QWidget &dialog_; @@ -879,9 +877,15 @@ void LteRlcStatisticsDialog::captureFileClosing() void LteRlcStatisticsDialog::launchULGraphButtonClicked() { if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) { + // Get the channel item. QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; RlcChannelTreeWidgetItem *rc_ti = static_cast(ti); - rc_ti->launchGraph(DIRECTION_UPLINK, cf_); + emit launchRLCGraph(true, + rc_ti->get_ueid(), + rc_ti->get_mode(), + rc_ti->get_channelType(), + rc_ti->get_channelId(), + DIRECTION_UPLINK); } } @@ -889,9 +893,15 @@ void LteRlcStatisticsDialog::launchULGraphButtonClicked() void LteRlcStatisticsDialog::launchDLGraphButtonClicked() { if (statsTreeWidget()->selectedItems().count() > 0 && statsTreeWidget()->selectedItems()[0]->type() == rlc_channel_row_type_) { + // Get the channel item. QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; RlcChannelTreeWidgetItem *rc_ti = static_cast(ti); - rc_ti->launchGraph(DIRECTION_DOWNLINK, cf_); + emit launchRLCGraph(true, + rc_ti->get_ueid(), + rc_ti->get_mode(), + rc_ti->get_channelType(), + rc_ti->get_channelId(), + DIRECTION_DOWNLINK); } } diff --git a/ui/qt/lte_rlc_statistics_dialog.h b/ui/qt/lte_rlc_statistics_dialog.h index cbfec3309b..8d83b68f02 100644 --- a/ui/qt/lte_rlc_statistics_dialog.h +++ b/ui/qt/lte_rlc_statistics_dialog.h @@ -39,6 +39,12 @@ public: protected: +signals: + void launchRLCGraph(bool channelKnown, + guint16 ueid, guint8 rlcMode, + guint16 channelType, guint16 channelId, + guint8 direction); + private: // Extra controls needed for this dialog. QCheckBox *useRLCFramesFromMacCheckBox_; diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index d83bd94777..be314ed24e 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -245,6 +245,9 @@ public slots: void filterExpressionsChanged(); + void launchRLCGraph(bool channelKnown, guint16 ueid, guint8 rlcMode, + guint16 channelType, guint16 channelId, guint8 direction); + private slots: // Manually connected slots (no "on__"). diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index e5c366c06f..32786a08b9 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -3037,6 +3037,11 @@ void MainWindow::statCommandLteRlcStatistics(const char *arg, void *) LteRlcStatisticsDialog *lte_rlc_stats_dlg = new LteRlcStatisticsDialog(*this, capture_file_, arg); connect(lte_rlc_stats_dlg, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType)), this, SLOT(filterAction(QString&,FilterAction::Action,FilterAction::ActionType))); + // N.B. It is necessary for the RLC Statistics window to launch the RLC graph in this way, to ensure + // that the goToPacket() signal/slot connection gets set up... + connect(lte_rlc_stats_dlg, SIGNAL(launchRLCGraph(bool, guint16, guint8, guint16, guint16, guint8)), + this, SLOT(launchRLCGraph(bool, guint16, guint8, guint16, guint16, guint8))); + lte_rlc_stats_dlg->show(); } @@ -3045,10 +3050,24 @@ void MainWindow::on_actionTelephonyLteRlcStatistics_triggered() statCommandLteRlcStatistics(NULL, NULL); } +void MainWindow::launchRLCGraph(bool channelKnown, + guint16 ueid, guint8 rlcMode, + guint16 channelType, guint16 channelId, guint8 direction) +{ + LteRlcGraphDialog *lrg_dialog = new LteRlcGraphDialog(*this, capture_file_, channelKnown); + connect(lrg_dialog, SIGNAL(goToPacket(int)), packet_list_, SLOT(goToPacket(int))); + // This is a bit messy, but wanted to hide these parameters from users of + // on_actionTelephonyLteRlcGraph_triggered(). + if (channelKnown) { + lrg_dialog->setChannelInfo(ueid, rlcMode, channelType, channelId, direction); + } + lrg_dialog->show(); +} + void MainWindow::on_actionTelephonyLteRlcGraph_triggered() { - LteRlcGraphDialog *lrg_dialog = new LteRlcGraphDialog(*this, capture_file_, false); - lrg_dialog->show(); + // We don't yet know the channel. + launchRLCGraph(false, 0, 0, 0, 0, 0); } void MainWindow::on_actionTelephonyMtp3Summary_triggered()