|
@@ -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 后触发超时 |
|
|
} |
|
|
} |