EEBus: apply the configured failsafe limits consistently (#33288)
This commit is contained in:
parent
f193eb199a
commit
490f209e57
2 changed files with 69 additions and 11 deletions
|
|
@ -232,6 +232,9 @@ func (c *EEBus) run() error {
|
|||
if c.failsafeProductionLimit != nil {
|
||||
// production limit is negative, failsafe limits are always positive
|
||||
c.setProductionLimit(-*c.failsafeProductionLimit, true)
|
||||
} else {
|
||||
// no failsafe limit configured, release any stale Energy Guard limit
|
||||
c.setProductionLimit(0, false)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -352,6 +355,24 @@ func (c *EEBus) setProductionLimit(limit float64, active bool) {
|
|||
}
|
||||
}
|
||||
|
||||
// effectiveConsumptionLimit returns the LPC limit in force: the configured failsafe
|
||||
// limit while in failsafe, else the Energy Guard's. Caller holds the mutex.
|
||||
func (c *EEBus) effectiveConsumptionLimit() float64 {
|
||||
if c.status == StatusFailsafe {
|
||||
return c.failsafeConsumptionLimit
|
||||
}
|
||||
return c.consumptionLimit.Value
|
||||
}
|
||||
|
||||
// effectiveProductionLimit returns the LPP limit in force as positive watts, see
|
||||
// effectiveConsumptionLimit. LPP states its limits as negative watts.
|
||||
func (c *EEBus) effectiveProductionLimit() float64 {
|
||||
if c.status == StatusFailsafe && c.failsafeProductionLimit != nil {
|
||||
return *c.failsafeProductionLimit
|
||||
}
|
||||
return -c.productionLimit.Value
|
||||
}
|
||||
|
||||
var _ api.HEMS = (*EEBus)(nil)
|
||||
|
||||
// CurtailedPercent implements api.HEMS, converting the active LPP production
|
||||
|
|
@ -372,8 +393,7 @@ func (c *EEBus) CurtailedPercent() *int {
|
|||
|
||||
percent := 100
|
||||
if limitActive(c.productionLimitActivated) {
|
||||
// production limits are negative watts
|
||||
percent = int(-c.productionLimit.Value / c.productionNominalMax * 100)
|
||||
percent = int(c.effectiveProductionLimit() / c.productionNominalMax * 100)
|
||||
}
|
||||
|
||||
return &percent
|
||||
|
|
@ -390,10 +410,7 @@ func (c *EEBus) MaxConsumptionPower() *float64 {
|
|||
if !limitActive(c.consumptionLimitActivated) {
|
||||
return new(0.0)
|
||||
}
|
||||
if c.status == StatusFailsafe {
|
||||
return new(c.failsafeConsumptionLimit)
|
||||
}
|
||||
return new(c.consumptionLimit.Value)
|
||||
return new(c.effectiveConsumptionLimit())
|
||||
}
|
||||
|
||||
// MaxProductionPower implements api.HEMS: nil until first connected,
|
||||
|
|
@ -407,9 +424,5 @@ func (c *EEBus) MaxProductionPower() *float64 {
|
|||
if !limitActive(c.productionLimitActivated) {
|
||||
return new(0.0)
|
||||
}
|
||||
if c.status == StatusFailsafe {
|
||||
return c.failsafeProductionLimit
|
||||
}
|
||||
// production limits are negative watts, the api.HEMS cap is positive
|
||||
return new(-c.productionLimit.Value)
|
||||
return new(c.effectiveProductionLimit())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -280,6 +280,51 @@ func TestRun_LimitWithoutDuration(t *testing.T) {
|
|||
assertProductionLimit(t, c, true)
|
||||
}
|
||||
|
||||
// TestProductionLimit_Failsafe verifies both api.HEMS production getters report the
|
||||
// configured failsafe limit while in failsafe, not the stale EG-supplied limit
|
||||
// (#33286): the EG limit had already been released (Value=0) before the heartbeat
|
||||
// was lost, which must not curtail feed-in to 0%.
|
||||
func TestProductionLimit_Failsafe(t *testing.T) {
|
||||
c := newTestEEBus(t)
|
||||
// EG limit already released before the heartbeat is lost.
|
||||
c.productionLimit = ucapi.LoadLimit{Value: 0, IsActive: false}
|
||||
|
||||
require.NoError(t, c.run())
|
||||
require.Equal(t, StatusFailsafe, c.status)
|
||||
|
||||
percent := c.CurtailedPercent()
|
||||
require.NotNil(t, percent)
|
||||
assert.Equal(t, int(testFailsafeProduction/testProductionNominal*100), *percent)
|
||||
|
||||
power := c.MaxProductionPower()
|
||||
require.NotNil(t, power)
|
||||
assert.Equal(t, testFailsafeProduction, *power)
|
||||
}
|
||||
|
||||
// TestProductionLimit_FailsafeUnconfigured verifies that entering failsafe without a
|
||||
// configured failsafe production limit releases a stale EG limit. run() returns early
|
||||
// while in failsafe, so a limit left applied here would stay applied indefinitely.
|
||||
func TestProductionLimit_FailsafeUnconfigured(t *testing.T) {
|
||||
c := newTestEEBus(t)
|
||||
c.failsafeProductionLimit = nil
|
||||
c.heartbeat.Set(struct{}{})
|
||||
|
||||
c.productionLimit = ucapi.LoadLimit{Value: -500, IsActive: true}
|
||||
require.NoError(t, c.run())
|
||||
assertProductionLimit(t, c, true)
|
||||
|
||||
// heartbeat lost
|
||||
c.heartbeat = util.NewValue[struct{}](time.Hour)
|
||||
|
||||
require.NoError(t, c.run())
|
||||
require.Equal(t, StatusFailsafe, c.status)
|
||||
assertProductionLimit(t, c, false)
|
||||
|
||||
power := c.MaxProductionPower()
|
||||
require.NotNil(t, power)
|
||||
assert.Equal(t, 0.0, *power)
|
||||
}
|
||||
|
||||
// TestMaxProductionPower verifies the api.HEMS export cap is a positive wattage,
|
||||
// while LPP states its limits as negative watts.
|
||||
func TestMaxProductionPower(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue