Skip to content

Commit

Permalink
RCT: add retry (evcc-io#9231)
Browse files Browse the repository at this point in the history
  • Loading branch information
andig authored Aug 3, 2023
1 parent d12ff40 commit 06c17c3
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 21 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ require (
github.com/mergermarket/go-pkcs7 v0.0.0-20170926155232-153b18ea13c9
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.5.0
github.com/mlnoga/rct v0.1.2-0.20230227143934-71af1fb7dfa1
github.com/mlnoga/rct v0.1.2-0.20230731074838-03eacb926f99
github.com/muka/go-bluetooth v0.0.0-20221213043340-85dc80edc4e1
github.com/mxschmitt/golang-combinations v1.1.0
github.com/nicksnyder/go-i18n/v2 v2.2.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -822,8 +822,8 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
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.20230227143934-71af1fb7dfa1 h1:+OVWP3tJu+en06qieaXY/pcX+1qKp4X1oX9g3hMgOCk=
github.com/mlnoga/rct v0.1.2-0.20230227143934-71af1fb7dfa1/go.mod h1:0lfd2mmBnBzIvuzYtdhG+2371u+cUfIxsYErm4P9KRI=
github.com/mlnoga/rct v0.1.2-0.20230731074838-03eacb926f99 h1:sX4VwA7zgImnMqRgEkzP+3oKCgUHRF0OvHghFJ0pntY=
github.com/mlnoga/rct v0.1.2-0.20230731074838-03eacb926f99/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=
Expand Down
60 changes: 42 additions & 18 deletions meter/rct.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"
"time"

"github.com/cenkalti/backoff/v4"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"

Expand Down Expand Up @@ -42,6 +43,7 @@ meters:

// 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
}
Expand Down Expand Up @@ -80,9 +82,14 @@ func NewRCT(uri, usage string, cache time.Duration, capacity func() float64) (ap
return nil, err
}

bo := backoff.NewExponentialBackOff()
bo.MaxElapsedTime = time.Second
bo.InitialInterval = 10 * time.Millisecond

m := &RCT{
usage: strings.ToLower(usage),
conn: conn,
bo: bo,
}

// decorate api.MeterEnergy
Expand All @@ -104,24 +111,24 @@ func NewRCT(uri, usage string, cache time.Duration, capacity func() float64) (ap
func (m *RCT) CurrentPower() (float64, error) {
switch m.usage {
case "grid":
res, err := m.conn.QueryFloat32(rct.TotalGridPowerW)
return float64(res), err
res, err := m.queryFloat(rct.TotalGridPowerW)
return res, err

case "pv":
a, err := m.conn.QueryFloat32(rct.SolarGenAPowerW)
a, err := m.queryFloat(rct.SolarGenAPowerW)
if err != nil {
return 0, err
}
b, err := m.conn.QueryFloat32(rct.SolarGenBPowerW)
b, err := m.queryFloat(rct.SolarGenBPowerW)
if err != nil {
return 0, err
}
c, err := m.conn.QueryFloat32(rct.S0ExternalPowerW)
return float64(a + b + c), err
c, err := m.queryFloat(rct.S0ExternalPowerW)
return a + b + c, err

case "battery":
res, err := m.conn.QueryFloat32(rct.BatteryPowerW)
return float64(res), err
res, err := m.queryFloat(rct.BatteryPowerW)
return res, err

default:
return 0, fmt.Errorf("invalid usage: %s", m.usage)
Expand All @@ -132,24 +139,24 @@ func (m *RCT) CurrentPower() (float64, error) {
func (m *RCT) totalEnergy() (float64, error) {
switch m.usage {
case "grid":
res, err := m.conn.QueryFloat32(rct.TotalEnergyGridWh)
return float64(res / 1000), err
res, err := m.queryFloat(rct.TotalEnergyGridWh)
return res / 1000, err

case "pv":
a, err := m.conn.QueryFloat32(rct.TotalEnergySolarGenAWh)
a, err := m.queryFloat(rct.TotalEnergySolarGenAWh)
if err != nil {
return 0, err
}
b, err := m.conn.QueryFloat32(rct.TotalEnergySolarGenBWh)
return float64((a + b) / 1000), err
b, err := m.queryFloat(rct.TotalEnergySolarGenBWh)
return (a + b) / 1000, err

case "battery":
in, err := m.conn.QueryFloat32(rct.TotalEnergyBattInWh)
in, err := m.queryFloat(rct.TotalEnergyBattInWh)
if err != nil {
return 0, err
}
out, err := m.conn.QueryFloat32(rct.TotalEnergyBattOutWh)
return float64((in - out) / 1000), err
out, err := m.queryFloat(rct.TotalEnergyBattOutWh)
return (in - out) / 1000, err

default:
return 0, fmt.Errorf("invalid usage: %s", m.usage)
Expand All @@ -158,6 +165,23 @@ func (m *RCT) totalEnergy() (float64, error) {

// batterySoc implements the api.Battery interface
func (m *RCT) batterySoc() (float64, error) {
res, err := m.conn.QueryFloat32(rct.BatterySoC)
return float64(res * 100), err
res, err := m.queryFloat(rct.BatterySoC)
return res * 100, err
}

// queryFloat adds retry logic of recoverable errors to QueryFloat32
func (m *RCT) queryFloat(id rct.Identifier) (float64, error) {
started := time.Now()
m.bo.Reset()

for {
next := m.bo.NextBackOff()

res, err := m.conn.QueryFloat32(id)
if err == nil || !errors.Is(err, &rct.RecoverableError{}) || time.Since(started)+next > m.bo.MaxElapsedTime {
return float64(res), err
}

time.Sleep(next)
}
}

0 comments on commit 06c17c3

Please sign in to comment.