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.
 
 
 

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