EEBus: fix invalid missing currents errors (#16223)
Fix a scenario were measurements were provided after the last limit change. But if the interval is set >15s and measurements even came in after 15s timeframe, this was considered to be invalid. To fix this, every time measurements are provided and the limitUpdate timestamp is not zero, meaning a limit was set, consider data to available by setting limitUpdate to zero again.
This commit is contained in:
parent
57e1de7a5d
commit
92c1aa36f4
2 changed files with 106 additions and 13 deletions
|
|
@ -39,8 +39,7 @@ type EEBus struct {
|
|||
lp loadpoint.API
|
||||
minMaxG func() (minMax, error)
|
||||
|
||||
limitUpdated time.Time // time of last limit change
|
||||
currentsUpdated time.Time // time of last measurement
|
||||
limitUpdated time.Time // time of last limit change
|
||||
|
||||
vasVW bool // wether the EVSE supports VW VAS with ISO15118-2
|
||||
enabled bool
|
||||
|
|
@ -127,8 +126,10 @@ func (c *EEBus) UseCaseEvent(device spineapi.DeviceRemoteInterface, entity spine
|
|||
c.ev = nil
|
||||
|
||||
case evcem.DataUpdateCurrentPerPhase:
|
||||
// do not use the timestamp of the measurement itself, as some devices don't provide it
|
||||
c.currentsUpdated = time.Now()
|
||||
// if there was a limit change before this measurement, set its update time to zero, as we got what we needed
|
||||
if !c.limitUpdated.IsZero() {
|
||||
c.limitUpdated = time.Time{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -334,6 +335,9 @@ func (c *EEBus) writeCurrentLimitData(evEntity spineapi.EntityRemoteInterface, c
|
|||
// if VAS VW is available, limits are completely covered by it
|
||||
// this way evcc can fully control the charging behaviour
|
||||
if c.writeLoadControlLimitsVASVW(evEntity, limits) {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
c.limitUpdated = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
|
@ -351,6 +355,9 @@ func (c *EEBus) writeCurrentLimitData(evEntity spineapi.EntityRemoteInterface, c
|
|||
// set overload protection limits
|
||||
_, err = c.uc.OpEV.WriteLoadControlLimits(evEntity, limits, nil)
|
||||
if err == nil {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
c.limitUpdated = time.Now()
|
||||
}
|
||||
|
||||
|
|
@ -567,20 +574,17 @@ func (c *EEBus) currents() (float64, float64, float64, error) {
|
|||
}
|
||||
|
||||
c.mux.Lock()
|
||||
ts := c.currentsUpdated
|
||||
lu := c.limitUpdated
|
||||
c.mux.Unlock()
|
||||
|
||||
// if there is no measurement data available within 15 seconds after the last limit change, return an error
|
||||
// only consider it an error, if there is no measurement data since 15 seconds after the last limit change
|
||||
// a measurement only being available before the last limit change is not an error, because that can happen often
|
||||
if d := ts.Sub(c.limitUpdated); d > 15*time.Second && !c.limitUpdated.IsZero() {
|
||||
// if the last limit update is not zero (meaning no measurement was provided yet)
|
||||
// only consider this an error, if the last limit update is older than 15 seconds
|
||||
// this covers the case where this function may be called shortly after setting a limit
|
||||
// but too short for a measurement can even be received
|
||||
if d := time.Now().Sub(lu); d > 15*time.Second && !lu.IsZero() {
|
||||
return 0, 0, 0, api.ErrNotAvailable
|
||||
}
|
||||
|
||||
// we got a valid measurement, reset the limitUpdate to zero
|
||||
// otherwise frequent measurement updates will trigger an unwanted error as the time between those is > 15 seconds
|
||||
c.limitUpdated = time.Time{}
|
||||
|
||||
res, err := c.uc.EvCem.CurrentPerPhase(evEntity)
|
||||
if err != nil {
|
||||
return 0, 0, 0, eebus.WrapError(err)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ package charger
|
|||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
evcemuc "github.com/enbility/eebus-go/usecases/cem/evcem"
|
||||
"github.com/enbility/eebus-go/usecases/mocks"
|
||||
spinemocks "github.com/enbility/spine-go/mocks"
|
||||
"github.com/evcc-io/evcc/server/eebus"
|
||||
|
|
@ -14,6 +16,93 @@ import (
|
|||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
// Test measurements updated after writing limits detction works
|
||||
func TestEEBusNoCurrents(t *testing.T) {
|
||||
evcc := mocks.NewCemEVCCInterface(t)
|
||||
evcem := mocks.NewCemEVCEMInterface(t)
|
||||
|
||||
evEntity := spinemocks.NewEntityRemoteInterface(t)
|
||||
eebus := &EEBus{
|
||||
uc: &eebus.UseCasesEVSE{
|
||||
EvCC: evcc,
|
||||
EvCem: evcem,
|
||||
},
|
||||
ev: evEntity,
|
||||
log: util.NewLogger("test"),
|
||||
}
|
||||
|
||||
evcc.EXPECT().EVConnected(evEntity).Return(true)
|
||||
evcem.EXPECT().IsScenarioAvailableAtEntity(evEntity, mock.Anything).Return(true)
|
||||
|
||||
// limit set 15:04:45, measurement receviced afterwards before calling currents
|
||||
eebus.limitUpdated = time.Date(2024, 9, 16, 15, 4, 45, 0, time.UTC)
|
||||
eebus.UseCaseEvent(nil, evEntity, evcemuc.DataUpdateCurrentPerPhase)
|
||||
|
||||
evcem.EXPECT().CurrentPerPhase(evEntity).Return([]float64{10.5, 10.5, 10.5}, nil).Once()
|
||||
|
||||
l1, l2, l3, err := eebus.currents()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 10.5, l1)
|
||||
assert.Equal(t, 10.5, l2)
|
||||
assert.Equal(t, 10.5, l3)
|
||||
|
||||
// limit set 15:05:09, measurement receviced afterwards before calling currents
|
||||
eebus.limitUpdated = time.Date(2024, 9, 16, 15, 5, 9, 0, time.UTC)
|
||||
eebus.UseCaseEvent(nil, evEntity, evcemuc.DataUpdateCurrentPerPhase)
|
||||
|
||||
evcem.EXPECT().CurrentPerPhase(evEntity).Return([]float64{6.6, 6.6, 6.6}, nil).Once()
|
||||
|
||||
l1, l2, l3, err = eebus.currents()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 6.6, l1)
|
||||
assert.Equal(t, 6.6, l2)
|
||||
assert.Equal(t, 6.6, l3)
|
||||
|
||||
// limit set 15:05:39, measurement received afterwards before calling currents
|
||||
eebus.limitUpdated = time.Date(2024, 9, 16, 15, 5, 39, 0, time.UTC)
|
||||
eebus.UseCaseEvent(nil, evEntity, evcemuc.DataUpdateCurrentPerPhase)
|
||||
|
||||
evcem.EXPECT().CurrentPerPhase(evEntity).Return([]float64{10.4, 10.5, 10.4}, nil).Once()
|
||||
|
||||
l1, l2, l3, err = eebus.currents()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 10.4, l1)
|
||||
assert.Equal(t, 10.5, l2)
|
||||
assert.Equal(t, 10.4, l3)
|
||||
|
||||
// limit set 15:06:09, measurement received afterwards before calling currents
|
||||
eebus.limitUpdated = time.Date(2024, 9, 16, 15, 6, 9, 0, time.UTC)
|
||||
eebus.UseCaseEvent(nil, evEntity, evcemuc.DataUpdateCurrentPerPhase)
|
||||
|
||||
evcem.EXPECT().CurrentPerPhase(evEntity).Return([]float64{10.4, 10.4, 10.4}, nil).Once()
|
||||
|
||||
l1, l2, l3, err = eebus.currents()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 10.4, l1)
|
||||
assert.Equal(t, 10.4, l2)
|
||||
assert.Equal(t, 10.4, l3)
|
||||
|
||||
// limit set 20 seconds ago, no measurement received yet
|
||||
eebus.limitUpdated = time.Now().Add(-20 * time.Second)
|
||||
|
||||
l1, l2, l3, err = eebus.currents()
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, 0.0, l1)
|
||||
assert.Equal(t, 0.0, l2)
|
||||
assert.Equal(t, 0.0, l3)
|
||||
|
||||
// now we got a measurement again
|
||||
eebus.UseCaseEvent(nil, evEntity, evcemuc.DataUpdateCurrentPerPhase)
|
||||
|
||||
evcem.EXPECT().CurrentPerPhase(evEntity).Return([]float64{10.4, 10.4, 10.4}, nil).Once()
|
||||
|
||||
l1, l2, l3, err = eebus.currents()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 10.4, l1)
|
||||
assert.Equal(t, 10.4, l2)
|
||||
assert.Equal(t, 10.4, l3)
|
||||
}
|
||||
|
||||
func TestEEBusIsCharging(t *testing.T) {
|
||||
type limitStruct struct {
|
||||
min, max, pause float64
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue