You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ntool-biz/nmodbus/protocol/packer_rtu.go

80 lines
1.9 KiB
Go

1 year ago
package protocol
import (
"bytes"
"encoding/binary"
"fmt"
"git.noahlan.cn/noahlan/nnet/packet"
"git.noahlan.cn/noahlan/ntool-biz/nmodbus/util"
)
type RTUPacker struct {
buf *bytes.Buffer
byteOrder binary.ByteOrder
}
func NewRTUPacker(byteOrder binary.ByteOrder) *RTUPacker {
p := &RTUPacker{
buf: bytes.NewBuffer(nil),
byteOrder: byteOrder,
}
return p
}
func (d *RTUPacker) Pack(header interface{}, data []byte) ([]byte, error) {
modbusHeader, ok := header.(*ModbusHeader)
if !ok {
return nil, packet.ErrWrongPacketType
}
bs := make([]byte, 2)
bs[0] = modbusHeader.Address
bs[1] = modbusHeader.Function
bs = append(bs, data...)
// calc CRC
pLen := len(bs)
crc := util.Checksum(bs[0:pLen])
bs = append(bs, []byte{0, 0}...)
d.byteOrder.PutUint16(bs[pLen:pLen+2], crc)
return bs, nil
}
func (d *RTUPacker) Unpack(data []byte) ([]packet.IPacket, error) {
d.buf.Write(data)
//nlog.Debugf("接收RTU数据: %x", data)
1 year ago
var (
packets []packet.IPacket
err error
)
pLen := d.buf.Len()
if pLen < 5 {
return packets, fmt.Errorf("RTU Frame error: packet less than 5 bytes")
}
// ModbusRTU 串口通讯 发送端在发送报文时一帧结束后必须有3.5个字符周期的间隔时间4.01ms
// 本质上不会发生粘包的情况,故此可以直接读取一个完整的包
// 否则无法处理粘包状况,这是由于协议帧没有描述具体包长度的字节
bs := d.buf.Next(d.buf.Len())
// check crc 取[0:len-2]进行计算,与最后一个字节对比最后一个字节为crc校验码
crcExpect := d.byteOrder.Uint16(bs[pLen-2 : pLen])
crcCalc := util.Checksum(bs[0 : pLen-2])
if crcCalc != crcExpect {
return nil, fmt.Errorf("RTU Frame error: CRC (expected 0x%x, got 0x%x)", crcExpect, crcCalc)
}
p := NewRTUPacket()
p.Address = bs[0]
p.Function = bs[1]
p.Data = bs[2 : pLen-2]
p.CRC = crcExpect
packets = append(packets, p)
d.buf.Reset()
return packets, err
}