Changeset - 9f791e07b3c2
[Not reviewed]
default
0 4 0
Hasan Yavuz ÖZDERYA - 8 years ago 2017-11-05 03:30:38
hy@ozderya.net
move port error handling to portcontrol
4 files changed with 61 insertions and 63 deletions:
0 comments (0 inline, 0 general)
src/mainwindow.cpp
Show inline comments
 
@@ -86,443 +86,381 @@ MainWindow::MainWindow(QWidget *parent) 
 
    menuBar()->insertMenu(ui->menuHelp->menuAction(), snapshotMan.menu());
 
    menuBar()->insertMenu(ui->menuHelp->menuAction(), commandPanel.menu());
 

	
 
    connect(&commandPanel, &CommandPanel::focusRequested, [this]()
 
            {
 
                this->ui->tabWidget->setCurrentWidget(&commandPanel);
 
                this->ui->tabWidget->showTabs();
 
            });
 

	
 
    tbPortControl->setObjectName("tbPortControl");
 
    ui->plotToolBar->setObjectName("tbPlot");
 

	
 
    setupAboutDialog();
 

	
 
    // init view menu
 
    ui->menuBar->insertMenu(ui->menuSecondary->menuAction(), &plotMenu);
 
    plotMenu.addSeparator();
 
    QMenu* tbMenu = plotMenu.addMenu("Toolbars");
 
    tbMenu->addAction(ui->plotToolBar->toggleViewAction());
 
    tbMenu->addAction(portControl.toolBar()->toggleViewAction());
 

	
 
    // init secondary plot menu
 
    auto group = new QActionGroup(this);
 
    group->addAction(ui->actionVertical);
 
    group->addAction(ui->actionHorizontal);
 

	
 
    // init UI signals
 

	
 
    // Secondary plot menu signals
 
    connect(ui->actionBarPlot, &QAction::triggered,
 
            this, &MainWindow::showBarPlot);
 

	
 
    connect(ui->actionVertical, &QAction::triggered,
 
            [this](bool checked)
 
            {
 
                if (checked) ui->splitter->setOrientation(Qt::Vertical);
 
            });
 

	
 
    connect(ui->actionHorizontal, &QAction::triggered,
 
            [this](bool checked)
 
            {
 
                if (checked) ui->splitter->setOrientation(Qt::Horizontal);
 
            });
 

	
 
    // Help menu signals
 
    QObject::connect(ui->actionHelpAbout, &QAction::triggered,
 
              &aboutDialog, &QWidget::show);
 

	
 
    QObject::connect(ui->actionCheckUpdate, &QAction::triggered,
 
              &updateCheckDialog, &QWidget::show);
 

	
 
    QObject::connect(ui->actionReportBug, &QAction::triggered,
 
                     [](){QDesktopServices::openUrl(QUrl(BUG_REPORT_URL));});
 

	
 
    // File menu signals
 
    QObject::connect(ui->actionExportCsv, &QAction::triggered,
 
                     this, &MainWindow::onExportCsv);
 

	
 
    QObject::connect(ui->actionSaveSettings, &QAction::triggered,
 
                     this, &MainWindow::onSaveSettings);
 

	
 
    QObject::connect(ui->actionLoadSettings, &QAction::triggered,
 
                     this, &MainWindow::onLoadSettings);
 

	
 
    ui->actionQuit->setShortcutContext(Qt::ApplicationShortcut);
 

	
 
    QObject::connect(ui->actionQuit, &QAction::triggered,
 
                     this, &MainWindow::close);
 

	
 
    // port control signals
 
    QObject::connect(&portControl, &PortControl::portToggled,
 
                     this, &MainWindow::onPortToggled);
 

	
 
    // plot control signals
 
    connect(&plotControlPanel, &PlotControlPanel::numOfSamplesChanged,
 
            this, &MainWindow::onNumOfSamplesChanged);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::numOfSamplesChanged,
 
            plotMan, &PlotManager::setNumOfSamples);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::yScaleChanged,
 
            plotMan, &PlotManager::setYAxis);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::xScaleChanged,
 
            plotMan, &PlotManager::setXAxis);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::plotWidthChanged,
 
            plotMan, &PlotManager::setPlotWidth);
 

	
 
    // plot toolbar signals
 
    QObject::connect(ui->actionClear, SIGNAL(triggered(bool)),
 
                     this, SLOT(clearPlot()));
 

	
 
    QObject::connect(snapshotMan.takeSnapshotAction(), &QAction::triggered,
 
                     plotMan, &PlotManager::flashSnapshotOverlay);
 

	
 
    // init port signals
 
    QObject::connect(&(this->serialPort), SIGNAL(error(QSerialPort::SerialPortError)),
 
                     this, SLOT(onPortError(QSerialPort::SerialPortError)));
 

	
 
    // init data format and reader
 
    QObject::connect(&channelMan, &ChannelManager::dataAdded,
 
                     plotMan, &PlotManager::replot);
 

	
 
    QObject::connect(ui->actionPause, &QAction::triggered,
 
                     &channelMan, &ChannelManager::pause);
 

	
 
    QObject::connect(&recordPanel, &RecordPanel::recordStarted,
 
                     &dataFormatPanel, &DataFormatPanel::startRecording);
 

	
 
    QObject::connect(&recordPanel, &RecordPanel::recordStopped,
 
                     &dataFormatPanel, &DataFormatPanel::stopRecording);
 

	
 
    QObject::connect(ui->actionPause, &QAction::triggered,
 
                     [this](bool enabled)
 
                     {
 
                         if (enabled && !recordPanel.recordPaused())
 
                         {
 
                             dataFormatPanel.pause(true);
 
                         }
 
                         else
 
                         {
 
                             dataFormatPanel.pause(false);
 
                         }
 
                     });
 

	
 
    QObject::connect(&recordPanel, &RecordPanel::recordPausedChanged,
 
                     [this](bool enabled)
 
                     {
 
                         if (ui->actionPause->isChecked() && enabled)
 
                         {
 
                             dataFormatPanel.pause(false);
 
                         }
 
                     });
 

	
 
    connect(&serialPort, &QIODevice::aboutToClose,
 
            &recordPanel, &RecordPanel::onPortClose);
 

	
 
    // init data arrays and plot
 
    numOfSamples = plotControlPanel.numOfSamples();
 
    unsigned numOfChannels = dataFormatPanel.numOfChannels();
 

	
 
    channelMan.setNumOfSamples(numOfSamples);
 
    channelMan.setNumOfChannels(dataFormatPanel.numOfChannels());
 

	
 
    connect(&dataFormatPanel, &DataFormatPanel::numOfChannelsChanged,
 
            &channelMan, &ChannelManager::setNumOfChannels);
 

	
 
    connect(&channelMan, &ChannelManager::numOfChannelsChanged,
 
            this, &MainWindow::onNumOfChannelsChanged);
 

	
 
    plotControlPanel.setChannelInfoModel(channelMan.infoModel());
 

	
 
    // init curve list
 
    for (unsigned int i = 0; i < numOfChannels; i++)
 
    {
 
        plotMan->addCurve(channelMan.channelName(i), channelMan.channelBuffer(i));
 
    }
 

	
 
    // init scales
 
    plotMan->setYAxis(plotControlPanel.autoScale(),
 
                      plotControlPanel.yMin(), plotControlPanel.yMax());
 
    plotMan->setXAxis(plotControlPanel.xAxisAsIndex(),
 
                      plotControlPanel.xMin(), plotControlPanel.xMax());
 
    plotMan->setNumOfSamples(numOfSamples);
 
    plotMan->setPlotWidth(plotControlPanel.plotWidth());
 

	
 
    // Init sps (sample per second) counter
 
    spsLabel.setText("0sps");
 
    spsLabel.setToolTip("samples per second (per channel)");
 
    ui->statusBar->addPermanentWidget(&spsLabel);
 
    QObject::connect(&dataFormatPanel,
 
                     &DataFormatPanel::samplesPerSecondChanged,
 
                     this, &MainWindow::onSpsChanged);
 

	
 
    // init demo
 
    QObject::connect(ui->actionDemoMode, &QAction::toggled,
 
                     this, &MainWindow::enableDemo);
 

	
 
    QObject::connect(ui->actionDemoMode, &QAction::toggled,
 
                     plotMan, &PlotManager::showDemoIndicator);
 

	
 
    // load default settings
 
    QSettings settings("serialplot", "serialplot");
 
    loadAllSettings(&settings);
 

	
 
    // ensure command panel has 1 command if none loaded
 
    if (!commandPanel.numOfCommands())
 
    {
 
        commandPanel.newCommandAction()->trigger();
 
    }
 

	
 
    // Important: This should be after newCommandAction is triggered
 
    // (above) we don't want user to be greeted with command panel on
 
    // the very first run.
 
    connect(commandPanel.newCommandAction(), &QAction::triggered, [this]()
 
            {
 
                this->ui->tabWidget->setCurrentWidget(&commandPanel);
 
                this->ui->tabWidget->showTabs();
 
            });
 
}
 

	
 
MainWindow::~MainWindow()
 
{
 
    if (serialPort.isOpen())
 
    {
 
        serialPort.close();
 
    }
 

	
 
    delete plotMan;
 

	
 
    delete ui;
 
    ui = NULL; // we check if ui is deleted in messageHandler
 
}
 

	
 
void MainWindow::closeEvent(QCloseEvent * event)
 
{
 
    // save snapshots
 
    if (!snapshotMan.isAllSaved())
 
    {
 
        auto clickedButton = QMessageBox::warning(
 
            this, "Closing SerialPlot",
 
            "There are un-saved snapshots. If you close you will loose the data.",
 
            QMessageBox::Discard, QMessageBox::Cancel);
 
        if (clickedButton == QMessageBox::Cancel)
 
        {
 
            event->ignore();
 
            return;
 
        }
 
    }
 

	
 
    // save settings
 
    QSettings settings("serialplot", "serialplot");
 
    saveAllSettings(&settings);
 
    settings.sync();
 

	
 
    if (settings.status() != QSettings::NoError)
 
    {
 
        QString errorText;
 

	
 
        if (settings.status() == QSettings::AccessError)
 
        {
 
            QString file = settings.fileName();
 
            errorText = QString("Serialplot cannot save settings due to access error. \
 
This happens if you have run serialplot as root (with sudo for ex.) previously. \
 
Try fixing the permissions of file: %1, or just delete it.").arg(file);
 
        }
 
        else
 
        {
 
            errorText = QString("Serialplot cannot save settings due to unknown error: %1").\
 
                arg(settings.status());
 
        }
 

	
 
        auto button = QMessageBox::critical(
 
            NULL,
 
            "Failed to save settings!", errorText,
 
            QMessageBox::Cancel | QMessageBox::Ok);
 

	
 
        if (button == QMessageBox::Cancel)
 
        {
 
            event->ignore();
 
            return;
 
        }
 
    }
 

	
 
    QMainWindow::closeEvent(event);
 
}
 

	
 
void MainWindow::setupAboutDialog()
 
{
 
    Ui_AboutDialog uiAboutDialog;
 
    uiAboutDialog.setupUi(&aboutDialog);
 

	
 
    QObject::connect(uiAboutDialog.pbAboutQt, &QPushButton::clicked,
 
                     [](){ QApplication::aboutQt();});
 

	
 
    QString aboutText = uiAboutDialog.lbAbout->text();
 
    aboutText.replace("$VERSION_STRING$", VERSION_STRING);
 
    aboutText.replace("$VERSION_REVISION$", VERSION_REVISION);
 
    uiAboutDialog.lbAbout->setText(aboutText);
 
}
 

	
 
void MainWindow::onPortToggled(bool open)
 
{
 
    // make sure demo mode is disabled
 
    if (open && isDemoRunning()) enableDemo(false);
 
    ui->actionDemoMode->setEnabled(!open);
 
}
 

	
 
void MainWindow::onPortError(QSerialPort::SerialPortError error)
 
{
 
    switch(error)
 
    {
 
        case QSerialPort::NoError :
 
            break;
 
        case QSerialPort::ResourceError :
 
            qWarning() << "Port error: resource unavaliable; most likely device removed.";
 
            if (serialPort.isOpen())
 
            {
 
                qWarning() << "Closing port on resource error: " << serialPort.portName();
 
                portControl.togglePort();
 
            }
 
            portControl.loadPortList();
 
            break;
 
        case QSerialPort::DeviceNotFoundError:
 
            qCritical() << "Device doesn't exists: " << serialPort.portName();
 
            break;
 
        case QSerialPort::PermissionError:
 
            qCritical() << "Permission denied. Either you don't have \
 
required privileges or device is already opened by another process.";
 
            break;
 
        case QSerialPort::OpenError:
 
            qWarning() << "Device is already opened!";
 
            break;
 
        case QSerialPort::NotOpenError:
 
            qCritical() << "Device is not open!";
 
            break;
 
        case QSerialPort::ParityError:
 
            qCritical() << "Parity error detected.";
 
            break;
 
        case QSerialPort::FramingError:
 
            qCritical() << "Framing error detected.";
 
            break;
 
        case QSerialPort::BreakConditionError:
 
            qCritical() << "Break condition is detected.";
 
            break;
 
        case QSerialPort::WriteError:
 
            qCritical() << "An error occurred while writing data.";
 
            break;
 
        case QSerialPort::ReadError:
 
            qCritical() << "An error occurred while reading data.";
 
            break;
 
        case QSerialPort::UnsupportedOperationError:
 
            qCritical() << "Operation is not supported.";
 
            break;
 
        case QSerialPort::TimeoutError:
 
            qCritical() << "A timeout error occurred.";
 
            break;
 
        case QSerialPort::UnknownError:
 
            qCritical() << "Unknown error! Error: " << serialPort.errorString();
 
            break;
 
        default:
 
            qCritical() << "Unhandled port error: " << error;
 
            break;
 
    }
 
}
 

	
 
void MainWindow::clearPlot()
 
{
 
    for (unsigned ci = 0; ci < channelMan.numOfChannels(); ci++)
 
    {
 
        channelMan.channelBuffer(ci)->clear();
 
    }
 
    plotMan->replot();
 
}
 

	
 
void MainWindow::onNumOfSamplesChanged(int value)
 
{
 
    numOfSamples = value;
 
    channelMan.setNumOfSamples(value);
 
    plotMan->replot();
 
}
 

	
 
void MainWindow::onNumOfChannelsChanged(unsigned value)
 
{
 
    unsigned int oldNum = plotMan->numOfCurves();
 
    unsigned numOfChannels = value;
 

	
 
    if (numOfChannels > oldNum)
 
    {
 
        // add new channels
 
        for (unsigned int i = oldNum; i < numOfChannels; i++)
 
        {
 
            plotMan->addCurve(channelMan.channelName(i), channelMan.channelBuffer(i));
 
        }
 
    }
 
    else if(numOfChannels < oldNum)
 
    {
 
        plotMan->removeCurves(oldNum - numOfChannels);
 
    }
 

	
 
    plotMan->replot();
 
}
 

	
 
void MainWindow::onSpsChanged(unsigned sps)
 
{
 
    spsLabel.setText(QString::number(sps) + "sps");
 
}
 

	
 
bool MainWindow::isDemoRunning()
 
{
 
    return ui->actionDemoMode->isChecked();
 
}
 

	
 
void MainWindow::enableDemo(bool enabled)
 
{
 
    if (enabled)
 
    {
 
        if (!serialPort.isOpen())
 
        {
 
            dataFormatPanel.enableDemo(true);
 
        }
 
        else
 
        {
 
            ui->actionDemoMode->setChecked(false);
 
        }
 
    }
 
    else
 
    {
 
        dataFormatPanel.enableDemo(false);
 
        ui->actionDemoMode->setChecked(false);
 
    }
 
}
 

	
 
void MainWindow::showSecondary(QWidget* wid)
 
{
 
    if (secondaryPlot != NULL)
 
    {
 
        secondaryPlot->deleteLater();
 
    }
 

	
 
    secondaryPlot = wid;
 
    ui->splitter->addWidget(wid);
 
    ui->splitter->setStretchFactor(0, 1);
 
    ui->splitter->setStretchFactor(1, 0);
 
}
 

	
 
void MainWindow::hideSecondary()
 
{
 
    if (secondaryPlot == NULL)
 
    {
 
        qFatal("Secondary plot doesn't exist!");
 
    }
 

	
 
    secondaryPlot->deleteLater();
 
    secondaryPlot = NULL;
 
}
 

	
 
void MainWindow::showBarPlot(bool show)
 
{
 
    if (show)
 
    {
 
        auto plot = new BarPlot(&channelMan, &plotMenu);
src/mainwindow.h
Show inline comments
 
@@ -19,112 +19,111 @@
 

	
 
#ifndef MAINWINDOW_H
 
#define MAINWINDOW_H
 

	
 
#include <QMainWindow>
 
#include <QButtonGroup>
 
#include <QLabel>
 
#include <QString>
 
#include <QVector>
 
#include <QList>
 
#include <QSerialPort>
 
#include <QSignalMapper>
 
#include <QTimer>
 
#include <QColor>
 
#include <QtGlobal>
 
#include <QSettings>
 
#include <qwt_plot_curve.h>
 

	
 
#include "portcontrol.h"
 
#include "commandpanel.h"
 
#include "dataformatpanel.h"
 
#include "plotcontrolpanel.h"
 
#include "recordpanel.h"
 
#include "ui_about_dialog.h"
 
#include "framebuffer.h"
 
#include "channelmanager.h"
 
#include "snapshotmanager.h"
 
#include "plotmanager.h"
 
#include "plotmenu.h"
 
#include "datarecorder.h"
 
#include "updatecheckdialog.h"
 

	
 
namespace Ui {
 
class MainWindow;
 
}
 

	
 
class MainWindow : public QMainWindow
 
{
 
    Q_OBJECT
 

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

	
 
    PlotViewSettings viewSettings() const;
 

	
 
    void messageHandler(QtMsgType type, const QMessageLogContext &context,
 
                        const QString &msg);
 

	
 
private:
 
    Ui::MainWindow *ui;
 

	
 
    QDialog aboutDialog;
 
    void setupAboutDialog();
 

	
 
    QSerialPort serialPort;
 
    PortControl portControl;
 

	
 
    unsigned int numOfSamples;
 

	
 
    QList<QwtPlotCurve*> curves;
 
    ChannelManager channelMan;
 
    PlotManager* plotMan;
 
    QWidget* secondaryPlot;
 
    SnapshotManager snapshotMan;
 
    DataRecorder recorder;       // operated by `recordPanel`
 

	
 
    QLabel spsLabel;
 
    CommandPanel commandPanel;
 
    DataFormatPanel dataFormatPanel;
 
    RecordPanel recordPanel;
 
    PlotControlPanel plotControlPanel;
 
    PlotMenu plotMenu;
 
    UpdateCheckDialog updateCheckDialog;
 

	
 
    /// Returns true if demo is running
 
    bool isDemoRunning();
 
    /// Display a secondary plot in the splitter, removing and
 
    /// deleting previous one if it exists
 
    void showSecondary(QWidget* wid);
 
    /// Hide secondary plot
 
    void hideSecondary();
 
    /// Stores settings for all modules
 
    void saveAllSettings(QSettings* settings);
 
    /// Load settings for all modules
 
    void loadAllSettings(QSettings* settings);
 
    /// Stores main window settings into a `QSettings`
 
    void saveMWSettings(QSettings* settings);
 
    /// Loads main window settings from a `QSettings`
 
    void loadMWSettings(QSettings* settings);
 

	
 
    /// `QWidget::closeEvent` handler
 
    void closeEvent(QCloseEvent * event);
 

	
 
private slots:
 
    void onPortToggled(bool open);
 
    void onPortError(QSerialPort::SerialPortError error);
 

	
 
    void onNumOfSamplesChanged(int value);
 
    void onNumOfChannelsChanged(unsigned value);
 

	
 
    void clearPlot();
 
    void onSpsChanged(unsigned sps);
 
    void enableDemo(bool enabled);
 
    void showBarPlot(bool show);
 

	
 
    void onExportCsv();
 
    void onSaveSettings();
 
    void onLoadSettings();
 
};
 

	
 
#endif // MAINWINDOW_H
src/portcontrol.cpp
Show inline comments
 
/*
 
  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 "portcontrol.h"
 
#include "ui_portcontrol.h"
 

	
 
#include <QSerialPortInfo>
 
#include <QKeySequence>
 
#include <QLabel>
 
#include <QMap>
 
#include <QtDebug>
 

	
 
#include "setting_defines.h"
 
#include "utils.h"
 

	
 
#define TBPORTLIST_MINWIDTH (200)
 

	
 
// setting mappings
 
const QMap<QSerialPort::Parity, QString> paritySettingMap({
 
        {QSerialPort::NoParity, "none"},
 
        {QSerialPort::OddParity, "odd"},
 
        {QSerialPort::EvenParity, "even"},
 
    });
 

	
 
PortControl::PortControl(QSerialPort* port, QWidget* parent) :
 
    QWidget(parent),
 
    ui(new Ui::PortControl),
 
    portToolBar("Port Toolbar"),
 
    openAction("Open", this),
 
    loadPortListAction("↺", this)
 
{
 
    ui->setupUi(this);
 

	
 
    serialPort = port;
 
    connect(serialPort, SIGNAL(error(QSerialPort::SerialPortError)),
 
            this, SLOT(onPortError(QSerialPort::SerialPortError)));
 

	
 
    // setup actions
 
    openAction.setCheckable(true);
 
    openAction.setShortcut(QKeySequence("F12"));
 
    openAction.setToolTip("Open Port");
 
    QObject::connect(&openAction, &QAction::triggered,
 
                     this, &PortControl::openActionTriggered);
 

	
 
    loadPortListAction.setToolTip("Reload port list");
 
    QObject::connect(&loadPortListAction, &QAction::triggered,
 
                     [this](bool checked){loadPortList();});
 

	
 
    // setup toolbar
 
    portToolBar.addWidget(&tbPortList);
 
    portToolBar.addAction(&loadPortListAction);
 
    portToolBar.addAction(&openAction);
 

	
 
    // setup port selection widgets
 
    tbPortList.setMinimumWidth(TBPORTLIST_MINWIDTH);
 
    tbPortList.setModel(&portList);
 
    ui->cbPortList->setModel(&portList);
 
    QObject::connect(ui->cbPortList,
 
                     SELECT<int>::OVERLOAD_OF(&QComboBox::activated),
 
                     this, &PortControl::onCbPortListActivated);
 
    QObject::connect(&tbPortList,
 
                     SELECT<int>::OVERLOAD_OF(&QComboBox::activated),
 
                     this, &PortControl::onTbPortListActivated);
 
    QObject::connect(ui->cbPortList,
 
                     SELECT<const QString&>::OVERLOAD_OF(&QComboBox::activated),
 
                     this, &PortControl::selectPort);
 
    QObject::connect(&tbPortList,
 
                     SELECT<const QString&>::OVERLOAD_OF(&QComboBox::activated),
 
                     this, &PortControl::selectPort);
 

	
 
    // setup buttons
 
    ui->pbOpenPort->setDefaultAction(&openAction);
 
    ui->pbReloadPorts->setDefaultAction(&loadPortListAction);
 

	
 
    // setup baud rate selection widget
 
    QObject::connect(ui->cbBaudRate,
 
                     SELECT<const QString&>::OVERLOAD_OF(&QComboBox::activated),
 
                     this, &PortControl::selectBaudRate);
 

	
 
    // setup parity selection buttons
 
    parityButtons.addButton(ui->rbNoParity, (int) QSerialPort::NoParity);
 
    parityButtons.addButton(ui->rbEvenParity, (int) QSerialPort::EvenParity);
 
    parityButtons.addButton(ui->rbOddParity, (int) QSerialPort::OddParity);
 

	
 
    QObject::connect(&parityButtons,
 
                     SELECT<int>::OVERLOAD_OF(&QButtonGroup::buttonClicked),
 
                     this, &PortControl::selectParity);
 

	
 
    // setup data bits selection buttons
 
    dataBitsButtons.addButton(ui->rb8Bits, (int) QSerialPort::Data8);
 
    dataBitsButtons.addButton(ui->rb7Bits, (int) QSerialPort::Data7);
 
    dataBitsButtons.addButton(ui->rb6Bits, (int) QSerialPort::Data6);
 
    dataBitsButtons.addButton(ui->rb5Bits, (int) QSerialPort::Data5);
 

	
 
    QObject::connect(&dataBitsButtons,
 
                     SELECT<int>::OVERLOAD_OF(&QButtonGroup::buttonClicked),
 
                     this, &PortControl::selectDataBits);
 

	
 
    // setup stop bits selection buttons
 
    stopBitsButtons.addButton(ui->rb1StopBit, (int) QSerialPort::OneStop);
 
    stopBitsButtons.addButton(ui->rb2StopBit, (int) QSerialPort::TwoStop);
 

	
 
    QObject::connect(&stopBitsButtons,
 
                     SELECT<int>::OVERLOAD_OF(&QButtonGroup::buttonClicked),
 
                     this, &PortControl::selectStopBits);
 

	
 
    // setup flow control selection buttons
 
    flowControlButtons.addButton(ui->rbNoFlowControl,
 
                                 (int) QSerialPort::NoFlowControl);
 
    flowControlButtons.addButton(ui->rbHardwareControl,
 
                                 (int) QSerialPort::HardwareControl);
 
    flowControlButtons.addButton(ui->rbSoftwareControl,
 
                                 (int) QSerialPort::SoftwareControl);
 

	
 
    QObject::connect(&flowControlButtons,
 
                     SELECT<int>::OVERLOAD_OF(&QButtonGroup::buttonClicked),
 
                     this, &PortControl::selectFlowControl);
 

	
 
    // initialize signal leds
 
    ui->ledDTR->setOn(true);
 
    ui->ledRTS->setOn(true);
 

	
 
    // connect output signals
 
    connect(ui->pbDTR, &QPushButton::clicked, [this]()
 
            {
 
                // toggle DTR
 
                ui->ledDTR->toggle();
 
                if (serialPort->isOpen())
 
                {
 
                    serialPort->setDataTerminalReady(ui->ledDTR->isOn());
 
                }
 
            });
 
@@ -269,192 +271,250 @@ void PortControl::togglePort()
 
        QString portName;
 
        int portIndex = portList.indexOf(portText);
 
        if (portIndex < 0) // not in list, add to model and update the selections
 
        {
 
            portList.appendRow(new PortListItem(portText));
 
            ui->cbPortList->setCurrentIndex(portList.rowCount()-1);
 
            tbPortList.setCurrentIndex(portList.rowCount()-1);
 
            portName = portText;
 
        }
 
        else
 
        {
 
            // get the port name from the data field
 
            portName = static_cast<PortListItem*>(portList.item(portIndex))->portName();
 
        }
 

	
 
        serialPort->setPortName(ui->cbPortList->currentData(PortNameRole).toString());
 

	
 
        // open port
 
        if (serialPort->open(QIODevice::ReadWrite))
 
        {
 
            // set port settings
 
            selectBaudRate(ui->cbBaudRate->currentText());
 
            selectParity((QSerialPort::Parity) parityButtons.checkedId());
 
            selectDataBits((QSerialPort::DataBits) dataBitsButtons.checkedId());
 
            selectStopBits((QSerialPort::StopBits) stopBitsButtons.checkedId());
 
            selectFlowControl((QSerialPort::FlowControl) flowControlButtons.checkedId());
 

	
 
            // set output signals
 
            serialPort->setDataTerminalReady(ui->ledDTR->isOn());
 
            serialPort->setRequestToSend(ui->ledRTS->isOn());
 

	
 
            // update pin signals
 
            updatePinLeds();
 
            pinUpdateTimer.start();
 

	
 
            qDebug() << "Opened port:" << serialPort->portName();
 
            emit portToggled(true);
 
        }
 
    }
 
    openAction.setChecked(serialPort->isOpen());
 
}
 

	
 
void PortControl::selectPort(QString portName)
 
{
 
    // portName may be coming from combobox
 
    portName = portName.split(" ")[0];
 
    // has selection actually changed
 
    if (portName != serialPort->portName())
 
    {
 
        // if another port is already open, close it by toggling
 
        if (serialPort->isOpen())
 
        {
 
            togglePort();
 

	
 
            // open new selection by toggling
 
            togglePort();
 
        }
 
    }
 
}
 

	
 
QString PortControl::selectedPortName()
 
{
 
    QString portText = ui->cbPortList->currentText();
 
    int portIndex = portList.indexOf(portText);
 
    if (portIndex < 0) // not in the list yet
 
    {
 
        // return the displayed name as port name
 
        return portText;
 
    }
 
    else
 
    {
 
        // get the port name from the 'port list'
 
        return static_cast<PortListItem*>(portList.item(portIndex))->portName();
 
    }
 
}
 

	
 
QToolBar* PortControl::toolBar()
 
{
 
    return &portToolBar;
 
}
 

	
 
void PortControl::openActionTriggered(bool checked)
 
{
 
    togglePort();
 
}
 

	
 
void PortControl::onCbPortListActivated(int index)
 
{
 
    tbPortList.setCurrentIndex(index);
 
}
 

	
 
void PortControl::onTbPortListActivated(int index)
 
{
 
    ui->cbPortList->setCurrentIndex(index);
 
}
 

	
 
void PortControl::onPortError(QSerialPort::SerialPortError error)
 
{
 
    switch(error)
 
    {
 
        case QSerialPort::NoError :
 
            break;
 
        case QSerialPort::ResourceError :
 
            qWarning() << "Port error: resource unavaliable; most likely device removed.";
 
            if (serialPort->isOpen())
 
            {
 
                qWarning() << "Closing port on resource error: " << serialPort->portName();
 
                togglePort();
 
            }
 
            loadPortList();
 
            break;
 
        case QSerialPort::DeviceNotFoundError:
 
            qCritical() << "Device doesn't exists: " << serialPort->portName();
 
            break;
 
        case QSerialPort::PermissionError:
 
            qCritical() << "Permission denied. Either you don't have \
 
required privileges or device is already opened by another process.";
 
            break;
 
        case QSerialPort::OpenError:
 
            qWarning() << "Device is already opened!";
 
            break;
 
        case QSerialPort::NotOpenError:
 
            qCritical() << "Device is not open!";
 
            break;
 
        case QSerialPort::ParityError:
 
            qCritical() << "Parity error detected.";
 
            break;
 
        case QSerialPort::FramingError:
 
            qCritical() << "Framing error detected.";
 
            break;
 
        case QSerialPort::BreakConditionError:
 
            qCritical() << "Break condition is detected.";
 
            break;
 
        case QSerialPort::WriteError:
 
            qCritical() << "An error occurred while writing data.";
 
            break;
 
        case QSerialPort::ReadError:
 
            qCritical() << "An error occurred while reading data.";
 
            break;
 
        case QSerialPort::UnsupportedOperationError:
 
            qCritical() << "Operation is not supported.";
 
            break;
 
        case QSerialPort::TimeoutError:
 
            qCritical() << "A timeout error occurred.";
 
            break;
 
        case QSerialPort::UnknownError:
 
            qCritical() << "Unknown error! Error: " << serialPort->errorString();
 
            break;
 
        default:
 
            qCritical() << "Unhandled port error: " << error;
 
            break;
 
    }
 
}
 

	
 
void PortControl::updatePinLeds(void)
 
{
 
    auto pins = serialPort->pinoutSignals();
 
    ui->ledDCD->setOn(pins & QSerialPort::DataCarrierDetectSignal);
 
    ui->ledDSR->setOn(pins & QSerialPort::DataSetReadySignal);
 
    ui->ledRI->setOn(pins & QSerialPort::RingIndicatorSignal);
 
    ui->ledCTS->setOn(pins & QSerialPort::ClearToSendSignal);
 
}
 

	
 
QString PortControl::currentParityText()
 
{
 
    return paritySettingMap.value(
 
        (QSerialPort::Parity) parityButtons.checkedId());
 
}
 

	
 
QString PortControl::currentFlowControlText()
 
{
 
    if (flowControlButtons.checkedId() == QSerialPort::HardwareControl)
 
    {
 
        return "hardware";
 
    }
 
    else if (flowControlButtons.checkedId() == QSerialPort::SoftwareControl)
 
    {
 
        return "software";
 
    }
 
    else // no parity
 
    {
 
        return "none";
 
    }
 
}
 

	
 
void PortControl::saveSettings(QSettings* settings)
 
{
 
    settings->beginGroup(SettingGroup_Port);
 
    settings->setValue(SG_Port_SelectedPort, selectedPortName());
 
    settings->setValue(SG_Port_BaudRate, ui->cbBaudRate->currentText());
 
    settings->setValue(SG_Port_Parity, currentParityText());
 
    settings->setValue(SG_Port_DataBits, dataBitsButtons.checkedId());
 
    settings->setValue(SG_Port_StopBits, stopBitsButtons.checkedId());
 
    settings->setValue(SG_Port_FlowControl, currentFlowControlText());
 
    settings->endGroup();
 
}
 

	
 
void PortControl::loadSettings(QSettings* settings)
 
{
 
    // make sure the port is closed
 
    if (serialPort->isOpen()) togglePort();
 

	
 
    settings->beginGroup(SettingGroup_Port);
 

	
 
    // set port name if it exists in the current list otherwise ignore
 
    QString portName = settings->value(SG_Port_SelectedPort, QString()).toString();
 
    if (!portName.isEmpty())
 
    {
 
        int index = portList.indexOfName(portName);
 
        if (index > -1) ui->cbPortList->setCurrentIndex(index);
 
    }
 

	
 
    // load baud rate setting if it exists in baud rate list
 
    QString baudSetting = settings->value(
 
        SG_Port_BaudRate, ui->cbBaudRate->currentText()).toString();
 
    int baudIndex = ui->cbBaudRate->findText(baudSetting);
 
    if (baudIndex > -1) ui->cbBaudRate->setCurrentIndex(baudIndex);
 

	
 
    // load parity setting
 
    QString parityText =
 
        settings->value(SG_Port_Parity, currentParityText()).toString();
 
    QSerialPort::Parity paritySetting = paritySettingMap.key(
 
        parityText, (QSerialPort::Parity) parityButtons.checkedId());
 
    parityButtons.button(paritySetting)->setChecked(true);
 

	
 
    // load number of bits
 
    int dataBits = settings->value(SG_Port_Parity, dataBitsButtons.checkedId()).toInt();
 
    if (dataBits >=5 && dataBits <= 8)
 
    {
 
        dataBitsButtons.button((QSerialPort::DataBits) dataBits)->setChecked(true);
 
    }
 

	
 
    // load stop bits
 
    int stopBits = settings->value(SG_Port_StopBits, stopBitsButtons.checkedId()).toInt();
 
    if (stopBits == QSerialPort::OneStop)
 
    {
 
        ui->rb1StopBit->setChecked(true);
 
    }
 
    else if (stopBits == QSerialPort::TwoStop)
 
    {
 
        ui->rb2StopBit->setChecked(true);
 
    }
 

	
 
    // load flow control
 
    QString flowControlSetting =
 
        settings->value(SG_Port_FlowControl, currentFlowControlText()).toString();
 
    if (flowControlSetting == "hardware")
 
    {
 
        ui->rbHardwareControl->setChecked(true);
 
    }
src/portcontrol.h
Show inline comments
 
/*
 
  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/>.
 
*/
 

	
 
#ifndef PORTCONTROL_H
 
#define PORTCONTROL_H
 

	
 
#include <QWidget>
 
#include <QButtonGroup>
 
#include <QSerialPort>
 
#include <QStringList>
 
#include <QToolBar>
 
#include <QAction>
 
#include <QComboBox>
 
#include <QSettings>
 
#include <QTimer>
 

	
 
#include "portlist.h"
 

	
 
namespace Ui {
 
class PortControl;
 
}
 

	
 
class PortControl : public QWidget
 
{
 
    Q_OBJECT
 

	
 
public:
 
    explicit PortControl(QSerialPort* port, QWidget* parent = 0);
 
    ~PortControl();
 

	
 
    QSerialPort* serialPort;
 
    QToolBar* toolBar();
 

	
 
    /// Stores port settings into a `QSettings`
 
    void saveSettings(QSettings* settings);
 
    /// Loads port settings from a `QSettings`. If open serial port is closed.
 
    void loadSettings(QSettings* settings);
 

	
 
private:
 
    Ui::PortControl *ui;
 

	
 
    QButtonGroup parityButtons;
 
    QButtonGroup dataBitsButtons;
 
    QButtonGroup stopBitsButtons;
 
    QButtonGroup flowControlButtons;
 

	
 
    QToolBar portToolBar;
 
    QAction openAction;
 
    QAction loadPortListAction;
 
    QComboBox tbPortList;
 
    PortList portList;
 

	
 
    /// Used to refresh pinout signal leds periodically
 
    QTimer pinUpdateTimer;
 

	
 
    /// Returns the currently selected (entered) "portName" in the UI
 
    QString selectedPortName();
 
    /// Returns currently selected parity as text to be saved in settings
 
    QString currentParityText();
 
    /// Returns currently selected flow control as text to be saved in settings
 
    QString currentFlowControlText();
 

	
 
public slots:
 
    void loadPortList();
 
    void loadBaudRateList();
 
    void togglePort();
 
    void selectPort(QString portName);
 

	
 
    void selectBaudRate(QString baudRate);
 
    void selectParity(int parity); // parity must be one of QSerialPort::Parity
 
    void selectDataBits(int dataBits); // bits must be one of QSerialPort::DataBits
 
    void selectStopBits(int stopBits); // stopBits must be one of QSerialPort::StopBits
 
    void selectFlowControl(int flowControl); // flowControl must be one of QSerialPort::FlowControl
 

	
 
private slots:
 
    void openActionTriggered(bool checked);
 
    void onCbPortListActivated(int index);
 
    void onTbPortListActivated(int index);
 
    void onPortError(QSerialPort::SerialPortError error);
 
    void updatePinLeds(void);
 

	
 
signals:
 
    void portToggled(bool open);
 
};
 

	
 
#endif // PORTCONTROL_H
0 comments (0 inline, 0 general)