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