实现了创建线程使用串口的功能
具备功能:
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,以允许moveToThread
m_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 的,但逻辑上是 const
QMetaObject::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());
}
}