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