|
|
package ADL200
|
|
|
|
|
|
import (
|
|
|
"encoding/binary"
|
|
|
"fmt"
|
|
|
"github.com/sigurn/crc16"
|
|
|
)
|
|
|
|
|
|
// ReadHoldingRegistersBuilder 构造读取保持寄存器命令 (功能码 0x03)
|
|
|
func ReadHoldingRegistersBuilder(address byte, startAddr, quantity uint16) []byte {
|
|
|
// 构造 Modbus-RTU 请求帧
|
|
|
request := make([]byte, 6)
|
|
|
request[0] = address // 从站地址
|
|
|
request[1] = 0x03 // 功能码: 读取保持寄存器
|
|
|
binary.BigEndian.PutUint16(request[2:4], startAddr) // 起始地址 (大端)
|
|
|
binary.BigEndian.PutUint16(request[4:6], quantity) // 寄存器数量 (大端)
|
|
|
|
|
|
// 计算 CRC16 Modbus 校验
|
|
|
crcTable := crc16.MakeTable(crc16.CRC16_MODBUS)
|
|
|
crc := crc16.Checksum(request[:6], crcTable)
|
|
|
// 添加 CRC,低字节在前
|
|
|
request = append(request, byte(crc&0xFF), byte(crc>>8))
|
|
|
|
|
|
return request
|
|
|
}
|
|
|
|
|
|
// ReadHoldingRegisterParse 解析保持寄存器响应数据
|
|
|
func ReadHoldingRegisterParse(result []byte, address byte, quantity uint16) ([]uint16, error) {
|
|
|
// 检查响应最小长度
|
|
|
if len(result) < 5 {
|
|
|
return nil, fmt.Errorf("响应<E5938D><E5BA94><EFBFBD>度不足: %d bytes", len(result))
|
|
|
}
|
|
|
|
|
|
// 提取数据和 CRC
|
|
|
data := result[:len(result)-2]
|
|
|
receivedCRC := binary.LittleEndian.Uint16(result[len(result)-2:]) // 解析 CRC (小端)
|
|
|
|
|
|
// 校验 CRC
|
|
|
crcTable := crc16.MakeTable(crc16.CRC16_MODBUS)
|
|
|
calculatedCRC := crc16.Checksum(data, crcTable)
|
|
|
if calculatedCRC != receivedCRC {
|
|
|
return nil, fmt.Errorf("CRC校验失败,计算值: %x,接收值: %x", calculatedCRC, receivedCRC)
|
|
|
}
|
|
|
|
|
|
// 验证地址和功能码
|
|
|
if data[0] != address {
|
|
|
return nil, fmt.Errorf("响应地址不符,期望: %x,实际: %x", address, data[0])
|
|
|
}
|
|
|
|
|
|
switch data[1] {
|
|
|
case 0x03: // 正常响应
|
|
|
byteCount := data[2]
|
|
|
if len(data) < 3+int(byteCount) {
|
|
|
return nil, fmt.Errorf("响应数据不完整")
|
|
|
}
|
|
|
if int(byteCount) != 2*int(quantity) {
|
|
|
return nil, fmt.Errorf("数据长度不符,期望: %d,实际: %d", 2*quantity, byteCount)
|
|
|
}
|
|
|
|
|
|
// 解析寄存器值 (大端序)
|
|
|
registers := make([]uint16, quantity)
|
|
|
for i := 0; i < int(quantity); i++ {
|
|
|
registers[i] = binary.BigEndian.Uint16(data[3+2*i : 5+2*i])
|
|
|
}
|
|
|
return registers, nil
|
|
|
|
|
|
case 0x83: // 异常响应
|
|
|
if len(data) < 3 {
|
|
|
return nil, fmt.Errorf("异常响应格式错误")
|
|
|
}
|
|
|
return nil, fmt.Errorf("Modbus异常码: %x", data[2])
|
|
|
|
|
|
default:
|
|
|
return nil, fmt.Errorf("未知功能码: %x", data[1])
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// WriteMultipleRegistersBuilder 构造写入多个保持寄存器命令 (功能码 0x10)
|
|
|
func WriteMultipleRegistersBuilder(address byte, startAddr uint16, data []uint16) (writeData []byte, quantity uint16) {
|
|
|
// 构造 Modbus-RTU 请求帧
|
|
|
quantity = uint16(len(data))
|
|
|
byteCount := 2 * quantity
|
|
|
|
|
|
request := make([]byte, 7+byteCount)
|
|
|
request[0] = address // 从站地址
|
|
|
request[1] = 0x10 // 功能码: 写多个寄存器
|
|
|
binary.BigEndian.PutUint16(request[2:4], startAddr) // 起始地址
|
|
|
binary.BigEndian.PutUint16(request[4:6], quantity) // 寄存器数量
|
|
|
request[6] = byte(byteCount) // 字节数
|
|
|
|
|
|
// 填充数据 (大端序)
|
|
|
for i, value := range data {
|
|
|
binary.BigEndian.PutUint16(request[7+2*i:9+2*i], value)
|
|
|
}
|
|
|
|
|
|
// 计算 CRC 校验
|
|
|
crcTable := crc16.MakeTable(crc16.CRC16_MODBUS)
|
|
|
crc := crc16.Checksum(request[:7+byteCount], crcTable)
|
|
|
// 添加 CRC (低字节在前)
|
|
|
request = append(request, byte(crc&0xFF), byte(crc>>8))
|
|
|
|
|
|
return request, quantity
|
|
|
}
|
|
|
|
|
|
// WriteMultipleRegistersParse 解析写入多个保持<E4BF9D><E68C81><EFBFBD>存器响应
|
|
|
func WriteMultipleRegistersParse(address byte, startAddr uint16, quantity uint16, response []byte) error {
|
|
|
// 检查响应最小长度
|
|
|
if len(response) < 6 {
|
|
|
return fmt.Errorf("响应长度不足: %d bytes", len(response))
|
|
|
}
|
|
|
|
|
|
// 提取数据和 CRC
|
|
|
respData := response[:len(response)-2]
|
|
|
receivedCRC := binary.LittleEndian.Uint16(response[len(response)-2:])
|
|
|
|
|
|
// 校验 CRC
|
|
|
crcTable := crc16.MakeTable(crc16.CRC16_MODBUS)
|
|
|
calculatedCRC := crc16.Checksum(respData, crcTable)
|
|
|
if calculatedCRC != receivedCRC {
|
|
|
return fmt.Errorf("CRC校验失败,计算值: %x,接收值: %x", calculatedCRC, receivedCRC)
|
|
|
}
|
|
|
|
|
|
// 验证地址和功能码
|
|
|
if respData[0] != address {
|
|
|
return fmt.Errorf("响应地址不符,期望: %x,实际: %x", address, respData[0])
|
|
|
}
|
|
|
|
|
|
// 处理正常/异常响应
|
|
|
switch respData[1] {
|
|
|
case 0x10:
|
|
|
// 验证写入的地址和数量
|
|
|
respStart := binary.BigEndian.Uint16(respData[2:4])
|
|
|
respQuantity := binary.BigEndian.Uint16(respData[4:6])
|
|
|
if respStart != startAddr || respQuantity != quantity {
|
|
|
return fmt.Errorf("响应参数不匹配,地址期望: %x 实际: %x,数量期望: %d 实际: %d",
|
|
|
startAddr, respStart, quantity, respQuantity)
|
|
|
}
|
|
|
return nil // 写入成功
|
|
|
case 0x90: // 异常响应
|
|
|
if len(respData) < 3 {
|
|
|
return fmt.Errorf("异常响应格式错误")
|
|
|
}
|
|
|
return fmt.Errorf("Modbus异常码: %x", respData[2])
|
|
|
default:
|
|
|
return fmt.Errorf("未知功能码: %x", respData[1])
|
|
|
}
|
|
|
}
|