614 lines
21 KiB
C++
614 lines
21 KiB
C++
/*
|
|
* Copyright (c) 2015, RPTU Kaiserslautern-Landau
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
|
|
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Authors:
|
|
* Janik Schlemminger
|
|
* Robert Gernhardt
|
|
* Matthias Jung
|
|
* Derek Christ
|
|
* Iron Prando da Silva
|
|
*/
|
|
|
|
#include "tracefiletab.h"
|
|
#include "businessObjects/commentmodel.h"
|
|
#include "businessObjects/configmodels.h"
|
|
#include "businessObjects/dramTimeDependencies/phasedependenciestracker.h"
|
|
#include "businessObjects/traceplotlinemodel.h"
|
|
#include "businessObjects/tracetime.h"
|
|
#include "queryeditor.h"
|
|
#include "traceanalyzer.h"
|
|
#include "ui_tracefiletab.h"
|
|
|
|
#include <QCloseEvent>
|
|
#include <QDebug>
|
|
#include <QFileDialog>
|
|
#include <QFileInfo>
|
|
#include <QItemDelegate>
|
|
#include <QMessageBox>
|
|
#include <QStandardItemModel>
|
|
#include <QString>
|
|
#include <QTextStream>
|
|
#include <QtConcurrent/QtConcurrent>
|
|
#include <qwt_legend.h>
|
|
#include <qwt_plot_canvas.h>
|
|
#include <qwt_plot_curve.h>
|
|
#include <qwt_plot_histogram.h>
|
|
#include <qwt_plot_magnifier.h>
|
|
#include <qwt_plot_panner.h>
|
|
#include <qwt_scale_draw.h>
|
|
#include <qwt_scale_widget.h>
|
|
|
|
#include <fstream>
|
|
#include <pybind11/pybind11.h>
|
|
|
|
TraceFileTab::TraceFileTab(std::string_view traceFilePath,
|
|
PythonCaller& pythonCaller,
|
|
QWidget* parent) :
|
|
QWidget(parent),
|
|
traceFilePath(traceFilePath),
|
|
ui(new Ui::TraceFileTab),
|
|
commentModel(new CommentModel(this)),
|
|
navigator(new TraceNavigator(traceFilePath.data(), commentModel, this)),
|
|
mcConfigModel(new McConfigModel(navigator->TraceFile(), this)),
|
|
memSpecModel(new MemSpecModel(navigator->TraceFile(), this)),
|
|
availableRowsModel(new AvailableTracePlotLineModel(navigator->GeneralTraceInfo(), this)),
|
|
selectedRowsModel(new SelectedTracePlotLineModel(navigator->GeneralTraceInfo(), this)),
|
|
tracePlotLineDataSource(new TracePlotLineDataSource(selectedRowsModel, this)),
|
|
depInfosView(new DependencyInfosModel(navigator->TraceFile(), this)),
|
|
pythonCaller(pythonCaller),
|
|
savingChangesToDB(false)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
std::cout << "Opening new tab for \"" << traceFilePath << "\"" << std::endl;
|
|
|
|
ui->mcConfigView->setModel(mcConfigModel);
|
|
ui->mcConfigView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
|
|
ui->memSpecView->setModel(memSpecModel);
|
|
ui->memSpecView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
|
|
ui->depInfosView->setModel(depInfosView);
|
|
ui->depInfosView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
|
|
setUpTraceSelector();
|
|
initNavigatorAndItsDependentWidgets();
|
|
setUpFileWatcher(traceFilePath.data());
|
|
setUpTraceplotScrollbar();
|
|
setUpCommentView();
|
|
|
|
setUpPossiblePhases();
|
|
|
|
tracefileChanged();
|
|
}
|
|
|
|
TraceFileTab::~TraceFileTab()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void TraceFileTab::commitChangesToDB()
|
|
{
|
|
savingChangesToDB = true;
|
|
navigator->commitChangesToDB();
|
|
}
|
|
|
|
void TraceFileTab::exportAsVCD()
|
|
{
|
|
std::string filename =
|
|
QFileDialog::getSaveFileName(this, "Export to VCD", "", "VCD files (*.vcd)").toStdString();
|
|
|
|
auto dump = PythonCaller::dumpVcd(traceFilePath);
|
|
|
|
std::ofstream file(filename);
|
|
file << dump;
|
|
|
|
Q_EMIT statusChanged(QString("VCD export finished."));
|
|
}
|
|
|
|
void TraceFileTab::setUpTraceSelector()
|
|
{
|
|
ui->availableTreeView->setModel(availableRowsModel);
|
|
ui->availableTreeView->setSelectionModel(availableRowsModel->selectionModel());
|
|
ui->availableTreeView->installEventFilter(availableRowsModel);
|
|
|
|
ui->selectedTreeView->setModel(selectedRowsModel);
|
|
ui->selectedTreeView->setSelectionModel(selectedRowsModel->selectionModel());
|
|
ui->selectedTreeView->installEventFilter(selectedRowsModel);
|
|
|
|
connect(availableRowsModel,
|
|
&AvailableTracePlotLineModel::returnPressed,
|
|
selectedRowsModel,
|
|
&SelectedTracePlotLineModel::addIndexesFromAvailableModel);
|
|
|
|
connect(ui->availableTreeView,
|
|
&QAbstractItemView::doubleClicked,
|
|
availableRowsModel,
|
|
&AvailableTracePlotLineModel::itemsDoubleClicked);
|
|
connect(ui->selectedTreeView,
|
|
&QAbstractItemView::doubleClicked,
|
|
selectedRowsModel,
|
|
&SelectedTracePlotLineModel::itemsDoubleClicked);
|
|
|
|
connect(selectedRowsModel,
|
|
&QAbstractItemModel::dataChanged,
|
|
tracePlotLineDataSource,
|
|
&TracePlotLineDataSource::updateModel);
|
|
connect(selectedRowsModel,
|
|
&QAbstractItemModel::rowsInserted,
|
|
tracePlotLineDataSource,
|
|
&TracePlotLineDataSource::updateModel);
|
|
connect(selectedRowsModel,
|
|
&QAbstractItemModel::rowsRemoved,
|
|
tracePlotLineDataSource,
|
|
&TracePlotLineDataSource::updateModel);
|
|
}
|
|
|
|
void TraceFileTab::setUpTraceplotScrollbar()
|
|
{
|
|
QObject::connect(ui->traceplotScrollbar,
|
|
SIGNAL(valueChanged(int)),
|
|
ui->traceplot,
|
|
SLOT(verticalScrollbarChanged(int)));
|
|
}
|
|
|
|
void TraceFileTab::initNavigatorAndItsDependentWidgets()
|
|
{
|
|
ui->traceplot->init(navigator, ui->traceplotScrollbar, tracePlotLineDataSource, commentModel);
|
|
|
|
ui->traceScroller->init(navigator, ui->traceplot, tracePlotLineDataSource);
|
|
connect(this,
|
|
SIGNAL(colorGroupingChanged(ColorGrouping)),
|
|
ui->traceScroller,
|
|
SLOT(colorGroupingChanged(ColorGrouping)));
|
|
|
|
ui->selectedTransactionTree->init(navigator);
|
|
// ui->debugMessages->init(navigator,ui->traceplot);
|
|
|
|
ui->bandwidthPlot->canvas()->installEventFilter(this);
|
|
ui->powerPlot->canvas()->installEventFilter(this);
|
|
ui->bufferPlot->canvas()->installEventFilter(this);
|
|
}
|
|
|
|
void TraceFileTab::setUpFileWatcher(QString path)
|
|
{
|
|
fileWatcher = new QFileSystemWatcher(QStringList(path), this);
|
|
QObject::connect(fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(tracefileChanged()));
|
|
}
|
|
|
|
void TraceFileTab::setUpCommentView()
|
|
{
|
|
ui->commentView->setModel(commentModel);
|
|
ui->commentView->setSelectionModel(commentModel->selectionModel());
|
|
ui->commentView->installEventFilter(commentModel);
|
|
ui->commentView->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
QObject::connect(ui->commentView,
|
|
&QTableView::customContextMenuRequested,
|
|
commentModel,
|
|
&CommentModel::openContextMenu);
|
|
|
|
QObject::connect(commentModel,
|
|
&CommentModel::editTriggered,
|
|
ui->commentView,
|
|
[=](const QModelIndex& index)
|
|
{
|
|
ui->tabWidget->setCurrentWidget(ui->tabComments);
|
|
ui->commentView->edit(index);
|
|
ui->commentView->scrollTo(index);
|
|
});
|
|
|
|
QObject::connect(
|
|
ui->commentView, &QTableView::doubleClicked, commentModel, &CommentModel::rowDoubleClicked);
|
|
}
|
|
|
|
void TraceFileTab::setUpPossiblePhases()
|
|
{
|
|
const auto possiblePhases = ConfigurationFactory::possiblePhases(navigator->TraceFile());
|
|
for (auto p : possiblePhases)
|
|
{
|
|
auto item = new QListWidgetItem(p, ui->depTabPossiblePhases);
|
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag
|
|
item->setCheckState(Qt::Unchecked); // AND initialize check state
|
|
}
|
|
|
|
ui->calculateDependencies->setEnabled(
|
|
ConfigurationFactory::deviceSupported(navigator->TraceFile()));
|
|
}
|
|
|
|
void TraceFileTab::tracefileChanged()
|
|
{
|
|
if (savingChangesToDB == true)
|
|
{
|
|
// Database has changed due to user action (e.g., saving comments).
|
|
// No need to disable the "Save changes to DB" menu.
|
|
savingChangesToDB = false;
|
|
Q_EMIT statusChanged(QString("Changes saved "));
|
|
}
|
|
else
|
|
{
|
|
// External event changed the database file (e.g., the database file
|
|
// was overwritten when running a new test).
|
|
// The "Save changes to DB" menu must be disabled to avoid saving
|
|
// changes to a corrupted or inconsistent file.
|
|
Q_EMIT statusChanged(QString("At least one database has changed on disk "));
|
|
}
|
|
navigator->refreshData();
|
|
}
|
|
|
|
void TraceFileTab::closeEvent(QCloseEvent* event)
|
|
{
|
|
if (navigator->existChangesToCommit())
|
|
{
|
|
QMessageBox saveDialog;
|
|
saveDialog.setWindowTitle(QFileInfo(traceFilePath.data()).baseName());
|
|
saveDialog.setText("The trace file has been modified.");
|
|
saveDialog.setInformativeText(
|
|
"Do you want to save your changes?<br><b>Unsaved changes will be lost.</b>");
|
|
saveDialog.setStandardButtons(QMessageBox::Save | QMessageBox::Discard |
|
|
QMessageBox::Cancel);
|
|
saveDialog.setDefaultButton(QMessageBox::Save);
|
|
saveDialog.setIcon(QMessageBox::Warning);
|
|
int returnCode = saveDialog.exec();
|
|
|
|
switch (returnCode)
|
|
{
|
|
case QMessageBox::Cancel:
|
|
event->ignore();
|
|
break;
|
|
case QMessageBox::Discard:
|
|
event->accept();
|
|
break;
|
|
case QMessageBox::Save:
|
|
commitChangesToDB();
|
|
event->accept();
|
|
break;
|
|
};
|
|
}
|
|
else
|
|
event->accept();
|
|
}
|
|
|
|
traceTime TraceFileTab::getCurrentTraceTime() const
|
|
{
|
|
return navigator->CurrentTraceTime();
|
|
}
|
|
|
|
void TraceFileTab::navigateToTime(traceTime time)
|
|
{
|
|
navigator->navigateToTime(time);
|
|
}
|
|
|
|
traceTime TraceFileTab::getZoomLevel() const
|
|
{
|
|
TracePlot* traceplot = static_cast<TracePlot*>(ui->traceplot);
|
|
return traceplot->ZoomLevel();
|
|
}
|
|
|
|
void TraceFileTab::setZoomLevel(traceTime zoomLevel)
|
|
{
|
|
TracePlot* traceplot = static_cast<TracePlot*>(ui->traceplot);
|
|
TraceScroller* tracescroller = static_cast<TraceScroller*>(ui->traceScroller);
|
|
traceplot->setZoomLevel(zoomLevel);
|
|
tracescroller->tracePlotZoomChanged();
|
|
}
|
|
|
|
std::shared_ptr<AbstractTracePlotLineModel::Node> TraceFileTab::saveTraceSelectorState() const
|
|
{
|
|
return selectedRowsModel->getClonedRootNode();
|
|
}
|
|
|
|
void TraceFileTab::restoreTraceSelectorState(
|
|
std::shared_ptr<AbstractTracePlotLineModel::Node> rootNode)
|
|
{
|
|
selectedRowsModel->setRootNode(std::move(rootNode));
|
|
}
|
|
|
|
class ItemDelegate : public QItemDelegate
|
|
{
|
|
public:
|
|
ItemDelegate(QObject* parent = nullptr) : QItemDelegate(parent) {}
|
|
|
|
void
|
|
paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
|
|
{
|
|
if (index.column() == 1)
|
|
{
|
|
double progress = index.data().toDouble();
|
|
QStyleOptionProgressBar opt;
|
|
opt.rect = option.rect;
|
|
opt.minimum = 0;
|
|
opt.maximum = 100;
|
|
opt.progress = static_cast<int>(floor(progress));
|
|
opt.text = QString::number(progress, 'f', 2) + " %";
|
|
opt.textVisible = true;
|
|
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &opt, painter, nullptr);
|
|
}
|
|
else
|
|
{
|
|
QItemDelegate::paint(painter, option, index);
|
|
}
|
|
}
|
|
};
|
|
|
|
void TraceFileTab::on_latencyTreeView_doubleClicked(const QModelIndex& index)
|
|
{
|
|
// Get onlye the leaf:
|
|
if (index.column() == 0 && index.model()->hasChildren(index) == false)
|
|
{
|
|
unsigned int id = index.data().toUInt();
|
|
if (id != 0)
|
|
{
|
|
navigator->selectTransaction(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TraceFileTab::eventFilter(QObject* object, QEvent* event)
|
|
{
|
|
if (auto canvas = qobject_cast<QwtPlotCanvas*>(object))
|
|
{
|
|
if (event->type() == QEvent::MouseButtonDblClick)
|
|
{
|
|
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
|
|
|
if (mouseEvent->button() != Qt::LeftButton)
|
|
return false;
|
|
|
|
QwtPlot* plot = canvas->plot();
|
|
|
|
double realTime = plot->invTransform(QwtPlot::xBottom, mouseEvent->x());
|
|
|
|
// Convert from seconds to picoseconds
|
|
traceTime time = realTime * 1000 * 1000 * 1000 * 1000;
|
|
|
|
navigator->navigateToTime(time);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return QWidget::eventFilter(object, event);
|
|
}
|
|
|
|
void TraceFileTab::on_calculateDependencies_clicked()
|
|
{
|
|
std::vector<QString> dependencyFilter;
|
|
for (int row = 0; row < ui->depTabPossiblePhases->count(); row++)
|
|
{
|
|
auto item = ui->depTabPossiblePhases->item(row);
|
|
if (item->checkState() == Qt::Checked)
|
|
dependencyFilter.push_back(item->text());
|
|
}
|
|
|
|
savingChangesToDB = true;
|
|
PhaseDependenciesTracker::calculateDependencies(navigator->TraceFile(), dependencyFilter);
|
|
depInfosView = new DependencyInfosModel(navigator->TraceFile(), this);
|
|
ui->depInfosView->setModel(depInfosView);
|
|
ui->depInfosView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
}
|
|
|
|
void TraceFileTab::on_startLatencyAnalysis_clicked()
|
|
{
|
|
// Setup Database:
|
|
QSqlDatabase db = navigator->TraceFile().getDatabase();
|
|
QSqlQuery query(db);
|
|
|
|
// Check the count of transactions:
|
|
QString sql = "SELECT COUNT(*) FROM Transactions;";
|
|
query.exec(sql);
|
|
query.next();
|
|
int maxTransactions = query.value(0).toInt();
|
|
|
|
// Create Database Setup and Query:
|
|
sql = "SELECT ((p2.PhaseEnd - p1.PhaseBegin)/1000) as latency, t.id "
|
|
"FROM Transactions t, Phases p1, Phases p2 "
|
|
"WHERE t.id = p1.Transact "
|
|
"AND t.id = p2.Transact "
|
|
"AND p1.PhaseName = \"REQ\" "
|
|
"AND p2.PhaseName = \"RESP\" ORDER BY latency;";
|
|
|
|
query.exec(sql);
|
|
|
|
// Creatoe model and fill it from Database:
|
|
QStandardItemModel* model = new QStandardItemModel();
|
|
|
|
int currentLatency = 0;
|
|
QStandardItem* currentLatencyItem = nullptr;
|
|
int counter = 0;
|
|
while (query.next())
|
|
{
|
|
if (query.value(0) != currentLatency)
|
|
{
|
|
currentLatencyItem = new QStandardItem(QString::number(query.value(0).toInt()) + " ns");
|
|
currentLatency = query.value(0).toInt();
|
|
QList<QStandardItem*> row;
|
|
row.append(currentLatencyItem);
|
|
row.append(new QStandardItem());
|
|
model->appendRow(row);
|
|
}
|
|
QStandardItem* id = new QStandardItem(query.value(1).toString());
|
|
currentLatencyItem->appendRow(id);
|
|
counter++;
|
|
|
|
int percentage = int(ceil((double(counter)) / (double(maxTransactions)) * 100.0));
|
|
ui->latencyAnalysisProgressBar->setValue(percentage);
|
|
}
|
|
QStringList header = {"Latency", "Occurences"};
|
|
model->setHorizontalHeaderLabels(header);
|
|
|
|
// Generate Histrogram and Tree:
|
|
QwtPlotHistogram* hist = new QwtPlotHistogram;
|
|
QVector<QwtIntervalSample>* intervals = new QVector<QwtIntervalSample>;
|
|
for (int i = 0; i < model->rowCount(); i++)
|
|
{
|
|
double latency = model->item(i, 0)->text().replace(" ns", "").toDouble();
|
|
int numberOfChilds = model->item(i)->rowCount();
|
|
double percentage = 100 * ((double(numberOfChilds)) / (double(counter)));
|
|
model->item(i, 1)->setText(QString::number(percentage));
|
|
intervals->append(QwtIntervalSample(percentage, latency, latency + 1));
|
|
}
|
|
ui->latencyTreeView->setItemDelegate(new ItemDelegate(ui->latencyTreeView));
|
|
ui->latencyTreeView->setModel(model);
|
|
hist->setSamples(*intervals);
|
|
hist->attach(ui->latencyPlot);
|
|
hist->setPen(QPen(QColor(255, 0, 0, 100)));
|
|
hist->setBrush(QBrush(QColor(255, 0, 0, 255)));
|
|
ui->latencyPlot->setAxisTitle(0, "Occurences [%]");
|
|
QwtText axisTitle("Latency [ns]");
|
|
axisTitle.setFont(ui->latencyPlot->axisTitle(QwtPlot::xBottom).font());
|
|
ui->latencyPlot->setAxisTitle(QwtPlot::xBottom, axisTitle);
|
|
ui->latencyPlot->replot();
|
|
}
|
|
|
|
void TraceFileTab::on_startPowerAnalysis_clicked()
|
|
{
|
|
qDebug() << "Power Analysis";
|
|
QSqlDatabase db = navigator->TraceFile().getDatabase();
|
|
QSqlQuery query(db);
|
|
|
|
QString sql = "SELECT time, AveragePower FROM Power;";
|
|
|
|
query.exec(sql);
|
|
|
|
QwtPointSeriesData* data = new QwtPointSeriesData;
|
|
QwtPlotCurve* cur = new QwtPlotCurve("Speed");
|
|
QVector<QPointF>* samples = new QVector<QPointF>;
|
|
|
|
while (query.next())
|
|
{
|
|
double time = query.value(0).toDouble();
|
|
double power = query.value(1).toDouble();
|
|
samples->push_back(QPointF(time, power));
|
|
}
|
|
|
|
// ui->powerPlot->setAxisTitle(QwtPlot::xBottom,"Time");
|
|
// ui->powerPlot->setAxisLabelRotation(QwtPlot::xBottom,-50.0);
|
|
// ui->powerPlot->setAxisLabelAlignment(QwtPlot::xBottom,Qt::AlignLeft|Qt::AlignBottom);
|
|
// ui->powerPlot->setAxisTitle(QwtPlot::yLeft,"Power");
|
|
|
|
data->setSamples(*samples);
|
|
cur->setData(data);
|
|
cur->attach(ui->powerPlot);
|
|
cur->setPen(QPen(QColor(255, 0, 0)));
|
|
|
|
QwtPlotMagnifier* mag1 = new QwtPlotMagnifier(ui->powerPlot->canvas());
|
|
mag1->setAxisEnabled(QwtPlot::xBottom, true);
|
|
mag1->setAxisEnabled(QwtPlot::yLeft, false);
|
|
mag1->setWheelFactor(5);
|
|
QwtPlotPanner* pan1 = new QwtPlotPanner(ui->powerPlot->canvas());
|
|
pan1->setAxisEnabled(QwtPlot::xBottom, true);
|
|
pan1->setAxisEnabled(QwtPlot::yLeft, false);
|
|
|
|
ui->powerPlot->replot();
|
|
|
|
// Bandwidth analysis:
|
|
sql = "SELECT time, AverageBandwidth FROM Bandwidth;";
|
|
query.exec(sql);
|
|
|
|
QwtPointSeriesData* data2 = new QwtPointSeriesData;
|
|
QwtPlotCurve* cur2 = new QwtPlotCurve("Speed");
|
|
QVector<QPointF>* samples2 = new QVector<QPointF>;
|
|
|
|
while (query.next())
|
|
{
|
|
double time = query.value(0).toDouble();
|
|
double percentage = query.value(1).toDouble() * 100.0;
|
|
samples2->push_back(QPointF(time, percentage));
|
|
}
|
|
|
|
data2->setSamples(*samples2);
|
|
cur2->setData(data2);
|
|
cur2->attach(ui->bandwidthPlot);
|
|
cur2->setPen(QPen(QColor(255, 0, 0)));
|
|
|
|
ui->bandwidthPlot->setAxisTitle(0, "Bandwidth [%]");
|
|
ui->bandwidthPlot->setAxisScale(0, 0.0, 100.0);
|
|
QwtText axisTitle2("Time [s]");
|
|
axisTitle2.setFont(ui->bandwidthPlot->axisTitle(QwtPlot::xBottom).font());
|
|
ui->bandwidthPlot->setAxisTitle(QwtPlot::xBottom, axisTitle2);
|
|
|
|
QwtPlotMagnifier* mag2 = new QwtPlotMagnifier(ui->bandwidthPlot->canvas());
|
|
mag2->setAxisEnabled(QwtPlot::xBottom, true);
|
|
mag2->setAxisEnabled(QwtPlot::yLeft, false);
|
|
mag2->setWheelFactor(5);
|
|
QwtPlotPanner* pan2 = new QwtPlotPanner(ui->bandwidthPlot->canvas());
|
|
pan2->setAxisEnabled(QwtPlot::xBottom, true);
|
|
pan2->setAxisEnabled(QwtPlot::yLeft, false);
|
|
|
|
ui->bandwidthPlot->replot();
|
|
|
|
// Buffer analysis:
|
|
// sql = "select max(BufferNumber) from BufferDepth;";
|
|
// query.exec(sql);
|
|
// query.next();
|
|
// unsigned int numberOfBuffers = query.value(0).toUInt();
|
|
|
|
sql = "select MaxBufferDepth from GeneralInfo;";
|
|
query.exec(sql);
|
|
query.next();
|
|
unsigned int maxBufferDepth = query.value(0).toUInt();
|
|
|
|
sql = "select Time, AverageBufferDepth from BufferDepth where BufferNumber = 0"; // TODO
|
|
query.exec(sql);
|
|
|
|
QwtPointSeriesData* data3 = new QwtPointSeriesData;
|
|
QwtPlotCurve* cur3 = new QwtPlotCurve("Speed");
|
|
QVector<QPointF>* samples3 = new QVector<QPointF>;
|
|
|
|
while (query.next())
|
|
{
|
|
double time = query.value(0).toDouble();
|
|
double queue = query.value(1).toDouble();
|
|
samples3->push_back(QPointF(time, queue));
|
|
}
|
|
|
|
data3->setSamples(*samples3);
|
|
cur3->setData(data3);
|
|
cur3->attach(ui->bufferPlot);
|
|
cur3->setPen(QPen(QColor(255, 0, 0)));
|
|
|
|
ui->bufferPlot->setAxisTitle(0, "Buffer Utilization");
|
|
ui->bufferPlot->setAxisScale(0, 0.0, maxBufferDepth);
|
|
QwtText axisTitle3("Time [s]");
|
|
axisTitle3.setFont(ui->bufferPlot->axisTitle(QwtPlot::xBottom).font());
|
|
ui->bufferPlot->setAxisTitle(QwtPlot::xBottom, axisTitle3);
|
|
|
|
QwtPlotMagnifier* mag3 = new QwtPlotMagnifier(ui->bufferPlot->canvas());
|
|
mag3->setAxisEnabled(QwtPlot::xBottom, true);
|
|
mag3->setAxisEnabled(QwtPlot::yLeft, false);
|
|
mag3->setWheelFactor(5);
|
|
QwtPlotPanner* pan3 = new QwtPlotPanner(ui->bufferPlot->canvas());
|
|
pan3->setAxisEnabled(QwtPlot::xBottom, true);
|
|
pan3->setAxisEnabled(QwtPlot::yLeft, false);
|
|
|
|
ui->bufferPlot->replot();
|
|
}
|