Changeset - f90eae47c75b
[Not reviewed]
stream
0 8 0
Hasan Yavuz Ă–ZDERYA - 7 years ago 2018-06-23 10:12:14
hy@ozderya.net
snapshots now use read only buffer
8 files changed with 63 insertions and 57 deletions:
0 comments (0 inline, 0 general)
CMakeLists.txt
Show inline comments
 
@@ -117,24 +117,25 @@ add_executable(${PROGRAM_NAME} WIN32
 
  src/dataformatpanel.cpp
 
  src/plotcontrolpanel.cpp
 
  src/recordpanel.cpp
 
  src/datarecorder.cpp
 
  src/tooltipfilter.cpp
 
  src/sneakylineedit.cpp
 
  src/stream.cpp
 
  src/streamchannel.cpp
 
  src/channelinfomodel.cpp
 
  src/ringbuffer.cpp
 
  src/ringbuffer.cpp
 
  src/indexbuffer.cpp
 
  src/readonlybuffer.cpp
 
  src/framebufferseries.cpp
 
  src/numberformatbox.cpp
 
  src/endiannessbox.cpp
 
  src/abstractreader.cpp
 
  src/binarystreamreader.cpp
 
  src/binarystreamreadersettings.cpp
 
  src/asciireader.cpp
 
  src/asciireadersettings.cpp
 
  src/demoreader.cpp
 
  src/demoreadersettings.cpp
 
  src/framedreader.cpp
 
  src/framedreadersettings.cpp
src/plotmanager.cpp
Show inline comments
 
@@ -38,49 +38,48 @@ PlotManager::PlotManager(QWidget* plotAr
 
    // connect to ChannelInfoModel
 
    infoModel = _stream->infoModel();
 
    connect(infoModel, &QAbstractItemModel::dataChanged,
 
                this, &PlotManager::onChannelInfoChanged);
 
    connect(infoModel, &QAbstractItemModel::modelReset,
 
            [this]()
 
            {
 
                onChannelInfoChanged(infoModel->index(0, 0), // start
 
                                     infoModel->index(infoModel->rowCount()-1, 0), // end
 
                                     {}); // roles ignored
 
            });
 

	
 

	
 
    connect(stream, &Stream::numChannelsChanged, this, &PlotManager::onNumChannelsChanged);
 
    connect(stream, &Stream::dataAdded, this, &PlotManager::replot);
 

	
 
    // add initial curves if any?
 
    for (unsigned int i = 0; i < stream->numChannels(); i++)
 
    {
 
        addCurve(stream->channel(i)->name(), stream->channel(i)->yData());
 
    }
 

	
 
}
 

	
 
PlotManager::PlotManager(QWidget* plotArea, PlotMenu* menu,
 
                         Snapshot* snapshot, QObject *parent) :
 
    QObject(parent)
 
{
 
    construct(plotArea, menu);
 

	
 
    setNumOfSamples(snapshot->numSamples());
 
    setPlotWidth(snapshot->numSamples());
 
    infoModel = snapshot->infoModel();
 

	
 
    for (unsigned ci = 0; ci < snapshot->numChannels(); ci++)
 
    {
 
        addCurve(snapshot->channelName(ci), snapshot->data[ci]);
 
        addCurve(snapshot->channelName(ci), snapshot->yData[ci]);
 
    }
 

	
 
    connect(infoModel, &QAbstractItemModel::dataChanged,
 
            this, &PlotManager::onChannelInfoChanged);
 
}
 

	
 
void PlotManager::construct(QWidget* plotArea, PlotMenu* menu)
 
{
 
    _menu = menu;
 
    _plotArea = plotArea;
 
    _autoScaled = true;
 
    _yMin = 0;
 
@@ -329,31 +328,24 @@ Plot* PlotManager::addPlotWidget()
 
    return plot;
 
}
 

	
 
void PlotManager::addCurve(QString title, const FrameBuffer* buffer)
 
{
 
    auto curve = new QwtPlotCurve(title);
 
    auto series = new FrameBufferSeries(buffer);
 
    series->setXAxis(_xAxisAsIndex, _xMin, _xMax);
 
    curve->setSamples(series);
 
    _addCurve(curve);
 
}
 

	
 
void PlotManager::addCurve(QString title, QVector<QPointF> data)
 
{
 
    auto curve = new QwtPlotCurve(title);
 
    curve->setSamples(data);
 
    _addCurve(curve);
 
}
 

	
 
void PlotManager::_addCurve(QwtPlotCurve* curve)
 
{
 
    // store and init the curve
 
    curves.append(curve);
 

	
 
    unsigned index = curves.size()-1;
 
    auto color = infoModel->color(index);
 
    curve->setPen(color);
 

	
 
    // create the plot for the curve if we are on multi display
 
    Plot* plot;
 
    if (isMulti)
src/plotmanager.h
Show inline comments
 
@@ -41,26 +41,24 @@ class PlotManager : public QObject
 

	
 
public:
 
    explicit PlotManager(QWidget* plotArea, PlotMenu* menu,
 
                         const Stream* stream = NULL,
 
                         QObject *parent = 0);
 
    explicit PlotManager(QWidget* plotArea, PlotMenu* menu,
 
                         Snapshot* snapshot,
 
                         QObject *parent = 0);
 
    ~PlotManager();
 
    /// Add a new curve with title and buffer. A color is
 
    /// automatically chosen for curve.
 
    void addCurve(QString title, const FrameBuffer* buffer);
 
    /// Alternative of `addCurve` for static curve data (snapshots).
 
    void addCurve(QString title, QVector<QPointF> data);
 
    /// Removes curves from the end
 
    void removeCurves(unsigned number);
 
    /// Returns current number of curves known by plot manager
 
    unsigned numOfCurves();
 

	
 
public slots:
 
    /// Enable/Disable multiple plot display
 
    void setMulti(bool enabled);
 
    /// Update all plot widgets
 
    void replot();
 
    /// Enable display of a "DEMO" label on each plot
 
    void showDemoIndicator(bool show = true);
src/readonlybuffer.cpp
Show inline comments
 
/*
 
  Copyright © 2017 Hasan Yavuz Özderya
 
  Copyright © 2018 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
  serialplot 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 3 of the License, or
 
  (at your option) any later version.
 

	
 
  serialplot 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 serialplot.  If not, see <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#include <QtGlobal>
 
#include <string.h>
 

	
 
#include "readonlybuffer.h"
 

	
 
ReadOnlyBuffer::ReadOnlyBuffer(const FrameBuffer* source) :
 
    ReadOnlyBuffer(source, 0, source->size())
 
{
 
    // empty
 
    // intentionally empty, see ↑
 
}
 

	
 
ReadOnlyBuffer::ReadOnlyBuffer(const FrameBuffer* source, unsigned start, unsigned n)
 
{
 
    Q_ASSERT(source->size() > 0);
 
    Q_ASSERT(start + n <= source->size());
 

	
 
    _size = n;
 
    data = new double[_size];
 

	
 
    for (unsigned i = 0; i < n; i++)
 
    {
 
        data[i] = source->sample(start + i);
 
    }
 

	
 
    /// if not exact copy of source re-calculate limits
 
    if (start == 0 && n == source->size())
 
    {
 
        _limits = source->limits();
 
    }
 
    else
 
    {
 
        // TODO: code duplication with RingBuffer::updateLimits, consider reuse
 
        _limits.start = data[0];
 
        _limits.end = data[0];
 
        updateLimits();
 
    }
 
}
 

	
 
        for (unsigned i = 0; i < _size; i++)
 
        {
 
            if (data[i] > _limits.end)
 
            {
 
                _limits.end = data[i];
 
            }
 
            else if (data[i] < _limits.start)
 
            {
 
                _limits.start = data[i];
 
            }
 
        }
 
    }
 
ReadOnlyBuffer::ReadOnlyBuffer(const double* source, unsigned ssize)
 
{
 
    Q_ASSERT(source != nullptr && ssize);
 

	
 
    _size = ssize;
 
    data = new double[_size];
 
    memcpy(data, source, sizeof(double) * ssize);
 
    updateLimits();
 
}
 

	
 
ReadOnlyBuffer::~ReadOnlyBuffer()
 
{
 
    delete[] data;
 
}
 

	
 
unsigned ReadOnlyBuffer::size() const
 
{
 
    return _size;
 
}
 

	
 
double ReadOnlyBuffer::sample(unsigned i) const
 
{
 
    return data[i];
 
}
 

	
 
Range ReadOnlyBuffer::limits() const
 
{
 
    return _limits;
 
}
 

	
 
void ReadOnlyBuffer::updateLimits()
 
{
 
    Q_ASSERT(_size);
 

	
 
    _limits.start = data[0];
 
    _limits.end = data[0];
 

	
 
    for (unsigned i = 0; i < _size; i++)
 
    {
 
        if (data[i] > _limits.end)
 
        {
 
            _limits.end = data[i];
 
        }
 
        else if (data[i] < _limits.start)
 
        {
 
            _limits.start = data[i];
 
        }
 
    }
 
}
src/readonlybuffer.h
Show inline comments
 
@@ -32,25 +32,31 @@ public:
 
    /// empty.
 
    ReadOnlyBuffer(const FrameBuffer* source);
 

	
 
    /// Creates a buffer from a slice of the `source`.
 
    ///
 
    /// @param start start of the slice
 
    /// @param n number of samples
 
    ///
 
    /// @important (start + n) should be smaller or equal than `source->size()`,
 
    /// otherwise it's an error.
 
    ReadOnlyBuffer(const FrameBuffer* source, unsigned start, unsigned n);
 

	
 
    /// Creates a buffer with data copied from an array
 
    ReadOnlyBuffer(const double* source, unsigned ssize);
 

	
 
    ~ReadOnlyBuffer();
 

	
 
    virtual unsigned size() const;
 
    virtual double sample(unsigned i) const;
 
    virtual Range limits() const;
 

	
 
private:
 
    double* data;    ///< data storage
 
    unsigned _size;  ///< data size
 
    Range _limits;   ///< limits cache
 

	
 
    // TODO: duplicate with `RingBuffer`
 
    void updateLimits(); ///< Updates limits cache
 
};
 

	
 
#endif // READONLYBUFFER_H
src/snapshot.cpp
Show inline comments
 
@@ -101,74 +101,70 @@ QString Snapshot::displayName()
 
    }
 
}
 

	
 
void Snapshot::setName(QString name)
 
{
 
    _name = name;
 
    _showAction.setText(_name);
 
    emit nameChanged(this);
 
}
 

	
 
unsigned Snapshot::numChannels() const
 
{
 
    return data.size();
 
    return yData.size();
 
}
 

	
 
unsigned Snapshot::numSamples() const
 
{
 
    return data[0].size();
 
    return yData[0]->size();
 
}
 

	
 
const ChannelInfoModel* Snapshot::infoModel() const
 
{
 
    return &cInfoModel;
 
}
 

	
 
ChannelInfoModel* Snapshot::infoModel()
 
{
 
    return const_cast<ChannelInfoModel*>(static_cast<const Snapshot&>(*this).infoModel());
 
}
 

	
 
QString Snapshot::channelName(unsigned channel)
 
{
 
    return cInfoModel.name(channel);
 
}
 

	
 
void Snapshot::save(QString fileName)
 
{
 
    // TODO: remove code duplication (MainWindow::onExportCsv)
 
    QSaveFile file(fileName);
 

	
 
    if (file.open(QIODevice::WriteOnly | QIODevice::Text))
 
    {
 
        QTextStream fileStream(&file);
 

	
 
        unsigned numOfChannels = data.size();
 
        unsigned numOfSamples = data[0].size();
 

	
 
        // print header
 
        for (unsigned int ci = 0; ci < numOfChannels; ci++)
 
        for (unsigned int ci = 0; ci < numChannels(); ci++)
 
        {
 
            fileStream << channelName(ci);
 
            if (ci != numOfChannels-1) fileStream << ",";
 
            if (ci != numChannels()-1) fileStream << ",";
 
        }
 
        fileStream << '\n';
 

	
 
        // print rows
 
        for (unsigned int i = 0; i < numOfSamples; i++)
 
        for (unsigned int i = 0; i < numSamples(); i++)
 
        {
 
            for (unsigned int ci = 0; ci < numOfChannels; ci++)
 
            for (unsigned int ci = 0; ci < numChannels(); ci++)
 
            {
 
                fileStream << data[ci][i].y();
 
                if (ci != numOfChannels-1) fileStream << ",";
 
                fileStream << yData[ci]->sample(i);
 
                if (ci != numChannels()-1) fileStream << ",";
 
            }
 
            fileStream << '\n';
 
        }
 

	
 
        if (!file.commit())
 
        {
 
            qCritical() << "File save error during snapshot save: " << file.error();
 
        }
 
        else
 
        {
 
            _saved = true;
 
            _showAction.setText(displayName());
src/snapshot.h
Show inline comments
 
@@ -18,37 +18,39 @@
 
*/
 

	
 
#ifndef SNAPSHOT_H
 
#define SNAPSHOT_H
 

	
 
#include <QObject>
 
#include <QAction>
 
#include <QVector>
 
#include <QString>
 
#include <QStringList>
 

	
 
#include "channelinfomodel.h"
 
#include "readonlybuffer.h"
 

	
 
class SnapshotView;
 
class MainWindow;
 

	
 
class Snapshot : public QObject
 
{
 
    Q_OBJECT
 

	
 
public:
 
    Snapshot(MainWindow* parent, QString name, ChannelInfoModel infoModel, bool saved = false);
 
    ~Snapshot();
 

	
 
    QVector<QVector<QPointF>> data;
 
    // TODO: yData of snapshot shouldn't be public, preferable should be handled in constructor
 
    QVector<ReadOnlyBuffer*> yData;
 
    QAction* showAction();
 
    QAction* deleteAction();
 

	
 
    QString name();
 
    QString displayName(); ///< `name()` plus '*' if snapshot is not saved
 
    unsigned numChannels() const; ///< number of channels in this snapshot
 
    unsigned numSamples() const;  ///< number of samples in every channel
 
    const ChannelInfoModel* infoModel() const;
 
    ChannelInfoModel* infoModel();
 
    void setName(QString name);
 
    QString channelName(unsigned channel);
 

	
src/snapshotmanager.cpp
Show inline comments
 
@@ -59,36 +59,27 @@ SnapshotManager::~SnapshotManager()
 
{
 
    for (auto snapshot : snapshots)
 
    {
 
        delete snapshot;
 
    }
 
}
 

	
 
Snapshot* SnapshotManager::makeSnapshot() const
 
{
 
    QString name = QTime::currentTime().toString("'Snapshot ['HH:mm:ss']'");
 
    auto snapshot = new Snapshot(_mainWindow, name, *(_stream->infoModel()));
 

	
 
    unsigned numChannels = _stream->numChannels();
 
    unsigned numSamples = _stream->numSamples();
 

	
 
    for (unsigned ci = 0; ci < numChannels; ci++)
 
    for (unsigned ci = 0; ci < _stream->numChannels(); ci++)
 
    {
 
        snapshot->data.append(QVector<QPointF>(numSamples));
 
        auto x = _stream->channel(ci)->xData();
 
        auto y = _stream->channel(ci)->yData();
 
        for (unsigned i = 0; i < numSamples; i++)
 
        {
 
            snapshot->data[ci][i] = QPointF(x->sample(i), y->sample(i));
 
        }
 
        snapshot->yData.append(new ReadOnlyBuffer(_stream->channel(ci)->yData()));
 
    }
 

	
 
    return snapshot;
 
}
 

	
 
void SnapshotManager::takeSnapshot()
 
{
 
    addSnapshot(makeSnapshot());
 
}
 

	
 
void SnapshotManager::addSnapshot(Snapshot* snapshot, bool update_menu)
 
{
 
@@ -151,25 +142,25 @@ void SnapshotManager::loadSnapshotFromFi
 
    {
 
        qCritical() << "Couldn't open file: " << fileName;
 
        qCritical() << file.errorString();
 
        return;
 
    }
 

	
 
    // read first row as headlines and determine number of channels
 
    auto headLine = QString(file.readLine());
 
    QStringList channelNames = headLine.split(',');
 
    unsigned numOfChannels = channelNames.size();
 

	
 
    // read data
 
    QVector<QVector<QPointF>> data(numOfChannels);
 
    QVector<QVector<double>> data(numOfChannels);
 
    unsigned lineNum = 1;
 
    while (file.canReadLine())
 
    {
 
        // parse line
 
        auto line = QString(file.readLine());
 
        auto split = line.split(',');
 

	
 
        if (split.size() != (int) numOfChannels)
 
        {
 
            qCritical() << "Parsing error at line " << lineNum
 
                        << ": number of columns is not consistent.";
 
            return;
 
@@ -179,35 +170,38 @@ void SnapshotManager::loadSnapshotFromFi
 
        {
 
            // parse column
 
            bool ok;
 
            double y = split[ci].toDouble(&ok);
 
            if (!ok)
 
            {
 
                qCritical() << "Parsing error at line " << lineNum
 
                            << ", column " << ci
 
                            << ": can't convert \"" << split[ci]
 
                            << "\" to double.";
 
                return;
 
            }
 
            data[ci].append(QPointF(lineNum-1, y));
 
            data[ci].append(y);
 
        }
 
        lineNum++;
 
    }
 

	
 
    ChannelInfoModel channelInfo(channelNames);
 

	
 
    // create snapshot
 
    auto snapshot = new Snapshot(
 
        _mainWindow, QFileInfo(fileName).baseName(),
 
        ChannelInfoModel(channelNames), true);
 
    snapshot->data = data;
 

	
 
    for (unsigned ci = 0; ci < numOfChannels; ci++)
 
    {
 
        snapshot->yData.append(new ReadOnlyBuffer(data[ci].data(), data[ci].size()));
 
    }
 

	
 
    addSnapshot(snapshot, false);
 
}
 

	
 
QMenu* SnapshotManager::menu()
 
{
 
    return &_menu;
 
}
 

	
 
QAction* SnapshotManager::takeSnapshotAction()
 
{
 
    return &_takeSnapshotAction;
0 comments (0 inline, 0 general)