RCT: fix timeouts (#19264)
This commit is contained in:
parent
b0e961efcb
commit
766a6f6b2b
3 changed files with 20 additions and 70 deletions
3
go.mod
3
go.mod
|
|
@ -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
6
go.sum
|
|
@ -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=
|
||||
|
|
|
|||
81
meter/rct.go
81
meter/rct.go
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue