package modbus import ( "encoding/binary" "fmt" ) // ProtocolDataUnit (PDU) is independent of underlying communication layers. type ProtocolDataUnit struct { FuncCode byte Data []byte } func EncodeRTUFrame(slaveID byte, pdu ProtocolDataUnit) ([]byte, error) { length := len(pdu.Data) + 4 if length > rtuAduMaxSize { return nil, fmt.Errorf("modbus: length of data '%v' must not be bigger than '%v'", length, rtuAduMaxSize) } requestAdu := make([]byte, 0, length) requestAdu = append(requestAdu, slaveID, pdu.FuncCode) requestAdu = append(requestAdu, pdu.Data...) checksum := CRC16(requestAdu) requestAdu = append(requestAdu, byte(checksum), byte(checksum>>8)) return requestAdu, nil } // DecodeRTUFrame decode extracts slaveID and PDU from RTU frame and verify CRC. func DecodeRTUFrame(adu []byte) (uint8, []byte, error) { if len(adu) < rtuAduMinSize { // Minimum size (including address, funcCode and CRC) return 0, nil, fmt.Errorf("modbus: response length '%v' does not meet minimum '%v'", len(adu), rtuAduMinSize) } // Calculate checksum crc := CRC16(adu[0 : len(adu)-2]) expect := binary.LittleEndian.Uint16(adu[len(adu)-2:]) if crc != expect { return 0, nil, fmt.Errorf("modbus: response crc '%x' does not match expected '%x'", expect, crc) } // slaveID & PDU but pass crc return adu[0], adu[1 : len(adu)-2], nil } // Verify verify confirms valid data(including slaveID,funcCode,response data) //其中rspPDU = ProtocolDataUnit{pdu[0], pdu[1:]} func Verify(reqSlaveID, rspSlaveID uint8, reqPDU, rspPDU ProtocolDataUnit) error { switch { case reqSlaveID != rspSlaveID: // Check slaveID same return fmt.Errorf("modbus: response slave id '%v' does not match request '%v'", rspSlaveID, reqSlaveID) case rspPDU.FuncCode != reqPDU.FuncCode: // Check correct function code returned (exception) return responseError(rspPDU) case rspPDU.Data == nil || len(rspPDU.Data) == 0: // check Empty response return fmt.Errorf("modbus: response data is empty") } return nil } // responseError response error func responseError(response ProtocolDataUnit) error { mbError := &ExceptionError{} if response.Data != nil && len(response.Data) > 0 { mbError.ExceptionCode = response.Data[0] } return mbError }