|
|
@@ -24,23 +24,30 @@ Widget::Widget(QWidget *parent) : |
|
|
|
serialComm->setResendTimeout(1000); // 重发间隔1s |
|
|
|
|
|
|
|
// 连接信号槽(串口->界面) |
|
|
|
//当串口接收到完整数据后,SerialCommunicator会发出dataReceived信号 |
|
|
|
connect(serialComm, &SerialCommunicator::dataReceived, this, &Widget::onSerialDataReceived); |
|
|
|
//当串口状态改变时,SerialCommunicator会发出statusChanged信号 |
|
|
|
connect(serialComm, &SerialCommunicator::statusChanged, this, &Widget::onSerialStatusChanged); |
|
|
|
//当发送数据超过超时重发次数后仍未收到数据,SerialCommunicator会发出timeoutOccurred信号 |
|
|
|
connect(serialComm, &SerialCommunicator::timeoutOccurred, this, &Widget::onSerialTimeout); |
|
|
|
//当串口连接出现错误时(例如串口突然断开),SerialCommunicator会发出physicalDisconnected信号 |
|
|
|
connect(serialComm, &SerialCommunicator::physicalDisconnected, this, [=](){ |
|
|
|
ui->btnConnect->setText("连接"); |
|
|
|
ui->btn_read->setEnabled(false); |
|
|
|
ui->pushWrite->setEnabled(false); |
|
|
|
QMessageBox::warning(this, "警告", "物理连接已断开"); |
|
|
|
QMessageBox::warning(this, "警告", "串口已断开连接"); |
|
|
|
}); |
|
|
|
|
|
|
|
//设置默认波特率、数据位、校验位 |
|
|
|
ui->comboBox_baudRate->setCurrentIndex(3); |
|
|
|
ui->comboBox_dataBit->setCurrentIndex(3); |
|
|
|
ui->comboBox_xiaoyan->setCurrentIndex(2); |
|
|
|
|
|
|
|
//串口未连接前锁定读取和写入按钮 |
|
|
|
ui->btn_read->setEnabled(0); |
|
|
|
ui->pushWrite->setEnabled(0); |
|
|
|
|
|
|
|
//检查当前可用串口 |
|
|
|
QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts(); |
|
|
|
for(QSerialPortInfo serialInfo : serialList) |
|
|
|
{ |
|
|
@@ -101,43 +108,53 @@ void Widget::on_btnConnect_clicked() |
|
|
|
//写线圈和写寄存器 |
|
|
|
void Widget::on_pushWrite_clicked() |
|
|
|
{ |
|
|
|
switch (ui->comboBox_gongnengma->currentIndex()) |
|
|
|
switch (ui->comboBox_gongnengma->currentIndex()) //判断当前功能码 |
|
|
|
{ |
|
|
|
case 2: //写多个线圈 |
|
|
|
{ |
|
|
|
QString sendData = ui->lineEdit->text().trimmed(); |
|
|
|
QString sendData = ui->lineEdit->text().trimmed(); //读取输入框中的数据 |
|
|
|
//判断输入框中是否为空 |
|
|
|
if (sendData.isEmpty()) |
|
|
|
{ |
|
|
|
QMessageBox::warning(this, "提示", "请至少输入一个数据"); |
|
|
|
return; |
|
|
|
} |
|
|
|
for (QChar ch : sendData) { |
|
|
|
if (ch != '0' && ch != '1') { |
|
|
|
sendData.remove(" "); //移除输入数据中的空格 |
|
|
|
//判断输入数据是否为二进制格式 |
|
|
|
for (QChar ch : sendData) |
|
|
|
{ |
|
|
|
if (ch != '0' && ch != '1') |
|
|
|
{ |
|
|
|
QMessageBox::warning(this, "提示", "只允许输入 0 或 1!"); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//拆分数据 |
|
|
|
QVector<bool> coils; |
|
|
|
for (QChar ch : sendData) |
|
|
|
{ |
|
|
|
coils.append(ch == '1'); |
|
|
|
} |
|
|
|
|
|
|
|
//配置从站参数 |
|
|
|
quint16 stationAddress = ui->lineEdit_stationAddress->text().toInt(); |
|
|
|
quint16 functionCode = 0x0f; |
|
|
|
quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt(); |
|
|
|
quint16 length = ui->lineEdit_length->text().toInt(); |
|
|
|
|
|
|
|
//判断输入数据长度与设置的长度是否一致 |
|
|
|
if (coils.size() != length) |
|
|
|
{ |
|
|
|
QMessageBox::warning(this, "提示", "输入数据数与设置的长度不匹配"); |
|
|
|
return; |
|
|
|
} |
|
|
|
//将从站参数传入modbus中 |
|
|
|
modbus->Set(stationAddress,functionCode,stratAddress,length); |
|
|
|
modbus->WriteCoil(coils); |
|
|
|
modbus->WriteCoil(coils); //使用WriteCoil方法生成命令报文 |
|
|
|
|
|
|
|
serialComm->sendData(modbus->SendCommand()); |
|
|
|
serialComm->sendData(modbus->SendCommand()); //发送生成的命令报文 |
|
|
|
//锁定按钮 |
|
|
|
ui->btn_read->setEnabled(0); |
|
|
|
ui->pushWrite->setEnabled(0); |
|
|
|
|
|
|
@@ -146,13 +163,15 @@ void Widget::on_pushWrite_clicked() |
|
|
|
|
|
|
|
case 3: //写多个寄存器 |
|
|
|
{ |
|
|
|
QString sendData = ui->lineEdit->text().trimmed(); |
|
|
|
QString sendData = ui->lineEdit->text().trimmed(); //读取数据 |
|
|
|
//判断输入框中是否为空 |
|
|
|
if (sendData.isEmpty()) |
|
|
|
{ |
|
|
|
QMessageBox::warning(this, "提示", "请输入完整的寄存器数据"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
//拆分输入的寄存器数据 |
|
|
|
QStringList sl = sendData.split(','); |
|
|
|
QVector<quint16> values; |
|
|
|
bool ok; |
|
|
@@ -169,6 +188,7 @@ void Widget::on_pushWrite_clicked() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//若判断输入的数据不是10进制,则按16进制数据处理 |
|
|
|
for (const QString &s : sl) |
|
|
|
{ |
|
|
|
quint16 v; |
|
|
@@ -195,20 +215,24 @@ void Widget::on_pushWrite_clicked() |
|
|
|
values.append(v); |
|
|
|
} |
|
|
|
|
|
|
|
//配置从站参数 |
|
|
|
quint16 stationAddress = ui->lineEdit_stationAddress->text().toInt(); |
|
|
|
quint16 functionCode = 0x10; |
|
|
|
quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt(); |
|
|
|
quint16 length = ui->lineEdit_length->text().toInt(); |
|
|
|
|
|
|
|
//判断输入数据长度与设置的长度是否一致 |
|
|
|
if (values.size() != length) |
|
|
|
{ |
|
|
|
QMessageBox::warning(this, "提示", "输入数据数与设置的长度不匹配"); |
|
|
|
return; |
|
|
|
} |
|
|
|
//将从站参数传入modbus中 |
|
|
|
modbus->Set(stationAddress,functionCode,stratAddress,length); |
|
|
|
modbus->WriteRegister(values); //要发送的报文 |
|
|
|
modbus->WriteRegister(values); //使用WriteRegister方法生成命令报文 |
|
|
|
|
|
|
|
serialComm->sendData(modbus->SendCommand()); |
|
|
|
serialComm->sendData(modbus->SendCommand()); //发送生成的命令报文 |
|
|
|
//锁定按钮 |
|
|
|
ui->btn_read->setEnabled(0); |
|
|
|
ui->pushWrite->setEnabled(0); |
|
|
|
|
|
|
@@ -216,6 +240,7 @@ void Widget::on_pushWrite_clicked() |
|
|
|
} |
|
|
|
default: |
|
|
|
{ |
|
|
|
//若当前操作设置错误则进行提示 |
|
|
|
QMessageBox::warning(this, "提示", "请将“操作”切换为写线圈或写寄存器"); |
|
|
|
break; |
|
|
|
} |
|
|
@@ -226,17 +251,18 @@ void Widget::on_pushWrite_clicked() |
|
|
|
//发送读线圈和寄存器报文 |
|
|
|
void Widget::on_btn_read_clicked() |
|
|
|
{ |
|
|
|
//若当前操作设置错误则进行提示 |
|
|
|
if (ui->comboBox_gongnengma->currentIndex() == 2 || |
|
|
|
ui->comboBox_gongnengma->currentIndex() == 3) |
|
|
|
{ |
|
|
|
QMessageBox::warning(this, "提示", "请将“操作”切换为读线圈或读寄存器"); |
|
|
|
return; |
|
|
|
} |
|
|
|
//设置从站参数 |
|
|
|
quint16 stationAddress = ui->lineEdit_stationAddress->text().toInt(); |
|
|
|
quint16 functionCode; |
|
|
|
quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt(); |
|
|
|
quint16 length = ui->lineEdit_length->text().toInt(); |
|
|
|
|
|
|
|
if (ui->comboBox_gongnengma->currentIndex() == 0) //读线圈 |
|
|
|
{ |
|
|
|
functionCode = 0x01; |
|
|
@@ -246,46 +272,58 @@ void Widget::on_btn_read_clicked() |
|
|
|
functionCode = 0x03; |
|
|
|
} |
|
|
|
|
|
|
|
//将从站参数传入modbus中 |
|
|
|
modbus->Set(stationAddress,functionCode,stratAddress,length); |
|
|
|
modbus->ReadCoilAndReg(); |
|
|
|
modbus->ReadCoilAndReg(); //使用ReadCoilAndReg方法生成命令报文 |
|
|
|
|
|
|
|
serialComm->sendData(modbus->SendCommand()); |
|
|
|
serialComm->sendData(modbus->SendCommand()); //发送生成的命令报文 |
|
|
|
//锁定按钮 |
|
|
|
ui->btn_read->setEnabled(0); |
|
|
|
ui->pushWrite->setEnabled(0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
//保存通信历史数据 |
|
|
|
void Widget::on_btn_SaveDate_clicked() |
|
|
|
{ |
|
|
|
SaveDate(this,ui->textEdit_2); |
|
|
|
} |
|
|
|
|
|
|
|
//读取通信历史数据 |
|
|
|
void Widget::on_btn_ReadDate_clicked() |
|
|
|
{ |
|
|
|
ReadDate(this,ui->textEdit_2); |
|
|
|
} |
|
|
|
|
|
|
|
//清空状态通知文本框中的信息 |
|
|
|
void Widget::on_btn_ClearDate_clicked() |
|
|
|
{ |
|
|
|
ui->textEdit_2->clear(); |
|
|
|
} |
|
|
|
|
|
|
|
//清空数据读取文本框中的信息 |
|
|
|
void Widget::on_btn_ClearRead_clicked() |
|
|
|
{ |
|
|
|
ui->textEdit->clear(); |
|
|
|
} |
|
|
|
|
|
|
|
//处理接收到的返回报文 |
|
|
|
void Widget::onSerialDataReceived(const QByteArray &data) |
|
|
|
{ |
|
|
|
QByteArray revMessage = modbus->Receive(data); // 交给Modbus解析 |
|
|
|
if (revMessage.isEmpty()) return; |
|
|
|
|
|
|
|
QByteArray revMessage = modbus->Receive(data); // 接收的原始数据交给Modbus解析 |
|
|
|
// 启用操作按钮 |
|
|
|
ui->btn_read->setEnabled(true); |
|
|
|
ui->pushWrite->setEnabled(true); |
|
|
|
if (revMessage.isEmpty()) //若modbus检测响应报文格式错误则丢弃数据,返回值为空 |
|
|
|
{ |
|
|
|
QMessageBox::warning(this, "提示", "返回报文站地址或CRC校验错误,请重试"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
//显示接收到的响应报文 |
|
|
|
ui->textEdit_2->append("接收报文:" + revMessage.toHex().toUpper()); |
|
|
|
|
|
|
|
// 检查Modbus错误码 |
|
|
|
// 检查响应报文中是否带有错误码 |
|
|
|
int exCode = modbus->ErrorCheck(); |
|
|
|
if (exCode) |
|
|
|
{ |
|
|
@@ -307,7 +345,7 @@ void Widget::onSerialDataReceived(const QByteArray &data) |
|
|
|
{ |
|
|
|
case 0: |
|
|
|
{ // 读线圈 |
|
|
|
QVector<bool> coils = modbus->AnalReadCoil(); |
|
|
|
QVector<bool> coils = modbus->AnalReadCoil(); //使用AnalReadCoil方法解析线圈数据 |
|
|
|
ui->textEdit->append("线圈状态:"); |
|
|
|
for (int i = 0; i < coils.size(); i++) |
|
|
|
{ |
|
|
@@ -317,7 +355,7 @@ void Widget::onSerialDataReceived(const QByteArray &data) |
|
|
|
} |
|
|
|
case 1: |
|
|
|
{ // 读寄存器 |
|
|
|
QVector<quint16> regs = modbus->AnalReadReg(); |
|
|
|
QVector<quint16> regs = modbus->AnalReadReg(); //使用AnalReadReg方法解析寄存器数据 |
|
|
|
ui->textEdit->append("寄存器值:"); |
|
|
|
for (int i = 0; i < regs.size(); i++) |
|
|
|
{ |
|
|
@@ -328,12 +366,13 @@ void Widget::onSerialDataReceived(const QByteArray &data) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//显示串口状态 |
|
|
|
void Widget::onSerialStatusChanged(const QString &status) |
|
|
|
{ |
|
|
|
ui->textEdit_2->append(status); // 显示状态信息(如连接成功、超时重发等) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//超过超时重发次数后触发 |
|
|
|
void Widget::onSerialTimeout() |
|
|
|
{ |
|
|
|
QMessageBox::warning(this, "提示", "等待响应超时,请检查设备"); |
|
|
|