@@ -24,36 +24,43 @@
/// If set to this value number of channels is determined from input
#define NUMOFCHANNELS_AUTO (0)
AsciiReader::AsciiReader(QIODevice* device, ChannelManager* channelMan,
DataRecorder* recorder, QObject* parent) :
AbstractReader(device, channelMan, recorder, parent)
{
paused = false;
discardFirstLine = true;
_numOfChannels = _settingsWidget.numOfChannels();
autoNumOfChannels = (_numOfChannels == NUMOFCHANNELS_AUTO);
delimiter = _settingsWidget.delimiter();
connect(&_settingsWidget, &AsciiReaderSettings::numOfChannelsChanged,
[this](unsigned value)
_numOfChannels = value;
if (!autoNumOfChannels)
emit numOfChannelsChanged(value);
}
});
connect(&_settingsWidget, &AsciiReaderSettings::delimiterChanged,
[this](QChar d)
delimiter = d;
connect(device, &QIODevice::aboutToClose, [this](){discardFirstLine=true;});
QWidget* AsciiReader::settingsWidget()
return &_settingsWidget;
unsigned AsciiReader::numOfChannels()
// do not allow '0'
if (_numOfChannels == 0)
@@ -81,25 +88,25 @@ void AsciiReader::enable(bool enabled)
void AsciiReader::pause(bool enabled)
paused = enabled;
void AsciiReader::onDataReady()
while(_device->canReadLine())
QByteArray line = _device->readLine();
QString line = QString(_device->readLine());
// discard only once when we just started reading
if (discardFirstLine)
discardFirstLine = false;
continue;
// discard data if paused
if (paused)
@@ -107,25 +114,25 @@ void AsciiReader::onDataReady()
// 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())
auto separatedValues = line.split(',');
auto separatedValues = line.split(delimiter, QString::SkipEmptyParts);
unsigned numReadChannels; // effective number of channels to read
unsigned numComingChannels = separatedValues.length();
if (autoNumOfChannels)
// did number of channels changed?
if (numComingChannels != _numOfChannels)
_numOfChannels = numComingChannels;
emit numOfChannelsChanged(numComingChannels);
@@ -134,34 +141,36 @@ void AsciiReader::onDataReady()
else if (numComingChannels >= _numOfChannels)
numReadChannels = _numOfChannels;
else // there is missing channel data
numReadChannels = separatedValues.length();
qWarning() << "Incoming data is missing data for some channels!";
qWarning() << "Read line: " << line;
// parse read line
unsigned numDataBroken = 0;
double* channelSamples = new double[_numOfChannels]();
for (unsigned ci = 0; ci < numReadChannels; ci++)
bool ok;
channelSamples[ci] = separatedValues[ci].toDouble(&ok);
if (!ok)
qWarning() << "Data parsing error for channel: " << ci;
channelSamples[ci] = 0;
numDataBroken++;
// commit data
addData(channelSamples, _numOfChannels);
delete[] channelSamples;
void AsciiReader::saveSettings(QSettings* settings)
@@ -39,23 +39,24 @@ public:
void saveSettings(QSettings* settings);
/// Loads settings from a `QSettings`.
void loadSettings(QSettings* settings);
public slots:
void pause(bool);
private:
AsciiReaderSettings _settingsWidget;
unsigned _numOfChannels;
/// number of channels will be determined from incoming data
unsigned autoNumOfChannels;
QChar delimiter; ///< selected column delimiter
bool paused;
// We may have (usually true) started reading in the middle of a
// line, so its a better idea to just discard first line.
bool discardFirstLine;
private slots:
void onDataReady();
};
#endif // ASCIIREADER_H
/*
Copyright © 2016 Hasan Yavuz Özderya
Copyright © 2017 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 <QRegularExpressionValidator>
#include <QRegularExpression>
#include "utils.h"
#include "setting_defines.h"
#include "asciireadersettings.h"
#include "ui_asciireadersettings.h"
#include <QtDebug>
AsciiReaderSettings::AsciiReaderSettings(QWidget *parent) :
QWidget(parent),
ui(new Ui::AsciiReaderSettings)
ui->setupUi(this);
auto validator = new QRegularExpressionValidator(QRegularExpression("[^\\d]?"), this);
ui->leDelimiter->setValidator(validator);
connect(ui->rbComma, &QAbstractButton::toggled,
this, &AsciiReaderSettings::delimiterToggled);
connect(ui->rbSpace, &QAbstractButton::toggled,
connect(ui->rbTab, &QAbstractButton::toggled,
connect(ui->rbOtherDelimiter, &QAbstractButton::toggled,
connect(ui->leDelimiter, &QLineEdit::textChanged,
this, &AsciiReaderSettings::customDelimiterChanged);
// Note: if directly connected we get a runtime warning on incompatible signal arguments
connect(ui->spNumOfChannels, SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged),
[this](int value)
AsciiReaderSettings::~AsciiReaderSettings()
delete ui;
unsigned AsciiReaderSettings::numOfChannels()
unsigned AsciiReaderSettings::numOfChannels() const
return ui->spNumOfChannels->value();
QChar AsciiReaderSettings::delimiter() const
if (ui->rbComma->isChecked())
return QChar(',');
else if (ui->rbSpace->isChecked())
return QChar(' ');
else if (ui->rbTab->isChecked())
return QChar('\t');
else // rbOther
auto t = ui->leDelimiter->text();
return t.isEmpty() ? QChar() : t.at(0);
void AsciiReaderSettings::delimiterToggled(bool checked)
if (!checked) return;
auto d = delimiter();
if (!d.isNull())
emit delimiterChanged(d);
void AsciiReaderSettings::customDelimiterChanged(const QString text)
if (ui->rbOtherDelimiter->isChecked())
if (!text.isEmpty()) emit delimiterChanged(text.at(0));
void AsciiReaderSettings::saveSettings(QSettings* settings)
settings->beginGroup(SettingGroup_ASCII);
// save number of channels setting
QString numOfChannelsSetting = QString::number(numOfChannels());
if (numOfChannelsSetting == "0") numOfChannelsSetting = "auto";
settings->setValue(SG_ASCII_NumOfChannels, numOfChannelsSetting);
settings->endGroup();
#ifndef ASCIIREADERSETTINGS_H
#define ASCIIREADERSETTINGS_H
#include <QWidget>
#include <QSettings>
#include <QChar>
namespace Ui {
class AsciiReaderSettings;
class AsciiReaderSettings : public QWidget
Q_OBJECT
public:
explicit AsciiReaderSettings(QWidget *parent = 0);
~AsciiReaderSettings();
unsigned numOfChannels();
unsigned numOfChannels() const;
QChar delimiter() const;
/// Stores settings into a `QSettings`
signals:
void numOfChannelsChanged(unsigned);
/// Signaled only with a valid delimiter
void delimiterChanged(QChar);
Ui::AsciiReaderSettings *ui;
void delimiterToggled(bool checked);
void customDelimiterChanged(const QString text);
#endif // ASCIIREADERSETTINGS_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AsciiReaderSettings</class>
<widget class="QWidget" name="AsciiReaderSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>414</width>
<width>493</width>
<height>171</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
<property name="leftMargin">
<number>0</number>
<property name="topMargin">
<property name="rightMargin">
<property name="bottomMargin">
<item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Number Of Channels:</string>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="spNumOfChannels">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
<property name="toolTip">
<string>Select number of channels or set to 0 for Auto (determined from incoming data)</string>
<property name="specialValueText">
<string>Auto</string>
<property name="keyboardTracking">
<bool>false</bool>
<property name="minimum">
<property name="maximum">
<number>32</number>
<item row="2" column="0">
<widget class="QLabel" name="label">
<string>Column Delimiter:</string>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<widget class="QRadioButton" name="rbComma">
<string>comma</string>
<property name="checked">
<bool>true</bool>
<widget class="QRadioButton" name="rbSpace">
<string>space</string>
<widget class="QRadioButton" name="rbTab">
<string>tab</string>
<widget class="QRadioButton" name="rbOtherDelimiter">
<string>other:</string>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<widget class="QLineEdit" name="leDelimiter">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<property name="sizeHint" stdset="0">
<property name="maximumSize">
<width>1</width>
<height>20</height>
<width>30</width>
<height>16777215</height>
</spacer>
<string>Enter a custom delimiter character</string>
<property name="inputMask">
<string/>
<string>|</string>
</layout>
<spacer name="verticalSpacer">
<enum>Qt::Vertical</enum>
<width>20</width>
<height>1</height>
<resources/>
<connections/>
</ui>
Status change: