# HG changeset patch # User Hasan Yavuz ÖZDERYA # Date 2018-02-24 14:14:27 # Node ID 5c0005f331cfb3cb6d1c6ef386ca1b93bce731df # Parent 391ab62116c8cdc7937ccf931ed9ce75282cc967 various fixes to get stream to compile and some basic tests diff --git a/src/channelinfomodel.cpp b/src/channelinfomodel.cpp --- a/src/channelinfomodel.cpp +++ b/src/channelinfomodel.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2017 Hasan Yavuz Özderya + Copyright © 2018 Hasan Yavuz Özderya This file is part of serialplot. @@ -336,7 +336,7 @@ void ChannelInfoModel::resetVisibility() endResetModel(); } -void ChannelInfoModel::saveSettings(QSettings* settings) +void ChannelInfoModel::saveSettings(QSettings* settings) const { settings->beginGroup(SettingGroup_Channels); settings->beginWriteArray(SG_Channels_Channel); diff --git a/src/channelinfomodel.h b/src/channelinfomodel.h --- a/src/channelinfomodel.h +++ b/src/channelinfomodel.h @@ -1,5 +1,5 @@ /* - Copyright © 2017 Hasan Yavuz Özderya + Copyright © 2018 Hasan Yavuz Özderya This file is part of serialplot. @@ -57,7 +57,7 @@ public: void setNumOfChannels(unsigned number); /// Stores all channel info into a `QSettings` - void saveSettings(QSettings* settings); + void saveSettings(QSettings* settings) const; /// Loads all channel info from a `QSettings`. void loadSettings(QSettings* settings); diff --git a/src/framebuffer2.h b/src/framebuffer2.h --- a/src/framebuffer2.h +++ b/src/framebuffer2.h @@ -1,5 +1,5 @@ /* - Copyright © 2017 Hasan Yavuz Özderya + Copyright © 2018 Hasan Yavuz Özderya This file is part of serialplot. @@ -45,6 +45,7 @@ public: /// Common base class for index and writable frame buffers class ResizableBuffer : public FrameBuffer { +public: /// Resize the buffer. /// /// @important Resizing to same value is an error. diff --git a/src/samplepack.cpp b/src/samplepack.cpp --- a/src/samplepack.cpp +++ b/src/samplepack.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2017 Hasan Yavuz Özderya + Copyright © 2018 Hasan Yavuz Özderya This file is part of serialplot. @@ -76,3 +76,13 @@ double* SamplePack::data(unsigned channe return &_yData[channel * _numSamples]; } + +double* SamplePack::xData() +{ + return const_cast(static_cast(*this).xData()); +} + +double* SamplePack::data(unsigned channel) +{ + return const_cast(static_cast(*this).data(channel)); +} diff --git a/src/samplepack.h b/src/samplepack.h --- a/src/samplepack.h +++ b/src/samplepack.h @@ -32,6 +32,9 @@ public: double* xData() const; double* data(unsigned channel) const; + double* xData(); + double* data(unsigned channel); + private: unsigned _numSamples, _numChannels; double* _xData; diff --git a/src/sink.h b/src/sink.h --- a/src/sink.h +++ b/src/sink.h @@ -30,12 +30,15 @@ class Sink public: /// Placeholder virtual destructor virtual ~Sink() {}; + /// Connects a sink to get any data that this sink /// gets. Connecting an already connected sink is an error. void connectFollower(Sink* sink); + /// Disconnects a follower. Disconnecting an unconnected sink is /// an error. void disconnectFollower(Sink* sink); + /// Returns the connected source. `NULL` if it's not connected. const Source* connectedSource() const; @@ -43,9 +46,11 @@ protected: /// Entry point for incoming data. Re-implementations should /// call this function to feed followers. virtual void feedIn(const SamplePack& data); + /// Is set by connected source. Re-implementations should call /// this function to update followers. virtual void setNumChannels(unsigned nc, bool x); + /// Set by the connected source when its connected. When /// disconnecting it's set to `NULL`. /// diff --git a/src/source.cpp b/src/source.cpp --- a/src/source.cpp +++ b/src/source.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2017 Hasan Yavuz Özderya + Copyright © 2018 Hasan Yavuz Özderya This file is part of serialplot. @@ -21,6 +21,14 @@ #include "source.h" +Source::~Source() +{ + for (auto sink : sinks) + { + sink->setSource(NULL); + } +} + void Source::connect(Sink* sink) { Q_ASSERT(!sinks.contains(sink)); diff --git a/src/source.h b/src/source.h --- a/src/source.h +++ b/src/source.h @@ -28,16 +28,19 @@ class Source { public: - /// Placeholder virtual destructor - virtual ~Source() {}; + /// Virtual destructor. Must be called by implementors to notify sinks. + virtual ~Source(); + /// Returns true if source has X data virtual bool hasX() const = 0; + /// Returns number of channels virtual unsigned numChannels() const = 0; /// Connects a sink to this source. Trying to connect an already /// connected sink is an error. void connect(Sink* sink); + /// Disconnects an already connected sink. Trying to disconnect an /// unconnected sink is an error. void disconnect(Sink* sink); @@ -45,6 +48,7 @@ public: protected: /// Feeds "in" given data to connected sinks void feedOut(const SamplePack& data) const; + /// Updates "number of channels" of connected sinks. Must be /// called when num. channels or hasX changes. void updateNumChannels() const; diff --git a/src/stream.cpp b/src/stream.cpp --- a/src/stream.cpp +++ b/src/stream.cpp @@ -19,6 +19,7 @@ #include "stream.h" #include "ringbuffer.h" +#include "indexbuffer.h" Stream::Stream(unsigned nc, bool x, unsigned ns) : _infoModel(nc) @@ -56,7 +57,7 @@ bool Stream::hasX() const return _hasx; } -unsigned Stream::numChannels() +unsigned Stream::numChannels() const { return channels.length(); } @@ -72,52 +73,57 @@ const StreamChannel* Stream::channel(uns return channels[index]; } +StreamChannel* Stream::channel(unsigned index) +{ + return const_cast(static_cast(*this).channel(index)); +} + void Stream::setNumChannels(unsigned nc, bool x) { unsigned oldNum = numChannels(); - if (oldNum == nc && x == _hasX) return; + if (oldNum == nc && x == _hasx) return; // adjust the number of channels if (nc > oldNum) { - for (int i = oldNum; i < nc; i++) + for (unsigned i = oldNum; i < nc; i++) { - auto c = new StreamChannel(i, xData, new RingBuffer(ns), &_infoModel); + auto c = new StreamChannel(i, xData, new RingBuffer(_numSamples), &_infoModel); channels.append(c); } } else if (nc < oldNum) { - for (int i = oldNum-1; i > nc-1; i--) + for (unsigned i = oldNum-1; i > nc-1; i--) { delete channels.takeLast(); } } // change the xdata - if (x != _hasX) + if (x != _hasx) { if (x) { - xData = new RingBuffer(ns); + xData = new RingBuffer(_numSamples); } else { - xData = new IndexBuffer(ns); + xData = new IndexBuffer(_numSamples); } for (auto c : channels) { - c.setX(xData); + c->setX(xData); } - _hasX = x; + _hasx = x; } if (nc != oldNum) { - _infoModel.setNumChannels(nc); + _infoModel.setNumOfChannels(nc); // TODO: how abut X change? - emit numOfChannelsChanged(nc); + emit numChannelsChanged(nc); } Sink::setNumChannels(nc, x); @@ -131,13 +137,13 @@ void Stream::feedIn(const SamplePack& da if (_paused) return; unsigned ns = data.numSamples(); - if (_hasX) + if (_hasx) { - xData.addSamples(data.xData(), ns); + static_cast(xData)->addSamples(data.xData(), ns); } for (unsigned i = 0; i < numChannels(); i++) { - channels[i].yData()->addSamples(data.data(i), ns); + static_cast(channels[i]->yData())->addSamples(data.data(i), ns); } Sink::feedIn(data); @@ -153,10 +159,10 @@ void Stream::setNumSamples(unsigned valu if (value == _numSamples) return; _numSamples = value; - xData.resize(value); + xData->resize(value); for (auto c : channels) { - channels[i].yData()->resize(value); + static_cast(c->yData())->resize(value); } } diff --git a/src/stream.h b/src/stream.h --- a/src/stream.h +++ b/src/stream.h @@ -29,21 +29,23 @@ #include "source.h" #include "channelinfomodel.h" #include "streamchannel.h" -#include "resizablebuffer.h" +#include "framebuffer2.h" /** * Main waveform storage class. It consists of channels. Channels are * synchronized with each other. * * Implements `Sink` class for data entry. It's expected to be - * connected to a `Device` source. Also implements a `Source` class - * for data that exists the buffers. + * connected to a `Device` source. */ -class Stream : public Sink, public QObject +class Stream : public QObject, public Sink { + Q_OBJECT + public: /** * @param nc number of channels + * @param x has X data input * @param ns number of samples */ Stream(unsigned nc = 0, bool x = false, unsigned ns = 0); @@ -51,10 +53,11 @@ public: // implementations for `Source` virtual bool hasX() const; - virtual unsigned numChannels(); + virtual unsigned numChannels() const; unsigned numSamples() const; const StreamChannel* channel(unsigned index) const; + StreamChannel* channel(unsigned index); /// Saves channel information void saveSettings(QSettings* settings) const; @@ -75,7 +78,7 @@ signals: public slots: // TODO: these won't be public - void setNumChannels(unsigned number); + // void setNumChannels(unsigned number); void setNumSamples(unsigned value); /// When paused data feed is ignored diff --git a/src/streamchannel.cpp b/src/streamchannel.cpp --- a/src/streamchannel.cpp +++ b/src/streamchannel.cpp @@ -34,11 +34,11 @@ StreamChannel::~StreamChannel() } unsigned StreamChannel::index() const {return _index;} -QString StreamChannel::name() const {return _info->name(_index)}; -QColor StreamChannel::color() const {return _info->color(_index)}; -bool StreamChannel::visible() const {return _info->isVisible(_index)}; +QString StreamChannel::name() const {return _info->name(_index);}; +QColor StreamChannel::color() const {return _info->color(_index);}; +bool StreamChannel::visible() const {return _info->isVisible(_index);}; const FrameBuffer* StreamChannel::xData() const {return _x;} const FrameBuffer* StreamChannel::yData() const {return _y;} FrameBuffer* StreamChannel::yData() {return _y;} const ChannelInfoModel* StreamChannel::info() const {return _info;} -void StreamChannel::setX(const FrameBuffer* x) {_x = x}; +void StreamChannel::setX(const FrameBuffer* x) {_x = x;}; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright © 2017 Hasan Yavuz Özderya +# Copyright © 2018 Hasan Yavuz Özderya # # This file is part of serialplot. # @@ -24,6 +24,7 @@ include_directories("../src") add_executable(Test EXCLUDE_FROM_ALL test.cpp + test_stream.cpp ../src/samplepack.cpp ../src/sink.cpp ../src/source.cpp @@ -31,6 +32,9 @@ add_executable(Test EXCLUDE_FROM_ALL ../src/linindexbuffer.cpp ../src/ringbuffer.cpp ../src/readonlybuffer.cpp + ../src/stream.cpp + ../src/streamchannel.cpp + ../src/channelinfomodel.cpp ) add_test(NAME test1 COMMAND Test) qt5_use_modules(Test Widgets) diff --git a/tests/test.cpp b/tests/test.cpp --- a/tests/test.cpp +++ b/tests/test.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2017 Hasan Yavuz Özderya + Copyright © 2018 Hasan Yavuz Özderya This file is part of serialplot. @@ -27,6 +27,8 @@ #include "ringbuffer.h" #include "readonlybuffer.h" +#include "test_helpers.h" + TEST_CASE("samplepack with no X", "[memory]") { SamplePack pack(100, 3, false); @@ -53,48 +55,6 @@ TEST_CASE("samplepack with X", "[memory] REQUIRE(pack.xData() != nullptr); } -class TestSink : public Sink -{ -public: - int totalFed; - int _numChannels; - bool _hasX; - - TestSink() - { - totalFed = 0; - _numChannels = 0; - _hasX = false; - }; - - void feedIn(const SamplePack& data) - { - REQUIRE(data.numChannels() == numChannels()); - - totalFed += data.numSamples(); - - Sink::feedIn(data); - }; - - void setNumChannels(unsigned nc, bool x) - { - _numChannels = nc; - _hasX = x; - - Sink::setNumChannels(nc, x); - }; - - virtual unsigned numChannels() const - { - return _numChannels; - }; - - virtual bool hasX() const - { - return _hasX; - }; -}; - TEST_CASE("sink", "[memory, stream]") { TestSink sink; @@ -129,42 +89,6 @@ TEST_CASE("sink must be created unconnec REQUIRE(sink.connectedSource() == NULL); } -class TestSource : public Source -{ -public: - int _numChannels; - bool _hasX; - - TestSource(unsigned nc, bool x) - { - _numChannels = nc; - _hasX = x; - }; - - virtual unsigned numChannels() const - { - return _numChannels; - }; - - virtual bool hasX() const - { - return _hasX; - }; - - void _feed(const SamplePack& data) const - { - feedOut(data); - }; - - void _setNumChannels(unsigned nc, bool x) - { - _numChannels = nc; - _hasX = x; - - updateNumChannels(); - }; -}; - TEST_CASE("source", "[memory, stream]") { TestSink sink; diff --git a/tests/test_helpers.h b/tests/test_helpers.h new file mode 100644 --- /dev/null +++ b/tests/test_helpers.h @@ -0,0 +1,86 @@ +#ifndef TEST_HELPERS_H +#define TEST_HELPERS_H + +#include "source.h" +#include "sink.h" + +class TestSink : public Sink +{ +public: + int totalFed; + int _numChannels; + bool _hasX; + + TestSink() + { + totalFed = 0; + _numChannels = 0; + _hasX = false; + }; + + void feedIn(const SamplePack& data) + { + REQUIRE(data.numChannels() == numChannels()); + + totalFed += data.numSamples(); + + Sink::feedIn(data); + }; + + void setNumChannels(unsigned nc, bool x) + { + _numChannels = nc; + _hasX = x; + + Sink::setNumChannels(nc, x); + }; + + virtual unsigned numChannels() const + { + return _numChannels; + }; + + virtual bool hasX() const + { + return _hasX; + }; +}; + +class TestSource : public Source +{ +public: + int _numChannels; + bool _hasX; + + TestSource(unsigned nc, bool x) + { + _numChannels = nc; + _hasX = x; + }; + + virtual unsigned numChannels() const + { + return _numChannels; + }; + + virtual bool hasX() const + { + return _hasX; + }; + + void _feed(const SamplePack& data) const + { + feedOut(data); + }; + + void _setNumChannels(unsigned nc, bool x) + { + _numChannels = nc; + _hasX = x; + + updateNumChannels(); + }; +}; + + +#endif // TEST_HELPERS_H diff --git a/tests/test_stream.cpp b/tests/test_stream.cpp new file mode 100644 --- /dev/null +++ b/tests/test_stream.cpp @@ -0,0 +1,85 @@ +/* + 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 . +*/ + +#include "stream.h" + +#include "catch.hpp" +#include "test_helpers.h" + +TEST_CASE("construction of stream with default values", "[memory, stream]") +{ + // default values are an empty stream with no channels + Stream s; + + REQUIRE(s.numChannels() == 0); + REQUIRE(!s.hasX()); + REQUIRE(s.numSamples() == 0); +} + +TEST_CASE("construction of stream with parameters", "[memory, stream]") +{ + Stream s(4, true, 100); + + REQUIRE(s.numChannels() == 4); + REQUIRE(s.hasX()); + REQUIRE(s.numSamples() == 100); + + for (unsigned i = 0; i < 4; i++) + { + const StreamChannel* c = s.channel(i); + REQUIRE(c != NULL); + REQUIRE(c->index() == i); + } +} + +TEST_CASE("adding data to a stream with no X", "[memory, stream, data]") +{ + Stream s(3, false, 10); + + // prepare data + SamplePack pack(5, 3, false); + for (unsigned ci = 0; ci < 3; ci++) + { + for (unsigned i = 0; i < 5; i++) + { + pack.data(ci)[i] = i; + } + } + + TestSource so(3, false); + so.connect(&s); + + // test + so._feed(pack); + + for (unsigned ci = 0; ci < 3; ci++) + { + const StreamChannel* c = s.channel(ci); + const FrameBuffer* y = c->yData(); + + for (unsigned i = 0; i < 5; i++) + { + REQUIRE(y->sample(i) == 0); + } + for (unsigned i = 5; i < 10; i++) + { + REQUIRE(y->sample(i) == i-5); + } + } +}