@@ -1,6 +1,6 @@ | |||
#include "crc.h" | |||
quint16 calculateCrc(const QByteArray &data) | |||
quint16 CalculateCrc(const QByteArray &data) | |||
{ | |||
quint16 crc = 0xFFFF; // MODBUS初始值 | |||
const quint16 polynomial = 0xA001; // MODBUS多项式(0x8005的反转) | |||
@@ -22,3 +22,33 @@ quint16 calculateCrc(const QByteArray &data) | |||
} | |||
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 <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\ | |||
widget.cpp \ | |||
crc.cpp | |||
crc.cpp \ | |||
mymodbus.cpp | |||
HEADERS += widget.h \ | |||
crc.h | |||
crc.h \ | |||
mymodbus.h | |||
FORMS += widget.ui |
@@ -1,6 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!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> | |||
<data> | |||
<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 <QMessageBox> | |||
#include <synchapi.h> | |||
#include <QFile> | |||
#include <QFileDialog> | |||
#include "crc.h" | |||
#include "mymodbus.h" | |||
Widget::Widget(QWidget *parent) : | |||
QWidget(parent), | |||
@@ -16,11 +19,28 @@ Widget::Widget(QWidget *parent) : | |||
{ | |||
ui->setupUi(this); | |||
serialPort = new QSerialPort(this); | |||
modbus = new MyModbus(); | |||
connect(serialPort,&QSerialPort::readyRead,this,&Widget::on_SerialData_ReadyToRead); | |||
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, "提示", "等待响应超时,请检查设备状态。"); | |||
ui->btn_read->setEnabled(1); | |||
ui->pushWrite->setEnabled(1); | |||
timer.stop(); | |||
} | |||
}); | |||
ui->comboBox_baudRate->setCurrentIndex(3); | |||
@@ -71,6 +91,7 @@ void Widget::on_btnConnect_clicked() | |||
if (serialPort->open(QIODevice::ReadWrite)) | |||
{ | |||
qDebug() << "Serial open success"; | |||
ui->textEdit_2->append("串口连接成功"); | |||
ui->btnConnect->setText("断开"); | |||
ui->btn_read->setEnabled(1); | |||
ui->pushWrite->setEnabled(1); | |||
@@ -78,15 +99,18 @@ void Widget::on_btnConnect_clicked() | |||
else | |||
{ | |||
qDebug() << "error"; | |||
QMessageBox::warning(this, "提示", "串口连接失败,请检查串口参数配置"); | |||
} | |||
} | |||
else | |||
{ | |||
delete modbus; | |||
serialPort->close(); | |||
ui->btn_read->setEnabled(0); | |||
ui->pushWrite->setEnabled(0); | |||
qDebug() << "Serial close"; | |||
ui->btnConnect->setText("连接"); | |||
ui->textEdit_2->append("串口断开"); | |||
} | |||
} | |||
@@ -117,43 +141,23 @@ void Widget::on_pushWrite_clicked() | |||
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.start(2000); // 2 s 后触发超时 | |||
timer.start(1000); // 1 s 后触发超时 | |||
qDebug() << "SenOk" <<sendData; | |||
break; | |||
@@ -182,31 +186,23 @@ void Widget::on_pushWrite_clicked() | |||
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.start(2000); // 2 s 后触发超时 | |||
timer.start(1000); // 1 s 后触发超时 | |||
qDebug() << "SenOk" <<sendData; | |||
break; | |||
@@ -223,117 +219,73 @@ void Widget::on_pushWrite_clicked() | |||
//处理回复报文 | |||
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(); | |||
qDebug() << 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, "提示", "请将“操作”切换为读线圈或读寄存器"); | |||
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) //读线圈 | |||
{ | |||
SendCommand.append(0x01); | |||
functionCode = 0x01; | |||
} | |||
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 <QWidget> | |||
#include <QTimer> | |||
#include "mymodbus.h" | |||
namespace Ui { | |||
class Widget; | |||
@@ -26,10 +27,18 @@ private slots: | |||
void on_btn_read_clicked(); | |||
void on_pushButton_clicked(); | |||
void on_pushButton_2_clicked(); | |||
void on_pushButton_3_clicked(); | |||
private: | |||
Ui::Widget *ui; | |||
QSerialPort *serialPort; | |||
MyModbus *modbus; | |||
QTimer timer; | |||
int comCount; | |||
}; | |||
#endif // WIDGET_H |
@@ -13,219 +13,11 @@ | |||
<property name="windowTitle"> | |||
<string>Widget</string> | |||
</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"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>330</x> | |||
<y>410</y> | |||
<x>340</x> | |||
<y>430</y> | |||
<width>301</width> | |||
<height>31</height> | |||
</rect> | |||
@@ -234,8 +26,8 @@ | |||
<widget class="QPushButton" name="pushWrite"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>330</x> | |||
<y>460</y> | |||
<x>350</x> | |||
<y>470</y> | |||
<width>93</width> | |||
<height>28</height> | |||
</rect> | |||
@@ -248,7 +40,7 @@ | |||
<property name="geometry"> | |||
<rect> | |||
<x>40</x> | |||
<y>230</y> | |||
<y>250</y> | |||
<width>251</width> | |||
<height>211</height> | |||
</rect> | |||
@@ -257,8 +49,8 @@ | |||
<widget class="QPushButton" name="btn_read"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>90</x> | |||
<y>460</y> | |||
<x>110</x> | |||
<y>470</y> | |||
<width>93</width> | |||
<height>28</height> | |||
</rect> | |||
@@ -374,7 +166,7 @@ | |||
<item> | |||
<widget class="QLineEdit" name="lineEdit_stratAddress"> | |||
<property name="text"> | |||
<string>00</string> | |||
<string>256</string> | |||
</property> | |||
</widget> | |||
</item> | |||
@@ -388,7 +180,7 @@ | |||
<item> | |||
<widget class="QLineEdit" name="lineEdit_length"> | |||
<property name="text"> | |||
<string>00</string> | |||
<string>1</string> | |||
</property> | |||
</widget> | |||
</item> | |||
@@ -397,8 +189,8 @@ | |||
<widget class="QLabel" name="label_13"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>330</x> | |||
<y>300</y> | |||
<x>340</x> | |||
<y>330</y> | |||
<width>311</width> | |||
<height>101</height> | |||
</rect> | |||
@@ -410,15 +202,206 @@ | |||
相邻寄存器的值之间用英文","分离。</string> | |||
</property> | |||
</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"> | |||
<rect> | |||
<x>40</x> | |||
<y>150</y> | |||
<width>671</width> | |||
<height>61</height> | |||
<height>91</height> | |||
</rect> | |||
</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> | |||
<layoutdefault spacing="6" margin="11"/> | |||