From dc719e4a157362519ea8f13e03872ecf8eb9c24f Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 18 Aug 2024 11:36:23 +0200 Subject: [PATCH] SolarEdge: workaround broken NaNs (#15464) --- provider/pipeline/pipeline.go | 7 ++++--- .../definition/meter/solaredge-hybrid.yaml | 6 +++--- util/modbus/register.go | 2 ++ util/modbus/register_test.go | 20 +++++++++++++++++++ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/provider/pipeline/pipeline.go b/provider/pipeline/pipeline.go index fe470e99a..00ef6de40 100644 --- a/provider/pipeline/pipeline.go +++ b/provider/pipeline/pipeline.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "regexp" + "strconv" "strings" xj "github.com/basgys/goxml2json" @@ -140,7 +141,7 @@ func (p *Pipeline) unpackValue(value []byte) (string, error) { // decode a hex string to a proper value // TODO reuse similar code from Modbus -func (p *Pipeline) decodeValue(value []byte) (interface{}, error) { +func (p *Pipeline) decodeValue(value []byte) (float64, error) { switch p.decode { case "float32", "ieee754": return rs485.RTUIeee754ToFloat64(value), nil @@ -164,7 +165,7 @@ func (p *Pipeline) decodeValue(value []byte) (interface{}, error) { return rs485.RTUInt32ToFloat64Swapped(value), nil } - return nil, fmt.Errorf("invalid decoding: %s", p.decode) + return 0, fmt.Errorf("invalid decoding: %s", p.decode) } func (p *Pipeline) Process(in []byte) ([]byte, error) { @@ -202,7 +203,7 @@ func (p *Pipeline) Process(in []byte) ([]byte, error) { if err != nil { return b, err } - b = []byte(fmt.Sprintf("%v", v)) + b = []byte(strconv.FormatFloat(v, 'f', -1, 64)) } return b, nil diff --git a/templates/definition/meter/solaredge-hybrid.yaml b/templates/definition/meter/solaredge-hybrid.yaml index 2e85e0c1c..791292ad4 100644 --- a/templates/definition/meter/solaredge-hybrid.yaml +++ b/templates/definition/meter/solaredge-hybrid.yaml @@ -111,7 +111,7 @@ render: | register: address: 62836 # Battery 1 Instantaneous Power type: holding - decode: float32s + decode: float32nans {{- end }} {{- if eq .usage "battery" }} power: @@ -121,7 +121,7 @@ render: | register: address: 0xE174 # Battery 1 Instantaneous Power type: holding - decode: float32s + decode: float32nans scale: -1 soc: source: modbus @@ -130,7 +130,7 @@ render: | register: address: 0xE184 # Battery 1 State of Energy (SOE) type: holding - decode: float32s + decode: float32nans batterymode: source: watchdog timeout: {{ .watchdog }} diff --git a/util/modbus/register.go b/util/modbus/register.go index 85e277564..d2aade888 100644 --- a/util/modbus/register.go +++ b/util/modbus/register.go @@ -113,6 +113,8 @@ func (r Register) DecodeFunc() (func([]byte) float64, error) { return asFloat64(encoding.Float32), nil case "float32s", "ieee754s": return asFloat64(encoding.Float32LswFirst), nil + case "float32nans": + return decodeNaN32(asFloat64(encoding.Float32LswFirst), 0xffffff7f), nil // 64 bit case "int64": diff --git a/util/modbus/register_test.go b/util/modbus/register_test.go index f13ae9aee..e1ff98d77 100644 --- a/util/modbus/register_test.go +++ b/util/modbus/register_test.go @@ -63,3 +63,23 @@ func TestEncoding(t *testing.T) { require.Equal(t, tc.out, res, tc) } } + +func TestDecoding(t *testing.T) { + tc := []struct { + r Register + in []byte + out float64 + }{ + {Register{Decode: "float32"}, []byte{0x4b, 0x3c, 0x61, 0x4e}, 12345678}, + {Register{Decode: "float32"}, []byte{0xff, 0xff, 0xff, 0x7f}, 0}, // NaN + {Register{Decode: "float32s"}, []byte{0x61, 0x4e, 0x4b, 0x3c}, 12345678}, + {Register{Decode: "float32s"}, []byte{0xff, 0x7f, 0xff, 0xff}, 0}, // NaN swapped + {Register{Decode: "float32nans"}, []byte{0xff, 0xff, 0xff, 0x7f}, 0}, // NaN + } + + for _, tc := range tc { + fun, err := tc.r.DecodeFunc() + require.NoError(t, err, tc) + require.Equal(t, tc.out, fun(tc.in), tc) + } +}