evcc-io/provider/watchdog.go
2024-10-12 10:57:33 +02:00

150 lines
2.9 KiB
Go

package provider
import (
"context"
"strconv"
"sync"
"time"
"github.com/evcc-io/evcc/util"
)
type watchdogProvider struct {
mu sync.Mutex
ctx context.Context
log *util.Logger
reset *string
set Config
timeout time.Duration
cancel func()
}
func init() {
registry.AddCtx("watchdog", NewWatchDogFromConfig)
}
// NewWatchDogFromConfig creates watchDog provider
func NewWatchDogFromConfig(ctx context.Context, other map[string]interface{}) (Provider, error) {
var cc struct {
Reset *string
Set Config
Timeout time.Duration
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
o := &watchdogProvider{
ctx: ctx,
log: contextLogger(ctx, util.NewLogger("watchdog")),
reset: cc.Reset,
set: cc.Set,
timeout: cc.Timeout,
}
return o, nil
}
func (o *watchdogProvider) wdt(ctx context.Context, set func() error) {
tick := time.NewTicker(o.timeout / 2)
for range tick.C {
select {
case <-ctx.Done():
tick.Stop()
return
default:
if err := set(); err != nil {
o.log.ERROR.Println(err)
}
}
}
}
// setter is the generic setter function for watchdogProvider
// it is currently not possible to write this as a method
func setter[T comparable](o *watchdogProvider, set func(T) error, reset *T) func(T) error {
return func(val T) error {
o.mu.Lock()
// stop wdt on new write
if o.cancel != nil {
o.cancel()
o.cancel = nil
}
// start wdt on non-reset value
if reset == nil || val != *reset {
var ctx context.Context
ctx, o.cancel = context.WithCancel(context.Background())
go o.wdt(ctx, func() error {
return set(val)
})
}
o.mu.Unlock()
return set(val)
}
}
var _ SetIntProvider = (*watchdogProvider)(nil)
func (o *watchdogProvider) IntSetter(param string) (func(int64) error, error) {
set, err := NewIntSetterFromConfig(o.ctx, param, o.set)
if err != nil {
return nil, err
}
var reset *int64
if o.reset != nil {
val, err := strconv.ParseInt(*o.reset, 10, 64)
if err != nil {
return nil, err
}
reset = &val
}
return setter(o, set, reset), nil
}
var _ SetFloatProvider = (*watchdogProvider)(nil)
func (o *watchdogProvider) FloatSetter(param string) (func(float64) error, error) {
set, err := NewFloatSetterFromConfig(o.ctx, param, o.set)
if err != nil {
return nil, err
}
var reset *float64
if o.reset != nil {
val, err := strconv.ParseFloat(*o.reset, 64)
if err != nil {
return nil, err
}
reset = &val
}
return setter(o, set, reset), nil
}
var _ SetBoolProvider = (*watchdogProvider)(nil)
func (o *watchdogProvider) BoolSetter(param string) (func(bool) error, error) {
set, err := NewBoolSetterFromConfig(o.ctx, param, o.set)
if err != nil {
return nil, err
}
var reset *bool
if o.reset != nil {
val, err := strconv.ParseBool(*o.reset)
if err != nil {
return nil, err
}
reset = &val
}
return setter(o, set, reset), nil
}