У меня проблема с корректной работой 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 *
Заранее спасибо!