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.

147 lines
4.8 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 ADL400
import (
"encoding/binary"
"fmt"
"github.com/sigurn/crc16"
)
// readHoldingRegistersBuilder 读取保持寄存器
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) // 寄存器数量
// 计算CRC校验
crcTable := crc16.MakeTable(crc16.CRC16_MODBUS)
crc := crc16.Checksum(request[:6], crcTable)
request = append(request, byte(crc&0xFF), byte(crc>>8)) // 添加CRC低字节在前
return request
}
// 解析保持寄存器
func ReadHoldingRegisterParse(result []byte, address byte, quantity uint16) ([]uint16, error) {
// 检查响应最小长度
if len(result) < 5 {
return nil, fmt.Errorf("响应长度不足: %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])
}
}
// WriteMultipleRegisters 写入多个保持寄存器功能码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)
request = append(request, byte(crc&0xFF), byte(crc>>8)) // 添加CRC低字节在前
return request, quantity
}
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)
}
//log.Print("Modbus:写入成功")
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])
}
}