@@ -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-24T09:24:34. --> | |||||
<!-- Written by QtCreator 4.0.3, 2025-07-24T14:53:28. --> | |||||
<qtcreator> | <qtcreator> | ||||
<data> | <data> | ||||
<variable>EnvironmentId</variable> | <variable>EnvironmentId</variable> | ||||
@@ -4,6 +4,9 @@ | |||||
#include <QDebug> | #include <QDebug> | ||||
#include <QSerialPort> | #include <QSerialPort> | ||||
#include <QByteArray> | #include <QByteArray> | ||||
#include <QString> | |||||
#include <QVector> | |||||
#include <QMessageBox> | |||||
#include "crc.h" | #include "crc.h" | ||||
Widget::Widget(QWidget *parent) : | Widget::Widget(QWidget *parent) : | ||||
@@ -69,6 +72,7 @@ void Widget::on_btnConnect_clicked() | |||||
else | else | ||||
{ | { | ||||
serialPort->close(); | serialPort->close(); | ||||
qDebug() << "Serial close"; | |||||
ui->btnConnect->setText("连接"); | ui->btnConnect->setText("连接"); | ||||
} | } | ||||
@@ -76,11 +80,106 @@ void Widget::on_btnConnect_clicked() | |||||
void Widget::on_pushWrite_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; | 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() | void Widget::on_SerialData_ReadyToRead() | ||||
{ | { | ||||
QByteArray revMessage = serialPort->readAll(); | QByteArray revMessage = serialPort->readAll(); | ||||
@@ -88,30 +187,113 @@ void Widget::on_SerialData_ReadyToRead() | |||||
qDebug() << hexData; | qDebug() << hexData; | ||||
ui->textEdit->append(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() | 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); | SendCommand.append(0x01); | ||||
} | } | ||||
else if(ui->comboBox_gongnengma->currentIndex() == 1) | |||||
else if(ui->comboBox_gongnengma->currentIndex() == 1) //读寄存器 | |||||
{ | { | ||||
SendCommand.append(0x03); | 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_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); | 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); | SendCommand.append(temp/256); | ||||
qDebug() << SendCommand.toHex(); | qDebug() << SendCommand.toHex(); | ||||
serialPort->write(SendCommand); | |||||
serialPort->write(SendCommand); //发送报文 | |||||
} | } |
@@ -140,8 +140,8 @@ | |||||
<widget class="QPushButton" name="btnConnect"> | <widget class="QPushButton" name="btnConnect"> | ||||
<property name="geometry"> | <property name="geometry"> | ||||
<rect> | <rect> | ||||
<x>480</x> | |||||
<y>30</y> | |||||
<x>340</x> | |||||
<y>60</y> | |||||
<width>93</width> | <width>93</width> | ||||
<height>28</height> | <height>28</height> | ||||
</rect> | </rect> | ||||
@@ -224,200 +224,190 @@ | |||||
<widget class="QLineEdit" name="lineEdit"> | <widget class="QLineEdit" name="lineEdit"> | ||||
<property name="geometry"> | <property name="geometry"> | ||||
<rect> | <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> | </rect> | ||||
</property> | </property> | ||||
</widget> | </widget> | ||||
<widget class="QPushButton" name="pushWrite"> | <widget class="QPushButton" name="pushWrite"> | ||||
<property name="geometry"> | <property name="geometry"> | ||||
<rect> | <rect> | ||||
<x>660</x> | |||||
<y>60</y> | |||||
<x>340</x> | |||||
<y>290</y> | |||||
<width>93</width> | <width>93</width> | ||||
<height>28</height> | <height>28</height> | ||||
</rect> | </rect> | ||||
</property> | </property> | ||||
<property name="text"> | <property name="text"> | ||||
<string>发送</string> | |||||
<string>写</string> | |||||
</property> | </property> | ||||
</widget> | </widget> | ||||
<widget class="QTextEdit" name="textEdit"> | <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"> | <property name="geometry"> | ||||
<rect> | <rect> | ||||
<x>40</x> | <x>40</x> | ||||
<y>120</y> | |||||
<width>45</width> | |||||
<height>16</height> | |||||
<y>170</y> | |||||
<width>251</width> | |||||
<height>271</height> | |||||
</rect> | </rect> | ||||
</property> | </property> | ||||
<property name="text"> | |||||
<string>站地址</string> | |||||
</property> | |||||
</widget> | </widget> | ||||
<widget class="QLineEdit" name="lineEdit_length"> | |||||
<widget class="QPushButton" name="btn_read"> | |||||
<property name="geometry"> | <property name="geometry"> | ||||
<rect> | <rect> | ||||
<x>90</x> | <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> | </rect> | ||||
</property> | </property> | ||||
<property name="text"> | <property name="text"> | ||||
<string>00</string> | |||||
<string>读</string> | |||||
</property> | </property> | ||||
</widget> | </widget> | ||||
<widget class="QLabel" name="label_12"> | |||||
<widget class="QWidget" name="layoutWidget"> | |||||
<property name="geometry"> | <property name="geometry"> | ||||
<rect> | <rect> | ||||
<x>40</x> | <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> | </rect> | ||||
</property> | </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> | ||||
<widget class="QPushButton" name="btn_read"> | |||||
<widget class="QLabel" name="label_13"> | |||||
<property name="geometry"> | <property name="geometry"> | ||||
<rect> | <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> | </rect> | ||||
</property> | </property> | ||||
<property name="text"> | <property name="text"> | ||||
<string>读</string> | |||||
<string>写线圈时,使用1、0代表线圈的开关, | |||||
从左到右依次输入每个线圈的开关; | |||||
写寄存器时,每个寄存器的值用16进制表示, | |||||
相邻寄存器的值之间用","分离。</string> | |||||
</property> | </property> | ||||
</widget> | </widget> | ||||
</widget> | </widget> | ||||