@@ -1,6 +1,6 @@ | |||||
#include "crc.h" | #include "crc.h" | ||||
quint16 calculateCrc(const QByteArray &data) | |||||
quint16 CalculateCrc(const QByteArray &data) | |||||
{ | { | ||||
quint16 crc = 0xFFFF; // MODBUS初始值 | quint16 crc = 0xFFFF; // MODBUS初始值 | ||||
const quint16 polynomial = 0xA001; // MODBUS多项式(0x8005的反转) | const quint16 polynomial = 0xA001; // MODBUS多项式(0x8005的反转) | ||||
@@ -22,3 +22,33 @@ quint16 calculateCrc(const QByteArray &data) | |||||
} | } | ||||
return crc; | return crc; | ||||
} | } | ||||
bool CrcCheck(const QByteArray &data) | |||||
{ | |||||
//首先对接收报文的长度进行检验 | |||||
if (data.size() < 3) | |||||
{ | |||||
return false; | |||||
} | |||||
//对接收的报文进行CRC校验 | |||||
QByteArray payload = data.left(data.length() - 2); | |||||
//分离接收值的crc校验位 | |||||
quint8 receivedCrcLow = static_cast<quint8>(data.at(data.length() - 2)); | |||||
quint8 receivedCrcHigh = static_cast<quint8>(data.at(data.length() - 1)); | |||||
//计算返回的报文的crc | |||||
quint16 crc = CalculateCrc(payload); | |||||
quint8 calcCrcLow = crc & 0xFF; | |||||
quint8 calcCrcHigh = (crc >> 8) & 0xFF; | |||||
//比较计算的crc值和接收到的crc值是否一致 | |||||
if(calcCrcLow == receivedCrcLow && calcCrcHigh == receivedCrcHigh) | |||||
{ | |||||
return true; | |||||
} | |||||
else | |||||
{ | |||||
return false; | |||||
} | |||||
} |
@@ -5,4 +5,5 @@ | |||||
#include <QByteArray> | #include <QByteArray> | ||||
#include <QtGlobal> | #include <QtGlobal> | ||||
quint16 calculateCrc(const QByteArray &data); | |||||
quint16 CalculateCrc(const QByteArray &data); | |||||
bool CrcCheck(const QByteArray &data); |
@@ -15,9 +15,11 @@ TEMPLATE = app | |||||
SOURCES += main.cpp\ | SOURCES += main.cpp\ | ||||
widget.cpp \ | widget.cpp \ | ||||
crc.cpp | |||||
crc.cpp \ | |||||
mymodbus.cpp | |||||
HEADERS += widget.h \ | HEADERS += widget.h \ | ||||
crc.h | |||||
crc.h \ | |||||
mymodbus.h | |||||
FORMS += widget.ui | FORMS += widget.ui |
@@ -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-25T12:36:51. --> | |||||
<!-- Written by QtCreator 4.0.3, 2025-07-26T12:31:36. --> | |||||
<qtcreator> | <qtcreator> | ||||
<data> | <data> | ||||
<variable>EnvironmentId</variable> | <variable>EnvironmentId</variable> | ||||
@@ -0,0 +1,200 @@ | |||||
#include "mymodbus.h" | |||||
MyModbus::MyModbus() | |||||
{ | |||||
} | |||||
void MyModbus::Set(quint16 stationAddress, quint16 functionCode, quint16 startAdress, quint16 length) | |||||
{ | |||||
this->stationAddress = stationAddress; | |||||
this->functionCode = functionCode; | |||||
this->startAdress = startAdress; | |||||
this->length = length; | |||||
} | |||||
void MyModbus::ReadColiAndReg() | |||||
{ | |||||
sendCommand.clear(); | |||||
sendCommand.append(stationAddress%256); | |||||
sendCommand.append(functionCode%256); | |||||
sendCommand.append(startAdress/256); | |||||
sendCommand.append(startAdress%256); | |||||
sendCommand.append(length/256); | |||||
sendCommand.append(length%256); | |||||
quint16 temp = CalculateCrc(sendCommand); | |||||
sendCommand.append(temp%256); | |||||
sendCommand.append(temp/256); | |||||
} | |||||
void MyModbus::WriteCoil(QVector<bool> &coils) | |||||
{ | |||||
quint16 coilCount = coils.size(); | |||||
int byteCount = (coilCount + 7) / 8; | |||||
sendCommand.clear(); | |||||
sendCommand.append(stationAddress%256); | |||||
sendCommand.append(0x0f); | |||||
sendCommand.append(startAdress/256); | |||||
sendCommand.append(startAdress%256); | |||||
sendCommand.append(length/256); | |||||
sendCommand.append(length%256); | |||||
sendCommand.append(byteCount); | |||||
for (int i = 0; i < byteCount; ++i) | |||||
{ | |||||
quint8 byte = 0; | |||||
for (int j = 0; j < 8; ++j) | |||||
{ | |||||
int bitIndex = i * 8 + j; | |||||
if (bitIndex < coils.size() && coils[bitIndex]) | |||||
byte |= (1 << j); | |||||
} | |||||
sendCommand.append(static_cast<char>(byte)); | |||||
} | |||||
quint16 temp = CalculateCrc(sendCommand); //计算crc | |||||
sendCommand.append(temp%256); //加入计算的crc值 | |||||
sendCommand.append(temp/256); | |||||
} | |||||
void MyModbus::WriteRegister(QVector<quint16> &values) | |||||
{ | |||||
sendCommand.clear(); | |||||
sendCommand.append(stationAddress%256); | |||||
sendCommand.append(0x10); | |||||
sendCommand.append(startAdress/256); | |||||
sendCommand.append(startAdress%256); | |||||
sendCommand.append(length/256); | |||||
sendCommand.append(length%256); | |||||
sendCommand.append(static_cast<char>(values.size() * 2)); | |||||
for (quint16 v : values) | |||||
{ | |||||
sendCommand.append(static_cast<char>((v >> 8) & 0xFF)); | |||||
sendCommand.append(static_cast<char>(v & 0xFF)); | |||||
} | |||||
quint16 temp = CalculateCrc(sendCommand); //计算crc | |||||
sendCommand.append(temp%256); //加入计算的crc值 | |||||
sendCommand.append(temp/256); | |||||
} | |||||
QByteArray MyModbus::SendCommand() | |||||
{ | |||||
return sendCommand; | |||||
} | |||||
QByteArray MyModbus::Receive(const QByteArray &revMessage) | |||||
{ | |||||
receive.clear(); | |||||
if (CrcCheck(revMessage)) | |||||
{ | |||||
this->receive = revMessage; | |||||
} | |||||
return receive; | |||||
} | |||||
int MyModbus::ErrorCheck() | |||||
{ | |||||
if ((receive.at(1) & 0x80) == 0x80) | |||||
{ | |||||
// MODBUS异常响应结构:地址 | 功能码+0x80 | 异常码 | CRC | |||||
quint8 exCode = receive.at(2); | |||||
return exCode; | |||||
} | |||||
else | |||||
{ | |||||
return 0; | |||||
} | |||||
} | |||||
int MyModbus::ErrorCheck(QByteArray rev) | |||||
{ | |||||
if ((rev.at(1) & 0x80) == 0x80) | |||||
{ | |||||
// MODBUS异常响应结构:地址 | 功能码+0x80 | 异常码 | CRC | |||||
quint8 exCode = rev.at(2); | |||||
return exCode; | |||||
} | |||||
else | |||||
{ | |||||
return 0; | |||||
} | |||||
} | |||||
QVector<bool> MyModbus::AnalReadCoil() | |||||
{ | |||||
quint8 byteCount = static_cast<quint8>(receive[2]); | |||||
QVector<bool> coil; | |||||
for (int byteIndex = 0; byteIndex < byteCount; byteIndex++) | |||||
{ | |||||
quint8 byteValue = static_cast<quint8>(receive[3 + byteIndex]); | |||||
// 解析每个字节的8个位 | |||||
for (int bitIndex = 0; bitIndex < 8; bitIndex++) | |||||
{ | |||||
int coilIndex = byteIndex * 8 + bitIndex; | |||||
if (coilIndex < length) | |||||
{ | |||||
bool state = byteValue & (1 << bitIndex); | |||||
coil.append(state); | |||||
} | |||||
} | |||||
} | |||||
return coil; | |||||
} | |||||
QVector<quint16> MyModbus::AnalReadReg() | |||||
{ | |||||
int byteCount = receive.at(2); | |||||
QByteArray data = receive.mid(3, byteCount); | |||||
QVector<quint16> registers; | |||||
for (int i = 0; i < data.size(); i += 2) | |||||
{ | |||||
quint16 value = (static_cast<quint8>(data[i]) << 8) | static_cast<quint8>(data[i+1]); | |||||
registers.append(value); | |||||
} | |||||
return registers; | |||||
} | |||||
QVector<bool> MyModbus::AnalReadCoil(QByteArray rev) | |||||
{ | |||||
quint8 byteCount = static_cast<quint8>(rev[2]); | |||||
QVector<bool> coil; | |||||
for (int byteIndex = 0; byteIndex < byteCount; byteIndex++) | |||||
{ | |||||
quint8 byteValue = static_cast<quint8>(rev[3 + byteIndex]); | |||||
// 解析每个字节的8个位 | |||||
for (int bitIndex = 0; bitIndex < 8; bitIndex++) | |||||
{ | |||||
int coilIndex = byteIndex * 8 + bitIndex; | |||||
if (coilIndex < length) | |||||
{ | |||||
bool state = byteValue & (1 << bitIndex); | |||||
coil.append(state); | |||||
} | |||||
} | |||||
} | |||||
return coil; | |||||
} | |||||
QVector<quint16> MyModbus::AnalReadReg(QByteArray rev) | |||||
{ | |||||
int byteCount = rev.at(2); | |||||
QByteArray data = rev.mid(3, byteCount); | |||||
QVector<quint16> registers; | |||||
for (int i = 0; i < data.size(); i += 2) | |||||
{ | |||||
quint16 value = (static_cast<quint8>(data[i]) << 8) | static_cast<quint8>(data[i+1]); | |||||
registers.append(value); | |||||
} | |||||
return registers; | |||||
} | |||||
@@ -0,0 +1,35 @@ | |||||
#ifndef MYMODBUS_H | |||||
#define MYMODBUS_H | |||||
#include <QByteArray> | |||||
#include <QVector> | |||||
#include <QString> | |||||
#include "crc.h" | |||||
class MyModbus | |||||
{ | |||||
private: | |||||
quint16 stationAddress; | |||||
quint16 functionCode; | |||||
quint16 startAdress; | |||||
quint16 length; | |||||
QByteArray sendCommand; | |||||
QByteArray receive; | |||||
public: | |||||
MyModbus(); | |||||
void Set(quint16 stationAddress,quint16 functionCode,quint16 startAdress,quint16 length); | |||||
void ReadColiAndReg(); | |||||
void WriteCoil(QVector<bool> &coils); | |||||
void WriteRegister(QVector<quint16> &values); | |||||
QByteArray SendCommand(); | |||||
QByteArray Receive(const QByteArray &revMessage); | |||||
int ErrorCheck(); | |||||
int ErrorCheck(QByteArray rev); | |||||
QVector<bool> AnalReadCoil(); | |||||
QVector<quint16> AnalReadReg(); | |||||
QVector<bool> AnalReadCoil(QByteArray rev); | |||||
QVector<quint16> AnalReadReg(QByteArray rev); | |||||
}; | |||||
#endif // MYMODBUS_H |
@@ -8,7 +8,10 @@ | |||||
#include <QVector> | #include <QVector> | ||||
#include <QMessageBox> | #include <QMessageBox> | ||||
#include <synchapi.h> | #include <synchapi.h> | ||||
#include <QFile> | |||||
#include <QFileDialog> | |||||
#include "crc.h" | #include "crc.h" | ||||
#include "mymodbus.h" | |||||
Widget::Widget(QWidget *parent) : | Widget::Widget(QWidget *parent) : | ||||
QWidget(parent), | QWidget(parent), | ||||
@@ -16,11 +19,28 @@ Widget::Widget(QWidget *parent) : | |||||
{ | { | ||||
ui->setupUi(this); | ui->setupUi(this); | ||||
serialPort = new QSerialPort(this); | serialPort = new QSerialPort(this); | ||||
modbus = new MyModbus(); | |||||
connect(serialPort,&QSerialPort::readyRead,this,&Widget::on_SerialData_ReadyToRead); | connect(serialPort,&QSerialPort::readyRead,this,&Widget::on_SerialData_ReadyToRead); | ||||
QObject::connect(&timer, &QTimer::timeout, [&]{ | QObject::connect(&timer, &QTimer::timeout, [&]{ | ||||
if (comCount < 3) | |||||
{ | |||||
ui->textEdit_2->append("通信超时,重新发送中"); | |||||
serialPort->write(modbus->SendCommand()); | |||||
Sleep(1000); | |||||
timer.isActive(); | |||||
timer.start(1000); | |||||
comCount ++; | |||||
} | |||||
else | |||||
{ | |||||
QMessageBox::warning(this, "提示", "等待响应超时,请检查设备状态。"); | QMessageBox::warning(this, "提示", "等待响应超时,请检查设备状态。"); | ||||
ui->btn_read->setEnabled(1); | |||||
ui->pushWrite->setEnabled(1); | |||||
timer.stop(); | timer.stop(); | ||||
} | |||||
}); | }); | ||||
ui->comboBox_baudRate->setCurrentIndex(3); | ui->comboBox_baudRate->setCurrentIndex(3); | ||||
@@ -71,6 +91,7 @@ void Widget::on_btnConnect_clicked() | |||||
if (serialPort->open(QIODevice::ReadWrite)) | if (serialPort->open(QIODevice::ReadWrite)) | ||||
{ | { | ||||
qDebug() << "Serial open success"; | qDebug() << "Serial open success"; | ||||
ui->textEdit_2->append("串口连接成功"); | |||||
ui->btnConnect->setText("断开"); | ui->btnConnect->setText("断开"); | ||||
ui->btn_read->setEnabled(1); | ui->btn_read->setEnabled(1); | ||||
ui->pushWrite->setEnabled(1); | ui->pushWrite->setEnabled(1); | ||||
@@ -78,15 +99,18 @@ void Widget::on_btnConnect_clicked() | |||||
else | else | ||||
{ | { | ||||
qDebug() << "error"; | qDebug() << "error"; | ||||
QMessageBox::warning(this, "提示", "串口连接失败,请检查串口参数配置"); | |||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
delete modbus; | |||||
serialPort->close(); | serialPort->close(); | ||||
ui->btn_read->setEnabled(0); | ui->btn_read->setEnabled(0); | ||||
ui->pushWrite->setEnabled(0); | ui->pushWrite->setEnabled(0); | ||||
qDebug() << "Serial close"; | qDebug() << "Serial close"; | ||||
ui->btnConnect->setText("连接"); | ui->btnConnect->setText("连接"); | ||||
ui->textEdit_2->append("串口断开"); | |||||
} | } | ||||
} | } | ||||
@@ -117,43 +141,23 @@ void Widget::on_pushWrite_clicked() | |||||
coils.append(ch == '1'); | coils.append(ch == '1'); | ||||
} | } | ||||
quint16 coilCount = coils.size(); | |||||
QByteArray SendCommand; //要发送的报文 | |||||
SendCommand.append(ui->comboBox_stationAddress->currentText().toInt()%256); //加入站地址 | |||||
SendCommand.append(0x0f); | |||||
SendCommand.append(ui->lineEdit_stratAddress->text().toInt()/256); //加入起始地址 | |||||
SendCommand.append(ui->lineEdit_stratAddress->text().toInt()%256); | |||||
SendCommand.append(ui->lineEdit_length->text().toInt()/256); //加入长度 | |||||
SendCommand.append(ui->lineEdit_length->text().toInt()%256); | |||||
int byteCount = (coilCount + 7) / 8; | |||||
SendCommand.append(byteCount); //字节数 | |||||
for (int i = 0; i < byteCount; ++i) | |||||
{ | |||||
quint8 byte = 0; | |||||
for (int j = 0; j < 8; ++j) | |||||
{ | |||||
int bitIndex = i * 8 + j; | |||||
if (bitIndex < coils.size() && coils[bitIndex]) | |||||
byte |= (1 << j); | |||||
} | |||||
SendCommand.append(static_cast<char>(byte)); | |||||
} | |||||
quint16 stationAddress = ui->comboBox_stationAddress->currentText().toInt(); | |||||
quint16 functionCode = 0x0f; | |||||
quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt(); | |||||
quint16 length = ui->lineEdit_length->text().toInt(); | |||||
quint16 temp = calculateCrc(SendCommand); //计算crc | |||||
modbus->Set(stationAddress,functionCode,stratAddress,length); | |||||
modbus->WriteCoil(coils); | |||||
SendCommand.append(temp%256); //加入计算的crc值 | |||||
SendCommand.append(temp/256); | |||||
ui->textEdit_2->append("发送报文"+SendCommand.toHex().toUpper()); | |||||
serialPort->write(SendCommand); | |||||
ui->textEdit_2->append("发送报文"+modbus->SendCommand().toHex().toUpper()); | |||||
serialPort->write(modbus->SendCommand()); | |||||
ui->btn_read->setEnabled(0); | |||||
ui->pushWrite->setEnabled(0); | |||||
Sleep(100); | |||||
comCount = 0; | |||||
Sleep(200); | |||||
timer.isActive(); | timer.isActive(); | ||||
timer.start(2000); // 2 s 后触发超时 | |||||
timer.start(1000); // 1 s 后触发超时 | |||||
qDebug() << "SenOk" <<sendData; | qDebug() << "SenOk" <<sendData; | ||||
break; | break; | ||||
@@ -182,31 +186,23 @@ void Widget::on_pushWrite_clicked() | |||||
values.append(v); | values.append(v); | ||||
} | } | ||||
QByteArray SendCommand; //要发送的报文 | |||||
SendCommand.append(ui->comboBox_stationAddress->currentText().toInt()%256); //加入站地址 | |||||
SendCommand.append(0x10); | |||||
SendCommand.append(ui->lineEdit_stratAddress->text().toInt()/256); //加入起始地址 | |||||
SendCommand.append(ui->lineEdit_stratAddress->text().toInt()%256); | |||||
SendCommand.append(ui->lineEdit_length->text().toInt()/256); //加入长度 | |||||
SendCommand.append(ui->lineEdit_length->text().toInt()%256); | |||||
SendCommand.append(static_cast<char>(values.size() * 2)); | |||||
for (quint16 v : values) | |||||
{ | |||||
SendCommand.append(static_cast<char>((v >> 8) & 0xFF)); | |||||
SendCommand.append(static_cast<char>(v & 0xFF)); | |||||
} | |||||
quint16 temp = calculateCrc(SendCommand); //计算crc | |||||
quint16 stationAddress = ui->comboBox_stationAddress->currentText().toInt(); | |||||
quint16 functionCode = 0x0f; | |||||
quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt(); | |||||
quint16 length = ui->lineEdit_length->text().toInt(); | |||||
SendCommand.append(temp%256); //加入计算的crc值 | |||||
SendCommand.append(temp/256); | |||||
modbus->Set(stationAddress,functionCode,stratAddress,length); | |||||
modbus->WriteRegister(values); //要发送的报文 | |||||
ui->textEdit_2->append("发送报文"+SendCommand.toHex().toUpper()); | |||||
serialPort->write(SendCommand); | |||||
ui->textEdit_2->append("发送报文"+modbus->SendCommand().toHex().toUpper()); | |||||
serialPort->write(modbus->SendCommand()); | |||||
ui->btn_read->setEnabled(0); | |||||
ui->pushWrite->setEnabled(0); | |||||
Sleep(100); | |||||
comCount = 0; | |||||
Sleep(200); | |||||
timer.isActive(); | timer.isActive(); | ||||
timer.start(2000); // 2 s 后触发超时 | |||||
timer.start(1000); // 1 s 后触发超时 | |||||
qDebug() << "SenOk" <<sendData; | qDebug() << "SenOk" <<sendData; | ||||
break; | break; | ||||
@@ -223,117 +219,73 @@ void Widget::on_pushWrite_clicked() | |||||
//处理回复报文 | //处理回复报文 | ||||
void Widget::on_SerialData_ReadyToRead() | void Widget::on_SerialData_ReadyToRead() | ||||
{ | { | ||||
timer.stop(); | |||||
QByteArray revMessage = serialPort->readAll(); | |||||
QByteArray revMessage = modbus->Receive(serialPort->readAll()); | |||||
if (!revMessage.isEmpty()) | |||||
{ | |||||
ui->btn_read->setEnabled(1); | |||||
ui->pushWrite->setEnabled(1); | |||||
timer.stop(); | |||||
} | |||||
else | |||||
{ | |||||
return; | |||||
} | |||||
QString hexData = revMessage.toHex().toUpper(); | QString hexData = revMessage.toHex().toUpper(); | ||||
qDebug() << hexData; | qDebug() << hexData; | ||||
ui->textEdit_2->append("接收报文"+hexData); | ui->textEdit_2->append("接收报文"+hexData); | ||||
//首先对接收报文的长度进行检验 | |||||
if (revMessage.size() < 3) { | |||||
QMessageBox::warning(this, "警告", "无效的响应格式"); | |||||
qDebug() << "响应报文过短,长度 =" << revMessage.size(); | |||||
return; | |||||
} | |||||
//对接收的报文进行CRC校验 | |||||
QByteArray payload = revMessage.left(revMessage.length() - 2); | |||||
qDebug() << "接收成功"; | |||||
int exCode = modbus->ErrorCheck(revMessage); | |||||
if (exCode) | |||||
{ | |||||
QString errorMsg; | |||||
switch (exCode) | |||||
{ | |||||
case 0x01: errorMsg = "非法功能码"; break; | |||||
case 0x02: errorMsg = "非法数据地址"; break; | |||||
case 0x03: errorMsg = "非法数据值"; break; | |||||
case 0x04: errorMsg = "从站设备故障"; break; | |||||
default: errorMsg = "未知异常"; break; | |||||
} | |||||
//分离接收值的crc校验位 | |||||
quint8 receivedCrcLow = static_cast<quint8>(revMessage.at(revMessage.length() - 2)); | |||||
quint8 receivedCrcHigh = static_cast<quint8>(revMessage.at(revMessage.length() - 1)); | |||||
QMessageBox::warning(this, "异常响应", | |||||
QString("异常\n错误码: 0x%1 (%2)") | |||||
.arg(QString::number(exCode, 16).toUpper().rightJustified(2, '0')) | |||||
.arg(errorMsg)); | |||||
//计算返回的报文的crc | |||||
quint16 crc = calculateCrc(payload); | |||||
quint8 calcCrcLow = crc & 0xFF; | |||||
quint8 calcCrcHigh = (crc >> 8) & 0xFF; | |||||
return; | |||||
} | |||||
//比较计算的crc值和接收到的crc值是否一致 | |||||
if(calcCrcLow == receivedCrcLow && calcCrcHigh == receivedCrcHigh) | |||||
switch (ui->comboBox_gongnengma->currentIndex()) | |||||
{ | { | ||||
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; | |||||
} | |||||
//解析读线圈的返回报文 | |||||
case 0: | |||||
{ | |||||
QVector<bool> coil = modbus->AnalReadCoil(revMessage); | |||||
switch (ui->comboBox_gongnengma->currentIndex()) | |||||
ui->textEdit->append("线圈状态:"); | |||||
for (int i = 0; i < coil.size(); i++) | |||||
{ | { | ||||
//解析读线圈的返回报文 | |||||
case 0: | |||||
{ | |||||
quint8 byteCount = static_cast<quint8>(revMessage[2]); | |||||
for (int byteIndex = 0; byteIndex < byteCount; byteIndex++) | |||||
{ | |||||
quint8 byteValue = static_cast<quint8>(revMessage[3 + byteIndex]); | |||||
// 解析每个字节的8个位 | |||||
for (int bitIndex = 0; bitIndex < 8; bitIndex++) | |||||
{ | |||||
int coilIndex = byteIndex * 8 + bitIndex; | |||||
if (coilIndex < ui->lineEdit_length->text().toInt()) | |||||
{ | |||||
bool state = byteValue & (1 << bitIndex); | |||||
qDebug() << coilIndex <<" " << state; | |||||
ui->textEdit->append("线圈"+QString::number(coilIndex+1)+":"+QString::number(state)); | |||||
} | |||||
} | |||||
} | |||||
break; | |||||
bool state = coil.at(i); | |||||
ui->textEdit->append("线圈"+QString::number(i+1)+":"+QString::number(state)); | |||||
} | } | ||||
break; | |||||
} | |||||
//解析读寄存器的返回报文 | //解析读寄存器的返回报文 | ||||
case 1: | |||||
{ | |||||
if (revMessage.size() >= 5 && revMessage.at(1) == 0x03) | |||||
case 1: | |||||
{ | |||||
QVector<quint16> registers = modbus->AnalReadReg(revMessage); | |||||
ui->textEdit->append("寄存器的值:"); | |||||
for (int i = 0; i < registers.size(); i++) | |||||
{ | { | ||||
int byteCount = revMessage.at(2); | |||||
QByteArray data = revMessage.mid(3, byteCount); | |||||
QVector<quint16> registers; | |||||
for (int i = 0; i < data.size(); i += 2) | |||||
{ | |||||
quint16 value = (static_cast<quint8>(data[i]) << 8) | static_cast<quint8>(data[i+1]); | |||||
registers.append(value); | |||||
} | |||||
for (int i = 0; i < registers.size(); i++) | |||||
{ | |||||
qDebug() << "Register value:" << registers.at(i); | |||||
ui->textEdit->append("寄存器"+QString::number(i)+":"+QString::number(registers.at(i))); | |||||
} | |||||
ui->textEdit->append("寄存器"+QString::number(i+1)+":"+QString::number(registers.at(i))); | |||||
} | } | ||||
break; | |||||
} | |||||
break; | |||||
} | |||||
} | } | ||||
else | |||||
{ | |||||
qDebug() << "接收失败"; | |||||
} | } | ||||
} | } | ||||
@@ -346,32 +298,88 @@ void Widget::on_btn_read_clicked() | |||||
QMessageBox::warning(this, "提示", "请将“操作”切换为读线圈或读寄存器"); | QMessageBox::warning(this, "提示", "请将“操作”切换为读线圈或读寄存器"); | ||||
return; | return; | ||||
} | } | ||||
QByteArray SendCommand; //要发送的报文 | |||||
SendCommand.append(ui->comboBox_stationAddress->currentText().toInt()%256); //加入站地址 | |||||
quint16 stationAddress = ui->comboBox_stationAddress->currentText().toInt(); | |||||
quint16 functionCode; | |||||
quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt(); | |||||
quint16 length = ui->lineEdit_length->text().toInt(); | |||||
QByteArray SendCommand; | |||||
if (ui->comboBox_gongnengma->currentIndex() == 0) //读线圈 | if (ui->comboBox_gongnengma->currentIndex() == 0) //读线圈 | ||||
{ | { | ||||
SendCommand.append(0x01); | |||||
functionCode = 0x01; | |||||
} | } | ||||
else if(ui->comboBox_gongnengma->currentIndex() == 1) //读寄存器 | else if(ui->comboBox_gongnengma->currentIndex() == 1) //读寄存器 | ||||
{ | { | ||||
SendCommand.append(0x03); | |||||
functionCode = 0x03; | |||||
} | } | ||||
SendCommand.append(ui->lineEdit_stratAddress->text().toInt()/256); //加入起始地址 | |||||
SendCommand.append(ui->lineEdit_stratAddress->text().toInt()%256); | |||||
SendCommand.append(ui->lineEdit_length->text().toInt()/256); //加入长度 | |||||
SendCommand.append(ui->lineEdit_length->text().toInt()%256); | |||||
modbus->Set(stationAddress,functionCode,stratAddress,length); | |||||
modbus->ReadColiAndReg(); | |||||
serialPort->write(modbus->SendCommand()); //发送报文 | |||||
ui->textEdit_2->append("发送报文"+modbus->SendCommand().toHex().toUpper()); | |||||
ui->btn_read->setEnabled(0); | |||||
ui->pushWrite->setEnabled(0); | |||||
comCount = 0; | |||||
Sleep(200); | |||||
timer.isActive(); | |||||
timer.start(1000); // 1 s 后触发超时 | |||||
} | |||||
void Widget::on_pushButton_clicked() | |||||
{ | |||||
QString fileName = QFileDialog::getSaveFileName( | |||||
this, | |||||
"保存文本", | |||||
QDir::homePath() + "/note.txt", | |||||
"文本文件 (*.txt);;所有文件 (*.*)"); | |||||
if (fileName.isEmpty()) | |||||
return; | |||||
quint16 temp = calculateCrc(SendCommand); //计算crc | |||||
QFile file(fileName); | |||||
if (!file.open(QIODevice::Append | QIODevice::Text)) // | |||||
{ | |||||
QMessageBox::warning(this, "错误", "无法打开文件"); | |||||
return; | |||||
} | |||||
SendCommand.append(temp%256); //加入计算的crc值 | |||||
SendCommand.append(temp/256); | |||||
// 每次追加前换行 | |||||
if (file.size() > 0) // 文件非空 | |||||
file.write("\n"); // 先换行,再写内容 | |||||
qDebug() << SendCommand.toHex(); | |||||
ui->textEdit_2->append("发送报文"+SendCommand.toHex().toUpper()); | |||||
serialPort->write(SendCommand); //发送报文 | |||||
QTextStream out(&file); | |||||
out.setCodec("UTF-8"); | |||||
out << ui->textEdit_2->toPlainText(); // 追加写入 | |||||
file.close(); | |||||
} | |||||
Sleep(100); | |||||
timer.isActive(); | |||||
timer.start(2000); // 2 s 后触发超时 | |||||
void Widget::on_pushButton_2_clicked() | |||||
{ | |||||
QString fileName = QFileDialog::getOpenFileName( | |||||
this, | |||||
"打开文本", | |||||
QDir::homePath(), | |||||
"文本文件 (*.txt);;所有文件 (*.*)"); | |||||
if (fileName.isEmpty()) | |||||
return; | |||||
QFile file(fileName); | |||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) | |||||
{ | |||||
QMessageBox::warning(this, "错误", "无法读取文件"); | |||||
return; | |||||
} | |||||
QTextStream in(&file); | |||||
in.setCodec("UTF-8"); | |||||
ui->textEdit_2->setPlainText(in.readAll()); | |||||
file.close(); | |||||
} | |||||
void Widget::on_pushButton_3_clicked() | |||||
{ | |||||
ui->textEdit_2->clear(); | |||||
} | } |
@@ -4,6 +4,7 @@ | |||||
#include <QSerialPort> | #include <QSerialPort> | ||||
#include <QWidget> | #include <QWidget> | ||||
#include <QTimer> | #include <QTimer> | ||||
#include "mymodbus.h" | |||||
namespace Ui { | namespace Ui { | ||||
class Widget; | class Widget; | ||||
@@ -26,10 +27,18 @@ private slots: | |||||
void on_btn_read_clicked(); | void on_btn_read_clicked(); | ||||
void on_pushButton_clicked(); | |||||
void on_pushButton_2_clicked(); | |||||
void on_pushButton_3_clicked(); | |||||
private: | private: | ||||
Ui::Widget *ui; | Ui::Widget *ui; | ||||
QSerialPort *serialPort; | QSerialPort *serialPort; | ||||
MyModbus *modbus; | |||||
QTimer timer; | QTimer timer; | ||||
int comCount; | |||||
}; | }; | ||||
#endif // WIDGET_H | #endif // WIDGET_H |
@@ -13,219 +13,11 @@ | |||||
<property name="windowTitle"> | <property name="windowTitle"> | ||||
<string>Widget</string> | <string>Widget</string> | ||||
</property> | </property> | ||||
<widget class="QLabel" name="label"> | |||||
<property name="geometry"> | |||||
<rect> | |||||
<x>31</x> | |||||
<y>31</y> | |||||
<width>30</width> | |||||
<height>16</height> | |||||
</rect> | |||||
</property> | |||||
<property name="text"> | |||||
<string>串口</string> | |||||
</property> | |||||
</widget> | |||||
<widget class="QComboBox" name="comboBox_serialNum"> | |||||
<property name="geometry"> | |||||
<rect> | |||||
<x>68</x> | |||||
<y>31</y> | |||||
<width>87</width> | |||||
<height>21</height> | |||||
</rect> | |||||
</property> | |||||
</widget> | |||||
<widget class="QLabel" name="label_2"> | |||||
<property name="geometry"> | |||||
<rect> | |||||
<x>162</x> | |||||
<y>31</y> | |||||
<width>45</width> | |||||
<height>16</height> | |||||
</rect> | |||||
</property> | |||||
<property name="text"> | |||||
<string>波特率</string> | |||||
</property> | |||||
</widget> | |||||
<widget class="QLabel" name="label_3"> | |||||
<property name="geometry"> | |||||
<rect> | |||||
<x>308</x> | |||||
<y>31</y> | |||||
<width>45</width> | |||||
<height>16</height> | |||||
</rect> | |||||
</property> | |||||
<property name="text"> | |||||
<string>数据位</string> | |||||
</property> | |||||
</widget> | |||||
<widget class="QComboBox" name="comboBox_baudRate"> | |||||
<property name="geometry"> | |||||
<rect> | |||||
<x>214</x> | |||||
<y>31</y> | |||||
<width>87</width> | |||||
<height>21</height> | |||||
</rect> | |||||
</property> | |||||
<item> | |||||
<property name="text"> | |||||
<string>1200</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>2400</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>4800</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>9600</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>19200</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>38400</string> | |||||
</property> | |||||
</item> | |||||
</widget> | |||||
<widget class="QComboBox" name="comboBox_dataBit"> | |||||
<property name="geometry"> | |||||
<rect> | |||||
<x>360</x> | |||||
<y>31</y> | |||||
<width>61</width> | |||||
<height>21</height> | |||||
</rect> | |||||
</property> | |||||
<item> | |||||
<property name="text"> | |||||
<string>5</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>6</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>7</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>8</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>NONE</string> | |||||
</property> | |||||
</item> | |||||
</widget> | |||||
<widget class="QPushButton" name="btnConnect"> | |||||
<property name="geometry"> | |||||
<rect> | |||||
<x>340</x> | |||||
<y>60</y> | |||||
<width>93</width> | |||||
<height>28</height> | |||||
</rect> | |||||
</property> | |||||
<property name="text"> | |||||
<string>连接</string> | |||||
</property> | |||||
</widget> | |||||
<widget class="QComboBox" name="comboBox_xiaoyan"> | |||||
<property name="geometry"> | |||||
<rect> | |||||
<x>80</x> | |||||
<y>70</y> | |||||
<width>91</width> | |||||
<height>21</height> | |||||
</rect> | |||||
</property> | |||||
<item> | |||||
<property name="text"> | |||||
<string>无校验(N)</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>奇校验(O)</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>偶校验(E)</string> | |||||
</property> | |||||
</item> | |||||
</widget> | |||||
<widget class="QLabel" name="label_7"> | |||||
<property name="geometry"> | |||||
<rect> | |||||
<x>30</x> | |||||
<y>70</y> | |||||
<width>51</width> | |||||
<height>21</height> | |||||
</rect> | |||||
</property> | |||||
<property name="text"> | |||||
<string>校验位</string> | |||||
</property> | |||||
</widget> | |||||
<widget class="QLabel" name="label_8"> | |||||
<property name="geometry"> | |||||
<rect> | |||||
<x>198</x> | |||||
<y>70</y> | |||||
<width>45</width> | |||||
<height>16</height> | |||||
</rect> | |||||
</property> | |||||
<property name="text"> | |||||
<string>停止位</string> | |||||
</property> | |||||
</widget> | |||||
<widget class="QComboBox" name="comboBox_stopBit"> | |||||
<property name="geometry"> | |||||
<rect> | |||||
<x>250</x> | |||||
<y>70</y> | |||||
<width>61</width> | |||||
<height>21</height> | |||||
</rect> | |||||
</property> | |||||
<item> | |||||
<property name="text"> | |||||
<string>1</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>2</string> | |||||
</property> | |||||
</item> | |||||
</widget> | |||||
<widget class="QLineEdit" name="lineEdit"> | <widget class="QLineEdit" name="lineEdit"> | ||||
<property name="geometry"> | <property name="geometry"> | ||||
<rect> | <rect> | ||||
<x>330</x> | |||||
<y>410</y> | |||||
<x>340</x> | |||||
<y>430</y> | |||||
<width>301</width> | <width>301</width> | ||||
<height>31</height> | <height>31</height> | ||||
</rect> | </rect> | ||||
@@ -234,8 +26,8 @@ | |||||
<widget class="QPushButton" name="pushWrite"> | <widget class="QPushButton" name="pushWrite"> | ||||
<property name="geometry"> | <property name="geometry"> | ||||
<rect> | <rect> | ||||
<x>330</x> | |||||
<y>460</y> | |||||
<x>350</x> | |||||
<y>470</y> | |||||
<width>93</width> | <width>93</width> | ||||
<height>28</height> | <height>28</height> | ||||
</rect> | </rect> | ||||
@@ -248,7 +40,7 @@ | |||||
<property name="geometry"> | <property name="geometry"> | ||||
<rect> | <rect> | ||||
<x>40</x> | <x>40</x> | ||||
<y>230</y> | |||||
<y>250</y> | |||||
<width>251</width> | <width>251</width> | ||||
<height>211</height> | <height>211</height> | ||||
</rect> | </rect> | ||||
@@ -257,8 +49,8 @@ | |||||
<widget class="QPushButton" name="btn_read"> | <widget class="QPushButton" name="btn_read"> | ||||
<property name="geometry"> | <property name="geometry"> | ||||
<rect> | <rect> | ||||
<x>90</x> | |||||
<y>460</y> | |||||
<x>110</x> | |||||
<y>470</y> | |||||
<width>93</width> | <width>93</width> | ||||
<height>28</height> | <height>28</height> | ||||
</rect> | </rect> | ||||
@@ -374,7 +166,7 @@ | |||||
<item> | <item> | ||||
<widget class="QLineEdit" name="lineEdit_stratAddress"> | <widget class="QLineEdit" name="lineEdit_stratAddress"> | ||||
<property name="text"> | <property name="text"> | ||||
<string>00</string> | |||||
<string>256</string> | |||||
</property> | </property> | ||||
</widget> | </widget> | ||||
</item> | </item> | ||||
@@ -388,7 +180,7 @@ | |||||
<item> | <item> | ||||
<widget class="QLineEdit" name="lineEdit_length"> | <widget class="QLineEdit" name="lineEdit_length"> | ||||
<property name="text"> | <property name="text"> | ||||
<string>00</string> | |||||
<string>1</string> | |||||
</property> | </property> | ||||
</widget> | </widget> | ||||
</item> | </item> | ||||
@@ -397,8 +189,8 @@ | |||||
<widget class="QLabel" name="label_13"> | <widget class="QLabel" name="label_13"> | ||||
<property name="geometry"> | <property name="geometry"> | ||||
<rect> | <rect> | ||||
<x>330</x> | |||||
<y>300</y> | |||||
<x>340</x> | |||||
<y>330</y> | |||||
<width>311</width> | <width>311</width> | ||||
<height>101</height> | <height>101</height> | ||||
</rect> | </rect> | ||||
@@ -410,15 +202,206 @@ | |||||
相邻寄存器的值之间用英文","分离。</string> | 相邻寄存器的值之间用英文","分离。</string> | ||||
</property> | </property> | ||||
</widget> | </widget> | ||||
<widget class="QTextEdit" name="textEdit_2"> | |||||
<widget class="QWidget" name=""> | |||||
<property name="geometry"> | |||||
<rect> | |||||
<x>40</x> | |||||
<y>30</y> | |||||
<width>378</width> | |||||
<height>62</height> | |||||
</rect> | |||||
</property> | |||||
<layout class="QVBoxLayout" name="verticalLayout"> | |||||
<item> | |||||
<layout class="QHBoxLayout" name="horizontalLayout_2"> | |||||
<item> | |||||
<widget class="QLabel" name="label"> | |||||
<property name="text"> | |||||
<string>串口</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
<item> | |||||
<widget class="QComboBox" name="comboBox_serialNum"/> | |||||
</item> | |||||
<item> | |||||
<widget class="QLabel" name="label_2"> | |||||
<property name="text"> | |||||
<string>波特率</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
<item> | |||||
<widget class="QComboBox" name="comboBox_baudRate"> | |||||
<item> | |||||
<property name="text"> | |||||
<string>1200</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>2400</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>4800</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>9600</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>19200</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>38400</string> | |||||
</property> | |||||
</item> | |||||
</widget> | |||||
</item> | |||||
<item> | |||||
<widget class="QLabel" name="label_3"> | |||||
<property name="text"> | |||||
<string>数据位</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
<item> | |||||
<widget class="QComboBox" name="comboBox_dataBit"> | |||||
<item> | |||||
<property name="text"> | |||||
<string>5</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>6</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>7</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>8</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>NONE</string> | |||||
</property> | |||||
</item> | |||||
</widget> | |||||
</item> | |||||
</layout> | |||||
</item> | |||||
<item> | |||||
<layout class="QHBoxLayout" name="horizontalLayout_3"> | |||||
<item> | |||||
<widget class="QLabel" name="label_7"> | |||||
<property name="text"> | |||||
<string>校验位</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
<item> | |||||
<widget class="QComboBox" name="comboBox_xiaoyan"> | |||||
<item> | |||||
<property name="text"> | |||||
<string>无校验(N)</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>奇校验(O)</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>偶校验(E)</string> | |||||
</property> | |||||
</item> | |||||
</widget> | |||||
</item> | |||||
<item> | |||||
<widget class="QLabel" name="label_8"> | |||||
<property name="text"> | |||||
<string>停止位</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
<item> | |||||
<widget class="QComboBox" name="comboBox_stopBit"> | |||||
<item> | |||||
<property name="text"> | |||||
<string>1</string> | |||||
</property> | |||||
</item> | |||||
<item> | |||||
<property name="text"> | |||||
<string>2</string> | |||||
</property> | |||||
</item> | |||||
</widget> | |||||
</item> | |||||
<item> | |||||
<widget class="QPushButton" name="btnConnect"> | |||||
<property name="text"> | |||||
<string>连接</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
</layout> | |||||
</item> | |||||
</layout> | |||||
</widget> | |||||
<widget class="QWidget" name=""> | |||||
<property name="geometry"> | <property name="geometry"> | ||||
<rect> | <rect> | ||||
<x>40</x> | <x>40</x> | ||||
<y>150</y> | <y>150</y> | ||||
<width>671</width> | <width>671</width> | ||||
<height>61</height> | |||||
<height>91</height> | |||||
</rect> | </rect> | ||||
</property> | </property> | ||||
<layout class="QHBoxLayout" name="horizontalLayout_4"> | |||||
<item> | |||||
<widget class="QTextEdit" name="textEdit_2"/> | |||||
</item> | |||||
<item> | |||||
<layout class="QVBoxLayout" name="verticalLayout_2"> | |||||
<item> | |||||
<widget class="QPushButton" name="pushButton_3"> | |||||
<property name="text"> | |||||
<string>清空收发信息</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
<item> | |||||
<widget class="QPushButton" name="pushButton"> | |||||
<property name="text"> | |||||
<string>保存收发信息</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
<item> | |||||
<widget class="QPushButton" name="pushButton_2"> | |||||
<property name="text"> | |||||
<string>读取历史收发信息</string> | |||||
</property> | |||||
</widget> | |||||
</item> | |||||
</layout> | |||||
</item> | |||||
</layout> | |||||
</widget> | </widget> | ||||
</widget> | </widget> | ||||
<layoutdefault spacing="6" margin="11"/> | <layoutdefault spacing="6" margin="11"/> | ||||