Merge pull request #22 from andig/getter
Improve caching implementation
This commit is contained in:
commit
4dc9984358
7 changed files with 162 additions and 78 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -7,3 +7,4 @@ evcc
|
|||
linux-*.Dockerfile
|
||||
!evcc.dist.yaml
|
||||
!modules/**
|
||||
dist
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ release:
|
|||
before:
|
||||
hooks:
|
||||
# you may remove this if you don't need go generate
|
||||
- go generate ./...
|
||||
- make assets
|
||||
builds:
|
||||
- id: evcc
|
||||
# Path to main.go file or main package.
|
||||
main: main.go
|
||||
ldflags:
|
||||
- -X github.com/andig/evcc.Version={{.Version}} -X github.com/andig/evcc.Commit={{.ShortCommit}}
|
||||
- -X github.com/andig/evcc/server.Version={{.Version}} -X github.com/andig/evcc/server.Commit={{.ShortCommit}}
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
|
|
@ -25,6 +25,17 @@ builds:
|
|||
- arm
|
||||
goarm:
|
||||
- "6"
|
||||
archives:
|
||||
- builds:
|
||||
- evcc
|
||||
replacements:
|
||||
amd64: 64-bit
|
||||
386: 32-bit
|
||||
darwin: macOS
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
|
|
|
|||
|
|
@ -2,107 +2,126 @@ package provider
|
|||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/benbjohnson/clock"
|
||||
)
|
||||
|
||||
// CacheGetter wraps a getter with a cache
|
||||
type CacheGetter struct {
|
||||
// Cached wraps a getter with a cache
|
||||
type Cached struct {
|
||||
clck clock.Clock
|
||||
updated time.Time
|
||||
cache time.Duration
|
||||
getter interface{}
|
||||
val interface{}
|
||||
}
|
||||
|
||||
// NewCacheGetter wraps a getter with a cache
|
||||
func NewCacheGetter(getter interface{}, cache time.Duration) *CacheGetter {
|
||||
if g, ok := getter.(func() (float64, error)); ok {
|
||||
getter = FloatGetter(g)
|
||||
}
|
||||
if g, ok := getter.(func() (int64, error)); ok {
|
||||
getter = IntGetter(g)
|
||||
}
|
||||
|
||||
return &CacheGetter{
|
||||
// NewCached wraps a getter with a cache
|
||||
func NewCached(getter interface{}, cache time.Duration) *Cached {
|
||||
return &Cached{
|
||||
clck: clock.New(),
|
||||
getter: getter,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
// FloatGetter gets float value
|
||||
func (c *CacheGetter) FloatGetter() (float64, error) {
|
||||
if time.Since(c.updated) > c.cache {
|
||||
g, ok := c.getter.(FloatGetter)
|
||||
if !ok {
|
||||
func (c *Cached) FloatGetter() FloatGetter {
|
||||
g, ok := c.getter.(FloatGetter)
|
||||
if !ok {
|
||||
if g, ok = c.getter.(func() (float64, error)); !ok {
|
||||
log.FATAL.Fatalf("invalid type: %T", c.getter)
|
||||
}
|
||||
|
||||
val, err := g()
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
c.updated = time.Now()
|
||||
c.val = val
|
||||
g = FloatGetter(g)
|
||||
}
|
||||
|
||||
return c.val.(float64), nil
|
||||
return FloatGetter(func() (float64, error) {
|
||||
if c.clck.Since(c.updated) > c.cache {
|
||||
val, err := g()
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
c.updated = c.clck.Now()
|
||||
c.val = val
|
||||
}
|
||||
|
||||
return c.val.(float64), nil
|
||||
})
|
||||
}
|
||||
|
||||
// IntGetter gets int value
|
||||
func (c *CacheGetter) IntGetter() (int64, error) {
|
||||
if time.Since(c.updated) > c.cache {
|
||||
g, ok := c.getter.(IntGetter)
|
||||
if !ok {
|
||||
func (c *Cached) IntGetter() IntGetter {
|
||||
g, ok := c.getter.(IntGetter)
|
||||
if !ok {
|
||||
if g, ok = c.getter.(func() (int64, error)); !ok {
|
||||
log.FATAL.Fatalf("invalid type: %T", c.getter)
|
||||
}
|
||||
|
||||
val, err := g()
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
c.updated = time.Now()
|
||||
c.val = val
|
||||
g = IntGetter(g)
|
||||
}
|
||||
|
||||
return c.val.(int64), nil
|
||||
return IntGetter(func() (int64, error) {
|
||||
if c.clck.Since(c.updated) > c.cache {
|
||||
val, err := g()
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
c.updated = c.clck.Now()
|
||||
c.val = val
|
||||
}
|
||||
|
||||
return c.val.(int64), nil
|
||||
})
|
||||
}
|
||||
|
||||
// StringGetter gets string value
|
||||
func (c *CacheGetter) StringGetter() (string, error) {
|
||||
if time.Since(c.updated) > c.cache {
|
||||
g, ok := c.getter.(StringGetter)
|
||||
if !ok {
|
||||
func (c *Cached) StringGetter() StringGetter {
|
||||
g, ok := c.getter.(StringGetter)
|
||||
if !ok {
|
||||
if g, ok = c.getter.(func() (string, error)); !ok {
|
||||
log.FATAL.Fatalf("invalid type: %T", c.getter)
|
||||
}
|
||||
|
||||
val, err := g()
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
c.updated = time.Now()
|
||||
c.val = val
|
||||
g = StringGetter(g)
|
||||
}
|
||||
|
||||
return c.val.(string), nil
|
||||
return StringGetter(func() (string, error) {
|
||||
if c.clck.Since(c.updated) > c.cache {
|
||||
|
||||
val, err := g()
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
c.updated = c.clck.Now()
|
||||
c.val = val
|
||||
}
|
||||
|
||||
return c.val.(string), nil
|
||||
})
|
||||
}
|
||||
|
||||
// BoolGetter gets bool value
|
||||
func (c *CacheGetter) BoolGetter() (bool, error) {
|
||||
if time.Since(c.updated) > c.cache {
|
||||
g, ok := c.getter.(BoolGetter)
|
||||
if !ok {
|
||||
func (c *Cached) BoolGetter() BoolGetter {
|
||||
g, ok := c.getter.(BoolGetter)
|
||||
if !ok {
|
||||
if g, ok = c.getter.(func() (bool, error)); !ok {
|
||||
log.FATAL.Fatalf("invalid type: %T", g)
|
||||
}
|
||||
|
||||
val, err := g()
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
c.updated = time.Now()
|
||||
c.val = val
|
||||
g = BoolGetter(g)
|
||||
}
|
||||
|
||||
return c.val.(bool), nil
|
||||
return BoolGetter(func() (bool, error) {
|
||||
if c.clck.Since(c.updated) > c.cache {
|
||||
|
||||
val, err := g()
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
|
||||
c.updated = c.clck.Now()
|
||||
c.val = val
|
||||
}
|
||||
|
||||
return c.val.(bool), nil
|
||||
})
|
||||
}
|
||||
|
|
|
|||
53
provider/cache_test.go
Normal file
53
provider/cache_test.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/benbjohnson/clock"
|
||||
)
|
||||
|
||||
func TestCachedGetter(t *testing.T) {
|
||||
var idx int
|
||||
cases := []struct {
|
||||
f float64
|
||||
e error
|
||||
}{
|
||||
{f: 1, e: nil},
|
||||
{f: 2, e: nil},
|
||||
{f: 3, e: errors.New("3")},
|
||||
}
|
||||
|
||||
g := func() (float64, error) {
|
||||
f := cases[idx].f
|
||||
e := cases[idx].e
|
||||
idx++
|
||||
return f, e
|
||||
}
|
||||
|
||||
duration := time.Second
|
||||
c := NewCached(g, duration)
|
||||
clck := clock.NewMock()
|
||||
c.clck = clck
|
||||
getter := c.FloatGetter()
|
||||
|
||||
expect := func(s struct {
|
||||
f float64
|
||||
e error
|
||||
}) {
|
||||
f, e := getter()
|
||||
if f != s.f || e != s.e {
|
||||
t.Errorf("unexpected cache value: %f, %v\n", f, e)
|
||||
}
|
||||
}
|
||||
|
||||
expect(cases[0])
|
||||
expect(cases[0])
|
||||
|
||||
clck.Add(2 * duration)
|
||||
expect(cases[1])
|
||||
|
||||
clck.Add(2 * duration)
|
||||
expect(cases[2])
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ func NewStringGetterFromConfig(pc *Config) (res StringGetter) {
|
|||
}
|
||||
|
||||
if pc.Cache > 0 {
|
||||
res = NewCacheGetter(res, pc.Cache).StringGetter
|
||||
res = NewCached(res, pc.Cache).StringGetter()
|
||||
}
|
||||
|
||||
return
|
||||
|
|
@ -56,7 +56,7 @@ func NewBoolGetterFromConfig(pc *Config) (res BoolGetter) {
|
|||
}
|
||||
|
||||
if pc.Cache > 0 {
|
||||
res = NewCacheGetter(res, pc.Cache).BoolGetter
|
||||
res = NewCached(res, pc.Cache).BoolGetter()
|
||||
}
|
||||
|
||||
return
|
||||
|
|
@ -84,7 +84,7 @@ func NewFloatGetterFromConfig(pc *Config) (res FloatGetter) {
|
|||
}
|
||||
|
||||
if pc.Cache > 0 {
|
||||
res = NewCacheGetter(res, pc.Cache).FloatGetter
|
||||
res = NewCached(res, pc.Cache).FloatGetter()
|
||||
}
|
||||
|
||||
return
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ type Audi struct {
|
|||
token string
|
||||
tokenValid time.Time
|
||||
cache time.Duration
|
||||
chargeStateG *provider.CacheGetter
|
||||
chargeStateG provider.FloatGetter
|
||||
}
|
||||
|
||||
type audiTokenResponse struct {
|
||||
|
|
@ -79,7 +79,7 @@ func NewAudiFromConfig(log *api.Logger, other map[string]interface{}) api.Vehicl
|
|||
cache: cc.Cache,
|
||||
}
|
||||
|
||||
v.chargeStateG = provider.NewCacheGetter(v.chargeState, cc.Cache)
|
||||
v.chargeStateG = provider.NewCached(v.chargeState, cc.Cache).FloatGetter()
|
||||
|
||||
return v
|
||||
}
|
||||
|
|
@ -202,5 +202,5 @@ func (v *Audi) chargeState() (float64, error) {
|
|||
|
||||
// ChargeState implements the Vehicle.ChargeState interface
|
||||
func (v *Audi) ChargeState() (float64, error) {
|
||||
return v.chargeStateG.FloatGetter()
|
||||
return v.chargeStateG()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import (
|
|||
type Tesla struct {
|
||||
*embed
|
||||
vehicle *tesla.Vehicle
|
||||
chargeStateG *provider.CacheGetter
|
||||
chargedEnergyG *provider.CacheGetter
|
||||
chargeStateG provider.FloatGetter
|
||||
chargedEnergyG provider.FloatGetter
|
||||
}
|
||||
|
||||
// NewTeslaFromConfig creates a new Tesla vehicle
|
||||
|
|
@ -47,8 +47,8 @@ func NewTeslaFromConfig(log *api.Logger, other map[string]interface{}) api.Vehic
|
|||
vehicle: vehicles[0].Vehicle,
|
||||
}
|
||||
|
||||
v.chargeStateG = provider.NewCacheGetter(v.chargeState, cc.Cache)
|
||||
v.chargedEnergyG = provider.NewCacheGetter(v.chargedEnergy, cc.Cache)
|
||||
v.chargeStateG = provider.NewCached(v.chargeState, cc.Cache).FloatGetter()
|
||||
v.chargedEnergyG = provider.NewCached(v.chargedEnergy, cc.Cache).FloatGetter()
|
||||
|
||||
return v
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ func (v *Tesla) chargeState() (float64, error) {
|
|||
|
||||
// ChargeState implements the Vehicle.ChargeState interface
|
||||
func (v *Tesla) ChargeState() (float64, error) {
|
||||
return v.chargeStateG.FloatGetter()
|
||||
return v.chargeStateG()
|
||||
}
|
||||
|
||||
// chargedEnergy implements the ChargeRater.ChargedEnergy interface
|
||||
|
|
@ -72,7 +72,7 @@ func (v *Tesla) chargedEnergy() (float64, error) {
|
|||
|
||||
// ChargedEnergy implements the ChargeRater.ChargedEnergy interface
|
||||
func (v *Tesla) ChargedEnergy() (float64, error) {
|
||||
return v.chargedEnergyG.FloatGetter()
|
||||
return v.chargedEnergyG()
|
||||
}
|
||||
|
||||
// depends on https://github.com/jsgoecke/tesla/issues/28
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue