Не могу получить 2 QThreads для правильного общения - PullRequest
0 голосов
/ 23 января 2020

У меня проблема с корректной работой 2 QThreads. Arduino Due отправляет данные на мой P C, и мне удалось построить их, используя класс QSerialPort из Qt и библиотеку QCustomPlot. Однако, как только я перемещаю или изменяю размер окна, отображающего график, я теряю данные, и на графике появляются пики.

Я использую Qt Creator 4.10.2 с Qt 5.12.6 на Windows 7.

Итак, я искал на net, и люди советовали использовать 2 потока, один для получения данных из последовательного порта и отправки их в основной поток и один для вывода данных (основной поток). Дело в том, что я не могу заставить эти два потока общаться без потери данных. Действительно, поскольку я посылаю данные слишком быстро на последовательный порт (10 кГц, хотя мне это не кажется быстрым для компьютера), мой основной поток не может получить данные и отобразить их. На самом деле происходит то, что я посылаю QArrayByte из рабочего потока в основной поток, я добавляю эти байты в вектор, чтобы отобразить его после, но в середине этого обновления QByteArray обновляется (через ReadyRead SIGNAL ) прежде чем я смогу обработать все данные из первого QByteArray. Параллельно с этим я также записываю данные в файл CSV для последующего использования. Вот фрагмент моего кода:

MainWindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QWidget>
#include <QTimer>
#include <QDebug>
#include <QMessageBox>
#include <QFile>
#include <time.h>
#include <QElapsedTimer>
#include <dialog.h>
#include <iostream>
#include <fstream>
#include <bitset>
#include <stdio.h>
#include <tchar.h>
#include <QThread>
#include <QProcess>
#include "serialthread.h"



QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    QVector<double> multiply(QVector<double> vec, double mult);
    QVector<double> add(QVector<double> vec, double value);
    QSerialPort *Arduino;
    Dialog portConfig;


    SerialThread *SerialCom;
    //QThread *WorkThread;


public slots:
    void readSerial(QByteArray *dataR);
    void on_scale_clicked();


private slots:
    void on_clear_clicked();
    void clearData();
    void plot();
    void addPoint1(double t,double cos);
    void addPoint2(double sin);
    void updateLCD();
    void updatePlot();

signals:
    void ReadFinished();


private:
    Ui::MainWindow *ui;
    QElapsedTimer timer;
    QVector<double> temps, ORD1, ORD2, ORD1bis, ORD2bis;
    QByteArray dataRecieved, dataRecieved_r;
    QFile Fichier;
    qint64 time;
    qint16 res1, res2;
    int  dataLength, erreur, compteur, z_latch;
    double mean1 = 0, mean2 = 0;
    QTimer *timer2;
    std::ofstream Data;
    int nbElem;


};

#endif // MAINWINDOW_H

MainWindow. cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{

        //Initialisation de la Fenêtre principale
    ui->setupUi(this);

        //Initialisation du thread de travail (réalisant la communication série)
    SerialCom = new SerialThread();
    QThread *WorkThread = new QThread(this);
    SerialCom->moveToThread(WorkThread);
    connect(WorkThread, &QThread::finished, WorkThread, &QThread::deleteLater);
    connect(WorkThread, &QThread::finished, SerialCom, &SerialThread::deleteLater);
    connect(WorkThread, &QThread::started, SerialCom, &SerialThread::SerialRecieve);
    WorkThread->start();

        //Initialisation du graphique
    QList<QCPAxis *> axis;
    axis.append(ui->plotPos->axisRect()->axis(QCPAxis::atBottom));
    axis.append(ui->plotPos->axisRect()->axis(QCPAxis::atLeft));
    axis.append(ui->plotPos->axisRect()->axis(QCPAxis::atRight));
    ui->plotPos->setInteractions( QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes | QCP::iMultiSelect);
    ui->plotPos->addGraph();
    ui->plotPos->graph(0)->setPen(QPen(QColor(40, 110, 255)));
    ui->plotPos->addGraph(ui->plotPos->xAxis,ui->plotPos->yAxis2);
    ui->plotPos->graph(1)->setPen(QPen(QColor(255, 110, 40)));
    ui->plotPos->xAxis->setLabel("Time (in seconds)");
    ui->plotPos->yAxis->setLabel("y Axis");
    ui->plotPos->yAxis2->setLabel("y Axis 2");
    ui->plotPos->yAxis2->setVisible(true);
    ui->plotPos->yAxis->setLabelColor(QColor(40, 110, 255));
    ui->plotPos->yAxis2->setLabelColor(QColor(255, 110, 40));
    ui->plotPos->axisRect()->setRangeDragAxes(axis);
    ui->plotPos->axisRect()->setRangeZoomAxes(axis);

    Fichier.setFileName("C:/Users/PcInno/Documents/DonneesCapteur/HAHATFOU.csv");
    Fichier.open(QIODevice::WriteOnly);



    time = 0;
    dataLength = 9;
    erreur = 0;
    compteur = 0;
    z_latch = 0;


    timer.start();



    QTimer *dataTimer = new QTimer(this);
    connect(dataTimer, SIGNAL(timeout()), this, SLOT(updateLCD()));
    dataTimer->start(500);

    QTimer *dataTimer2 = new QTimer(this);
    connect(dataTimer2, SIGNAL(timeout()), this, SLOT(updatePlot()));
    dataTimer2->start(100);

    connect(SerialCom,SIGNAL(Send(QByteArray*)), this, SLOT(readSerial(QByteArray*)));
    //connect(dataTimer2, SIGNAL(timeout()), this, SLOT(readSerial()));
}

MainWindow::~MainWindow()
{
    delete ui;

}

void MainWindow::readSerial(QByteArray *dataR)
{

    SerialCom->ReadySend = false;
        //Fichier pour stocker les données au format CSV
    QTextStream out(&Fichier);
    if (ui->acquisition->isChecked() == true)
    {   //Récupération des données
        time++;

        if (dataR->size() > 8)
        {
            QByteArray dataTemp;
            dataTemp.append(*dataR);

        for (int compteurGeneral = 0; compteurGeneral  < dataTemp.size(); compteurGeneral+=9)
        {
            compteur++;
            if (compteur >= 100)
            {
                if (compteur == 100)
                    out << "COMPTEUR_GLOBAL;COMPTEUR_ERREURS;HEADER;FPGA_COUNTER;COS;SIN;SERIAL_AVAILABLE;ARDUINO_COUNTER;TIME_EXCHANGE;\n";
                else
                    out << "\n";
                out << float(compteur - 10)/10000 << ";" << erreur << ";";
                for (int z = compteurGeneral; z < compteurGeneral+9; z++)
                {

                    qDebug() << "Taille du compteur : " << compteurGeneral;
                    qDebug() << "Taille du vecteur  de la fonction: " << dataR->size();
                    qDebug() << "Taille du vecteur reçu : " << dataTemp.size();
                    if (z%9 < 2 || z%9 > 5)
                    {
                        out << quint8(dataTemp.at(z));
                        out << ";";
                    }
                    if (z%9 == 2)
                    {
                        int x = dataTemp.at(z) << 8; //Bits de poids fort
                        int y = dataTemp.at(z+1); //Bits de poids faible
                        int res1 = x | ( y & 0x00FF );
                        out << res1;
                        out << ";";
                        temps.append(double(compteur - 10)/10000);
                        ORD1.append(res1);

                    }
                    if (z%9 == 4)
                    {

                        int x2 = dataTemp.at(z) << 8; //Bits de poids fort
                        int y2 = dataTemp.at(z+1); //Bits de poids faible
                        int res2 = x2 | ( y2 & 0x00FF );
                        out << res2;
                        out << ";";
                        ORD2.append(res2);
                    }
                }

            }
        }
            dataTemp.clear();
        }
        //emit ReadFinished();
        SerialCom->ReadySend = true;
    }

}

void MainWindow::clearData()
{
    temps.clear();
    ORD1.clear();
    ORD2.clear();
}

void MainWindow::plot()
{
    ORD1bis = ORD1;
    ORD2bis = ORD2;

    if (ui->gain->isChecked() == true)
    {
        ORD1bis = multiply(ORD1bis,ui->gain_value->value());
        ORD2bis = multiply(ORD2bis,ui->gain_value->value());
    }

    if (ui->offset->isChecked() == true)
    {
        ORD1bis = add(ORD1bis,ui->offset_value->value());
        ORD2bis = add(ORD2bis,ui->offset_value->value());
    }

    ui->plotPos->graph(0)->addData(temps, ORD1bis);
    ui->plotPos->graph(1)->addData(temps, ORD2bis);
    ui->plotPos->replot();
    ui->plotPos->update();

    temps.clear();
    ORD1.clear();
    ORD2.clear();
    ORD1bis.clear();
    ORD2bis.clear();
}

void MainWindow::on_clear_clicked()
{
    clearData();
    plot();
}

void MainWindow::on_scale_clicked()
{
    if (ui->graph1->isChecked() == true && ui->graph2->isChecked() == false)
    {
       ui->plotPos->graph(0)->rescaleValueAxis();
       ui->plotPos->yAxis2->setRange(ui->plotPos->yAxis->range().lower,ui->plotPos->yAxis->range().upper);
    }

    else if (ui->graph1->isChecked() == false && ui->graph2->isChecked() == true)
    {
        ui->plotPos->graph(1)->rescaleValueAxis();
        ui->plotPos->yAxis->setRange(ui->plotPos->yAxis2->range().lower,ui->plotPos->yAxis2->range().upper);
    }

    else
    {
        ui->plotPos->graph(0)->rescaleValueAxis();
        ui->plotPos->graph(1)->rescaleValueAxis();
    }

    ui->plotPos->xAxis->setRange(*std::min_element(temps.constBegin(), temps.constEnd()),*std::max_element(temps.constBegin(), temps.constEnd()));
}

QVector<double> MainWindow::multiply(QVector<double> vec, double mult)
{
    QVector<double> result = vec;
    for (int i = 0; i < vec.size(); ++i)
        result.replace(i,vec[i]*mult);
    return result;
}

QVector<double> MainWindow::add(QVector<double> vec, double value)
{
    QVector<double> result = vec;
    for (int i = 0; i < vec.size(); ++i)
        result.replace(i,vec[i]+value);
    return result;
}

void MainWindow::addPoint1(double t,double cos)
{
    if (compteur < 1000)
    {
        temps.append(t);
        ORD1.append(cos);
    }
    else
    {
        temps.clear();
        ORD1.clear();
        temps.append(t);
        ORD1.append(cos);
    }
}

void MainWindow::addPoint2(double sin)
{
    if (compteur < 1000)
        ORD2.append(sin);
    else
    {
        //plot();
        ORD2.clear();
        ORD2.append(sin);
    }
}

void MainWindow::updateLCD()
{
        //Calcul de moyenne pour les 2 courbes
    if (ORD1bis.size() > ui->samplingBox1->value())
        for (int k = 1; k < ui->samplingBox1->value()+1; k++)
            mean1 = ORD1bis[ORD1bis.size()-k] + mean1;
    mean1 = mean1 / ui->samplingBox1->value();
    ui->ldcMean1->display(mean1);

    if (ORD2bis.size() > ui->samplingBox2->value())
        for (int k = 1; k < ui->samplingBox2->value()+1; k++)
            mean2 = ORD2bis[ORD2bis.size()-k] + mean2;
    mean2 = mean2 / ui->samplingBox2->value();
    ui->ldcMean2->display(mean2);


}

void MainWindow::updatePlot()
{
            //Zoom carré en appuyant sur Control
    if(QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier))
    {
        ui->plotPos->setSelectionRectMode(QCP::srmZoom);
    }
    else
        ui->plotPos->setSelectionRectMode(QCP::srmNone);

    //Cacher ou afficher les graphiques en fonction de si la case est cochée ou pas
    if(ui->graph1->isChecked() == true)
    {
        ui->plotPos->graph(0)->setVisible(true);
        ui->plotPos->yAxis->setLabelColor(QColor(40, 110, 255));
        ui->plotPos->yAxis->setLabel("y Axis");
    }
    else
    {
        ui->plotPos->graph(0)->setVisible(false);
        ui->plotPos->yAxis->setLabelColor(QColor(255, 110, 40));
        ui->plotPos->yAxis->setLabel("y Axis 2");
    }

    if(ui->graph2->isChecked() == true)
    {
        ui->plotPos->graph(1)->setVisible(true);
        ui->plotPos->yAxis2->setLabelColor(QColor(255, 110, 40));
        ui->plotPos->yAxis2->setLabel("y Axis 2");
    }
    else
    {
        ui->plotPos->graph(1)->setVisible(false);
        ui->plotPos->yAxis2->setLabelColor(QColor(40, 110, 255));
        ui->plotPos->yAxis2->setLabel("y Axis");
    }

    //Mise à l'échelle des axes automatiquement à l'aide de la fonction qui permet de le faire une seule fois
    if (ui->autoscale->isChecked())
    {
        on_scale_clicked();
        ui->plotPos->xAxis->setRange(timer.elapsed()/1000.0, ui->time_range->value(), Qt::AlignRight);
    }

    //Mise à l'échelle manuelle
    if (ui->manualscale->isChecked())
    {
        ui->plotPos->xAxis->setRange(ui->xleft->value(),ui->xright->value());
        ui->plotPos->yAxis->setRange(ui->yleft->value(),ui->yright->value());
        ui->plotPos->yAxis2->setRange(ui->yleft_2->value(),ui->yright_2->value());
    }

    plot();
}

и вот мой класс, который я вставил в рабочий поток:

SerialThread.h:

#ifndef SERIALTHREAD_H
#define SERIALTHREAD_H

#include <QSerialPort>
#include <QSerialPortInfo>
#include <QMessageBox>
#include <QDebug>
#include <QMutex>
#include <QMutexLocker>
#include <QSemaphore>
#include <QTimer>

class SerialThread : public QObject
{
    Q_OBJECT

public:
    SerialThread(QObject *parent = nullptr); //SerialThread(QSerialPort::BaudR, QSerialPort::dataBits,....)
    ~SerialThread();

    QSerialPort *Arduino;
    QByteArray dataR;
    bool ReadySend = true;



public slots:
    void SerialRecieve();
    void SerialSend();

signals:
    void Send(QByteArray *data);

private:



};

#endif // SERIALTHREAD_H

SerialThread. cpp:

#include "serialthread.h"

SerialThread::SerialThread(QObject *parent)
        :QObject(parent)
{


    Arduino = new QSerialPort(this);

    Arduino->setPortName("COM7");
    if (!Arduino->open(QSerialPort::ReadOnly))
        qDebug() << "FAIL";

    Arduino->setBaudRate(16000000);
    Arduino->setDataBits(QSerialPort::Data8);
    Arduino->setParity(QSerialPort::NoParity);
    Arduino->setStopBits(QSerialPort::OneStop);
    Arduino->setFlowControl(QSerialPort::NoFlowControl);

    connect(Arduino, &QSerialPort::readyRead, this, &SerialThread::SerialRecieve);

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(SerialSend()),Qt::QueuedConnection);
    timer->start(100);

}

SerialThread::~SerialThread()
{

}

void SerialThread::SerialRecieve()
{
    dataR.append(Arduino->readAll());
}

void SerialThread::SerialSend()
{
        emit Send(&dataR);
        dataR.clear();
}

Я попытался изменить момент отправки данных через Send СИГНАЛ, помещая в метод SerialRecieve (помещая условие на размер dataR), но это тоже не работает. 1023 *

Заранее спасибо!

...