【嵌入式——QT】QT多线程编程

发布于:2024-12-21 ⋅ 阅读:(6) ⋅ 点赞:(0)

这里介绍的是moveToThread的方式,继承QThread的方式可以参考我的另一篇文章【嵌入式——QT】QThread创建多线程

编程实现

首先创建一个类,但是这个类一定要继承QObject
SerialWorker.h

#ifndef SERIALWORKER_H
#define SERIALWORKER_H

#include <QObject>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QThread>
#include <QQueue>
#include <QTimer>

class SerialWorker : public QObject
{
    Q_OBJECT
public:
    SerialWorker(QObject *parent = nullptr);
    ~SerialWorker();

    void setProperties(qint32 baudRate, QSerialPort::DataBits dataBits,
                       QSerialPort::Parity parity, QSerialPort::StopBits stopBits,
                       QSerialPort::FlowControl flowControl);

    void setPortName(QString name);

    void setStop(bool isStop = true);

public slots:
    void openSerialPort();

    void closeSerialPort();

    void writeData(const QByteArray &data);

    void readData();

    void handleSerialError(QSerialPort::SerialPortError error);

    QByteArray readDataDelay();

    void process();

    void handleReadTimeout();

signals:
    void dataReceived(const QByteArray &data, QString curCmd);
    void errorOccurred(const QString &error);

public:
    QQueue<QString> cmdQueue;
    QTimer *qtimer = NULL;
    QTimer *readTimeout = NULL;

private:
    QSerialPort *serialPort = NULL;

    QString portName;

    QByteArray sumData;

    QString curCmd;

    bool isStop = false;
};

#endif // SERIALWORKER_H

SerialWorker.cpp

#include "SerialWorker.h"
#include <QDebug>


//指令头
#define PROTOCOL_STX 0xFF
//指令尾
#define PROTOCOL_ETX 0xFE

SerialWorker::SerialWorker(QObject *parent)
    : QObject(parent)
{
    serialPort = new QSerialPort(this);
    connect(serialPort, &QSerialPort::readyRead, this, &SerialWorker::readData);
    connect(serialPort, &QSerialPort::errorOccurred, this, &SerialWorker::handleSerialError);

    qtimer = new QTimer(this);
    connect(qtimer, &QTimer::timeout, this, &SerialWorker::process);
    qtimer->start(100);

    readTimeout = new QTimer(this);
    readTimeout->setSingleShot(true); // 只触发一次超时
    connect(readTimeout, &QTimer::timeout, this, &SerialWorker::handleReadTimeout);


}

SerialWorker::~SerialWorker()
{
    if (serialPort->isOpen()) {
        serialPort->close();
    }
    delete serialPort;
    delete qtimer;
    delete readTimeout;
}

void SerialWorker::setProperties(qint32 baudRate, QSerialPort::DataBits dataBits, QSerialPort::Parity parity,
                                 QSerialPort::StopBits stopBits, QSerialPort::FlowControl flowControl)
{
    serialPort->setBaudRate(baudRate);
    serialPort->setDataBits(dataBits);
    serialPort->setParity(parity);
    serialPort->setStopBits(stopBits);
    serialPort->setFlowControl(flowControl);

}

void SerialWorker::setPortName(QString name)
{
    this->portName = name;
}

void SerialWorker::process()
{
    if (!serialPort->isOpen()) {
        qDebug() << "Serial port is not open!";
        return;
    }

    // qDebug() << "process";
    // while(!isStop) {
    //     QThread::msleep(500);
    if(!cmdQueue.isEmpty()) {
        QString cmd = cmdQueue.dequeue();
        curCmd = cmd;
        writeData(cmd.toUtf8());
    } else {
        cmdQueue.enqueue("1 sts\n");
        // QThread::msleep(100);
        cmdQueue.enqueue("2 sts\n");
    }

    // }
}

void SerialWorker::handleReadTimeout()
{
    emit errorOccurred("Read data error");
    cmdQueue.clear();
}

void SerialWorker::setStop(bool isStop)
{
    this->isStop = isStop;
}

void SerialWorker::openSerialPort()
{
    serialPort->setPortName(portName);
    if (!serialPort->open(QIODevice::ReadWrite)) {
        qDebug() << "Serial port faile";
    } else {
        qDebug() << "Serial port opened";
    }
}

void SerialWorker::closeSerialPort()
{
    if (serialPort->isOpen()) {
        serialPort->close();
        qDebug() << "Serial port closed";
    }
}

void SerialWorker::writeData(const QByteArray &data)
{
    // qDebug() << "writeData" << QThread::currentThreadId();

    if (serialPort->isOpen()) {
        if(data.contains("1 sts\n") || data.contains("2 sts\n")) {

        } else {
            qDebug() << "writeData" << data;
        }

        if(serialPort->write(data) > 0) {
            readTimeout->start(2000);
        }

    }
}

void SerialWorker::readData()
{
    // qDebug() << "readData" << QThread::currentThreadId();
    QByteArray recvData = serialPort->readAll();
    // qDebug() << "recvData" << recvData;
    if (!recvData.isEmpty()) {
        sumData.append(recvData);

        unsigned char firstByte = static_cast<unsigned char>(sumData[0]);
        unsigned char lastByte = static_cast<unsigned char>(sumData[sumData.size() - 1]);
        //判断指令头指令尾是否正确
        if (lastByte == PROTOCOL_ETX && firstByte == PROTOCOL_STX) {
            if (readTimeout->isActive()) {
                readTimeout->stop(); // 停止超时定时器
            }
            // qDebug() << "sumData" << sumData.toHex().toUpper();
            emit dataReceived(sumData, curCmd);
            sumData.clear();


        }
    }


}

/*
QSerialPort::ResourceError: 串口设备意外断开。
QSerialPort::DeviceNotFoundError: 找不到设备(通常在尝试打开不存在的串口时)。
QSerialPort::ReadError: 读取数据时发生错误。
QSerialPort::WriteError: 写入数据时发生错误。
*/
void SerialWorker::handleSerialError(QSerialPort::SerialPortError error)
{
    qDebug() << "error" << error;
    if (error == QSerialPort::ResourceError) {
        // qDebug() << "Serial port disconnected!";
        emit errorOccurred("Serial port disconnected");
        closeSerialPort();  // 关闭串口,清理资源
    } else if(error == QSerialPort::DeviceNotFoundError) {
        emit errorOccurred("Device not found");
    } else if(error == QSerialPort::ReadError) {
        emit errorOccurred("Read data error");
    } else if(error == QSerialPort::WriteError) {
        emit errorOccurred("Write data error");
    }



}

QByteArray SerialWorker::readDataDelay()
{
    int retryCount = 0;
    QByteArray data;
    while(retryCount <= 10) {
        if(serialPort->waitForReadyRead(500)) {
            QByteArray recvData = serialPort->readAll();
            if (!recvData.isEmpty()) {
                data.append(recvData);

                unsigned char firstByte = static_cast<unsigned char>(data[0]);
                unsigned char lastByte = static_cast<unsigned char>(data[data.size() - 1]);
                //判断指令头指令尾是否正确
                if (lastByte == PROTOCOL_ETX && firstByte == PROTOCOL_STX) {
                    qDebug() << "data" << data.toHex().toUpper();
                    return data;
                }
            }
        } else {
            retryCount++;
        }
    }

    return QByteArray();
}

在主线程中创建线程实例

QThread *workerThread = new QThread(this);

将我们上面创建的类移动到线程中去执行,这样之后在这个类中执行的所有方法,都将是在子线程中执行的,主线程和子线程之间可以通过信号槽的方式来进行通信。

serialWorker->moveToThread(workerThread);

当然将这个工作类移动到子线程之后,还是需要将这个线程启动的,需要调用QThread的start函数来进行启动,表示这个线程启动了,你之后调用的方法都是在这个子线程中执行的。

workerThread->start();

也可以在子线程中编写死循环一直使其去执行,然后绑定个信号,在子线程启动的时候触发这个槽函数

QThread *workerThread = new QThread(this);
connect(workerThread, &QThread::started, serialWorker, &SerialWorker::process);

//子线程槽函数
void SerialWorker::process()
{

    while(!isStop) {
      QThread::msleep(500);
         
      //逻辑处理
    }
}

其实这种写法在子线程中编写死循环是存在问题的,会导致其他的函数在主线程中无法调用,所以这种方式最好不要使用死循环的方式来实现,如果想要编写死循环的话,可以在按照同样的方式重新创建一个线程,或者使用继承QThread的方式来实现。