package modbus import ( "fmt" "math" ) // orderWords returns the registers in big-word-first order, reversing them when // the register declares little word order. The slice is copied so the caller's // data is left untouched. func (r Register) orderWords(words []uint16) []uint16 { if !r.littleWordOrder() { return words } out := make([]uint16, len(words)) for i, w := range words { out[len(words)-1-i] = w } return out } // decode converts the raw registers into the register's numeric value and // applies scale/offset. The result type is int64 or float64 per dataType. func (r Register) decode(words []uint16) (any, error) { w := r.orderWords(words) var raw float64 var rawInt int64 switch r.encoding() { case "uint16": rawInt = int64(w[0]) raw = float64(w[0]) case "int16": rawInt = int64(int16(w[0])) raw = float64(int16(w[0])) case "uint32": u := uint32(w[0])<<16 | uint32(w[1]) rawInt = int64(u) raw = float64(u) case "int32": u := uint32(w[0])<<16 | uint32(w[1]) rawInt = int64(int32(u)) raw = float64(int32(u)) case "float32": u := uint32(w[0])<<16 | uint32(w[1]) raw = float64(math.Float32frombits(u)) case "float64": u := uint64(w[0])<<48 | uint64(w[1])<<32 | uint64(w[2])<<16 | uint64(w[3]) raw = math.Float64frombits(u) default: return nil, fmt.Errorf("modbus: unknown encoding %q", r.Encoding) } // Integer fast-path: no scaling/offset, integer encoding. if r.scale() == 1 && r.Offset == 0 { switch r.encoding() { case "uint16", "int16", "uint32", "int32": return rawInt, nil } } return raw*r.scale() + r.Offset, nil } // encode converts a value destined for a Write back into raw registers, // inverting scale/offset. Only the holding-register encodings are writable. func (r Register) encode(value float64) ([]uint16, error) { v := (value - r.Offset) / r.scale() var words []uint16 switch r.encoding() { case "uint16": words = []uint16{uint16(int64(math.Round(v)))} case "int16": words = []uint16{uint16(int16(int64(math.Round(v))))} case "uint32": u := uint32(int64(math.Round(v))) words = []uint16{uint16(u >> 16), uint16(u)} case "int32": u := uint32(int32(int64(math.Round(v)))) words = []uint16{uint16(u >> 16), uint16(u)} case "float32": u := math.Float32bits(float32(v)) words = []uint16{uint16(u >> 16), uint16(u)} case "float64": u := math.Float64bits(v) words = []uint16{uint16(u >> 48), uint16(u >> 32), uint16(u >> 16), uint16(u)} default: return nil, fmt.Errorf("modbus: unknown encoding %q", r.Encoding) } return r.orderWords(words), nil }