Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

309 lignes
9.0 KiB

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. #include <QSerialPortInfo>
  4. #include <QDebug>
  5. #include <QSerialPort>
  6. #include <QByteArray>
  7. #include <QString>
  8. #include <QVector>
  9. #include <QMessageBox>
  10. #include <synchapi.h>
  11. Widget::Widget(QWidget *parent) :
  12. QWidget(parent),
  13. ui(new Ui::Widget)
  14. {
  15. modbus = new MyModbus();
  16. ui->setupUi(this);
  17. serialComm = new SerialCommunicator(this); // 初始化串口通信类
  18. // 配置串口参数
  19. serialComm->setMaxRetry(3); // 最大重发3次
  20. serialComm->setRecvTimeout(50); // 接收超时50ms
  21. serialComm->setResendTimeout(1000); // 重发间隔1s
  22. // 连接信号槽(串口->界面)
  23. connect(serialComm, &SerialCommunicator::dataReceived, this, &Widget::onSerialDataReceived);
  24. connect(serialComm, &SerialCommunicator::statusChanged, this, &Widget::onSerialStatusChanged);
  25. connect(serialComm, &SerialCommunicator::timeoutOccurred, this, &Widget::onSerialTimeout);
  26. ui->comboBox_baudRate->setCurrentIndex(3);
  27. ui->comboBox_dataBit->setCurrentIndex(3);
  28. ui->comboBox_xiaoyan->setCurrentIndex(2);
  29. ui->btn_read->setEnabled(0);
  30. ui->pushWrite->setEnabled(0);
  31. QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
  32. for(QSerialPortInfo serialInfo : serialList)
  33. {
  34. ui->comboBox_serialNum->addItem(serialInfo.portName());
  35. }
  36. }
  37. Widget::~Widget()
  38. {
  39. delete modbus;
  40. delete serialComm;
  41. delete ui;
  42. }
  43. //串口连接
  44. void Widget::on_btnConnect_clicked()
  45. {
  46. if (ui->btnConnect->text() == "连接")
  47. {
  48. // 获取界面配置的串口参数
  49. QString portName = ui->comboBox_serialNum->currentText();
  50. qint32 baudRate = ui->comboBox_baudRate->currentText().toInt();
  51. QSerialPort::DataBits dataBits = QSerialPort::DataBits(ui->comboBox_dataBit->currentText().toInt());
  52. QSerialPort::Parity parity;
  53. switch (ui->comboBox_xiaoyan->currentIndex())
  54. {
  55. case 0: parity = QSerialPort::NoParity; break;
  56. case 1: parity = QSerialPort::OddParity; break;
  57. case 2: parity = QSerialPort::EvenParity; break;
  58. default: parity = QSerialPort::NoParity;
  59. }
  60. QSerialPort::StopBits stopBits = QSerialPort::StopBits(ui->comboBox_stopBit->currentText().toInt());
  61. // 配置并打开串口
  62. serialComm->setPortParams(portName, baudRate, dataBits, parity, stopBits);
  63. if (serialComm->open())
  64. {
  65. ui->btnConnect->setText("断开");
  66. ui->btn_read->setEnabled(true);
  67. ui->pushWrite->setEnabled(true);
  68. }
  69. else
  70. {
  71. QMessageBox::warning(this, "提示", "串口连接失败");
  72. }
  73. }
  74. else
  75. {
  76. // 断开串口
  77. serialComm->close();
  78. ui->btnConnect->setText("连接");
  79. ui->btn_read->setEnabled(false);
  80. ui->pushWrite->setEnabled(false);
  81. }
  82. }
  83. //写线圈和写寄存器
  84. void Widget::on_pushWrite_clicked()
  85. {
  86. switch (ui->comboBox_gongnengma->currentIndex())
  87. {
  88. case 2: //写多个线圈
  89. {
  90. QString sendData = ui->lineEdit->text().trimmed();
  91. if (sendData.isEmpty())
  92. {
  93. QMessageBox::warning(this, "提示", "请至少输入一个数据");
  94. return;
  95. }
  96. for (QChar ch : sendData) {
  97. if (ch != '0' && ch != '1') {
  98. QMessageBox::warning(this, "提示", "只允许输入 0 或 1!");
  99. return;
  100. }
  101. }
  102. QVector<bool> coils;
  103. for (QChar ch : sendData)
  104. {
  105. coils.append(ch == '1');
  106. }
  107. quint16 stationAddress = ui->comboBox_stationAddress->currentText().toInt();
  108. quint16 functionCode = 0x0f;
  109. quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt();
  110. quint16 length = ui->lineEdit_length->text().toInt();
  111. if (coils.size() != length)
  112. {
  113. QMessageBox::warning(this, "提示", "输入数据数与设置的长度不匹配");
  114. return;
  115. }
  116. modbus->Set(stationAddress,functionCode,stratAddress,length);
  117. modbus->WriteCoil(coils);
  118. serialComm->sendData(modbus->SendCommand());
  119. ui->btn_read->setEnabled(0);
  120. ui->pushWrite->setEnabled(0);
  121. break;
  122. }
  123. case 3:
  124. {
  125. QString sendData = ui->lineEdit->text().trimmed();
  126. if (sendData.isEmpty())
  127. {
  128. QMessageBox::warning(this, "提示", "请输入完整的寄存器数据");
  129. return;
  130. }
  131. QStringList sl = sendData.split(',');
  132. QVector<quint16> values;
  133. bool ok;
  134. for (const QString &s : sl)
  135. {
  136. quint16 v = s.toUShort(&ok, 16);
  137. if (!ok)
  138. {
  139. QMessageBox::warning(this, "提示", "请输入正确的十六进制值,或检查逗号是否是英文格式!");
  140. return;
  141. }
  142. values.append(v);
  143. }
  144. quint16 stationAddress = ui->comboBox_stationAddress->currentText().toInt();
  145. quint16 functionCode = 0x10;
  146. quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt();
  147. quint16 length = ui->lineEdit_length->text().toInt();
  148. if (values.size() != length)
  149. {
  150. QMessageBox::warning(this, "提示", "输入数据数与设置的长度不匹配");
  151. return;
  152. }
  153. modbus->Set(stationAddress,functionCode,stratAddress,length);
  154. modbus->WriteRegister(values); //要发送的报文
  155. serialComm->sendData(modbus->SendCommand());
  156. ui->btn_read->setEnabled(0);
  157. ui->pushWrite->setEnabled(0);
  158. break;
  159. }
  160. default:
  161. {
  162. QMessageBox::warning(this, "提示", "请将“操作”切换为写线圈或写寄存器");
  163. break;
  164. }
  165. }
  166. }
  167. //发送读线圈和寄存器报文
  168. void Widget::on_btn_read_clicked()
  169. {
  170. if (ui->comboBox_gongnengma->currentIndex() == 2 ||
  171. ui->comboBox_gongnengma->currentIndex() == 3)
  172. {
  173. QMessageBox::warning(this, "提示", "请将“操作”切换为读线圈或读寄存器");
  174. return;
  175. }
  176. quint16 stationAddress = ui->comboBox_stationAddress->currentText().toInt();
  177. quint16 functionCode;
  178. quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt();
  179. quint16 length = ui->lineEdit_length->text().toInt();
  180. if (ui->comboBox_gongnengma->currentIndex() == 0) //读线圈
  181. {
  182. functionCode = 0x01;
  183. }
  184. else if(ui->comboBox_gongnengma->currentIndex() == 1) //读寄存器
  185. {
  186. functionCode = 0x03;
  187. }
  188. modbus->Set(stationAddress,functionCode,stratAddress,length);
  189. modbus->ReadCoilAndReg();
  190. serialComm->sendData(modbus->SendCommand());
  191. ui->btn_read->setEnabled(0);
  192. ui->pushWrite->setEnabled(0);
  193. }
  194. void Widget::on_btn_SaveDate_clicked()
  195. {
  196. SaveDate(this,ui->textEdit_2);
  197. }
  198. void Widget::on_btn_ReadDate_clicked()
  199. {
  200. ReadDate(this,ui->textEdit_2);
  201. }
  202. void Widget::on_btn_ClearDate_clicked()
  203. {
  204. ui->textEdit_2->clear();
  205. }
  206. void Widget::on_btn_ClearRead_clicked()
  207. {
  208. ui->textEdit->clear();
  209. }
  210. void Widget::onSerialDataReceived(const QByteArray &data)
  211. {
  212. QByteArray revMessage = modbus->Receive(data); // 交给Modbus解析
  213. if (revMessage.isEmpty()) return;
  214. // 启用操作按钮
  215. ui->btn_read->setEnabled(true);
  216. ui->pushWrite->setEnabled(true);
  217. ui->textEdit_2->append("接收报文:" + revMessage.toHex().toUpper());
  218. // 检查Modbus错误码
  219. int exCode = modbus->ErrorCheck();
  220. if (exCode)
  221. {
  222. QString errorMsg;
  223. switch (exCode) {
  224. case 0x01: errorMsg = "非法功能码"; break;
  225. case 0x02: errorMsg = "非法数据地址"; break;
  226. case 0x03: errorMsg = "非法数据值"; break;
  227. case 0x04: errorMsg = "从站设备故障"; break;
  228. default: errorMsg = "未知异常"; break;
  229. }
  230. QMessageBox::warning(this, "异常响应",
  231. QString("错误码: 0x%1(%2)").arg(QString::number(exCode, 16).toUpper(), errorMsg));
  232. return;
  233. }
  234. // 解析并显示数据(根据功能码)
  235. switch (ui->comboBox_gongnengma->currentIndex())
  236. {
  237. case 0:
  238. { // 读线圈
  239. QVector<bool> coils = modbus->AnalReadCoil();
  240. ui->textEdit->append("线圈状态:");
  241. for (int i = 0; i < coils.size(); i++)
  242. {
  243. ui->textEdit->append(QString("线圈%1: %2").arg(i+1).arg(coils[i] ? "1" : "0"));
  244. }
  245. break;
  246. }
  247. case 1:
  248. { // 读寄存器
  249. QVector<quint16> regs = modbus->AnalReadReg();
  250. ui->textEdit->append("寄存器值:");
  251. for (int i = 0; i < regs.size(); i++)
  252. {
  253. ui->textEdit->append(QString("寄存器%1: %2").arg(i+1).arg(regs[i]));
  254. }
  255. break;
  256. }
  257. }
  258. }
  259. void Widget::onSerialStatusChanged(const QString &status)
  260. {
  261. ui->textEdit_2->append(status); // 显示状态信息(如连接成功、超时重发等)
  262. }
  263. void Widget::onSerialTimeout()
  264. {
  265. QMessageBox::warning(this, "提示", "等待响应超时,请检查设备");
  266. ui->btn_read->setEnabled(true);
  267. ui->pushWrite->setEnabled(true);
  268. }