You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
4.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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])
}
}