/*
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
#include "asciireader.h"
/// If set to this value number of channels is determined from input
#define NUMOFCHANNELS_AUTO (0)
AsciiReader::AsciiReader(QIODevice* device, QObject* parent) :
AbstractReader(device, parent)
{
paused = false;
_numChannels = _settingsWidget.numOfChannels();
autoNumOfChannels = (_numChannels == NUMOFCHANNELS_AUTO);
delimiter = _settingsWidget.delimiter();
connect(&_settingsWidget, &AsciiReaderSettings::numOfChannelsChanged,
[this](unsigned value)
{
_numChannels = value;
updateNumChannels(); // TODO: setting numchannels = 0, should remove all buffers
// do we want this?
autoNumOfChannels = (_numChannels == NUMOFCHANNELS_AUTO);
if (!autoNumOfChannels)
{
emit numOfChannelsChanged(value);
}
});
connect(&_settingsWidget, &AsciiReaderSettings::delimiterChanged,
[this](QChar d)
{
delimiter = d;
});
}
QWidget* AsciiReader::settingsWidget()
{
return &_settingsWidget;
}
unsigned AsciiReader::numChannels() const
{
// TODO: an alternative is to never set _numChannels to '0'
// do not allow '0'
return _numChannels == 0 ? 1 : _numChannels;
}
void AsciiReader::enable(bool enabled)
{
if (enabled)
{
firstReadAfterEnable = true;
QObject::connect(_device, &QIODevice::readyRead,
this, &AsciiReader::onDataReady);
}
else
{
QObject::disconnect(_device, 0, this, 0);
disconnectSinks();
}
}
void AsciiReader::onDataReady()
{
while(_device->canReadLine())
{
QString line = QString(_device->readLine());
// discard only once when we just started reading
if (firstReadAfterEnable)
{
firstReadAfterEnable = false;
continue;
}
// discard data if paused
if (paused)
{
continue;
}
// parse data
line = line.trimmed();
// Note: When data coming from pseudo terminal is buffered by
// system CR is converted to LF for some reason. This causes
// empty lines in the input when the port is just opened.
if (line.isEmpty())
{
continue;
}
const SamplePack* samples = parseLine(line);
if (samples != nullptr) {
// update number of channels if in auto mode
if (autoNumOfChannels ) {
unsigned nc = samples->numChannels();
if (nc != _numChannels) {
_numChannels = nc;
updateNumChannels();
// TODO: is `numOfChannelsChanged` signal still used?
emit numOfChannelsChanged(nc);
}
}
Q_ASSERT(samples->numChannels() == _numChannels);
// commit data
feedOut(*samples);
delete samples;
}
}
}
SamplePack* AsciiReader::parseLine(const QString& line) const
{
auto separatedValues = line.split(delimiter, QString::SkipEmptyParts);
unsigned numComingChannels = separatedValues.length();
// check number of channels (skipped if auto num channels is enabled)
if ((!numComingChannels) || (!autoNumOfChannels && numComingChannels != _numChannels))
{
qWarning() << "Line parsing error: invalid number of channels!";
qWarning() << "Read line: " << line;
return nullptr;
}
// parse data per channel
auto samples = new SamplePack(1, numComingChannels);
for (unsigned ci = 0; ci < numComingChannels; ci++)
{
bool ok;
samples->data(ci)[0] = separatedValues[ci].toDouble(&ok);
if (!ok)
{
qWarning() << "Data parsing error for channel: " << ci;
qWarning() << "Read line: " << line;
delete samples;
return nullptr;
}
}
return samples;
}
void AsciiReader::saveSettings(QSettings* settings)
{
_settingsWidget.saveSettings(settings);
}
void AsciiReader::loadSettings(QSettings* settings)
{
_settingsWidget.loadSettings(settings);
}