Bläddra i källkod

将crc校验加入modbus类中,修改了注释

master
鹏鹏 李 1 vecka sedan
förälder
incheckning
bd59807ccb
9 ändrade filer med 423 tillägg och 137 borttagningar
  1. +91
    -12
      communicationhistory.cpp
  2. +33
    -0
      communicationhistory.h
  3. +0
    -54
      crc.cpp
  4. +0
    -10
      crc.h
  5. +0
    -2
      modbus.pro
  6. +1
    -1
      modbus.pro.user
  7. +185
    -49
      mymodbus.cpp
  8. +103
    -8
      mymodbus.h
  9. +10
    -1
      widget.cpp

+ 91
- 12
communicationhistory.cpp Visa fil

@@ -1,54 +1,133 @@
/*******************************
* Copyright (C) 2025-.
*
* File Name: communicationhistory.cpp
* Description: 通信历史记录模块头文件,提供通信数据的保存和读取功能
* Others:
* Version: 1.0.0
* Author: lipengpeng
* Date: 2025-7-25
*******************************/

#include "communicationhistory.h"

/**
* @brief 保存文本内容到指定文件
* @param parent 父窗口指针,用于对话框的模态显示
* @param edit 包含待保存数据的文本编辑框指针
* @return bool 保存成功返回true,用户取消或失败返回false
* @note 功能流程:
* 1. 弹出标准文件保存对话框
* 2. 以追加模式打开文件(保留历史记录)
* 3. 自动处理文件内容分隔(添加换行符)
* 4. 使用UTF-8编码保存文本
* 5. 错误处理包含用户提示
*/
bool SaveDate(QWidget *parent, QTextEdit *edit)
{
// 弹出文件保存对话框,默认保存到用户主目录的note.txt文件
QString fileName = QFileDialog::getSaveFileName(
parent,
"保存文本",
QDir::homePath() + "/note.txt",
"文本文件 (*.txt);;所有文件 (*)");
parent,
"保存文本", // 对话框标题
QDir::homePath() + "/note.txt", // 默认路径和文件名
"文本文件 (*.txt);;所有文件 (*)"); // 文件过滤器

// 检查用户是否取消操作
if (fileName.isEmpty())
{
return false;
}

// 以追加模式和文本模式打开文件
QFile file(fileName);
if (!file.open(QIODevice::Append | QIODevice::Text))
{
QMessageBox::warning(parent, "错误", "无法打开文件");
QMessageBox::warning(parent,
"错误",
QString("无法打开文件\n%1").arg(file.errorString()));
return false;
}

// 如果文件已有内容,先追加换行符保持内容分隔
if (file.size() > 0)
file.write("\n");
{
if (file.write("\n") == -1)
{
// 检查换行符写入是否成功
QMessageBox::warning(parent, "错误", "无法写入分隔符");
file.close();
return false;
}
}

// 设置UTF-8编码并写入文本内容
QTextStream out(&file);
out.setCodec("UTF-8");
out << edit->toPlainText();

// 检查流状态确保写入成功
if (out.status() != QTextStream::Ok)
{
QMessageBox::warning(parent, "错误", "写入文件时发生错误");
file.close();
return false;
}

file.close();
return true;
}

/**
* @brief 从文件读取文本内容
* @param parent 父窗口指针,用于对话框的模态显示
* @param edit 用于显示读取内容的文本编辑框指针
* @return bool 读取成功返回true,用户取消或失败返回false
* @note 功能流程:
* 1. 弹出标准文件打开对话框
* 2. 以只读模式打开文件
* 3. 使用UTF-8编码读取内容
* 4. 错误处理包含用户提示
*/
bool ReadDate(QWidget *parent, QTextEdit *edit)
{
// 弹出文件打开对话框,默认从用户主目录开始浏览
QString fileName = QFileDialog::getOpenFileName(
parent,
"打开文本",
QDir::homePath(),
"文本文件 (*.txt);;所有文件 (*)");
parent,
"打开文本", // 对话框标题
QDir::homePath(), // 默认路径
"文本文件 (*.txt);;所有文件 (*)"); // 文件过滤器

// 检查用户是否取消操作
if (fileName.isEmpty())
{
return false;
}

// 以只读模式和文本模式打开文件
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QMessageBox::warning(parent, "错误", "无法读取文件");
QMessageBox::warning(parent,
"错误",
QString("无法读取文件\n%1").arg(file.errorString()));
return false;
}

// 设置UTF-8编码并读取全部内容
QTextStream in(&file);
in.setCodec("UTF-8");
edit->setPlainText(in.readAll());
QString content = in.readAll();

// 检查流状态确保读取成功
if (in.status() != QTextStream::Ok)
{
QMessageBox::warning(parent, "错误", "读取文件时发生错误");
file.close();
return false;
}

// 将内容设置到文本编辑框
edit->setPlainText(content);
file.close();
return true;
}

+ 33
- 0
communicationhistory.h Visa fil

@@ -1,3 +1,14 @@
/*******************************
* Copyright (C) 2025-.
*
* File Name: communicationhistory.h
* Description: 通信历史记录模块头文件,提供通信数据的保存和读取功能
* Others:
* Version: 1.0.0
* Author: lipengpeng
* Date: 2025-7-25
*******************************/

#ifndef COMMUNICATIONHISTORY_H
#define COMMUNICATIONHISTORY_H

@@ -10,8 +21,30 @@
#include <QDir>
#include <QTextEdit>

/**
* @brief 保存通信数据到文件
* @param parent 父窗口指针,用于对话框的模态显示
* @param edit 包含待保存通信数据的文本编辑框指针
* @return bool 保存成功返回true,失败返回false
* @note 此函数将:
* 1. 弹出文件保存对话框让用户选择保存路径
* 2. 将文本编辑框内容写入选定文件
* 3. 支持UTF-8编码格式保存
* 4. 操作失败时会显示错误提示
*/
bool SaveDate(QWidget *parent, QTextEdit *edit);

/**
* @brief 从文件读取通信数据
* @param parent 父窗口指针,用于对话框的模态显示
* @param edit 用于显示读取内容的文本编辑框指针
* @return bool 读取成功返回true,失败返回false
* @note 此函数将:
* 1. 弹出文件选择对话框让用户选择文件
* 2. 读取选定文件内容到文本编辑框
* 3. 支持UTF-8编码格式读取
* 4. 操作失败时会显示错误提示
*/
bool ReadDate(QWidget *parent, QTextEdit *edit);

#endif // COMMUNICATIONHISTORY_H

+ 0
- 54
crc.cpp Visa fil

@@ -1,54 +0,0 @@
#include "crc.h"

quint16 CalculateCrc(const QByteArray &data)
{
quint16 crc = 0xFFFF; // MODBUS初始值
const quint16 polynomial = 0xA001; // MODBUS多项式(0x8005的反转)

for (int i = 0; i < data.size(); ++i)
{
crc ^= static_cast<quint8>(data.at(i));

for (int bit = 0; bit < 8; ++bit)
{
if (crc & 0x0001)
{ // 检查最低位
crc = (crc >> 1) ^ polynomial; // 右移并异或
} else
{
crc >>= 1; // 仅右移
}
}
}
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;
}
}

+ 0
- 10
crc.h Visa fil

@@ -1,10 +0,0 @@
#ifndef CRC_H
#define CRC_H

#include <QByteArray>
#include <QtGlobal>

quint16 CalculateCrc(const QByteArray &data);
bool CrcCheck(const QByteArray &data);

#endif // CRC_H

+ 0
- 2
modbus.pro Visa fil

@@ -15,12 +15,10 @@ TEMPLATE = app

SOURCES += main.cpp\
widget.cpp \
crc.cpp \
mymodbus.cpp \
communicationhistory.cpp

HEADERS += widget.h \
crc.h \
mymodbus.h \
communicationhistory.h



+ 1
- 1
modbus.pro.user Visa fil

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.0.3, 2025-07-28T12:59:31. -->
<!-- Written by QtCreator 4.0.3, 2025-07-28T16:03:38. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>


+ 185
- 49
mymodbus.cpp Visa fil

@@ -1,14 +1,37 @@
#include "mymodbus.h"
/******************************************************************************
* Copyright (C) 2025-.
*
* File Name: mymodbus.cpp
* Description: Modbus RTU协议封装类,支持线圈和寄存器的读写操作
* Others:
* Version: 1.0
* Author: lipengpeng
* Date: 2025-7-23
*
******************************************************************************/

#include "mymodbus.h"

/**
* @brief MyModbus类构造函数
* @note 初始化默认Modbus参数:站地址=1,功能码=0x01(读线圈),
* 起始地址=256,数据长度=1
*/
MyModbus::MyModbus()
{
this->stationAddress_ = 1;
this->functionCode_ = 0x01;
this->startAdress_ = 256;
this->length_ = 1;
this->stationAddress_ = 1; // 默认站地址
this->functionCode_ = 0x01; // 默认功能码(读线圈)
this->startAdress_ = 256; // 默认起始地址
this->length_ = 1; // 默认数据长度
}

/**
* @brief 设置Modbus通信参数
* @param stationAddress 从站地址
* @param functionCode 功能码
* @param startAdress 起始地址
* @param length 数据长度
*/
void MyModbus::Set(quint16 stationAddress, quint16 functionCode, quint16 startAdress, quint16 length)
{
this->stationAddress_ = stationAddress;
@@ -18,92 +41,129 @@ void MyModbus::Set(quint16 stationAddress, quint16 functionCode, quint16 startAd

}

/**
* @brief 生成读取线圈/寄存器的Modbus命令
* @note 命令结构:[站地址][功能码][起始地址Hi][起始地址Lo][长度Hi][长度Lo][CRC Lo][CRC Hi]
*/
void MyModbus::ReadCoilAndReg()
{
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);

// 计算并附加CRC校验
quint16 temp = CalculateCrc(sendCommand_);
sendCommand_.append(temp%256);
sendCommand_.append(temp/256);
sendCommand_.append(temp%256); // CRC低字节在前
sendCommand_.append(temp/256); // CRC高字节在后
}

/**
* @brief 生成写线圈命令
* @param coils 要写入的线圈状态数组
* @note 命令结构:[站地址][0x0F][起始地址Hi][起始地址Lo][数量Hi][数量Lo][字节数][数据...][CRC]
*/
void MyModbus::WriteCoil(QVector<bool> &coils)
{
quint16 coilCount = coils.size();
int byteCount = (coilCount + 7) / 8;
int byteCount = (coilCount + 7) / 8; // 计算所需字节数(每字节8位)

sendCommand_.clear();
sendCommand_.append(stationAddress_%256);
sendCommand_.append(0x0f);
sendCommand_.append(0x0F); // 写多个线圈功能码
sendCommand_.append(startAdress_/256);
sendCommand_.append(startAdress_%256);
sendCommand_.append(length_/256);
sendCommand_.append(length_%256);
sendCommand_.append(byteCount);
sendCommand_.append(coilCount/256); // 线圈数量
sendCommand_.append(coilCount%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);
{
byte |= (1 << j); // 设置对应位
}
}
sendCommand_.append(static_cast<char>(byte));
}

quint16 temp = CalculateCrc(sendCommand_); //计算crc
sendCommand_.append(temp%256); //加入计算的crc值
// 计算并附加CRC校验
quint16 temp = CalculateCrc(sendCommand_);
sendCommand_.append(temp%256);
sendCommand_.append(temp/256);
}

/**
* @brief 生成写寄存器命令
* @param values 要写入的寄存器值数组
* @note 命令结构:[站地址][0x10][起始地址Hi][起始地址Lo][数量Hi][数量Lo][字节数][数据Hi][数据Lo...][CRC]
*/
void MyModbus::WriteRegister(QVector<quint16> &values)
{
sendCommand_.clear();
sendCommand_.append(stationAddress_%256);
sendCommand_.append(0x10);
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));
sendCommand_.append(values.size()/256); // 寄存器数量
sendCommand_.append(values.size()%256);
sendCommand_.append(static_cast<char>(values.size() * 2)); // 后续字节数(每个寄存器2字节)

// 打包寄存器值(高位在前)
for (quint16 v : values)
{
sendCommand_.append(static_cast<char>((v >> 8) & 0xFF));
sendCommand_.append(static_cast<char>(v & 0xFF));
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值

// 计算并附加CRC校验
quint16 temp = CalculateCrc(sendCommand_);
sendCommand_.append(temp%256);
sendCommand_.append(temp/256);
}

/**
* @brief 获取生成的Modbus命令
* @return 完整的Modbus命令字节数组
*/
QByteArray MyModbus::SendCommand()
{
return sendCommand_;
}

/**
* @brief 处理接收到的Modbus响应
* @param revMessage 接收到的原始报文
* @return 处理后的有效数据部分(移除地址/功能码/CRC)
* @note 执行站地址匹配和CRC校验
*/
QByteArray MyModbus::Receive(const QByteArray &revMessage)
{
receive_.clear();

// 最小Modbus响应长度检查
// 最小长度检查:站地址1 + 功能码1 + CRC2
if(revMessage.size() < 4)
{ // 地址1 + 功能码1 + CRC2
return receive_;
{
return receive_; // 返回空数组表示无效报文
}

// 检查站地址匹配
// 站地址匹配检查
if(static_cast<quint8>(revMessage[0]) != stationAddress_)
{
return receive_;
}

// CRC校验
// CRC校验检查
if (!CrcCheck(revMessage))
{
return receive_;
@@ -113,72 +173,148 @@ QByteArray MyModbus::Receive(const QByteArray &revMessage)
return receive_;
}

/**
* @brief 检查Modbus异常响应
* @return 0-正常响应, >0-Modbus异常码, -1-响应报文错误
*/
int MyModbus::ErrorCheck()
{
// 异常响应最小长度检查
// 异常响应最小长度:地址1 + 异常功能码1 + 异常码1 + CRC2
if(receive_.size() < 5)
{
// 地址1 + 异常功能码1 + 异常码1 + CRC2
return -1; // 特殊错误码表示协议错误
return -1; // 响应报文错误
}

// 检查功能码高位(异常响应标志)
if ((receive_.at(1) & 0x80) == 0x80)
{
// MODBUS异常响应结构:地址 | 功能码+0x80 | 异常码 | CRC
quint8 exCode = receive_.at(2);
return exCode;
}
else
{
return 0;
return static_cast<quint8>(receive_.at(2)); // 返回异常码
}
return 0; // 正常响应
}

/**
* @brief 解析读取线圈的响应数据
* @return 线圈状态数组
*/
QVector<bool> MyModbus::AnalReadCoil()
{
QVector<bool> coil;
// 最小长度检查:地址1+功能码1+字节数1+数据1+CRC2
if(receive_.size() < 6)
{
// 最小长度: 地址1 + 功能码1 + 长度1 + 数据1 + CRC2
return coil;
}

quint8 byteCount = static_cast<quint8>(receive_[2]);
quint8 byteCount = static_cast<quint8>(receive_[2]); // 数据字节数

// 解析每个数据字节
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);
// 只处理请求的长度
coil.append(byteValue & (1 << bitIndex));
}
}
}
return coil;
}

/**
* @brief 解析读取寄存器的响应数据
* @return 寄存器值数组
*/
QVector<quint16> MyModbus::AnalReadReg()
{
QVector<quint16> registers;
// 最小长度检查:地址1+功能码1+字节数1+数据2+CRC2
if(receive_.size() < 7)
{
// 最小长度: 地址1 + 功能码1 + 长度1 + 数据2 + CRC2
return registers;
}

int byteCount = receive_.at(2);
QByteArray data = receive_.mid(3, byteCount);
int byteCount = receive_.at(2); // 数据字节数
QByteArray data = receive_.mid(3, byteCount); // 提取数据部分

// 每两个字节组合为一个寄存器值(高位在前)
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);
quint16 high = static_cast<quint8>(data[i]);
quint16 low = static_cast<quint8>(data[i+1]);
registers.append((high << 8) | low);
}

return registers;
}

/**
* @brief 计算Modbus CRC16校验码
* @param data 输入数据字节数组
* @return CRC16校验值
* @note 使用MODBUS标准多项式0xA001(0x8005的反转)
*/
quint16 MyModbus::CalculateCrc(const QByteArray &data)
{
quint16 crc = 0xFFFF; // MODBUS初始值
const quint16 polynomial = 0xA001; // MODBUS多项式(0x8005的反转)

for (int i = 0; i < data.size(); ++i)
{
crc ^= static_cast<quint8>(data.at(i)); // 异或当前字节

// 处理每个字节的8位
for (int bit = 0; bit < 8; ++bit)
{
if (crc & 0x0001)
{
// 检查最低位
crc = (crc >> 1) ^ polynomial; // 右移并且异或多项式
} else
{
crc >>= 1; // 仅右移
}
}
}
return crc;
}

/**
* @brief 校验接收数据的CRC
* @param data 完整接收数据(包含末尾CRC)
* @return true-CRC校验通过, false-校验失败
*/
bool MyModbus::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;
}
}

+ 103
- 8
mymodbus.h Visa fil

@@ -4,29 +4,124 @@
#include <QByteArray>
#include <QVector>
#include <QString>
#include "crc.h"
#include <QtGlobal>

/******************************************************************************
* Copyright (C) 2025-.
*
* File Name: mymodbus.h
* Description: Modbus RTU协议封装类,支持线圈和寄存器的读写操作
* Others:
* Version: 1.0
* Author: lipengpeng
* Date: 2025-7-23
*
******************************************************************************/

/**
* @class MyModbus
* @brief Modbus RTU协议封装类
*
* 提供Modbus RTU协议的核心功能实现,包括:
* 1. 读线圈/寄存器(功能码0x01/0x03)
* 2. 写多个线圈(功能码0x0F)
* 3. 写多个寄存器(功能码0x10)
* 4. CRC校验计算与验证
* 5. 响应报文解析
*
* @note 使用时需先通过Set()方法设置从站参数
*/

class MyModbus
{
private:
quint16 stationAddress_;
quint16 functionCode_;
quint16 startAdress_;
quint16 length_;
QByteArray sendCommand_;
QByteArray receive_;
quint16 stationAddress_; // 从站地址
quint16 functionCode_; // 功能码
quint16 startAdress_; // 起始地址
quint16 length_; // 数据长度
QByteArray sendCommand_; // 待发送的命令报文
QByteArray receive_; // 接收到的响应报文

public:
/**
* @brief 默认构造函数
*/
MyModbus();
void Set(quint16 stationAddress_,quint16 functionCode_,quint16 startAdress_,quint16 length_);

/**
* @brief 设置Modbus通信参数
* @param stationAddress 从站地址
* @param functionCode 功能码
* @param startAdress 起始地址
* @param length 数据长度
*/
void Set(quint16 stationAddress, quint16 functionCode,
quint16 startAdress, quint16 length);

/**
* @brief 生成读线圈/寄存器命令
*
* 根据当前参数生成读取命令(功能码01或03)
*/
void ReadCoilAndReg();

/**
* @brief 生成写多个线圈命令
* @param coils 要写入的线圈状态数组
*/
void WriteCoil(QVector<bool> &coils);

/**
* @brief 生成写多个寄存器命令
* @param values 要写入的寄存器值数组
*/
void WriteRegister(QVector<quint16> &values);

/**
* @brief 获取待发送的命令报文
* @return 完整的Modbus命令报文
*/
QByteArray SendCommand();

/**
* @brief 处理接收到的响应报文
* @param revMessage 接收到的原始报文
* @return 通过CRC校验的有效报文
*/
QByteArray Receive(const QByteArray &revMessage);

/**
* @brief 检查响应报文中的错误码
* @return 0表示正常,非0表示异常码
*/
int ErrorCheck();

/**
* @brief 解析读线圈响应报文
* @return 线圈状态数组
*/
QVector<bool> AnalReadCoil();

/**
* @brief 解析读寄存器响应报文
* @return 寄存器值数组
*/
QVector<quint16> AnalReadReg();

/**
* @brief 计算CRC校验值
* @param data 待计算的数据
* @return CRC校验值
*/
quint16 CalculateCrc(const QByteArray &data);

/**
* @brief 校验报文的CRC值
* @param data 待校验的报文
* @return true表示校验通过,false表示校验失败
*/
bool CrcCheck(const QByteArray &data);

};

#endif // MYMODBUS_H

+ 10
- 1
widget.cpp Visa fil

@@ -8,7 +8,6 @@
#include <QVector>
#include <QMessageBox>
#include <synchapi.h>
#include "crc.h"
#include "mymodbus.h"
#include "communicationhistory.h"

@@ -142,6 +141,11 @@ void Widget::on_pushWrite_clicked()
quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt();
quint16 length = ui->lineEdit_length->text().toInt();

if (coils.size() != length)
{
QMessageBox::warning(this, "提示", "输入数据数与设置的长度不匹配");
return;
}
modbus->Set(stationAddress,functionCode,stratAddress,length);
modbus->WriteCoil(coils);

@@ -184,6 +188,11 @@ void Widget::on_pushWrite_clicked()
quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt();
quint16 length = ui->lineEdit_length->text().toInt();

if (values.size() != length)
{
QMessageBox::warning(this, "提示", "输入数据数与设置的长度不匹配");
return;
}
modbus->Set(stationAddress,functionCode,stratAddress,length);
modbus->WriteRegister(values); //要发送的报文



Laddar…
Avbryt
Spara