25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

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