195 lines
3.6 KiB
Go
195 lines
3.6 KiB
Go
package plugin
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"slices"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/evcc-io/evcc/util"
|
|
)
|
|
|
|
type watchdogPlugin struct {
|
|
mu sync.Mutex
|
|
ctx context.Context
|
|
log *util.Logger
|
|
reset []string
|
|
initial *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{}) (Plugin, error) {
|
|
var cc struct {
|
|
Reset []string
|
|
Initial *string
|
|
Set Config
|
|
Timeout time.Duration
|
|
}
|
|
|
|
if err := util.DecodeOther(other, &cc); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
o := &watchdogPlugin{
|
|
ctx: ctx,
|
|
log: contextLogger(ctx, util.NewLogger("watchdog")),
|
|
reset: cc.Reset,
|
|
initial: cc.Initial,
|
|
set: cc.Set,
|
|
timeout: cc.Timeout,
|
|
}
|
|
|
|
return o, nil
|
|
}
|
|
|
|
func (o *watchdogPlugin) wdt(ctx context.Context, set func() error) {
|
|
for tick := time.Tick(o.timeout / 2); ; {
|
|
select {
|
|
case <-tick:
|
|
if err := set(); err != nil {
|
|
o.log.ERROR.Println(err)
|
|
}
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// setter is the generic setter function for watchdogPlugin
|
|
// it is currently not possible to write this as a method
|
|
func setter[T comparable](o *watchdogPlugin, 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 !slices.Contains(reset, val) {
|
|
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 _ IntSetter = (*watchdogPlugin)(nil)
|
|
|
|
func (o *watchdogPlugin) IntSetter(param string) (func(int64) error, error) {
|
|
set, err := o.set.IntSetter(o.ctx, param)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var reset []int64
|
|
if o.reset != nil {
|
|
for _, v := range o.reset {
|
|
val, err := strconv.ParseInt(v, 10, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reset = append(reset, val)
|
|
}
|
|
}
|
|
|
|
res := setter(o, set, reset)
|
|
if o.initial != nil {
|
|
val, err := strconv.ParseInt(*o.initial, 10, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := res(val); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
var _ FloatSetter = (*watchdogPlugin)(nil)
|
|
|
|
func (o *watchdogPlugin) FloatSetter(param string) (func(float64) error, error) {
|
|
set, err := o.set.FloatSetter(o.ctx, param)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var reset []float64
|
|
if o.reset != nil {
|
|
for _, v := range o.reset {
|
|
val, err := strconv.ParseFloat(v, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reset = append(reset, val)
|
|
}
|
|
}
|
|
|
|
res := setter(o, set, reset)
|
|
if o.initial != nil {
|
|
val, err := strconv.ParseFloat(*o.initial, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := res(val); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
var _ BoolSetter = (*watchdogPlugin)(nil)
|
|
|
|
func (o *watchdogPlugin) BoolSetter(param string) (func(bool) error, error) {
|
|
set, err := o.set.BoolSetter(o.ctx, param)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var reset []bool
|
|
if len(o.reset) > 1 {
|
|
return nil, fmt.Errorf("more than one boolean reset value")
|
|
} else if len(o.reset) == 1 {
|
|
val, err := strconv.ParseBool(o.reset[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reset = append(reset, val)
|
|
}
|
|
|
|
res := setter(o, set, reset)
|
|
if o.initial != nil {
|
|
val, err := strconv.ParseBool(*o.initial)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := res(val); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return res, nil
|
|
}
|