package modbus import ( "encoding/binary" "fmt" "math" ) type convert64 func([]byte) uint64 func binaryMSWLEU64(b []byte) uint64 { _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 return uint64(binary.LittleEndian.Uint16(b[0:]))<<48 | uint64(binary.LittleEndian.Uint16(b[2:]))<<32 | uint64(binary.LittleEndian.Uint16(b[4:]))<<16 | uint64(binary.LittleEndian.Uint16(b[6:])) } func binaryLSWBEU64(b []byte) uint64 { _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 return uint64(binary.BigEndian.Uint16(b[6:]))<<48 | uint64(binary.BigEndian.Uint16(b[4:]))<<32 | uint64(binary.BigEndian.Uint16(b[2:]))<<16 | uint64(binary.BigEndian.Uint16(b[0:])) } func endianessConverter64(byteOrder string) (convert64, error) { switch byteOrder { case "ABCD": // Big endian (Motorola) return binary.BigEndian.Uint64, nil case "BADC": // Big endian with bytes swapped return binaryMSWLEU64, nil case "CDAB": // Little endian with bytes swapped return binaryLSWBEU64, nil case "DCBA": // Little endian (Intel) return binary.LittleEndian.Uint64, nil } return nil, fmt.Errorf("invalid byte-order: %s", byteOrder) } // I64 - no scale func determineConverterI64(outType, byteOrder string) (fieldConverterFunc, error) { tohost, err := endianessConverter64(byteOrder) if err != nil { return nil, err } switch outType { case "native", "INT64": return func(b []byte) interface{} { return int64(tohost(b)) }, nil case "UINT64": return func(b []byte) interface{} { in := int64(tohost(b)) return uint64(in) }, nil case "FLOAT64": return func(b []byte) interface{} { in := int64(tohost(b)) return float64(in) }, nil } return nil, fmt.Errorf("invalid output data-type: %s", outType) } // U64 - no scale func determineConverterU64(outType, byteOrder string) (fieldConverterFunc, error) { tohost, err := endianessConverter64(byteOrder) if err != nil { return nil, err } switch outType { case "INT64": return func(b []byte) interface{} { return int64(tohost(b)) }, nil case "native", "UINT64": return func(b []byte) interface{} { return tohost(b) }, nil case "FLOAT64": return func(b []byte) interface{} { return float64(tohost(b)) }, nil } return nil, fmt.Errorf("invalid output data-type: %s", outType) } // F64 - no scale func determineConverterF64(outType, byteOrder string) (fieldConverterFunc, error) { tohost, err := endianessConverter64(byteOrder) if err != nil { return nil, err } switch outType { case "native", "FLOAT64": return func(b []byte) interface{} { raw := tohost(b) return math.Float64frombits(raw) }, nil } return nil, fmt.Errorf("invalid output data-type: %s", outType) } // I64 - scale func determineConverterI64Scale(outType, byteOrder string, scale float64) (fieldConverterFunc, error) { tohost, err := endianessConverter64(byteOrder) if err != nil { return nil, err } switch outType { case "native": return func(b []byte) interface{} { in := int64(tohost(b)) return int64(float64(in) * scale) }, nil case "INT64": return func(b []byte) interface{} { in := int64(tohost(b)) return int64(float64(in) * scale) }, nil case "UINT64": return func(b []byte) interface{} { in := int64(tohost(b)) return uint64(float64(in) * scale) }, nil case "FLOAT64": return func(b []byte) interface{} { in := int64(tohost(b)) return float64(in) * scale }, nil } return nil, fmt.Errorf("invalid output data-type: %s", outType) } // U64 - scale func determineConverterU64Scale(outType, byteOrder string, scale float64) (fieldConverterFunc, error) { tohost, err := endianessConverter64(byteOrder) if err != nil { return nil, err } switch outType { case "native": return func(b []byte) interface{} { in := tohost(b) return uint64(float64(in) * scale) }, nil case "INT64": return func(b []byte) interface{} { in := tohost(b) return int64(float64(in) * scale) }, nil case "UINT64": return func(b []byte) interface{} { in := tohost(b) return uint64(float64(in) * scale) }, nil case "FLOAT64": return func(b []byte) interface{} { in := tohost(b) return float64(in) * scale }, nil } return nil, fmt.Errorf("invalid output data-type: %s", outType) } // F64 - scale func determineConverterF64Scale(outType, byteOrder string, scale float64) (fieldConverterFunc, error) { tohost, err := endianessConverter64(byteOrder) if err != nil { return nil, err } switch outType { case "native", "FLOAT64": return func(b []byte) interface{} { raw := tohost(b) in := math.Float64frombits(raw) return in * scale }, nil } return nil, fmt.Errorf("invalid output data-type: %s", outType) }