Loadpoint: fix coarse power-limited chargers never enable on fractional current (#31685)

This commit is contained in:
andig 2026-07-11 12:43:38 +02:00 • committed by GitHub
parent 8f570f84c1
commit 999bed78c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 42 additions and 1 deletions

View file

@ -1,6 +1,7 @@
package core
import (
"math"
"slices"
"time"
@ -166,6 +167,11 @@ func (lp *Loadpoint) effectiveMinCurrent() float64 {
if c, ok := api.Cap[api.PowerLimiter](lp.charger); ok {
if res, _, err := c.GetMinMaxPower(); err == nil && res > 0 {
chargerMin = res / (Voltage * float64(lp.minActivePhases()))
// coarse chargers truncate to full amps in setLimit, so round the
// demand up to keep the enable gate reachable (#31549)
if lp.coarseCurrent() {
chargerMin = math.Ceil(chargerMin)
}
}
}
@ -197,7 +203,13 @@ func (lp *Loadpoint) effectiveMaxCurrent() float64 {
if c, ok := api.Cap[api.PowerLimiter](lp.charger); ok {
if _, res, err := c.GetMinMaxPower(); err == nil && res > 0 {
maxCurrent = min(maxCurrent, res/(Voltage*float64(lp.maxActivePhases())))
powerMax := res / (Voltage * float64(lp.maxActivePhases()))
// match effectiveMinCurrent's rounding so a fixed power request
// (min == max) doesn't yield min > max on coarse chargers (#31549)
if lp.coarseCurrent() {
powerMax = math.Ceil(powerMax)
}
maxCurrent = min(maxCurrent, powerMax)
}
}

View file

@ -95,6 +95,35 @@ func TestEffectivePowerLimiter(t *testing.T) {
assert.Equal(t, 12.0, lp.effectiveMaxCurrent(), "max")
}
// coarse power-limited charger with fixed request must not yield min > max (#31549)
func TestEffectivePowerLimiterCoarse(t *testing.T) {
Voltage = 230
ctrl := gomock.NewController(t)
lp := NewLoadpoint(util.NewLogger("foo"), nil)
phases := float64(lp.minActivePhases())
powerLimiter := api.NewMockPowerLimiter(ctrl)
// fixed 5.5 A/phase request -> fractional, coarse charger truncates to 5 A
power := 230 * phases * 5.5
powerLimiter.EXPECT().GetMinMaxPower().Return(power, power, nil).AnyTimes()
// MockCharger does not implement api.ChargerEx -> coarseCurrent() == true
lp.charger = struct {
api.Charger
api.PowerLimiter
}{
Charger: api.NewMockCharger(ctrl),
PowerLimiter: powerLimiter,
}
minCurrent := lp.effectiveMinCurrent()
maxCurrent := lp.effectiveMaxCurrent()
assert.Equal(t, 6.0, minCurrent, "min rounded up to full amps")
assert.Equal(t, 6.0, maxCurrent, "max rounded up to full amps")
assert.LessOrEqual(t, minCurrent, maxCurrent, "min must not exceed max")
}
func TestNextPlan(t *testing.T) {
clock := clock.NewMock()