Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

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