Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 

373 Zeilen
9.8 KiB

  1. /*******************************
  2. * Copyright (C) 2025.
  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. connect(serialPort_, SIGNAL(error(QSerialPort::SerialPortError)),
  39. this, SLOT(handleSerialError(QSerialPort::SerialPortError)));
  40. }
  41. /**
  42. * @brief 析构函数
  43. * @note 确保串口关闭并释放资源
  44. */
  45. SerialCommunicator::~SerialCommunicator()
  46. {
  47. //停止所有定时器,避免在对象销毁后触发定时器事件
  48. if (recvTimer_->isActive())
  49. {
  50. recvTimer_->stop();
  51. }
  52. if (resendTimer_->isActive())
  53. {
  54. resendTimer_->stop();
  55. }
  56. close();
  57. delete recvTimer_;
  58. delete resendTimer_;
  59. delete serialPort_;
  60. }
  61. /**
  62. * @brief 设置串口参数
  63. * @param portName 串口号(如"COM1")
  64. * @param baudRate 波特率(如9600)
  65. * @param dataBits 数据位
  66. * @param parity 校验位
  67. * @param stopBits 停止位
  68. *
  69. * @note 此方法应在打开串口前调用,设置值将在下次open()时生效
  70. */
  71. void SerialCommunicator::setPortParams(QString portName, qint32 baudRate,
  72. QSerialPort::DataBits dataBits,
  73. QSerialPort::Parity parity,
  74. QSerialPort::StopBits stopBits)
  75. {
  76. this->portName_ = portName;
  77. this->baudRate_ = baudRate;
  78. this->dataBits_ = dataBits;
  79. this->parity_ = parity;
  80. this->stopBits_ = stopBits;
  81. }
  82. /**
  83. * @brief 打开串口
  84. * @return true-打开成功, false-打开失败
  85. *
  86. * @details 执行流程:
  87. * 1. 关闭已打开的串口(如果存在)
  88. * 2. 配置串口参数(波特率、数据位等)
  89. * 3. 尝试打开串口
  90. * 4. 发送状态变化信号
  91. */
  92. bool SerialCommunicator::open()
  93. {
  94. // 关闭已打开的串口(如果存在)
  95. if (serialPort_->isOpen())
  96. {
  97. serialPort_->close();
  98. }
  99. // 配置串口参数
  100. serialPort_->setPortName(portName_);
  101. if (!serialPort_->setBaudRate(baudRate_))
  102. {
  103. emit statusChanged("波特率设置失败");
  104. return false;
  105. }
  106. if (!serialPort_->setDataBits(dataBits_))
  107. {
  108. emit statusChanged("数据位设置失败");
  109. return false;
  110. }
  111. if (!serialPort_->setParity(parity_))
  112. {
  113. emit statusChanged("校验位设置失败");
  114. return false;
  115. }
  116. if (!serialPort_->setStopBits(stopBits_))
  117. {
  118. emit statusChanged("停止位设置失败");
  119. return false;
  120. }
  121. serialPort_->setFlowControl(QSerialPort::NoFlowControl); // 无流控制
  122. // 打开串口
  123. if (serialPort_->open(QIODevice::ReadWrite))
  124. {
  125. emit statusChanged("串口连接成功");
  126. return true;
  127. }
  128. else
  129. {
  130. emit statusChanged("串口连接失败,请检查参数");
  131. return false;
  132. }
  133. }
  134. /**
  135. * @brief 关闭串口
  136. *
  137. * @details 执行流程:
  138. * 1. 检查串口是否打开
  139. * 2. 关闭串口
  140. * 3. 发送状态变化信号
  141. */
  142. void SerialCommunicator::close()
  143. {
  144. if (serialPort_->isOpen())
  145. {
  146. serialPort_->close();
  147. emit statusChanged("串口断开");
  148. }
  149. }
  150. /**
  151. * @brief 发送数据并启动超时重发机制
  152. * @param data 待发送的数据
  153. *
  154. * @details 执行流程:
  155. * 1. 检查串口是否打开
  156. * 2. 保存待发送数据(用于重发)
  157. * 3. 重置重发计数器
  158. * 4. 发送数据
  159. * 5. 启动重发超时定时器
  160. */
  161. void SerialCommunicator::sendData(const QByteArray &data)
  162. {
  163. if (!serialPort_->isOpen())
  164. {
  165. emit statusChanged("串口未打开,无法发送数据");
  166. return;
  167. }
  168. // 保存待重发数据,初始化计数器,启动重发定时器
  169. currentData_ = data;
  170. comCount_ = 0;
  171. serialPort_->write(data); // 发送数据
  172. emit statusChanged("发送报文:" + data.toHex().toUpper());
  173. resendTimer_->start(resendTimeout_); // 启动超时计时
  174. }
  175. /**
  176. * @brief 设置最大重发次数
  177. * @param count 最大重发次数
  178. */
  179. void SerialCommunicator::setMaxRetry(int count)
  180. {
  181. maxRetry_ = count;
  182. }
  183. /**
  184. * @brief 设置接收完成超时时间
  185. * @param ms 超时时间(毫秒)
  186. *
  187. * @note 当串口持续ms毫秒无新数据时,认为接收完成
  188. */
  189. void SerialCommunicator::setRecvTimeout(int ms)
  190. {
  191. recvTimeout_ = ms;
  192. }
  193. /**
  194. * @brief 设置重发间隔时间
  195. * @param ms 重发间隔(毫秒)
  196. */
  197. void SerialCommunicator::setResendTimeout(int ms)
  198. {
  199. resendTimeout_ = ms;
  200. }
  201. /**
  202. * @brief 检查串口是否打开
  203. * @return true-已打开, false-已关闭
  204. */
  205. bool SerialCommunicator::isOpen() const
  206. {
  207. return serialPort_->isOpen();
  208. }
  209. // ====================== 私有槽函数 ====================== //
  210. /**
  211. * @brief 串口数据到达处理
  212. *
  213. * @details 执行流程:
  214. * 1. 读取所有可用数据到接收缓冲区
  215. * 2. 重启接收超时定时器
  216. * 3. 定时器超时后将触发onRecvTimeout
  217. */
  218. void SerialCommunicator::onReadyRead()
  219. {
  220. // 读取数据到缓冲区
  221. recvBuffer_.append(serialPort_->readAll());
  222. //重置接收超时定时器
  223. recvTimer_->start(recvTimeout_);
  224. }
  225. /**
  226. * @brief 数据接收完成处理
  227. *
  228. * @details 执行流程:
  229. * 1. 停止重发定时器(表示已收到响应)
  230. * 2. 发送dataReceived信号包含完整数据
  231. * 3. 清空接收缓冲区
  232. */
  233. void SerialCommunicator::onRecvTimeout()
  234. {
  235. // 接收完成,发送完整数据信号,停止重发定时器
  236. resendTimer_->stop();
  237. emit dataReceived(recvBuffer_);
  238. recvBuffer_.clear();
  239. }
  240. /**
  241. * @brief 重发超时处理
  242. *
  243. * @details 执行流程:
  244. * 1. 检查是否达到最大重发次数
  245. * 2. 未达到则重发数据并更新计数器
  246. * 3. 达到最大次数则触发超时信号
  247. */
  248. void SerialCommunicator::onResendTimeout()
  249. {
  250. if (comCount_ < maxRetry_)
  251. {
  252. // 重发数据,更新计数器
  253. serialPort_->write(currentData_);
  254. comCount_++;
  255. emit statusChanged(QString("通信超时,重新发送中(%1/%2)").arg(comCount_).arg(maxRetry_));
  256. resendTimer_->start(resendTimeout_); // 继续计时
  257. }
  258. else
  259. {
  260. // 达到最大重发次数,触发超时信号
  261. resendTimer_->stop();
  262. emit timeoutOccurred();
  263. }
  264. }
  265. /**
  266. * @brief 处理串口错误
  267. * @param error 串口错误代码
  268. *
  269. * @details 执行流程:
  270. * 1. 忽略无错误和超时错误(超时由其他机制处理)
  271. * 2. 根据错误代码生成错误描述
  272. * 3. 对于物理断开错误(ResourceError):
  273. * - 自动关闭串口
  274. * - 停止所有定时器
  275. * - 清空接收缓冲区
  276. * - 发送物理断开信号
  277. * 4. 其他错误只发送状态通知
  278. */
  279. void SerialCommunicator::handleSerialError(QSerialPort::SerialPortError error)
  280. {
  281. // 忽略无错误和超时错误(超时由其他机制处理)
  282. if (error == QSerialPort::NoError || error == QSerialPort::TimeoutError)
  283. {
  284. return;
  285. }
  286. QString errorMsg;
  287. switch (error)
  288. {
  289. case QSerialPort::DeviceNotFoundError:
  290. errorMsg = "设备未找到";
  291. break;
  292. case QSerialPort::PermissionError:
  293. errorMsg = "没有权限访问设备";
  294. break;
  295. case QSerialPort::OpenError:
  296. errorMsg = "设备已打开";
  297. break;
  298. case QSerialPort::ParityError:
  299. errorMsg = "奇偶校验错误";
  300. break;
  301. case QSerialPort::FramingError:
  302. errorMsg = "帧错误";
  303. break;
  304. case QSerialPort::BreakConditionError:
  305. errorMsg = "Break条件错误";
  306. break;
  307. case QSerialPort::WriteError:
  308. errorMsg = "写入错误";
  309. break;
  310. case QSerialPort::ReadError:
  311. errorMsg = "读取错误";
  312. break;
  313. case QSerialPort::ResourceError:
  314. errorMsg = "资源错误(设备已断开)";
  315. break;
  316. case QSerialPort::UnsupportedOperationError:
  317. errorMsg = "不支持的操作";
  318. break;
  319. case QSerialPort::UnknownError:
  320. errorMsg = "未知错误";
  321. break;
  322. case QSerialPort::NotOpenError:
  323. errorMsg = "设备未打开";
  324. break;
  325. default:
  326. errorMsg = "未定义错误";
  327. }
  328. // 物理断开处理(资源错误)
  329. if (error == QSerialPort::ResourceError)
  330. {
  331. // 自动关闭串口
  332. close();
  333. // 停止所有定时器
  334. recvTimer_->stop();
  335. resendTimer_->stop();
  336. // 清空缓冲区
  337. recvBuffer_.clear();
  338. // 通知上层
  339. emit statusChanged("物理连接断开: " + errorMsg);
  340. emit physicalDisconnected();
  341. }
  342. else
  343. {
  344. // 其他错误只通知不自动断开
  345. emit statusChanged("串口错误: " + errorMsg);
  346. }
  347. }