Changeset - cd91cca62e03
[Not reviewed]
new-reader
0 5 0
Hasan Yavuz ÖZDERYA - 9 years ago 2016-05-29 16:58:21
hy@ozderya.net
added debug mode (extra log messages) for framed reader
5 files changed with 32 insertions and 1 deletions:
0 comments (0 inline, 0 general)
src/framedreader.cpp
Show inline comments
 
/*
 
  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 <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#include <QtDebug>
 
#include <QtEndian>
 
#include "floatswap.h"
 

	
 
#include "framedreader.h"
 

	
 
FramedReader::FramedReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) :
 
    AbstractReader(device, channelMan, parent)
 
{
 
    paused = false;
 

	
 
    // initial settings
 
    settingsInvalid = 0;
 
    _numOfChannels = _settingsWidget.numOfChannels();
 
    hasSizeByte = _settingsWidget.frameSize() == 0;
 
    frameSize = _settingsWidget.frameSize();
 
    syncWord = _settingsWidget.syncWord();
 
    checksumEnabled = _settingsWidget.isChecksumEnabled();
 
    onNumberFormatChanged(_settingsWidget.numberFormat());
 
    debugModeEnabled = _settingsWidget.isDebugModeEnabled();
 
    checkSettings();
 

	
 
    // init setting connections
 
    connect(&_settingsWidget, &FramedReaderSettings::numberFormatChanged,
 
            this, &FramedReader::onNumberFormatChanged);
 

	
 
    connect(&_settingsWidget, &FramedReaderSettings::numOfChannelsChanged,
 
            this, &FramedReader::onNumOfChannelsChanged);
 

	
 
    connect(&_settingsWidget, &FramedReaderSettings::syncWordChanged,
 
            this, &FramedReader::onSyncWordChanged);
 

	
 
    connect(&_settingsWidget, &FramedReaderSettings::frameSizeChanged,
 
            this, &FramedReader::onFrameSizeChanged);
 

	
 
    connect(&_settingsWidget, &FramedReaderSettings::checksumChanged,
 
            [this](bool enabled){checksumEnabled = enabled; reset();});
 

	
 
    connect(&_settingsWidget, &FramedReaderSettings::debugModeChanged,
 
            [this](bool enabled){debugModeEnabled = enabled;});
 

	
 
    // init reader state
 
    reset();
 
}
 

	
 
void FramedReader::enable(bool enabled)
 
{
 
    if (enabled)
 
    {
 
        connect(_device, &QIODevice::readyRead,
 
                this, &FramedReader::onDataReady);
 
    }
 
    else
 
    {
 
        QObject::disconnect(_device, 0, this, 0);
 
    }
 
}
 

	
 
QWidget* FramedReader::settingsWidget()
 
{
 
    return &_settingsWidget;
 
}
 

	
 
unsigned FramedReader::numOfChannels()
 
{
 
    return _numOfChannels;
 
}
 

	
 
void FramedReader::pause(bool enabled)
 
{
 
    paused = enabled;
 
}
 

	
 
void FramedReader::onNumberFormatChanged(NumberFormat numberFormat)
 
{
 
    switch(numberFormat)
 
    {
 
        case NumberFormat_uint8:
 
            sampleSize = 1;
 
            readSample = &FramedReader::readSampleAs<quint8>;
 
            break;
 
        case NumberFormat_int8:
 
            sampleSize = 1;
 
            readSample = &FramedReader::readSampleAs<qint8>;
 
            break;
 
        case NumberFormat_uint16:
 
            sampleSize = 2;
 
            readSample = &FramedReader::readSampleAs<quint16>;
 
            break;
 
@@ -190,96 +194,97 @@ void FramedReader::onFrameSizeChanged(un
 
    {
 
        hasSizeByte = false;
 
        frameSize = value;
 
    }
 
    checkSettings();
 
    reset();
 
}
 

	
 
void FramedReader::onDataReady()
 
{
 
    if (settingsInvalid) return;
 

	
 
    // loop until we run out of bytes or more bytes is required
 
    unsigned bytesAvailable;
 
    while ((bytesAvailable = _device->bytesAvailable()))
 
    {
 
        if (!gotSync) // read sync word
 
        {
 
            char c;
 
            _device->getChar(&c);
 
            if (c == syncWord[sync_i]) // correct sync byte?
 
            {
 
                sync_i++;
 
                if (sync_i == (unsigned) syncWord.length())
 
                {
 
                    gotSync = true;
 
                }
 
            }
 
        }
 
        else if (hasSizeByte && !gotSize) // skipped if fixed frame size
 
        {
 
            frameSize = 0;
 
            _device->getChar((char*) &frameSize);
 

	
 
            if (frameSize == 0) // check size
 
            {
 
                qCritical() << "Frame size is 0!";
 
                reset();
 
            }
 
            else if (frameSize % (_numOfChannels * sampleSize) != 0)
 
            {
 
                qCritical() <<
 
                    QString("Frame size is not multiple of %1 (#channels * sample size)!") \
 
                    .arg(_numOfChannels * sampleSize);
 
                reset();
 
            }
 
            else
 
            {
 
                if (debugModeEnabled) qDebug() << "Frame size:" << frameSize;
 
                gotSize = true;
 
            }
 
        }
 
        else // read data bytes
 
        {
 
            // have enough data bytes? (+1 for checksum)
 
            if (bytesAvailable < (checksumEnabled ? frameSize+1 : frameSize))
 
            {
 
                break;
 
            }
 
            else // read data bytes and checksum
 
            {
 
                readFrameDataAndCheck();
 
                reset();
 
            }
 
        }
 
    }
 
}
 

	
 
void FramedReader::reset()
 
{
 
    sync_i = 0;
 
    gotSync = false;
 
    gotSize = false;
 
    if (hasSizeByte) frameSize = 0;
 
    calcChecksum = 0;
 
}
 

	
 
// Important: this function assumes device has enough bytes to read a full frames data and checksum
 
void FramedReader::readFrameDataAndCheck()
 
{
 
    // a package is 1 set of samples for all channels
 
    unsigned numOfPackagesToRead = frameSize / (_numOfChannels * sampleSize);
 
    double* channelSamples = new double[numOfPackagesToRead * _numOfChannels];
 

	
 
    for (unsigned i = 0; i < numOfPackagesToRead; i++)
 
    {
 
        for (unsigned int ci = 0; ci < _numOfChannels; ci++)
 
        {
 
            channelSamples[ci*numOfPackagesToRead+i] = (this->*readSample)();
 
        }
 
    }
 

	
 
    // read checksum
 
    unsigned rChecksum = 0;
 
    bool checksumPassed = false;
 
    if (checksumEnabled)
 
    {
src/framedreader.h
Show inline comments
 
@@ -12,81 +12,81 @@
 
  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/>.
 
*/
 

	
 
#ifndef FRAMEDREADER_H
 
#define FRAMEDREADER_H
 

	
 
#include "abstractreader.h"
 
#include "framedreadersettings.h"
 

	
 
/**
 
 * Reads data in a customizable framed format.
 
 */
 
class FramedReader : public AbstractReader
 
{
 
    Q_OBJECT
 

	
 
public:
 
    explicit FramedReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0);
 
    QWidget* settingsWidget();
 
    unsigned numOfChannels();
 
    void enable(bool enabled = true);
 

	
 
public slots:
 
    void pause(bool);
 

	
 
private:
 
    /// bit wise fields for `settingsValid` member
 
    enum SettingInvalidFlag
 
    {
 
        SYNCWORD_INVALID = 1,
 
        FRAMESIZE_INVALID = 2
 
    };
 

	
 
    // settings related members
 
    FramedReaderSettings _settingsWidget;
 
    unsigned _numOfChannels;
 
    unsigned sampleSize;
 
    bool paused;
 
    unsigned settingsInvalid;   /// settings are all valid if this is 0, if not no reading is done
 
    QByteArray syncWord;
 
    bool checksumEnabled;
 
    bool hasSizeByte;
 
    unsigned frameSize;
 
    bool debugModeEnabled;
 

	
 
    /// Checks the validity of syncWord and frameSize then shows an
 
    /// error message. Also updates `settingsInvalid`. If settings are
 
    /// valid `settingsInvalid` should be `0`.
 
    void checkSettings();
 

	
 
    // read state related members
 
    unsigned sync_i; /// sync byte index to be read next
 
    bool gotSync;    /// indicates if sync word is captured
 
    bool gotSize;    /// indicates if size is captured, ignored if size byte is disabled (fixed size)
 
    unsigned calcChecksum;
 

	
 
    void reset();    /// Resets the reading state. Used in case of error or setting change.
 
    /// points to the readSampleAs function for currently selected number format
 
    double (FramedReader::*readSample)();
 
    template<typename T> double readSampleAs();
 
    /// reads payload portion of the frame, calculates checksum and commits data
 
    /// @note should be called only if there are enough bytes on device
 
    void readFrameDataAndCheck();
 
    // `data` contains i th channels data
 
    void addChannelData(unsigned int channel, double* data, unsigned size);
 

	
 

	
 
private slots:
 
    void onDataReady();
 

	
 
    void onNumberFormatChanged(NumberFormat numberFormat);
 
    void onNumOfChannelsChanged(unsigned value);
 
    void onSyncWordChanged(QByteArray);
 
    void onFrameSizeChanged(unsigned);
 
};
 

	
 
#endif // FRAMEDREADER_H
src/framedreadersettings.cpp
Show inline comments
 
/*
 
  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 <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#include "utils.h"
 
#include "framedreadersettings.h"
 
#include "ui_framedreadersettings.h"
 

	
 
FramedReaderSettings::FramedReaderSettings(QWidget *parent) :
 
    QWidget(parent),
 
    ui(new Ui::FramedReaderSettings)
 
{
 
    ui->setupUi(this);
 

	
 
    ui->leSyncWord->setMode(false); // hex mode
 

	
 
    connect(ui->cbChecksum, &QCheckBox::toggled,
 
            [this](bool enabled)
 
            {
 
                emit checksumChanged(enabled);
 
            });
 

	
 
    connect(ui->cbDebugMode, &QCheckBox::toggled,
 
            this, &FramedReaderSettings::debugModeChanged);
 

	
 
    connect(ui->rbFixedSize, &QRadioButton::toggled,
 
            ui->spSize, &QWidget::setEnabled);
 

	
 
    connect(ui->rbFixedSize, &QRadioButton::toggled,
 
            [this](bool checked)
 
            {
 
                emit frameSizeChanged(frameSize());
 
            });
 

	
 
    // Note: if directly connected we get a runtime warning on incompatible signal arguments
 
    connect(ui->spSize, SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged),
 
            [this](int value)
 
            {
 
                emit frameSizeChanged(value);
 
            });
 

	
 
    connect(ui->spNumOfChannels, SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged),
 
            [this](int value)
 
            {
 
                emit numOfChannelsChanged(value);
 
            });
 

	
 
    connect(ui->leSyncWord, &QLineEdit::textChanged,
 
            this, &FramedReaderSettings::onSyncWordEdited);
 

	
 
    connect(ui->nfBox, SIGNAL(selectionChanged(NumberFormat)),
 
            this, SIGNAL(numberFormatChanged(NumberFormat)));
 
}
 

	
 
FramedReaderSettings::~FramedReaderSettings()
 
{
 
    delete ui;
 
}
 

	
 
void FramedReaderSettings::showMessage(QString message, bool error)
 
{
 
    ui->lMessage->setText(message);
 
    if (error)
 
    {
 
        ui->lMessage->setStyleSheet("color: red;");
 
    }
 
    else
 
    {
 
        ui->lMessage->setStyleSheet("");
 
    }
 
}
 

	
 
unsigned FramedReaderSettings::numOfChannels()
 
@@ -90,48 +93,53 @@ unsigned FramedReaderSettings::numOfChan
 
NumberFormat FramedReaderSettings::numberFormat()
 
{
 
    return ui->nfBox->currentSelection();
 
}
 

	
 
Endianness FramedReaderSettings::endianness()
 
{
 
    return ui->endiBox->currentSelection();
 
}
 

	
 
QByteArray FramedReaderSettings::syncWord()
 
{
 
    QString text = ui->leSyncWord->text().remove(' ');
 

	
 
    // check if nibble is missing
 
    if (text.size() % 2 == 1)
 
    {
 
        // TODO: remove this warning
 
        return QByteArray();
 
    }
 
    else
 
    {
 
        return QByteArray::fromHex(text.toLatin1());
 
    }
 
}
 

	
 
void FramedReaderSettings::onSyncWordEdited()
 
{
 
    // TODO: emit with a delay so that error message doesn't flash!
 
    emit syncWordChanged(syncWord());
 
}
 

	
 
unsigned FramedReaderSettings::frameSize()
 
{
 
    if (ui->rbFixedSize->isChecked())
 
    {
 
        return ui->spSize->value();
 
    }
 
    else
 
    {
 
        return 0; // frame byte is enabled
 
    }
 
}
 

	
 
bool FramedReaderSettings::isChecksumEnabled()
 
{
 
    return ui->cbChecksum->isChecked();
 
}
 

	
 
bool FramedReaderSettings::isDebugModeEnabled()
 
{
 
    return ui->cbDebugMode->isChecked();
 
}
src/framedreadersettings.h
Show inline comments
 
/*
 
  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 <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#ifndef FRAMEDREADERSETTINGS_H
 
#define FRAMEDREADERSETTINGS_H
 

	
 
#include <QWidget>
 
#include <QByteArray>
 

	
 
#include "numberformatbox.h"
 
#include "endiannessbox.h"
 

	
 
namespace Ui {
 
class FramedReaderSettings;
 
}
 

	
 
class FramedReaderSettings : public QWidget
 
{
 
    Q_OBJECT
 

	
 
public:
 
    explicit FramedReaderSettings(QWidget *parent = 0);
 
    ~FramedReaderSettings();
 

	
 
    void showMessage(QString message, bool error = false);
 

	
 
    unsigned numOfChannels();
 
    NumberFormat numberFormat();
 
    Endianness endianness();
 
    QByteArray syncWord();
 
    unsigned frameSize(); /// If frame bye is enabled `0` is returned
 
    bool isChecksumEnabled();
 
    bool isDebugModeEnabled();
 

	
 
signals:
 
    /// If sync word is invalid (empty or 1 nibble missing at the end)
 
    /// signaled with an empty array
 
    void syncWordChanged(QByteArray);
 
    /// `0` indicates frame size byte is enabled
 
    void frameSizeChanged(unsigned);
 
    void checksumChanged(bool);
 
    void numOfChannelsChanged(unsigned);
 
    void numberFormatChanged(NumberFormat);
 
    void debugModeChanged(bool);
 

	
 
private:
 
    Ui::FramedReaderSettings *ui;
 

	
 
private slots:
 
    void onSyncWordEdited();
 
};
 

	
 
#endif // FRAMEDREADERSETTINGS_H
src/framedreadersettings.ui
Show inline comments
 
@@ -30,96 +30,112 @@
 
     <y>40</y>
 
     <width>120</width>
 
     <height>80</height>
 
    </rect>
 
   </property>
 
  </widget>
 
  <widget class="QWidget" name="layoutWidget">
 
   <property name="geometry">
 
    <rect>
 
     <x>210</x>
 
     <y>0</y>
 
     <width>211</width>
 
     <height>29</height>
 
    </rect>
 
   </property>
 
   <layout class="QHBoxLayout" name="horizontalLayout_3">
 
    <item>
 
     <widget class="QLabel" name="label_2">
 
      <property name="text">
 
       <string>Number Of Channels:</string>
 
      </property>
 
     </widget>
 
    </item>
 
    <item>
 
     <widget class="QSpinBox" name="spNumOfChannels">
 
      <property name="minimum">
 
       <number>1</number>
 
      </property>
 
      <property name="maximum">
 
       <number>32</number>
 
      </property>
 
     </widget>
 
    </item>
 
   </layout>
 
  </widget>
 
  <widget class="QLabel" name="lMessage">
 
   <property name="geometry">
 
    <rect>
 
     <x>0</x>
 
     <y>180</y>
 
     <width>471</width>
 
     <height>17</height>
 
    </rect>
 
   </property>
 
   <property name="text">
 
    <string>All is well.</string>
 
   </property>
 
  </widget>
 
  <widget class="QCheckBox" name="cbDebugMode">
 
   <property name="geometry">
 
    <rect>
 
     <x>360</x>
 
     <y>160</y>
 
     <width>111</width>
 
     <height>22</height>
 
    </rect>
 
   </property>
 
   <property name="toolTip">
 
    <string>Enable printing of extra log messages that can be useful for debugging</string>
 
   </property>
 
   <property name="text">
 
    <string>Debug Mode</string>
 
   </property>
 
  </widget>
 
  <widget class="QGroupBox" name="groupBox">
 
   <property name="geometry">
 
    <rect>
 
     <x>1</x>
 
     <y>39</y>
 
     <width>204</width>
 
     <height>93</height>
 
    </rect>
 
   </property>
 
   <property name="title">
 
    <string>Frame Size:</string>
 
   </property>
 
   <layout class="QVBoxLayout" name="verticalLayout">
 
    <item>
 
     <layout class="QHBoxLayout" name="horizontalLayout">
 
      <item>
 
       <widget class="QRadioButton" name="rbFixedSize">
 
        <property name="text">
 
         <string>Fixed Size:</string>
 
        </property>
 
       </widget>
 
      </item>
 
      <item>
 
       <widget class="QSpinBox" name="spSize">
 
        <property name="enabled">
 
         <bool>false</bool>
 
        </property>
 
        <property name="minimum">
 
         <number>1</number>
 
        </property>
 
        <property name="maximum">
 
         <number>255</number>
 
        </property>
 
       </widget>
 
      </item>
 
     </layout>
 
    </item>
 
    <item>
 
     <widget class="QRadioButton" name="rbSizeByte">
 
      <property name="text">
 
       <string>First byte of frame is size</string>
 
      </property>
 
      <property name="checked">
 
       <bool>true</bool>
 
      </property>
 
     </widget>
 
    </item>
 
   </layout>
0 comments (0 inline, 0 general)