|
- /*******************************
- * Copyright (C) 2023-, XINJE Electronic Co., Ltd.
- *
- * File Name: serialcommunicator.cpp
- * Description: 串口通信管理类实现文件
- * Others:
- * Version: 1.0
- * Author: lipengpeng
- * Date: 2025-7-23
- *******************************/
-
- #include "serialcommunicator.h"
-
- /**
- * @brief 构造函数
- * @param parent 父对象指针
- *
- * @details 初始化串口通信组件,包括:
- * 1. 创建串口对象
- * 2. 创建接收和重发定时器
- * 3. 设置默认超时参数
- * 4. 连接信号槽
- */
- SerialCommunicator::SerialCommunicator(QObject *parent) : QObject(parent)
- , serialPort(new QSerialPort(this))
- , recvTimer(new QTimer(this))
- , resendTimer(new QTimer(this))
- , comCount(0)
- , maxRetry(3)
- , recvTimeout(50)
- , resendTimeout(1000)
- {
- // 配置定时器(单次触发)
- recvTimer->setSingleShot(true);
- resendTimer->setSingleShot(true);
-
- // 连接信号槽
- connect(serialPort, &QSerialPort::readyRead, this, &SerialCommunicator::onReadyRead);
- connect(recvTimer, &QTimer::timeout, this, &SerialCommunicator::onRecvTimeout);
- connect(resendTimer, &QTimer::timeout, this, &SerialCommunicator::onResendTimeout);
- }
-
- /**
- * @brief 析构函数
- * @note 确保串口关闭并释放资源
- */
- SerialCommunicator::~SerialCommunicator()
- {
- close();
- }
-
- /**
- * @brief 设置串口参数
- * @param portName 串口号(如"COM1")
- * @param baudRate 波特率(如9600)
- * @param dataBits 数据位
- * @param parity 校验位
- * @param stopBits 停止位
- *
- * @note 此方法应在打开串口前调用,设置值将在下次open()时生效
- */
- void SerialCommunicator::setPortParams(const QString &portName, qint32 baudRate,
- QSerialPort::DataBits dataBits,
- QSerialPort::Parity parity,
- QSerialPort::StopBits stopBits)
- {
- this->portName = portName;
- this->baudRate = baudRate;
- this->dataBits = dataBits;
- this->parity = parity;
- this->stopBits = stopBits;
- }
-
- /**
- * @brief 打开串口
- * @return true-打开成功, false-打开失败
- *
- * @details 执行流程:
- * 1. 关闭已打开的串口(如果存在)
- * 2. 配置串口参数(波特率、数据位等)
- * 3. 尝试打开串口
- * 4. 发送状态变化信号
- */
- bool SerialCommunicator::open()
- {
- // 关闭已打开的串口(如果存在)
- if (serialPort->isOpen())
- {
- serialPort->close();
- }
-
- // 配置串口参数
- serialPort->setPortName(portName);
- if (!serialPort->setBaudRate(baudRate))
- {
- emit statusChanged("波特率设置失败");
- return false;
- }
- if (!serialPort->setDataBits(dataBits))
- {
- emit statusChanged("数据位设置失败");
- return false;
- }
- if (!serialPort->setParity(parity))
- {
- emit statusChanged("校验位设置失败");
- return false;
- }
- if (!serialPort->setStopBits(stopBits))
- {
- emit statusChanged("停止位设置失败");
- return false;
- }
- serialPort->setFlowControl(QSerialPort::NoFlowControl); // 无流控制
-
- // 打开串口
- if (serialPort->open(QIODevice::ReadWrite))
- {
- emit statusChanged("串口连接成功");
- return true;
- }
- else
- {
- emit statusChanged("串口连接失败,请检查参数");
- return false;
- }
- }
-
- /**
- * @brief 关闭串口
- *
- * @details 执行流程:
- * 1. 检查串口是否打开
- * 2. 关闭串口
- * 3. 发送状态变化信号
- */
- void SerialCommunicator::close()
- {
- if (serialPort->isOpen())
- {
- serialPort->close();
- emit statusChanged("串口断开");
- }
- }
-
- /**
- * @brief 发送数据并启动超时重发机制
- * @param data 待发送的数据
- *
- * @details 执行流程:
- * 1. 检查串口是否打开
- * 2. 保存待发送数据(用于重发)
- * 3. 重置重发计数器
- * 4. 发送数据
- * 5. 启动重发超时定时器
- */
- void SerialCommunicator::sendData(const QByteArray &data)
- {
- if (!serialPort->isOpen())
- {
- emit statusChanged("串口未打开,无法发送数据");
- return;
- }
- // 保存待重发数据,初始化计数器,启动重发定时器
- currentData = data;
- comCount = 0;
- serialPort->write(data); // 发送数据
- emit statusChanged("发送报文:" + data.toHex().toUpper());
- resendTimer->start(resendTimeout); // 启动超时计时
- }
-
- /**
- * @brief 设置最大重发次数
- * @param count 最大重发次数
- */
- void SerialCommunicator::setMaxRetry(int count)
- {
- maxRetry = count;
- }
-
- /**
- * @brief 设置接收完成超时时间
- * @param ms 超时时间(毫秒)
- *
- * @note 当串口持续ms毫秒无新数据时,认为接收完成
- */
- void SerialCommunicator::setRecvTimeout(int ms)
- {
- recvTimeout = ms;
- }
-
- /**
- * @brief 设置重发间隔时间
- * @param ms 重发间隔(毫秒)
- */
- void SerialCommunicator::setResendTimeout(int ms)
- {
- resendTimeout = ms;
- }
-
- /**
- * @brief 检查串口是否打开
- * @return true-已打开, false-已关闭
- */
- bool SerialCommunicator::isOpen() const
- {
- return serialPort->isOpen();
- }
-
- // ====================== 私有槽函数 ====================== //
-
- /**
- * @brief 串口数据到达处理
- *
- * @details 执行流程:
- * 1. 读取所有可用数据到接收缓冲区
- * 2. 重启接收超时定时器
- * 3. 定时器超时后将触发onRecvTimeout
- */
- void SerialCommunicator::onReadyRead()
- {
- // 读取数据到缓冲区
- recvBuffer.append(serialPort->readAll());
- //重置接收超时定时器
- recvTimer->start(recvTimeout);
- }
-
- /**
- * @brief 接收完成超时处理
- *
- * @details 执行流程:
- * 1. 发送dataReceived信号包含完整数据
- * 2. 清空接收缓冲区
- * 3. 停止重发定时器(表示已收到响应)
- */
- void SerialCommunicator::onRecvTimeout()
- {
- // 接收完成,发送完整数据信号,停止重发定时器
- emit dataReceived(recvBuffer);
- recvBuffer.clear();
- resendTimer->stop();
- }
-
- /**
- * @brief 重发超时处理
- *
- * @details 执行流程:
- * 1. 检查是否达到最大重发次数
- * 2. 未达到则重发数据并更新计数器
- * 3. 达到最大次数则触发超时信号
- */
- void SerialCommunicator::onResendTimeout()
- {
- if (comCount < maxRetry)
- {
- // 重发数据,更新计数器
- serialPort->write(currentData);
- comCount++;
- emit statusChanged(QString("通信超时,重新发送中(%1/%2)").arg(comCount).arg(maxRetry));
- resendTimer->start(resendTimeout); // 继续计时
- }
- else
- {
- // 达到最大重发次数,触发超时信号
- resendTimer->stop();
- emit timeoutOccurred();
- }
- }
|