package msgdata import ( "bytes" "encoding/binary" "fmt" "pbx-api-gin/pkg/lfshook" ) // Protocol 定义协议数据结构 type Protocol struct { StartBytes [3]byte // 协议开始符 SourceID uint8 // 源设备号 DestinationID uint8 // 目的设备号 MessageID uint8 // 消息号 DataLength uint16 // 数据长度 Data []byte // 数据 Checksum uint8 // 异或校验码 EndByte uint8 // 协议结束符 } // NewProtocol 创建一个新的 Protocol 实例 func NewProtocol() *Protocol { return &Protocol{ StartBytes: [3]byte{0x7F, 0x8E, 0x9D}, EndByte: 0xFE, } } // Encode 将 Protocol 结构体编码为字节切片 func (p *Protocol) Encode() ([]byte, error) { // 初始化字节缓冲区 var buf bytes.Buffer // 写入协议开始符 buf.Write(p.StartBytes[:]) //init src and dst ID p.SourceID = 0x02 p.DestinationID = 0x01 // 写入源设备号、目的设备号和消息号 binary.Write(&buf, binary.BigEndian, p.SourceID) binary.Write(&buf, binary.BigEndian, p.DestinationID) binary.Write(&buf, binary.BigEndian, p.MessageID) // 写入数据长度 binary.Write(&buf, binary.BigEndian, p.DataLength) // 写入数据 buf.Write(p.Data[:]) // 计算校验码 checksum := p.CalculateChecksum() p.Checksum = checksum // 写入校验码 binary.Write(&buf, binary.BigEndian, p.Checksum) // 写入协议结束符 binary.Write(&buf, binary.BigEndian, p.EndByte) return buf.Bytes(), nil } // CalculateChecksum 计算校验码 func (p *Protocol) CalculateChecksum() uint8 { // 初始化校验码 checksum := uint8(0) // 跳过协议开始符 data := append([]byte{byte(p.SourceID), byte(p.DestinationID), byte(p.MessageID)}, append([]byte{byte(p.DataLength >> 8), byte(p.DataLength)}, p.Data...)...) // 计算校验码 for _, b := range data { checksum ^= b } return checksum } // Decode 解码字节切片为 Protocol 结构体 func Decode(data []byte) (*Protocol, error) { if len(data) < 10 { // 最小长度:3(StartBytes) +1(SourceID) +1(DestinationID) +1(MessageID) +2(DataLength) +1(Checksum) +1(EndByte) return nil, fmt.Errorf("data too short") } p := NewProtocol() // 读取协议开始符 copy(p.StartBytes[:], data[:3]) // 读取源设备号、目的设备号和消息号 p.SourceID = data[3] p.DestinationID = data[4] p.MessageID = data[5] // 读取数据长度 p.DataLength = binary.BigEndian.Uint16(data[6:8]) // 读取数据 dataLength := int(p.DataLength) if len(data) < 10+dataLength { return nil, fmt.Errorf("data length mismatch") } p.Data = data[8 : 8+dataLength] // 读取校验码 p.Checksum = data[8+dataLength] // 读取协议结束符 p.EndByte = data[9+dataLength] // 验证校验码 if p.Checksum != p.CalculateChecksum() { return nil, fmt.Errorf("checksum mismatch") } return p, nil } func SubstrByRune(s string, start, length int) string { runes := []rune(s) if start >= len(runes) { return "" } end := start + length if end > len(runes) { end = len(runes) } return string(runes[start:end]) } func ExtractPacket(buf *bytes.Buffer) ([]byte, error) { data := buf.Bytes() leng := buf.Len() //lfshook.NewLogger().Logger.Infof("========start=========ExtractPacket raw data len=%d, hex=%x", leng, data) // Step 1: 查找起始标记 0x7f 0x8e 0x9d startIdx := -1 for i := 0; i <= len(data)-3; i++ { // <= len-3 防止越界 if data[i] == 0x7f && data[i+1] == 0x8e && data[i+2] == 0x9d { startIdx = i break } } if startIdx == -1 { //无起始标记:保守策略——丢弃过长脏数据,保留最后 MAX_LOOKAHEAD 字节防漏包 const MAX_LOOKAHEAD = 32 if leng > MAX_LOOKAHEAD { buf.Next(leng - MAX_LOOKAHEAD) lfshook.NewLogger().Logger.Warnf("no start marker found, trimmed buffer to last %d bytes", MAX_LOOKAHEAD) } return nil, nil // 明确告知:需继续读取 } // Step 2: 安全读取 DataLength(偏移 startIdx+6 ~ startIdx+8) if startIdx+8 > len(data) { lfshook.NewLogger().Logger.Infof("startIdx=%d, need data[%d:%d] for DataLength but len=%d → insufficient data", startIdx, startIdx+6, startIdx+8, len(data)) return nil, nil // 数据不够,等下次读取 } dataLen := binary.BigEndian.Uint16(data[startIdx+6 : startIdx+8]) //lfshook.NewLogger().Logger.Infof("ExtractPacket datalen=%d (0x%04x) at offset %d", dataLen, dataLen, startIdx+6) // Step 3: 计算理论包结束位置:startIdx + 8(头长) + dataLen + 1(0xFE) endPosTheo := startIdx + 8 + int(dataLen) + 1 if endPosTheo > len(data) { lfshook.NewLogger().Logger.Infof("theoretical endPos=%d > data len=%d → need more data", endPosTheo, len(data)) return nil, nil // 包未收全,等下次读取 } //lfshook.NewLogger().Logger.Infof("t=================end================%x", data[endPosTheo]) // Step 4: 在 [startIdx+8+dataLen, endPosTheo] 范围内查找 0xFE(容错:允许在理论位置附近 1~2 字节浮动) searchStart := startIdx + 8 + int(dataLen) searchEnd := endPosTheo if searchEnd > len(data) { searchEnd = len(data) // 安全截断 } endIdx := -1 for i := searchStart; i <= searchEnd; i++ { if data[i] == 0xFE { endIdx = i break } } if endIdx == -1 { lfshook.NewLogger().Logger.Infof("0xFE not found in expected range [%d,%d), dataLen=%d", searchStart, searchEnd, dataLen) return nil, nil // 结束符缺失,但可能是延迟到达,继续等 } //找到完整包:[startIdx, endIdx](含两端) packetLen := endIdx - startIdx + 1 packet := make([]byte, packetLen) copy(packet, data[startIdx:endIdx+1]) // 从 buffer 中移除已处理部分 buf.Next(endIdx + 1) //lfshook.NewLogger().Logger.Infof("extracted packet len=%d, start=0x7f8e9d, end=0xFE", packetLen) //lfshook.NewLogger().Logger.Infof("=============return============== %x", packet) return packet, nil }