Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

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