Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

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