From 766194d0c0cba549881577566e607fe38f655e2f Mon Sep 17 00:00:00 2001 From: andig Date: Sat, 1 Mar 2025 12:03:04 +0100 Subject: [PATCH] Ocpp: use context (#19282) --- charger/ocpp.go | 19 +++++++++++++------ charger/ocpp/connector.go | 13 ++++++++++--- charger/ocpp/cp_setup.go | 5 ++++- charger/ocpp_test.go | 9 +++++---- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/charger/ocpp.go b/charger/ocpp.go index 18907f464..15e25f5e0 100644 --- a/charger/ocpp.go +++ b/charger/ocpp.go @@ -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() } diff --git a/charger/ocpp/connector.go b/charger/ocpp/connector.go index 43cbf7a8b..258e52024 100644 --- a/charger/ocpp/connector.go +++ b/charger/ocpp/connector.go @@ -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: + } } } diff --git a/charger/ocpp/cp_setup.go b/charger/ocpp/cp_setup.go index 1b9999947..5f9d7d49d 100644 --- a/charger/ocpp/cp_setup.go +++ b/charger/ocpp/cp_setup.go @@ -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: diff --git a/charger/ocpp_test.go b/charger/ocpp_test.go index 2339c388f..4dbc72bde 100644 --- a/charger/ocpp_test.go +++ b/charger/ocpp_test.go @@ -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) }