@@ -1,6 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE QtCreatorProject> | |||
<!-- Written by QtCreator 4.0.3, 2025-07-24T09:24:34. --> | |||
<!-- Written by QtCreator 4.0.3, 2025-07-24T14:53:28. --> | |||
<qtcreator> | |||
<data> | |||
<variable>EnvironmentId</variable> | |||
@@ -4,6 +4,9 @@ | |||
#include <QDebug> | |||
#include <QSerialPort> | |||
#include <QByteArray> | |||
#include <QString> | |||
#include <QVector> | |||
#include <QMessageBox> | |||
#include "crc.h" | |||
Widget::Widget(QWidget *parent) : | |||
@@ -69,6 +72,7 @@ void Widget::on_btnConnect_clicked() | |||
else | |||
{ | |||
serialPort->close(); | |||
qDebug() << "Serial close"; | |||
ui->btnConnect->setText("连接"); | |||
} | |||
@@ -76,11 +80,106 @@ void Widget::on_btnConnect_clicked() | |||
void Widget::on_pushWrite_clicked() | |||
{ | |||
const char *sendData = ui->lineEdit->text().toStdString().c_str(); | |||
serialPort->write(sendData); | |||
switch (ui->comboBox_gongnengma->currentIndex()) | |||
{ | |||
case (2): //写多个线圈 | |||
{ | |||
QString sendData = ui->lineEdit->text().trimmed(); | |||
if (sendData.isEmpty()) return; | |||
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 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 temp = calculateCrc(SendCommand); //计算crc | |||
SendCommand.append(temp%256); //加入计算的crc值 | |||
SendCommand.append(temp/256); | |||
serialPort->write(SendCommand); | |||
qDebug() << "SenOk" <<sendData; | |||
} | |||
case 3: | |||
{ | |||
QString sendData = ui->lineEdit->text().trimmed(); | |||
if (sendData.isEmpty()) return; | |||
QStringList sl = sendData.split(','); | |||
QVector<quint16> values; | |||
bool ok; | |||
for (const QString &s : sl) | |||
{ | |||
quint16 v = s.toUShort(&ok, 16); | |||
if (!ok) | |||
{ | |||
QMessageBox::warning(this, "提示", "请输入正确的十六进制值!"); | |||
return; | |||
} | |||
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 | |||
SendCommand.append(temp%256); //加入计算的crc值 | |||
SendCommand.append(temp/256); | |||
serialPort->write(SendCommand); | |||
qDebug() << "SenOk" <<sendData; | |||
} | |||
} | |||
} | |||
void Widget::on_SerialData_ReadyToRead() | |||
{ | |||
QByteArray revMessage = serialPort->readAll(); | |||
@@ -88,30 +187,113 @@ void Widget::on_SerialData_ReadyToRead() | |||
qDebug() << hexData; | |||
ui->textEdit->append(hexData); | |||
//对接收的报文进行CRC校验 | |||
QByteArray payload = revMessage.left(revMessage.length() - 2); | |||
//分离接收值的crc校验位 | |||
quint8 receivedCrcLow = static_cast<quint8>(revMessage.at(revMessage.length() - 2)); | |||
quint8 receivedCrcHigh = static_cast<quint8>(revMessage.at(revMessage.length() - 1)); | |||
//计算返回的报文的crc | |||
quint16 crc = calculateCrc(payload); | |||
quint8 calcCrcLow = crc & 0xFF; | |||
quint8 calcCrcHigh = (crc >> 8) & 0xFF; | |||
//比较计算的crc值和接收到的crc值是否一致 | |||
if(calcCrcLow == receivedCrcLow && calcCrcHigh == receivedCrcHigh) | |||
{ | |||
qDebug() << "接收成功"; | |||
switch (ui->comboBox_gongnengma->currentIndex()) | |||
{ | |||
//解析读线圈的返回报文 | |||
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; | |||
} | |||
//解析读寄存器的返回报文 | |||
case 1: | |||
{ | |||
if (revMessage.size() >= 5 && revMessage.at(1) == 0x03) | |||
{ | |||
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))); | |||
} | |||
} | |||
break; | |||
} | |||
case 2: | |||
{ | |||
if (revMessage.at(1) == '\x8f') | |||
{ | |||
quint8 exCode = revMessage.at(2); | |||
qDebug() << "异常响应,异常码 =" << exCode; | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
qDebug() << "接收失败"; | |||
} | |||
} | |||
//发送读线圈和寄存器报文 | |||
void Widget::on_btn_read_clicked() | |||
{ | |||
QByteArray SendCommand; | |||
SendCommand.append(ui->comboBox_stationAddress->currentText().toInt()%256); | |||
if (ui->comboBox_gongnengma->currentIndex() == 0) | |||
QByteArray SendCommand; //要发送的报文 | |||
SendCommand.append(ui->comboBox_stationAddress->currentText().toInt()%256); //加入站地址 | |||
if (ui->comboBox_gongnengma->currentIndex() == 0) //读线圈 | |||
{ | |||
SendCommand.append(0x01); | |||
} | |||
else if(ui->comboBox_gongnengma->currentIndex() == 1) | |||
else if(ui->comboBox_gongnengma->currentIndex() == 1) //读寄存器 | |||
{ | |||
SendCommand.append(0x03); | |||
} | |||
SendCommand.append(ui->lineEdit_stratAddress->text().toInt()/256); | |||
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(ui->lineEdit_length->text().toInt()%256); | |||
quint16 temp = calculateCrc(SendCommand); | |||
quint16 temp = calculateCrc(SendCommand); //计算crc | |||
SendCommand.append(temp%256); | |||
SendCommand.append(temp%256); //加入计算的crc值 | |||
SendCommand.append(temp/256); | |||
qDebug() << SendCommand.toHex(); | |||
serialPort->write(SendCommand); | |||
serialPort->write(SendCommand); //发送报文 | |||
} |
@@ -140,8 +140,8 @@ | |||
<widget class="QPushButton" name="btnConnect"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>480</x> | |||
<y>30</y> | |||
<x>340</x> | |||
<y>60</y> | |||
<width>93</width> | |||
<height>28</height> | |||
</rect> | |||
@@ -224,200 +224,190 @@ | |||
<widget class="QLineEdit" name="lineEdit"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>420</x> | |||
<y>70</y> | |||
<width>231</width> | |||
<height>21</height> | |||
<x>340</x> | |||
<y>250</y> | |||
<width>301</width> | |||
<height>31</height> | |||
</rect> | |||
</property> | |||
</widget> | |||
<widget class="QPushButton" name="pushWrite"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>660</x> | |||
<y>60</y> | |||
<x>340</x> | |||
<y>290</y> | |||
<width>93</width> | |||
<height>28</height> | |||
</rect> | |||
</property> | |||
<property name="text"> | |||
<string>发送</string> | |||
<string>写</string> | |||
</property> | |||
</widget> | |||
<widget class="QTextEdit" name="textEdit"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>420</x> | |||
<y>100</y> | |||
<width>251</width> | |||
<height>151</height> | |||
</rect> | |||
</property> | |||
</widget> | |||
<widget class="QLabel" name="label_10"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>70</x> | |||
<y>150</y> | |||
<width>21</width> | |||
<height>16</height> | |||
</rect> | |||
</property> | |||
<property name="text"> | |||
<string>读</string> | |||
</property> | |||
</widget> | |||
<widget class="QComboBox" name="comboBox_stationAddress"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>92</x> | |||
<y>120</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> | |||
<item> | |||
<property name="text"> | |||
<string>3</string> | |||
</property> | |||
</item> | |||
<item> | |||
<property name="text"> | |||
<string>4</string> | |||
</property> | |||
</item> | |||
<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>9</string> | |||
</property> | |||
</item> | |||
</widget> | |||
<widget class="QLabel" name="label_9"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>40</x> | |||
<y>120</y> | |||
<width>45</width> | |||
<height>16</height> | |||
<y>170</y> | |||
<width>251</width> | |||
<height>271</height> | |||
</rect> | |||
</property> | |||
<property name="text"> | |||
<string>站地址</string> | |||
</property> | |||
</widget> | |||
<widget class="QLineEdit" name="lineEdit_length"> | |||
<widget class="QPushButton" name="btn_read"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>90</x> | |||
<y>210</y> | |||
<width>51</width> | |||
<height>21</height> | |||
</rect> | |||
</property> | |||
<property name="text"> | |||
<string>00</string> | |||
</property> | |||
</widget> | |||
<widget class="QLineEdit" name="lineEdit_stratAddress"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>100</x> | |||
<y>180</y> | |||
<width>51</width> | |||
<height>21</height> | |||
<y>460</y> | |||
<width>93</width> | |||
<height>28</height> | |||
</rect> | |||
</property> | |||
<property name="text"> | |||
<string>00</string> | |||
<string>读</string> | |||
</property> | |||
</widget> | |||
<widget class="QLabel" name="label_12"> | |||
<widget class="QWidget" name="layoutWidget"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>40</x> | |||
<y>210</y> | |||
<width>41</width> | |||
<height>16</height> | |||
</rect> | |||
</property> | |||
<property name="text"> | |||
<string>长度</string> | |||
</property> | |||
</widget> | |||
<widget class="QLabel" name="label_11"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>30</x> | |||
<y>180</y> | |||
<width>71</width> | |||
<height>16</height> | |||
</rect> | |||
</property> | |||
<property name="text"> | |||
<string>起始地址</string> | |||
</property> | |||
</widget> | |||
<widget class="QComboBox" name="comboBox_gongnengma"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>90</x> | |||
<y>150</y> | |||
<width>61</width> | |||
<height>21</height> | |||
<y>115</y> | |||
<width>672</width> | |||
<height>23</height> | |||
</rect> | |||
</property> | |||
<item> | |||
<property name="text"> | |||
<string>线圈</string> | |||
</property> | |||
</item> | |||
<item> | |||
<property name="text"> | |||
<string>寄存器</string> | |||
</property> | |||
</item> | |||
<layout class="QHBoxLayout" name="horizontalLayout"> | |||
<item> | |||
<widget class="QLabel" name="label_9"> | |||
<property name="text"> | |||
<string>站地址</string> | |||
</property> | |||
</widget> | |||
</item> | |||
<item> | |||
<widget class="QComboBox" name="comboBox_stationAddress"> | |||
<item> | |||
<property name="text"> | |||
<string>1</string> | |||
</property> | |||
</item> | |||
<item> | |||
<property name="text"> | |||
<string>2</string> | |||
</property> | |||
</item> | |||
<item> | |||
<property name="text"> | |||
<string>3</string> | |||
</property> | |||
</item> | |||
<item> | |||
<property name="text"> | |||
<string>4</string> | |||
</property> | |||
</item> | |||
<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>9</string> | |||
</property> | |||
</item> | |||
</widget> | |||
</item> | |||
<item> | |||
<widget class="QLabel" name="label_10"> | |||
<property name="text"> | |||
<string>对象</string> | |||
</property> | |||
</widget> | |||
</item> | |||
<item> | |||
<widget class="QComboBox" name="comboBox_gongnengma"> | |||
<item> | |||
<property name="text"> | |||
<string>读线圈</string> | |||
</property> | |||
</item> | |||
<item> | |||
<property name="text"> | |||
<string>读寄存器</string> | |||
</property> | |||
</item> | |||
<item> | |||
<property name="text"> | |||
<string>写线圈</string> | |||
</property> | |||
</item> | |||
<item> | |||
<property name="text"> | |||
<string>写寄存器</string> | |||
</property> | |||
</item> | |||
</widget> | |||
</item> | |||
<item> | |||
<widget class="QLabel" name="label_11"> | |||
<property name="text"> | |||
<string>起始地址</string> | |||
</property> | |||
</widget> | |||
</item> | |||
<item> | |||
<widget class="QLineEdit" name="lineEdit_stratAddress"> | |||
<property name="text"> | |||
<string>00</string> | |||
</property> | |||
</widget> | |||
</item> | |||
<item> | |||
<widget class="QLabel" name="label_12"> | |||
<property name="text"> | |||
<string>长度</string> | |||
</property> | |||
</widget> | |||
</item> | |||
<item> | |||
<widget class="QLineEdit" name="lineEdit_length"> | |||
<property name="text"> | |||
<string>00</string> | |||
</property> | |||
</widget> | |||
</item> | |||
</layout> | |||
</widget> | |||
<widget class="QPushButton" name="btn_read"> | |||
<widget class="QLabel" name="label_13"> | |||
<property name="geometry"> | |||
<rect> | |||
<x>40</x> | |||
<y>260</y> | |||
<width>93</width> | |||
<height>28</height> | |||
<x>340</x> | |||
<y>160</y> | |||
<width>311</width> | |||
<height>91</height> | |||
</rect> | |||
</property> | |||
<property name="text"> | |||
<string>读</string> | |||
<string>写线圈时,使用1、0代表线圈的开关, | |||
从左到右依次输入每个线圈的开关; | |||
写寄存器时,每个寄存器的值用16进制表示, | |||
相邻寄存器的值之间用","分离。</string> | |||
</property> | |||
</widget> | |||
</widget> | |||