# HG changeset patch # User Hasan Yavuz ÖZDERYA # Date 2019-04-17 15:36:18 # Node ID a31b6ac829d672841de4470314be3cd6c376c4f2 # Parent e3f742bbe8b86ebc28b92e9861231e1b3692e4a9 # Parent bd0977f58c6040f4553a1ea9759e727194ecf573 Merge with reader-stat diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,7 @@ add_executable(${PROGRAM_NAME} WIN32 src/samplecounter.cpp src/ledwidget.cpp src/datatextview.cpp + src/bpslabel.cpp misc/windows_icon.rc ${UI_FILES} ${RES_FILES} diff --git a/serialplot.pro b/serialplot.pro --- a/serialplot.pro +++ b/serialplot.pro @@ -72,7 +72,8 @@ SOURCES += \ src/updatechecker.cpp \ src/updatecheckdialog.cpp \ src/demoreadersettings.cpp \ - src/datatextview.cpp + src/datatextview.cpp \ + src/bpslabel.cpp HEADERS += \ src/mainwindow.h \ @@ -116,7 +117,8 @@ HEADERS += \ src/updatechecker.h \ src/updatecheckdialog.h \ src/demoreadersettings.h \ - src/datatextview.h + src/datatextview.h \ + src/bpslabel.h FORMS += \ src/mainwindow.ui \ diff --git a/src/abstractreader.cpp b/src/abstractreader.cpp --- a/src/abstractreader.cpp +++ b/src/abstractreader.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2018 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -23,6 +23,7 @@ AbstractReader::AbstractReader(QIODevice QObject(parent) { _device = device; + bytesRead = 0; } void AbstractReader::pause(bool enabled) @@ -43,3 +44,15 @@ void AbstractReader::enable(bool enabled disconnectSinks(); } } + +void AbstractReader::onDataReady() +{ + bytesRead += readData(); +} + +unsigned AbstractReader::getBytesRead() +{ + unsigned r = bytesRead; + bytesRead = 0; + return r; +} diff --git a/src/abstractreader.h b/src/abstractreader.h --- a/src/abstractreader.h +++ b/src/abstractreader.h @@ -1,5 +1,5 @@ /* - Copyright © 2018 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -49,6 +49,9 @@ public: /// None of the current readers support X channel at the moment bool hasX() const final { return false; }; + /// Read and 'zero' the byte counter + unsigned getBytesRead(); + signals: // TODO: should we keep this? void numOfChannelsChanged(unsigned); @@ -63,12 +66,25 @@ public slots: void pause(bool enabled); protected: + /// Reader should read from this device in `readData()` function. QIODevice* _device; + + /// Reader should check this variable to determine if reading is + /// paused in `readData()` bool paused; -protected slots: - /// all derived readers has to override this function - virtual void onDataReady() = 0; + /** + * Called when `readyRead` is signaled by the device. This is + * where the implementors should read the data and return the + * exact number of bytes read from the device. + */ + virtual unsigned readData() = 0; + +private: + unsigned bytesRead; + +private slots: + void onDataReady(); }; #endif // ABSTRACTREADER_H diff --git a/src/asciireader.cpp b/src/asciireader.cpp --- a/src/asciireader.cpp +++ b/src/asciireader.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2018 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -70,21 +70,20 @@ 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(); - } + + AbstractReader::enable(enabled); } -void AsciiReader::onDataReady() +unsigned AsciiReader::readData() { + unsigned numBytesRead = 0; + while(_device->canReadLine()) { - QString line = QString(_device->readLine()); + QByteArray bytes = _device->readLine(); + QString line = QString(bytes); + numBytesRead += bytes.size(); // discard only once when we just started reading if (firstReadAfterEnable) @@ -130,6 +129,8 @@ void AsciiReader::onDataReady() delete samples; } } + + return numBytesRead; } SamplePack* AsciiReader::parseLine(const QString& line) const diff --git a/src/asciireader.h b/src/asciireader.h --- a/src/asciireader.h +++ b/src/asciireader.h @@ -1,5 +1,5 @@ /* - Copyright © 2018 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -50,8 +50,10 @@ private: bool firstReadAfterEnable = false; + unsigned readData() override; + private slots: - void onDataReady() override; + /** * Parses given line and returns sample pack. * diff --git a/src/binarystreamreader.cpp b/src/binarystreamreader.cpp --- a/src/binarystreamreader.cpp +++ b/src/binarystreamreader.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2018 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -107,50 +107,58 @@ void BinaryStreamReader::onNumOfChannels emit numOfChannelsChanged(value); } -void BinaryStreamReader::onDataReady() +unsigned BinaryStreamReader::readData() { // a package is a set of channel data like {CHAN0_SAMPLE, CHAN1_SAMPLE...} - int packageSize = sampleSize * _numChannels; - int bytesAvailable = _device->bytesAvailable(); + unsigned packageSize = sampleSize * _numChannels; + unsigned bytesAvailable = _device->bytesAvailable(); + unsigned totalRead = 0; // skip 1 byte if requested if (skipByteRequested && bytesAvailable > 0) { _device->read(1); + totalRead++; skipByteRequested = false; bytesAvailable--; } // skip 1 sample (channel) if requested - if (skipSampleRequested && bytesAvailable >= (int) sampleSize) + if (skipSampleRequested && bytesAvailable >= sampleSize) { _device->read(sampleSize); + totalRead += sampleSize; skipSampleRequested = false; bytesAvailable -= sampleSize; } - if (bytesAvailable < packageSize) return; + if (bytesAvailable < packageSize) return totalRead; - int numOfPackagesToRead = + unsigned numOfPackagesToRead = (bytesAvailable - (bytesAvailable % packageSize)) / packageSize; + unsigned numBytesToRead = numOfPackagesToRead * packageSize; + + totalRead += numBytesToRead; if (paused) { // read and discard data - _device->read(numOfPackagesToRead*packageSize); - return; + _device->read(numBytesToRead); + return totalRead; } // actual reading SamplePack samples(numOfPackagesToRead, _numChannels); - for (int i = 0; i < numOfPackagesToRead; i++) + for (unsigned i = 0; i < numOfPackagesToRead; i++) { - for (unsigned int ci = 0; ci < _numChannels; ci++) + for (unsigned ci = 0; ci < _numChannels; ci++) { samples.data(ci)[i] = (this->*readSample)(); } } feedOut(samples); + + return totalRead; } template double BinaryStreamReader::readSampleAs() diff --git a/src/binarystreamreader.h b/src/binarystreamreader.h --- a/src/binarystreamreader.h +++ b/src/binarystreamreader.h @@ -1,5 +1,5 @@ /* - Copyright © 2018 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -60,10 +60,11 @@ private: */ template double readSampleAs(); + unsigned readData() override; + private slots: void onNumberFormatChanged(NumberFormat numberFormat); void onNumOfChannelsChanged(unsigned value); - void onDataReady() override; }; #endif // BINARYSTREAMREADER_H diff --git a/src/bpslabel.cpp b/src/bpslabel.cpp new file mode 100644 --- /dev/null +++ b/src/bpslabel.cpp @@ -0,0 +1,80 @@ +/* + Copyright © 2019 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 "bpslabel.h" + +const char* BPS_TOOLTIP = "bits per second"; +const char* BPS_TOOLTIP_ERR = "Maximum baud rate may be reached!"; + +BPSLabel::BPSLabel(PortControl* portControl, + DataFormatPanel* dataFormatPanel, + QWidget *parent) : + QLabel(parent) +{ + _portControl = portControl; + _dataFormatPanel = dataFormatPanel; + prevBytesRead = 0; + + setText("0bps"); + setToolTip(tr(BPS_TOOLTIP)); + + connect(&bpsTimer, &QTimer::timeout, + this, &BPSLabel::onBpsTimeout); + + connect(portControl, &PortControl::portToggled, + this, &BPSLabel::onPortToggled); +} + +void BPSLabel::onBpsTimeout() +{ + uint64_t curBytesRead = _dataFormatPanel->bytesRead(); + uint64_t bytesRead = curBytesRead - prevBytesRead; + prevBytesRead = curBytesRead; + + unsigned bits = bytesRead * 8; + unsigned maxBps = _portControl->maxBitRate(); + QString str; + if (bits >= maxBps) + { + // TODO: an icon for bps warning + str = QString(tr("!%1/%2bps")).arg(bits).arg(maxBps); + setToolTip(tr(BPS_TOOLTIP_ERR)); + } + else + { + str = QString(tr("%1bps")).arg(bits); + setToolTip(tr(BPS_TOOLTIP)); + } + setText(str); +} + +void BPSLabel::onPortToggled(bool open) +{ + if (open) + { + bpsTimer.start(1000); + } + else + { + bpsTimer.stop(); + // if not cleared last displayed value is stuck + setText("0bps"); + setToolTip(tr(BPS_TOOLTIP)); + } +} diff --git a/src/bpslabel.h b/src/bpslabel.h new file mode 100644 --- /dev/null +++ b/src/bpslabel.h @@ -0,0 +1,55 @@ +/* + Copyright © 2019 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 BPSLABEL_H +#define BPSLABEL_H + +#include +#include + +#include "portcontrol.h" +#include "dataformatpanel.h" + +/** + * Displays bits per second read from device. + * + * Displays a warning if maximum bit rate is reached. + */ +class BPSLabel : public QLabel +{ + Q_OBJECT + +public: + explicit BPSLabel(PortControl* portControl, + DataFormatPanel* dataFormatPanel, + QWidget *parent = 0); + +private: + PortControl* _portControl; + DataFormatPanel* _dataFormatPanel; + QTimer bpsTimer; + + uint64_t prevBytesRead; + +private slots: + void onBpsTimeout(); + void onPortToggled(bool open); +}; + +#endif // BPSLABEL_H diff --git a/src/dataformatpanel.cpp b/src/dataformatpanel.cpp --- a/src/dataformatpanel.cpp +++ b/src/dataformatpanel.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2018 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -39,6 +39,7 @@ DataFormatPanel::DataFormatPanel(QSerial serialPort = port; paused = false; readerBeforeDemo = nullptr; + _bytesRead = 0; // initalize default reader currentReader = &bsReader; @@ -130,6 +131,12 @@ void DataFormatPanel::selectReader(Abstr emit sourceChanged(currentReader); } +uint64_t DataFormatPanel::bytesRead() +{ + _bytesRead += currentReader->getBytesRead(); + return _bytesRead; +} + void DataFormatPanel::saveSettings(QSettings* settings) { settings->beginGroup(SettingGroup_DataFormat); diff --git a/src/dataformatpanel.h b/src/dataformatpanel.h --- a/src/dataformatpanel.h +++ b/src/dataformatpanel.h @@ -1,5 +1,5 @@ /* - Copyright © 2018 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -20,9 +20,9 @@ #ifndef DATAFORMATPANEL_H #define DATAFORMATPANEL_H +#include #include #include -#include #include #include #include @@ -50,6 +50,8 @@ public: unsigned numChannels() const; /// Returns active source (reader) Source* activeSource(); + /// Returns total number of bytes read + uint64_t bytesRead(); /// Stores data format panel settings into a `QSettings` void saveSettings(QSettings* settings); /// Loads data format panel settings from a `QSettings`. @@ -77,6 +79,7 @@ private: void selectReader(AbstractReader* reader); bool paused; + uint64_t _bytesRead; DemoReader demoReader; AbstractReader* readerBeforeDemo; diff --git a/src/demoreader.cpp b/src/demoreader.cpp --- a/src/demoreader.cpp +++ b/src/demoreader.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2018 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -53,8 +53,9 @@ void DemoReader::enable(bool enabled) else { timer.stop(); - disconnectSinks(); } + + AbstractReader::enable(enabled); } unsigned DemoReader::numChannels() const @@ -91,7 +92,8 @@ void DemoReader::onNumChannelsChanged(un updateNumChannels(); } -void DemoReader::onDataReady() +unsigned DemoReader::readData() { // intentionally empty, required by AbstractReader + return 0; } diff --git a/src/demoreader.h b/src/demoreader.h --- a/src/demoreader.h +++ b/src/demoreader.h @@ -1,5 +1,5 @@ /* - Copyright © 2018 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -55,10 +55,11 @@ private: QTimer timer; int count; + unsigned readData() override; + private slots: void demoTimerTimeout(); void onNumChannelsChanged(unsigned value); - void onDataReady() override; }; #endif // DEMOREADER_H diff --git a/src/framedreader.cpp b/src/framedreader.cpp --- a/src/framedreader.cpp +++ b/src/framedreader.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2018 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -184,9 +184,11 @@ void FramedReader::onFrameSizeChanged(un reset(); } -void FramedReader::onDataReady() +unsigned FramedReader::readData() { - if (settingsInvalid) return; + unsigned numBytesRead = 0; + + if (settingsInvalid) return numBytesRead; // loop until we run out of bytes or more bytes is required unsigned bytesAvailable; @@ -196,6 +198,7 @@ void FramedReader::onDataReady() { char c; _device->getChar(&c); + numBytesRead++; if (c == syncWord[sync_i]) // correct sync byte? { sync_i++; @@ -213,6 +216,7 @@ void FramedReader::onDataReady() { frameSize = 0; _device->getChar((char*) &frameSize); + numBytesRead++; if (frameSize == 0) // check size { @@ -242,10 +246,13 @@ void FramedReader::onDataReady() else // read data bytes and checksum { readFrameDataAndCheck(); + numBytesRead += checksumEnabled ? frameSize+1 : frameSize; reset(); } } } + + return numBytesRead; } void FramedReader::reset() @@ -263,7 +270,7 @@ void FramedReader::readFrameDataAndCheck // if paused just read and waste data if (paused) { - _device->read((checksumEnabled ? frameSize+1 : frameSize)); + _device->read(checksumEnabled ? frameSize+1 : frameSize); return; } diff --git a/src/framedreader.h b/src/framedreader.h --- a/src/framedreader.h +++ b/src/framedreader.h @@ -1,5 +1,5 @@ /* - Copyright © 2018 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -79,8 +79,9 @@ private: /// @note should be called only if there are enough bytes on device void readFrameDataAndCheck(); + unsigned readData() override; + private slots: - void onDataReady() override; void onNumberFormatChanged(NumberFormat numberFormat); void onNumOfChannelsChanged(unsigned value); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -73,7 +73,8 @@ MainWindow::MainWindow(QWidget *parent) dataFormatPanel(&serialPort), recordPanel(&stream), textView(&stream), - updateCheckDialog(this) + updateCheckDialog(this), + bpsLabel(&portControl, &dataFormatPanel, this) { ui->setupUi(this); @@ -234,13 +235,21 @@ MainWindow::MainWindow(QWidget *parent) plotMan->setNumOfSamples(numOfSamples); plotMan->setPlotWidth(plotControlPanel.plotWidth()); + // init bps (bits per second) counter + ui->statusBar->addPermanentWidget(&bpsLabel); + // Init sps (sample per second) counter spsLabel.setText("0sps"); - spsLabel.setToolTip("samples per second (per channel)"); + spsLabel.setToolTip(tr("samples per second (per channel)")); ui->statusBar->addPermanentWidget(&spsLabel); connect(&sampleCounter, &SampleCounter::spsChanged, this, &MainWindow::onSpsChanged); + bpsLabel.setMinimumWidth(70); + bpsLabel.setAlignment(Qt::AlignRight); + spsLabel.setMinimumWidth(70); + spsLabel.setAlignment(Qt::AlignRight); + // init demo QObject::connect(ui->actionDemoMode, &QAction::toggled, this, &MainWindow::enableDemo); @@ -360,6 +369,11 @@ void MainWindow::onPortToggled(bool open // make sure demo mode is disabled if (open && isDemoRunning()) enableDemo(false); ui->actionDemoMode->setEnabled(!open); + + if (!open) + { + spsLabel.setText("0sps"); + } } void MainWindow::onSourceChanged(Source* source) diff --git a/src/mainwindow.h b/src/mainwindow.h --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -47,6 +47,7 @@ #include "updatecheckdialog.h" #include "samplecounter.h" #include "datatextview.h" +#include "bpslabel.h" namespace Ui { class MainWindow; @@ -91,6 +92,7 @@ private: PlotMenu plotMenu; DataTextView textView; UpdateCheckDialog updateCheckDialog; + BPSLabel bpsLabel; void handleCommandLineOptions(const QCoreApplication &app); diff --git a/src/portcontrol.cpp b/src/portcontrol.cpp --- a/src/portcontrol.cpp +++ b/src/portcontrol.cpp @@ -514,6 +514,27 @@ void PortControl::openPort() } } +unsigned PortControl::maxBitRate() const +{ + float baud = serialPort->baudRate(); + float dataBits = serialPort->dataBits(); + float parityBits = serialPort->parity() == QSerialPort::NoParity ? 0 : 1; + + float stopBits; + if (serialPort->stopBits() == QSerialPort::OneAndHalfStop) + { + stopBits = 1.5; + } + else + { + stopBits = serialPort->stopBits(); + } + + float frame_size = 1 /* start bit */ + dataBits + parityBits + stopBits; + + return float(baud) / frame_size; +} + void PortControl::saveSettings(QSettings* settings) { settings->beginGroup(SettingGroup_Port); diff --git a/src/portcontrol.h b/src/portcontrol.h --- a/src/portcontrol.h +++ b/src/portcontrol.h @@ -1,5 +1,5 @@ /* - Copyright © 2017 Hasan Yavuz Özderya + Copyright © 2019 Hasan Yavuz Özderya This file is part of serialplot. @@ -50,6 +50,8 @@ public: void selectPort(QString portName); void selectBaudrate(QString baudRate); void openPort(); + /// Returns maximum bit rate for current baud rate + unsigned maxBitRate() const; /// Stores port settings into a `QSettings` void saveSettings(QSettings* settings);