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_tcp.go

118 lines
2.3 KiB
Go

package protocol
import (
"bytes"
"encoding/binary"
"fmt"
"git.noahlan.cn/noahlan/nnet/packet"
"git.noahlan.cn/noahlan/ntool/nlog"
)
type TCPPackerOption func(*TCPPacker)
type TCPPacker struct {
buf *bytes.Buffer
header *TCPHeader
size int // 最近一次读取的 body size
byteOrder binary.ByteOrder
}
const (
tcpHeadLength = 2 + 2 + 2
tcpMaxPacketSize = 64 * 1024
)
func NewTCPPacker(byteOrder binary.ByteOrder) *TCPPacker {
p := &TCPPacker{
buf: bytes.NewBuffer(nil),
byteOrder: byteOrder,
}
p.resetFlags()
return p
}
func (d *TCPPacker) resetFlags() {
d.header = nil
d.size = -1
}
func (d *TCPPacker) Pack(header interface{}, data []byte) ([]byte, error) {
tcpHeader, ok := header.(*TCPHeader)
if !ok {
return nil, packet.ErrWrongPacketType
}
bs := make([]byte, 8)
d.byteOrder.PutUint16(bs[0:2], tcpHeader.TransactionIdentifier)
d.byteOrder.PutUint16(bs[2:4], tcpHeader.ProtocolIdentifier)
d.byteOrder.PutUint16(bs[4:6], uint16(2+len(data)))
bs[6] = tcpHeader.Address
bs[7] = tcpHeader.Function
bs = append(bs, data...)
return bs, nil
}
func (d *TCPPacker) Unpack(data []byte) ([]packet.IPacket, error) {
d.buf.Write(data)
nlog.Debugf("接收TCP数据: %x", data)
var (
packets []packet.IPacket
err error
)
if d.buf.Len() < 9 {
return packets, fmt.Errorf("TCP Frame error: packet less than 9 bytes")
}
if d.size < 0 {
if err = d.readHeader(); err != nil {
return nil, err
}
}
for (d.size - 2) <= d.buf.Len() {
p := NewTCPPacket()
p.TCPHeader = d.header
p.Data = d.buf.Next(d.size - 2)
packets = append(packets, p)
if d.buf.Len() < (tcpHeadLength + 2) {
d.resetFlags()
break
}
if err = d.readHeader(); err != nil {
return packets, err
}
}
if packets == nil || len(packets) == 0 {
d.resetFlags()
d.buf.Reset()
}
return packets, err
}
func (d *TCPPacker) readHeader() error {
header := d.buf.Next(tcpHeadLength + 2)
d.header = &TCPHeader{
TransactionIdentifier: d.byteOrder.Uint16(header[0:2]),
ProtocolIdentifier: d.byteOrder.Uint16(header[2:4]),
Length: d.byteOrder.Uint16(header[4:6]),
ModbusHeader: ModbusHeader{
Address: header[6],
Function: header[7],
},
}
d.size = int(d.header.Length)
// 最大包限定
if d.size > tcpMaxPacketSize {
return packet.ErrPacketSizeExceed
}
return nil
}