Ocpp: use context (#19282)

This commit is contained in:
andig 2025-03-01 12:03:04 +01:00 • committed by GitHub
parent 1cafc24627
commit 766194d0c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 32 additions and 14 deletions

View file

@ -19,6 +19,7 @@ package charger
import (
"cmp"
"context"
"fmt"
"math"
"slices"
@ -50,11 +51,11 @@ type OCPP struct {
const defaultIdTag = "evcc" // RemoteStartTransaction only
func init() {
registry.Add("ocpp", NewOCPPFromConfig)
registry.AddCtx("ocpp", NewOCPPFromConfig)
}
// NewOCPPFromConfig creates a OCPP charger from generic config
func NewOCPPFromConfig(other map[string]interface{}) (api.Charger, error) {
func NewOCPPFromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
cc := struct {
StationId string
IdTag string
@ -84,7 +85,8 @@ func NewOCPPFromConfig(other map[string]interface{}) (api.Charger, error) {
stackLevelZero := cc.StackLevelZero != nil && *cc.StackLevelZero
c, err := NewOCPP(cc.StationId, cc.Connector, cc.IdTag,
c, err := NewOCPP(ctx,
cc.StationId, cc.Connector, cc.IdTag,
cc.MeterValues, cc.MeterInterval,
stackLevelZero, cc.RemoteStart,
cc.ConnectTimeout)
@ -137,7 +139,8 @@ func NewOCPPFromConfig(other map[string]interface{}) (api.Charger, error) {
//go:generate go tool decorate -f decorateOCPP -b *OCPP -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)" -t "api.CurrentGetter,GetMaxCurrent,func() (float64, error)" -t "api.PhaseSwitcher,Phases1p3p,func(int) error" -t "api.Battery,Soc,func() (float64, error)"
// NewOCPP creates OCPP charger
func NewOCPP(id string, connector int, idTag string,
func NewOCPP(ctx context.Context,
id string, connector int, idTag string,
meterValues string, meterInterval time.Duration,
stackLevelZero, remoteStart bool,
connectTimeout time.Duration,
@ -154,12 +157,14 @@ func NewOCPP(id string, connector int, idTag string,
log.DEBUG.Printf("waiting for chargepoint: %v", connectTimeout)
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(connectTimeout):
return api.ErrTimeout
case <-cp.HasConnected():
}
return cp.Setup(meterValues, meterInterval)
return cp.Setup(ctx, meterValues, meterInterval)
},
)
if err != nil {
@ -188,7 +193,9 @@ func NewOCPP(id string, connector int, idTag string,
stackLevelZero: stackLevelZero,
}
go conn.WatchDog(10 * time.Second)
if cp.HasRemoteTriggerFeature {
go conn.WatchDog(ctx, 10*time.Second)
}
return c, conn.Initialized()
}

View file

@ -1,6 +1,7 @@
package ocpp
import (
"context"
"fmt"
"strconv"
"strings"
@ -100,16 +101,22 @@ func (conn *Connector) GetScheduleLimit(duration int) (float64, error) {
// WatchDog triggers meter values messages if older than timeout.
// Must be wrapped in a goroutine.
func (conn *Connector) WatchDog(timeout time.Duration) {
func (conn *Connector) WatchDog(ctx context.Context, timeout time.Duration) {
tick := time.NewTicker(2 * time.Second)
for ; true; <-tick.C {
for {
conn.mu.Lock()
update := conn.clock.Since(conn.meterUpdated) > timeout
conn.mu.Unlock()
if update && conn.cp.HasRemoteTriggerFeature {
if update {
conn.TriggerMessageRequest(core.MeterValuesFeatureName)
}
select {
case <-ctx.Done():
return
case <-tick.C:
}
}
}

View file

@ -1,6 +1,7 @@
package ocpp
import (
"context"
"strconv"
"strings"
"time"
@ -12,7 +13,7 @@ import (
"github.com/samber/lo"
)
func (cp *CP) Setup(meterValues string, meterInterval time.Duration) error {
func (cp *CP) Setup(ctx context.Context, meterValues string, meterInterval time.Duration) error {
if err := cp.ChangeAvailabilityRequest(0, core.AvailabilityTypeOperative); err != nil {
cp.log.DEBUG.Printf("failed configuring availability: %v", err)
}
@ -136,6 +137,8 @@ func (cp *CP) Setup(meterValues string, meterInterval time.Duration) error {
if err := cp.TriggerMessageRequest(0, core.MeterValuesFeatureName); err == nil {
// wait for meter values
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(Timeout):
cp.log.WARN.Println("meter timeout")
case <-cp.meterC:

View file

@ -1,6 +1,7 @@
package charger
import (
"context"
"errors"
"testing"
"time"
@ -112,7 +113,7 @@ func (suite *ocppTestSuite) TestConnect() {
suite.Require().True(cp1.IsConnected())
// 1st charge point- local
c1, err := NewOCPP("test-1", 1, "", "", 0, false, true, ocppTestConnectTimeout)
c1, err := NewOCPP(context.TODO(), "test-1", 1, "", "", 0, false, true, ocppTestConnectTimeout)
suite.Require().NoError(err)
// status and meter values
@ -161,7 +162,7 @@ func (suite *ocppTestSuite) TestConnect() {
suite.Require().True(cp2.IsConnected())
// 2nd charge point - local
c2, err := NewOCPP("test-2", 1, "", "", 0, false, true, ocppTestConnectTimeout)
c2, err := NewOCPP(context.TODO(), "test-2", 1, "", "", 0, false, true, ocppTestConnectTimeout)
suite.Require().NoError(err)
{
@ -203,7 +204,7 @@ func (suite *ocppTestSuite) TestAutoStart() {
suite.Require().True(cp1.IsConnected())
// 1st charge point- local
c1, err := NewOCPP("test-3", 1, "", "", 0, false, false, ocppTestConnectTimeout)
c1, err := NewOCPP(context.TODO(), "test-3", 1, "", "", 0, false, false, ocppTestConnectTimeout)
suite.Require().NoError(err)
// status and meter values
@ -250,7 +251,7 @@ func (suite *ocppTestSuite) TestTimeout() {
})
// 1st charge point- local
_, err := NewOCPP("test-4", 1, "", "", 0, false, false, ocppTestConnectTimeout)
_, err := NewOCPP(context.TODO(), "test-4", 1, "", "", 0, false, false, ocppTestConnectTimeout)
suite.Require().NoError(err)
}