Sfoglia il codice sorgente

异常处理

master
鹏鹏 李 2 settimane fa
parent
commit
7e12821346
4 ha cambiato i file con 117 aggiunte e 32 eliminazioni
  1. +1
    -1
      modbus.pro.user
  2. +91
    -18
      widget.cpp
  3. +2
    -0
      widget.h
  4. +23
    -13
      widget.ui

+ 1
- 1
modbus.pro.user Vedi File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.0.3, 2025-07-24T14:53:28. -->
<!-- Written by QtCreator 4.0.3, 2025-07-25T10:54:01. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>


+ 91
- 18
widget.cpp Vedi File

@@ -17,11 +17,18 @@ Widget::Widget(QWidget *parent) :
serialPort = new QSerialPort(this); serialPort = new QSerialPort(this);


connect(serialPort,&QSerialPort::readyRead,this,&Widget::on_SerialData_ReadyToRead); connect(serialPort,&QSerialPort::readyRead,this,&Widget::on_SerialData_ReadyToRead);
QObject::connect(&timer, &QTimer::timeout, [&]{
QMessageBox::warning(this, "提示", "等待响应超时,请检查设备状态。");
timer.stop();
});


ui->comboBox_baudRate->setCurrentIndex(3); ui->comboBox_baudRate->setCurrentIndex(3);
ui->comboBox_dataBit->setCurrentIndex(3); ui->comboBox_dataBit->setCurrentIndex(3);
ui->comboBox_xiaoyan->setCurrentIndex(2); ui->comboBox_xiaoyan->setCurrentIndex(2);


ui->btn_read->setEnabled(0);
ui->pushWrite->setEnabled(0);

QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts(); QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
for(QSerialPortInfo serialInfo : serialList) for(QSerialPortInfo serialInfo : serialList)
{ {
@@ -34,6 +41,7 @@ Widget::~Widget()
delete ui; delete ui;
} }


//串口连接
void Widget::on_btnConnect_clicked() void Widget::on_btnConnect_clicked()
{ {
if (ui->btnConnect->text() == "连接") if (ui->btnConnect->text() == "连接")
@@ -63,6 +71,8 @@ void Widget::on_btnConnect_clicked()
{ {
qDebug() << "Serial open success"; qDebug() << "Serial open success";
ui->btnConnect->setText("断开"); ui->btnConnect->setText("断开");
ui->btn_read->setEnabled(1);
ui->pushWrite->setEnabled(1);
} }
else else
{ {
@@ -72,21 +82,27 @@ void Widget::on_btnConnect_clicked()
else else
{ {
serialPort->close(); serialPort->close();
ui->btn_read->setEnabled(0);
ui->pushWrite->setEnabled(0);
qDebug() << "Serial close"; qDebug() << "Serial close";
ui->btnConnect->setText("连接"); ui->btnConnect->setText("连接");
} }


} }


//写线圈和写寄存器
void Widget::on_pushWrite_clicked() void Widget::on_pushWrite_clicked()
{ {
switch (ui->comboBox_gongnengma->currentIndex()) switch (ui->comboBox_gongnengma->currentIndex())
{ {
case (2): //写多个线圈
case 2: //写多个线圈
{ {
QString sendData = ui->lineEdit->text().trimmed(); QString sendData = ui->lineEdit->text().trimmed();
if (sendData.isEmpty()) return;

if (sendData.isEmpty())
{
QMessageBox::warning(this, "提示", "请至少输入一个数据,缺少的数据默认为0");
return;
}
for (QChar ch : sendData) { for (QChar ch : sendData) {
if (ch != '0' && ch != '1') { if (ch != '0' && ch != '1') {
QMessageBox::warning(this, "提示", "只允许输入 0 或 1!"); QMessageBox::warning(this, "提示", "只允许输入 0 或 1!");
@@ -131,13 +147,24 @@ void Widget::on_pushWrite_clicked()
SendCommand.append(temp%256); //加入计算的crc值 SendCommand.append(temp%256); //加入计算的crc值
SendCommand.append(temp/256); SendCommand.append(temp/256);


ui->textEdit_2->append("发送报文"+SendCommand.toHex().toUpper());
serialPort->write(SendCommand); serialPort->write(SendCommand);

timer.isActive();
timer.start(2000); // 2 s 后触发超时

qDebug() << "SenOk" <<sendData; qDebug() << "SenOk" <<sendData;
break;
} }

case 3: case 3:
{ {
QString sendData = ui->lineEdit->text().trimmed(); QString sendData = ui->lineEdit->text().trimmed();
if (sendData.isEmpty()) return;
if (sendData.isEmpty())
{
QMessageBox::warning(this, "提示", "请输入完整的寄存器数据");
return;
}


QStringList sl = sendData.split(','); QStringList sl = sendData.split(',');
QVector<quint16> values; QVector<quint16> values;
@@ -147,7 +174,7 @@ void Widget::on_pushWrite_clicked()
quint16 v = s.toUShort(&ok, 16); quint16 v = s.toUShort(&ok, 16);
if (!ok) if (!ok)
{ {
QMessageBox::warning(this, "提示", "请输入正确的十六进制值!");
QMessageBox::warning(this, "提示", "请输入正确的十六进制值,或检查逗号是否是英文格式!");
return; return;
} }
values.append(v); values.append(v);
@@ -172,20 +199,40 @@ void Widget::on_pushWrite_clicked()
SendCommand.append(temp%256); //加入计算的crc值 SendCommand.append(temp%256); //加入计算的crc值
SendCommand.append(temp/256); SendCommand.append(temp/256);


ui->textEdit_2->append("发送报文"+SendCommand.toHex().toUpper());
serialPort->write(SendCommand); serialPort->write(SendCommand);

timer.isActive();
timer.start(2000); // 2 s 后触发超时

qDebug() << "SenOk" <<sendData; qDebug() << "SenOk" <<sendData;
break;
}
default:
{
QMessageBox::warning(this, "提示", "请将“操作”切换为写线圈或写寄存器");
break;
} }


} }
} }



//处理回复报文
void Widget::on_SerialData_ReadyToRead() void Widget::on_SerialData_ReadyToRead()
{ {
timer.stop();

QByteArray revMessage = serialPort->readAll(); QByteArray revMessage = serialPort->readAll();
QString hexData = revMessage.toHex().toUpper(); QString hexData = revMessage.toHex().toUpper();
qDebug() << hexData; qDebug() << hexData;
ui->textEdit->append(hexData);
ui->textEdit_2->append("接收报文"+hexData);

//首先对接收报文的长度进行检验
if (revMessage.size() < 3) {
QMessageBox::warning(this, "警告", "无效的响应格式");
qDebug() << "响应报文过短,长度 =" << revMessage.size();
return;
}


//对接收的报文进行CRC校验 //对接收的报文进行CRC校验
QByteArray payload = revMessage.left(revMessage.length() - 2); QByteArray payload = revMessage.left(revMessage.length() - 2);
@@ -203,6 +250,33 @@ void Widget::on_SerialData_ReadyToRead()
if(calcCrcLow == receivedCrcLow && calcCrcHigh == receivedCrcHigh) if(calcCrcLow == receivedCrcLow && calcCrcHigh == receivedCrcHigh)
{ {
qDebug() << "接收成功"; qDebug() << "接收成功";
if ((revMessage.at(1) & 0x80) == 0x80) {
// MODBUS异常响应结构:地址 | 功能码+0x80 | 异常码 | CRC
quint8 originalFunctionCode = revMessage.at(1) & 0x7F; // 去掉最高位
quint8 exCode = revMessage.at(2);

QString errorMsg;
switch (exCode) {
case 0x01: errorMsg = "非法功能码"; break;
case 0x02: errorMsg = "非法数据地址"; break;
case 0x03: errorMsg = "非法数据值"; break;
case 0x04: errorMsg = "从站设备故障"; break;
case 0x05: errorMsg = "确认"; break;
case 0x06: errorMsg = "从站设备忙"; break;
case 0x07: errorMsg = "负确认"; break;
case 0x08: errorMsg = "存储奇偶错误"; break;
default: errorMsg = "未知异常"; break;
}

QMessageBox::warning(this, "异常响应",
QString("功能码 0x%1 异常\n错误码: 0x%2 (%3)")
.arg(QString::number(originalFunctionCode, 16).toUpper())
.arg(QString::number(exCode, 16).toUpper().rightJustified(2, '0'))
.arg(errorMsg));

return;
}

switch (ui->comboBox_gongnengma->currentIndex()) switch (ui->comboBox_gongnengma->currentIndex())
{ {
//解析读线圈的返回报文 //解析读线圈的返回报文
@@ -251,17 +325,6 @@ void Widget::on_SerialData_ReadyToRead()
} }
break; break;
} }
case 2:
{

if (revMessage.at(1) == '\x8f')
{
quint8 exCode = revMessage.at(2);
qDebug() << "异常响应,异常码 =" << exCode;
return;
}
}



} }
} }
@@ -274,6 +337,12 @@ void Widget::on_SerialData_ReadyToRead()
//发送读线圈和寄存器报文 //发送读线圈和寄存器报文
void Widget::on_btn_read_clicked() void Widget::on_btn_read_clicked()
{ {
if (ui->comboBox_gongnengma->currentIndex() == 2 ||
ui->comboBox_gongnengma->currentIndex() == 3)
{
QMessageBox::warning(this, "提示", "请将“操作”切换为读线圈或读寄存器");
return;
}
QByteArray SendCommand; //要发送的报文 QByteArray SendCommand; //要发送的报文
SendCommand.append(ui->comboBox_stationAddress->currentText().toInt()%256); //加入站地址 SendCommand.append(ui->comboBox_stationAddress->currentText().toInt()%256); //加入站地址
if (ui->comboBox_gongnengma->currentIndex() == 0) //读线圈 if (ui->comboBox_gongnengma->currentIndex() == 0) //读线圈
@@ -295,5 +364,9 @@ void Widget::on_btn_read_clicked()
SendCommand.append(temp/256); SendCommand.append(temp/256);


qDebug() << SendCommand.toHex(); qDebug() << SendCommand.toHex();
ui->textEdit_2->append("发送报文"+SendCommand.toHex().toUpper());
serialPort->write(SendCommand); //发送报文 serialPort->write(SendCommand); //发送报文

timer.isActive();
timer.start(2000); // 2 s 后触发超时
} }

+ 2
- 0
widget.h Vedi File

@@ -3,6 +3,7 @@


#include <QSerialPort> #include <QSerialPort>
#include <QWidget> #include <QWidget>
#include <QTimer>


namespace Ui { namespace Ui {
class Widget; class Widget;
@@ -28,6 +29,7 @@ private slots:
private: private:
Ui::Widget *ui; Ui::Widget *ui;
QSerialPort *serialPort; QSerialPort *serialPort;
QTimer timer;
}; };


#endif // WIDGET_H #endif // WIDGET_H

+ 23
- 13
widget.ui Vedi File

@@ -224,8 +224,8 @@
<widget class="QLineEdit" name="lineEdit"> <widget class="QLineEdit" name="lineEdit">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>340</x>
<y>250</y>
<x>330</x>
<y>410</y>
<width>301</width> <width>301</width>
<height>31</height> <height>31</height>
</rect> </rect>
@@ -234,8 +234,8 @@
<widget class="QPushButton" name="pushWrite"> <widget class="QPushButton" name="pushWrite">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>340</x>
<y>290</y>
<x>330</x>
<y>460</y>
<width>93</width> <width>93</width>
<height>28</height> <height>28</height>
</rect> </rect>
@@ -248,9 +248,9 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>40</x> <x>40</x>
<y>170</y>
<y>230</y>
<width>251</width> <width>251</width>
<height>271</height>
<height>211</height>
</rect> </rect>
</property> </property>
</widget> </widget>
@@ -336,7 +336,7 @@
<item> <item>
<widget class="QLabel" name="label_10"> <widget class="QLabel" name="label_10">
<property name="text"> <property name="text">
<string>对象</string>
<string>操作</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -397,17 +397,27 @@
<widget class="QLabel" name="label_13"> <widget class="QLabel" name="label_13">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>340</x>
<y>160</y>
<x>330</x>
<y>300</y>
<width>311</width> <width>311</width>
<height>91</height>
<height>101</height>
</rect> </rect>
</property> </property>
<property name="text"> <property name="text">
<string>写线圈时,使用1、0代表线圈的开关,
<string>写线圈时,使用1、0代表线圈的开关状态
从左到右依次输入每个线圈的开关; 从左到右依次输入每个线圈的开关;
写寄存器时,每个寄存器的值用16进制表示,
相邻寄存器的值之间用&quot;,&quot;分离。</string>
写寄存器时要写入所有寄存器的16进制数值,
相邻寄存器的值之间用英文&quot;,&quot;分离。</string>
</property>
</widget>
<widget class="QTextEdit" name="textEdit_2">
<property name="geometry">
<rect>
<x>40</x>
<y>150</y>
<width>671</width>
<height>61</height>
</rect>
</property> </property>
</widget> </widget>
</widget> </widget>


Caricamento…
Annulla
Salva