diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,10 @@ qt5_wrap_ui(UI_FILES src/commandwidget.ui src/dataformatpanel.ui src/plotcontrolpanel.ui + src/numberformatbox.ui + src/endiannessbox.ui + src/binarystreamreadersettings.ui + src/asciireadersettings.ui ) if (WIN32) @@ -93,6 +97,13 @@ add_executable(${PROGRAM_NAME} WIN32 src/sneakylineedit.cpp src/channelmanager.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 misc/windows_icon.rc ${UI_FILES} ${RES_FILES} diff --git a/src/abstractreader.cpp b/src/abstractreader.cpp new file mode 100644 --- /dev/null +++ b/src/abstractreader.cpp @@ -0,0 +1,27 @@ +/* + Copyright © 2016 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 "abstractreader.h" + +AbstractReader::AbstractReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) : + QObject(parent) +{ + _device = device; + _channelMan = channelMan; +} diff --git a/src/abstractreader.h b/src/abstractreader.h new file mode 100644 --- /dev/null +++ b/src/abstractreader.h @@ -0,0 +1,77 @@ +/* + Copyright © 2016 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 . +*/ + +#ifndef ABSTRACTREADER_H +#define ABSTRACTREADER_H + +#include +#include +#include + +#include "channelmanager.h" + +/** + * All reader classes must inherit this class. + */ +class AbstractReader : public QObject +{ + Q_OBJECT +public: + explicit AbstractReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0); + + /** + * Returns a widget to be shown in data format panel when reader + * is selected. + */ + virtual QWidget* settingsWidget() = 0; + + /** + * Number of channels being read. + * + * This number may be user selected or automatically determined + * from incoming stream. + */ + virtual unsigned numOfChannels() = 0; + + /// Reader should only read when enabled. Default state should be + /// 'disabled'. + virtual void enable(bool enabled = true) = 0; + +signals: + void numOfChannelsChanged(unsigned); + // TODO: this must be signaled by 'channel man' for better abstraction + void dataAdded(); ///< emitted when data added to channel man. + // TODO: this should be a part of 'channel man' + void samplesPerSecondChanged(unsigned); + +public slots: + /** + * Pauses the reading. + * + * Reader should actually continue reading to keep the + * synchronization but shouldn't commit data. + */ + virtual void pause(bool) = 0; + +protected: + QIODevice* _device; + ChannelManager* _channelMan; +}; + +#endif // ABSTRACTREADER_H diff --git a/src/asciireader.cpp b/src/asciireader.cpp new file mode 100644 --- /dev/null +++ b/src/asciireader.cpp @@ -0,0 +1,110 @@ +/* + Copyright © 2016 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 + +#include "asciireader.h" + +AsciiReader::AsciiReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) : + AbstractReader(device, channelMan, parent) +{ + paused = false; + sampleCount = 0; + // TODO: sps counter + + _numOfChannels = _settingsWidget.numOfChannels(); + connect(&_settingsWidget, &AsciiReaderSettings::numOfChannelsChanged, + this, &AsciiReader::numOfChannelsChanged); + connect(&_settingsWidget, &AsciiReaderSettings::numOfChannelsChanged, + [this](unsigned value){_numOfChannels = value;}); +} + +QWidget* AsciiReader::settingsWidget() +{ + return &_settingsWidget; +} + +unsigned AsciiReader::numOfChannels() +{ + return _numOfChannels; +} + +// TODO: this could be a part of AbstractReader +void AsciiReader::enable(bool enabled) +{ + if (enabled) + { + QObject::connect(_device, &QIODevice::readyRead, + this, &AsciiReader::onDataReady); + } + else + { + QObject::disconnect(_device, 0, this, 0); + } +} + +void AsciiReader::pause(bool enabled) +{ + paused = enabled; +} + +void AsciiReader::onDataReady() +{ + while(_device->canReadLine()) + { + QByteArray line = _device->readLine(); + + // discard data if paused + if (paused) + { + return; + } + + line = line.trimmed(); + auto separatedValues = line.split(','); + + int numReadChannels; // effective number of channels to read + if (separatedValues.length() >= int(_numOfChannels)) + { + numReadChannels = _numOfChannels; + } + else // there is missing channel data + { + numReadChannels = separatedValues.length(); + qWarning() << "Incoming data is missing data for some channels!"; + } + + // parse read line + for (int ci = 0; ci < numReadChannels; ci++) + { + bool ok; + double channelSample = separatedValues[ci].toDouble(&ok); + if (ok) + { + _channelMan->addChannelData(ci, &channelSample, 1); + sampleCount++; + } + else + { + qWarning() << "Data parsing error for channel: " << ci; + } + } + emit dataAdded(); + } +} diff --git a/src/asciireader.h b/src/asciireader.h new file mode 100644 --- /dev/null +++ b/src/asciireader.h @@ -0,0 +1,49 @@ +/* + Copyright © 2016 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 . +*/ + +#ifndef ASCIIREADER_H +#define ASCIIREADER_H + +#include "abstractreader.h" +#include "asciireadersettings.h" + +class AsciiReader : public AbstractReader +{ + Q_OBJECT + +public: + explicit AsciiReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0); + QWidget* settingsWidget(); + unsigned numOfChannels(); + void enable(bool enabled = true); + +public slots: + void pause(bool); + +private: + AsciiReaderSettings _settingsWidget; + unsigned _numOfChannels; + bool paused; + unsigned sampleCount; ///< used for sps counter + +private slots: + void onDataReady(); +}; + +#endif // ASCIIREADER_H diff --git a/src/asciireadersettings.cpp b/src/asciireadersettings.cpp new file mode 100644 --- /dev/null +++ b/src/asciireadersettings.cpp @@ -0,0 +1,47 @@ +/* + Copyright © 2016 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 "utils.h" + +#include "asciireadersettings.h" +#include "ui_asciireadersettings.h" + +AsciiReaderSettings::AsciiReaderSettings(QWidget *parent) : + QWidget(parent), + ui(new Ui::AsciiReaderSettings) +{ + ui->setupUi(this); + + // Note: if directly connected we get a runtime warning on incompatible signal arguments + connect(ui->spNumOfChannels, SELECT::OVERLOAD_OF(&QSpinBox::valueChanged), + [this](int value) + { + emit numOfChannelsChanged(value); + }); +} + +AsciiReaderSettings::~AsciiReaderSettings() +{ + delete ui; +} + +unsigned AsciiReaderSettings::numOfChannels() +{ + return ui->spNumOfChannels->value(); +} diff --git a/src/asciireadersettings.h b/src/asciireadersettings.h new file mode 100644 --- /dev/null +++ b/src/asciireadersettings.h @@ -0,0 +1,46 @@ +/* + Copyright © 2016 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 . +*/ + +#ifndef ASCIIREADERSETTINGS_H +#define ASCIIREADERSETTINGS_H + +#include + +namespace Ui { +class AsciiReaderSettings; +} + +class AsciiReaderSettings : public QWidget +{ + Q_OBJECT + +public: + explicit AsciiReaderSettings(QWidget *parent = 0); + ~AsciiReaderSettings(); + + unsigned numOfChannels(); + +signals: + void numOfChannelsChanged(unsigned); + +private: + Ui::AsciiReaderSettings *ui; +}; + +#endif // ASCIIREADERSETTINGS_H diff --git a/src/asciireadersettings.ui b/src/asciireadersettings.ui new file mode 100644 --- /dev/null +++ b/src/asciireadersettings.ui @@ -0,0 +1,57 @@ + + + AsciiReaderSettings + + + + 0 + 0 + 414 + 171 + + + + Form + + + + + 153 + 10 + 60 + 27 + + + + + 60 + 0 + + + + false + + + 1 + + + 32 + + + + + + 20 + 10 + 127 + 27 + + + + Number Of Channels: + + + + + + diff --git a/src/binarystreamreader.cpp b/src/binarystreamreader.cpp new file mode 100644 --- /dev/null +++ b/src/binarystreamreader.cpp @@ -0,0 +1,192 @@ +/* + Copyright © 2016 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 +#include + +#include "binarystreamreader.h" +#include "floatswap.h" + +BinaryStreamReader::BinaryStreamReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) : + AbstractReader(device, channelMan, parent) +{ + paused = false; + skipByteRequested = false; + sampleCount = 0; + + _numOfChannels = _settingsWidget.numOfChannels(); + connect(&_settingsWidget, &BinaryStreamReaderSettings::numOfChannelsChanged, + this, &BinaryStreamReader::numOfChannelsChanged); + connect(&_settingsWidget, &BinaryStreamReaderSettings::numOfChannelsChanged, + this, &BinaryStreamReader::onNumOfChannelsChanged); + + // initial number format selection + onNumberFormatChanged(_settingsWidget.numberFormat()); + connect(&_settingsWidget, &BinaryStreamReaderSettings::numberFormatChanged, + this, &BinaryStreamReader::onNumberFormatChanged); + + // enable skip byte button + connect(&_settingsWidget, &BinaryStreamReaderSettings::skipByteRequested, + [this]() + { + skipByteRequested = true; + }); + + // TODO sps counter +} + +QWidget* BinaryStreamReader::settingsWidget() +{ + return &_settingsWidget; +} + +unsigned BinaryStreamReader::numOfChannels() +{ + return _numOfChannels; +} + +void BinaryStreamReader::enable(bool enabled) +{ + if (enabled) + { + QObject::connect(_device, &QIODevice::readyRead, + this, &BinaryStreamReader::onDataReady); + } + else + { + QObject::disconnect(_device, 0, this, 0); + } +} + +void BinaryStreamReader::pause(bool enabled) +{ + paused = enabled; +} + +void BinaryStreamReader::onNumberFormatChanged(NumberFormat numberFormat) +{ + switch(numberFormat) + { + case NumberFormat_uint8: + sampleSize = 1; + readSample = &BinaryStreamReader::readSampleAs; + break; + case NumberFormat_int8: + sampleSize = 1; + readSample = &BinaryStreamReader::readSampleAs; + break; + case NumberFormat_uint16: + sampleSize = 2; + readSample = &BinaryStreamReader::readSampleAs; + break; + case NumberFormat_int16: + sampleSize = 2; + readSample = &BinaryStreamReader::readSampleAs; + break; + case NumberFormat_uint32: + sampleSize = 4; + readSample = &BinaryStreamReader::readSampleAs; + break; + case NumberFormat_int32: + sampleSize = 4; + readSample = &BinaryStreamReader::readSampleAs; + break; + case NumberFormat_float: + sampleSize = 4; + readSample = &BinaryStreamReader::readSampleAs; + break; + } +} + +void BinaryStreamReader::onNumOfChannelsChanged(unsigned value) +{ + _numOfChannels = value; +} + +void BinaryStreamReader::onDataReady() +{ + // a package is a set of channel data like {CHAN0_SAMPLE, CHAN1_SAMPLE...} + int packageSize = sampleSize * _numOfChannels; + int bytesAvailable = _device->bytesAvailable(); + int numOfPackagesToRead = + (bytesAvailable - (bytesAvailable % packageSize)) / packageSize; + + if (paused) + { + // read and discard data + _device->read(numOfPackagesToRead*packageSize); + return; + } + + if (bytesAvailable > 0 && skipByteRequested) + { + _device->read(1); + skipByteRequested = false; + bytesAvailable--; + } + + if (bytesAvailable < packageSize) return; + + double* channelSamples = new double[numOfPackagesToRead*_numOfChannels]; + + for (int i = 0; i < numOfPackagesToRead; i++) + { + for (unsigned int ci = 0; ci < _numOfChannels; ci++) + { + // channelSamples[ci].replace(i, (this->*readSample)()); + channelSamples[ci*numOfPackagesToRead+i] = (this->*readSample)(); + } + } + + for (unsigned int ci = 0; ci < _numOfChannels; ci++) + { + addChannelData(ci, + channelSamples + ci*numOfPackagesToRead, + numOfPackagesToRead); + } + emit dataAdded(); + + delete channelSamples; +} + + +template double BinaryStreamReader::readSampleAs() +{ + T data; + + _device->read((char*) &data, sizeof(data)); + + if (_settingsWidget.endianness() == LittleEndian) + { + data = qFromLittleEndian(data); + } + else + { + data = qFromBigEndian(data); + } + + return double(data); +} + +void BinaryStreamReader::addChannelData(unsigned int channel, + double* data, unsigned size) +{ + _channelMan->addChannelData(channel, data, size); + sampleCount += size; +} diff --git a/src/binarystreamreader.h b/src/binarystreamreader.h new file mode 100644 --- /dev/null +++ b/src/binarystreamreader.h @@ -0,0 +1,71 @@ +/* + Copyright © 2016 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 . +*/ + +#ifndef BINARYSTREAMREADER_H +#define BINARYSTREAMREADER_H + +#include "abstractreader.h" +#include "binarystreamreadersettings.h" + +/** + * Reads a simple stream of samples in binary form from the + * device. There is no means of synchronization other than a button + * that should be manually triggered by user. + */ +class BinaryStreamReader : public AbstractReader +{ + Q_OBJECT +public: + explicit BinaryStreamReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0); + QWidget* settingsWidget(); + unsigned numOfChannels(); + void enable(bool enabled = true); + +public slots: + void pause(bool); + +private: + BinaryStreamReaderSettings _settingsWidget; + unsigned _numOfChannels; + unsigned sampleSize; + bool paused; + bool skipByteRequested; + unsigned sampleCount; ///< used for sps counter + + /// points to the readSampleAs function for currently selected number format + double (BinaryStreamReader::*readSample)(); + + /** + * Reads 1 sample from the device in given format. + * + * @note Device should already have enough bytes present before + * calling this function. + */ + template double readSampleAs(); + + // `data` contains i th channels data + void addChannelData(unsigned int channel, double* data, unsigned size); + +private slots: + void onNumberFormatChanged(NumberFormat numberFormat); + void onNumOfChannelsChanged(unsigned value); + void onDataReady(); +}; + +#endif // BINARYSTREAMREADER_H diff --git a/src/binarystreamreadersettings.cpp b/src/binarystreamreadersettings.cpp new file mode 100644 --- /dev/null +++ b/src/binarystreamreadersettings.cpp @@ -0,0 +1,62 @@ +/* + Copyright © 2016 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 "binarystreamreadersettings.h" +#include "ui_binarystreamreadersettings.h" + +#include "utils.h" + +BinaryStreamReaderSettings::BinaryStreamReaderSettings(QWidget *parent) : + QWidget(parent), + ui(new Ui::BinaryStreamReaderSettings) +{ + ui->setupUi(this); + + // Note: if directly connected we get a runtime warning on incompatible signal arguments + connect(ui->spNumOfChannels, SELECT::OVERLOAD_OF(&QSpinBox::valueChanged), + [this](int value) + { + emit numOfChannelsChanged(value); + }); + + connect(ui->nfBox, SIGNAL(selectionChanged(NumberFormat)), + this, SIGNAL(numberFormatChanged(NumberFormat))); + + connect(ui->pbSkipByte, SIGNAL(clicked()), this, SIGNAL(skipByteRequested())); +} + +BinaryStreamReaderSettings::~BinaryStreamReaderSettings() +{ + delete ui; +} + +unsigned BinaryStreamReaderSettings::numOfChannels() +{ + return ui->spNumOfChannels->value(); +} + +NumberFormat BinaryStreamReaderSettings::numberFormat() +{ + return ui->nfBox->currentSelection(); +} + +Endianness BinaryStreamReaderSettings::endianness() +{ + return ui->endiBox->currentSelection(); +} diff --git a/src/binarystreamreadersettings.h b/src/binarystreamreadersettings.h new file mode 100644 --- /dev/null +++ b/src/binarystreamreadersettings.h @@ -0,0 +1,52 @@ +/* + Copyright © 2016 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 . +*/ + +#ifndef BINARYSTREAMREADERSETTINGS_H +#define BINARYSTREAMREADERSETTINGS_H + +#include +#include "numberformatbox.h" +#include "endiannessbox.h" + +namespace Ui { +class BinaryStreamReaderSettings; +} + +class BinaryStreamReaderSettings : public QWidget +{ + Q_OBJECT + +public: + explicit BinaryStreamReaderSettings(QWidget *parent = 0); + ~BinaryStreamReaderSettings(); + + unsigned numOfChannels(); + NumberFormat numberFormat(); + Endianness endianness(); + +signals: + void numOfChannelsChanged(unsigned); + void numberFormatChanged(NumberFormat); + void skipByteRequested(); + +private: + Ui::BinaryStreamReaderSettings *ui; +}; + +#endif // BINARYSTREAMREADERSETTINGS_H diff --git a/src/binarystreamreadersettings.ui b/src/binarystreamreadersettings.ui new file mode 100644 --- /dev/null +++ b/src/binarystreamreadersettings.ui @@ -0,0 +1,107 @@ + + + BinaryStreamReaderSettings + + + + 0 + 0 + 432 + 203 + + + + Form + + + + + 17 + 10 + 127 + 27 + + + + Number Of Channels: + + + + + + 150 + 10 + 60 + 27 + + + + + 60 + 0 + + + + false + + + 1 + + + 32 + + + + + + 20 + 50 + 161 + 141 + + + + + + + 190 + 50 + 120 + 80 + + + + + + + 220 + 10 + 85 + 27 + + + + Skip reading 1 byte to correct the alignment + + + Skip Byte + + + + + + NumberFormatBox + QWidget +
numberformatbox.h
+ 1 +
+ + EndiannessBox + QWidget +
endiannessbox.h
+ 1 +
+
+ + +
diff --git a/src/dataformatpanel.cpp b/src/dataformatpanel.cpp --- a/src/dataformatpanel.cpp +++ b/src/dataformatpanel.cpp @@ -20,6 +20,7 @@ #include "dataformatpanel.h" #include "ui_dataformatpanel.h" +#include #include #include @@ -30,7 +31,9 @@ DataFormatPanel::DataFormatPanel(QSerial ChannelManager* channelMan, QWidget *parent) : QWidget(parent), - ui(new Ui::DataFormatPanel) + ui(new Ui::DataFormatPanel), + bsReader(port, channelMan), + asciiReader(port, channelMan) { ui->setupUi(this); @@ -38,35 +41,31 @@ DataFormatPanel::DataFormatPanel(QSerial _channelMan = channelMan; paused = false; - // setup number format buttons - numberFormatButtons.addButton(ui->rbUint8, NumberFormat_uint8); - numberFormatButtons.addButton(ui->rbUint16, NumberFormat_uint16); - numberFormatButtons.addButton(ui->rbUint32, NumberFormat_uint32); - numberFormatButtons.addButton(ui->rbInt8, NumberFormat_int8); - numberFormatButtons.addButton(ui->rbInt16, NumberFormat_int16); - numberFormatButtons.addButton(ui->rbInt32, NumberFormat_int32); - numberFormatButtons.addButton(ui->rbFloat, NumberFormat_float); - numberFormatButtons.addButton(ui->rbASCII, NumberFormat_ASCII); + // initalize default reader + currentReader = &bsReader; + bsReader.enable(); + ui->rbBinary->setChecked(true); + ui->horizontalLayout->addWidget(bsReader.settingsWidget(), 1); + connect(&bsReader, SIGNAL(dataAdded()), this, SIGNAL(dataAdded())); + connect(&bsReader, SIGNAL(numOfChannelsChanged(unsigned)), + this, SIGNAL(numOfChannelsChanged(unsigned))); - QObject::connect( - &numberFormatButtons, SIGNAL(buttonToggled(int, bool)), - this, SLOT(onNumberFormatButtonToggled(int, bool))); - - // init number format - selectNumberFormat((NumberFormat) numberFormatButtons.checkedId()); + // initalize reader selection buttons + connect(ui->rbBinary, &QRadioButton::toggled, [this](bool checked) + { + if (checked) selectReader(&bsReader); + }); - // setup number of channels spinbox - QObject::connect(ui->spNumOfChannels, - SELECT::OVERLOAD_OF(&QSpinBox::valueChanged), - this, &DataFormatPanel::onNumOfChannelsSP); - - _numOfChannels = ui->spNumOfChannels->value(); + connect(ui->rbAscii, &QRadioButton::toggled, [this](bool checked) + { + if (checked) selectReader(&asciiReader); + }); // Init sps (sample per second) counter - sampleCount = 0; - QObject::connect(&spsTimer, &QTimer::timeout, - this, &DataFormatPanel::spsTimerTimeout); - spsTimer.start(SPS_UPDATE_TIMEOUT * 1000); + // sampleCount = 0; + // QObject::connect(&spsTimer, &QTimer::timeout, + // this, &DataFormatPanel::spsTimerTimeout); + // spsTimer.start(SPS_UPDATE_TIMEOUT * 1000); // Init demo mode demoCount = 0; @@ -80,84 +79,18 @@ DataFormatPanel::~DataFormatPanel() delete ui; } -void DataFormatPanel::onNumberFormatButtonToggled(int numberFormatId, - bool checked) -{ - if (checked) selectNumberFormat((NumberFormat) numberFormatId); -} - -void DataFormatPanel::selectNumberFormat(NumberFormat numberFormatId) -{ - numberFormat = numberFormatId; - - switch(numberFormat) - { - case NumberFormat_uint8: - sampleSize = 1; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_int8: - sampleSize = 1; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_uint16: - sampleSize = 2; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_int16: - sampleSize = 2; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_uint32: - sampleSize = 4; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_int32: - sampleSize = 4; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_float: - sampleSize = 4; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_ASCII: - sampleSize = 0; // these two members should not be used - readSample = NULL; // in this mode - break; - } - - if (numberFormat == NumberFormat_ASCII) - { - QObject::disconnect(serialPort, &QSerialPort::readyRead, 0, 0); - QObject::connect(this->serialPort, &QSerialPort::readyRead, - this, &DataFormatPanel::onDataReadyASCII); - } - else - { - QObject::disconnect(serialPort, &QSerialPort::readyRead, 0, 0); - QObject::connect(serialPort, &QSerialPort::readyRead, - this, &DataFormatPanel::onDataReady); - } - - emit skipByteEnabledChanged(skipByteEnabled()); -} - +// TODO: remove bool DataFormatPanel::skipByteEnabled() { - return numberFormat != NumberFormat_ASCII; + return false; } unsigned DataFormatPanel::numOfChannels() { - return _numOfChannels; + return currentReader->numOfChannels(); } -void DataFormatPanel::onNumOfChannelsSP(int value) -{ - _numOfChannels = value; - emit numOfChannelsChanged(value); -} - +// TODO: remove void DataFormatPanel::requestSkipByte() { skipByteRequested = true; @@ -165,7 +98,7 @@ void DataFormatPanel::requestSkipByte() void DataFormatPanel::pause(bool enabled) { - paused = enabled; + currentReader->pause(enabled); } void DataFormatPanel::enableDemo(bool enabled) @@ -182,16 +115,15 @@ void DataFormatPanel::enableDemo(bool en void DataFormatPanel::spsTimerTimeout() { - unsigned currentSps = _samplesPerSecond; - _samplesPerSecond = (sampleCount/_numOfChannels)/SPS_UPDATE_TIMEOUT; - if (currentSps != _samplesPerSecond) - { - emit samplesPerSecondChanged(_samplesPerSecond); - } - sampleCount = 0; + // unsigned currentSps = _samplesPerSecond; + // _samplesPerSecond = (sampleCount/_numOfChannels)/SPS_UPDATE_TIMEOUT; + // if (currentSps != _samplesPerSecond) + // { + // emit samplesPerSecondChanged(_samplesPerSecond); + // } + // sampleCount = 0; } - void DataFormatPanel::demoTimerTimeout() { const double period = 100; @@ -200,7 +132,7 @@ void DataFormatPanel::demoTimerTimeout() if (!paused) { - for (unsigned ci = 0; ci < _numOfChannels; ci++) + for (unsigned ci = 0; ci < currentReader->numOfChannels(); ci++) { // we are calculating the fourier components of square wave double value = 4*sin(2*M_PI*double((ci+1)*demoCount)/period)/((2*(ci+1))*M_PI); @@ -210,116 +142,29 @@ void DataFormatPanel::demoTimerTimeout() } } -void DataFormatPanel::onDataReady() -{ - // a package is a set of channel data like {CHAN0_SAMPLE, CHAN1_SAMPLE...} - int packageSize = sampleSize * _numOfChannels; - int bytesAvailable = serialPort->bytesAvailable(); - int numOfPackagesToRead = - (bytesAvailable - (bytesAvailable % packageSize)) / packageSize; - - if (paused) - { - // read and discard data - serialPort->read(numOfPackagesToRead*packageSize); - return; - } - - if (bytesAvailable > 0 && skipByteRequested) - { - serialPort->read(1); - skipByteRequested = false; - bytesAvailable--; - } - - if (bytesAvailable < packageSize) return; - - double* channelSamples = new double[numOfPackagesToRead*_numOfChannels]; - - for (int i = 0; i < numOfPackagesToRead; i++) - { - for (unsigned int ci = 0; ci < _numOfChannels; ci++) - { - // channelSamples[ci].replace(i, (this->*readSample)()); - channelSamples[ci*numOfPackagesToRead+i] = (this->*readSample)(); - } - } - - for (unsigned int ci = 0; ci < _numOfChannels; ci++) - { - addChannelData(ci, - channelSamples + ci*numOfPackagesToRead, - numOfPackagesToRead); - } - emit dataAdded(); - - delete channelSamples; -} - -void DataFormatPanel::onDataReadyASCII() -{ - while(serialPort->canReadLine()) - { - QByteArray line = serialPort->readLine(); - - // discard data if paused - if (paused) - { - return; - } - - line = line.trimmed(); - auto separatedValues = line.split(','); - - int numReadChannels; // effective number of channels to read - if (separatedValues.length() >= int(_numOfChannels)) - { - numReadChannels = _numOfChannels; - } - else // there is missing channel data - { - numReadChannels = separatedValues.length(); - qWarning() << "Incoming data is missing data for some channels!"; - } - - // parse read line - for (int ci = 0; ci < numReadChannels; ci++) - { - bool ok; - double channelSample = separatedValues[ci].toDouble(&ok); - if (ok) - { - addChannelData(ci, &channelSample, 1); - } - else - { - qWarning() << "Data parsing error for channel: " << ci; - } - } - emit dataAdded(); - } -} - -template double DataFormatPanel::readSampleAs() -{ - T data; - serialPort->read((char*) &data, sizeof(data)); - - if (ui->rbLittleE->isChecked()) - { - data = qFromLittleEndian(data); - } - else - { - data = qFromBigEndian(data); - } - - return double(data); -} - void DataFormatPanel::addChannelData(unsigned int channel, double* data, unsigned size) { _channelMan->addChannelData(channel, data, size); sampleCount += size; } + +void DataFormatPanel::selectReader(AbstractReader* reader) +{ + currentReader->enable(false); + reader->enable(); + + // re-connect signals + disconnect(currentReader, 0, this, 0); + connect(reader, SIGNAL(dataAdded()), this, SIGNAL(dataAdded())); + connect(reader, SIGNAL(numOfChannelsChanged(unsigned)), + this, SIGNAL(numOfChannelsChanged(unsigned))); + + // switch the settings widget + ui->horizontalLayout->removeWidget(currentReader->settingsWidget()); + currentReader->settingsWidget()->hide(); + ui->horizontalLayout->addWidget(reader->settingsWidget(), 1); + reader->settingsWidget()->show(); + + currentReader = reader; +} diff --git a/src/dataformatpanel.h b/src/dataformatpanel.h --- a/src/dataformatpanel.h +++ b/src/dataformatpanel.h @@ -29,6 +29,8 @@ #include "framebuffer.h" #include "channelmanager.h" +#include "binarystreamreader.h" +#include "asciireader.h" namespace Ui { class DataFormatPanel; @@ -58,33 +60,24 @@ public slots: signals: void numOfChannelsChanged(unsigned); void samplesPerSecondChanged(unsigned); - void skipByteEnabledChanged(bool); + void skipByteEnabledChanged(bool); // remove void dataAdded(); private: - enum NumberFormat - { - NumberFormat_uint8, - NumberFormat_uint16, - NumberFormat_uint32, - NumberFormat_int8, - NumberFormat_int16, - NumberFormat_int32, - NumberFormat_float, - NumberFormat_ASCII - }; - Ui::DataFormatPanel *ui; - QButtonGroup numberFormatButtons; QSerialPort* serialPort; ChannelManager* _channelMan; - unsigned int _numOfChannels; - NumberFormat numberFormat; - unsigned int sampleSize; // number of bytes in the selected number format - bool skipByteRequested; - bool paused; + BinaryStreamReader bsReader; + AsciiReader asciiReader; + /// Currently selected reader + AbstractReader* currentReader; + /// Disable current reader and enable a another one + void selectReader(AbstractReader* reader); + + bool skipByteRequested; // remove + bool paused; // remove const int SPS_UPDATE_TIMEOUT = 1; // second unsigned _samplesPerSecond; @@ -95,22 +88,10 @@ private: QTimer demoTimer; int demoCount; - void selectNumberFormat(NumberFormat numberFormatId); - - // points to the readSampleAs function for currently selected number format - double (DataFormatPanel::*readSample)(); - - // note that serialPort should already have enough bytes present - template double readSampleAs(); - // `data` contains i th channels data void addChannelData(unsigned int channel, double* data, unsigned size); private slots: - void onDataReady(); // used with binary number formats - void onDataReadyASCII(); // used with ASCII number format - void onNumberFormatButtonToggled(int numberFormatId, bool checked); - void onNumOfChannelsSP(int value); void spsTimerTimeout(); void demoTimerTimeout(); }; diff --git a/src/dataformatpanel.ui b/src/dataformatpanel.ui --- a/src/dataformatpanel.ui +++ b/src/dataformatpanel.ui @@ -15,195 +15,22 @@ - - - + + + - Number Of Channels: + Simple Binary - - - - - - - 60 - 0 - - - - false - - - 1 - - - 32 + + true - - - - - - Qt::Vertical - - - - - - - 0 - - - - Number Format: + + + ASCII - - - - - unsigned 4 bytes integer - - - uint32 - - - - - - - signed 1 byte integer - - - int8 - - - - - - - unsigned 1 byte integer - - - uint8 - - - true - - - - - - - Comma Separated Values - - - ASCII(CSV) - - - - - - - signed 2 bytes integer - - - int16 - - - - - - - signed 4 bytes integer - - - int32 - - - - - - - 4 bytes floating point number - - - float - - - - - - - unsigned 2 bytes integer - - - uint16 - - - - - - - - - - Qt::Vertical - - - - 20 - 1 - - - - - - - - - - 0 - - - - - - 0 - 0 - - - - Byte Order: - - - - - - least significant byte first - - - Little Endian - - - true - - - - - - - most significant byte first - - - Big Endian - - - - @@ -214,7 +41,7 @@ 20 - 1 + 40 @@ -222,20 +49,11 @@ - + - Qt::Horizontal - - - QSizePolicy::MinimumExpanding + Qt::Vertical - - - 37 - 20 - - - +