RCT: fix timeouts (#19264)

This commit is contained in:
andig 2025-03-01 18:37:06 +01:00 • committed by GitHub
parent b0e961efcb
commit 766a6f6b2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 20 additions and 70 deletions

3
go.mod
View file

@ -117,6 +117,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/breml/rootcerts v0.2.10 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/cronokirby/saferith v0.33.0 // indirect
@ -220,3 +221,5 @@ replace gopkg.in/yaml.v3 => github.com/andig/yaml v0.0.0-20240531135838-1ff5761a
replace github.com/grid-x/modbus => github.com/evcc-io/modbus v0.0.0-20241027151224-722a7a5ae529
replace github.com/lorenzodonini/ocpp-go => github.com/evcc-io/ocpp-go v0.0.0-20241230132027-815870498cc3
replace github.com/mlnoga/rct => github.com/andig/rct v0.1.2-0.20250301173047-991884936f23

6
go.sum
View file

@ -39,6 +39,8 @@ github.com/andig/gosunspec v0.0.0-20240918203654-860ce51d602b h1:81UMfM949I7StrR
github.com/andig/gosunspec v0.0.0-20240918203654-860ce51d602b/go.mod h1:c6P6szcR+ROkqZruOR4f6qbDKFjZX6OitPpj+yJ/r8k=
github.com/andig/mbserver v0.0.0-20230310211055-1d29cbb5820e h1:m/NTP3JWpR7M0ljLxiQU4fzR25jjhe1LDtxLMNcoNJQ=
github.com/andig/mbserver v0.0.0-20230310211055-1d29cbb5820e/go.mod h1:4VtYzTm//oUipwvO3yh0g/udTE7pYJM+U/kyAuFDsgM=
github.com/andig/rct v0.1.2-0.20250301173047-991884936f23 h1:2kEZKOc1EzKCgzSXuG/iEkEf9yc/U4ArGGGmfFcqU3M=
github.com/andig/rct v0.1.2-0.20250301173047-991884936f23/go.mod h1:KRjBDV8kuvGd2kJfN8/F0kjH8JjkFVpBpn7MhW+ACL8=
github.com/andig/yaml v0.0.0-20240531135838-1ff5761ab467 h1:JqIoHxsQSV39xaemvVRu3HOkSf5AbhQ1YEENJa2cL3Q=
github.com/andig/yaml v0.0.0-20240531135838-1ff5761ab467/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
@ -82,6 +84,8 @@ github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
@ -458,8 +462,6 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mlnoga/rct v0.1.2-0.20250202175314-91abcbf32e53 h1:wav6CUbSFXojyUwSRmMhgzIQHUuVCoS3V7BZ3nRi4vE=
github.com/mlnoga/rct v0.1.2-0.20250202175314-91abcbf32e53/go.mod h1:0lfd2mmBnBzIvuzYtdhG+2371u+cUfIxsYErm4P9KRI=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

View file

@ -1,63 +1,33 @@
package meter
import (
"context"
"encoding/binary"
"errors"
"fmt"
"math"
"strings"
"sync"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/mlnoga/rct"
)
/*
This meter supports devices implementing the RCT communication protocol, e.g. the RCT PS 6.0 with / without battery.
** Usages **
The following usages are supported:
- grid ... for reading the power imported or exported to the grid
- pv ... for reading the power produced by the pv
- battery ... for reading the power imported or exported to the battery
** Example configuration **
meters:
- name: GridMeter
type: rct
uri: 192.168.1.23
cache: 2s
usage: grid
- name: PvMeter
type: rct
uri: 192.168.1.23
cache: 2s
usage: pv
- name: BatteryMeter
type: rct
uri: 192.168.1.23
cache: 2s
usage: battery
*/
// RCT implements the api.Meter interface
type RCT struct {
bo *backoff.ExponentialBackOff
conn *rct.Connection // connection with the RCT device
usage string // grid, pv, battery
}
func init() {
registry.Add("rct", NewRCTFromConfig)
registry.AddCtx("rct", NewRCTFromConfig)
}
//go:generate go tool decorate -f decorateRCT -b *RCT -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatteryController,SetBatteryMode,func(api.BatteryMode) error" -t "api.BatteryCapacity,Capacity,func() float64"
// NewRCTFromConfig creates an RCT from generic config
func NewRCTFromConfig(other map[string]interface{}) (api.Meter, error) {
func NewRCTFromConfig(ctx context.Context, other map[string]interface{}) (api.Meter, error) {
cc := struct {
capacity `mapstructure:",squash"`
Uri, Usage string
@ -75,29 +45,25 @@ func NewRCTFromConfig(other map[string]interface{}) (api.Meter, error) {
return nil, errors.New("missing usage")
}
return NewRCT(cc.Uri, cc.Usage, cc.MinSoc, cc.MaxSoc, cc.Cache, cc.capacity.Decorator())
return NewRCT(ctx, cc.Uri, cc.Usage, cc.MinSoc, cc.MaxSoc, cc.Cache, cc.capacity.Decorator())
}
var rctMu sync.Mutex
// NewRCT creates an RCT meter
func NewRCT(uri, usage string, minSoc, maxSoc int, cache time.Duration, capacity func() float64) (api.Meter, error) {
rctMu.Lock()
defer rctMu.Unlock()
func NewRCT(ctx context.Context, uri, usage string, minSoc, maxSoc int, cache time.Duration, capacity func() float64) (api.Meter, error) {
log := util.NewLogger("rct")
conn, err := rct.NewConnection(uri, cache)
conn, err := rct.NewConnection(ctx, uri, rct.WithErrorCallback(func(err error) {
if err != nil {
log.ERROR.Println(err)
}
}), rct.WithTimeout(cache))
if err != nil {
return nil, err
}
bo := backoff.NewExponentialBackOff(
backoff.WithInitialInterval(10*time.Millisecond),
backoff.WithMaxElapsedTime(time.Second))
m := &RCT{
usage: strings.ToLower(usage),
conn: conn,
bo: bo,
}
// decorate api.MeterEnergy
@ -232,32 +198,11 @@ func (m *RCT) batterySoc() (float64, error) {
// queryFloat adds retry logic of recoverable errors to QueryFloat32
func (m *RCT) queryFloat(id rct.Identifier) (float64, error) {
m.bo.Reset()
res, err := backoff.RetryWithData(func() (float32, error) {
res, err := m.conn.QueryFloat32(id)
if err != nil && !errors.As(err, new(rct.RecoverableError)) {
err = backoff.Permanent(err)
}
return res, err
}, m.bo)
res, err := m.conn.QueryFloat32(id)
return float64(res), err
}
// queryInt32 adds retry logic of recoverable errors to QueryInt32
func (m *RCT) queryInt32(id rct.Identifier) (int32, error) {
m.bo.Reset()
res, err := backoff.RetryWithData(func() (int32, error) {
res, err := m.conn.QueryInt32(id)
if err != nil && !errors.As(err, new(rct.RecoverableError)) {
err = backoff.Permanent(err)
}
return res, err
}, m.bo)
return res, err
return m.conn.QueryInt32(id)
}