/******************************* * 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(); } }