forked from osmocom/wireshark
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:
parent
8e9fc80e29
commit
8efd83ef82
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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>").
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue