/* 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); } } } 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); }