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 <martin.r.mathieson@googlemail.com>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Martin Mathieson <martin.r.mathieson@googlemail.com>
This commit is contained in:
Martin Mathieson 2015-10-17 08:54:55 -07:00
parent 8e9fc80e29
commit 8efd83ef82
7 changed files with 155 additions and 47 deletions

View File

@ -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<IOGraph *>();
// interval_packet = iog->packetFromTime(ts);
// }
// }
if (!packet_seg) {
tracer_->setVisible(false);
// hint += "Hover over the graph for details. " + stream_desc_ + "</i></small>";
// 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();

View File

@ -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

View File

@ -228,6 +228,17 @@
<string>PgDown</string>
</property>
</action>
<action name="actionGoToPacket">
<property name="text">
<string>Go To Packet Under Cursor</string>
</property>
<property name="toolTip">
<string>Go to packet currently under the cursor</string>
</property>
<property name="shortcut">
<string>G</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -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<RlcChannelTreeWidgetItem*>(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<RlcChannelTreeWidgetItem*>(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);
}
}

View File

@ -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_;

View File

@ -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_<object>_<signal>").

View File

@ -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()