實現了創建線程使用串口的功能
具備功能:
1.線程使用串口
2.定時發送隊列內容,防止粘包
3.沒處理接收粘包,根據你的需求來,handleReadyRead函數中,可以通過m_receiveBuffer來緩存接收,然后拆分數據來處理
源碼
serialportmanager.h
#ifndef SERIALPORTMANAGER_H
#define SERIALPORTMANAGER_H#include <QObject>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QObject>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QThread> // 添加 QThread 頭文件
#include <QMetaObject> // 添加 QMetaObject 頭文件
#include <QQueue> // 添加 QQueue 頭文件
#include <QTimer> // 添加 QTimer 頭文件class SerialPortManager : public QObject
{Q_OBJECT
public:explicit SerialPortManager(QObject *parent = nullptr);~SerialPortManager();// 打開串口 (調用將在工作線程執行)Q_INVOKABLE bool openPort(const QString &portName, int baudRate, QSerialPort::DataBits dataBits = QSerialPort::Data8,QSerialPort::Parity parity = QSerialPort::NoParity, QSerialPort::StopBits stopBits = QSerialPort::OneStop);// 關閉串口 (調用將在工作線程執行)Q_INVOKABLE void closePort();// 寫入數據 (調用將在工作線程執行)Q_INVOKABLE qint64 writeData(const QByteArray &data);Q_INVOKABLE qint64 writeString(const QString &str);//帶參數的Q_INVOKABLE qint64 writeCommnd(const QString &baseCommad,QStringList strParas);// 檢查串口是否打開 (調用將在工作線程執行)Q_INVOKABLE bool isOpen();// 獲取可用串口列表 (靜態方法,主線程安全)static QStringList availablePorts();signals:void dataReceived(const QByteArray &data);void portError(const QString &errorDescription);void portOpened();void portClosed();// 內部信號,用于觸發工作線程中的初始化void initSerialPortSignal();public slots:void _closePortSlot();
private slots:// 這些槽函數將在工作線程中執行void _openPortSlot(const QString &portName, int baudRate, QSerialPort::DataBits dataBits,QSerialPort::Parity parity, QSerialPort::StopBits stopBits, bool *result);void _writeDataSlot(const QByteArray &data, qint64 *result);void _isOpenSlot(bool *result) const;void handleReadyRead();void handleError(QSerialPort::SerialPortError error);void _initSerialPort(); // 在工作線程中初始化串口對象void _cleanupSlot(); // 在工作線程退出前執行清理void sendDataFromQueue(); // 定時器超時槽函數,發送隊列中的數據private:QSerialPort *m_serialPort; // 將在工作線程中創建和使用QThread *m_workerThread; // 串口操作的工作線程QQueue<QByteArray> m_dataQueue; // 數據發送隊列QTimer *m_sendTimer; // 發送定時器QByteArray m_receiveBuffer; // 接收數據緩沖區
};#endif // SERIALPORTMANAGER_H
serialportmanager.cpp
#include "serialportmanager.h"
#include <QDebug> // 用于調試SerialPortManager::SerialPortManager(QObject *parent) : QObject(nullptr), // 關鍵: 初始化時設置父對象為nullptr,以允許moveToThreadm_serialPort(nullptr), // 初始化為 nullptr,將在工作線程中創建m_workerThread(new QThread()) // m_workerThread 創建時不指定父對象,其生命周期將單獨管理
{Q_UNUSED(parent); // 如果傳遞了parent,明確表示它在這里沒有被使用來設置父子關系// 將 SerialPortManager 對象本身移動到工作線程this->moveToThread(m_workerThread);// 連接信號以在工作線程啟動后初始化串口connect(m_workerThread, &QThread::started, this, &SerialPortManager::_initSerialPort);// 連接信號以在工作線程結束時自動刪除 QThread 對象connect(m_workerThread, &QThread::finished, m_workerThread, &QObject::deleteLater);// 啟動工作線程m_workerThread->start();
}SerialPortManager::~SerialPortManager()
{if (m_workerThread->isRunning()) {// 連接 cleanupSlot 以在線程完成前執行清理// 使用 Qt::DirectConnection 確保 _cleanupSlot 在 m_workerThread 中執行connect(m_workerThread, &QThread::finished, this, &SerialPortManager::_cleanupSlot, Qt::DirectConnection);// 請求退出線程m_workerThread->quit();// 等待線程結束,m_workerThread 會通過之前連接的 deleteLater 自行刪除if (!m_workerThread->wait(5000)) { // 增加等待時間以確保清理完成qWarning() << u8"SerialPortManager: 工作線程未及時完成,嘗試終止。";m_workerThread->terminate(); // 強制終止作為最后手段m_workerThread->wait(); // 等待終止完成}qDebug() << u8"SerialPortManager: 工作線程已完成。";} else {// 如果線程沒有運行,但 m_workerThread 對象仍然存在,也應該安排刪除// (雖然理論上 connect m_workerThread finished to deleteLater 應該處理了,但作為安全措施)if (m_workerThread) {m_workerThread->deleteLater();}}qDebug() << u8"SerialPortManager 已銷毀。";
}void SerialPortManager::_cleanupSlot()
{if (m_serialPort && m_serialPort->isOpen()) {m_serialPort->close();qDebug() << u8"串口在 _cleanupSlot 中關閉。";}
}void SerialPortManager::_initSerialPort()
{// 這個函數在 m_workerThread 中執行m_serialPort = new QSerialPort(this); // parent 為 this,確保隨 SerialPortManager 銷毀connect(m_serialPort, &QSerialPort::readyRead, this, &SerialPortManager::handleReadyRead);connect(m_serialPort, &QSerialPort::errorOccurred, this, &SerialPortManager::handleError);m_sendTimer = new QTimer(this); // parent 為 this,確保隨 SerialPortManager 銷毀connect(m_sendTimer, &QTimer::timeout, this, &SerialPortManager::sendDataFromQueue);m_sendTimer->start(100); // 0.1秒 (100毫秒) 執行一次qDebug() << u8"SerialPortManager 在線程中初始化:" << QThread::currentThreadId();
}bool SerialPortManager::openPort(const QString &portName, int baudRate, QSerialPort::DataBits dataBits,QSerialPort::Parity parity, QSerialPort::StopBits stopBits)
{if (QThread::currentThread() == m_workerThread) {// 如果已經在工作線程,直接調用bool result = false;_openPortSlot(portName, baudRate, dataBits, parity, stopBits, &result);return result;}bool result = false;// 使用 BlockingQueuedConnection 確保調用完成并獲取結果QMetaObject::invokeMethod(this, "_openPortSlot", Qt::BlockingQueuedConnection,Q_ARG(QString, portName),Q_ARG(int, baudRate),Q_ARG(QSerialPort::DataBits, dataBits),Q_ARG(QSerialPort::Parity, parity),Q_ARG(QSerialPort::StopBits, stopBits),Q_ARG(bool*, &result));return result;
}void SerialPortManager::_openPortSlot(const QString &portName, int baudRate, QSerialPort::DataBits dataBits,QSerialPort::Parity parity, QSerialPort::StopBits stopBits, bool *result)
{if (!m_serialPort) {*result = false;emit portError(u8"串口對象未初始化。");return;}if (m_serialPort->isOpen()) {m_serialPort->close();}m_serialPort->setPortName(portName);m_serialPort->setBaudRate(baudRate);m_serialPort->setDataBits(dataBits);m_serialPort->setParity(parity);m_serialPort->setStopBits(stopBits);m_serialPort->setFlowControl(QSerialPort::NoFlowControl);if (m_serialPort->open(QIODevice::ReadWrite)) {emit portOpened();*result = true;} else {emit portError(m_serialPort->errorString());*result = false;}
}void SerialPortManager::sendDataFromQueue()
{if (m_serialPort && m_serialPort->isOpen() && !m_dataQueue.isEmpty()) {QByteArray dataToSend = m_dataQueue.dequeue();qDebug() << u8"從隊列發送數據:" << dataToSend;m_serialPort->write(dataToSend);}
}void SerialPortManager::closePort()
{if (QThread::currentThread() == m_workerThread) {_closePortSlot();return;}QMetaObject::invokeMethod(this, "_closePortSlot", Qt::QueuedConnection);}void SerialPortManager::_closePortSlot()
{m_dataQueue.clear(); // 清空隊列中的剩余數據if (m_serialPort && m_serialPort->isOpen()) {m_serialPort->close();emit portClosed();}
}qint64 SerialPortManager::writeData(const QByteArray &data)
{if (QThread::currentThread() == m_workerThread) {qint64 result = -1;_writeDataSlot(data, &result);return result;}qint64 result = -1;QMetaObject::invokeMethod(this, "_writeDataSlot", Qt::BlockingQueuedConnection,Q_ARG(QByteArray, data),Q_ARG(qint64*, &result));return result;
}qint64 SerialPortManager::writeString(const QString &str)
{return writeData(str.toUtf8());
}qint64 SerialPortManager::writeCommnd(const QString &baseCommad, QStringList strParas)
{QString strSendData = "<";strSendData+=baseCommad;for(QString para : strParas){strSendData+=" ";strSendData +=para;}strSendData +=">";return writeString(strSendData);
}void SerialPortManager::_writeDataSlot(const QByteArray &data, qint64 *result)
{// 將數據添加到隊列,而不是直接發送m_dataQueue.enqueue(data);qDebug() << u8"數據已添加到發送隊列,當前隊列大小:" << m_dataQueue.size();*result = data.size(); // 返回添加到隊列的數據大小
}bool SerialPortManager::isOpen()
{if (QThread::currentThread() == m_workerThread) {bool localResult = false;_isOpenSlot(&localResult);return localResult;}bool result = false;// 使用 const_cast 是因為 invokeMethod 的槽函數參數不能是 const 的,但邏輯上是 constQMetaObject::invokeMethod(const_cast<SerialPortManager*>(this), "_isOpenSlot", Qt::BlockingQueuedConnection,Q_ARG(bool*, &result));return result;
}void SerialPortManager::_isOpenSlot(bool *result) const
{if (m_serialPort) {*result = m_serialPort->isOpen();} else {*result = false;}
}QStringList SerialPortManager::availablePorts()
{QStringList portNames;const auto infos = QSerialPortInfo::availablePorts();for (const QSerialPortInfo &info : infos) {portNames.append(info.portName());}return portNames;
}void SerialPortManager::handleReadyRead()
{if (!m_serialPort || !m_serialPort->isOpen() || !m_serialPort->bytesAvailable())return;QByteArray data = m_serialPort->readAll();if (data.isEmpty()) {return;}emit dataReceived(data);}
}void SerialPortManager::handleError(QSerialPort::SerialPortError error)
{if (!m_serialPort) return;// NoError 也是一個 error 枚舉值,但通常我們只關心實際的錯誤if (error != QSerialPort::NoError) {emit portError(m_serialPort->errorString());}
}