/*********************************************************************** * Copyright (C) 2025-, XINJE Co., Ltd. * * File Name: modbus_master.h * Description: Modbus主站的ModbusRTUMaster头文件,主要负责四种请求帧的生成:(01)(03)(0F)(10)、响应帧的解析。 * Others: * Version: v1.0 * Author: weikai XINJE * Date: 2025-7-30 ***********************************************************************/ #ifndef MODBUS_RTU_MASTER_H #define MODBUS_RTU_MASTER_H #include #include #include #include const int TYPE_REGISTERS = 1; const int TYPE_COILS = 2; /********************************************************************** * Iterates over the contents of a ModbusRTUMaster. *ModbusRTUMaster: *提供Modbus RTU协议的主站功能,支持生成各种Modbus RTU请求帧, *以及解析从站返回的响应帧。所有协议相关参数均作为类成员变量,可通过setter方法进行设置。 ***********************************************************************/ class ModbusRTUMaster : public QObject { Q_OBJECT public: //构造函数 explicit ModbusRTUMaster(QObject *parent = nullptr); //功能码枚举 - 定义Modbus协议支持的功能码 enum FunctionCode { READ_COILS = 0x01, // 读线圈状态 READ_HOLDING_REGISTERS = 0x03, // 读保持寄存器 WRITE_MULTIPLE_COILS = 0x0F, // 写多个线圈 WRITE_MULTIPLE_REGISTERS = 0x10 // 写多个保持寄存器 }; //错误码枚举 - 定义Modbus协议可能返回的错误码 enum ErrorCode { NO_ERROR = 0x00, // 无错误 ILLEGAL_FUNCTION = 0x01, // 非法功能 - 从站不支持该功能码 ILLEGAL_DATA_ADDRESS = 0x02, // 非法数据地址 - 从站不支持该地址 ILLEGAL_DATA_VALUE = 0x03, // 非法数据值 - 数据值超出范围 SERVER_DEVICE_FAILURE = 0x04,// 从站设备故障 ACKNOWLEDGE = 0x05, // 确认 - 请求已接收但未处理 SERVER_DEVICE_BUSY = 0x06, // 从站设备忙 - 无法处理请求 MEMORY_PARITY_ERROR = 0x08 // 内存奇偶性错误 }; //设置从站地址 void setSlaveAddr(quint8 slaveAddr) { slaveAddr_ = slaveAddr; } //设置功能码 void setFuncCode(FunctionCode funcCode) { funcCode_ = funcCode; } //设置起始地址 void setStartAddr(quint16 startAddr) { startAddr_ = startAddr; } //设置读取数量 void setReadCount(quint16 readCount) { readCount_ = readCount; } //设置线圈数据 void setCoils(const QVector& coils) { coils_ = coils; } //设置寄存器数据 void setRegisters(const QVector& registers) { registers_ = registers; } //提取错误码对应描述 QString getErrorDescription(int errorCode); //获取起始地址 quint16 getStartAddr() const; //生成读线圈请求帧 (功能码01) QByteArray createReadCoilsFrame(); //生成读保持寄存器请求帧 (功能码03) QByteArray createReadHoldingRegistersFrame(); //生成写多个线圈请求帧 (功能码0F) QByteArray createWriteMultipleCoilsFrame(); //生成写多个保持寄存器请求帧 (功能码10) QByteArray createWriteMultipleRegistersFrame(); /*********************************************************************** *@brief: 处理接收到的数据流并解析完整帧 *@param: data 接收到的原始数据 *@param: slaveAddr 输出参数,从响应中解析出的从站地址 *@param: funcCode 输出参数,从响应中解析出的功能码 *@param: parsedData 输出参数,解析后的数据 *@param: errorCode 输出参数,错误码(NoError表示成功) *@param: extractedFrame 输出参数,提取到的完整帧 *@return:是否成功解析 *@note: 提取到帧后会进行解析 ***********************************************************************/ bool processReceivedData(const QByteArray& data, quint8& slaveAddr, quint8& funcCode, QVector& parsedData, quint8& errorCode,QByteArray& extractedFrame,bool& isComFrame); public: /*********************************************************************** *@brief: 创建读请求帧 *@param: frame 输出参数 请求帧 *@param: type 请求类型 ***********************************************************************/ void buildReadFrame(QByteArray& frame,FunctionCode funcCode); /*********************************************************************** *@brief: 创建部分写请求帧 *@param: frame 输出参数 请求帧 *@param: type 请求类型 ***********************************************************************/ void buildWriteFrame(QByteArray& frame,FunctionCode funcCode); /*********************************************************************** *@brief: 解析读线圈响应数据 *@param: responseData 响应中的数据部分 *@param: coils 输出参数(bool类型的数组),解析出的线圈状态 *@return:是否成功解析 ***********************************************************************/ bool parseReadCoilsResponse(const QByteArray& responseData, QVector& coils); /*********************************************************************** *@brief: 解析读保持寄存器响应数据 *@param: responseData 响应中的数据部分 *@param: registers 输出参数(quint16类型的数组),解析出的寄存器值 *@return:是否成功解析 ***********************************************************************/ bool parseReadHoldingRegistersResponse(const QByteArray& responseData, QVector& registers); /*********************************************************************** *@brief: 解析单帧响应 *@param: response 从站返回的响应数据 *@param: slaveAddr 输出参数,从响应中解析出的从站地址 *@param: funcCode 输出参数,从响应中解析出的功能码 *@param: data 输出参数,解析后的数据 *@param: errorCode 输出参数,错误码(NoError表示成功) *@return:是否成功解析 ***********************************************************************/ bool parseResponse(const QByteArray& response, quint8& slaveAddr, quint8& funcCode, QVector& data, quint8& errorCode); /*********************************************************************** *@brief: 从缓冲区提取完整帧 *@param: frame 输出参数,提取的完整帧 *@return:是否成功提取到一帧 ***********************************************************************/ bool extractFrame(QByteArray& frame,bool& isComFrame); //计算CRC16校验值 quint16 calculateCRC(const QByteArray& data); //验证帧的CRC16校验 bool verifyCRC(const QByteArray& frame); //Modbus RTU协议参数 quint8 slaveAddr_; // 从站地址 FunctionCode funcCode_; // 功能码 quint16 startAddr_; // 起始地址 quint16 readCount_; // 读取数量 QVector coils_; // 线圈数据(写入) QVector registers_; // 寄存器数据(写入) //其他 QHash errMessage; //错误码描述映射表 QByteArray buffer_; // 数据缓冲区 }; #endif // MODBUS_RTU_MASTER_H