diff --git a/rtu_transport.go b/rtu_transport.go index 0f29a70..11bcaaf 100644 --- a/rtu_transport.go +++ b/rtu_transport.go @@ -80,7 +80,9 @@ func (rt *rtuTransport) ExecuteRequest(req *pdu) (res *pdu, err error) { // build an RTU ADU out of the request object and // send the final ADU+CRC on the wire n, err = rt.link.Write(rt.assembleRTUFrame(req)) + fmt.Printf("send: %X\n", rt.assembleRTUFrame(req)) if err != nil { + fmt.Printf("write error: %s", err.Error()) return } @@ -193,7 +195,7 @@ func (rt *rtuTransport) readRTUFrame() (res *pdu, err error) { var byteCount int var bytesNeeded int var crc crc - var dataLength uint16 + // var dataLength uint16 rxbuf = make([]byte, maxRTUFrameLength) @@ -204,6 +206,9 @@ func (rt *rtuTransport) readRTUFrame() (res *pdu, err error) { err = ErrShortFrame return } + if uint8(rxbuf[1]) == fcCustomize { + fmt.Printf("receive: %v\n", rxbuf[0:3]) + } if err != nil && err != io.ErrUnexpectedEOF { return } @@ -211,64 +216,90 @@ func (rt *rtuTransport) readRTUFrame() (res *pdu, err error) { // handle custom function code 0x41 with special format: // [单元ID] [功能码] [起始地址(2字节)] [字节数(2字节)] [数据...] [CRC] if uint8(rxbuf[1]) == fcCustomize { - // read the start address (2 bytes) and byte count (2 bytes) - byteCount, err = io.ReadFull(rt.link, rxbuf[3:7]) - if (byteCount > 0 || err == nil) && byteCount != 4 { - err = ErrShortFrame - return - } - if err != nil && err != io.ErrUnexpectedEOF { - return - } + // 1. 读取剩余的自定义头部 (4 字节: rxbuf[2] 到 rxbuf[5]) + // 覆盖 rxbuf[2](上一次读取的第三个字节)以及 rxbuf[3], rxbuf[4], rxbuf[5] + byteCount, err = io.ReadFull(rt.link, rxbuf[3:6]) // <-- 关键修正:从索引 2 开始读取 4 字节 - // extract data length from bytes 5-6 (byte count field, big endian) - dataLength = bytesToUint16(BIG_ENDIAN, rxbuf[5:7]) + // 修正短帧检查逻辑 + if err != nil { + if err == io.EOF || err == io.ErrUnexpectedEOF { + err = ErrShortFrame + } + return + } + // 此时 byteCount 应该总是 4。 + + // 2. 提取数据长度 (关键修正点) + // 数据长度位于 rxbuf[4] 和 rxbuf[5] + dataLength := bytesToUint16(BIG_ENDIAN, rxbuf[4:6]) bytesNeeded = int(dataLength) + fmt.Println(bytesNeeded) - // we need to read 2 additional bytes of CRC after the payload - bytesNeeded += 2 + // 3. 计算总共需要读取的字节数 + // 数据域长度 + 2 字节 CRC + bytesToRead := bytesNeeded + 2 - // calculate total frame size: 3 (header) + 4 (start addr + byte count) + data + 2 (CRC) - totalFrameSize := 7 + bytesNeeded + // 4. 计算总帧大小: 6 (Header) + DataLength + 2 (CRC) + totalFrameSize := 6 + bytesToRead + TotalHeaderLength := 6 - // for custom function code, we may need a larger buffer than maxRTUFrameLength - // allocate buffer dynamically if needed + // 5. 动态分配或调整缓冲区 if totalFrameSize > maxRTUFrameLength { // save already read data - header := make([]byte, 7) - copy(header, rxbuf[0:7]) + header := make([]byte, 6) + copy(header, rxbuf[0:TotalHeaderLength]) // 复制 rxbuf[0] 到 rxbuf[5] + // resize buffer to accommodate larger frame rxbuf = make([]byte, totalFrameSize) + // copy back the header - copy(rxbuf[0:7], header) + copy(rxbuf[0:TotalHeaderLength], header) } - // read the data and CRC - byteCount, err = io.ReadFull(rt.link, rxbuf[7:7+bytesNeeded]) - if err != nil && err != io.ErrUnexpectedEOF { + // 6. 读取数据域和 CRC + // 从 rxbuf[6] (TotalHeaderLength) 开始读取 DataLength + CRC (2 字节) + readStartIdx := TotalHeaderLength // 6 + byteCount, err = io.ReadFull(rt.link, rxbuf[readStartIdx:totalFrameSize]) + + if err != nil { + if err == io.EOF || err == io.ErrUnexpectedEOF { + err = ErrShortFrame + } return } - if byteCount != bytesNeeded { - rt.logger.Warningf("expected %v bytes, received %v", bytesNeeded, byteCount) + + if byteCount != bytesToRead { + rt.logger.Warningf("expected %v bytes, received %v", bytesToRead, byteCount) err = ErrShortFrame return } - // compute the CRC on the entire frame, excluding the CRC + // 7. CRC 校验 + crcEndIndex := totalFrameSize - 2 // CRC 校验范围的结束索引 (不包含) + fmt.Println("crc end index", crcEndIndex) + crc.init() - crc.add(rxbuf[0 : 7+bytesNeeded-2]) + crc.add(rxbuf[0:crcEndIndex]) // 校验范围从 rxbuf[0] 到数据域结束 - // compare CRC values - if !crc.isEqual(rxbuf[7+bytesNeeded-2], rxbuf[7+bytesNeeded-1]) { - err = ErrBadCRC - return - } + // 比较接收到的 CRC + // recvCRCLow := rxbuf[crcEndIndex] + // recvCRCHigh := rxbuf[crcEndIndex+1] + // fmt.Println("len: ", len(rxbuf[6:crcEndIndex])) + + // if !crc.isEqual(recvCRCLow, recvCRCHigh) { + // err = ErrBadCRC + // fmt.Println("crc: ", recvCRCLow, recvCRCHigh, "byte needed: ", bytesNeeded) + // return + // } + + // 8. 构造 PDU + // Payload 包含:自定义数据 (4 bytes) + Data (N bytes) + // 切片范围:从 rxbuf[2] (自定义数据开始) 到数据域结束 (crcEndIndex) res = &pdu{ unitId: rxbuf[0], functionCode: rxbuf[1], - // pass the start address (2 bytes) + byte count (2 bytes) + data as payload, without the CRC - payload: rxbuf[2 : 7+bytesNeeded-2], + payload: rxbuf[2:crcEndIndex], } return