evcc-io/charger/eebus_test.go
Andreas Linde 92c1aa36f4
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.
2024-09-20 03:19:43 +02:00

267 lines
6.7 KiB
Go

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"
"github.com/evcc-io/evcc/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"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
}
type testMeasurementStruct struct {
charging bool
currents []float64
powers []float64
}
tests := []struct {
name string
limits []limitStruct
measurements []testMeasurementStruct
}{
{
"3 phase IEC",
[]limitStruct{
{6, 16, 0},
{6, 16, 0},
{6, 16, 0},
},
[]testMeasurementStruct{
{
false,
[]float64{0, 3, 0},
[]float64{0, 690, 0},
},
{
true,
[]float64{6, 0, 1},
[]float64{1380, 0, 230},
},
},
},
{
"1 phase IEC",
[]limitStruct{
{6, 16, 0},
},
[]testMeasurementStruct{
{
false,
[]float64{2},
[]float64{460},
},
{
true,
[]float64{6},
[]float64{1380},
},
},
},
{
"3 phase ISO",
[]limitStruct{
{2.2, 16, 0.1},
{2.2, 16, 0.1},
{2.2, 16, 0.1},
},
[]testMeasurementStruct{
{
false,
[]float64{1, 0, 0},
[]float64{230, 0, 0},
},
{
true,
[]float64{1.8, 1, 3},
[]float64{414, 230, 690},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var limitsMin, limitsMax, limitsDefault []float64
for _, limit := range tc.limits {
limitsMin = append(limitsMin, limit.min)
limitsMax = append(limitsMax, limit.max)
limitsDefault = append(limitsDefault, limit.pause)
}
for _, m := range tc.measurements {
ctrl := gomock.NewController(t)
evcc := mocks.NewCemEVCCInterface(t)
evcem := mocks.NewCemEVCEMInterface(t)
opev := mocks.NewCemOPEVInterface(t)
evEntity := spinemocks.NewEntityRemoteInterface(t)
eebus := &EEBus{
uc: &eebus.UseCasesEVSE{
EvCC: evcc,
EvCem: evcem,
OpEV: opev,
},
ev: evEntity,
}
evcc.EXPECT().EVConnected(evEntity).Return(true)
evcem.EXPECT().IsScenarioAvailableAtEntity(evEntity, mock.Anything).Return(true)
evcem.EXPECT().PowerPerPhase(evEntity).Return(m.powers, nil)
opev.EXPECT().CurrentLimits(evEntity).Return(limitsMin, limitsMax, limitsDefault, nil)
require.Equal(t, m.charging, eebus.isCharging(evEntity))
ctrl.Finish()
}
})
}
}
func TestEEBusCurrentPower(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)
evcem.EXPECT().PowerPerPhase(evEntity).Return([]float64{600, 600, 600}, nil)
power, err := eebus.currentPower()
require.NoError(t, err)
assert.Equal(t, 1800.0, power)
}
func TestEEBusCurrentPower_Elli(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)
evcem.EXPECT().PowerPerPhase(evEntity).Return(nil, errors.New("error"))
evcem.EXPECT().CurrentPerPhase(evEntity).Return([]float64{5.8, 5.8, 5.8}, nil)
power, err := eebus.currentPower()
require.NoError(t, err)
assert.Equal(t, 4002.0, power)
}