Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

269 строки
6.8 KiB

  1. /*******************************
  2. * Copyright (C) 2023-, XINJE Electronic Co., Ltd.
  3. *
  4. * File Name: serialcommunicator.cpp
  5. * Description: 串口通信管理类实现文件
  6. * Others:
  7. * Version: 1.0
  8. * Author: lipengpeng
  9. * Date: 2025-7-23
  10. *******************************/
  11. #include "serialcommunicator.h"
  12. /**
  13. * @brief 构造函数
  14. * @param parent 父对象指针
  15. *
  16. * @details 初始化串口通信组件,包括:
  17. * 1. 创建串口对象
  18. * 2. 创建接收和重发定时器
  19. * 3. 设置默认超时参数
  20. * 4. 连接信号槽
  21. */
  22. SerialCommunicator::SerialCommunicator(QObject *parent) : QObject(parent)
  23. , serialPort(new QSerialPort(this))
  24. , recvTimer(new QTimer(this))
  25. , resendTimer(new QTimer(this))
  26. , comCount(0)
  27. , maxRetry(3)
  28. , recvTimeout(50)
  29. , resendTimeout(1000)
  30. {
  31. // 配置定时器(单次触发)
  32. recvTimer->setSingleShot(true);
  33. resendTimer->setSingleShot(true);
  34. // 连接信号槽
  35. connect(serialPort, &QSerialPort::readyRead, this, &SerialCommunicator::onReadyRead);
  36. connect(recvTimer, &QTimer::timeout, this, &SerialCommunicator::onRecvTimeout);
  37. connect(resendTimer, &QTimer::timeout, this, &SerialCommunicator::onResendTimeout);
  38. }
  39. /**
  40. * @brief 析构函数
  41. * @note 确保串口关闭并释放资源
  42. */
  43. SerialCommunicator::~SerialCommunicator()
  44. {
  45. close();
  46. }
  47. /**
  48. * @brief 设置串口参数
  49. * @param portName 串口号(如"COM1")
  50. * @param baudRate 波特率(如9600)
  51. * @param dataBits 数据位
  52. * @param parity 校验位
  53. * @param stopBits 停止位
  54. *
  55. * @note 此方法应在打开串口前调用,设置值将在下次open()时生效
  56. */
  57. void SerialCommunicator::setPortParams(const QString &portName, qint32 baudRate,
  58. QSerialPort::DataBits dataBits,
  59. QSerialPort::Parity parity,
  60. QSerialPort::StopBits stopBits)
  61. {
  62. this->portName = portName;
  63. this->baudRate = baudRate;
  64. this->dataBits = dataBits;
  65. this->parity = parity;
  66. this->stopBits = stopBits;
  67. }
  68. /**
  69. * @brief 打开串口
  70. * @return true-打开成功, false-打开失败
  71. *
  72. * @details 执行流程:
  73. * 1. 关闭已打开的串口(如果存在)
  74. * 2. 配置串口参数(波特率、数据位等)
  75. * 3. 尝试打开串口
  76. * 4. 发送状态变化信号
  77. */
  78. bool SerialCommunicator::open()
  79. {
  80. // 关闭已打开的串口(如果存在)
  81. if (serialPort->isOpen())
  82. {
  83. serialPort->close();
  84. }
  85. // 配置串口参数
  86. serialPort->setPortName(portName);
  87. if (!serialPort->setBaudRate(baudRate))
  88. {
  89. emit statusChanged("波特率设置失败");
  90. return false;
  91. }
  92. if (!serialPort->setDataBits(dataBits))
  93. {
  94. emit statusChanged("数据位设置失败");
  95. return false;
  96. }
  97. if (!serialPort->setParity(parity))
  98. {
  99. emit statusChanged("校验位设置失败");
  100. return false;
  101. }
  102. if (!serialPort->setStopBits(stopBits))
  103. {
  104. emit statusChanged("停止位设置失败");
  105. return false;
  106. }
  107. serialPort->setFlowControl(QSerialPort::NoFlowControl); // 无流控制
  108. // 打开串口
  109. if (serialPort->open(QIODevice::ReadWrite))
  110. {
  111. emit statusChanged("串口连接成功");
  112. return true;
  113. }
  114. else
  115. {
  116. emit statusChanged("串口连接失败,请检查参数");
  117. return false;
  118. }
  119. }
  120. /**
  121. * @brief 关闭串口
  122. *
  123. * @details 执行流程:
  124. * 1. 检查串口是否打开
  125. * 2. 关闭串口
  126. * 3. 发送状态变化信号
  127. */
  128. void SerialCommunicator::close()
  129. {
  130. if (serialPort->isOpen())
  131. {
  132. serialPort->close();
  133. emit statusChanged("串口断开");
  134. }
  135. }
  136. /**
  137. * @brief 发送数据并启动超时重发机制
  138. * @param data 待发送的数据
  139. *
  140. * @details 执行流程:
  141. * 1. 检查串口是否打开
  142. * 2. 保存待发送数据(用于重发)
  143. * 3. 重置重发计数器
  144. * 4. 发送数据
  145. * 5. 启动重发超时定时器
  146. */
  147. void SerialCommunicator::sendData(const QByteArray &data)
  148. {
  149. if (!serialPort->isOpen())
  150. {
  151. emit statusChanged("串口未打开,无法发送数据");
  152. return;
  153. }
  154. // 保存待重发数据,初始化计数器,启动重发定时器
  155. currentData = data;
  156. comCount = 0;
  157. serialPort->write(data); // 发送数据
  158. emit statusChanged("发送报文:" + data.toHex().toUpper());
  159. resendTimer->start(resendTimeout); // 启动超时计时
  160. }
  161. /**
  162. * @brief 设置最大重发次数
  163. * @param count 最大重发次数
  164. */
  165. void SerialCommunicator::setMaxRetry(int count)
  166. {
  167. maxRetry = count;
  168. }
  169. /**
  170. * @brief 设置接收完成超时时间
  171. * @param ms 超时时间(毫秒)
  172. *
  173. * @note 当串口持续ms毫秒无新数据时,认为接收完成
  174. */
  175. void SerialCommunicator::setRecvTimeout(int ms)
  176. {
  177. recvTimeout = ms;
  178. }
  179. /**
  180. * @brief 设置重发间隔时间
  181. * @param ms 重发间隔(毫秒)
  182. */
  183. void SerialCommunicator::setResendTimeout(int ms)
  184. {
  185. resendTimeout = ms;
  186. }
  187. /**
  188. * @brief 检查串口是否打开
  189. * @return true-已打开, false-已关闭
  190. */
  191. bool SerialCommunicator::isOpen() const
  192. {
  193. return serialPort->isOpen();
  194. }
  195. // ====================== 私有槽函数 ====================== //
  196. /**
  197. * @brief 串口数据到达处理
  198. *
  199. * @details 执行流程:
  200. * 1. 读取所有可用数据到接收缓冲区
  201. * 2. 重启接收超时定时器
  202. * 3. 定时器超时后将触发onRecvTimeout
  203. */
  204. void SerialCommunicator::onReadyRead()
  205. {
  206. // 读取数据到缓冲区
  207. recvBuffer.append(serialPort->readAll());
  208. //重置接收超时定时器
  209. recvTimer->start(recvTimeout);
  210. }
  211. /**
  212. * @brief 接收完成超时处理
  213. *
  214. * @details 执行流程:
  215. * 1. 发送dataReceived信号包含完整数据
  216. * 2. 清空接收缓冲区
  217. * 3. 停止重发定时器(表示已收到响应)
  218. */
  219. void SerialCommunicator::onRecvTimeout()
  220. {
  221. // 接收完成,发送完整数据信号,停止重发定时器
  222. emit dataReceived(recvBuffer);
  223. recvBuffer.clear();
  224. resendTimer->stop();
  225. }
  226. /**
  227. * @brief 重发超时处理
  228. *
  229. * @details 执行流程:
  230. * 1. 检查是否达到最大重发次数
  231. * 2. 未达到则重发数据并更新计数器
  232. * 3. 达到最大次数则触发超时信号
  233. */
  234. void SerialCommunicator::onResendTimeout()
  235. {
  236. if (comCount < maxRetry)
  237. {
  238. // 重发数据,更新计数器
  239. serialPort->write(currentData);
  240. comCount++;
  241. emit statusChanged(QString("通信超时,重新发送中(%1/%2)").arg(comCount).arg(maxRetry));
  242. resendTimer->start(resendTimeout); // 继续计时
  243. }
  244. else
  245. {
  246. // 达到最大重发次数,触发超时信号
  247. resendTimer->stop();
  248. emit timeoutOccurred();
  249. }
  250. }