Site: remove root circuit configuration (#14244)
This commit is contained in:
parent
477141e7f1
commit
68e66af64f
4 changed files with 100 additions and 46 deletions
32
cmd/setup.go
32
cmd/setup.go
|
|
@ -22,7 +22,6 @@ import (
|
|||
"github.com/evcc-io/evcc/cmd/shutdown"
|
||||
"github.com/evcc-io/evcc/core"
|
||||
"github.com/evcc-io/evcc/core/keys"
|
||||
"github.com/evcc-io/evcc/core/site"
|
||||
"github.com/evcc-io/evcc/hems"
|
||||
"github.com/evcc-io/evcc/meter"
|
||||
"github.com/evcc-io/evcc/provider/golang"
|
||||
|
|
@ -914,7 +913,7 @@ func configureSiteAndLoadpoints(conf *globalconfig.All) (*core.Site, error) {
|
|||
}
|
||||
|
||||
if len(config.Circuits().Devices()) > 0 {
|
||||
if err := validateCircuits(site, loadpoints); err != nil {
|
||||
if err := validateCircuits(loadpoints); err != nil {
|
||||
return nil, &ClassError{ClassCircuit, err}
|
||||
}
|
||||
}
|
||||
|
|
@ -922,26 +921,39 @@ func configureSiteAndLoadpoints(conf *globalconfig.All) (*core.Site, error) {
|
|||
return site, nil
|
||||
}
|
||||
|
||||
func validateCircuits(site site.API, loadpoints []*core.Loadpoint) error {
|
||||
func validateCircuits(loadpoints []*core.Loadpoint) error {
|
||||
var hasRoot bool
|
||||
|
||||
CONTINUE:
|
||||
for _, dev := range config.Circuits().Devices() {
|
||||
instance := dev.Instance()
|
||||
|
||||
if instance.HasMeter() || site.GetCircuit() == instance {
|
||||
continue
|
||||
isRoot := instance.GetParent() == nil
|
||||
if isRoot {
|
||||
if hasRoot {
|
||||
return errors.New("multiple root circuits")
|
||||
}
|
||||
|
||||
hasRoot = true
|
||||
}
|
||||
|
||||
for _, lp := range loadpoints {
|
||||
if lp.GetCircuit() == instance {
|
||||
if isRoot {
|
||||
return fmt.Errorf("root circuit must not be assigned to loadpoint %s", lp.Title())
|
||||
}
|
||||
|
||||
continue CONTINUE
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("circuit %s has no meter or loadpoint assigned", dev.Config().Name)
|
||||
if !isRoot && !instance.HasMeter() {
|
||||
return fmt.Errorf("circuit %s has no meter and no loadpoint assigned", dev.Config().Name)
|
||||
}
|
||||
}
|
||||
|
||||
if site.GetCircuit() == nil {
|
||||
return errors.New("site has no circuit")
|
||||
if !hasRoot {
|
||||
return errors.New("missing root circuit")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -957,10 +969,6 @@ func configureSite(conf map[string]interface{}, loadpoints []*core.Loadpoint, ta
|
|||
return nil, fmt.Errorf("failed configuring site: %w", err)
|
||||
}
|
||||
|
||||
if len(config.Circuits().Devices()) > 0 && site.GetCircuit() == nil {
|
||||
return nil, errors.New("site has no circuit")
|
||||
}
|
||||
|
||||
return site, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ import (
|
|||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/api/globalconfig"
|
||||
"github.com/evcc-io/evcc/core"
|
||||
"github.com/evcc-io/evcc/meter"
|
||||
"github.com/evcc-io/evcc/server/db"
|
||||
"github.com/evcc-io/evcc/tariff"
|
||||
"github.com/evcc-io/evcc/util/config"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.uber.org/mock/gomock"
|
||||
|
|
@ -39,7 +36,8 @@ func (suite *circuitsTestSuite) TestCircuitConf() {
|
|||
var conf globalconfig.All
|
||||
viper.SetConfigType("yaml")
|
||||
|
||||
suite.Require().NoError(viper.ReadConfig(strings.NewReader(`circuits:
|
||||
suite.Require().NoError(viper.ReadConfig(strings.NewReader(`
|
||||
circuits:
|
||||
- name: master
|
||||
maxPower: 10000
|
||||
- name: slave
|
||||
|
|
@ -63,11 +61,43 @@ loadpoints:
|
|||
|
||||
lps, err := configureLoadpoints(conf)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Len(lps, 1)
|
||||
suite.Require().NotNil(lps[0].GetCircuit())
|
||||
}
|
||||
|
||||
func (suite *circuitsTestSuite) TestLoadpointMissingCircuitError() {
|
||||
func (suite *circuitsTestSuite) TestCircuitMissingLoadpoint() {
|
||||
var conf globalconfig.All
|
||||
viper.SetConfigType("yaml")
|
||||
|
||||
suite.Require().NoError(viper.ReadConfig(strings.NewReader(`
|
||||
circuits:
|
||||
- name: master
|
||||
- name: slave
|
||||
parent: master
|
||||
loadpoints:
|
||||
- charger: test
|
||||
`)))
|
||||
|
||||
suite.Require().NoError(viper.UnmarshalExact(&conf))
|
||||
|
||||
suite.Require().NoError(configureCircuits(conf.Circuits))
|
||||
suite.Require().Len(config.Circuits().Devices(), 2)
|
||||
suite.Require().False(config.Circuits().Devices()[0].Instance().HasMeter())
|
||||
|
||||
// empty charger
|
||||
suite.Require().NoError(config.Chargers().Add(config.NewStaticDevice(config.Named{
|
||||
Name: "test",
|
||||
}, api.Charger(nil))))
|
||||
|
||||
lps, err := configureLoadpoints(conf)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// circuit without device
|
||||
err = validateCircuits(lps)
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Equal("circuit slave has no meter and no loadpoint assigned", err.Error())
|
||||
}
|
||||
|
||||
func (suite *circuitsTestSuite) TestMissingRootCircuit() {
|
||||
var conf globalconfig.All
|
||||
viper.SetConfigType("yaml")
|
||||
|
||||
|
|
@ -94,47 +124,50 @@ loadpoints:
|
|||
lps, err := configureLoadpoints(conf)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
site := core.NewSite()
|
||||
circuit.EXPECT().HasMeter().Return(false)
|
||||
suite.Require().Error(validateCircuits(site, lps))
|
||||
// root circuit
|
||||
circuit.EXPECT().GetParent().Return(nil)
|
||||
circuit.EXPECT().HasMeter().Return(true)
|
||||
suite.Require().NoError(validateCircuits(lps))
|
||||
|
||||
// no root circuit
|
||||
circuit.EXPECT().GetParent().Return(circuit)
|
||||
// circuit.EXPECT().HasMeter().Return(true)
|
||||
err = validateCircuits(lps)
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Equal("missing root circuit", err.Error())
|
||||
}
|
||||
|
||||
func (suite *circuitsTestSuite) TestSiteMissingCircuitError() {
|
||||
func (suite *circuitsTestSuite) TestLoadpointUsingRootCircuit() {
|
||||
var conf globalconfig.All
|
||||
viper.SetConfigType("yaml")
|
||||
|
||||
suite.Require().NoError(viper.ReadConfig(strings.NewReader(`
|
||||
loadpoints:
|
||||
- charger: test
|
||||
site:
|
||||
meters:
|
||||
grid: grid
|
||||
circuit: root
|
||||
`)))
|
||||
|
||||
suite.Require().NoError(viper.UnmarshalExact(&conf))
|
||||
|
||||
lps := []*core.Loadpoint{
|
||||
new(core.Loadpoint),
|
||||
}
|
||||
ctrl := gomock.NewController(suite.T())
|
||||
circuit := api.NewMockCircuit(ctrl)
|
||||
|
||||
// mock circuit
|
||||
suite.Require().NoError(config.Circuits().Add(config.NewStaticDevice(config.Named{
|
||||
Name: "test",
|
||||
}, api.Circuit(nil))))
|
||||
|
||||
// mock meter
|
||||
m, _ := meter.NewConfigurable(func() (float64, error) {
|
||||
return 0, nil
|
||||
})
|
||||
suite.Require().NoError(config.Meters().Add(config.NewStaticDevice(config.Named{
|
||||
Name: "grid",
|
||||
}, api.Meter(m))))
|
||||
Name: "root",
|
||||
}, api.Circuit(circuit))))
|
||||
|
||||
// mock charger
|
||||
suite.Require().NoError(config.Chargers().Add(config.NewStaticDevice(config.Named{
|
||||
Name: "test",
|
||||
}, api.Charger(nil))))
|
||||
|
||||
_, err := configureSite(conf.Site, lps, new(tariff.Tariffs))
|
||||
lps, err := configureLoadpoints(conf)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// root circuit
|
||||
circuit.EXPECT().GetParent().Return(nil)
|
||||
err = validateCircuits(lps)
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Equal("root circuit must not be assigned to loadpoint ", err.Error())
|
||||
}
|
||||
|
|
|
|||
15
core/circuit/config.go
Normal file
15
core/circuit/config.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package circuit
|
||||
|
||||
import (
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/util/config"
|
||||
)
|
||||
|
||||
func Root() api.Circuit {
|
||||
for _, dev := range config.Circuits().Devices() {
|
||||
if c := dev.Instance(); c.GetParent() == nil {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
12
core/site.go
12
core/site.go
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/cmd/shutdown"
|
||||
"github.com/evcc-io/evcc/core/circuit"
|
||||
"github.com/evcc-io/evcc/core/coordinator"
|
||||
"github.com/evcc-io/evcc/core/keys"
|
||||
"github.com/evcc-io/evcc/core/loadpoint"
|
||||
|
|
@ -69,7 +70,8 @@ type Site struct {
|
|||
Voltage float64 `mapstructure:"voltage"` // Operating voltage. 230V for Germany.
|
||||
ResidualPower float64 `mapstructure:"residualPower"` // PV meter only: household usage. Grid meter: household safety margin
|
||||
Meters MetersConfig `mapstructure:"meters"` // Meter references
|
||||
CircuitRef string `mapstructure:"circuit"` // Circuit reference
|
||||
// TODO deprecated
|
||||
CircuitRef_ string `mapstructure:"circuit"` // Circuit reference
|
||||
|
||||
MaxGridSupplyWhileBatteryCharging float64 `mapstructure:"maxGridSupplyWhileBatteryCharging"` // ignore battery charging if AC consumption is above this value
|
||||
|
||||
|
|
@ -169,12 +171,8 @@ func (site *Site) Boot(log *util.Logger, loadpoints []*Loadpoint, tariffs *tarif
|
|||
}
|
||||
|
||||
// circuit
|
||||
if site.CircuitRef != "" {
|
||||
dev, err := config.Circuits().ByName(site.CircuitRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
site.circuit = dev.Instance()
|
||||
if c := circuit.Root(); c != nil {
|
||||
site.circuit = c
|
||||
}
|
||||
|
||||
// grid meter
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue