Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

352 рядки
9.8 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. #include "mymodbus.h"
  12. #include "communicationhistory.h"
  13. Widget::Widget(QWidget *parent) :
  14. QWidget(parent),
  15. ui(new Ui::Widget)
  16. {
  17. ui->setupUi(this);
  18. serialPort = new QSerialPort(this);
  19. modbus = new MyModbus();
  20. recvTimer = new QTimer(this);
  21. connect(serialPort, &QSerialPort::readyRead, this, &Widget::onReadyRead);
  22. connect(recvTimer, &QTimer::timeout, this, &Widget::on_SerialData_ReadyToRead);
  23. QObject::connect(&timer, &QTimer::timeout, [this]{
  24. if (comCount < 3)
  25. {
  26. ui->textEdit_2->append("通信超时,重新发送中");
  27. serialPort->write(modbus->SendCommand());
  28. comCount ++;
  29. }
  30. else
  31. {
  32. QMessageBox::warning(this, "提示", "等待响应超时,请检查设备状态。");
  33. ui->btn_read->setEnabled(1);
  34. ui->pushWrite->setEnabled(1);
  35. timer.stop();
  36. }
  37. });
  38. ui->comboBox_baudRate->setCurrentIndex(3);
  39. ui->comboBox_dataBit->setCurrentIndex(3);
  40. ui->comboBox_xiaoyan->setCurrentIndex(2);
  41. ui->btn_read->setEnabled(0);
  42. ui->pushWrite->setEnabled(0);
  43. QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
  44. for(QSerialPortInfo serialInfo : serialList)
  45. {
  46. ui->comboBox_serialNum->addItem(serialInfo.portName());
  47. }
  48. }
  49. Widget::~Widget()
  50. {
  51. delete modbus;
  52. delete ui;
  53. }
  54. //串口连接
  55. void Widget::on_btnConnect_clicked()
  56. {
  57. if (ui->btnConnect->text() == "连接")
  58. {
  59. //配置串口号
  60. serialPort->setPortName(ui->comboBox_serialNum->currentText());
  61. //配置波特率
  62. serialPort->setBaudRate(ui->comboBox_baudRate->currentText().toInt());
  63. //配置数据位
  64. serialPort->setDataBits(QSerialPort::DataBits(ui->comboBox_dataBit->currentText().toInt()));
  65. //配置校验位
  66. switch (ui->comboBox_xiaoyan->currentIndex())
  67. {
  68. case 0:
  69. serialPort->setParity(QSerialPort::NoParity);
  70. break;
  71. case 1:
  72. serialPort->setParity(QSerialPort::OddParity);
  73. break;
  74. case 2:
  75. serialPort->setParity(QSerialPort::EvenParity);
  76. }
  77. //配置停止位
  78. serialPort->setStopBits(QSerialPort::StopBits(ui->comboBox_stopBit->currentText().toInt()));
  79. //打开串口
  80. if (serialPort->open(QIODevice::ReadWrite))
  81. {
  82. ui->textEdit_2->append("串口连接成功");
  83. ui->btnConnect->setText("断开");
  84. ui->btn_read->setEnabled(1);
  85. ui->pushWrite->setEnabled(1);
  86. }
  87. else
  88. {
  89. QMessageBox::warning(this, "提示", "串口连接失败,请检查串口参数配置");
  90. }
  91. }
  92. else
  93. {
  94. serialPort->close();
  95. ui->btn_read->setEnabled(0);
  96. ui->pushWrite->setEnabled(0);
  97. qDebug() << "Serial close";
  98. ui->btnConnect->setText("连接");
  99. ui->textEdit_2->append("串口断开");
  100. }
  101. }
  102. //写线圈和写寄存器
  103. void Widget::on_pushWrite_clicked()
  104. {
  105. switch (ui->comboBox_gongnengma->currentIndex())
  106. {
  107. case 2: //写多个线圈
  108. {
  109. QString sendData = ui->lineEdit->text().trimmed();
  110. if (sendData.isEmpty())
  111. {
  112. QMessageBox::warning(this, "提示", "请至少输入一个数据");
  113. return;
  114. }
  115. for (QChar ch : sendData) {
  116. if (ch != '0' && ch != '1') {
  117. QMessageBox::warning(this, "提示", "只允许输入 0 或 1!");
  118. return;
  119. }
  120. }
  121. QVector<bool> coils;
  122. for (QChar ch : sendData)
  123. {
  124. coils.append(ch == '1');
  125. }
  126. quint16 stationAddress = ui->comboBox_stationAddress->currentText().toInt();
  127. quint16 functionCode = 0x0f;
  128. quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt();
  129. quint16 length = ui->lineEdit_length->text().toInt();
  130. if (coils.size() != length)
  131. {
  132. QMessageBox::warning(this, "提示", "输入数据数与设置的长度不匹配");
  133. return;
  134. }
  135. modbus->Set(stationAddress,functionCode,stratAddress,length);
  136. modbus->WriteCoil(coils);
  137. ui->textEdit_2->append("发送报文"+modbus->SendCommand().toHex().toUpper());
  138. serialPort->write(modbus->SendCommand());
  139. ui->btn_read->setEnabled(0);
  140. ui->pushWrite->setEnabled(0);
  141. comCount = 0;
  142. timer.start(1000); // 1 s 后触发超时
  143. break;
  144. }
  145. case 3:
  146. {
  147. QString sendData = ui->lineEdit->text().trimmed();
  148. if (sendData.isEmpty())
  149. {
  150. QMessageBox::warning(this, "提示", "请输入完整的寄存器数据");
  151. return;
  152. }
  153. QStringList sl = sendData.split(',');
  154. QVector<quint16> values;
  155. bool ok;
  156. for (const QString &s : sl)
  157. {
  158. quint16 v = s.toUShort(&ok, 16);
  159. if (!ok)
  160. {
  161. QMessageBox::warning(this, "提示", "请输入正确的十六进制值,或检查逗号是否是英文格式!");
  162. return;
  163. }
  164. values.append(v);
  165. }
  166. quint16 stationAddress = ui->comboBox_stationAddress->currentText().toInt();
  167. quint16 functionCode = 0x10;
  168. quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt();
  169. quint16 length = ui->lineEdit_length->text().toInt();
  170. if (values.size() != length)
  171. {
  172. QMessageBox::warning(this, "提示", "输入数据数与设置的长度不匹配");
  173. return;
  174. }
  175. modbus->Set(stationAddress,functionCode,stratAddress,length);
  176. modbus->WriteRegister(values); //要发送的报文
  177. ui->textEdit_2->append("发送报文"+modbus->SendCommand().toHex().toUpper());
  178. serialPort->write(modbus->SendCommand());
  179. ui->btn_read->setEnabled(0);
  180. ui->pushWrite->setEnabled(0);
  181. comCount = 0;
  182. timer.start(1000); // 1 s 后触发超时
  183. break;
  184. }
  185. default:
  186. {
  187. QMessageBox::warning(this, "提示", "请将“操作”切换为写线圈或写寄存器");
  188. break;
  189. }
  190. }
  191. }
  192. //处理回复报文
  193. void Widget::on_SerialData_ReadyToRead()
  194. {
  195. QByteArray revMessage = recvBuffer;
  196. recvBuffer.clear();
  197. revMessage = modbus->Receive(revMessage);
  198. if (!revMessage.isEmpty())
  199. {
  200. ui->btn_read->setEnabled(1);
  201. ui->pushWrite->setEnabled(1);
  202. timer.stop();
  203. }
  204. else
  205. {
  206. return;
  207. }
  208. QString hexData = revMessage.toHex().toUpper();
  209. ui->textEdit_2->append("接收报文"+hexData);
  210. int exCode = modbus->ErrorCheck();
  211. if (exCode)
  212. {
  213. QString errorMsg;
  214. switch (exCode)
  215. {
  216. case 0x01: errorMsg = "非法功能码"; break;
  217. case 0x02: errorMsg = "非法数据地址"; break;
  218. case 0x03: errorMsg = "非法数据值"; break;
  219. case 0x04: errorMsg = "从站设备故障"; break;
  220. default: errorMsg = "未知异常"; break;
  221. }
  222. QMessageBox::warning(this, "异常响应",
  223. QString("异常\n错误码: 0x%1 (%2)")
  224. .arg(QString::number(exCode, 16).toUpper().rightJustified(2, '0'))
  225. .arg(errorMsg));
  226. return;
  227. }
  228. switch (ui->comboBox_gongnengma->currentIndex())
  229. {
  230. //解析读线圈的返回报文
  231. case 0:
  232. {
  233. QVector<bool> coil = modbus->AnalReadCoil();
  234. ui->textEdit->append("线圈状态:");
  235. for (int i = 0; i < coil.size(); i++)
  236. {
  237. bool state = coil.at(i);
  238. ui->textEdit->append("线圈"+QString::number(i+1)+":"+QString::number(state));
  239. }
  240. break;
  241. }
  242. //解析读寄存器的返回报文
  243. case 1:
  244. {
  245. QVector<quint16> registers = modbus->AnalReadReg();
  246. ui->textEdit->append("寄存器的值:");
  247. for (int i = 0; i < registers.size(); i++)
  248. {
  249. ui->textEdit->append("寄存器"+QString::number(i+1)+":"+QString::number(registers.at(i)));
  250. }
  251. break;
  252. }
  253. }
  254. }
  255. //发送读线圈和寄存器报文
  256. void Widget::on_btn_read_clicked()
  257. {
  258. if (ui->comboBox_gongnengma->currentIndex() == 2 ||
  259. ui->comboBox_gongnengma->currentIndex() == 3)
  260. {
  261. QMessageBox::warning(this, "提示", "请将“操作”切换为读线圈或读寄存器");
  262. return;
  263. }
  264. quint16 stationAddress = ui->comboBox_stationAddress->currentText().toInt();
  265. quint16 functionCode;
  266. quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt();
  267. quint16 length = ui->lineEdit_length->text().toInt();
  268. if (ui->comboBox_gongnengma->currentIndex() == 0) //读线圈
  269. {
  270. functionCode = 0x01;
  271. }
  272. else if(ui->comboBox_gongnengma->currentIndex() == 1) //读寄存器
  273. {
  274. functionCode = 0x03;
  275. }
  276. modbus->Set(stationAddress,functionCode,stratAddress,length);
  277. modbus->ReadCoilAndReg();
  278. serialPort->write(modbus->SendCommand()); //发送报文
  279. ui->textEdit_2->append("发送报文"+modbus->SendCommand().toHex().toUpper());
  280. ui->btn_read->setEnabled(0);
  281. ui->pushWrite->setEnabled(0);
  282. comCount = 0;
  283. timer.start(1000); // 1 s 后触发超时
  284. }
  285. void Widget::on_btn_SaveDate_clicked()
  286. {
  287. SaveDate(this,ui->textEdit_2);
  288. }
  289. void Widget::on_btn_ReadDate_clicked()
  290. {
  291. ReadDate(this,ui->textEdit_2);
  292. }
  293. void Widget::on_btn_ClearDate_clicked()
  294. {
  295. ui->textEdit_2->clear();
  296. }
  297. void Widget::onReadyRead()
  298. {
  299. recvBuffer.append(serialPort->readAll());
  300. recvTimer->start(50); // 50ms 内无新数据即认为接收完成
  301. }
  302. void Widget::on_btn_ClearRead_clicked()
  303. {
  304. ui->textEdit->clear();
  305. }