# HG changeset patch # User Hasan Yavuz ÖZDERYA # Date 2018-04-15 13:34:06 # Node ID f6ca721ac7595473d3c2b9fe7c4c1cdda0ea31c8 # Parent 94b1aaec4e69e34684a582b8cc058c1d8500cc92 added reworked DataRecorder with simple test diff --git a/src/datarecorder.cpp b/src/datarecorder.cpp --- a/src/datarecorder.cpp +++ b/src/datarecorder.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2017 Hasan Yavuz Özderya + Copyright © 2018 Hasan Yavuz Özderya This file is part of serialplot. @@ -54,26 +54,29 @@ bool DataRecorder::startRecording(QStrin return true; } -void DataRecorder::addData(double* data, unsigned length, unsigned numOfChannels) +void DataRecorder::feedIn(const SamplePack& data) { - Q_ASSERT(length > 0); - Q_ASSERT(length % numOfChannels == 0); + Q_ASSERT(file.isOpen()); // recorder should be disconnected before stopping recording + Q_ASSERT(!data.hasX()); // NYI - if (lastNumChannels != 0 && numOfChannels != lastNumChannels) + // check if number of channels has changed during recording and warn + unsigned numChannels = data.numChannels(); + if (lastNumChannels != 0 && numChannels != lastNumChannels) { qWarning() << "Number of channels changed from " << lastNumChannels - << " to " << numOfChannels << + << " to " << numChannels << " during recording, CSV file is corrupted but no data will be lost."; } - lastNumChannels = numOfChannels; + lastNumChannels = numChannels; - unsigned numOfSamples = length / numOfChannels; // per channel - for (unsigned int i = 0; i < numOfSamples; i++) + // write data + unsigned numSamples = data.numSamples(); + for (unsigned int i = 0; i < numSamples; i++) { - for (unsigned ci = 0; ci < numOfChannels; ci++) + for (unsigned ci = 0; ci < numChannels; ci++) { - fileStream << data[ci * numOfSamples + i]; - if (ci != numOfChannels-1) fileStream << _sep; + fileStream << data.data(ci)[i]; + if (ci != numChannels-1) fileStream << _sep; } fileStream << le(); } diff --git a/src/datarecorder.h b/src/datarecorder.h --- a/src/datarecorder.h +++ b/src/datarecorder.h @@ -1,5 +1,5 @@ /* - Copyright © 2017 Hasan Yavuz Özderya + Copyright © 2018 Hasan Yavuz Özderya This file is part of serialplot. @@ -24,7 +24,15 @@ #include #include -class DataRecorder : public QObject +#include "sink.h" + +/** + * Implemented as a `Sink` that writes incoming data to a file. Before + * connecting a `Source` recording must be started with the `startRecording` + * method. Also before calling `stopRecording`, recorder should be disconnected + * from source. + */ +class DataRecorder : public QObject, public Sink { Q_OBJECT public: @@ -44,7 +52,8 @@ public: /** * @brief Starts recording data to a file in CSV format. * - * File is opened and header line (names of channels) is written. + * File is opened and header line (names of channels) is written. After + * calling this function recorder should be connected to a `Source`. * * @param fileName name of the recording file * @param separator column separator @@ -75,6 +84,9 @@ public: /// Stops recording, closes file. void stopRecording(); +protected: + virtual void feedIn(const SamplePack& data); + private: unsigned lastNumChannels; ///< used for error message only QFile file; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -71,6 +71,21 @@ add_executable(TestReaders EXCLUDE_FROM_ qt5_use_modules(TestReaders Widgets Test) add_test(NAME test_readers COMMAND TestReaders) +# test for recroder +add_executable(TestRecorder EXCLUDE_FROM_ALL + test_recorder.cpp + ../src/samplepack.cpp + ../src/sink.cpp + ../src/source.cpp + ../src/datarecorder.cpp +) +qt5_use_modules(TestRecorder Widgets Test) +add_test(NAME test_recorder COMMAND TestRecorder) + set(CMAKE_CTEST_COMMAND ctest -V) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) -add_dependencies(check Test TestReaders) +add_dependencies(check + Test + TestReaders + TestRecorder + ) diff --git a/tests/test_recorder.cpp b/tests/test_recorder.cpp new file mode 100644 --- /dev/null +++ b/tests/test_recorder.cpp @@ -0,0 +1,64 @@ +/* + 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 . +*/ + +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file +#include "catch.hpp" + +#include +#include "datarecorder.h" +#include "test_helpers.h" + +#define TEST_FILE_NAME "sp_test_recording.csv" + +TEST_CASE("test recording single channel", "[recorder]") +{ + DataRecorder rec; + TestSource source(1, false); + + // temporary file, remove if exists + auto fileName = QDir::tempPath() + QString("/" TEST_FILE_NAME); + if (QFile::exists(fileName)) QFile::remove(fileName); + + // connect source → sink + source.connectSink(&rec); + + // prepare data + QStringList channelNames({"Channel 1"}); + SamplePack samples(5, 1); + for (int i = 0; i < 5; i++) + { + samples.data(0)[i] = i+1; + } + + // test + rec.startRecording(fileName, ",", channelNames); + source._feed(samples); + rec.stopRecording(); + + // read file contents back + QFile recordFile(fileName); + REQUIRE(recordFile.open(QIODevice::ReadOnly | QIODevice::Text)); + // NOTE: mind the extra parantheses, otherwise 'catch' macros fail to compile + REQUIRE((recordFile.readLine() == "Channel 1\n")); + for (int i = 0; i < 5; i++) + REQUIRE((recordFile.readLine() == QString("%1\n").arg(i+1))); + + // cleanup + if (QFile::exists(fileName)) QFile::remove(fileName); +}